@ringcentral/spring-ui 1.11.2 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/Accordion.js +90 -34
  2. package/AccordionHeader.js +72 -20
  3. package/Alert.js +120 -28
  4. package/Animation.js +2 -45
  5. package/Announcement.js +46 -35
  6. package/Autocomplete.js +1011 -580
  7. package/Avatar.js +134 -73
  8. package/AvatarGroup.js +50 -7
  9. package/Backdrop.js +45 -7
  10. package/Badge.js +77 -15
  11. package/Block.js +34 -12
  12. package/BlockHeader.js +55 -11
  13. package/BottomNavigation.js +181 -65
  14. package/BottomNavigationItem.js +93 -30
  15. package/Button.js +160 -24
  16. package/CallButton.js +53 -31
  17. package/Checkbox.js +88 -26
  18. package/Chip.js +126 -26
  19. package/CircularProgressIndicator.js +80 -8
  20. package/ClickAwayListener.js +4 -1
  21. package/ComponentsProps.js +1 -0
  22. package/DatePicker.js +611 -190
  23. package/DateUtilsContext.js +43 -25
  24. package/Dialer.js +576 -250
  25. package/Dialog.js +100 -45
  26. package/Divider.js +43 -7
  27. package/DragHandle.js +32 -6
  28. package/Drawer.js +117 -19
  29. package/Dropdown.js +154 -74
  30. package/EmptyState.js +63 -11
  31. package/FabButton.js +51 -17
  32. package/FocusTrap.js +109 -12
  33. package/FormField.js +106 -8
  34. package/FormLabel.js +81 -7
  35. package/Icon.js +1 -8
  36. package/IconButton.js +142 -26
  37. package/IconButtonGroup.js +35 -12
  38. package/InlineEditable.js +98 -44
  39. package/LinearProgressIndicator.js +73 -24
  40. package/Link.js +40 -11
  41. package/List.js +54 -9
  42. package/ListItem.js +152 -23
  43. package/ListItemText.js +44 -16
  44. package/ListNavigation.js +272 -20
  45. package/Menu.js +139 -23
  46. package/MenuActions.js +37 -12
  47. package/MenuDivider.js +34 -8
  48. package/MenuFooter.js +36 -12
  49. package/MenuHeader.js +54 -15
  50. package/MenuItem.js +402 -33
  51. package/MenuItemText.js +51 -8
  52. package/MenuList.js +87 -34
  53. package/Modal.js +345 -16
  54. package/NestedMenuContext.js +183 -11
  55. package/Option.js +32 -34
  56. package/Overlap.js +54 -7
  57. package/PageHeader.js +62 -24
  58. package/PickerTextField.js +156 -33
  59. package/Popover.js +262 -21
  60. package/Popper.js +130 -77
  61. package/PopperPaper.js +54 -8
  62. package/Portal.js +6 -1
  63. package/Radio.js +65 -21
  64. package/RadioGroup.js +56 -17
  65. package/Rating.js +223 -87
  66. package/Select.js +387 -41
  67. package/SingleFilter.js +148 -77
  68. package/Skeleton.js +68 -17
  69. package/Slider.js +168 -69
  70. package/Snackbar.js +122 -59
  71. package/SortableList.js +32 -12
  72. package/SortableListDndContext.js +69 -22
  73. package/SortableListItem.js +92 -47
  74. package/SortableVirtualizedList.js +52 -28
  75. package/SplitButton.js +124 -86
  76. package/Squircle.js +4 -7
  77. package/StatusIndicator.js +51 -9
  78. package/Step.js +63 -38
  79. package/StepButton.js +51 -23
  80. package/StepConnector.js +48 -8
  81. package/StepContent.js +68 -25
  82. package/StepIcon.js +90 -9
  83. package/StepLabel.js +90 -14
  84. package/Stepper.js +65 -23
  85. package/SubMenu.js +203 -99
  86. package/SwipeableDrawer.js +439 -194
  87. package/Switch.js +70 -23
  88. package/Tab.js +120 -57
  89. package/TabContext.js +59 -15
  90. package/TabPanel.js +40 -13
  91. package/Table.js +60 -17
  92. package/TableBody.js +42 -12
  93. package/TableCell.js +84 -50
  94. package/TableContainer.js +32 -10
  95. package/TableFooter.js +38 -10
  96. package/TableHead.js +49 -15
  97. package/TablePagination.js +153 -77
  98. package/TableRow.js +60 -21
  99. package/Tabs.js +593 -268
  100. package/Tag.js +51 -16
  101. package/Text.js +68 -37
  102. package/TextField.js +209 -29
  103. package/Textarea.js +51 -40
  104. package/TimePicker.js +490 -230
  105. package/Tooltip.js +48 -23
  106. package/Tray.js +237 -13
  107. package/TrayMenu.js +62 -41
  108. package/VirtualizedList.js +208 -109
  109. package/VirtualizedMenu.js +359 -29
  110. package/VisuallyHidden.js +29 -6
  111. package/chunks/1250.js +98 -0
  112. package/chunks/1294.js +126 -0
  113. package/chunks/2956.js +18 -0
  114. package/chunks/4476.js +460 -0
  115. package/chunks/4491.js +10 -0
  116. package/chunks/4535.js +364 -0
  117. package/chunks/5047.js +3 -0
  118. package/chunks/6335.js +24 -0
  119. package/chunks/7279.js +10 -0
  120. package/chunks/8011.js +8 -0
  121. package/chunks/8307.js +56 -0
  122. package/chunks/8467.js +8 -0
  123. package/chunks/8809.js +8 -0
  124. package/chunks/{RadioGroup.context.js → 9551.js} +12 -6
  125. package/chunks/987.js +8 -0
  126. package/index.css +2624 -2416
  127. package/index.js +245 -193
  128. package/internal/utils/index.d.ts +1 -0
  129. package/internal/utils/setRef.d.ts +2 -0
  130. package/package.json +8 -8
  131. package/src/Alert/Alert.type.d.ts +2 -1
  132. package/src/Animation/components/Fade/Fade.d.ts +4 -2
  133. package/src/Animation/components/Grow/Grow.d.ts +4 -2
  134. package/src/Animation/components/ShowHideEye/ShowHideEye.d.ts +1 -1
  135. package/src/Animation/components/SlideIn/SlideIn.d.ts +4 -2
  136. package/src/Animation/components/SlideIn/getSlideIn.d.ts +1 -1
  137. package/src/Animation/hooks/useMotionSafeTransition/useMotionSafeTransition.d.ts +2 -0
  138. package/src/Autocomplete/SuggestionList/SuggestionList.d.ts +1 -1
  139. package/src/Autocomplete/useAutocomplete.d.ts +18 -18
  140. package/src/Autocomplete/useAutocompleteTag.d.ts +18 -18
  141. package/src/Badge/Badge.d.ts +2 -1
  142. package/src/Button/Button.classes.d.ts +1 -1
  143. package/src/ComponentsProps.d.ts +111 -110
  144. package/src/DatePicker/DatePickerDataAttributes.d.ts +11 -0
  145. package/src/DatePicker/utils/DatePickerAriaLabelUtils.d.ts +2 -1
  146. package/src/DatePicker/utils/DatePickerUtils.d.ts +1 -1
  147. package/src/Dialer/DialDelete/DialDelete.d.ts +2 -2
  148. package/src/Dialer/DialPad/DialPad.d.ts +2 -2
  149. package/src/Dialer/Dialer.d.ts +4 -2
  150. package/src/Dropdown/Dropdown.type.d.ts +2 -2
  151. package/src/InlineEditable/InlineEditable.type.d.ts +2 -1
  152. package/src/ListNavigation/useListNavigation.d.ts +3 -3
  153. package/src/MenuActions/MenuActions.d.ts +2 -2
  154. package/src/MenuItem/MenuItem.type.d.ts +1 -0
  155. package/src/MenuItem/useMenuItem.d.ts +1 -1
  156. package/src/MenuItem/useVirtualizedMenuItem.d.ts +1 -1
  157. package/src/MenuList/MenuList.type.d.ts +1 -1
  158. package/src/MenuList/MenuListContext.d.ts +2 -3
  159. package/src/Modal/Modal.type.d.ts +1 -0
  160. package/src/NestedMenuContext/MenuOverlayGroupContext.d.ts +2 -2
  161. package/src/Popover/Popover.classes.d.ts +1 -1
  162. package/src/Popover/Popover.internal.d.ts +8 -0
  163. package/src/Popover/Popover.type.d.ts +1 -0
  164. package/src/Popper/Popper.type.d.ts +10 -7
  165. package/src/Rating/utils.d.ts +3 -2
  166. package/src/Select/Select.type.d.ts +7 -5
  167. package/src/Slider/Slider.d.ts +2 -2
  168. package/src/Slider/SliderThumb.d.ts +2 -1
  169. package/src/Squircle/Squircle.d.ts +1 -1
  170. package/src/StatusIndicator/StatusIndicator.d.ts +2 -1
  171. package/src/SubMenu/PopperMenu.type.d.ts +4 -1
  172. package/src/Table/Table.type.d.ts +3 -0
  173. package/src/Table/TableContext.d.ts +3 -0
  174. package/src/Table/TableRowContext.d.ts +7 -0
  175. package/src/Table/index.d.ts +1 -0
  176. package/src/TableCell/TableCell.classes.d.ts +1 -1
  177. package/src/TableFooter/TableFooter.classes.d.ts +1 -1
  178. package/src/TableFooter/TableFooter.type.d.ts +1 -0
  179. package/src/TableHead/TableHead.classes.d.ts +1 -1
  180. package/src/TableHead/TableHead.type.d.ts +1 -0
  181. package/src/TableRow/TableRow.type.d.ts +2 -0
  182. package/chunks/AccordionContext.js +0 -6
  183. package/chunks/Alert.js +0 -48
  184. package/chunks/AvatarGroup.js +0 -22
  185. package/chunks/Backdrop.js +0 -18
  186. package/chunks/Badge.js +0 -32
  187. package/chunks/BottomNavigationItem.js +0 -36
  188. package/chunks/Button.js +0 -88
  189. package/chunks/Chip.js +0 -73
  190. package/chunks/CircularProgressIndicator.js +0 -36
  191. package/chunks/Divider.js +0 -19
  192. package/chunks/DragHandle.js +0 -11
  193. package/chunks/Drawer.js +0 -32
  194. package/chunks/ExpandCollapseCaret.js +0 -24
  195. package/chunks/FormLabel.js +0 -27
  196. package/chunks/Icon.js +0 -25
  197. package/chunks/IconButtonLabel.js +0 -68
  198. package/chunks/List.js +0 -22
  199. package/chunks/ListItem.js +0 -83
  200. package/chunks/Menu.js +0 -61
  201. package/chunks/MenuDivider.js +0 -14
  202. package/chunks/MenuItemText.js +0 -17
  203. package/chunks/MenuList.js +0 -46
  204. package/chunks/Modal.js +0 -212
  205. package/chunks/Overlap.js +0 -25
  206. package/chunks/PickerTextField.js +0 -85
  207. package/chunks/Popover.js +0 -53
  208. package/chunks/PopperPaper.js +0 -15
  209. package/chunks/Select.js +0 -220
  210. package/chunks/ShowHideEye.js +0 -56
  211. package/chunks/SlideIn.js +0 -19
  212. package/chunks/SortableArrow.js +0 -73
  213. package/chunks/Squircle.js +0 -31
  214. package/chunks/StatusIndicator.js +0 -20
  215. package/chunks/StepConnector.js +0 -21
  216. package/chunks/StepIcon.js +0 -43
  217. package/chunks/StepLabel.js +0 -29
  218. package/chunks/StepperContext.js +0 -9
  219. package/chunks/TableContext.js +0 -6
  220. package/chunks/Tablelvl2Context.js +0 -6
  221. package/chunks/TabsContext.js +0 -6
  222. package/chunks/TextField.js +0 -110
  223. package/chunks/Tooltip.js +0 -217
  224. package/chunks/VirtualizedMenu.js +0 -164
  225. package/chunks/VisuallyHidden.js +0 -12
  226. package/chunks/getFade.js +0 -27
  227. package/chunks/getGrow.js +0 -70
  228. package/chunks/getSlideIn.js +0 -30
  229. package/chunks/hasContent.js +0 -3
  230. package/chunks/useAnimationFrame.js +0 -15
  231. package/chunks/useFocusTrapStack.js +0 -36
  232. package/chunks/useHelperTextId.js +0 -22
  233. package/chunks/useListNavigation.js +0 -199
  234. package/chunks/useMenuItemSelect.js +0 -273
  235. package/chunks/useMotionSafeTransition.js +0 -9
  236. package/chunks/useNestedMenuItem.js +0 -27
  237. package/chunks/useNestedParentMenu.js +0 -113
  238. package/chunks/usePrefersReducedMotion.js +0 -36
  239. package/chunks/useTooltipForceHide.js +0 -27
  240. package/chunks/useTrayNavigate.js +0 -150
  241. package/chunks/withTooltip.js +0 -15
package/Autocomplete.js CHANGED
@@ -1,78 +1,119 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import { forwardRef, useRef, useMemo, useCallback, useEffect, useLayoutEffect, useState, useImperativeHandle } from 'react';
3
- import clsx from 'clsx';
4
- import { Virtuoso } from 'react-virtuoso';
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
- import { E as ExpandCollapseCaret } from './chunks/ExpandCollapseCaret.js';
8
- import { AnimatePresence, motion } from 'framer-motion';
9
- import './chunks/Icon.js';
10
- import { I as IconButton } from './chunks/IconButtonLabel.js';
11
- import { Xsm } from '@ringcentral/spring-icon';
12
- import { u as usePrefersReducedMotion } from './chunks/usePrefersReducedMotion.js';
13
- import { g as getFade } from './chunks/getFade.js';
14
- import { C as Chip } from './chunks/Chip.js';
15
- import { Popper } from './Popper.js';
16
- import { T as TextField } from './chunks/TextField.js';
17
- import { u as useAnimationFrame } from './chunks/useAnimationFrame.js';
18
- import uniqueId from 'lodash/uniqueId';
19
- import './chunks/useMotionSafeTransition.js';
20
- import '@mui/base';
21
- import './chunks/Squircle.js';
22
- import './chunks/withTooltip.js';
23
- import './chunks/Tooltip.js';
24
- import './chunks/PopperPaper.js';
25
- import '@floating-ui/react-dom';
26
- import './chunks/Modal.js';
27
- import './chunks/useFocusTrapStack.js';
28
- import './chunks/Backdrop.js';
29
- import './chunks/ShowHideEye.js';
30
- import './chunks/useHelperTextId.js';
31
-
32
- 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"};
33
-
34
- const SuggestionListItem = forwardRef((props, ref) => {
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
3
+ import clsx from "clsx";
4
+ import { Virtuoso } from "react-virtuoso";
5
+ import { combineProps, getSelectionPosition, logInDev, useChange, useClasses, useControlled, useDepsChange, useEventCallback, useForceUpdate, useForkRef, useId, useKeyboardMoveFocus, usePrevious, useRefState, useRetry, useSleep, useVirtuosoExternalWindow } from "@ringcentral/spring-base";
6
+ import { useClassesWithMode, useThemeProps } from "@ringcentral/spring-theme";
7
+ import { AnimatePresence, motion } from "framer-motion";
8
+ import { Xsm } from "@ringcentral/spring-icon";
9
+ import uniqueId from "lodash/uniqueId";
10
+ import { getFade } from "./chunks/4476.js";
11
+ import { IconButton as IconButton_IconButton } from "./IconButton.js";
12
+ import { useAnimationFrame } from "./chunks/6335.js";
13
+ import { Chip as Chip_Chip } from "./Chip.js";
14
+ import { Popper } from "./Popper.js";
15
+ import { TextField as TextField_TextField } from "./TextField.js";
16
+ import { usePrefersReducedMotion, ExpandCollapseCaret as ExpandCollapseCaret_ExpandCollapseCaret } from "./chunks/1294.js";
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+ // extracted by css-extract-rspack-plugin
25
+ /* export default */ var SuggestionList = ({"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"});
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+ const SuggestionListItem = /*#__PURE__*/ forwardRef((props, ref)=>{
35
35
  const { id, label, children, disabled, isGroupTitle = false, isTitleMode = false, highlighted = false, selected = false, expanded = false, expandIconProps, classes, className, role, ...rest } = props;
36
- const cls = useClassesWithMode('suggestion-list', classes, className, ['item']);
36
+ const cls = useClassesWithMode('suggestion-list', classes, className, [
37
+ 'item'
38
+ ]);
37
39
  const readonly = isGroupTitle && isTitleMode;
38
40
  const ariaDisabled = role !== undefined && disabled ? true : undefined;
39
- return (jsxs("div", { "aria-disabled": ariaDisabled, role: role, ...rest, id: id, className: cls('item', {
41
+ return /*#__PURE__*/ jsxs("div", {
42
+ "aria-disabled": ariaDisabled,
43
+ role: role,
44
+ ...rest,
45
+ id: id,
46
+ className: cls('item', {
40
47
  'group-title': isGroupTitle,
41
48
  readonly,
42
49
  'group-title-readonly': readonly,
43
50
  highlighted: highlighted,
44
51
  selected: selected,
45
- disabled,
46
- }), 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 }) }))] }));
52
+ disabled
53
+ }),
54
+ children: [
55
+ label || children,
56
+ expandIconProps && /*#__PURE__*/ jsx(IconButton_IconButton, {
57
+ "aria-hidden": "true",
58
+ role: "presentation",
59
+ tabIndex: -1,
60
+ component: "div",
61
+ shape: 'squircle',
62
+ background: false,
63
+ color: 'secondary',
64
+ variant: "contained",
65
+ size: "small",
66
+ ...expandIconProps,
67
+ children: /*#__PURE__*/ jsx(ExpandCollapseCaret_ExpandCollapseCaret, {
68
+ expanded: expanded
69
+ })
70
+ })
71
+ ]
72
+ });
47
73
  });
