@jobber/components 8.20.2 → 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 (55) hide show
  1. package/dist/BottomSheet-cjs.js +9 -58
  2. package/dist/BottomSheet-es.js +2 -47
  3. package/dist/Card/index.cjs +4 -0
  4. package/dist/Card/index.mjs +4 -0
  5. package/dist/ComboboxChipRemove-cjs.js +3418 -0
  6. package/dist/ComboboxChipRemove-es.js +3366 -0
  7. package/dist/ComboboxPrimitive-cjs.js +170 -0
  8. package/dist/ComboboxPrimitive-es.js +168 -0
  9. package/dist/DataDump/index.cjs +4 -0
  10. package/dist/DataDump/index.mjs +4 -0
  11. package/dist/InputNumberExperimental-cjs.js +45 -44
  12. package/dist/InputNumberExperimental-es.js +3 -2
  13. package/dist/InternalBackdrop-cjs.js +4278 -0
  14. package/dist/InternalBackdrop-es.js +4204 -0
  15. package/dist/Menu/index.cjs +4 -0
  16. package/dist/Menu/index.mjs +4 -0
  17. package/dist/Menu-cjs.js +9 -8
  18. package/dist/Menu-es.js +2 -1
  19. package/dist/MenuSubmenuTrigger-cjs.js +208 -1969
  20. package/dist/MenuSubmenuTrigger-es.js +7 -1759
  21. package/dist/NumberFieldInput-cjs.js +56 -439
  22. package/dist/NumberFieldInput-es.js +6 -376
  23. package/dist/Page/index.cjs +4 -0
  24. package/dist/Page/index.mjs +4 -0
  25. package/dist/ScrollAreaViewport-cjs.js +108 -4356
  26. package/dist/ScrollAreaViewport-es.js +6 -4201
  27. package/dist/buttonRenderAdapter-cjs.js +56 -0
  28. package/dist/buttonRenderAdapter-es.js +51 -0
  29. package/dist/clamp-cjs.js +0 -1194
  30. package/dist/clamp-es.js +1 -1091
  31. package/dist/docs/empty-states/empty-states.md +29 -0
  32. package/dist/index.cjs +4 -0
  33. package/dist/index.mjs +4 -0
  34. package/dist/primitives/BottomSheet/index.cjs +5 -2
  35. package/dist/primitives/BottomSheet/index.mjs +5 -2
  36. package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.d.ts +32 -0
  37. package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.types.d.ts +30 -0
  38. package/dist/primitives/ComboboxPrimitive/index.cjs +28 -0
  39. package/dist/primitives/ComboboxPrimitive/index.d.ts +2 -0
  40. package/dist/primitives/ComboboxPrimitive/index.mjs +22 -0
  41. package/dist/primitives/InputNumberExperimental/index.cjs +4 -2
  42. package/dist/primitives/InputNumberExperimental/index.mjs +4 -2
  43. package/dist/primitives/index.cjs +11 -2
  44. package/dist/primitives/index.d.ts +1 -0
  45. package/dist/primitives/index.mjs +10 -2
  46. package/dist/styles.css +767 -0
  47. package/dist/unstyledPrimitives/index.cjs +237 -3588
  48. package/dist/unstyledPrimitives/index.mjs +96 -3447
  49. package/dist/useButton-cjs.js +1196 -0
  50. package/dist/useButton-es.js +1091 -0
  51. package/dist/useCompositeListItem-cjs.js +1792 -0
  52. package/dist/useCompositeListItem-es.js +1762 -0
  53. package/dist/useLabel-cjs.js +411 -0
  54. package/dist/useLabel-es.js +378 -0
  55. package/package.json +2 -2
@@ -1,16 +1,21 @@
1
1
  'use strict';
2
2
 
3
3
  var React = require('react');
4
- var clamp = require('../clamp-cjs.js');
5
- var ScrollAreaViewport = require('../ScrollAreaViewport-cjs.js');
6
- var useRenderElement = require('../useRenderElement-cjs.js');
4
+ var useButton = require('../useButton-cjs.js');
5
+ var ComboboxChipRemove = require('../ComboboxChipRemove-cjs.js');
7
6
  var jsxRuntime = require('react/jsx-runtime');
8
- var NumberFieldInput = require('../NumberFieldInput-cjs.js');
9
- var MenuSubmenuTrigger = require('../MenuSubmenuTrigger-cjs.js');
10
- var ReactDOM = require('react-dom');
11
7
  var Separator = require('../Separator-cjs.js');
8
+ var ScrollAreaViewport = require('../ScrollAreaViewport-cjs.js');
9
+ var MenuSubmenuTrigger = require('../MenuSubmenuTrigger-cjs.js');
10
+ var useRenderElement = require('../useRenderElement-cjs.js');
12
11
  var floatingUi_utils_dom = require('../floating-ui.utils.dom-cjs.js');
12
+ var clamp = require('../clamp-cjs.js');
13
+ var InternalBackdrop = require('../InternalBackdrop-cjs.js');
14
+ var ReactDOM = require('react-dom');
15
+ var useCompositeListItem = require('../useCompositeListItem-cjs.js');
13
16
  var index_esm = require('../index.esm-cjs.js');
17
+ var NumberFieldInput = require('../NumberFieldInput-cjs.js');
18
+ require('../useLabel-cjs.js');
14
19
  require('../floating-ui.react-dom-cjs.js');
15
20
 
