@ringcentral/spring-ui 1.7.0 → 1.9.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 +216 -243
- 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 +18 -6
- 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/Modal.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/StepButton.js +2 -3
- package/StepContent.js +3 -8
- package/SubMenu.js +0 -4
- package/SwipeableDrawer.js +37 -162
- 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 +2 -4
- package/Tabs.js +22 -121
- package/Text.js +2 -4
- package/TimePicker.js +7 -46
- package/VirtualizedList.js +6 -39
- package/VirtualizedMenu.js +1 -1
- 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 +6 -5
- package/chunks/MenuDivider.js +2 -3
- package/chunks/MenuItemText.js +3 -3
- package/chunks/MenuList.js +3 -6
- package/chunks/Modal.js +1 -1
- 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 +45 -99
- package/chunks/getFade.js +4 -0
- package/chunks/useFocusTrapStack.js +0 -8
- package/chunks/useHelperTextId.js +0 -24
- package/chunks/useListNavigation.js +5 -17
- package/chunks/useMenuItemSelect.js +9 -22
- package/chunks/useMotionSafeTransition.js +0 -4
- package/chunks/useNestedParentMenu.js +5 -18
- package/chunks/usePrefersReducedMotion.js +6 -12
- package/chunks/useTooltipForceHide.js +0 -4
- package/chunks/useTrayNavigate.js +0 -1
- package/index.css +434 -135
- package/index.js +1 -1
- package/package.json +4 -4
- package/src/Accordion/Accordion.d.ts +1 -1
- package/src/Accordion/Accordion.type.d.ts +0 -21
- package/src/AccordionHeader/AccordionHeader.d.ts +1 -1
- package/src/AccordionHeader/AccordionHeader.type.d.ts +0 -14
- package/src/Alert/Alert.d.ts +1 -1
- 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 +4 -23
- 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 +5 -66
- package/src/Autocomplete/useAutocompleteTag.d.ts +3 -36
- 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.d.ts +1 -1
- 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 +1 -8
- package/src/DatePicker/Year.d.ts +1 -7
- 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.classes.d.ts +1 -1
- package/src/Dialog/Dialog.d.ts +1 -1
- package/src/Dialog/Dialog.type.d.ts +3 -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 +1 -29
- package/src/Drawer/Drawer.type.d.ts +0 -84
- package/src/Dropdown/Dropdown.type.d.ts +0 -64
- package/src/EmptyState/EmptyState.d.ts +1 -1
- 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.d.ts +1 -1
- package/src/FormLabel/FormLabel.type.d.ts +0 -14
- package/src/Icon/Icon.d.ts +1 -1
- package/src/Icon/Icon.type.d.ts +0 -15
- package/src/IconButton/IconButton.type.d.ts +0 -30
- package/src/IconButton/IconButtonLabel.d.ts +1 -1
- 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.d.ts +1 -1
- package/src/ListItemText/ListItemText.type.d.ts +0 -21
- package/src/ListNavigation/useListNavigation.d.ts +0 -16
- package/src/Menu/Menu.d.ts +1 -1
- 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.d.ts +1 -1
- 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.d.ts +1 -1
- 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.d.ts +2 -2
- package/src/Modal/InnerModal.type.d.ts +0 -5
- package/src/Modal/Modal.classes.d.ts +1 -1
- package/src/Modal/Modal.d.ts +3 -3
- package/src/Modal/Modal.type.d.ts +0 -11
- package/src/Modal/ModalContext.d.ts +0 -4
- package/src/NestedMenuContext/NestedMenuContext.d.ts +1 -1
- package/src/Option/Option.d.ts +1 -1
- 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.d.ts +1 -1
- package/src/Popover/Popover.type.d.ts +0 -29
- package/src/Popper/Popper.d.ts +1 -1
- package/src/Popper/Popper.type.d.ts +0 -61
- package/src/PopperPaper/PopperPaper.d.ts +1 -1
- package/src/Radio/Radio.type.d.ts +0 -5
- package/src/RadioGroup/RadioGroup.d.ts +1 -1
- package/src/RadioGroup/RadioGroup.type.d.ts +0 -4
- package/src/Rating/Rating.d.ts +1 -1
- package/src/Rating/Rating.type.d.ts +0 -74
- package/src/Rating/RatingItem.d.ts +1 -1
- package/src/Select/Select.type.d.ts +0 -84
- package/src/SingleFilter/FilterButton/FilterButton.d.ts +1 -1
- package/src/SingleFilter/FilterButton/FilterButton.type.d.ts +0 -16
- package/src/SingleFilter/SingleFilter.d.ts +1 -1
- 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.d.ts +2 -2
- package/src/Slider/Slider.type.d.ts +0 -104
- package/src/Snackbar/Snackbar.type.d.ts +0 -18
- package/src/SortableListItem/SortableListItemOverlay.d.ts +1 -1
- package/src/SplitButton/SplitButton.d.ts +1 -1
- 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.d.ts +1 -1
- package/src/StepContent/StepContent.type.d.ts +0 -7
- package/src/StepIcon/StepIcon.type.d.ts +0 -26
- package/src/StepLabel/StepLabel.d.ts +1 -1
- package/src/StepLabel/StepLabel.type.d.ts +0 -23
- package/src/Stepper/Stepper.type.d.ts +0 -27
- package/src/SubMenu/PopperMenu.d.ts +1 -1
- package/src/SubMenu/SubMenu.d.ts +1 -1
- package/src/SwipeableDrawer/SwipeableDrawer.d.ts +1 -27
- 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.d.ts +1 -1
- 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.d.ts +1 -1
- 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 +1 -11
- 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.d.ts +1 -1
- 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.d.ts +1 -1
- package/src/Textarea/Textarea.type.d.ts +0 -13
- package/src/TimePicker/NumberPicker.d.ts +1 -1
- package/src/TimePicker/NumberPicker.type.d.ts +0 -12
- package/src/TimePicker/SelectionView.d.ts +0 -6
- package/src/TimePicker/TimeButton.d.ts +1 -4
- package/src/TimePicker/TimePicker.type.d.ts +0 -11
- package/src/TimePicker/ToggleText.d.ts +1 -1
- 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.d.ts +1 -1
- 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/TrayMenu/TrayMenu.d.ts +1 -1
- package/src/TrayMenu/TrayMenuItem.d.ts +1 -1
- package/src/VirtualizedList/VirtualizedList.type.d.ts +0 -27
- package/src/VirtualizedMenu/VirtualizedMenu.classes.d.ts +1 -1
- package/src/VirtualizedMenu/VirtualizedMenu.type.d.ts +0 -17
package/Autocomplete.js
CHANGED
|
@@ -2,12 +2,12 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import { forwardRef, useRef, useMemo, useCallback, useEffect, useLayoutEffect, useState, useImperativeHandle } from 'react';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { Virtuoso } from 'react-virtuoso';
|
|
5
|
-
import {
|
|
6
|
-
import { useThemeProps } from '@ringcentral/spring-theme';
|
|
5
|
+
import { usePrevious, useRefState, useEventCallback, logInDev, combineProps, useDepsChange, useControlled, useId, useForceUpdate, useKeyboardMoveFocus, useForkRef, useVirtuosoExternalWindow, useSleep, useRetry, useChange, getSelectionPosition, useClasses } from '@ringcentral/spring-base';
|
|
6
|
+
import { useClassesWithMode, useThemeProps } from '@ringcentral/spring-theme';
|
|
7
7
|
import { E as ExpandCollapseCaret } from './chunks/ExpandCollapseCaret.js';
|
|
8
|
-
import { I as IconButton } from './chunks/IconButtonLabel.js';
|
|
9
8
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
10
9
|
import './chunks/Icon.js';
|
|
10
|
+
import { I as IconButton } from './chunks/IconButtonLabel.js';
|
|
11
11
|
import { Xsm } from '@ringcentral/spring-icon';
|
|
12
12
|
import { u as usePrefersReducedMotion } from './chunks/usePrefersReducedMotion.js';
|
|
13
13
|
import { g as getFade } from './chunks/getFade.js';
|
|
@@ -28,11 +28,11 @@ import './chunks/Backdrop.js';
|
|
|
28
28
|
import './chunks/ShowHideEye.js';
|
|
29
29
|
import './chunks/useHelperTextId.js';
|
|
30
30
|
|
|
31
|
-
var SuggestionList$1 = {"sui-suggestion-list-root":"sui-suggestion-list-root","sui-suggestion-list-item":"sui-suggestion-list-item","sui-suggestion-list-readonly":"sui-suggestion-list-readonly","sui-disabled":"sui-disabled","sui-focus-visible":"sui-focus-visible","sui-suggestion-list-selected":"sui-suggestion-list-selected","sui-suggestion-list-highlighted":"sui-suggestion-list-highlighted","sui-suggestion-list-group-title-readonly":"sui-suggestion-list-group-title-readonly","sui-suggestion-list-expand-icon":"sui-suggestion-list-expand-icon"};
|
|
31
|
+
var SuggestionList$1 = {"sui-suggestion-list-root":"sui-suggestion-list-root","sui-suggestion-list-item":"sui-suggestion-list-item","sui-suggestion-list-readonly":"sui-suggestion-list-readonly","sui-disabled":"sui-disabled","sui-focus-visible":"sui-focus-visible","sui-suggestion-list-selected":"sui-suggestion-list-selected","sui-suggestion-list-highlighted":"sui-suggestion-list-highlighted","sui-suggestion-list-group-title-readonly":"sui-suggestion-list-group-title-readonly","sui-suggestion-list-expand-icon":"sui-suggestion-list-expand-icon","sui-compact":"sui-compact"};
|
|
32
32
|
|
|
33
33
|
const SuggestionListItem = forwardRef((props, ref) => {
|
|
34
|
-
const { id, label, children, disabled, isGroupTitle = false, isTitleMode = false, highlighted = false, selected = false, expanded = false,
|
|
35
|
-
const cls =
|
|
34
|
+
const { id, label, children, disabled, isGroupTitle = false, isTitleMode = false, highlighted = false, selected = false, expanded = false, expandIconProps, classes, className, role, ...rest } = props;
|
|
35
|
+
const cls = useClassesWithMode('suggestion-list', classes, className, ['item']);
|
|
36
36
|
const readonly = isGroupTitle && isTitleMode;
|
|
37
37
|
const ariaDisabled = role !== undefined && disabled ? true : undefined;
|
|
38
38
|
return (jsxs("div", { "aria-disabled": ariaDisabled, role: role, ...rest, id: id, className: cls('item', {
|
|
@@ -42,24 +42,23 @@ const SuggestionListItem = forwardRef((props, ref) => {
|
|
|
42
42
|
highlighted: highlighted,
|
|
43
43
|
selected: selected,
|
|
44
44
|
disabled,
|
|
45
|
-
}), children: [label || children,
|
|
45
|
+
}), children: [label || children, expandIconProps && (jsx(IconButton, { "aria-hidden": "true", role: "presentation", tabIndex: -1, component: "div", shape: 'squircle', background: false, color: 'secondary', variant: "contained", size: "small", ...expandIconProps, children: jsx(ExpandCollapseCaret, { expanded: expanded }) }))] }));
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const isFirstInGroup = (option) => {
|
|
49
|
+
return option === option?.group?.options[0];
|
|
50
|
+
};
|
|
51
|
+
|
|
51
52
|
const useDynamicHeight = ({ itemCount, maxContainerHeight, onContainerHeightChange, }) => {
|
|
52
53
|
const prevItemCount = usePrevious(() => itemCount);
|
|
53
54
|
const fullHeight = maxContainerHeight === '100%';
|
|
54
55
|
const maxContainerSize = fullHeight ? -1 : maxContainerHeight;
|
|
55
56
|
const containerHeighRef = useRef(maxContainerSize);
|
|
56
57
|
if (!fullHeight) {
|
|
57
|
-
// * when prev item count is zero, and current is not zero, set hight for vl to trigger totalListHeightChanged to re-change height
|
|
58
58
|
if (prevItemCount === 0 && itemCount !== 0) {
|
|
59
59
|
containerHeighRef.current = maxContainerSize;
|
|
60
60
|
}
|
|
61
61
|
else if (itemCount === 0) {
|
|
62
|
-
// * when item count is zero always height to zero, when zero init
|
|
63
62
|
containerHeighRef.current = 0;
|
|
64
63
|
}
|
|
65
64
|
}
|
|
@@ -87,46 +86,55 @@ const useDynamicHeight = ({ itemCount, maxContainerHeight, onContainerHeightChan
|
|
|
87
86
|
};
|
|
88
87
|
};
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
* scroll into current highlight index
|
|
92
|
-
*/
|
|
93
|
-
function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
|
|
89
|
+
function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToIndex, }) {
|
|
94
90
|
const renderedItemsRef = useRef([]);
|
|
95
91
|
const scrollerRef = useRef();
|
|
96
92
|
const scrollToHighlightedIndex = (prevHighlightedIndex, currHighlightedIndex, topHighlightIndex = 0) => {
|
|
93
|
+
const needsScrollReset = prevHighlightedIndex === -1;
|
|
97
94
|
if (!scrollerRef.current) {
|
|
98
95
|
if (currHighlightedIndex !== topHighlightIndex) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
align: 'start',
|
|
103
|
-
});
|
|
96
|
+
scrollToIndex({
|
|
97
|
+
index: currHighlightedIndex,
|
|
98
|
+
align: 'start',
|
|
104
99
|
});
|
|
105
100
|
}
|
|
106
101
|
return;
|
|
107
102
|
}
|
|
108
103
|
const scrollElm = scrollerRef.current;
|
|
104
|
+
if (!virtualize) {
|
|
105
|
+
if (needsScrollReset && currHighlightedIndex === topHighlightIndex) {
|
|
106
|
+
scrollElm.scrollTop = 0;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
scrollToIndex({
|
|
110
|
+
index: currHighlightedIndex,
|
|
111
|
+
align: 'start',
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
109
115
|
const renderedItems = renderedItemsRef.current;
|
|
110
|
-
const
|
|
111
|
-
|
|
116
|
+
const lastHighlightedIndex = needsScrollReset
|
|
117
|
+
? currHighlightedIndex
|
|
118
|
+
: prevHighlightedIndex;
|
|
119
|
+
const isBackward = lastHighlightedIndex > currHighlightedIndex;
|
|
120
|
+
const isForward = lastHighlightedIndex < currHighlightedIndex;
|
|
112
121
|
const currentVisibleOffset = {
|
|
113
122
|
start: scrollElm.scrollTop,
|
|
114
123
|
stop: scrollElm.scrollTop + containerHeighRef.current,
|
|
115
124
|
};
|
|
116
|
-
|
|
117
|
-
if (!
|
|
125
|
+
const isInRenderedRange = renderedItems.some((x) => x.index === currHighlightedIndex);
|
|
126
|
+
if (!isInRenderedRange) {
|
|
118
127
|
scrollToIndex({
|
|
119
128
|
index: currHighlightedIndex,
|
|
120
129
|
align: isBackward ? 'start' : 'end',
|
|
121
130
|
});
|
|
122
131
|
return;
|
|
123
132
|
}
|
|
124
|
-
// backward
|
|
125
133
|
if (isBackward) {
|
|
126
134
|
let firstShouldScrollIndex = currHighlightedIndex;
|
|
127
135
|
for (let i = renderedItems.length - 1; i >= 0; i--) {
|
|
128
136
|
const item = renderedItems[i];
|
|
129
|
-
if (item.offset
|
|
137
|
+
if (item.offset < currentVisibleOffset.start) {
|
|
130
138
|
break;
|
|
131
139
|
}
|
|
132
140
|
firstShouldScrollIndex = item.index;
|
|
@@ -137,18 +145,18 @@ function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
|
|
|
137
145
|
align: 'start',
|
|
138
146
|
});
|
|
139
147
|
}
|
|
140
|
-
// forward
|
|
141
148
|
}
|
|
142
149
|
else if (isForward) {
|
|
143
150
|
let lastShouldScrollIndex = currHighlightedIndex;
|
|
144
151
|
for (let i = 0; i < renderedItems.length; i++) {
|
|
145
152
|
const item = renderedItems[i];
|
|
146
|
-
|
|
153
|
+
const itemBottom = item.offset + item.size;
|
|
154
|
+
if (itemBottom >= currentVisibleOffset.stop) {
|
|
147
155
|
break;
|
|
148
156
|
}
|
|
149
157
|
lastShouldScrollIndex = item.index;
|
|
150
158
|
}
|
|
151
|
-
if (currHighlightedIndex
|
|
159
|
+
if (currHighlightedIndex > lastShouldScrollIndex) {
|
|
152
160
|
scrollToIndex({
|
|
153
161
|
index: currHighlightedIndex,
|
|
154
162
|
align: 'end',
|
|
@@ -156,31 +164,45 @@ function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
|
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
166
|
else {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
const currentItem = renderedItems.find((item) => item.index === currHighlightedIndex);
|
|
168
|
+
if (needsScrollReset) {
|
|
169
|
+
if (currHighlightedIndex === topHighlightIndex) {
|
|
170
|
+
if (scrollerRef.current) {
|
|
171
|
+
scrollerRef.current.scrollTop = 0;
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
scrollToIndex({
|
|
177
|
+
index: currHighlightedIndex,
|
|
178
|
+
align: 'start',
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!currentItem) {
|
|
184
|
+
scrollToIndex({
|
|
185
|
+
index: currHighlightedIndex,
|
|
186
|
+
align: 'start',
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const itemBottom = currentItem.offset + currentItem.size;
|
|
191
|
+
const isTopVisible = currentItem.offset >= currentVisibleOffset.start;
|
|
192
|
+
const isBottomVisible = itemBottom < currentVisibleOffset.stop;
|
|
193
|
+
const isFullyVisible = isTopVisible && isBottomVisible;
|
|
194
|
+
if (!isFullyVisible) {
|
|
195
|
+
scrollToIndex({
|
|
196
|
+
index: currHighlightedIndex,
|
|
197
|
+
align: 'start',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
163
200
|
}
|
|
164
201
|
};
|
|
165
202
|
return {
|
|
166
|
-
/**
|
|
167
|
-
* scroller reference
|
|
168
|
-
*/
|
|
169
203
|
scrollerRef,
|
|
170
|
-
/**
|
|
171
|
-
* Scroll to target highlighted index
|
|
172
|
-
* @param prevHighlightedIndex previous highlightedIndex
|
|
173
|
-
* @param currHighlightedIndex current highlightedIndex
|
|
174
|
-
* @param topHighlightIndex top highlighIndex for check should scroll when init
|
|
175
|
-
*/
|
|
176
204
|
scrollToHighlightedIndex,
|
|
177
|
-
/**
|
|
178
|
-
* set to virtuoso `scrollerRef` prop, auto bind inner `scrollerRef`
|
|
179
|
-
*/
|
|
180
205
|
scrollerRefFn: (elm) => (scrollerRef.current = elm),
|
|
181
|
-
/**
|
|
182
|
-
* set to virtuoso `itemsRendered` prop, auto bind inner `renderedItemsRef`
|
|
183
|
-
*/
|
|
184
206
|
itemsRendered: (e) => (renderedItemsRef.current = e),
|
|
185
207
|
};
|
|
186
208
|
}
|
|
@@ -216,11 +238,9 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
|
|
|
216
238
|
e.stopPropagation();
|
|
217
239
|
});
|
|
218
240
|
const optionsGroupList = useMemo(() => {
|
|
219
|
-
/** key with this group count */
|
|
220
241
|
if (!groupBy || !options) {
|
|
221
242
|
return [];
|
|
222
243
|
}
|
|
223
|
-
// used to keep track of key and indexes in the result array
|
|
224
244
|
const indexBy = new Map();
|
|
225
245
|
let warn = false;
|
|
226
246
|
const afterGroupFn = [];
|
|
@@ -274,11 +294,8 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
|
|
|
274
294
|
id: `${id}-${group}`,
|
|
275
295
|
label: group,
|
|
276
296
|
disabled: true,
|
|
277
|
-
// * here cause by self Type reference
|
|
278
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
279
297
|
group: newGroup,
|
|
280
298
|
};
|
|
281
|
-
// push fn to array, do that after all options be add into group
|
|
282
299
|
if (getOptionDisabled) {
|
|
283
300
|
afterGroupFn.push(() => {
|
|
284
301
|
groupTitleOption.disabled = getOptionDisabled(groupTitleOption);
|
|
@@ -319,7 +336,6 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
|
|
|
319
336
|
initGroupExpandedState(groupExpanded);
|
|
320
337
|
}, [groupExpanded]);
|
|
321
338
|
const groupExpandedMap = groupExpandedMapRef.current;
|
|
322
|
-
// * speared logic with group, prevent calculate every time.
|
|
323
339
|
const groupedResult = useMemo(() => {
|
|
324
340
|
const addExpandedResult = Object.entries(groupExpandedMap).reduce((prev, [key, expended]) => {
|
|
325
341
|
const group = prev.find((x) => x.group === key);
|
|
@@ -351,10 +367,8 @@ const isItemCanSelected = (item) => {
|
|
|
351
367
|
};
|
|
352
368
|
const toOptionIdString = (id) => typeof id === 'number' ? String(id) : id;
|
|
353
369
|
const DEFAULT_GET_OPTION_LABEL = (item) => item?.label || '';
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
*/
|
|
357
|
-
const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DEFAULT_GET_OPTION_LABEL, filterOptions, disabledItemsHighlightable, options, onInputChange: onInputChangeProp, inputRef, onKeyDown: onKeyDownProp, onSelect, onClear, getOptionDisabled, groupBy, onGroupExpanded, groupVariant, groupExpanded, groupDefaultExpanded, getExpandIconProps, processFilteredResult, }) => {
|
|
370
|
+
const defaultIsOptionEqualToValue = (option, value) => option === value;
|
|
371
|
+
const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DEFAULT_GET_OPTION_LABEL, filterOptions, disabledItemsHighlightable, options, onInputChange: onInputChangeProp, inputRef, onKeyDown: onKeyDownProp, onSelect, onClear, getOptionDisabled, groupBy, onGroupExpanded, groupVariant, groupExpanded, groupDefaultExpanded, getExpandIconProps, processFilteredResult, isOptionEqualToValue = defaultIsOptionEqualToValue, }) => {
|
|
358
372
|
const isTitleMode = groupVariant === 'normal';
|
|
359
373
|
const [inputValue, setInputValue] = useControlled({
|
|
360
374
|
controlled: inputValueProp,
|
|
@@ -376,12 +390,12 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
376
390
|
inputValue,
|
|
377
391
|
getOptionLabel,
|
|
378
392
|
selectedItems: [],
|
|
393
|
+
isOptionEqualToValue,
|
|
379
394
|
});
|
|
380
395
|
}
|
|
381
396
|
return items;
|
|
382
|
-
}, [filterOptions, getOptionLabel, inputValue]);
|
|
397
|
+
}, [filterOptions, getOptionLabel, inputValue, isOptionEqualToValue]);
|
|
383
398
|
const filteredResult = useMemo(() => {
|
|
384
|
-
// * only when isOpen calculate the filtered result
|
|
385
399
|
let results = getFilteredItems(options);
|
|
386
400
|
results = processFilteredResult
|
|
387
401
|
? processFilteredResult(results, inputValue)
|
|
@@ -454,7 +468,7 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
454
468
|
});
|
|
455
469
|
const getItemProps = ({ item, index = 0, }) => {
|
|
456
470
|
const currGroup = item.group;
|
|
457
|
-
const
|
|
471
|
+
const isItemFirstInGroup = isFirstInGroup(item);
|
|
458
472
|
const itemCount = optionItems.length;
|
|
459
473
|
const groupOrder = currGroup?.order || 0;
|
|
460
474
|
const stringId = toOptionIdString(item.id);
|
|
@@ -469,8 +483,7 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
469
483
|
setHighlightedIndex(index, { reason: 'mouse', reRender: true });
|
|
470
484
|
}
|
|
471
485
|
},
|
|
472
|
-
|
|
473
|
-
...(isTitleMode && isGroupTitle
|
|
486
|
+
...(isTitleMode && isItemFirstInGroup
|
|
474
487
|
? {}
|
|
475
488
|
: {
|
|
476
489
|
role: 'option',
|
|
@@ -509,9 +522,8 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
509
522
|
return;
|
|
510
523
|
if (highlightedIndex !== DEFAULT_HIGHLIGHTED_INDEX$1) {
|
|
511
524
|
const currOption = optionItems[highlightedIndex];
|
|
512
|
-
const
|
|
513
|
-
if (
|
|
514
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
525
|
+
const isCurrOptionFirstInGroup = isFirstInGroup(currOption);
|
|
526
|
+
if (isCurrOptionFirstInGroup) {
|
|
515
527
|
const { onClick } = currOption.group.getExpandIconProps();
|
|
516
528
|
onClick?.(e);
|
|
517
529
|
}
|
|
@@ -520,7 +532,6 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
520
532
|
}
|
|
521
533
|
e.stopPropagation();
|
|
522
534
|
}
|
|
523
|
-
// always preventDefault for not inset all enter into textarea
|
|
524
535
|
e.preventDefault();
|
|
525
536
|
break;
|
|
526
537
|
}
|
|
@@ -597,30 +608,54 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
597
608
|
changeHighlightedIndexReasonRef,
|
|
598
609
|
changeHighlightedIndexReason: changeHighlightedIndexReasonRef.current,
|
|
599
610
|
optionsGroupList,
|
|
600
|
-
/** current unique id */
|
|
601
611
|
id: currUniqueId,
|
|
602
612
|
getNextFocusableOption,
|
|
603
613
|
handleF10KeyDown,
|
|
604
614
|
};
|
|
605
615
|
};
|
|
606
616
|
|
|
617
|
+
const DEFAULT_PADDING = 8;
|
|
618
|
+
const getPaddingValue = (padding, itemCount) => {
|
|
619
|
+
if (itemCount === 0) {
|
|
620
|
+
return 0;
|
|
621
|
+
}
|
|
622
|
+
if (padding === false) {
|
|
623
|
+
return 0;
|
|
624
|
+
}
|
|
625
|
+
if (padding === true) {
|
|
626
|
+
return DEFAULT_PADDING;
|
|
627
|
+
}
|
|
628
|
+
if (typeof padding === 'number') {
|
|
629
|
+
return Math.max(0, padding);
|
|
630
|
+
}
|
|
631
|
+
return DEFAULT_PADDING;
|
|
632
|
+
};
|
|
607
633
|
const List = forwardRef((props, ref) => {
|
|
608
|
-
const { className, context } = props;
|
|
634
|
+
const { className, context, style, ...restProps } = props;
|
|
609
635
|
const listRef = context?.listRef;
|
|
610
636
|
const rootClass = context?.rootClass;
|
|
637
|
+
const paddingValue = context?.paddingValue ?? 0;
|
|
611
638
|
const forkRef = useForkRef(ref, listRef);
|
|
612
|
-
return (jsx("div", { ref: forkRef, className: clsx(className, rootClass), "data-suggestion-list": true,
|
|
639
|
+
return (jsx("div", { ref: forkRef, ...restProps, className: clsx(className, rootClass), "data-suggestion-list": true, style: {
|
|
640
|
+
...style,
|
|
641
|
+
['--sui-suggestion-list-vertical-padding']: `${paddingValue}px`,
|
|
642
|
+
} }));
|
|
613
643
|
});
|
|
614
644
|
const SuggestionList = forwardRef((inProps, ref) => {
|
|
615
645
|
const props = useThemeProps({ props: inProps, name: 'SuggestionList' });
|
|
616
|
-
const { highlightedIndex, options, getItemProps, virtualize = true, renderOption, value: selectedOptions, inputValue, groupVariant, groupExpanded, renderGroup, optionsGroupList, getOptionDisabled, getOptionSelected, changeHighlightedIndexReason, isKeepHighlightedIndex, components: componentsProp, onContainerHeightChange, getOptionLabel, padding =
|
|
646
|
+
const { highlightedIndex, prevHighlightedIndex, options, getItemProps, virtualize = true, renderOption, value: selectedOptions, inputValue, groupVariant, groupExpanded, renderGroup, optionsGroupList, getOptionDisabled, getOptionSelected, changeHighlightedIndexReason, isKeepHighlightedIndex, components: componentsProp, onContainerHeightChange, getOptionLabel, padding = DEFAULT_PADDING, maxContainerHeight = '100%', className: classNameProp, classes: classesProp, getSuggestionListProps, ...rest } = props;
|
|
617
647
|
const vlRef = useRef(null);
|
|
618
648
|
const forkVlRef = useForkRef(ref, vlRef);
|
|
619
649
|
const isTitleMode = groupVariant === 'normal';
|
|
620
650
|
const listRef = useRef(null);
|
|
621
651
|
const itemData = options;
|
|
622
652
|
const itemCount = options.length;
|
|
623
|
-
const
|
|
653
|
+
const externalWindowProps = useVirtuosoExternalWindow();
|
|
654
|
+
const internalPrevHighlightedIndex = usePrevious(() => highlightedIndex, true);
|
|
655
|
+
const actualPrevHighlightedIndex = prevHighlightedIndex ?? internalPrevHighlightedIndex;
|
|
656
|
+
const cls = useClassesWithMode('suggestion-list', classesProp, classNameProp);
|
|
657
|
+
const paddingValue = getPaddingValue(padding, itemCount);
|
|
658
|
+
const hasItemsRenderedRef = useRef(false);
|
|
624
659
|
const { totalListHeightChanged, style, containerHeighRef } = useDynamicHeight({
|
|
625
660
|
itemCount,
|
|
626
661
|
maxContainerHeight,
|
|
@@ -632,43 +667,96 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
632
667
|
}
|
|
633
668
|
},
|
|
634
669
|
});
|
|
670
|
+
const handleTotalListHeightChanged = (height) => {
|
|
671
|
+
const adjustedHeight = paddingValue > 0 ? height + paddingValue * 2 : height;
|
|
672
|
+
if (!hasItemsRenderedRef.current &&
|
|
673
|
+
adjustedHeight < containerHeighRef.current) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
totalListHeightChanged(adjustedHeight);
|
|
677
|
+
};
|
|
635
678
|
const { sleep } = useSleep();
|
|
636
679
|
const { retry: scrollToIndexWithRetry } = useRetry(async (location) => {
|
|
637
680
|
const toIndex = location.index;
|
|
638
681
|
if (!virtualize) {
|
|
639
|
-
const
|
|
640
|
-
|
|
682
|
+
const scroller = listRef.current;
|
|
683
|
+
const needsScrollReset = prevHighlightedIndex === -1;
|
|
684
|
+
const isLastItem = itemCount > 1 && toIndex === itemCount - 1;
|
|
685
|
+
if (paddingValue > 0 && needsScrollReset && isLastItem && scroller) {
|
|
686
|
+
scroller.scrollTop = scroller.scrollHeight;
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
const toElm = scroller?.querySelector(`[data-item-index="${toIndex}"]`);
|
|
690
|
+
if (!toElm || !scroller) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
const scrollerRect = scroller.getBoundingClientRect();
|
|
694
|
+
const elementRect = toElm.getBoundingClientRect();
|
|
695
|
+
const scrollerTop = scrollerRect.top;
|
|
696
|
+
const scrollerBottom = scrollerRect.bottom;
|
|
697
|
+
const elementTop = elementRect.top;
|
|
698
|
+
const elementBottom = elementRect.bottom;
|
|
699
|
+
const isFullyVisible = elementTop >= scrollerTop && elementBottom <= scrollerBottom;
|
|
700
|
+
if (!isFullyVisible) {
|
|
701
|
+
let scrollOffset = 0;
|
|
702
|
+
if (elementTop < scrollerTop) {
|
|
703
|
+
scrollOffset = toElm.offsetTop - paddingValue;
|
|
704
|
+
}
|
|
705
|
+
else if (elementBottom > scrollerBottom) {
|
|
706
|
+
scrollOffset =
|
|
707
|
+
toElm.offsetTop -
|
|
708
|
+
scroller.clientHeight +
|
|
709
|
+
toElm.offsetHeight +
|
|
710
|
+
paddingValue;
|
|
711
|
+
}
|
|
712
|
+
scroller.scrollTo({
|
|
713
|
+
top: scrollOffset,
|
|
714
|
+
behavior: 'auto',
|
|
715
|
+
});
|
|
716
|
+
}
|
|
641
717
|
return true;
|
|
642
718
|
}
|
|
643
|
-
if (
|
|
719
|
+
if (paddingValue > 0 &&
|
|
720
|
+
(location.align === 'start' || location.align === 'end')) {
|
|
644
721
|
location = {
|
|
645
722
|
...location,
|
|
646
|
-
offset:
|
|
723
|
+
offset: paddingValue,
|
|
647
724
|
};
|
|
648
725
|
}
|
|
649
726
|
vlRef.current?.scrollToIndex(location);
|
|
650
727
|
await sleep(0);
|
|
651
|
-
// confirm that scrollInto view
|
|
652
728
|
const toElm = listRef.current?.querySelector(`[data-item-index="${toIndex}"]`);
|
|
653
729
|
return !!toElm;
|
|
654
730
|
}, {
|
|
655
731
|
retryTimes: 10,
|
|
656
732
|
intervalTime: 20,
|
|
657
733
|
});
|
|
658
|
-
const { scrollerRef, scrollerRefFn, itemsRendered, scrollToHighlightedIndex, } = useHighlightScroll({
|
|
734
|
+
const { scrollerRef, scrollerRefFn, itemsRendered: itemsRenderedFromHook, scrollToHighlightedIndex, } = useHighlightScroll({
|
|
735
|
+
virtualize,
|
|
659
736
|
containerHeighRef,
|
|
660
737
|
scrollToIndex: scrollToIndexWithRetry,
|
|
661
738
|
});
|
|
662
|
-
const
|
|
739
|
+
const handleItemsRendered = (items) => {
|
|
740
|
+
if (items.length > 0 || itemCount === 0) {
|
|
741
|
+
hasItemsRenderedRef.current = true;
|
|
742
|
+
}
|
|
743
|
+
itemsRenderedFromHook(items);
|
|
744
|
+
};
|
|
745
|
+
useEffect(() => {
|
|
746
|
+
if (itemCount > 0) {
|
|
747
|
+
hasItemsRenderedRef.current = false;
|
|
748
|
+
}
|
|
749
|
+
}, [itemCount]);
|
|
663
750
|
useLayoutEffect(() => {
|
|
664
|
-
|
|
751
|
+
const shouldScroll = (!virtualize || vlRef.current) &&
|
|
665
752
|
!isKeepHighlightedIndex &&
|
|
666
|
-
// * only scroll when reason is 'keyboard'
|
|
667
753
|
changeHighlightedIndexReason &&
|
|
668
|
-
changeHighlightedIndexReason !== 'mouse'
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
754
|
+
changeHighlightedIndexReason !== 'mouse';
|
|
755
|
+
const firstOption = options[0];
|
|
756
|
+
const isFirstItemGroupTitle = isTitleMode && isFirstInGroup(firstOption);
|
|
757
|
+
const topHighlightIndex = isFirstItemGroupTitle ? 1 : 0;
|
|
758
|
+
if (shouldScroll) {
|
|
759
|
+
scrollToHighlightedIndex(actualPrevHighlightedIndex, highlightedIndex, topHighlightIndex);
|
|
672
760
|
}
|
|
673
761
|
});
|
|
674
762
|
const handleScrolling = (scrolling) => {
|
|
@@ -678,12 +766,11 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
678
766
|
list.style.pointerEvents = scrolling ? 'none' : '';
|
|
679
767
|
};
|
|
680
768
|
const itemContent = (index, option) => {
|
|
681
|
-
const
|
|
682
|
-
const isGroupTitle = option === currGroup?.options[0];
|
|
769
|
+
const isOptionFirstInGroup = isFirstInGroup(option);
|
|
683
770
|
const isFixedGroupExpanded = typeof groupExpanded === 'boolean';
|
|
684
|
-
const expandable = !isTitleMode && !isFixedGroupExpanded &&
|
|
771
|
+
const expandable = !isTitleMode && !isFixedGroupExpanded && isOptionFirstInGroup;
|
|
685
772
|
const expanded = option.group?.expanded;
|
|
686
|
-
const expandIconProps =
|
|
773
|
+
const expandIconProps = expandable
|
|
687
774
|
? option.group?.getExpandIconProps?.({
|
|
688
775
|
className: cls('expand-icon'),
|
|
689
776
|
'aria-expanded': expanded,
|
|
@@ -704,7 +791,6 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
704
791
|
const selected = getOptionSelected?.(option);
|
|
705
792
|
const indexInOwnGroup = resultProps.indexInOwnGroup;
|
|
706
793
|
delete resultProps.indexInOwnGroup;
|
|
707
|
-
// * when item is not disabled, that should check outside is that disabled
|
|
708
794
|
if (!resultProps.freeSolo && !resultProps.disabled && getOptionDisabled) {
|
|
709
795
|
resultProps.disabled = getOptionDisabled(option);
|
|
710
796
|
}
|
|
@@ -714,7 +800,7 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
714
800
|
index,
|
|
715
801
|
indexInOwnGroup,
|
|
716
802
|
};
|
|
717
|
-
if (renderGroup &&
|
|
803
|
+
if (renderGroup && isOptionFirstInGroup) {
|
|
718
804
|
return renderGroup(resultProps, {
|
|
719
805
|
...state,
|
|
720
806
|
expanded,
|
|
@@ -725,40 +811,33 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
725
811
|
return renderOption(resultProps, state);
|
|
726
812
|
}
|
|
727
813
|
const { error, group, freeSolo, key, ...restProps } = resultProps;
|
|
728
|
-
return (
|
|
814
|
+
return (jsx(SuggestionListItem, { ...restProps, id: typeof resultProps.id === 'number'
|
|
729
815
|
? `${resultProps.id}`
|
|
730
|
-
: resultProps.id,
|
|
816
|
+
: resultProps.id, expanded: expanded, isGroupTitle: isOptionFirstInGroup, isTitleMode: isTitleMode, highlighted: highlighted, selected: selected, disabled: resultProps.disabled, label: getOptionLabel ? getOptionLabel(option) : option.label, expandIconProps: expandIconProps }, key));
|
|
731
817
|
};
|
|
732
|
-
const PaddingComponent = (() => {
|
|
733
|
-
const paddingValue = padding !== undefined && itemCount > 0
|
|
734
|
-
? typeof padding === 'number'
|
|
735
|
-
? padding
|
|
736
|
-
: 8
|
|
737
|
-
: 0;
|
|
738
|
-
return () => jsx("div", { style: { height: paddingValue } });
|
|
739
|
-
})();
|
|
740
818
|
const components = {
|
|
741
819
|
List: List,
|
|
742
|
-
Header: PaddingComponent,
|
|
743
|
-
Footer: PaddingComponent,
|
|
744
820
|
...componentsProp,
|
|
745
821
|
};
|
|
746
822
|
const rootClass = cls('root');
|
|
747
|
-
const context = { listRef, rootClass };
|
|
748
|
-
return virtualize ? (jsx(Virtuoso, { ref: forkVlRef, totalCount: itemCount, data: itemData, context: context, components: components, itemContent: itemContent, ...getSuggestionListProps(), ...combineProps({
|
|
823
|
+
const context = { listRef, rootClass, paddingValue };
|
|
824
|
+
return virtualize ? (jsx(Virtuoso, { ref: forkVlRef, totalCount: itemCount, data: itemData, context: context, components: components, itemContent: itemContent, ...externalWindowProps, ...getSuggestionListProps(), ...combineProps({
|
|
749
825
|
scrollerRef: (scrollElm) => {
|
|
750
826
|
scrollerRefFn(scrollElm);
|
|
751
827
|
},
|
|
752
|
-
itemsRendered,
|
|
753
|
-
totalListHeightChanged,
|
|
828
|
+
itemsRendered: handleItemsRendered,
|
|
829
|
+
totalListHeightChanged: handleTotalListHeightChanged,
|
|
754
830
|
style,
|
|
755
831
|
isScrolling: handleScrolling,
|
|
756
|
-
}, rest) })) : (
|
|
832
|
+
}, rest) })) : (jsx(List, { context: context, style: {
|
|
833
|
+
maxHeight: style.height,
|
|
834
|
+
overflow: 'auto',
|
|
835
|
+
}, ref: (scrollElm) => {
|
|
757
836
|
listRef.current = scrollElm;
|
|
758
837
|
scrollerRefFn(scrollElm);
|
|
759
|
-
}, className: cls('root'), ...getSuggestionListProps(), ...rest, children:
|
|
760
|
-
|
|
761
|
-
|
|
838
|
+
}, className: cls('root'), ...getSuggestionListProps(), ...rest, children: options.map((x, i) => {
|
|
839
|
+
return itemContent(i, x);
|
|
840
|
+
}) }));
|
|
762
841
|
});
|
|
763
842
|
SuggestionList.displayName = 'SuggestionList';
|
|
764
843
|
|
|
@@ -770,7 +849,6 @@ function stringArrToRegExp(keyToTags) {
|
|
|
770
849
|
const TAG_TOKEN = 'rc-chip-';
|
|
771
850
|
const DEFAULT_LIMIT_CHIPS$1 = 20;
|
|
772
851
|
const DEFAULT_KEY_TO_CHIPS = [';', ',', '\\n'];
|
|
773
|
-
/** @inner inner hook work with useAutocomplete */
|
|
774
852
|
const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [], onChange: onSelectChange, variant, getOptionLabel = DEFAULT_GET_OPTION_LABEL, onInputChange: onInputChangeProp, freeSolo, multiple, wrapperRef, inputRef, maxFreeSolo = DEFAULT_LIMIT_CHIPS$1, keyToTags = DEFAULT_KEY_TO_CHIPS, onMaxFreeSolo, required, disabled, getStopCreateFreeSolo, onReset, onAddTags, }) => {
|
|
775
853
|
const isAutocomplete = variant === 'autocomplete';
|
|
776
854
|
const isSelectedFromAutocompleteRef = useRef(false);
|
|
@@ -810,7 +888,6 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
810
888
|
if (focus)
|
|
811
889
|
_focusTag(tagToFocus);
|
|
812
890
|
});
|
|
813
|
-
/** when return `true` mean add item success */
|
|
814
891
|
const checkAndAddFreeSolo = ({ selectedItem, value, } = {}, e) => {
|
|
815
892
|
let isAddItem = false;
|
|
816
893
|
const currentValue = value ?? inputRef.current?.value;
|
|
@@ -869,7 +946,6 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
869
946
|
onAddTags?.();
|
|
870
947
|
};
|
|
871
948
|
const addTagOnSelect = (selectedItem) => {
|
|
872
|
-
// * change to uniqueId
|
|
873
949
|
if (selectedItem.freeSolo) {
|
|
874
950
|
if (freeSoloCount < maxFreeSolo) {
|
|
875
951
|
selectedItem.id = uniqueId(TAG_TOKEN);
|
|
@@ -895,32 +971,20 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
895
971
|
event?.preventDefault();
|
|
896
972
|
},
|
|
897
973
|
});
|
|
898
|
-
/**
|
|
899
|
-
* All Get props methods
|
|
900
|
-
*/
|
|
901
974
|
const getTagListBoxProps = (props) => {
|
|
902
975
|
return {
|
|
903
976
|
'aria-required': required || false,
|
|
904
977
|
'aria-disabled': disabled || false,
|
|
905
|
-
// TODO: wait for input can complete that.
|
|
906
|
-
// 'aria-multiselectable': false,
|
|
907
|
-
// role: selectedItems.length > 0 ? 'listbox' : undefined,
|
|
908
|
-
// 'aria-orientation': 'horizontal',
|
|
909
978
|
...props,
|
|
910
979
|
};
|
|
911
980
|
};
|
|
912
981
|
const getTagProps = ({ index, selectedItem, ...restTagProps }) => {
|
|
913
|
-
const tabIndex =
|
|
914
|
-
// * when tag is not focus
|
|
915
|
-
!focused &&
|
|
916
|
-
// * and is first item
|
|
982
|
+
const tabIndex = !focused &&
|
|
917
983
|
index === 0
|
|
918
984
|
? 0
|
|
919
985
|
: -1;
|
|
920
986
|
return combineProps({
|
|
921
987
|
tabIndex,
|
|
922
|
-
// TODO: wait for input can complete that.
|
|
923
|
-
// role: 'option',
|
|
924
988
|
role: 'button',
|
|
925
989
|
key: `${AutocompleteId}-selected-item-${index}`,
|
|
926
990
|
'data-tag-index': index,
|
|
@@ -939,9 +1003,7 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
939
1003
|
case 'Backspace':
|
|
940
1004
|
{
|
|
941
1005
|
const lengthBeforeDelete = tags.length;
|
|
942
|
-
focusTag(
|
|
943
|
-
// * when length is 1 mean that will be remove all
|
|
944
|
-
lengthBeforeDelete === 1
|
|
1006
|
+
focusTag(lengthBeforeDelete === 1
|
|
945
1007
|
? -1
|
|
946
1008
|
: focusedIndex === 0
|
|
947
1009
|
? 0
|
|
@@ -952,9 +1014,7 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
952
1014
|
case 'Delete':
|
|
953
1015
|
{
|
|
954
1016
|
const lengthBeforeDelete = tags.length;
|
|
955
|
-
focusTag(
|
|
956
|
-
// * when length is 1 mean that will be remove all
|
|
957
|
-
lengthBeforeDelete === 1
|
|
1017
|
+
focusTag(lengthBeforeDelete === 1
|
|
958
1018
|
? -1
|
|
959
1019
|
: focusedIndex === tags.length - 1
|
|
960
1020
|
? focusedIndex - 1
|
|
@@ -968,7 +1028,6 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
968
1028
|
}
|
|
969
1029
|
},
|
|
970
1030
|
onDelete: (e) => {
|
|
971
|
-
// * only allow left button click
|
|
972
1031
|
if (e.button !== 0)
|
|
973
1032
|
return;
|
|
974
1033
|
removeTag(selectedItem);
|
|
@@ -1009,7 +1068,7 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
1009
1068
|
const EMPTY_TAG_TOKEN = 'rc-chip-empty';
|
|
1010
1069
|
const DEFAULT_HIGHLIGHTED_INDEX = -1;
|
|
1011
1070
|
const DEFAULT_LIMIT_CHIPS = 20;
|
|
1012
|
-
const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputValueProp, getOptionLabel = DEFAULT_GET_OPTION_LABEL, keyToTags = DEFAULT_KEY_TO_CHIPS, filterOptions: filterOptionsProp, disabledItemsHighlightable, options, freeSolo, onInputChange: onInputChangeProp, maxFreeSolo = DEFAULT_LIMIT_CHIPS, onMaxFreeSolo, openOnFocus, toggleWithInput, disableCloseOnSelect, initialIsOpen, autoSelect, inputContainerRef, inputRef, wrapperRef, onKeyDown: onKeyDownProp, onChange: onSelectChange, value: selectedItemsProp = [], disabled, renderNoOptions, onClear, autoHighlight, getOptionDisabled, addNoOptionItem, groupBy, onGroupExpanded, groupVariant, groupExpanded, groupDefaultExpanded, getExpandIconProps, required, open: openProp, onOpen, onClose, focused, }) => {
|
|
1071
|
+
const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputValueProp, getOptionLabel = DEFAULT_GET_OPTION_LABEL, keyToTags = DEFAULT_KEY_TO_CHIPS, filterOptions: filterOptionsProp, disabledItemsHighlightable, options, freeSolo, onInputChange: onInputChangeProp, maxFreeSolo = DEFAULT_LIMIT_CHIPS, onMaxFreeSolo, openOnFocus, toggleWithInput, disableCloseOnSelect, initialIsOpen, autoSelect, inputContainerRef, inputRef, wrapperRef, onKeyDown: onKeyDownProp, onChange: onSelectChange, value: selectedItemsProp = [], disabled, renderNoOptions, onClear, autoHighlight, getOptionDisabled, addNoOptionItem, groupBy, onGroupExpanded, groupVariant, groupExpanded, groupDefaultExpanded, getExpandIconProps, required, open: openProp, onOpen, onClose, focused, isOptionEqualToValue, }) => {
|
|
1013
1072
|
const isAutocomplete = variant === 'autocomplete';
|
|
1014
1073
|
const autoCompleteSelectedIndexRef = useRef(DEFAULT_HIGHLIGHTED_INDEX);
|
|
1015
1074
|
const AutocompleteId = useId('Autocomplete', true);
|
|
@@ -1031,7 +1090,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1031
1090
|
const keepHighlightedIndexRef = useRef(false);
|
|
1032
1091
|
const popperElementRef = useRef(null);
|
|
1033
1092
|
const { sleep, getSleeping } = useSleep();
|
|
1034
|
-
// * when that is autocomplete, that will never be multiple
|
|
1035
1093
|
const multiple = isAutocomplete ? false : multipleProp;
|
|
1036
1094
|
const inputContainerScrollToBottom = () => {
|
|
1037
1095
|
requestAnimationFrame(() => {
|
|
@@ -1057,7 +1115,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1057
1115
|
multiple,
|
|
1058
1116
|
required,
|
|
1059
1117
|
disabled,
|
|
1060
|
-
// * when is composition, stop freeSolo create new item
|
|
1061
1118
|
getStopCreateFreeSolo: () => isCompositionRef.current,
|
|
1062
1119
|
onReset: () => {
|
|
1063
1120
|
resetState();
|
|
@@ -1076,7 +1133,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1076
1133
|
}
|
|
1077
1134
|
return null;
|
|
1078
1135
|
};
|
|
1079
|
-
// * only when isOpen calculate the filtered result
|
|
1080
1136
|
noOptionItemRef.current = null;
|
|
1081
1137
|
if (addNoOptionItem) {
|
|
1082
1138
|
const item = getInputValueAsItem();
|
|
@@ -1093,9 +1149,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1093
1149
|
else if (results.length === 0) {
|
|
1094
1150
|
const item = getInputValueAsItem();
|
|
1095
1151
|
noOptionItemRef.current = item;
|
|
1096
|
-
// * only when not set render no options will auto add item
|
|
1097
1152
|
if (item && freeSolo && !renderNoOptions) {
|
|
1098
|
-
// * when outside not use filter, that will be original array, must use spread
|
|
1099
1153
|
return [...results, item];
|
|
1100
1154
|
}
|
|
1101
1155
|
}
|
|
@@ -1130,6 +1184,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1130
1184
|
groupBy,
|
|
1131
1185
|
onGroupExpanded,
|
|
1132
1186
|
getExpandIconProps,
|
|
1187
|
+
isOptionEqualToValue,
|
|
1133
1188
|
onSelect: (e, selectedItem) => {
|
|
1134
1189
|
addTagOnSelect(selectedItem);
|
|
1135
1190
|
resetState(e);
|
|
@@ -1137,10 +1192,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1137
1192
|
if (autoHighlight) {
|
|
1138
1193
|
const currIndex = highlightedIndexRef.current;
|
|
1139
1194
|
const nextValidIndex = getNextFocusableOption();
|
|
1140
|
-
const toIndex =
|
|
1141
|
-
// * if that index is bigger than current index or nextValidIndex is last index,
|
|
1142
|
-
// * cut 1 (because current item will be remove)
|
|
1143
|
-
nextValidIndex > currIndex ||
|
|
1195
|
+
const toIndex = nextValidIndex > currIndex ||
|
|
1144
1196
|
nextValidIndex === optionItems.length - 1
|
|
1145
1197
|
? nextValidIndex - 1
|
|
1146
1198
|
: nextValidIndex;
|
|
@@ -1155,23 +1207,12 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1155
1207
|
},
|
|
1156
1208
|
});
|
|
1157
1209
|
const getItemProps = (props) => {
|
|
1158
|
-
return
|
|
1159
|
-
onMouseDown: (e) => {
|
|
1160
|
-
// `getPopperProps` will prevent change focus when mouse down popper
|
|
1161
|
-
// but suggestion item is an exception.
|
|
1162
|
-
// so we need stop propagation to make sure focus can be changed
|
|
1163
|
-
e.stopPropagation();
|
|
1164
|
-
},
|
|
1165
|
-
onMouseUp: (e) => {
|
|
1166
|
-
focusInput();
|
|
1167
|
-
},
|
|
1168
|
-
});
|
|
1210
|
+
return getSuggestionListItemProps(props);
|
|
1169
1211
|
};
|
|
1170
1212
|
const readOnly = !isAutocomplete && !multiple && tags.length >= 1 ? true : undefined;
|
|
1171
1213
|
const closeMenu = (e, reason) => {
|
|
1172
1214
|
keepHighlightedIndexRef.current = false;
|
|
1173
1215
|
isInputValueChangedRef.current = false;
|
|
1174
|
-
// * set to default highlighted
|
|
1175
1216
|
setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
|
|
1176
1217
|
if (isOpen) {
|
|
1177
1218
|
if (e && reason) {
|
|
@@ -1200,8 +1241,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1200
1241
|
const handleInputChange = (newValue, open = true, e) => {
|
|
1201
1242
|
if (inputValue !== newValue) {
|
|
1202
1243
|
setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
|
|
1203
|
-
// !!! Get regexp every time for prevent bug with browser
|
|
1204
|
-
// https://stackoverflow.com/questions/3827456/what-is-wrong-with-my-date-regex/3827500#3827500
|
|
1205
1244
|
if (stringArrToRegExp(keyToTags).test((newValue || '').trim())) {
|
|
1206
1245
|
const resultHaveAdd = checkAndAddFreeSolo({ value: newValue }, e);
|
|
1207
1246
|
if (!disableCloseOnSelect) {
|
|
@@ -1219,7 +1258,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1219
1258
|
return false;
|
|
1220
1259
|
};
|
|
1221
1260
|
const resetState = (e) => {
|
|
1222
|
-
// * when autocomplete select mode not reset input value
|
|
1223
1261
|
if (!isSelectedFromAutocompleteRef.current) {
|
|
1224
1262
|
clearInput();
|
|
1225
1263
|
}
|
|
@@ -1239,18 +1277,15 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1239
1277
|
const keepHighlightedIndex = () => {
|
|
1240
1278
|
keepHighlightedIndexRef.current = true;
|
|
1241
1279
|
};
|
|
1242
|
-
// * when outside options props props change also need set highlightedIndex to default
|
|
1243
1280
|
useChange(() => {
|
|
1244
1281
|
const shouldResetHighlightedIndex = !keepHighlightedIndexRef.current;
|
|
1245
1282
|
if (options !== undefined && shouldResetHighlightedIndex) {
|
|
1246
1283
|
setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
|
|
1247
1284
|
}
|
|
1248
1285
|
}, () => options);
|
|
1249
|
-
// * check that defaultHighlightedIndex
|
|
1250
1286
|
const defaultHighlightedIndex = useMemo(() => {
|
|
1251
1287
|
return autoHighlight ? getNextFocusableOption(0) : -1;
|
|
1252
1288
|
}, [autoHighlight, getNextFocusableOption]);
|
|
1253
|
-
// * only when menu is open set to defaultHighlightedIndex
|
|
1254
1289
|
if (isOpen) {
|
|
1255
1290
|
if (highlightedIndexRef.current === DEFAULT_HIGHLIGHTED_INDEX) {
|
|
1256
1291
|
let toIndex = defaultHighlightedIndex;
|
|
@@ -1283,13 +1318,11 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1283
1318
|
onInputChangeProp?.(result);
|
|
1284
1319
|
}
|
|
1285
1320
|
};
|
|
1286
|
-
// * use force update to sync open state when init is open
|
|
1287
1321
|
useLayoutEffect(() => {
|
|
1288
1322
|
if (isOpen) {
|
|
1289
1323
|
forceUpdate();
|
|
1290
1324
|
}
|
|
1291
1325
|
handleAutocompleteText();
|
|
1292
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1293
1326
|
}, []);
|
|
1294
1327
|
const getInputProps = (props) => {
|
|
1295
1328
|
const suggestionListInputProps = getSuggestionListInputProps(props);
|
|
@@ -1301,9 +1334,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1301
1334
|
const { position } = getSelectionPosition(inputRef);
|
|
1302
1335
|
const currText = inputValue ?? '';
|
|
1303
1336
|
const pasteText = clipboardData.getData('Text') ?? '';
|
|
1304
|
-
// * because input can't get next line char(\n), we need get that value in onPaste
|
|
1305
|
-
// * get current input result use paste
|
|
1306
|
-
// * remember combine current input value, that will be use in onChange
|
|
1307
1337
|
fromPasteString.current =
|
|
1308
1338
|
currText.slice(0, position.start) +
|
|
1309
1339
|
pasteText +
|
|
@@ -1313,7 +1343,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1313
1343
|
onChange: (e) => {
|
|
1314
1344
|
const changeValue = fromPasteString.current || e.target.value;
|
|
1315
1345
|
handleInputChange(changeValue, undefined, e);
|
|
1316
|
-
// * when input value clear all, clear selected item
|
|
1317
1346
|
if (isAutocomplete && changeValue.length === 0) {
|
|
1318
1347
|
clearTags();
|
|
1319
1348
|
}
|
|
@@ -1324,18 +1353,14 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1324
1353
|
setInputFocused(true);
|
|
1325
1354
|
if (openOnFocus)
|
|
1326
1355
|
openMenu(e);
|
|
1327
|
-
// * when autocomplete mode, always select all text when focus
|
|
1328
1356
|
if (isAutocomplete) {
|
|
1329
1357
|
inputRef.current?.select();
|
|
1330
1358
|
}
|
|
1331
|
-
// * reset stopAutoSelect in focus input
|
|
1332
1359
|
stopAutoSelectRef.current = false;
|
|
1333
1360
|
blurTags();
|
|
1334
1361
|
},
|
|
1335
1362
|
onBlur: (e) => {
|
|
1336
1363
|
setInputFocused(false);
|
|
1337
|
-
// mouse down suggestion item will change focus,
|
|
1338
|
-
// but we don't want to close popper in this case
|
|
1339
1364
|
if (popperElementRef.current?.contains(e.relatedTarget))
|
|
1340
1365
|
return;
|
|
1341
1366
|
if (autoSelect && !stopAutoSelectRef.current) {
|
|
@@ -1343,7 +1368,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1343
1368
|
selectItem(e, optionItems[highlightedIndexRef.current]);
|
|
1344
1369
|
checkAndAddFreeSolo({}, e);
|
|
1345
1370
|
}
|
|
1346
|
-
// * when blur should restore text when have selected item
|
|
1347
1371
|
handleAutocompleteText();
|
|
1348
1372
|
closeMenu(e, 'blur');
|
|
1349
1373
|
},
|
|
@@ -1351,7 +1375,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1351
1375
|
isCompositionRef.current = true;
|
|
1352
1376
|
},
|
|
1353
1377
|
onCompositionEnd: async () => {
|
|
1354
|
-
// sleep for prevent tab in composition
|
|
1355
1378
|
await sleep(20);
|
|
1356
1379
|
isCompositionRef.current = false;
|
|
1357
1380
|
},
|
|
@@ -1372,9 +1395,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1372
1395
|
switch (e.key) {
|
|
1373
1396
|
case 'Escape':
|
|
1374
1397
|
if (isOpen) {
|
|
1375
|
-
// Avoid Opera to exit fullscreen mode.
|
|
1376
1398
|
e.preventDefault();
|
|
1377
|
-
// Avoid the Modal to handle the event.
|
|
1378
1399
|
e.stopPropagation();
|
|
1379
1400
|
closeMenu(e, 'escape');
|
|
1380
1401
|
}
|
|
@@ -1387,13 +1408,9 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1387
1408
|
e.stopPropagation();
|
|
1388
1409
|
return;
|
|
1389
1410
|
}
|
|
1390
|
-
// * when shift+ tab also not need autoSelect
|
|
1391
1411
|
if (e.shiftKey && currTagsLength > 0) {
|
|
1392
1412
|
stopAutoSelectRef.current = true;
|
|
1393
1413
|
}
|
|
1394
|
-
// ! [Browser] when in composition,
|
|
1395
|
-
// ! and keydown 'Tab' that will trigger twice keydown,
|
|
1396
|
-
// ! must check is that is not in composition mode
|
|
1397
1414
|
if (!e.shiftKey && !isCompositionRef.current) {
|
|
1398
1415
|
const currentHighlightItem = optionItems[highlightedIndex];
|
|
1399
1416
|
if (isOpen && currentHighlightItem) {
|
|
@@ -1427,7 +1444,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1427
1444
|
selectItem(e, optionItems[highlightedIndex]);
|
|
1428
1445
|
e.stopPropagation();
|
|
1429
1446
|
}
|
|
1430
|
-
// always preventDefault for not inset all enter into textarea
|
|
1431
1447
|
e.preventDefault();
|
|
1432
1448
|
break;
|
|
1433
1449
|
case 'F10':
|
|
@@ -1439,7 +1455,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1439
1455
|
e.preventDefault();
|
|
1440
1456
|
return;
|
|
1441
1457
|
}
|
|
1442
|
-
// * check state for is that should move key in option items
|
|
1443
1458
|
if (isAutocomplete && tags.length > 0 && !isOpen) {
|
|
1444
1459
|
openMenu(e);
|
|
1445
1460
|
e.preventDefault();
|
|
@@ -1458,15 +1473,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1458
1473
|
},
|
|
1459
1474
|
onMouseDown: toggleWithInput
|
|
1460
1475
|
? (e) => {
|
|
1461
|
-
/**
|
|
1462
|
-
* If the popup isn't open and `openOnFocus` is disabled we should
|
|
1463
|
-
* show the popup. If `openOnFocus` is enabled the `onFocus` handler
|
|
1464
|
-
* will take care of opening the popup when necessary.
|
|
1465
|
-
*
|
|
1466
|
-
* If the popup is open and the user hasn't typed a value we should
|
|
1467
|
-
* hide it, but if they have typed a value we shouldn't hide it to
|
|
1468
|
-
* ensure the the available results remain visible to the user.
|
|
1469
|
-
*/
|
|
1470
1476
|
if (!isOpen || (isOpen && !inputValue)) {
|
|
1471
1477
|
toggleMenu(e, false);
|
|
1472
1478
|
}
|
|
@@ -1559,7 +1565,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1559
1565
|
reset,
|
|
1560
1566
|
forceUpdate,
|
|
1561
1567
|
optionsGroupList,
|
|
1562
|
-
/** that state of current Autocomplete focused, both tag or input focused */
|
|
1563
1568
|
focused: focused ?? (tagFocused || inputFocused ? true : undefined),
|
|
1564
1569
|
id: AutocompleteId,
|
|
1565
1570
|
inputChanged: isInputValueChangedRef.current,
|
|
@@ -1574,9 +1579,7 @@ const ChipSizeMap = {
|
|
|
1574
1579
|
medium: 'small',
|
|
1575
1580
|
};
|
|
1576
1581
|
const InputComponent = forwardRef(({ startAdornment, containerRef, containerClassName, onContainerClick, ...rest }, ref) => {
|
|
1577
|
-
return (
|
|
1578
|
-
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
|
|
1579
|
-
jsxs("div", { ref: containerRef, className: containerClassName, onClick: onContainerClick, children: [startAdornment, jsx("input", { ...rest, ref: ref })] }));
|
|
1582
|
+
return (jsxs("div", { ref: containerRef, className: containerClassName, onClick: onContainerClick, children: [startAdornment, jsx("input", { ...rest, ref: ref })] }));
|
|
1580
1583
|
});
|
|
1581
1584
|
const TextInput = forwardRef(({ renderInput, ...rest }, ref) => {
|
|
1582
1585
|
if (renderInput) {
|
|
@@ -1587,14 +1590,7 @@ const TextInput = forwardRef(({ renderInput, ...rest }, ref) => {
|
|
|
1587
1590
|
const Autocomplete = forwardRef((inProps, ref) => {
|
|
1588
1591
|
const props = useThemeProps({ props: inProps, name: 'Autocomplete' });
|
|
1589
1592
|
const isAutocomplete = props.variant === 'autocomplete';
|
|
1590
|
-
const { inputVariant = 'standard', variant, groupExpanded, onGroupExpanded, groupDefaultExpanded, groupVariant = 'normal', getExpandIconProps, groupBy, value: valueProp, autoHighlight = true, clearBtn = false, toggleButton = false, multiple = false, ClearButtonProps, onClear, ToggleButtonProps, getToggleButtonProps: getToggleButtonPropsProp, inputValue: inputValueProp, onKeyDown: onKeyDownProp, helperText: helperTextProp, options, openOnFocus = true, toggleWithInput = true, getOptionLabel = DEFAULT_GET_OPTION_LABEL, renderInput, label, inputRef: inputRefProp, error, placeholder, showPlaceholderWithTags, freeSolo, keyToTags = DEFAULT_KEY_TO_CHIPS, maxFreeSolo, SuggestionListProps: { virtualize = true, ...SuggestionListProps } = {}, autoSelect, PopperComponent = Popper, PopperProps: { anchorElType = 'root',
|
|
1591
|
-
// transition: popperTransition,
|
|
1592
|
-
// TransitionComponent = virtualize ? RcFade : RcGrow,
|
|
1593
|
-
// transitionDuration: transitionDurationProp = 'auto',
|
|
1594
|
-
// TransitionProps: TransitionPropsProp = {},
|
|
1595
|
-
...PopperProps } = {}, initialIsOpen, disabled, required: requiredProp, disableCloseOnSelect = multiple, onChange: onChangeProp, onInputChange: onInputChangeProp, onMaxFreeSolo, FormHelperTextProps, action, filterOptions, renderOption, renderTags, getOptionDisabled, renderGroup, renderNoOptions, inputProps,
|
|
1596
|
-
// InputProps: InputPropsProp,
|
|
1597
|
-
debug, disabledItemsHighlightable, open: openProp, addNoOptionItem, onOpen, onClose, focused, classes, className, fullWidth = true, size = 'xlarge', ...rest } = props;
|
|
1593
|
+
const { inputVariant = 'standard', variant, groupExpanded, onGroupExpanded, groupDefaultExpanded, groupVariant = 'normal', getExpandIconProps, groupBy, value: valueProp, autoHighlight = true, clearBtn = false, toggleButton = false, multiple = false, ClearButtonProps, onClear, ToggleButtonProps, getToggleButtonProps: getToggleButtonPropsProp, inputValue: inputValueProp, onKeyDown: onKeyDownProp, helperText: helperTextProp, options, openOnFocus = true, toggleWithInput = true, getOptionLabel = DEFAULT_GET_OPTION_LABEL, renderInput, label, inputRef: inputRefProp, error, placeholder, showPlaceholderWithTags, freeSolo, keyToTags = DEFAULT_KEY_TO_CHIPS, maxFreeSolo, SuggestionListProps: { virtualize = true, ...SuggestionListProps } = {}, autoSelect, PopperComponent = Popper, PopperProps: { anchorElType = 'root', ...PopperProps } = {}, initialIsOpen, disabled, required: requiredProp, disableCloseOnSelect = multiple, onChange: onChangeProp, onInputChange: onInputChangeProp, onMaxFreeSolo, FormHelperTextProps, action, filterOptions, renderOption, renderTags, getOptionDisabled, renderGroup, renderNoOptions, inputProps, debug, disabledItemsHighlightable, open: openProp, addNoOptionItem, onOpen, onClose, focused, classes, className, fullWidth = true, size = 'xlarge', isOptionEqualToValue, ...rest } = props;
|
|
1598
1594
|
const cls = useClasses('autocomplete', classes, className);
|
|
1599
1595
|
const innerInputRef = useRef(null);
|
|
1600
1596
|
const textFieldRef = useRef(null);
|
|
@@ -1605,12 +1601,7 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1605
1601
|
const anchorElRef = anchorElType === 'input' ? formFieldContentRef : textFieldRef;
|
|
1606
1602
|
let oneOfTagError = false;
|
|
1607
1603
|
const required = requiredProp;
|
|
1608
|
-
const { focusInput, optionItems, optionsGroupList, tags: selectedItems, highlightedIndex, getToggleButtonProps, getTagProps, getTagListBoxProps, getSuggestionListProps, getPopperProps, getInputProps, getInputAriaProps,
|
|
1609
|
-
// getLabelProps,
|
|
1610
|
-
getItemProps, isOpen, inputValue, activeIndex, setActiveIndex, setHighlightedIndex, onInputChange, keepHighlightedIndex, changeHighlightedIndexReason, closeMenu, openMenu, reset, getClearButtonProps, noOptionItem, getNoOptionsProps, isKeepHighlightedIndex,
|
|
1611
|
-
// focused: isAutocompleteFocused,
|
|
1612
|
-
// autoCompleteSelectedIndex,
|
|
1613
|
-
popperElementRef, } = useAutocomplete({
|
|
1604
|
+
const { focusInput, optionItems, optionsGroupList, tags: selectedItems, highlightedIndex, getToggleButtonProps, getTagProps, getTagListBoxProps, getSuggestionListProps, getPopperProps, getInputProps, getInputAriaProps, getItemProps, isOpen, inputValue, activeIndex, setActiveIndex, setHighlightedIndex, onInputChange, keepHighlightedIndex, changeHighlightedIndexReason, closeMenu, openMenu, reset, getClearButtonProps, noOptionItem, getNoOptionsProps, isKeepHighlightedIndex, popperElementRef, } = useAutocomplete({
|
|
1614
1605
|
focused,
|
|
1615
1606
|
open: openProp,
|
|
1616
1607
|
addNoOptionItem,
|
|
@@ -1650,6 +1641,7 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1650
1641
|
onKeyDown: onKeyDownProp,
|
|
1651
1642
|
disabledItemsHighlightable,
|
|
1652
1643
|
onGroupExpanded,
|
|
1644
|
+
isOptionEqualToValue,
|
|
1653
1645
|
});
|
|
1654
1646
|
const open = Boolean((isOpen || noOptionItem) && textFieldRef.current);
|
|
1655
1647
|
const { onBlur, ...InputProps } = getInputProps(inputProps);
|
|
@@ -1681,7 +1673,6 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1681
1673
|
const endAdornment = (toggleButton || clearBtn) && (jsxs(Fragment, { children: [clearBtn && (selectedItems.length > 0 || inputValue) && (jsx(IconButton, { shape: 'squircle', background: false, color: 'secondary', variant: "contained", size: "small", className: cls('clear'), symbol: Xsm, ...getClearButtonProps(ClearButtonProps) })), toggleButton && (jsx(IconButton, { "aria-hidden": true, tabIndex: -1, shape: 'squircle', color: 'secondary', background: false, variant: "contained", size: "small", className: cls('chevron', error && 'error-chevron', open && `${inputVariant}-expanded-chevron`), children: jsx(ExpandCollapseCaret, { size: 'small', expanded: open }), ...getToggleButtonProps({
|
|
1682
1674
|
...ToggleButtonProps,
|
|
1683
1675
|
...getToggleButtonPropsProp?.(open),
|
|
1684
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1685
1676
|
}) }))] }));
|
|
1686
1677
|
const TextFieldInputProps = {
|
|
1687
1678
|
'aria-label': typeof label === 'string' ? label : undefined,
|
|
@@ -1704,32 +1695,14 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1704
1695
|
}));
|
|
1705
1696
|
const hasTags = selectedItems.length > 0;
|
|
1706
1697
|
const willRenderNoOptions = !!noOptionItem && !!renderNoOptions;
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
// const handleUpdatePopper = useEventCallback(() => {
|
|
1710
|
-
// popperRef.current?.update();
|
|
1711
|
-
// });
|
|
1712
|
-
const menuChildren =
|
|
1713
|
-
// * that root element for animation host
|
|
1714
|
-
isOpen && (jsxs(Fragment, { children: [jsx(SuggestionList
|
|
1715
|
-
// TODO: deprecated, need to check if it will cause error when variant is 'autocomplete'
|
|
1716
|
-
// selectedIndex={isAutocomplete ? autoCompleteSelectedIndex : undefined}
|
|
1717
|
-
, {
|
|
1718
|
-
// TODO: deprecated, need to check if it will cause error when variant is 'autocomplete'
|
|
1719
|
-
// selectedIndex={isAutocomplete ? autoCompleteSelectedIndex : undefined}
|
|
1720
|
-
highlightedIndex: highlightedIndex, optionsGroupList: optionsGroupList, options: optionItems, groupVariant: groupVariant, groupExpanded: groupExpanded, renderGroup: renderGroup, renderOption: renderOption, inputValue: inputValue, getItemProps: getItemProps, getSuggestionListProps: getSuggestionListProps, changeHighlightedIndexReason: changeHighlightedIndexReason, getOptionDisabled: getOptionDisabled, isKeepHighlightedIndex: isKeepHighlightedIndex,
|
|
1721
|
-
// onContainerHeightChange={handleUpdatePopper}
|
|
1722
|
-
maxContainerHeight: 180, getOptionLabel: getOptionLabel, position: "unset", virtualize: virtualize, ...SuggestionListProps }), willRenderNoOptions &&
|
|
1698
|
+
const prevHighlightedIndex = usePrevious(() => highlightedIndex, true);
|
|
1699
|
+
const menuChildren = isOpen && (jsxs(Fragment, { children: [jsx(SuggestionList, { highlightedIndex: highlightedIndex, prevHighlightedIndex: prevHighlightedIndex, optionsGroupList: optionsGroupList, options: optionItems, groupVariant: groupVariant, groupExpanded: groupExpanded, renderGroup: renderGroup, renderOption: renderOption, inputValue: inputValue, getItemProps: getItemProps, getSuggestionListProps: getSuggestionListProps, changeHighlightedIndexReason: changeHighlightedIndexReason, getOptionDisabled: getOptionDisabled, isKeepHighlightedIndex: isKeepHighlightedIndex, maxContainerHeight: 180, getOptionLabel: getOptionLabel, position: "unset", virtualize: virtualize, ...SuggestionListProps }), willRenderNoOptions &&
|
|
1723
1700
|
renderNoOptions?.(getNoOptionsProps, noOptionItem)] }));
|
|
1724
1701
|
const isPopperContentEmpty = !isOpen || (!willRenderNoOptions && optionItems.length === 0);
|
|
1725
1702
|
const popperCls = useClasses('autocomplete-popper', PopperProps?.classes, PopperProps?.className);
|
|
1726
|
-
return (jsxs(Fragment, { children: [jsx(TextInput, { renderInput: renderInput,
|
|
1727
|
-
// hasTags={hasTags}
|
|
1728
|
-
variant: inputVariant, ref: toTextFieldRef, inputRef: inputRef, fullWidth: fullWidth, placeholder: !hasTags || (hasTags && multiple && showPlaceholderWithTags)
|
|
1703
|
+
return (jsxs(Fragment, { children: [jsx(TextInput, { renderInput: renderInput, variant: inputVariant, ref: toTextFieldRef, inputRef: inputRef, fullWidth: fullWidth, placeholder: !hasTags || (hasTags && multiple && showPlaceholderWithTags)
|
|
1729
1704
|
? placeholder
|
|
1730
|
-
: undefined, label: label, disabled: disabled, required: required,
|
|
1731
|
-
// * if below one of tag is error, that main Autocomplete default will be error
|
|
1732
|
-
error: error ?? oneOfTagError, FormHelperTextProps: FormHelperTextProps, inputComponent: InputComponent, endAdornment: endAdornment, helperText: helperText, size: size, ...combineProps({
|
|
1705
|
+
: undefined, label: label, disabled: disabled, required: required, error: error ?? oneOfTagError, FormHelperTextProps: FormHelperTextProps, inputComponent: InputComponent, endAdornment: endAdornment, helperText: helperText, size: size, ...combineProps({
|
|
1733
1706
|
RootProps: {
|
|
1734
1707
|
contentRef: formFieldContentRef,
|
|
1735
1708
|
},
|
|
@@ -1752,16 +1725,16 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1752
1725
|
}), children: menuChildren }) })) })] }));
|
|
1753
1726
|
});
|
|
1754
1727
|
Autocomplete.displayName = 'SuiAutocomplete';
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
getOptionLabel?.(item)
|
|
1728
|
+
const RcAutocompleteDefaultFilterOptions = (options, { inputValue, getOptionLabel, selectedItems, isOptionEqualToValue }) => {
|
|
1729
|
+
return options.filter((item) => {
|
|
1730
|
+
const isSelected = selectedItems.some((selected) => isOptionEqualToValue(item, selected));
|
|
1731
|
+
if (isSelected)
|
|
1732
|
+
return false;
|
|
1733
|
+
const optionLabel = getOptionLabel?.(item) || '';
|
|
1734
|
+
return optionLabel
|
|
1763
1735
|
.toLowerCase()
|
|
1764
|
-
.startsWith(inputValue?.toLowerCase() || '')
|
|
1736
|
+
.startsWith(inputValue?.toLowerCase() || '');
|
|
1737
|
+
});
|
|
1765
1738
|
};
|
|
1766
1739
|
|
|
1767
|
-
export { Autocomplete, DEFAULT_GET_OPTION_LABEL, DEFAULT_KEY_TO_CHIPS, RcAutocompleteDefaultFilterOptions, SuggestionList, SuggestionListItem, Autocomplete$1 as autocompleteStyles, stringArrToRegExp, SuggestionList$1 as suggestionListStyles, useAutocomplete, useAutocompleteGroup, useAutocompleteTag, useDynamicHeight, useHighlightScroll, useSuggestionList };
|
|
1740
|
+
export { Autocomplete, DEFAULT_GET_OPTION_LABEL, DEFAULT_KEY_TO_CHIPS, DEFAULT_PADDING, RcAutocompleteDefaultFilterOptions, SuggestionList, SuggestionListItem, Autocomplete$1 as autocompleteStyles, getPaddingValue, isFirstInGroup, stringArrToRegExp, SuggestionList$1 as suggestionListStyles, useAutocomplete, useAutocompleteGroup, useAutocompleteTag, useDynamicHeight, useHighlightScroll, useSuggestionList };
|