@jobber/components 8.21.1 → 8.23.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 (93) hide show
  1. package/Dialog.d.ts +1 -0
  2. package/Dialog.js +17 -0
  3. package/dist/BottomSheet-cjs.js +34 -7
  4. package/dist/BottomSheet-es.js +34 -8
  5. package/dist/CSPContext-cjs.js +69 -0
  6. package/dist/CSPContext-es.js +46 -0
  7. package/dist/Card/index.cjs +3 -0
  8. package/dist/Card/index.mjs +3 -0
  9. package/dist/ComboboxChipRemove-cjs.js +54 -211
  10. package/dist/ComboboxChipRemove-es.js +8 -164
  11. package/dist/DataDump/index.cjs +3 -0
  12. package/dist/DataDump/index.mjs +3 -0
  13. package/dist/Dialog/Dialog.d.ts +3 -0
  14. package/dist/Dialog/Dialog.types.d.ts +205 -0
  15. package/dist/Dialog/DialogBottomSheet.d.ts +28 -0
  16. package/dist/Dialog/DialogModal.d.ts +20 -0
  17. package/dist/Dialog/constants.d.ts +1 -0
  18. package/dist/Dialog/dialogComposableShared.d.ts +53 -0
  19. package/dist/Dialog/dialogReturnFocus.d.ts +43 -0
  20. package/dist/Dialog/index.cjs +33 -0
  21. package/dist/Dialog/index.d.ts +4 -0
  22. package/dist/Dialog/index.mjs +25 -0
  23. package/dist/DrawerDescription-cjs.js +410 -0
  24. package/dist/DrawerDescription-es.js +383 -0
  25. package/dist/DrawerTitle-cjs.js +116 -0
  26. package/dist/DrawerTitle-es.js +92 -0
  27. package/dist/InputNumberExperimental-cjs.js +24 -23
  28. package/dist/InputNumberExperimental-es.js +3 -2
  29. package/dist/InternalBackdrop-es.js +3 -3
  30. package/dist/Menu/index.cjs +3 -0
  31. package/dist/Menu/index.mjs +3 -0
  32. package/dist/Menu-cjs.js +6 -4
  33. package/dist/Menu-es.js +6 -4
  34. package/dist/MenuSubmenuTrigger-cjs.js +53 -103
  35. package/dist/MenuSubmenuTrigger-es.js +55 -103
  36. package/dist/NumberFieldInput-cjs.js +9 -9
  37. package/dist/NumberFieldInput-es.js +3 -3
  38. package/dist/Page/index.cjs +3 -0
  39. package/dist/Page/index.mjs +3 -0
  40. package/dist/ScrollAreaViewport-cjs.js +921 -960
  41. package/dist/ScrollAreaViewport-es.js +917 -958
  42. package/dist/SelectGroupLabel-cjs.js +1924 -0
  43. package/dist/SelectGroupLabel-es.js +1888 -0
  44. package/dist/SelectPrimitive-cjs.js +112 -0
  45. package/dist/SelectPrimitive-es.js +110 -0
  46. package/dist/ToolbarRootContext-cjs.js +35 -0
  47. package/dist/ToolbarRootContext-es.js +14 -0
  48. package/dist/dialogReturnFocus-cjs.js +427 -0
  49. package/dist/dialogReturnFocus-es.js +423 -0
  50. package/dist/docs/SelectPrimitive/SelectPrimitive.md +326 -0
  51. package/dist/docs/index.md +1 -0
  52. package/dist/floating-ui.react-dom-cjs.js +1 -0
  53. package/dist/floating-ui.react-dom-es.js +2 -2
  54. package/dist/floating-ui.utils.dom-es.js +1 -1
  55. package/dist/index.cjs +15 -7
  56. package/dist/index.d.mts +1 -0
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.mjs +12 -7
  59. package/dist/primitives/BottomSheet/BottomSheet.d.ts +19 -1
  60. package/dist/primitives/BottomSheet/BottomSheet.types.d.ts +8 -0
  61. package/dist/primitives/BottomSheet/index.cjs +2 -0
  62. package/dist/primitives/BottomSheet/index.d.ts +1 -1
  63. package/dist/primitives/BottomSheet/index.mjs +2 -1
  64. package/dist/primitives/ComboboxPrimitive/index.cjs +3 -1
  65. package/dist/primitives/ComboboxPrimitive/index.mjs +3 -1
  66. package/dist/primitives/InputNumberExperimental/index.cjs +2 -1
  67. package/dist/primitives/InputNumberExperimental/index.mjs +2 -1
  68. package/dist/primitives/SelectPrimitive/SelectPrimitive.d.ts +63 -0
  69. package/dist/primitives/SelectPrimitive/index.cjs +29 -0
  70. package/dist/primitives/SelectPrimitive/index.d.ts +2 -0
  71. package/dist/primitives/SelectPrimitive/index.mjs +23 -0
  72. package/dist/primitives/SelectPrimitive/types.d.ts +16 -0
  73. package/dist/primitives/index.cjs +9 -1
  74. package/dist/primitives/index.d.ts +1 -0
  75. package/dist/primitives/index.mjs +8 -2
  76. package/dist/primitives/mergeClassName.d.ts +12 -0
  77. package/dist/resolveAriaLabelledBy-cjs.js +195 -0
  78. package/dist/resolveAriaLabelledBy-es.js +162 -0
  79. package/dist/styles.css +685 -0
  80. package/dist/unstyledPrimitives/index.cjs +434 -259
  81. package/dist/unstyledPrimitives/index.d.ts +2 -0
  82. package/dist/unstyledPrimitives/index.mjs +425 -252
  83. package/dist/useButton-cjs.js +2 -0
  84. package/dist/useButton-es.js +3 -2
  85. package/dist/useCompositeListItem-es.js +4 -4
  86. package/dist/useLabel-cjs.js +3 -329
  87. package/dist/useLabel-es.js +4 -299
  88. package/dist/useLabelableId-cjs.js +332 -0
  89. package/dist/useLabelableId-es.js +300 -0
  90. package/dist/useRenderElement-cjs.js +3 -0
  91. package/dist/useRenderElement-es.js +4 -1
  92. package/dist/utils/meta/meta.json +11 -1
  93. package/package.json +2 -2
