@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.
- package/Alert.js +3 -3
- package/Announcement.js +1 -1
- package/Autocomplete.js +174 -236
- package/Avatar.js +27 -13
- package/BottomNavigation.js +0 -3
- package/Checkbox.js +0 -2
- package/DatePicker.js +5 -13
- package/DateUtilsContext.js +0 -1
- package/Dialer.js +2 -50
- package/Dialog.js +4 -4
- package/Dropdown.js +0 -7
- package/FocusTrap.js +0 -32
- package/InlineEditable.js +0 -4
- package/Link.js +1 -1
- package/ListItem.js +1 -1
- package/ListNavigation.js +0 -6
- package/Menu.js +1 -1
- package/MenuDivider.js +2 -2
- package/MenuFooter.js +4 -4
- package/MenuHeader.js +4 -4
- package/MenuItem.js +1 -1
- package/MenuItemText.js +1 -1
- package/PageHeader.js +6 -4
- package/Popper.js +0 -1
- package/Rating.js +0 -5
- package/SingleFilter.js +0 -1
- package/Slider.js +1 -4
- package/Snackbar.js +0 -14
- package/SortableListItem.js +0 -1
- package/SplitButton.js +1 -3
- package/StatusIndicator.js +1 -1
- package/SubMenu.js +0 -4
- package/SwipeableDrawer.js +8 -146
- package/Switch.js +1 -4
- package/Tab.js +7 -8
- package/TabContext.js +0 -5
- package/TableBody.js +1 -1
- package/TableCell.js +0 -6
- package/TableRow.js +0 -1
- package/Tabs.js +20 -118
- package/Text.js +2 -4
- package/TimePicker.js +7 -46
- package/VirtualizedList.js +2 -30
- package/chunks/Alert.js +8 -7
- package/chunks/BottomNavigationItem.js +0 -1
- package/chunks/Button.js +0 -1
- package/chunks/Chip.js +0 -5
- package/chunks/Drawer.js +1 -30
- package/chunks/ListItem.js +3 -6
- package/chunks/Menu.js +3 -3
- package/chunks/MenuDivider.js +2 -3
- package/chunks/MenuItemText.js +3 -3
- package/chunks/MenuList.js +0 -3
- package/chunks/PickerTextField.js +0 -1
- package/chunks/Popover.js +0 -5
- package/chunks/Select.js +2 -39
- package/chunks/Squircle.js +0 -7
- package/chunks/StatusIndicator.js +1 -0
- package/chunks/StepIcon.js +1 -3
- package/chunks/StepperContext.js +0 -7
- package/chunks/Tooltip.js +0 -36
- package/chunks/VirtualizedMenu.js +2 -78
- package/chunks/getFade.js +2 -0
- package/chunks/useFocusTrapStack.js +0 -8
- package/chunks/useHelperTextId.js +0 -24
- package/chunks/useListNavigation.js +2 -17
- package/chunks/useMenuItemSelect.js +6 -19
- package/chunks/useMotionSafeTransition.js +0 -4
- package/chunks/useNestedParentMenu.js +0 -2
- package/chunks/usePrefersReducedMotion.js +0 -10
- package/chunks/useTooltipForceHide.js +0 -4
- package/chunks/useTrayNavigate.js +0 -1
- package/index.css +392 -120
- package/index.js +1 -1
- package/package.json +4 -4
- package/src/Accordion/Accordion.type.d.ts +0 -21
- package/src/AccordionHeader/AccordionHeader.type.d.ts +0 -14
- package/src/Alert/Alert.type.d.ts +0 -32
- package/src/Animation/components/ExpandCollapseCaret/ExpandCollapseCaret.d.ts +0 -4
- package/src/Animation/components/ShowHideEye/ShowHideEye.d.ts +0 -4
- package/src/Animation/hooks/useMotionSafeTransition/useMotionSafeTransition.d.ts +0 -4
- package/src/Autocomplete/Autocomplete.d.ts +0 -5
- package/src/Autocomplete/Autocomplete.type.d.ts +0 -84
- package/src/Autocomplete/SuggestionList/SuggestionList.d.ts +5 -89
- package/src/Autocomplete/SuggestionList/SuggestionListItem.d.ts +0 -2
- package/src/Autocomplete/SuggestionList/utils/SuggestionListItem.d.ts +1 -13
- package/src/Autocomplete/SuggestionList/utils/useDynamicHeight.d.ts +0 -6
- package/src/Autocomplete/SuggestionList/utils/useHighlightScroll.d.ts +0 -20
- package/src/Autocomplete/SuggestionList/utils/useSuggestionList.d.ts +3 -41
- package/src/Autocomplete/SuggestionList/utils/useSuggestionListGroup.d.ts +0 -49
- package/src/Autocomplete/useAutocomplete.d.ts +1 -62
- package/src/Autocomplete/useAutocompleteTag.d.ts +0 -33
- package/src/Avatar/Avatar.type.d.ts +0 -58
- package/src/AvatarGroup/AvatarGroup.type.d.ts +0 -3
- package/src/Backdrop/Backdrop.type.d.ts +0 -4
- package/src/Badge/Badge.type.d.ts +0 -33
- package/src/Block/Block.type.d.ts +0 -24
- package/src/BlockHeader/BlockHeader.type.d.ts +0 -5
- package/src/BottomNavigation/BottomNavigation.type.d.ts +0 -34
- package/src/BottomNavigationItem/BottomNavigationItem.type.d.ts +0 -39
- package/src/Button/Button.type.d.ts +0 -30
- package/src/CallButton/CallButton.type.d.ts +0 -4
- package/src/Checkbox/Checkbox.type.d.ts +0 -17
- package/src/Chip/Chip.type.d.ts +0 -41
- package/src/CircularProgressIndicator/CircularProgressIndicator.type.d.ts +0 -19
- package/src/DatePicker/Calendar.d.ts +1 -20
- package/src/DatePicker/DatePicker.type.d.ts +0 -18
- package/src/DatePicker/DatePickerHeader.d.ts +1 -12
- package/src/DatePicker/Day.d.ts +0 -7
- package/src/DatePicker/Year.d.ts +0 -6
- package/src/DatePicker/Years.d.ts +0 -7
- package/src/Dialer/DialDelete/DialDelete.d.ts +0 -3
- package/src/Dialer/DialPad/DialPad.d.ts +0 -106
- package/src/Dialer/DialPad/utils/types.d.ts +0 -5
- package/src/Dialer/DialPad/utils/useDialKeyboard.d.ts +0 -5
- package/src/Dialer/DialPad/utils/useKeyAudio.d.ts +0 -9
- package/src/Dialer/DialPadButton/DialPadButton.d.ts +0 -14
- package/src/Dialer/DialTextField/DialTextField.d.ts +0 -23
- package/src/Dialer/DialTextField/utils/getDialPadValueOnlyRegex.d.ts +0 -3
- package/src/Dialer/DialTextField/utils/useFixedEndSelection.d.ts +0 -3
- package/src/Dialog/Dialog.type.d.ts +0 -11
- package/src/Divider/Divider.type.d.ts +0 -17
- package/src/DragHandle/DragHandle.type.d.ts +0 -4
- package/src/Drawer/Drawer.d.ts +0 -28
- package/src/Drawer/Drawer.type.d.ts +0 -84
- package/src/Dropdown/Dropdown.type.d.ts +0 -64
- package/src/EmptyState/EmptyState.type.d.ts +0 -12
- package/src/FabButton/FabButton.type.d.ts +0 -5
- package/src/FocusTrap/FocusTrap.d.ts +0 -38
- package/src/FocusTrap/useFocusTrapStack.d.ts +0 -9
- package/src/FormField/FormField.type.d.ts +0 -26
- package/src/FormField/useHelperTextId.d.ts +0 -25
- package/src/FormLabel/FormLabel.type.d.ts +0 -14
- package/src/Icon/Icon.type.d.ts +0 -15
- package/src/IconButton/IconButton.type.d.ts +0 -30
- package/src/IconButton/IconButtonLabel.type.d.ts +0 -3
- package/src/IconButtonGroup/IconButtonGroup.type.d.ts +0 -8
- package/src/InlineEditable/InlineEditable.type.d.ts +0 -75
- package/src/LinearProgressIndicator/LinearProgressIndicator.type.d.ts +0 -31
- package/src/Link/Link.classes.d.ts +1 -1
- package/src/Link/Link.type.d.ts +1 -10
- package/src/ListItem/ListItem.type.d.ts +0 -41
- package/src/ListItemText/ListItemText.type.d.ts +0 -21
- package/src/ListNavigation/useListNavigation.d.ts +0 -16
- package/src/Menu/Menu.type.d.ts +0 -10
- package/src/MenuFooter/MenuFooter.type.d.ts +0 -4
- package/src/MenuHeader/MenuHeader.type.d.ts +0 -10
- package/src/MenuItem/MenuItem.type.d.ts +0 -19
- package/src/MenuItem/MenuItemSelectContext.d.ts +0 -3
- package/src/MenuItem/useMenuItemSelect.d.ts +0 -21
- package/src/MenuItemText/MenuItemText.type.d.ts +0 -17
- package/src/MenuList/MenuList.type.d.ts +0 -4
- package/src/MenuList/MenuListContext.d.ts +0 -6
- package/src/Modal/InnerModal.type.d.ts +0 -5
- package/src/Modal/Modal.type.d.ts +0 -11
- package/src/Modal/ModalContext.d.ts +0 -4
- package/src/Option/Option.type.d.ts +0 -3
- package/src/Overlap/Overlap.type.d.ts +0 -11
- package/src/PageHeader/PageHeader.type.d.ts +0 -4
- package/src/PickerTextField/PickerTextField.type.d.ts +0 -10
- package/src/Popover/Popover.type.d.ts +0 -29
- package/src/Popper/Popper.type.d.ts +0 -61
- package/src/Radio/Radio.type.d.ts +0 -5
- package/src/RadioGroup/RadioGroup.type.d.ts +0 -4
- package/src/Rating/Rating.type.d.ts +0 -74
- package/src/Select/Select.type.d.ts +0 -84
- package/src/SingleFilter/FilterButton/FilterButton.type.d.ts +0 -16
- package/src/SingleFilter/SingleFilter.type.d.ts +0 -26
- package/src/Skeleton/Skeleton.type.d.ts +0 -1
- package/src/Skeleton/SkeletonContainer/SkeletonContainer.classes.d.ts +1 -1
- package/src/Slider/Slider.classes.d.ts +1 -5
- package/src/Slider/Slider.type.d.ts +0 -104
- package/src/Snackbar/Snackbar.type.d.ts +0 -18
- package/src/SplitButton/SplitButton.type.d.ts +0 -21
- package/src/Squircle/Squircle.type.d.ts +0 -3
- package/src/StatusIndicator/StatusIndicator.classes.d.ts +1 -1
- package/src/StatusIndicator/StatusIndicator.type.d.ts +1 -9
- package/src/Step/Step.type.d.ts +0 -29
- package/src/Step/StepContext.d.ts +0 -7
- package/src/StepButton/StepButton.type.d.ts +0 -15
- package/src/StepContent/StepContent.type.d.ts +0 -7
- package/src/StepIcon/StepIcon.type.d.ts +0 -26
- package/src/StepLabel/StepLabel.type.d.ts +0 -23
- package/src/Stepper/Stepper.type.d.ts +0 -27
- package/src/SwipeableDrawer/SwipeableDrawer.d.ts +0 -26
- package/src/SwipeableDrawer/SwipeableDrawer.types.d.ts +0 -67
- package/src/SwipeableDrawer/hooks/useDrawerGestures.d.ts +0 -110
- package/src/SwipeableDrawer/hooks/useElementSizeObserver.d.ts +0 -5
- package/src/SwipeableDrawer/hooks/useSnapPoints.d.ts +0 -60
- package/src/Switch/Switch.type.d.ts +0 -9
- package/src/Tab/Tab.classes.d.ts +3 -2
- package/src/Tab/Tab.type.d.ts +0 -29
- package/src/TabContext/TabContext.d.ts +0 -4
- package/src/TabContext/TabContext.type.d.ts +0 -7
- package/src/TabPanel/TabPanel.type.d.ts +0 -10
- package/src/Table/Table.type.d.ts +0 -19
- package/src/TableBody/TableBody.type.d.ts +0 -3
- package/src/TableCell/TableCell.d.ts +0 -4
- package/src/TableCell/TableCell.type.d.ts +0 -35
- package/src/TableContainer/TableContainer.type.d.ts +0 -7
- package/src/TableFooter/TableFooter.type.d.ts +0 -3
- package/src/TableHead/TableHead.type.d.ts +0 -6
- package/src/TablePagination/TablePagination.type.d.ts +0 -47
- package/src/TableRow/TableRow.type.d.ts +0 -17
- package/src/Tabs/TabMoreMenu.d.ts +0 -16
- package/src/Tabs/TabScrollButton.d.ts +0 -10
- package/src/Tabs/Tabs.classes.d.ts +1 -1
- package/src/Tabs/Tabs.d.ts +1 -1
- package/src/Tabs/Tabs.type.d.ts +2 -36
- package/src/Tabs/TabsContext.d.ts +2 -0
- package/src/Tabs/hooks/useTabsScroller.d.ts +0 -30
- package/src/Tag/Tag.type.d.ts +0 -12
- package/src/Text/Text.type.d.ts +0 -13
- package/src/TextField/TextField.type.d.ts +0 -64
- package/src/Textarea/Textarea.type.d.ts +0 -13
- package/src/TimePicker/NumberPicker.type.d.ts +0 -12
- package/src/TimePicker/SelectionView.d.ts +0 -6
- package/src/TimePicker/TimeButton.d.ts +0 -3
- package/src/TimePicker/TimePicker.type.d.ts +0 -11
- package/src/TimePicker/ToggleText.type.d.ts +0 -3
- package/src/TimePicker/utils/TimeBoundary.d.ts +0 -3
- package/src/TimePicker/utils/TimePickerHelper.d.ts +0 -19
- package/src/TimePicker/utils/getNumberPickerBoundary.d.ts +0 -3
- package/src/Tooltip/TooltipTrigger.a11y.d.ts +0 -9
- package/src/Tooltip/TooltipTrigger.type.d.ts +0 -44
- package/src/Tooltip/TooltipView.type.d.ts +0 -25
- package/src/Tooltip/useTooltipForceHide.d.ts +0 -4
- package/src/Tray/Tray.type.d.ts +0 -13
- package/src/Tray/TrayContext.d.ts +0 -4
- package/src/Tray/hooks/useTray.d.ts +0 -16
- package/src/Tray/hooks/useTrayPanel.d.ts +0 -3
- package/src/VirtualizedList/VirtualizedList.type.d.ts +0 -27
- package/src/VirtualizedMenu/VirtualizedMenu.type.d.ts +0 -17
package/chunks/Squircle.js
CHANGED
|
@@ -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);
|
package/chunks/StepIcon.js
CHANGED
|
@@ -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);
|
package/chunks/StepperContext.js
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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 =
|
|
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();
|