@ringcentral/spring-ui 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/Alert.js +3 -3
  2. package/Announcement.js +1 -1
  3. package/Autocomplete.js +174 -236
  4. package/Avatar.js +27 -13
  5. package/BottomNavigation.js +0 -3
  6. package/Checkbox.js +0 -2
  7. package/DatePicker.js +5 -13
  8. package/DateUtilsContext.js +0 -1
  9. package/Dialer.js +2 -50
  10. package/Dialog.js +4 -4
  11. package/Dropdown.js +0 -7
  12. package/FocusTrap.js +0 -32
  13. package/InlineEditable.js +0 -4
  14. package/Link.js +1 -1
  15. package/ListItem.js +1 -1
  16. package/ListNavigation.js +0 -6
  17. package/Menu.js +1 -1
  18. package/MenuDivider.js +2 -2
  19. package/MenuFooter.js +4 -4
  20. package/MenuHeader.js +4 -4
  21. package/MenuItem.js +1 -1
  22. package/MenuItemText.js +1 -1
  23. package/PageHeader.js +6 -4
  24. package/Popper.js +0 -1
  25. package/Rating.js +0 -5
  26. package/SingleFilter.js +0 -1
  27. package/Slider.js +1 -4
  28. package/Snackbar.js +0 -14
  29. package/SortableListItem.js +0 -1
  30. package/SplitButton.js +1 -3
  31. package/StatusIndicator.js +1 -1
  32. package/SubMenu.js +0 -4
  33. package/SwipeableDrawer.js +8 -146
  34. package/Switch.js +1 -4
  35. package/Tab.js +7 -8
  36. package/TabContext.js +0 -5
  37. package/TableBody.js +1 -1
  38. package/TableCell.js +0 -6
  39. package/TableRow.js +0 -1
  40. package/Tabs.js +20 -118
  41. package/Text.js +2 -4
  42. package/TimePicker.js +7 -46
  43. package/VirtualizedList.js +2 -30
  44. package/chunks/Alert.js +8 -7
  45. package/chunks/BottomNavigationItem.js +0 -1
  46. package/chunks/Button.js +0 -1
  47. package/chunks/Chip.js +0 -5
  48. package/chunks/Drawer.js +1 -30
  49. package/chunks/ListItem.js +3 -6
  50. package/chunks/Menu.js +3 -3
  51. package/chunks/MenuDivider.js +2 -3
  52. package/chunks/MenuItemText.js +3 -3
  53. package/chunks/MenuList.js +0 -3
  54. package/chunks/PickerTextField.js +0 -1
  55. package/chunks/Popover.js +0 -5
  56. package/chunks/Select.js +2 -39
  57. package/chunks/Squircle.js +0 -7
  58. package/chunks/StatusIndicator.js +1 -0
  59. package/chunks/StepIcon.js +1 -3
  60. package/chunks/StepperContext.js +0 -7
  61. package/chunks/Tooltip.js +0 -36
  62. package/chunks/VirtualizedMenu.js +2 -78
  63. package/chunks/getFade.js +2 -0
  64. package/chunks/useFocusTrapStack.js +0 -8
  65. package/chunks/useHelperTextId.js +0 -24
  66. package/chunks/useListNavigation.js +2 -17
  67. package/chunks/useMenuItemSelect.js +6 -19
  68. package/chunks/useMotionSafeTransition.js +0 -4
  69. package/chunks/useNestedParentMenu.js +0 -2
  70. package/chunks/usePrefersReducedMotion.js +0 -10
  71. package/chunks/useTooltipForceHide.js +0 -4
  72. package/chunks/useTrayNavigate.js +0 -1
  73. package/index.css +392 -120
  74. package/index.js +1 -1
  75. package/package.json +4 -4
  76. package/src/Accordion/Accordion.type.d.ts +0 -21
  77. package/src/AccordionHeader/AccordionHeader.type.d.ts +0 -14
  78. package/src/Alert/Alert.type.d.ts +0 -32
  79. package/src/Animation/components/ExpandCollapseCaret/ExpandCollapseCaret.d.ts +0 -4
  80. package/src/Animation/components/ShowHideEye/ShowHideEye.d.ts +0 -4
  81. package/src/Animation/hooks/useMotionSafeTransition/useMotionSafeTransition.d.ts +0 -4
  82. package/src/Autocomplete/Autocomplete.d.ts +0 -5
  83. package/src/Autocomplete/Autocomplete.type.d.ts +0 -84
  84. package/src/Autocomplete/SuggestionList/SuggestionList.d.ts +5 -89
  85. package/src/Autocomplete/SuggestionList/SuggestionListItem.d.ts +0 -2
  86. package/src/Autocomplete/SuggestionList/utils/SuggestionListItem.d.ts +1 -13
  87. package/src/Autocomplete/SuggestionList/utils/useDynamicHeight.d.ts +0 -6
  88. package/src/Autocomplete/SuggestionList/utils/useHighlightScroll.d.ts +0 -20
  89. package/src/Autocomplete/SuggestionList/utils/useSuggestionList.d.ts +3 -41
  90. package/src/Autocomplete/SuggestionList/utils/useSuggestionListGroup.d.ts +0 -49
  91. package/src/Autocomplete/useAutocomplete.d.ts +1 -62
  92. package/src/Autocomplete/useAutocompleteTag.d.ts +0 -33
  93. package/src/Avatar/Avatar.type.d.ts +0 -58
  94. package/src/AvatarGroup/AvatarGroup.type.d.ts +0 -3
  95. package/src/Backdrop/Backdrop.type.d.ts +0 -4
  96. package/src/Badge/Badge.type.d.ts +0 -33
  97. package/src/Block/Block.type.d.ts +0 -24
  98. package/src/BlockHeader/BlockHeader.type.d.ts +0 -5
  99. package/src/BottomNavigation/BottomNavigation.type.d.ts +0 -34
  100. package/src/BottomNavigationItem/BottomNavigationItem.type.d.ts +0 -39
  101. package/src/Button/Button.type.d.ts +0 -30
  102. package/src/CallButton/CallButton.type.d.ts +0 -4
  103. package/src/Checkbox/Checkbox.type.d.ts +0 -17
  104. package/src/Chip/Chip.type.d.ts +0 -41
  105. package/src/CircularProgressIndicator/CircularProgressIndicator.type.d.ts +0 -19
  106. package/src/DatePicker/Calendar.d.ts +1 -20
  107. package/src/DatePicker/DatePicker.type.d.ts +0 -18
  108. package/src/DatePicker/DatePickerHeader.d.ts +1 -12
  109. package/src/DatePicker/Day.d.ts +0 -7
  110. package/src/DatePicker/Year.d.ts +0 -6
  111. package/src/DatePicker/Years.d.ts +0 -7
  112. package/src/Dialer/DialDelete/DialDelete.d.ts +0 -3
  113. package/src/Dialer/DialPad/DialPad.d.ts +0 -106
  114. package/src/Dialer/DialPad/utils/types.d.ts +0 -5
  115. package/src/Dialer/DialPad/utils/useDialKeyboard.d.ts +0 -5
  116. package/src/Dialer/DialPad/utils/useKeyAudio.d.ts +0 -9
  117. package/src/Dialer/DialPadButton/DialPadButton.d.ts +0 -14
  118. package/src/Dialer/DialTextField/DialTextField.d.ts +0 -23
  119. package/src/Dialer/DialTextField/utils/getDialPadValueOnlyRegex.d.ts +0 -3
  120. package/src/Dialer/DialTextField/utils/useFixedEndSelection.d.ts +0 -3
  121. package/src/Dialog/Dialog.type.d.ts +0 -11
  122. package/src/Divider/Divider.type.d.ts +0 -17
  123. package/src/DragHandle/DragHandle.type.d.ts +0 -4
  124. package/src/Drawer/Drawer.d.ts +0 -28
  125. package/src/Drawer/Drawer.type.d.ts +0 -84
  126. package/src/Dropdown/Dropdown.type.d.ts +0 -64
  127. package/src/EmptyState/EmptyState.type.d.ts +0 -12
  128. package/src/FabButton/FabButton.type.d.ts +0 -5
  129. package/src/FocusTrap/FocusTrap.d.ts +0 -38
  130. package/src/FocusTrap/useFocusTrapStack.d.ts +0 -9
  131. package/src/FormField/FormField.type.d.ts +0 -26
  132. package/src/FormField/useHelperTextId.d.ts +0 -25
  133. package/src/FormLabel/FormLabel.type.d.ts +0 -14
  134. package/src/Icon/Icon.type.d.ts +0 -15
  135. package/src/IconButton/IconButton.type.d.ts +0 -30
  136. package/src/IconButton/IconButtonLabel.type.d.ts +0 -3
  137. package/src/IconButtonGroup/IconButtonGroup.type.d.ts +0 -8
  138. package/src/InlineEditable/InlineEditable.type.d.ts +0 -75
  139. package/src/LinearProgressIndicator/LinearProgressIndicator.type.d.ts +0 -31
  140. package/src/Link/Link.classes.d.ts +1 -1
  141. package/src/Link/Link.type.d.ts +1 -10
  142. package/src/ListItem/ListItem.type.d.ts +0 -41
  143. package/src/ListItemText/ListItemText.type.d.ts +0 -21
  144. package/src/ListNavigation/useListNavigation.d.ts +0 -16
  145. package/src/Menu/Menu.type.d.ts +0 -10
  146. package/src/MenuFooter/MenuFooter.type.d.ts +0 -4
  147. package/src/MenuHeader/MenuHeader.type.d.ts +0 -10
  148. package/src/MenuItem/MenuItem.type.d.ts +0 -19
  149. package/src/MenuItem/MenuItemSelectContext.d.ts +0 -3
  150. package/src/MenuItem/useMenuItemSelect.d.ts +0 -21
  151. package/src/MenuItemText/MenuItemText.type.d.ts +0 -17
  152. package/src/MenuList/MenuList.type.d.ts +0 -4
  153. package/src/MenuList/MenuListContext.d.ts +0 -6
  154. package/src/Modal/InnerModal.type.d.ts +0 -5
  155. package/src/Modal/Modal.type.d.ts +0 -11
  156. package/src/Modal/ModalContext.d.ts +0 -4
  157. package/src/Option/Option.type.d.ts +0 -3
  158. package/src/Overlap/Overlap.type.d.ts +0 -11
  159. package/src/PageHeader/PageHeader.type.d.ts +0 -4
  160. package/src/PickerTextField/PickerTextField.type.d.ts +0 -10
  161. package/src/Popover/Popover.type.d.ts +0 -29
  162. package/src/Popper/Popper.type.d.ts +0 -61
  163. package/src/Radio/Radio.type.d.ts +0 -5
  164. package/src/RadioGroup/RadioGroup.type.d.ts +0 -4
  165. package/src/Rating/Rating.type.d.ts +0 -74
  166. package/src/Select/Select.type.d.ts +0 -84
  167. package/src/SingleFilter/FilterButton/FilterButton.type.d.ts +0 -16
  168. package/src/SingleFilter/SingleFilter.type.d.ts +0 -26
  169. package/src/Skeleton/Skeleton.type.d.ts +0 -1
  170. package/src/Skeleton/SkeletonContainer/SkeletonContainer.classes.d.ts +1 -1
  171. package/src/Slider/Slider.classes.d.ts +1 -5
  172. package/src/Slider/Slider.type.d.ts +0 -104
  173. package/src/Snackbar/Snackbar.type.d.ts +0 -18
  174. package/src/SplitButton/SplitButton.type.d.ts +0 -21
  175. package/src/Squircle/Squircle.type.d.ts +0 -3
  176. package/src/StatusIndicator/StatusIndicator.classes.d.ts +1 -1
  177. package/src/StatusIndicator/StatusIndicator.type.d.ts +1 -9
  178. package/src/Step/Step.type.d.ts +0 -29
  179. package/src/Step/StepContext.d.ts +0 -7
  180. package/src/StepButton/StepButton.type.d.ts +0 -15
  181. package/src/StepContent/StepContent.type.d.ts +0 -7
  182. package/src/StepIcon/StepIcon.type.d.ts +0 -26
  183. package/src/StepLabel/StepLabel.type.d.ts +0 -23
  184. package/src/Stepper/Stepper.type.d.ts +0 -27
  185. package/src/SwipeableDrawer/SwipeableDrawer.d.ts +0 -26
  186. package/src/SwipeableDrawer/SwipeableDrawer.types.d.ts +0 -67
  187. package/src/SwipeableDrawer/hooks/useDrawerGestures.d.ts +0 -110
  188. package/src/SwipeableDrawer/hooks/useElementSizeObserver.d.ts +0 -5
  189. package/src/SwipeableDrawer/hooks/useSnapPoints.d.ts +0 -60
  190. package/src/Switch/Switch.type.d.ts +0 -9
  191. package/src/Tab/Tab.classes.d.ts +3 -2
  192. package/src/Tab/Tab.type.d.ts +0 -29
  193. package/src/TabContext/TabContext.d.ts +0 -4
  194. package/src/TabContext/TabContext.type.d.ts +0 -7
  195. package/src/TabPanel/TabPanel.type.d.ts +0 -10
  196. package/src/Table/Table.type.d.ts +0 -19
  197. package/src/TableBody/TableBody.type.d.ts +0 -3
  198. package/src/TableCell/TableCell.d.ts +0 -4
  199. package/src/TableCell/TableCell.type.d.ts +0 -35
  200. package/src/TableContainer/TableContainer.type.d.ts +0 -7
  201. package/src/TableFooter/TableFooter.type.d.ts +0 -3
  202. package/src/TableHead/TableHead.type.d.ts +0 -6
  203. package/src/TablePagination/TablePagination.type.d.ts +0 -47
  204. package/src/TableRow/TableRow.type.d.ts +0 -17
  205. package/src/Tabs/TabMoreMenu.d.ts +0 -16
  206. package/src/Tabs/TabScrollButton.d.ts +0 -10
  207. package/src/Tabs/Tabs.classes.d.ts +1 -1
  208. package/src/Tabs/Tabs.d.ts +1 -1
  209. package/src/Tabs/Tabs.type.d.ts +2 -36
  210. package/src/Tabs/TabsContext.d.ts +2 -0
  211. package/src/Tabs/hooks/useTabsScroller.d.ts +0 -30
  212. package/src/Tag/Tag.type.d.ts +0 -12
  213. package/src/Text/Text.type.d.ts +0 -13
  214. package/src/TextField/TextField.type.d.ts +0 -64
  215. package/src/Textarea/Textarea.type.d.ts +0 -13
  216. package/src/TimePicker/NumberPicker.type.d.ts +0 -12
  217. package/src/TimePicker/SelectionView.d.ts +0 -6
  218. package/src/TimePicker/TimeButton.d.ts +0 -3
  219. package/src/TimePicker/TimePicker.type.d.ts +0 -11
  220. package/src/TimePicker/ToggleText.type.d.ts +0 -3
  221. package/src/TimePicker/utils/TimeBoundary.d.ts +0 -3
  222. package/src/TimePicker/utils/TimePickerHelper.d.ts +0 -19
  223. package/src/TimePicker/utils/getNumberPickerBoundary.d.ts +0 -3
  224. package/src/Tooltip/TooltipTrigger.a11y.d.ts +0 -9
  225. package/src/Tooltip/TooltipTrigger.type.d.ts +0 -44
  226. package/src/Tooltip/TooltipView.type.d.ts +0 -25
  227. package/src/Tooltip/useTooltipForceHide.d.ts +0 -4
  228. package/src/Tray/Tray.type.d.ts +0 -13
  229. package/src/Tray/TrayContext.d.ts +0 -4
  230. package/src/Tray/hooks/useTray.d.ts +0 -16
  231. package/src/Tray/hooks/useTrayPanel.d.ts +0 -3
  232. package/src/VirtualizedList/VirtualizedList.type.d.ts +0 -27
  233. package/src/VirtualizedMenu/VirtualizedMenu.type.d.ts +0 -17