48
74
 
49
- const isFirstInGroup = (option) => {
75
+ /**
76
+ * Check if an option is the first option in its group.
77
+ *
78
+ * Note: Whether this option is actually a "group title" depends on the `groupVariant`:
79
+ * - In `'normal'` mode: the first option is a dedicated title item (disabled, non-selectable)
80
+ * - In `'expanded'` mode: the first option is a regular data item (clickable, selectable)
81
+ *
82
+ * @param option - The option to check
83
+ * @returns true if the option is the first in its group, false otherwise
84
+ */ const isFirstInGroup = (option)=>{
50
85
  return option === option?.group?.options[0];
51
86
  };
52
87
 
53
- const useDynamicHeight = ({ itemCount, maxContainerHeight, onContainerHeightChange, }) => {
54
- const prevItemCount = usePrevious(() => itemCount);
88
+
89
+
90
+
91
+
92
+ /**
93
+ * auto calculate container height
94
+ */ const useDynamicHeight = ({ itemCount, maxContainerHeight, onContainerHeightChange })=>{
95
+ const prevItemCount = usePrevious(()=>itemCount);
55
96
  const fullHeight = maxContainerHeight === '100%';
56
97
  const maxContainerSize = fullHeight ? -1 : maxContainerHeight;
57
98
  const containerHeighRef = useRef(maxContainerSize);
58
99
  if (!fullHeight) {
100
+ // * when prev item count is zero, and current is not zero, set hight for vl to trigger totalListHeightChanged to re-change height
59
101
  if (prevItemCount === 0 && itemCount !== 0) {
60
102
  containerHeighRef.current = maxContainerSize;
61
- }
62
- else if (itemCount === 0) {
103
+ } else if (itemCount === 0) {
104
+ // * when item count is zero always height to zero, when zero init
63
105
  containerHeighRef.current = 0;
64
106
  }
65
107
  }
66
108
  return {
67
109
  containerHeighRef,
68
- totalListHeightChanged: (height) => {
110
+ totalListHeightChanged: (height)=>{
69
111
  if (!fullHeight) {
70
112
  const containerHeigh = containerHeighRef.current;
71
113
  let changeHeight = -1;
72
114
  if (height < containerHeigh) {
73
115
  changeHeight = height;
74
- }
75
- else if (containerHeigh !== maxContainerSize) {
116
+ } else if (containerHeigh !== maxContainerSize) {
76
117
  changeHeight = Math.min(height, maxContainerSize);
77
118
  }
78
119
  if (changeHeight !== -1) {
@@ -82,59 +123,76 @@ const useDynamicHeight = ({ itemCount, maxContainerHeight, onContainerHeightChan
82
123
  }
83
124
  },
84
125
  style: {
85
- height: fullHeight ? '100%' : containerHeighRef.current,
86
- },
126
+ height: fullHeight ? '100%' : containerHeighRef.current
127
+ }
87
128
  };
88
129
  };
89
130
 
90
- function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToIndex, }) {
131
+
132
+
133
+ /**
134
+ * scroll into current highlight index
135
+ */ function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToIndex }) {
91
136
  const renderedItemsRef = useRef([]);
92
137
  const scrollerRef = useRef();
93
- const scrollToHighlightedIndex = (prevHighlightedIndex, currHighlightedIndex, topHighlightIndex = 0) => {
138
+ const scrollToHighlightedIndex = (prevHighlightedIndex, currHighlightedIndex, topHighlightIndex = 0)=>{
139
+ // Reset scroll position when list needs re-initialization (prevHighlightedIndex === -1)
140
+ // This occurs when: menu reopens, input changes, options update, etc.
141
+ // Required because AnimatePresence preserves DOM elements, keeping old scrollTop
94
142
  const needsScrollReset = prevHighlightedIndex === -1;
143
+ // Handle case when scroller element is not yet mounted
95
144
  if (!scrollerRef.current) {
145
+ // Delegate to scrollToIndex if not at top position
146
+ // The retry mechanism will handle timing until scroller is ready
96
147
  if (currHighlightedIndex !== topHighlightIndex) {
97
148
  scrollToIndex({
98
149
  index: currHighlightedIndex,
99
- align: 'start',
150
+ align: 'start'
100
151
  });
101
152
  }
102
153
  return;
103
154
  }
104
155
  const scrollElm = scrollerRef.current;
156
+ // Non-virtualized mode: use simplified scroll logic
105
157
  if (!virtualize) {
158
+ // Reset scroll position when opening at the first item
106
159
  if (needsScrollReset && currHighlightedIndex === topHighlightIndex) {
107
160
  scrollElm.scrollTop = 0;
108
161
  return;
109
162
  }
163
+ // Delegate to scrollToIndex for all other cases
164
+ // scrollToIndex will check if item is visible and only scroll if necessary
110
165
  scrollToIndex({
111
166
  index: currHighlightedIndex,
112
- align: 'start',
167
+ align: 'start'
113
168
  });
114
169
  return;
115
170
  }
171
+ // Virtualized mode: calculate visibility using rendered item positions
116
172
  const renderedItems = renderedItemsRef.current;
117
- const lastHighlightedIndex = needsScrollReset
118
- ? currHighlightedIndex
119
- : prevHighlightedIndex;
173
+ const lastHighlightedIndex = needsScrollReset ? currHighlightedIndex : prevHighlightedIndex;
120
174
  const isBackward = lastHighlightedIndex > currHighlightedIndex;
121
175
  const isForward = lastHighlightedIndex < currHighlightedIndex;
176
+ // Calculate visible viewport boundaries
122
177
  const currentVisibleOffset = {
123
178
  start: scrollElm.scrollTop,
124
- stop: scrollElm.scrollTop + containerHeighRef.current,
179
+ stop: scrollElm.scrollTop + containerHeighRef.current
125
180
  };
126
- const isInRenderedRange = renderedItems.some((x) => x.index === currHighlightedIndex);
181
+ // Check if target item is within the currently rendered range
182
+ const isInRenderedRange = renderedItems.some((x)=>x.index === currHighlightedIndex);
127
183
  if (!isInRenderedRange) {
128
184
  scrollToIndex({
129
185
  index: currHighlightedIndex,
130
- align: isBackward ? 'start' : 'end',
186
+ align: isBackward ? 'start' : 'end'
131
187
  });
132
188
  return;
133
189
  }
190
+ // Handle backward navigation (scrolling up)
134
191
  if (isBackward) {
135
192
  let firstShouldScrollIndex = currHighlightedIndex;
136
- for (let i = renderedItems.length - 1; i >= 0; i--) {
193
+ for(let i = renderedItems.length - 1; i >= 0; i--){
137
194
  const item = renderedItems[i];
195
+ // Stop when item top is above visible area
138
196
  if (item.offset < currentVisibleOffset.start) {
139
197
  break;
140
198
  }
@@ -143,15 +201,16 @@ function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToInde
143
201
  if (currHighlightedIndex < firstShouldScrollIndex) {
144
202
  scrollToIndex({
145
203
  index: currHighlightedIndex,
146
- align: 'start',
204
+ align: 'start'
147
205
  });
148
206
  }
149
- }
150
- else if (isForward) {
207
+ // Handle forward navigation (scrolling down)
208
+ } else if (isForward) {
151
209
  let lastShouldScrollIndex = currHighlightedIndex;
152
- for (let i = 0; i < renderedItems.length; i++) {
210
+ for(let i = 0; i < renderedItems.length; i++){
153
211
  const item = renderedItems[i];
154
212
  const itemBottom = item.offset + item.size;
213
+ // Stop when item bottom extends beyond visible area
155
214
  if (itemBottom >= currentVisibleOffset.stop) {
156
215
  break;
157
216
  }
@@ -160,23 +219,27 @@ function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToInde
160
219
  if (currHighlightedIndex > lastShouldScrollIndex) {
161
220
  scrollToIndex({
162
221
  index: currHighlightedIndex,
163
- align: 'end',
222
+ align: 'end'
164
223
  });
165
224
  }
166
- }
167
- else {
168
- const currentItem = renderedItems.find((item) => item.index === currHighlightedIndex);
225
+ } else {
226
+ // Handle no direction change
227
+ const currentItem = renderedItems.find((item)=>item.index === currHighlightedIndex);
169
228
  if (needsScrollReset) {
229
+ // When list needs re-initialization (menu reopens, input changes, etc.)
170
230
  if (currHighlightedIndex === topHighlightIndex) {
231
+ // At top position: reset scrollTop and skip scroll
232
+ // This prevents unnecessary scroll when menu opens at the default position
171
233
  if (scrollerRef.current) {
172
234
  scrollerRef.current.scrollTop = 0;
173
235
  }
174
236
  return;
175
- }
176
- else {
237
+ } else {
238
+ // Not at top position: let scrollToIndex handle the scroll position
239
+ // No need to reset scrollTop as scrollToIndex will set the correct position
177
240
  scrollToIndex({
178
241
  index: currHighlightedIndex,
179
- align: 'start',
242
+ align: 'start'
180
243
  });
181
244
  return;
182
245
  }
@@ -184,10 +247,11 @@ function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToInde
184
247
  if (!currentItem) {
185
248
  scrollToIndex({
186
249
  index: currHighlightedIndex,
187
- align: 'start',
250
+ align: 'start'
188
251
  });
189
252
  return;
190
253
  }
254
+ // Verify item is fully visible within viewport
191
255
  const itemBottom = currentItem.offset + currentItem.size;
192
256
  const isTopVisible = currentItem.offset >= currentVisibleOffset.start;
193
257
  const isBottomVisible = itemBottom < currentVisibleOffset.stop;
@@ -195,23 +259,40 @@ function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToInde
195
259
  if (!isFullyVisible) {
196
260
  scrollToIndex({
197
261
  index: currHighlightedIndex,
198
- align: 'start',
262
+ align: 'start'
199
263
  });
200
264
  }
201
265
  }
202
266
  };
203
267
  return {
204
- scrollerRef,
205
- scrollToHighlightedIndex,
206
- scrollerRefFn: (elm) => (scrollerRef.current = elm),
207
- itemsRendered: (e) => (renderedItemsRef.current = e),
268
+ /**
269
+ * scroller reference
270
+ */ scrollerRef,
271
+ /**
272
+ * Scroll to target highlighted index
273
+ * @param prevHighlightedIndex previous highlightedIndex
274
+ * @param currHighlightedIndex current highlightedIndex
275
+ * @param topHighlightIndex top highlighIndex for check should scroll when init
276
+ */ scrollToHighlightedIndex,
277
+ /**
278
+ * set to virtuoso `scrollerRef` prop, auto bind inner `scrollerRef`
279
+ */ scrollerRefFn: (elm)=>scrollerRef.current = elm,
280
+ /**
281
+ * set to virtuoso `itemsRendered` prop, auto bind inner `renderedItemsRef`
282
+ */ itemsRendered: (e)=>renderedItemsRef.current = e
208
283
  };
209
284
  }
210
285
 
211
- const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconProps, groupExpanded, groupDefaultExpanded, onGroupExpanded, groupVariant, getOptionDisabled, id, }) => {
286
+
287
+
288
+
289
+
290
+
291
+
292
+ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconProps, groupExpanded, groupDefaultExpanded, onGroupExpanded, groupVariant, getOptionDisabled, id })=>{
212
293
  const [groupExpandedMapRef, setGroupExpandedMap] = useRefState({});
213
294
  const isTitleGroup = groupVariant === 'normal';
214
- const handleGroupExpandedChange = (groupName) => {
295
+ const handleGroupExpandedChange = (groupName)=>{
215
296
  const groupExpandedMap = groupExpandedMapRef.current;
216
297
  if (typeof groupExpanded === 'boolean') {
217
298
  return;
@@ -219,40 +300,39 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
219
300
  const toExpandedState = !groupExpandedMap[groupName];
220
301
  const newExpandedMap = {
221
302
  ...groupExpandedMap,
222
- [groupName]: toExpandedState,
303
+ [groupName]: toExpandedState
223
304
  };
224
305
  setGroupExpandedMap(newExpandedMap);
225
- if (!onGroupExpanded)
226
- return;
227
- const group = optionsGroupList.find((x) => x.group === groupName);
228
- if (!group)
229
- return;
306
+ if (!onGroupExpanded) return;
307
+ const group = optionsGroupList.find((x)=>x.group === groupName);
308
+ if (!group) return;
230
309
  const { getExpandIconProps, ...restGroupProps } = group;
231
310
  onGroupExpanded({
232
311
  ...restGroupProps,
233
- expanded: toExpandedState,
312
+ expanded: toExpandedState
234
313
  }, newExpandedMap);
235
314
  };
236
- const handleExpandIconClick = useEventCallback((e, groupName) => {
315
+ const handleExpandIconClick = useEventCallback((e, groupName)=>{
237
316
  handleGroupExpandedChange(groupName);
238
317
  e.preventDefault();
239
318
  e.stopPropagation();
240
319
  });
241
- const optionsGroupList = useMemo(() => {
242
- if (!groupBy || !options) {
320
+ const optionsGroupList = useMemo(()=>{
321
+ /** key with this group count */ if (!groupBy || !options) {
243
322
  return [];
244
323
  }
324
+ // used to keep track of key and indexes in the result array
245
325
  const indexBy = new Map();
246
326
  let warn = false;
247
327
  const afterGroupFn = [];
248
- const result = filteredResult.reduce((acc, option, index) => {
328
+ const result = filteredResult.reduce((acc, option, index)=>{
249
329
  const group = groupBy(option);
250
330
  if (acc.length > 0 && acc[acc.length - 1].group === group) {
251
331
  const currGroup = acc[acc.length - 1];
252
332
  acc[acc.length - 1].options.push({
253
333
  ...option,
254
334
  group: currGroup,
255
- indexInOwnGroup: currGroup.options.length - 1,
335
+ indexInOwnGroup: currGroup.options.length - 1
256
336
  });
257
337
  return acc;
258
338
  }
@@ -261,7 +341,7 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
261
341
  logInDev({
262
342
  scope: 'useAutocomplete',
263
343
  message: `Material-UI: The options provided combined with the \`groupBy\` method returns duplicated headers.
264
- You can solve the issue by sorting the options with the output of \`groupBy\``,
344
+ You can solve the issue by sorting the options with the output of \`groupBy\``
265
345
  });
266
346
  warn = true;
267
347
  }
@@ -272,22 +352,25 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
272
352
  index,
273
353
  group,
274
354
  order: acc.length,
275
- options: [{ ...option, indexInOwnGroup: 0 }],
355
+ options: [
356
+ {
357
+ ...option,
358
+ indexInOwnGroup: 0
359
+ }
360
+ ],
276
361
  expanded: false,
277
- getExpandIconProps: (additionExpandIconProps) => {
278
- return newGroup.options.length > 1
279
- ? combineProps({
280
- onClick: (e) => {
281
- handleExpandIconClick(e, group);
282
- },
283
- onMouseDown: (e) => {
284
- e.preventDefault();
285
- e.stopPropagation();
286
- },
287
- tabIndex: -1,
288
- }, combineProps(additionExpandIconProps, getExpandIconProps?.(newGroup)))
289
- : undefined;
290
- },
362
+ getExpandIconProps: (additionExpandIconProps)=>{
363
+ return newGroup.options.length > 1 ? combineProps({
364
+ onClick: (e)=>{
365
+ handleExpandIconClick(e, group);
366
+ },
367
+ onMouseDown: (e)=>{
368
+ e.preventDefault();
369
+ e.stopPropagation();
370
+ },
371
+ tabIndex: -1
372
+ }, combineProps(additionExpandIconProps, getExpandIconProps?.(newGroup))) : undefined;
373
+ }
291
374
  };
292
375
  newGroup.options[0].group = newGroup;
293
376
  if (isTitleGroup) {
@@ -295,10 +378,13 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
295
378
  id: `${id}-${group}`,
296
379
  label: group,
297
380
  disabled: true,
298
- group: newGroup,
381
+ // * here cause by self Type reference
382
+ // oxlint-disable-next-line @typescript-eslint/no-explicit-any
383
+ group: newGroup
299
384
  };
385
+ // push fn to array, do that after all options be add into group
300
386
  if (getOptionDisabled) {
301
- afterGroupFn.push(() => {
387
+ afterGroupFn.push(()=>{
302
388
  groupTitleOption.disabled = getOptionDisabled(groupTitleOption);
303
389
  });
304
390
  }
@@ -307,7 +393,7 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
307
393
  acc.push(newGroup);
308
394
  return acc;
309
395
  }, []);
310
- afterGroupFn.forEach((fn) => fn());
396
+ afterGroupFn.forEach((fn)=>fn());
311
397
  return result;
312
398
  }, [
313
399
  filteredResult,
@@ -317,92 +403,112 @@ const useAutocompleteGroup = ({ groupBy, options, filteredResult, getExpandIconP
317
403
  handleExpandIconClick,
318
404
  id,
319
405
  isTitleGroup,
320
- options,
406
+ options
321
407
  ]);
322
- const initGroupExpandedState = (expandedState) => {
408
+ const initGroupExpandedState = (expandedState)=>{
323
409
  if (typeof expandedState === 'object') {
324
410
  setGroupExpandedMap(expandedState, false);
325
- }
326
- else if (typeof expandedState === 'boolean') {
327
- setGroupExpandedMap(optionsGroupList.reduce((prev, curr) => {
411
+ } else if (typeof expandedState === 'boolean') {
412
+ setGroupExpandedMap(optionsGroupList.reduce((prev, curr)=>{
328
413
  prev[curr.group] = expandedState;
329
414
  return prev;
330
415
  }, {}), false);
331
416
  }
332
417
  };
333
- useDepsChange(() => {
418
+ useDepsChange(()=>{
334
419
  initGroupExpandedState(groupDefaultExpanded);
335
420
  }, []);
336
- useDepsChange(() => {
421
+ useDepsChange(()=>{
337
422
  initGroupExpandedState(groupExpanded);
338
- }, [groupExpanded]);
423
+ }, [
424
+ groupExpanded
425
+ ]);
339
426
  const groupExpandedMap = groupExpandedMapRef.current;
340
- const groupedResult = useMemo(() => {
341
- const addExpandedResult = Object.entries(groupExpandedMap).reduce((prev, [key, expended]) => {
342
- const group = prev.find((x) => x.group === key);
343
- if (group)
344
- group.expanded = expended;
427
+ // * speared logic with group, prevent calculate every time.
428
+ const groupedResult = useMemo(()=>{
429
+ const addExpandedResult = Object.entries(groupExpandedMap).reduce((prev, [key, expended])=>{
430
+ const group = prev.find((x)=>x.group === key);
431
+ if (group) group.expanded = expended;
345
432
  return prev;
346
- }, [...optionsGroupList]);
347
- return addExpandedResult.reduce((prev, curr) => {
433
+ }, [
434
+ ...optionsGroupList
435
+ ]);
436
+ return addExpandedResult.reduce((prev, curr)=>{
348
437
  if (curr.expanded) {
349
438
  prev.push(...curr.options);
350
- }
351
- else {
439
+ } else {
352
440
  prev.push(curr.options[0]);
353
441
  }
354
442
  return prev;
355
443
  }, []);
356
- }, [groupExpandedMap, optionsGroupList]);
444
+ }, [
445
+ groupExpandedMap,
446
+ optionsGroupList
447
+ ]);
357
448
  return {
358
449
  optionsGroupList,
359
450
  groupedResult,
360
451
  groupExpandedMap,
361
- handleGroupExpandedChange,
452
+ handleGroupExpandedChange
362
453
  };
363
454
  };
364
455
 
365
- const DEFAULT_HIGHLIGHTED_INDEX$1 = -1;
366
- const isItemCanSelected = (item) => {
456
+
457
+
458
+
459
+
460
+ const DEFAULT_HIGHLIGHTED_INDEX = -1;
461
+ const isItemCanSelected = (item)=>{
367
462
  return Boolean(item && !item.disabled);
368
463
  };
369
- const toOptionIdString = (id) => typeof id === 'number' ? String(id) : id;
370
- const DEFAULT_GET_OPTION_LABEL = (item) => item?.label || '';
371
- const defaultIsOptionEqualToValue = (option, value) => option === value;
372
- 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, }) => {
464
+ const toOptionIdString = (id)=>typeof id === 'number' ? String(id) : id;
465
+ const DEFAULT_GET_OPTION_LABEL = (item)=>item?.label || '';
466
+ /**
467
+ * provide suggestion list with search function and expandable group with virtualized list
468
+ */ /** Default equality check using strict equality */ const defaultIsOptionEqualToValue = (option, value)=>option === value;
469
+ 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 })=>{
373
470
  const isTitleMode = groupVariant === 'normal';
374
471
  const [inputValue, setInputValue] = useControlled({
375
472
  controlled: inputValueProp,
376
473
  default: '',
377
- name: 'useSuggestionList',
474
+ name: 'useSuggestionList'
378
475
  });
379
- const updateInputValue = (newValue) => {
476
+ const updateInputValue = (newValue)=>{
380
477
  onInputChangeProp?.(newValue);
381
478
  setInputValue(newValue);
382
479
  };
383
- const focusInput = () => inputRef.current?.focus();
480
+ const focusInput = ()=>inputRef.current?.focus();
384
481
  const currUniqueId = useId(id || 'suggestion-list', !id);
385
- const highlightedIndexRef = useRef(DEFAULT_HIGHLIGHTED_INDEX$1);
482
+ const highlightedIndexRef = useRef(DEFAULT_HIGHLIGHTED_INDEX);
386
483
  const changeHighlightedIndexReasonRef = useRef();
387
484
  const forceUpdate = useForceUpdate();
388
- const getFilteredItems = useCallback((items) => {
485
+ const getFilteredItems = useCallback((items)=>{
389
486
  if (filterOptions) {
390
487
  return filterOptions(items, {
391
488
  inputValue,
392
489
  getOptionLabel,
393
490
  selectedItems: [],
394
- isOptionEqualToValue,
491
+ isOptionEqualToValue
395
492
  });
396
493
  }
397
494
  return items;
398
- }, [filterOptions, getOptionLabel, inputValue, isOptionEqualToValue]);
399
- const filteredResult = useMemo(() => {
495
+ }, [
496
+ filterOptions,
497
+ getOptionLabel,
498
+ inputValue,
499
+ isOptionEqualToValue
500
+ ]);
501
+ const filteredResult = useMemo(()=>{
502
+ // * only when isOpen calculate the filtered result
400
503
  let results = getFilteredItems(options);
401
- results = processFilteredResult
402
- ? processFilteredResult(results, inputValue)
403
- : results;
504
+ results = processFilteredResult ? processFilteredResult(results, inputValue) : results;
404
505
  return results;
405
- }, [getFilteredItems, inputValue, options, processFilteredResult]);
506
+ }, [
507
+ getFilteredItems,
508
+ inputValue,
509
+ options,
510
+ processFilteredResult
511
+ ]);
406
512
  const { groupedResult, handleGroupExpandedChange, optionsGroupList } = useAutocompleteGroup({
407
513
  id: currUniqueId,
408
514
  options,
@@ -413,61 +519,61 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
413
519
  groupBy,
414
520
  getExpandIconProps,
415
521
  onGroupExpanded,
416
- getOptionDisabled,
522
+ getOptionDisabled
417
523
  });
418
524
  const optionItems = groupBy ? groupedResult : filteredResult;
419
- const setHighlightedIndex = (index, { reRender = false, reason, }) => {
525
+ const setHighlightedIndex = (index, { reRender = false, reason })=>{
420
526
  changeHighlightedIndexReasonRef.current = reason;
421
527
  if (highlightedIndexRef.current !== index) {
422
528
  highlightedIndexRef.current = index;
423
- if (reRender)
424
- forceUpdate();
529
+ if (reRender) forceUpdate();
425
530
  }
426
531
  };
427
- const handleInputChange = (newValue) => {
532
+ const handleInputChange = (newValue)=>{
428
533
  if (inputValue !== newValue) {
429
- setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX$1, { reason: 'auto' });
534
+ setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, {
535
+ reason: 'auto'
536
+ });
430
537
  updateInputValue(newValue);
431
538
  }
432
539
  return false;
433
540
  };
434
- const getIsItemCanSelected = (item) => {
435
- return ((!!item && item.freeSolo) ||
436
- (isItemCanSelected(item) && !getOptionDisabled?.(item)));
541
+ const getIsItemCanSelected = (item)=>{
542
+ return !!item && item.freeSolo || isItemCanSelected(item) && !getOptionDisabled?.(item);
437
543
  };
438
- const selectItem = (e, selectedItem) => {
544
+ const selectItem = (e, selectedItem)=>{
439
545
  if (getIsItemCanSelected(selectedItem)) {
440
546
  onSelect?.(e, selectedItem);
441
547
  return true;
442
548
  }
443
549
  return false;
444
550
  };
445
- const clearInput = () => {
551
+ const clearInput = ()=>{
446
552
  if (inputRef.current && inputRef.current.value.length > 0) {
447
553
  updateInputValue('');
448
554
  }
449
555
  };
450
- const reset = (isFocus) => {
556
+ const reset = (isFocus)=>{
451
557
  clearInput();
452
558
  onInputChangeProp?.('');
453
- if (isFocus)
454
- focusInput();
559
+ if (isFocus) focusInput();
455
560
  };
456
561
  const { onKeyFocusedIndexHandle, getNextFocusableOption } = useKeyboardMoveFocus({
457
562
  options: optionItems,
458
563
  focusedIndexRef: highlightedIndexRef,
459
564
  infinite: true,
460
- onFocusedIndexChange: (event, toIndex) => {
461
- setHighlightedIndex(toIndex, { reason: 'keyboard', reRender: true });
565
+ onFocusedIndexChange: (event, toIndex)=>{
566
+ setHighlightedIndex(toIndex, {
567
+ reason: 'keyboard',
568
+ reRender: true
569
+ });
462
570
  event?.preventDefault();
463
571
  },
464
- getOptionDisabled: disabledItemsHighlightable
465
- ? undefined
466
- : (child) => {
467
- return !getIsItemCanSelected(child);
468
- },
572
+ getOptionDisabled: disabledItemsHighlightable ? undefined : (child)=>{
573
+ return !getIsItemCanSelected(child);
574
+ }
469
575
  });
470
- const getItemProps = ({ item, index = 0, }) => {
576
+ const getItemProps = ({ item, index = 0 })=>{
471
577
  const currGroup = item.group;
472
578
  const isItemFirstInGroup = isFirstInGroup(item);
473
579
  const itemCount = optionItems.length;
@@ -475,75 +581,76 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
475
581
  const stringId = toOptionIdString(item.id);
476
582
  return {
477
583
  id: stringId ?? `${currUniqueId}-option-${index}`,
478
- onClick: (e) => {
584
+ onClick: (e)=>{
479
585
  selectItem(e, item);
480
586
  },
481
- onMouseOver: () => {
482
- if (highlightedIndexRef.current !== index &&
483
- getIsItemCanSelected(item)) {
484
- setHighlightedIndex(index, { reason: 'mouse', reRender: true });
587
+ onMouseOver: ()=>{
588
+ if (highlightedIndexRef.current !== index && getIsItemCanSelected(item)) {
589
+ setHighlightedIndex(index, {
590
+ reason: 'mouse',
591
+ reRender: true
592
+ });
485
593
  }
486
594
  },
487
- ...(isTitleMode && isItemFirstInGroup
488
- ? {}
489
- : {
490
- role: 'option',
491
- 'aria-setsize': itemCount - (isTitleMode ? optionsGroupList?.length || 0 : 0),
492
- 'aria-posinset': index + 1 - (isTitleMode ? groupOrder + 1 : 0),
493
- }),
595
+ // When title mode and is title, skip that posinset
596
+ ...isTitleMode && isItemFirstInGroup ? {} : {
597
+ role: 'option',
598
+ 'aria-setsize': itemCount - (isTitleMode ? optionsGroupList?.length || 0 : 0),
599
+ 'aria-posinset': index + 1 - (isTitleMode ? groupOrder + 1 : 0)
600
+ }
494
601
  };
495
602
  };
496
- const handleF10KeyDown = (e) => {
603
+ const handleF10KeyDown = (e)=>{
497
604
  const highlightedIndex = highlightedIndexRef.current;
498
- if (!e.shiftKey)
499
- return;
605
+ if (!e.shiftKey) return;
500
606
  const currOption = optionItems[highlightedIndex];
501
607
  const currentGroup = currOption.group;
502
608
  if (currentGroup && currentGroup.options.length > 1) {
503
609
  handleGroupExpandedChange(currentGroup.group);
504
610
  }
505
611
  };
506
- const getInputProps = (props) => {
612
+ const getInputProps = (props)=>{
507
613
  return combineProps({
508
614
  id: `${currUniqueId}-input`,
509
615
  autoComplete: 'off',
510
- onChange: (e) => {
616
+ onChange: (e)=>{
511
617
  const changeValue = e.target.value;
512
618
  handleInputChange(changeValue);
513
619
  },
514
- onKeyDown: (e) => {
620
+ onKeyDown: (e)=>{
515
621
  onKeyDownProp?.(e, highlightedIndexRef.current);
516
- switch (e.key) {
622
+ switch(e.key){
517
623
  case 'F10':
518
624
  handleF10KeyDown(e);
519
625
  break;
520
- case 'Enter': {
521
- const highlightedIndex = highlightedIndexRef.current;
522
- if (e.which === 229)
523
- return;
524
- if (highlightedIndex !== DEFAULT_HIGHLIGHTED_INDEX$1) {
525
- const currOption = optionItems[highlightedIndex];
526
- const isCurrOptionFirstInGroup = isFirstInGroup(currOption);
527
- if (isCurrOptionFirstInGroup) {
528
- const { onClick } = currOption.group.getExpandIconProps();
529
- onClick?.(e);
530
- }
531
- else {
532
- selectItem(e, currOption);
626
+ case 'Enter':
627
+ {
628
+ const highlightedIndex = highlightedIndexRef.current;
629
+ if (e.which === 229) return;
630
+ if (highlightedIndex !== DEFAULT_HIGHLIGHTED_INDEX) {
631
+ const currOption = optionItems[highlightedIndex];
632
+ const isCurrOptionFirstInGroup = isFirstInGroup(currOption);
633
+ if (isCurrOptionFirstInGroup) {
634
+ // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion
635
+ const { onClick } = currOption.group.getExpandIconProps();
636
+ onClick?.(e);
637
+ } else {
638
+ selectItem(e, currOption);
639
+ }
640
+ e.stopPropagation();
533
641
  }
534
- e.stopPropagation();
642
+ // always preventDefault for not inset all enter into textarea
643
+ e.preventDefault();
644
+ break;
535
645
  }
536
- e.preventDefault();
537
- break;
538
- }
539
646
  default:
540
647
  onKeyFocusedIndexHandle(e);
541
648
  break;
542
649
  }
543
- },
650
+ }
544
651
  }, props);
545
652
  };
546
- const getInputAriaProps = (props) => {
653
+ const getInputAriaProps = (props)=>{
547
654
  return combineProps({
548
655
  role: 'combobox',
549
656
  'aria-autocomplete': 'list',
@@ -551,39 +658,38 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
551
658
  'aria-haspopup': true,
552
659
  'aria-controls': `${currUniqueId}-menu`,
553
660
  'aria-owns': `${currUniqueId}-menu`,
554
- 'aria-activedescendant': (() => {
661
+ 'aria-activedescendant': (()=>{
555
662
  const index = highlightedIndexRef.current;
556
- if (index < 0)
557
- return undefined;
663
+ if (index < 0) return undefined;
558
664
  const option = optionItems[index];
559
665
  const optionId = toOptionIdString(option?.id);
560
666
  return optionId ?? `${currUniqueId}-option-${index}`;
561
- })(),
667
+ })()
562
668
  }, props);
563
669
  };
564
- const getLabelProps = (props) => {
670
+ const getLabelProps = (props)=>{
565
671
  return combineProps({
566
672
  htmlFor: `${currUniqueId}-input`,
567
- id: `${currUniqueId}-label`,
673
+ id: `${currUniqueId}-label`
568
674
  }, props);
569
675
  };
570
- const getSuggestionListProps = (restMenuProps) => {
676
+ const getSuggestionListProps = (restMenuProps)=>{
571
677
  return combineProps({
572
678
  'aria-labelledby': `${currUniqueId}-label`,
573
679
  id: `${currUniqueId}-menu`,
574
- role: 'listbox',
680
+ role: 'listbox'
575
681
  }, restMenuProps);
576
682
  };
577
- const getClearButtonProps = (props) => {
683
+ const getClearButtonProps = (props)=>{
578
684
  return combineProps({
579
685
  id: `${currUniqueId}-clear-button`,
580
- onClick: (e) => {
686
+ onClick: (e)=>{
581
687
  onClear?.(e);
582
688
  reset(true);
583
- },
689
+ }
584
690
  }, props);
585
691
  };
586
- useEffect(() => {
692
+ useEffect(()=>{
587
693
  changeHighlightedIndexReasonRef.current = undefined;
588
694
  });
589
695
  return {
@@ -609,41 +715,75 @@ const useSuggestionList = ({ id, inputValue: inputValueProp, getOptionLabel = DE
609
715
  changeHighlightedIndexReasonRef,
610
716
  changeHighlightedIndexReason: changeHighlightedIndexReasonRef.current,
611
717
  optionsGroupList,
612
- id: currUniqueId,
718
+ /** current unique id */ id: currUniqueId,
613
719
  getNextFocusableOption,
614
- handleF10KeyDown,
720
+ handleF10KeyDown
615
721
  };
616
722
  };
617
723
 
724
+
725
+
726
+
727
+
728
+
729
+
730
+
731
+
732
+
733
+
734
+
735
+
736
+
737
+
738
+
739
+
618
740
  const DEFAULT_PADDING = 8;
619
- const getPaddingValue = (padding, itemCount) => {
741
+ /**
742
+ * Calculates the padding value from the padding prop.
743
+ * Note: padding will never be undefined due to default parameter (padding = DEFAULT_PADDING).
744
+ * @param padding - padding prop value (boolean | number)
745
+ * @param itemCount - number of items in the list
746
+ * @returns padding value as a number representing pixels (>= 0)
747
+ */ const getPaddingValue = (padding, itemCount)=>{
620
748
  if (itemCount === 0) {
621
749
  return 0;
622
750
  }
623
751
  if (padding === false) {
624
- return 0;
752
+ return 0; // explicitly disabled
625
753
  }
626
754
  if (padding === true) {
627
- return DEFAULT_PADDING;
755
+ return DEFAULT_PADDING; // default enabled
628
756
  }
757
+ // padding is a number (including default value when not passed)
758
+ // Clamp to 0 to handle negative values
629
759
  if (typeof padding === 'number') {
630
760
  return Math.max(0, padding);
631
761
  }
762
+ // Fallback (should never reach here due to default parameter)
632
763
  return DEFAULT_PADDING;
633
764
  };
634
- const List = forwardRef((props, ref) => {
765
+ const List = /*#__PURE__*/ forwardRef((props, ref)=>{
635
766
  const { className, context, style, ...restProps } = props;
636
767
  const listRef = context?.listRef;
637
768
  const rootClass = context?.rootClass;
638
769
  const paddingValue = context?.paddingValue ?? 0;
639
770
  const forkRef = useForkRef(ref, listRef);
640
- return (jsx("div", { ref: forkRef, ...restProps, className: clsx(className, rootClass), "data-suggestion-list": true, style: {
771
+ return /*#__PURE__*/ jsx("div", {
772
+ ref: forkRef,
773
+ ...restProps,
774
+ className: clsx(className, rootClass),
775
+ "data-suggestion-list": true,
776
+ style: {
641
777
  ...style,
642
- ['--sui-suggestion-list-vertical-padding']: `${paddingValue}px`,
643
- } }));
778
+ ['--sui-suggestion-list-vertical-padding']: `${paddingValue}px`
779
+ }
780
+ });
644
781
  });
645
- const SuggestionList = forwardRef((inProps, ref) => {
646
- const props = useThemeProps({ props: inProps, name: 'SuggestionList' });
782
+ const SuggestionList_SuggestionList = /*#__PURE__*/ forwardRef((inProps, ref)=>{
783
+ const props = useThemeProps({
784
+ props: inProps,
785
+ name: 'SuggestionList'
786
+ });
647
787
  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;
648
788
  const vlRef = useRef(null);
649
789
  const forkVlRef = useForkRef(ref, vlRef);
@@ -652,146 +792,158 @@ const SuggestionList = forwardRef((inProps, ref) => {
652
792
  const itemData = options;
653
793
  const itemCount = options.length;
654
794
  const externalWindowProps = useVirtuosoExternalWindow();
655
- const internalPrevHighlightedIndex = usePrevious(() => highlightedIndex, true);
795
+ // Track previous highlightedIndex internally for backward compatibility
796
+ // Using isDefault: true ensures consistent behavior across render cycles
797
+ const internalPrevHighlightedIndex = usePrevious(()=>highlightedIndex, true);
798
+ // Use parent-provided value if available, otherwise fall back to internal tracking
656
799
  const actualPrevHighlightedIndex = prevHighlightedIndex ?? internalPrevHighlightedIndex;
657
800
  const cls = useClassesWithMode('suggestion-list', classesProp, classNameProp);
658
801
  const paddingValue = getPaddingValue(padding, itemCount);
802
+ // Track whether items have been rendered to ensure accurate height calculations
659
803
  const hasItemsRenderedRef = useRef(false);
660
804
  const { totalListHeightChanged, style, containerHeighRef } = useDynamicHeight({
661
805
  itemCount,
662
806
  maxContainerHeight,
663
- onContainerHeightChange: (changeHeight) => {
807
+ onContainerHeightChange: (changeHeight)=>{
664
808
  const scroller = scrollerRef.current;
665
809
  if (scroller?.style) {
666
810
  scroller.style.height = `${changeHeight}px`;
667
811
  onContainerHeightChange?.();
668
812
  }
669
- },
813
+ }
670
814
  });
671
- const handleTotalListHeightChanged = (height) => {
815
+ /**
816
+ * Adjusts Virtuoso's reported height to include padding from :before/:after pseudo-elements.
817
+ * Prevents reporting height changes before items are rendered to ensure accurate height calculation.
818
+ */ const handleTotalListHeightChanged = (height)=>{
672
819
  const adjustedHeight = paddingValue > 0 ? height + paddingValue * 2 : height;
673
- if (!hasItemsRenderedRef.current &&
674
- adjustedHeight < containerHeighRef.current) {
820
+ if (!hasItemsRenderedRef.current && adjustedHeight < containerHeighRef.current) {
675
821
  return;
676
822
  }
677
823
  totalListHeightChanged(adjustedHeight);
678
824
  };
679
825
  const { sleep } = useSleep();
680
- const { retry: scrollToIndexWithRetry } = useRetry(async (location) => {
826
+ const { retry: scrollToIndexWithRetry } = useRetry(async (location)=>{
681
827
  const toIndex = location.index;
682
828
  if (!virtualize) {
683
829
  const scroller = listRef.current;
684
830
  const needsScrollReset = prevHighlightedIndex === -1;
685
831
  const isLastItem = itemCount > 1 && toIndex === itemCount - 1;
832
+ // Reveal bottom padding when scrolling to last item after scroll reset
686
833
  if (paddingValue > 0 && needsScrollReset && isLastItem && scroller) {
687
834
  scroller.scrollTop = scroller.scrollHeight;
688
835
  return true;
689
836
  }
837
+ // Use custom scrollTo logic to avoid affecting page scroll
690
838
  const toElm = scroller?.querySelector(`[data-item-index="${toIndex}"]`);
691
839
  if (!toElm || !scroller) {
692
840
  return false;
693
841
  }
842
+ // Calculate element position relative to scroller
694
843
  const scrollerRect = scroller.getBoundingClientRect();
695
844
  const elementRect = toElm.getBoundingClientRect();
696
845
  const scrollerTop = scrollerRect.top;
697
846
  const scrollerBottom = scrollerRect.bottom;
698
847
  const elementTop = elementRect.top;
699
848
  const elementBottom = elementRect.bottom;
849
+ // Check if element is fully visible
700
850
  const isFullyVisible = elementTop >= scrollerTop && elementBottom <= scrollerBottom;
701
851
  if (!isFullyVisible) {
852
+ // Scroll element into view using scrollTo (affects only the scroller)
702
853
  let scrollOffset = 0;
703
854
  if (elementTop < scrollerTop) {
855
+ // Element is above viewport - align to start
704
856
  scrollOffset = toElm.offsetTop - paddingValue;
705
- }
706
- else if (elementBottom > scrollerBottom) {
707
- scrollOffset =
708
- toElm.offsetTop -
709
- scroller.clientHeight +
710
- toElm.offsetHeight +
711
- paddingValue;
857
+ } else if (elementBottom > scrollerBottom) {
858
+ // Element is below viewport - align to end
859
+ scrollOffset = toElm.offsetTop - scroller.clientHeight + toElm.offsetHeight + paddingValue;
712
860
  }
713
861
  scroller.scrollTo({
714
862
  top: scrollOffset,
715
- behavior: 'auto',
863
+ behavior: 'auto'
716
864
  });
717
865
  }
718
866
  return true;
719
867
  }
720
- if (paddingValue > 0 &&
721
- (location.align === 'start' || location.align === 'end')) {
868
+ // Apply padding offset to ensure full item visibility in virtualized mode
869
+ if (paddingValue > 0 && (location.align === 'start' || location.align === 'end')) {
722
870
  location = {
723
871
  ...location,
724
- offset: paddingValue,
872
+ offset: paddingValue
725
873
  };
726
874
  }
727
875
  vlRef.current?.scrollToIndex(location);
728
876
  await sleep(0);
877
+ // Verify the item has been rendered
729
878
  const toElm = listRef.current?.querySelector(`[data-item-index="${toIndex}"]`);
730
879
  return !!toElm;
731
880
  }, {
732
881
  retryTimes: 10,
733
- intervalTime: 20,
882
+ intervalTime: 20
734
883
  });
735
- const { scrollerRef, scrollerRefFn, itemsRendered: itemsRenderedFromHook, scrollToHighlightedIndex, } = useHighlightScroll({
884
+ const { scrollerRef, scrollerRefFn, itemsRendered: itemsRenderedFromHook, scrollToHighlightedIndex } = useHighlightScroll({
736
885
  virtualize,
737
886
  containerHeighRef,
738
- scrollToIndex: scrollToIndexWithRetry,
887
+ scrollToIndex: scrollToIndexWithRetry
739
888
  });
740
- const handleItemsRendered = (items) => {
889
+ /**
890
+ * Wraps itemsRendered callback to track when items have been rendered.
891
+ */ const handleItemsRendered = (items)=>{
741
892
  if (items.length > 0 || itemCount === 0) {
742
893
  hasItemsRenderedRef.current = true;
743
894
  }
744
895
  itemsRenderedFromHook(items);
745
896
  };
746
- useEffect(() => {
897
+ // Reset flag when itemCount changes to non-zero
898
+ useEffect(()=>{
747
899
  if (itemCount > 0) {
748
900
  hasItemsRenderedRef.current = false;
749
901
  }
750
- }, [itemCount]);
751
- useLayoutEffect(() => {
752
- const shouldScroll = (!virtualize || vlRef.current) &&
753
- !isKeepHighlightedIndex &&
754
- changeHighlightedIndexReason &&
755
- changeHighlightedIndexReason !== 'mouse';
902
+ }, [
903
+ itemCount
904
+ ]);
905
+ useLayoutEffect(()=>{
906
+ const shouldScroll = (!virtualize || vlRef.current) && !isKeepHighlightedIndex && // Only scroll when reason is 'keyboard'
907
+ changeHighlightedIndexReason && changeHighlightedIndexReason !== 'mouse';
908
+ // Check if the first item is actually a group title
756
909
  const firstOption = options[0];
757
910
  const isFirstItemGroupTitle = isTitleMode && isFirstInGroup(firstOption);
911
+ // In title group mode, first item is group title, so start from index 1
758
912
  const topHighlightIndex = isFirstItemGroupTitle ? 1 : 0;
759
913
  if (shouldScroll) {
760
914
  scrollToHighlightedIndex(actualPrevHighlightedIndex, highlightedIndex, topHighlightIndex);
761
915
  }
762
916
  });
763
- const handleScrolling = (scrolling) => {
917
+ const handleScrolling = (scrolling)=>{
764
918
  const list = listRef.current;
765
- if (!list)
766
- return;
919
+ if (!list) return;
767
920
  list.style.pointerEvents = scrolling ? 'none' : '';
768
921
  };
769
- const itemContent = (index, option) => {
922
+ const itemContent = (index, option)=>{
770
923
  const isOptionFirstInGroup = isFirstInGroup(option);
771
924
  const isFixedGroupExpanded = typeof groupExpanded === 'boolean';
772
925
  const expandable = !isTitleMode && !isFixedGroupExpanded && isOptionFirstInGroup;
773
926
  const expanded = option.group?.expanded;
774
- const expandIconProps = expandable
775
- ? option.group?.getExpandIconProps?.({
776
- className: cls('expand-icon'),
777
- 'aria-expanded': expanded,
778
- })
779
- : undefined;
927
+ const expandIconProps = expandable ? option.group?.getExpandIconProps?.({
928
+ className: cls('expand-icon'),
929
+ 'aria-expanded': expanded
930
+ }) : undefined;
780
931
  const itemProps = getItemProps({
781
932
  item: option,
782
- index,
933
+ index
783
934
  });
784
935
  const resultProps = {
785
936
  'data-item-index': !virtualize ? index : undefined,
786
937
  ...option,
787
938
  ...itemProps,
788
939
  label: option.label,
789
- key: itemProps.id,
940
+ key: itemProps.id
790
941
  };
791
942
  const highlighted = highlightedIndex === index;
792
943
  const selected = getOptionSelected?.(option);
793
944
  const indexInOwnGroup = resultProps.indexInOwnGroup;
794
945
  delete resultProps.indexInOwnGroup;
946
+ // Check external disabled state if item is not already disabled
795
947
  if (!resultProps.freeSolo && !resultProps.disabled && getOptionDisabled) {
796
948
  resultProps.disabled = getOptionDisabled(option);
797
949
  }
@@ -799,69 +951,128 @@ const SuggestionList = forwardRef((inProps, ref) => {
799
951
  inputValue,
800
952
  highlighted,
801
953
  index,
802
- indexInOwnGroup,
954
+ indexInOwnGroup
803
955
  };
804
956
  if (renderGroup && isOptionFirstInGroup) {
805
957
  return renderGroup(resultProps, {
806
958
  ...state,
807
959
  expanded,
808
- expandIconProps,
960
+ expandIconProps
809
961
  });
810
962
  }
811
963
  if (renderOption) {
812
964
  return renderOption(resultProps, state);
813
965
  }
814
966
  const { error, group, freeSolo, key, ...restProps } = resultProps;
815
- return (jsx(SuggestionListItem, { ...restProps, id: typeof resultProps.id === 'number'
816
- ? `${resultProps.id}`
817
- : resultProps.id, expanded: expanded, isGroupTitle: isOptionFirstInGroup, isTitleMode: isTitleMode, highlighted: highlighted, selected: selected, disabled: resultProps.disabled, label: getOptionLabel ? getOptionLabel(option) : option.label, expandIconProps: expandIconProps }, key));
967
+ return /*#__PURE__*/ jsx(SuggestionListItem, {
968
+ ...restProps,
969
+ id: typeof resultProps.id === 'number' ? `${resultProps.id}` : resultProps.id,
970
+ expanded: expanded,
971
+ isGroupTitle: isOptionFirstInGroup,
972
+ isTitleMode: isTitleMode,
973
+ highlighted: highlighted,
974
+ selected: selected,
975
+ disabled: resultProps.disabled,
976
+ label: getOptionLabel ? getOptionLabel(option) : option.label,
977
+ expandIconProps: expandIconProps
978
+ }, key);
818
979
  };
819
980
  const components = {
820
981
  List: List,
821
- ...componentsProp,
982
+ ...componentsProp
822
983
  };
823
984
  const rootClass = cls('root');
824
- const context = { listRef, rootClass, paddingValue };
825
- return virtualize ? (jsx(Virtuoso, { ref: forkVlRef, totalCount: itemCount, data: itemData, context: context, components: components, itemContent: itemContent, ...externalWindowProps, ...getSuggestionListProps(), ...combineProps({
826
- scrollerRef: (scrollElm) => {
985
+ const context = {
986
+ listRef,
987
+ rootClass,
988
+ paddingValue
989
+ };
990
+ return virtualize ? /*#__PURE__*/ jsx(Virtuoso, {
991
+ ref: forkVlRef,
992
+ totalCount: itemCount,
993
+ data: itemData,
994
+ context: context,
995
+ components: components,
996
+ itemContent: itemContent,
997
+ ...externalWindowProps,
998
+ ...getSuggestionListProps(),
999
+ ...combineProps({
1000
+ scrollerRef: (scrollElm)=>{
827
1001
  scrollerRefFn(scrollElm);
828
1002
  },
829
1003
  itemsRendered: handleItemsRendered,
830
1004
  totalListHeightChanged: handleTotalListHeightChanged,
831
1005
  style,
832
- isScrolling: handleScrolling,
833
- }, rest) })) : (jsx(List, { context: context, style: {
1006
+ isScrolling: handleScrolling
1007
+ }, rest)
1008
+ }) : /*#__PURE__*/ jsx(List, {
1009
+ context: context,
1010
+ style: {
834
1011
  maxHeight: style.height,
835
- overflow: 'auto',
836
- }, ref: (scrollElm) => {
1012
+ overflow: 'auto'
1013
+ },
1014
+ ref: (scrollElm)=>{
837
1015
  listRef.current = scrollElm;
838
1016
  scrollerRefFn(scrollElm);
839
- }, className: cls('root'), ...getSuggestionListProps(), ...rest, children: options.map((x, i) => {
1017
+ },
1018
+ className: cls('root'),
1019
+ ...getSuggestionListProps(),
1020
+ ...rest,
1021
+ children: options.map((x, i)=>{
840
1022
  return itemContent(i, x);
841
- }) }));
1023
+ })
1024
+ });
842
1025
  });
843
- SuggestionList.displayName = 'SuggestionList';
1026
+ SuggestionList_SuggestionList.displayName = 'SuggestionList';
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+
1033
+
1034
+
1035
+
1036
+
1037
+
1038
+
1039
+
1040
+
1041
+ // extracted by css-extract-rspack-plugin
1042
+ /* export default */ var Autocomplete = ({"sui-autocomplete-chevron":"sui-autocomplete-chevron","sui-autocomplete-clear":"sui-autocomplete-clear","sui-autocomplete-root":"sui-autocomplete-root","sui-focused":"sui-focused","sui-disabled":"sui-disabled","sui-autocomplete-input-container":"sui-autocomplete-input-container","sui-autocomplete-form-field-content":"sui-autocomplete-form-field-content","sui-autocomplete-form-field-content-xlarge":"sui-autocomplete-form-field-content-xlarge","sui-autocomplete-form-field-content-large":"sui-autocomplete-form-field-content-large","sui-autocomplete-form-field-content-medium":"sui-autocomplete-form-field-content-medium","sui-autocomplete-input-container-xlarge":"sui-autocomplete-input-container-xlarge","sui-autocomplete-input-container-large":"sui-autocomplete-input-container-large","sui-autocomplete-input-container-medium":"sui-autocomplete-input-container-medium","sui-autocomplete-input":"sui-autocomplete-input","sui-autocomplete-input-xlarge":"sui-autocomplete-input-xlarge","sui-autocomplete-input-large":"sui-autocomplete-input-large","sui-autocomplete-input-medium":"sui-autocomplete-input-medium","sui-autocomplete-outlined-expanded":"sui-autocomplete-outlined-expanded","sui-autocomplete-outlined-expanded-chevron":"sui-autocomplete-outlined-expanded-chevron","sui-autocomplete-standard-expanded-chevron":"sui-autocomplete-standard-expanded-chevron","sui-autocomplete-error-chevron":"sui-autocomplete-error-chevron","sui-autocomplete-end-adornment":"sui-autocomplete-end-adornment","sui-autocomplete-popper-root":"sui-autocomplete-popper-root","sui-popper-widget":"sui-popper-widget","sui-autocomplete-popper-paper":"sui-autocomplete-popper-paper","sui-autocomplete-popper-empty":"sui-autocomplete-popper-empty","sui-autocomplete-status-container":"sui-autocomplete-status-container","sui-autocomplete-status-label":"sui-autocomplete-status-label"});
1043
+
1044
+
1045
+
1046
+
1047
+
1048
+
1049
+
844
1050
 
845
- var Autocomplete$1 = {"sui-autocomplete-chevron":"sui-autocomplete-chevron","sui-autocomplete-clear":"sui-autocomplete-clear","sui-autocomplete-root":"sui-autocomplete-root","sui-focused":"sui-focused","sui-disabled":"sui-disabled","sui-autocomplete-input-container":"sui-autocomplete-input-container","sui-autocomplete-form-field-content":"sui-autocomplete-form-field-content","sui-autocomplete-form-field-content-xlarge":"sui-autocomplete-form-field-content-xlarge","sui-autocomplete-form-field-content-large":"sui-autocomplete-form-field-content-large","sui-autocomplete-form-field-content-medium":"sui-autocomplete-form-field-content-medium","sui-autocomplete-input-container-xlarge":"sui-autocomplete-input-container-xlarge","sui-autocomplete-input-container-large":"sui-autocomplete-input-container-large","sui-autocomplete-input-container-medium":"sui-autocomplete-input-container-medium","sui-autocomplete-input":"sui-autocomplete-input","sui-autocomplete-input-xlarge":"sui-autocomplete-input-xlarge","sui-autocomplete-input-large":"sui-autocomplete-input-large","sui-autocomplete-input-medium":"sui-autocomplete-input-medium","sui-autocomplete-outlined-expanded":"sui-autocomplete-outlined-expanded","sui-autocomplete-outlined-expanded-chevron":"sui-autocomplete-outlined-expanded-chevron","sui-autocomplete-standard-expanded-chevron":"sui-autocomplete-standard-expanded-chevron","sui-autocomplete-error-chevron":"sui-autocomplete-error-chevron","sui-autocomplete-end-adornment":"sui-autocomplete-end-adornment","sui-autocomplete-popper-root":"sui-autocomplete-popper-root","sui-popper-widget":"sui-popper-widget","sui-autocomplete-popper-paper":"sui-autocomplete-popper-paper","sui-autocomplete-popper-empty":"sui-autocomplete-popper-empty","sui-autocomplete-status-container":"sui-autocomplete-status-container","sui-autocomplete-status-label":"sui-autocomplete-status-label"};
846
1051
 
847
1052
  function stringArrToRegExp(keyToTags) {
848
1053
  return new RegExp(keyToTags?.join('|') || '', 'g');
849
1054
  }
850
1055
  const TAG_TOKEN = 'rc-chip-';
851
- const DEFAULT_LIMIT_CHIPS$1 = 20;
852
- const DEFAULT_KEY_TO_CHIPS = [';', ',', '\\n'];
853
- 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, }) => {
1056
+ const DEFAULT_LIMIT_CHIPS = 20;
1057
+ const DEFAULT_KEY_TO_CHIPS = [
1058
+ ';',
1059
+ ',',
1060
+ '\\n'
1061
+ ];
1062
+ /** @inner inner hook work with useAutocomplete */ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [], onChange: onSelectChange, variant, getOptionLabel = DEFAULT_GET_OPTION_LABEL, onInputChange: onInputChangeProp, freeSolo, multiple, wrapperRef, inputRef, maxFreeSolo = DEFAULT_LIMIT_CHIPS, keyToTags = DEFAULT_KEY_TO_CHIPS, onMaxFreeSolo, required, disabled, getStopCreateFreeSolo, onReset, onAddTags })=>{
854
1063
  const isAutocomplete = variant === 'autocomplete';
855
1064
  const isSelectedFromAutocompleteRef = useRef(false);
856
1065
  const [tags, _setTags] = useControlled({
857
1066
  controlled: selectedItemsProp,
858
1067
  default: [],
859
- name: 'useAutocompleteTag',
1068
+ name: 'useAutocompleteTag'
860
1069
  });
861
1070
  const [focused, setFocused] = useState(false);
862
1071
  const [focusedIndexRef, setFocusedIndex] = useRefState(0);
863
- const freeSoloCount = useMemo(() => tags.filter((x) => x.freeSolo).length, [tags]);
864
- const setTags = (_selectedItems) => {
1072
+ const freeSoloCount = useMemo(()=>tags.filter((x)=>x.freeSolo).length, [
1073
+ tags
1074
+ ]);
1075
+ const setTags = (_selectedItems)=>{
865
1076
  _setTags(_selectedItems);
866
1077
  onSelectChange?.(_selectedItems);
867
1078
  if (isAutocomplete && _selectedItems.length === 1) {
@@ -870,38 +1081,26 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
870
1081
  isSelectedFromAutocompleteRef.current = true;
871
1082
  }
872
1083
  };
873
- const _focusTag = (tagToFocus) => {
1084
+ const _focusTag = (tagToFocus)=>{
874
1085
  const textFieldElm = wrapperRef?.current;
875
1086
  const inputElm = inputRef.current;
876
1087
  if (tagToFocus === -1) {
877
1088
  inputElm?.focus();
878
- }
879
- else {
880
- textFieldElm
881
- ?.querySelector(`[data-tag-index="${tagToFocus}"]`)
882
- ?.focus();
1089
+ } else {
1090
+ textFieldElm?.querySelector(`[data-tag-index="${tagToFocus}"]`)?.focus();
883
1091
  }
884
1092
  };
885
- const focusTag = useEventCallback((tagToFocus, focus = true) => {
886
- if (focusedIndexRef.current === tagToFocus)
887
- return;
1093
+ const focusTag = useEventCallback((tagToFocus, focus = true)=>{
1094
+ if (focusedIndexRef.current === tagToFocus) return;
888
1095
  setFocusedIndex(tagToFocus);
889
- if (focus)
890
- _focusTag(tagToFocus);
1096
+ if (focus) _focusTag(tagToFocus);
891
1097
  });
892
- const checkAndAddFreeSolo = ({ selectedItem, value, } = {}, e) => {
1098
+ /** when return `true` mean add item success */ const checkAndAddFreeSolo = ({ selectedItem, value } = {}, e)=>{
893
1099
  let isAddItem = false;
894
1100
  const currentValue = value ?? inputRef.current?.value;
895
- if (!getStopCreateFreeSolo?.() &&
896
- !selectedItem &&
897
- freeSolo &&
898
- currentValue &&
899
- currentValue.length > 0) {
1101
+ if (!getStopCreateFreeSolo?.() && !selectedItem && freeSolo && currentValue && currentValue.length > 0) {
900
1102
  if (freeSoloCount < maxFreeSolo) {
901
- const items = currentValue
902
- .trim()
903
- .split(stringArrToRegExp(keyToTags))
904
- .filter((x) => x.trim() !== '');
1103
+ const items = currentValue.trim().split(stringArrToRegExp(keyToTags)).filter((x)=>x.trim() !== '');
905
1104
  const toLength = freeSoloCount + items.length;
906
1105
  if (toLength > maxFreeSolo) {
907
1106
  items.splice(-(toLength - maxFreeSolo));
@@ -910,116 +1109,122 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
910
1109
  if (items.length > 0) {
911
1110
  addTags([
912
1111
  ...tags,
913
- ...items.map((label) => ({
914
- id: uniqueId(TAG_TOKEN),
915
- freeSolo: true,
916
- label,
917
- })),
1112
+ ...items.map((label)=>({
1113
+ id: uniqueId(TAG_TOKEN),
1114
+ freeSolo: true,
1115
+ label
1116
+ }))
918
1117
  ]);
919
1118
  isAddItem = true;
920
1119
  }
921
- }
922
- else {
1120
+ } else {
923
1121
  onMaxFreeSolo?.(maxFreeSolo);
924
1122
  }
925
1123
  onReset?.(e);
926
1124
  }
927
1125
  return isAddItem;
928
1126
  };
929
- const onInputChange = (e) => {
1127
+ const onInputChange = (e)=>{
930
1128
  const newValue = e.target['value'];
931
1129
  if (stringArrToRegExp(keyToTags).test(newValue || '')) {
932
- return checkAndAddFreeSolo({ value: newValue });
1130
+ return checkAndAddFreeSolo({
1131
+ value: newValue
1132
+ });
933
1133
  }
934
1134
  return false;
935
1135
  };
936
- const removeTag = (selectedItem) => {
1136
+ const removeTag = (selectedItem)=>{
937
1137
  const selectedItemIndex = tags.indexOf(selectedItem);
938
1138
  if (selectedItemIndex > -1) {
939
1139
  setTags([
940
1140
  ...tags.slice(0, selectedItemIndex),
941
- ...tags.slice(selectedItemIndex + 1),
1141
+ ...tags.slice(selectedItemIndex + 1)
942
1142
  ]);
943
1143
  }
944
1144
  };
945
- const addTags = (tags) => {
1145
+ const addTags = (tags)=>{
946
1146
  setTags(tags);
947
1147
  onAddTags?.();
948
1148
  };
949
- const addTagOnSelect = (selectedItem) => {
1149
+ const addTagOnSelect = (selectedItem)=>{
1150
+ // * change to uniqueId
950
1151
  if (selectedItem.freeSolo) {
951
1152
  if (freeSoloCount < maxFreeSolo) {
952
1153
  selectedItem.id = uniqueId(TAG_TOKEN);
953
- }
954
- else {
1154
+ } else {
955
1155
  onMaxFreeSolo?.(maxFreeSolo);
956
1156
  return;
957
1157
  }
958
1158
  }
959
1159
  if (multiple) {
960
- addTags([...tags, selectedItem]);
961
- }
962
- else {
963
- addTags([selectedItem]);
1160
+ addTags([
1161
+ ...tags,
1162
+ selectedItem
1163
+ ]);
1164
+ } else {
1165
+ addTags([
1166
+ selectedItem
1167
+ ]);
964
1168
  }
965
1169
  };
966
1170
  const { onKeyFocusedIndexHandle: handleTagKey } = useKeyboardMoveFocus({
967
1171
  options: tags,
968
1172
  focusedIndexRef,
969
1173
  infinite: 'order',
970
- onFocusedIndexChange: (event, toIndex) => {
1174
+ onFocusedIndexChange: (event, toIndex)=>{
971
1175
  focusTag(toIndex);
972
1176
  event?.preventDefault();
973
- },
1177
+ }
974
1178
  });
975
- const getTagListBoxProps = (props) => {
1179
+ /**
1180
+ * All Get props methods
1181
+ */ const getTagListBoxProps = (props)=>{
976
1182
  return {
977
1183
  'aria-required': required || false,
978
1184
  'aria-disabled': disabled || false,
979
- ...props,
1185
+ // TODO: wait for input can complete that.
1186
+ // 'aria-multiselectable': false,
1187
+ // role: selectedItems.length > 0 ? 'listbox' : undefined,
1188
+ // 'aria-orientation': 'horizontal',
1189
+ ...props
980
1190
  };
981
1191
  };
982
- const getTagProps = ({ index, selectedItem, ...restTagProps }) => {
983
- const tabIndex = !focused &&
984
- index === 0
985
- ? 0
986
- : -1;
1192
+ const getTagProps = ({ index, selectedItem, ...restTagProps })=>{
1193
+ const tabIndex = // * when tag is not focus
1194
+ !focused && // * and is first item
1195
+ index === 0 ? 0 : -1;
987
1196
  return combineProps({
988
1197
  tabIndex,
1198
+ // TODO: wait for input can complete that.
1199
+ // role: 'option',
989
1200
  role: 'button',
990
1201
  key: `${AutocompleteId}-selected-item-${index}`,
991
1202
  'data-tag-index': index,
992
1203
  'data-item-last': index === tags.length - 1,
993
- onClick: (e) => {
1204
+ onClick: (e)=>{
994
1205
  e.stopPropagation();
995
1206
  },
996
- onBlur: () => setFocused(false),
997
- onFocus: () => {
1207
+ onBlur: ()=>setFocused(false),
1208
+ onFocus: ()=>{
998
1209
  focusTag(index);
999
1210
  setFocused(true);
1000
1211
  },
1001
- onKeyDown: (e) => {
1212
+ onKeyDown: (e)=>{
1002
1213
  const focusedIndex = focusedIndexRef.current;
1003
- switch (e.key) {
1214
+ switch(e.key){
1004
1215
  case 'Backspace':
1005
1216
  {
1006
1217
  const lengthBeforeDelete = tags.length;
1007
- focusTag(lengthBeforeDelete === 1
1008
- ? -1
1009
- : focusedIndex === 0
1010
- ? 0
1011
- : focusedIndex - 1);
1218
+ focusTag(// * when length is 1 mean that will be remove all
1219
+ lengthBeforeDelete === 1 ? -1 : focusedIndex === 0 ? 0 : focusedIndex - 1);
1012
1220
  removeTag(selectedItem);
1013
1221
  }
1014
1222
  break;
1015
1223
  case 'Delete':
1016
1224
  {
1017
1225
  const lengthBeforeDelete = tags.length;
1018
- focusTag(lengthBeforeDelete === 1
1019
- ? -1
1020
- : focusedIndex === tags.length - 1
1021
- ? focusedIndex - 1
1022
- : focusedIndex);
1226
+ focusTag(// * when length is 1 mean that will be remove all
1227
+ lengthBeforeDelete === 1 ? -1 : focusedIndex === tags.length - 1 ? focusedIndex - 1 : focusedIndex);
1023
1228
  removeTag(selectedItem);
1024
1229
  }
1025
1230
  break;
@@ -1028,13 +1233,13 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
1028
1233
  break;
1029
1234
  }
1030
1235
  },
1031
- onDelete: (e) => {
1032
- if (e.button !== 0)
1033
- return;
1236
+ onDelete: (e)=>{
1237
+ // * only allow left button click
1238
+ if (e.button !== 0) return;
1034
1239
  removeTag(selectedItem);
1035
1240
  setFocused(false);
1036
1241
  inputRef.current?.focus();
1037
- },
1242
+ }
1038
1243
  }, restTagProps);
1039
1244
  };
1040
1245
  return {
@@ -1049,39 +1254,43 @@ const useAutocompleteTag = ({ id: AutocompleteId, value: selectedItemsProp = [],
1049
1254
  getTagProps,
1050
1255
  focusTag,
1051
1256
  addTagOnSelect,
1052
- clearTags: () => setTags([]),
1053
- blur: () => {
1257
+ clearTags: ()=>setTags([]),
1258
+ blur: ()=>{
1054
1259
  if (focusedIndexRef.current !== -1) {
1055
1260
  focusTag(-1, false);
1056
1261
  }
1057
1262
  setFocused(false);
1058
1263
  },
1059
- reset: () => focusTag(-1),
1060
- focusLast: () => {
1264
+ reset: ()=>focusTag(-1),
1265
+ focusLast: ()=>{
1061
1266
  const currTagsLength = tags.length;
1062
- if (currTagsLength <= 0)
1063
- return;
1267
+ if (currTagsLength <= 0) return;
1064
1268
  focusTag(currTagsLength - 1);
1065
- },
1269
+ }
1066
1270
  };
1067
1271
  };
1068
1272
 
1273
+
1274
+
1275
+
1276
+
1277
+
1069
1278
  const EMPTY_TAG_TOKEN = 'rc-chip-empty';
1070
- const DEFAULT_HIGHLIGHTED_INDEX = -1;
1071
- const DEFAULT_LIMIT_CHIPS = 20;
1072
- 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, }) => {
1279
+ const useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX = -1;
1280
+ const useAutocomplete_DEFAULT_LIMIT_CHIPS = 20;
1281
+ 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 = useAutocomplete_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 })=>{
1073
1282
  const isAutocomplete = variant === 'autocomplete';
1074
- const autoCompleteSelectedIndexRef = useRef(DEFAULT_HIGHLIGHTED_INDEX);
1283
+ const autoCompleteSelectedIndexRef = useRef(useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX);
1075
1284
  const AutocompleteId = useId('Autocomplete', true);
1076
1285
  const [inputFocused, setInputFocused] = useControlled({
1077
1286
  controlled: focused,
1078
1287
  default: false,
1079
- name: 'useAutocomplete',
1288
+ name: 'useAutocomplete'
1080
1289
  });
1081
1290
  const [isOpen, setIsOpen] = useControlled({
1082
1291
  controlled: openProp,
1083
1292
  default: initialIsOpen || false,
1084
- name: 'useAutocomplete',
1293
+ name: 'useAutocomplete'
1085
1294
  });
1086
1295
  const isInputValueChangedRef = useRef(false);
1087
1296
  const isCompositionRef = useRef(false);
@@ -1092,16 +1301,17 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1092
1301
  const popperElementRef = useRef(null);
1093
1302
  const { sleep, getSleeping } = useSleep();
1094
1303
  const { requestAnimationFrame } = useAnimationFrame();
1304
+ // * when that is autocomplete, that will never be multiple
1095
1305
  const multiple = isAutocomplete ? false : multipleProp;
1096
- const inputContainerScrollToBottom = () => {
1097
- requestAnimationFrame(() => {
1306
+ const inputContainerScrollToBottom = ()=>{
1307
+ requestAnimationFrame(()=>{
1098
1308
  const inputContainer = inputContainerRef?.current;
1099
1309
  if (inputContainer) {
1100
1310
  inputContainer.scrollTop = inputContainer.scrollHeight;
1101
1311
  }
1102
1312
  });
1103
1313
  };
1104
- const { isSelectedFromAutocompleteRef, tags, focused: tagFocused, addTagOnSelect, checkAndAddFreeSolo, focusedIndex: focusedTagIndex, focusTag, focusLast: focusLastTag, clearTags, blur: blurTags, getTagProps, getTagListBoxProps, } = useAutocompleteTag({
1314
+ const { isSelectedFromAutocompleteRef, tags, focused: tagFocused, addTagOnSelect, checkAndAddFreeSolo, focusedIndex: focusedTagIndex, focusTag, focusLast: focusLastTag, clearTags, blur: blurTags, getTagProps, getTagListBoxProps } = useAutocompleteTag({
1105
1315
  id: AutocompleteId,
1106
1316
  value: selectedItemsProp,
1107
1317
  onChange: onSelectChange,
@@ -1117,50 +1327,58 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1117
1327
  multiple,
1118
1328
  required,
1119
1329
  disabled,
1120
- getStopCreateFreeSolo: () => isCompositionRef.current,
1121
- onReset: () => {
1330
+ // * when is composition, stop freeSolo create new item
1331
+ getStopCreateFreeSolo: ()=>isCompositionRef.current,
1332
+ onReset: ()=>{
1122
1333
  resetState();
1123
1334
  },
1124
- onAddTags: inputContainerScrollToBottom,
1335
+ onAddTags: inputContainerScrollToBottom
1125
1336
  });
1126
- const processFilteredResult = useCallback((results, inputValue = '') => {
1127
- const getInputValueAsItem = () => {
1337
+ const processFilteredResult = useCallback((results, inputValue = '')=>{
1338
+ const getInputValueAsItem = ()=>{
1128
1339
  const label = inputValue.trim();
1129
1340
  if (label.length > 0) {
1130
1341
  return {
1131
1342
  id: EMPTY_TAG_TOKEN,
1132
1343
  freeSolo: true,
1133
- label,
1344
+ label
1134
1345
  };
1135
1346
  }
1136
1347
  return null;
1137
1348
  };
1349
+ // * only when isOpen calculate the filtered result
1138
1350
  noOptionItemRef.current = null;
1139
1351
  if (addNoOptionItem) {
1140
1352
  const item = getInputValueAsItem();
1141
1353
  noOptionItemRef.current = item;
1142
1354
  if (item && freeSolo) {
1143
- switch (addNoOptionItem) {
1355
+ switch(addNoOptionItem){
1144
1356
  case 'first':
1145
1357
  noOptionItemRef.current = null;
1146
- return [item, ...results];
1358
+ return [
1359
+ item,
1360
+ ...results
1361
+ ];
1147
1362
  case 'last':
1148
1363
  noOptionItemRef.current = null;
1149
- return [...results, item];
1364
+ return [
1365
+ ...results,
1366
+ item
1367
+ ];
1368
+ default:
1369
+ break;
1150
1370
  }
1151
1371
  }
1152
1372
  if (results.length === 0 && renderNoOptions === true) {
1153
1373
  if (item) {
1154
1374
  noOptionItemRef.current = item;
1155
- }
1156
- else {
1375
+ } else {
1157
1376
  noOptionItemRef.current = {
1158
1377
  id: EMPTY_TAG_TOKEN,
1159
- label: '',
1378
+ label: ''
1160
1379
  };
1161
1380
  }
1162
- }
1163
- else if (results.length > 0) {
1381
+ } else if (results.length > 0) {
1164
1382
  noOptionItemRef.current = null;
1165
1383
  }
1166
1384
  return results;
@@ -1168,36 +1386,48 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1168
1386
  if (results.length === 0) {
1169
1387
  const item = getInputValueAsItem();
1170
1388
  if (renderNoOptions === true) {
1389
+ // * when using boolean renderNoOptions, always create a noOptionItem
1171
1390
  if (item) {
1172
1391
  noOptionItemRef.current = item;
1173
- }
1174
- else {
1392
+ } else {
1175
1393
  noOptionItemRef.current = {
1176
1394
  id: EMPTY_TAG_TOKEN,
1177
- label: '',
1395
+ label: ''
1178
1396
  };
1179
1397
  }
1180
- }
1181
- else {
1398
+ } else {
1399
+ // * keep existing behavior for function / undefined renderNoOptions
1182
1400
  noOptionItemRef.current = item;
1183
1401
  }
1402
+ // * only when not set render no options will auto add item
1184
1403
  if (item && freeSolo && !renderNoOptions) {
1185
- return [...results, item];
1404
+ // * when outside not use filter, that will be original array, must use spread
1405
+ return [
1406
+ ...results,
1407
+ item
1408
+ ];
1186
1409
  }
1187
1410
  }
1188
1411
  return results;
1189
- }, [addNoOptionItem, freeSolo, renderNoOptions]);
1190
- const filterOptions = useCallback((items, state) => {
1412
+ }, [
1413
+ addNoOptionItem,
1414
+ freeSolo,
1415
+ renderNoOptions
1416
+ ]);
1417
+ const filterOptions = useCallback((items, state)=>{
1191
1418
  if (filterOptionsProp) {
1192
1419
  return filterOptionsProp(items, {
1193
1420
  ...state,
1194
1421
  inputChanged: isInputValueChangedRef.current,
1195
- selectedItems: tags,
1422
+ selectedItems: tags
1196
1423
  });
1197
1424
  }
1198
1425
  return items;
1199
- }, [filterOptionsProp, tags]);
1200
- const { inputValue, optionItems, optionsGroupList, highlightedIndexRef, changeHighlightedIndexReasonRef, forceUpdate, selectItem, handleF10KeyDown, onKeyFocusedIndexHandle, setHighlightedIndex, updateInputValue, clearInput, getNextFocusableOption, getItemProps: getSuggestionListItemProps, focusInput, reset: resetSuggestionList, getInputProps: getSuggestionListInputProps, getInputAriaProps: getSuggestionListInputAriaProps, getClearButtonProps: getSuggestionListClearButtonProps, getLabelProps, getSuggestionListProps, } = useSuggestionList({
1426
+ }, [
1427
+ filterOptionsProp,
1428
+ tags
1429
+ ]);
1430
+ const { inputValue, optionItems, optionsGroupList, highlightedIndexRef, changeHighlightedIndexReasonRef, forceUpdate, selectItem, handleF10KeyDown, onKeyFocusedIndexHandle, setHighlightedIndex, updateInputValue, clearInput, getNextFocusableOption, getItemProps: getSuggestionListItemProps, focusInput, reset: resetSuggestionList, getInputProps: getSuggestionListInputProps, getInputAriaProps: getSuggestionListInputAriaProps, getClearButtonProps: getSuggestionListClearButtonProps, getLabelProps, getSuggestionListProps } = useSuggestionList({
1201
1431
  id: AutocompleteId,
1202
1432
  inputRef,
1203
1433
  inputValue: inputValueProp,
@@ -1217,35 +1447,36 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1217
1447
  onGroupExpanded,
1218
1448
  getExpandIconProps,
1219
1449
  isOptionEqualToValue,
1220
- onSelect: (e, selectedItem) => {
1450
+ onSelect: (e, selectedItem)=>{
1221
1451
  addTagOnSelect(selectedItem);
1222
1452
  resetState(e);
1223
1453
  if (disableCloseOnSelect) {
1224
1454
  if (autoHighlight) {
1225
1455
  const currIndex = highlightedIndexRef.current;
1226
1456
  const nextValidIndex = getNextFocusableOption();
1227
- const toIndex = nextValidIndex > currIndex ||
1228
- nextValidIndex === optionItems.length - 1
1229
- ? nextValidIndex - 1
1230
- : nextValidIndex;
1457
+ const toIndex = // * if that index is bigger than current index or nextValidIndex is last index,
1458
+ // * cut 1 (because current item will be remove)
1459
+ nextValidIndex > currIndex || nextValidIndex === optionItems.length - 1 ? nextValidIndex - 1 : nextValidIndex;
1231
1460
  setHighlightedIndex(toIndex, {
1232
- reRender: true,
1461
+ reRender: true
1233
1462
  });
1234
- }
1235
- else {
1236
- highlightedIndexRef.current = DEFAULT_HIGHLIGHTED_INDEX;
1463
+ } else {
1464
+ highlightedIndexRef.current = useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX;
1237
1465
  }
1238
1466
  }
1239
- },
1467
+ }
1240
1468
  });
1241
- const getItemProps = (props) => {
1469
+ const getItemProps = (props)=>{
1242
1470
  return getSuggestionListItemProps(props);
1243
1471
  };
1244
1472
  const readOnly = !isAutocomplete && !multiple && tags.length >= 1 ? true : undefined;
1245
- const closeMenu = (e, reason) => {
1473
+ const closeMenu = (e, reason)=>{
1246
1474
  keepHighlightedIndexRef.current = false;
1247
1475
  isInputValueChangedRef.current = false;
1248
- setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
1476
+ // * set to default highlighted
1477
+ setHighlightedIndex(useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX, {
1478
+ reason: 'auto'
1479
+ });
1249
1480
  if (isOpen) {
1250
1481
  if (e && reason) {
1251
1482
  onClose?.(e, reason);
@@ -1253,43 +1484,45 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1253
1484
  setIsOpen(false);
1254
1485
  }
1255
1486
  };
1256
- const openMenu = (e) => {
1487
+ const openMenu = (e)=>{
1257
1488
  if (!isOpen) {
1258
- if (e)
1259
- onOpen?.(e);
1489
+ if (e) onOpen?.(e);
1260
1490
  setIsOpen(true);
1261
1491
  }
1262
1492
  };
1263
- const toggleMenu = (e, focus = true) => {
1493
+ const toggleMenu = (e, focus = true)=>{
1264
1494
  if (isOpen) {
1265
1495
  closeMenu(e, 'toggleInput');
1266
- }
1267
- else {
1496
+ } else {
1268
1497
  openMenu(e);
1269
- if (focus)
1270
- focusInput();
1498
+ if (focus) focusInput();
1271
1499
  }
1272
1500
  };
1273
- const handleInputChange = (newValue, open = true, e) => {
1501
+ const handleInputChange = (newValue, open = true, e)=>{
1274
1502
  if (inputValue !== newValue) {
1275
- setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
1503
+ setHighlightedIndex(useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX, {
1504
+ reason: 'auto'
1505
+ });
1506
+ // !!! Get regexp every time for prevent bug with browser
1507
+ // https://stackoverflow.com/questions/3827456/what-is-wrong-with-my-date-regex/3827500#3827500
1276
1508
  if (stringArrToRegExp(keyToTags).test((newValue || '').trim())) {
1277
- const resultHaveAdd = checkAndAddFreeSolo({ value: newValue }, e);
1509
+ const resultHaveAdd = checkAndAddFreeSolo({
1510
+ value: newValue
1511
+ }, e);
1278
1512
  if (!disableCloseOnSelect) {
1279
1513
  closeMenu(e, 'toggleInput');
1280
- }
1281
- else if (open) {
1514
+ } else if (open) {
1282
1515
  openMenu(e);
1283
1516
  }
1284
1517
  return resultHaveAdd;
1285
1518
  }
1286
1519
  updateInputValue(newValue);
1287
1520
  }
1288
- if (open)
1289
- openMenu(e);
1521
+ if (open) openMenu(e);
1290
1522
  return false;
1291
1523
  };
1292
- const resetState = (e) => {
1524
+ const resetState = (e)=>{
1525
+ // * when autocomplete select mode not reset input value
1293
1526
  if (!isSelectedFromAutocompleteRef.current) {
1294
1527
  clearInput();
1295
1528
  }
@@ -1301,148 +1534,164 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1301
1534
  closeMenu(e, 'select-option');
1302
1535
  }
1303
1536
  };
1304
- const reset = (isFocus) => {
1537
+ const reset = (isFocus)=>{
1305
1538
  resetState();
1306
1539
  onSelectChange?.([]);
1307
1540
  resetSuggestionList(isFocus);
1308
1541
  };
1309
- const keepHighlightedIndex = () => {
1542
+ const keepHighlightedIndex = ()=>{
1310
1543
  keepHighlightedIndexRef.current = true;
1311
1544
  };
1312
- useChange(() => {
1545
+ // * when outside options props props change also need set highlightedIndex to default
1546
+ useChange(()=>{
1313
1547
  const shouldResetHighlightedIndex = !keepHighlightedIndexRef.current;
1314
1548
  if (options !== undefined && shouldResetHighlightedIndex) {
1315
- setHighlightedIndex(DEFAULT_HIGHLIGHTED_INDEX, { reason: 'auto' });
1549
+ setHighlightedIndex(useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX, {
1550
+ reason: 'auto'
1551
+ });
1316
1552
  }
1317
- }, () => options);
1318
- const defaultHighlightedIndex = useMemo(() => {
1553
+ }, ()=>options);
1554
+ // * check that defaultHighlightedIndex
1555
+ const defaultHighlightedIndex = useMemo(()=>{
1319
1556
  return autoHighlight ? getNextFocusableOption(0) : -1;
1320
- }, [autoHighlight, getNextFocusableOption]);
1557
+ }, [
1558
+ autoHighlight,
1559
+ getNextFocusableOption
1560
+ ]);
1561
+ // * only when menu is open set to defaultHighlightedIndex
1321
1562
  if (isOpen) {
1322
- if (highlightedIndexRef.current === DEFAULT_HIGHLIGHTED_INDEX) {
1563
+ if (highlightedIndexRef.current === useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX) {
1323
1564
  let toIndex = defaultHighlightedIndex;
1324
1565
  if (isAutocomplete && tags.length === 1) {
1325
1566
  const itemText = getOptionLabel(tags[0]);
1326
- const fIndex = optionItems.findIndex((x) => getOptionLabel(x) === itemText);
1567
+ const fIndex = optionItems.findIndex((x)=>getOptionLabel(x) === itemText);
1327
1568
  if (fIndex > -1) {
1328
1569
  toIndex = fIndex;
1329
1570
  autoCompleteSelectedIndexRef.current = fIndex;
1330
1571
  }
1331
1572
  }
1332
- setHighlightedIndex(toIndex, { reason: 'auto' });
1333
- }
1334
- else {
1573
+ setHighlightedIndex(toIndex, {
1574
+ reason: 'auto'
1575
+ });
1576
+ } else {
1335
1577
  const currIndex = highlightedIndexRef.current;
1336
1578
  const availableIndex = getNextFocusableOption(currIndex);
1337
1579
  if (currIndex !== availableIndex) {
1338
- requestAnimationFrame(() => {
1580
+ requestAnimationFrame(()=>{
1339
1581
  setHighlightedIndex(availableIndex, {
1340
1582
  reason: 'auto',
1341
- reRender: true,
1583
+ reRender: true
1342
1584
  });
1343
1585
  });
1344
1586
  }
1345
1587
  }
1346
1588
  }
1347
- const handleAutocompleteText = () => {
1589
+ const handleAutocompleteText = ()=>{
1348
1590
  if (isAutocomplete && tags.length > 0) {
1349
1591
  const result = getOptionLabel(tags[0]);
1350
1592
  onInputChangeProp?.(result);
1351
1593
  }
1352
1594
  };
1353
- useLayoutEffect(() => {
1595
+ // * use force update to sync open state when init is open
1596
+ useLayoutEffect(()=>{
1354
1597
  if (isOpen) {
1355
1598
  forceUpdate();
1356
1599
  }
1357
1600
  handleAutocompleteText();
1601
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
1358
1602
  }, []);
1359
- const getInputProps = (props) => {
1603
+ const getInputProps = (props)=>{
1360
1604
  const suggestionListInputProps = getSuggestionListInputProps(props);
1361
1605
  return combineProps({
1362
1606
  ...suggestionListInputProps,
1363
- onPaste: (e) => {
1607
+ onPaste: (e)=>{
1364
1608
  if (freeSolo) {
1365
1609
  const clipboardData = e.clipboardData;
1366
1610
  const { position } = getSelectionPosition(inputRef);
1367
1611
  const currText = inputValue ?? '';
1368
1612
  const pasteText = clipboardData.getData('Text') ?? '';
1369
- fromPasteString.current =
1370
- currText.slice(0, position.start) +
1371
- pasteText +
1372
- currText.slice(position.end);
1613
+ // * because input can't get next line char(\n), we need get that value in onPaste
1614
+ // * get current input result use paste
1615
+ // * remember combine current input value, that will be use in onChange
1616
+ fromPasteString.current = currText.slice(0, position.start) + pasteText + currText.slice(position.end);
1373
1617
  }
1374
1618
  },
1375
- onChange: (e) => {
1619
+ onChange: (e)=>{
1376
1620
  const changeValue = fromPasteString.current || e.target.value;
1377
1621
  handleInputChange(changeValue, undefined, e);
1622
+ // * when input value clear all, clear selected item
1378
1623
  if (isAutocomplete && changeValue.length === 0) {
1379
1624
  clearTags();
1380
1625
  }
1381
1626
  fromPasteString.current = '';
1382
1627
  isInputValueChangedRef.current = true;
1383
1628
  },
1384
- onFocus: (e) => {
1629
+ onFocus: (e)=>{
1385
1630
  setInputFocused(true);
1386
- if (openOnFocus)
1387
- openMenu(e);
1631
+ if (openOnFocus) openMenu(e);
1632
+ // * when autocomplete mode, always select all text when focus
1388
1633
  if (isAutocomplete) {
1389
1634
  inputRef.current?.select();
1390
1635
  }
1636
+ // * reset stopAutoSelect in focus input
1391
1637
  stopAutoSelectRef.current = false;
1392
1638
  blurTags();
1393
1639
  },
1394
- onBlur: (e) => {
1640
+ onBlur: (e)=>{
1395
1641
  setInputFocused(false);
1396
- if (popperElementRef.current?.contains(e.relatedTarget))
1397
- return;
1642
+ // mouse down suggestion item will change focus,
1643
+ // but we don't want to close popper in this case
1644
+ if (popperElementRef.current?.contains(e.relatedTarget)) return;
1398
1645
  if (autoSelect && !stopAutoSelectRef.current) {
1399
- if (!freeSolo)
1400
- selectItem(e, optionItems[highlightedIndexRef.current]);
1646
+ if (!freeSolo) selectItem(e, optionItems[highlightedIndexRef.current]);
1401
1647
  checkAndAddFreeSolo({}, e);
1402
1648
  }
1649
+ // * when blur should restore text when have selected item
1403
1650
  handleAutocompleteText();
1404
1651
  closeMenu(e, 'blur');
1405
1652
  },
1406
- onCompositionStart: () => {
1653
+ onCompositionStart: ()=>{
1407
1654
  isCompositionRef.current = true;
1408
1655
  },
1409
- onCompositionEnd: async () => {
1656
+ onCompositionEnd: async ()=>{
1657
+ // sleep for prevent tab in composition
1410
1658
  await sleep(20);
1411
1659
  isCompositionRef.current = false;
1412
1660
  },
1413
- onKeyDown: (e) => {
1661
+ onKeyDown: (e)=>{
1414
1662
  keepHighlightedIndexRef.current = false;
1415
1663
  onKeyDownProp?.(e, highlightedIndexRef.current);
1416
1664
  const { isSelectRange, position } = getSelectionPosition(inputRef);
1417
1665
  const currTagsLength = tags.length;
1418
- if (!isSelectRange &&
1419
- position.start === 0 &&
1420
- currTagsLength > 0 &&
1421
- e.key === 'Backspace') {
1666
+ if (!isSelectRange && position.start === 0 && currTagsLength > 0 && e.key === 'Backspace') {
1422
1667
  stopAutoSelectRef.current = true;
1423
1668
  focusLastTag();
1424
- }
1425
- else {
1669
+ } else {
1426
1670
  const highlightedIndex = highlightedIndexRef.current;
1427
- switch (e.key) {
1671
+ switch(e.key){
1428
1672
  case 'Escape':
1429
1673
  if (isOpen) {
1674
+ // Avoid Opera to exit fullscreen mode.
1430
1675
  e.preventDefault();
1676
+ // Avoid the Modal to handle the event.
1431
1677
  e.stopPropagation();
1432
1678
  closeMenu(e, 'escape');
1433
1679
  }
1434
1680
  break;
1435
1681
  case 'Tab':
1436
- if (e.which === 229)
1437
- return;
1682
+ if (e.which === 229) return;
1438
1683
  if (getSleeping()) {
1439
1684
  e.preventDefault();
1440
1685
  e.stopPropagation();
1441
1686
  return;
1442
1687
  }
1688
+ // * when shift+ tab also not need autoSelect
1443
1689
  if (e.shiftKey && currTagsLength > 0) {
1444
1690
  stopAutoSelectRef.current = true;
1445
1691
  }
1692
+ // ! [Browser] when in composition,
1693
+ // ! and keydown 'Tab' that will trigger twice keydown,
1694
+ // ! must check is that is not in composition mode
1446
1695
  if (!e.shiftKey && !isCompositionRef.current) {
1447
1696
  const currentHighlightItem = optionItems[highlightedIndex];
1448
1697
  if (isOpen && currentHighlightItem) {
@@ -1450,32 +1699,24 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1450
1699
  e.preventDefault();
1451
1700
  e.stopPropagation();
1452
1701
  }
1453
- }
1454
- else if (checkAndAddFreeSolo({}, e)) {
1702
+ } else if (checkAndAddFreeSolo({}, e)) {
1455
1703
  e.preventDefault();
1456
1704
  e.stopPropagation();
1457
1705
  }
1458
1706
  }
1459
1707
  break;
1460
1708
  case 'Enter':
1461
- if (e.which === 229)
1462
- return;
1463
- if (freeSolo &&
1464
- highlightedIndex === DEFAULT_HIGHLIGHTED_INDEX &&
1465
- checkAndAddFreeSolo({
1466
- selectedItem: isOpen
1467
- ? optionItems[highlightedIndex]
1468
- : undefined,
1469
- }, e)) {
1709
+ if (e.which === 229) return;
1710
+ if (freeSolo && highlightedIndex === useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX && checkAndAddFreeSolo({
1711
+ selectedItem: isOpen ? optionItems[highlightedIndex] : undefined
1712
+ }, e)) {
1470
1713
  e.stopPropagation();
1471
1714
  closeMenu(e, 'select-option');
1472
- }
1473
- else if (highlightedIndex !== DEFAULT_HIGHLIGHTED_INDEX &&
1474
- isOpen &&
1475
- !isCompositionRef.current) {
1715
+ } else if (highlightedIndex !== useAutocomplete_DEFAULT_HIGHLIGHTED_INDEX && isOpen && !isCompositionRef.current) {
1476
1716
  selectItem(e, optionItems[highlightedIndex]);
1477
1717
  e.stopPropagation();
1478
1718
  }
1719
+ // always preventDefault for not inset all enter into textarea
1479
1720
  e.preventDefault();
1480
1721
  break;
1481
1722
  case 'F10':
@@ -1487,6 +1728,7 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1487
1728
  e.preventDefault();
1488
1729
  return;
1489
1730
  }
1731
+ // * check state for is that should move key in option items
1490
1732
  if (isAutocomplete && tags.length > 0 && !isOpen) {
1491
1733
  openMenu(e);
1492
1734
  e.preventDefault();
@@ -1503,66 +1745,70 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1503
1745
  }
1504
1746
  }
1505
1747
  },
1506
- onMouseDown: toggleWithInput
1507
- ? (e) => {
1508
- if (!isOpen || (isOpen && !inputValue)) {
1509
- toggleMenu(e, false);
1510
- }
1748
+ onMouseDown: toggleWithInput ? (e)=>{
1749
+ /**
1750
+ * If the popup isn't open and `openOnFocus` is disabled we should
1751
+ * show the popup. If `openOnFocus` is enabled the `onFocus` handler
1752
+ * will take care of opening the popup when necessary.
1753
+ *
1754
+ * If the popup is open and the user hasn't typed a value we should
1755
+ * hide it, but if they have typed a value we shouldn't hide it to
1756
+ * ensure the the available results remain visible to the user.
1757
+ */ if (!isOpen || isOpen && !inputValue) {
1758
+ toggleMenu(e, false);
1511
1759
  }
1512
- : undefined,
1760
+ } : undefined
1513
1761
  }, props);
1514
1762
  };
1515
- const getInputAriaProps = (props) => {
1763
+ const getInputAriaProps = (props)=>{
1516
1764
  const suggestionListInputAriaProps = getSuggestionListInputAriaProps(props);
1517
1765
  return combineProps({
1518
1766
  ...suggestionListInputAriaProps,
1519
1767
  readOnly,
1520
1768
  unselectable: readOnly ? 'on' : undefined,
1521
- 'aria-owns': isOpen
1522
- ? suggestionListInputAriaProps['aria-owns']
1523
- : undefined,
1524
- 'aria-expanded': !!isOpen,
1769
+ 'aria-owns': isOpen ? suggestionListInputAriaProps['aria-owns'] : undefined,
1770
+ 'aria-expanded': !!isOpen
1525
1771
  }, props);
1526
1772
  };
1527
- const getPopperProps = (restPopperProps) => {
1773
+ const getPopperProps = (restPopperProps)=>{
1528
1774
  return combineProps({
1529
1775
  id: `${AutocompleteId}-popper`,
1530
1776
  role: 'presentation',
1531
- onMouseDown: (e) => {
1777
+ onMouseDown: (e)=>{
1532
1778
  e.preventDefault();
1533
1779
  e.stopPropagation();
1534
- },
1780
+ }
1535
1781
  }, restPopperProps);
1536
1782
  };
1537
- const getToggleButtonProps = (props) => {
1783
+ const getToggleButtonProps = (props)=>{
1538
1784
  return combineProps({
1539
- disabled: (!isOpen && optionItems.length === 0) || disabled || readOnly,
1785
+ disabled: !isOpen && optionItems.length === 0 || disabled || readOnly,
1540
1786
  id: `${AutocompleteId}-toggle-button`,
1541
- onMouseDown: (e) => {
1787
+ onMouseDown: (e)=>{
1542
1788
  e.preventDefault();
1543
1789
  e.stopPropagation();
1544
1790
  },
1545
1791
  onClick: toggleMenu,
1546
- tabIndex: -1,
1792
+ tabIndex: -1
1547
1793
  }, props);
1548
1794
  };
1549
- const getClearButtonProps = (props) => {
1795
+ const getClearButtonProps = (props)=>{
1550
1796
  const suggestionListClearButtonProps = getSuggestionListClearButtonProps(props);
1551
1797
  return combineProps({
1552
1798
  ...suggestionListClearButtonProps,
1553
1799
  disabled,
1554
- onClick: (e) => {
1800
+ onClick: (e)=>{
1555
1801
  onClear?.(e);
1556
1802
  reset(true);
1557
- },
1803
+ }
1558
1804
  }, props);
1559
1805
  };
1560
- const getNoOptionsProps = (additionProps) => {
1806
+ const getNoOptionsProps = (additionProps)=>{
1561
1807
  return combineProps({
1562
- onClick: (e) => {
1808
+ onClick: (e)=>{
1563
1809
  closeMenu(e, 'blur');
1564
1810
  },
1565
- role: 'presentation',
1811
+ role: 'presentation'
1566
1812
  }, additionProps);
1567
1813
  };
1568
1814
  return {
@@ -1597,32 +1843,74 @@ const useAutocomplete = ({ multiple: multipleProp, variant, inputValue: inputVal
1597
1843
  reset,
1598
1844
  forceUpdate,
1599
1845
  optionsGroupList,
1600
- focused: focused ?? (tagFocused || inputFocused ? true : undefined),
1846
+ /** that state of current Autocomplete focused, both tag or input focused */ focused: focused ?? (tagFocused || inputFocused ? true : undefined),
1601
1847
  id: AutocompleteId,
1602
1848
  inputChanged: isInputValueChangedRef.current,
1603
1849
  autoCompleteSelectedIndex: autoCompleteSelectedIndexRef.current,
1604
- popperElementRef,
1850
+ popperElementRef
1605
1851
  };
1606
1852
  };
1607
1853
 
1854
+
1855
+
1856
+
1857
+
1858
+
1859
+
1860
+
1861
+
1862
+
1863
+
1864
+
1865
+
1866
+
1867
+
1868
+
1869
+
1608
1870
  const ChipSizeMap = {
1609
1871
  xlarge: 'large',
1610
1872
  large: 'medium',
1611
- medium: 'small',
1873
+ medium: 'small'
1612
1874
  };
1613
- const InputComponent = forwardRef(({ startAdornment, containerRef, containerClassName, onContainerClick, ...rest }, ref) => {
1614
- return (jsxs("div", { ref: containerRef, className: containerClassName, onClick: onContainerClick, children: [startAdornment, jsx("input", { ...rest, ref: ref })] }));
1875
+ const InputComponent = /*#__PURE__*/ forwardRef(({ startAdornment, containerRef, containerClassName, onContainerClick, ...rest }, ref)=>{
1876
+ return(// oxlint-disable-next-line jsx-a11y/click-events-have-key-events
1877
+ /*#__PURE__*/ jsxs("div", {
1878
+ ref: containerRef,
1879
+ className: containerClassName,
1880
+ onClick: onContainerClick,
1881
+ children: [
1882
+ startAdornment,
1883
+ /*#__PURE__*/ jsx("input", {
1884
+ ...rest,
1885
+ ref: ref
1886
+ })
1887
+ ]
1888
+ }));
1615
1889
  });
1616
- const TextInput = forwardRef(({ renderInput, ...rest }, ref) => {
1890
+ const TextInput = /*#__PURE__*/ forwardRef(({ renderInput, ...rest }, ref)=>{
1617
1891
  if (renderInput) {
1618
- return renderInput({ ...rest, ref });
1892
+ return renderInput({
1893
+ ...rest,
1894
+ ref
1895
+ });
1619
1896
  }
1620
- return jsx(TextField, { ref: ref, ...rest });
1897
+ return /*#__PURE__*/ jsx(TextField_TextField, {
1898
+ ref: ref,
1899
+ ...rest
1900
+ });
1621
1901
  });
1622
- const Autocomplete = forwardRef((inProps, ref) => {
1623
- const props = useThemeProps({ props: inProps, name: 'Autocomplete' });
1902
+ const Autocomplete_Autocomplete = /*#__PURE__*/ forwardRef((inProps, ref)=>{
1903
+ const props = useThemeProps({
1904
+ props: inProps,
1905
+ name: 'Autocomplete'
1906
+ });
1624
1907
  const isAutocomplete = props.variant === 'autocomplete';
1625
- 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, noOptionsText = 'No matches found', inputProps, debug, disabledItemsHighlightable, open: openProp, addNoOptionItem, onOpen, onClose, focused, classes, className, fullWidth = true, size = 'xlarge', isOptionEqualToValue, ...rest } = props;
1908
+ 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', // transition: popperTransition,
1909
+ // TransitionComponent = virtualize ? RcFade : RcGrow,
1910
+ // transitionDuration: transitionDurationProp = 'auto',
1911
+ // TransitionProps: TransitionPropsProp = {},
1912
+ children: _popperChildrenIgnored, ...PopperProps } = {}, initialIsOpen, disabled, required: requiredProp, disableCloseOnSelect = multiple, onChange: onChangeProp, onInputChange: onInputChangeProp, onMaxFreeSolo, FormHelperTextProps, action, filterOptions, renderOption, renderTags, getOptionDisabled, renderGroup, renderNoOptions, noOptionsText = 'No matches found', inputProps, // InputProps: InputPropsProp,
1913
+ debug, disabledItemsHighlightable, open: openProp, addNoOptionItem, onOpen, onClose, focused, classes, className, fullWidth = true, size = 'xlarge', isOptionEqualToValue, ...rest } = props;
1626
1914
  const cls = useClasses('autocomplete', classes, className);
1627
1915
  const innerInputRef = useRef(null);
1628
1916
  const textFieldRef = useRef(null);
@@ -1633,7 +1921,10 @@ const Autocomplete = forwardRef((inProps, ref) => {
1633
1921
  const anchorElRef = anchorElType === 'input' ? formFieldContentRef : textFieldRef;
1634
1922
  let oneOfTagError = false;
1635
1923
  const required = requiredProp;
1636
- 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({
1924
+ const { focusInput, optionItems, optionsGroupList, tags: selectedItems, highlightedIndex, getToggleButtonProps, getTagProps, getTagListBoxProps, getSuggestionListProps, getPopperProps, getInputProps, getInputAriaProps, // getLabelProps,
1925
+ getItemProps, isOpen, inputValue, activeIndex, setActiveIndex, setHighlightedIndex, onInputChange, keepHighlightedIndex, changeHighlightedIndexReason, closeMenu, openMenu, reset, getClearButtonProps, noOptionItem, getNoOptionsProps, isKeepHighlightedIndex, // focused: isAutocompleteFocused,
1926
+ // autoCompleteSelectedIndex,
1927
+ popperElementRef } = useAutocomplete({
1637
1928
  focused,
1638
1929
  open: openProp,
1639
1930
  addNoOptionItem,
@@ -1674,102 +1965,242 @@ const Autocomplete = forwardRef((inProps, ref) => {
1674
1965
  disabledItemsHighlightable,
1675
1966
  onGroupExpanded,
1676
1967
  isOptionEqualToValue,
1968
+ onClear
1677
1969
  });
1678
1970
  const open = Boolean((isOpen || noOptionItem) && textFieldRef.current);
1679
1971
  const { onBlur, ...InputProps } = getInputProps(inputProps);
1680
1972
  const helperText = helperTextProp;
1681
1973
  const toTextFieldRef = useForkRef(textFieldRef, ref);
1682
- const startAdornment = (() => {
1974
+ const startAdornment = (()=>{
1683
1975
  if (isAutocomplete || selectedItems.length === 0) {
1684
1976
  return undefined;
1685
1977
  }
1686
- const getCustomizedTagProps = (selectedItem, index) => {
1978
+ const getCustomizedTagProps = (selectedItem, index)=>{
1687
1979
  const tagProps = getTagProps({
1688
1980
  selectedItem,
1689
1981
  index,
1690
1982
  label: getOptionLabel(selectedItem),
1691
- disabled,
1983
+ disabled
1692
1984
  });
1693
1985
  if (selectedItem.error) {
1694
1986
  oneOfTagError = true;
1695
1987
  }
1696
- return { ...tagProps, size: ChipSizeMap[size], className: cls('chip') };
1988
+ return {
1989
+ ...tagProps,
1990
+ size: ChipSizeMap[size],
1991
+ className: cls('chip')
1992
+ };
1697
1993
  };
1698
- return renderTags
1699
- ? renderTags(selectedItems, getCustomizedTagProps)
1700
- : selectedItems.map((selectedItem, index) => {
1701
- const { children, key, ...itemChipProps } = getCustomizedTagProps(selectedItem, index);
1702
- return (jsx(Chip, { "aria-label": `${itemChipProps.label}, press Backspace to remove`, color: selectedItem.error ? 'error' : undefined, ...itemChipProps }, key));
1703
- });
1994
+ return renderTags ? renderTags(selectedItems, getCustomizedTagProps) : selectedItems.map((selectedItem, index)=>{
1995
+ const { children, key, ...itemChipProps } = getCustomizedTagProps(selectedItem, index);
1996
+ return /*#__PURE__*/ jsx(Chip_Chip, {
1997
+ "aria-label": `${itemChipProps.label}, press Backspace to remove`,
1998
+ color: selectedItem.error ? 'error' : undefined,
1999
+ ...itemChipProps
2000
+ }, key);
2001
+ });
1704
2002
  })();
1705
- 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({
2003
+ const endAdornment = (toggleButton || clearBtn) && /*#__PURE__*/ jsxs(Fragment, {
2004
+ children: [
2005
+ clearBtn && (selectedItems.length > 0 || inputValue) && /*#__PURE__*/ jsx(IconButton_IconButton, {
2006
+ shape: 'squircle',
2007
+ background: false,
2008
+ color: 'secondary',
2009
+ variant: "contained",
2010
+ size: "small",
2011
+ symbol: Xsm,
2012
+ ...getClearButtonProps(combineProps({
2013
+ className: cls('clear')
2014
+ }, ClearButtonProps))
2015
+ }),
2016
+ toggleButton && /*#__PURE__*/ jsx(IconButton_IconButton, {
2017
+ "aria-hidden": true,
2018
+ tabIndex: -1,
2019
+ shape: 'squircle',
2020
+ color: 'secondary',
2021
+ background: false,
2022
+ variant: "contained",
2023
+ size: "small",
2024
+ className: cls('chevron', error && 'error-chevron', open && `${inputVariant}-expanded-chevron`),
2025
+ children: /*#__PURE__*/ jsx(ExpandCollapseCaret_ExpandCollapseCaret, {
2026
+ size: 'small',
2027
+ expanded: open
2028
+ }),
2029
+ ...getToggleButtonProps({
1706
2030
  ...ToggleButtonProps,
1707
- ...getToggleButtonPropsProp?.(open),
1708
- }) }))] }));
2031
+ ...getToggleButtonPropsProp?.(open)
2032
+ })
2033
+ })
2034
+ ]
2035
+ });
1709
2036
  const TextFieldInputProps = {
1710
2037
  'aria-label': typeof label === 'string' ? label : undefined,
1711
2038
  ...getTagListBoxProps(),
1712
2039
  ...InputProps,
1713
- onBlur: debug ? undefined : onBlur,
2040
+ onBlur: debug ? undefined : onBlur
1714
2041
  };
1715
- useImperativeHandle(action, () => ({
1716
- getActiveIndex: () => activeIndex,
1717
- setActiveIndex,
1718
- getFilterResultItems: () => optionItems,
1719
- getHighlightedIndex: () => highlightedIndex,
1720
- setHighlightedIndex: (index) => setHighlightedIndex(index, { reason: 'auto', reRender: true }),
1721
- closeMenu,
1722
- openMenu,
1723
- reset,
1724
- clearInput: () => onInputChange('', false),
1725
- keepHighlightedIndex: () => keepHighlightedIndex(),
1726
- focus: focusInput,
1727
- }));
2042
+ useImperativeHandle(action, ()=>({
2043
+ getActiveIndex: ()=>activeIndex,
2044
+ setActiveIndex,
2045
+ getFilterResultItems: ()=>optionItems,
2046
+ getHighlightedIndex: ()=>highlightedIndex,
2047
+ setHighlightedIndex: (index)=>setHighlightedIndex(index, {
2048
+ reason: 'auto',
2049
+ reRender: true
2050
+ }),
2051
+ closeMenu,
2052
+ openMenu,
2053
+ reset,
2054
+ clearInput: ()=>onInputChange('', false),
2055
+ keepHighlightedIndex: ()=>keepHighlightedIndex(),
2056
+ focus: focusInput
2057
+ }));
1728
2058
  const hasTags = selectedItems.length > 0;
1729
2059
  const hasNoOptionItem = !!noOptionItem;
1730
2060
  const shouldRenderNoOptions = renderNoOptions === true || typeof renderNoOptions === 'function';
1731
2061
  const willRenderNoOptions = hasNoOptionItem && shouldRenderNoOptions;
1732
- const prevHighlightedIndex = usePrevious(() => highlightedIndex, true);
1733
- 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 && (jsx(Fragment, { children: renderNoOptions === true ? (jsx("div", { ...getNoOptionsProps({
1734
- className: cls('status-container'),
1735
- }), children: jsx("span", { className: cls('status-label'), children: noOptionsText }) })) : typeof renderNoOptions === 'function' ? (renderNoOptions(getNoOptionsProps, noOptionItem)) : null }))] }));
1736
- const isPopperContentEmpty = !isOpen || (!willRenderNoOptions && optionItems.length === 0);
2062
+ // const popperRef: ComponentProps<typeof StyledPopper>['popperRef'] =
2063
+ // useRef(null);
2064
+ // const handleUpdatePopper = useEventCallback(() => {
2065
+ // popperRef.current?.update();
2066
+ // });
2067
+ // Track previous highlightedIndex to detect list re-initialization in SuggestionList
2068
+ // When prev is -1, it indicates the list needs scroll reset (menu opened, input changed, etc.)
2069
+ const prevHighlightedIndex = usePrevious(()=>highlightedIndex, true);
2070
+ const menuChildren = // * that root element for animation host
2071
+ isOpen && /*#__PURE__*/ jsxs(Fragment, {
2072
+ children: [
2073
+ /*#__PURE__*/ jsx(SuggestionList_SuggestionList, {
2074
+ // TODO: deprecated, need to check if it will cause error when variant is 'autocomplete'
2075
+ // selectedIndex={isAutocomplete ? autoCompleteSelectedIndex : undefined}
2076
+ highlightedIndex: highlightedIndex,
2077
+ prevHighlightedIndex: prevHighlightedIndex,
2078
+ optionsGroupList: optionsGroupList,
2079
+ options: optionItems,
2080
+ groupVariant: groupVariant,
2081
+ groupExpanded: groupExpanded,
2082
+ renderGroup: renderGroup,
2083
+ renderOption: renderOption,
2084
+ inputValue: inputValue,
2085
+ getItemProps: getItemProps,
2086
+ getSuggestionListProps: getSuggestionListProps,
2087
+ changeHighlightedIndexReason: changeHighlightedIndexReason,
2088
+ getOptionDisabled: getOptionDisabled,
2089
+ isKeepHighlightedIndex: isKeepHighlightedIndex,
2090
+ // onContainerHeightChange={handleUpdatePopper}
2091
+ maxContainerHeight: 180,
2092
+ getOptionLabel: getOptionLabel,
2093
+ position: "unset",
2094
+ virtualize: virtualize,
2095
+ ...SuggestionListProps
2096
+ }),
2097
+ willRenderNoOptions && /*#__PURE__*/ jsx(Fragment, {
2098
+ children: renderNoOptions === true ? /*#__PURE__*/ jsx("div", {
2099
+ ...getNoOptionsProps({
2100
+ className: cls('status-container')
2101
+ }),
2102
+ children: /*#__PURE__*/ jsx("span", {
2103
+ className: cls('status-label'),
2104
+ children: noOptionsText
2105
+ })
2106
+ }) : typeof renderNoOptions === 'function' ? renderNoOptions(getNoOptionsProps, noOptionItem) : null
2107
+ })
2108
+ ]
2109
+ });
2110
+ const isPopperContentEmpty = !isOpen || !willRenderNoOptions && optionItems.length === 0;
1737
2111
  const popperCls = useClasses('autocomplete-popper', PopperProps?.classes, PopperProps?.className);
1738
- return (jsxs(Fragment, { children: [jsx(TextInput, { renderInput: renderInput, variant: inputVariant, ref: toTextFieldRef, inputRef: inputRef, fullWidth: fullWidth, placeholder: !hasTags || (hasTags && multiple && showPlaceholderWithTags)
1739
- ? placeholder
1740
- : undefined, label: label, disabled: disabled, required: required, error: error ?? oneOfTagError, FormHelperTextProps: FormHelperTextProps, inputComponent: InputComponent, endAdornment: endAdornment, helperText: helperText, size: size, ...combineProps({
2112
+ return /*#__PURE__*/ jsxs(Fragment, {
2113
+ children: [
2114
+ /*#__PURE__*/ jsx(TextInput, {
2115
+ renderInput: renderInput,
2116
+ // hasTags={hasTags}
2117
+ variant: inputVariant,
2118
+ ref: toTextFieldRef,
2119
+ inputRef: inputRef,
2120
+ fullWidth: fullWidth,
2121
+ placeholder: !hasTags || hasTags && multiple && showPlaceholderWithTags ? placeholder : undefined,
2122
+ label: label,
2123
+ disabled: disabled,
2124
+ required: required,
2125
+ // * if below one of tag is error, that main Autocomplete default will be error
2126
+ error: error ?? oneOfTagError,
2127
+ FormHelperTextProps: FormHelperTextProps,
2128
+ inputComponent: InputComponent,
2129
+ endAdornment: endAdornment,
2130
+ helperText: helperText,
2131
+ size: size,
2132
+ ...combineProps({
1741
2133
  RootProps: {
1742
- contentRef: formFieldContentRef,
2134
+ contentRef: formFieldContentRef
1743
2135
  },
1744
2136
  inputProps: {
1745
2137
  startAdornment,
1746
2138
  containerRef: inputContainerRef,
1747
2139
  ...getInputAriaProps(),
1748
2140
  ...TextFieldInputProps,
1749
- containerClassName: clsx(cls('input-container', `input-container-${size}`, `${inputVariant}-input-container`), TextFieldInputProps?.containerClassName),
1750
- },
1751
- }, rest), classes: {
1752
- root: cls('root', { error }),
1753
- formFieldContent: cls('form-field-content', `${inputVariant}-form-field-content`, `form-field-content-${size}`, open && `${inputVariant}-expanded`, { expanded: open }),
2141
+ containerClassName: clsx(cls('input-container', `input-container-${size}`, `${inputVariant}-input-container`), TextFieldInputProps?.containerClassName)
2142
+ }
2143
+ }, rest),
2144
+ classes: {
2145
+ root: cls('root', {
2146
+ error
2147
+ }),
2148
+ formFieldContent: cls('form-field-content', `${inputVariant}-form-field-content`, `form-field-content-${size}`, open && `${inputVariant}-expanded`, {
2149
+ expanded: open
2150
+ }),
1754
2151
  standard: cls('standard'),
1755
2152
  outlined: cls('outlined'),
1756
2153
  endAdornment: cls('end-adornment'),
1757
- input: cls('input', `input-${size}`),
1758
- }, value: inputValue, clearBtn: false }), jsx(AnimatePresence, { children: open && (jsx(PopperComponent, { matchAnchorWidth: true, offset: 4, ...getPopperProps(PopperProps), className: popperCls('root'), ref: popperElementRef, anchorEl: anchorElRef.current, children: jsx(motion.div, { ...getFade({ prefersReducedMotion }), className: popperCls('paper', {
1759
- empty: isPopperContentEmpty,
1760
- }), children: menuChildren }) })) })] }));
2154
+ input: cls('input', `input-${size}`)
2155
+ },
2156
+ value: inputValue,
2157
+ clearBtn: false
2158
+ }),
2159
+ /*#__PURE__*/ jsx(AnimatePresence, {
2160
+ children: open && /*#__PURE__*/ jsx(PopperComponent, {
2161
+ matchAnchorWidth: true,
2162
+ offset: 2,
2163
+ ...getPopperProps(PopperProps),
2164
+ className: popperCls('root'),
2165
+ ref: popperElementRef,
2166
+ anchorEl: anchorElRef.current,
2167
+ children: /*#__PURE__*/ jsx(motion.div, {
2168
+ ...getFade({
2169
+ prefersReducedMotion
2170
+ }),
2171
+ className: popperCls('paper', {
2172
+ empty: isPopperContentEmpty
2173
+ }),
2174
+ children: menuChildren
2175
+ })
2176
+ })
2177
+ })
2178
+ ]
2179
+ });
1761
2180
  });
1762
- Autocomplete.displayName = 'SuiAutocomplete';
1763
- const RcAutocompleteDefaultFilterOptions = (options, { inputValue, getOptionLabel, selectedItems, isOptionEqualToValue }) => {
1764
- return options.filter((item) => {
1765
- const isSelected = selectedItems.some((selected) => isOptionEqualToValue(item, selected));
1766
- if (isSelected)
1767
- return false;
2181
+ Autocomplete_Autocomplete.displayName = 'SuiAutocomplete';
2182
+
2183
+ /**
2184
+ * default Autocomplete filter options method, use with `variant="tags"`
2185
+ *
2186
+ * - filter options by input value and selected items
2187
+ * - uses `isOptionEqualToValue` to determine if an option is already selected
2188
+ */ const RcAutocompleteDefaultFilterOptions = (options, { inputValue, getOptionLabel, selectedItems, isOptionEqualToValue })=>{
2189
+ return options.filter((item)=>{
2190
+ // Check if this option is already selected using the equality function
2191
+ const isSelected = selectedItems.some((selected)=>isOptionEqualToValue(item, selected));
2192
+ if (isSelected) return false;
2193
+ // Filter by input text
1768
2194
  const optionLabel = getOptionLabel?.(item) || '';
1769
- return optionLabel
1770
- .toLowerCase()
1771
- .startsWith(inputValue?.toLowerCase() || '');
2195
+ return optionLabel.toLowerCase().startsWith(inputValue?.toLowerCase() || '');
1772
2196
  });
1773
2197
  };
1774
2198
 
1775
- 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 };
2199
+
2200
+
2201
+
2202
+
2203
+
2204
+
2205
+
2206
+ export { Autocomplete as autocompleteStyles, Autocomplete_Autocomplete as Autocomplete, DEFAULT_GET_OPTION_LABEL, DEFAULT_KEY_TO_CHIPS, DEFAULT_PADDING, RcAutocompleteDefaultFilterOptions, SuggestionList as suggestionListStyles, SuggestionListItem, SuggestionList_SuggestionList as SuggestionList, getPaddingValue, isFirstInGroup, stringArrToRegExp, useAutocomplete, useAutocompleteGroup, useAutocompleteTag, useDynamicHeight, useHighlightScroll, useSuggestionList };