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

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