Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ContextMenuItem.tsx 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import React, { ReactNode, useCallback } from 'react';
  2. import { useSelector } from 'react-redux';
  3. import { makeStyles } from 'tss-react/mui';
  4. import { showOverflowDrawer } from '../../../../toolbox/functions.web';
  5. import Icon from '../../../icons/components/Icon';
  6. import { withPixelLineHeight } from '../../../styles/functions.web';
  7. import { TEXT_OVERFLOW_TYPES } from '../../constants.any';
  8. import TextWithOverflow from './TextWithOverflow';
  9. export interface IProps {
  10. /**
  11. * Label used for accessibility.
  12. */
  13. accessibilityLabel: string;
  14. /**
  15. * The context menu item background color.
  16. */
  17. backgroundColor?: string;
  18. /**
  19. * Component children.
  20. */
  21. children?: ReactNode;
  22. /**
  23. * CSS class name used for custom styles.
  24. */
  25. className?: string;
  26. /**
  27. * Id of dom element controlled by this item. Matches aria-controls.
  28. * Useful if you need this item as a tab element.
  29. *
  30. */
  31. controls?: string;
  32. /**
  33. * Custom icon. If used, the icon prop is ignored.
  34. * Used to allow custom children instead of just the default icons.
  35. */
  36. customIcon?: ReactNode;
  37. /**
  38. * Whether or not the action is disabled.
  39. */
  40. disabled?: boolean;
  41. /**
  42. * Default icon for action.
  43. */
  44. icon?: Function;
  45. /**
  46. * Id of the action container.
  47. */
  48. id?: string;
  49. /**
  50. * Click handler.
  51. */
  52. onClick?: (e?: React.MouseEvent<any>) => void;
  53. /**
  54. * Keydown handler.
  55. */
  56. onKeyDown?: (e: React.KeyboardEvent<HTMLDivElement>) => void;
  57. /**
  58. * Keypress handler.
  59. */
  60. onKeyPress?: (e?: React.KeyboardEvent) => void;
  61. /**
  62. * Overflow type for item text.
  63. */
  64. overflowType?: TEXT_OVERFLOW_TYPES;
  65. /**
  66. * You can use this item as a tab. Defaults to button if not set.
  67. *
  68. * If no onClick handler is provided, we assume the context menu item is
  69. * not interactive and no role will be set.
  70. */
  71. role?: 'tab' | 'button' | 'menuitem';
  72. /**
  73. * Whether the item is marked as selected.
  74. */
  75. selected?: boolean;
  76. /**
  77. * TestId of the element, if any.
  78. */
  79. testId?: string;
  80. /**
  81. * Action text.
  82. */
  83. text?: string;
  84. /**
  85. * Class name for the text.
  86. */
  87. textClassName?: string;
  88. }
  89. const useStyles = makeStyles()(theme => {
  90. return {
  91. contextMenuItem: {
  92. alignItems: 'center',
  93. cursor: 'pointer',
  94. display: 'flex',
  95. minHeight: '40px',
  96. padding: '10px 16px',
  97. boxSizing: 'border-box',
  98. '& > *:not(:last-child)': {
  99. marginRight: theme.spacing(3)
  100. },
  101. '&:hover': {
  102. backgroundColor: theme.palette.ui02
  103. },
  104. '&:active': {
  105. backgroundColor: theme.palette.ui03
  106. },
  107. '&.focus-visible': {
  108. boxShadow: `inset 0 0 0 2px ${theme.palette.action01Hover}`
  109. }
  110. },
  111. selected: {
  112. borderLeft: `3px solid ${theme.palette.action01Hover}`,
  113. paddingLeft: '13px',
  114. backgroundColor: theme.palette.ui02
  115. },
  116. contextMenuItemDisabled: {
  117. pointerEvents: 'none'
  118. },
  119. contextMenuItemDrawer: {
  120. padding: '13px 16px'
  121. },
  122. contextMenuItemIcon: {
  123. '& svg': {
  124. fill: theme.palette.icon01
  125. }
  126. },
  127. text: {
  128. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  129. color: theme.palette.text01
  130. },
  131. drawerText: {
  132. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
  133. }
  134. };
  135. });
  136. const ContextMenuItem = ({
  137. accessibilityLabel,
  138. backgroundColor,
  139. children,
  140. className,
  141. controls,
  142. customIcon,
  143. disabled,
  144. id,
  145. icon,
  146. onClick,
  147. onKeyDown,
  148. onKeyPress,
  149. overflowType,
  150. role = 'button',
  151. selected,
  152. testId,
  153. text,
  154. textClassName }: IProps) => {
  155. const { classes: styles, cx } = useStyles();
  156. const _overflowDrawer: boolean = useSelector(showOverflowDrawer);
  157. const style = backgroundColor ? { backgroundColor } : {};
  158. const onKeyPressHandler = useCallback(e => {
  159. // only trigger the fallback behavior (onClick) if we dont have any explicit keyboard event handler
  160. if (onClick && !onKeyPress && !onKeyDown && (e.key === 'Enter' || e.key === ' ')) {
  161. e.preventDefault();
  162. onClick(e);
  163. }
  164. if (onKeyPress) {
  165. onKeyPress(e);
  166. }
  167. }, [ onClick, onKeyPress, onKeyDown ]);
  168. let tabIndex: undefined | 0 | -1;
  169. if (role === 'tab') {
  170. tabIndex = selected ? 0 : -1;
  171. }
  172. if (role === 'button' && !disabled) {
  173. tabIndex = 0;
  174. }
  175. return (
  176. <div
  177. aria-controls = { controls }
  178. aria-disabled = { disabled }
  179. aria-label = { accessibilityLabel }
  180. aria-selected = { role === 'tab' ? selected : undefined }
  181. className = { cx(styles.contextMenuItem,
  182. _overflowDrawer && styles.contextMenuItemDrawer,
  183. disabled && styles.contextMenuItemDisabled,
  184. selected && styles.selected,
  185. className
  186. ) }
  187. data-testid = { testId }
  188. id = { id }
  189. key = { text }
  190. onClick = { disabled ? undefined : onClick }
  191. onKeyDown = { disabled ? undefined : onKeyDown }
  192. onKeyPress = { disabled ? undefined : onKeyPressHandler }
  193. role = { onClick ? role : undefined }
  194. style = { style }
  195. tabIndex = { onClick ? tabIndex : undefined }>
  196. {customIcon ? customIcon
  197. : icon && <Icon
  198. className = { styles.contextMenuItemIcon }
  199. size = { 20 }
  200. src = { icon } />}
  201. {text && (
  202. <TextWithOverflow
  203. className = { cx(styles.text,
  204. _overflowDrawer && styles.drawerText,
  205. textClassName) }
  206. overflowType = { overflowType } >
  207. {text}
  208. </TextWithOverflow>
  209. )}
  210. {children}
  211. </div>
  212. );
  213. };
  214. export default ContextMenuItem;