@@ -7,17 +7,10 @@ const SQUIRCLE_PATH = 'M 0.5 0 C 0.879 0 1 0.121 1 0.5 C 1 0.879 0.879 1 0.5 1 C
7
7
  const Squircle = forwardRef((inProps, ref) => {
8
8
  const props = useThemeProps({ props: inProps, name: 'Squircle' });
9
9
  const { component = 'div', focusVisible, active, children: childrenProp, className, classes, interactive = true, fillShape: fillShapeProp = false, ...rest } = props;
10
- /**
11
- * If the children passed to this component have a conditionally rendered
12
- * element, using toArray will ensure that any falsy children are removed.
13
- * Otherwise fillShape will always be false even if only 1 truthy child
14
- * is passed to this component.
15
- */
16
10
  const children = React.Children.toArray(childrenProp);
17
11
  const firstChild = children[0];
18
12
  const fillShape = fillShapeProp &&
19
13
  firstChild &&
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
14
  isValidElement(firstChild);
22
15
  const cls = useClasses('squircle', classes, className);
23
16
  const clipId = useId('sui-squircle-clip', true);
@@ -7,6 +7,7 @@ import { I as Icon } from './Icon.js';
7
7
  const IconSizeMap = {
8
8
  large: 'small',
9
9
  medium: 'xsmall',
10
+ small: 'xsmall',
10
11
  };
11
12
  const StatusIndicator = (inProps) => {
12
13
  const props = useThemeProps({ props: inProps, name: 'StatusIndicator' });
@@ -5,9 +5,7 @@ import { AlertFilledMd, AlertMd, CheckBoldMd, CheckMd } from '@ringcentral/sprin
5
5
  import { useThemeProps } from '@ringcentral/spring-theme';
6
6
  import { I as Icon } from './Icon.js';
7
7
 
8
- const StepIcon = forwardRef(
9
- // @ts-ignore
10
- (inProps, ref) => {
8
+ const StepIcon = forwardRef((inProps, ref) => {
11
9
  const props = useThemeProps({ props: inProps, name: 'StepIcon' });
12
10
  const { classes, className, children, active = false, completed = false, error = false, disabled = false, variant = 'contained', icon, symbol, ...rest } = props;
13
11
  const cls = useClasses('step-icon', classes, className);
@@ -1,13 +1,6 @@
1
1
  import { createContext, useContext } from 'react';
2
2
 
3
- /**
4
- * Provides information about the current step in Stepper.
5
- */
6
3
  const StepContext = createContext({});
7
- /**
8
- * Returns the current StepContext or an empty object if no StepContext
9
- * has been defined in the component tree.
10
- */
11
4
  const useStepContext = () => useContext(StepContext);
12
5
 
13
6
  const StepperContext = createContext({});
package/chunks/Tooltip.js CHANGED
@@ -10,15 +10,6 @@ import { g as getFade } from './getFade.js';
10
10
  import { Popper } from '../Popper.js';
11
11
  import { P as PopperPaper } from './PopperPaper.js';
12
12
 
13
- /**
14
- * Accessibility naming strategy:
15
- * - Default (label mode): `title` is treated as an accessible label
16
- * - string `title` → `aria-label`
17
- * - React element `title` → `aria-labelledby` while the tooltip is open
18
- * - Description mode (`describeChild = true`): `title` is treated as an accessible description
19
- * - when closed → native `title` attribute
20
- * - when open → `aria-describedby` pointing to the tooltip id
21
- */
22
13
  const ARIA_DESCRIBEDBY = 'aria-describedby';
23
14
  const ARIA_LABEL = 'aria-label';
24
15
  const ARIA_LABELLEDBY = 'aria-labelledby';
@@ -47,49 +38,33 @@ function computeTooltipA11y({ title, describeChild, triggerWhenDisabled, open, t
47
38
  });
48
39
  }
49
40
  if (describeChild) {
50
- // Description mode: tooltip acts as an accessible description
51
- // When closed: add title attribute (unless user provided their own)
52
- // When open: swap to aria-describedby
53
41
  if (!hasAriaDescribedBy) {
54
42
  if (!hasUserTitle) {
55
- // Only set tooltip title if user hasn't provided one
56
43
  nameOrDescProps.title =
57
44
  !open && titleIsString ? title : null;
58
45
  }
59
46
  else if (open) {
60
- // If user provided a title and tooltip is open, remove native title to prevent duplicate
61
47
  nameOrDescProps.title = null;
62
48
  }
63
49
  nameOrDescProps[ARIA_DESCRIBEDBY] = open ? tooltipId : null;
64
50
  }
65
51
  }
66
52
  else {
67
- // Label mode (default): tooltip acts as an accessible label
68
- // Use aria-label for strings, aria-labelledby for React elements
69
53
  if (titleIsString && !hasAriaLabel) {
70
54
  nameOrDescProps[ARIA_LABEL] = title;
71
- // For string titles, Tooltip fully owns the label; always remove native title
72
55
  nameOrDescProps.title = null;
73
56
  }
74
57
  else if (!titleIsString && !hasAriaLabelledBy && title) {
75
- // Only set aria-labelledby if title is truthy (React element exists)
76
58
  nameOrDescProps[ARIA_LABELLEDBY] = open ? tooltipId : null;
77
- // Only remove native title when tooltip is open to prevent duplicate tooltips.
78
- // When closed, leave existing title as a fallback accessible name.
79
59
  if (open) {
80
60
  nameOrDescProps.title = null;
81
61
  }
82
62
  }
83
63
  else if (open) {
84
- // Tooltip is open but we're not providing a label (e.g. child already has aria-label);
85
- // still remove native title to avoid double tooltips.
86
64
  nameOrDescProps.title = null;
87
65
  }
88
66
  }
89
67
  if (triggerWhenDisabled) {
90
- // When open and child does not already have aria-describedby: use tooltip id,
91
- // and always remove native title on the disabled control to avoid double tooltips.
92
- // When closed and title is a string: use native title as a fallback for SR on disabled.
93
68
  if (open) {
94
69
  if (!hasAriaDescribedBy) {
95
70
  disabledOverrides[ARIA_DESCRIBEDBY] = tooltipId;
@@ -149,15 +124,10 @@ const TooltipTrigger = (props, ref) => {
149
124
  onOpenStateChange?.(e, false);
150
125
  };
151
126
  useEventListener(anchorRef, 'mouseenter', (e) => {
152
- // Ensures touch devices do not trigger the tooltip, mouseleave will never be fired and the tooltip will be stuck open
153
127
  if (didInitiateWithTouch.current) {
154
- // Immediately reset the flag, touchstart will fire again on the next tap and will set the flag back to true
155
128
  didInitiateWithTouch.current = false;
156
129
  return;
157
130
  }
158
- // Remove the title ahead of time from the trigger.
159
- // We don't want to wait for the next render commit.
160
- // We would risk displaying two tooltips at the same time (native + this one).
161
131
  if (anchorRef.current) {
162
132
  anchorRef.current.removeAttribute('title');
163
133
  }
@@ -174,22 +144,17 @@ const TooltipTrigger = (props, ref) => {
174
144
  useEventListener(anchorRef, 'focus', (e) => {
175
145
  if (disableFocusListener)
176
146
  return;
177
- // handleFocusVisible only support react event type, but it only need target event
178
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
147
  handleFocusVisible(e);
180
148
  if (isFocusVisibleRef.current === true) {
181
149
  handleOpen(e);
182
150
  }
183
151
  });
184
152
  useEventListener(anchorRef, 'blur', (e) => {
185
- // handleBlurVisible only support react event type, but this param is useless
186
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
153
  handleBlurVisible(e);
188
154
  if (isFocusVisibleRef.current === false) {
189
155
  handleClose(e);
190
156
  }
191
157
  });
192
- // Handle Escape key to close tooltip
193
158
  useEventListener(open ? document : null, 'keydown', (e) => {
194
159
  if (e.key === 'Escape') {
195
160
  handleClose(e);
@@ -241,7 +206,6 @@ const Tooltip = forwardRef((inProps, ref) => {
241
206
  const props = useThemeProps({ props: inProps, name: 'Tooltip' });
242
207
  const { children, open: openProp, delay, onOpenStateChange, title, disableFocusListener, disableBackdropAnimation, triggerWhenDisabled, triggerWrapperProps, PopperProps, describeChild = false, ...rest } = props;
243
208
  const renderTooltip = (open, getAnchorEl, tooltipId) => {
244
- // hides '', null/undefined, false, NaN)
245
209
  const effectiveOpen = open && !(!title && title !== 0);
246
210
  const content = effectiveOpen ? (jsx(TooltipView, { disableInteractions: openProp === undefined, ...rest, PopperProps: { ...PopperProps, id: tooltipId }, anchorEl: getAnchorEl, ref: ref, children: title })) : null;
247
211
  return disableBackdropAnimation ? (jsx(Fragment, { children: content })) : (jsx(AnimatePresence, { children: content }));
@@ -24,8 +24,7 @@ const VirtualizedMenuList = (props) => {
24
24
  else if (typeof initialFocus === 'number') {
25
25
  actions.current?.focusByIndex(initialFocus);
26
26
  }
27
- // eslint-disable-next-line react-hooks/exhaustive-deps
28
- }, []); // only excute effect when menu list mounted
27
+ }, []);
29
28
  return (jsx(VirtualizedList, { actions: handleListActions, ...rest, ListProps: combineProps({
30
29
  role: 'menu',
31
30
  }, rest.ListProps), ref: listRef, totalListHeightChanged: totalListHeightChangedProp, children: children }));
@@ -71,66 +70,24 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
71
70
  if (!popperPaperContentRef.current)
72
71
  return;
73
72
  popperPaperContentRef.current.style.maxHeight = `${availableHeight.current}px`;
74
- /**
75
- * Reset the paper content container height to `auto`, otherwise the virtuoso scroller
76
- * will start misbehaving. We only need to set the height of the container during the
77
- * initial list height computation to ensure the Popover places it appropriately.
78
- */
79
73
  if (paperContentContainerRef.current) {
80
74
  paperContentContainerRef.current.style.height = `auto`;
81
75
  }
82
76
  });
83
77
  const handleTotalListHeightChanged = useEventCallback((listHeight) => {
84
- /**
85
- * As the VirtualizedList has components (Header/Footer used for vertical padding), the
86
- * first 2 calls to `totalListHeightChanged` after triggering the Menu to open will have
87
- * only computed the height of those components and not the full list. The next calls
88
- * will include the actual list height. We ignore this call to prevent the menu from
89
- * flickering between positions/performing any other unnecessary computations. We also
90
- * need to know the entire list height to be able to choose the correct placement.
91
- *
92
- * If we didn't do this, we'd compute the available height prematurely based on an
93
- * incorrect initial placement, and the height parameter in further calls to
94
- * `handleTotalListHeightChanged` would be the adjusted height of the popover for
95
- * the available space in the viewport and not the actual height of the list. For
96
- * example, if the user set the `placement` prop to `top` but had a very long list
97
- * which could not fit above the anchor element, the menu could initially appear
98
- * above the input despite the fact its best placement is below. The user would
99
- * then see this placement change when re-opening the menu or scrolling, where the
100
- * Popover would auto-update the position and then choose the correct placement.
101
- */
102
78
  if (waitForListHeightComputeCallsCount.current < 2) {
103
79
  waitForListHeightComputeCallsCount.current += 1;
104
80
  return;
105
81
  }
106
82
  computedListHeight.current = listHeight;
107
- /**
108
- * Set the height of the paper content container to the height of the list. This is
109
- * necessary to ensure the Popover determines the correct amount of space needed for
110
- * the list. If we set the height directly on the list element it would cause an
111
- * expensive rerender and if we didn't do either the computed available height
112
- * for the Popover would not be correct.
113
- */
114
83
  if (paperContentContainerRef.current) {
115
84
  paperContentContainerRef.current.style.height = `${listHeight}px`;
116
85
  }
117
- // Update the Menu height now we've correctly computed the list height
118
86
  updateMenuHeight();
119
- // Update the Popper position after the list height has been computed
120
87
  popperActions.current?.update();
121
- // No need to wait for the height compute once we have the list height
122
88
  if (waitForHeightCompute) {
123
- /**
124
- * Perform all pending actions once we know the list height has settled. This is necessary
125
- * as those pending actions expect the list to have fully rendered before they can be ran.
126
- */
127
89
  requestAnimationFrame(() => {
128
90
  setDeferPendingVirtualizedListActions(false);
129
- /**
130
- * Wait for the next repaint to ensure the pending actions have been completed and the height
131
- * has been computed and painted. If we didn't do this, the menu could flicker if the initial
132
- * scroll position was set to a value that would cause the list to scroll down.
133
- */
134
91
  requestAnimationFrame(() => {
135
92
  setWaitForHeightCompute(false);
136
93
  });
@@ -138,10 +95,6 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
138
95
  }
139
96
  });
140
97
  const handleAvailableHeightChange = useEventCallback((value) => {
141
- /**
142
- * If we haven't yet computed the height of the virtualized list, we need to ignore
143
- * the available height as the value will be for when the list has not yet fully rendered.
144
- */
145
98
  if (computedListHeight.current === -1) {
146
99
  return;
147
100
  }
@@ -150,34 +103,14 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
150
103
  updatePopoverHeight();
151
104
  });
152
105
  useEffect(() => {
153
- /**
154
- * Reset the computed values when the Menu is closed, we'll recompute them when it's reopened
155
- */
156
106
  if (!open) {
157
107
  waitForListHeightComputeCallsCount.current = 0;
158
108
  computedListHeight.current = -1;
159
109
  availableHeight.current = -1;
160
110
  }
161
- /**
162
- * Hide the menu immediately after being triggered to open to allow time to compute the rendered
163
- * list height. We need to do this to avoid a potential flicker when the menu first renders, as
164
- * the virtualized list may not have rendered yet, but its adorning components may have. This may
165
- * cause the menu to have a different initial placement than it would have after the list has
166
- * rendered, for example if the user has chosen a preferred `top` placement but the menu cannot
167
- * actually fit within the space above the anchor element.
168
- *
169
- * This is only necessary for the initial render. After the menu has found its correct placement,
170
- * we can rely on the Popover to auto-update its placement and size as needed in response to list
171
- * height changes/window resize events.
172
- *
173
- * In the `handleTotalListHeightChanged` callback we then reset this flag once we know the total list
174
- * has actually rendered, so the first place the user sees the menu is in its correct position after
175
- * all other height computation has been performed.
176
- */
177
111
  if (open) {
178
112
  setWaitForHeightCompute(true);
179
113
  setDeferPendingVirtualizedListActions(true);
180
- // Reset the max height of the popper paper content to ensure the list is not constrained
181
114
  if (popperPaperContentRef.current) {
182
115
  popperPaperContentRef.current.style.maxHeight = 'none';
183
116
  }
@@ -185,17 +118,8 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
185
118
  }, [open]);
186
119
  return (jsx(Popover, { bordered: true, PopperProps: { ...PopperProps, padding: popperPadding }, ...rest, open: open, className: clsx(cls('root'), waitForHeightCompute && 'opacity-0'), PopperPaperProps: {
187
120
  contentRef: popperPaperContentRef,
188
- }, onClose: onClose, onAvailableHeightChange: handleAvailableHeightChange, ref: ref,
189
- // disable animation. The Grow animation will cause vl height calculate incorrectly
190
- motionProps: { variants: undefined }, actions: handlePopperActions, children: jsx("div", { className: cls('paper'), children: jsx("div", { ref: paperContentContainerRef, className: cls('paper-content-container'), children: jsx(VirtualizedMenuContext.Provider, { value: menuContextValue, children: jsx(VirtualizedMenuList, { data: data, totalCount: totalCount, isReady: !deferPendingVirtualizedListActions, isItemDisabled: isItemDisabled, initialFocus: initialFocus, style: { height: menuHeight }, ...combineProps(VirtualizedListProps, {
121
+ }, onClose: onClose, onAvailableHeightChange: handleAvailableHeightChange, ref: ref, motionProps: { variants: undefined }, actions: handlePopperActions, children: jsx("div", { className: cls('paper'), children: jsx("div", { ref: paperContentContainerRef, className: cls('paper-content-container'), children: jsx(VirtualizedMenuContext.Provider, { value: menuContextValue, children: jsx(VirtualizedMenuList, { data: data, totalCount: totalCount, isReady: !deferPendingVirtualizedListActions, isItemDisabled: isItemDisabled, initialFocus: initialFocus, style: { height: menuHeight }, ...combineProps(VirtualizedListProps, {
191
122
  components: {
192
- /**
193
- * Use Header/Footer components to add padding to the top and bottom of the
194
- * list. We need to do this as Virtuoso doesn't support padding on the list
195
- * due to how it uses padding/margin to simulate scrolling. Passing a List
196
- * wrapper component also doesn't work as the list height computation
197
- * doesn't account for the padding around the wrapper element.
198
- */
199
123
  Header: VirtualizedListVerticalPadding,
200
124
  Footer: VirtualizedListVerticalPadding,
201
125
  },
package/chunks/getFade.js CHANGED
@@ -10,12 +10,14 @@ const getFade = ({ toggle = true, prefersReducedMotion = false, toggleOnTransiti
10
10
  transition: prefersReducedMotion
11
11
  ? motionTransition.instant
12
12
  : toggleOnTransition || motionTransition['haste-01-fast'],
13
+ pointerEvents: 'auto',
13
14
  },
14
15
  hidden: {
15
16
  opacity: 0,
16
17
  transition: prefersReducedMotion
17
18
  ? motionTransition.instant
18
19
  : toggleOffTransition || motionTransition['haste-01-slow'],
20
+ pointerEvents: 'none',
19
21
  },
20
22
  },
21
23
  });
@@ -2,19 +2,12 @@ import { useEffect } from 'react';
2
2
  import { useWindowContext, useId, useEventCallback } from '@ringcentral/spring-base';
3
3
 
4
4
  const FOCUS_TRAP_STACK = '__SUI_FOCUS_TRAP_STACK__';
5
- // use global to ensure the work in the same window
6
5
  const getFocusTrapStack = (global) => {
7
- // @ts-ignore
8
6
  if (!global[FOCUS_TRAP_STACK]) {
9
- // @ts-ignore
10
7
  global[FOCUS_TRAP_STACK] = [];
11
8
  }
12
- // @ts-ignore
13
9
  return global[FOCUS_TRAP_STACK];
14
10
  };
15
- /**
16
- * Hook to manage focus trap stack for proper layering and focus management
17
- */
18
11
  const useFocusTrapStack = ({ open, }) => {
19
12
  const { globalThis } = useWindowContext();
20
13
  const id = useId();
@@ -30,7 +23,6 @@ const useFocusTrapStack = ({ open, }) => {
30
23
  stack.splice(index, 1);
31
24
  }
32
25
  };
33
- // eslint-disable-next-line react-hooks/exhaustive-deps
34
26
  }, [open]);
35
27
  const isEnabled = useEventCallback(() => {
36
28
  const stack = getFocusTrapStack(globalThis);
@@ -12,34 +12,10 @@ const FormField = forwardRef((inProps, ref) => {
12
12
  });
13
13
  FormField.displayName = 'SuiFormField';
14
14
 
15
- /**
16
- * Hook to generate consistent helper text IDs for form field accessibility
17
- *
18
- * Follows the pattern: `${id}-helper-text`
19
- *
20
- * @param options Configuration options
21
- * @returns Generated helper text ID or undefined if no helper text
22
- *
23
- * @example
24
- * ```tsx
25
- * const Select = ({ helperText, FormHelperTextProps, ...props }) => {
26
- * const id = useId(idProp);
27
- * const helperTextId = useHelperTextId({ helperText, id, FormHelperTextProps });
28
- *
29
- * return (
30
- * <FormField FormHelperTextProps={{ id: helperTextId, ...FormHelperTextProps }}>
31
- * <button aria-describedby={helperTextId}>
32
- * </FormField>
33
- * );
34
- * };
35
- * ```
36
- */
37
15
  const useHelperTextId = ({ helperText, id, FormHelperTextProps, }) => {
38
- // Only generate ID if helper text exists
39
16
  if (!helperText) {
40
17
  return undefined;
41
18
  }
42
- // Use existing ID if provided, otherwise generate standard pattern
43
19
  return FormHelperTextProps?.id || `${id}-helper-text`;
44
20
  };
45
21
 
@@ -14,26 +14,19 @@ const useListNavigation = (props) => {
14
14
  const skipEnforceFocusRef = useRef(false);
15
15
  const focusList = () => {
16
16
  skipEnforceFocusRef.current = true;
17
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
18
17
  listRef.current.focus();
19
18
  };
20
19
  const focusTabBoundary = () => {
21
20
  skipEnforceFocusRef.current = true;
22
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
23
21
  sentinelTabBoundaryRef.current.focus();
24
22
  };
25
23
  const itemActions = useMemo(() => ({
26
- // Prevent cache invalidation
27
- // eslint-disable-next-line sonarjs/no-identical-functions
28
24
  onTab: () => {
29
25
  skipEnforceFocusRef.current = true;
30
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
31
26
  sentinelTabBoundaryRef.current.focus();
32
27
  },
33
- // eslint-disable-next-line sonarjs/no-identical-functions
34
28
  onShiftTab: () => {
35
29
  skipEnforceFocusRef.current = true;
36
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
37
30
  listRef.current.focus();
38
31
  },
39
32
  updateFocusedItem: (el) => {
@@ -48,9 +41,7 @@ const useListNavigation = (props) => {
48
41
  });
49
42
  useImperativeHandle(actions, () => ({
50
43
  focus: focusFirstItem,
51
- }),
52
- // eslint-disable-next-line react-hooks/exhaustive-deps
53
- []);
44
+ }), []);
54
45
  const sentinelFocusHandler = (e) => {
55
46
  if (e.target === e.currentTarget) {
56
47
  const skipEnforceFocus = skipEnforceFocusRef.current;
@@ -63,9 +54,7 @@ const useListNavigation = (props) => {
63
54
  };
64
55
  const searchAndFocusItemHandler = (searchElementOption) => {
65
56
  return searchAndFocusItemHandlerProp(searchElementOption, {
66
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
67
57
  sentinelEnd: sentinelArrowNavRef.current,
68
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
69
58
  list: listRef.current,
70
59
  });
71
60
  };
@@ -96,7 +85,6 @@ const useListNavigation = (props) => {
96
85
  }
97
86
  }
98
87
  else {
99
- // cover case about all item are disabled
100
88
  if (e.target === e.currentTarget && key === 'Tab' && !e.shiftKey) {
101
89
  focusTabBoundary();
102
90
  }
@@ -125,10 +113,7 @@ const useListNavigation = (props) => {
125
113
  return (jsxs(ListNavigationContext.Provider, { value: itemActions, children: [items, jsx("div", { "aria-hidden": "true", ref: sentinelArrowNavRef })] }));
126
114
  };
127
115
  const renderTabBoundary = () => {
128
- return (jsx("div", {
129
- // make sentinel end always render in scroller viewport center to
130
- // prevent scroll when focus to sentinel end
131
- style: { position: 'fixed', bottom: '50%' }, ref: sentinelTabBoundaryRef, tabIndex: 0, onFocus: sentinelFocusHandler }));
116
+ return (jsx("div", { style: { position: 'fixed', bottom: '50%' }, ref: sentinelTabBoundaryRef, tabIndex: 0, onFocus: sentinelFocusHandler }));
132
117
  };
133
118
  return { renderItems, renderTabBoundary, getListProps, listRef };
134
119
  };
@@ -1,8 +1,8 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { createContext, useContext, forwardRef, useRef, useEffect, useCallback, useMemo } from 'react';
3
- import { useFocusVisible, useActive, useForkRef, useClasses, useId, useHover, useEventCallback } from '@ringcentral/spring-base';
3
+ import { useFocusVisible, useActive, useForkRef, useId, useHover, useEventCallback } from '@ringcentral/spring-base';
4
4
  import { CheckBoldMd } from '@ringcentral/spring-icon';
5
- import { useThemeProps } from '@ringcentral/spring-theme';
5
+ import { useThemeProps, useClassesWithMode } from '@ringcentral/spring-theme';
6
6
  import { I as Icon } from './Icon.js';
7
7
  import './useNestedParentMenu.js';
8
8
  import { u as useNestedMenuItem } from './useNestedMenuItem.js';
@@ -15,9 +15,7 @@ const MenuListContext = createContext({
15
15
  });
16
16
  const useMenuListContext = () => useContext(MenuListContext);
17
17
 
18
- const MenuItemSelectContext =
19
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
- createContext(null);
18
+ const MenuItemSelectContext = createContext(null);
21
19
  const useMenuItemSelectContext = () => useContext(MenuItemSelectContext);
22
20
 
23
21
  const useMenuItem = (props) => {
@@ -47,7 +45,6 @@ const useMenuItem = (props) => {
47
45
  },
48
46
  });
49
47
  const handleItemRef = useForkRef(ref, focusVisibleRef, itemRef);
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
48
  const getRootProps = (otherProps) => {
52
49
  return {
53
50
  role: 'menuitem',
@@ -66,9 +63,7 @@ const useVirtualizedMenuItem = (props) => {
66
63
  const { onBlur, onFocus, onKeyDown, onKeyUp, onClick, autoClose, ref } = props;
67
64
  const { keyboardNavHandlers, itemRef } = useItemNavigation() ?? {};
68
65
  const vlItemProps = useVirtualizedItemPropsContext();
69
- const { focusVisible, focusVisibleRef, focusVisibleHandlers } =
70
- // eslint-disable-next-line react-hooks/rules-of-hooks
71
- useFocusVisible({
66
+ const { focusVisible, focusVisibleRef, focusVisibleHandlers } = useFocusVisible({
72
67
  onFocus: (e) => {
73
68
  keyboardNavHandlers?.onFocus(e);
74
69
  onFocus?.(e);
@@ -92,7 +87,6 @@ const useVirtualizedMenuItem = (props) => {
92
87
  },
93
88
  });
94
89
  const handleItemRef = useForkRef(ref, focusVisibleRef, itemRef);
95
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
96
90
  const getRootProps = (otherProps) => {
97
91
  return {
98
92
  role: 'menuitem',
@@ -110,7 +104,7 @@ const useVirtualizedMenuItem = (props) => {
110
104
  const MenuItem = forwardRef((inProps, ref) => {
111
105
  const props = useThemeProps({ props: inProps, name: 'MenuItem' });
112
106
  const { classes, className, children, selected, disabled, onMouseEnter, onMouseLeave, highlighted = false, focused = false, autoClose = true, autoFocus, ...rest } = props;
113
- const cls = useClasses('menu-item', classes, className);
107
+ const cls = useClassesWithMode('menu-item', classes, className);
114
108
  const vlItemProps = useVirtualizedItemPropsContext();
115
109
  const isInVlRef = useRef(Boolean(vlItemProps));
116
110
  const useMenuItemHook = (() => {
@@ -141,9 +135,7 @@ const MenuItem = forwardRef((inProps, ref) => {
141
135
  if (autoFocus) {
142
136
  itemRef.current?.focus();
143
137
  requestAnimationFrame(() => {
144
- if (
145
- // jsdom doesn't support scrollIntoView, this avoids users needing to bring their own mock
146
- process.env.NODE_ENV === 'test' &&
138
+ if (process.env.NODE_ENV === 'test' &&
147
139
  typeof itemRef.current?.scrollIntoView !== 'function') {
148
140
  return;
149
141
  }
@@ -153,7 +145,6 @@ const MenuItem = forwardRef((inProps, ref) => {
153
145
  });
154
146
  });
155
147
  }
156
- // eslint-disable-next-line react-hooks/exhaustive-deps
157
148
  }, []);
158
149
  return (jsx("div", { className: cls('root', {
159
150
  selectable,
@@ -194,9 +185,6 @@ const useMenuItemSelect = (props) => {
194
185
  autoFocus = value[0] === itemValue;
195
186
  }
196
187
  const menuItemRole = selectMode === 'single' ? 'menuitemradio' : 'menuitemcheckbox';
197
- // Use appropriate ARIA attributes based on widget type and role:
198
- // - For listbox/option pattern: use aria-selected
199
- // - For menu with selectable items: use menuitemradio/menuitemcheckbox with aria-checked
200
188
  const ariaProps = widget === 'menu'
201
189
  ? { 'aria-checked': selected }
202
190
  : { 'aria-selected': selected };
@@ -210,7 +198,6 @@ const useMenuItemSelect = (props) => {
210
198
  const handleSelect = useEventCallback((itemValue) => {
211
199
  let action = Action.SELECT;
212
200
  if (isSingleMode) {
213
- // User clicked on an already selected item
214
201
  if (value === itemValue) {
215
202
  onClose?.();
216
203
  }
@@ -1,10 +1,6 @@
1
1
  import { motionTransition } from '@ringcentral/spring-theme';
2
2
  import { u as usePrefersReducedMotion } from './usePrefersReducedMotion.js';
3
3
 
4
- /**
5
- * A hook that returns an instant transition if the user has reduced motion enabled.
6
- * If reduced motion is not enabled, returns the given motion transition.
7
- */
8
4
  const useMotionSafeTransition = (transition) => {
9
5
  const prefersReducedMotion = usePrefersReducedMotion();
10
6
  return prefersReducedMotion ? motionTransition.instant : transition;
@@ -34,12 +34,10 @@ const useNestedParentMenu = (props) => {
34
34
  }, []);
35
35
  useEffect(() => {
36
36
  const symbol = openedSubMenuSymbolRef.current;
37
- // symbol existed means subMenu exited
38
37
  if (symbol && hoveredItemId && hoveredItemId !== symbol.anchorId) {
39
38
  symbol.closeSubMenu();
40
39
  }
41
40
  }, [hoveredItemId]);
42
- // Ensure that all menus can be visually closed at the same time
43
41
  useLayoutEffect(() => {
44
42
  if (!open) {
45
43
  openedSubMenuSymbolRef.current?.closeSubMenu();
@@ -2,14 +2,9 @@ import { useGlobalListener } from '@ringcentral/spring-base';
2
2
  import { MotionConfigContext } from 'framer-motion';
3
3
  import { useState, useContext } from 'react';
4
4
 
5
- /**
6
- * in all component that uses this hook, we always use same instance of MediaQueryList
7
- * so we can cache the instance here
8
- */
9
5
  let instance;
10
6
  const getPrefersReducedMotionMediaQuery = () => {
11
7
  if (!instance ||
12
- // in test environment, we need to reassign the instance to the window object
13
8
  process.env.NODE_ENV === 'test') {
14
9
  instance = window?.matchMedia?.('(prefers-reduced-motion)');
15
10
  }
@@ -25,11 +20,6 @@ const usePrefersReducedMotion = () => {
25
20
  target: mediaQuery,
26
21
  customKey: 'prefers-reduced-motion',
27
22
  });
28
- /**
29
- * As we're determining the reduced motion state ourselves after the initial mount,
30
- * we need to check the MotionConfigContext to see how the prefers-reduced-motion
31
- * state should be handled.
32
- */
33
23
  if (context.reducedMotion === 'always') {
34
24
  return true;
35
25
  }
@@ -1,9 +1,5 @@
1
1
  import { useState, useRef, useCallback, useEffect } from 'react';
2
2
 
3
- /**
4
- * store tooltip open state when forceHide is true and set tooltip open to false
5
- * restore open state once forceHide changed to false
6
- */
7
3
  const useTooltipForceHide = (forceHide) => {
8
4
  const [open, setOpen] = useState(false);
9
5
  const openStateStore = useRef();
@@ -43,7 +43,6 @@ const useTray = (parameters) => {
43
43
  return;
44
44
  }
45
45
  if (handledValue === null) {
46
- // return to previous TrayPanel when value is false
47
46
  handledValue = stack[stack.length - 2];
48
47
  }
49
48
  containerRef.current?.focus();