@jobber/components 9.6.1 → 9.6.2

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 (141) hide show
  1. package/dist/Autocomplete/index.cjs +2 -0
  2. package/dist/Autocomplete/index.mjs +2 -0
  3. package/dist/Autocomplete-cjs.js +5 -4
  4. package/dist/Autocomplete-es.js +2 -1
  5. package/dist/Card/index.cjs +2 -1
  6. package/dist/Card/index.mjs +2 -1
  7. package/dist/Chip/index.cjs +2 -0
  8. package/dist/Chip/index.mjs +2 -0
  9. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +2 -0
  10. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +2 -0
  11. package/dist/Chips/InternalChipDismissible/index.cjs +2 -0
  12. package/dist/Chips/InternalChipDismissible/index.mjs +2 -0
  13. package/dist/Chips/index.cjs +2 -0
  14. package/dist/Chips/index.mjs +2 -0
  15. package/dist/Combobox/index.cjs +4 -2
  16. package/dist/Combobox/index.mjs +4 -2
  17. package/dist/{ComboboxSeparator-cjs.js → ComboboxChipRemove-cjs.js} +455 -501
  18. package/dist/{ComboboxSeparator-es.js → ComboboxChipRemove-es.js} +456 -500
  19. package/dist/ComboboxPrimitive-cjs.js +30 -29
  20. package/dist/ComboboxPrimitive-es.js +3 -2
  21. package/dist/ConfirmationModal/index.cjs +2 -0
  22. package/dist/ConfirmationModal/index.mjs +2 -0
  23. package/dist/DataDump/index.cjs +2 -1
  24. package/dist/DataDump/index.mjs +2 -1
  25. package/dist/DataList/components/DataListActions/index.cjs +2 -0
  26. package/dist/DataList/components/DataListActions/index.mjs +2 -0
  27. package/dist/DataList/components/DataListBulkActions/index.cjs +2 -0
  28. package/dist/DataList/components/DataListBulkActions/index.mjs +2 -0
  29. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +2 -0
  30. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +2 -0
  31. package/dist/DataList/components/DataListFilters/index.cjs +2 -0
  32. package/dist/DataList/components/DataListFilters/index.mjs +2 -0
  33. package/dist/DataList/components/DataListHeader/index.cjs +2 -0
  34. package/dist/DataList/components/DataListHeader/index.mjs +2 -0
  35. package/dist/DataList/components/DataListItem/index.cjs +2 -0
  36. package/dist/DataList/components/DataListItem/index.mjs +2 -0
  37. package/dist/DataList/components/DataListItemActions/index.cjs +2 -0
  38. package/dist/DataList/components/DataListItemActions/index.mjs +2 -0
  39. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +2 -0
  40. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +2 -0
  41. package/dist/DataList/components/DataListItems/index.cjs +2 -0
  42. package/dist/DataList/components/DataListItems/index.mjs +2 -0
  43. package/dist/DataList/components/DataListLayout/index.cjs +2 -0
  44. package/dist/DataList/components/DataListLayout/index.mjs +2 -0
  45. package/dist/DataList/components/DataListLayoutActions/index.cjs +2 -0
  46. package/dist/DataList/components/DataListLayoutActions/index.mjs +2 -0
  47. package/dist/DataList/index.cjs +2 -0
  48. package/dist/DataList/index.mjs +2 -0
  49. package/dist/DatePicker/index.cjs +2 -0
  50. package/dist/DatePicker/index.mjs +2 -0
  51. package/dist/DatePicker-cjs.js +5 -4
  52. package/dist/DatePicker-es.js +2 -1
  53. package/dist/Dialog/index.cjs +2 -1
  54. package/dist/Dialog/index.mjs +2 -1
  55. package/dist/DrawerDescription-cjs.js +99 -268
  56. package/dist/DrawerDescription-es.js +103 -270
  57. package/dist/DrawerTitle-cjs.js +6 -2
  58. package/dist/DrawerTitle-es.js +7 -3
  59. package/dist/FieldDescription-cjs.js +181 -184
  60. package/dist/FieldDescription-es.js +184 -187
  61. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +2 -0
  62. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +2 -0
  63. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +2 -0
  64. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +2 -0
  65. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +2 -0
  66. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +2 -0
  67. package/dist/FilterPicker/index.cjs +2 -0
  68. package/dist/FilterPicker/index.mjs +2 -0
  69. package/dist/FilterPickerContent-cjs.js +5 -4
  70. package/dist/FilterPickerContent-es.js +2 -1
  71. package/dist/FormatFile/index.cjs +2 -0
  72. package/dist/FormatFile/index.mjs +2 -0
  73. package/dist/Gallery/index.cjs +2 -0
  74. package/dist/Gallery/index.mjs +2 -0
  75. package/dist/InputDate/index.cjs +2 -0
  76. package/dist/InputDate/index.mjs +2 -0
  77. package/dist/InputNumber/index.cjs +2 -1
  78. package/dist/InputNumber/index.mjs +2 -1
  79. package/dist/LightBox/index.cjs +2 -0
  80. package/dist/LightBox/index.mjs +2 -0
  81. package/dist/Menu/index.cjs +2 -1
  82. package/dist/Menu/index.mjs +2 -1
  83. package/dist/Menu-cjs.js +5 -4
  84. package/dist/Menu-es.js +2 -1
  85. package/dist/MenuSubmenuTrigger-cjs.js +164 -206
  86. package/dist/MenuSubmenuTrigger-es.js +152 -194
  87. package/dist/Modal/index.cjs +2 -0
  88. package/dist/Modal/index.mjs +3 -1
  89. package/dist/NumberFieldInput-cjs.js +308 -187
  90. package/dist/NumberFieldInput-es.js +308 -187
  91. package/dist/Page/index.cjs +2 -1
  92. package/dist/Page/index.mjs +2 -1
  93. package/dist/Popover/index.cjs +2 -0
  94. package/dist/Popover/index.mjs +2 -0
  95. package/dist/Popover-cjs.js +8 -7
  96. package/dist/Popover-es.js +2 -1
  97. package/dist/ScrollAreaViewport-cjs.js +1093 -747
  98. package/dist/ScrollAreaViewport-es.js +1081 -734
  99. package/dist/Select/index.cjs +2 -1
  100. package/dist/Select/index.mjs +2 -1
  101. package/dist/SelectGroupLabel-cjs.js +306 -207
  102. package/dist/SelectGroupLabel-es.js +302 -203
  103. package/dist/Tooltip/index.cjs +2 -0
  104. package/dist/Tooltip/index.mjs +2 -0
  105. package/dist/Tooltip-cjs.js +7 -6
  106. package/dist/Tooltip-es.js +2 -1
  107. package/dist/dialogReturnFocus-cjs.js +1 -1
  108. package/dist/dialogReturnFocus-es.js +2 -2
  109. package/dist/floating-ui.react-cjs.js +192 -2564
  110. package/dist/floating-ui.react-dom-cjs.js +2103 -0
  111. package/dist/floating-ui.react-dom-es.js +2072 -0
  112. package/dist/floating-ui.react-es.js +156 -2519
  113. package/dist/floating-ui.utils-cjs.js +342 -0
  114. package/dist/floating-ui.utils-es.js +300 -0
  115. package/dist/index.cjs +4 -3
  116. package/dist/index.mjs +4 -3
  117. package/dist/primitives/BottomSheet/index.cjs +1 -1
  118. package/dist/primitives/BottomSheet/index.mjs +1 -1
  119. package/dist/primitives/ComboboxPrimitive/index.cjs +4 -2
  120. package/dist/primitives/ComboboxPrimitive/index.mjs +4 -2
  121. package/dist/primitives/SelectPrimitive/index.cjs +2 -0
  122. package/dist/primitives/SelectPrimitive/index.mjs +2 -0
  123. package/dist/primitives/index.cjs +3 -2
  124. package/dist/primitives/index.mjs +3 -2
  125. package/dist/scrollEdges-cjs.js +11 -1
  126. package/dist/scrollEdges-es.js +11 -1
  127. package/dist/unstyledPrimitives/index.cjs +478 -720
  128. package/dist/unstyledPrimitives/index.mjs +421 -663
  129. package/dist/useCompositeListItem-cjs.js +361 -2108
  130. package/dist/useCompositeListItem-es.js +331 -2076
  131. package/dist/useLabel-cjs.js +3 -2
  132. package/dist/useLabel-es.js +3 -2
  133. package/dist/useRenderElement-cjs.js +7 -9
  134. package/dist/useRenderElement-es.js +7 -9
  135. package/dist/useScrollLock-cjs.js +344 -819
  136. package/dist/useScrollLock-es.js +296 -732
  137. package/dist/useScrollToActive-cjs.js +5 -4
  138. package/dist/useScrollToActive-es.js +2 -1
  139. package/package.json +3 -3
  140. package/dist/scrollable-cjs.js +0 -58
  141. package/dist/scrollable-es.js +0 -54
