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