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