You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ListItem.tsx 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import { Theme } from '@mui/material';
  2. import React, { ReactNode } from 'react';
  3. import { makeStyles } from 'tss-react/mui';
  4. import { ACTION_TRIGGER } from '../../../participants-pane/constants';
  5. import { isMobileBrowser } from '../../environment/utils';
  6. import { withPixelLineHeight } from '../../styles/functions.web';
  7. import participantsPaneTheme from '../themes/participantsPaneTheme.json';
  8. type Props = {
  9. /**
  10. * List item actions.
  11. */
  12. actions: ReactNode;
  13. /**
  14. * List item container class name.
  15. */
  16. className?: string;
  17. /**
  18. * Whether or not the actions should be hidden.
  19. */
  20. hideActions?: boolean;
  21. /**
  22. * Icon to be displayed on the list item. (Avatar for participants).
  23. */
  24. icon: ReactNode;
  25. /**
  26. * Id of the container.
  27. */
  28. id?: string;
  29. /**
  30. * Indicators to be displayed on the list item.
  31. */
  32. indicators?: ReactNode;
  33. /**
  34. * Whether or not the item is highlighted.
  35. */
  36. isHighlighted?: boolean;
  37. /**
  38. * Click handler.
  39. */
  40. onClick?: (e?: React.MouseEvent) => void;
  41. /**
  42. * Long press handler.
  43. */
  44. onLongPress?: (e?: EventTarget) => void;
  45. /**
  46. * Mouse leave handler.
  47. */
  48. onMouseLeave?: (e?: React.MouseEvent) => void;
  49. /**
  50. * Data test id.
  51. */
  52. testId?: string;
  53. /**
  54. * Text children to be displayed on the list item.
  55. */
  56. textChildren: ReactNode | string;
  57. /**
  58. * The actions trigger. Can be Hover or Permanent.
  59. */
  60. trigger: string;
  61. };
  62. const useStyles = makeStyles()((theme: Theme) => {
  63. return {
  64. container: {
  65. alignItems: 'center',
  66. color: theme.palette.text01,
  67. display: 'flex',
  68. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  69. margin: `0 -${participantsPaneTheme.panePadding}px`,
  70. padding: `0 ${participantsPaneTheme.panePadding}px`,
  71. position: 'relative',
  72. boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)',
  73. minHeight: '40px',
  74. '&:hover': {
  75. backgroundColor: theme.palette.ui02,
  76. '& .indicators': {
  77. display: 'none'
  78. },
  79. '& .actions': {
  80. display: 'flex',
  81. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
  82. backgroundColor: theme.palette.ui02
  83. }
  84. },
  85. [`@media(max-width: ${participantsPaneTheme.MD_BREAKPOINT})`]: {
  86. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
  87. padding: `${theme.spacing(2)} ${participantsPaneTheme.panePadding}px`
  88. }
  89. },
  90. highlighted: {
  91. backgroundColor: theme.palette.ui02
  92. },
  93. detailsContainer: {
  94. display: 'flex',
  95. alignItems: 'center',
  96. flex: 1,
  97. height: '100%',
  98. overflow: 'hidden',
  99. position: 'relative'
  100. },
  101. name: {
  102. display: 'flex',
  103. flex: 1,
  104. marginRight: theme.spacing(2),
  105. overflow: 'hidden',
  106. flexDirection: 'column',
  107. justifyContent: 'flex-start'
  108. },
  109. indicators: {
  110. display: 'flex',
  111. justifyContent: 'flex-end',
  112. '& > *': {
  113. alignItems: 'center',
  114. display: 'flex',
  115. justifyContent: 'center'
  116. },
  117. '& > *:not(:last-child)': {
  118. marginRight: theme.spacing(2)
  119. },
  120. '& .jitsi-icon': {
  121. padding: '3px'
  122. }
  123. },
  124. indicatorsHidden: {
  125. display: 'none'
  126. },
  127. actionsContainer: {
  128. display: 'none',
  129. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
  130. backgroundColor: theme.palette.ui02
  131. },
  132. actionsPermanent: {
  133. display: 'flex',
  134. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui01}`,
  135. backgroundColor: theme.palette.ui01
  136. },
  137. actionsVisible: {
  138. display: 'flex',
  139. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
  140. backgroundColor: theme.palette.ui02
  141. }
  142. };
  143. });
  144. const ListItem = ({
  145. actions,
  146. className,
  147. icon,
  148. id,
  149. hideActions = false,
  150. indicators,
  151. isHighlighted,
  152. onClick,
  153. onLongPress,
  154. onMouseLeave,
  155. testId,
  156. textChildren,
  157. trigger
  158. }: Props) => {
  159. const { classes: styles, cx } = useStyles();
  160. const _isMobile = isMobileBrowser();
  161. let timeoutHandler: number;
  162. /**
  163. * Set calling long press handler after x milliseconds.
  164. *
  165. * @param {TouchEvent} e - Touch start event.
  166. * @returns {void}
  167. */
  168. function _onTouchStart(e: React.TouchEvent) {
  169. const target = e.touches[0].target;
  170. timeoutHandler = window.setTimeout(() => onLongPress?.(target), 600);
  171. }
  172. /**
  173. * Cancel calling on long press after x milliseconds if the number of milliseconds is not reached
  174. * before a touch move(drag), or just clears the timeout.
  175. *
  176. * @returns {void}
  177. */
  178. function _onTouchMove() {
  179. clearTimeout(timeoutHandler);
  180. }
  181. /**
  182. * Cancel calling on long press after x milliseconds if the number of milliseconds is not reached yet,
  183. * or just clears the timeout.
  184. *
  185. * @returns {void}
  186. */
  187. function _onTouchEnd() {
  188. clearTimeout(timeoutHandler);
  189. }
  190. return (
  191. <div
  192. className = { cx('list-item-container',
  193. styles.container,
  194. isHighlighted && styles.highlighted,
  195. className
  196. ) }
  197. data-testid = { testId }
  198. id = { id }
  199. onClick = { onClick }
  200. { ...(_isMobile
  201. ? {
  202. onTouchEnd: _onTouchEnd,
  203. onTouchMove: _onTouchMove,
  204. onTouchStart: _onTouchStart
  205. }
  206. : {
  207. onMouseLeave
  208. }
  209. ) }>
  210. <div> {icon} </div>
  211. <div className = { styles.detailsContainer }>
  212. <div className = { styles.name }>
  213. {textChildren}
  214. </div>
  215. {indicators && (
  216. <div
  217. className = { cx('indicators',
  218. styles.indicators,
  219. (isHighlighted || trigger === ACTION_TRIGGER.PERMANENT) && styles.indicatorsHidden
  220. ) }>
  221. {indicators}
  222. </div>
  223. )}
  224. {!hideActions && (
  225. <div
  226. className = { cx('actions',
  227. styles.actionsContainer,
  228. trigger === ACTION_TRIGGER.PERMANENT && styles.actionsPermanent,
  229. isHighlighted && styles.actionsVisible
  230. ) }>
  231. {actions}
  232. </div>
  233. )}
  234. </div>
  235. </div>
  236. );
  237. };
  238. export default ListItem;