16
21
  function _interopNamespaceDefault(e) {
@@ -33,3098 +38,140 @@ function _interopNamespaceDefault(e) {
33
38
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
34
39
  var ReactDOM__namespace = /*#__PURE__*/_interopNamespaceDefault(ReactDOM);
35
40
 
36
- const ComboboxRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
37
- if (process.env.NODE_ENV !== "production") ComboboxRootContext.displayName = "ComboboxRootContext";
38
- const ComboboxFloatingContext = /*#__PURE__*/React__namespace.createContext(undefined);
39
- if (process.env.NODE_ENV !== "production") ComboboxFloatingContext.displayName = "ComboboxFloatingContext";
40
- const ComboboxDerivedItemsContext = /*#__PURE__*/React__namespace.createContext(undefined);
41
- // `inputValue` can't be placed in the store.
42
- // https://github.com/mui/base-ui/issues/2703
43
- if (process.env.NODE_ENV !== "production") ComboboxDerivedItemsContext.displayName = "ComboboxDerivedItemsContext";
44
- const ComboboxInputValueContext = /*#__PURE__*/React__namespace.createContext('');
45
- if (process.env.NODE_ENV !== "production") ComboboxInputValueContext.displayName = "ComboboxInputValueContext";
46
- function useComboboxRootContext() {
47
- const context = React__namespace.useContext(ComboboxRootContext);
48
- if (!context) {
49
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxRootContext is missing. Combobox parts must be placed within <Combobox.Root>.' : useRenderElement.formatErrorMessage(22));
50
- }
51
- return context;
52
- }
53
- function useComboboxFloatingContext() {
54
- const context = React__namespace.useContext(ComboboxFloatingContext);
55
- if (!context) {
56
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxFloatingContext is missing. Combobox parts must be placed within <Combobox.Root>.' : useRenderElement.formatErrorMessage(23));
57
- }
58
- return context;
59
- }
60
- function useComboboxDerivedItemsContext() {
61
- const context = React__namespace.useContext(ComboboxDerivedItemsContext);
62
- if (!context) {
63
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxItemsContext is missing. Combobox parts must be placed within <Combobox.Root>.' : useRenderElement.formatErrorMessage(24));
64
- }
65
- return context;
66
- }
67
- function useComboboxInputValueContext() {
68
- return React__namespace.useContext(ComboboxInputValueContext);
69
- }
70
-
71
- const defaultItemEquality = (itemValue, selectedValue) => Object.is(itemValue, selectedValue);
72
- function compareItemEquality(itemValue, selectedValue, comparer) {
73
- if (itemValue == null || selectedValue == null) {
74
- return Object.is(itemValue, selectedValue);
75
- }
76
- return comparer(itemValue, selectedValue);
77
- }
78
- function selectedValueIncludes(selectedValues, itemValue, comparer) {
79
- if (!selectedValues || selectedValues.length === 0) {
80
- return false;
81
- }
82
- return selectedValues.some(selectedValue => {
83
- if (selectedValue === undefined) {
84
- return false;
85
- }
86
- return compareItemEquality(itemValue, selectedValue, comparer);
87
- });
88
- }
89
- function findItemIndex(itemValues, selectedValue, comparer) {
90
- if (!itemValues || itemValues.length === 0) {
91
- return -1;
92
- }
93
- return itemValues.findIndex(itemValue => {
94
- if (itemValue === undefined) {
95
- return false;
96
- }
97
- return compareItemEquality(itemValue, selectedValue, comparer);
98
- });
99
- }
100
- function removeItem(selectedValues, itemValue, comparer) {
101
- return selectedValues.filter(selectedValue => !compareItemEquality(itemValue, selectedValue, comparer));
102
- }
103
-
104
- function serializeValue(value) {
105
- if (value == null) {
106
- return '';
107
- }
108
- if (typeof value === 'string') {
109
- return value;
110
- }
111
- try {
112
- return JSON.stringify(value);
113
- } catch {
114
- return String(value);
115
- }
116
- }
117
-
118
- function isGroupedItems(items) {
119
- return items != null && items.length > 0 && typeof items[0] === 'object' && items[0] != null && 'items' in items[0];
120
- }
121
-
122
- /**
123
- * Checks if the items array contains an item with a null value that has a non-null label.
124
- */
125
- function hasNullItemLabel(items) {
126
- if (!Array.isArray(items)) {
127
- return items != null && 'null' in items;
128
- }
129
- const arrayItems = items;
130
- if (isGroupedItems(arrayItems)) {
131
- for (const group of arrayItems) {
132
- for (const item of group.items) {
133
- if (item && item.value == null && item.label != null) {
134
- return true;
135
- }
136
- }
137
- }
138
- return false;
139
- }
140
- for (const item of arrayItems) {
141
- if (item && item.value == null && item.label != null) {
142
- return true;
143
- }
144
- }
145
- return false;
146
- }
147
- function stringifyAsLabel(item, itemToStringLabel) {
148
- if (itemToStringLabel && item != null) {
149
- return itemToStringLabel(item) ?? '';
150
- }
151
- if (item && typeof item === 'object') {
152
- if ('label' in item && item.label != null) {
153
- return String(item.label);
154
- }
155
- if ('value' in item) {
156
- return String(item.value);
157
- }
158
- }
159
- return serializeValue(item);
160
- }
161
- function stringifyAsValue(item, itemToStringValue) {
162
- if (itemToStringValue && item != null) {
163
- return itemToStringValue(item) ?? '';
164
- }
165
- if (item && typeof item === 'object' && 'value' in item && 'label' in item) {
166
- return serializeValue(item.value);
167
- }
168
- return serializeValue(item);
169
- }
170
- function resolveSelectedLabel(value, items, itemToStringLabel) {
171
- function fallback() {
172
- return stringifyAsLabel(value, itemToStringLabel);
173
- }
174
- if (itemToStringLabel && value != null) {
175
- return itemToStringLabel(value);
176
- }
177
-
178
- // Custom object with explicit label takes precedence
179
- if (value && typeof value === 'object' && 'label' in value && value.label != null) {
180
- return value.label;
181
- }
182
-
183
- // Items provided as plain record map
184
- if (items && !Array.isArray(items)) {
185
- return items[value] ?? fallback();
186
- }
187
-
188
- // Items provided as array (flat or grouped)
189
- if (Array.isArray(items)) {
190
- const arrayItems = items;
191
- const flatItems = isGroupedItems(arrayItems) ? arrayItems.flatMap(group => group.items) : arrayItems;
192
- if (value == null || typeof value !== 'object') {
193
- const match = flatItems.find(item => item.value === value);
194
- if (match && match.label != null) {
195
- return match.label;
196
- }
197
- return fallback();
198
- }
199
-
200
- // Object without explicit label: try matching by its `value` property
201
- if ('value' in value) {
202
- const match = flatItems.find(item => item && item.value === value.value);
203
- if (match && match.label != null) {
204
- return match.label;
205
- }
206
- }
207
- }
208
- return fallback();
209
- }
210
- function resolveMultipleLabels(values, items, itemToStringLabel) {
211
- return values.reduce((acc, value, index) => {
212
- if (index > 0) {
213
- acc.push(', ');
214
- }
215
- acc.push(/*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
216
- children: resolveSelectedLabel(value, items, itemToStringLabel)
217
- }, index));
218
- return acc;
219
- }, []);
220
- }
221
-
222
- const selectors = {
223
- id: ScrollAreaViewport.createSelector(state => state.id),
224
- labelId: ScrollAreaViewport.createSelector(state => state.labelId),
225
- query: ScrollAreaViewport.createSelector(state => state.query),
226
- items: ScrollAreaViewport.createSelector(state => state.items),
227
- selectedValue: ScrollAreaViewport.createSelector(state => state.selectedValue),
228
- hasSelectionChips: ScrollAreaViewport.createSelector(state => {
229
- const selectedValue = state.selectedValue;
230
- return Array.isArray(selectedValue) && selectedValue.length > 0;
231
- }),
232
- hasSelectedValue: ScrollAreaViewport.createSelector(state => {
233
- const {
234
- selectedValue,
235
- selectionMode
236
- } = state;
237
- if (selectedValue == null) {
238
- return false;
239
- }
240
- if (selectionMode === 'multiple' && Array.isArray(selectedValue)) {
241
- return selectedValue.length > 0;
242
- }
243
- return true;
244
- }),
245
- hasNullItemLabel: ScrollAreaViewport.createSelector((state, enabled) => {
246
- return enabled ? hasNullItemLabel(state.items) : false;
247
- }),
248
- open: ScrollAreaViewport.createSelector(state => state.open),
249
- mounted: ScrollAreaViewport.createSelector(state => state.mounted),
250
- forceMounted: ScrollAreaViewport.createSelector(state => state.forceMounted),
251
- inline: ScrollAreaViewport.createSelector(state => state.inline),
252
- activeIndex: ScrollAreaViewport.createSelector(state => state.activeIndex),
253
- selectedIndex: ScrollAreaViewport.createSelector(state => state.selectedIndex),
254
- isActive: ScrollAreaViewport.createSelector((state, index) => state.activeIndex === index),
255
- isSelected: ScrollAreaViewport.createSelector((state, itemValue) => {
256
- const comparer = state.isItemEqualToValue;
257
- const selectedValue = state.selectedValue;
258
- if (Array.isArray(selectedValue)) {
259
- return selectedValue.some(selectedItem => compareItemEquality(itemValue, selectedItem, comparer));
260
- }
261
- return compareItemEquality(itemValue, selectedValue, comparer);
262
- }),
263
- transitionStatus: ScrollAreaViewport.createSelector(state => state.transitionStatus),
264
- popupProps: ScrollAreaViewport.createSelector(state => state.popupProps),
265
- inputProps: ScrollAreaViewport.createSelector(state => state.inputProps),
266
- triggerProps: ScrollAreaViewport.createSelector(state => state.triggerProps),
267
- getItemProps: ScrollAreaViewport.createSelector(state => state.getItemProps),
268
- positionerElement: ScrollAreaViewport.createSelector(state => state.positionerElement),
269
- listElement: ScrollAreaViewport.createSelector(state => state.listElement),
270
- triggerElement: ScrollAreaViewport.createSelector(state => state.triggerElement),
271
- inputElement: ScrollAreaViewport.createSelector(state => state.inputElement),
272
- inputGroupElement: ScrollAreaViewport.createSelector(state => state.inputGroupElement),
273
- popupSide: ScrollAreaViewport.createSelector(state => state.popupSide),
274
- openMethod: ScrollAreaViewport.createSelector(state => state.openMethod),
275
- inputInsidePopup: ScrollAreaViewport.createSelector(state => state.inputInsidePopup),
276
- selectionMode: ScrollAreaViewport.createSelector(state => state.selectionMode),
277
- listRef: ScrollAreaViewport.createSelector(state => state.listRef),
278
- labelsRef: ScrollAreaViewport.createSelector(state => state.labelsRef),
279
- popupRef: ScrollAreaViewport.createSelector(state => state.popupRef),
280
- emptyRef: ScrollAreaViewport.createSelector(state => state.emptyRef),
281
- inputRef: ScrollAreaViewport.createSelector(state => state.inputRef),
282
- keyboardActiveRef: ScrollAreaViewport.createSelector(state => state.keyboardActiveRef),
283
- chipsContainerRef: ScrollAreaViewport.createSelector(state => state.chipsContainerRef),
284
- clearRef: ScrollAreaViewport.createSelector(state => state.clearRef),
285
- valuesRef: ScrollAreaViewport.createSelector(state => state.valuesRef),
286
- allValuesRef: ScrollAreaViewport.createSelector(state => state.allValuesRef),
287
- name: ScrollAreaViewport.createSelector(state => state.name),
288
- disabled: ScrollAreaViewport.createSelector(state => state.disabled),
289
- readOnly: ScrollAreaViewport.createSelector(state => state.readOnly),
290
- required: ScrollAreaViewport.createSelector(state => state.required),
291
- grid: ScrollAreaViewport.createSelector(state => state.grid),
292
- isGrouped: ScrollAreaViewport.createSelector(state => state.isGrouped),
293
- virtualized: ScrollAreaViewport.createSelector(state => state.virtualized),
294
- onOpenChangeComplete: ScrollAreaViewport.createSelector(state => state.onOpenChangeComplete),
295
- openOnInputClick: ScrollAreaViewport.createSelector(state => state.openOnInputClick),
296
- itemToStringLabel: ScrollAreaViewport.createSelector(state => state.itemToStringLabel),
297
- isItemEqualToValue: ScrollAreaViewport.createSelector(state => state.isItemEqualToValue),
298
- modal: ScrollAreaViewport.createSelector(state => state.modal),
299
- autoHighlight: ScrollAreaViewport.createSelector(state => state.autoHighlight),
300
- submitOnItemClick: ScrollAreaViewport.createSelector(state => state.submitOnItemClick)
301
- };
302
-
303
- /**
304
- * Enhanced filter using Intl.Collator for more robust string matching.
305
- * Uses the provided `itemToStringLabel` function if available, otherwise falls back to:
306
- * • When `item` is an object with a `value` property, that property is used.
307
- * • When `item` is a primitive (e.g. `string`), it is used directly.
308
- */
309
- function createCollatorItemFilter(collatorFilter, itemToStringLabel) {
310
- return (item, query) => {
311
- if (item == null) {
312
- return false;
313
- }
314
- const itemString = stringifyAsLabel(item, itemToStringLabel);
315
- return collatorFilter.contains(itemString, query);
316
- };
317
- }
318
-
319
- /**
320
- * Enhanced filter for single selection mode using Intl.Collator that shows all items
321
- * when query is empty or matches the current selection, making it easier to browse options.
322
- */
323
- function createSingleSelectionCollatorFilter(collatorFilter, itemToStringLabel, selectedValue) {
324
- return (item, query) => {
325
- if (item == null) {
326
- return false;
327
- }
328
- if (!query) {
329
- return true;
330
- }
331
- const itemString = stringifyAsLabel(item, itemToStringLabel);
332
- const selectedString = selectedValue != null ? stringifyAsLabel(selectedValue, itemToStringLabel) : '';
333
-
334
- // Handle case-insensitive matching consistently
335
- if (selectedString && collatorFilter.contains(selectedString, query) && selectedString.length === query.length) {
336
- return true;
337
- }
338
- return collatorFilter.contains(itemString, query);
339
- };
340
- }
341
-
342
- const filterCache = new Map();
343
- function stringifyLocale(locale) {
344
- if (Array.isArray(locale)) {
345
- return locale.map(value => stringifyLocale(value)).join(',');
346
- }
347
- if (locale == null) {
348
- return '';
349
- }
350
- return String(locale);
351
- }
352
- function getFilter(options = {}) {
353
- const mergedOptions = {
354
- usage: 'search',
355
- sensitivity: 'base',
356
- ignorePunctuation: true,
357
- ...options
358
- };
359
- const cacheKey = `${stringifyLocale(options.locale)}|${JSON.stringify(mergedOptions)}`;
360
- const cachedFilter = filterCache.get(cacheKey);
361
- if (cachedFilter) {
362
- return cachedFilter;
363
- }
364
- const collator = new Intl.Collator(options.locale, mergedOptions);
365
- const filter = {
366
- contains(item, query, itemToString) {
367
- if (!query) {
368
- return true;
369
- }
370
- const itemString = stringifyAsLabel(item, itemToString);
371
- for (let i = 0; i <= itemString.length - query.length; i += 1) {
372
- if (collator.compare(itemString.slice(i, i + query.length), query) === 0) {
373
- return true;
374
- }
375
- }
376
- return false;
377
- },
378
- startsWith(item, query, itemToString) {
379
- if (!query) {
380
- return true;
381
- }
382
- const itemString = stringifyAsLabel(item, itemToString);
383
- return collator.compare(itemString.slice(0, query.length), query) === 0;
384
- },
385
- endsWith(item, query, itemToString) {
386
- if (!query) {
387
- return true;
388
- }
389
- const itemString = stringifyAsLabel(item, itemToString);
390
- const queryLength = query.length;
391
- return itemString.length >= queryLength && collator.compare(itemString.slice(itemString.length - queryLength), query) === 0;
392
- }
393
- };
394
- filterCache.set(cacheKey, filter);
395
- return filter;
396
- }
397
-
398
- /**
399
- * Matches items against a query using `Intl.Collator` for robust string matching.
400
- */
401
- const useCoreFilter = getFilter;
402
- /**
403
- * Matches items against a query using `Intl.Collator` for robust string matching.
404
- */
405
- function useComboboxFilter(options = {}) {
41
+ function AutocompleteRoot(props) {
406
42
  const {
407
- multiple = false,
43
+ openOnInputClick = false,
408
44
  value,
409
- ...collatorOptions
410
- } = options;
411
- const coreFilter = getFilter(collatorOptions);
412
- const contains = React__namespace.useCallback((item, query, itemToString) => {
413
- if (multiple) {
414
- return createCollatorItemFilter(coreFilter, itemToString)(item, query);
415
- }
416
- return createSingleSelectionCollatorFilter(coreFilter, itemToString, value)(item, query);
417
- }, [coreFilter, value, multiple]);
418
- return React__namespace.useMemo(() => ({
419
- contains,
420
- startsWith: coreFilter.startsWith,
421
- endsWith: coreFilter.endsWith
422
- }), [contains, coreFilter]);
423
- }
424
-
425
- const NO_ACTIVE_VALUE = Symbol('none');
426
- const INITIAL_LAST_HIGHLIGHT = {
427
- value: NO_ACTIVE_VALUE,
428
- index: -1
429
- };
430
-
431
- function AriaCombobox(props) {
432
- const {
433
- id: idProp,
434
- onOpenChangeComplete: onOpenChangeCompleteProp,
435
- defaultSelectedValue = null,
436
- selectedValue: selectedValueProp,
437
- onSelectedValueChange,
438
- defaultInputValue: defaultInputValueProp,
439
- inputValue: inputValueProp,
440
- selectionMode = 'none',
441
- onItemHighlighted: onItemHighlightedProp,
442
- name: nameProp,
443
- disabled: disabledProp = false,
444
- readOnly = false,
445
- required = false,
446
- inputRef: inputRefProp,
447
- grid = false,
448
- items,
449
- filteredItems: filteredItemsProp,
450
- filter: filterProp,
451
- openOnInputClick = true,
452
- autoHighlight = false,
453
- keepHighlight = false,
454
- highlightItemOnHover = true,
455
- loopFocus = true,
456
- itemToStringLabel,
45
+ defaultValue,
46
+ onValueChange,
47
+ mode = 'list',
457
48
  itemToStringValue,
458
- isItemEqualToValue = defaultItemEquality,
459
- virtualized = false,
460
- inline: inlineProp = false,
461
- fillInputOnItemPress = true,
462
- modal = false,
463
- limit = -1,
464
- autoComplete = 'list',
465
- formAutoComplete,
466
- locale,
467
- submitOnItemClick = false
49
+ ...other
468
50
  } = props;
469
- const {
470
- clearErrors
471
- } = NumberFieldInput.useFormContext();
472
- const {
473
- setDirty,
474
- validityData,
475
- shouldValidateOnChange,
476
- setFilled,
477
- name: fieldName,
478
- disabled: fieldDisabled,
479
- setTouched,
480
- setFocused,
481
- validationMode,
482
- validation
483
- } = NumberFieldInput.useFieldRootContext();
484
- const id = NumberFieldInput.useLabelableId({
485
- id: idProp
486
- });
487
- const collatorFilter = useCoreFilter({
488
- locale
489
- });
490
- const [queryChangedAfterOpen, setQueryChangedAfterOpen] = React__namespace.useState(false);
491
- const [closeQuery, setCloseQuery] = React__namespace.useState(null);
492
- const listRef = React__namespace.useRef([]);
493
- const labelsRef = React__namespace.useRef([]);
494
- const popupRef = React__namespace.useRef(null);
495
- const inputRef = React__namespace.useRef(null);
496
- const startDismissRef = React__namespace.useRef(null);
497
- const endDismissRef = React__namespace.useRef(null);
498
- const emptyRef = React__namespace.useRef(null);
499
- const keyboardActiveRef = React__namespace.useRef(true);
500
- const hadInputClearRef = React__namespace.useRef(false);
501
- const chipsContainerRef = React__namespace.useRef(null);
502
- const clearRef = React__namespace.useRef(null);
503
- const selectionEventRef = React__namespace.useRef(null);
504
- const lastHighlightRef = React__namespace.useRef(INITIAL_LAST_HIGHLIGHT);
505
- const pendingQueryHighlightRef = React__namespace.useRef(null);
506
-
507
- /**
508
- * Contains the currently visible list of item values post-filtering.
509
- */
510
- const valuesRef = React__namespace.useRef([]);
511
- /**
512
- * Contains all item values in a stable, unfiltered order.
513
- * This is only used when `items` prop is not provided.
514
- * It accumulates values on first mount and does not remove them on unmount due to
515
- * filtering, providing a stable index for selected value tracking.
516
- */
517
- const allValuesRef = React__namespace.useRef([]);
518
- const disabled = fieldDisabled || disabledProp;
519
- const name = fieldName ?? nameProp;
520
- const multiple = selectionMode === 'multiple';
521
- const single = selectionMode === 'single';
522
- const hasInputValue = inputValueProp !== undefined || defaultInputValueProp !== undefined;
523
- const hasItems = items !== undefined;
524
- const hasFilteredItemsProp = filteredItemsProp !== undefined;
525
- let autoHighlightMode;
526
- if (autoHighlight === 'always') {
527
- autoHighlightMode = 'always';
528
- } else {
529
- autoHighlightMode = autoHighlight ? 'input-change' : false;
530
- }
531
- const [selectedValue, setSelectedValueUnwrapped] = clamp.useControlled({
532
- controlled: selectedValueProp,
533
- default: multiple ? defaultSelectedValue ?? useRenderElement.EMPTY_ARRAY : defaultSelectedValue,
534
- name: 'Combobox',
535
- state: 'selectedValue'
536
- });
537
- const filter = React__namespace.useMemo(() => {
538
- if (filterProp === null) {
539
- return () => true;
540
- }
541
- if (filterProp !== undefined) {
542
- return filterProp;
543
- }
544
- if (single && !queryChangedAfterOpen) {
545
- return createSingleSelectionCollatorFilter(collatorFilter, itemToStringLabel, selectedValue);
546
- }
547
- return createCollatorItemFilter(collatorFilter, itemToStringLabel);
548
- }, [filterProp, single, selectedValue, queryChangedAfterOpen, collatorFilter, itemToStringLabel]);
549
-
550
- // If neither inputValue nor defaultInputValue are provided, derive it from the
551
- // selected value for single mode so the input reflects the selection on mount.
552
- const initialDefaultInputValue = useRenderElement.useRefWithInit(() => {
553
- if (hasInputValue) {
554
- return defaultInputValueProp ?? '';
555
- }
556
- if (single) {
557
- return stringifyAsLabel(selectedValue, itemToStringLabel);
558
- }
559
- return '';
560
- }).current;
561
- const [inputValue, setInputValueUnwrapped] = clamp.useControlled({
562
- controlled: inputValueProp,
563
- default: initialDefaultInputValue,
564
- name: 'Combobox',
565
- state: 'inputValue'
566
- });
567
- const [open, setOpenUnwrapped] = clamp.useControlled({
568
- controlled: props.open,
569
- default: props.defaultOpen,
570
- name: 'Combobox',
571
- state: 'open'
572
- });
573
- const isGrouped = isGroupedItems(items);
574
- const query = closeQuery ?? (inputValue === '' ? '' : String(inputValue).trim());
575
- const selectedLabelString = single ? stringifyAsLabel(selectedValue, itemToStringLabel) : '';
576
- const shouldBypassFiltering = single && !queryChangedAfterOpen && query !== '' && selectedLabelString !== '' && selectedLabelString.length === query.length && collatorFilter.contains(selectedLabelString, query);
577
- const filterQuery = shouldBypassFiltering ? '' : query;
578
- const shouldIgnoreExternalFiltering = hasItems && hasFilteredItemsProp && shouldBypassFiltering;
579
- const flatItems = React__namespace.useMemo(() => {
580
- if (!items) {
581
- return useRenderElement.EMPTY_ARRAY;
582
- }
583
- if (isGrouped) {
584
- return items.flatMap(group => group.items);
585
- }
586
- return items;
587
- }, [items, isGrouped]);
588
- const filteredItems = React__namespace.useMemo(() => {
589
- if (filteredItemsProp && !shouldIgnoreExternalFiltering) {
590
- return filteredItemsProp;
591
- }
592
- if (!items) {
593
- return useRenderElement.EMPTY_ARRAY;
594
- }
595
- if (isGrouped) {
596
- const groupedItems = items;
597
- const resultingGroups = [];
598
- let currentCount = 0;
599
- for (const group of groupedItems) {
600
- if (limit > -1 && currentCount >= limit) {
601
- break;
602
- }
603
- const candidateItems = filterQuery === '' ? group.items : group.items.filter(item => filter(item, filterQuery, itemToStringLabel));
604
- if (candidateItems.length === 0) {
605
- continue;
606
- }
607
- const remainingLimit = limit > -1 ? limit - currentCount : Infinity;
608
- const itemsToTake = candidateItems.slice(0, remainingLimit);
609
- if (itemsToTake.length > 0) {
610
- const newGroup = {
611
- ...group,
612
- items: itemsToTake
613
- };
614
- resultingGroups.push(newGroup);
615
- currentCount += itemsToTake.length;
616
- }
617
- }
618
- return resultingGroups;
619
- }
620
- if (filterQuery === '') {
621
- return limit > -1 ? flatItems.slice(0, limit) :
622
- // The cast here is done as `flatItems` is readonly.
623
- // valuesRef.current, a mutable ref, can be set to `flatFilteredItems`, which may
624
- // reference this exact readonly value, creating a mutation risk.
625
- // However, <Combobox.Item> can never mutate this value as the mutating effect
626
- // bails early when `items` is provided, and this is only ever returned
627
- // when `items` is provided due to the early return at the top of this hook.
628
- flatItems;
629
- }
630
- const limitedItems = [];
631
- for (const item of flatItems) {
632
- if (limit > -1 && limitedItems.length >= limit) {
633
- break;
634
- }
635
- if (filter(item, filterQuery, itemToStringLabel)) {
636
- limitedItems.push(item);
637
- }
638
- }
639
- return limitedItems;
640
- }, [filteredItemsProp, shouldIgnoreExternalFiltering, items, isGrouped, filterQuery, limit, filter, itemToStringLabel, flatItems]);
641
- const flatFilteredItems = React__namespace.useMemo(() => {
642
- if (isGrouped) {
643
- const groups = filteredItems;
644
- return groups.flatMap(g => g.items);
645
- }
646
- return filteredItems;
647
- }, [filteredItems, isGrouped]);
648
- const store = useRenderElement.useRefWithInit(() => new ScrollAreaViewport.Store({
649
- id,
650
- labelId: undefined,
651
- selectedValue,
652
- open,
653
- filter,
654
- query,
655
- items,
656
- selectionMode,
657
- listRef,
658
- labelsRef,
659
- popupRef,
660
- emptyRef,
661
- inputRef,
662
- startDismissRef,
663
- endDismissRef,
664
- keyboardActiveRef,
665
- chipsContainerRef,
666
- clearRef,
667
- valuesRef,
668
- allValuesRef,
669
- selectionEventRef,
670
- name,
671
- disabled,
672
- readOnly,
673
- required,
674
- grid,
675
- isGrouped,
676
- virtualized,
677
- openOnInputClick,
678
- itemToStringLabel,
679
- isItemEqualToValue,
680
- modal,
681
- autoHighlight: autoHighlightMode,
682
- submitOnItemClick,
683
- hasInputValue,
684
- mounted: false,
685
- forceMounted: false,
686
- transitionStatus: 'idle',
687
- inline: inlineProp,
688
- activeIndex: null,
689
- selectedIndex: null,
690
- popupProps: {},
691
- inputProps: {},
692
- triggerProps: {},
693
- positionerElement: null,
694
- listElement: null,
695
- triggerElement: null,
696
- inputElement: null,
697
- inputGroupElement: null,
698
- popupSide: null,
699
- openMethod: null,
700
- inputInsidePopup: true,
701
- onOpenChangeComplete: onOpenChangeCompleteProp || useRenderElement.NOOP,
702
- // Placeholder callbacks replaced on first render
703
- setOpen: useRenderElement.NOOP,
704
- setInputValue: useRenderElement.NOOP,
705
- setSelectedValue: useRenderElement.NOOP,
706
- setIndices: useRenderElement.NOOP,
707
- onItemHighlighted: useRenderElement.NOOP,
708
- handleSelection: useRenderElement.NOOP,
709
- getItemProps: () => useRenderElement.EMPTY_OBJECT,
710
- forceMount: useRenderElement.NOOP,
711
- requestSubmit: useRenderElement.NOOP
712
- })).current;
713
- const fieldRawValue = selectionMode === 'none' ? inputValue : selectedValue;
714
- const fieldStringValue = React__namespace.useMemo(() => {
715
- if (selectionMode === 'none') {
716
- return fieldRawValue;
717
- }
718
- if (Array.isArray(selectedValue)) {
719
- return selectedValue.map(value => stringifyAsValue(value, itemToStringValue));
720
- }
721
- return stringifyAsValue(selectedValue, itemToStringValue);
722
- }, [fieldRawValue, itemToStringValue, selectionMode, selectedValue]);
723
- const onItemHighlighted = clamp.useStableCallback(onItemHighlightedProp);
724
- const onOpenChangeComplete = clamp.useStableCallback(onOpenChangeCompleteProp);
725
- const activeIndex = ScrollAreaViewport.useStore(store, selectors.activeIndex);
726
- const selectedIndex = ScrollAreaViewport.useStore(store, selectors.selectedIndex);
727
- const positionerElement = ScrollAreaViewport.useStore(store, selectors.positionerElement);
728
- const listElement = ScrollAreaViewport.useStore(store, selectors.listElement);
729
- const triggerElement = ScrollAreaViewport.useStore(store, selectors.triggerElement);
730
- const inputElement = ScrollAreaViewport.useStore(store, selectors.inputElement);
731
- const inputGroupElement = ScrollAreaViewport.useStore(store, selectors.inputGroupElement);
732
- const inline = ScrollAreaViewport.useStore(store, selectors.inline);
733
- const inputInsidePopup = ScrollAreaViewport.useStore(store, selectors.inputInsidePopup);
734
- const triggerRef = clamp.useValueAsRef(triggerElement);
735
- const {
736
- mounted,
737
- setMounted,
738
- transitionStatus
739
- } = clamp.useTransitionStatus(open);
740
- const {
741
- openMethod,
742
- triggerProps
743
- } = ScrollAreaViewport.useOpenInteractionType(open);
744
- NumberFieldInput.useField({
745
- id,
746
- name,
747
- commit: validation.commit,
748
- value: fieldRawValue,
749
- controlRef: inputInsidePopup ? triggerRef : inputRef,
750
- getValue: () => fieldStringValue
751
- });
752
- const forceMount = clamp.useStableCallback(() => {
753
- if (items) {
754
- // Ensure typeahead works on a closed list.
755
- labelsRef.current = flatFilteredItems.map(item => stringifyAsLabel(item, itemToStringLabel));
756
- } else {
757
- store.set('forceMounted', true);
758
- }
759
- });
760
- const initialSelectedValueRef = React__namespace.useRef(selectedValue);
761
- clamp.useIsoLayoutEffect(() => {
762
- // Ensure the values and labels are registered for programmatic value changes.
763
- if (selectedValue !== initialSelectedValueRef.current) {
764
- forceMount();
765
- }
766
- }, [forceMount, selectedValue]);
767
- const setIndices = clamp.useStableCallback(options => {
768
- store.update(options);
769
- const type = options.type || 'none';
770
- if (options.activeIndex === undefined) {
771
- return;
772
- }
773
- if (options.activeIndex === null) {
774
- if (lastHighlightRef.current !== INITIAL_LAST_HIGHLIGHT) {
775
- lastHighlightRef.current = INITIAL_LAST_HIGHLIGHT;
776
- onItemHighlighted(undefined, clamp.createGenericEventDetails(type, undefined, {
777
- index: -1
778
- }));
779
- }
780
- } else {
781
- const activeValue = valuesRef.current[options.activeIndex];
782
- lastHighlightRef.current = {
783
- value: activeValue,
784
- index: options.activeIndex
785
- };
786
- onItemHighlighted(activeValue, clamp.createGenericEventDetails(type, undefined, {
787
- index: options.activeIndex
788
- }));
789
- }
790
- });
791
- const setInputValue = clamp.useStableCallback((next, eventDetails) => {
792
- hadInputClearRef.current = eventDetails.reason === clamp.inputClear;
793
- props.onInputValueChange?.(next, eventDetails);
794
- if (eventDetails.isCanceled) {
795
- return;
796
- }
51
+ const enableInline = mode === 'inline' || mode === 'both';
52
+ const staticItems = mode === 'inline' || mode === 'none';
797
53
 
798
- // If user is typing, ensure we don't auto-highlight on open due to a race
799
- // with the post-open effect that sets this flag.
800
- if (eventDetails.reason === clamp.inputChange) {
801
- const event = eventDetails.event;
802
- const inputType = event.inputType;
803
- // Treat composition commits as typed input; autofill may omit `inputType` or
804
- // report `insertReplacementText`.
805
- const isTypedInput = event.type === 'compositionend' || inputType != null && inputType !== '' && inputType !== 'insertReplacementText';
806
- if (isTypedInput) {
807
- const hasQuery = next.trim() !== '';
808
- if (hasQuery) {
809
- setQueryChangedAfterOpen(true);
810
- }
811
- // Defer index updates until after the filtered items have been derived to ensure
812
- // `onItemHighlighted` receives the latest item.
813
- pendingQueryHighlightRef.current = {
814
- hasQuery
815
- };
816
- if (hasQuery && autoHighlightMode && store.state.activeIndex == null) {
817
- store.set('activeIndex', 0);
818
- }
819
- }
820
- }
821
- setInputValueUnwrapped(next);
822
- });
823
- const setOpen = clamp.useStableCallback((nextOpen, eventDetails) => {
824
- if (open === nextOpen) {
825
- return;
54
+ // Mirror the typed value for uncontrolled usage so we can compose the temporary
55
+ // inline input value.
56
+ const isControlled = value !== undefined;
57
+ const [internalValue, setInternalValue] = React__namespace.useState(defaultValue ?? '');
58
+ const [inlineInputValue, setInlineInputValue] = React__namespace.useState('');
59
+ React__namespace.useEffect(() => {
60
+ if (isControlled) {
61
+ setInlineInputValue('');
826
62
  }
63
+ }, [value, isControlled]);
827
64
 
828
- // If the `Empty` component is not used, the positioner or popup should be hidden
829
- // with CSS. In this case, allow the Escape key to bubble to close a parent popup
830
- // if there are no items to show.
831
- if (eventDetails.reason === 'escape-key' && hasItems && flatFilteredItems.length === 0 && !store.state.emptyRef.current) {
832
- eventDetails.allowPropagation();
833
- }
834
- props.onOpenChange?.(nextOpen, eventDetails);
835
- if (eventDetails.isCanceled) {
836
- return;
837
- }
838
- if (!nextOpen && queryChangedAfterOpen) {
839
- if (single) {
840
- if (!inline) {
841
- setCloseQuery(query);
842
- }
843
- // Avoid a flicker when closing the popup with an empty query.
844
- if (query === '') {
845
- setQueryChangedAfterOpen(false);
846
- }
847
- } else if (multiple) {
848
- if (inline || inputInsidePopup) {
849
- setIndices({
850
- activeIndex: null
851
- });
852
- } else {
853
- // Freeze the current query so filtering remains stable while exiting.
854
- setCloseQuery(query);
855
- }
856
- // Clear the input immediately on close while retaining filtering via closeQuery for exit animations
857
- // if the input is outside the popup.
858
- setInputValue('', clamp.createChangeEventDetails(clamp.inputClear, eventDetails.event));
859
- }
860
- }
861
- setOpenUnwrapped(nextOpen);
862
- if (!nextOpen && inputInsidePopup && (eventDetails.reason === clamp.focusOut || eventDetails.reason === clamp.outsidePress)) {
863
- setTouched(true);
864
- setFocused(false);
865
- if (validationMode === 'onBlur') {
866
- const valueToValidate = selectionMode === 'none' ? inputValue : selectedValue;
867
- validation.commit(valueToValidate);
868
- }
869
- }
870
- });
871
- const setSelectedValue = clamp.useStableCallback((nextValue, eventDetails) => {
872
- // Cast to `any` due to conditional value type (single vs. multiple).
873
- // The runtime implementation already ensures the correct value shape.
874
- onSelectedValueChange?.(nextValue, eventDetails);
875
- if (eventDetails.isCanceled) {
876
- return;
877
- }
878
- setSelectedValueUnwrapped(nextValue);
879
- const shouldFillInput = selectionMode === 'none' && popupRef.current && fillInputOnItemPress || single && !store.state.inputInsidePopup;
880
- if (shouldFillInput) {
881
- setInputValue(stringifyAsLabel(nextValue, itemToStringLabel), clamp.createChangeEventDetails(eventDetails.reason, eventDetails.event));
882
- }
883
- if (single && nextValue != null && eventDetails.reason !== clamp.inputChange && queryChangedAfterOpen && !inline) {
884
- setCloseQuery(query);
65
+ // Compose the input value shown to the user: inline value takes precedence when present.
66
+ let resolvedInputValue;
67
+ if (enableInline && inlineInputValue !== '') {
68
+ resolvedInputValue = inlineInputValue;
69
+ } else if (isControlled) {
70
+ resolvedInputValue = value ?? '';
71
+ } else {
72
+ resolvedInputValue = internalValue;
73
+ }
74
+ const handleValueChange = useButton.useStableCallback((nextValue, eventDetails) => {
75
+ setInlineInputValue('');
76
+ if (!isControlled) {
77
+ setInternalValue(nextValue);
885
78
  }
79
+ onValueChange?.(nextValue, eventDetails);
886
80
  });
887
- const handleSelection = clamp.useStableCallback((event, passedValue) => {
888
- let itemValue = passedValue;
889
- if (itemValue === undefined) {
890
- if (activeIndex === null) {
891
- return;
892
- }
893
- itemValue = valuesRef.current[activeIndex];
81
+ const collator = ComboboxChipRemove.useCoreFilter();
82
+ const baseFilter = React__namespace.useMemo(() => {
83
+ if (other.filter !== undefined) {
84
+ return other.filter;
894
85
  }
895
- const targetEl = clamp.getTarget(event);
896
- const overrideEvent = selectionEventRef.current ?? event;
897
- selectionEventRef.current = null;
898
- const eventDetails = clamp.createChangeEventDetails(clamp.itemPress, overrideEvent);
899
-
900
- // Let the link handle the click.
901
- const href = targetEl?.closest('a')?.getAttribute('href');
902
- if (href) {
903
- if (href.startsWith('#')) {
904
- setOpen(false, eventDetails);
905
- }
906
- return;
907
- }
908
- if (multiple) {
909
- const currentSelectedValue = Array.isArray(selectedValue) ? selectedValue : [];
910
- const isCurrentlySelected = selectedValueIncludes(currentSelectedValue, itemValue, store.state.isItemEqualToValue);
911
- const nextValue = isCurrentlySelected ? removeItem(currentSelectedValue, itemValue, store.state.isItemEqualToValue) : [...currentSelectedValue, itemValue];
912
- setSelectedValue(nextValue, eventDetails);
913
- const wasFiltering = inputRef.current ? inputRef.current.value.trim() !== '' : false;
914
- if (!wasFiltering) {
915
- return;
916
- }
917
- if (store.state.inputInsidePopup) {
918
- setInputValue('', clamp.createChangeEventDetails(clamp.inputClear, eventDetails.event));
919
- } else {
920
- setOpen(false, eventDetails);
921
- }
922
- } else {
923
- setSelectedValue(itemValue, eventDetails);
924
- setOpen(false, eventDetails);
925
- }
926
- });
927
- const requestSubmit = clamp.useStableCallback(() => {
928
- if (!store.state.submitOnItemClick) {
929
- return;
930
- }
931
- const form = store.state.inputElement?.form;
932
- if (form && typeof form.requestSubmit === 'function') {
933
- form.requestSubmit();
934
- }
935
- });
936
- const handleUnmount = clamp.useStableCallback(() => {
937
- setMounted(false);
938
- onOpenChangeComplete?.(false);
939
- setQueryChangedAfterOpen(false);
940
- setCloseQuery(null);
941
- if (selectionMode === 'none') {
942
- setIndices({
943
- activeIndex: null,
944
- selectedIndex: null
945
- });
946
- } else {
947
- setIndices({
948
- activeIndex: null
949
- });
950
- }
951
-
952
- // Multiple selection mode:
953
- // If the user typed a filter and didn't select in multiple mode, clear the input
954
- // after close completes to avoid mid-exit flicker and start fresh on next open.
955
- if (multiple && inputRef.current && inputRef.current.value !== '' && !hadInputClearRef.current) {
956
- setInputValue('', clamp.createChangeEventDetails(clamp.inputClear));
957
- }
958
-
959
- // Single selection mode:
960
- // - If input is rendered inside the popup, clear it so the next open is blank
961
- // - If input is outside the popup, sync it to the selected value
962
- if (single) {
963
- if (store.state.inputInsidePopup) {
964
- if (inputRef.current && inputRef.current.value !== '') {
965
- setInputValue('', clamp.createChangeEventDetails(clamp.inputClear));
966
- }
967
- } else {
968
- const stringVal = stringifyAsLabel(selectedValue, itemToStringLabel);
969
- if (inputRef.current && inputRef.current.value !== stringVal) {
970
- // If no selection was made, treat this as clearing the typed filter.
971
- const reason = stringVal === '' ? clamp.inputClear : clamp.none;
972
- setInputValue(stringVal, clamp.createChangeEventDetails(reason));
973
- }
974
- }
975
- }
976
- });
977
-
978
- // Support composing the Dialog component around an inline combobox.
979
- // `[role="dialog"]` is more interoperable than using a context, e.g. it can work
980
- // with third-party modal libraries, though the limitation is that the closest
981
- // `role=dialog` part must be the animated element.
982
- const resolvedPopupRef = React__namespace.useMemo(() => {
983
- if (inline && positionerElement) {
984
- return {
985
- current: positionerElement.closest('[role="dialog"]')
986
- };
987
- }
988
- return popupRef;
989
- }, [inline, positionerElement]);
990
- clamp.useOpenChangeComplete({
991
- enabled: !props.actionsRef,
992
- open,
993
- ref: resolvedPopupRef,
994
- onComplete() {
995
- if (!open) {
996
- handleUnmount();
997
- }
998
- }
999
- });
1000
- React__namespace.useImperativeHandle(props.actionsRef, () => ({
1001
- unmount: handleUnmount
1002
- }), [handleUnmount]);
1003
- clamp.useIsoLayoutEffect(function syncSelectedIndex() {
1004
- if (open || selectionMode === 'none') {
1005
- return;
1006
- }
1007
- const registry = items ? flatItems : allValuesRef.current;
1008
- if (multiple) {
1009
- const currentValue = Array.isArray(selectedValue) ? selectedValue : [];
1010
- const lastValue = currentValue[currentValue.length - 1];
1011
- const lastIndex = findItemIndex(registry, lastValue, isItemEqualToValue);
1012
- setIndices({
1013
- selectedIndex: lastIndex === -1 ? null : lastIndex
1014
- });
1015
- } else {
1016
- const index = findItemIndex(registry, selectedValue, isItemEqualToValue);
1017
- setIndices({
1018
- selectedIndex: index === -1 ? null : index
1019
- });
1020
- }
1021
- }, [open, selectedValue, items, selectionMode, flatItems, multiple, isItemEqualToValue, setIndices]);
1022
- clamp.useIsoLayoutEffect(() => {
1023
- if (items) {
1024
- valuesRef.current = flatFilteredItems;
1025
- listRef.current.length = flatFilteredItems.length;
1026
- }
1027
- }, [items, flatFilteredItems]);
1028
- clamp.useIsoLayoutEffect(() => {
1029
- const pendingHighlight = pendingQueryHighlightRef.current;
1030
- if (pendingHighlight) {
1031
- if (pendingHighlight.hasQuery) {
1032
- if (autoHighlightMode) {
1033
- store.set('activeIndex', 0);
1034
- }
1035
- } else if (autoHighlightMode === 'always') {
1036
- store.set('activeIndex', 0);
1037
- }
1038
- pendingQueryHighlightRef.current = null;
1039
- }
1040
- if (!open && !inline) {
1041
- return;
1042
- }
1043
- const shouldUseFlatFilteredItems = hasItems || hasFilteredItemsProp;
1044
- const candidateItems = shouldUseFlatFilteredItems ? flatFilteredItems : valuesRef.current;
1045
- const storeActiveIndex = store.state.activeIndex;
1046
- if (storeActiveIndex == null) {
1047
- if (autoHighlightMode === 'always' && candidateItems.length > 0) {
1048
- store.set('activeIndex', 0);
1049
- return;
1050
- }
1051
- if (lastHighlightRef.current !== INITIAL_LAST_HIGHLIGHT) {
1052
- lastHighlightRef.current = INITIAL_LAST_HIGHLIGHT;
1053
- store.state.onItemHighlighted(undefined, clamp.createGenericEventDetails(clamp.none, undefined, {
1054
- index: -1
1055
- }));
1056
- }
1057
- return;
1058
- }
1059
- if (storeActiveIndex >= candidateItems.length) {
1060
- if (lastHighlightRef.current !== INITIAL_LAST_HIGHLIGHT) {
1061
- lastHighlightRef.current = INITIAL_LAST_HIGHLIGHT;
1062
- store.state.onItemHighlighted(undefined, clamp.createGenericEventDetails(clamp.none, undefined, {
1063
- index: -1
1064
- }));
1065
- }
1066
- store.set('activeIndex', null);
1067
- return;
1068
- }
1069
- const itemValue = candidateItems[storeActiveIndex];
1070
- const previouslyHighlightedItemValue = lastHighlightRef.current.value;
1071
- const isSameItem = previouslyHighlightedItemValue !== NO_ACTIVE_VALUE && compareItemEquality(itemValue, previouslyHighlightedItemValue, store.state.isItemEqualToValue);
1072
- if (lastHighlightRef.current.index !== storeActiveIndex || !isSameItem) {
1073
- lastHighlightRef.current = {
1074
- value: itemValue,
1075
- index: storeActiveIndex
1076
- };
1077
- store.state.onItemHighlighted(itemValue, clamp.createGenericEventDetails(clamp.none, undefined, {
1078
- index: storeActiveIndex
1079
- }));
1080
- }
1081
- }, [activeIndex, autoHighlightMode, hasFilteredItemsProp, hasItems, flatFilteredItems, inline, open, store]);
1082
- clamp.useIsoLayoutEffect(() => {
1083
- if (selectionMode === 'none') {
1084
- setFilled(String(inputValue) !== '');
1085
- return;
1086
- }
1087
- setFilled(multiple ? Array.isArray(selectedValue) && selectedValue.length > 0 : selectedValue != null);
1088
- }, [setFilled, selectionMode, inputValue, selectedValue, multiple]);
1089
-
1090
- // Ensures that the active index is not set to 0 when the list is empty.
1091
- // This avoids needing to press ArrowDown twice under certain conditions.
1092
- React__namespace.useEffect(() => {
1093
- if (hasItems && autoHighlightMode && flatFilteredItems.length === 0) {
1094
- setIndices({
1095
- activeIndex: null
1096
- });
1097
- }
1098
- }, [hasItems, autoHighlightMode, flatFilteredItems.length, setIndices]);
1099
- clamp.useValueChanged(query, () => {
1100
- if (!open || query === '' || query === String(initialDefaultInputValue)) {
1101
- return;
1102
- }
1103
- setQueryChangedAfterOpen(true);
1104
- });
1105
- clamp.useValueChanged(selectedValue, () => {
1106
- if (selectionMode === 'none') {
1107
- return;
1108
- }
1109
- clearErrors(name);
1110
- setDirty(selectedValue !== validityData.initialValue);
1111
- if (shouldValidateOnChange()) {
1112
- validation.commit(selectedValue);
1113
- } else {
1114
- validation.commit(selectedValue, true);
1115
- }
1116
- if (single && !hasInputValue && !inputInsidePopup) {
1117
- const nextInputValue = stringifyAsLabel(selectedValue, itemToStringLabel);
1118
- if (inputValue !== nextInputValue) {
1119
- setInputValue(nextInputValue, clamp.createChangeEventDetails(clamp.none));
1120
- }
1121
- }
1122
- });
1123
- clamp.useValueChanged(inputValue, () => {
1124
- if (selectionMode !== 'none') {
1125
- return;
1126
- }
1127
- clearErrors(name);
1128
- setDirty(inputValue !== validityData.initialValue);
1129
- if (shouldValidateOnChange()) {
1130
- validation.commit(inputValue);
1131
- } else {
1132
- validation.commit(inputValue, true);
1133
- }
1134
- });
1135
- clamp.useValueChanged(items, () => {
1136
- if (!single || hasInputValue || inputInsidePopup || queryChangedAfterOpen) {
1137
- return;
1138
- }
1139
- const nextInputValue = stringifyAsLabel(selectedValue, itemToStringLabel);
1140
- if (inputValue !== nextInputValue) {
1141
- setInputValue(nextInputValue, clamp.createChangeEventDetails(clamp.none));
1142
- }
1143
- });
1144
- const floatingRootContext = MenuSubmenuTrigger.useFloatingRootContext({
1145
- open: inline ? true : open,
1146
- onOpenChange: setOpen,
1147
- elements: {
1148
- reference: inputInsidePopup ? triggerElement : inputElement,
1149
- floating: positionerElement
1150
- }
1151
- });
1152
- let ariaHasPopup;
1153
- let ariaExpanded;
1154
- if (!inline) {
1155
- ariaHasPopup = grid ? 'grid' : 'listbox';
1156
- ariaExpanded = open ? 'true' : 'false';
1157
- }
1158
- const role = React__namespace.useMemo(() => {
1159
- const isPlainInput = inputElement?.tagName === 'INPUT';
1160
- // During SSR and initial hydration, the input ref is not available yet.
1161
- // Assume an input-like control so combobox ARIA attributes are present.
1162
- const shouldTreatAsInput = inputElement == null || isPlainInput;
1163
- const shouldApplyAria = shouldTreatAsInput || open;
1164
- const reference = shouldTreatAsInput ? {
1165
- autoComplete: 'off',
1166
- spellCheck: 'false',
1167
- autoCorrect: 'off',
1168
- autoCapitalize: 'none'
1169
- } : {};
1170
- if (shouldApplyAria) {
1171
- reference.role = 'combobox';
1172
- reference['aria-expanded'] = ariaExpanded;
1173
- reference['aria-haspopup'] = ariaHasPopup;
1174
- reference['aria-controls'] = open ? listElement?.id : undefined;
1175
- reference['aria-autocomplete'] = autoComplete;
1176
- }
1177
- return {
1178
- reference,
1179
- floating: {
1180
- role: 'presentation'
1181
- }
1182
- };
1183
- }, [inputElement, open, ariaExpanded, ariaHasPopup, listElement?.id, autoComplete]);
1184
- const click = ScrollAreaViewport.useClick(floatingRootContext, {
1185
- enabled: !readOnly && !disabled && openOnInputClick,
1186
- event: 'mousedown-only',
1187
- toggle: false,
1188
- // Apply a small delay for touch to let iOS viewport centering settle.
1189
- // This avoids top-bottom flip flickers if the preferred position is "top" when first tapping.
1190
- touchOpenDelay: inputInsidePopup ? 0 : 50,
1191
- reason: clamp.inputPress
1192
- });
1193
- const dismiss = ScrollAreaViewport.useDismiss(floatingRootContext, {
1194
- enabled: !readOnly && !disabled && !inline,
1195
- outsidePressEvent: {
1196
- mouse: 'sloppy',
1197
- // The visual viewport (affected by the mobile software keyboard) can be
1198
- // somewhat small. The user may want to scroll the screen to see more of
1199
- // the popup.
1200
- touch: 'intentional'
1201
- },
1202
- // Without a popup, let the Escape key bubble the event up to other popups' handlers.
1203
- bubbles: inline ? true : undefined,
1204
- outsidePress(event) {
1205
- const target = clamp.getTarget(event);
1206
- return !clamp.contains(triggerElement, target) && !clamp.contains(clearRef.current, target) && !clamp.contains(chipsContainerRef.current, target) && !clamp.contains(inputGroupElement, target);
1207
- }
1208
- });
1209
- const listNavigation = MenuSubmenuTrigger.useListNavigation(floatingRootContext, {
1210
- enabled: !readOnly && !disabled,
1211
- id,
1212
- listRef,
1213
- activeIndex,
1214
- selectedIndex,
1215
- virtual: true,
1216
- loopFocus,
1217
- allowEscape: loopFocus && !autoHighlightMode,
1218
- focusItemOnOpen: queryChangedAfterOpen || selectionMode === 'none' && !autoHighlightMode ? false : 'auto',
1219
- focusItemOnHover: highlightItemOnHover,
1220
- resetOnPointerLeave: !keepHighlight,
1221
- // `cols` > 1 enables grid navigation.
1222
- // Since <Combobox.Row> infers column sizes (and is required when building a grid),
1223
- // it works correctly even with a value of `2`.
1224
- // Floating UI tests don't require `role="row"` wrappers, so retains the number API.
1225
- cols: grid ? 2 : 1,
1226
- orientation: grid ? 'horizontal' : undefined,
1227
- disabledIndices: useRenderElement.EMPTY_ARRAY,
1228
- onNavigate(nextActiveIndex, event) {
1229
- // Retain the highlight only while actually transitioning out or closed.
1230
- if (!event && !open || transitionStatus === 'ending') {
1231
- return;
1232
- }
1233
- if (!event) {
1234
- setIndices({
1235
- activeIndex: nextActiveIndex
1236
- });
1237
- } else {
1238
- setIndices({
1239
- activeIndex: nextActiveIndex,
1240
- type: keyboardActiveRef.current ? 'keyboard' : 'pointer'
1241
- });
1242
- }
1243
- }
1244
- });
1245
- const {
1246
- getReferenceProps,
1247
- getFloatingProps,
1248
- getItemProps
1249
- } = ScrollAreaViewport.useInteractions([role, click, dismiss, listNavigation]);
1250
- ScrollAreaViewport.useOnFirstRender(() => {
1251
- store.update({
1252
- inline: inlineProp,
1253
- popupProps: getFloatingProps(),
1254
- inputProps: getReferenceProps(),
1255
- triggerProps,
1256
- getItemProps,
1257
- setOpen,
1258
- setInputValue,
1259
- setSelectedValue,
1260
- setIndices,
1261
- onItemHighlighted,
1262
- handleSelection,
1263
- forceMount,
1264
- requestSubmit
1265
- });
1266
- });
1267
- clamp.useIsoLayoutEffect(() => {
1268
- store.update({
1269
- id,
1270
- selectedValue,
1271
- open,
1272
- mounted,
1273
- transitionStatus,
1274
- items,
1275
- inline: inlineProp,
1276
- popupProps: getFloatingProps(),
1277
- inputProps: getReferenceProps(),
1278
- triggerProps,
1279
- openMethod,
1280
- getItemProps,
1281
- selectionMode,
1282
- name,
1283
- disabled,
1284
- readOnly,
1285
- required,
1286
- grid,
1287
- isGrouped,
1288
- virtualized,
1289
- onOpenChangeComplete,
1290
- openOnInputClick,
1291
- itemToStringLabel,
1292
- modal,
1293
- autoHighlight: autoHighlightMode,
1294
- isItemEqualToValue,
1295
- submitOnItemClick,
1296
- hasInputValue,
1297
- requestSubmit
1298
- });
1299
- }, [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]);
1300
- const hiddenInputRef = useRenderElement.useMergedRefs(inputRefProp, validation.inputRef);
1301
- const itemsContextValue = React__namespace.useMemo(() => ({
1302
- query,
1303
- hasItems,
1304
- filteredItems,
1305
- flatFilteredItems
1306
- }), [query, hasItems, filteredItems, flatFilteredItems]);
1307
- const serializedValue = React__namespace.useMemo(() => {
1308
- if (Array.isArray(fieldRawValue)) {
1309
- return '';
1310
- }
1311
- return stringifyAsValue(fieldRawValue, itemToStringValue);
1312
- }, [fieldRawValue, itemToStringValue]);
1313
- const hasMultipleSelection = multiple && Array.isArray(selectedValue) && selectedValue.length > 0;
1314
- const hiddenInputName = multiple || selectionMode === 'none' ? undefined : name;
1315
- const hiddenInputs = React__namespace.useMemo(() => {
1316
- if (!multiple || !Array.isArray(selectedValue) || !name) {
1317
- return null;
1318
- }
1319
- return selectedValue.map(value => {
1320
- const currentSerializedValue = stringifyAsValue(value, itemToStringValue);
1321
- return /*#__PURE__*/jsxRuntime.jsx("input", {
1322
- type: "hidden",
1323
- name: name,
1324
- value: currentSerializedValue
1325
- }, currentSerializedValue);
1326
- });
1327
- }, [multiple, selectedValue, name, itemToStringValue]);
1328
- const children = /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
1329
- children: [props.children, /*#__PURE__*/jsxRuntime.jsx("input", {
1330
- ...validation.getInputValidationProps({
1331
- // Move focus when the hidden input is focused.
1332
- onFocus() {
1333
- if (inputInsidePopup) {
1334
- triggerElement?.focus();
1335
- return;
1336
- }
1337
- (inputRef.current || triggerElement)?.focus();
1338
- },
1339
- // Handle browser autofill.
1340
- onChange(event) {
1341
- // Workaround for https://github.com/facebook/react/issues/9023
1342
- if (event.nativeEvent.defaultPrevented) {
1343
- return;
1344
- }
1345
- const nextValue = event.target.value;
1346
- const details = clamp.createChangeEventDetails(clamp.none, event.nativeEvent);
1347
- function handleChange() {
1348
- // Browser autofill only writes a single scalar value.
1349
- if (multiple) {
1350
- return;
1351
- }
1352
- if (selectionMode === 'none') {
1353
- setDirty(nextValue !== validityData.initialValue);
1354
- setInputValue(nextValue, details);
1355
- if (shouldValidateOnChange()) {
1356
- validation.commit(nextValue);
1357
- }
1358
- return;
1359
- }
1360
- const matchingValue = valuesRef.current.find(v => {
1361
- const candidate = stringifyAsValue(v, itemToStringValue);
1362
- if (candidate.toLowerCase() === nextValue.toLowerCase()) {
1363
- return true;
1364
- }
1365
- return false;
1366
- });
1367
- if (matchingValue != null) {
1368
- setDirty(matchingValue !== validityData.initialValue);
1369
- setSelectedValue?.(matchingValue, details);
1370
- if (shouldValidateOnChange()) {
1371
- validation.commit(matchingValue);
1372
- }
1373
- }
1374
- }
1375
- if (items) {
1376
- handleChange();
1377
- } else {
1378
- forceMount();
1379
- queueMicrotask(handleChange);
1380
- }
1381
- }
1382
- }),
1383
- id: id && hiddenInputName == null ? `${id}-hidden-input` : undefined,
1384
- name: hiddenInputName,
1385
- autoComplete: formAutoComplete,
1386
- disabled: disabled,
1387
- required: required && !hasMultipleSelection,
1388
- readOnly: readOnly,
1389
- value: serializedValue,
1390
- ref: hiddenInputRef,
1391
- style: hiddenInputName ? clamp.visuallyHiddenInput : clamp.visuallyHidden,
1392
- tabIndex: -1,
1393
- "aria-hidden": true
1394
- }), hiddenInputs]
1395
- });
1396
- return /*#__PURE__*/jsxRuntime.jsx(ComboboxRootContext.Provider, {
1397
- value: store,
1398
- children: /*#__PURE__*/jsxRuntime.jsx(ComboboxFloatingContext.Provider, {
1399
- value: floatingRootContext,
1400
- children: /*#__PURE__*/jsxRuntime.jsx(ComboboxDerivedItemsContext.Provider, {
1401
- value: itemsContextValue,
1402
- children: /*#__PURE__*/jsxRuntime.jsx(ComboboxInputValueContext.Provider, {
1403
- value: inputValue,
1404
- children: children
1405
- })
1406
- })
1407
- })
1408
- });
1409
- }
1410
-
1411
- function AutocompleteRoot(props) {
1412
- const {
1413
- openOnInputClick = false,
1414
- value,
1415
- defaultValue,
1416
- onValueChange,
1417
- mode = 'list',
1418
- itemToStringValue,
1419
- ...other
1420
- } = props;
1421
- const enableInline = mode === 'inline' || mode === 'both';
1422
- const staticItems = mode === 'inline' || mode === 'none';
1423
-
1424
- // Mirror the typed value for uncontrolled usage so we can compose the temporary
1425
- // inline input value.
1426
- const isControlled = value !== undefined;
1427
- const [internalValue, setInternalValue] = React__namespace.useState(defaultValue ?? '');
1428
- const [inlineInputValue, setInlineInputValue] = React__namespace.useState('');
1429
- React__namespace.useEffect(() => {
1430
- if (isControlled) {
1431
- setInlineInputValue('');
1432
- }
1433
- }, [value, isControlled]);
1434
-
1435
- // Compose the input value shown to the user: inline value takes precedence when present.
1436
- let resolvedInputValue;
1437
- if (enableInline && inlineInputValue !== '') {
1438
- resolvedInputValue = inlineInputValue;
1439
- } else if (isControlled) {
1440
- resolvedInputValue = value ?? '';
1441
- } else {
1442
- resolvedInputValue = internalValue;
1443
- }
1444
- const handleValueChange = clamp.useStableCallback((nextValue, eventDetails) => {
1445
- setInlineInputValue('');
1446
- if (!isControlled) {
1447
- setInternalValue(nextValue);
1448
- }
1449
- onValueChange?.(nextValue, eventDetails);
1450
- });
1451
- const collator = useCoreFilter();
1452
- const baseFilter = React__namespace.useMemo(() => {
1453
- if (other.filter !== undefined) {
1454
- return other.filter;
1455
- }
1456
- return collator.contains;
1457
- }, [other.filter, collator]);
1458
- const resolvedQuery = String(isControlled ? value : internalValue).trim();
86
+ return collator.contains;
87
+ }, [other.filter, collator]);
88
+ const resolvedQuery = String(isControlled ? value : internalValue).trim();
1459
89
 
1460
90
  // In "both", wrap filtering to use only the typed value, ignoring the inline value.
1461
91
  const resolvedFilter = React__namespace.useMemo(() => {
1462
- if (mode !== 'both') {
1463
- return staticItems ? null : baseFilter;
1464
- }
1465
- if (baseFilter === null) {
1466
- return null;
1467
- }
1468
- return (item, _query, toString) => {
1469
- return baseFilter(item, resolvedQuery, toString);
1470
- };
1471
- }, [baseFilter, mode, resolvedQuery, staticItems]);
1472
- const handleItemHighlighted = clamp.useStableCallback((highlightedValue, eventDetails) => {
1473
- props.onItemHighlighted?.(highlightedValue, eventDetails);
1474
- if (eventDetails.reason === clamp.pointer) {
1475
- return;
1476
- }
1477
- if (enableInline) {
1478
- if (highlightedValue == null) {
1479
- setInlineInputValue('');
1480
- } else {
1481
- setInlineInputValue(stringifyAsLabel(highlightedValue, itemToStringValue));
1482
- }
1483
- } else {
1484
- setInlineInputValue('');
1485
- }
1486
- });
1487
- return /*#__PURE__*/jsxRuntime.jsx(AriaCombobox, {
1488
- ...other,
1489
- itemToStringLabel: itemToStringValue,
1490
- openOnInputClick: openOnInputClick,
1491
- selectionMode: "none",
1492
- fillInputOnItemPress: true,
1493
- filter: resolvedFilter,
1494
- autoComplete: mode,
1495
- inputValue: resolvedInputValue,
1496
- defaultInputValue: defaultValue,
1497
- onInputValueChange: handleValueChange,
1498
- onItemHighlighted: handleItemHighlighted
1499
- });
1500
- }
1501
-
1502
- function AutocompleteValue(props) {
1503
- const {
1504
- children
1505
- } = props;
1506
- const inputValue = useComboboxInputValueContext();
1507
- let returnValue = null;
1508
- if (typeof children === 'function') {
1509
- returnValue = children(String(inputValue));
1510
- } else if (children != null) {
1511
- returnValue = children;
1512
- } else {
1513
- returnValue = inputValue;
1514
- }
1515
- return /*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
1516
- children: returnValue
1517
- });
1518
- }
1519
-
1520
- const triggerStateAttributesMapping = {
1521
- ...ScrollAreaViewport.pressableTriggerOpenStateMapping,
1522
- ...NumberFieldInput.fieldValidityMapping,
1523
- popupSide: side => side ? {
1524
- 'data-popup-side': side
1525
- } : null,
1526
- listEmpty: empty => empty ? {
1527
- 'data-list-empty': ''
1528
- } : null
1529
- };
1530
-
1531
- function getDefaultLabelId(id) {
1532
- return id == null ? undefined : `${id}-label`;
1533
- }
1534
- function resolveAriaLabelledBy(fieldLabelId, localLabelId) {
1535
- return fieldLabelId ?? localLabelId;
1536
- }
1537
-
1538
- const BOUNDARY_OFFSET = 2;
1539
-
1540
- /**
1541
- * A button that opens the popup.
1542
- * Renders a `<button>` element.
1543
- */
1544
- const ComboboxTrigger = /*#__PURE__*/React__namespace.forwardRef(function ComboboxTrigger(componentProps, forwardedRef) {
1545
- const {
1546
- render,
1547
- className,
1548
- nativeButton = true,
1549
- disabled: disabledProp = false,
1550
- id: idProp,
1551
- ...elementProps
1552
- } = componentProps;
1553
- const {
1554
- state: fieldState,
1555
- disabled: fieldDisabled,
1556
- setTouched,
1557
- setFocused,
1558
- validationMode,
1559
- validation
1560
- } = NumberFieldInput.useFieldRootContext();
1561
- const {
1562
- labelId: fieldLabelId
1563
- } = NumberFieldInput.useLabelableContext();
1564
- const store = useComboboxRootContext();
1565
- const {
1566
- filteredItems
1567
- } = useComboboxDerivedItemsContext();
1568
- const selectionMode = ScrollAreaViewport.useStore(store, selectors.selectionMode);
1569
- const comboboxDisabled = ScrollAreaViewport.useStore(store, selectors.disabled);
1570
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
1571
- const required = ScrollAreaViewport.useStore(store, selectors.required);
1572
- const mounted = ScrollAreaViewport.useStore(store, selectors.mounted);
1573
- const popupSideValue = ScrollAreaViewport.useStore(store, selectors.popupSide);
1574
- const positionerElement = ScrollAreaViewport.useStore(store, selectors.positionerElement);
1575
- const listElement = ScrollAreaViewport.useStore(store, selectors.listElement);
1576
- const triggerProps = ScrollAreaViewport.useStore(store, selectors.triggerProps);
1577
- const triggerElement = ScrollAreaViewport.useStore(store, selectors.triggerElement);
1578
- const inputInsidePopup = ScrollAreaViewport.useStore(store, selectors.inputInsidePopup);
1579
- const rootId = ScrollAreaViewport.useStore(store, selectors.id);
1580
- const comboboxLabelId = ScrollAreaViewport.useStore(store, selectors.labelId);
1581
- const open = ScrollAreaViewport.useStore(store, selectors.open);
1582
- const selectedValue = ScrollAreaViewport.useStore(store, selectors.selectedValue);
1583
- const activeIndex = ScrollAreaViewport.useStore(store, selectors.activeIndex);
1584
- const selectedIndex = ScrollAreaViewport.useStore(store, selectors.selectedIndex);
1585
- const hasSelectedValue = ScrollAreaViewport.useStore(store, selectors.hasSelectedValue);
1586
- const floatingRootContext = useComboboxFloatingContext();
1587
- const inputValue = useComboboxInputValueContext();
1588
- const focusTimeout = clamp.useTimeout();
1589
- const disabled = fieldDisabled || comboboxDisabled || disabledProp;
1590
- const listEmpty = filteredItems.length === 0;
1591
- const popupSide = mounted && positionerElement ? popupSideValue : null;
1592
- NumberFieldInput.useLabelableId({
1593
- id: inputInsidePopup ? idProp : undefined
1594
- });
1595
- const id = inputInsidePopup ? idProp ?? rootId : idProp;
1596
- const ariaLabelledBy = resolveAriaLabelledBy(fieldLabelId, comboboxLabelId);
1597
- const currentPointerTypeRef = React__namespace.useRef('');
1598
- function trackPointerType(event) {
1599
- currentPointerTypeRef.current = event.pointerType;
1600
- }
1601
- const domReference = floatingRootContext.useState('domReferenceElement');
1602
-
1603
- // Update the floating root context to use the trigger element when it differs from the current reference.
1604
- // This ensures useClick and useTypeahead attach handlers to the correct element.
1605
- React__namespace.useEffect(() => {
1606
- if (!inputInsidePopup) {
1607
- return;
1608
- }
1609
- if (triggerElement && triggerElement !== domReference) {
1610
- floatingRootContext.set('domReferenceElement', triggerElement);
1611
- }
1612
- }, [triggerElement, domReference, floatingRootContext, inputInsidePopup]);
1613
- const {
1614
- reference: triggerTypeaheadProps
1615
- } = MenuSubmenuTrigger.useTypeahead(floatingRootContext, {
1616
- enabled: !open && !readOnly && !comboboxDisabled && selectionMode === 'single',
1617
- listRef: store.state.labelsRef,
1618
- activeIndex,
1619
- selectedIndex,
1620
- onMatch(index) {
1621
- const nextSelectedValue = store.state.valuesRef.current[index];
1622
- if (nextSelectedValue !== undefined) {
1623
- store.state.setSelectedValue(nextSelectedValue, clamp.createChangeEventDetails('none'));
1624
- }
1625
- }
1626
- });
1627
- const {
1628
- reference: triggerClickProps
1629
- } = ScrollAreaViewport.useClick(floatingRootContext, {
1630
- enabled: !readOnly && !comboboxDisabled,
1631
- event: 'mousedown'
1632
- });
1633
- const {
1634
- buttonRef,
1635
- getButtonProps
1636
- } = clamp.useButton({
1637
- native: nativeButton,
1638
- disabled
1639
- });
1640
- const state = {
1641
- ...fieldState,
1642
- open,
1643
- disabled,
1644
- popupSide,
1645
- listEmpty,
1646
- placeholder: !hasSelectedValue
1647
- };
1648
- const setTriggerElement = clamp.useStableCallback(element => {
1649
- store.set('triggerElement', element);
1650
- });
1651
- const element = useRenderElement.useRenderElement('button', componentProps, {
1652
- ref: [forwardedRef, buttonRef, setTriggerElement],
1653
- state,
1654
- props: [triggerProps, triggerClickProps, triggerTypeaheadProps, {
1655
- id,
1656
- tabIndex: inputInsidePopup ? 0 : -1,
1657
- role: inputInsidePopup ? 'combobox' : undefined,
1658
- 'aria-expanded': open ? 'true' : 'false',
1659
- 'aria-haspopup': inputInsidePopup ? 'dialog' : 'listbox',
1660
- 'aria-controls': open ? listElement?.id : undefined,
1661
- 'aria-required': inputInsidePopup ? required || undefined : undefined,
1662
- 'aria-labelledby': ariaLabelledBy,
1663
- onPointerDown: trackPointerType,
1664
- onPointerEnter: trackPointerType,
1665
- onFocus() {
1666
- setFocused(true);
1667
- if (disabled || readOnly) {
1668
- return;
1669
- }
1670
- focusTimeout.start(0, store.state.forceMount);
1671
- },
1672
- onBlur(event) {
1673
- // If focus is moving into the popup, don't count it as a blur.
1674
- if (clamp.contains(positionerElement, event.relatedTarget)) {
1675
- return;
1676
- }
1677
- setTouched(true);
1678
- setFocused(false);
1679
- if (validationMode === 'onBlur') {
1680
- const valueToValidate = selectionMode === 'none' ? inputValue : selectedValue;
1681
- validation.commit(valueToValidate);
1682
- }
1683
- },
1684
- onMouseDown(event) {
1685
- if (disabled || readOnly) {
1686
- return;
1687
- }
1688
- if (!inputInsidePopup) {
1689
- floatingRootContext.set('domReferenceElement', event.currentTarget);
1690
- }
1691
-
1692
- // Ensure items are registered for initial selection highlight.
1693
- store.state.forceMount();
1694
- if (currentPointerTypeRef.current !== 'touch') {
1695
- store.state.inputRef.current?.focus();
1696
- if (!inputInsidePopup) {
1697
- event.preventDefault();
1698
- }
1699
- }
1700
- if (open) {
1701
- return;
1702
- }
1703
- const doc = clamp.ownerDocument(event.currentTarget);
1704
- function handleMouseUp(mouseEvent) {
1705
- if (!triggerElement) {
1706
- return;
1707
- }
1708
- const mouseUpTarget = clamp.getTarget(mouseEvent);
1709
- const positioner = store.state.positionerElement;
1710
- const list = store.state.listElement;
1711
- if (clamp.contains(triggerElement, mouseUpTarget) || clamp.contains(positioner, mouseUpTarget) || clamp.contains(list, mouseUpTarget) || mouseUpTarget === triggerElement) {
1712
- return;
1713
- }
1714
- const bounds = MenuSubmenuTrigger.getPseudoElementBounds(triggerElement);
1715
- const withinHorizontal = mouseEvent.clientX >= bounds.left - BOUNDARY_OFFSET && mouseEvent.clientX <= bounds.right + BOUNDARY_OFFSET;
1716
- const withinVertical = mouseEvent.clientY >= bounds.top - BOUNDARY_OFFSET && mouseEvent.clientY <= bounds.bottom + BOUNDARY_OFFSET;
1717
- if (withinHorizontal && withinVertical) {
1718
- return;
1719
- }
1720
- store.state.setOpen(false, clamp.createChangeEventDetails('cancel-open', mouseEvent));
1721
- }
1722
- if (inputInsidePopup) {
1723
- doc.addEventListener('mouseup', handleMouseUp, {
1724
- once: true
1725
- });
1726
- }
1727
- },
1728
- onKeyDown(event) {
1729
- if (disabled || readOnly) {
1730
- return;
1731
- }
1732
- if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
1733
- clamp.stopEvent(event);
1734
- store.state.setOpen(true, clamp.createChangeEventDetails(clamp.listNavigation, event.nativeEvent));
1735
- store.state.inputRef.current?.focus();
1736
- }
1737
- }
1738
- }, validation ? validation.getValidationProps(elementProps) : elementProps, getButtonProps],
1739
- stateAttributesMapping: triggerStateAttributesMapping
1740
- });
1741
- return element;
1742
- });
1743
- if (process.env.NODE_ENV !== "production") ComboboxTrigger.displayName = "ComboboxTrigger";
1744
-
1745
- const ComboboxChipsContext = /*#__PURE__*/React__namespace.createContext(undefined);
1746
- if (process.env.NODE_ENV !== "production") ComboboxChipsContext.displayName = "ComboboxChipsContext";
1747
- function useComboboxChipsContext() {
1748
- return React__namespace.useContext(ComboboxChipsContext);
1749
- }
1750
-
1751
- const ComboboxPositionerContext = /*#__PURE__*/React__namespace.createContext(undefined);
1752
- if (process.env.NODE_ENV !== "production") ComboboxPositionerContext.displayName = "ComboboxPositionerContext";
1753
- function useComboboxPositionerContext(optional) {
1754
- const context = React__namespace.useContext(ComboboxPositionerContext);
1755
- if (context === undefined && !optional) {
1756
- 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));
1757
- }
1758
- return context;
1759
- }
1760
-
1761
- /**
1762
- * @internal
1763
- */
1764
- const ComboboxInternalDismissButton = /*#__PURE__*/React__namespace.forwardRef(function ComboboxInternalDismissButton(_, forwardedRef) {
1765
- const store = useComboboxRootContext();
1766
- const {
1767
- buttonRef,
1768
- getButtonProps
1769
- } = clamp.useButton({
1770
- native: false
1771
- });
1772
- const mergedRef = useRenderElement.useMergedRefs(forwardedRef, buttonRef);
1773
- const handleDismiss = clamp.useStableCallback(event => {
1774
- store.state.setOpen(false, clamp.createChangeEventDetails(clamp.closePress, event.nativeEvent, event.currentTarget));
1775
- });
1776
- const dismissProps = getButtonProps({
1777
- onClick: handleDismiss
1778
- });
1779
- return /*#__PURE__*/jsxRuntime.jsx("span", {
1780
- ref: mergedRef,
1781
- ...dismissProps,
1782
- "aria-label": "Dismiss",
1783
- tabIndex: undefined,
1784
- style: clamp.visuallyHiddenInput
1785
- });
1786
- });
1787
- if (process.env.NODE_ENV !== "production") ComboboxInternalDismissButton.displayName = "ComboboxInternalDismissButton";
1788
-
1789
- const ComboboxInput = /*#__PURE__*/React__namespace.forwardRef(function ComboboxInput(componentProps, forwardedRef) {
1790
- const {
1791
- render,
1792
- className,
1793
- disabled: disabledProp = false,
1794
- id: idProp,
1795
- ...elementProps
1796
- } = componentProps;
1797
- const {
1798
- state: fieldState,
1799
- disabled: fieldDisabled,
1800
- setTouched,
1801
- setFocused,
1802
- validationMode,
1803
- validation
1804
- } = NumberFieldInput.useFieldRootContext();
1805
- const {
1806
- labelId: fieldLabelId
1807
- } = NumberFieldInput.useLabelableContext();
1808
- const comboboxChipsContext = useComboboxChipsContext();
1809
- const positioning = useComboboxPositionerContext(true);
1810
- const hasPositionerParent = Boolean(positioning);
1811
- const store = useComboboxRootContext();
1812
- const {
1813
- filteredItems
1814
- } = useComboboxDerivedItemsContext();
1815
- // `inputValue` can't be placed in the store.
1816
- // https://github.com/mui/base-ui/issues/2703
1817
- const inputValue = useComboboxInputValueContext();
1818
- const direction = ScrollAreaViewport.useDirection();
1819
- const required = ScrollAreaViewport.useStore(store, selectors.required);
1820
- const comboboxDisabled = ScrollAreaViewport.useStore(store, selectors.disabled);
1821
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
1822
- const name = ScrollAreaViewport.useStore(store, selectors.name);
1823
- const selectionMode = ScrollAreaViewport.useStore(store, selectors.selectionMode);
1824
- const autoHighlightMode = ScrollAreaViewport.useStore(store, selectors.autoHighlight);
1825
- const inputProps = ScrollAreaViewport.useStore(store, selectors.inputProps);
1826
- const triggerProps = ScrollAreaViewport.useStore(store, selectors.triggerProps);
1827
- const open = ScrollAreaViewport.useStore(store, selectors.open);
1828
- const mounted = ScrollAreaViewport.useStore(store, selectors.mounted);
1829
- const selectedValue = ScrollAreaViewport.useStore(store, selectors.selectedValue);
1830
- const popupSideValue = ScrollAreaViewport.useStore(store, selectors.popupSide);
1831
- const positionerElement = ScrollAreaViewport.useStore(store, selectors.positionerElement);
1832
- const rootId = ScrollAreaViewport.useStore(store, selectors.id);
1833
- const inline = ScrollAreaViewport.useStore(store, selectors.inline);
1834
- const modal = ScrollAreaViewport.useStore(store, selectors.modal);
1835
- const autoHighlightEnabled = Boolean(autoHighlightMode);
1836
- const popupSide = mounted && positionerElement ? popupSideValue : null;
1837
- const disabled = fieldDisabled || comboboxDisabled || disabledProp;
1838
- const listEmpty = filteredItems.length === 0;
1839
- const isInsidePopup = hasPositionerParent || inline;
1840
- const focusManagerModal = !isInsidePopup || modal;
1841
- const id = clamp.useBaseUiId(idProp ?? (!isInsidePopup ? rootId : undefined));
1842
- const ariaLabelledBy = resolveAriaLabelledBy(fieldLabelId, undefined);
1843
- const fieldStateForInput = hasPositionerParent ? NumberFieldInput.DEFAULT_FIELD_STATE_ATTRIBUTES : fieldState;
1844
- const [composingValue, setComposingValue] = React__namespace.useState(null);
1845
- const isComposingRef = React__namespace.useRef(false);
1846
- const lastActiveIndexRef = React__namespace.useRef(null);
1847
- const shouldRestoreActiveIndexRef = React__namespace.useRef(false);
1848
- const setInputElement = clamp.useStableCallback(element => {
1849
- const nextIsInsidePopup = hasPositionerParent || store.state.inline;
1850
- if (nextIsInsidePopup && !store.state.hasInputValue) {
1851
- store.state.setInputValue('', clamp.createChangeEventDetails(clamp.none));
1852
- }
1853
- store.update({
1854
- inputElement: element,
1855
- inputInsidePopup: nextIsInsidePopup
1856
- });
1857
- });
1858
- const validationProps = hasPositionerParent || !validation ? elementProps : validation.getValidationProps(elementProps);
1859
- const state = {
1860
- ...fieldStateForInput,
1861
- open,
1862
- disabled,
1863
- readOnly,
1864
- popupSide,
1865
- listEmpty
1866
- };
1867
- function handleKeyDown(event) {
1868
- if (!comboboxChipsContext) {
1869
- return undefined;
1870
- }
1871
- let nextIndex;
1872
- const {
1873
- highlightedChipIndex
1874
- } = comboboxChipsContext;
1875
- if (highlightedChipIndex !== undefined) {
1876
- if (event.key === 'ArrowLeft') {
1877
- event.preventDefault();
1878
- if (highlightedChipIndex > 0) {
1879
- nextIndex = highlightedChipIndex - 1;
1880
- } else {
1881
- nextIndex = undefined;
1882
- }
1883
- } else if (event.key === 'ArrowRight') {
1884
- event.preventDefault();
1885
- if (highlightedChipIndex < selectedValue.length - 1) {
1886
- nextIndex = highlightedChipIndex + 1;
1887
- } else {
1888
- nextIndex = undefined;
1889
- }
1890
- } else if (event.key === 'Backspace' || event.key === 'Delete') {
1891
- event.preventDefault();
1892
- // Move highlight appropriately after removal.
1893
- const computedNextIndex = highlightedChipIndex >= selectedValue.length - 1 ? selectedValue.length - 2 : highlightedChipIndex;
1894
- // If the computed index is negative, treat it as no highlight.
1895
- nextIndex = computedNextIndex >= 0 ? computedNextIndex : undefined;
1896
- store.state.setIndices({
1897
- activeIndex: null,
1898
- selectedIndex: null,
1899
- type: 'keyboard'
1900
- });
1901
- }
1902
- return nextIndex;
1903
- }
1904
-
1905
- // Handle navigation when no chip is highlighted
1906
- if (event.key === 'ArrowLeft' && (event.currentTarget.selectionStart ?? 0) === 0 && selectedValue.length > 0) {
1907
- event.preventDefault();
1908
- const lastChipIndex = Math.max(selectedValue.length - 1, 0);
1909
- nextIndex = lastChipIndex;
1910
- } else if (event.key === 'Backspace' && event.currentTarget.value === '' && selectedValue.length > 0) {
1911
- store.state.setIndices({
1912
- activeIndex: null,
1913
- selectedIndex: null,
1914
- type: 'keyboard'
1915
- });
1916
- event.preventDefault();
1917
- }
1918
- return nextIndex;
1919
- }
1920
- const element = useRenderElement.useRenderElement('input', componentProps, {
1921
- state,
1922
- ref: [forwardedRef, store.state.inputRef, setInputElement],
1923
- props: [inputProps, triggerProps, {
1924
- type: 'text',
1925
- value: componentProps.value ?? composingValue ?? inputValue,
1926
- 'aria-readonly': readOnly || undefined,
1927
- 'aria-required': required || undefined,
1928
- 'aria-labelledby': ariaLabelledBy,
1929
- disabled,
1930
- readOnly,
1931
- required: selectionMode === 'none' ? required : undefined,
1932
- ...(selectionMode === 'none' && name && {
1933
- name
1934
- }),
1935
- id,
1936
- onFocus() {
1937
- setFocused(true);
1938
- if (!inline || !shouldRestoreActiveIndexRef.current) {
1939
- return;
1940
- }
1941
- shouldRestoreActiveIndexRef.current = false;
1942
- const nextActiveIndex = lastActiveIndexRef.current;
1943
- if (nextActiveIndex == null ||
1944
- // `valuesRef` can be sparse, so guard against restoring a removed slot.
1945
- !Object.hasOwn(store.state.valuesRef.current, nextActiveIndex)) {
1946
- return;
1947
- }
1948
- store.state.setIndices({
1949
- activeIndex: nextActiveIndex
1950
- });
1951
- },
1952
- onBlur() {
1953
- setTouched(true);
1954
- setFocused(false);
1955
- const activeIndex = store.state.activeIndex;
1956
- if (inline && activeIndex !== null && autoHighlightMode !== 'always') {
1957
- lastActiveIndexRef.current = activeIndex;
1958
- shouldRestoreActiveIndexRef.current = true;
1959
- store.state.setIndices({
1960
- activeIndex: null
1961
- });
1962
- }
1963
- if (validationMode === 'onBlur') {
1964
- const valueToValidate = selectionMode === 'none' ? inputValue : selectedValue;
1965
- validation.commit(valueToValidate);
1966
- }
1967
- },
1968
- onCompositionStart(event) {
1969
- if (clamp.isAndroid) {
1970
- return;
1971
- }
1972
- isComposingRef.current = true;
1973
- setComposingValue(event.currentTarget.value);
1974
- },
1975
- onCompositionEnd(event) {
1976
- isComposingRef.current = false;
1977
- const next = event.currentTarget.value;
1978
- setComposingValue(null);
1979
- store.state.setInputValue(next, clamp.createChangeEventDetails(clamp.inputChange, event.nativeEvent));
1980
- },
1981
- onChange(event) {
1982
- // Autofill may not provide `inputType` (Chrome) or may report
1983
- // `insertReplacementText` (Firefox).
1984
- const inputType = event.nativeEvent.inputType;
1985
- const autofillLikeInput = !inputType || inputType === 'insertReplacementText';
1986
- const shouldOpenOnInput = isComposingRef.current || !autofillLikeInput;
1987
-
1988
- // During IME composition, avoid propagating controlled updates to prevent
1989
- // filtering the options prematurely so `Empty` won't show incorrectly.
1990
- // We can't rely on this check for Android due to how it handles composition
1991
- // events with some keyboards (e.g. Samsung keyboard with predictive text on
1992
- // treats all text as always-composing).
1993
- // https://github.com/mui/base-ui/issues/2942
1994
- if (isComposingRef.current) {
1995
- const nextVal = event.currentTarget.value;
1996
- setComposingValue(nextVal);
1997
- if (nextVal === '' && !store.state.openOnInputClick && !store.state.inputInsidePopup) {
1998
- store.state.setOpen(false, clamp.createChangeEventDetails(clamp.inputClear, event.nativeEvent));
1999
- }
2000
- const trimmed = nextVal.trim();
2001
- const shouldMaintainHighlight = autoHighlightEnabled && trimmed !== '';
2002
- if (!readOnly && !disabled && trimmed) {
2003
- if (shouldOpenOnInput) {
2004
- store.state.setOpen(true, clamp.createChangeEventDetails(clamp.inputChange, event.nativeEvent));
2005
- if (!autoHighlightEnabled) {
2006
- store.state.setIndices({
2007
- activeIndex: null,
2008
- selectedIndex: null,
2009
- type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
2010
- });
2011
- }
2012
- }
2013
- }
2014
- if (open && store.state.activeIndex !== null && !shouldMaintainHighlight) {
2015
- store.state.setIndices({
2016
- activeIndex: null,
2017
- selectedIndex: null,
2018
- type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
2019
- });
2020
- }
2021
- return;
2022
- }
2023
- store.state.setInputValue(event.currentTarget.value, clamp.createChangeEventDetails(clamp.inputChange, event.nativeEvent));
2024
- const empty = event.currentTarget.value === '';
2025
- const clearDetails = clamp.createChangeEventDetails(clamp.inputClear, event.nativeEvent);
2026
- if (empty && !store.state.inputInsidePopup) {
2027
- if (selectionMode === 'single') {
2028
- store.state.setSelectedValue(null, clearDetails);
2029
- }
2030
- if (!store.state.openOnInputClick) {
2031
- store.state.setOpen(false, clearDetails);
2032
- }
2033
- }
2034
- const trimmed = event.currentTarget.value.trim();
2035
- if (!readOnly && !disabled && trimmed) {
2036
- if (shouldOpenOnInput) {
2037
- store.state.setOpen(true, clamp.createChangeEventDetails(clamp.inputChange, event.nativeEvent));
2038
- // When autoHighlight is enabled, keep the highlight (will be set to 0 in root).
2039
- if (!autoHighlightEnabled) {
2040
- store.state.setIndices({
2041
- activeIndex: null,
2042
- selectedIndex: null,
2043
- type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
2044
- });
2045
- }
2046
- }
2047
- }
2048
-
2049
- // When the user types, ensure the list resets its highlight so that
2050
- // virtual focus returns to the input (aria-activedescendant is
2051
- // cleared).
2052
- if (open && store.state.activeIndex !== null && !autoHighlightEnabled) {
2053
- store.state.setIndices({
2054
- activeIndex: null,
2055
- selectedIndex: null,
2056
- type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
2057
- });
2058
- }
2059
- },
2060
- onKeyDown(event) {
2061
- if (disabled || readOnly) {
2062
- return;
2063
- }
2064
- if (event.ctrlKey || event.shiftKey || event.altKey || event.metaKey) {
2065
- return;
2066
- }
2067
- store.state.keyboardActiveRef.current = true;
2068
- const input = event.currentTarget;
2069
- const scrollAmount = input.scrollWidth - input.clientWidth;
2070
- const isRTL = direction === 'rtl';
2071
- if (event.key === 'Home') {
2072
- clamp.stopEvent(event);
2073
- const cursor = clamp.isFirefox && isRTL ? input.value.length : 0;
2074
- input.setSelectionRange(cursor, cursor);
2075
- input.scrollLeft = 0;
2076
- return;
2077
- }
2078
- if (event.key === 'End') {
2079
- clamp.stopEvent(event);
2080
- const cursor = clamp.isFirefox && isRTL ? 0 : input.value.length;
2081
- input.setSelectionRange(cursor, cursor);
2082
- input.scrollLeft = isRTL ? -scrollAmount : scrollAmount;
2083
- return;
2084
- }
2085
- if (!mounted && event.key === 'Escape') {
2086
- const isClear = selectionMode === 'multiple' && Array.isArray(selectedValue) ? selectedValue.length === 0 : selectedValue === null;
2087
- const details = clamp.createChangeEventDetails(clamp.escapeKey, event.nativeEvent);
2088
- const value = selectionMode === 'multiple' ? [] : null;
2089
- store.state.setInputValue('', details);
2090
- store.state.setSelectedValue(value, details);
2091
- if (!isClear && !store.state.inline && !details.isPropagationAllowed) {
2092
- event.stopPropagation();
2093
- }
2094
- return;
2095
- }
2096
-
2097
- // Handle deletion when no chip is highlighted and the input is empty.
2098
- if (comboboxChipsContext && event.key === 'Backspace' && input.value === '' && comboboxChipsContext.highlightedChipIndex === undefined && Array.isArray(selectedValue) && selectedValue.length > 0) {
2099
- const newValue = selectedValue.slice(0, -1);
2100
- // If the removed item was also the active (highlighted) item, clear highlight
2101
- store.state.setIndices({
2102
- activeIndex: null,
2103
- selectedIndex: null,
2104
- type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
2105
- });
2106
- store.state.setSelectedValue(newValue, clamp.createChangeEventDetails(clamp.none, event.nativeEvent));
2107
- return;
2108
- }
2109
- const hadHighlightedChip = comboboxChipsContext?.highlightedChipIndex !== undefined;
2110
- const nextIndex = handleKeyDown(event);
2111
- comboboxChipsContext?.setHighlightedChipIndex(nextIndex);
2112
- if (nextIndex !== undefined) {
2113
- comboboxChipsContext?.chipsRef.current[nextIndex]?.focus();
2114
- } else if (hadHighlightedChip) {
2115
- store.state.inputRef.current?.focus();
2116
- }
2117
-
2118
- // event.isComposing
2119
- if (event.which === 229) {
2120
- return;
2121
- }
2122
- if (event.key === 'Enter' && open) {
2123
- const activeIndex = store.state.activeIndex;
2124
- const nativeEvent = event.nativeEvent;
2125
- if (activeIndex === null) {
2126
- if (inline) {
2127
- return;
2128
- }
2129
-
2130
- // Allow form submission when no item is highlighted.
2131
- store.state.setOpen(false, clamp.createChangeEventDetails(clamp.none, nativeEvent));
2132
- return;
2133
- }
2134
- clamp.stopEvent(event);
2135
- const listItem = store.state.listRef.current[activeIndex];
2136
- if (listItem) {
2137
- store.state.selectionEventRef.current = nativeEvent;
2138
- listItem.click();
2139
- store.state.selectionEventRef.current = null;
2140
- }
2141
- }
2142
- },
2143
- onPointerMove() {
2144
- store.state.keyboardActiveRef.current = false;
2145
- },
2146
- onPointerDown() {
2147
- store.state.keyboardActiveRef.current = false;
2148
- }
2149
- }, validationProps],
2150
- stateAttributesMapping: triggerStateAttributesMapping
2151
- });
2152
- return /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
2153
- children: [open && focusManagerModal && /*#__PURE__*/jsxRuntime.jsx(ComboboxInternalDismissButton, {
2154
- ref: store.state.startDismissRef
2155
- }), element]
2156
- });
2157
- });
2158
- if (process.env.NODE_ENV !== "production") ComboboxInput.displayName = "ComboboxInput";
2159
-
2160
- /**
2161
- * A wrapper for the input and its associated controls.
2162
- * Renders a `<div>` element.
2163
- */
2164
- const ComboboxInputGroup = /*#__PURE__*/React__namespace.forwardRef(function ComboboxInputGroup(componentProps, forwardedRef) {
2165
- const {
2166
- render,
2167
- className,
2168
- ...elementProps
2169
- } = componentProps;
2170
- const {
2171
- state: fieldState,
2172
- disabled: fieldDisabled
2173
- } = NumberFieldInput.useFieldRootContext();
2174
- const store = useComboboxRootContext();
2175
- const {
2176
- filteredItems
2177
- } = useComboboxDerivedItemsContext();
2178
- const open = ScrollAreaViewport.useStore(store, selectors.open);
2179
- const mounted = ScrollAreaViewport.useStore(store, selectors.mounted);
2180
- const popupSideValue = ScrollAreaViewport.useStore(store, selectors.popupSide);
2181
- const positionerElement = ScrollAreaViewport.useStore(store, selectors.positionerElement);
2182
- const comboboxDisabled = ScrollAreaViewport.useStore(store, selectors.disabled);
2183
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
2184
- const hasSelectedValue = ScrollAreaViewport.useStore(store, selectors.hasSelectedValue);
2185
- const selectionMode = ScrollAreaViewport.useStore(store, selectors.selectionMode);
2186
- const popupSide = mounted && positionerElement ? popupSideValue : null;
2187
- const disabled = fieldDisabled || comboboxDisabled;
2188
- const listEmpty = filteredItems.length === 0;
2189
- const placeholder = selectionMode === 'none' ? false : !hasSelectedValue;
2190
- const state = {
2191
- ...fieldState,
2192
- open,
2193
- disabled,
2194
- readOnly,
2195
- popupSide,
2196
- listEmpty,
2197
- placeholder
2198
- };
2199
- const setInputGroupElement = clamp.useStableCallback(element => {
2200
- store.set('inputGroupElement', element);
2201
- });
2202
- return useRenderElement.useRenderElement('div', componentProps, {
2203
- ref: [forwardedRef, setInputGroupElement],
2204
- props: [{
2205
- role: 'group'
2206
- }, elementProps],
2207
- state,
2208
- stateAttributesMapping: triggerStateAttributesMapping
2209
- });
2210
- });
2211
- if (process.env.NODE_ENV !== "production") ComboboxInputGroup.displayName = "ComboboxInputGroup";
2212
-
2213
- /**
2214
- * An icon that indicates that the trigger button opens the popup.
2215
- * Renders a `<span>` element.
2216
- */
2217
- const ComboboxIcon = /*#__PURE__*/React__namespace.forwardRef(function ComboboxIcon(componentProps, forwardedRef) {
2218
- const {
2219
- className,
2220
- render,
2221
- ...elementProps
2222
- } = componentProps;
2223
- const element = useRenderElement.useRenderElement('span', componentProps, {
2224
- ref: forwardedRef,
2225
- props: [{
2226
- 'aria-hidden': true,
2227
- children: '▼'
2228
- }, elementProps]
2229
- });
2230
- return element;
2231
- });
2232
- if (process.env.NODE_ENV !== "production") ComboboxIcon.displayName = "ComboboxIcon";
2233
-
2234
- const stateAttributesMapping$8 = {
2235
- ...clamp.transitionStatusMapping,
2236
- ...ScrollAreaViewport.triggerOpenStateMapping
2237
- };
2238
-
2239
- /**
2240
- * Clears the value when clicked.
2241
- * Renders a `<button>` element.
2242
- */
2243
- const ComboboxClear = /*#__PURE__*/React__namespace.forwardRef(function ComboboxClear(componentProps, forwardedRef) {
2244
- const {
2245
- render,
2246
- className,
2247
- disabled: disabledProp = false,
2248
- nativeButton = true,
2249
- keepMounted = false,
2250
- ...elementProps
2251
- } = componentProps;
2252
- const {
2253
- disabled: fieldDisabled
2254
- } = NumberFieldInput.useFieldRootContext();
2255
- const store = useComboboxRootContext();
2256
- const selectionMode = ScrollAreaViewport.useStore(store, selectors.selectionMode);
2257
- const comboboxDisabled = ScrollAreaViewport.useStore(store, selectors.disabled);
2258
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
2259
- const open = ScrollAreaViewport.useStore(store, selectors.open);
2260
- const selectedValue = ScrollAreaViewport.useStore(store, selectors.selectedValue);
2261
- const hasSelectionChips = ScrollAreaViewport.useStore(store, selectors.hasSelectionChips);
2262
- const inputValue = useComboboxInputValueContext();
2263
- let visible = false;
2264
- if (selectionMode === 'none') {
2265
- visible = inputValue !== '';
2266
- } else if (selectionMode === 'single') {
2267
- visible = selectedValue != null;
2268
- } else {
2269
- visible = hasSelectionChips;
2270
- }
2271
- const disabled = fieldDisabled || comboboxDisabled || disabledProp;
2272
- const {
2273
- buttonRef,
2274
- getButtonProps
2275
- } = clamp.useButton({
2276
- native: nativeButton,
2277
- disabled
2278
- });
2279
- const {
2280
- mounted,
2281
- transitionStatus,
2282
- setMounted
2283
- } = clamp.useTransitionStatus(visible);
2284
- const state = {
2285
- disabled,
2286
- open,
2287
- transitionStatus
2288
- };
2289
- clamp.useOpenChangeComplete({
2290
- open: visible,
2291
- ref: store.state.clearRef,
2292
- onComplete() {
2293
- if (!visible) {
2294
- setMounted(false);
2295
- }
2296
- }
2297
- });
2298
- const element = useRenderElement.useRenderElement('button', componentProps, {
2299
- state,
2300
- ref: [forwardedRef, buttonRef, store.state.clearRef],
2301
- props: [{
2302
- tabIndex: -1,
2303
- children: 'x',
2304
- // Avoid stealing focus from the input.
2305
- onMouseDown(event) {
2306
- event.preventDefault();
2307
- },
2308
- onClick(event) {
2309
- if (disabled || readOnly) {
2310
- return;
2311
- }
2312
- const keyboardActiveRef = store.state.keyboardActiveRef;
2313
- store.state.setInputValue('', clamp.createChangeEventDetails(clamp.clearPress, event.nativeEvent));
2314
- if (selectionMode !== 'none') {
2315
- store.state.setSelectedValue(Array.isArray(selectedValue) ? [] : null, clamp.createChangeEventDetails(clamp.clearPress, event.nativeEvent));
2316
- store.state.setIndices({
2317
- activeIndex: null,
2318
- selectedIndex: null,
2319
- type: keyboardActiveRef.current ? 'keyboard' : 'pointer'
2320
- });
2321
- } else {
2322
- store.state.setIndices({
2323
- activeIndex: null,
2324
- type: keyboardActiveRef.current ? 'keyboard' : 'pointer'
2325
- });
2326
- }
2327
- store.state.inputRef.current?.focus();
2328
- }
2329
- }, elementProps, getButtonProps],
2330
- stateAttributesMapping: stateAttributesMapping$8
2331
- });
2332
- const shouldRender = keepMounted || mounted;
2333
- if (!shouldRender) {
2334
- return null;
2335
- }
2336
- return element;
2337
- });
2338
- if (process.env.NODE_ENV !== "production") ComboboxClear.displayName = "ComboboxClear";
2339
-
2340
- const GroupCollectionContext = /*#__PURE__*/React__namespace.createContext(null);
2341
- if (process.env.NODE_ENV !== "production") GroupCollectionContext.displayName = "GroupCollectionContext";
2342
- function useGroupCollectionContext() {
2343
- return React__namespace.useContext(GroupCollectionContext);
2344
- }
2345
- function GroupCollectionProvider(props) {
2346
- const {
2347
- children,
2348
- items
2349
- } = props;
2350
- const contextValue = React__namespace.useMemo(() => ({
2351
- items
2352
- }), [items]);
2353
- return /*#__PURE__*/jsxRuntime.jsx(GroupCollectionContext.Provider, {
2354
- value: contextValue,
2355
- children: children
2356
- });
2357
- }
2358
-
2359
- function ComboboxCollection(props) {
2360
- const {
2361
- children
2362
- } = props;
2363
- const {
2364
- filteredItems
2365
- } = useComboboxDerivedItemsContext();
2366
- const groupContext = useGroupCollectionContext();
2367
- const itemsToRender = groupContext ? groupContext.items : filteredItems;
2368
- if (!itemsToRender) {
2369
- return null;
2370
- }
2371
- return /*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
2372
- children: itemsToRender.map(children)
2373
- });
2374
- }
2375
-
2376
- const ComboboxList = /*#__PURE__*/React__namespace.forwardRef(function ComboboxList(componentProps, forwardedRef) {
2377
- var _ComboboxCollection;
2378
- const {
2379
- render,
2380
- className,
2381
- children,
2382
- ...elementProps
2383
- } = componentProps;
2384
- const store = useComboboxRootContext();
2385
- const floatingRootContext = useComboboxFloatingContext();
2386
- const hasPositionerContext = Boolean(useComboboxPositionerContext(true));
2387
- const {
2388
- filteredItems
2389
- } = useComboboxDerivedItemsContext();
2390
- const items = ScrollAreaViewport.useStore(store, selectors.items);
2391
- const labelsRef = ScrollAreaViewport.useStore(store, selectors.labelsRef);
2392
- const listRef = ScrollAreaViewport.useStore(store, selectors.listRef);
2393
- const selectionMode = ScrollAreaViewport.useStore(store, selectors.selectionMode);
2394
- const grid = ScrollAreaViewport.useStore(store, selectors.grid);
2395
- const popupProps = ScrollAreaViewport.useStore(store, selectors.popupProps);
2396
- const disabled = ScrollAreaViewport.useStore(store, selectors.disabled);
2397
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
2398
- const virtualized = ScrollAreaViewport.useStore(store, selectors.virtualized);
2399
- const multiple = selectionMode === 'multiple';
2400
- const empty = filteredItems.length === 0;
2401
- const setPositionerElement = clamp.useStableCallback(element => {
2402
- store.set('positionerElement', element);
2403
- });
2404
- const setListElement = clamp.useStableCallback(element => {
2405
- store.set('listElement', element);
2406
- });
2407
-
2408
- // Support "closed template" API: if children is a function, implicitly wrap it
2409
- // with a Combobox.Collection that reads items from context/root.
2410
- // Ensures this component's `popupProps` subscription does not cause <Combobox.Item>
2411
- // to re-render on every active index change.
2412
- const resolvedChildren = React__namespace.useMemo(() => {
2413
- if (typeof children === 'function') {
2414
- return _ComboboxCollection || (_ComboboxCollection = /*#__PURE__*/jsxRuntime.jsx(ComboboxCollection, {
2415
- children: children
2416
- }));
2417
- }
2418
- return children;
2419
- }, [children]);
2420
- const state = {
2421
- empty
2422
- };
2423
- const floatingId = floatingRootContext.useState('floatingId');
2424
- const element = useRenderElement.useRenderElement('div', componentProps, {
2425
- state,
2426
- ref: [forwardedRef, setListElement, hasPositionerContext ? null : setPositionerElement],
2427
- props: [popupProps, {
2428
- children: resolvedChildren,
2429
- tabIndex: -1,
2430
- id: floatingId,
2431
- role: grid ? 'grid' : 'listbox',
2432
- 'aria-multiselectable': multiple ? 'true' : undefined,
2433
- onKeyDown(event) {
2434
- if (disabled || readOnly) {
2435
- return;
2436
- }
2437
- if (event.key === 'Enter') {
2438
- const activeIndex = store.state.activeIndex;
2439
- if (activeIndex == null) {
2440
- // Allow form submission when no item is highlighted.
2441
- return;
2442
- }
2443
- clamp.stopEvent(event);
2444
- const nativeEvent = event.nativeEvent;
2445
- const listItem = store.state.listRef.current[activeIndex];
2446
- if (listItem) {
2447
- store.state.selectionEventRef.current = nativeEvent;
2448
- listItem.click();
2449
- store.state.selectionEventRef.current = null;
2450
- }
2451
- }
2452
- },
2453
- onKeyDownCapture() {
2454
- store.state.keyboardActiveRef.current = true;
2455
- },
2456
- onPointerMoveCapture() {
2457
- store.state.keyboardActiveRef.current = false;
2458
- }
2459
- }, elementProps]
2460
- });
2461
- if (virtualized) {
2462
- return element;
2463
- }
2464
- return /*#__PURE__*/jsxRuntime.jsx(MenuSubmenuTrigger.CompositeList, {
2465
- elementsRef: listRef,
2466
- labelsRef: items ? undefined : labelsRef,
2467
- children: element
2468
- });
2469
- });
2470
- if (process.env.NODE_ENV !== "production") ComboboxList.displayName = "ComboboxList";
2471
-
2472
- /**
2473
- * Displays a status message whose content changes are announced politely to screen readers.
2474
- * Useful for conveying the status of an asynchronously loaded list.
2475
- * Renders a `<div>` element.
2476
- */
2477
- const ComboboxStatus = /*#__PURE__*/React__namespace.forwardRef(function ComboboxStatus(componentProps, forwardedRef) {
2478
- const {
2479
- render,
2480
- className,
2481
- ...elementProps
2482
- } = componentProps;
2483
- return useRenderElement.useRenderElement('div', componentProps, {
2484
- ref: forwardedRef,
2485
- props: [{
2486
- role: 'status',
2487
- 'aria-live': 'polite',
2488
- 'aria-atomic': true
2489
- }, elementProps]
2490
- });
2491
- });
2492
- if (process.env.NODE_ENV !== "production") ComboboxStatus.displayName = "ComboboxStatus";
2493
-
2494
- const ComboboxPortalContext = /*#__PURE__*/React__namespace.createContext(undefined);
2495
- if (process.env.NODE_ENV !== "production") ComboboxPortalContext.displayName = "ComboboxPortalContext";
2496
- function useComboboxPortalContext() {
2497
- const context = React__namespace.useContext(ComboboxPortalContext);
2498
- if (context === undefined) {
2499
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Combobox.Portal> is missing.' : useRenderElement.formatErrorMessage(20));
2500
- }
2501
- return context;
2502
- }
2503
-
2504
- const ComboboxPortal = /*#__PURE__*/React__namespace.forwardRef(function ComboboxPortal(props, forwardedRef) {
2505
- const {
2506
- keepMounted = false,
2507
- ...portalProps
2508
- } = props;
2509
- const store = useComboboxRootContext();
2510
- const mounted = ScrollAreaViewport.useStore(store, selectors.mounted);
2511
- const forceMounted = ScrollAreaViewport.useStore(store, selectors.forceMounted);
2512
- const shouldRender = mounted || keepMounted || forceMounted;
2513
- if (!shouldRender) {
2514
- return null;
2515
- }
2516
- return /*#__PURE__*/jsxRuntime.jsx(ComboboxPortalContext.Provider, {
2517
- value: keepMounted,
2518
- children: /*#__PURE__*/jsxRuntime.jsx(ScrollAreaViewport.FloatingPortal, {
2519
- ref: forwardedRef,
2520
- ...portalProps
2521
- })
2522
- });
2523
- });
2524
- if (process.env.NODE_ENV !== "production") ComboboxPortal.displayName = "ComboboxPortal";
2525
-
2526
- const stateAttributesMapping$7 = {
2527
- ...ScrollAreaViewport.popupStateMapping,
2528
- ...clamp.transitionStatusMapping
2529
- };
2530
-
2531
- /**
2532
- * An overlay displayed beneath the popup.
2533
- * Renders a `<div>` element.
2534
- */
2535
- const ComboboxBackdrop = /*#__PURE__*/React__namespace.forwardRef(function ComboboxBackdrop(componentProps, forwardedRef) {
2536
- const {
2537
- className,
2538
- render,
2539
- ...elementProps
2540
- } = componentProps;
2541
- const store = useComboboxRootContext();
2542
- const open = ScrollAreaViewport.useStore(store, selectors.open);
2543
- const mounted = ScrollAreaViewport.useStore(store, selectors.mounted);
2544
- const transitionStatus = ScrollAreaViewport.useStore(store, selectors.transitionStatus);
2545
- const state = {
2546
- open,
2547
- transitionStatus
2548
- };
2549
- return useRenderElement.useRenderElement('div', componentProps, {
2550
- state,
2551
- ref: forwardedRef,
2552
- stateAttributesMapping: stateAttributesMapping$7,
2553
- props: [{
2554
- role: 'presentation',
2555
- hidden: !mounted,
2556
- style: {
2557
- userSelect: 'none',
2558
- WebkitUserSelect: 'none'
2559
- }
2560
- }, elementProps]
2561
- });
2562
- });
2563
- if (process.env.NODE_ENV !== "production") ComboboxBackdrop.displayName = "ComboboxBackdrop";
2564
-
2565
- const ComboboxPositioner = /*#__PURE__*/React__namespace.forwardRef(function ComboboxPositioner(componentProps, forwardedRef) {
2566
- const {
2567
- render,
2568
- className,
2569
- anchor,
2570
- positionMethod = 'absolute',
2571
- side = 'bottom',
2572
- align = 'center',
2573
- sideOffset = 0,
2574
- alignOffset = 0,
2575
- collisionBoundary = 'clipping-ancestors',
2576
- collisionPadding = 5,
2577
- arrowPadding = 5,
2578
- sticky = false,
2579
- disableAnchorTracking = false,
2580
- collisionAvoidance = ScrollAreaViewport.DROPDOWN_COLLISION_AVOIDANCE,
2581
- ...elementProps
2582
- } = componentProps;
2583
- const store = useComboboxRootContext();
2584
- const {
2585
- filteredItems
2586
- } = useComboboxDerivedItemsContext();
2587
- const floatingRootContext = useComboboxFloatingContext();
2588
- const keepMounted = useComboboxPortalContext();
2589
- const modal = ScrollAreaViewport.useStore(store, selectors.modal);
2590
- const open = ScrollAreaViewport.useStore(store, selectors.open);
2591
- const mounted = ScrollAreaViewport.useStore(store, selectors.mounted);
2592
- const openMethod = ScrollAreaViewport.useStore(store, selectors.openMethod);
2593
- const triggerElement = ScrollAreaViewport.useStore(store, selectors.triggerElement);
2594
- const inputElement = ScrollAreaViewport.useStore(store, selectors.inputElement);
2595
- const inputGroupElement = ScrollAreaViewport.useStore(store, selectors.inputGroupElement);
2596
- const inputInsidePopup = ScrollAreaViewport.useStore(store, selectors.inputInsidePopup);
2597
- const transitionStatus = ScrollAreaViewport.useStore(store, selectors.transitionStatus);
2598
- const empty = filteredItems.length === 0;
2599
- const resolvedAnchor = anchor ?? (inputInsidePopup ? triggerElement : inputGroupElement ?? inputElement);
2600
- const positioning = MenuSubmenuTrigger.useAnchorPositioning({
2601
- anchor: resolvedAnchor,
2602
- floatingRootContext,
2603
- positionMethod,
2604
- mounted,
2605
- side,
2606
- sideOffset,
2607
- align,
2608
- alignOffset,
2609
- arrowPadding,
2610
- collisionBoundary,
2611
- collisionPadding,
2612
- sticky,
2613
- disableAnchorTracking,
2614
- keepMounted,
2615
- collisionAvoidance,
2616
- lazyFlip: true
2617
- });
2618
- ScrollAreaViewport.useScrollLock(open && modal && openMethod !== 'touch', triggerElement);
2619
- const defaultProps = React__namespace.useMemo(() => {
2620
- const style = {
2621
- ...positioning.positionerStyles
2622
- };
2623
- if (!open) {
2624
- style.pointerEvents = 'none';
2625
- }
2626
- return {
2627
- role: 'presentation',
2628
- hidden: !mounted,
2629
- style
2630
- };
2631
- }, [open, mounted, positioning.positionerStyles]);
2632
- const state = {
2633
- open,
2634
- side: positioning.side,
2635
- align: positioning.align,
2636
- anchorHidden: positioning.anchorHidden,
2637
- empty
2638
- };
2639
- clamp.useIsoLayoutEffect(() => {
2640
- store.set('popupSide', positioning.side);
2641
- }, [store, positioning.side]);
2642
- const contextValue = React__namespace.useMemo(() => ({
2643
- side: positioning.side,
2644
- align: positioning.align,
2645
- arrowRef: positioning.arrowRef,
2646
- arrowUncentered: positioning.arrowUncentered,
2647
- arrowStyles: positioning.arrowStyles,
2648
- anchorHidden: positioning.anchorHidden,
2649
- isPositioned: positioning.isPositioned
2650
- }), [positioning.side, positioning.align, positioning.arrowRef, positioning.arrowUncentered, positioning.arrowStyles, positioning.anchorHidden, positioning.isPositioned]);
2651
- const setPositionerElement = clamp.useStableCallback(element => {
2652
- store.set('positionerElement', element);
2653
- });
2654
- const element = useRenderElement.useRenderElement('div', componentProps, {
2655
- state,
2656
- ref: [forwardedRef, setPositionerElement],
2657
- props: [defaultProps, MenuSubmenuTrigger.getDisabledMountTransitionStyles(transitionStatus), elementProps],
2658
- stateAttributesMapping: ScrollAreaViewport.popupStateMapping
2659
- });
2660
- return /*#__PURE__*/jsxRuntime.jsxs(ComboboxPositionerContext.Provider, {
2661
- value: contextValue,
2662
- children: [mounted && modal && /*#__PURE__*/jsxRuntime.jsx(ScrollAreaViewport.InternalBackdrop, {
2663
- inert: ScrollAreaViewport.inertValue(!open),
2664
- cutout: inputGroupElement ?? inputElement ?? triggerElement
2665
- }), element]
2666
- });
2667
- });
2668
- if (process.env.NODE_ENV !== "production") ComboboxPositioner.displayName = "ComboboxPositioner";
2669
-
2670
- const stateAttributesMapping$6 = {
2671
- ...ScrollAreaViewport.popupStateMapping,
2672
- ...clamp.transitionStatusMapping
2673
- };
2674
-
2675
- /**
2676
- * A container for the list.
2677
- * Renders a `<div>` element.
2678
- */
2679
- const ComboboxPopup = /*#__PURE__*/React__namespace.forwardRef(function ComboboxPopup(componentProps, forwardedRef) {
2680
- const {
2681
- render,
2682
- className,
2683
- initialFocus,
2684
- finalFocus,
2685
- ...elementProps
2686
- } = componentProps;
2687
- const store = useComboboxRootContext();
2688
- const positioning = useComboboxPositionerContext();
2689
- const floatingRootContext = useComboboxFloatingContext();
2690
- const {
2691
- filteredItems
2692
- } = useComboboxDerivedItemsContext();
2693
- const mounted = ScrollAreaViewport.useStore(store, selectors.mounted);
2694
- const open = ScrollAreaViewport.useStore(store, selectors.open);
2695
- const openMethod = ScrollAreaViewport.useStore(store, selectors.openMethod);
2696
- const transitionStatus = ScrollAreaViewport.useStore(store, selectors.transitionStatus);
2697
- const inputInsidePopup = ScrollAreaViewport.useStore(store, selectors.inputInsidePopup);
2698
- const inputElement = ScrollAreaViewport.useStore(store, selectors.inputElement);
2699
- const modal = ScrollAreaViewport.useStore(store, selectors.modal);
2700
- const empty = filteredItems.length === 0;
2701
- clamp.useOpenChangeComplete({
2702
- open,
2703
- ref: store.state.popupRef,
2704
- onComplete() {
2705
- if (open) {
2706
- store.state.onOpenChangeComplete(true);
2707
- }
2708
- }
2709
- });
2710
- const state = {
2711
- open,
2712
- side: positioning.side,
2713
- align: positioning.align,
2714
- anchorHidden: positioning.anchorHidden,
2715
- transitionStatus,
2716
- empty
2717
- };
2718
- const element = useRenderElement.useRenderElement('div', componentProps, {
2719
- state,
2720
- ref: [forwardedRef, store.state.popupRef],
2721
- props: [{
2722
- role: inputInsidePopup ? 'dialog' : 'presentation',
2723
- tabIndex: -1,
2724
- onFocus(event) {
2725
- const target = clamp.getTarget(event.nativeEvent);
2726
- if (openMethod !== 'touch' && (clamp.contains(store.state.listElement, target) || target === event.currentTarget)) {
2727
- store.state.inputRef.current?.focus();
2728
- }
2729
- }
2730
- }, MenuSubmenuTrigger.getDisabledMountTransitionStyles(transitionStatus), elementProps],
2731
- stateAttributesMapping: stateAttributesMapping$6
2732
- });
2733
-
2734
- // Default initial focus logic:
2735
- // If opened by touch, focus the popup element to prevent the virtual keyboard from opening
2736
- // (this is required for Android specifically as iOS handles this automatically).
2737
- const computedDefaultInitialFocus = inputInsidePopup ? interactionType => interactionType === 'touch' ? store.state.popupRef.current : inputElement : false;
2738
- const resolvedInitialFocus = initialFocus === undefined ? computedDefaultInitialFocus : initialFocus;
2739
- let resolvedFinalFocus;
2740
- if (finalFocus != null) {
2741
- resolvedFinalFocus = finalFocus;
2742
- } else {
2743
- resolvedFinalFocus = inputInsidePopup ? undefined : false;
2744
- }
2745
- const focusManagerModal = !inputInsidePopup || modal;
2746
- return /*#__PURE__*/jsxRuntime.jsx(ScrollAreaViewport.FloatingFocusManager, {
2747
- context: floatingRootContext,
2748
- disabled: !mounted,
2749
- modal: focusManagerModal,
2750
- openInteractionType: openMethod,
2751
- initialFocus: resolvedInitialFocus,
2752
- returnFocus: resolvedFinalFocus,
2753
- getInsideElements: () => [store.state.startDismissRef.current, store.state.endDismissRef.current],
2754
- children: /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
2755
- children: [element, focusManagerModal && /*#__PURE__*/jsxRuntime.jsx(ComboboxInternalDismissButton, {
2756
- ref: store.state.endDismissRef
2757
- })]
2758
- })
2759
- });
2760
- });
2761
- if (process.env.NODE_ENV !== "production") ComboboxPopup.displayName = "ComboboxPopup";
2762
-
2763
- /**
2764
- * Displays an element positioned against the anchor.
2765
- * Renders a `<div>` element.
2766
- */
2767
- const ComboboxArrow = /*#__PURE__*/React__namespace.forwardRef(function ComboboxArrow(componentProps, forwardedRef) {
2768
- const {
2769
- className,
2770
- render,
2771
- ...elementProps
2772
- } = componentProps;
2773
- const store = useComboboxRootContext();
2774
- const {
2775
- arrowRef,
2776
- side,
2777
- align,
2778
- arrowUncentered,
2779
- arrowStyles
2780
- } = useComboboxPositionerContext();
2781
- const open = ScrollAreaViewport.useStore(store, selectors.open);
2782
- const state = {
2783
- open,
2784
- side,
2785
- align,
2786
- uncentered: arrowUncentered
2787
- };
2788
- return useRenderElement.useRenderElement('div', componentProps, {
2789
- ref: [arrowRef, forwardedRef],
2790
- stateAttributesMapping: ScrollAreaViewport.popupStateMapping,
2791
- state,
2792
- props: {
2793
- style: arrowStyles,
2794
- 'aria-hidden': true,
2795
- ...elementProps
2796
- }
2797
- });
2798
- });
2799
- if (process.env.NODE_ENV !== "production") ComboboxArrow.displayName = "ComboboxArrow";
2800
-
2801
- const ComboboxGroupContext = /*#__PURE__*/React__namespace.createContext(undefined);
2802
- if (process.env.NODE_ENV !== "production") ComboboxGroupContext.displayName = "ComboboxGroupContext";
2803
- function useComboboxGroupContext() {
2804
- const context = React__namespace.useContext(ComboboxGroupContext);
2805
- if (context === undefined) {
2806
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxGroupContext is missing. ComboboxGroup parts must be placed within <Combobox.Group>.' : useRenderElement.formatErrorMessage(18));
2807
- }
2808
- return context;
2809
- }
2810
-
2811
- const ComboboxGroup = /*#__PURE__*/React__namespace.forwardRef(function ComboboxGroup(componentProps, forwardedRef) {
2812
- const {
2813
- render,
2814
- className,
2815
- items,
2816
- ...elementProps
2817
- } = componentProps;
2818
- const [labelId, setLabelId] = React__namespace.useState();
2819
- const contextValue = React__namespace.useMemo(() => ({
2820
- labelId,
2821
- setLabelId,
2822
- items
2823
- }), [labelId, setLabelId, items]);
2824
- const element = useRenderElement.useRenderElement('div', componentProps, {
2825
- ref: forwardedRef,
2826
- props: [{
2827
- role: 'group',
2828
- 'aria-labelledby': labelId
2829
- }, elementProps]
2830
- });
2831
- const wrappedElement = /*#__PURE__*/jsxRuntime.jsx(ComboboxGroupContext.Provider, {
2832
- value: contextValue,
2833
- children: element
2834
- });
2835
- if (items) {
2836
- return /*#__PURE__*/jsxRuntime.jsx(GroupCollectionProvider, {
2837
- items: items,
2838
- children: wrappedElement
2839
- });
2840
- }
2841
- return wrappedElement;
2842
- });
2843
- if (process.env.NODE_ENV !== "production") ComboboxGroup.displayName = "ComboboxGroup";
2844
-
2845
- /**
2846
- * An accessible label that is automatically associated with its parent group.
2847
- * Renders a `<div>` element.
2848
- */
2849
- const ComboboxGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function ComboboxGroupLabel(componentProps, forwardedRef) {
2850
- const {
2851
- render,
2852
- className,
2853
- id: idProp,
2854
- ...elementProps
2855
- } = componentProps;
2856
- const {
2857
- setLabelId
2858
- } = useComboboxGroupContext();
2859
- const id = clamp.useBaseUiId(idProp);
2860
- clamp.useIsoLayoutEffect(() => {
2861
- setLabelId(id);
2862
- return () => {
2863
- setLabelId(undefined);
2864
- };
2865
- }, [id, setLabelId]);
2866
- const element = useRenderElement.useRenderElement('div', componentProps, {
2867
- ref: forwardedRef,
2868
- props: [{
2869
- id
2870
- }, elementProps]
2871
- });
2872
- return element;
2873
- });
2874
- if (process.env.NODE_ENV !== "production") ComboboxGroupLabel.displayName = "ComboboxGroupLabel";
2875
-
2876
- const ComboboxItemContext = /*#__PURE__*/React__namespace.createContext(undefined);
2877
- if (process.env.NODE_ENV !== "production") ComboboxItemContext.displayName = "ComboboxItemContext";
2878
- function useComboboxItemContext() {
2879
- const context = React__namespace.useContext(ComboboxItemContext);
2880
- if (!context) {
2881
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ComboboxItemContext is missing. ComboboxItem parts must be placed within <Combobox.Item>.' : useRenderElement.formatErrorMessage(19));
2882
- }
2883
- return context;
2884
- }
2885
-
2886
- const ComboboxRowContext = /*#__PURE__*/React__namespace.createContext(false);
2887
- if (process.env.NODE_ENV !== "production") ComboboxRowContext.displayName = "ComboboxRowContext";
2888
- function useComboboxRowContext() {
2889
- return React__namespace.useContext(ComboboxRowContext);
2890
- }
2891
-
2892
- const ComboboxItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef(function ComboboxItem(componentProps, forwardedRef) {
2893
- const {
2894
- render,
2895
- className,
2896
- value: itemValue = null,
2897
- index: indexProp,
2898
- disabled = false,
2899
- nativeButton = false,
2900
- ...elementProps
2901
- } = componentProps;
2902
- const didPointerDownRef = React__namespace.useRef(false);
2903
- const textRef = React__namespace.useRef(null);
2904
- const listItem = MenuSubmenuTrigger.useCompositeListItem({
2905
- index: indexProp,
2906
- textRef,
2907
- indexGuessBehavior: MenuSubmenuTrigger.IndexGuessBehavior.GuessFromOrder
2908
- });
2909
- const store = useComboboxRootContext();
2910
- const isRow = useComboboxRowContext();
2911
- const {
2912
- flatFilteredItems,
2913
- hasItems
2914
- } = useComboboxDerivedItemsContext();
2915
- const open = ScrollAreaViewport.useStore(store, selectors.open);
2916
- const selectionMode = ScrollAreaViewport.useStore(store, selectors.selectionMode);
2917
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
2918
- const virtualized = ScrollAreaViewport.useStore(store, selectors.virtualized);
2919
- const isItemEqualToValue = ScrollAreaViewport.useStore(store, selectors.isItemEqualToValue);
2920
- const selectable = selectionMode !== 'none';
2921
- const index = indexProp ?? (virtualized ? findItemIndex(flatFilteredItems, itemValue, isItemEqualToValue) : listItem.index);
2922
- const hasRegistered = listItem.index !== -1;
2923
- const rootId = ScrollAreaViewport.useStore(store, selectors.id);
2924
- const highlighted = ScrollAreaViewport.useStore(store, selectors.isActive, index);
2925
- const matchesSelectedValue = ScrollAreaViewport.useStore(store, selectors.isSelected, itemValue);
2926
- const getItemProps = ScrollAreaViewport.useStore(store, selectors.getItemProps);
2927
- const itemRef = React__namespace.useRef(null);
2928
- const id = rootId != null && hasRegistered ? `${rootId}-${index}` : undefined;
2929
- const selected = matchesSelectedValue && selectable;
2930
- clamp.useIsoLayoutEffect(() => {
2931
- const shouldRun = hasRegistered && (virtualized || indexProp != null);
2932
- if (!shouldRun) {
2933
- return undefined;
2934
- }
2935
- const list = store.state.listRef.current;
2936
- list[index] = itemRef.current;
2937
- return () => {
2938
- delete list[index];
2939
- };
2940
- }, [hasRegistered, virtualized, index, indexProp, store]);
2941
- clamp.useIsoLayoutEffect(() => {
2942
- if (!hasRegistered || hasItems) {
2943
- return undefined;
2944
- }
2945
- const visibleMap = store.state.valuesRef.current;
2946
- visibleMap[index] = itemValue;
2947
-
2948
- // Stable registry that doesn't depend on filtering. Assume that no
2949
- // filtering had occurred at this point; otherwise, an `items` prop is
2950
- // required.
2951
- if (selectionMode !== 'none') {
2952
- store.state.allValuesRef.current.push(itemValue);
2953
- }
2954
- return () => {
2955
- delete visibleMap[index];
2956
- };
2957
- }, [hasRegistered, hasItems, index, itemValue, store, selectionMode]);
2958
- clamp.useIsoLayoutEffect(() => {
2959
- if (!open) {
2960
- didPointerDownRef.current = false;
2961
- return;
2962
- }
2963
- if (!hasRegistered || hasItems) {
2964
- return;
2965
- }
2966
- const selectedValue = store.state.selectedValue;
2967
- const lastSelectedValue = Array.isArray(selectedValue) ? selectedValue[selectedValue.length - 1] : selectedValue;
2968
- if (compareItemEquality(itemValue, lastSelectedValue, isItemEqualToValue)) {
2969
- store.set('selectedIndex', index);
2970
- }
2971
- }, [hasRegistered, hasItems, open, store, index, itemValue, isItemEqualToValue]);
2972
- const state = {
2973
- disabled,
2974
- selected,
2975
- highlighted
2976
- };
2977
- const rootProps = getItemProps({
2978
- active: highlighted,
2979
- selected
2980
- });
2981
- rootProps.id = undefined;
2982
- rootProps.onFocus = undefined;
2983
- const {
2984
- getButtonProps,
2985
- buttonRef
2986
- } = clamp.useButton({
2987
- disabled,
2988
- focusableWhenDisabled: true,
2989
- native: nativeButton,
2990
- composite: true
2991
- });
2992
- function commitSelection(nativeEvent) {
2993
- function selectItem() {
2994
- store.state.handleSelection(nativeEvent, itemValue);
92
+ if (mode !== 'both') {
93
+ return staticItems ? null : baseFilter;
2995
94
  }
2996
- if (store.state.submitOnItemClick) {
2997
- ReactDOM__namespace.flushSync(selectItem);
2998
- store.state.requestSubmit();
2999
- } else {
3000
- selectItem();
95
+ if (baseFilter === null) {
96
+ return null;
3001
97
  }
3002
- }
3003
- const defaultProps = {
3004
- id,
3005
- role: isRow ? 'gridcell' : 'option',
3006
- 'aria-selected': selectable ? selected : undefined,
3007
- // Focusable items steal focus from the input upon mouseup.
3008
- // Warn if the user renders a natively focusable element like `<button>`,
3009
- // as it should be a `<div>` instead.
3010
- tabIndex: undefined,
3011
- onPointerDownCapture(event) {
3012
- didPointerDownRef.current = true;
3013
- event.preventDefault();
3014
- },
3015
- onClick(event) {
3016
- if (disabled || readOnly) {
3017
- return;
3018
- }
3019
- commitSelection(event.nativeEvent);
3020
- },
3021
- onMouseUp(event) {
3022
- const pointerStartedOnItem = didPointerDownRef.current;
3023
- didPointerDownRef.current = false;
3024
- if (disabled || readOnly || event.button !== 0 || pointerStartedOnItem || !highlighted) {
3025
- return;
98
+ return (item, _query, toString) => {
99
+ return baseFilter(item, resolvedQuery, toString);
100
+ };
101
+ }, [baseFilter, mode, resolvedQuery, staticItems]);
102
+ const handleItemHighlighted = useButton.useStableCallback((highlightedValue, eventDetails) => {
103
+ props.onItemHighlighted?.(highlightedValue, eventDetails);
104
+ if (eventDetails.reason === useButton.pointer) {
105
+ return;
106
+ }
107
+ if (enableInline) {
108
+ if (highlightedValue == null) {
109
+ setInlineInputValue('');
110
+ } else {
111
+ setInlineInputValue(ComboboxChipRemove.stringifyAsLabel(highlightedValue, itemToStringValue));
3026
112
  }
3027
- commitSelection(event.nativeEvent);
113
+ } else {
114
+ setInlineInputValue('');
3028
115
  }
3029
- };
3030
- const element = useRenderElement.useRenderElement('div', componentProps, {
3031
- ref: [buttonRef, forwardedRef, listItem.ref, itemRef],
3032
- state,
3033
- props: [rootProps, defaultProps, elementProps, getButtonProps]
3034
- });
3035
- const contextValue = React__namespace.useMemo(() => ({
3036
- selected,
3037
- textRef
3038
- }), [selected, textRef]);
3039
- return /*#__PURE__*/jsxRuntime.jsx(ComboboxItemContext.Provider, {
3040
- value: contextValue,
3041
- children: element
3042
- });
3043
- }));
3044
- if (process.env.NODE_ENV !== "production") ComboboxItem.displayName = "ComboboxItem";
3045
-
3046
- const ComboboxRow = /*#__PURE__*/React__namespace.forwardRef(function ComboboxRow(componentProps, forwardedRef) {
3047
- const {
3048
- render,
3049
- className,
3050
- ...elementProps
3051
- } = componentProps;
3052
- const element = useRenderElement.useRenderElement('div', componentProps, {
3053
- ref: forwardedRef,
3054
- props: [{
3055
- role: 'row'
3056
- }, elementProps]
3057
116
  });
3058
- return /*#__PURE__*/jsxRuntime.jsx(ComboboxRowContext.Provider, {
3059
- value: true,
3060
- children: element
117
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxChipRemove.AriaCombobox, {
118
+ ...other,
119
+ itemToStringLabel: itemToStringValue,
120
+ openOnInputClick: openOnInputClick,
121
+ selectionMode: "none",
122
+ fillInputOnItemPress: true,
123
+ filter: resolvedFilter,
124
+ autoComplete: mode,
125
+ inputValue: resolvedInputValue,
126
+ defaultInputValue: defaultValue,
127
+ onInputValueChange: handleValueChange,
128
+ onItemHighlighted: handleItemHighlighted
3061
129
  });
3062
- });
3063
- if (process.env.NODE_ENV !== "production") ComboboxRow.displayName = "ComboboxRow";
130
+ }
3064
131
 
3065
- /**
3066
- * Renders its children only when the list is empty.
3067
- * Requires the `items` prop on the root component.
3068
- * Announces changes politely to screen readers.
3069
- * Renders a `<div>` element.
3070
- */
3071
- const ComboboxEmpty = /*#__PURE__*/React__namespace.forwardRef(function ComboboxEmpty(componentProps, forwardedRef) {
3072
- const {
3073
- render,
3074
- className,
3075
- children: childrenProp,
3076
- ...elementProps
3077
- } = componentProps;
132
+ function AutocompleteValue(props) {
3078
133
  const {
3079
- filteredItems
3080
- } = useComboboxDerivedItemsContext();
3081
- const store = useComboboxRootContext();
3082
- const children = filteredItems.length === 0 ? childrenProp : null;
3083
- return useRenderElement.useRenderElement('div', componentProps, {
3084
- ref: [forwardedRef, store.state.emptyRef],
3085
- props: [{
3086
- children,
3087
- role: 'status',
3088
- 'aria-live': 'polite',
3089
- 'aria-atomic': true
3090
- }, elementProps]
134
+ children
135
+ } = props;
136
+ const inputValue = ComboboxChipRemove.useComboboxInputValueContext();
137
+ let returnValue = null;
138
+ if (typeof children === 'function') {
139
+ returnValue = children(String(inputValue));
140
+ } else if (children != null) {
141
+ returnValue = children;
142
+ } else {
143
+ returnValue = inputValue;
144
+ }
145
+ return /*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
146
+ children: returnValue
3091
147
  });
3092
- });
3093
- if (process.env.NODE_ENV !== "production") ComboboxEmpty.displayName = "ComboboxEmpty";
3094
-
3095
- /**
3096
- * Returns the internally filtered items.
3097
- */
3098
- function useFilteredItems() {
3099
- const items = useComboboxDerivedItemsContext();
3100
- return items.filteredItems;
3101
148
  }
3102
149
 
3103
150
  var index_parts$5 = /*#__PURE__*/Object.freeze({
3104
151
  __proto__: null,
3105
- Arrow: ComboboxArrow,
3106
- Backdrop: ComboboxBackdrop,
3107
- Clear: ComboboxClear,
3108
- Collection: ComboboxCollection,
3109
- Empty: ComboboxEmpty,
3110
- Group: ComboboxGroup,
3111
- GroupLabel: ComboboxGroupLabel,
3112
- Icon: ComboboxIcon,
3113
- Input: ComboboxInput,
3114
- InputGroup: ComboboxInputGroup,
3115
- Item: ComboboxItem,
3116
- List: ComboboxList,
3117
- Popup: ComboboxPopup,
3118
- Portal: ComboboxPortal,
3119
- Positioner: ComboboxPositioner,
152
+ Arrow: ComboboxChipRemove.ComboboxArrow,
153
+ Backdrop: ComboboxChipRemove.ComboboxBackdrop,
154
+ Clear: ComboboxChipRemove.ComboboxClear,
155
+ Collection: ComboboxChipRemove.ComboboxCollection,
156
+ Empty: ComboboxChipRemove.ComboboxEmpty,
157
+ Group: ComboboxChipRemove.ComboboxGroup,
158
+ GroupLabel: ComboboxChipRemove.ComboboxGroupLabel,
159
+ Icon: ComboboxChipRemove.ComboboxIcon,
160
+ Input: ComboboxChipRemove.ComboboxInput,
161
+ InputGroup: ComboboxChipRemove.ComboboxInputGroup,
162
+ Item: ComboboxChipRemove.ComboboxItem,
163
+ List: ComboboxChipRemove.ComboboxList,
164
+ Popup: ComboboxChipRemove.ComboboxPopup,
165
+ Portal: ComboboxChipRemove.ComboboxPortal,
166
+ Positioner: ComboboxChipRemove.ComboboxPositioner,
3120
167
  Root: AutocompleteRoot,
3121
- Row: ComboboxRow,
168
+ Row: ComboboxChipRemove.ComboboxRow,
3122
169
  Separator: Separator.Separator,
3123
- Status: ComboboxStatus,
3124
- Trigger: ComboboxTrigger,
170
+ Status: ComboboxChipRemove.ComboboxStatus,
171
+ Trigger: ComboboxChipRemove.ComboboxTrigger,
3125
172
  Value: AutocompleteValue,
3126
- useFilter: useCoreFilter,
3127
- useFilteredItems: useFilteredItems
173
+ useFilter: ComboboxChipRemove.useCoreFilter,
174
+ useFilteredItems: ComboboxChipRemove.useFilteredItems
3128
175
  });
3129
176
 
3130
177
  /**
@@ -3143,7 +190,7 @@ const DialogDescription = /*#__PURE__*/React__namespace.forwardRef(function Dial
3143
190
  const {
3144
191
  store
3145
192
  } = ScrollAreaViewport.useDialogRootContext();
3146
- const id = clamp.useBaseUiId(idProp);
193
+ const id = useButton.useBaseUiId(idProp);
3147
194
  store.useSyncedValueWithCleanup('descriptionElementId', id);
3148
195
  return useRenderElement.useRenderElement('p', componentProps, {
3149
196
  ref: forwardedRef,
@@ -3192,7 +239,7 @@ const DrawerIndent = /*#__PURE__*/React__namespace.forwardRef(function DrawerInd
3192
239
  const active = providerContext?.active ?? false;
3193
240
  const visualStateStore = providerContext?.visualStateStore;
3194
241
  const indentRef = React__namespace.useRef(null);
3195
- clamp.useIsoLayoutEffect(() => {
242
+ useButton.useIsoLayoutEffect(() => {
3196
243
  const element = indentRef.current;
3197
244
  if (!element || !visualStateStore) {
3198
245
  return undefined;
@@ -3282,7 +329,7 @@ function DrawerProvider(props) {
3282
329
  } = props;
3283
330
  const [openById, setOpenById] = React__namespace.useState(() => new Map());
3284
331
  const [visualStateStore] = React__namespace.useState(createVisualStateStore);
3285
- const setDrawerOpen = clamp.useStableCallback((drawerId, open) => {
332
+ const setDrawerOpen = useButton.useStableCallback((drawerId, open) => {
3286
333
  setOpenById(prev => {
3287
334
  const prevOpen = prev.get(drawerId);
3288
335
  if (prevOpen === open) {
@@ -3293,7 +340,7 @@ function DrawerProvider(props) {
3293
340
  return next;
3294
341
  });
3295
342
  });
3296
- const removeDrawer = clamp.useStableCallback(drawerId => {
343
+ const removeDrawer = useButton.useStableCallback(drawerId => {
3297
344
  setOpenById(prev => {
3298
345
  if (!prev.has(drawerId)) {
3299
346
  return prev;
@@ -3363,19 +410,19 @@ let DialogViewportDataAttributes = function (DialogViewportDataAttributes) {
3363
410
  /**
3364
411
  * Present when the dialog is open.
3365
412
  */
3366
- DialogViewportDataAttributes[DialogViewportDataAttributes["open"] = ScrollAreaViewport.CommonPopupDataAttributes.open] = "open";
413
+ DialogViewportDataAttributes[DialogViewportDataAttributes["open"] = InternalBackdrop.CommonPopupDataAttributes.open] = "open";
3367
414
  /**
3368
415
  * Present when the dialog is closed.
3369
416
  */
3370
- DialogViewportDataAttributes[DialogViewportDataAttributes["closed"] = ScrollAreaViewport.CommonPopupDataAttributes.closed] = "closed";
417
+ DialogViewportDataAttributes[DialogViewportDataAttributes["closed"] = InternalBackdrop.CommonPopupDataAttributes.closed] = "closed";
3371
418
  /**
3372
419
  * Present when the dialog is animating in.
3373
420
  */
3374
- DialogViewportDataAttributes[DialogViewportDataAttributes["startingStyle"] = ScrollAreaViewport.CommonPopupDataAttributes.startingStyle] = "startingStyle";
421
+ DialogViewportDataAttributes[DialogViewportDataAttributes["startingStyle"] = InternalBackdrop.CommonPopupDataAttributes.startingStyle] = "startingStyle";
3375
422
  /**
3376
423
  * Present when the dialog is animating out.
3377
424
  */
3378
- DialogViewportDataAttributes[DialogViewportDataAttributes["endingStyle"] = ScrollAreaViewport.CommonPopupDataAttributes.endingStyle] = "endingStyle";
425
+ DialogViewportDataAttributes[DialogViewportDataAttributes["endingStyle"] = InternalBackdrop.CommonPopupDataAttributes.endingStyle] = "endingStyle";
3379
426
  /**
3380
427
  * Present when the dialog is nested within another dialog.
3381
428
  */
@@ -3388,8 +435,8 @@ let DialogViewportDataAttributes = function (DialogViewportDataAttributes) {
3388
435
  }({});
3389
436
 
3390
437
  const stateAttributesMapping$3 = {
3391
- ...ScrollAreaViewport.popupStateMapping,
3392
- ...clamp.transitionStatusMapping,
438
+ ...InternalBackdrop.popupStateMapping,
439
+ ...useButton.transitionStatusMapping,
3393
440
  nested(value) {
3394
441
  return value ? {
3395
442
  [DialogViewportDataAttributes.nested]: ''
@@ -3477,7 +524,7 @@ class DialogHandle {
3477
524
  console.warn(`Base UI: DialogHandle.open: No trigger found with id "${triggerId}". The dialog will open, but the trigger will not be associated with the dialog.`);
3478
525
  }
3479
526
  }
3480
- this.store.setOpen(true, clamp.createChangeEventDetails(clamp.imperativeAction, undefined, triggerElement));
527
+ this.store.setOpen(true, useButton.createChangeEventDetails(useButton.imperativeAction, undefined, triggerElement));
3481
528
  }
3482
529
 
3483
530
  /**
@@ -3488,14 +535,14 @@ class DialogHandle {
3488
535
  */
3489
536
  openWithPayload(payload) {
3490
537
  this.store.set('payload', payload);
3491
- this.store.setOpen(true, clamp.createChangeEventDetails(clamp.imperativeAction, undefined, undefined));
538
+ this.store.setOpen(true, useButton.createChangeEventDetails(useButton.imperativeAction, undefined, undefined));
3492
539
  }
3493
540
 
3494
541
  /**
3495
542
  * Closes the dialog.
3496
543
  */
3497
544
  close() {
3498
- this.store.setOpen(false, clamp.createChangeEventDetails(clamp.imperativeAction, undefined, undefined));
545
+ this.store.setOpen(false, useButton.createChangeEventDetails(useButton.imperativeAction, undefined, undefined));
3499
546
  }
3500
547
 
3501
548
  /**
@@ -3707,7 +754,7 @@ function useSwipeDismiss(options) {
3707
754
  });
3708
755
  const lastProgressDetailsRef = React__namespace.useRef(null);
3709
756
  const isSwipingRef = React__namespace.useRef(false);
3710
- const setSwiping = clamp.useStableCallback(nextSwiping => {
757
+ const setSwiping = useButton.useStableCallback(nextSwiping => {
3711
758
  if (isSwipingRef.current === nextSwiping) {
3712
759
  return;
3713
760
  }
@@ -3733,7 +780,7 @@ function useSwipeDismiss(options) {
3733
780
  });
3734
781
  swipeThresholdRef.current = Math.max(0, value);
3735
782
  }
3736
- const updateSwipeProgress = clamp.useStableCallback((progress, details) => {
783
+ const updateSwipeProgress = useButton.useStableCallback((progress, details) => {
3737
784
  const nextProgress = Number.isFinite(progress) ? clamp.clamp(progress, 0, 1) : 0;
3738
785
  const progressChanged = nextProgress !== swipeProgressRef.current;
3739
786
  let detailsChanged = false;
@@ -3850,9 +897,9 @@ function useSwipeDismiss(options) {
3850
897
  return event.pointerType === 'touch';
3851
898
  }
3852
899
  function getTargetAtPoint(position, nativeEvent) {
3853
- const doc = clamp.ownerDocument(elementRef.current);
900
+ const doc = useButton.ownerDocument(elementRef.current);
3854
901
  const elementAtPoint = getElementAtPoint(doc, position.x, position.y);
3855
- const target = elementAtPoint ?? clamp.getTarget(nativeEvent);
902
+ const target = elementAtPoint ?? useButton.getTarget(nativeEvent);
3856
903
  return target;
3857
904
  }
3858
905
  function findGestureScrollableTouchTarget(target, root) {
@@ -3868,7 +915,7 @@ function useSwipeDismiss(options) {
3868
915
  swipeFromScrollableRef.current = false;
3869
916
  const touchLike = isTouchLikeEvent(event);
3870
917
  const target = getTargetAtPoint(position, event.nativeEvent);
3871
- const doc = clamp.ownerDocument(elementRef.current);
918
+ const doc = useButton.ownerDocument(elementRef.current);
3872
919
  const body = doc.body;
3873
920
  const scrollableTarget = touchLike && body ? findGestureScrollableTouchTarget(target, body) : null;
3874
921
  const ignoreScrollableTarget = startOptions?.ignoreScrollableTarget ?? false;
@@ -4010,7 +1057,7 @@ function useSwipeDismiss(options) {
4010
1057
  }
4011
1058
  return null;
4012
1059
  }
4013
- const handleStart = clamp.useStableCallback(event => {
1060
+ const handleStart = useButton.useStableCallback(event => {
4014
1061
  if (!enabled) {
4015
1062
  return;
4016
1063
  }
@@ -4043,7 +1090,7 @@ function useSwipeDismiss(options) {
4043
1090
  if (!enabled || !isSwipingRef.current) {
4044
1091
  return;
4045
1092
  }
4046
- const target = clamp.getTarget(event.nativeEvent);
1093
+ const target = useButton.getTarget(event.nativeEvent);
4047
1094
  if (isTouchLikeEvent(event) && !swipeFromScrollableRef.current) {
4048
1095
  const boundaryElement = event.currentTarget;
4049
1096
  if (findGestureScrollableTouchTarget(target, boundaryElement)) {
@@ -4206,7 +1253,7 @@ function useSwipeDismiss(options) {
4206
1253
  direction: swipeDirectionDetails
4207
1254
  });
4208
1255
  }
4209
- const handleMove = clamp.useStableCallback(event => {
1256
+ const handleMove = useButton.useStableCallback(event => {
4210
1257
  const currentPos = getPrimaryPointerPosition(event);
4211
1258
  if (!currentPos) {
4212
1259
  return;
@@ -4241,10 +1288,10 @@ function useSwipeDismiss(options) {
4241
1288
  const element = elementRef.current;
4242
1289
  if (pendingStartPos && element) {
4243
1290
  const target = getTargetAtPoint(currentPos, event.nativeEvent);
4244
- const doc = clamp.ownerDocument(element);
1291
+ const doc = useButton.ownerDocument(element);
4245
1292
  const body = doc.body;
4246
1293
  const scrollTarget = body ? findGestureScrollableTouchTarget(target, body) : null;
4247
- if (scrollTarget && (clamp.contains(element, scrollTarget) || clamp.contains(scrollTarget, element))) {
1294
+ if (scrollTarget && (useButton.contains(element, scrollTarget) || useButton.contains(scrollTarget, element))) {
4248
1295
  const deltaX = currentPos.x - pendingStartPos.x;
4249
1296
  const deltaY = currentPos.y - pendingStartPos.y;
4250
1297
  const canSwipeFromEdge = canSwipeFromScrollEdgeOnPendingMove(scrollTarget, deltaX, deltaY);
@@ -4291,7 +1338,7 @@ function useSwipeDismiss(options) {
4291
1338
  lastMovePosRef.current = currentPos;
4292
1339
  handleMoveCore(event, currentPos, movement);
4293
1340
  });
4294
- const handleEnd = clamp.useStableCallback(event => {
1341
+ const handleEnd = useButton.useStableCallback(event => {
4295
1342
  if (!enabled) {
4296
1343
  return;
4297
1344
  }
@@ -4488,11 +1535,11 @@ let DrawerSwipeAreaDataAttributes = function (DrawerSwipeAreaDataAttributes) {
4488
1535
  /**
4489
1536
  * Present when the drawer is open.
4490
1537
  */
4491
- DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["open"] = ScrollAreaViewport.CommonPopupDataAttributes.open] = "open";
1538
+ DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["open"] = InternalBackdrop.CommonPopupDataAttributes.open] = "open";
4492
1539
  /**
4493
1540
  * Present when the drawer is closed.
4494
1541
  */
4495
- DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["closed"] = ScrollAreaViewport.CommonPopupDataAttributes.closed] = "closed";
1542
+ DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["closed"] = InternalBackdrop.CommonPopupDataAttributes.closed] = "closed";
4496
1543
  /**
4497
1544
  * Present when the swipe area is disabled.
4498
1545
  */
@@ -4574,7 +1621,7 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
4574
1621
  } = ScrollAreaViewport.useDrawerRootContext();
4575
1622
  const providerContext = ScrollAreaViewport.useDrawerProviderContext();
4576
1623
  const [swipeActive, setSwipeActive] = React__namespace.useState(false);
4577
- const releaseDismissTimeout = clamp.useTimeout();
1624
+ const releaseDismissTimeout = useButton.useTimeout();
4578
1625
  const swipeAreaRef = React__namespace.useRef(null);
4579
1626
  const swipeStartEventRef = React__namespace.useRef(null);
4580
1627
  const openedBySwipeRef = React__namespace.useRef(false);
@@ -4585,13 +1632,13 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
4585
1632
  const closedOffsetRef = React__namespace.useRef(null);
4586
1633
  const appliedSwipeStylesRef = React__namespace.useRef(false);
4587
1634
  const popupTransitionRef = React__namespace.useRef(null);
4588
- const swipeAreaId = clamp.useBaseUiId(componentProps.id);
1635
+ const swipeAreaId = useButton.useBaseUiId(componentProps.id);
4589
1636
  const registerTrigger = ScrollAreaViewport.useTriggerRegistration(swipeAreaId, store);
4590
1637
  const open = store.useState('open');
4591
1638
  const resolvedSwipeDirection = swipeDirectionProp ?? oppositeSwipeDirection[swipeDirection];
4592
1639
  const dismissDirection = oppositeSwipeDirection[resolvedSwipeDirection];
4593
1640
  const enabled = !disabled && (!open || swipeActive);
4594
- const resetDragDelta = clamp.useStableCallback(() => {
1641
+ const resetDragDelta = useButton.useStableCallback(() => {
4595
1642
  dragDeltaRef.current.x = 0;
4596
1643
  dragDeltaRef.current.y = 0;
4597
1644
  });
@@ -4698,7 +1745,7 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
4698
1745
  });
4699
1746
  appliedSwipeStylesRef.current = true;
4700
1747
  }
4701
- const clearSwipeStyles = clamp.useStableCallback(() => {
1748
+ const clearSwipeStyles = useButton.useStableCallback(() => {
4702
1749
  const popupElement = store.context.popupRef.current;
4703
1750
  if (popupElement && appliedSwipeStylesRef.current) {
4704
1751
  popupElement.style.removeProperty(ScrollAreaViewport.DrawerPopupCssVars.swipeMovementX);
@@ -4726,13 +1773,13 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
4726
1773
  return;
4727
1774
  }
4728
1775
  openedBySwipeRef.current = true;
4729
- store.setOpen(true, clamp.createChangeEventDetails(clamp.swipe, event, swipeAreaRef.current ?? undefined));
1776
+ store.setOpen(true, useButton.createChangeEventDetails(useButton.swipe, event, swipeAreaRef.current ?? undefined));
4730
1777
  }
4731
1778
  function closeDrawer(event) {
4732
1779
  if (!store.select('open')) {
4733
1780
  return;
4734
1781
  }
4735
- store.setOpen(false, clamp.createChangeEventDetails(clamp.swipe, event, swipeAreaRef.current ?? undefined));
1782
+ store.setOpen(false, useButton.createChangeEventDetails(useButton.swipe, event, swipeAreaRef.current ?? undefined));
4736
1783
  }
4737
1784
  const swipe = useSwipeDismiss({
4738
1785
  enabled,
@@ -4931,7 +1978,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
4931
1978
  const [swipeRelease, setSwipeRelease] = React__namespace.useState(null);
4932
1979
  const pendingSwipeCloseSnapPointRef = React__namespace.useRef(undefined);
4933
1980
  const resetSwipeRef = React__namespace.useRef(null);
4934
- const controlledDismissFrame = clamp.useAnimationFrame();
1981
+ const controlledDismissFrame = useButton.useAnimationFrame();
4935
1982
  const nestedSwipeActiveRef = React__namespace.useRef(false);
4936
1983
  const lastPointerTypeRef = React__namespace.useRef('');
4937
1984
  const ignoreNextTouchStartFromPenRef = React__namespace.useRef(false);
@@ -4979,15 +2026,15 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
4979
2026
  }
4980
2027
  return [swipeDirection];
4981
2028
  }, [snapPoints, swipeDirection]);
4982
- const setSwipeDismissed = clamp.useStableCallback(dismissed => {
2029
+ const setSwipeDismissed = useButton.useStableCallback(dismissed => {
4983
2030
  setSwipeDismissedElements(store.context.popupRef.current, store.context.backdropRef.current, dismissed);
4984
2031
  });
4985
- const clearSwipeRelease = clamp.useStableCallback(() => {
2032
+ const clearSwipeRelease = useButton.useStableCallback(() => {
4986
2033
  setSwipeDismissed(false);
4987
- store.context.popupRef.current?.removeAttribute(clamp.TransitionStatusDataAttributes.endingStyle);
2034
+ store.context.popupRef.current?.removeAttribute(useButton.TransitionStatusDataAttributes.endingStyle);
4988
2035
  setSwipeRelease(null);
4989
2036
  });
4990
- const applySwipeProgress = clamp.useStableCallback(({
2037
+ const applySwipeProgress = useButton.useStableCallback(({
4991
2038
  resolvedProgress,
4992
2039
  shouldTrackProgress,
4993
2040
  notifyParent
@@ -5101,14 +2148,14 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5101
2148
  if (!popupElement) {
5102
2149
  return;
5103
2150
  }
5104
- const doc = clamp.ownerDocument(popupElement);
2151
+ const doc = useButton.ownerDocument(popupElement);
5105
2152
  const selection = doc.getSelection?.();
5106
2153
  if (!selection || selection.isCollapsed) {
5107
2154
  return;
5108
2155
  }
5109
2156
  const anchorElement = floatingUi_utils_dom.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
5110
2157
  const focusElement = floatingUi_utils_dom.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
5111
- if (!clamp.contains(popupElement, anchorElement) && !clamp.contains(popupElement, focusElement)) {
2158
+ if (!useButton.contains(popupElement, anchorElement) && !useButton.contains(popupElement, focusElement)) {
5112
2159
  return;
5113
2160
  }
5114
2161
  selection.removeAllRanges();
@@ -5133,7 +2180,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5133
2180
  }
5134
2181
  const doc = popupElement.ownerDocument;
5135
2182
  const elementAtPoint = getElementAtPoint(doc, position.x, position.y);
5136
- if (!elementAtPoint || !clamp.contains(popupElement, elementAtPoint)) {
2183
+ if (!elementAtPoint || !useButton.contains(popupElement, elementAtPoint)) {
5137
2184
  return false;
5138
2185
  }
5139
2186
  const nativeEvent = details.nativeEvent;
@@ -5202,7 +2249,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5202
2249
  notifyParentSwipingChange?.(false);
5203
2250
  setSwipeDismissed(true);
5204
2251
  popupElement.style.removeProperty('transition');
5205
- popupElement.setAttribute(clamp.TransitionStatusDataAttributes.endingStyle, '');
2252
+ popupElement.setAttribute(useButton.TransitionStatusDataAttributes.endingStyle, '');
5206
2253
  ReactDOM__namespace.flushSync(() => {
5207
2254
  setSwipeRelease(resolveSwipeRelease({
5208
2255
  direction: resolvedDirection,
@@ -5273,7 +2320,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5273
2320
  const dragTargetOffset = clamp.clamp(currentOffset + dragDelta, 0, popupHeight);
5274
2321
  const velocityOffset = Number.isFinite(resolvedDirectionalVelocity) && Math.abs(resolvedDirectionalVelocity) >= SNAP_VELOCITY_THRESHOLD ? clamp.clamp(resolvedDirectionalVelocity, -MAX_SNAP_VELOCITY, MAX_SNAP_VELOCITY) * SNAP_VELOCITY_MULTIPLIER : 0;
5275
2322
  const targetOffset = snapToSequentialPoints ? dragTargetOffset : clamp.clamp(dragTargetOffset + velocityOffset, 0, popupHeight);
5276
- const snapPointEventDetails = clamp.createChangeEventDetails(clamp.swipe, event);
2323
+ const snapPointEventDetails = useButton.createChangeEventDetails(useButton.swipe, event);
5277
2324
  const closeFromSnapPoints = () => {
5278
2325
  pendingSwipeCloseSnapPointRef.current = activeSnapPoint;
5279
2326
  setActiveSnapPoint?.(null, snapPointEventDetails);
@@ -5361,12 +2408,12 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5361
2408
  backdropElement.style.setProperty(ScrollAreaViewport.DrawerBackdropCssVars.swipeProgress, '0');
5362
2409
  backdropElement.style.removeProperty(ScrollAreaViewport.DrawerPopupCssVars.height);
5363
2410
  }
5364
- const dismissEventDetails = clamp.createChangeEventDetails(clamp.swipe, event);
2411
+ const dismissEventDetails = useButton.createChangeEventDetails(useButton.swipe, event);
5365
2412
  store.setOpen(false, dismissEventDetails);
5366
2413
  if (dismissEventDetails.isCanceled) {
5367
2414
  const pendingSnapPoint = pendingSwipeCloseSnapPointRef.current;
5368
2415
  if (pendingSnapPoint !== undefined) {
5369
- setActiveSnapPoint?.(pendingSnapPoint, clamp.createChangeEventDetails(clamp.swipe, event));
2416
+ setActiveSnapPoint?.(pendingSnapPoint, useButton.createChangeEventDetails(useButton.swipe, event));
5370
2417
  }
5371
2418
  pendingSwipeCloseSnapPointRef.current = undefined;
5372
2419
  resetSwipeRef.current?.();
@@ -5388,7 +2435,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5388
2435
  // Parent rejected: revert animation and restore snap point.
5389
2436
  const pendingSnapPoint = pendingSwipeCloseSnapPointRef.current;
5390
2437
  if (pendingSnapPoint !== undefined) {
5391
- setActiveSnapPoint?.(pendingSnapPoint, clamp.createChangeEventDetails(clamp.swipe, savedEvent));
2438
+ setActiveSnapPoint?.(pendingSnapPoint, useButton.createChangeEventDetails(useButton.swipe, savedEvent));
5392
2439
  }
5393
2440
  pendingSwipeCloseSnapPointRef.current = undefined;
5394
2441
  clearSwipeRelease();
@@ -5414,7 +2461,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5414
2461
  return undefined;
5415
2462
  }
5416
2463
  const resolvedRootElement = rootElement;
5417
- const doc = clamp.ownerDocument(resolvedRootElement);
2464
+ const doc = useButton.ownerDocument(resolvedRootElement);
5418
2465
  const win = floatingUi_utils_dom.getWindow(doc);
5419
2466
  function handleNativeTouchMove(event) {
5420
2467
  if (ignoreTouchSwipeRef.current) {
@@ -5557,7 +2604,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5557
2604
  if (!open || !mounted || nestedDrawerOpen) {
5558
2605
  return;
5559
2606
  }
5560
- const doc = clamp.ownerDocument(event.currentTarget);
2607
+ const doc = useButton.ownerDocument(event.currentTarget);
5561
2608
  const elementAtPoint = getElementAtPoint(doc, event.clientX, event.clientY);
5562
2609
  if (isSwipeIgnoredTarget(elementAtPoint) || isDrawerContentTarget(elementAtPoint)) {
5563
2610
  return;
@@ -5613,7 +2660,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5613
2660
  touchScrollStateRef.current = null;
5614
2661
  return;
5615
2662
  }
5616
- const doc = clamp.ownerDocument(event.currentTarget);
2663
+ const doc = useButton.ownerDocument(event.currentTarget);
5617
2664
  const elementAtPoint = getElementAtPoint(doc, touch.clientX, touch.clientY);
5618
2665
  ignoreTouchSwipeRef.current = isSwipeIgnoredTarget(elementAtPoint);
5619
2666
  if (ignoreTouchSwipeRef.current) {
@@ -5624,7 +2671,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5624
2671
  const target = floatingUi_utils_dom.isElement(event.target) ? event.target : null;
5625
2672
  let scrollTarget = null;
5626
2673
  let hasCrossAxisScrollableContent = false;
5627
- if (rootElement && target && clamp.contains(rootElement, target)) {
2674
+ if (rootElement && target && useButton.contains(rootElement, target)) {
5628
2675
  scrollTarget = findScrollableTouchTarget(target, rootElement, scrollAxis);
5629
2676
  hasCrossAxisScrollableContent = findScrollableTouchTarget(target, rootElement, crossScrollAxis) != null;
5630
2677
  }
@@ -5700,7 +2747,7 @@ function setBackdropSwipingAttribute(backdropElement, swiping) {
5700
2747
  backdropElement.removeAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swiping);
5701
2748
  }
5702
2749
  function isSwipeIgnoredTarget(target) {
5703
- return Boolean(target?.closest(ScrollAreaViewport.BASE_UI_SWIPE_IGNORE_SELECTOR));
2750
+ return Boolean(target?.closest(InternalBackdrop.BASE_UI_SWIPE_IGNORE_SELECTOR));
5704
2751
  }
5705
2752
  function isDrawerContentTarget(target) {
5706
2753
  return Boolean(target?.closest(DRAWER_CONTENT_SELECTOR));
@@ -5721,11 +2768,11 @@ function isTextSelectionControl(target) {
5721
2768
  function hasExpandedSelectionWithinTarget(selection, target) {
5722
2769
  const anchorElement = floatingUi_utils_dom.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
5723
2770
  const focusElement = floatingUi_utils_dom.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
5724
- return selection.containsNode(target, true) || clamp.contains(target, anchorElement) || clamp.contains(target, focusElement);
2771
+ return selection.containsNode(target, true) || useButton.contains(target, anchorElement) || useButton.contains(target, focusElement);
5725
2772
  }
5726
2773
  function shouldIgnoreSwipeForTextSelection(doc, rootElement) {
5727
2774
  const activeElement = doc.activeElement;
5728
- const activeElementWithinRoot = Boolean(activeElement && clamp.contains(rootElement, activeElement));
2775
+ const activeElementWithinRoot = Boolean(activeElement && useButton.contains(rootElement, activeElement));
5729
2776
  if (activeElementWithinRoot && isTextSelectionControl(activeElement)) {
5730
2777
  const {
5731
2778
  selectionStart,
@@ -5885,7 +2932,7 @@ const MenuArrow = /*#__PURE__*/React__namespace.forwardRef(function MenuArrow(co
5885
2932
  };
5886
2933
  return useRenderElement.useRenderElement('div', componentProps, {
5887
2934
  ref: [arrowRef, forwardedRef],
5888
- stateAttributesMapping: ScrollAreaViewport.popupStateMapping,
2935
+ stateAttributesMapping: InternalBackdrop.popupStateMapping,
5889
2936
  state,
5890
2937
  props: {
5891
2938
  style: arrowStyles,
@@ -5897,8 +2944,8 @@ const MenuArrow = /*#__PURE__*/React__namespace.forwardRef(function MenuArrow(co
5897
2944
  if (process.env.NODE_ENV !== "production") MenuArrow.displayName = "MenuArrow";
5898
2945
 
5899
2946
  const stateAttributesMapping$1 = {
5900
- ...ScrollAreaViewport.popupStateMapping,
5901
- ...clamp.transitionStatusMapping
2947
+ ...InternalBackdrop.popupStateMapping,
2948
+ ...useButton.transitionStatusMapping
5902
2949
  };
5903
2950
 
5904
2951
  /**
@@ -5933,7 +2980,7 @@ const MenuBackdrop = /*#__PURE__*/React__namespace.forwardRef(function MenuBackd
5933
2980
  role: 'presentation',
5934
2981
  hidden: !mounted,
5935
2982
  style: {
5936
- pointerEvents: lastOpenChangeReason === clamp.triggerHover ? 'none' : undefined,
2983
+ pointerEvents: lastOpenChangeReason === useButton.triggerHover ? 'none' : undefined,
5937
2984
  userSelect: 'none',
5938
2985
  WebkitUserSelect: 'none'
5939
2986
  }
@@ -5972,17 +3019,17 @@ const MenuCheckboxItem = /*#__PURE__*/React__namespace.forwardRef(function MenuC
5972
3019
  onCheckedChange,
5973
3020
  ...elementProps
5974
3021
  } = componentProps;
5975
- const listItem = MenuSubmenuTrigger.useCompositeListItem({
3022
+ const listItem = useCompositeListItem.useCompositeListItem({
5976
3023
  label
5977
3024
  });
5978
3025
  const menuPositionerContext = MenuSubmenuTrigger.useMenuPositionerContext(true);
5979
- const id = clamp.useBaseUiId(idProp);
3026
+ const id = useButton.useBaseUiId(idProp);
5980
3027
  const {
5981
3028
  store
5982
3029
  } = MenuSubmenuTrigger.useMenuRootContext();
5983
3030
  const highlighted = store.useState('isActive', listItem.index);
5984
3031
  const itemProps = store.useState('itemProps');
5985
- const [checked, setChecked] = clamp.useControlled({
3032
+ const [checked, setChecked] = useButton.useControlled({
5986
3033
  controlled: checkedProp,
5987
3034
  default: defaultChecked ?? false,
5988
3035
  name: 'MenuCheckboxItem',
@@ -6006,9 +3053,9 @@ const MenuCheckboxItem = /*#__PURE__*/React__namespace.forwardRef(function MenuC
6006
3053
  highlighted,
6007
3054
  checked
6008
3055
  }), [disabled, highlighted, checked]);
6009
- const handleClick = clamp.useStableCallback(event => {
3056
+ const handleClick = useButton.useStableCallback(event => {
6010
3057
  const details = {
6011
- ...clamp.createChangeEventDetails(clamp.itemPress, event.nativeEvent),
3058
+ ...useButton.createChangeEventDetails(useButton.itemPress, event.nativeEvent),
6012
3059
  preventUnmountOnClose: () => {}
6013
3060
  };
6014
3061
  onCheckedChange?.(!checked, details);
@@ -6052,8 +3099,8 @@ const MenuCheckboxItemIndicator = /*#__PURE__*/React__namespace.forwardRef(funct
6052
3099
  const {
6053
3100
  transitionStatus,
6054
3101
  setMounted
6055
- } = clamp.useTransitionStatus(item.checked);
6056
- clamp.useOpenChangeComplete({
3102
+ } = useButton.useTransitionStatus(item.checked);
3103
+ useButton.useOpenChangeComplete({
6057
3104
  open: item.checked,
6058
3105
  ref: indicatorRef,
6059
3106
  onComplete() {
@@ -6138,14 +3185,14 @@ function usePopupAutoResize(parameters) {
6138
3185
  side,
6139
3186
  direction
6140
3187
  } = parameters;
6141
- const runOnceAnimationsFinish = clamp.useAnimationsFinished(popupElement, true, false);
6142
- const animationFrame = clamp.useAnimationFrame();
3188
+ const runOnceAnimationsFinish = useButton.useAnimationsFinished(popupElement, true, false);
3189
+ const animationFrame = useButton.useAnimationFrame();
6143
3190
  const committedDimensionsRef = React__namespace.useRef(null);
6144
3191
  const liveDimensionsRef = React__namespace.useRef(null);
6145
3192
  const isInitialRenderRef = React__namespace.useRef(true);
6146
3193
  const restoreAnchoringStylesRef = React__namespace.useRef(useRenderElement.NOOP);
6147
- const onMeasureLayout = clamp.useStableCallback(onMeasureLayoutParam);
6148
- const onMeasureLayoutComplete = clamp.useStableCallback(onMeasureLayoutCompleteParam);
3194
+ const onMeasureLayout = useButton.useStableCallback(onMeasureLayoutParam);
3195
+ const onMeasureLayoutComplete = useButton.useStableCallback(onMeasureLayoutCompleteParam);
6149
3196
  const anchoringStyles = React__namespace.useMemo(() => {
6150
3197
  // Ensure popup size transitions correctly when anchored to `bottom` (side=top) or `right` (side=left).
6151
3198
  let isOriginSide = side === 'top';
@@ -6163,7 +3210,7 @@ function usePopupAutoResize(parameters) {
6163
3210
  [isPhysicalLeft ? 'right' : 'left']: '0'
6164
3211
  } : useRenderElement.EMPTY_OBJECT;
6165
3212
  }, [side, direction]);
6166
- clamp.useIsoLayoutEffect(() => {
3213
+ useButton.useIsoLayoutEffect(() => {
6167
3214
  // Reset the state when the popup is closed.
6168
3215
  if (!mounted || !enabled() || typeof ResizeObserver !== 'function') {
6169
3216
  restoreAnchoringStylesRef.current = useRenderElement.NOOP;
@@ -6304,7 +3351,7 @@ function usePopupViewport(parameters) {
6304
3351
  cssVars,
6305
3352
  children
6306
3353
  } = parameters;
6307
- const direction = ScrollAreaViewport.useDirection();
3354
+ const direction = InternalBackdrop.useDirection();
6308
3355
  const activeTrigger = store.useState('activeTriggerElement');
6309
3356
  const activeTriggerId = store.useState('activeTriggerId');
6310
3357
  const open = store.useState('open');
@@ -6321,22 +3368,22 @@ function usePopupViewport(parameters) {
6321
3368
  const [newTriggerOffset, setNewTriggerOffset] = React__namespace.useState(null);
6322
3369
  const currentContainerRef = React__namespace.useRef(null);
6323
3370
  const previousContainerRef = React__namespace.useRef(null);
6324
- const onAnimationsFinished = clamp.useAnimationsFinished(currentContainerRef, true, false);
6325
- const cleanupFrame = clamp.useAnimationFrame();
3371
+ const onAnimationsFinished = useButton.useAnimationsFinished(currentContainerRef, true, false);
3372
+ const cleanupFrame = useButton.useAnimationFrame();
6326
3373
  const [previousContentDimensions, setPreviousContentDimensions] = React__namespace.useState(null);
6327
3374
  const [showStartingStyleAttribute, setShowStartingStyleAttribute] = React__namespace.useState(false);
6328
- clamp.useIsoLayoutEffect(() => {
3375
+ useButton.useIsoLayoutEffect(() => {
6329
3376
  store.set('hasViewport', true);
6330
3377
  return () => {
6331
3378
  store.set('hasViewport', false);
6332
3379
  };
6333
3380
  }, [store]);
6334
- const handleMeasureLayout = clamp.useStableCallback(() => {
3381
+ const handleMeasureLayout = useButton.useStableCallback(() => {
6335
3382
  currentContainerRef.current?.style.setProperty('animation', 'none');
6336
3383
  currentContainerRef.current?.style.setProperty('transition', 'none');
6337
3384
  previousContainerRef.current?.style.setProperty('display', 'none');
6338
3385
  });
6339
- const handleMeasureLayoutComplete = clamp.useStableCallback(previousDimensions => {
3386
+ const handleMeasureLayoutComplete = useButton.useStableCallback(previousDimensions => {
6340
3387
  currentContainerRef.current?.style.removeProperty('animation');
6341
3388
  currentContainerRef.current?.style.removeProperty('transition');
6342
3389
  previousContainerRef.current?.style.removeProperty('display');
@@ -6345,7 +3392,7 @@ function usePopupViewport(parameters) {
6345
3392
  }
6346
3393
  });
6347
3394
  const lastHandledTriggerRef = React__namespace.useRef(null);
6348
- clamp.useIsoLayoutEffect(() => {
3395
+ useButton.useIsoLayoutEffect(() => {
6349
3396
  // When a trigger changes, set the captured children HTML to state,
6350
3397
  // so we can render both new and old content.
6351
3398
  if (activeTrigger && previousActiveTrigger && activeTrigger !== previousActiveTrigger && lastHandledTriggerRef.current !== activeTrigger && capturedNodeRef.current) {
@@ -6372,7 +3419,7 @@ function usePopupViewport(parameters) {
6372
3419
 
6373
3420
  // Capture a clone of the current content DOM subtree when not transitioning.
6374
3421
  // We can't store previous React nodes as they may be stateful; instead we capture DOM clones for visual continuity.
6375
- clamp.useIsoLayoutEffect(() => {
3422
+ useButton.useIsoLayoutEffect(() => {
6376
3423
  // When a transition is in progress, we store the next content in capturedNodeRef.
6377
3424
  // This handles the case where the trigger changes multiple times before the transition finishes.
6378
3425
  // We want to always capture the latest content for the previous snapshot.
@@ -6401,7 +3448,7 @@ function usePopupViewport(parameters) {
6401
3448
  childrenToRender = /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
6402
3449
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
6403
3450
  "data-previous": true,
6404
- inert: ScrollAreaViewport.inertValue(true),
3451
+ inert: InternalBackdrop.inertValue(true),
6405
3452
  ref: previousContainerRef,
6406
3453
  style: {
6407
3454
  ...(previousContentDimensions ? {
@@ -6421,7 +3468,7 @@ function usePopupViewport(parameters) {
6421
3468
  }
6422
3469
 
6423
3470
  // When previousContentNode is present, imperatively populate the previous container with the cloned children.
6424
- clamp.useIsoLayoutEffect(() => {
3471
+ useButton.useIsoLayoutEffect(() => {
6425
3472
  const container = previousContainerRef.current;
6426
3473
  if (!container || !previousContentNode) {
6427
3474
  return;
@@ -6508,7 +3555,7 @@ function usePopupContentKey(activeTriggerId, payload) {
6508
3555
  const previousActiveTriggerIdRef = React__namespace.useRef(activeTriggerId);
6509
3556
  const previousPayloadRef = React__namespace.useRef(payload);
6510
3557
  const pendingPayloadUpdateRef = React__namespace.useRef(false);
6511
- clamp.useIsoLayoutEffect(() => {
3558
+ useButton.useIsoLayoutEffect(() => {
6512
3559
  // Compare against the last committed values to decide whether we need a new DOM subtree.
6513
3560
  const previousActiveTriggerId = previousActiveTriggerIdRef.current;
6514
3561
  const previousPayload = previousPayloadRef.current;
@@ -6621,14 +3668,14 @@ class MenuHandle {
6621
3668
  if (triggerId && !triggerElement) {
6622
3669
  throw new Error(process.env.NODE_ENV !== "production" ? `Base UI: MenuHandle.open: No trigger found with id "${triggerId}".` : useRenderElement.formatErrorMessage(83, triggerId));
6623
3670
  }
6624
- this.store.setOpen(true, clamp.createChangeEventDetails('imperative-action', undefined, triggerElement));
3671
+ this.store.setOpen(true, useButton.createChangeEventDetails('imperative-action', undefined, triggerElement));
6625
3672
  }
6626
3673
 
6627
3674
  /**
6628
3675
  * Closes the menu.
6629
3676
  */
6630
3677
  close() {
6631
- this.store.setOpen(false, clamp.createChangeEventDetails('imperative-action', undefined, undefined));
3678
+ this.store.setOpen(false, useButton.createChangeEventDetails('imperative-action', undefined, undefined));
6632
3679
  }
6633
3680
 
6634
3681
  /**
@@ -6742,7 +3789,7 @@ const ScrollAreaScrollbar = /*#__PURE__*/React__namespace.forwardRef(function Sc
6742
3789
  overflowYEnd: overflowEdges.yEnd,
6743
3790
  cornerHidden: hiddenState.corner
6744
3791
  };
6745
- const direction = ScrollAreaViewport.useDirection();
3792
+ const direction = InternalBackdrop.useDirection();
6746
3793
  const hideTrackUntilMeasured = !hasMeasuredScrollbar && !keepMounted;
6747
3794
  React__namespace.useEffect(() => {
6748
3795
  const viewportEl = viewportRef.current;
@@ -6900,7 +3947,7 @@ const ScrollAreaContent = /*#__PURE__*/React__namespace.forwardRef(function Scro
6900
3947
  const {
6901
3948
  viewportState
6902
3949
  } = ScrollAreaViewport.useScrollAreaRootContext();
6903
- clamp.useIsoLayoutEffect(() => {
3950
+ useButton.useIsoLayoutEffect(() => {
6904
3951
  if (typeof ResizeObserver === 'undefined') {
6905
3952
  return undefined;
6906
3953
  }
@@ -7040,434 +4087,36 @@ var index_parts$2 = /*#__PURE__*/Object.freeze({
7040
4087
  Viewport: ScrollAreaViewport.ScrollAreaViewport
7041
4088
  });
7042
4089
 
7043
- function ComboboxRoot(props) {
7044
- const {
7045
- multiple = false,
7046
- defaultValue,
7047
- value,
7048
- onValueChange,
7049
- autoComplete,
7050
- ...other
7051
- } = props;
7052
- return /*#__PURE__*/jsxRuntime.jsx(AriaCombobox, {
7053
- ...other,
7054
- selectionMode: multiple ? 'multiple' : 'single',
7055
- selectedValue: value,
7056
- defaultSelectedValue: defaultValue,
7057
- onSelectedValueChange: onValueChange,
7058
- formAutoComplete: autoComplete
7059
- });
7060
- }
7061
-
7062
- /**
7063
- * An accessible label that is automatically associated with the combobox trigger.
7064
- * Renders a `<div>` element.
7065
- *
7066
- * Documentation: [Base UI Combobox](https://base-ui.com/react/components/combobox)
7067
- */
7068
- const ComboboxLabel = /*#__PURE__*/React__namespace.forwardRef(function ComboboxLabel(componentProps, forwardedRef) {
7069
- const {
7070
- render,
7071
- className,
7072
- ...elementProps
7073
- } = componentProps;
7074
- // Keep label id derived from the root and ignore runtime `id` overrides from untyped consumers.
7075
- const elementPropsWithoutId = elementProps;
7076
- delete elementPropsWithoutId.id;
7077
- const fieldRootContext = NumberFieldInput.useFieldRootContext();
7078
- const store = useComboboxRootContext();
7079
- const inputInsidePopup = ScrollAreaViewport.useStore(store, selectors.inputInsidePopup);
7080
- const triggerElement = ScrollAreaViewport.useStore(store, selectors.triggerElement);
7081
- const inputElement = ScrollAreaViewport.useStore(store, selectors.inputElement);
7082
- const rootId = ScrollAreaViewport.useStore(store, selectors.id);
7083
- const defaultLabelId = getDefaultLabelId(rootId);
7084
- const localControlId = triggerElement?.id ?? (inputInsidePopup ? rootId : undefined);
7085
- if (process.env.NODE_ENV !== 'production') {
7086
- // eslint-disable-next-line react-hooks/rules-of-hooks
7087
- React__namespace.useEffect(() => {
7088
- if (!inputElement || inputInsidePopup) {
7089
- return;
7090
- }
7091
- const ownerStackMessage = clamp.SafeReact.captureOwnerStack?.() || '';
7092
- const message = '<Combobox.Label> labels <Combobox.Trigger> only. ' + 'When <Combobox.Input> is the form control, use a native <label> or <Field.Label> instead.';
7093
- clamp.error(`${message}${ownerStackMessage}`);
7094
- }, [inputElement, inputInsidePopup]);
7095
- }
7096
- const labelProps = NumberFieldInput.useLabel({
7097
- id: defaultLabelId,
7098
- fallbackControlId: localControlId,
7099
- setLabelId(nextLabelId) {
7100
- store.set('labelId', nextLabelId);
7101
- }
7102
- });
7103
- return useRenderElement.useRenderElement('div', componentProps, {
7104
- ref: forwardedRef,
7105
- state: fieldRootContext.state,
7106
- props: [labelProps, elementProps],
7107
- stateAttributesMapping: NumberFieldInput.fieldValidityMapping
7108
- });
7109
- });
7110
- if (process.env.NODE_ENV !== "production") ComboboxLabel.displayName = "ComboboxLabel";
7111
-
7112
- function ComboboxValue(props) {
7113
- const {
7114
- children: childrenProp,
7115
- placeholder
7116
- } = props;
7117
- const store = useComboboxRootContext();
7118
- const itemToStringLabel = ScrollAreaViewport.useStore(store, selectors.itemToStringLabel);
7119
- const selectedValue = ScrollAreaViewport.useStore(store, selectors.selectedValue);
7120
- const items = ScrollAreaViewport.useStore(store, selectors.items);
7121
- const multiple = ScrollAreaViewport.useStore(store, selectors.selectionMode) === 'multiple';
7122
- const hasSelectedValue = ScrollAreaViewport.useStore(store, selectors.hasSelectedValue);
7123
- const shouldCheckNullItemLabel = !hasSelectedValue && placeholder != null && childrenProp == null;
7124
- const hasNullLabel = ScrollAreaViewport.useStore(store, selectors.hasNullItemLabel, shouldCheckNullItemLabel);
7125
- let children = null;
7126
- if (typeof childrenProp === 'function') {
7127
- children = childrenProp(selectedValue);
7128
- } else if (childrenProp != null) {
7129
- children = childrenProp;
7130
- } else if (!hasSelectedValue && placeholder != null && !hasNullLabel) {
7131
- children = placeholder;
7132
- } else if (multiple && Array.isArray(selectedValue)) {
7133
- children = resolveMultipleLabels(selectedValue, items, itemToStringLabel);
7134
- } else {
7135
- children = resolveSelectedLabel(selectedValue, items, itemToStringLabel);
7136
- }
7137
- return /*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
7138
- children: children
7139
- });
7140
- }
7141
-
7142
- const ComboboxItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function ComboboxItemIndicator(componentProps, forwardedRef) {
7143
- const keepMounted = componentProps.keepMounted ?? false;
7144
- const {
7145
- selected
7146
- } = useComboboxItemContext();
7147
- const shouldRender = keepMounted || selected;
7148
- if (!shouldRender) {
7149
- return null;
7150
- }
7151
-
7152
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
7153
- return /*#__PURE__*/jsxRuntime.jsx(Inner, {
7154
- ...componentProps,
7155
- ref: forwardedRef
7156
- });
7157
- });
7158
-
7159
- /** The core implementation of the indicator is split here to avoid paying the hooks
7160
- * costs unless the element needs to be mounted. */
7161
- if (process.env.NODE_ENV !== "production") ComboboxItemIndicator.displayName = "ComboboxItemIndicator";
7162
- const Inner = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef((componentProps, forwardedRef) => {
7163
- const {
7164
- render,
7165
- className,
7166
- keepMounted,
7167
- ...elementProps
7168
- } = componentProps;
7169
- const {
7170
- selected
7171
- } = useComboboxItemContext();
7172
- const indicatorRef = React__namespace.useRef(null);
7173
- const {
7174
- transitionStatus,
7175
- setMounted
7176
- } = clamp.useTransitionStatus(selected);
7177
- const state = {
7178
- selected,
7179
- transitionStatus
7180
- };
7181
- const element = useRenderElement.useRenderElement('span', componentProps, {
7182
- ref: [forwardedRef, indicatorRef],
7183
- state,
7184
- props: [{
7185
- 'aria-hidden': true,
7186
- children: '✔️'
7187
- }, elementProps],
7188
- stateAttributesMapping: clamp.transitionStatusMapping
7189
- });
7190
- clamp.useOpenChangeComplete({
7191
- open: selected,
7192
- ref: indicatorRef,
7193
- onComplete() {
7194
- if (!selected) {
7195
- setMounted(false);
7196
- }
7197
- }
7198
- });
7199
- return element;
7200
- }));
7201
- if (process.env.NODE_ENV !== "production") Inner.displayName = "Inner";
7202
-
7203
- const ComboboxChips = /*#__PURE__*/React__namespace.forwardRef(function ComboboxChips(componentProps, forwardedRef) {
7204
- const {
7205
- render,
7206
- className,
7207
- ...elementProps
7208
- } = componentProps;
7209
- const store = useComboboxRootContext();
7210
- const open = ScrollAreaViewport.useStore(store, selectors.open);
7211
- const hasSelectionChips = ScrollAreaViewport.useStore(store, selectors.hasSelectionChips);
7212
- const [highlightedChipIndex, setHighlightedChipIndex] = React__namespace.useState(undefined);
7213
- if (open && highlightedChipIndex !== undefined) {
7214
- setHighlightedChipIndex(undefined);
7215
- }
7216
- const chipsRef = React__namespace.useRef([]);
7217
- const element = useRenderElement.useRenderElement('div', componentProps, {
7218
- ref: [forwardedRef, store.state.chipsContainerRef],
7219
- // NVDA enters browse mode instead of staying in focus mode when navigating with
7220
- // arrow keys inside a container unless it has a toolbar role.
7221
- props: [hasSelectionChips ? {
7222
- role: 'toolbar'
7223
- } : useRenderElement.EMPTY_OBJECT, elementProps]
7224
- });
7225
- const contextValue = React__namespace.useMemo(() => ({
7226
- highlightedChipIndex,
7227
- setHighlightedChipIndex,
7228
- chipsRef
7229
- }), [highlightedChipIndex, setHighlightedChipIndex, chipsRef]);
7230
- return /*#__PURE__*/jsxRuntime.jsx(ComboboxChipsContext.Provider, {
7231
- value: contextValue,
7232
- children: /*#__PURE__*/jsxRuntime.jsx(MenuSubmenuTrigger.CompositeList, {
7233
- elementsRef: chipsRef,
7234
- children: element
7235
- })
7236
- });
7237
- });
7238
- if (process.env.NODE_ENV !== "production") ComboboxChips.displayName = "ComboboxChips";
7239
-
7240
- const ComboboxChipContext = /*#__PURE__*/React__namespace.createContext(undefined);
7241
- if (process.env.NODE_ENV !== "production") ComboboxChipContext.displayName = "ComboboxChipContext";
7242
- function useComboboxChipContext() {
7243
- const context = React__namespace.useContext(ComboboxChipContext);
7244
- if (!context) {
7245
- throw new Error(process.env.NODE_ENV !== "production" ? 'useComboboxChipContext must be used within a ComboboxChip' : useRenderElement.formatErrorMessage(17));
7246
- }
7247
- return context;
7248
- }
7249
-
7250
- const ComboboxChip = /*#__PURE__*/React__namespace.forwardRef(function ComboboxChip(componentProps, forwardedRef) {
7251
- const {
7252
- render,
7253
- className,
7254
- ...elementProps
7255
- } = componentProps;
7256
- const store = useComboboxRootContext();
7257
- const {
7258
- setHighlightedChipIndex,
7259
- chipsRef
7260
- } = useComboboxChipsContext();
7261
- const disabled = ScrollAreaViewport.useStore(store, selectors.disabled);
7262
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
7263
- const selectedValue = ScrollAreaViewport.useStore(store, selectors.selectedValue);
7264
- const {
7265
- ref,
7266
- index
7267
- } = MenuSubmenuTrigger.useCompositeListItem();
7268
- function handleKeyDown(event) {
7269
- let nextIndex = index;
7270
- if (event.key === 'ArrowLeft') {
7271
- event.preventDefault();
7272
- if (index > 0) {
7273
- nextIndex = index - 1;
7274
- } else {
7275
- nextIndex = undefined;
7276
- }
7277
- } else if (event.key === 'ArrowRight') {
7278
- event.preventDefault();
7279
- if (index < selectedValue.length - 1) {
7280
- nextIndex = index + 1;
7281
- } else {
7282
- nextIndex = undefined;
7283
- }
7284
- } else if (event.key === 'Backspace' || event.key === 'Delete') {
7285
- const computedNextIndex = index >= selectedValue.length - 1 ? selectedValue.length - 2 : index;
7286
- nextIndex = computedNextIndex >= 0 ? computedNextIndex : undefined;
7287
- clamp.stopEvent(event);
7288
- store.state.setIndices({
7289
- activeIndex: null,
7290
- selectedIndex: null,
7291
- type: 'keyboard'
7292
- });
7293
- store.state.setSelectedValue(selectedValue.filter((_, i) => i !== index), clamp.createChangeEventDetails(clamp.none, event.nativeEvent));
7294
- } else if (event.key === 'Enter' || event.key === ' ') {
7295
- clamp.stopEvent(event);
7296
- nextIndex = undefined;
7297
- } else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
7298
- clamp.stopEvent(event);
7299
- store.state.setOpen(true, clamp.createChangeEventDetails(clamp.listNavigation, event.nativeEvent));
7300
- nextIndex = undefined;
7301
- } else if (
7302
- // Check for printable characters (letters, numbers, symbols)
7303
- event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
7304
- nextIndex = undefined;
7305
- }
7306
- return nextIndex;
7307
- }
7308
- const state = {
7309
- disabled
7310
- };
7311
- const element = useRenderElement.useRenderElement('div', componentProps, {
7312
- ref: [forwardedRef, ref],
7313
- state,
7314
- props: [{
7315
- tabIndex: -1,
7316
- 'aria-disabled': disabled || undefined,
7317
- 'aria-readonly': readOnly || undefined,
7318
- onKeyDown(event) {
7319
- if (disabled || readOnly) {
7320
- return;
7321
- }
7322
- const nextIndex = handleKeyDown(event);
7323
- ReactDOM__namespace.flushSync(() => {
7324
- setHighlightedChipIndex(nextIndex);
7325
- });
7326
- if (nextIndex === undefined) {
7327
- store.state.inputRef.current?.focus();
7328
- } else {
7329
- chipsRef.current[nextIndex]?.focus();
7330
- }
7331
- },
7332
- onMouseDown(event) {
7333
- if (readOnly) {
7334
- return;
7335
- }
7336
- event.preventDefault();
7337
- if (disabled) {
7338
- return;
7339
- }
7340
- store.state.inputRef.current?.focus();
7341
- }
7342
- }, elementProps]
7343
- });
7344
- const contextValue = React__namespace.useMemo(() => ({
7345
- index
7346
- }), [index]);
7347
- return /*#__PURE__*/jsxRuntime.jsx(ComboboxChipContext.Provider, {
7348
- value: contextValue,
7349
- children: element
7350
- });
7351
- });
7352
- if (process.env.NODE_ENV !== "production") ComboboxChip.displayName = "ComboboxChip";
7353
-
7354
- /**
7355
- * A button to remove a chip.
7356
- * Renders a `<button>` element.
7357
- */
7358
- const ComboboxChipRemove = /*#__PURE__*/React__namespace.forwardRef(function ComboboxChipRemove(componentProps, forwardedRef) {
7359
- const {
7360
- render,
7361
- className,
7362
- disabled: disabledProp = false,
7363
- nativeButton = true,
7364
- ...elementProps
7365
- } = componentProps;
7366
- const store = useComboboxRootContext();
7367
- const {
7368
- index
7369
- } = useComboboxChipContext();
7370
- const comboboxDisabled = ScrollAreaViewport.useStore(store, selectors.disabled);
7371
- const readOnly = ScrollAreaViewport.useStore(store, selectors.readOnly);
7372
- const selectedValue = ScrollAreaViewport.useStore(store, selectors.selectedValue);
7373
- const isItemEqualToValue = ScrollAreaViewport.useStore(store, selectors.isItemEqualToValue);
7374
- const disabled = comboboxDisabled || disabledProp;
7375
- const {
7376
- buttonRef,
7377
- getButtonProps
7378
- } = clamp.useButton({
7379
- native: nativeButton,
7380
- disabled: disabled || readOnly,
7381
- focusableWhenDisabled: true
7382
- });
7383
- const state = {
7384
- disabled
7385
- };
7386
- function clearActiveIndexForRemovedItem(removedItem) {
7387
- const activeIndex = store.state.activeIndex;
7388
- if (activeIndex == null) {
7389
- return;
7390
- }
7391
-
7392
- // Try current visible list first; if not found, it's filtered out.
7393
- // No need to clear highlight in that case since it can't equal activeIndex.
7394
- const removedIndex = findItemIndex(store.state.valuesRef.current, removedItem, isItemEqualToValue);
7395
- if (removedIndex !== -1 && activeIndex === removedIndex) {
7396
- store.state.setIndices({
7397
- activeIndex: null,
7398
- type: store.state.keyboardActiveRef.current ? 'keyboard' : 'pointer'
7399
- });
7400
- }
7401
- }
7402
- function removeChip(event) {
7403
- const eventDetails = clamp.createChangeEventDetails(clamp.chipRemovePress, event.nativeEvent);
7404
- const removedItem = selectedValue[index];
7405
- clearActiveIndexForRemovedItem(removedItem);
7406
- store.state.setSelectedValue(selectedValue.filter((_, i) => i !== index), eventDetails);
7407
- store.state.inputRef.current?.focus();
7408
- return eventDetails;
7409
- }
7410
- const element = useRenderElement.useRenderElement('button', componentProps, {
7411
- ref: [forwardedRef, buttonRef],
7412
- state,
7413
- props: [{
7414
- tabIndex: -1,
7415
- onClick(event) {
7416
- if (disabled || readOnly) {
7417
- return;
7418
- }
7419
- const eventDetails = removeChip(event);
7420
- if (!eventDetails.isPropagationAllowed) {
7421
- event.stopPropagation();
7422
- }
7423
- },
7424
- onKeyDown(event) {
7425
- if (disabled || readOnly) {
7426
- return;
7427
- }
7428
- if (event.key === 'Enter' || event.key === ' ') {
7429
- const eventDetails = removeChip(event);
7430
- if (!eventDetails.isPropagationAllowed) {
7431
- clamp.stopEvent(event);
7432
- }
7433
- }
7434
- }
7435
- }, elementProps, getButtonProps]
7436
- });
7437
- return element;
7438
- });
7439
- if (process.env.NODE_ENV !== "production") ComboboxChipRemove.displayName = "ComboboxChipRemove";
7440
-
7441
4090
  var index_parts$1 = /*#__PURE__*/Object.freeze({
7442
4091
  __proto__: null,
7443
- Arrow: ComboboxArrow,
7444
- Backdrop: ComboboxBackdrop,
7445
- Chip: ComboboxChip,
7446
- ChipRemove: ComboboxChipRemove,
7447
- Chips: ComboboxChips,
7448
- Clear: ComboboxClear,
7449
- Collection: ComboboxCollection,
7450
- Empty: ComboboxEmpty,
7451
- Group: ComboboxGroup,
7452
- GroupLabel: ComboboxGroupLabel,
7453
- Icon: ComboboxIcon,
7454
- Input: ComboboxInput,
7455
- InputGroup: ComboboxInputGroup,
7456
- Item: ComboboxItem,
7457
- ItemIndicator: ComboboxItemIndicator,
7458
- Label: ComboboxLabel,
7459
- List: ComboboxList,
7460
- Popup: ComboboxPopup,
7461
- Portal: ComboboxPortal,
7462
- Positioner: ComboboxPositioner,
7463
- Root: ComboboxRoot,
7464
- Row: ComboboxRow,
4092
+ Arrow: ComboboxChipRemove.ComboboxArrow,
4093
+ Backdrop: ComboboxChipRemove.ComboboxBackdrop,
4094
+ Chip: ComboboxChipRemove.ComboboxChip,
4095
+ ChipRemove: ComboboxChipRemove.ComboboxChipRemove,
4096
+ Chips: ComboboxChipRemove.ComboboxChips,
4097
+ Clear: ComboboxChipRemove.ComboboxClear,
4098
+ Collection: ComboboxChipRemove.ComboboxCollection,
4099
+ Empty: ComboboxChipRemove.ComboboxEmpty,
4100
+ Group: ComboboxChipRemove.ComboboxGroup,
4101
+ GroupLabel: ComboboxChipRemove.ComboboxGroupLabel,
4102
+ Icon: ComboboxChipRemove.ComboboxIcon,
4103
+ Input: ComboboxChipRemove.ComboboxInput,
4104
+ InputGroup: ComboboxChipRemove.ComboboxInputGroup,
4105
+ Item: ComboboxChipRemove.ComboboxItem,
4106
+ ItemIndicator: ComboboxChipRemove.ComboboxItemIndicator,
4107
+ Label: ComboboxChipRemove.ComboboxLabel,
4108
+ List: ComboboxChipRemove.ComboboxList,
4109
+ Popup: ComboboxChipRemove.ComboboxPopup,
4110
+ Portal: ComboboxChipRemove.ComboboxPortal,
4111
+ Positioner: ComboboxChipRemove.ComboboxPositioner,
4112
+ Root: ComboboxChipRemove.ComboboxRoot,
4113
+ Row: ComboboxChipRemove.ComboboxRow,
7465
4114
  Separator: Separator.Separator,
7466
- Status: ComboboxStatus,
7467
- Trigger: ComboboxTrigger,
7468
- Value: ComboboxValue,
7469
- useFilter: useComboboxFilter,
7470
- useFilteredItems: useFilteredItems
4115
+ Status: ComboboxChipRemove.ComboboxStatus,
4116
+ Trigger: ComboboxChipRemove.ComboboxTrigger,
4117
+ Value: ComboboxChipRemove.ComboboxValue,
4118
+ useFilter: ComboboxChipRemove.useComboboxFilter,
4119
+ useFilteredItems: ComboboxChipRemove.useFilteredItems
7471
4120
  });
7472
4121
 
7473
4122
  const NumberFieldScrubAreaContext = /*#__PURE__*/React__namespace.createContext(undefined);
@@ -7559,7 +4208,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
7559
4208
  y: 0
7560
4209
  });
7561
4210
  const visualScaleRef = React__namespace.useRef(1);
7562
- const exitPointerLockTimeout = clamp.useTimeout();
4211
+ const exitPointerLockTimeout = useButton.useTimeout();
7563
4212
  const [isTouchInput, setIsTouchInput] = React__namespace.useState(false);
7564
4213
  const [isPointerLockDenied, setIsPointerLockDenied] = React__namespace.useState(false);
7565
4214
  const [isScrubbing, setIsScrubbing] = React__namespace.useState(false);
@@ -7574,7 +4223,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
7574
4223
  scrubAreaCursorRef.current.style.transform = `translate3d(${x}px,${y}px,0) scale(${1 / visualScaleRef.current})`;
7575
4224
  }
7576
4225
  }
7577
- const onScrub = clamp.useStableCallback(({
4226
+ const onScrub = useButton.useStableCallback(({
7578
4227
  movementX,
7579
4228
  movementY
7580
4229
  }) => {
@@ -7604,7 +4253,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
7604
4253
  virtualCursorCoords.current = newCoords;
7605
4254
  updateCursorTransform(newCoords.x, newCoords.y);
7606
4255
  });
7607
- const onScrubbingChange = clamp.useStableCallback((scrubbingValue, {
4256
+ const onScrubbingChange = useButton.useStableCallback((scrubbingValue, {
7608
4257
  clientX,
7609
4258
  clientY
7610
4259
  }) => {
@@ -7632,13 +4281,13 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
7632
4281
  function handleScrubPointerUp(event) {
7633
4282
  function handler() {
7634
4283
  try {
7635
- clamp.ownerDocument(scrubAreaRef.current).exitPointerLock();
4284
+ useButton.ownerDocument(scrubAreaRef.current).exitPointerLock();
7636
4285
  } catch {
7637
4286
  // Ignore errors.
7638
4287
  } finally {
7639
4288
  isScrubbingRef.current = false;
7640
4289
  onScrubbingChange(false, event);
7641
- onValueCommitted(lastChangedValueRef.current ?? valueRef.current, clamp.createGenericEventDetails(clamp.scrub, event));
4290
+ onValueCommitted(lastChangedValueRef.current ?? valueRef.current, useButton.createGenericEventDetails(useButton.scrub, event));
7642
4291
 
7643
4292
  // Manually dispatch a click event if no movement happened, since
7644
4293
  // preventDefault on pointerdown prevents the browser click event.
@@ -7652,7 +4301,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
7652
4301
  pointerDownTargetRef.current = null;
7653
4302
  }
7654
4303
  }
7655
- if (clamp.isFirefox) {
4304
+ if (useButton.isFirefox) {
7656
4305
  // Firefox needs a small delay here when soft-clicking as the pointer
7657
4306
  // lock will not release otherwise.
7658
4307
  exitPointerLockTimeout.start(20, handler);
@@ -7683,7 +4332,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
7683
4332
  incrementValue(Math.abs(rawAmount), {
7684
4333
  direction: rawAmount >= 0 ? 1 : -1,
7685
4334
  event,
7686
- reason: clamp.scrub
4335
+ reason: useButton.scrub
7687
4336
  });
7688
4337
  }
7689
4338
  }
@@ -7738,12 +4387,12 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
7738
4387
  onScrubbingChange(true, event.nativeEvent);
7739
4388
 
7740
4389
  // WebKit causes significant layout shift with the native message, so we can't use it.
7741
- if (!isTouch && !clamp.isWebKit) {
4390
+ if (!isTouch && !useButton.isWebKit) {
7742
4391
  try {
7743
4392
  // Avoid non-deterministic errors in testing environments. This error sometimes
7744
4393
  // appears:
7745
4394
  // "The root document of this element is not valid for pointer lock."
7746
- await clamp.ownerDocument(scrubAreaRef.current).body.requestPointerLock();
4395
+ await useButton.ownerDocument(scrubAreaRef.current).body.requestPointerLock();
7747
4396
  setIsPointerLockDenied(false);
7748
4397
  } catch (error) {
7749
4398
  setIsPointerLockDenied(true);
@@ -7805,7 +4454,7 @@ const NumberFieldScrubAreaCursor = /*#__PURE__*/React__namespace.forwardRef(func
7805
4454
  scrubAreaCursorRef
7806
4455
  } = useNumberFieldScrubAreaContext();
7807
4456
  const [domElement, setDomElement] = React__namespace.useState(null);
7808
- const shouldRender = isScrubbing && !clamp.isWebKit && !isTouchInput && !isPointerLockDenied;
4457
+ const shouldRender = isScrubbing && !useButton.isWebKit && !isTouchInput && !isPointerLockDenied;
7809
4458
  const element = useRenderElement.useRenderElement('span', componentProps, {
7810
4459
  enabled: shouldRender,
7811
4460
  ref: [forwardedRef, scrubAreaCursorRef, setDomElement],
@@ -7821,7 +4470,7 @@ const NumberFieldScrubAreaCursor = /*#__PURE__*/React__namespace.forwardRef(func
7821
4470
  }, elementProps],
7822
4471
  stateAttributesMapping: NumberFieldInput.stateAttributesMapping
7823
4472
  });
7824
- return element && /*#__PURE__*/ReactDOM__namespace.createPortal(element, clamp.ownerDocument(domElement).body);
4473
+ return element && /*#__PURE__*/ReactDOM__namespace.createPortal(element, useButton.ownerDocument(domElement).body);
7825
4474
  });
7826
4475
  if (process.env.NODE_ENV !== "production") NumberFieldScrubAreaCursor.displayName = "NumberFieldScrubAreaCursor";
7827
4476