@jobber/components 8.20.1 → 8.21.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 (84) hide show
  1. package/LegacySelect.d.ts +1 -0
  2. package/{Select.js → LegacySelect.js} +3 -3
  3. package/dist/BottomSheet-cjs.js +9 -58
  4. package/dist/BottomSheet-es.js +2 -47
  5. package/dist/Card/index.cjs +4 -0
  6. package/dist/Card/index.mjs +4 -0
  7. package/dist/ComboboxChipRemove-cjs.js +3418 -0
  8. package/dist/ComboboxChipRemove-es.js +3366 -0
  9. package/dist/ComboboxPrimitive-cjs.js +170 -0
  10. package/dist/ComboboxPrimitive-es.js +168 -0
  11. package/dist/DataDump/index.cjs +4 -0
  12. package/dist/DataDump/index.mjs +4 -0
  13. package/dist/DataTable/index.cjs +1 -1
  14. package/dist/DataTable/index.mjs +1 -1
  15. package/dist/DataTable-cjs.js +3 -3
  16. package/dist/DataTable-es.js +2 -2
  17. package/dist/InputNumberExperimental-cjs.js +45 -44
  18. package/dist/InputNumberExperimental-es.js +3 -2
  19. package/dist/InternalBackdrop-cjs.js +4278 -0
  20. package/dist/InternalBackdrop-es.js +4204 -0
  21. package/dist/LegacySelect/LegacySelect.d.ts +7 -0
  22. package/dist/{Select/Select.types.d.ts → LegacySelect/LegacySelect.types.d.ts} +1 -1
  23. package/dist/{Select → LegacySelect}/index.cjs +3 -3
  24. package/dist/LegacySelect/index.d.ts +3 -0
  25. package/dist/{Select → LegacySelect}/index.mjs +1 -1
  26. package/dist/{Select-cjs.js → LegacySelect-cjs.js} +5 -5
  27. package/dist/{Select-es.js → LegacySelect-es.js} +5 -5
  28. package/dist/Menu/index.cjs +4 -0
  29. package/dist/Menu/index.mjs +4 -0
  30. package/dist/Menu-cjs.js +9 -8
  31. package/dist/Menu-es.js +2 -1
  32. package/dist/MenuSubmenuTrigger-cjs.js +208 -1969
  33. package/dist/MenuSubmenuTrigger-es.js +7 -1759
  34. package/dist/NumberFieldInput-cjs.js +56 -439
  35. package/dist/NumberFieldInput-es.js +6 -376
  36. package/dist/Page/index.cjs +4 -0
  37. package/dist/Page/index.mjs +4 -0
  38. package/dist/ScrollAreaViewport-cjs.js +108 -4356
  39. package/dist/ScrollAreaViewport-es.js +6 -4201
  40. package/dist/buttonRenderAdapter-cjs.js +56 -0
  41. package/dist/buttonRenderAdapter-es.js +51 -0
  42. package/dist/clamp-cjs.js +0 -1194
  43. package/dist/clamp-es.js +1 -1091
  44. package/dist/docs/Autocomplete/Autocomplete.md +1 -1
  45. package/dist/docs/Chip/Chip.md +2 -2
  46. package/dist/docs/{Select/Select.md → LegacySelect/LegacySelect.md} +67 -62
  47. package/dist/docs/Menu/Menu.md +1 -1
  48. package/dist/docs/MultiSelect/MultiSelect.md +1 -1
  49. package/dist/docs/Tabs/Tabs.md +2 -1
  50. package/dist/docs/empty-states/empty-states.md +29 -0
  51. package/dist/docs/index.md +1 -1
  52. package/dist/index.cjs +7 -3
  53. package/dist/index.d.mts +1 -1
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.mjs +5 -1
  56. package/dist/primitives/BottomSheet/index.cjs +5 -2
  57. package/dist/primitives/BottomSheet/index.mjs +5 -2
  58. package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.d.ts +32 -0
  59. package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.types.d.ts +30 -0
  60. package/dist/primitives/ComboboxPrimitive/index.cjs +28 -0
  61. package/dist/primitives/ComboboxPrimitive/index.d.ts +2 -0
  62. package/dist/primitives/ComboboxPrimitive/index.mjs +22 -0
  63. package/dist/primitives/InputNumberExperimental/index.cjs +4 -2
  64. package/dist/primitives/InputNumberExperimental/index.mjs +4 -2
  65. package/dist/primitives/index.cjs +11 -2
  66. package/dist/primitives/index.d.ts +1 -0
  67. package/dist/primitives/index.mjs +10 -2
  68. package/dist/styles.css +793 -26
  69. package/dist/unstyledPrimitives/index.cjs +237 -3588
  70. package/dist/unstyledPrimitives/index.mjs +96 -3447
  71. package/dist/useButton-cjs.js +1196 -0
  72. package/dist/useButton-es.js +1091 -0
  73. package/dist/useCompositeListItem-cjs.js +1792 -0
  74. package/dist/useCompositeListItem-es.js +1762 -0
  75. package/dist/useLabel-cjs.js +411 -0
  76. package/dist/useLabel-es.js +378 -0
  77. package/dist/utils/meta/meta.json +3 -3
  78. package/package.json +7 -7
  79. package/Select.d.ts +0 -1
  80. package/dist/Select/Select.d.ts +0 -7
  81. package/dist/Select/index.d.ts +0 -3
  82. /package/dist/{Select → LegacySelect}/Option.d.ts +0 -0
  83. /package/dist/{Select → LegacySelect}/OptionGroup.d.ts +0 -0
  84. /package/dist/{Select → LegacySelect}/hooks/useSelectActions.d.ts +0 -0