@@ -0,0 +1,1924 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var useButton = require('./useButton-cjs.js');
5
+ var useRenderElement = require('./useRenderElement-cjs.js');
6
+ var InternalBackdrop = require('./InternalBackdrop-cjs.js');
7
+ var useLabelableId = require('./useLabelableId-cjs.js');
8
+ var resolveAriaLabelledBy = require('./resolveAriaLabelledBy-cjs.js');
9
+ var jsxRuntime = require('react/jsx-runtime');
10
+ var useCompositeListItem = require('./useCompositeListItem-cjs.js');
11
+ var ReactDOM = require('react-dom');
12
+ var index_esm = require('./index.esm-cjs.js');
13
+ var CSPContext = require('./CSPContext-cjs.js');
14
+ var ToolbarRootContext = require('./ToolbarRootContext-cjs.js');
15
+ var clamp = require('./clamp-cjs.js');
16
+ var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
17
+ var floatingUi_reactDom = require('./floating-ui.react-dom-cjs.js');
18
+
19
+ function _interopNamespaceDefault(e) {
20
+ var n = Object.create(null);
21
+ if (e) {
22
+ Object.keys(e).forEach(function (k) {
23
+ if (k !== 'default') {
24
+ var d = Object.getOwnPropertyDescriptor(e, k);
25
+ Object.defineProperty(n, k, d.get ? d : {
26
+ enumerable: true,
27
+ get: function () { return e[k]; }
28
+ });
29
+ }
30
+ });
31
+ }
32
+ n.default = e;
33
+ return Object.freeze(n);
34
+ }
35
+
36
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
37
+ var ReactDOM__namespace = /*#__PURE__*/_interopNamespaceDefault(ReactDOM);
38
+
39
+ const SelectRootContext = /*#__PURE__*/React__namespace.createContext(null);
40
+ if (process.env.NODE_ENV !== "production") SelectRootContext.displayName = "SelectRootContext";
41
+ const SelectFloatingContext = /*#__PURE__*/React__namespace.createContext(null);
42
+ if (process.env.NODE_ENV !== "production") SelectFloatingContext.displayName = "SelectFloatingContext";
43
+ function useSelectRootContext() {
44
+ const context = React__namespace.useContext(SelectRootContext);
45
+ if (context === null) {
46
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectRootContext is missing. Select parts must be placed within <Select.Root>.' : useRenderElement.formatErrorMessage(60));
47
+ }
48
+ return context;
49
+ }
50
+ function useSelectFloatingContext() {
51
+ const context = React__namespace.useContext(SelectFloatingContext);
52
+ if (context === null) {
53
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectFloatingContext is missing. Select parts must be placed within <Select.Root>.' : useRenderElement.formatErrorMessage(61));
54
+ }
55
+ return context;
56
+ }
57
+
58
+ const selectors = {
59
+ id: InternalBackdrop.createSelector(state => state.id),
60
+ labelId: InternalBackdrop.createSelector(state => state.labelId),
61
+ modal: InternalBackdrop.createSelector(state => state.modal),
62
+ multiple: InternalBackdrop.createSelector(state => state.multiple),
63
+ items: InternalBackdrop.createSelector(state => state.items),
64
+ itemToStringLabel: InternalBackdrop.createSelector(state => state.itemToStringLabel),
65
+ itemToStringValue: InternalBackdrop.createSelector(state => state.itemToStringValue),
66
+ isItemEqualToValue: InternalBackdrop.createSelector(state => state.isItemEqualToValue),
67
+ value: InternalBackdrop.createSelector(state => state.value),
68
+ hasSelectedValue: InternalBackdrop.createSelector(state => {
69
+ const {
70
+ value,
71
+ multiple,
72
+ itemToStringValue
73
+ } = state;
74
+ if (value == null) {
75
+ return false;
76
+ }
77
+ if (multiple && Array.isArray(value)) {
78
+ return value.length > 0;
79
+ }
80
+ return resolveAriaLabelledBy.stringifyAsValue(value, itemToStringValue) !== '';
81
+ }),
82
+ hasNullItemLabel: InternalBackdrop.createSelector((state, enabled) => {
83
+ return enabled ? resolveAriaLabelledBy.hasNullItemLabel(state.items) : false;
84
+ }),
85
+ open: InternalBackdrop.createSelector(state => state.open),
86
+ mounted: InternalBackdrop.createSelector(state => state.mounted),
87
+ forceMount: InternalBackdrop.createSelector(state => state.forceMount),
88
+ transitionStatus: InternalBackdrop.createSelector(state => state.transitionStatus),
89
+ openMethod: InternalBackdrop.createSelector(state => state.openMethod),
90
+ activeIndex: InternalBackdrop.createSelector(state => state.activeIndex),
91
+ selectedIndex: InternalBackdrop.createSelector(state => state.selectedIndex),
92
+ isActive: InternalBackdrop.createSelector((state, index) => state.activeIndex === index),
93
+ isSelected: InternalBackdrop.createSelector((state, index, itemValue) => {
94
+ const comparer = state.isItemEqualToValue;
95
+ const storeValue = state.value;
96
+ if (state.multiple) {
97
+ return Array.isArray(storeValue) && storeValue.some(selectedItem => resolveAriaLabelledBy.compareItemEquality(itemValue, selectedItem, comparer));
98
+ }
99
+
100
+ // `selectedIndex` is only updated after the items mount for the first time,
101
+ // the value check avoids a re-render for the initially selected item.
102
+ if (state.selectedIndex === index && state.selectedIndex !== null) {
103
+ return true;
104
+ }
105
+ return resolveAriaLabelledBy.compareItemEquality(itemValue, storeValue, comparer);
106
+ }),
107
+ isSelectedByFocus: InternalBackdrop.createSelector((state, index) => {
108
+ return state.selectedIndex === index;
109
+ }),
110
+ popupProps: InternalBackdrop.createSelector(state => state.popupProps),
111
+ triggerProps: InternalBackdrop.createSelector(state => state.triggerProps),
112
+ triggerElement: InternalBackdrop.createSelector(state => state.triggerElement),
113
+ positionerElement: InternalBackdrop.createSelector(state => state.positionerElement),
114
+ listElement: InternalBackdrop.createSelector(state => state.listElement),
115
+ scrollUpArrowVisible: InternalBackdrop.createSelector(state => state.scrollUpArrowVisible),
116
+ scrollDownArrowVisible: InternalBackdrop.createSelector(state => state.scrollDownArrowVisible),
117
+ hasScrollArrows: InternalBackdrop.createSelector(state => state.hasScrollArrows)
118
+ };
119
+
120
+ function SelectRoot(props) {
121
+ const {
122
+ id,
123
+ value: valueProp,
124
+ defaultValue = null,
125
+ onValueChange,
126
+ open: openProp,
127
+ defaultOpen = false,
128
+ onOpenChange,
129
+ name: nameProp,
130
+ autoComplete,
131
+ disabled: disabledProp = false,
132
+ readOnly = false,
133
+ required = false,
134
+ modal = true,
135
+ actionsRef,
136
+ inputRef,
137
+ onOpenChangeComplete,
138
+ items,
139
+ multiple = false,
140
+ itemToStringLabel,
141
+ itemToStringValue,
142
+ isItemEqualToValue = resolveAriaLabelledBy.defaultItemEquality,
143
+ highlightItemOnHover = true,
144
+ children
145
+ } = props;
146
+ const {
147
+ clearErrors
148
+ } = useLabelableId.useFormContext();
149
+ const {
150
+ setDirty,
151
+ setTouched,
152
+ setFocused,
153
+ shouldValidateOnChange,
154
+ validityData,
155
+ setFilled,
156
+ name: fieldName,
157
+ disabled: fieldDisabled,
158
+ validation,
159
+ validationMode
160
+ } = useLabelableId.useFieldRootContext();
161
+ const generatedId = useLabelableId.useLabelableId({
162
+ id
163
+ });
164
+ const disabled = fieldDisabled || disabledProp;
165
+ const name = fieldName ?? nameProp;
166
+ const [value, setValueUnwrapped] = useButton.useControlled({
167
+ controlled: valueProp,
168
+ default: multiple ? defaultValue ?? useRenderElement.EMPTY_ARRAY : defaultValue,
169
+ name: 'Select',
170
+ state: 'value'
171
+ });
172
+ const [open, setOpenUnwrapped] = useButton.useControlled({
173
+ controlled: openProp,
174
+ default: defaultOpen,
175
+ name: 'Select',
176
+ state: 'open'
177
+ });
178
+ const listRef = React__namespace.useRef([]);
179
+ const labelsRef = React__namespace.useRef([]);
180
+ const popupRef = React__namespace.useRef(null);
181
+ const scrollHandlerRef = React__namespace.useRef(null);
182
+ const scrollArrowsMountedCountRef = React__namespace.useRef(0);
183
+ const valueRef = React__namespace.useRef(null);
184
+ const valuesRef = React__namespace.useRef([]);
185
+ const typingRef = React__namespace.useRef(false);
186
+ const keyboardActiveRef = React__namespace.useRef(false);
187
+ const selectedItemTextRef = React__namespace.useRef(null);
188
+ const selectionRef = React__namespace.useRef({
189
+ allowSelectedMouseUp: false,
190
+ allowUnselectedMouseUp: false
191
+ });
192
+ const alignItemWithTriggerActiveRef = React__namespace.useRef(false);
193
+ const {
194
+ mounted,
195
+ setMounted,
196
+ transitionStatus
197
+ } = useButton.useTransitionStatus(open);
198
+ const {
199
+ openMethod,
200
+ triggerProps: interactionTypeProps
201
+ } = InternalBackdrop.useOpenInteractionType(open);
202
+ const store = useRenderElement.useRefWithInit(() => new InternalBackdrop.Store({
203
+ id: generatedId,
204
+ labelId: undefined,
205
+ modal,
206
+ multiple,
207
+ itemToStringLabel,
208
+ itemToStringValue,
209
+ isItemEqualToValue,
210
+ value,
211
+ open,
212
+ mounted,
213
+ transitionStatus,
214
+ items,
215
+ forceMount: false,
216
+ openMethod: null,
217
+ activeIndex: null,
218
+ selectedIndex: null,
219
+ popupProps: {},
220
+ triggerProps: {},
221
+ triggerElement: null,
222
+ positionerElement: null,
223
+ listElement: null,
224
+ scrollUpArrowVisible: false,
225
+ scrollDownArrowVisible: false,
226
+ hasScrollArrows: false
227
+ })).current;
228
+ const activeIndex = InternalBackdrop.useStore(store, selectors.activeIndex);
229
+ const selectedIndex = InternalBackdrop.useStore(store, selectors.selectedIndex);
230
+ const triggerElement = InternalBackdrop.useStore(store, selectors.triggerElement);
231
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
232
+ const serializedValue = React__namespace.useMemo(() => {
233
+ if (multiple && Array.isArray(value) && value.length === 0) {
234
+ return '';
235
+ }
236
+ return resolveAriaLabelledBy.stringifyAsValue(value, itemToStringValue);
237
+ }, [multiple, value, itemToStringValue]);
238
+ const fieldStringValue = React__namespace.useMemo(() => {
239
+ if (multiple && Array.isArray(value)) {
240
+ return value.map(currentValue => resolveAriaLabelledBy.stringifyAsValue(currentValue, itemToStringValue));
241
+ }
242
+ return resolveAriaLabelledBy.stringifyAsValue(value, itemToStringValue);
243
+ }, [multiple, value, itemToStringValue]);
244
+ const controlRef = useButton.useValueAsRef(store.state.triggerElement);
245
+ useLabelableId.useField({
246
+ id: generatedId,
247
+ commit: validation.commit,
248
+ value,
249
+ controlRef,
250
+ name,
251
+ getValue: () => fieldStringValue
252
+ });
253
+ const initialValueRef = React__namespace.useRef(value);
254
+ useButton.useIsoLayoutEffect(() => {
255
+ // Ensure the values and labels are registered for programmatic value changes.
256
+ if (value !== initialValueRef.current) {
257
+ store.set('forceMount', true);
258
+ }
259
+ }, [store, value]);
260
+ useButton.useIsoLayoutEffect(() => {
261
+ setFilled(multiple ? Array.isArray(value) && value.length > 0 : value != null);
262
+ }, [multiple, value, setFilled]);
263
+ useButton.useIsoLayoutEffect(function syncSelectedIndex() {
264
+ if (open) {
265
+ return;
266
+ }
267
+ const registry = valuesRef.current;
268
+ if (multiple) {
269
+ const currentValue = Array.isArray(value) ? value : [];
270
+ if (currentValue.length === 0) {
271
+ store.set('selectedIndex', null);
272
+ return;
273
+ }
274
+ const lastValue = currentValue[currentValue.length - 1];
275
+ const lastIndex = resolveAriaLabelledBy.findItemIndex(registry, lastValue, isItemEqualToValue);
276
+ store.set('selectedIndex', lastIndex === -1 ? null : lastIndex);
277
+ return;
278
+ }
279
+ const index = resolveAriaLabelledBy.findItemIndex(registry, value, isItemEqualToValue);
280
+ store.set('selectedIndex', index === -1 ? null : index);
281
+ }, [multiple, open, value, valuesRef, isItemEqualToValue, store]);
282
+ useButton.useValueChanged(value, () => {
283
+ clearErrors(name);
284
+ setDirty(value !== validityData.initialValue);
285
+ if (shouldValidateOnChange()) {
286
+ validation.commit(value);
287
+ } else {
288
+ validation.commit(value, true);
289
+ }
290
+ });
291
+ const setOpen = useButton.useStableCallback((nextOpen, eventDetails) => {
292
+ onOpenChange?.(nextOpen, eventDetails);
293
+ if (eventDetails.isCanceled) {
294
+ return;
295
+ }
296
+ setOpenUnwrapped(nextOpen);
297
+ if (!nextOpen && (eventDetails.reason === useButton.focusOut || eventDetails.reason === useButton.outsidePress)) {
298
+ setTouched(true);
299
+ setFocused(false);
300
+ if (validationMode === 'onBlur') {
301
+ validation.commit(value);
302
+ }
303
+ }
304
+
305
+ // The active index will sync to the last selected index on the next open.
306
+ // Workaround `enableFocusInside` in Floating UI setting `tabindex=0` of a non-highlighted
307
+ // option upon close when tabbing out due to `keepMounted=true`:
308
+ // https://github.com/floating-ui/floating-ui/pull/3004/files#diff-962a7439cdeb09ea98d4b622a45d517bce07ad8c3f866e089bda05f4b0bbd875R194-R199
309
+ // This otherwise causes options to retain `tabindex=0` incorrectly when the popup is closed
310
+ // when tabbing outside.
311
+ if (!nextOpen && store.state.activeIndex !== null) {
312
+ const activeOption = listRef.current[store.state.activeIndex];
313
+ // Wait for Floating UI's focus effect to have fired
314
+ queueMicrotask(() => {
315
+ activeOption?.setAttribute('tabindex', '-1');
316
+ });
317
+ }
318
+ });
319
+ const handleUnmount = useButton.useStableCallback(() => {
320
+ setMounted(false);
321
+ store.set('activeIndex', null);
322
+ onOpenChangeComplete?.(false);
323
+ });
324
+ useButton.useOpenChangeComplete({
325
+ enabled: !actionsRef,
326
+ open,
327
+ ref: popupRef,
328
+ onComplete() {
329
+ if (!open) {
330
+ handleUnmount();
331
+ }
332
+ }
333
+ });
334
+ React__namespace.useImperativeHandle(actionsRef, () => ({
335
+ unmount: handleUnmount
336
+ }), [handleUnmount]);
337
+ const setValue = useButton.useStableCallback((nextValue, eventDetails) => {
338
+ onValueChange?.(nextValue, eventDetails);
339
+ if (eventDetails.isCanceled) {
340
+ return;
341
+ }
342
+ setValueUnwrapped(nextValue);
343
+ });
344
+ const handleScrollArrowVisibility = useButton.useStableCallback(() => {
345
+ const scroller = store.state.listElement || popupRef.current;
346
+ if (!scroller) {
347
+ return;
348
+ }
349
+ const viewportTop = scroller.scrollTop;
350
+ const viewportBottom = scroller.scrollTop + scroller.clientHeight;
351
+ const shouldShowUp = viewportTop > 1;
352
+ const shouldShowDown = viewportBottom < scroller.scrollHeight - 1;
353
+ if (store.state.scrollUpArrowVisible !== shouldShowUp) {
354
+ store.set('scrollUpArrowVisible', shouldShowUp);
355
+ }
356
+ if (store.state.scrollDownArrowVisible !== shouldShowDown) {
357
+ store.set('scrollDownArrowVisible', shouldShowDown);
358
+ }
359
+ });
360
+ const floatingContext = useCompositeListItem.useFloatingRootContext({
361
+ open,
362
+ onOpenChange: setOpen,
363
+ elements: {
364
+ reference: triggerElement,
365
+ floating: positionerElement
366
+ }
367
+ });
368
+ const click = InternalBackdrop.useClick(floatingContext, {
369
+ enabled: !readOnly && !disabled,
370
+ event: 'mousedown'
371
+ });
372
+ const dismiss = InternalBackdrop.useDismiss(floatingContext, {
373
+ bubbles: false
374
+ });
375
+ const listNavigation = useCompositeListItem.useListNavigation(floatingContext, {
376
+ enabled: !readOnly && !disabled,
377
+ listRef,
378
+ activeIndex,
379
+ selectedIndex,
380
+ disabledIndices: useRenderElement.EMPTY_ARRAY,
381
+ onNavigate(nextActiveIndex) {
382
+ // Retain the highlight while transitioning out.
383
+ if (nextActiveIndex === null && !open) {
384
+ return;
385
+ }
386
+ store.set('activeIndex', nextActiveIndex);
387
+ },
388
+ // Implement our own listeners since `onPointerLeave` on each option fires while scrolling with
389
+ // the `alignItemWithTrigger=true`, causing a performance issue on Chrome.
390
+ focusItemOnHover: false
391
+ });
392
+ const typeahead = useCompositeListItem.useTypeahead(floatingContext, {
393
+ enabled: !readOnly && !disabled && (open || !multiple),
394
+ listRef: labelsRef,
395
+ activeIndex,
396
+ selectedIndex,
397
+ onMatch(index) {
398
+ if (open) {
399
+ store.set('activeIndex', index);
400
+ } else {
401
+ setValue(valuesRef.current[index], useButton.createChangeEventDetails('none'));
402
+ }
403
+ },
404
+ onTypingChange(typing) {
405
+ typingRef.current = typing;
406
+ }
407
+ });
408
+ const {
409
+ getReferenceProps,
410
+ getFloatingProps,
411
+ getItemProps
412
+ } = InternalBackdrop.useInteractions([click, dismiss, listNavigation, typeahead]);
413
+ const mergedTriggerProps = React__namespace.useMemo(() => {
414
+ return useRenderElement.mergeProps(getReferenceProps(), interactionTypeProps, generatedId ? {
415
+ id: generatedId
416
+ } : useRenderElement.EMPTY_OBJECT);
417
+ }, [getReferenceProps, interactionTypeProps, generatedId]);
418
+ InternalBackdrop.useOnFirstRender(() => {
419
+ store.update({
420
+ popupProps: getFloatingProps(),
421
+ triggerProps: mergedTriggerProps
422
+ });
423
+ });
424
+ useButton.useIsoLayoutEffect(() => {
425
+ store.update({
426
+ id: generatedId,
427
+ modal,
428
+ multiple,
429
+ value,
430
+ open,
431
+ mounted,
432
+ transitionStatus,
433
+ popupProps: getFloatingProps(),
434
+ triggerProps: mergedTriggerProps,
435
+ items,
436
+ itemToStringLabel,
437
+ itemToStringValue,
438
+ isItemEqualToValue,
439
+ openMethod
440
+ });
441
+ }, [store, generatedId, modal, multiple, value, open, mounted, transitionStatus, getFloatingProps, mergedTriggerProps, items, itemToStringLabel, itemToStringValue, isItemEqualToValue, openMethod]);
442
+ const contextValue = React__namespace.useMemo(() => ({
443
+ store,
444
+ name,
445
+ required,
446
+ disabled,
447
+ readOnly,
448
+ multiple,
449
+ itemToStringLabel,
450
+ itemToStringValue,
451
+ highlightItemOnHover,
452
+ setValue,
453
+ setOpen,
454
+ listRef,
455
+ popupRef,
456
+ scrollHandlerRef,
457
+ handleScrollArrowVisibility,
458
+ scrollArrowsMountedCountRef,
459
+ getItemProps,
460
+ events: floatingContext.context.events,
461
+ valueRef,
462
+ valuesRef,
463
+ labelsRef,
464
+ typingRef,
465
+ selectionRef,
466
+ selectedItemTextRef,
467
+ validation,
468
+ onOpenChangeComplete,
469
+ keyboardActiveRef,
470
+ alignItemWithTriggerActiveRef,
471
+ initialValueRef
472
+ }), [store, name, required, disabled, readOnly, multiple, itemToStringLabel, itemToStringValue, highlightItemOnHover, setValue, setOpen, getItemProps, floatingContext.context.events, validation, onOpenChangeComplete, handleScrollArrowVisibility]);
473
+ const ref = useRenderElement.useMergedRefs(inputRef, validation.inputRef);
474
+ const hasMultipleSelection = multiple && Array.isArray(value) && value.length > 0;
475
+ const hiddenInputName = multiple ? undefined : name;
476
+ const hiddenInputs = React__namespace.useMemo(() => {
477
+ if (!multiple || !Array.isArray(value) || !name) {
478
+ return null;
479
+ }
480
+ return value.map(v => {
481
+ const currentSerializedValue = resolveAriaLabelledBy.stringifyAsValue(v, itemToStringValue);
482
+ return /*#__PURE__*/jsxRuntime.jsx("input", {
483
+ type: "hidden",
484
+ name: name,
485
+ value: currentSerializedValue
486
+ }, currentSerializedValue);
487
+ });
488
+ }, [multiple, value, name, itemToStringValue]);
489
+ return /*#__PURE__*/jsxRuntime.jsx(SelectRootContext.Provider, {
490
+ value: contextValue,
491
+ children: /*#__PURE__*/jsxRuntime.jsxs(SelectFloatingContext.Provider, {
492
+ value: floatingContext,
493
+ children: [children, /*#__PURE__*/jsxRuntime.jsx("input", {
494
+ ...validation.getInputValidationProps({
495
+ onFocus() {
496
+ // Move focus to the trigger element when the hidden input is focused.
497
+ store.state.triggerElement?.focus({
498
+ // Supported in Chrome from 144 (January 2026)
499
+ // @ts-expect-error - focusVisible is not yet in the lib.dom.d.ts
500
+ focusVisible: true
501
+ });
502
+ },
503
+ // Handle browser autofill.
504
+ onChange(event) {
505
+ // Workaround for https://github.com/facebook/react/issues/9023
506
+ if (event.nativeEvent.defaultPrevented) {
507
+ return;
508
+ }
509
+ const nextValue = event.target.value;
510
+ const details = useButton.createChangeEventDetails(useButton.none, event.nativeEvent);
511
+ function handleChange() {
512
+ if (multiple) {
513
+ // Browser autofill only writes a single scalar value.
514
+ return;
515
+ }
516
+
517
+ // Handle single selection: match against registered values using serialization
518
+ const matchingValue = valuesRef.current.find(v => {
519
+ const candidate = resolveAriaLabelledBy.stringifyAsValue(v, itemToStringValue);
520
+ if (candidate.toLowerCase() === nextValue.toLowerCase()) {
521
+ return true;
522
+ }
523
+ return false;
524
+ });
525
+ if (matchingValue != null) {
526
+ setDirty(matchingValue !== validityData.initialValue);
527
+ setValue(matchingValue, details);
528
+ if (shouldValidateOnChange()) {
529
+ validation.commit(matchingValue);
530
+ }
531
+ }
532
+ }
533
+ store.set('forceMount', true);
534
+ queueMicrotask(handleChange);
535
+ }
536
+ }),
537
+ id: generatedId && hiddenInputName == null ? `${generatedId}-hidden-input` : undefined,
538
+ name: hiddenInputName,
539
+ autoComplete: autoComplete,
540
+ value: serializedValue,
541
+ disabled: disabled,
542
+ required: required && !hasMultipleSelection,
543
+ readOnly: readOnly,
544
+ ref: ref,
545
+ style: name ? useButton.visuallyHiddenInput : useButton.visuallyHidden,
546
+ tabIndex: -1,
547
+ "aria-hidden": true
548
+ }), hiddenInputs]
549
+ })
550
+ });
551
+ }
552
+
553
+ const BOUNDARY_OFFSET = 2;
554
+ const SELECTED_DELAY = 400;
555
+ const UNSELECTED_DELAY = 200;
556
+ const stateAttributesMapping$2 = {
557
+ ...InternalBackdrop.pressableTriggerOpenStateMapping,
558
+ ...useLabelableId.fieldValidityMapping,
559
+ value: () => null
560
+ };
561
+
562
+ /**
563
+ * A button that opens the select popup.
564
+ * Renders a `<button>` element.
565
+ *
566
+ * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
567
+ */
568
+ const SelectTrigger = /*#__PURE__*/React__namespace.forwardRef(function SelectTrigger(componentProps, forwardedRef) {
569
+ const {
570
+ render,
571
+ className,
572
+ id: idProp,
573
+ disabled: disabledProp = false,
574
+ nativeButton = true,
575
+ ...elementProps
576
+ } = componentProps;
577
+ const {
578
+ setTouched,
579
+ setFocused,
580
+ validationMode,
581
+ state: fieldState,
582
+ disabled: fieldDisabled
583
+ } = useLabelableId.useFieldRootContext();
584
+ const {
585
+ labelId: fieldLabelId
586
+ } = useLabelableId.useLabelableContext();
587
+ const {
588
+ store,
589
+ setOpen,
590
+ selectionRef,
591
+ validation,
592
+ readOnly,
593
+ required,
594
+ alignItemWithTriggerActiveRef,
595
+ disabled: selectDisabled,
596
+ keyboardActiveRef
597
+ } = useSelectRootContext();
598
+ const disabled = fieldDisabled || selectDisabled || disabledProp;
599
+ const open = InternalBackdrop.useStore(store, selectors.open);
600
+ const value = InternalBackdrop.useStore(store, selectors.value);
601
+ const triggerProps = InternalBackdrop.useStore(store, selectors.triggerProps);
602
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
603
+ const listElement = InternalBackdrop.useStore(store, selectors.listElement);
604
+ const rootId = InternalBackdrop.useStore(store, selectors.id);
605
+ const selectLabelId = InternalBackdrop.useStore(store, selectors.labelId);
606
+ const hasSelectedValue = InternalBackdrop.useStore(store, selectors.hasSelectedValue);
607
+ const shouldCheckNullItemLabel = !hasSelectedValue && open;
608
+ const hasNullItemLabel = InternalBackdrop.useStore(store, selectors.hasNullItemLabel, shouldCheckNullItemLabel);
609
+ const id = idProp ?? rootId;
610
+ const ariaLabelledBy = resolveAriaLabelledBy.resolveAriaLabelledBy(fieldLabelId, selectLabelId);
611
+ useLabelableId.useLabelableId({
612
+ id
613
+ });
614
+ const positionerRef = useButton.useValueAsRef(positionerElement);
615
+ const triggerRef = React__namespace.useRef(null);
616
+ const {
617
+ getButtonProps,
618
+ buttonRef
619
+ } = useButton.useButton({
620
+ disabled,
621
+ native: nativeButton
622
+ });
623
+ const setTriggerElement = useButton.useStableCallback(element => {
624
+ store.set('triggerElement', element);
625
+ });
626
+ const mergedRef = useRenderElement.useMergedRefs(forwardedRef, triggerRef, buttonRef, setTriggerElement);
627
+ const timeoutFocus = useButton.useTimeout();
628
+ const timeoutMouseDown = useButton.useTimeout();
629
+ const selectedDelayTimeout = useButton.useTimeout();
630
+ const unselectedDelayTimeout = useButton.useTimeout();
631
+ React__namespace.useEffect(() => {
632
+ if (open) {
633
+ const hasSelectedItemInList = hasSelectedValue || hasNullItemLabel;
634
+ const shouldDelayUnselectedMouseUpLonger = !hasSelectedItemInList;
635
+
636
+ // When there is no selected item in the list (placeholder-only selects), a mousedown
637
+ // on the trigger followed by a quick mouseup over the first option can accidentally select
638
+ // within 200ms. Delay unselected mouseup to match the safer 400ms window.
639
+ if (shouldDelayUnselectedMouseUpLonger) {
640
+ selectedDelayTimeout.start(SELECTED_DELAY, () => {
641
+ selectionRef.current.allowUnselectedMouseUp = true;
642
+ selectionRef.current.allowSelectedMouseUp = true;
643
+ });
644
+ } else {
645
+ // mousedown -> move to unselected item -> mouseup should not select within 200ms.
646
+ unselectedDelayTimeout.start(UNSELECTED_DELAY, () => {
647
+ selectionRef.current.allowUnselectedMouseUp = true;
648
+
649
+ // mousedown -> mouseup on selected item should not select within 400ms.
650
+ selectedDelayTimeout.start(UNSELECTED_DELAY, () => {
651
+ selectionRef.current.allowSelectedMouseUp = true;
652
+ });
653
+ });
654
+ }
655
+ return () => {
656
+ selectedDelayTimeout.clear();
657
+ unselectedDelayTimeout.clear();
658
+ };
659
+ }
660
+ selectionRef.current = {
661
+ allowSelectedMouseUp: false,
662
+ allowUnselectedMouseUp: false
663
+ };
664
+ timeoutMouseDown.clear();
665
+ return undefined;
666
+ }, [open, hasSelectedValue, hasNullItemLabel, selectionRef, timeoutMouseDown, selectedDelayTimeout, unselectedDelayTimeout]);
667
+ const ariaControlsId = React__namespace.useMemo(() => {
668
+ return listElement?.id ?? useButton.getFloatingFocusElement(positionerElement)?.id;
669
+ }, [listElement, positionerElement]);
670
+ const props = useRenderElement.mergeProps(triggerProps, {
671
+ id,
672
+ role: 'combobox',
673
+ 'aria-expanded': open ? 'true' : 'false',
674
+ 'aria-haspopup': 'listbox',
675
+ 'aria-controls': open ? ariaControlsId : undefined,
676
+ 'aria-labelledby': ariaLabelledBy,
677
+ 'aria-readonly': readOnly || undefined,
678
+ 'aria-required': required || undefined,
679
+ tabIndex: disabled ? -1 : 0,
680
+ ref: mergedRef,
681
+ onFocus(event) {
682
+ setFocused(true);
683
+
684
+ // The popup element shouldn't obscure the focused trigger.
685
+ if (open && alignItemWithTriggerActiveRef.current) {
686
+ setOpen(false, useButton.createChangeEventDetails(useButton.none, event.nativeEvent));
687
+ }
688
+
689
+ // Saves a re-render on initial click: `forceMount === true` mounts
690
+ // the items before `open === true`. We could sync those cycles better
691
+ // without a timeout, but this is enough for now.
692
+ //
693
+ // XXX: might be causing `act()` warnings.
694
+ timeoutFocus.start(0, () => {
695
+ store.set('forceMount', true);
696
+ });
697
+ },
698
+ onBlur(event) {
699
+ // If focus is moving into the popup, don't count it as a blur.
700
+ if (useButton.contains(positionerElement, event.relatedTarget)) {
701
+ return;
702
+ }
703
+ setTouched(true);
704
+ setFocused(false);
705
+ if (validationMode === 'onBlur') {
706
+ validation.commit(value);
707
+ }
708
+ },
709
+ onPointerMove() {
710
+ keyboardActiveRef.current = false;
711
+ },
712
+ onKeyDown() {
713
+ keyboardActiveRef.current = true;
714
+ },
715
+ onMouseDown(event) {
716
+ if (open) {
717
+ return;
718
+ }
719
+ const doc = useButton.ownerDocument(event.currentTarget);
720
+ function handleMouseUp(mouseEvent) {
721
+ if (!triggerRef.current) {
722
+ return;
723
+ }
724
+ const mouseUpTarget = mouseEvent.target;
725
+
726
+ // Early return if clicked on trigger element or its children
727
+ if (useButton.contains(triggerRef.current, mouseUpTarget) || useButton.contains(positionerRef.current, mouseUpTarget) || mouseUpTarget === triggerRef.current) {
728
+ return;
729
+ }
730
+ const bounds = useCompositeListItem.getPseudoElementBounds(triggerRef.current);
731
+ if (mouseEvent.clientX >= bounds.left - BOUNDARY_OFFSET && mouseEvent.clientX <= bounds.right + BOUNDARY_OFFSET && mouseEvent.clientY >= bounds.top - BOUNDARY_OFFSET && mouseEvent.clientY <= bounds.bottom + BOUNDARY_OFFSET) {
732
+ return;
733
+ }
734
+ setOpen(false, useButton.createChangeEventDetails(useButton.cancelOpen, mouseEvent));
735
+ }
736
+
737
+ // Firefox can fire this upon mousedown
738
+ timeoutMouseDown.start(0, () => {
739
+ doc.addEventListener('mouseup', handleMouseUp, {
740
+ once: true
741
+ });
742
+ });
743
+ }
744
+ }, validation.getValidationProps, elementProps, getButtonProps);
745
+
746
+ // ensure nested useButton does not overwrite the combobox role:
747
+ // <Toolbar.Button render={<Select.Trigger />} />
748
+ props.role = 'combobox';
749
+ const state = {
750
+ ...fieldState,
751
+ open,
752
+ disabled,
753
+ value,
754
+ readOnly,
755
+ placeholder: !hasSelectedValue
756
+ };
757
+ return useRenderElement.useRenderElement('button', componentProps, {
758
+ ref: [forwardedRef, triggerRef],
759
+ state,
760
+ stateAttributesMapping: stateAttributesMapping$2,
761
+ props
762
+ });
763
+ });
764
+ if (process.env.NODE_ENV !== "production") SelectTrigger.displayName = "SelectTrigger";
765
+
766
+ const stateAttributesMapping$1 = {
767
+ value: () => null
768
+ };
769
+
770
+ /**
771
+ * A text label of the currently selected item.
772
+ * Renders a `<span>` element.
773
+ *
774
+ * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
775
+ */
776
+ const SelectValue = /*#__PURE__*/React__namespace.forwardRef(function SelectValue(componentProps, forwardedRef) {
777
+ const {
778
+ className,
779
+ render,
780
+ children: childrenProp,
781
+ placeholder,
782
+ ...elementProps
783
+ } = componentProps;
784
+ const {
785
+ store,
786
+ valueRef
787
+ } = useSelectRootContext();
788
+ const value = InternalBackdrop.useStore(store, selectors.value);
789
+ const items = InternalBackdrop.useStore(store, selectors.items);
790
+ const itemToStringLabel = InternalBackdrop.useStore(store, selectors.itemToStringLabel);
791
+ const hasSelectedValue = InternalBackdrop.useStore(store, selectors.hasSelectedValue);
792
+ const shouldCheckNullItemLabel = !hasSelectedValue && placeholder != null && childrenProp == null;
793
+ const hasNullLabel = InternalBackdrop.useStore(store, selectors.hasNullItemLabel, shouldCheckNullItemLabel);
794
+ const state = {
795
+ value,
796
+ placeholder: !hasSelectedValue
797
+ };
798
+ let children = null;
799
+ if (typeof childrenProp === 'function') {
800
+ children = childrenProp(value);
801
+ } else if (childrenProp != null) {
802
+ children = childrenProp;
803
+ } else if (!hasSelectedValue && placeholder != null && !hasNullLabel) {
804
+ children = placeholder;
805
+ } else if (Array.isArray(value)) {
806
+ children = resolveAriaLabelledBy.resolveMultipleLabels(value, items, itemToStringLabel);
807
+ } else {
808
+ children = resolveAriaLabelledBy.resolveSelectedLabel(value, items, itemToStringLabel);
809
+ }
810
+ const element = useRenderElement.useRenderElement('span', componentProps, {
811
+ state,
812
+ ref: [forwardedRef, valueRef],
813
+ props: [{
814
+ children
815
+ }, elementProps],
816
+ stateAttributesMapping: stateAttributesMapping$1
817
+ });
818
+ return element;
819
+ });
820
+ if (process.env.NODE_ENV !== "production") SelectValue.displayName = "SelectValue";
821
+
822
+ const SelectPortalContext = /*#__PURE__*/React__namespace.createContext(undefined);
823
+ if (process.env.NODE_ENV !== "production") SelectPortalContext.displayName = "SelectPortalContext";
824
+
825
+ const SelectPortal = /*#__PURE__*/React__namespace.forwardRef(function SelectPortal(portalProps, forwardedRef) {
826
+ const {
827
+ store
828
+ } = useSelectRootContext();
829
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
830
+ const forceMount = InternalBackdrop.useStore(store, selectors.forceMount);
831
+ const shouldRender = mounted || forceMount;
832
+ if (!shouldRender) {
833
+ return null;
834
+ }
835
+ return /*#__PURE__*/jsxRuntime.jsx(SelectPortalContext.Provider, {
836
+ value: true,
837
+ children: /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.FloatingPortal, {
838
+ ref: forwardedRef,
839
+ ...portalProps
840
+ })
841
+ });
842
+ });
843
+ if (process.env.NODE_ENV !== "production") SelectPortal.displayName = "SelectPortal";
844
+
845
+ const SelectPositionerContext = /*#__PURE__*/React__namespace.createContext(undefined);
846
+ if (process.env.NODE_ENV !== "production") SelectPositionerContext.displayName = "SelectPositionerContext";
847
+ function useSelectPositionerContext() {
848
+ const context = React__namespace.useContext(SelectPositionerContext);
849
+ if (!context) {
850
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectPositionerContext is missing. SelectPositioner parts must be placed within <Select.Positioner>.' : useRenderElement.formatErrorMessage(59));
851
+ }
852
+ return context;
853
+ }
854
+
855
+ function clearStyles(element, originalStyles) {
856
+ if (element) {
857
+ Object.assign(element.style, originalStyles);
858
+ }
859
+ }
860
+ const LIST_FUNCTIONAL_STYLES = {
861
+ position: 'relative',
862
+ maxHeight: '100%',
863
+ overflowX: 'hidden',
864
+ overflowY: 'auto'
865
+ };
866
+
867
+ const FIXED = {
868
+ position: 'fixed'
869
+ };
870
+
871
+ /**
872
+ * Positions the select popup.
873
+ * Renders a `<div>` element.
874
+ *
875
+ * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
876
+ */
877
+ const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function SelectPositioner(componentProps, forwardedRef) {
878
+ const {
879
+ anchor,
880
+ positionMethod = 'absolute',
881
+ className,
882
+ render,
883
+ side = 'bottom',
884
+ align = 'center',
885
+ sideOffset = 0,
886
+ alignOffset = 0,
887
+ collisionBoundary = 'clipping-ancestors',
888
+ collisionPadding,
889
+ arrowPadding = 5,
890
+ sticky = false,
891
+ disableAnchorTracking,
892
+ alignItemWithTrigger = true,
893
+ collisionAvoidance = InternalBackdrop.DROPDOWN_COLLISION_AVOIDANCE,
894
+ ...elementProps
895
+ } = componentProps;
896
+ const {
897
+ store,
898
+ listRef,
899
+ labelsRef,
900
+ alignItemWithTriggerActiveRef,
901
+ selectedItemTextRef,
902
+ valuesRef,
903
+ initialValueRef,
904
+ popupRef,
905
+ setValue
906
+ } = useSelectRootContext();
907
+ const floatingRootContext = useSelectFloatingContext();
908
+ const open = InternalBackdrop.useStore(store, selectors.open);
909
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
910
+ const modal = InternalBackdrop.useStore(store, selectors.modal);
911
+ const value = InternalBackdrop.useStore(store, selectors.value);
912
+ const openMethod = InternalBackdrop.useStore(store, selectors.openMethod);
913
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
914
+ const triggerElement = InternalBackdrop.useStore(store, selectors.triggerElement);
915
+ const isItemEqualToValue = InternalBackdrop.useStore(store, selectors.isItemEqualToValue);
916
+ const transitionStatus = InternalBackdrop.useStore(store, selectors.transitionStatus);
917
+ const scrollUpArrowRef = React__namespace.useRef(null);
918
+ const scrollDownArrowRef = React__namespace.useRef(null);
919
+ const [controlledAlignItemWithTrigger, setControlledAlignItemWithTrigger] = React__namespace.useState(alignItemWithTrigger);
920
+ const alignItemWithTriggerActive = mounted && controlledAlignItemWithTrigger && openMethod !== 'touch';
921
+ if (!mounted && controlledAlignItemWithTrigger !== alignItemWithTrigger) {
922
+ setControlledAlignItemWithTrigger(alignItemWithTrigger);
923
+ }
924
+ useButton.useIsoLayoutEffect(() => {
925
+ if (!mounted) {
926
+ if (selectors.scrollUpArrowVisible(store.state)) {
927
+ store.set('scrollUpArrowVisible', false);
928
+ }
929
+ if (selectors.scrollDownArrowVisible(store.state)) {
930
+ store.set('scrollDownArrowVisible', false);
931
+ }
932
+ }
933
+ }, [store, mounted]);
934
+ React__namespace.useImperativeHandle(alignItemWithTriggerActiveRef, () => alignItemWithTriggerActive);
935
+ InternalBackdrop.useScrollLock((alignItemWithTriggerActive || modal) && open && openMethod !== 'touch', triggerElement);
936
+ const positioning = useCompositeListItem.useAnchorPositioning({
937
+ anchor,
938
+ floatingRootContext,
939
+ positionMethod,
940
+ mounted,
941
+ side,
942
+ sideOffset,
943
+ align,
944
+ alignOffset,
945
+ arrowPadding,
946
+ collisionBoundary,
947
+ collisionPadding,
948
+ sticky,
949
+ disableAnchorTracking: disableAnchorTracking ?? alignItemWithTriggerActive,
950
+ collisionAvoidance,
951
+ keepMounted: true
952
+ });
953
+ const renderedSide = alignItemWithTriggerActive ? 'none' : positioning.side;
954
+ const positionerStyles = alignItemWithTriggerActive ? FIXED : positioning.positionerStyles;
955
+ const defaultProps = React__namespace.useMemo(() => {
956
+ const hiddenStyles = {};
957
+ if (!open) {
958
+ hiddenStyles.pointerEvents = 'none';
959
+ }
960
+ return {
961
+ role: 'presentation',
962
+ hidden: !mounted,
963
+ style: {
964
+ ...positionerStyles,
965
+ ...hiddenStyles
966
+ }
967
+ };
968
+ }, [open, mounted, positionerStyles]);
969
+ const state = {
970
+ open,
971
+ side: renderedSide,
972
+ align: positioning.align,
973
+ anchorHidden: positioning.anchorHidden
974
+ };
975
+ const setPositionerElement = useButton.useStableCallback(element => {
976
+ store.set('positionerElement', element);
977
+ });
978
+ const element = useRenderElement.useRenderElement('div', componentProps, {
979
+ ref: [forwardedRef, setPositionerElement],
980
+ state,
981
+ stateAttributesMapping: InternalBackdrop.popupStateMapping,
982
+ props: [defaultProps, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), elementProps]
983
+ });
984
+ const prevMapSizeRef = React__namespace.useRef(0);
985
+ const onMapChange = useButton.useStableCallback(map => {
986
+ if (map.size === 0 && prevMapSizeRef.current === 0) {
987
+ return;
988
+ }
989
+ if (valuesRef.current.length === 0) {
990
+ return;
991
+ }
992
+ const prevSize = prevMapSizeRef.current;
993
+ prevMapSizeRef.current = map.size;
994
+ if (map.size === prevSize) {
995
+ return;
996
+ }
997
+ const eventDetails = useButton.createChangeEventDetails(useButton.none);
998
+ if (prevSize !== 0 && !store.state.multiple && value !== null) {
999
+ const selectedValueIndex = resolveAriaLabelledBy.findItemIndex(valuesRef.current, value, isItemEqualToValue);
1000
+ if (selectedValueIndex === -1) {
1001
+ const initialSelectedValue = initialValueRef.current;
1002
+ const hasInitial = initialSelectedValue != null && resolveAriaLabelledBy.findItemIndex(valuesRef.current, initialSelectedValue, isItemEqualToValue) !== -1;
1003
+ const nextValue = hasInitial ? initialSelectedValue : null;
1004
+ setValue(nextValue, eventDetails);
1005
+ if (nextValue === null) {
1006
+ store.set('selectedIndex', null);
1007
+ selectedItemTextRef.current = null;
1008
+ }
1009
+ }
1010
+ }
1011
+ if (prevSize !== 0 && store.state.multiple && Array.isArray(value)) {
1012
+ const hasVisibleItem = selectedItemValue => resolveAriaLabelledBy.findItemIndex(valuesRef.current, selectedItemValue, isItemEqualToValue) !== -1;
1013
+ const nextValue = value.filter(selectedItemValue => hasVisibleItem(selectedItemValue));
1014
+ if (nextValue.length !== value.length || nextValue.some(selectedItemValue => !resolveAriaLabelledBy.selectedValueIncludes(value, selectedItemValue, isItemEqualToValue))) {
1015
+ setValue(nextValue, eventDetails);
1016
+ if (nextValue.length === 0) {
1017
+ store.set('selectedIndex', null);
1018
+ selectedItemTextRef.current = null;
1019
+ }
1020
+ }
1021
+ }
1022
+ if (open && alignItemWithTriggerActive) {
1023
+ store.update({
1024
+ scrollUpArrowVisible: false,
1025
+ scrollDownArrowVisible: false
1026
+ });
1027
+ const stylesToClear = {
1028
+ height: ''
1029
+ };
1030
+ clearStyles(positionerElement, stylesToClear);
1031
+ clearStyles(popupRef.current, stylesToClear);
1032
+ }
1033
+ });
1034
+ const contextValue = React__namespace.useMemo(() => ({
1035
+ ...positioning,
1036
+ side: renderedSide,
1037
+ alignItemWithTriggerActive,
1038
+ setControlledAlignItemWithTrigger,
1039
+ scrollUpArrowRef,
1040
+ scrollDownArrowRef
1041
+ }), [positioning, renderedSide, alignItemWithTriggerActive, setControlledAlignItemWithTrigger]);
1042
+ return /*#__PURE__*/jsxRuntime.jsx(useCompositeListItem.CompositeList, {
1043
+ elementsRef: listRef,
1044
+ labelsRef: labelsRef,
1045
+ onMapChange: onMapChange,
1046
+ children: /*#__PURE__*/jsxRuntime.jsxs(SelectPositionerContext.Provider, {
1047
+ value: contextValue,
1048
+ children: [mounted && modal && /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.InternalBackdrop, {
1049
+ inert: InternalBackdrop.inertValue(!open),
1050
+ cutout: triggerElement
1051
+ }), element]
1052
+ })
1053
+ });
1054
+ });
1055
+ if (process.env.NODE_ENV !== "production") SelectPositioner.displayName = "SelectPositioner";
1056
+
1057
+ function isMouseWithinBounds(event) {
1058
+ const targetRect = event.currentTarget.getBoundingClientRect();
1059
+
1060
+ // Safari randomly fires `mouseleave` incorrectly when the item is
1061
+ // aligned to the trigger. This is a workaround to prevent the highlight
1062
+ // from being removed while the cursor is still within the bounds of the item.
1063
+ // https://github.com/mui/base-ui/issues/869
1064
+ const isWithinBounds = targetRect.top + 1 <= event.clientY && event.clientY <= targetRect.bottom - 1 && targetRect.left + 1 <= event.clientX && event.clientX <= targetRect.right - 1;
1065
+ return isWithinBounds;
1066
+ }
1067
+
1068
+ const SCROLL_EPS_PX = 1;
1069
+ const stateAttributesMapping = {
1070
+ ...InternalBackdrop.popupStateMapping,
1071
+ ...useButton.transitionStatusMapping
1072
+ };
1073
+
1074
+ /**
1075
+ * A container for the select list.
1076
+ * Renders a `<div>` element.
1077
+ *
1078
+ * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
1079
+ */
1080
+ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopup(componentProps, forwardedRef) {
1081
+ const {
1082
+ render,
1083
+ className,
1084
+ finalFocus,
1085
+ ...elementProps
1086
+ } = componentProps;
1087
+ const {
1088
+ store,
1089
+ popupRef,
1090
+ onOpenChangeComplete,
1091
+ setOpen,
1092
+ valueRef,
1093
+ selectedItemTextRef,
1094
+ keyboardActiveRef,
1095
+ multiple,
1096
+ handleScrollArrowVisibility,
1097
+ scrollHandlerRef,
1098
+ highlightItemOnHover
1099
+ } = useSelectRootContext();
1100
+ const {
1101
+ side,
1102
+ align,
1103
+ alignItemWithTriggerActive,
1104
+ setControlledAlignItemWithTrigger,
1105
+ scrollDownArrowRef,
1106
+ scrollUpArrowRef
1107
+ } = useSelectPositionerContext();
1108
+ const insideToolbar = ToolbarRootContext.useToolbarRootContext(true) != null;
1109
+ const floatingRootContext = useSelectFloatingContext();
1110
+ const {
1111
+ nonce,
1112
+ disableStyleElements
1113
+ } = CSPContext.useCSPContext();
1114
+ const highlightTimeout = useButton.useTimeout();
1115
+ const id = InternalBackdrop.useStore(store, selectors.id);
1116
+ const open = InternalBackdrop.useStore(store, selectors.open);
1117
+ const mounted = InternalBackdrop.useStore(store, selectors.mounted);
1118
+ const popupProps = InternalBackdrop.useStore(store, selectors.popupProps);
1119
+ const transitionStatus = InternalBackdrop.useStore(store, selectors.transitionStatus);
1120
+ const triggerElement = InternalBackdrop.useStore(store, selectors.triggerElement);
1121
+ const positionerElement = InternalBackdrop.useStore(store, selectors.positionerElement);
1122
+ const listElement = InternalBackdrop.useStore(store, selectors.listElement);
1123
+ const initialHeightRef = React__namespace.useRef(0);
1124
+ const reachedMaxHeightRef = React__namespace.useRef(false);
1125
+ const maxHeightRef = React__namespace.useRef(0);
1126
+ const initialPlacedRef = React__namespace.useRef(false);
1127
+ const originalPositionerStylesRef = React__namespace.useRef({});
1128
+ const scrollArrowFrame = useButton.useAnimationFrame();
1129
+ const handleScroll = useButton.useStableCallback(scroller => {
1130
+ if (!positionerElement || !popupRef.current || !initialPlacedRef.current) {
1131
+ return;
1132
+ }
1133
+ if (reachedMaxHeightRef.current || !alignItemWithTriggerActive) {
1134
+ handleScrollArrowVisibility();
1135
+ return;
1136
+ }
1137
+ const isTopPositioned = positionerElement.style.top === '0px';
1138
+ const isBottomPositioned = positionerElement.style.bottom === '0px';
1139
+ const currentHeight = positionerElement.getBoundingClientRect().height;
1140
+ const doc = useButton.ownerDocument(positionerElement);
1141
+ const positionerStyles = getComputedStyle(positionerElement);
1142
+ const marginTop = parseFloat(positionerStyles.marginTop);
1143
+ const marginBottom = parseFloat(positionerStyles.marginBottom);
1144
+ const maxPopupHeight = getMaxPopupHeight(getComputedStyle(popupRef.current));
1145
+ const maxAvailableHeight = Math.min(doc.documentElement.clientHeight - marginTop - marginBottom, maxPopupHeight);
1146
+ const scrollTop = scroller.scrollTop;
1147
+ const maxScrollTop = getMaxScrollTop(scroller);
1148
+ let nextPositionerHeight = 0;
1149
+ let nextScrollTop = null;
1150
+ let setReachedMax = false;
1151
+ let scrollToMax = false;
1152
+ const setHeight = height => {
1153
+ positionerElement.style.height = `${height}px`;
1154
+ };
1155
+ const handleSmallDiff = (diff, targetScrollTop) => {
1156
+ const heightDelta = clamp.clamp(diff, 0, maxAvailableHeight - currentHeight);
1157
+ if (heightDelta > 0) {
1158
+ // Consume the remaining scroll in height.
1159
+ setHeight(currentHeight + heightDelta);
1160
+ }
1161
+ scroller.scrollTop = targetScrollTop;
1162
+ if (maxAvailableHeight - (currentHeight + heightDelta) <= SCROLL_EPS_PX) {
1163
+ reachedMaxHeightRef.current = true;
1164
+ }
1165
+ handleScrollArrowVisibility();
1166
+ };
1167
+ if (isTopPositioned) {
1168
+ const diff = maxScrollTop - scrollTop;
1169
+ const idealHeight = currentHeight + diff;
1170
+ const nextHeight = Math.min(idealHeight, maxAvailableHeight);
1171
+ nextPositionerHeight = nextHeight;
1172
+ if (diff <= SCROLL_EPS_PX) {
1173
+ handleSmallDiff(diff, maxScrollTop);
1174
+ return;
1175
+ }
1176
+ if (maxAvailableHeight - nextHeight > SCROLL_EPS_PX) {
1177
+ scrollToMax = true;
1178
+ } else {
1179
+ setReachedMax = true;
1180
+ }
1181
+ } else if (isBottomPositioned) {
1182
+ const diff = scrollTop;
1183
+ const idealHeight = currentHeight + diff;
1184
+ const nextHeight = Math.min(idealHeight, maxAvailableHeight);
1185
+ const overshoot = idealHeight - maxAvailableHeight;
1186
+ nextPositionerHeight = nextHeight;
1187
+ if (diff <= SCROLL_EPS_PX) {
1188
+ handleSmallDiff(diff, 0);
1189
+ return;
1190
+ }
1191
+ if (maxAvailableHeight - nextHeight > SCROLL_EPS_PX) {
1192
+ nextScrollTop = 0;
1193
+ } else {
1194
+ setReachedMax = true;
1195
+ if (scrollTop < maxScrollTop) {
1196
+ nextScrollTop = scrollTop - (diff - overshoot);
1197
+ }
1198
+ }
1199
+ }
1200
+ nextPositionerHeight = Math.ceil(nextPositionerHeight);
1201
+ if (nextPositionerHeight !== 0) {
1202
+ setHeight(nextPositionerHeight);
1203
+ }
1204
+ if (scrollToMax || nextScrollTop != null) {
1205
+ // Recompute bounds after resizing (clientHeight likely changed).
1206
+ const nextMaxScrollTop = getMaxScrollTop(scroller);
1207
+ const target = scrollToMax ? nextMaxScrollTop : clamp.clamp(nextScrollTop, 0, nextMaxScrollTop);
1208
+
1209
+ // Avoid adjustments that re-trigger scroll events forever.
1210
+ if (Math.abs(scroller.scrollTop - target) > SCROLL_EPS_PX) {
1211
+ scroller.scrollTop = target;
1212
+ }
1213
+ }
1214
+ if (setReachedMax || nextPositionerHeight >= maxAvailableHeight - SCROLL_EPS_PX) {
1215
+ reachedMaxHeightRef.current = true;
1216
+ }
1217
+ handleScrollArrowVisibility();
1218
+ });
1219
+ React__namespace.useImperativeHandle(scrollHandlerRef, () => handleScroll, [handleScroll]);
1220
+ useButton.useOpenChangeComplete({
1221
+ open,
1222
+ ref: popupRef,
1223
+ onComplete() {
1224
+ if (open) {
1225
+ onOpenChangeComplete?.(true);
1226
+ }
1227
+ }
1228
+ });
1229
+ const state = {
1230
+ open,
1231
+ transitionStatus,
1232
+ side,
1233
+ align
1234
+ };
1235
+ useButton.useIsoLayoutEffect(() => {
1236
+ if (!positionerElement || !popupRef.current || Object.keys(originalPositionerStylesRef.current).length) {
1237
+ return;
1238
+ }
1239
+ originalPositionerStylesRef.current = {
1240
+ top: positionerElement.style.top || '0',
1241
+ left: positionerElement.style.left || '0',
1242
+ right: positionerElement.style.right,
1243
+ height: positionerElement.style.height,
1244
+ bottom: positionerElement.style.bottom,
1245
+ minHeight: positionerElement.style.minHeight,
1246
+ maxHeight: positionerElement.style.maxHeight,
1247
+ marginTop: positionerElement.style.marginTop,
1248
+ marginBottom: positionerElement.style.marginBottom
1249
+ };
1250
+ }, [popupRef, positionerElement]);
1251
+ useButton.useIsoLayoutEffect(() => {
1252
+ if (open || alignItemWithTriggerActive) {
1253
+ return;
1254
+ }
1255
+ initialPlacedRef.current = false;
1256
+ reachedMaxHeightRef.current = false;
1257
+ initialHeightRef.current = 0;
1258
+ maxHeightRef.current = 0;
1259
+ clearStyles(positionerElement, originalPositionerStylesRef.current);
1260
+ }, [open, alignItemWithTriggerActive, positionerElement, popupRef]);
1261
+ useButton.useIsoLayoutEffect(() => {
1262
+ const popupElement = popupRef.current;
1263
+ if (!open || !triggerElement || !positionerElement || !popupElement || store.state.transitionStatus === 'ending') {
1264
+ return;
1265
+ }
1266
+ if (!alignItemWithTriggerActive) {
1267
+ initialPlacedRef.current = true;
1268
+ scrollArrowFrame.request(handleScrollArrowVisibility);
1269
+ popupElement.style.removeProperty('--transform-origin');
1270
+ return;
1271
+ }
1272
+
1273
+ // Wait for `selectedItemTextRef.current` to be set.
1274
+ queueMicrotask(() => {
1275
+ // Ensure we remove any transforms that can affect the location of the popup
1276
+ // and therefore the calculations.
1277
+ const restoreTransformStyles = unsetTransformStyles(popupElement);
1278
+ popupElement.style.removeProperty('--transform-origin');
1279
+ try {
1280
+ const positionerStyles = getComputedStyle(positionerElement);
1281
+ const popupStyles = getComputedStyle(popupElement);
1282
+ const doc = useButton.ownerDocument(triggerElement);
1283
+ const win = floatingUi_utils_dom.getWindow(positionerElement);
1284
+ const scale = getScale(triggerElement);
1285
+ const triggerRect = normalizeRect(triggerElement.getBoundingClientRect(), scale);
1286
+ const positionerRect = normalizeRect(positionerElement.getBoundingClientRect(), scale);
1287
+ const triggerX = triggerRect.left;
1288
+ const triggerHeight = triggerRect.height;
1289
+ const scroller = listElement || popupElement;
1290
+ const scrollHeight = scroller.scrollHeight;
1291
+ const borderBottom = parseFloat(popupStyles.borderBottomWidth);
1292
+ const marginTop = parseFloat(positionerStyles.marginTop) || 10;
1293
+ const marginBottom = parseFloat(positionerStyles.marginBottom) || 10;
1294
+ const minHeight = parseFloat(positionerStyles.minHeight) || 100;
1295
+ const maxPopupHeight = getMaxPopupHeight(popupStyles);
1296
+ const paddingLeft = 5;
1297
+ const paddingRight = 5;
1298
+ const triggerCollisionThreshold = 20;
1299
+ const viewportHeight = doc.documentElement.clientHeight - marginTop - marginBottom;
1300
+ const viewportWidth = doc.documentElement.clientWidth;
1301
+ const availableSpaceBeneathTrigger = viewportHeight - triggerRect.bottom + triggerHeight;
1302
+ const textElement = selectedItemTextRef.current;
1303
+ const valueElement = valueRef.current;
1304
+ let textRect;
1305
+ let offsetX = 0;
1306
+ let offsetY = 0;
1307
+ if (textElement && valueElement) {
1308
+ const valueRect = normalizeRect(valueElement.getBoundingClientRect(), scale);
1309
+ textRect = normalizeRect(textElement.getBoundingClientRect(), scale);
1310
+ const valueLeftFromTriggerLeft = valueRect.left - triggerX;
1311
+ const textLeftFromPositionerLeft = textRect.left - positionerRect.left;
1312
+ const valueCenterFromPositionerTop = valueRect.top - triggerRect.top + valueRect.height / 2;
1313
+ const textCenterFromTriggerTop = textRect.top - positionerRect.top + textRect.height / 2;
1314
+ offsetX = valueLeftFromTriggerLeft - textLeftFromPositionerLeft;
1315
+ offsetY = textCenterFromTriggerTop - valueCenterFromPositionerTop;
1316
+ }
1317
+ const idealHeight = availableSpaceBeneathTrigger + offsetY + marginBottom + borderBottom;
1318
+ let height = Math.min(viewportHeight, idealHeight);
1319
+ const maxHeight = viewportHeight - marginTop - marginBottom;
1320
+ const scrollTop = idealHeight - height;
1321
+ const left = Math.max(paddingLeft, triggerX + offsetX);
1322
+ const maxRight = viewportWidth - paddingRight;
1323
+ const rightOverflow = Math.max(0, left + positionerRect.width - maxRight);
1324
+ positionerElement.style.left = `${left - rightOverflow}px`;
1325
+ positionerElement.style.height = `${height}px`;
1326
+ positionerElement.style.maxHeight = 'auto';
1327
+ positionerElement.style.marginTop = `${marginTop}px`;
1328
+ positionerElement.style.marginBottom = `${marginBottom}px`;
1329
+ popupElement.style.height = '100%';
1330
+ const maxScrollTop = getMaxScrollTop(scroller);
1331
+ const isTopPositioned = scrollTop >= maxScrollTop - SCROLL_EPS_PX;
1332
+ if (isTopPositioned) {
1333
+ height = Math.min(viewportHeight, positionerRect.height) - (scrollTop - maxScrollTop);
1334
+ }
1335
+
1336
+ // When the trigger is too close to the top or bottom of the viewport, or the minHeight is
1337
+ // reached, we fallback to aligning the popup to the trigger as the UX is poor otherwise.
1338
+ const fallbackToAlignPopupToTrigger = triggerRect.top < triggerCollisionThreshold || triggerRect.bottom > viewportHeight - triggerCollisionThreshold || Math.ceil(height) + SCROLL_EPS_PX < Math.min(scrollHeight, minHeight);
1339
+
1340
+ // Safari doesn't position the popup correctly when pinch-zoomed.
1341
+ const isPinchZoomed = (win.visualViewport?.scale ?? 1) !== 1 && useButton.isWebKit;
1342
+ if (fallbackToAlignPopupToTrigger || isPinchZoomed) {
1343
+ initialPlacedRef.current = true;
1344
+ clearStyles(positionerElement, originalPositionerStylesRef.current);
1345
+ ReactDOM__namespace.flushSync(() => setControlledAlignItemWithTrigger(false));
1346
+ return;
1347
+ }
1348
+ if (isTopPositioned) {
1349
+ const topOffset = Math.max(0, viewportHeight - idealHeight);
1350
+ positionerElement.style.top = positionerRect.height >= maxHeight ? '0' : `${topOffset}px`;
1351
+ positionerElement.style.height = `${height}px`;
1352
+ scroller.scrollTop = getMaxScrollTop(scroller);
1353
+ initialHeightRef.current = Math.max(minHeight, height);
1354
+ } else {
1355
+ positionerElement.style.bottom = '0';
1356
+ initialHeightRef.current = Math.max(minHeight, height);
1357
+ scroller.scrollTop = scrollTop;
1358
+ }
1359
+ if (textRect) {
1360
+ const popupTop = positionerRect.top;
1361
+ const popupHeight = positionerRect.height;
1362
+ const textCenterY = textRect.top + textRect.height / 2;
1363
+ const transformOriginY = popupHeight > 0 ? (textCenterY - popupTop) / popupHeight * 100 : 50;
1364
+ const clampedY = clamp.clamp(transformOriginY, 0, 100);
1365
+ popupElement.style.setProperty('--transform-origin', `50% ${clampedY}%`);
1366
+ }
1367
+ if (initialHeightRef.current === viewportHeight || height >= maxPopupHeight) {
1368
+ reachedMaxHeightRef.current = true;
1369
+ }
1370
+ handleScrollArrowVisibility();
1371
+
1372
+ // Avoid the `onScroll` event logic from triggering before the popup is placed.
1373
+ setTimeout(() => {
1374
+ initialPlacedRef.current = true;
1375
+ });
1376
+ } finally {
1377
+ restoreTransformStyles();
1378
+ }
1379
+ });
1380
+ }, [store, open, positionerElement, triggerElement, valueRef, selectedItemTextRef, popupRef, handleScrollArrowVisibility, alignItemWithTriggerActive, setControlledAlignItemWithTrigger, scrollArrowFrame, scrollDownArrowRef, scrollUpArrowRef, listElement]);
1381
+ React__namespace.useEffect(() => {
1382
+ if (!alignItemWithTriggerActive || !positionerElement || !open) {
1383
+ return undefined;
1384
+ }
1385
+ const win = floatingUi_utils_dom.getWindow(positionerElement);
1386
+ function handleResize(event) {
1387
+ setOpen(false, useButton.createChangeEventDetails(useButton.windowResize, event));
1388
+ }
1389
+ win.addEventListener('resize', handleResize);
1390
+ return () => {
1391
+ win.removeEventListener('resize', handleResize);
1392
+ };
1393
+ }, [setOpen, alignItemWithTriggerActive, positionerElement, open]);
1394
+ const defaultProps = {
1395
+ ...(listElement ? {
1396
+ role: 'presentation',
1397
+ 'aria-orientation': undefined
1398
+ } : {
1399
+ role: 'listbox',
1400
+ 'aria-multiselectable': multiple || undefined,
1401
+ id: `${id}-list`
1402
+ }),
1403
+ onKeyDown(event) {
1404
+ keyboardActiveRef.current = true;
1405
+ if (insideToolbar && CSPContext.COMPOSITE_KEYS.has(event.key)) {
1406
+ event.stopPropagation();
1407
+ }
1408
+ },
1409
+ onMouseMove() {
1410
+ keyboardActiveRef.current = false;
1411
+ },
1412
+ onPointerLeave(event) {
1413
+ if (!highlightItemOnHover || isMouseWithinBounds(event) || event.pointerType === 'touch') {
1414
+ return;
1415
+ }
1416
+ const popup = event.currentTarget;
1417
+ highlightTimeout.start(0, () => {
1418
+ store.set('activeIndex', null);
1419
+ popup.focus({
1420
+ preventScroll: true
1421
+ });
1422
+ });
1423
+ },
1424
+ onScroll(event) {
1425
+ if (listElement) {
1426
+ return;
1427
+ }
1428
+ handleScroll(event.currentTarget);
1429
+ },
1430
+ ...(alignItemWithTriggerActive && {
1431
+ style: listElement ? {
1432
+ height: '100%'
1433
+ } : LIST_FUNCTIONAL_STYLES
1434
+ })
1435
+ };
1436
+ const element = useRenderElement.useRenderElement('div', componentProps, {
1437
+ ref: [forwardedRef, popupRef],
1438
+ state,
1439
+ stateAttributesMapping,
1440
+ props: [popupProps, defaultProps, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), {
1441
+ className: !listElement && alignItemWithTriggerActive ? CSPContext.styleDisableScrollbar.className : undefined
1442
+ }, elementProps]
1443
+ });
1444
+ return /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
1445
+ children: [!disableStyleElements && CSPContext.styleDisableScrollbar.getElement(nonce), /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.FloatingFocusManager, {
1446
+ context: floatingRootContext,
1447
+ modal: false,
1448
+ disabled: !mounted,
1449
+ returnFocus: finalFocus,
1450
+ restoreFocus: true,
1451
+ children: element
1452
+ })]
1453
+ });
1454
+ });
1455
+ if (process.env.NODE_ENV !== "production") SelectPopup.displayName = "SelectPopup";
1456
+ function getMaxPopupHeight(popupStyles) {
1457
+ const maxHeightStyle = popupStyles.maxHeight || '';
1458
+ return maxHeightStyle.endsWith('px') ? parseFloat(maxHeightStyle) || Infinity : Infinity;
1459
+ }
1460
+ function getMaxScrollTop(scroller) {
1461
+ return Math.max(0, scroller.scrollHeight - scroller.clientHeight);
1462
+ }
1463
+ function getScale(element) {
1464
+ // The platform API is async-capable, but the DOM platform returns a plain scale object.
1465
+ return floatingUi_reactDom.platform.getScale(element);
1466
+ }
1467
+ function normalizeRect(rect, scale) {
1468
+ return index_esm.rectToClientRect({
1469
+ x: rect.x / scale.x,
1470
+ y: rect.y / scale.y,
1471
+ width: rect.width / scale.x,
1472
+ height: rect.height / scale.y
1473
+ });
1474
+ }
1475
+ const TRANSFORM_STYLE_RESETS = [['transform', 'none'], ['scale', '1'], ['translate', '0 0']];
1476
+ function unsetTransformStyles(popupElement) {
1477
+ const {
1478
+ style
1479
+ } = popupElement;
1480
+ const originalStyles = {};
1481
+ for (const [property, value] of TRANSFORM_STYLE_RESETS) {
1482
+ originalStyles[property] = style.getPropertyValue(property);
1483
+ style.setProperty(property, value, 'important');
1484
+ }
1485
+ return () => {
1486
+ for (const [property] of TRANSFORM_STYLE_RESETS) {
1487
+ const originalValue = originalStyles[property];
1488
+ if (originalValue) {
1489
+ style.setProperty(property, originalValue);
1490
+ } else {
1491
+ style.removeProperty(property);
1492
+ }
1493
+ }
1494
+ };
1495
+ }
1496
+
1497
+ /**
1498
+ * A container for the select items.
1499
+ * Renders a `<div>` element.
1500
+ *
1501
+ * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
1502
+ */
1503
+ const SelectList = /*#__PURE__*/React__namespace.forwardRef(function SelectList(componentProps, forwardedRef) {
1504
+ const {
1505
+ className,
1506
+ render,
1507
+ ...elementProps
1508
+ } = componentProps;
1509
+ const {
1510
+ store,
1511
+ scrollHandlerRef
1512
+ } = useSelectRootContext();
1513
+ const {
1514
+ alignItemWithTriggerActive
1515
+ } = useSelectPositionerContext();
1516
+ const hasScrollArrows = InternalBackdrop.useStore(store, selectors.hasScrollArrows);
1517
+ const openMethod = InternalBackdrop.useStore(store, selectors.openMethod);
1518
+ const multiple = InternalBackdrop.useStore(store, selectors.multiple);
1519
+ const id = InternalBackdrop.useStore(store, selectors.id);
1520
+ const defaultProps = {
1521
+ id: `${id}-list`,
1522
+ role: 'listbox',
1523
+ 'aria-multiselectable': multiple || undefined,
1524
+ onScroll(event) {
1525
+ scrollHandlerRef.current?.(event.currentTarget);
1526
+ },
1527
+ ...(alignItemWithTriggerActive && {
1528
+ style: LIST_FUNCTIONAL_STYLES
1529
+ }),
1530
+ className: hasScrollArrows && openMethod !== 'touch' ? CSPContext.styleDisableScrollbar.className : undefined
1531
+ };
1532
+ const setListElement = useButton.useStableCallback(element => {
1533
+ store.set('listElement', element);
1534
+ });
1535
+ return useRenderElement.useRenderElement('div', componentProps, {
1536
+ ref: [forwardedRef, setListElement],
1537
+ props: [defaultProps, elementProps]
1538
+ });
1539
+ });
1540
+ if (process.env.NODE_ENV !== "production") SelectList.displayName = "SelectList";
1541
+
1542
+ const SelectItemContext = /*#__PURE__*/React__namespace.createContext(undefined);
1543
+ if (process.env.NODE_ENV !== "production") SelectItemContext.displayName = "SelectItemContext";
1544
+ function useSelectItemContext() {
1545
+ const context = React__namespace.useContext(SelectItemContext);
1546
+ if (!context) {
1547
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectItemContext is missing. SelectItem parts must be placed within <Select.Item>.' : useRenderElement.formatErrorMessage(57));
1548
+ }
1549
+ return context;
1550
+ }
1551
+
1552
+ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef(function SelectItem(componentProps, forwardedRef) {
1553
+ const {
1554
+ render,
1555
+ className,
1556
+ value: itemValue = null,
1557
+ label,
1558
+ disabled = false,
1559
+ nativeButton = false,
1560
+ ...elementProps
1561
+ } = componentProps;
1562
+ const textRef = React__namespace.useRef(null);
1563
+ const listItem = useCompositeListItem.useCompositeListItem({
1564
+ label,
1565
+ textRef,
1566
+ indexGuessBehavior: useCompositeListItem.IndexGuessBehavior.GuessFromOrder
1567
+ });
1568
+ const {
1569
+ store,
1570
+ getItemProps,
1571
+ setOpen,
1572
+ setValue,
1573
+ selectionRef,
1574
+ typingRef,
1575
+ valuesRef,
1576
+ keyboardActiveRef,
1577
+ multiple,
1578
+ highlightItemOnHover
1579
+ } = useSelectRootContext();
1580
+ const highlightTimeout = useButton.useTimeout();
1581
+ const highlighted = InternalBackdrop.useStore(store, selectors.isActive, listItem.index);
1582
+ const selected = InternalBackdrop.useStore(store, selectors.isSelected, listItem.index, itemValue);
1583
+ const selectedByFocus = InternalBackdrop.useStore(store, selectors.isSelectedByFocus, listItem.index);
1584
+ const isItemEqualToValue = InternalBackdrop.useStore(store, selectors.isItemEqualToValue);
1585
+ const index = listItem.index;
1586
+ const hasRegistered = index !== -1;
1587
+ const itemRef = React__namespace.useRef(null);
1588
+ const indexRef = useButton.useValueAsRef(index);
1589
+ useButton.useIsoLayoutEffect(() => {
1590
+ if (!hasRegistered) {
1591
+ return undefined;
1592
+ }
1593
+ const values = valuesRef.current;
1594
+ values[index] = itemValue;
1595
+ return () => {
1596
+ delete values[index];
1597
+ };
1598
+ }, [hasRegistered, index, itemValue, valuesRef]);
1599
+ useButton.useIsoLayoutEffect(() => {
1600
+ if (!hasRegistered) {
1601
+ return undefined;
1602
+ }
1603
+ const selectedValue = store.state.value;
1604
+ let selectedCandidate = selectedValue;
1605
+ if (multiple && Array.isArray(selectedValue) && selectedValue.length > 0) {
1606
+ selectedCandidate = selectedValue[selectedValue.length - 1];
1607
+ }
1608
+ if (selectedCandidate !== undefined && resolveAriaLabelledBy.compareItemEquality(itemValue, selectedCandidate, isItemEqualToValue)) {
1609
+ store.set('selectedIndex', index);
1610
+ }
1611
+ return undefined;
1612
+ }, [hasRegistered, index, multiple, isItemEqualToValue, store, itemValue]);
1613
+ const state = {
1614
+ disabled,
1615
+ selected,
1616
+ highlighted
1617
+ };
1618
+ const rootProps = getItemProps({
1619
+ active: highlighted,
1620
+ selected
1621
+ });
1622
+ // With our custom `focusItemOnHover` implementation, this interferes with the logic and can
1623
+ // cause the index state to be stuck when leaving the select popup.
1624
+ rootProps.onFocus = undefined;
1625
+ rootProps.id = undefined;
1626
+ const lastKeyRef = React__namespace.useRef(null);
1627
+ const pointerTypeRef = React__namespace.useRef('mouse');
1628
+ const didPointerDownRef = React__namespace.useRef(false);
1629
+ const {
1630
+ getButtonProps,
1631
+ buttonRef
1632
+ } = useButton.useButton({
1633
+ disabled,
1634
+ focusableWhenDisabled: true,
1635
+ native: nativeButton,
1636
+ composite: true
1637
+ });
1638
+ function commitSelection(event) {
1639
+ const selectedValue = store.state.value;
1640
+ if (multiple) {
1641
+ const currentValue = Array.isArray(selectedValue) ? selectedValue : [];
1642
+ const nextValue = selected ? resolveAriaLabelledBy.removeItem(currentValue, itemValue, isItemEqualToValue) : [...currentValue, itemValue];
1643
+ setValue(nextValue, useButton.createChangeEventDetails(useButton.itemPress, event));
1644
+ } else {
1645
+ setValue(itemValue, useButton.createChangeEventDetails(useButton.itemPress, event));
1646
+ setOpen(false, useButton.createChangeEventDetails(useButton.itemPress, event));
1647
+ }
1648
+ }
1649
+ const defaultProps = {
1650
+ role: 'option',
1651
+ 'aria-selected': selected,
1652
+ tabIndex: highlighted ? 0 : -1,
1653
+ onFocus() {
1654
+ store.set('activeIndex', index);
1655
+ },
1656
+ onMouseEnter() {
1657
+ if (!keyboardActiveRef.current && store.state.selectedIndex === null && highlightItemOnHover) {
1658
+ store.set('activeIndex', index);
1659
+ }
1660
+ },
1661
+ onMouseMove() {
1662
+ if (highlightItemOnHover) {
1663
+ store.set('activeIndex', index);
1664
+ }
1665
+ },
1666
+ onMouseLeave(event) {
1667
+ if (!highlightItemOnHover || keyboardActiveRef.current || isMouseWithinBounds(event)) {
1668
+ return;
1669
+ }
1670
+ highlightTimeout.start(0, () => {
1671
+ if (store.state.activeIndex === index) {
1672
+ store.set('activeIndex', null);
1673
+ }
1674
+ });
1675
+ },
1676
+ onTouchStart() {
1677
+ selectionRef.current = {
1678
+ allowSelectedMouseUp: false,
1679
+ allowUnselectedMouseUp: false
1680
+ };
1681
+ },
1682
+ onKeyDown(event) {
1683
+ lastKeyRef.current = event.key;
1684
+ store.set('activeIndex', index);
1685
+ if (event.key === ' ' && typingRef.current) {
1686
+ event.preventDefault();
1687
+ }
1688
+ },
1689
+ onClick(event) {
1690
+ didPointerDownRef.current = false;
1691
+
1692
+ // Prevent double commit on {Enter}
1693
+ if (event.type === 'keydown' && lastKeyRef.current === null) {
1694
+ return;
1695
+ }
1696
+ if (disabled || event.type === 'keydown' && lastKeyRef.current === ' ' && typingRef.current || pointerTypeRef.current !== 'touch' && !highlighted) {
1697
+ return;
1698
+ }
1699
+ lastKeyRef.current = null;
1700
+ commitSelection(event.nativeEvent);
1701
+ },
1702
+ onPointerEnter(event) {
1703
+ pointerTypeRef.current = event.pointerType;
1704
+ },
1705
+ onPointerDown(event) {
1706
+ pointerTypeRef.current = event.pointerType;
1707
+ didPointerDownRef.current = true;
1708
+ },
1709
+ onMouseUp() {
1710
+ if (disabled) {
1711
+ return;
1712
+ }
1713
+
1714
+ // Regular click (pointerdown on this element) if didPointerDownRef is set, otherwise drag-to-select
1715
+ if (didPointerDownRef.current) {
1716
+ didPointerDownRef.current = false;
1717
+ return;
1718
+ }
1719
+ const disallowSelectedMouseUp = !selectionRef.current.allowSelectedMouseUp && selected;
1720
+ const disallowUnselectedMouseUp = !selectionRef.current.allowUnselectedMouseUp && !selected;
1721
+ if (disallowSelectedMouseUp || disallowUnselectedMouseUp || pointerTypeRef.current !== 'touch' && !highlighted) {
1722
+ return;
1723
+ }
1724
+ itemRef.current?.click();
1725
+ }
1726
+ };
1727
+ const element = useRenderElement.useRenderElement('div', componentProps, {
1728
+ ref: [buttonRef, forwardedRef, listItem.ref, itemRef],
1729
+ state,
1730
+ props: [rootProps, defaultProps, elementProps, getButtonProps]
1731
+ });
1732
+ const contextValue = React__namespace.useMemo(() => ({
1733
+ selected,
1734
+ indexRef,
1735
+ textRef,
1736
+ selectedByFocus,
1737
+ hasRegistered
1738
+ }), [selected, indexRef, textRef, selectedByFocus, hasRegistered]);
1739
+ return /*#__PURE__*/jsxRuntime.jsx(SelectItemContext.Provider, {
1740
+ value: contextValue,
1741
+ children: element
1742
+ });
1743
+ }));
1744
+ if (process.env.NODE_ENV !== "production") SelectItem.displayName = "SelectItem";
1745
+
1746
+ const SelectItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function SelectItemIndicator(componentProps, forwardedRef) {
1747
+ const keepMounted = componentProps.keepMounted ?? false;
1748
+ const {
1749
+ selected
1750
+ } = useSelectItemContext();
1751
+ const shouldRender = keepMounted || selected;
1752
+ if (!shouldRender) {
1753
+ return null;
1754
+ }
1755
+
1756
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
1757
+ return /*#__PURE__*/jsxRuntime.jsx(Inner, {
1758
+ ...componentProps,
1759
+ ref: forwardedRef
1760
+ });
1761
+ });
1762
+
1763
+ /** The core implementation of the indicator is split here to avoid paying the hooks
1764
+ * costs unless the element needs to be mounted. */
1765
+ if (process.env.NODE_ENV !== "production") SelectItemIndicator.displayName = "SelectItemIndicator";
1766
+ const Inner = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef((componentProps, forwardedRef) => {
1767
+ const {
1768
+ render,
1769
+ className,
1770
+ keepMounted,
1771
+ ...elementProps
1772
+ } = componentProps;
1773
+ const {
1774
+ selected
1775
+ } = useSelectItemContext();
1776
+ const indicatorRef = React__namespace.useRef(null);
1777
+ const {
1778
+ transitionStatus,
1779
+ setMounted
1780
+ } = useButton.useTransitionStatus(selected);
1781
+ const state = {
1782
+ selected,
1783
+ transitionStatus
1784
+ };
1785
+ const element = useRenderElement.useRenderElement('span', componentProps, {
1786
+ ref: [forwardedRef, indicatorRef],
1787
+ state,
1788
+ props: [{
1789
+ 'aria-hidden': true,
1790
+ children: '✔️'
1791
+ }, elementProps],
1792
+ stateAttributesMapping: useButton.transitionStatusMapping
1793
+ });
1794
+ useButton.useOpenChangeComplete({
1795
+ open: selected,
1796
+ ref: indicatorRef,
1797
+ onComplete() {
1798
+ if (!selected) {
1799
+ setMounted(false);
1800
+ }
1801
+ }
1802
+ });
1803
+ return element;
1804
+ }));
1805
+ if (process.env.NODE_ENV !== "production") Inner.displayName = "Inner";
1806
+
1807
+ /**
1808
+ * A text label of the select item.
1809
+ * Renders a `<div>` element.
1810
+ *
1811
+ * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
1812
+ */
1813
+ const SelectItemText = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef(function SelectItemText(componentProps, forwardedRef) {
1814
+ const {
1815
+ indexRef,
1816
+ textRef,
1817
+ selectedByFocus,
1818
+ hasRegistered
1819
+ } = useSelectItemContext();
1820
+ const {
1821
+ selectedItemTextRef
1822
+ } = useSelectRootContext();
1823
+ const {
1824
+ className,
1825
+ render,
1826
+ ...elementProps
1827
+ } = componentProps;
1828
+ const localRef = React__namespace.useCallback(node => {
1829
+ if (!node || !hasRegistered) {
1830
+ return;
1831
+ }
1832
+ const hasNoSelectedItemText = selectedItemTextRef.current === null || !selectedItemTextRef.current.isConnected;
1833
+ if (selectedByFocus || hasNoSelectedItemText && indexRef.current === 0) {
1834
+ selectedItemTextRef.current = node;
1835
+ }
1836
+ }, [selectedItemTextRef, indexRef, selectedByFocus, hasRegistered]);
1837
+ const element = useRenderElement.useRenderElement('div', componentProps, {
1838
+ ref: [localRef, forwardedRef, textRef],
1839
+ props: elementProps
1840
+ });
1841
+ return element;
1842
+ }));
1843
+ if (process.env.NODE_ENV !== "production") SelectItemText.displayName = "SelectItemText";
1844
+
1845
+ const SelectGroupContext = /*#__PURE__*/React__namespace.createContext(undefined);
1846
+ if (process.env.NODE_ENV !== "production") SelectGroupContext.displayName = "SelectGroupContext";
1847
+ function useSelectGroupContext() {
1848
+ const context = React__namespace.useContext(SelectGroupContext);
1849
+ if (context === undefined) {
1850
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectGroupContext is missing. SelectGroup parts must be placed within <Select.Group>.' : useRenderElement.formatErrorMessage(56));
1851
+ }
1852
+ return context;
1853
+ }
1854
+
1855
+ const SelectGroup = /*#__PURE__*/React__namespace.forwardRef(function SelectGroup(componentProps, forwardedRef) {
1856
+ const {
1857
+ className,
1858
+ render,
1859
+ ...elementProps
1860
+ } = componentProps;
1861
+ const [labelId, setLabelId] = React__namespace.useState();
1862
+ const contextValue = React__namespace.useMemo(() => ({
1863
+ labelId,
1864
+ setLabelId
1865
+ }), [labelId, setLabelId]);
1866
+ const element = useRenderElement.useRenderElement('div', componentProps, {
1867
+ ref: forwardedRef,
1868
+ props: [{
1869
+ role: 'group',
1870
+ 'aria-labelledby': labelId
1871
+ }, elementProps]
1872
+ });
1873
+ return /*#__PURE__*/jsxRuntime.jsx(SelectGroupContext.Provider, {
1874
+ value: contextValue,
1875
+ children: element
1876
+ });
1877
+ });
1878
+ if (process.env.NODE_ENV !== "production") SelectGroup.displayName = "SelectGroup";
1879
+
1880
+ /**
1881
+ * An accessible label that is automatically associated with its parent group.
1882
+ * Renders a `<div>` element.
1883
+ *
1884
+ * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
1885
+ */
1886
+ const SelectGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function SelectGroupLabel(componentProps, forwardedRef) {
1887
+ const {
1888
+ className,
1889
+ render,
1890
+ id: idProp,
1891
+ ...elementProps
1892
+ } = componentProps;
1893
+ const {
1894
+ setLabelId
1895
+ } = useSelectGroupContext();
1896
+ const id = useButton.useBaseUiId(idProp);
1897
+ useButton.useIsoLayoutEffect(() => {
1898
+ setLabelId(id);
1899
+ }, [id, setLabelId]);
1900
+ const element = useRenderElement.useRenderElement('div', componentProps, {
1901
+ ref: forwardedRef,
1902
+ props: [{
1903
+ id
1904
+ }, elementProps]
1905
+ });
1906
+ return element;
1907
+ });
1908
+ if (process.env.NODE_ENV !== "production") SelectGroupLabel.displayName = "SelectGroupLabel";
1909
+
1910
+ exports.SelectGroup = SelectGroup;
1911
+ exports.SelectGroupLabel = SelectGroupLabel;
1912
+ exports.SelectItem = SelectItem;
1913
+ exports.SelectItemIndicator = SelectItemIndicator;
1914
+ exports.SelectItemText = SelectItemText;
1915
+ exports.SelectList = SelectList;
1916
+ exports.SelectPopup = SelectPopup;
1917
+ exports.SelectPortal = SelectPortal;
1918
+ exports.SelectPositioner = SelectPositioner;
1919
+ exports.SelectRoot = SelectRoot;
1920
+ exports.SelectTrigger = SelectTrigger;
1921
+ exports.SelectValue = SelectValue;
1922
+ exports.selectors = selectors;
1923
+ exports.useSelectPositionerContext = useSelectPositionerContext;
1924
+ exports.useSelectRootContext = useSelectRootContext;