@@ -6,6 +6,8 @@ var useRenderElement = require('./useRenderElement-cjs.js');
6
6
  var useCompositeListItem = require('./useCompositeListItem-cjs.js');
7
7
  var scrollEdges = require('./scrollEdges-cjs.js');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
+ var floatingUi_utils = require('./floating-ui.utils-cjs.js');
10
+ var floatingUi_reactDom = require('./floating-ui.react-dom-cjs.js');
9
11
 
10
12
  function _interopNamespaceDefault(e) {
11
13
  var n = Object.create(null);
@@ -46,7 +48,7 @@ function usePreviousValue(value) {
46
48
  current: value,
47
49
  previous: null
48
50
  });
49
- if (!Object.is(value, state.current)) {
51
+ if (value !== state.current) {
50
52
  setState({
51
53
  current: value,
52
54
  previous: state.current
@@ -61,6 +63,8 @@ function isElementDisabled(element) {
61
63
 
62
64
  const SelectRootContext = /*#__PURE__*/React__namespace.createContext(null);
63
65
  if (process.env.NODE_ENV !== "production") SelectRootContext.displayName = "SelectRootContext";
66
+ const SelectFloatingContext = /*#__PURE__*/React__namespace.createContext(null);
67
+ if (process.env.NODE_ENV !== "production") SelectFloatingContext.displayName = "SelectFloatingContext";
64
68
  function useSelectRootContext() {
65
69
  const context = React__namespace.useContext(SelectRootContext);
66
70
  if (context === null) {
@@ -68,16 +72,25 @@ function useSelectRootContext() {
68
72
  }
69
73
  return context;
70
74
  }
75
+ function useSelectFloatingContext() {
76
+ const context = React__namespace.useContext(SelectFloatingContext);
77
+ if (context === null) {
78
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectFloatingContext is missing. Select parts must be placed within <Select.Root>.' : useRenderElement.formatErrorMessage(61));
79
+ }
80
+ return context;
81
+ }
71
82
 
72
83
  const selectors = {
73
- id: state => state.id,
74
- labelId: state => state.labelId,
75
- modal: state => state.modal,
76
- items: state => state.items,
77
- itemToStringLabel: state => state.itemToStringLabel,
78
- isItemEqualToValue: state => state.isItemEqualToValue,
79
- value: state => state.value,
80
- hasSelectedValue: state => {
84
+ id: useScrollLock.createSelector(state => state.id),
85
+ labelId: useScrollLock.createSelector(state => state.labelId),
86
+ modal: useScrollLock.createSelector(state => state.modal),
87
+ multiple: useScrollLock.createSelector(state => state.multiple),
88
+ items: useScrollLock.createSelector(state => state.items),
89
+ itemToStringLabel: useScrollLock.createSelector(state => state.itemToStringLabel),
90
+ itemToStringValue: useScrollLock.createSelector(state => state.itemToStringValue),
91
+ isItemEqualToValue: useScrollLock.createSelector(state => state.isItemEqualToValue),
92
+ value: useScrollLock.createSelector(state => state.value),
93
+ hasSelectedValue: useScrollLock.createSelector(state => {
81
94
  const {
82
95
  value,
83
96
  multiple,
@@ -90,19 +103,19 @@ const selectors = {
90
103
  return value.length > 0;
91
104
  }
92
105
  return useCompositeListItem.stringifyAsValue(value, itemToStringValue) !== '';
93
- },
94
- hasNullItemLabel: (state, enabled) => {
106
+ }),
107
+ hasNullItemLabel: useScrollLock.createSelector((state, enabled) => {
95
108
  return enabled ? useCompositeListItem.hasNullItemLabel(state.items) : false;
96
- },
97
- open: state => state.open,
98
- mounted: state => state.mounted,
99
- forceMount: state => state.forceMount,
100
- transitionStatus: state => state.transitionStatus,
101
- openMethod: state => state.openMethod,
102
- activeIndex: state => state.activeIndex,
103
- selectedIndex: state => state.selectedIndex,
104
- isActive: (state, index) => state.activeIndex === index,
105
- isSelected: (state, itemValue) => {
109
+ }),
110
+ open: useScrollLock.createSelector(state => state.open),
111
+ mounted: useScrollLock.createSelector(state => state.mounted),
112
+ forceMount: useScrollLock.createSelector(state => state.forceMount),
113
+ transitionStatus: useScrollLock.createSelector(state => state.transitionStatus),
114
+ openMethod: useScrollLock.createSelector(state => state.openMethod),
115
+ activeIndex: useScrollLock.createSelector(state => state.activeIndex),
116
+ selectedIndex: useScrollLock.createSelector(state => state.selectedIndex),
117
+ isActive: useScrollLock.createSelector((state, index) => state.activeIndex === index),
118
+ isSelected: useScrollLock.createSelector((state, itemValue) => {
106
119
  const comparer = state.isItemEqualToValue;
107
120
  const storeValue = state.value;
108
121
  if (state.multiple) {
@@ -113,19 +126,19 @@ const selectors = {
113
126
  // value changes while the popup is open, where the index sync is deferred) must not
114
127
  // keep a previously selected item marked as selected.
115
128
  return useCompositeListItem.compareItemEquality(itemValue, storeValue, comparer);
116
- },
117
- isSelectedByFocus: (state, index) => {
129
+ }),
130
+ isSelectedByFocus: useScrollLock.createSelector((state, index) => {
118
131
  return state.selectedIndex === index;
119
- },
120
- popupProps: state => state.popupProps,
121
- triggerProps: state => state.triggerProps,
122
- triggerElement: state => state.triggerElement,
123
- positionerElement: state => state.positionerElement,
124
- listElement: state => state.listElement,
125
- popupSide: state => state.popupSide,
126
- scrollUpArrowVisible: state => state.scrollUpArrowVisible,
127
- scrollDownArrowVisible: state => state.scrollDownArrowVisible,
128
- hasScrollArrows: state => state.hasScrollArrows
132
+ }),
133
+ popupProps: useScrollLock.createSelector(state => state.popupProps),
134
+ triggerProps: useScrollLock.createSelector(state => state.triggerProps),
135
+ triggerElement: useScrollLock.createSelector(state => state.triggerElement),
136
+ positionerElement: useScrollLock.createSelector(state => state.positionerElement),
137
+ listElement: useScrollLock.createSelector(state => state.listElement),
138
+ popupSide: useScrollLock.createSelector(state => state.popupSide),
139
+ scrollUpArrowVisible: useScrollLock.createSelector(state => state.scrollUpArrowVisible),
140
+ scrollDownArrowVisible: useScrollLock.createSelector(state => state.scrollDownArrowVisible),
141
+ hasScrollArrows: useScrollLock.createSelector(state => state.hasScrollArrows)
129
142
  };
130
143
 
131
144
  function SelectRoot(props) {
@@ -211,7 +224,7 @@ function SelectRoot(props) {
211
224
  openMethod,
212
225
  triggerProps: interactionTypeProps
213
226
  } = useScrollLock.useOpenInteractionType(open);
214
- const store = useRenderElement.useRefWithInit(() => new useScrollLock.ReactStore({
227
+ const store = useRenderElement.useRefWithInit(() => new useScrollLock.Store({
215
228
  id: generatedId,
216
229
  labelId: undefined,
217
230
  modal,
@@ -243,7 +256,7 @@ function SelectRoot(props) {
243
256
  const triggerElement = useScrollLock.useStore(store, selectors.triggerElement);
244
257
  const positionerElement = useScrollLock.useStore(store, selectors.positionerElement);
245
258
  const previousOpenMethod = usePreviousValue(openMethod);
246
- const renderedOpenMethod = openMethod ?? previousOpenMethod;
259
+ const renderedOpenMethod = openMethod ?? previousOpenMethod ?? null;
247
260
  const serializedValue = React__namespace.useMemo(() => {
248
261
  // In multiple mode the shared input is nameless; per-value entries are submitted via
249
262
  // `hiddenInputs`. Its value is therefore irrelevant, and passing the whole array to
@@ -259,26 +272,38 @@ function SelectRoot(props) {
259
272
  }
260
273
  return useCompositeListItem.stringifyAsValue(value, itemToStringValue);
261
274
  }, [multiple, value, itemToStringValue]);
262
- const controlRef = useScrollLock.useValueAsRef(triggerElement);
275
+ const controlRef = useScrollLock.useValueAsRef(store.state.triggerElement);
263
276
  const getStringifiedValueForForm = useScrollLock.useStableCallback(() => fieldStringValue);
264
277
  useCompositeListItem.useRegisterFieldControl(controlRef, generatedId, value, getStringifiedValueForForm, !disabled, nameProp);
265
278
  const initialValueRef = React__namespace.useRef(value);
266
279
  // Mirror the `hasSelectedValue` store selector so the Field's filled state agrees with the
267
280
  // trigger/value placeholder semantics (a value serializing to `''` counts as empty).
268
- const hasSelectedValue = multiple ? Array.isArray(value) && value.length > 0 : value != null && serializedValue !== '';
281
+ const hasSelectedValue = multiple ? Array.isArray(value) && value.length > 0 : value != null && useCompositeListItem.stringifyAsValue(value, itemToStringValue) !== '';
282
+ useScrollLock.useIsoLayoutEffect(() => {
283
+ // Ensure the values and labels are registered for programmatic value changes.
284
+ if (value !== initialValueRef.current) {
285
+ store.set('forceMount', true);
286
+ }
287
+ }, [store, value]);
269
288
  useScrollLock.useIsoLayoutEffect(() => {
270
289
  setFilled(hasSelectedValue);
271
290
  }, [hasSelectedValue, setFilled]);
272
291
  useScrollLock.useIsoLayoutEffect(function syncSelectedIndex() {
273
- let target = value;
274
- let empty = false;
292
+ const registry = valuesRef.current;
293
+ let nextIndex;
275
294
  if (multiple) {
276
295
  const currentValue = Array.isArray(value) ? value : [];
277
- empty = currentValue.length === 0;
278
- target = currentValue[currentValue.length - 1];
296
+ if (currentValue.length === 0) {
297
+ nextIndex = null;
298
+ } else {
299
+ const lastValue = currentValue[currentValue.length - 1];
300
+ const lastIndex = useCompositeListItem.findItemIndex(registry, lastValue, isItemEqualToValue);
301
+ nextIndex = lastIndex === -1 ? null : lastIndex;
302
+ }
303
+ } else {
304
+ const index = useCompositeListItem.findItemIndex(registry, value, isItemEqualToValue);
305
+ nextIndex = index === -1 ? null : index;
279
306
  }
280
- const index = empty ? -1 : useCompositeListItem.findItemIndex(valuesRef.current, target, isItemEqualToValue);
281
- const nextIndex = index === -1 ? null : index;
282
307
  if (nextIndex === null) {
283
308
  selectedItemTextRef.current = null;
284
309
  }
@@ -286,7 +311,7 @@ function SelectRoot(props) {
286
311
  return;
287
312
  }
288
313
  store.set('selectedIndex', nextIndex);
289
- }, [multiple, open, value, isItemEqualToValue, store]);
314
+ }, [hasSelectedValue, multiple, open, value, valuesRef, isItemEqualToValue, store, selectedItemTextRef]);
290
315
  function isSelectedValueDirty(currentValue) {
291
316
  const initialValue = validityData.initialValue;
292
317
  if (Array.isArray(currentValue) && Array.isArray(initialValue)) {
@@ -317,9 +342,7 @@ function SelectRoot(props) {
317
342
  setMounted(false);
318
343
  store.update({
319
344
  activeIndex: null,
320
- openMethod: null,
321
- scrollUpArrowVisible: false,
322
- scrollDownArrowVisible: false
345
+ openMethod: null
323
346
  });
324
347
  onOpenChangeComplete?.(false);
325
348
  });
@@ -343,13 +366,21 @@ function SelectRoot(props) {
343
366
  }
344
367
  setValueUnwrapped(nextValue);
345
368
  });
346
- const handleScrollArrowVisibility = useScrollLock.useStableCallback(scroller => {
369
+ const handleScrollArrowVisibility = useScrollLock.useStableCallback(() => {
370
+ const scroller = store.state.listElement || popupRef.current;
371
+ if (!scroller) {
372
+ return;
373
+ }
347
374
  const maxScrollTop = scrollEdges.getMaxScrollOffset(scroller.scrollHeight, scroller.clientHeight);
348
375
  const scrollTop = scrollEdges.normalizeScrollOffset(scroller.scrollTop, maxScrollTop);
349
376
  const shouldShowUp = scrollTop > 0;
350
377
  const shouldShowDown = scrollTop < maxScrollTop;
351
- store.set('scrollUpArrowVisible', shouldShowUp);
352
- store.set('scrollDownArrowVisible', shouldShowDown);
378
+ if (store.state.scrollUpArrowVisible !== shouldShowUp) {
379
+ store.set('scrollUpArrowVisible', shouldShowUp);
380
+ }
381
+ if (store.state.scrollDownArrowVisible !== shouldShowDown) {
382
+ store.set('scrollDownArrowVisible', shouldShowDown);
383
+ }
353
384
  });
354
385
  const floatingContext = useCompositeListItem.useFloatingRootContext({
355
386
  open,
@@ -394,43 +425,49 @@ function SelectRoot(props) {
394
425
  if (open) {
395
426
  store.set('activeIndex', index);
396
427
  } else {
397
- setValue(valuesRef.current[index], useScrollLock.createChangeEventDetails(useScrollLock.none));
428
+ setValue(valuesRef.current[index], useScrollLock.createChangeEventDetails('none'));
398
429
  }
399
430
  },
400
431
  onTyping(typing) {
401
432
  typingRef.current = typing;
402
433
  }
403
434
  });
404
-
405
- // `Select.Trigger` applies the id itself from the store, so it's deliberately not merged here.
406
- const mergedTriggerProps = React__namespace.useMemo(() => useRenderElement.mergeProps(typeahead.reference, listNavigation.reference, dismiss.reference, click.reference, interactionTypeProps), [click.reference, typeahead.reference, listNavigation.reference, dismiss.reference, interactionTypeProps]);
435
+ const mergedTriggerProps = React__namespace.useMemo(() => {
436
+ const triggerInteractionProps = useRenderElement.mergeProps(typeahead.reference, listNavigation.reference, dismiss.reference, click.reference, interactionTypeProps);
437
+ if (generatedId) {
438
+ triggerInteractionProps.id = generatedId;
439
+ }
440
+ return triggerInteractionProps;
441
+ }, [click.reference, typeahead.reference, listNavigation.reference, dismiss.reference, interactionTypeProps, generatedId]);
407
442
  const popupProps = React__namespace.useMemo(() => useRenderElement.mergeProps(useScrollLock.FOCUSABLE_POPUP_PROPS, typeahead.floating, listNavigation.floating, dismiss.floating), [typeahead.floating, listNavigation.floating, dismiss.floating]);
408
443
  const itemProps = listNavigation.item ?? useRenderElement.EMPTY_OBJECT;
409
- useCompositeListItem.useOnFirstRender(() => {
444
+ useScrollLock.useOnFirstRender(() => {
410
445
  store.update({
411
446
  popupProps,
412
447
  triggerProps: mergedTriggerProps
413
448
  });
414
449
  });
415
- store.useSyncedValues({
416
- id: generatedId,
417
- modal,
418
- multiple,
419
- value,
420
- open,
421
- mounted,
422
- transitionStatus,
423
- popupProps,
424
- triggerProps: mergedTriggerProps,
425
- items,
426
- itemToStringLabel,
427
- itemToStringValue,
428
- isItemEqualToValue,
429
- openMethod: renderedOpenMethod
430
- });
450
+ useScrollLock.useIsoLayoutEffect(() => {
451
+ store.update({
452
+ id: generatedId,
453
+ modal,
454
+ multiple,
455
+ value,
456
+ open,
457
+ mounted,
458
+ transitionStatus,
459
+ popupProps,
460
+ triggerProps: mergedTriggerProps,
461
+ items,
462
+ itemToStringLabel,
463
+ itemToStringValue,
464
+ isItemEqualToValue,
465
+ openMethod: renderedOpenMethod
466
+ });
467
+ }, [store, generatedId, modal, multiple, value, open, mounted, transitionStatus, popupProps, mergedTriggerProps, items, itemToStringLabel, itemToStringValue, isItemEqualToValue, renderedOpenMethod]);
431
468
  const contextValue = React__namespace.useMemo(() => ({
432
469
  store,
433
- floatingContext,
470
+ name,
434
471
  required,
435
472
  disabled,
436
473
  readOnly,
@@ -455,8 +492,9 @@ function SelectRoot(props) {
455
492
  onOpenChangeComplete,
456
493
  alignItemWithTriggerActiveRef,
457
494
  initialValueRef
458
- }), [store, floatingContext, required, disabled, readOnly, multiple, highlightItemOnHover, setValue, setOpen, itemProps, validation, onOpenChangeComplete, handleScrollArrowVisibility]);
495
+ }), [store, name, required, disabled, readOnly, multiple, highlightItemOnHover, setValue, setOpen, itemProps, validation, onOpenChangeComplete, handleScrollArrowVisibility]);
459
496
  const ref = useRenderElement.useMergedRefs(inputRef, validation.inputRef);
497
+ const hasMultipleSelection = multiple && Array.isArray(value) && value.length > 0;
460
498
  const hiddenInputName = multiple ? undefined : name;
461
499
  const hiddenInputs = React__namespace.useMemo(() => {
462
500
  if (!multiple || !Array.isArray(value) || !name) {
@@ -473,69 +511,73 @@ function SelectRoot(props) {
473
511
  }, currentSerializedValue);
474
512
  });
475
513
  }, [multiple, value, form, name, itemToStringValue, disabled]);
476
- return /*#__PURE__*/jsxRuntime.jsxs(SelectRootContext.Provider, {
514
+ return /*#__PURE__*/jsxRuntime.jsx(SelectRootContext.Provider, {
477
515
  value: contextValue,
478
- children: [children, /*#__PURE__*/jsxRuntime.jsx("input", {
479
- ...validation.getValidationProps(disabled, {
480
- onFocus() {
481
- // Move focus to the trigger element when the hidden input is focused.
482
- store.state.triggerElement?.focus({
483
- // Supported in Chrome from 144 (January 2026)
484
- focusVisible: true
485
- });
486
- },
487
- // Handle browser autofill.
488
- onChange(event) {
489
- // Workaround for https://github.com/react/react/issues/9023
490
- if (event.nativeEvent.defaultPrevented || disabled || readOnly) {
491
- return;
492
- }
493
- const nextValue = event.currentTarget.value;
494
- const details = useScrollLock.createChangeEventDetails(useScrollLock.none, event.nativeEvent);
495
- function handleChange() {
496
- if (multiple) {
497
- // Browser autofill only writes a single scalar value.
516
+ children: /*#__PURE__*/jsxRuntime.jsxs(SelectFloatingContext.Provider, {
517
+ value: floatingContext,
518
+ children: [children, /*#__PURE__*/jsxRuntime.jsx("input", {
519
+ ...validation.getValidationProps(disabled, {
520
+ onFocus() {
521
+ // Move focus to the trigger element when the hidden input is focused.
522
+ store.state.triggerElement?.focus({
523
+ // Supported in Chrome from 144 (January 2026)
524
+ focusVisible: true
525
+ });
526
+ },
527
+ // Handle browser autofill.
528
+ onChange(event) {
529
+ // Workaround for https://github.com/facebook/react/issues/9023
530
+ if (event.nativeEvent.defaultPrevented || disabled || readOnly) {
498
531
  return;
499
532
  }
533
+ const nextValue = event.currentTarget.value;
534
+ const details = useScrollLock.createChangeEventDetails(useScrollLock.none, event.nativeEvent);
535
+ function handleChange() {
536
+ if (multiple) {
537
+ // Browser autofill only writes a single scalar value.
538
+ return;
539
+ }
500
540
 
501
- // Preserve the original serialized matching, then fall back to rendered text,
502
- // which browsers can autofill for primitive values like `value="US">United States`.
503
- const nextValueLower = nextValue.toLowerCase();
504
- let matchingIndex = valuesRef.current.findIndex(candidate => useCompositeListItem.stringifyAsValue(candidate, itemToStringValue).toLowerCase() === nextValueLower || useCompositeListItem.stringifyAsLabel(candidate, itemToStringLabel).toLowerCase() === nextValueLower);
505
- if (matchingIndex === -1) {
506
- matchingIndex = valuesRef.current.findIndex((_, index) => {
507
- const renderedLabel = labelsRef.current[index];
508
- return renderedLabel != null && renderedLabel.toLowerCase() === nextValueLower;
509
- });
510
- }
511
- const matchingValue = valuesRef.current[matchingIndex];
512
- if (matchingValue != null) {
513
- // `setValue` may be canceled by `onValueChange`; rely on `useValueChanged` to
514
- // mark the field dirty and run validation only when the value actually changes.
515
- setValue(matchingValue, details);
541
+ // Preserve the original serialized matching, then fall back to rendered text,
542
+ // which browsers can autofill for primitive values like `value="US">United States`.
543
+ const nextValueLower = nextValue.toLowerCase();
544
+ let matchingIndex = valuesRef.current.findIndex(candidate => useCompositeListItem.stringifyAsValue(candidate, itemToStringValue).toLowerCase() === nextValueLower || useCompositeListItem.stringifyAsLabel(candidate, itemToStringLabel).toLowerCase() === nextValueLower);
545
+ if (matchingIndex === -1) {
546
+ matchingIndex = valuesRef.current.findIndex((_, index) => {
547
+ const renderedLabel = labelsRef.current[index];
548
+ return renderedLabel != null && renderedLabel.toLowerCase() === nextValueLower;
549
+ });
550
+ }
551
+ const matchingValue = matchingIndex === -1 ? undefined : valuesRef.current[matchingIndex];
552
+ if (matchingValue != null) {
553
+ // `setValue` may be canceled by `onValueChange`; rely on `useValueChanged` to
554
+ // mark the field dirty and run validation only when the value actually changes.
555
+ setValue(matchingValue, details);
556
+ }
516
557
  }
558
+ store.set('forceMount', true);
559
+ queueMicrotask(handleChange);
517
560
  }
518
- store.set('forceMount', true);
519
- queueMicrotask(handleChange);
520
- }
521
- }),
522
- id: generatedId && hiddenInputName == null ? `${generatedId}-hidden-input` : undefined,
523
- form: form,
524
- name: hiddenInputName,
525
- autoComplete: autoComplete,
526
- value: serializedValue,
527
- disabled: disabled,
528
- required: required && !(multiple && hasSelectedValue),
529
- readOnly: readOnly,
530
- ref: ref,
531
- style: name ? useScrollLock.visuallyHiddenInput : useScrollLock.visuallyHidden,
532
- tabIndex: -1,
533
- "aria-hidden": true,
534
- suppressHydrationWarning: true
535
- }), hiddenInputs]
561
+ }),
562
+ id: generatedId && hiddenInputName == null ? `${generatedId}-hidden-input` : undefined,
563
+ form: form,
564
+ name: hiddenInputName,
565
+ autoComplete: autoComplete,
566
+ value: serializedValue,
567
+ disabled: disabled,
568
+ required: required && !hasMultipleSelection,
569
+ readOnly: readOnly,
570
+ ref: ref,
571
+ style: name ? useScrollLock.visuallyHiddenInput : useScrollLock.visuallyHidden,
572
+ tabIndex: -1,
573
+ "aria-hidden": true,
574
+ suppressHydrationWarning: true
575
+ }), hiddenInputs]
576
+ })
536
577
  });
537
578
  }
538
579
 
580
+ const BOUNDARY_OFFSET = 2;
539
581
  const SELECTED_DELAY = 400;
540
582
  const stateAttributesMapping$2 = {
541
583
  ...useScrollLock.pressableTriggerOpenStateMapping,
@@ -608,7 +650,9 @@ const SelectTrigger = /*#__PURE__*/React__namespace.forwardRef(function SelectTr
608
650
  disabled,
609
651
  native: nativeButton
610
652
  });
611
- const setTriggerElement = store.useStateSetter('triggerElement');
653
+ const setTriggerElement = useScrollLock.useStableCallback(element => {
654
+ store.set('triggerElement', element);
655
+ });
612
656
  const timeoutFocus = useScrollLock.useTimeout();
613
657
  const timeoutMouseDown = useScrollLock.useTimeout();
614
658
  const selectedDelayTimeout = useScrollLock.useTimeout();
@@ -637,7 +681,7 @@ const SelectTrigger = /*#__PURE__*/React__namespace.forwardRef(function SelectTr
637
681
  const mergedProps = useRenderElement.mergeProps(triggerProps, {
638
682
  id,
639
683
  role: 'combobox',
640
- 'aria-expanded': open,
684
+ 'aria-expanded': open ? 'true' : 'false',
641
685
  'aria-haspopup': 'listbox',
642
686
  'aria-controls': open ? listElement?.id ?? useScrollLock.getFloatingFocusElement(positionerElement)?.id : undefined,
643
687
  'aria-labelledby': ariaLabelledBy,
@@ -686,7 +730,8 @@ const SelectTrigger = /*#__PURE__*/React__namespace.forwardRef(function SelectTr
686
730
  if (useScrollLock.contains(triggerRef.current, mouseUpTarget) || useScrollLock.contains(positionerRef.current, mouseUpTarget)) {
687
731
  return;
688
732
  }
689
- if (useCompositeListItem.isMouseWithinBounds(mouseEvent, triggerRef.current)) {
733
+ const bounds = useCompositeListItem.getPseudoElementBounds(triggerRef.current);
734
+ 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) {
690
735
  return;
691
736
  }
692
737
  setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.cancelOpen, mouseEvent));
@@ -761,7 +806,7 @@ const SelectValue = /*#__PURE__*/React__namespace.forwardRef(function SelectValu
761
806
  children = childrenProp(value);
762
807
  } else if (childrenProp != null) {
763
808
  children = childrenProp;
764
- } else if (shouldCheckNullItemLabel && !hasNullLabel) {
809
+ } else if (!hasSelectedValue && placeholder != null && !hasNullLabel) {
765
810
  children = placeholder;
766
811
  } else if (Array.isArray(value)) {
767
812
  children = useCompositeListItem.resolveMultipleLabels(value, items, itemToStringLabel);
@@ -780,6 +825,9 @@ const SelectValue = /*#__PURE__*/React__namespace.forwardRef(function SelectValu
780
825
  });
781
826
  if (process.env.NODE_ENV !== "production") SelectValue.displayName = "SelectValue";
782
827
 
828
+ const SelectPortalContext = /*#__PURE__*/React__namespace.createContext(undefined);
829
+ if (process.env.NODE_ENV !== "production") SelectPortalContext.displayName = "SelectPortalContext";
830
+
783
831
  const SelectPortal = /*#__PURE__*/React__namespace.forwardRef(function SelectPortal(portalProps, forwardedRef) {
784
832
  const {
785
833
  store
@@ -790,9 +838,12 @@ const SelectPortal = /*#__PURE__*/React__namespace.forwardRef(function SelectPor
790
838
  if (!shouldRender) {
791
839
  return null;
792
840
  }
793
- return /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingPortal, {
794
- ref: forwardedRef,
795
- ...portalProps
841
+ return /*#__PURE__*/jsxRuntime.jsx(SelectPortalContext.Provider, {
842
+ value: true,
843
+ children: /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingPortal, {
844
+ ref: forwardedRef,
845
+ ...portalProps
846
+ })
796
847
  });
797
848
  });
798
849
  if (process.env.NODE_ENV !== "production") SelectPortal.displayName = "SelectPortal";
@@ -832,19 +883,17 @@ const FIXED = {
832
883
  const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function SelectPositioner(componentProps, forwardedRef) {
833
884
  const {
834
885
  anchor,
886
+ positionMethod = 'absolute',
835
887
  className,
836
888
  render,
837
- // `useAnchorPositioning` applies the same defaults to the undefined values; the names
838
- // remain destructured to exclude the props from `elementProps`.
839
- positionMethod,
840
- side,
841
- align,
842
- sideOffset,
843
- alignOffset,
889
+ side = 'bottom',
890
+ align = 'center',
891
+ sideOffset = 0,
892
+ alignOffset = 0,
844
893
  collisionBoundary = 'clipping-ancestors',
845
894
  collisionPadding,
846
- arrowPadding,
847
- sticky,
895
+ arrowPadding = 5,
896
+ sticky = false,
848
897
  disableAnchorTracking,
849
898
  alignItemWithTrigger = true,
850
899
  collisionAvoidance = useScrollLock.DROPDOWN_COLLISION_AVOIDANCE,
@@ -860,9 +909,9 @@ const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function Selec
860
909
  valuesRef,
861
910
  initialValueRef,
862
911
  popupRef,
863
- setValue,
864
- floatingContext: floatingRootContext
912
+ setValue
865
913
  } = useSelectRootContext();
914
+ const floatingRootContext = useSelectFloatingContext();
866
915
  const open = useScrollLock.useStore(store, selectors.open);
867
916
  const mounted = useScrollLock.useStore(store, selectors.mounted);
868
917
  const modal = useScrollLock.useStore(store, selectors.modal);
@@ -879,6 +928,16 @@ const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function Selec
879
928
  if (!mounted && controlledAlignItemWithTrigger !== alignItemWithTrigger) {
880
929
  setControlledAlignItemWithTrigger(alignItemWithTrigger);
881
930
  }
931
+ useScrollLock.useIsoLayoutEffect(() => {
932
+ if (!mounted) {
933
+ if (selectors.scrollUpArrowVisible(store.state)) {
934
+ store.set('scrollUpArrowVisible', false);
935
+ }
936
+ if (selectors.scrollDownArrowVisible(store.state)) {
937
+ store.set('scrollDownArrowVisible', false);
938
+ }
939
+ }
940
+ }, [store, mounted]);
882
941
  React__namespace.useImperativeHandle(alignItemWithTriggerActiveRef, () => alignItemWithTriggerActive);
883
942
  useCompositeListItem.useAnchoredPopupScrollLock((alignItemWithTriggerActive || modal) && open, openMethod === 'touch', positionerElement, triggerElement);
884
943
  const positioning = useCompositeListItem.useAnchorPositioning({
@@ -909,7 +968,9 @@ const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function Selec
909
968
  useScrollLock.useIsoLayoutEffect(() => {
910
969
  store.set('popupSide', positioning.side);
911
970
  }, [store, positioning.side]);
912
- const setPositionerElement = store.useStateSetter('positionerElement');
971
+ const setPositionerElement = useScrollLock.useStableCallback(element => {
972
+ store.set('positionerElement', element);
973
+ });
913
974
  const element = useCompositeListItem.usePositioner(componentProps, state, {
914
975
  styles: positionerStyles,
915
976
  transitionStatus,
@@ -920,6 +981,9 @@ const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function Selec
920
981
  });
921
982
  const prevMapSizeRef = React__namespace.useRef(0);
922
983
  const onMapChange = useScrollLock.useStableCallback(map => {
984
+ if (map.size === 0 && prevMapSizeRef.current === 0) {
985
+ return;
986
+ }
923
987
  if (valuesRef.current.length === 0) {
924
988
  return;
925
989
  }
@@ -943,8 +1007,9 @@ const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function Selec
943
1007
  }
944
1008
  }
945
1009
  if (prevSize !== 0 && store.state.multiple && Array.isArray(value)) {
946
- const nextValue = value.filter(selectedItemValue => useCompositeListItem.findItemIndex(valuesRef.current, selectedItemValue, isItemEqualToValue) !== -1);
947
- if (nextValue.length !== value.length) {
1010
+ const hasVisibleItem = selectedItemValue => useCompositeListItem.findItemIndex(valuesRef.current, selectedItemValue, isItemEqualToValue) !== -1;
1011
+ const nextValue = value.filter(selectedItemValue => hasVisibleItem(selectedItemValue));
1012
+ if (nextValue.length !== value.length || nextValue.some(selectedItemValue => !useCompositeListItem.selectedValueIncludes(value, selectedItemValue, isItemEqualToValue))) {
948
1013
  setValue(nextValue, eventDetails);
949
1014
  if (nextValue.length === 0) {
950
1015
  store.set('selectedIndex', null);
@@ -1018,8 +1083,7 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1018
1083
  handleScrollArrowVisibility,
1019
1084
  scrollHandlerRef,
1020
1085
  listRef,
1021
- highlightItemOnHover,
1022
- floatingContext: floatingRootContext
1086
+ highlightItemOnHover
1023
1087
  } = useSelectRootContext();
1024
1088
  const {
1025
1089
  side,
@@ -1029,6 +1093,7 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1029
1093
  setControlledAlignItemWithTrigger
1030
1094
  } = useSelectPositionerContext();
1031
1095
  const insideToolbar = useToolbarRootContext(true) != null;
1096
+ const floatingRootContext = useSelectFloatingContext();
1032
1097
  const direction = useScrollLock.useDirection();
1033
1098
  const {
1034
1099
  nonce,
@@ -1051,16 +1116,20 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1051
1116
  if (!positionerElement || !popupRef.current || !initialPlacedRef.current) {
1052
1117
  return;
1053
1118
  }
1119
+ if (reachedMaxHeightRef.current || !alignItemWithTriggerActive) {
1120
+ handleScrollArrowVisibility();
1121
+ return;
1122
+ }
1054
1123
  const isTopPositioned = positionerElement.style.top === '0px';
1055
1124
  const isBottomPositioned = positionerElement.style.bottom === '0px';
1056
- if (reachedMaxHeightRef.current || !alignItemWithTriggerActive || !isTopPositioned && !isBottomPositioned) {
1057
- handleScrollArrowVisibility(scroller);
1125
+ if (!isTopPositioned && !isBottomPositioned) {
1126
+ handleScrollArrowVisibility();
1058
1127
  return;
1059
1128
  }
1060
1129
  const scale = getScale(positionerElement);
1061
1130
  const currentHeight = normalizeSize(positionerElement.getBoundingClientRect().height, 'y', scale);
1062
1131
  const doc = useScrollLock.ownerDocument(positionerElement);
1063
- const win = useScrollLock.getWindow(positionerElement);
1132
+ const win = floatingUi_utils.getWindow(positionerElement);
1064
1133
  const positionerStyles = win.getComputedStyle(positionerElement);
1065
1134
  const marginTop = parseFloat(positionerStyles.marginTop);
1066
1135
  const marginBottom = parseFloat(positionerStyles.marginBottom);
@@ -1068,50 +1137,63 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1068
1137
  const maxAvailableHeight = Math.min(doc.documentElement.clientHeight - marginTop - marginBottom, maxPopupHeight);
1069
1138
  const scrollTop = scroller.scrollTop;
1070
1139
  const maxScrollTop = getMaxScrollTop(scroller);
1071
-
1072
- // `Infinity` requests a scroll to the recomputed maximum offset.
1140
+ let nextPositionerHeight = 0;
1073
1141
  let nextScrollTop = null;
1142
+ let setReachedMax = false;
1143
+ let scrollToMax = false;
1074
1144
  const setHeight = height => {
1075
1145
  positionerElement.style.height = `${height}px`;
1076
1146
  };
1077
- const diff = isTopPositioned ? maxScrollTop - scrollTop : scrollTop;
1078
- const nextHeight = Math.min(currentHeight + diff, maxAvailableHeight);
1079
- if (diff <= scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
1147
+ const handleSmallDiff = (diff, targetScrollTop) => {
1080
1148
  const heightDelta = scrollEdges.clamp(diff, 0, maxAvailableHeight - currentHeight);
1081
1149
  if (heightDelta > 0) {
1082
1150
  // Consume the remaining scroll in height.
1083
1151
  setHeight(currentHeight + heightDelta);
1084
1152
  }
1085
- scroller.scrollTop = isTopPositioned ? maxScrollTop : 0;
1153
+ scroller.scrollTop = targetScrollTop;
1086
1154
  if (maxAvailableHeight - (currentHeight + heightDelta) <= scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
1087
1155
  reachedMaxHeightRef.current = true;
1088
1156
  }
1089
- handleScrollArrowVisibility(scroller);
1157
+ handleScrollArrowVisibility();
1158
+ };
1159
+ const diff = isTopPositioned ? maxScrollTop - scrollTop : scrollTop;
1160
+ const nextHeight = Math.min(currentHeight + diff, maxAvailableHeight);
1161
+ nextPositionerHeight = nextHeight;
1162
+ if (diff <= scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
1163
+ handleSmallDiff(diff, isTopPositioned ? maxScrollTop : 0);
1090
1164
  return;
1091
1165
  }
1092
1166
  if (maxAvailableHeight - nextHeight > scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
1093
- nextScrollTop = isTopPositioned ? Infinity : 0;
1094
- } else if (isBottomPositioned && scrollTop < maxScrollTop) {
1095
- const overshoot = currentHeight + diff - maxAvailableHeight;
1096
- nextScrollTop = scrollTop - (diff - overshoot);
1167
+ if (isTopPositioned) {
1168
+ scrollToMax = true;
1169
+ } else {
1170
+ nextScrollTop = 0;
1171
+ }
1172
+ } else {
1173
+ setReachedMax = true;
1174
+ if (isBottomPositioned && scrollTop < maxScrollTop) {
1175
+ const overshoot = currentHeight + diff - maxAvailableHeight;
1176
+ nextScrollTop = scrollTop - (diff - overshoot);
1177
+ }
1097
1178
  }
1098
- const nextPositionerHeight = Math.ceil(nextHeight);
1179
+ nextPositionerHeight = Math.ceil(nextPositionerHeight);
1099
1180
  if (nextPositionerHeight !== 0) {
1100
1181
  setHeight(nextPositionerHeight);
1101
1182
  }
1102
- if (nextScrollTop != null) {
1183
+ if (scrollToMax || nextScrollTop != null) {
1103
1184
  // Recompute bounds after resizing (clientHeight likely changed).
1104
- const target = scrollEdges.clamp(nextScrollTop, 0, getMaxScrollTop(scroller));
1185
+ const nextMaxScrollTop = getMaxScrollTop(scroller);
1186
+ const target = scrollToMax ? nextMaxScrollTop : scrollEdges.clamp(nextScrollTop, 0, nextMaxScrollTop);
1105
1187
 
1106
1188
  // Avoid adjustments that re-trigger scroll events forever.
1107
1189
  if (Math.abs(scroller.scrollTop - target) > scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
1108
1190
  scroller.scrollTop = target;
1109
1191
  }
1110
1192
  }
1111
- if (nextPositionerHeight >= maxAvailableHeight - scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
1193
+ if (setReachedMax || nextPositionerHeight >= maxAvailableHeight - scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
1112
1194
  reachedMaxHeightRef.current = true;
1113
1195
  }
1114
- handleScrollArrowVisibility(scroller);
1196
+ handleScrollArrowVisibility();
1115
1197
  });
1116
1198
  React__namespace.useImperativeHandle(scrollHandlerRef, () => handleScroll, [handleScroll]);
1117
1199
  useScrollLock.useOpenChangeComplete({
@@ -1162,18 +1244,17 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1162
1244
  if (!open || !triggerElement || !positionerElement || !popupElement || alignItemWithTriggerActive && !isPositioned || store.state.transitionStatus === 'ending') {
1163
1245
  return;
1164
1246
  }
1165
- initialPlacedRef.current = true;
1166
- popupElement.style.removeProperty('--transform-origin');
1167
1247
  if (!alignItemWithTriggerActive) {
1168
- // The wrapper supplies the scroller: the list owns scrolling once it has mounted, and
1169
- // this effect re-runs (cancelling the stale frame) when that happens.
1170
- scrollArrowFrame.request(() => handleScrollArrowVisibility(listElement || popupElement));
1248
+ initialPlacedRef.current = true;
1249
+ scrollArrowFrame.request(handleScrollArrowVisibility);
1250
+ popupElement.style.removeProperty('--transform-origin');
1171
1251
  return;
1172
1252
  }
1173
1253
 
1174
1254
  // Ensure we remove any transforms that can affect the location of the popup
1175
1255
  // and therefore the calculations.
1176
1256
  const restoreTransformStyles = unsetTransformStyles(popupElement);
1257
+ popupElement.style.removeProperty('--transform-origin');
1177
1258
  try {
1178
1259
  let textElement = selectedItemTextRef.current;
1179
1260
  if (!textElement?.isConnected) {
@@ -1181,7 +1262,7 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1181
1262
  textElement = !hasSelectedValue && firstItemTextRef.current?.isConnected ? firstItemTextRef.current : null;
1182
1263
  }
1183
1264
  const valueElement = valueRef.current;
1184
- const win = useScrollLock.getWindow(positionerElement);
1265
+ const win = floatingUi_utils.getWindow(positionerElement);
1185
1266
  const positionerStyles = win.getComputedStyle(positionerElement);
1186
1267
  const popupStyles = win.getComputedStyle(popupElement);
1187
1268
  const doc = useScrollLock.ownerDocument(triggerElement);
@@ -1242,6 +1323,7 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1242
1323
  // Safari doesn't position the popup correctly when pinch-zoomed.
1243
1324
  const isPinchZoomed = (win.visualViewport?.scale ?? 1) !== 1 && useScrollLock.webkit;
1244
1325
  if (fallbackToAlignPopupToTrigger || isPinchZoomed) {
1326
+ initialPlacedRef.current = true;
1245
1327
  clearStyles(positionerElement, originalPositionerStylesRef.current);
1246
1328
  setControlledAlignItemWithTrigger(false);
1247
1329
  return;
@@ -1260,16 +1342,18 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1260
1342
  const popupTop = positionerRect.top;
1261
1343
  const popupHeight = positionerRect.height;
1262
1344
  const textCenterY = textRect.top + textRect.height / 2;
1263
- const clampedY = scrollEdges.clamp(popupHeight > 0 ? (textCenterY - popupTop) / popupHeight * 100 : 50, 0, 100);
1345
+ const transformOriginY = popupHeight > 0 ? (textCenterY - popupTop) / popupHeight * 100 : 50;
1346
+ const clampedY = scrollEdges.clamp(transformOriginY, 0, 100);
1264
1347
  popupElement.style.setProperty('--transform-origin', `50% ${clampedY}%`);
1265
1348
  }
1266
1349
  if (initialHeight === viewportHeight || height >= maxPopupHeight) {
1267
1350
  reachedMaxHeightRef.current = true;
1268
1351
  }
1269
- handleScrollArrowVisibility(scroller);
1352
+ handleScrollArrowVisibility();
1270
1353
  if (highlightItemOnHover && store.state.selectedIndex === null && store.state.activeIndex === null && listRef.current[0] != null) {
1271
1354
  store.set('activeIndex', 0);
1272
1355
  }
1356
+ initialPlacedRef.current = true;
1273
1357
  } finally {
1274
1358
  restoreTransformStyles();
1275
1359
  }
@@ -1278,7 +1362,7 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1278
1362
  if (!alignItemWithTriggerActive || !positionerElement || !open) {
1279
1363
  return undefined;
1280
1364
  }
1281
- const win = useScrollLock.getWindow(positionerElement);
1365
+ const win = floatingUi_utils.getWindow(positionerElement);
1282
1366
  function handleResize(event) {
1283
1367
  setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.windowResize, event));
1284
1368
  }
@@ -1308,14 +1392,15 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1308
1392
  style: listElement ? {
1309
1393
  height: '100%'
1310
1394
  } : LIST_FUNCTIONAL_STYLES
1311
- }),
1312
- className: !listElement && alignItemWithTriggerActive ? scrollEdges.styleDisableScrollbar.className : undefined
1395
+ })
1313
1396
  };
1314
1397
  const element = useRenderElement.useRenderElement('div', componentProps, {
1315
1398
  ref: [forwardedRef, popupRef],
1316
1399
  state,
1317
1400
  stateAttributesMapping,
1318
- props: [popupProps, defaultProps, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), elementProps]
1401
+ props: [popupProps, defaultProps, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), {
1402
+ className: !listElement && alignItemWithTriggerActive ? scrollEdges.styleDisableScrollbar.className : undefined
1403
+ }, elementProps]
1319
1404
  });
1320
1405
  return /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
1321
1406
  children: [!disableStyleElements && scrollEdges.styleDisableScrollbar.getElement(nonce), /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingFocusManager, {
@@ -1331,7 +1416,7 @@ const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopu
1331
1416
  });
1332
1417
  if (process.env.NODE_ENV !== "production") SelectPopup.displayName = "SelectPopup";
1333
1418
  function getMaxPopupHeight(popupStyles) {
1334
- const maxHeightStyle = popupStyles.maxHeight;
1419
+ const maxHeightStyle = popupStyles.maxHeight || '';
1335
1420
  return maxHeightStyle.endsWith('px') ? parseFloat(maxHeightStyle) || Infinity : Infinity;
1336
1421
  }
1337
1422
  function getMaxScrollTop(scroller) {
@@ -1339,13 +1424,13 @@ function getMaxScrollTop(scroller) {
1339
1424
  }
1340
1425
  function getScale(element) {
1341
1426
  // The platform API is async-capable, but the DOM platform returns a plain scale object.
1342
- return useCompositeListItem.platform.getScale(element);
1427
+ return floatingUi_reactDom.platform.getScale(element);
1343
1428
  }
1344
1429
  function normalizeSize(size, axis, scale) {
1345
1430
  return size / scale[axis];
1346
1431
  }
1347
1432
  function normalizeRect(rect, scale) {
1348
- return useScrollLock.rectToClientRect({
1433
+ return floatingUi_utils.rectToClientRect({
1349
1434
  x: normalizeSize(rect.x, 'x', scale),
1350
1435
  y: normalizeSize(rect.y, 'y', scale),
1351
1436
  width: normalizeSize(rect.width, 'x', scale),
@@ -1389,14 +1474,14 @@ const SelectList = /*#__PURE__*/React__namespace.forwardRef(function SelectList(
1389
1474
  } = componentProps;
1390
1475
  const {
1391
1476
  store,
1392
- scrollHandlerRef,
1393
- multiple
1477
+ scrollHandlerRef
1394
1478
  } = useSelectRootContext();
1395
1479
  const {
1396
1480
  alignItemWithTriggerActive
1397
1481
  } = useSelectPositionerContext();
1398
1482
  const hasScrollArrows = useScrollLock.useStore(store, selectors.hasScrollArrows);
1399
1483
  const openMethod = useScrollLock.useStore(store, selectors.openMethod);
1484
+ const multiple = useScrollLock.useStore(store, selectors.multiple);
1400
1485
  const id = useScrollLock.useStore(store, selectors.id);
1401
1486
  const defaultProps = {
1402
1487
  id: `${id}-list`,
@@ -1410,7 +1495,9 @@ const SelectList = /*#__PURE__*/React__namespace.forwardRef(function SelectList(
1410
1495
  }),
1411
1496
  className: hasScrollArrows && openMethod !== 'touch' ? scrollEdges.styleDisableScrollbar.className : undefined
1412
1497
  };
1413
- const setListElement = store.useStateSetter('listElement');
1498
+ const setListElement = useScrollLock.useStableCallback(element => {
1499
+ store.set('listElement', element);
1500
+ });
1414
1501
  return useRenderElement.useRenderElement('div', componentProps, {
1415
1502
  ref: [forwardedRef, setListElement],
1416
1503
  props: [defaultProps, elementProps]
@@ -1435,15 +1522,15 @@ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namesp
1435
1522
  style,
1436
1523
  value: itemValue = null,
1437
1524
  label,
1438
- disabled: disabledProp = false,
1525
+ disabled = false,
1439
1526
  nativeButton = false,
1440
1527
  ...elementProps
1441
1528
  } = componentProps;
1442
1529
  const textRef = React__namespace.useRef(null);
1443
1530
  const listItem = useCompositeListItem.useCompositeListItem({
1444
- guess: true,
1445
1531
  label,
1446
- textRef
1532
+ textRef,
1533
+ indexGuessBehavior: useCompositeListItem.IndexGuessBehavior.GuessFromOrder
1447
1534
  });
1448
1535
  const {
1449
1536
  store,
@@ -1458,22 +1545,28 @@ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namesp
1458
1545
  disabled: selectDisabled,
1459
1546
  readOnly
1460
1547
  } = useSelectRootContext();
1461
- const disabled = selectDisabled || disabledProp;
1462
1548
  const highlighted = useScrollLock.useStore(store, selectors.isActive, listItem.index);
1463
1549
  const open = useScrollLock.useStore(store, selectors.open);
1464
1550
  const selected = useScrollLock.useStore(store, selectors.isSelected, itemValue);
1465
1551
  const selectedByFocus = useScrollLock.useStore(store, selectors.isSelectedByFocus, listItem.index);
1466
1552
  const isItemEqualToValue = useScrollLock.useStore(store, selectors.isItemEqualToValue);
1467
1553
  const index = listItem.index;
1554
+ const hasRegistered = index !== -1;
1468
1555
  const itemRef = React__namespace.useRef(null);
1469
1556
  useScrollLock.useIsoLayoutEffect(() => {
1557
+ if (!hasRegistered) {
1558
+ return undefined;
1559
+ }
1470
1560
  const values = valuesRef.current;
1471
1561
  values[index] = itemValue;
1472
1562
  return () => {
1473
1563
  delete values[index];
1474
1564
  };
1475
- }, [index, itemValue, valuesRef]);
1565
+ }, [hasRegistered, index, itemValue, valuesRef]);
1476
1566
  useScrollLock.useIsoLayoutEffect(() => {
1567
+ if (!hasRegistered) {
1568
+ return;
1569
+ }
1477
1570
  const selectedValue = store.state.value;
1478
1571
  let selectedCandidate = selectedValue;
1479
1572
  if (multiple && Array.isArray(selectedValue)) {
@@ -1489,7 +1582,8 @@ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namesp
1489
1582
  selectedItemTextRef.current = textRef.current;
1490
1583
  }
1491
1584
  }
1492
- }, [index, multiple, isItemEqualToValue, store, itemValue, selectedItemTextRef]);
1585
+ }, [hasRegistered, index, multiple, isItemEqualToValue, store, itemValue, selectedItemTextRef]);
1586
+ const lastKeyRef = React__namespace.useRef(null);
1493
1587
  const pointerTypeRef = React__namespace.useRef('mouse');
1494
1588
  const allowMouseSelectionRef = React__namespace.useRef(false);
1495
1589
  const {
@@ -1530,15 +1624,14 @@ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namesp
1530
1624
  'aria-selected': selected,
1531
1625
  tabIndex: open && highlighted ? 0 : -1,
1532
1626
  onKeyDown(event) {
1627
+ lastKeyRef.current = event.key;
1533
1628
  store.set('activeIndex', index);
1534
1629
  if (event.key === ' ' && typingRef.current) {
1535
- // `useButton` skips Space activation for `role="option"` items when the keydown
1536
- // is `defaultPrevented`, keeping typeahead spaces from committing a selection.
1537
1630
  event.preventDefault();
1538
1631
  }
1539
1632
  },
1540
1633
  onClick(event) {
1541
- const isMouseClick = pointerTypeRef.current !== 'touch';
1634
+ const isMouseClick = event.type === 'click' && pointerTypeRef.current !== 'touch';
1542
1635
  const clickPointerType = event.nativeEvent.pointerType;
1543
1636
  const isVirtualMouseClick = isMouseClick && useScrollLock.isVirtualClick(event.nativeEvent) && (
1544
1637
  // Generic no-pointer `detail === 0` clicks stay tied to highlight state. Virtual
@@ -1550,9 +1643,15 @@ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namesp
1550
1643
  // assistive technology activation.
1551
1644
  const isInvalidMouseClick = isMouseClick && !isVirtualMouseClick && !allowMouseSelectionRef.current;
1552
1645
  allowMouseSelectionRef.current = false;
1553
- if (disabled || isInvalidMouseClick) {
1646
+
1647
+ // Prevent double commit on {Enter}
1648
+ if (event.type === 'keydown' && lastKeyRef.current === null) {
1649
+ return;
1650
+ }
1651
+ if (disabled || event.type === 'keydown' && lastKeyRef.current === ' ' && typingRef.current || isInvalidMouseClick) {
1554
1652
  return;
1555
1653
  }
1654
+ lastKeyRef.current = null;
1556
1655
  commitSelection(event.nativeEvent);
1557
1656
  },
1558
1657
  onPointerEnter(event) {
@@ -1601,8 +1700,9 @@ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namesp
1601
1700
  selected,
1602
1701
  index,
1603
1702
  textRef,
1604
- selectedByFocus
1605
- }), [selected, index, textRef, selectedByFocus]);
1703
+ selectedByFocus,
1704
+ hasRegistered
1705
+ }), [selected, index, textRef, selectedByFocus, hasRegistered]);
1606
1706
  return /*#__PURE__*/jsxRuntime.jsx(SelectItemContext.Provider, {
1607
1707
  value: contextValue,
1608
1708
  children: element
@@ -1611,10 +1711,11 @@ const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namesp
1611
1711
  if (process.env.NODE_ENV !== "production") SelectItem.displayName = "SelectItem";
1612
1712
 
1613
1713
  const SelectItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function SelectItemIndicator(componentProps, forwardedRef) {
1714
+ const keepMounted = componentProps.keepMounted ?? false;
1614
1715
  const {
1615
1716
  selected
1616
1717
  } = useSelectItemContext();
1617
- const shouldRender = componentProps.keepMounted || selected;
1718
+ const shouldRender = keepMounted || selected;
1618
1719
  if (!shouldRender) {
1619
1720
  return null;
1620
1721
  }
@@ -1680,7 +1781,8 @@ const SelectItemText = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__na
1680
1781
  const {
1681
1782
  index,
1682
1783
  textRef,
1683
- selectedByFocus
1784
+ selectedByFocus,
1785
+ hasRegistered
1684
1786
  } = useSelectItemContext();
1685
1787
  const {
1686
1788
  firstItemTextRef,
@@ -1696,13 +1798,13 @@ const SelectItemText = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__na
1696
1798
  if (!node) {
1697
1799
  return;
1698
1800
  }
1699
- if (index === 0) {
1801
+ if (hasRegistered && index === 0) {
1700
1802
  firstItemTextRef.current = node;
1701
1803
  }
1702
- if (selectedByFocus) {
1804
+ if (hasRegistered && selectedByFocus) {
1703
1805
  selectedItemTextRef.current = node;
1704
1806
  }
1705
- }, [firstItemTextRef, selectedItemTextRef, index, selectedByFocus]);
1807
+ }, [firstItemTextRef, selectedItemTextRef, index, selectedByFocus, hasRegistered]);
1706
1808
  const element = useRenderElement.useRenderElement('div', componentProps, {
1707
1809
  ref: [localRef, forwardedRef, textRef],
1708
1810
  props: elementProps
@@ -1767,9 +1869,6 @@ const SelectGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function Selec
1767
1869
  const id = useScrollLock.useBaseUiId(idProp);
1768
1870
  useScrollLock.useIsoLayoutEffect(() => {
1769
1871
  setLabelId(id);
1770
- return () => {
1771
- setLabelId(currentId => currentId === id ? undefined : currentId);
1772
- };
1773
1872
  }, [id, setLabelId]);
1774
1873
  const element = useRenderElement.useRenderElement('div', componentProps, {
1775
1874
  ref: forwardedRef,