@@ -0,0 +1,3418 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var useButton = require('./useButton-cjs.js');
5
+ var useRenderElement = require('./useRenderElement-cjs.js');
6
+ var useLabel = require('./useLabel-cjs.js');
7
+ var useCompositeListItem = require('./useCompositeListItem-cjs.js');
8
+ var InternalBackdrop = require('./InternalBackdrop-cjs.js');
9
+ var jsxRuntime = require('react/jsx-runtime');
10
+ var ReactDOM = require('react-dom');
11
+
12
+ function _interopNamespaceDefault(e) {
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
30
+ var ReactDOM__namespace = /*#__PURE__*/_interopNamespaceDefault(ReactDOM);
31
+
32
+ const ComboboxRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
33
+ if (process.env.NODE_ENV !== "production") ComboboxRootContext.displayName = "ComboboxRootContext";
34
+ const ComboboxFloatingContext = /*#__PURE__*/React__namespace.createContext(undefined);
35
+ if (process.env.NODE_ENV !== "production") ComboboxFloatingContext.displayName = "ComboboxFloatingContext";
36
+ const ComboboxDerivedItemsContext = /*#__PURE__*/React__namespace.createContext(undefined);
37
+ // `inputValue` can't be placed in the store.
38
+ // https://github.com/mui/base-ui/issues/2703
39
+ if (process.env.NODE_ENV !== "production") ComboboxDerivedItemsContext.displayName = "ComboboxDerivedItemsContext";
40
+ const ComboboxInputValueContext = /*#__PURE__*/React__namespace.createContext('');
41
+ if (process.env.NODE_ENV !== "production") ComboboxInputValueContext.displayName = "ComboboxInputValueContext";
42
+ function useComboboxRootContext() {
43
+ const context = React__namespace.useContext(ComboboxRootContext);
44
+ if (!context) {
45
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxRootContext is missing. Combobox parts must be placed within <Combobox.Root>.' : useRenderElement.formatErrorMessage(22));
46
+ }
47
+ return context;
48
+ }
49
+ function useComboboxFloatingContext() {
50
+ const context = React__namespace.useContext(ComboboxFloatingContext);
51
+ if (!context) {
52
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxFloatingContext is missing. Combobox parts must be placed within <Combobox.Root>.' : useRenderElement.formatErrorMessage(23));
53
+ }
54
+ return context;
55
+ }
56
+ function useComboboxDerivedItemsContext() {
57
+ const context = React__namespace.useContext(ComboboxDerivedItemsContext);
58
+ if (!context) {
59
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxItemsContext is missing. Combobox parts must be placed within <Combobox.Root>.' : useRenderElement.formatErrorMessage(24));
60
+ }
61
+ return context;
62
+ }
63
+ function useComboboxInputValueContext() {
64
+ return React__namespace.useContext(ComboboxInputValueContext);
65
+ }
66
+
67
+ const defaultItemEquality = (itemValue, selectedValue) => Object.is(itemValue, selectedValue);
68
+ function compareItemEquality(itemValue, selectedValue, comparer) {
69
+ if (itemValue == null || selectedValue == null) {
70
+ return Object.is(itemValue, selectedValue);
71
+ }
72
+ return comparer(itemValue, selectedValue);
73
+ }
74
+ function selectedValueIncludes(selectedValues, itemValue, comparer) {
75
+ if (!selectedValues || selectedValues.length === 0) {
76
+ return false;
77
+ }
78
+ return selectedValues.some(selectedValue => {
79
+ if (selectedValue === undefined) {
80
+ return false;
81
+ }
82
+ return compareItemEquality(itemValue, selectedValue, comparer);
83
+ });
84
+ }
85
+ function findItemIndex(itemValues, selectedValue, comparer) {
86
+ if (!itemValues || itemValues.length === 0) {
87
+ return -1;
88
+ }
89
+ return itemValues.findIndex(itemValue => {
90
+ if (itemValue === undefined) {
91
+ return false;
92
+ }
93
+ return compareItemEquality(itemValue, selectedValue, comparer);
94
+ });
95
+ }
96
+ function removeItem(selectedValues, itemValue, comparer) {
97
+ return selectedValues.filter(selectedValue => !compareItemEquality(itemValue, selectedValue, comparer));
98
+ }
99
+
100
+ function serializeValue(value) {
101
+ if (value == null) {
102
+ return '';
103
+ }
104
+ if (typeof value === 'string') {
105
+ return value;
106
+ }
107
+ try {
108
+ return JSON.stringify(value);
109
+ } catch {
110
+ return String(value);
111
+ }
112
+ }
113
+
114
+ function isGroupedItems(items) {
115
+ return items != null && items.length > 0 && typeof items[0] === 'object' && items[0] != null && 'items' in items[0];
116
+ }
117
+
118
+ /**
119
+ * Checks if the items array contains an item with a null value that has a non-null label.
120
+ */
121
+ function hasNullItemLabel(items) {
122
+ if (!Array.isArray(items)) {
123
+ return items != null && 'null' in items;
124
+ }
125
+ const arrayItems = items;
126
+ if (isGroupedItems(arrayItems)) {
127
+ for (const group of arrayItems) {
128
+ for (const item of group.items) {
129
+ if (item && item.value == null && item.label != null) {
130
+ return true;
131
+ }
132
+ }
133
+ }
134
+ return false;
135
+ }
136
+ for (const item of arrayItems) {
137
+ if (item && item.value == null && item.label != null) {
138
+ return true;
139
+ }
140
+ }
141
+ return false;
142
+ }
143
+ function stringifyAsLabel(item, itemToStringLabel) {
144
+ if (itemToStringLabel && item != null) {
145
+ return itemToStringLabel(item) ?? '';
146
+ }
147
+ if (item && typeof item === 'object') {
148
+ if ('label' in item && item.label != null) {
149
+ return String(item.label);
150
+ }
151
+ if ('value' in item) {
152
+ return String(item.value);
153
+ }
154
+ }
155
+ return serializeValue(item);
156
+ }
157
+ function stringifyAsValue(item, itemToStringValue) {
158
+ if (itemToStringValue && item != null) {
159
+ return itemToStringValue(item) ?? '';
160
+ }
161
+ if (item && typeof item === 'object' && 'value' in item && 'label' in item) {
162
+ return serializeValue(item.value);
163
+ }
164
+ return serializeValue(item);
165
+ }
166
+ function resolveSelectedLabel(value, items, itemToStringLabel) {
167
+ function fallback() {
168
+ return stringifyAsLabel(value, itemToStringLabel);
169
+ }
170
+ if (itemToStringLabel && value != null) {
171
+ return itemToStringLabel(value);
172
+ }
173
+
174
+ // Custom object with explicit label takes precedence
175
+ if (value && typeof value === 'object' && 'label' in value && value.label != null) {
176
+ return value.label;
177
+ }
178
+
179
+ // Items provided as plain record map
180
+ if (items && !Array.isArray(items)) {
181
+ return items[value] ?? fallback();
182
+ }
183
+
184
+ // Items provided as array (flat or grouped)
185
+ if (Array.isArray(items)) {
186
+ const arrayItems = items;
187
+ const flatItems = isGroupedItems(arrayItems) ? arrayItems.flatMap(group => group.items) : arrayItems;
188
+ if (value == null || typeof value !== 'object') {
189
+ const match = flatItems.find(item => item.value === value);
190
+ if (match && match.label != null) {
191
+ return match.label;
192
+ }
193
+ return fallback();
194
+ }
195
+
196
+ // Object without explicit label: try matching by its `value` property
197
+ if ('value' in value) {
198
+ const match = flatItems.find(item => item && item.value === value.value);
199
+ if (match && match.label != null) {
200
+ return match.label;
201
+ }
202
+ }
203
+ }
204
+ return fallback();
205
+ }
206
+ function resolveMultipleLabels(values, items, itemToStringLabel) {
207
+ return values.reduce((acc, value, index) => {
208
+ if (index > 0) {
209
+ acc.push(', ');
210
+ }
211
+ acc.push(/*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
212
+ children: resolveSelectedLabel(value, items, itemToStringLabel)
213
+ }, index));
214
+ return acc;
215
+ }, []);
216
+ }
217
+
218
+ const selectors = {
219
+ id: InternalBackdrop.createSelector(state => state.id),
220
+ labelId: InternalBackdrop.createSelector(state => state.labelId),
221
+ query: InternalBackdrop.createSelector(state => state.query),
222
+ items: InternalBackdrop.createSelector(state => state.items),
223
+ selectedValue: InternalBackdrop.createSelector(state => state.selectedValue),
224
+ hasSelectionChips: InternalBackdrop.createSelector(state => {
225
+ const selectedValue = state.selectedValue;
226
+ return Array.isArray(selectedValue) && selectedValue.length > 0;
227
+ }),
228
+ hasSelectedValue: InternalBackdrop.createSelector(state => {
229
+ const {
230
+ selectedValue,
231
+ selectionMode
232
+ } = state;
233
+ if (selectedValue == null) {
234
+ return false;
235
+ }
236
+ if (selectionMode === 'multiple' && Array.isArray(selectedValue)) {
237
+ return selectedValue.length > 0;
238
+ }
239
+ return true;
240
+ }),
241
+ hasNullItemLabel: InternalBackdrop.createSelector((state, enabled) => {
242
+ return enabled ? hasNullItemLabel(state.items) : false;
243
+ }),
244
+ open: InternalBackdrop.createSelector(state => state.open),
245
+ mounted: InternalBackdrop.createSelector(state => state.mounted),
246
+ forceMounted: InternalBackdrop.createSelector(state => state.forceMounted),
247
+ inline: InternalBackdrop.createSelector(state => state.inline),
248
+ activeIndex: InternalBackdrop.createSelector(state => state.activeIndex),
249
+ selectedIndex: InternalBackdrop.createSelector(state => state.selectedIndex),
250
+ isActive: InternalBackdrop.createSelector((state, index) => state.activeIndex === index),
251
+ isSelected: InternalBackdrop.createSelector((state, itemValue) => {
252
+ const comparer = state.isItemEqualToValue;
253
+ const selectedValue = state.selectedValue;
254
+ if (Array.isArray(selectedValue)) {
255
+ return selectedValue.some(selectedItem => compareItemEquality(itemValue, selectedItem, comparer));
256
+ }
257
+ return compareItemEquality(itemValue, selectedValue, comparer);
258
+ }),
259
+ transitionStatus: InternalBackdrop.createSelector(state => state.transitionStatus),
260
+ popupProps: InternalBackdrop.createSelector(state => state.popupProps),
261
+ inputProps: InternalBackdrop.createSelector(state => state.inputProps),
262
+ triggerProps: InternalBackdrop.createSelector(state => state.triggerProps),
263
+ getItemProps: InternalBackdrop.createSelector(state => state.getItemProps),
264
+ positionerElement: InternalBackdrop.createSelector(state => state.positionerElement),
265
+ listElement: InternalBackdrop.createSelector(state => state.listElement),
266
+ triggerElement: InternalBackdrop.createSelector(state => state.triggerElement),
267
+ inputElement: InternalBackdrop.createSelector(state => state.inputElement),
268
+ inputGroupElement: InternalBackdrop.createSelector(state => state.inputGroupElement),
269
+ popupSide: InternalBackdrop.createSelector(state => state.popupSide),
270
+ openMethod: InternalBackdrop.createSelector(state => state.openMethod),
271
+ inputInsidePopup: InternalBackdrop.createSelector(state => state.inputInsidePopup),
272
+ selectionMode: InternalBackdrop.createSelector(state => state.selectionMode),
273
+ listRef: InternalBackdrop.createSelector(state => state.listRef),
274
+ labelsRef: InternalBackdrop.createSelector(state => state.labelsRef),
275
+ popupRef: InternalBackdrop.createSelector(state => state.popupRef),
276
+ emptyRef: InternalBackdrop.createSelector(state => state.emptyRef),
277
+ inputRef: InternalBackdrop.createSelector(state => state.inputRef),
278
+ keyboardActiveRef: InternalBackdrop.createSelector(state => state.keyboardActiveRef),
279
+ chipsContainerRef: InternalBackdrop.createSelector(state => state.chipsContainerRef),
280
+ clearRef: InternalBackdrop.createSelector(state => state.clearRef),
281
+ valuesRef: InternalBackdrop.createSelector(state => state.valuesRef),
282
+ allValuesRef: InternalBackdrop.createSelector(state => state.allValuesRef),
283
+ name: InternalBackdrop.createSelector(state => state.name),
284
+ disabled: InternalBackdrop.createSelector(state => state.disabled),
285
+ readOnly: InternalBackdrop.createSelector(state => state.readOnly),
286
+ required: InternalBackdrop.createSelector(state => state.required),
287
+ grid: InternalBackdrop.createSelector(state => state.grid),
288
+ isGrouped: InternalBackdrop.createSelector(state => state.isGrouped),
289
+ virtualized: InternalBackdrop.createSelector(state => state.virtualized),
290
+ onOpenChangeComplete: InternalBackdrop.createSelector(state => state.onOpenChangeComplete),
291
+ openOnInputClick: InternalBackdrop.createSelector(state => state.openOnInputClick),
292
+ itemToStringLabel: InternalBackdrop.createSelector(state => state.itemToStringLabel),
293
+ isItemEqualToValue: InternalBackdrop.createSelector(state => state.isItemEqualToValue),
294
+ modal: InternalBackdrop.createSelector(state => state.modal),
295
+ autoHighlight: InternalBackdrop.createSelector(state => state.autoHighlight),
296
+ submitOnItemClick: InternalBackdrop.createSelector(state => state.submitOnItemClick)
297
+ };
298
+
299
+ /**
300
+ * Enhanced filter using Intl.Collator for more robust string matching.
301
+ * Uses the provided `itemToStringLabel` function if available, otherwise falls back to:
302
+ * • When `item` is an object with a `value` property, that property is used.
303
+ * • When `item` is a primitive (e.g. `string`), it is used directly.
304
+ */
305
+ function createCollatorItemFilter(collatorFilter, itemToStringLabel) {
306
+ return (item, query) => {
307
+ if (item == null) {
308
+ return false;
309
+ }
310
+ const itemString = stringifyAsLabel(item, itemToStringLabel);
311
+ return collatorFilter.contains(itemString, query);
312
+ };
313
+ }
314
+
315
+ /**
316
+ * Enhanced filter for single selection mode using Intl.Collator that shows all items
317
+ * when query is empty or matches the current selection, making it easier to browse options.
318
+ */
319
+ function createSingleSelectionCollatorFilter(collatorFilter, itemToStringLabel, selectedValue) {
320
+ return (item, query) => {
321
+ if (item == null) {
322
+ return false;
323
+ }
324
+ if (!query) {
325
+ return true;
326
+ }
327
+ const itemString = stringifyAsLabel(item, itemToStringLabel);
328
+ const selectedString = selectedValue != null ? stringifyAsLabel(selectedValue, itemToStringLabel) : '';
329
+
330
+ // Handle case-insensitive matching consistently
331
+ if (selectedString && collatorFilter.contains(selectedString, query) && selectedString.length === query.length) {
332
+ return true;
333
+ }
334
+ return collatorFilter.contains(itemString, query);
335
+ };
336
+ }
337
+
338
+ const filterCache = new Map();
339
+ function stringifyLocale(locale) {
340
+ if (Array.isArray(locale)) {
341
+ return locale.map(value => stringifyLocale(value)).join(',');
342
+ }
343
+ if (locale == null) {
344
+ return '';
345
+ }
346
+ return String(locale);
347
+ }
348
+ function getFilter(options = {}) {
349
+ const mergedOptions = {
350
+ usage: 'search',
351
+ sensitivity: 'base',
352
+ ignorePunctuation: true,
353
+ ...options
354
+ };
355
+ const cacheKey = `${stringifyLocale(options.locale)}|${JSON.stringify(mergedOptions)}`;
356
+ const cachedFilter = filterCache.get(cacheKey);
357
+ if (cachedFilter) {
358
+ return cachedFilter;
359
+ }
360
+ const collator = new Intl.Collator(options.locale, mergedOptions);
361
+ const filter = {
362
+ contains(item, query, itemToString) {
363
+ if (!query) {
364
+ return true;
365
+ }
366
+ const itemString = stringifyAsLabel(item, itemToString);
367
+ for (let i = 0; i <= itemString.length - query.length; i += 1) {
368
+ if (collator.compare(itemString.slice(i, i + query.length), query) === 0) {
369
+ return true;
370
+ }
371
+ }
372
+ return false;
373
+ },
374
+ startsWith(item, query, itemToString) {
375
+ if (!query) {
376
+ return true;
377
+ }
378
+ const itemString = stringifyAsLabel(item, itemToString);
379
+ return collator.compare(itemString.slice(0, query.length), query) === 0;
380
+ },
381
+ endsWith(item, query, itemToString) {
382
+ if (!query) {
383
+ return true;
384
+ }
385
+ const itemString = stringifyAsLabel(item, itemToString);
386
+ const queryLength = query.length;
387
+ return itemString.length >= queryLength && collator.compare(itemString.slice(itemString.length - queryLength), query) === 0;
388
+ }
389
+ };
390
+ filterCache.set(cacheKey, filter);
391
+ return filter;
392
+ }
393
+
394
+ /**
395
+ * Matches items against a query using `Intl.Collator` for robust string matching.
396
+ */
397
+ const useCoreFilter = getFilter;
398
+ /**
399
+ * Matches items against a query using `Intl.Collator` for robust string matching.
400
+ */
401
+ function useComboboxFilter(options = {}) {
402
+ const {
403
+ multiple = false,
404
+ value,
405
+ ...collatorOptions
406
+ } = options;
407
+ const coreFilter = getFilter(collatorOptions);
408
+ const contains = React__namespace.useCallback((item, query, itemToString) => {
409
+ if (multiple) {
410
+ return createCollatorItemFilter(coreFilter, itemToString)(item, query);
411
+ }
412
+ return createSingleSelectionCollatorFilter(coreFilter, itemToString, value)(item, query);
413
+ }, [coreFilter, value, multiple]);
414
+ return React__namespace.useMemo(() => ({
415
+ contains,
416
+ startsWith: coreFilter.startsWith,
417
+ endsWith: coreFilter.endsWith
418
+ }), [contains, coreFilter]);
419
+ }
420
+
421
+ const NO_ACTIVE_VALUE = Symbol('none');
422
+ const INITIAL_LAST_HIGHLIGHT = {
423
+ value: NO_ACTIVE_VALUE,
424
+ index: -1
425
+ };
426
+
427
+ function AriaCombobox(props) {
428
+ const {
429
+ id: idProp,
430
+ onOpenChangeComplete: onOpenChangeCompleteProp,
431
+ defaultSelectedValue = null,
432
+ selectedValue: selectedValueProp,
433
+ onSelectedValueChange,
434
+ defaultInputValue: defaultInputValueProp,
435
+ inputValue: inputValueProp,
436
+ selectionMode = 'none',
437
+ onItemHighlighted: onItemHighlightedProp,
438
+ name: nameProp,
439
+ disabled: disabledProp = false,
440
+ readOnly = false,
441
+ required = false,
442
+ inputRef: inputRefProp,
443
+ grid = false,
444
+ items,
445
+ filteredItems: filteredItemsProp,
446
+ filter: filterProp,
447
+ openOnInputClick = true,
448
+ autoHighlight = false,
449
+ keepHighlight = false,
450
+ highlightItemOnHover = true,
451
+ loopFocus = true,
452
+ itemToStringLabel,
453
+ itemToStringValue,
454
+ isItemEqualToValue = defaultItemEquality,
455
+ virtualized = false,
456
+ inline: inlineProp = false,
457
+ fillInputOnItemPress = true,
458
+ modal = false,
459
+ limit = -1,
460
+ autoComplete = 'list',
461
+ formAutoComplete,
462
+ locale,
463
+ submitOnItemClick = false
464
+ } = props;
465
+ const {
466
+ clearErrors
467
+ } = useLabel.useFormContext();
468
+ const {
469
+ setDirty,
470
+ validityData,
471
+ shouldValidateOnChange,
472
+ setFilled,
473
+ name: fieldName,
474
+ disabled: fieldDisabled,
475
+ setTouched,
476
+ setFocused,
477
+ validationMode,
478
+ validation
479
+ } = useLabel.useFieldRootContext();
480
+ const id = useLabel.useLabelableId({
481
+ id: idProp
482
+ });
483
+ const collatorFilter = useCoreFilter({
484
+ locale
485
+ });
486
+ const [queryChangedAfterOpen, setQueryChangedAfterOpen] = React__namespace.useState(false);
487
+ const [closeQuery, setCloseQuery] = React__namespace.useState(null);
488
+ const listRef = React__namespace.useRef([]);
489
+ const labelsRef = React__namespace.useRef([]);
490
+ const popupRef = React__namespace.useRef(null);
491
+ const inputRef = React__namespace.useRef(null);
492
+ const startDismissRef = React__namespace.useRef(null);
493
+ const endDismissRef = React__namespace.useRef(null);
494
+ const emptyRef = React__namespace.useRef(null);
495
+ const keyboardActiveRef = React__namespace.useRef(true);
496
+ const hadInputClearRef = React__namespace.useRef(false);
497
+ const chipsContainerRef = React__namespace.useRef(null);
498
+ const clearRef = React__namespace.useRef(null);
499
+ const selectionEventRef = React__namespace.useRef(null);
500
+ const lastHighlightRef = React__namespace.useRef(INITIAL_LAST_HIGHLIGHT);
501
+ const pendingQueryHighlightRef = React__namespace.useRef(null);
502
+
503
+ /**
504
+ * Contains the currently visible list of item values post-filtering.
505
+ */
506
+ const valuesRef = React__namespace.useRef([]);
507
+ /**
508
+ * Contains all item values in a stable, unfiltered order.
509
+ * This is only used when `items` prop is not provided.
510
+ * It accumulates values on first mount and does not remove them on unmount due to
511
+ * filtering, providing a stable index for selected value tracking.
512
+ */
513
+ const allValuesRef = React__namespace.useRef([]);
514
+ const disabled = fieldDisabled || disabledProp;
515
+ const name = fieldName ?? nameProp;
516
+ const multiple = selectionMode === 'multiple';
517
+ const single = selectionMode === 'single';
518
+ const hasInputValue = inputValueProp !== undefined || defaultInputValueProp !== undefined;
519
+ const hasItems = items !== undefined;
520
+ const hasFilteredItemsProp = filteredItemsProp !== undefined;
521
+ let autoHighlightMode;
522
+ if (autoHighlight === 'always') {
523
+ autoHighlightMode = 'always';
524
+ } else {
525
+ autoHighlightMode = autoHighlight ? 'input-change' : false;
526
+ }
527
+ const [selectedValue, setSelectedValueUnwrapped] = useButton.useControlled({
528
+ controlled: selectedValueProp,
529
+ default: multiple ? defaultSelectedValue ?? useRenderElement.EMPTY_ARRAY : defaultSelectedValue,
530
+ name: 'Combobox',
531
+ state: 'selectedValue'
532
+ });
533
+ const filter = React__namespace.useMemo(() => {
534
+ if (filterProp === null) {
535
+ return () => true;
536
+ }
537
+ if (filterProp !== undefined) {
538
+ return filterProp;
539
+ }
540
+ if (single && !queryChangedAfterOpen) {
541
+ return createSingleSelectionCollatorFilter(collatorFilter, itemToStringLabel, selectedValue);
542
+ }
543
+ return createCollatorItemFilter(collatorFilter, itemToStringLabel);
544
+ }, [filterProp, single, selectedValue, queryChangedAfterOpen, collatorFilter, itemToStringLabel]);
545
+
546
+ // If neither inputValue nor defaultInputValue are provided, derive it from the
547
+ // selected value for single mode so the input reflects the selection on mount.
548
+ const initialDefaultInputValue = useRenderElement.useRefWithInit(() => {
549
+ if (hasInputValue) {
550
+ return defaultInputValueProp ?? '';
551
+ }
552
+ if (single) {
553
+ return stringifyAsLabel(selectedValue, itemToStringLabel);
554
+ }
555
+ return '';
556
+ }).current;
557
+ const [inputValue, setInputValueUnwrapped] = useButton.useControlled({
558
+ controlled: inputValueProp,
559
+ default: initialDefaultInputValue,
560
+ name: 'Combobox',
561
+ state: 'inputValue'
562
+ });
563
+ const [open, setOpenUnwrapped] = useButton.useControlled({
564
+ controlled: props.open,
565
+ default: props.defaultOpen,
566
+ name: 'Combobox',
567
+ state: 'open'
568
+ });
569
+ const isGrouped = isGroupedItems(items);
570
+ const query = closeQuery ?? (inputValue === '' ? '' : String(inputValue).trim());
571
+ const selectedLabelString = single ? stringifyAsLabel(selectedValue, itemToStringLabel) : '';
572
+ const shouldBypassFiltering = single && !queryChangedAfterOpen && query !== '' && selectedLabelString !== '' && selectedLabelString.length === query.length && collatorFilter.contains(selectedLabelString, query);
573
+ const filterQuery = shouldBypassFiltering ? '' : query;
574
+ const shouldIgnoreExternalFiltering = hasItems && hasFilteredItemsProp && shouldBypassFiltering;
575
+ const flatItems = React__namespace.useMemo(() => {
576
+ if (!items) {
577
+ return useRenderElement.EMPTY_ARRAY;
578
+ }
579
+ if (isGrouped) {
580
+ return items.flatMap(group => group.items);
581
+ }
582
+ return items;
583
+ }, [items, isGrouped]);
584
+ const filteredItems = React__namespace.useMemo(() => {
585
+ if (filteredItemsProp && !shouldIgnoreExternalFiltering) {
586
+ return filteredItemsProp;
587
+ }
588
+ if (!items) {
589
+ return useRenderElement.EMPTY_ARRAY;
590
+ }
591
+ if (isGrouped) {
592
+ const groupedItems = items;
593
+ const resultingGroups = [];
594
+ let currentCount = 0;
595
+ for (const group of groupedItems) {
596
+ if (limit > -1 && currentCount >= limit) {
597
+ break;
598
+ }
599
+ const candidateItems = filterQuery === '' ? group.items : group.items.filter(item => filter(item, filterQuery, itemToStringLabel));
600
+ if (candidateItems.length === 0) {
601
+ continue;
602
+ }
603
+ const remainingLimit = limit > -1 ? limit - currentCount : Infinity;
604
+ const itemsToTake = candidateItems.slice(0, remainingLimit);
605
+ if (itemsToTake.length > 0) {
606
+ const newGroup = {
607
+ ...group,
608
+ items: itemsToTake
609
+ };
610
+ resultingGroups.push(newGroup);
611
+ currentCount += itemsToTake.length;
612
+ }
613
+ }
614
+ return resultingGroups;
615
+ }
616
+ if (filterQuery === '') {
617
+ return limit > -1 ? flatItems.slice(0, limit) :
618
+ // The cast here is done as `flatItems` is readonly.
619
+ // valuesRef.current, a mutable ref, can be set to `flatFilteredItems`, which may
620
+ // reference this exact readonly value, creating a mutation risk.
621
+ // However, <Combobox.Item> can never mutate this value as the mutating effect
622
+ // bails early when `items` is provided, and this is only ever returned
623
+ // when `items` is provided due to the early return at the top of this hook.
624
+ flatItems;
625
+ }
626
+ const limitedItems = [];
627
+ for (const item of flatItems) {
628
+ if (limit > -1 && limitedItems.length >= limit) {
629
+ break;
630
+ }
631
+ if (filter(item, filterQuery, itemToStringLabel)) {
632
+ limitedItems.push(item);
633
+ }
634
+ }
635
+ return limitedItems;
636
+ }, [filteredItemsProp, shouldIgnoreExternalFiltering, items, isGrouped, filterQuery, limit, filter, itemToStringLabel, flatItems]);
637
+ const flatFilteredItems = React__namespace.useMemo(() => {
638
+ if (isGrouped) {
639
+ const groups = filteredItems;
640
+ return groups.flatMap(g => g.items);
641
+ }
642
+ return filteredItems;
643
+ }, [filteredItems, isGrouped]);
644
+ const store = useRenderElement.useRefWithInit(() => new InternalBackdrop.Store({
645
+ id,
646
+ labelId: undefined,
647
+ selectedValue,
648
+ open,
649
+ filter,
650
+ query,
651
+ items,
652
+ selectionMode,
653
+ listRef,
654
+ labelsRef,
655
+ popupRef,
656
+ emptyRef,
657
+ inputRef,
658
+ startDismissRef,
659
+ endDismissRef,
660
+ keyboardActiveRef,
661
+ chipsContainerRef,
662
+ clearRef,
663
+ valuesRef,
664
+ allValuesRef,
665
+ selectionEventRef,
666
+ name,
667
+ disabled,
668
+ readOnly,
669
+ required,
670
+ grid,
671
+ isGrouped,
672
+ virtualized,
673
+ openOnInputClick,
674
+ itemToStringLabel,
675
+ isItemEqualToValue,
676
+ modal,
677
+ autoHighlight: autoHighlightMode,
678
+ submitOnItemClick,
679
+ hasInputValue,
680
+ mounted: false,
681
+ forceMounted: false,
682
+ transitionStatus: 'idle',
683
+ inline: inlineProp,
684
+ activeIndex: null,
685
+ selectedIndex: null,
686
+ popupProps: {},
687
+ inputProps: {},
688
+ triggerProps: {},
689
+ positionerElement: null,
690
+ listElement: null,
691
+ triggerElement: null,
692
+ inputElement: null,
693
+ inputGroupElement: null,
694
+ popupSide: null,
695
+ openMethod: null,
696
+ inputInsidePopup: true,
697
+ onOpenChangeComplete: onOpenChangeCompleteProp || useRenderElement.NOOP,
698
+ // Placeholder callbacks replaced on first render
699
+ setOpen: useRenderElement.NOOP,
700
+ setInputValue: useRenderElement.NOOP,
701
+ setSelectedValue: useRenderElement.NOOP,
702
+ setIndices: useRenderElement.NOOP,
703
+ onItemHighlighted: useRenderElement.NOOP,
704
+ handleSelection: useRenderElement.NOOP,
705
+ getItemProps: () => useRenderElement.EMPTY_OBJECT,
706
+ forceMount: useRenderElement.NOOP,
707
+ requestSubmit: useRenderElement.NOOP
708
+ })).current;
709
+ const fieldRawValue = selectionMode === 'none' ? inputValue : selectedValue;
710
+ const fieldStringValue = React__namespace.useMemo(() => {
711
+ if (selectionMode === 'none') {
712
+ return fieldRawValue;
713
+ }
714
+ if (Array.isArray(selectedValue)) {
715
+ return selectedValue.map(value => stringifyAsValue(value, itemToStringValue));
716
+ }
717
+ return stringifyAsValue(selectedValue, itemToStringValue);
718
+ }, [fieldRawValue, itemToStringValue, selectionMode, selectedValue]);
719
+ const onItemHighlighted = useButton.useStableCallback(onItemHighlightedProp);
720
+ const onOpenChangeComplete = useButton.useStableCallback(onOpenChangeCompleteProp);
721
+ const activeIndex = InternalBackdrop.useStore(store, selectors.activeIndex);
722
+ const selectedIndex = InternalBackdrop.useStore(store, selectors.selectedIndex);
723
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
724
+ const listElement = InternalBackdrop.useStore(store, selectors.listElement);
725
+ const triggerElement = InternalBackdrop.useStore(store, selectors.triggerElement);
726
+ const inputElement = InternalBackdrop.useStore(store, selectors.inputElement);
727
+ const inputGroupElement = InternalBackdrop.useStore(store, selectors.inputGroupElement);
728
+ const inline = InternalBackdrop.useStore(store, selectors.inline);
729
+ const inputInsidePopup = InternalBackdrop.useStore(store, selectors.inputInsidePopup);
730
+ const triggerRef = useButton.useValueAsRef(triggerElement);
731
+ const {
732
+ mounted,
733
+ setMounted,
734
+ transitionStatus
735
+ } = useButton.useTransitionStatus(open);
736
+ const {
737
+ openMethod,
738
+ triggerProps
739
+ } = InternalBackdrop.useOpenInteractionType(open);
740
+ useLabel.useField({
741
+ id,
742
+ name,
743
+ commit: validation.commit,
744
+ value: fieldRawValue,
745
+ controlRef: inputInsidePopup ? triggerRef : inputRef,
746
+ getValue: () => fieldStringValue
747
+ });
748
+ const forceMount = useButton.useStableCallback(() => {
749
+ if (items) {
750
+ // Ensure typeahead works on a closed list.
751
+ labelsRef.current = flatFilteredItems.map(item => stringifyAsLabel(item, itemToStringLabel));
752
+ } else {
753
+ store.set('forceMounted', true);
754
+ }
755
+ });
756
+ const initialSelectedValueRef = React__namespace.useRef(selectedValue);
757
+ useButton.useIsoLayoutEffect(() => {
758
+ // Ensure the values and labels are registered for programmatic value changes.
759
+ if (selectedValue !== initialSelectedValueRef.current) {
760
+ forceMount();
761
+ }
762
+ }, [forceMount, selectedValue]);
763
+ const setIndices = useButton.useStableCallback(options => {
764
+ store.update(options);
765
+ const type = options.type || 'none';
766
+ if (options.activeIndex === undefined) {
767
+ return;
768
+ }
769
+ if (options.activeIndex === null) {
770
+ if (lastHighlightRef.current !== INITIAL_LAST_HIGHLIGHT) {
771
+ lastHighlightRef.current = INITIAL_LAST_HIGHLIGHT;
772
+ onItemHighlighted(undefined, useButton.createGenericEventDetails(type, undefined, {
773
+ index: -1
774
+ }));
775
+ }
776
+ } else {
777
+ const activeValue = valuesRef.current[options.activeIndex];
778
+ lastHighlightRef.current = {
779
+ value: activeValue,
780
+ index: options.activeIndex
781
+ };
782
+ onItemHighlighted(activeValue, useButton.createGenericEventDetails(type, undefined, {
783
+ index: options.activeIndex
784
+ }));
785
+ }
786
+ });
787
+ const setInputValue = useButton.useStableCallback((next, eventDetails) => {
788
+ hadInputClearRef.current = eventDetails.reason === useButton.inputClear;
789
+ props.onInputValueChange?.(next, eventDetails);
790
+ if (eventDetails.isCanceled) {
791
+ return;
792
+ }
793
+
794
+ // If user is typing, ensure we don't auto-highlight on open due to a race
795
+ // with the post-open effect that sets this flag.
796
+ if (eventDetails.reason === useButton.inputChange) {
797
+ const event = eventDetails.event;
798
+ const inputType = event.inputType;
799
+ // Treat composition commits as typed input; autofill may omit `inputType` or
800
+ // report `insertReplacementText`.
801
+ const isTypedInput = event.type === 'compositionend' || inputType != null && inputType !== '' && inputType !== 'insertReplacementText';
802
+ if (isTypedInput) {
803
+ const hasQuery = next.trim() !== '';
804
+ if (hasQuery) {
805
+ setQueryChangedAfterOpen(true);
806
+ }
807
+ // Defer index updates until after the filtered items have been derived to ensure
808
+ // `onItemHighlighted` receives the latest item.
809
+ pendingQueryHighlightRef.current = {
810
+ hasQuery
811
+ };
812
+ if (hasQuery && autoHighlightMode && store.state.activeIndex == null) {
813
+ store.set('activeIndex', 0);
814
+ }
815
+ }
816
+ }
817
+ setInputValueUnwrapped(next);
818
+ });
819
+ const setOpen = useButton.useStableCallback((nextOpen, eventDetails) => {
820
+ if (open === nextOpen) {
821
+ return;
822
+ }
823
+
824
+ // If the `Empty` component is not used, the positioner or popup should be hidden
825
+ // with CSS. In this case, allow the Escape key to bubble to close a parent popup
826
+ // if there are no items to show.
827
+ if (eventDetails.reason === 'escape-key' && hasItems && flatFilteredItems.length === 0 && !store.state.emptyRef.current) {
828
+ eventDetails.allowPropagation();
829
+ }
830
+ props.onOpenChange?.(nextOpen, eventDetails);
831
+ if (eventDetails.isCanceled) {
832
+ return;
833
+ }
834
+ if (!nextOpen && queryChangedAfterOpen) {
835
+ if (single) {
836
+ if (!inline) {
837
+ setCloseQuery(query);
838
+ }
839
+ // Avoid a flicker when closing the popup with an empty query.
840
+ if (query === '') {
841
+ setQueryChangedAfterOpen(false);
842
+ }
843
+ } else if (multiple) {
844
+ if (inline || inputInsidePopup) {
845
+ setIndices({
846
+ activeIndex: null
847
+ });
848
+ } else {
849
+ // Freeze the current query so filtering remains stable while exiting.
850
+ setCloseQuery(query);
851
+ }
852
+ // Clear the input immediately on close while retaining filtering via closeQuery for exit animations
853
+ // if the input is outside the popup.
854
+ setInputValue('', useButton.createChangeEventDetails(useButton.inputClear, eventDetails.event));
855
+ }
856
+ }
857
+ setOpenUnwrapped(nextOpen);
858
+ if (!nextOpen && inputInsidePopup && (eventDetails.reason === useButton.focusOut || eventDetails.reason === useButton.outsidePress)) {
859
+ setTouched(true);
860
+ setFocused(false);
861
+ if (validationMode === 'onBlur') {
862
+ const valueToValidate = selectionMode === 'none' ? inputValue : selectedValue;
863
+ validation.commit(valueToValidate);
864
+ }
865
+ }
866
+ });
867
+ const setSelectedValue = useButton.useStableCallback((nextValue, eventDetails) => {
868
+ // Cast to `any` due to conditional value type (single vs. multiple).
869
+ // The runtime implementation already ensures the correct value shape.
870
+ onSelectedValueChange?.(nextValue, eventDetails);
871
+ if (eventDetails.isCanceled) {
872
+ return;
873
+ }
874
+ setSelectedValueUnwrapped(nextValue);
875
+ const shouldFillInput = selectionMode === 'none' && popupRef.current && fillInputOnItemPress || single && !store.state.inputInsidePopup;
876
+ if (shouldFillInput) {
877
+ setInputValue(stringifyAsLabel(nextValue, itemToStringLabel), useButton.createChangeEventDetails(eventDetails.reason, eventDetails.event));
878
+ }
879
+ if (single && nextValue != null && eventDetails.reason !== useButton.inputChange && queryChangedAfterOpen && !inline) {
880
+ setCloseQuery(query);
881
+ }
882
+ });
883
+ const handleSelection = useButton.useStableCallback((event, passedValue) => {
884
+ let itemValue = passedValue;
885
+ if (itemValue === undefined) {
886
+ if (activeIndex === null) {
887
+ return;
888
+ }
889
+ itemValue = valuesRef.current[activeIndex];
890
+ }
891
+ const targetEl = useButton.getTarget(event);
892
+ const overrideEvent = selectionEventRef.current ?? event;
893
+ selectionEventRef.current = null;
894
+ const eventDetails = useButton.createChangeEventDetails(useButton.itemPress, overrideEvent);
895
+
896
+ // Let the link handle the click.
897
+ const href = targetEl?.closest('a')?.getAttribute('href');
898
+ if (href) {
899
+ if (href.startsWith('#')) {
900
+ setOpen(false, eventDetails);
901
+ }
902
+ return;
903
+ }
904
+ if (multiple) {
905
+ const currentSelectedValue = Array.isArray(selectedValue) ? selectedValue : [];
906
+ const isCurrentlySelected = selectedValueIncludes(currentSelectedValue, itemValue, store.state.isItemEqualToValue);
907
+ const nextValue = isCurrentlySelected ? removeItem(currentSelectedValue, itemValue, store.state.isItemEqualToValue) : [...currentSelectedValue, itemValue];
908
+ setSelectedValue(nextValue, eventDetails);
909
+ const wasFiltering = inputRef.current ? inputRef.current.value.trim() !== '' : false;
910
+ if (!wasFiltering) {
911
+ return;
912
+ }
913
+ if (store.state.inputInsidePopup) {
914
+ setInputValue('', useButton.createChangeEventDetails(useButton.inputClear, eventDetails.event));
915
+ } else {
916
+ setOpen(false, eventDetails);
917
+ }
918
+ } else {
919
+ setSelectedValue(itemValue, eventDetails);
920
+ setOpen(false, eventDetails);
921
+ }
922
+ });
923
+ const requestSubmit = useButton.useStableCallback(() => {
924
+ if (!store.state.submitOnItemClick) {
925
+ return;
926
+ }
927
+ const form = store.state.inputElement?.form;
928
+ if (form && typeof form.requestSubmit === 'function') {
929
+ form.requestSubmit();
930
+ }
931
+ });
932
+ const handleUnmount = useButton.useStableCallback(() => {
933
+ setMounted(false);
934
+ onOpenChangeComplete?.(false);
935
+ setQueryChangedAfterOpen(false);
936
+ setCloseQuery(null);
937
+ if (selectionMode === 'none') {
938
+ setIndices({
939
+ activeIndex: null,
940
+ selectedIndex: null
941
+ });
942
+ } else {
943
+ setIndices({
944
+ activeIndex: null
945
+ });
946
+ }
947
+
948
+ // Multiple selection mode:
949
+ // If the user typed a filter and didn't select in multiple mode, clear the input
950
+ // after close completes to avoid mid-exit flicker and start fresh on next open.
951
+ if (multiple && inputRef.current && inputRef.current.value !== '' && !hadInputClearRef.current) {
952
+ setInputValue('', useButton.createChangeEventDetails(useButton.inputClear));
953
+ }
954
+
955
+ // Single selection mode:
956
+ // - If input is rendered inside the popup, clear it so the next open is blank
957
+ // - If input is outside the popup, sync it to the selected value
958
+ if (single) {
959
+ if (store.state.inputInsidePopup) {
960
+ if (inputRef.current && inputRef.current.value !== '') {
961
+ setInputValue('', useButton.createChangeEventDetails(useButton.inputClear));
962
+ }
963
+ } else {
964
+ const stringVal = stringifyAsLabel(selectedValue, itemToStringLabel);
965
+ if (inputRef.current && inputRef.current.value !== stringVal) {
966
+ // If no selection was made, treat this as clearing the typed filter.
967
+ const reason = stringVal === '' ? useButton.inputClear : useButton.none;
968
+ setInputValue(stringVal, useButton.createChangeEventDetails(reason));
969
+ }
970
+ }
971
+ }
972
+ });
973
+
974
+ // Support composing the Dialog component around an inline combobox.
975
+ // `[role="dialog"]` is more interoperable than using a context, e.g. it can work
976
+ // with third-party modal libraries, though the limitation is that the closest
977
+ // `role=dialog` part must be the animated element.
978
+ const resolvedPopupRef = React__namespace.useMemo(() => {
979
+ if (inline && positionerElement) {
980
+ return {
981
+ current: positionerElement.closest('[role="dialog"]')
982
+ };
983
+ }
984
+ return popupRef;
985
+ }, [inline, positionerElement]);
986
+ useButton.useOpenChangeComplete({
987
+ enabled: !props.actionsRef,
988
+ open,
989
+ ref: resolvedPopupRef,
990
+ onComplete() {
991
+ if (!open) {
992
+ handleUnmount();
993
+ }
994
+ }
995
+ });
996
+ React__namespace.useImperativeHandle(props.actionsRef, () => ({
997
+ unmount: handleUnmount
998
+ }), [handleUnmount]);
999
+ useButton.useIsoLayoutEffect(function syncSelectedIndex() {
1000
+ if (open || selectionMode === 'none') {
1001
+ return;
1002
+ }
1003
+ const registry = items ? flatItems : allValuesRef.current;
1004
+ if (multiple) {
1005
+ const currentValue = Array.isArray(selectedValue) ? selectedValue : [];
1006
+ const lastValue = currentValue[currentValue.length - 1];
1007
+ const lastIndex = findItemIndex(registry, lastValue, isItemEqualToValue);
1008
+ setIndices({
1009
+ selectedIndex: lastIndex === -1 ? null : lastIndex
1010
+ });
1011
+ } else {
1012
+ const index = findItemIndex(registry, selectedValue, isItemEqualToValue);
1013
+ setIndices({
1014
+ selectedIndex: index === -1 ? null : index
1015
+ });
1016
+ }
1017
+ }, [open, selectedValue, items, selectionMode, flatItems, multiple, isItemEqualToValue, setIndices]);
1018
+ useButton.useIsoLayoutEffect(() => {
1019
+ if (items) {
1020
+ valuesRef.current = flatFilteredItems;
1021
+ listRef.current.length = flatFilteredItems.length;
1022
+ }
1023
+ }, [items, flatFilteredItems]);
1024
+ useButton.useIsoLayoutEffect(() => {
1025
+ const pendingHighlight = pendingQueryHighlightRef.current;
1026
+ if (pendingHighlight) {
1027
+ if (pendingHighlight.hasQuery) {
1028
+ if (autoHighlightMode) {
1029
+ store.set('activeIndex', 0);
1030
+ }
1031
+ } else if (autoHighlightMode === 'always') {
1032
+ store.set('activeIndex', 0);
1033
+ }
1034
+ pendingQueryHighlightRef.current = null;
1035
+ }
1036
+ if (!open && !inline) {
1037
+ return;
1038
+ }
1039
+ const shouldUseFlatFilteredItems = hasItems || hasFilteredItemsProp;
1040
+ const candidateItems = shouldUseFlatFilteredItems ? flatFilteredItems : valuesRef.current;
1041
+ const storeActiveIndex = store.state.activeIndex;
1042
+ if (storeActiveIndex == null) {
1043
+ if (autoHighlightMode === 'always' && candidateItems.length > 0) {
1044
+ store.set('activeIndex', 0);
1045
+ return;
1046
+ }
1047
+ if (lastHighlightRef.current !== INITIAL_LAST_HIGHLIGHT) {
1048
+ lastHighlightRef.current = INITIAL_LAST_HIGHLIGHT;
1049
+ store.state.onItemHighlighted(undefined, useButton.createGenericEventDetails(useButton.none, undefined, {
1050
+ index: -1
1051
+ }));
1052
+ }
1053
+ return;
1054
+ }
1055
+ if (storeActiveIndex >= candidateItems.length) {
1056
+ if (lastHighlightRef.current !== INITIAL_LAST_HIGHLIGHT) {
1057
+ lastHighlightRef.current = INITIAL_LAST_HIGHLIGHT;
1058
+ store.state.onItemHighlighted(undefined, useButton.createGenericEventDetails(useButton.none, undefined, {
1059
+ index: -1
1060
+ }));
1061
+ }
1062
+ store.set('activeIndex', null);
1063
+ return;
1064
+ }
1065
+ const itemValue = candidateItems[storeActiveIndex];
1066
+ const previouslyHighlightedItemValue = lastHighlightRef.current.value;
1067
+ const isSameItem = previouslyHighlightedItemValue !== NO_ACTIVE_VALUE && compareItemEquality(itemValue, previouslyHighlightedItemValue, store.state.isItemEqualToValue);
1068
+ if (lastHighlightRef.current.index !== storeActiveIndex || !isSameItem) {
1069
+ lastHighlightRef.current = {
1070
+ value: itemValue,
1071
+ index: storeActiveIndex
1072
+ };
1073
+ store.state.onItemHighlighted(itemValue, useButton.createGenericEventDetails(useButton.none, undefined, {
1074
+ index: storeActiveIndex
1075
+ }));
1076
+ }
1077
+ }, [activeIndex, autoHighlightMode, hasFilteredItemsProp, hasItems, flatFilteredItems, inline, open, store]);
1078
+ useButton.useIsoLayoutEffect(() => {
1079
+ if (selectionMode === 'none') {
1080
+ setFilled(String(inputValue) !== '');
1081
+ return;
1082
+ }
1083
+ setFilled(multiple ? Array.isArray(selectedValue) && selectedValue.length > 0 : selectedValue != null);
1084
+ }, [setFilled, selectionMode, inputValue, selectedValue, multiple]);
1085
+
1086
+ // Ensures that the active index is not set to 0 when the list is empty.
1087
+ // This avoids needing to press ArrowDown twice under certain conditions.
1088
+ React__namespace.useEffect(() => {
1089
+ if (hasItems && autoHighlightMode && flatFilteredItems.length === 0) {
1090
+ setIndices({
1091
+ activeIndex: null
1092
+ });
1093
+ }
1094
+ }, [hasItems, autoHighlightMode, flatFilteredItems.length, setIndices]);
1095
+ useButton.useValueChanged(query, () => {
1096
+ if (!open || query === '' || query === String(initialDefaultInputValue)) {
1097
+ return;
1098
+ }
1099
+ setQueryChangedAfterOpen(true);
1100
+ });
1101
+ useButton.useValueChanged(selectedValue, () => {
1102
+ if (selectionMode === 'none') {
1103
+ return;
1104
+ }
1105
+ clearErrors(name);
1106
+ setDirty(selectedValue !== validityData.initialValue);
1107
+ if (shouldValidateOnChange()) {
1108
+ validation.commit(selectedValue);
1109
+ } else {
1110
+ validation.commit(selectedValue, true);
1111
+ }
1112
+ if (single && !hasInputValue && !inputInsidePopup) {
1113
+ const nextInputValue = stringifyAsLabel(selectedValue, itemToStringLabel);
1114
+ if (inputValue !== nextInputValue) {
1115
+ setInputValue(nextInputValue, useButton.createChangeEventDetails(useButton.none));
1116
+ }
1117
+ }
1118
+ });
1119
+ useButton.useValueChanged(inputValue, () => {
1120
+ if (selectionMode !== 'none') {
1121
+ return;
1122
+ }
1123
+ clearErrors(name);
1124
+ setDirty(inputValue !== validityData.initialValue);
1125
+ if (shouldValidateOnChange()) {
1126
+ validation.commit(inputValue);
1127
+ } else {
1128
+ validation.commit(inputValue, true);
1129
+ }
1130
+ });
1131
+ useButton.useValueChanged(items, () => {
1132
+ if (!single || hasInputValue || inputInsidePopup || queryChangedAfterOpen) {
1133
+ return;
1134
+ }
1135
+ const nextInputValue = stringifyAsLabel(selectedValue, itemToStringLabel);
1136
+ if (inputValue !== nextInputValue) {
1137
+ setInputValue(nextInputValue, useButton.createChangeEventDetails(useButton.none));
1138
+ }
1139
+ });
1140
+ const floatingRootContext = useCompositeListItem.useFloatingRootContext({
1141
+ open: inline ? true : open,
1142
+ onOpenChange: setOpen,
1143
+ elements: {
1144
+ reference: inputInsidePopup ? triggerElement : inputElement,
1145
+ floating: positionerElement
1146
+ }
1147
+ });
1148
+ let ariaHasPopup;
1149
+ let ariaExpanded;
1150
+ if (!inline) {
1151
+ ariaHasPopup = grid ? 'grid' : 'listbox';
1152
+ ariaExpanded = open ? 'true' : 'false';
1153
+ }
1154
+ const role = React__namespace.useMemo(() => {
1155
+ const isPlainInput = inputElement?.tagName === 'INPUT';
1156
+ // During SSR and initial hydration, the input ref is not available yet.
1157
+ // Assume an input-like control so combobox ARIA attributes are present.
1158
+ const shouldTreatAsInput = inputElement == null || isPlainInput;
1159
+ const shouldApplyAria = shouldTreatAsInput || open;
1160
+ const reference = shouldTreatAsInput ? {
1161
+ autoComplete: 'off',
1162
+ spellCheck: 'false',
1163
+ autoCorrect: 'off',
1164
+ autoCapitalize: 'none'
1165
+ } : {};
1166
+ if (shouldApplyAria) {
1167
+ reference.role = 'combobox';
1168
+ reference['aria-expanded'] = ariaExpanded;
1169
+ reference['aria-haspopup'] = ariaHasPopup;
1170
+ reference['aria-controls'] = open ? listElement?.id : undefined;
1171
+ reference['aria-autocomplete'] = autoComplete;
1172
+ }
1173
+ return {
1174
+ reference,
1175
+ floating: {
1176
+ role: 'presentation'
1177
+ }
1178
+ };
1179
+ }, [inputElement, open, ariaExpanded, ariaHasPopup, listElement?.id, autoComplete]);
1180
+ const click = InternalBackdrop.useClick(floatingRootContext, {
1181
+ enabled: !readOnly && !disabled && openOnInputClick,
1182
+ event: 'mousedown-only',
1183
+ toggle: false,
1184
+ // Apply a small delay for touch to let iOS viewport centering settle.
1185
+ // This avoids top-bottom flip flickers if the preferred position is "top" when first tapping.
1186
+ touchOpenDelay: inputInsidePopup ? 0 : 50,
1187
+ reason: useButton.inputPress
1188
+ });
1189
+ const dismiss = InternalBackdrop.useDismiss(floatingRootContext, {
1190
+ enabled: !readOnly && !disabled && !inline,
1191
+ outsidePressEvent: {
1192
+ mouse: 'sloppy',
1193
+ // The visual viewport (affected by the mobile software keyboard) can be
1194
+ // somewhat small. The user may want to scroll the screen to see more of
1195
+ // the popup.
1196
+ touch: 'intentional'
1197
+ },
1198
+ // Without a popup, let the Escape key bubble the event up to other popups' handlers.
1199
+ bubbles: inline ? true : undefined,
1200
+ outsidePress(event) {
1201
+ const target = useButton.getTarget(event);
1202
+ return !useButton.contains(triggerElement, target) && !useButton.contains(clearRef.current, target) && !useButton.contains(chipsContainerRef.current, target) && !useButton.contains(inputGroupElement, target);
1203
+ }
1204
+ });
1205
+ const listNavigation = useCompositeListItem.useListNavigation(floatingRootContext, {
1206
+ enabled: !readOnly && !disabled,
1207
+ id,
1208
+ listRef,
1209
+ activeIndex,
1210
+ selectedIndex,
1211
+ virtual: true,
1212
+ loopFocus,
1213
+ allowEscape: loopFocus && !autoHighlightMode,
1214
+ focusItemOnOpen: queryChangedAfterOpen || selectionMode === 'none' && !autoHighlightMode ? false : 'auto',
1215
+ focusItemOnHover: highlightItemOnHover,
1216
+ resetOnPointerLeave: !keepHighlight,
1217
+ // `cols` > 1 enables grid navigation.
1218
+ // Since <Combobox.Row> infers column sizes (and is required when building a grid),
1219
+ // it works correctly even with a value of `2`.
1220
+ // Floating UI tests don't require `role="row"` wrappers, so retains the number API.
1221
+ cols: grid ? 2 : 1,
1222
+ orientation: grid ? 'horizontal' : undefined,
1223
+ disabledIndices: useRenderElement.EMPTY_ARRAY,
1224
+ onNavigate(nextActiveIndex, event) {
1225
+ // Retain the highlight only while actually transitioning out or closed.
1226
+ if (!event && !open || transitionStatus === 'ending') {
1227
+ return;
1228
+ }
1229
+ if (!event) {
1230
+ setIndices({
1231
+ activeIndex: nextActiveIndex
1232
+ });
1233
+ } else {
1234
+ setIndices({
1235
+ activeIndex: nextActiveIndex,
1236
+ type: keyboardActiveRef.current ? 'keyboard' : 'pointer'
1237
+ });
1238
+ }
1239
+ }
1240
+ });
1241
+ const {
1242
+ getReferenceProps,
1243
+ getFloatingProps,
1244
+ getItemProps
1245
+ } = InternalBackdrop.useInteractions([role, click, dismiss, listNavigation]);
1246
+ InternalBackdrop.useOnFirstRender(() => {
1247
+ store.update({
1248
+ inline: inlineProp,
1249
+ popupProps: getFloatingProps(),
1250
+ inputProps: getReferenceProps(),
1251
+ triggerProps,
1252
+ getItemProps,
1253
+ setOpen,
1254
+ setInputValue,
1255
+ setSelectedValue,
1256
+ setIndices,
1257
+ onItemHighlighted,
1258
+ handleSelection,
1259
+ forceMount,
1260
+ requestSubmit
1261
+ });
1262
+ });
1263
+ useButton.useIsoLayoutEffect(() => {
1264
+ store.update({
1265
+ id,
1266
+ selectedValue,
1267
+ open,
1268
+ mounted,
1269
+ transitionStatus,
1270
+ items,
1271
+ inline: inlineProp,
1272
+ popupProps: getFloatingProps(),
1273
+ inputProps: getReferenceProps(),
1274
+ triggerProps,
1275
+ openMethod,
1276
+ getItemProps,
1277
+ selectionMode,
1278
+ name,
1279
+ disabled,
1280
+ readOnly,
1281
+ required,
1282
+ grid,
1283
+ isGrouped,
1284
+ virtualized,
1285
+ onOpenChangeComplete,
1286
+ openOnInputClick,
1287
+ itemToStringLabel,
1288
+ modal,
1289
+ autoHighlight: autoHighlightMode,
1290
+ isItemEqualToValue,
1291
+ submitOnItemClick,
1292
+ hasInputValue,
1293
+ requestSubmit
1294
+ });
1295
+ }, [store, id, selectedValue, open, mounted, transitionStatus, items, getFloatingProps, getReferenceProps, getItemProps, openMethod, triggerProps, selectionMode, name, disabled, readOnly, required, validation, grid, isGrouped, virtualized, onOpenChangeComplete, openOnInputClick, itemToStringLabel, modal, isItemEqualToValue, submitOnItemClick, hasInputValue, inlineProp, requestSubmit, autoHighlightMode]);
1296
+ const hiddenInputRef = useRenderElement.useMergedRefs(inputRefProp, validation.inputRef);
1297
+ const itemsContextValue = React__namespace.useMemo(() => ({
1298
+ query,
1299
+ hasItems,
1300
+ filteredItems,
1301
+ flatFilteredItems
1302
+ }), [query, hasItems, filteredItems, flatFilteredItems]);
1303
+ const serializedValue = React__namespace.useMemo(() => {
1304
+ if (Array.isArray(fieldRawValue)) {
1305
+ return '';
1306
+ }
1307
+ return stringifyAsValue(fieldRawValue, itemToStringValue);
1308
+ }, [fieldRawValue, itemToStringValue]);
1309
+ const hasMultipleSelection = multiple && Array.isArray(selectedValue) && selectedValue.length > 0;
1310
+ const hiddenInputName = multiple || selectionMode === 'none' ? undefined : name;
1311
+ const hiddenInputs = React__namespace.useMemo(() => {
1312
+ if (!multiple || !Array.isArray(selectedValue) || !name) {
1313
+ return null;
1314
+ }
1315
+ return selectedValue.map(value => {
1316
+ const currentSerializedValue = stringifyAsValue(value, itemToStringValue);
1317
+ return /*#__PURE__*/jsxRuntime.jsx("input", {
1318
+ type: "hidden",
1319
+ name: name,
1320
+ value: currentSerializedValue
1321
+ }, currentSerializedValue);
1322
+ });
1323
+ }, [multiple, selectedValue, name, itemToStringValue]);
1324
+ const children = /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
1325
+ children: [props.children, /*#__PURE__*/jsxRuntime.jsx("input", {
1326
+ ...validation.getInputValidationProps({
1327
+ // Move focus when the hidden input is focused.
1328
+ onFocus() {
1329
+ if (inputInsidePopup) {
1330
+ triggerElement?.focus();
1331
+ return;
1332
+ }
1333
+ (inputRef.current || triggerElement)?.focus();
1334
+ },
1335
+ // Handle browser autofill.
1336
+ onChange(event) {
1337
+ // Workaround for https://github.com/facebook/react/issues/9023
1338
+ if (event.nativeEvent.defaultPrevented) {
1339
+ return;
1340
+ }
1341
+ const nextValue = event.target.value;
1342
+ const details = useButton.createChangeEventDetails(useButton.none, event.nativeEvent);
1343
+ function handleChange() {
1344
+ // Browser autofill only writes a single scalar value.
1345
+ if (multiple) {
1346
+ return;
1347
+ }
1348
+ if (selectionMode === 'none') {
1349
+ setDirty(nextValue !== validityData.initialValue);
1350
+ setInputValue(nextValue, details);
1351
+ if (shouldValidateOnChange()) {
1352
+ validation.commit(nextValue);
1353
+ }
1354
+ return;
1355
+ }
1356
+ const matchingValue = valuesRef.current.find(v => {
1357
+ const candidate = stringifyAsValue(v, itemToStringValue);
1358
+ if (candidate.toLowerCase() === nextValue.toLowerCase()) {
1359
+ return true;
1360
+ }
1361
+ return false;
1362
+ });
1363
+ if (matchingValue != null) {
1364
+ setDirty(matchingValue !== validityData.initialValue);
1365
+ setSelectedValue?.(matchingValue, details);
1366
+ if (shouldValidateOnChange()) {
1367
+ validation.commit(matchingValue);
1368
+ }
1369
+ }
1370
+ }
1371
+ if (items) {
1372
+ handleChange();
1373
+ } else {
1374
+ forceMount();
1375
+ queueMicrotask(handleChange);
1376
+ }
1377
+ }
1378
+ }),
1379
+ id: id && hiddenInputName == null ? `${id}-hidden-input` : undefined,
1380
+ name: hiddenInputName,
1381
+ autoComplete: formAutoComplete,
1382
+ disabled: disabled,
1383
+ required: required && !hasMultipleSelection,
1384
+ readOnly: readOnly,
1385
+ value: serializedValue,
1386
+ ref: hiddenInputRef,
1387
+ style: hiddenInputName ? useButton.visuallyHiddenInput : useButton.visuallyHidden,
1388
+ tabIndex: -1,
1389
+ "aria-hidden": true
1390
+ }), hiddenInputs]
1391
+ });
1392
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxRootContext.Provider, {
1393
+ value: store,
1394
+ children: /*#__PURE__*/jsxRuntime.jsx(ComboboxFloatingContext.Provider, {
1395
+ value: floatingRootContext,
1396
+ children: /*#__PURE__*/jsxRuntime.jsx(ComboboxDerivedItemsContext.Provider, {
1397
+ value: itemsContextValue,
1398
+ children: /*#__PURE__*/jsxRuntime.jsx(ComboboxInputValueContext.Provider, {
1399
+ value: inputValue,
1400
+ children: children
1401
+ })
1402
+ })
1403
+ })
1404
+ });
1405
+ }
1406
+
1407
+ const triggerStateAttributesMapping = {
1408
+ ...InternalBackdrop.pressableTriggerOpenStateMapping,
1409
+ ...useLabel.fieldValidityMapping,
1410
+ popupSide: side => side ? {
1411
+ 'data-popup-side': side
1412
+ } : null,
1413
+ listEmpty: empty => empty ? {
1414
+ 'data-list-empty': ''
1415
+ } : null
1416
+ };
1417
+
1418
+ function getDefaultLabelId(id) {
1419
+ return id == null ? undefined : `${id}-label`;
1420
+ }
1421
+ function resolveAriaLabelledBy(fieldLabelId, localLabelId) {
1422
+ return fieldLabelId ?? localLabelId;
1423
+ }
1424
+
1425
+ const BOUNDARY_OFFSET = 2;
1426
+
1427
+ /**
1428
+ * A button that opens the popup.
1429
+ * Renders a `<button>` element.
1430
+ */
1431
+ const ComboboxTrigger = /*#__PURE__*/React__namespace.forwardRef(function ComboboxTrigger(componentProps, forwardedRef) {
1432
+ const {
1433
+ render,
1434
+ className,
1435
+ nativeButton = true,
1436
+ disabled: disabledProp = false,
1437
+ id: idProp,
1438
+ ...elementProps
1439
+ } = componentProps;
1440
+ const {
1441
+ state: fieldState,
1442
+ disabled: fieldDisabled,
1443
+ setTouched,
1444
+ setFocused,
1445
+ validationMode,
1446
+ validation
1447
+ } = useLabel.useFieldRootContext();
1448
+ const {
1449
+ labelId: fieldLabelId
1450
+ } = useLabel.useLabelableContext();
1451
+ const store = useComboboxRootContext();
1452
+ const {
1453
+ filteredItems
1454
+ } = useComboboxDerivedItemsContext();
1455
+ const selectionMode = InternalBackdrop.useStore(store, selectors.selectionMode);
1456
+ const comboboxDisabled = InternalBackdrop.useStore(store, selectors.disabled);
1457
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
1458
+ const required = InternalBackdrop.useStore(store, selectors.required);
1459
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
1460
+ const popupSideValue = InternalBackdrop.useStore(store, selectors.popupSide);
1461
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
1462
+ const listElement = InternalBackdrop.useStore(store, selectors.listElement);
1463
+ const triggerProps = InternalBackdrop.useStore(store, selectors.triggerProps);
1464
+ const triggerElement = InternalBackdrop.useStore(store, selectors.triggerElement);
1465
+ const inputInsidePopup = InternalBackdrop.useStore(store, selectors.inputInsidePopup);
1466
+ const rootId = InternalBackdrop.useStore(store, selectors.id);
1467
+ const comboboxLabelId = InternalBackdrop.useStore(store, selectors.labelId);
1468
+ const open = InternalBackdrop.useStore(store, selectors.open);
1469
+ const selectedValue = InternalBackdrop.useStore(store, selectors.selectedValue);
1470
+ const activeIndex = InternalBackdrop.useStore(store, selectors.activeIndex);
1471
+ const selectedIndex = InternalBackdrop.useStore(store, selectors.selectedIndex);
1472
+ const hasSelectedValue = InternalBackdrop.useStore(store, selectors.hasSelectedValue);
1473
+ const floatingRootContext = useComboboxFloatingContext();
1474
+ const inputValue = useComboboxInputValueContext();
1475
+ const focusTimeout = useButton.useTimeout();
1476
+ const disabled = fieldDisabled || comboboxDisabled || disabledProp;
1477
+ const listEmpty = filteredItems.length === 0;
1478
+ const popupSide = mounted && positionerElement ? popupSideValue : null;
1479
+ useLabel.useLabelableId({
1480
+ id: inputInsidePopup ? idProp : undefined
1481
+ });
1482
+ const id = inputInsidePopup ? idProp ?? rootId : idProp;
1483
+ const ariaLabelledBy = resolveAriaLabelledBy(fieldLabelId, comboboxLabelId);
1484
+ const currentPointerTypeRef = React__namespace.useRef('');
1485
+ function trackPointerType(event) {
1486
+ currentPointerTypeRef.current = event.pointerType;
1487
+ }
1488
+ const domReference = floatingRootContext.useState('domReferenceElement');
1489
+
1490
+ // Update the floating root context to use the trigger element when it differs from the current reference.
1491
+ // This ensures useClick and useTypeahead attach handlers to the correct element.
1492
+ React__namespace.useEffect(() => {
1493
+ if (!inputInsidePopup) {
1494
+ return;
1495
+ }
1496
+ if (triggerElement && triggerElement !== domReference) {
1497
+ floatingRootContext.set('domReferenceElement', triggerElement);
1498
+ }
1499
+ }, [triggerElement, domReference, floatingRootContext, inputInsidePopup]);
1500
+ const {
1501
+ reference: triggerTypeaheadProps
1502
+ } = useCompositeListItem.useTypeahead(floatingRootContext, {
1503
+ enabled: !open && !readOnly && !comboboxDisabled && selectionMode === 'single',
1504
+ listRef: store.state.labelsRef,
1505
+ activeIndex,
1506
+ selectedIndex,
1507
+ onMatch(index) {
1508
+ const nextSelectedValue = store.state.valuesRef.current[index];
1509
+ if (nextSelectedValue !== undefined) {
1510
+ store.state.setSelectedValue(nextSelectedValue, useButton.createChangeEventDetails('none'));
1511
+ }
1512
+ }
1513
+ });
1514
+ const {
1515
+ reference: triggerClickProps
1516
+ } = InternalBackdrop.useClick(floatingRootContext, {
1517
+ enabled: !readOnly && !comboboxDisabled,
1518
+ event: 'mousedown'
1519
+ });
1520
+ const {
1521
+ buttonRef,
1522
+ getButtonProps
1523
+ } = useButton.useButton({
1524
+ native: nativeButton,
1525
+ disabled
1526
+ });
1527
+ const state = {
1528
+ ...fieldState,
1529
+ open,
1530
+ disabled,
1531
+ popupSide,
1532
+ listEmpty,
1533
+ placeholder: !hasSelectedValue
1534
+ };
1535
+ const setTriggerElement = useButton.useStableCallback(element => {
1536
+ store.set('triggerElement', element);
1537
+ });
1538
+ const element = useRenderElement.useRenderElement('button', componentProps, {
1539
+ ref: [forwardedRef, buttonRef, setTriggerElement],
1540
+ state,
1541
+ props: [triggerProps, triggerClickProps, triggerTypeaheadProps, {
1542
+ id,
1543
+ tabIndex: inputInsidePopup ? 0 : -1,
1544
+ role: inputInsidePopup ? 'combobox' : undefined,
1545
+ 'aria-expanded': open ? 'true' : 'false',
1546
+ 'aria-haspopup': inputInsidePopup ? 'dialog' : 'listbox',
1547
+ 'aria-controls': open ? listElement?.id : undefined,
1548
+ 'aria-required': inputInsidePopup ? required || undefined : undefined,
1549
+ 'aria-labelledby': ariaLabelledBy,
1550
+ onPointerDown: trackPointerType,
1551
+ onPointerEnter: trackPointerType,
1552
+ onFocus() {
1553
+ setFocused(true);
1554
+ if (disabled || readOnly) {
1555
+ return;
1556
+ }
1557
+ focusTimeout.start(0, store.state.forceMount);
1558
+ },
1559
+ onBlur(event) {
1560
+ // If focus is moving into the popup, don't count it as a blur.
1561
+ if (useButton.contains(positionerElement, event.relatedTarget)) {
1562
+ return;
1563
+ }
1564
+ setTouched(true);
1565
+ setFocused(false);
1566
+ if (validationMode === 'onBlur') {
1567
+ const valueToValidate = selectionMode === 'none' ? inputValue : selectedValue;
1568
+ validation.commit(valueToValidate);
1569
+ }
1570
+ },
1571
+ onMouseDown(event) {
1572
+ if (disabled || readOnly) {
1573
+ return;
1574
+ }
1575
+ if (!inputInsidePopup) {
1576
+ floatingRootContext.set('domReferenceElement', event.currentTarget);
1577
+ }
1578
+
1579
+ // Ensure items are registered for initial selection highlight.
1580
+ store.state.forceMount();
1581
+ if (currentPointerTypeRef.current !== 'touch') {
1582
+ store.state.inputRef.current?.focus();
1583
+ if (!inputInsidePopup) {
1584
+ event.preventDefault();
1585
+ }
1586
+ }
1587
+ if (open) {
1588
+ return;
1589
+ }
1590
+ const doc = useButton.ownerDocument(event.currentTarget);
1591
+ function handleMouseUp(mouseEvent) {
1592
+ if (!triggerElement) {
1593
+ return;
1594
+ }
1595
+ const mouseUpTarget = useButton.getTarget(mouseEvent);
1596
+ const positioner = store.state.positionerElement;
1597
+ const list = store.state.listElement;
1598
+ if (useButton.contains(triggerElement, mouseUpTarget) || useButton.contains(positioner, mouseUpTarget) || useButton.contains(list, mouseUpTarget) || mouseUpTarget === triggerElement) {
1599
+ return;
1600
+ }
1601
+ const bounds = useCompositeListItem.getPseudoElementBounds(triggerElement);
1602
+ const withinHorizontal = mouseEvent.clientX >= bounds.left - BOUNDARY_OFFSET && mouseEvent.clientX <= bounds.right + BOUNDARY_OFFSET;
1603
+ const withinVertical = mouseEvent.clientY >= bounds.top - BOUNDARY_OFFSET && mouseEvent.clientY <= bounds.bottom + BOUNDARY_OFFSET;
1604
+ if (withinHorizontal && withinVertical) {
1605
+ return;
1606
+ }
1607
+ store.state.setOpen(false, useButton.createChangeEventDetails('cancel-open', mouseEvent));
1608
+ }
1609
+ if (inputInsidePopup) {
1610
+ doc.addEventListener('mouseup', handleMouseUp, {
1611
+ once: true
1612
+ });
1613
+ }
1614
+ },
1615
+ onKeyDown(event) {
1616
+ if (disabled || readOnly) {
1617
+ return;
1618
+ }
1619
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
1620
+ useButton.stopEvent(event);
1621
+ store.state.setOpen(true, useButton.createChangeEventDetails(useButton.listNavigation, event.nativeEvent));
1622
+ store.state.inputRef.current?.focus();
1623
+ }
1624
+ }
1625
+ }, validation ? validation.getValidationProps(elementProps) : elementProps, getButtonProps],
1626
+ stateAttributesMapping: triggerStateAttributesMapping
1627
+ });
1628
+ return element;
1629
+ });
1630
+ if (process.env.NODE_ENV !== "production") ComboboxTrigger.displayName = "ComboboxTrigger";
1631
+
1632
+ const ComboboxChipsContext = /*#__PURE__*/React__namespace.createContext(undefined);
1633
+ if (process.env.NODE_ENV !== "production") ComboboxChipsContext.displayName = "ComboboxChipsContext";
1634
+ function useComboboxChipsContext() {
1635
+ return React__namespace.useContext(ComboboxChipsContext);
1636
+ }
1637
+
1638
+ const ComboboxPositionerContext = /*#__PURE__*/React__namespace.createContext(undefined);
1639
+ if (process.env.NODE_ENV !== "production") ComboboxPositionerContext.displayName = "ComboboxPositionerContext";
1640
+ function useComboboxPositionerContext(optional) {
1641
+ const context = React__namespace.useContext(ComboboxPositionerContext);
1642
+ if (context === undefined && !optional) {
1643
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Combobox.Popup> and <Combobox.Arrow> must be used within the <Combobox.Positioner> component' : useRenderElement.formatErrorMessage(21));
1644
+ }
1645
+ return context;
1646
+ }
1647
+
1648
+ /**
1649
+ * @internal
1650
+ */
1651
+ const ComboboxInternalDismissButton = /*#__PURE__*/React__namespace.forwardRef(function ComboboxInternalDismissButton(_, forwardedRef) {
1652
+ const store = useComboboxRootContext();
1653
+ const {
1654
+ buttonRef,
1655
+ getButtonProps
1656
+ } = useButton.useButton({
1657
+ native: false
1658
+ });
1659
+ const mergedRef = useRenderElement.useMergedRefs(forwardedRef, buttonRef);
1660
+ const handleDismiss = useButton.useStableCallback(event => {
1661
+ store.state.setOpen(false, useButton.createChangeEventDetails(useButton.closePress, event.nativeEvent, event.currentTarget));
1662
+ });
1663
+ const dismissProps = getButtonProps({
1664
+ onClick: handleDismiss
1665
+ });
1666
+ return /*#__PURE__*/jsxRuntime.jsx("span", {
1667
+ ref: mergedRef,
1668
+ ...dismissProps,
1669
+ "aria-label": "Dismiss",
1670
+ tabIndex: undefined,
1671
+ style: useButton.visuallyHiddenInput
1672
+ });
1673
+ });
1674
+ if (process.env.NODE_ENV !== "production") ComboboxInternalDismissButton.displayName = "ComboboxInternalDismissButton";
1675
+
1676
+ const ComboboxInput = /*#__PURE__*/React__namespace.forwardRef(function ComboboxInput(componentProps, forwardedRef) {
1677
+ const {
1678
+ render,
1679
+ className,
1680
+ disabled: disabledProp = false,
1681
+ id: idProp,
1682
+ ...elementProps
1683
+ } = componentProps;
1684
+ const {
1685
+ state: fieldState,
1686
+ disabled: fieldDisabled,
1687
+ setTouched,
1688
+ setFocused,
1689
+ validationMode,
1690
+ validation
1691
+ } = useLabel.useFieldRootContext();
1692
+ const {
1693
+ labelId: fieldLabelId
1694
+ } = useLabel.useLabelableContext();
1695
+ const comboboxChipsContext = useComboboxChipsContext();
1696
+ const positioning = useComboboxPositionerContext(true);
1697
+ const hasPositionerParent = Boolean(positioning);
1698
+ const store = useComboboxRootContext();
1699
+ const {
1700
+ filteredItems
1701
+ } = useComboboxDerivedItemsContext();
1702
+ // `inputValue` can't be placed in the store.
1703
+ // https://github.com/mui/base-ui/issues/2703
1704
+ const inputValue = useComboboxInputValueContext();
1705
+ const direction = InternalBackdrop.useDirection();
1706
+ const required = InternalBackdrop.useStore(store, selectors.required);
1707
+ const comboboxDisabled = InternalBackdrop.useStore(store, selectors.disabled);
1708
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
1709
+ const name = InternalBackdrop.useStore(store, selectors.name);
1710
+ const selectionMode = InternalBackdrop.useStore(store, selectors.selectionMode);
1711
+ const autoHighlightMode = InternalBackdrop.useStore(store, selectors.autoHighlight);
1712
+ const inputProps = InternalBackdrop.useStore(store, selectors.inputProps);
1713
+ const triggerProps = InternalBackdrop.useStore(store, selectors.triggerProps);
1714
+ const open = InternalBackdrop.useStore(store, selectors.open);
1715
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
1716
+ const selectedValue = InternalBackdrop.useStore(store, selectors.selectedValue);
1717
+ const popupSideValue = InternalBackdrop.useStore(store, selectors.popupSide);
1718
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
1719
+ const rootId = InternalBackdrop.useStore(store, selectors.id);
1720
+ const inline = InternalBackdrop.useStore(store, selectors.inline);
1721
+ const modal = InternalBackdrop.useStore(store, selectors.modal);
1722
+ const autoHighlightEnabled = Boolean(autoHighlightMode);
1723
+ const popupSide = mounted && positionerElement ? popupSideValue : null;
1724
+ const disabled = fieldDisabled || comboboxDisabled || disabledProp;
1725
+ const listEmpty = filteredItems.length === 0;
1726
+ const isInsidePopup = hasPositionerParent || inline;
1727
+ const focusManagerModal = !isInsidePopup || modal;
1728
+ const id = useButton.useBaseUiId(idProp ?? (!isInsidePopup ? rootId : undefined));
1729
+ const ariaLabelledBy = resolveAriaLabelledBy(fieldLabelId, undefined);
1730
+ const fieldStateForInput = hasPositionerParent ? useLabel.DEFAULT_FIELD_STATE_ATTRIBUTES : fieldState;
1731
+ const [composingValue, setComposingValue] = React__namespace.useState(null);
1732
+ const isComposingRef = React__namespace.useRef(false);
1733
+ const lastActiveIndexRef = React__namespace.useRef(null);
1734
+ const shouldRestoreActiveIndexRef = React__namespace.useRef(false);
1735
+ const setInputElement = useButton.useStableCallback(element => {
1736
+ const nextIsInsidePopup = hasPositionerParent || store.state.inline;
1737
+ if (nextIsInsidePopup && !store.state.hasInputValue) {
1738
+ store.state.setInputValue('', useButton.createChangeEventDetails(useButton.none));
1739
+ }
1740
+ store.update({
1741
+ inputElement: element,
1742
+ inputInsidePopup: nextIsInsidePopup
1743
+ });
1744
+ });
1745
+ const validationProps = hasPositionerParent || !validation ? elementProps : validation.getValidationProps(elementProps);
1746
+ const state = {
1747
+ ...fieldStateForInput,
1748
+ open,
1749
+ disabled,
1750
+ readOnly,
1751
+ popupSide,
1752
+ listEmpty
1753
+ };
1754
+ function handleKeyDown(event) {
1755
+ if (!comboboxChipsContext) {
1756
+ return undefined;
1757
+ }
1758
+ let nextIndex;
1759
+ const {
1760
+ highlightedChipIndex
1761
+ } = comboboxChipsContext;
1762
+ if (highlightedChipIndex !== undefined) {
1763
+ if (event.key === 'ArrowLeft') {
1764
+ event.preventDefault();
1765
+ if (highlightedChipIndex > 0) {
1766
+ nextIndex = highlightedChipIndex - 1;
1767
+ } else {
1768
+ nextIndex = undefined;
1769
+ }
1770
+ } else if (event.key === 'ArrowRight') {
1771
+ event.preventDefault();
1772
+ if (highlightedChipIndex < selectedValue.length - 1) {
1773
+ nextIndex = highlightedChipIndex + 1;
1774
+ } else {
1775
+ nextIndex = undefined;
1776
+ }
1777
+ } else if (event.key === 'Backspace' || event.key === 'Delete') {
1778
+ event.preventDefault();
1779
+ // Move highlight appropriately after removal.
1780
+ const computedNextIndex = highlightedChipIndex >= selectedValue.length - 1 ? selectedValue.length - 2 : highlightedChipIndex;
1781
+ // If the computed index is negative, treat it as no highlight.
1782
+ nextIndex = computedNextIndex >= 0 ? computedNextIndex : undefined;
1783
+ store.state.setIndices({
1784
+ activeIndex: null,
1785
+ selectedIndex: null,
1786
+ type: 'keyboard'
1787
+ });
1788
+ }
1789
+ return nextIndex;
1790
+ }
1791
+
1792
+ // Handle navigation when no chip is highlighted
1793
+ if (event.key === 'ArrowLeft' && (event.currentTarget.selectionStart ?? 0) === 0 && selectedValue.length > 0) {
1794
+ event.preventDefault();
1795
+ const lastChipIndex = Math.max(selectedValue.length - 1, 0);
1796
+ nextIndex = lastChipIndex;
1797
+ } else if (event.key === 'Backspace' && event.currentTarget.value === '' && selectedValue.length > 0) {
1798
+ store.state.setIndices({
1799
+ activeIndex: null,
1800
+ selectedIndex: null,
1801
+ type: 'keyboard'
1802
+ });
1803
+ event.preventDefault();
1804
+ }
1805
+ return nextIndex;
1806
+ }
1807
+ const element = useRenderElement.useRenderElement('input', componentProps, {
1808
+ state,
1809
+ ref: [forwardedRef, store.state.inputRef, setInputElement],
1810
+ props: [inputProps, triggerProps, {
1811
+ type: 'text',
1812
+ value: componentProps.value ?? composingValue ?? inputValue,
1813
+ 'aria-readonly': readOnly || undefined,
1814
+ 'aria-required': required || undefined,
1815
+ 'aria-labelledby': ariaLabelledBy,
1816
+ disabled,
1817
+ readOnly,
1818
+ required: selectionMode === 'none' ? required : undefined,
1819
+ ...(selectionMode === 'none' && name && {
1820
+ name
1821
+ }),
1822
+ id,
1823
+ onFocus() {
1824
+ setFocused(true);
1825
+ if (!inline || !shouldRestoreActiveIndexRef.current) {
1826
+ return;
1827
+ }
1828
+ shouldRestoreActiveIndexRef.current = false;
1829
+ const nextActiveIndex = lastActiveIndexRef.current;
1830
+ if (nextActiveIndex == null ||
1831
+ // `valuesRef` can be sparse, so guard against restoring a removed slot.
1832
+ !Object.hasOwn(store.state.valuesRef.current, nextActiveIndex)) {
1833
+ return;
1834
+ }
1835
+ store.state.setIndices({
1836
+ activeIndex: nextActiveIndex
1837
+ });
1838
+ },
1839
+ onBlur() {
1840
+ setTouched(true);
1841
+ setFocused(false);
1842
+ const activeIndex = store.state.activeIndex;
1843
+ if (inline && activeIndex !== null && autoHighlightMode !== 'always') {
1844
+ lastActiveIndexRef.current = activeIndex;
1845
+ shouldRestoreActiveIndexRef.current = true;
1846
+ store.state.setIndices({
1847
+ activeIndex: null
1848
+ });
1849
+ }
1850
+ if (validationMode === 'onBlur') {
1851
+ const valueToValidate = selectionMode === 'none' ? inputValue : selectedValue;
1852
+ validation.commit(valueToValidate);
1853
+ }
1854
+ },
1855
+ onCompositionStart(event) {
1856
+ if (useButton.isAndroid) {
1857
+ return;
1858
+ }
1859
+ isComposingRef.current = true;
1860
+ setComposingValue(event.currentTarget.value);
1861
+ },
1862
+ onCompositionEnd(event) {
1863
+ isComposingRef.current = false;
1864
+ const next = event.currentTarget.value;
1865
+ setComposingValue(null);
1866
+ store.state.setInputValue(next, useButton.createChangeEventDetails(useButton.inputChange, event.nativeEvent));
1867
+ },
1868
+ onChange(event) {
1869
+ // Autofill may not provide `inputType` (Chrome) or may report
1870
+ // `insertReplacementText` (Firefox).
1871
+ const inputType = event.nativeEvent.inputType;
1872
+ const autofillLikeInput = !inputType || inputType === 'insertReplacementText';
1873
+ const shouldOpenOnInput = isComposingRef.current || !autofillLikeInput;
1874
+
1875
+ // During IME composition, avoid propagating controlled updates to prevent
1876
+ // filtering the options prematurely so `Empty` won't show incorrectly.
1877
+ // We can't rely on this check for Android due to how it handles composition
1878
+ // events with some keyboards (e.g. Samsung keyboard with predictive text on
1879
+ // treats all text as always-composing).
1880
+ // https://github.com/mui/base-ui/issues/2942
1881
+ if (isComposingRef.current) {
1882
+ const nextVal = event.currentTarget.value;
1883
+ setComposingValue(nextVal);
1884
+ if (nextVal === '' && !store.state.openOnInputClick && !store.state.inputInsidePopup) {
1885
+ store.state.setOpen(false, useButton.createChangeEventDetails(useButton.inputClear, event.nativeEvent));
1886
+ }
1887
+ const trimmed = nextVal.trim();
1888
+ const shouldMaintainHighlight = autoHighlightEnabled && trimmed !== '';
1889
+ if (!readOnly && !disabled && trimmed) {
1890
+ if (shouldOpenOnInput) {
1891
+ store.state.setOpen(true, useButton.createChangeEventDetails(useButton.inputChange, event.nativeEvent));
1892
+ if (!autoHighlightEnabled) {
1893
+ store.state.setIndices({
1894
+ activeIndex: null,
1895
+ selectedIndex: null,
1896
+ type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
1897
+ });
1898
+ }
1899
+ }
1900
+ }
1901
+ if (open && store.state.activeIndex !== null && !shouldMaintainHighlight) {
1902
+ store.state.setIndices({
1903
+ activeIndex: null,
1904
+ selectedIndex: null,
1905
+ type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
1906
+ });
1907
+ }
1908
+ return;
1909
+ }
1910
+ store.state.setInputValue(event.currentTarget.value, useButton.createChangeEventDetails(useButton.inputChange, event.nativeEvent));
1911
+ const empty = event.currentTarget.value === '';
1912
+ const clearDetails = useButton.createChangeEventDetails(useButton.inputClear, event.nativeEvent);
1913
+ if (empty && !store.state.inputInsidePopup) {
1914
+ if (selectionMode === 'single') {
1915
+ store.state.setSelectedValue(null, clearDetails);
1916
+ }
1917
+ if (!store.state.openOnInputClick) {
1918
+ store.state.setOpen(false, clearDetails);
1919
+ }
1920
+ }
1921
+ const trimmed = event.currentTarget.value.trim();
1922
+ if (!readOnly && !disabled && trimmed) {
1923
+ if (shouldOpenOnInput) {
1924
+ store.state.setOpen(true, useButton.createChangeEventDetails(useButton.inputChange, event.nativeEvent));
1925
+ // When autoHighlight is enabled, keep the highlight (will be set to 0 in root).
1926
+ if (!autoHighlightEnabled) {
1927
+ store.state.setIndices({
1928
+ activeIndex: null,
1929
+ selectedIndex: null,
1930
+ type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
1931
+ });
1932
+ }
1933
+ }
1934
+ }
1935
+
1936
+ // When the user types, ensure the list resets its highlight so that
1937
+ // virtual focus returns to the input (aria-activedescendant is
1938
+ // cleared).
1939
+ if (open && store.state.activeIndex !== null && !autoHighlightEnabled) {
1940
+ store.state.setIndices({
1941
+ activeIndex: null,
1942
+ selectedIndex: null,
1943
+ type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
1944
+ });
1945
+ }
1946
+ },
1947
+ onKeyDown(event) {
1948
+ if (disabled || readOnly) {
1949
+ return;
1950
+ }
1951
+ if (event.ctrlKey || event.shiftKey || event.altKey || event.metaKey) {
1952
+ return;
1953
+ }
1954
+ store.state.keyboardActiveRef.current = true;
1955
+ const input = event.currentTarget;
1956
+ const scrollAmount = input.scrollWidth - input.clientWidth;
1957
+ const isRTL = direction === 'rtl';
1958
+ if (event.key === 'Home') {
1959
+ useButton.stopEvent(event);
1960
+ const cursor = useButton.isFirefox && isRTL ? input.value.length : 0;
1961
+ input.setSelectionRange(cursor, cursor);
1962
+ input.scrollLeft = 0;
1963
+ return;
1964
+ }
1965
+ if (event.key === 'End') {
1966
+ useButton.stopEvent(event);
1967
+ const cursor = useButton.isFirefox && isRTL ? 0 : input.value.length;
1968
+ input.setSelectionRange(cursor, cursor);
1969
+ input.scrollLeft = isRTL ? -scrollAmount : scrollAmount;
1970
+ return;
1971
+ }
1972
+ if (!mounted && event.key === 'Escape') {
1973
+ const isClear = selectionMode === 'multiple' && Array.isArray(selectedValue) ? selectedValue.length === 0 : selectedValue === null;
1974
+ const details = useButton.createChangeEventDetails(useButton.escapeKey, event.nativeEvent);
1975
+ const value = selectionMode === 'multiple' ? [] : null;
1976
+ store.state.setInputValue('', details);
1977
+ store.state.setSelectedValue(value, details);
1978
+ if (!isClear && !store.state.inline && !details.isPropagationAllowed) {
1979
+ event.stopPropagation();
1980
+ }
1981
+ return;
1982
+ }
1983
+
1984
+ // Handle deletion when no chip is highlighted and the input is empty.
1985
+ if (comboboxChipsContext && event.key === 'Backspace' && input.value === '' && comboboxChipsContext.highlightedChipIndex === undefined && Array.isArray(selectedValue) && selectedValue.length > 0) {
1986
+ const newValue = selectedValue.slice(0, -1);
1987
+ // If the removed item was also the active (highlighted) item, clear highlight
1988
+ store.state.setIndices({
1989
+ activeIndex: null,
1990
+ selectedIndex: null,
1991
+ type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
1992
+ });
1993
+ store.state.setSelectedValue(newValue, useButton.createChangeEventDetails(useButton.none, event.nativeEvent));
1994
+ return;
1995
+ }
1996
+ const hadHighlightedChip = comboboxChipsContext?.highlightedChipIndex !== undefined;
1997
+ const nextIndex = handleKeyDown(event);
1998
+ comboboxChipsContext?.setHighlightedChipIndex(nextIndex);
1999
+ if (nextIndex !== undefined) {
2000
+ comboboxChipsContext?.chipsRef.current[nextIndex]?.focus();
2001
+ } else if (hadHighlightedChip) {
2002
+ store.state.inputRef.current?.focus();
2003
+ }
2004
+
2005
+ // event.isComposing
2006
+ if (event.which === 229) {
2007
+ return;
2008
+ }
2009
+ if (event.key === 'Enter' && open) {
2010
+ const activeIndex = store.state.activeIndex;
2011
+ const nativeEvent = event.nativeEvent;
2012
+ if (activeIndex === null) {
2013
+ if (inline) {
2014
+ return;
2015
+ }
2016
+
2017
+ // Allow form submission when no item is highlighted.
2018
+ store.state.setOpen(false, useButton.createChangeEventDetails(useButton.none, nativeEvent));
2019
+ return;
2020
+ }
2021
+ useButton.stopEvent(event);
2022
+ const listItem = store.state.listRef.current[activeIndex];
2023
+ if (listItem) {
2024
+ store.state.selectionEventRef.current = nativeEvent;
2025
+ listItem.click();
2026
+ store.state.selectionEventRef.current = null;
2027
+ }
2028
+ }
2029
+ },
2030
+ onPointerMove() {
2031
+ store.state.keyboardActiveRef.current = false;
2032
+ },
2033
+ onPointerDown() {
2034
+ store.state.keyboardActiveRef.current = false;
2035
+ }
2036
+ }, validationProps],
2037
+ stateAttributesMapping: triggerStateAttributesMapping
2038
+ });
2039
+ return /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
2040
+ children: [open && focusManagerModal && /*#__PURE__*/jsxRuntime.jsx(ComboboxInternalDismissButton, {
2041
+ ref: store.state.startDismissRef
2042
+ }), element]
2043
+ });
2044
+ });
2045
+ if (process.env.NODE_ENV !== "production") ComboboxInput.displayName = "ComboboxInput";
2046
+
2047
+ /**
2048
+ * A wrapper for the input and its associated controls.
2049
+ * Renders a `<div>` element.
2050
+ */
2051
+ const ComboboxInputGroup = /*#__PURE__*/React__namespace.forwardRef(function ComboboxInputGroup(componentProps, forwardedRef) {
2052
+ const {
2053
+ render,
2054
+ className,
2055
+ ...elementProps
2056
+ } = componentProps;
2057
+ const {
2058
+ state: fieldState,
2059
+ disabled: fieldDisabled
2060
+ } = useLabel.useFieldRootContext();
2061
+ const store = useComboboxRootContext();
2062
+ const {
2063
+ filteredItems
2064
+ } = useComboboxDerivedItemsContext();
2065
+ const open = InternalBackdrop.useStore(store, selectors.open);
2066
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
2067
+ const popupSideValue = InternalBackdrop.useStore(store, selectors.popupSide);
2068
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
2069
+ const comboboxDisabled = InternalBackdrop.useStore(store, selectors.disabled);
2070
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
2071
+ const hasSelectedValue = InternalBackdrop.useStore(store, selectors.hasSelectedValue);
2072
+ const selectionMode = InternalBackdrop.useStore(store, selectors.selectionMode);
2073
+ const popupSide = mounted && positionerElement ? popupSideValue : null;
2074
+ const disabled = fieldDisabled || comboboxDisabled;
2075
+ const listEmpty = filteredItems.length === 0;
2076
+ const placeholder = selectionMode === 'none' ? false : !hasSelectedValue;
2077
+ const state = {
2078
+ ...fieldState,
2079
+ open,
2080
+ disabled,
2081
+ readOnly,
2082
+ popupSide,
2083
+ listEmpty,
2084
+ placeholder
2085
+ };
2086
+ const setInputGroupElement = useButton.useStableCallback(element => {
2087
+ store.set('inputGroupElement', element);
2088
+ });
2089
+ return useRenderElement.useRenderElement('div', componentProps, {
2090
+ ref: [forwardedRef, setInputGroupElement],
2091
+ props: [{
2092
+ role: 'group'
2093
+ }, elementProps],
2094
+ state,
2095
+ stateAttributesMapping: triggerStateAttributesMapping
2096
+ });
2097
+ });
2098
+ if (process.env.NODE_ENV !== "production") ComboboxInputGroup.displayName = "ComboboxInputGroup";
2099
+
2100
+ /**
2101
+ * An icon that indicates that the trigger button opens the popup.
2102
+ * Renders a `<span>` element.
2103
+ */
2104
+ const ComboboxIcon = /*#__PURE__*/React__namespace.forwardRef(function ComboboxIcon(componentProps, forwardedRef) {
2105
+ const {
2106
+ className,
2107
+ render,
2108
+ ...elementProps
2109
+ } = componentProps;
2110
+ const element = useRenderElement.useRenderElement('span', componentProps, {
2111
+ ref: forwardedRef,
2112
+ props: [{
2113
+ 'aria-hidden': true,
2114
+ children: '▼'
2115
+ }, elementProps]
2116
+ });
2117
+ return element;
2118
+ });
2119
+ if (process.env.NODE_ENV !== "production") ComboboxIcon.displayName = "ComboboxIcon";
2120
+
2121
+ const stateAttributesMapping$2 = {
2122
+ ...useButton.transitionStatusMapping,
2123
+ ...InternalBackdrop.triggerOpenStateMapping
2124
+ };
2125
+
2126
+ /**
2127
+ * Clears the value when clicked.
2128
+ * Renders a `<button>` element.
2129
+ */
2130
+ const ComboboxClear = /*#__PURE__*/React__namespace.forwardRef(function ComboboxClear(componentProps, forwardedRef) {
2131
+ const {
2132
+ render,
2133
+ className,
2134
+ disabled: disabledProp = false,
2135
+ nativeButton = true,
2136
+ keepMounted = false,
2137
+ ...elementProps
2138
+ } = componentProps;
2139
+ const {
2140
+ disabled: fieldDisabled
2141
+ } = useLabel.useFieldRootContext();
2142
+ const store = useComboboxRootContext();
2143
+ const selectionMode = InternalBackdrop.useStore(store, selectors.selectionMode);
2144
+ const comboboxDisabled = InternalBackdrop.useStore(store, selectors.disabled);
2145
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
2146
+ const open = InternalBackdrop.useStore(store, selectors.open);
2147
+ const selectedValue = InternalBackdrop.useStore(store, selectors.selectedValue);
2148
+ const hasSelectionChips = InternalBackdrop.useStore(store, selectors.hasSelectionChips);
2149
+ const inputValue = useComboboxInputValueContext();
2150
+ let visible = false;
2151
+ if (selectionMode === 'none') {
2152
+ visible = inputValue !== '';
2153
+ } else if (selectionMode === 'single') {
2154
+ visible = selectedValue != null;
2155
+ } else {
2156
+ visible = hasSelectionChips;
2157
+ }
2158
+ const disabled = fieldDisabled || comboboxDisabled || disabledProp;
2159
+ const {
2160
+ buttonRef,
2161
+ getButtonProps
2162
+ } = useButton.useButton({
2163
+ native: nativeButton,
2164
+ disabled
2165
+ });
2166
+ const {
2167
+ mounted,
2168
+ transitionStatus,
2169
+ setMounted
2170
+ } = useButton.useTransitionStatus(visible);
2171
+ const state = {
2172
+ disabled,
2173
+ open,
2174
+ transitionStatus
2175
+ };
2176
+ useButton.useOpenChangeComplete({
2177
+ open: visible,
2178
+ ref: store.state.clearRef,
2179
+ onComplete() {
2180
+ if (!visible) {
2181
+ setMounted(false);
2182
+ }
2183
+ }
2184
+ });
2185
+ const element = useRenderElement.useRenderElement('button', componentProps, {
2186
+ state,
2187
+ ref: [forwardedRef, buttonRef, store.state.clearRef],
2188
+ props: [{
2189
+ tabIndex: -1,
2190
+ children: 'x',
2191
+ // Avoid stealing focus from the input.
2192
+ onMouseDown(event) {
2193
+ event.preventDefault();
2194
+ },
2195
+ onClick(event) {
2196
+ if (disabled || readOnly) {
2197
+ return;
2198
+ }
2199
+ const keyboardActiveRef = store.state.keyboardActiveRef;
2200
+ store.state.setInputValue('', useButton.createChangeEventDetails(useButton.clearPress, event.nativeEvent));
2201
+ if (selectionMode !== 'none') {
2202
+ store.state.setSelectedValue(Array.isArray(selectedValue) ? [] : null, useButton.createChangeEventDetails(useButton.clearPress, event.nativeEvent));
2203
+ store.state.setIndices({
2204
+ activeIndex: null,
2205
+ selectedIndex: null,
2206
+ type: keyboardActiveRef.current ? 'keyboard' : 'pointer'
2207
+ });
2208
+ } else {
2209
+ store.state.setIndices({
2210
+ activeIndex: null,
2211
+ type: keyboardActiveRef.current ? 'keyboard' : 'pointer'
2212
+ });
2213
+ }
2214
+ store.state.inputRef.current?.focus();
2215
+ }
2216
+ }, elementProps, getButtonProps],
2217
+ stateAttributesMapping: stateAttributesMapping$2
2218
+ });
2219
+ const shouldRender = keepMounted || mounted;
2220
+ if (!shouldRender) {
2221
+ return null;
2222
+ }
2223
+ return element;
2224
+ });
2225
+ if (process.env.NODE_ENV !== "production") ComboboxClear.displayName = "ComboboxClear";
2226
+
2227
+ const GroupCollectionContext = /*#__PURE__*/React__namespace.createContext(null);
2228
+ if (process.env.NODE_ENV !== "production") GroupCollectionContext.displayName = "GroupCollectionContext";
2229
+ function useGroupCollectionContext() {
2230
+ return React__namespace.useContext(GroupCollectionContext);
2231
+ }
2232
+ function GroupCollectionProvider(props) {
2233
+ const {
2234
+ children,
2235
+ items
2236
+ } = props;
2237
+ const contextValue = React__namespace.useMemo(() => ({
2238
+ items
2239
+ }), [items]);
2240
+ return /*#__PURE__*/jsxRuntime.jsx(GroupCollectionContext.Provider, {
2241
+ value: contextValue,
2242
+ children: children
2243
+ });
2244
+ }
2245
+
2246
+ function ComboboxCollection(props) {
2247
+ const {
2248
+ children
2249
+ } = props;
2250
+ const {
2251
+ filteredItems
2252
+ } = useComboboxDerivedItemsContext();
2253
+ const groupContext = useGroupCollectionContext();
2254
+ const itemsToRender = groupContext ? groupContext.items : filteredItems;
2255
+ if (!itemsToRender) {
2256
+ return null;
2257
+ }
2258
+ return /*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
2259
+ children: itemsToRender.map(children)
2260
+ });
2261
+ }
2262
+
2263
+ const ComboboxList = /*#__PURE__*/React__namespace.forwardRef(function ComboboxList(componentProps, forwardedRef) {
2264
+ var _ComboboxCollection;
2265
+ const {
2266
+ render,
2267
+ className,
2268
+ children,
2269
+ ...elementProps
2270
+ } = componentProps;
2271
+ const store = useComboboxRootContext();
2272
+ const floatingRootContext = useComboboxFloatingContext();
2273
+ const hasPositionerContext = Boolean(useComboboxPositionerContext(true));
2274
+ const {
2275
+ filteredItems
2276
+ } = useComboboxDerivedItemsContext();
2277
+ const items = InternalBackdrop.useStore(store, selectors.items);
2278
+ const labelsRef = InternalBackdrop.useStore(store, selectors.labelsRef);
2279
+ const listRef = InternalBackdrop.useStore(store, selectors.listRef);
2280
+ const selectionMode = InternalBackdrop.useStore(store, selectors.selectionMode);
2281
+ const grid = InternalBackdrop.useStore(store, selectors.grid);
2282
+ const popupProps = InternalBackdrop.useStore(store, selectors.popupProps);
2283
+ const disabled = InternalBackdrop.useStore(store, selectors.disabled);
2284
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
2285
+ const virtualized = InternalBackdrop.useStore(store, selectors.virtualized);
2286
+ const multiple = selectionMode === 'multiple';
2287
+ const empty = filteredItems.length === 0;
2288
+ const setPositionerElement = useButton.useStableCallback(element => {
2289
+ store.set('positionerElement', element);
2290
+ });
2291
+ const setListElement = useButton.useStableCallback(element => {
2292
+ store.set('listElement', element);
2293
+ });
2294
+
2295
+ // Support "closed template" API: if children is a function, implicitly wrap it
2296
+ // with a Combobox.Collection that reads items from context/root.
2297
+ // Ensures this component's `popupProps` subscription does not cause <Combobox.Item>
2298
+ // to re-render on every active index change.
2299
+ const resolvedChildren = React__namespace.useMemo(() => {
2300
+ if (typeof children === 'function') {
2301
+ return _ComboboxCollection || (_ComboboxCollection = /*#__PURE__*/jsxRuntime.jsx(ComboboxCollection, {
2302
+ children: children
2303
+ }));
2304
+ }
2305
+ return children;
2306
+ }, [children]);
2307
+ const state = {
2308
+ empty
2309
+ };
2310
+ const floatingId = floatingRootContext.useState('floatingId');
2311
+ const element = useRenderElement.useRenderElement('div', componentProps, {
2312
+ state,
2313
+ ref: [forwardedRef, setListElement, hasPositionerContext ? null : setPositionerElement],
2314
+ props: [popupProps, {
2315
+ children: resolvedChildren,
2316
+ tabIndex: -1,
2317
+ id: floatingId,
2318
+ role: grid ? 'grid' : 'listbox',
2319
+ 'aria-multiselectable': multiple ? 'true' : undefined,
2320
+ onKeyDown(event) {
2321
+ if (disabled || readOnly) {
2322
+ return;
2323
+ }
2324
+ if (event.key === 'Enter') {
2325
+ const activeIndex = store.state.activeIndex;
2326
+ if (activeIndex == null) {
2327
+ // Allow form submission when no item is highlighted.
2328
+ return;
2329
+ }
2330
+ useButton.stopEvent(event);
2331
+ const nativeEvent = event.nativeEvent;
2332
+ const listItem = store.state.listRef.current[activeIndex];
2333
+ if (listItem) {
2334
+ store.state.selectionEventRef.current = nativeEvent;
2335
+ listItem.click();
2336
+ store.state.selectionEventRef.current = null;
2337
+ }
2338
+ }
2339
+ },
2340
+ onKeyDownCapture() {
2341
+ store.state.keyboardActiveRef.current = true;
2342
+ },
2343
+ onPointerMoveCapture() {
2344
+ store.state.keyboardActiveRef.current = false;
2345
+ }
2346
+ }, elementProps]
2347
+ });
2348
+ if (virtualized) {
2349
+ return element;
2350
+ }
2351
+ return /*#__PURE__*/jsxRuntime.jsx(useCompositeListItem.CompositeList, {
2352
+ elementsRef: listRef,
2353
+ labelsRef: items ? undefined : labelsRef,
2354
+ children: element
2355
+ });
2356
+ });
2357
+ if (process.env.NODE_ENV !== "production") ComboboxList.displayName = "ComboboxList";
2358
+
2359
+ /**
2360
+ * Displays a status message whose content changes are announced politely to screen readers.
2361
+ * Useful for conveying the status of an asynchronously loaded list.
2362
+ * Renders a `<div>` element.
2363
+ */
2364
+ const ComboboxStatus = /*#__PURE__*/React__namespace.forwardRef(function ComboboxStatus(componentProps, forwardedRef) {
2365
+ const {
2366
+ render,
2367
+ className,
2368
+ ...elementProps
2369
+ } = componentProps;
2370
+ return useRenderElement.useRenderElement('div', componentProps, {
2371
+ ref: forwardedRef,
2372
+ props: [{
2373
+ role: 'status',
2374
+ 'aria-live': 'polite',
2375
+ 'aria-atomic': true
2376
+ }, elementProps]
2377
+ });
2378
+ });
2379
+ if (process.env.NODE_ENV !== "production") ComboboxStatus.displayName = "ComboboxStatus";
2380
+
2381
+ const ComboboxPortalContext = /*#__PURE__*/React__namespace.createContext(undefined);
2382
+ if (process.env.NODE_ENV !== "production") ComboboxPortalContext.displayName = "ComboboxPortalContext";
2383
+ function useComboboxPortalContext() {
2384
+ const context = React__namespace.useContext(ComboboxPortalContext);
2385
+ if (context === undefined) {
2386
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Combobox.Portal> is missing.' : useRenderElement.formatErrorMessage(20));
2387
+ }
2388
+ return context;
2389
+ }
2390
+
2391
+ const ComboboxPortal = /*#__PURE__*/React__namespace.forwardRef(function ComboboxPortal(props, forwardedRef) {
2392
+ const {
2393
+ keepMounted = false,
2394
+ ...portalProps
2395
+ } = props;
2396
+ const store = useComboboxRootContext();
2397
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
2398
+ const forceMounted = InternalBackdrop.useStore(store, selectors.forceMounted);
2399
+ const shouldRender = mounted || keepMounted || forceMounted;
2400
+ if (!shouldRender) {
2401
+ return null;
2402
+ }
2403
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxPortalContext.Provider, {
2404
+ value: keepMounted,
2405
+ children: /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.FloatingPortal, {
2406
+ ref: forwardedRef,
2407
+ ...portalProps
2408
+ })
2409
+ });
2410
+ });
2411
+ if (process.env.NODE_ENV !== "production") ComboboxPortal.displayName = "ComboboxPortal";
2412
+
2413
+ const stateAttributesMapping$1 = {
2414
+ ...InternalBackdrop.popupStateMapping,
2415
+ ...useButton.transitionStatusMapping
2416
+ };
2417
+
2418
+ /**
2419
+ * An overlay displayed beneath the popup.
2420
+ * Renders a `<div>` element.
2421
+ */
2422
+ const ComboboxBackdrop = /*#__PURE__*/React__namespace.forwardRef(function ComboboxBackdrop(componentProps, forwardedRef) {
2423
+ const {
2424
+ className,
2425
+ render,
2426
+ ...elementProps
2427
+ } = componentProps;
2428
+ const store = useComboboxRootContext();
2429
+ const open = InternalBackdrop.useStore(store, selectors.open);
2430
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
2431
+ const transitionStatus = InternalBackdrop.useStore(store, selectors.transitionStatus);
2432
+ const state = {
2433
+ open,
2434
+ transitionStatus
2435
+ };
2436
+ return useRenderElement.useRenderElement('div', componentProps, {
2437
+ state,
2438
+ ref: forwardedRef,
2439
+ stateAttributesMapping: stateAttributesMapping$1,
2440
+ props: [{
2441
+ role: 'presentation',
2442
+ hidden: !mounted,
2443
+ style: {
2444
+ userSelect: 'none',
2445
+ WebkitUserSelect: 'none'
2446
+ }
2447
+ }, elementProps]
2448
+ });
2449
+ });
2450
+ if (process.env.NODE_ENV !== "production") ComboboxBackdrop.displayName = "ComboboxBackdrop";
2451
+
2452
+ const ComboboxPositioner = /*#__PURE__*/React__namespace.forwardRef(function ComboboxPositioner(componentProps, forwardedRef) {
2453
+ const {
2454
+ render,
2455
+ className,
2456
+ anchor,
2457
+ positionMethod = 'absolute',
2458
+ side = 'bottom',
2459
+ align = 'center',
2460
+ sideOffset = 0,
2461
+ alignOffset = 0,
2462
+ collisionBoundary = 'clipping-ancestors',
2463
+ collisionPadding = 5,
2464
+ arrowPadding = 5,
2465
+ sticky = false,
2466
+ disableAnchorTracking = false,
2467
+ collisionAvoidance = InternalBackdrop.DROPDOWN_COLLISION_AVOIDANCE,
2468
+ ...elementProps
2469
+ } = componentProps;
2470
+ const store = useComboboxRootContext();
2471
+ const {
2472
+ filteredItems
2473
+ } = useComboboxDerivedItemsContext();
2474
+ const floatingRootContext = useComboboxFloatingContext();
2475
+ const keepMounted = useComboboxPortalContext();
2476
+ const modal = InternalBackdrop.useStore(store, selectors.modal);
2477
+ const open = InternalBackdrop.useStore(store, selectors.open);
2478
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
2479
+ const openMethod = InternalBackdrop.useStore(store, selectors.openMethod);
2480
+ const triggerElement = InternalBackdrop.useStore(store, selectors.triggerElement);
2481
+ const inputElement = InternalBackdrop.useStore(store, selectors.inputElement);
2482
+ const inputGroupElement = InternalBackdrop.useStore(store, selectors.inputGroupElement);
2483
+ const inputInsidePopup = InternalBackdrop.useStore(store, selectors.inputInsidePopup);
2484
+ const transitionStatus = InternalBackdrop.useStore(store, selectors.transitionStatus);
2485
+ const empty = filteredItems.length === 0;
2486
+ const resolvedAnchor = anchor ?? (inputInsidePopup ? triggerElement : inputGroupElement ?? inputElement);
2487
+ const positioning = useCompositeListItem.useAnchorPositioning({
2488
+ anchor: resolvedAnchor,
2489
+ floatingRootContext,
2490
+ positionMethod,
2491
+ mounted,
2492
+ side,
2493
+ sideOffset,
2494
+ align,
2495
+ alignOffset,
2496
+ arrowPadding,
2497
+ collisionBoundary,
2498
+ collisionPadding,
2499
+ sticky,
2500
+ disableAnchorTracking,
2501
+ keepMounted,
2502
+ collisionAvoidance,
2503
+ lazyFlip: true
2504
+ });
2505
+ InternalBackdrop.useScrollLock(open && modal && openMethod !== 'touch', triggerElement);
2506
+ const defaultProps = React__namespace.useMemo(() => {
2507
+ const style = {
2508
+ ...positioning.positionerStyles
2509
+ };
2510
+ if (!open) {
2511
+ style.pointerEvents = 'none';
2512
+ }
2513
+ return {
2514
+ role: 'presentation',
2515
+ hidden: !mounted,
2516
+ style
2517
+ };
2518
+ }, [open, mounted, positioning.positionerStyles]);
2519
+ const state = {
2520
+ open,
2521
+ side: positioning.side,
2522
+ align: positioning.align,
2523
+ anchorHidden: positioning.anchorHidden,
2524
+ empty
2525
+ };
2526
+ useButton.useIsoLayoutEffect(() => {
2527
+ store.set('popupSide', positioning.side);
2528
+ }, [store, positioning.side]);
2529
+ const contextValue = React__namespace.useMemo(() => ({
2530
+ side: positioning.side,
2531
+ align: positioning.align,
2532
+ arrowRef: positioning.arrowRef,
2533
+ arrowUncentered: positioning.arrowUncentered,
2534
+ arrowStyles: positioning.arrowStyles,
2535
+ anchorHidden: positioning.anchorHidden,
2536
+ isPositioned: positioning.isPositioned
2537
+ }), [positioning.side, positioning.align, positioning.arrowRef, positioning.arrowUncentered, positioning.arrowStyles, positioning.anchorHidden, positioning.isPositioned]);
2538
+ const setPositionerElement = useButton.useStableCallback(element => {
2539
+ store.set('positionerElement', element);
2540
+ });
2541
+ const element = useRenderElement.useRenderElement('div', componentProps, {
2542
+ state,
2543
+ ref: [forwardedRef, setPositionerElement],
2544
+ props: [defaultProps, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), elementProps],
2545
+ stateAttributesMapping: InternalBackdrop.popupStateMapping
2546
+ });
2547
+ return /*#__PURE__*/jsxRuntime.jsxs(ComboboxPositionerContext.Provider, {
2548
+ value: contextValue,
2549
+ children: [mounted && modal && /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.InternalBackdrop, {
2550
+ inert: InternalBackdrop.inertValue(!open),
2551
+ cutout: inputGroupElement ?? inputElement ?? triggerElement
2552
+ }), element]
2553
+ });
2554
+ });
2555
+ if (process.env.NODE_ENV !== "production") ComboboxPositioner.displayName = "ComboboxPositioner";
2556
+
2557
+ const stateAttributesMapping = {
2558
+ ...InternalBackdrop.popupStateMapping,
2559
+ ...useButton.transitionStatusMapping
2560
+ };
2561
+
2562
+ /**
2563
+ * A container for the list.
2564
+ * Renders a `<div>` element.
2565
+ */
2566
+ const ComboboxPopup = /*#__PURE__*/React__namespace.forwardRef(function ComboboxPopup(componentProps, forwardedRef) {
2567
+ const {
2568
+ render,
2569
+ className,
2570
+ initialFocus,
2571
+ finalFocus,
2572
+ ...elementProps
2573
+ } = componentProps;
2574
+ const store = useComboboxRootContext();
2575
+ const positioning = useComboboxPositionerContext();
2576
+ const floatingRootContext = useComboboxFloatingContext();
2577
+ const {
2578
+ filteredItems
2579
+ } = useComboboxDerivedItemsContext();
2580
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
2581
+ const open = InternalBackdrop.useStore(store, selectors.open);
2582
+ const openMethod = InternalBackdrop.useStore(store, selectors.openMethod);
2583
+ const transitionStatus = InternalBackdrop.useStore(store, selectors.transitionStatus);
2584
+ const inputInsidePopup = InternalBackdrop.useStore(store, selectors.inputInsidePopup);
2585
+ const inputElement = InternalBackdrop.useStore(store, selectors.inputElement);
2586
+ const modal = InternalBackdrop.useStore(store, selectors.modal);
2587
+ const empty = filteredItems.length === 0;
2588
+ useButton.useOpenChangeComplete({
2589
+ open,
2590
+ ref: store.state.popupRef,
2591
+ onComplete() {
2592
+ if (open) {
2593
+ store.state.onOpenChangeComplete(true);
2594
+ }
2595
+ }
2596
+ });
2597
+ const state = {
2598
+ open,
2599
+ side: positioning.side,
2600
+ align: positioning.align,
2601
+ anchorHidden: positioning.anchorHidden,
2602
+ transitionStatus,
2603
+ empty
2604
+ };
2605
+ const element = useRenderElement.useRenderElement('div', componentProps, {
2606
+ state,
2607
+ ref: [forwardedRef, store.state.popupRef],
2608
+ props: [{
2609
+ role: inputInsidePopup ? 'dialog' : 'presentation',
2610
+ tabIndex: -1,
2611
+ onFocus(event) {
2612
+ const target = useButton.getTarget(event.nativeEvent);
2613
+ if (openMethod !== 'touch' && (useButton.contains(store.state.listElement, target) || target === event.currentTarget)) {
2614
+ store.state.inputRef.current?.focus();
2615
+ }
2616
+ }
2617
+ }, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), elementProps],
2618
+ stateAttributesMapping
2619
+ });
2620
+
2621
+ // Default initial focus logic:
2622
+ // If opened by touch, focus the popup element to prevent the virtual keyboard from opening
2623
+ // (this is required for Android specifically as iOS handles this automatically).
2624
+ const computedDefaultInitialFocus = inputInsidePopup ? interactionType => interactionType === 'touch' ? store.state.popupRef.current : inputElement : false;
2625
+ const resolvedInitialFocus = initialFocus === undefined ? computedDefaultInitialFocus : initialFocus;
2626
+ let resolvedFinalFocus;
2627
+ if (finalFocus != null) {
2628
+ resolvedFinalFocus = finalFocus;
2629
+ } else {
2630
+ resolvedFinalFocus = inputInsidePopup ? undefined : false;
2631
+ }
2632
+ const focusManagerModal = !inputInsidePopup || modal;
2633
+ return /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.FloatingFocusManager, {
2634
+ context: floatingRootContext,
2635
+ disabled: !mounted,
2636
+ modal: focusManagerModal,
2637
+ openInteractionType: openMethod,
2638
+ initialFocus: resolvedInitialFocus,
2639
+ returnFocus: resolvedFinalFocus,
2640
+ getInsideElements: () => [store.state.startDismissRef.current, store.state.endDismissRef.current],
2641
+ children: /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
2642
+ children: [element, focusManagerModal && /*#__PURE__*/jsxRuntime.jsx(ComboboxInternalDismissButton, {
2643
+ ref: store.state.endDismissRef
2644
+ })]
2645
+ })
2646
+ });
2647
+ });
2648
+ if (process.env.NODE_ENV !== "production") ComboboxPopup.displayName = "ComboboxPopup";
2649
+
2650
+ /**
2651
+ * Displays an element positioned against the anchor.
2652
+ * Renders a `<div>` element.
2653
+ */
2654
+ const ComboboxArrow = /*#__PURE__*/React__namespace.forwardRef(function ComboboxArrow(componentProps, forwardedRef) {
2655
+ const {
2656
+ className,
2657
+ render,
2658
+ ...elementProps
2659
+ } = componentProps;
2660
+ const store = useComboboxRootContext();
2661
+ const {
2662
+ arrowRef,
2663
+ side,
2664
+ align,
2665
+ arrowUncentered,
2666
+ arrowStyles
2667
+ } = useComboboxPositionerContext();
2668
+ const open = InternalBackdrop.useStore(store, selectors.open);
2669
+ const state = {
2670
+ open,
2671
+ side,
2672
+ align,
2673
+ uncentered: arrowUncentered
2674
+ };
2675
+ return useRenderElement.useRenderElement('div', componentProps, {
2676
+ ref: [arrowRef, forwardedRef],
2677
+ stateAttributesMapping: InternalBackdrop.popupStateMapping,
2678
+ state,
2679
+ props: {
2680
+ style: arrowStyles,
2681
+ 'aria-hidden': true,
2682
+ ...elementProps
2683
+ }
2684
+ });
2685
+ });
2686
+ if (process.env.NODE_ENV !== "production") ComboboxArrow.displayName = "ComboboxArrow";
2687
+
2688
+ const ComboboxGroupContext = /*#__PURE__*/React__namespace.createContext(undefined);
2689
+ if (process.env.NODE_ENV !== "production") ComboboxGroupContext.displayName = "ComboboxGroupContext";
2690
+ function useComboboxGroupContext() {
2691
+ const context = React__namespace.useContext(ComboboxGroupContext);
2692
+ if (context === undefined) {
2693
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxGroupContext is missing. ComboboxGroup parts must be placed within <Combobox.Group>.' : useRenderElement.formatErrorMessage(18));
2694
+ }
2695
+ return context;
2696
+ }
2697
+
2698
+ const ComboboxGroup = /*#__PURE__*/React__namespace.forwardRef(function ComboboxGroup(componentProps, forwardedRef) {
2699
+ const {
2700
+ render,
2701
+ className,
2702
+ items,
2703
+ ...elementProps
2704
+ } = componentProps;
2705
+ const [labelId, setLabelId] = React__namespace.useState();
2706
+ const contextValue = React__namespace.useMemo(() => ({
2707
+ labelId,
2708
+ setLabelId,
2709
+ items
2710
+ }), [labelId, setLabelId, items]);
2711
+ const element = useRenderElement.useRenderElement('div', componentProps, {
2712
+ ref: forwardedRef,
2713
+ props: [{
2714
+ role: 'group',
2715
+ 'aria-labelledby': labelId
2716
+ }, elementProps]
2717
+ });
2718
+ const wrappedElement = /*#__PURE__*/jsxRuntime.jsx(ComboboxGroupContext.Provider, {
2719
+ value: contextValue,
2720
+ children: element
2721
+ });
2722
+ if (items) {
2723
+ return /*#__PURE__*/jsxRuntime.jsx(GroupCollectionProvider, {
2724
+ items: items,
2725
+ children: wrappedElement
2726
+ });
2727
+ }
2728
+ return wrappedElement;
2729
+ });
2730
+ if (process.env.NODE_ENV !== "production") ComboboxGroup.displayName = "ComboboxGroup";
2731
+
2732
+ /**
2733
+ * An accessible label that is automatically associated with its parent group.
2734
+ * Renders a `<div>` element.
2735
+ */
2736
+ const ComboboxGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function ComboboxGroupLabel(componentProps, forwardedRef) {
2737
+ const {
2738
+ render,
2739
+ className,
2740
+ id: idProp,
2741
+ ...elementProps
2742
+ } = componentProps;
2743
+ const {
2744
+ setLabelId
2745
+ } = useComboboxGroupContext();
2746
+ const id = useButton.useBaseUiId(idProp);
2747
+ useButton.useIsoLayoutEffect(() => {
2748
+ setLabelId(id);
2749
+ return () => {
2750
+ setLabelId(undefined);
2751
+ };
2752
+ }, [id, setLabelId]);
2753
+ const element = useRenderElement.useRenderElement('div', componentProps, {
2754
+ ref: forwardedRef,
2755
+ props: [{
2756
+ id
2757
+ }, elementProps]
2758
+ });
2759
+ return element;
2760
+ });
2761
+ if (process.env.NODE_ENV !== "production") ComboboxGroupLabel.displayName = "ComboboxGroupLabel";
2762
+
2763
+ const ComboboxItemContext = /*#__PURE__*/React__namespace.createContext(undefined);
2764
+ if (process.env.NODE_ENV !== "production") ComboboxItemContext.displayName = "ComboboxItemContext";
2765
+ function useComboboxItemContext() {
2766
+ const context = React__namespace.useContext(ComboboxItemContext);
2767
+ if (!context) {
2768
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxItemContext is missing. ComboboxItem parts must be placed within <Combobox.Item>.' : useRenderElement.formatErrorMessage(19));
2769
+ }
2770
+ return context;
2771
+ }
2772
+
2773
+ const ComboboxRowContext = /*#__PURE__*/React__namespace.createContext(false);
2774
+ if (process.env.NODE_ENV !== "production") ComboboxRowContext.displayName = "ComboboxRowContext";
2775
+ function useComboboxRowContext() {
2776
+ return React__namespace.useContext(ComboboxRowContext);
2777
+ }
2778
+
2779
+ const ComboboxItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef(function ComboboxItem(componentProps, forwardedRef) {
2780
+ const {
2781
+ render,
2782
+ className,
2783
+ value: itemValue = null,
2784
+ index: indexProp,
2785
+ disabled = false,
2786
+ nativeButton = false,
2787
+ ...elementProps
2788
+ } = componentProps;
2789
+ const didPointerDownRef = React__namespace.useRef(false);
2790
+ const textRef = React__namespace.useRef(null);
2791
+ const listItem = useCompositeListItem.useCompositeListItem({
2792
+ index: indexProp,
2793
+ textRef,
2794
+ indexGuessBehavior: useCompositeListItem.IndexGuessBehavior.GuessFromOrder
2795
+ });
2796
+ const store = useComboboxRootContext();
2797
+ const isRow = useComboboxRowContext();
2798
+ const {
2799
+ flatFilteredItems,
2800
+ hasItems
2801
+ } = useComboboxDerivedItemsContext();
2802
+ const open = InternalBackdrop.useStore(store, selectors.open);
2803
+ const selectionMode = InternalBackdrop.useStore(store, selectors.selectionMode);
2804
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
2805
+ const virtualized = InternalBackdrop.useStore(store, selectors.virtualized);
2806
+ const isItemEqualToValue = InternalBackdrop.useStore(store, selectors.isItemEqualToValue);
2807
+ const selectable = selectionMode !== 'none';
2808
+ const index = indexProp ?? (virtualized ? findItemIndex(flatFilteredItems, itemValue, isItemEqualToValue) : listItem.index);
2809
+ const hasRegistered = listItem.index !== -1;
2810
+ const rootId = InternalBackdrop.useStore(store, selectors.id);
2811
+ const highlighted = InternalBackdrop.useStore(store, selectors.isActive, index);
2812
+ const matchesSelectedValue = InternalBackdrop.useStore(store, selectors.isSelected, itemValue);
2813
+ const getItemProps = InternalBackdrop.useStore(store, selectors.getItemProps);
2814
+ const itemRef = React__namespace.useRef(null);
2815
+ const id = rootId != null && hasRegistered ? `${rootId}-${index}` : undefined;
2816
+ const selected = matchesSelectedValue && selectable;
2817
+ useButton.useIsoLayoutEffect(() => {
2818
+ const shouldRun = hasRegistered && (virtualized || indexProp != null);
2819
+ if (!shouldRun) {
2820
+ return undefined;
2821
+ }
2822
+ const list = store.state.listRef.current;
2823
+ list[index] = itemRef.current;
2824
+ return () => {
2825
+ delete list[index];
2826
+ };
2827
+ }, [hasRegistered, virtualized, index, indexProp, store]);
2828
+ useButton.useIsoLayoutEffect(() => {
2829
+ if (!hasRegistered || hasItems) {
2830
+ return undefined;
2831
+ }
2832
+ const visibleMap = store.state.valuesRef.current;
2833
+ visibleMap[index] = itemValue;
2834
+
2835
+ // Stable registry that doesn't depend on filtering. Assume that no
2836
+ // filtering had occurred at this point; otherwise, an `items` prop is
2837
+ // required.
2838
+ if (selectionMode !== 'none') {
2839
+ store.state.allValuesRef.current.push(itemValue);
2840
+ }
2841
+ return () => {
2842
+ delete visibleMap[index];
2843
+ };
2844
+ }, [hasRegistered, hasItems, index, itemValue, store, selectionMode]);
2845
+ useButton.useIsoLayoutEffect(() => {
2846
+ if (!open) {
2847
+ didPointerDownRef.current = false;
2848
+ return;
2849
+ }
2850
+ if (!hasRegistered || hasItems) {
2851
+ return;
2852
+ }
2853
+ const selectedValue = store.state.selectedValue;
2854
+ const lastSelectedValue = Array.isArray(selectedValue) ? selectedValue[selectedValue.length - 1] : selectedValue;
2855
+ if (compareItemEquality(itemValue, lastSelectedValue, isItemEqualToValue)) {
2856
+ store.set('selectedIndex', index);
2857
+ }
2858
+ }, [hasRegistered, hasItems, open, store, index, itemValue, isItemEqualToValue]);
2859
+ const state = {
2860
+ disabled,
2861
+ selected,
2862
+ highlighted
2863
+ };
2864
+ const rootProps = getItemProps({
2865
+ active: highlighted,
2866
+ selected
2867
+ });
2868
+ rootProps.id = undefined;
2869
+ rootProps.onFocus = undefined;
2870
+ const {
2871
+ getButtonProps,
2872
+ buttonRef
2873
+ } = useButton.useButton({
2874
+ disabled,
2875
+ focusableWhenDisabled: true,
2876
+ native: nativeButton,
2877
+ composite: true
2878
+ });
2879
+ function commitSelection(nativeEvent) {
2880
+ function selectItem() {
2881
+ store.state.handleSelection(nativeEvent, itemValue);
2882
+ }
2883
+ if (store.state.submitOnItemClick) {
2884
+ ReactDOM__namespace.flushSync(selectItem);
2885
+ store.state.requestSubmit();
2886
+ } else {
2887
+ selectItem();
2888
+ }
2889
+ }
2890
+ const defaultProps = {
2891
+ id,
2892
+ role: isRow ? 'gridcell' : 'option',
2893
+ 'aria-selected': selectable ? selected : undefined,
2894
+ // Focusable items steal focus from the input upon mouseup.
2895
+ // Warn if the user renders a natively focusable element like `<button>`,
2896
+ // as it should be a `<div>` instead.
2897
+ tabIndex: undefined,
2898
+ onPointerDownCapture(event) {
2899
+ didPointerDownRef.current = true;
2900
+ event.preventDefault();
2901
+ },
2902
+ onClick(event) {
2903
+ if (disabled || readOnly) {
2904
+ return;
2905
+ }
2906
+ commitSelection(event.nativeEvent);
2907
+ },
2908
+ onMouseUp(event) {
2909
+ const pointerStartedOnItem = didPointerDownRef.current;
2910
+ didPointerDownRef.current = false;
2911
+ if (disabled || readOnly || event.button !== 0 || pointerStartedOnItem || !highlighted) {
2912
+ return;
2913
+ }
2914
+ commitSelection(event.nativeEvent);
2915
+ }
2916
+ };
2917
+ const element = useRenderElement.useRenderElement('div', componentProps, {
2918
+ ref: [buttonRef, forwardedRef, listItem.ref, itemRef],
2919
+ state,
2920
+ props: [rootProps, defaultProps, elementProps, getButtonProps]
2921
+ });
2922
+ const contextValue = React__namespace.useMemo(() => ({
2923
+ selected,
2924
+ textRef
2925
+ }), [selected, textRef]);
2926
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxItemContext.Provider, {
2927
+ value: contextValue,
2928
+ children: element
2929
+ });
2930
+ }));
2931
+ if (process.env.NODE_ENV !== "production") ComboboxItem.displayName = "ComboboxItem";
2932
+
2933
+ const ComboboxRow = /*#__PURE__*/React__namespace.forwardRef(function ComboboxRow(componentProps, forwardedRef) {
2934
+ const {
2935
+ render,
2936
+ className,
2937
+ ...elementProps
2938
+ } = componentProps;
2939
+ const element = useRenderElement.useRenderElement('div', componentProps, {
2940
+ ref: forwardedRef,
2941
+ props: [{
2942
+ role: 'row'
2943
+ }, elementProps]
2944
+ });
2945
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxRowContext.Provider, {
2946
+ value: true,
2947
+ children: element
2948
+ });
2949
+ });
2950
+ if (process.env.NODE_ENV !== "production") ComboboxRow.displayName = "ComboboxRow";
2951
+
2952
+ /**
2953
+ * Renders its children only when the list is empty.
2954
+ * Requires the `items` prop on the root component.
2955
+ * Announces changes politely to screen readers.
2956
+ * Renders a `<div>` element.
2957
+ */
2958
+ const ComboboxEmpty = /*#__PURE__*/React__namespace.forwardRef(function ComboboxEmpty(componentProps, forwardedRef) {
2959
+ const {
2960
+ render,
2961
+ className,
2962
+ children: childrenProp,
2963
+ ...elementProps
2964
+ } = componentProps;
2965
+ const {
2966
+ filteredItems
2967
+ } = useComboboxDerivedItemsContext();
2968
+ const store = useComboboxRootContext();
2969
+ const children = filteredItems.length === 0 ? childrenProp : null;
2970
+ return useRenderElement.useRenderElement('div', componentProps, {
2971
+ ref: [forwardedRef, store.state.emptyRef],
2972
+ props: [{
2973
+ children,
2974
+ role: 'status',
2975
+ 'aria-live': 'polite',
2976
+ 'aria-atomic': true
2977
+ }, elementProps]
2978
+ });
2979
+ });
2980
+ if (process.env.NODE_ENV !== "production") ComboboxEmpty.displayName = "ComboboxEmpty";
2981
+
2982
+ /**
2983
+ * Returns the internally filtered items.
2984
+ */
2985
+ function useFilteredItems() {
2986
+ const items = useComboboxDerivedItemsContext();
2987
+ return items.filteredItems;
2988
+ }
2989
+
2990
+ function ComboboxRoot(props) {
2991
+ const {
2992
+ multiple = false,
2993
+ defaultValue,
2994
+ value,
2995
+ onValueChange,
2996
+ autoComplete,
2997
+ ...other
2998
+ } = props;
2999
+ return /*#__PURE__*/jsxRuntime.jsx(AriaCombobox, {
3000
+ ...other,
3001
+ selectionMode: multiple ? 'multiple' : 'single',
3002
+ selectedValue: value,
3003
+ defaultSelectedValue: defaultValue,
3004
+ onSelectedValueChange: onValueChange,
3005
+ formAutoComplete: autoComplete
3006
+ });
3007
+ }
3008
+
3009
+ /**
3010
+ * An accessible label that is automatically associated with the combobox trigger.
3011
+ * Renders a `<div>` element.
3012
+ *
3013
+ * Documentation: [Base UI Combobox](https://base-ui.com/react/components/combobox)
3014
+ */
3015
+ const ComboboxLabel = /*#__PURE__*/React__namespace.forwardRef(function ComboboxLabel(componentProps, forwardedRef) {
3016
+ const {
3017
+ render,
3018
+ className,
3019
+ ...elementProps
3020
+ } = componentProps;
3021
+ // Keep label id derived from the root and ignore runtime `id` overrides from untyped consumers.
3022
+ const elementPropsWithoutId = elementProps;
3023
+ delete elementPropsWithoutId.id;
3024
+ const fieldRootContext = useLabel.useFieldRootContext();
3025
+ const store = useComboboxRootContext();
3026
+ const inputInsidePopup = InternalBackdrop.useStore(store, selectors.inputInsidePopup);
3027
+ const triggerElement = InternalBackdrop.useStore(store, selectors.triggerElement);
3028
+ const inputElement = InternalBackdrop.useStore(store, selectors.inputElement);
3029
+ const rootId = InternalBackdrop.useStore(store, selectors.id);
3030
+ const defaultLabelId = getDefaultLabelId(rootId);
3031
+ const localControlId = triggerElement?.id ?? (inputInsidePopup ? rootId : undefined);
3032
+ if (process.env.NODE_ENV !== 'production') {
3033
+ // eslint-disable-next-line react-hooks/rules-of-hooks
3034
+ React__namespace.useEffect(() => {
3035
+ if (!inputElement || inputInsidePopup) {
3036
+ return;
3037
+ }
3038
+ const ownerStackMessage = useButton.SafeReact.captureOwnerStack?.() || '';
3039
+ const message = '<Combobox.Label> labels <Combobox.Trigger> only. ' + 'When <Combobox.Input> is the form control, use a native <label> or <Field.Label> instead.';
3040
+ useButton.error(`${message}${ownerStackMessage}`);
3041
+ }, [inputElement, inputInsidePopup]);
3042
+ }
3043
+ const labelProps = useLabel.useLabel({
3044
+ id: defaultLabelId,
3045
+ fallbackControlId: localControlId,
3046
+ setLabelId(nextLabelId) {
3047
+ store.set('labelId', nextLabelId);
3048
+ }
3049
+ });
3050
+ return useRenderElement.useRenderElement('div', componentProps, {
3051
+ ref: forwardedRef,
3052
+ state: fieldRootContext.state,
3053
+ props: [labelProps, elementProps],
3054
+ stateAttributesMapping: useLabel.fieldValidityMapping
3055
+ });
3056
+ });
3057
+ if (process.env.NODE_ENV !== "production") ComboboxLabel.displayName = "ComboboxLabel";
3058
+
3059
+ function ComboboxValue(props) {
3060
+ const {
3061
+ children: childrenProp,
3062
+ placeholder
3063
+ } = props;
3064
+ const store = useComboboxRootContext();
3065
+ const itemToStringLabel = InternalBackdrop.useStore(store, selectors.itemToStringLabel);
3066
+ const selectedValue = InternalBackdrop.useStore(store, selectors.selectedValue);
3067
+ const items = InternalBackdrop.useStore(store, selectors.items);
3068
+ const multiple = InternalBackdrop.useStore(store, selectors.selectionMode) === 'multiple';
3069
+ const hasSelectedValue = InternalBackdrop.useStore(store, selectors.hasSelectedValue);
3070
+ const shouldCheckNullItemLabel = !hasSelectedValue && placeholder != null && childrenProp == null;
3071
+ const hasNullLabel = InternalBackdrop.useStore(store, selectors.hasNullItemLabel, shouldCheckNullItemLabel);
3072
+ let children = null;
3073
+ if (typeof childrenProp === 'function') {
3074
+ children = childrenProp(selectedValue);
3075
+ } else if (childrenProp != null) {
3076
+ children = childrenProp;
3077
+ } else if (!hasSelectedValue && placeholder != null && !hasNullLabel) {
3078
+ children = placeholder;
3079
+ } else if (multiple && Array.isArray(selectedValue)) {
3080
+ children = resolveMultipleLabels(selectedValue, items, itemToStringLabel);
3081
+ } else {
3082
+ children = resolveSelectedLabel(selectedValue, items, itemToStringLabel);
3083
+ }
3084
+ return /*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
3085
+ children: children
3086
+ });
3087
+ }
3088
+
3089
+ const ComboboxItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function ComboboxItemIndicator(componentProps, forwardedRef) {
3090
+ const keepMounted = componentProps.keepMounted ?? false;
3091
+ const {
3092
+ selected
3093
+ } = useComboboxItemContext();
3094
+ const shouldRender = keepMounted || selected;
3095
+ if (!shouldRender) {
3096
+ return null;
3097
+ }
3098
+
3099
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
3100
+ return /*#__PURE__*/jsxRuntime.jsx(Inner, {
3101
+ ...componentProps,
3102
+ ref: forwardedRef
3103
+ });
3104
+ });
3105
+
3106
+ /** The core implementation of the indicator is split here to avoid paying the hooks
3107
+ * costs unless the element needs to be mounted. */
3108
+ if (process.env.NODE_ENV !== "production") ComboboxItemIndicator.displayName = "ComboboxItemIndicator";
3109
+ const Inner = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef((componentProps, forwardedRef) => {
3110
+ const {
3111
+ render,
3112
+ className,
3113
+ keepMounted,
3114
+ ...elementProps
3115
+ } = componentProps;
3116
+ const {
3117
+ selected
3118
+ } = useComboboxItemContext();
3119
+ const indicatorRef = React__namespace.useRef(null);
3120
+ const {
3121
+ transitionStatus,
3122
+ setMounted
3123
+ } = useButton.useTransitionStatus(selected);
3124
+ const state = {
3125
+ selected,
3126
+ transitionStatus
3127
+ };
3128
+ const element = useRenderElement.useRenderElement('span', componentProps, {
3129
+ ref: [forwardedRef, indicatorRef],
3130
+ state,
3131
+ props: [{
3132
+ 'aria-hidden': true,
3133
+ children: '✔️'
3134
+ }, elementProps],
3135
+ stateAttributesMapping: useButton.transitionStatusMapping
3136
+ });
3137
+ useButton.useOpenChangeComplete({
3138
+ open: selected,
3139
+ ref: indicatorRef,
3140
+ onComplete() {
3141
+ if (!selected) {
3142
+ setMounted(false);
3143
+ }
3144
+ }
3145
+ });
3146
+ return element;
3147
+ }));
3148
+ if (process.env.NODE_ENV !== "production") Inner.displayName = "Inner";
3149
+
3150
+ const ComboboxChips = /*#__PURE__*/React__namespace.forwardRef(function ComboboxChips(componentProps, forwardedRef) {
3151
+ const {
3152
+ render,
3153
+ className,
3154
+ ...elementProps
3155
+ } = componentProps;
3156
+ const store = useComboboxRootContext();
3157
+ const open = InternalBackdrop.useStore(store, selectors.open);
3158
+ const hasSelectionChips = InternalBackdrop.useStore(store, selectors.hasSelectionChips);
3159
+ const [highlightedChipIndex, setHighlightedChipIndex] = React__namespace.useState(undefined);
3160
+ if (open && highlightedChipIndex !== undefined) {
3161
+ setHighlightedChipIndex(undefined);
3162
+ }
3163
+ const chipsRef = React__namespace.useRef([]);
3164
+ const element = useRenderElement.useRenderElement('div', componentProps, {
3165
+ ref: [forwardedRef, store.state.chipsContainerRef],
3166
+ // NVDA enters browse mode instead of staying in focus mode when navigating with
3167
+ // arrow keys inside a container unless it has a toolbar role.
3168
+ props: [hasSelectionChips ? {
3169
+ role: 'toolbar'
3170
+ } : useRenderElement.EMPTY_OBJECT, elementProps]
3171
+ });
3172
+ const contextValue = React__namespace.useMemo(() => ({
3173
+ highlightedChipIndex,
3174
+ setHighlightedChipIndex,
3175
+ chipsRef
3176
+ }), [highlightedChipIndex, setHighlightedChipIndex, chipsRef]);
3177
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxChipsContext.Provider, {
3178
+ value: contextValue,
3179
+ children: /*#__PURE__*/jsxRuntime.jsx(useCompositeListItem.CompositeList, {
3180
+ elementsRef: chipsRef,
3181
+ children: element
3182
+ })
3183
+ });
3184
+ });
3185
+ if (process.env.NODE_ENV !== "production") ComboboxChips.displayName = "ComboboxChips";
3186
+
3187
+ const ComboboxChipContext = /*#__PURE__*/React__namespace.createContext(undefined);
3188
+ if (process.env.NODE_ENV !== "production") ComboboxChipContext.displayName = "ComboboxChipContext";
3189
+ function useComboboxChipContext() {
3190
+ const context = React__namespace.useContext(ComboboxChipContext);
3191
+ if (!context) {
3192
+ throw new Error(process.env.NODE_ENV !== "production" ? 'useComboboxChipContext must be used within a ComboboxChip' : useRenderElement.formatErrorMessage(17));
3193
+ }
3194
+ return context;
3195
+ }
3196
+
3197
+ const ComboboxChip = /*#__PURE__*/React__namespace.forwardRef(function ComboboxChip(componentProps, forwardedRef) {
3198
+ const {
3199
+ render,
3200
+ className,
3201
+ ...elementProps
3202
+ } = componentProps;
3203
+ const store = useComboboxRootContext();
3204
+ const {
3205
+ setHighlightedChipIndex,
3206
+ chipsRef
3207
+ } = useComboboxChipsContext();
3208
+ const disabled = InternalBackdrop.useStore(store, selectors.disabled);
3209
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
3210
+ const selectedValue = InternalBackdrop.useStore(store, selectors.selectedValue);
3211
+ const {
3212
+ ref,
3213
+ index
3214
+ } = useCompositeListItem.useCompositeListItem();
3215
+ function handleKeyDown(event) {
3216
+ let nextIndex = index;
3217
+ if (event.key === 'ArrowLeft') {
3218
+ event.preventDefault();
3219
+ if (index > 0) {
3220
+ nextIndex = index - 1;
3221
+ } else {
3222
+ nextIndex = undefined;
3223
+ }
3224
+ } else if (event.key === 'ArrowRight') {
3225
+ event.preventDefault();
3226
+ if (index < selectedValue.length - 1) {
3227
+ nextIndex = index + 1;
3228
+ } else {
3229
+ nextIndex = undefined;
3230
+ }
3231
+ } else if (event.key === 'Backspace' || event.key === 'Delete') {
3232
+ const computedNextIndex = index >= selectedValue.length - 1 ? selectedValue.length - 2 : index;
3233
+ nextIndex = computedNextIndex >= 0 ? computedNextIndex : undefined;
3234
+ useButton.stopEvent(event);
3235
+ store.state.setIndices({
3236
+ activeIndex: null,
3237
+ selectedIndex: null,
3238
+ type: 'keyboard'
3239
+ });
3240
+ store.state.setSelectedValue(selectedValue.filter((_, i) => i !== index), useButton.createChangeEventDetails(useButton.none, event.nativeEvent));
3241
+ } else if (event.key === 'Enter' || event.key === ' ') {
3242
+ useButton.stopEvent(event);
3243
+ nextIndex = undefined;
3244
+ } else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
3245
+ useButton.stopEvent(event);
3246
+ store.state.setOpen(true, useButton.createChangeEventDetails(useButton.listNavigation, event.nativeEvent));
3247
+ nextIndex = undefined;
3248
+ } else if (
3249
+ // Check for printable characters (letters, numbers, symbols)
3250
+ event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
3251
+ nextIndex = undefined;
3252
+ }
3253
+ return nextIndex;
3254
+ }
3255
+ const state = {
3256
+ disabled
3257
+ };
3258
+ const element = useRenderElement.useRenderElement('div', componentProps, {
3259
+ ref: [forwardedRef, ref],
3260
+ state,
3261
+ props: [{
3262
+ tabIndex: -1,
3263
+ 'aria-disabled': disabled || undefined,
3264
+ 'aria-readonly': readOnly || undefined,
3265
+ onKeyDown(event) {
3266
+ if (disabled || readOnly) {
3267
+ return;
3268
+ }
3269
+ const nextIndex = handleKeyDown(event);
3270
+ ReactDOM__namespace.flushSync(() => {
3271
+ setHighlightedChipIndex(nextIndex);
3272
+ });
3273
+ if (nextIndex === undefined) {
3274
+ store.state.inputRef.current?.focus();
3275
+ } else {
3276
+ chipsRef.current[nextIndex]?.focus();
3277
+ }
3278
+ },
3279
+ onMouseDown(event) {
3280
+ if (readOnly) {
3281
+ return;
3282
+ }
3283
+ event.preventDefault();
3284
+ if (disabled) {
3285
+ return;
3286
+ }
3287
+ store.state.inputRef.current?.focus();
3288
+ }
3289
+ }, elementProps]
3290
+ });
3291
+ const contextValue = React__namespace.useMemo(() => ({
3292
+ index
3293
+ }), [index]);
3294
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxChipContext.Provider, {
3295
+ value: contextValue,
3296
+ children: element
3297
+ });
3298
+ });
3299
+ if (process.env.NODE_ENV !== "production") ComboboxChip.displayName = "ComboboxChip";
3300
+
3301
+ /**
3302
+ * A button to remove a chip.
3303
+ * Renders a `<button>` element.
3304
+ */
3305
+ const ComboboxChipRemove = /*#__PURE__*/React__namespace.forwardRef(function ComboboxChipRemove(componentProps, forwardedRef) {
3306
+ const {
3307
+ render,
3308
+ className,
3309
+ disabled: disabledProp = false,
3310
+ nativeButton = true,
3311
+ ...elementProps
3312
+ } = componentProps;
3313
+ const store = useComboboxRootContext();
3314
+ const {
3315
+ index
3316
+ } = useComboboxChipContext();
3317
+ const comboboxDisabled = InternalBackdrop.useStore(store, selectors.disabled);
3318
+ const readOnly = InternalBackdrop.useStore(store, selectors.readOnly);
3319
+ const selectedValue = InternalBackdrop.useStore(store, selectors.selectedValue);
3320
+ const isItemEqualToValue = InternalBackdrop.useStore(store, selectors.isItemEqualToValue);
3321
+ const disabled = comboboxDisabled || disabledProp;
3322
+ const {
3323
+ buttonRef,
3324
+ getButtonProps
3325
+ } = useButton.useButton({
3326
+ native: nativeButton,
3327
+ disabled: disabled || readOnly,
3328
+ focusableWhenDisabled: true
3329
+ });
3330
+ const state = {
3331
+ disabled
3332
+ };
3333
+ function clearActiveIndexForRemovedItem(removedItem) {
3334
+ const activeIndex = store.state.activeIndex;
3335
+ if (activeIndex == null) {
3336
+ return;
3337
+ }
3338
+
3339
+ // Try current visible list first; if not found, it's filtered out.
3340
+ // No need to clear highlight in that case since it can't equal activeIndex.
3341
+ const removedIndex = findItemIndex(store.state.valuesRef.current, removedItem, isItemEqualToValue);
3342
+ if (removedIndex !== -1 && activeIndex === removedIndex) {
3343
+ store.state.setIndices({
3344
+ activeIndex: null,
3345
+ type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
3346
+ });
3347
+ }
3348
+ }
3349
+ function removeChip(event) {
3350
+ const eventDetails = useButton.createChangeEventDetails(useButton.chipRemovePress, event.nativeEvent);
3351
+ const removedItem = selectedValue[index];
3352
+ clearActiveIndexForRemovedItem(removedItem);
3353
+ store.state.setSelectedValue(selectedValue.filter((_, i) => i !== index), eventDetails);
3354
+ store.state.inputRef.current?.focus();
3355
+ return eventDetails;
3356
+ }
3357
+ const element = useRenderElement.useRenderElement('button', componentProps, {
3358
+ ref: [forwardedRef, buttonRef],
3359
+ state,
3360
+ props: [{
3361
+ tabIndex: -1,
3362
+ onClick(event) {
3363
+ if (disabled || readOnly) {
3364
+ return;
3365
+ }
3366
+ const eventDetails = removeChip(event);
3367
+ if (!eventDetails.isPropagationAllowed) {
3368
+ event.stopPropagation();
3369
+ }
3370
+ },
3371
+ onKeyDown(event) {
3372
+ if (disabled || readOnly) {
3373
+ return;
3374
+ }
3375
+ if (event.key === 'Enter' || event.key === ' ') {
3376
+ const eventDetails = removeChip(event);
3377
+ if (!eventDetails.isPropagationAllowed) {
3378
+ useButton.stopEvent(event);
3379
+ }
3380
+ }
3381
+ }
3382
+ }, elementProps, getButtonProps]
3383
+ });
3384
+ return element;
3385
+ });
3386
+ if (process.env.NODE_ENV !== "production") ComboboxChipRemove.displayName = "ComboboxChipRemove";
3387
+
3388
+ exports.AriaCombobox = AriaCombobox;
3389
+ exports.ComboboxArrow = ComboboxArrow;
3390
+ exports.ComboboxBackdrop = ComboboxBackdrop;
3391
+ exports.ComboboxChip = ComboboxChip;
3392
+ exports.ComboboxChipRemove = ComboboxChipRemove;
3393
+ exports.ComboboxChips = ComboboxChips;
3394
+ exports.ComboboxClear = ComboboxClear;
3395
+ exports.ComboboxCollection = ComboboxCollection;
3396
+ exports.ComboboxEmpty = ComboboxEmpty;
3397
+ exports.ComboboxGroup = ComboboxGroup;
3398
+ exports.ComboboxGroupLabel = ComboboxGroupLabel;
3399
+ exports.ComboboxIcon = ComboboxIcon;
3400
+ exports.ComboboxInput = ComboboxInput;
3401
+ exports.ComboboxInputGroup = ComboboxInputGroup;
3402
+ exports.ComboboxItem = ComboboxItem;
3403
+ exports.ComboboxItemIndicator = ComboboxItemIndicator;
3404
+ exports.ComboboxLabel = ComboboxLabel;
3405
+ exports.ComboboxList = ComboboxList;
3406
+ exports.ComboboxPopup = ComboboxPopup;
3407
+ exports.ComboboxPortal = ComboboxPortal;
3408
+ exports.ComboboxPositioner = ComboboxPositioner;
3409
+ exports.ComboboxRoot = ComboboxRoot;
3410
+ exports.ComboboxRow = ComboboxRow;
3411
+ exports.ComboboxStatus = ComboboxStatus;
3412
+ exports.ComboboxTrigger = ComboboxTrigger;
3413
+ exports.ComboboxValue = ComboboxValue;
3414
+ exports.stringifyAsLabel = stringifyAsLabel;
3415
+ exports.useComboboxFilter = useComboboxFilter;
3416
+ exports.useComboboxInputValueContext = useComboboxInputValueContext;
3417
+ exports.useCoreFilter = useCoreFilter;
3418
+ exports.useFilteredItems = useFilteredItems;