@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/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, useWindowContext, useRefState, useEventCallback, logInDev, combineProps, useDepsChange, useControlled, useId, useForceUpdate, useKeyboardMoveFocus, useForkRef, 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,16 +86,16 @@ const useDynamicHeight = ({ itemCount, maxContainerHeight, onContainerHeightChan
|
|
|
87
86
|
};
|
|
88
87
|
};
|
|
89
88
|
|
|
90
|
-
/**
|
|
91
|
-
* scroll into current highlight index
|
|
92
|
-
*/
|
|
93
89
|
function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
|
|
90
|
+
const { externalWindow } = useWindowContext();
|
|
94
91
|
const renderedItemsRef = useRef([]);
|
|
95
92
|
const scrollerRef = useRef();
|
|
96
93
|
const scrollToHighlightedIndex = (prevHighlightedIndex, currHighlightedIndex, topHighlightIndex = 0) => {
|
|
94
|
+
const needsScrollReset = prevHighlightedIndex === -1;
|
|
97
95
|
if (!scrollerRef.current) {
|
|
98
96
|
if (currHighlightedIndex !== topHighlightIndex) {
|
|
99
|
-
|
|
97
|
+
const win = externalWindow || window;
|
|
98
|
+
win.requestAnimationFrame(() => {
|
|
100
99
|
scrollToIndex({
|
|
101
100
|
index: currHighlightedIndex,
|
|
102
101
|
align: 'start',
|
|
@@ -107,26 +106,28 @@ function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
|
|
|
107
106
|
}
|
|
108
107
|
const scrollElm = scrollerRef.current;
|
|
109
108
|
const renderedItems = renderedItemsRef.current;
|
|
110
|
-
const
|
|
111
|
-
|
|
109
|
+
const lastHighlightedIndex = needsScrollReset
|
|
110
|
+
? currHighlightedIndex
|
|
111
|
+
: prevHighlightedIndex;
|
|
112
|
+
const isBackward = lastHighlightedIndex > currHighlightedIndex;
|
|
113
|
+
const isForward = lastHighlightedIndex < currHighlightedIndex;
|
|
112
114
|
const currentVisibleOffset = {
|
|
113
115
|
start: scrollElm.scrollTop,
|
|
114
116
|
stop: scrollElm.scrollTop + containerHeighRef.current,
|
|
115
117
|
};
|
|
116
|
-
|
|
117
|
-
if (!
|
|
118
|
+
const isInRenderedRange = renderedItems.some((x) => x.index === currHighlightedIndex);
|
|
119
|
+
if (!isInRenderedRange) {
|
|
118
120
|
scrollToIndex({
|
|
119
121
|
index: currHighlightedIndex,
|
|
120
122
|
align: isBackward ? 'start' : 'end',
|
|
121
123
|
});
|
|
122
124
|
return;
|
|
123
125
|
}
|
|
124
|
-
// backward
|
|
125
126
|
if (isBackward) {
|
|
126
127
|
let firstShouldScrollIndex = currHighlightedIndex;
|
|
127
128
|
for (let i = renderedItems.length - 1; i >= 0; i--) {
|
|
128
129
|
const item = renderedItems[i];
|
|
129
|
-
if (item.offset
|
|
130
|
+
if (item.offset < currentVisibleOffset.start) {
|
|
130
131
|
break;
|
|
131
132
|
}
|
|
132
133
|
firstShouldScrollIndex = item.index;
|
|
@@ -137,18 +138,18 @@ function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
|
|
|
137
138
|
align: 'start',
|
|
138
139
|
});
|
|
139
140
|
}
|
|
140
|
-
// forward
|
|
141
141
|
}
|
|
142
142
|
else if (isForward) {
|
|
143
143
|
let lastShouldScrollIndex = currHighlightedIndex;
|
|
144
144
|
for (let i = 0; i < renderedItems.length; i++) {
|
|
145
145
|
const item = renderedItems[i];
|
|
146
|
-
|
|
146
|
+
const itemBottom = item.offset + item.size;
|
|
147
|
+
if (itemBottom >= currentVisibleOffset.stop) {
|
|
147
148
|
break;
|
|
148
149
|
}
|
|
149
150
|
lastShouldScrollIndex = item.index;
|
|
150
151
|
}
|
|
151
|
-
if (currHighlightedIndex
|
|
152
|
+
if (currHighlightedIndex > lastShouldScrollIndex) {
|
|
152
153
|
scrollToIndex({
|
|
153
154
|
index: currHighlightedIndex,
|
|
154
155
|
align: 'end',
|
|
@@ -156,31 +157,45 @@ function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
|
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
else {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
const currentItem = renderedItems.find((item) => item.index === currHighlightedIndex);
|
|
161
|
+
if (needsScrollReset) {
|
|
162
|
+
if (currHighlightedIndex === topHighlightIndex) {
|
|
163
|
+
if (scrollerRef.current) {
|
|
164
|
+
scrollerRef.current.scrollTop = 0;
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
scrollToIndex({
|
|
170
|
+
index: currHighlightedIndex,
|
|
171
|
+
align: 'start',
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!currentItem) {
|
|
177
|
+
scrollToIndex({
|
|
178
|
+
index: currHighlightedIndex,
|
|
179
|
+
align: 'start',
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const itemBottom = currentItem.offset + currentItem.size;
|
|
184
|
+
const isTopVisible = currentItem.offset >= currentVisibleOffset.start;
|
|
185
|
+
const isBottomVisible = itemBottom < currentVisibleOffset.stop;
|
|
186
|
+
const isFullyVisible = isTopVisible && isBottomVisible;
|
|
187
|
+
if (!isFullyVisible) {
|
|
188
|
+
scrollToIndex({
|
|
189
|
+
index: currHighlightedIndex,
|
|
190
|
+
align: 'start',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
163
193
|
}
|
|
164
194
|
};
|
|
165
195
|
return {
|
|
166
|
-
/**
|
|
167
|
-
* scroller reference
|
|
168
|
-
*/
|
|
169
196
|
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
197
|
scrollToHighlightedIndex,
|
|
177
|
-
/**
|
|
178
|
-
* set to virtuoso `scrollerRef` prop, auto bind inner `scrollerRef`
|
|
179
|
-
*/
|
|
180
198
|
scrollerRefFn: (elm) => (scrollerRef.current = elm),
|
|
181
|
-
/**
|
|
182
|
-
* set to virtuoso `itemsRendered` prop, auto bind inner `renderedItemsRef`
|
|
183
|
-
*/
|
|
184
199
|
itemsRendered: (e) => (renderedItemsRef.current = e),
|
|
185
200
|
};
|
|
186
201
|
}
|
|
@@ -216,11 +231,9 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
|
|
|
216
231
|
e.stopPropagation();
|
|
217
232
|
});
|
|
218
233
|
const optionsGroupList = useMemo(() => {
|
|
219
|
-
/** key with this group count */
|
|
220
234
|
if (!groupBy || !options) {
|
|
221
235
|
return [];
|
|
222
236
|
}
|
|
223
|
-
// used to keep track of key and indexes in the result array
|
|
224
237
|
const indexBy = new Map();
|
|
225
238
|
let warn = false;
|
|
226
239
|
const afterGroupFn = [];
|
|
@@ -274,11 +287,8 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
|
|
|
274
287
|
id: `${id}-${group}`,
|
|
275
288
|
label: group,
|
|
276
289
|
disabled: true,
|
|
277
|
-
// * here cause by self Type reference
|
|
278
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
279
290
|
group: newGroup,
|
|
280
291
|
};
|
|
281
|
-
// push fn to array, do that after all options be add into group
|
|
282
292
|
if (getOptionDisabled) {
|
|
283
293
|
afterGroupFn.push(() => {
|
|
284
294
|
groupTitleOption.disabled = getOptionDisabled(groupTitleOption);
|
|
@@ -319,7 +329,6 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
|
|
|
319
329
|
initGroupExpandedState(groupExpanded);
|
|
320
330
|
}, [groupExpanded]);
|
|
321
331
|
const groupExpandedMap = groupExpandedMapRef.current;
|
|
322
|
-
// * speared logic with group, prevent calculate every time.
|
|
323
332
|
const groupedResult = useMemo(() => {
|
|
324
333
|
const addExpandedResult = Object.entries(groupExpandedMap).reduce((prev, [key, expended]) => {
|
|
325
334
|
const group = prev.find((x) => x.group === key);
|
|
@@ -351,10 +360,8 @@ const isItemCanSelected = (item) => {
|
|
|
351
360
|
};
|
|
352
361
|
const toOptionIdString = (id) => typeof id === 'number' ? String(id) : id;
|
|
353
362
|
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, }) => {
|
|
363
|
+
const defaultIsOptionEqualToValue = (option, value) => option === value;
|
|
364
|
+
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
365
|
const isTitleMode = groupVariant === 'normal';
|
|
359
366
|
const [inputValue, setInputValue] = useControlled({
|
|
360
367
|
controlled: inputValueProp,
|
|
@@ -376,12 +383,12 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
376
383
|
inputValue,
|
|
377
384
|
getOptionLabel,
|
|
378
385
|
selectedItems: [],
|
|
386
|
+
isOptionEqualToValue,
|
|
379
387
|
});
|
|
380
388
|
}
|
|
381
389
|
return items;
|
|
382
|
-
}, [filterOptions, getOptionLabel, inputValue]);
|
|
390
|
+
}, [filterOptions, getOptionLabel, inputValue, isOptionEqualToValue]);
|
|
383
391
|
const filteredResult = useMemo(() => {
|
|
384
|
-
// * only when isOpen calculate the filtered result
|
|
385
392
|
let results = getFilteredItems(options);
|
|
386
393
|
results = processFilteredResult
|
|
387
394
|
? processFilteredResult(results, inputValue)
|
|
@@ -454,7 +461,7 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
454
461
|
});
|
|
455
462
|
const getItemProps = ({ item, index = 0, }) => {
|
|
456
463
|
const currGroup = item.group;
|
|
457
|
-
const
|
|
464
|
+
const isItemFirstInGroup = isFirstInGroup(item);
|
|
458
465
|
const itemCount = optionItems.length;
|
|
459
466
|
const groupOrder = currGroup?.order || 0;
|
|
460
467
|
const stringId = toOptionIdString(item.id);
|
|
@@ -469,8 +476,7 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
469
476
|
setHighlightedIndex(index, { reason: 'mouse', reRender: true });
|
|
470
477
|
}
|
|
471
478
|
},
|
|
472
|
-
|
|
473
|
-
...(isTitleMode && isGroupTitle
|
|
479
|
+
...(isTitleMode && isItemFirstInGroup
|
|
474
480
|
? {}
|
|
475
481
|
: {
|
|
476
482
|
role: 'option',
|
|
@@ -509,9 +515,8 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
509
515
|
return;
|
|
510
516
|
if (highlightedIndex !== DEFAULT_HIGHLIGHTED_INDEX$1) {
|
|
511
517
|
const currOption = optionItems[highlightedIndex];
|
|
512
|
-
const
|
|
513
|
-
if (
|
|
514
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
518
|
+
const isCurrOptionFirstInGroup = isFirstInGroup(currOption);
|
|
519
|
+
if (isCurrOptionFirstInGroup) {
|
|
515
520
|
const { onClick } = currOption.group.getExpandIconProps();
|
|
516
521
|
onClick?.(e);
|
|
517
522
|
}
|
|
@@ -520,7 +525,6 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
520
525
|
}
|
|
521
526
|
e.stopPropagation();
|
|
522
527
|
}
|
|
523
|
-
// always preventDefault for not inset all enter into textarea
|
|
524
528
|
e.preventDefault();
|
|
525
529
|
break;
|
|
526
530
|
}
|
|
@@ -597,30 +601,53 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
|
|
|
597
601
|
changeHighlightedIndexReasonRef,
|
|
598
602
|
changeHighlightedIndexReason: changeHighlightedIndexReasonRef.current,
|
|
599
603
|
optionsGroupList,
|
|
600
|
-
/** current unique id */
|
|
601
604
|
id: currUniqueId,
|
|
602
605
|
getNextFocusableOption,
|
|
603
606
|
handleF10KeyDown,
|
|
604
607
|
};
|
|
605
608
|
};
|
|
606
609
|
|
|
610
|
+
const DEFAULT_PADDING = 8;
|
|
611
|
+
const getPaddingValue = (padding, itemCount) => {
|
|
612
|
+
if (itemCount === 0) {
|
|
613
|
+
return 0;
|
|
614
|
+
}
|
|
615
|
+
if (padding === false) {
|
|
616
|
+
return 0;
|
|
617
|
+
}
|
|
618
|
+
if (padding === true) {
|
|
619
|
+
return DEFAULT_PADDING;
|
|
620
|
+
}
|
|
621
|
+
if (typeof padding === 'number') {
|
|
622
|
+
return Math.max(0, padding);
|
|
623
|
+
}
|
|
624
|
+
return DEFAULT_PADDING;
|
|
625
|
+
};
|
|
607
626
|
const List = forwardRef((props, ref) => {
|
|
608
|
-
const { className, context } = props;
|
|
627
|
+
const { className, context, style, ...restProps } = props;
|
|
609
628
|
const listRef = context?.listRef;
|
|
610
629
|
const rootClass = context?.rootClass;
|
|
630
|
+
const paddingValue = context?.paddingValue ?? 0;
|
|
611
631
|
const forkRef = useForkRef(ref, listRef);
|
|
612
|
-
return (jsx("div", { ref: forkRef, className: clsx(className, rootClass), "data-suggestion-list": true,
|
|
632
|
+
return (jsx("div", { ref: forkRef, ...restProps, className: clsx(className, rootClass), "data-suggestion-list": true, style: {
|
|
633
|
+
...style,
|
|
634
|
+
['--sui-suggestion-list-vertical-padding']: `${paddingValue}px`,
|
|
635
|
+
} }));
|
|
613
636
|
});
|
|
614
637
|
const SuggestionList = forwardRef((inProps, ref) => {
|
|
615
638
|
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 =
|
|
639
|
+
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
640
|
const vlRef = useRef(null);
|
|
618
641
|
const forkVlRef = useForkRef(ref, vlRef);
|
|
619
642
|
const isTitleMode = groupVariant === 'normal';
|
|
620
643
|
const listRef = useRef(null);
|
|
621
644
|
const itemData = options;
|
|
622
645
|
const itemCount = options.length;
|
|
623
|
-
const
|
|
646
|
+
const internalPrevHighlightedIndex = usePrevious(() => highlightedIndex, true);
|
|
647
|
+
const actualPrevHighlightedIndex = prevHighlightedIndex ?? internalPrevHighlightedIndex;
|
|
648
|
+
const cls = useClassesWithMode('suggestion-list', classesProp, classNameProp);
|
|
649
|
+
const paddingValue = getPaddingValue(padding, itemCount);
|
|
650
|
+
const hasItemsRenderedRef = useRef(false);
|
|
624
651
|
const { totalListHeightChanged, style, containerHeighRef } = useDynamicHeight({
|
|
625
652
|
itemCount,
|
|
626
653
|
maxContainerHeight,
|
|
@@ -632,43 +659,69 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
632
659
|
}
|
|
633
660
|
},
|
|
634
661
|
});
|
|
662
|
+
const handleTotalListHeightChanged = (height) => {
|
|
663
|
+
const adjustedHeight = paddingValue > 0 ? height + paddingValue * 2 : height;
|
|
664
|
+
if (!hasItemsRenderedRef.current &&
|
|
665
|
+
adjustedHeight < containerHeighRef.current) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
totalListHeightChanged(adjustedHeight);
|
|
669
|
+
};
|
|
635
670
|
const { sleep } = useSleep();
|
|
636
671
|
const { retry: scrollToIndexWithRetry } = useRetry(async (location) => {
|
|
637
672
|
const toIndex = location.index;
|
|
638
673
|
if (!virtualize) {
|
|
639
|
-
const
|
|
674
|
+
const scroller = listRef.current;
|
|
675
|
+
const needsScrollReset = prevHighlightedIndex === -1;
|
|
676
|
+
const isLastItem = itemCount > 1 && toIndex === itemCount - 1;
|
|
677
|
+
if (paddingValue > 0 && needsScrollReset && isLastItem && scroller) {
|
|
678
|
+
scroller.scrollTop = scroller.scrollHeight;
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
const toElm = scroller?.querySelector(`[data-item-index="${toIndex}"]`);
|
|
640
682
|
toElm?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
641
683
|
return true;
|
|
642
684
|
}
|
|
643
|
-
if (
|
|
685
|
+
if (paddingValue > 0 &&
|
|
686
|
+
(location.align === 'start' || location.align === 'end')) {
|
|
644
687
|
location = {
|
|
645
688
|
...location,
|
|
646
|
-
offset:
|
|
689
|
+
offset: paddingValue,
|
|
647
690
|
};
|
|
648
691
|
}
|
|
649
692
|
vlRef.current?.scrollToIndex(location);
|
|
650
693
|
await sleep(0);
|
|
651
|
-
// confirm that scrollInto view
|
|
652
694
|
const toElm = listRef.current?.querySelector(`[data-item-index="${toIndex}"]`);
|
|
653
695
|
return !!toElm;
|
|
654
696
|
}, {
|
|
655
697
|
retryTimes: 10,
|
|
656
698
|
intervalTime: 20,
|
|
657
699
|
});
|
|
658
|
-
const { scrollerRef, scrollerRefFn, itemsRendered, scrollToHighlightedIndex, } = useHighlightScroll({
|
|
700
|
+
const { scrollerRef, scrollerRefFn, itemsRendered: itemsRenderedFromHook, scrollToHighlightedIndex, } = useHighlightScroll({
|
|
659
701
|
containerHeighRef,
|
|
660
702
|
scrollToIndex: scrollToIndexWithRetry,
|
|
661
703
|
});
|
|
662
|
-
const
|
|
704
|
+
const handleItemsRendered = (items) => {
|
|
705
|
+
if (items.length > 0 || itemCount === 0) {
|
|
706
|
+
hasItemsRenderedRef.current = true;
|
|
707
|
+
}
|
|
708
|
+
itemsRenderedFromHook(items);
|
|
709
|
+
};
|
|
710
|
+
useEffect(() => {
|
|
711
|
+
if (itemCount > 0) {
|
|
712
|
+
hasItemsRenderedRef.current = false;
|
|
713
|
+
}
|
|
714
|
+
}, [itemCount]);
|
|
663
715
|
useLayoutEffect(() => {
|
|
664
|
-
|
|
716
|
+
const shouldScroll = (!virtualize || vlRef.current) &&
|
|
665
717
|
!isKeepHighlightedIndex &&
|
|
666
|
-
// * only scroll when reason is 'keyboard'
|
|
667
718
|
changeHighlightedIndexReason &&
|
|
668
|
-
changeHighlightedIndexReason !== 'mouse'
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
719
|
+
changeHighlightedIndexReason !== 'mouse';
|
|
720
|
+
const firstOption = options[0];
|
|
721
|
+
const isFirstItemGroupTitle = isTitleMode && isFirstInGroup(firstOption);
|
|
722
|
+
const topHighlightIndex = isFirstItemGroupTitle ? 1 : 0;
|
|
723
|
+
if (shouldScroll) {
|
|
724
|
+
scrollToHighlightedIndex(actualPrevHighlightedIndex, highlightedIndex, topHighlightIndex);
|
|
672
725
|
}
|
|
673
726
|
});
|
|
674
727
|
const handleScrolling = (scrolling) => {
|
|
@@ -678,12 +731,11 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
678
731
|
list.style.pointerEvents = scrolling ? 'none' : '';
|
|
679
732
|
};
|
|
680
733
|
const itemContent = (index, option) => {
|
|
681
|
-
const
|
|
682
|
-
const isGroupTitle = option === currGroup?.options[0];
|
|
734
|
+
const isOptionFirstInGroup = isFirstInGroup(option);
|
|
683
735
|
const isFixedGroupExpanded = typeof groupExpanded === 'boolean';
|
|
684
|
-
const expandable = !isTitleMode && !isFixedGroupExpanded &&
|
|
736
|
+
const expandable = !isTitleMode && !isFixedGroupExpanded && isOptionFirstInGroup;
|
|
685
737
|
const expanded = option.group?.expanded;
|
|
686
|
-
const expandIconProps =
|
|
738
|
+
const expandIconProps = expandable
|
|
687
739
|
? option.group?.getExpandIconProps?.({
|
|
688
740
|
className: cls('expand-icon'),
|
|
689
741
|
'aria-expanded': expanded,
|
|
@@ -704,7 +756,6 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
704
756
|
const selected = getOptionSelected?.(option);
|
|
705
757
|
const indexInOwnGroup = resultProps.indexInOwnGroup;
|
|
706
758
|
delete resultProps.indexInOwnGroup;
|
|
707
|
-
// * when item is not disabled, that should check outside is that disabled
|
|
708
759
|
if (!resultProps.freeSolo && !resultProps.disabled && getOptionDisabled) {
|
|
709
760
|
resultProps.disabled = getOptionDisabled(option);
|
|
710
761
|
}
|
|
@@ -714,7 +765,7 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
714
765
|
index,
|
|
715
766
|
indexInOwnGroup,
|
|
716
767
|
};
|
|
717
|
-
if (renderGroup &&
|
|
768
|
+
if (renderGroup && isOptionFirstInGroup) {
|
|
718
769
|
return renderGroup(resultProps, {
|
|
719
770
|
...state,
|
|
720
771
|
expanded,
|
|
@@ -725,40 +776,33 @@ const SuggestionList = forwardRef((inProps, ref) => {
|
|
|
725
776
|
return renderOption(resultProps, state);
|
|
726
777
|
}
|
|
727
778
|
const { error, group, freeSolo, key, ...restProps } = resultProps;
|
|
728
|
-
return (
|
|
779
|
+
return (jsx(SuggestionListItem, { ...restProps, id: typeof resultProps.id === 'number'
|
|
729
780
|
? `${resultProps.id}`
|
|
730
|
-
: resultProps.id,
|
|
781
|
+
: 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
782
|
};
|
|
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
783
|
const components = {
|
|
741
784
|
List: List,
|
|
742
|
-
Header: PaddingComponent,
|
|
743
|
-
Footer: PaddingComponent,
|
|
744
785
|
...componentsProp,
|
|
745
786
|
};
|
|
746
787
|
const rootClass = cls('root');
|
|
747
|
-
const context = { listRef, rootClass };
|
|
788
|
+
const context = { listRef, rootClass, paddingValue };
|
|
748
789
|
return virtualize ? (jsx(Virtuoso, { ref: forkVlRef, totalCount: itemCount, data: itemData, context: context, components: components, itemContent: itemContent, ...getSuggestionListProps(), ...combineProps({
|
|
749
790
|
scrollerRef: (scrollElm) => {
|
|
750
791
|
scrollerRefFn(scrollElm);
|
|
751
792
|
},
|
|
752
|
-
itemsRendered,
|
|
753
|
-
totalListHeightChanged,
|
|
793
|
+
itemsRendered: handleItemsRendered,
|
|
794
|
+
totalListHeightChanged: handleTotalListHeightChanged,
|
|
754
795
|
style,
|
|
755
796
|
isScrolling: handleScrolling,
|
|
756
|
-
}, rest) })) : (
|
|
797
|
+
}, rest) })) : (jsx(List, { context: context, style: {
|
|
798
|
+
maxHeight: style.height,
|
|
799
|
+
overflow: 'auto',
|
|
800
|
+
}, ref: (scrollElm) => {
|
|
757
801
|
listRef.current = scrollElm;
|
|
758
802
|
scrollerRefFn(scrollElm);
|
|
759
|
-
}, className: cls('root'), ...getSuggestionListProps(), ...rest, children:
|
|
760
|
-
|
|
761
|
-
|
|
803
|
+
}, className: cls('root'), ...getSuggestionListProps(), ...rest, children: options.map((x, i) => {
|
|
804
|
+
return itemContent(i, x);
|
|
805
|
+
}) }));
|
|
762
806
|
});
|
|
763
807
|
SuggestionList.displayName = 'SuggestionList';
|
|
764
808
|
|
|
@@ -770,7 +814,6 @@ function stringArrToRegExp(keyToTags) {
|
|
|
770
814
|
const TAG_TOKEN = 'rc-chip-';
|
|
771
815
|
const DEFAULT_LIMIT_CHIPS$1 = 20;
|
|
772
816
|
const DEFAULT_KEY_TO_CHIPS = [';', ',', '\\n'];
|
|
773
|
-
/** @inner inner hook work with useAutocomplete */
|
|
774
817
|
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
818
|
const isAutocomplete = variant === 'autocomplete';
|
|
776
819
|
const isSelectedFromAutocompleteRef = useRef(false);
|
|
@@ -810,7 +853,6 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
810
853
|
if (focus)
|
|
811
854
|
_focusTag(tagToFocus);
|
|
812
855
|
});
|
|
813
|
-
/** when return `true` mean add item success */
|
|
814
856
|
const checkAndAddFreeSolo = ({ selectedItem, value, } = {}, e) => {
|
|
815
857
|
let isAddItem = false;
|
|
816
858
|
const currentValue = value ?? inputRef.current?.value;
|
|
@@ -869,7 +911,6 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
869
911
|
onAddTags?.();
|
|
870
912
|
};
|
|
871
913
|
const addTagOnSelect = (selectedItem) => {
|
|
872
|
-
// * change to uniqueId
|
|
873
914
|
if (selectedItem.freeSolo) {
|
|
874
915
|
if (freeSoloCount < maxFreeSolo) {
|
|
875
916
|
selectedItem.id = uniqueId(TAG_TOKEN);
|
|
@@ -895,32 +936,20 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
895
936
|
event?.preventDefault();
|
|
896
937
|
},
|
|
897
938
|
});
|
|
898
|
-
/**
|
|
899
|
-
* All Get props methods
|
|
900
|
-
*/
|
|
901
939
|
const getTagListBoxProps = (props) => {
|
|
902
940
|
return {
|
|
903
941
|
'aria-required': required || false,
|
|
904
942
|
'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
943
|
...props,
|
|
910
944
|
};
|
|
911
945
|
};
|
|
912
946
|
const getTagProps = ({ index, selectedItem, ...restTagProps }) => {
|
|
913
|
-
const tabIndex =
|
|
914
|
-
// * when tag is not focus
|
|
915
|
-
!focused &&
|
|
916
|
-
// * and is first item
|
|
947
|
+
const tabIndex = !focused &&
|
|
917
948
|
index === 0
|
|
918
949
|
? 0
|
|
919
950
|
: -1;
|
|
920
951
|
return combineProps({
|
|
921
952
|
tabIndex,
|
|
922
|
-
// TODO: wait for input can complete that.
|
|
923
|
-
// role: 'option',
|
|
924
953
|
role: 'button',
|
|
925
954
|
key: `${AutocompleteId}-selected-item-${index}`,
|
|
926
955
|
'data-tag-index': index,
|
|
@@ -939,9 +968,7 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
939
968
|
case 'Backspace':
|
|
940
969
|
{
|
|
941
970
|
const lengthBeforeDelete = tags.length;
|
|
942
|
-
focusTag(
|
|
943
|
-
// * when length is 1 mean that will be remove all
|
|
944
|
-
lengthBeforeDelete === 1
|
|
971
|
+
focusTag(lengthBeforeDelete === 1
|
|
945
972
|
? -1
|
|
946
973
|
: focusedIndex === 0
|
|
947
974
|
? 0
|
|
@@ -952,9 +979,7 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
952
979
|
case 'Delete':
|
|
953
980
|
{
|
|
954
981
|
const lengthBeforeDelete = tags.length;
|
|
955
|
-
focusTag(
|
|
956
|
-
// * when length is 1 mean that will be remove all
|
|
957
|
-
lengthBeforeDelete === 1
|
|
982
|
+
focusTag(lengthBeforeDelete === 1
|
|
958
983
|
? -1
|
|
959
984
|
: focusedIndex === tags.length - 1
|
|
960
985
|
? focusedIndex - 1
|
|
@@ -968,7 +993,6 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
968
993
|
}
|
|
969
994
|
},
|
|
970
995
|
onDelete: (e) => {
|
|
971
|
-
// * only allow left button click
|
|
972
996
|
if (e.button !== 0)
|
|
973
997
|
return;
|
|
974
998
|
removeTag(selectedItem);
|
|
@@ -1009,7 +1033,7 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
|
|
|
1009
1033
|
const EMPTY_TAG_TOKEN = 'rc-chip-empty';
|
|
1010
1034
|
const DEFAULT_HIGHLIGHTED_INDEX = -1;
|
|
1011
1035
|
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, }) => {
|
|
1036
|
+
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
1037
|
const isAutocomplete = variant === 'autocomplete';
|
|
1014
1038
|
const autoCompleteSelectedIndexRef = useRef(DEFAULT_HIGHLIGHTED_INDEX);
|
|
1015
1039
|
const AutocompleteId = useId('Autocomplete', true);
|
|
@@ -1031,7 +1055,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1031
1055
|
const keepHighlightedIndexRef = useRef(false);
|
|
1032
1056
|
const popperElementRef = useRef(null);
|
|
1033
1057
|
const { sleep, getSleeping } = useSleep();
|
|
1034
|
-
// * when that is autocomplete, that will never be multiple
|
|
1035
1058
|
const multiple = isAutocomplete ? false : multipleProp;
|
|
1036
1059
|
const inputContainerScrollToBottom = () => {
|
|
1037
1060
|
requestAnimationFrame(() => {
|
|
@@ -1057,7 +1080,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1057
1080
|
multiple,
|
|
1058
1081
|
required,
|
|
1059
1082
|
disabled,
|
|
1060
|
-
// * when is composition, stop freeSolo create new item
|
|
1061
1083
|
getStopCreateFreeSolo: () => isCompositionRef.current,
|
|
1062
1084
|
onReset: () => {
|
|
1063
1085
|
resetState();
|
|
@@ -1076,7 +1098,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1076
1098
|
}
|
|
1077
1099
|
return null;
|
|
1078
1100
|
};
|
|
1079
|
-
// * only when isOpen calculate the filtered result
|
|
1080
1101
|
noOptionItemRef.current = null;
|
|
1081
1102
|
if (addNoOptionItem) {
|
|
1082
1103
|
const item = getInputValueAsItem();
|
|
@@ -1093,9 +1114,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1093
1114
|
else if (results.length === 0) {
|
|
1094
1115
|
const item = getInputValueAsItem();
|
|
1095
1116
|
noOptionItemRef.current = item;
|
|
1096
|
-
// * only when not set render no options will auto add item
|
|
1097
1117
|
if (item && freeSolo && !renderNoOptions) {
|
|
1098
|
-
// * when outside not use filter, that will be original array, must use spread
|
|
1099
1118
|
return [...results, item];
|
|
1100
1119
|
}
|
|
1101
1120
|
}
|
|
@@ -1130,6 +1149,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1130
1149
|
groupBy,
|
|
1131
1150
|
onGroupExpanded,
|
|
1132
1151
|
getExpandIconProps,
|
|
1152
|
+
isOptionEqualToValue,
|
|
1133
1153
|
onSelect: (e, selectedItem) => {
|
|
1134
1154
|
addTagOnSelect(selectedItem);
|
|
1135
1155
|
resetState(e);
|
|
@@ -1137,10 +1157,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1137
1157
|
if (autoHighlight) {
|
|
1138
1158
|
const currIndex = highlightedIndexRef.current;
|
|
1139
1159
|
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 ||
|
|
1160
|
+
const toIndex = nextValidIndex > currIndex ||
|
|
1144
1161
|
nextValidIndex === optionItems.length - 1
|
|
1145
1162
|
? nextValidIndex - 1
|
|
1146
1163
|
: nextValidIndex;
|
|
@@ -1155,23 +1172,12 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1155
1172
|
},
|
|
1156
1173
|
});
|
|
1157
1174
|
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
|
-
});
|
|
1175
|
+
return getSuggestionListItemProps(props);
|
|
1169
1176
|
};
|
|
1170
1177
|
const readOnly = !isAutocomplete && !multiple && tags.length >= 1 ? true : undefined;
|
|
1171
1178
|
const closeMenu = (e, reason) => {
|
|
1172
1179
|
keepHighlightedIndexRef.current = false;
|
|
1173
1180
|
isInputValueChangedRef.current = false;
|
|
1174
|
-
// * set to default highlighted
|
|
1175
1181
|
setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
|
|
1176
1182
|
if (isOpen) {
|
|
1177
1183
|
if (e && reason) {
|
|
@@ -1200,8 +1206,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1200
1206
|
const handleInputChange = (newValue, open = true, e) => {
|
|
1201
1207
|
if (inputValue !== newValue) {
|
|
1202
1208
|
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
1209
|
if (stringArrToRegExp(keyToTags).test((newValue || '').trim())) {
|
|
1206
1210
|
const resultHaveAdd = checkAndAddFreeSolo({ value: newValue }, e);
|
|
1207
1211
|
if (!disableCloseOnSelect) {
|
|
@@ -1219,7 +1223,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1219
1223
|
return false;
|
|
1220
1224
|
};
|
|
1221
1225
|
const resetState = (e) => {
|
|
1222
|
-
// * when autocomplete select mode not reset input value
|
|
1223
1226
|
if (!isSelectedFromAutocompleteRef.current) {
|
|
1224
1227
|
clearInput();
|
|
1225
1228
|
}
|
|
@@ -1239,18 +1242,15 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1239
1242
|
const keepHighlightedIndex = () => {
|
|
1240
1243
|
keepHighlightedIndexRef.current = true;
|
|
1241
1244
|
};
|
|
1242
|
-
// * when outside options props props change also need set highlightedIndex to default
|
|
1243
1245
|
useChange(() => {
|
|
1244
1246
|
const shouldResetHighlightedIndex = !keepHighlightedIndexRef.current;
|
|
1245
1247
|
if (options !== undefined && shouldResetHighlightedIndex) {
|
|
1246
1248
|
setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
|
|
1247
1249
|
}
|
|
1248
1250
|
}, () => options);
|
|
1249
|
-
// * check that defaultHighlightedIndex
|
|
1250
1251
|
const defaultHighlightedIndex = useMemo(() => {
|
|
1251
1252
|
return autoHighlight ? getNextFocusableOption(0) : -1;
|
|
1252
1253
|
}, [autoHighlight, getNextFocusableOption]);
|
|
1253
|
-
// * only when menu is open set to defaultHighlightedIndex
|
|
1254
1254
|
if (isOpen) {
|
|
1255
1255
|
if (highlightedIndexRef.current === DEFAULT_HIGHLIGHTED_INDEX) {
|
|
1256
1256
|
let toIndex = defaultHighlightedIndex;
|
|
@@ -1283,13 +1283,11 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1283
1283
|
onInputChangeProp?.(result);
|
|
1284
1284
|
}
|
|
1285
1285
|
};
|
|
1286
|
-
// * use force update to sync open state when init is open
|
|
1287
1286
|
useLayoutEffect(() => {
|
|
1288
1287
|
if (isOpen) {
|
|
1289
1288
|
forceUpdate();
|
|
1290
1289
|
}
|
|
1291
1290
|
handleAutocompleteText();
|
|
1292
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1293
1291
|
}, []);
|
|
1294
1292
|
const getInputProps = (props) => {
|
|
1295
1293
|
const suggestionListInputProps = getSuggestionListInputProps(props);
|
|
@@ -1301,9 +1299,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1301
1299
|
const { position } = getSelectionPosition(inputRef);
|
|
1302
1300
|
const currText = inputValue ?? '';
|
|
1303
1301
|
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
1302
|
fromPasteString.current =
|
|
1308
1303
|
currText.slice(0, position.start) +
|
|
1309
1304
|
pasteText +
|
|
@@ -1313,7 +1308,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1313
1308
|
onChange: (e) => {
|
|
1314
1309
|
const changeValue = fromPasteString.current || e.target.value;
|
|
1315
1310
|
handleInputChange(changeValue, undefined, e);
|
|
1316
|
-
// * when input value clear all, clear selected item
|
|
1317
1311
|
if (isAutocomplete && changeValue.length === 0) {
|
|
1318
1312
|
clearTags();
|
|
1319
1313
|
}
|
|
@@ -1324,18 +1318,14 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1324
1318
|
setInputFocused(true);
|
|
1325
1319
|
if (openOnFocus)
|
|
1326
1320
|
openMenu(e);
|
|
1327
|
-
// * when autocomplete mode, always select all text when focus
|
|
1328
1321
|
if (isAutocomplete) {
|
|
1329
1322
|
inputRef.current?.select();
|
|
1330
1323
|
}
|
|
1331
|
-
// * reset stopAutoSelect in focus input
|
|
1332
1324
|
stopAutoSelectRef.current = false;
|
|
1333
1325
|
blurTags();
|
|
1334
1326
|
},
|
|
1335
1327
|
onBlur: (e) => {
|
|
1336
1328
|
setInputFocused(false);
|
|
1337
|
-
// mouse down suggestion item will change focus,
|
|
1338
|
-
// but we don't want to close popper in this case
|
|
1339
1329
|
if (popperElementRef.current?.contains(e.relatedTarget))
|
|
1340
1330
|
return;
|
|
1341
1331
|
if (autoSelect && !stopAutoSelectRef.current) {
|
|
@@ -1343,7 +1333,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1343
1333
|
selectItem(e, optionItems[highlightedIndexRef.current]);
|
|
1344
1334
|
checkAndAddFreeSolo({}, e);
|
|
1345
1335
|
}
|
|
1346
|
-
// * when blur should restore text when have selected item
|
|
1347
1336
|
handleAutocompleteText();
|
|
1348
1337
|
closeMenu(e, 'blur');
|
|
1349
1338
|
},
|
|
@@ -1351,7 +1340,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1351
1340
|
isCompositionRef.current = true;
|
|
1352
1341
|
},
|
|
1353
1342
|
onCompositionEnd: async () => {
|
|
1354
|
-
// sleep for prevent tab in composition
|
|
1355
1343
|
await sleep(20);
|
|
1356
1344
|
isCompositionRef.current = false;
|
|
1357
1345
|
},
|
|
@@ -1372,9 +1360,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1372
1360
|
switch (e.key) {
|
|
1373
1361
|
case 'Escape':
|
|
1374
1362
|
if (isOpen) {
|
|
1375
|
-
// Avoid Opera to exit fullscreen mode.
|
|
1376
1363
|
e.preventDefault();
|
|
1377
|
-
// Avoid the Modal to handle the event.
|
|
1378
1364
|
e.stopPropagation();
|
|
1379
1365
|
closeMenu(e, 'escape');
|
|
1380
1366
|
}
|
|
@@ -1387,13 +1373,9 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1387
1373
|
e.stopPropagation();
|
|
1388
1374
|
return;
|
|
1389
1375
|
}
|
|
1390
|
-
// * when shift+ tab also not need autoSelect
|
|
1391
1376
|
if (e.shiftKey && currTagsLength > 0) {
|
|
1392
1377
|
stopAutoSelectRef.current = true;
|
|
1393
1378
|
}
|
|
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
1379
|
if (!e.shiftKey && !isCompositionRef.current) {
|
|
1398
1380
|
const currentHighlightItem = optionItems[highlightedIndex];
|
|
1399
1381
|
if (isOpen && currentHighlightItem) {
|
|
@@ -1427,7 +1409,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1427
1409
|
selectItem(e, optionItems[highlightedIndex]);
|
|
1428
1410
|
e.stopPropagation();
|
|
1429
1411
|
}
|
|
1430
|
-
// always preventDefault for not inset all enter into textarea
|
|
1431
1412
|
e.preventDefault();
|
|
1432
1413
|
break;
|
|
1433
1414
|
case 'F10':
|
|
@@ -1439,7 +1420,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1439
1420
|
e.preventDefault();
|
|
1440
1421
|
return;
|
|
1441
1422
|
}
|
|
1442
|
-
// * check state for is that should move key in option items
|
|
1443
1423
|
if (isAutocomplete && tags.length > 0 && !isOpen) {
|
|
1444
1424
|
openMenu(e);
|
|
1445
1425
|
e.preventDefault();
|
|
@@ -1458,15 +1438,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1458
1438
|
},
|
|
1459
1439
|
onMouseDown: toggleWithInput
|
|
1460
1440
|
? (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
1441
|
if (!isOpen || (isOpen && !inputValue)) {
|
|
1471
1442
|
toggleMenu(e, false);
|
|
1472
1443
|
}
|
|
@@ -1559,7 +1530,6 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
|
|
|
1559
1530
|
reset,
|
|
1560
1531
|
forceUpdate,
|
|
1561
1532
|
optionsGroupList,
|
|
1562
|
-
/** that state of current Autocomplete focused, both tag or input focused */
|
|
1563
1533
|
focused: focused ?? (tagFocused || inputFocused ? true : undefined),
|
|
1564
1534
|
id: AutocompleteId,
|
|
1565
1535
|
inputChanged: isInputValueChangedRef.current,
|
|
@@ -1574,9 +1544,7 @@ const ChipSizeMap = {
|
|
|
1574
1544
|
medium: 'small',
|
|
1575
1545
|
};
|
|
1576
1546
|
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 })] }));
|
|
1547
|
+
return (jsxs("div", { ref: containerRef, className: containerClassName, onClick: onContainerClick, children: [startAdornment, jsx("input", { ...rest, ref: ref })] }));
|
|
1580
1548
|
});
|
|
1581
1549
|
const TextInput = forwardRef(({ renderInput, ...rest }, ref) => {
|
|
1582
1550
|
if (renderInput) {
|
|
@@ -1587,14 +1555,7 @@ const TextInput = forwardRef(({ renderInput, ...rest }, ref) => {
|
|
|
1587
1555
|
const Autocomplete = forwardRef((inProps, ref) => {
|
|
1588
1556
|
const props = useThemeProps({ props: inProps, name: 'Autocomplete' });
|
|
1589
1557
|
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;
|
|
1558
|
+
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
1559
|
const cls = useClasses('autocomplete', classes, className);
|
|
1599
1560
|
const innerInputRef = useRef(null);
|
|
1600
1561
|
const textFieldRef = useRef(null);
|
|
@@ -1605,12 +1566,7 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1605
1566
|
const anchorElRef = anchorElType === 'input' ? formFieldContentRef : textFieldRef;
|
|
1606
1567
|
let oneOfTagError = false;
|
|
1607
1568
|
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({
|
|
1569
|
+
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
1570
|
focused,
|
|
1615
1571
|
open: openProp,
|
|
1616
1572
|
addNoOptionItem,
|
|
@@ -1650,6 +1606,7 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1650
1606
|
onKeyDown: onKeyDownProp,
|
|
1651
1607
|
disabledItemsHighlightable,
|
|
1652
1608
|
onGroupExpanded,
|
|
1609
|
+
isOptionEqualToValue,
|
|
1653
1610
|
});
|
|
1654
1611
|
const open = Boolean((isOpen || noOptionItem) && textFieldRef.current);
|
|
1655
1612
|
const { onBlur, ...InputProps } = getInputProps(inputProps);
|
|
@@ -1681,7 +1638,6 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1681
1638
|
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
1639
|
...ToggleButtonProps,
|
|
1683
1640
|
...getToggleButtonPropsProp?.(open),
|
|
1684
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1685
1641
|
}) }))] }));
|
|
1686
1642
|
const TextFieldInputProps = {
|
|
1687
1643
|
'aria-label': typeof label === 'string' ? label : undefined,
|
|
@@ -1704,32 +1660,14 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1704
1660
|
}));
|
|
1705
1661
|
const hasTags = selectedItems.length > 0;
|
|
1706
1662
|
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 &&
|
|
1663
|
+
const prevHighlightedIndex = usePrevious(() => highlightedIndex, true);
|
|
1664
|
+
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
1665
|
renderNoOptions?.(getNoOptionsProps, noOptionItem)] }));
|
|
1724
1666
|
const isPopperContentEmpty = !isOpen || (!willRenderNoOptions && optionItems.length === 0);
|
|
1725
1667
|
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)
|
|
1668
|
+
return (jsxs(Fragment, { children: [jsx(TextInput, { renderInput: renderInput, variant: inputVariant, ref: toTextFieldRef, inputRef: inputRef, fullWidth: fullWidth, placeholder: !hasTags || (hasTags && multiple && showPlaceholderWithTags)
|
|
1729
1669
|
? 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({
|
|
1670
|
+
: undefined, label: label, disabled: disabled, required: required, error: error ?? oneOfTagError, FormHelperTextProps: FormHelperTextProps, inputComponent: InputComponent, endAdornment: endAdornment, helperText: helperText, size: size, ...combineProps({
|
|
1733
1671
|
RootProps: {
|
|
1734
1672
|
contentRef: formFieldContentRef,
|
|
1735
1673
|
},
|
|
@@ -1752,16 +1690,16 @@ const Autocomplete = forwardRef((inProps, ref) => {
|
|
|
1752
1690
|
}), children: menuChildren }) })) })] }));
|
|
1753
1691
|
});
|
|
1754
1692
|
Autocomplete.displayName = 'SuiAutocomplete';
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
getOptionLabel?.(item)
|
|
1693
|
+
const RcAutocompleteDefaultFilterOptions = (options, { inputValue, getOptionLabel, selectedItems, isOptionEqualToValue }) => {
|
|
1694
|
+
return options.filter((item) => {
|
|
1695
|
+
const isSelected = selectedItems.some((selected) => isOptionEqualToValue(item, selected));
|
|
1696
|
+
if (isSelected)
|
|
1697
|
+
return false;
|
|
1698
|
+
const optionLabel = getOptionLabel?.(item) || '';
|
|
1699
|
+
return optionLabel
|
|
1763
1700
|
.toLowerCase()
|
|
1764
|
-
.startsWith(inputValue?.toLowerCase() || '')
|
|
1701
|
+
.startsWith(inputValue?.toLowerCase() || '');
|
|
1702
|
+
});
|
|
1765
1703
|
};
|
|
1766
1704
|
|
|
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 };
|
|
1705
|
+
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 };
|