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.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import React, { ReactNode } from 'react';
  2. import { makeStyles } from 'tss-react/mui';
  3. import { ACTION_TRIGGER } from '../../../../participants-pane/constants';
  4. import participantsPaneTheme from '../../../components/themes/participantsPaneTheme.json';
  5. import { isMobileBrowser } from '../../../environment/utils';
  6. import { withPixelLineHeight } from '../../../styles/functions.web';
  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.bodyShortBold),
  68. margin: `0 -${participantsPaneTheme.panePadding}px`,
  69. padding: `${theme.spacing(2)} ${participantsPaneTheme.panePadding}px`,
  70. position: 'relative',
  71. boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)',
  72. minHeight: '40px',
  73. '&:hover, &:focus-within': {
  74. backgroundColor: theme.palette.ui02,
  75. '& .indicators': {
  76. display: 'none'
  77. },
  78. '& .actions': {
  79. display: 'flex',
  80. position: 'relative',
  81. top: 'auto',
  82. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
  83. backgroundColor: theme.palette.ui02
  84. }
  85. },
  86. [`@media(max-width: ${participantsPaneTheme.MD_BREAKPOINT})`]: {
  87. ...withPixelLineHeight(theme.typography.bodyShortBoldLarge),
  88. padding: `${theme.spacing(3)} ${participantsPaneTheme.panePadding}px`
  89. }
  90. },
  91. highlighted: {
  92. backgroundColor: theme.palette.ui02
  93. },
  94. detailsContainer: {
  95. display: 'flex',
  96. alignItems: 'center',
  97. flex: 1,
  98. height: '100%',
  99. overflow: 'hidden',
  100. position: 'relative'
  101. },
  102. name: {
  103. display: 'flex',
  104. flex: 1,
  105. marginRight: theme.spacing(2),
  106. overflow: 'hidden',
  107. flexDirection: 'column',
  108. justifyContent: 'flex-start'
  109. },
  110. indicators: {
  111. display: 'flex',
  112. justifyContent: 'flex-end',
  113. '& > *': {
  114. alignItems: 'center',
  115. display: 'flex',
  116. justifyContent: 'center'
  117. },
  118. '& > *:not(:last-child)': {
  119. marginRight: theme.spacing(2)
  120. },
  121. '& .jitsi-icon': {
  122. padding: '3px'
  123. }
  124. },
  125. indicatorsHidden: {
  126. display: 'none'
  127. },
  128. actionsContainer: {
  129. position: 'absolute',
  130. top: '-1000px',
  131. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
  132. backgroundColor: theme.palette.ui02
  133. },
  134. actionsPermanent: {
  135. display: 'flex',
  136. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui01}`,
  137. backgroundColor: theme.palette.ui01
  138. },
  139. actionsVisible: {
  140. display: 'flex',
  141. boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
  142. backgroundColor: theme.palette.ui02
  143. }
  144. };
  145. });
  146. const ListItem = ({
  147. actions,
  148. className,
  149. icon,
  150. id,
  151. hideActions = false,
  152. indicators,
  153. isHighlighted,
  154. onClick,
  155. onLongPress,
  156. onMouseLeave,
  157. testId,
  158. textChildren,
  159. trigger
  160. }: IProps) => {
  161. const { classes, cx } = useStyles();
  162. const isMobile = isMobileBrowser();
  163. let timeoutHandler: number;
  164. /**
  165. * Set calling long press handler after x milliseconds.
  166. *
  167. * @param {TouchEvent} e - Touch start event.
  168. * @returns {void}
  169. */
  170. function _onTouchStart(e: React.TouchEvent) {
  171. const target = e.touches[0].target;
  172. timeoutHandler = window.setTimeout(() => onLongPress?.(target), 600);
  173. }
  174. /**
  175. * Cancel calling on long press after x milliseconds if the number of milliseconds is not reached
  176. * before a touch move(drag), or just clears the timeout.
  177. *
  178. * @returns {void}
  179. */
  180. function _onTouchMove() {
  181. clearTimeout(timeoutHandler);
  182. }
  183. /**
  184. * Cancel calling on long press after x milliseconds if the number of milliseconds is not reached yet,
  185. * or just clears the timeout.
  186. *
  187. * @returns {void}
  188. */
  189. function _onTouchEnd() {
  190. clearTimeout(timeoutHandler);
  191. }
  192. return (
  193. <div
  194. className = { cx('list-item-container',
  195. classes.container,
  196. isHighlighted && classes.highlighted,
  197. className
  198. ) }
  199. data-testid = { testId }
  200. id = { id }
  201. onClick = { onClick }
  202. { ...(isMobile
  203. ? {
  204. onTouchEnd: _onTouchEnd,
  205. onTouchMove: _onTouchMove,
  206. onTouchStart: _onTouchStart
  207. }
  208. : {
  209. onMouseLeave
  210. }
  211. ) }>
  212. <div> {icon} </div>
  213. <div className = { classes.detailsContainer }>
  214. <div className = { classes.name }>
  215. {textChildren}
  216. </div>
  217. {indicators && (
  218. <div
  219. className = { cx('indicators',
  220. classes.indicators,
  221. (isHighlighted || trigger === ACTION_TRIGGER.PERMANENT) && classes.indicatorsHidden
  222. ) }>
  223. {indicators}
  224. </div>
  225. )}
  226. {!hideActions && (
  227. <div
  228. className = { cx('actions',
  229. classes.actionsContainer,
  230. trigger === ACTION_TRIGGER.PERMANENT && classes.actionsPermanent,
  231. isHighlighted && classes.actionsVisible
  232. ) }>
  233. {actions}
  234. </div>
  235. )}
  236. </div>
  237. </div>
  238. );
  239. };
  240. export default ListItem;