@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
@@ -1,23 +1,24 @@
1
1
  'use strict';
2
2
 
3
3
  var React = require('react');
4
- var ComboboxSeparator = require('../ComboboxSeparator-cjs.js');
4
+ var ComboboxChipRemove = require('../ComboboxChipRemove-cjs.js');
5
5
  var useCompositeListItem = require('../useCompositeListItem-cjs.js');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var useScrollLock = require('../useScrollLock-cjs.js');
8
+ var Separator = require('../Separator-cjs.js');
8
9
  var DrawerDescription = require('../DrawerDescription-cjs.js');
9
10
  var DrawerTitle = require('../DrawerTitle-cjs.js');
10
11
  var ScrollAreaViewport = require('../ScrollAreaViewport-cjs.js');
11
12
  var useRenderElement = require('../useRenderElement-cjs.js');
13
+ var floatingUi_utils = require('../floating-ui.utils-cjs.js');
12
14
  var scrollEdges = require('../scrollEdges-cjs.js');
13
- var scrollable = require('../scrollable-cjs.js');
14
15
  var MenuSubmenuTrigger = require('../MenuSubmenuTrigger-cjs.js');
15
16
  var ReactDOM = require('react-dom');
16
17
  var SelectGroupLabel = require('../SelectGroupLabel-cjs.js');
17
- var Separator = require('../Separator-cjs.js');
18
18
  var NumberFieldInput = require('../NumberFieldInput-cjs.js');
19
19
  var useLabel = require('../useLabel-cjs.js');
20
20
  require('../stringifyLocale-cjs.js');
21
+ require('../floating-ui.react-dom-cjs.js');
21
22
 
22
23
  function _interopNamespaceDefault(e) {
23
24
  var n = Object.create(null);
@@ -72,9 +73,7 @@ function AutocompleteRoot(props) {
72
73
  } else {
73
74
  resolvedInputValue = internalValue;
74
75
  }
75
- const collator = ComboboxSeparator.useCoreFilter({
76
- locale: other.locale
77
- });
76
+ const collator = ComboboxChipRemove.useCoreFilter();
78
77
  const baseFilter = React__namespace.useMemo(() => {
79
78
  if (other.filter !== undefined) {
80
79
  return other.filter;
@@ -107,9 +106,17 @@ function AutocompleteRoot(props) {
107
106
  if (eventDetails.reason === useScrollLock.pointer) {
108
107
  return;
109
108
  }
110
- setInlineInputValue(enableInline && highlightedValue != null ? useCompositeListItem.stringifyAsLabel(highlightedValue, itemToStringValue) : '');
109
+ if (enableInline) {
110
+ if (highlightedValue == null) {
111
+ setInlineInputValue('');
112
+ } else {
113
+ setInlineInputValue(useCompositeListItem.stringifyAsLabel(highlightedValue, itemToStringValue));
114
+ }
115
+ } else {
116
+ setInlineInputValue('');
117
+ }
111
118
  }
112
- return /*#__PURE__*/jsxRuntime.jsx(ComboboxSeparator.AriaCombobox, {
119
+ return /*#__PURE__*/jsxRuntime.jsx(ComboboxChipRemove.AriaCombobox, {
113
120
  ...other,
114
121
  itemToStringLabel: itemToStringValue,
115
122
  openOnInputClick: openOnInputClick,
@@ -128,7 +135,7 @@ function AutocompleteValue(props) {
128
135
  const {
129
136
  children
130
137
  } = props;
131
- const inputValue = ComboboxSeparator.useComboboxInputValueContext();
138
+ const inputValue = ComboboxChipRemove.useComboboxInputValueContext();
132
139
  let returnValue = null;
133
140
  if (typeof children === 'function') {
134
141
  returnValue = children(String(inputValue));
@@ -148,7 +155,7 @@ function AutocompleteValue(props) {
148
155
  *
149
156
  * Documentation: [Base UI Autocomplete](https://base-ui.com/react/components/autocomplete)
150
157
  */
151
- const AutocompleteTrigger = ComboboxSeparator.ComboboxTrigger;
158
+ const AutocompleteTrigger = ComboboxChipRemove.ComboboxTrigger;
152
159
 
153
160
  /**
154
161
  * A wrapper for the input and its associated controls.
@@ -156,7 +163,7 @@ const AutocompleteTrigger = ComboboxSeparator.ComboboxTrigger;
156
163
  *
157
164
  * Documentation: [Base UI Autocomplete](https://base-ui.com/react/components/autocomplete)
158
165
  */
159
- const AutocompleteInputGroup = ComboboxSeparator.ComboboxInputGroup;
166
+ const AutocompleteInputGroup = ComboboxChipRemove.ComboboxInputGroup;
160
167
 
161
168
  /**
162
169
  * An individual item in the list.
@@ -164,41 +171,33 @@ const AutocompleteInputGroup = ComboboxSeparator.ComboboxInputGroup;
164
171
  *
165
172
  * Documentation: [Base UI Autocomplete](https://base-ui.com/react/components/autocomplete)
166
173
  */
167
- const AutocompleteItem = ComboboxSeparator.ComboboxItem;
168
-
169
- /**
170
- * A visual separator between items or groups.
171
- * Renders a `<div>` element.
172
- *
173
- * Documentation: [Base UI Autocomplete](https://base-ui.com/react/components/autocomplete)
174
- */
175
- const AutocompleteSeparator = ComboboxSeparator.ListboxSeparator;
174
+ const AutocompleteItem = ComboboxChipRemove.ComboboxItem;
176
175
 
177
176
  var index_parts$7 = /*#__PURE__*/Object.freeze({
178
177
  __proto__: null,
179
- Arrow: ComboboxSeparator.ComboboxArrow,
180
- Backdrop: ComboboxSeparator.ComboboxBackdrop,
181
- Clear: ComboboxSeparator.ComboboxClear,
182
- Collection: ComboboxSeparator.ComboboxCollection,
183
- Empty: ComboboxSeparator.ComboboxEmpty,
184
- Group: ComboboxSeparator.ComboboxGroup,
185
- GroupLabel: ComboboxSeparator.ComboboxGroupLabel,
186
- Icon: ComboboxSeparator.ComboboxIcon,
187
- Input: ComboboxSeparator.ComboboxInput,
178
+ Arrow: ComboboxChipRemove.ComboboxArrow,
179
+ Backdrop: ComboboxChipRemove.ComboboxBackdrop,
180
+ Clear: ComboboxChipRemove.ComboboxClear,
181
+ Collection: ComboboxChipRemove.ComboboxCollection,
182
+ Empty: ComboboxChipRemove.ComboboxEmpty,
183
+ Group: ComboboxChipRemove.ComboboxGroup,
184
+ GroupLabel: ComboboxChipRemove.ComboboxGroupLabel,
185
+ Icon: ComboboxChipRemove.ComboboxIcon,
186
+ Input: ComboboxChipRemove.ComboboxInput,
188
187
  InputGroup: AutocompleteInputGroup,
189
188
  Item: AutocompleteItem,
190
- List: ComboboxSeparator.ComboboxList,
191
- Popup: ComboboxSeparator.ComboboxPopup,
192
- Portal: ComboboxSeparator.ComboboxPortal,
193
- Positioner: ComboboxSeparator.ComboboxPositioner,
189
+ List: ComboboxChipRemove.ComboboxList,
190
+ Popup: ComboboxChipRemove.ComboboxPopup,
191
+ Portal: ComboboxChipRemove.ComboboxPortal,
192
+ Positioner: ComboboxChipRemove.ComboboxPositioner,
194
193
  Root: AutocompleteRoot,
195
- Row: ComboboxSeparator.ComboboxRow,
196
- Separator: AutocompleteSeparator,
197
- Status: ComboboxSeparator.ComboboxStatus,
194
+ Row: ComboboxChipRemove.ComboboxRow,
195
+ Separator: Separator.Separator,
196
+ Status: ComboboxChipRemove.ComboboxStatus,
198
197
  Trigger: AutocompleteTrigger,
199
198
  Value: AutocompleteValue,
200
- useFilter: ComboboxSeparator.useCoreFilter,
201
- useFilteredItems: ComboboxSeparator.useFilteredItems
199
+ useFilter: ComboboxChipRemove.useCoreFilter,
200
+ useFilteredItems: ComboboxChipRemove.useFilteredItems
202
201
  });
203
202
 
204
203
  var index_parts$6 = /*#__PURE__*/Object.freeze({
@@ -209,14 +208,14 @@ var index_parts$6 = /*#__PURE__*/Object.freeze({
209
208
  Handle: DrawerDescription.DialogHandle,
210
209
  Popup: DrawerDescription.DialogPopup,
211
210
  Portal: ScrollAreaViewport.DialogPortal,
212
- Root: DrawerDescription.DialogRoot,
211
+ Root: ScrollAreaViewport.DialogRoot,
213
212
  Title: DrawerTitle.DialogTitle,
214
213
  Trigger: ScrollAreaViewport.DialogTrigger,
215
214
  Viewport: ScrollAreaViewport.DialogViewport,
216
215
  createHandle: DrawerDescription.createDialogHandle
217
216
  });
218
217
 
219
- const stateAttributesMapping$3 = {
218
+ const stateAttributesMapping$5 = {
220
219
  active(value) {
221
220
  if (value) {
222
221
  return {
@@ -287,12 +286,12 @@ const DrawerIndent = /*#__PURE__*/React__namespace.forwardRef(function DrawerInd
287
286
  [ScrollAreaViewport.DrawerBackdropCssVars.swipeProgress]: '0'
288
287
  }
289
288
  }, elementProps],
290
- stateAttributesMapping: stateAttributesMapping$3
289
+ stateAttributesMapping: stateAttributesMapping$5
291
290
  });
292
291
  });
293
292
  if (process.env.NODE_ENV !== "production") DrawerIndent.displayName = "DrawerIndent";
294
293
 
295
- const stateAttributesMapping$2 = {
294
+ const stateAttributesMapping$4 = {
296
295
  active(value) {
297
296
  if (value) {
298
297
  return {
@@ -327,7 +326,7 @@ const DrawerIndentBackground = /*#__PURE__*/React__namespace.forwardRef(function
327
326
  ref: forwardedRef,
328
327
  state,
329
328
  props: elementProps,
330
- stateAttributesMapping: stateAttributesMapping$2
329
+ stateAttributesMapping: stateAttributesMapping$4
331
330
  });
332
331
  });
333
332
  if (process.env.NODE_ENV !== "production") DrawerIndentBackground.displayName = "DrawerIndentBackground";
@@ -336,26 +335,37 @@ function DrawerProvider(props) {
336
335
  const {
337
336
  children
338
337
  } = props;
339
- const [openDrawers, setOpenDrawers] = React__namespace.useState(() => new Set());
338
+ const [openById, setOpenById] = React__namespace.useState(() => new Map());
340
339
  const [visualStateStore] = React__namespace.useState(createVisualStateStore);
341
- const setDrawerOpen = useScrollLock.useStableCallback((drawer, open) => {
342
- setOpenDrawers(prev => {
343
- if (prev.has(drawer) === open) {
340
+ const setDrawerOpen = useScrollLock.useStableCallback((drawerId, open) => {
341
+ setOpenById(prev => {
342
+ const prevOpen = prev.get(drawerId);
343
+ if (prevOpen === open) {
344
344
  return prev;
345
345
  }
346
- const next = new Set(prev);
347
- if (open) {
348
- next.add(drawer);
349
- } else {
350
- next.delete(drawer);
351
- }
346
+ const next = new Map(prev);
347
+ next.set(drawerId, open);
352
348
  return next;
353
349
  });
354
350
  });
355
- const removeDrawer = useScrollLock.useStableCallback(drawer => {
356
- setDrawerOpen(drawer, false);
351
+ const removeDrawer = useScrollLock.useStableCallback(drawerId => {
352
+ setOpenById(prev => {
353
+ if (!prev.has(drawerId)) {
354
+ return prev;
355
+ }
356
+ const next = new Map(prev);
357
+ next.delete(drawerId);
358
+ return next;
359
+ });
357
360
  });
358
- const active = openDrawers.size > 0;
361
+ const active = React__namespace.useMemo(() => {
362
+ for (const open of openById.values()) {
363
+ if (open) {
364
+ return true;
365
+ }
366
+ }
367
+ return false;
368
+ }, [openById]);
359
369
  const contextValue = React__namespace.useMemo(() => ({
360
370
  setDrawerOpen,
361
371
  removeDrawer,
@@ -445,7 +455,7 @@ const SWIPE_AREA_SWIPING_HOOK = {
445
455
  const SWIPE_AREA_DISABLED_HOOK = {
446
456
  [DrawerSwipeAreaDataAttributes.disabled]: ''
447
457
  };
448
- const stateAttributesMapping$1 = {
458
+ const stateAttributesMapping$3 = {
449
459
  open(value) {
450
460
  return value ? SWIPE_AREA_OPEN_HOOK : SWIPE_AREA_CLOSED_HOOK;
451
461
  },
@@ -453,9 +463,9 @@ const stateAttributesMapping$1 = {
453
463
  return value ? SWIPE_AREA_SWIPING_HOOK : null;
454
464
  },
455
465
  swipeDirection(value) {
456
- return {
466
+ return value ? {
457
467
  [DrawerSwipeAreaDataAttributes.swipeDirection]: value
458
- };
468
+ } : null;
459
469
  },
460
470
  disabled(value) {
461
471
  return value ? SWIPE_AREA_DISABLED_HOOK : null;
@@ -486,14 +496,16 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
486
496
  swipeDirection: swipeDirectionProp,
487
497
  ...elementProps
488
498
  } = componentProps;
489
- const store = ScrollAreaViewport.useDialogRootContext();
499
+ const {
500
+ store
501
+ } = ScrollAreaViewport.useDialogRootContext();
490
502
  const {
491
503
  swipeDirection,
492
- frontmostHeight,
493
- swipeAreaActiveRef
504
+ frontmostHeight
494
505
  } = ScrollAreaViewport.useDrawerRootContext();
495
506
  const providerContext = ScrollAreaViewport.useDrawerProviderContext();
496
507
  const [swipeActive, setSwipeActive] = React__namespace.useState(false);
508
+ const releaseDismissTimeout = useScrollLock.useTimeout();
497
509
  const swipeAreaRef = React__namespace.useRef(null);
498
510
  const swipeStartEventRef = React__namespace.useRef(null);
499
511
  const openedBySwipeRef = React__namespace.useRef(false);
@@ -503,10 +515,7 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
503
515
  });
504
516
  const closedOffsetRef = React__namespace.useRef(null);
505
517
  const appliedSwipeStylesRef = React__namespace.useRef(false);
506
- const swipePopupElementRef = React__namespace.useRef(null);
507
- const swipeBackdropElementRef = React__namespace.useRef(null);
508
518
  const popupTransitionRef = React__namespace.useRef(null);
509
- const releaseGuardCleanupRef = React__namespace.useRef(useRenderElement.NOOP);
510
519
  const swipeAreaId = useScrollLock.useBaseUiId(componentProps.id);
511
520
  const registerTrigger = useScrollLock.useTriggerRegistration(swipeAreaId, store);
512
521
  const open = store.useState('open');
@@ -518,40 +527,21 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
518
527
  const dismissDirection = oppositeSwipeDirection[resolvedSwipeDirection];
519
528
  const enabled = !disabled && (!open || swipeActive);
520
529
  function disableDismissForSwipe() {
521
- releaseGuardCleanupRef.current();
530
+ releaseDismissTimeout.clear();
522
531
  store.context.outsidePressEnabledRef.current = false;
523
532
  }
524
- const enableDismissAfterRelease = useScrollLock.useStableCallback(() => {
525
- releaseGuardCleanupRef.current();
526
- const doc = useScrollLock.ownerDocument(swipeAreaRef.current);
527
- function restore(event) {
528
- // The gesture's trailing release click is the one physical click with no `pointerdown` of
529
- // its own. Ignore it and keep waiting, so it cannot dismiss the drawer it just opened,
530
- // while a click-only activation (keyboard or assistive tech) still re-enables in time.
531
- if (event?.type === 'click' && event.detail !== 0 && !useScrollLock.isVirtualClick(event)) {
532
- return;
533
- }
534
- releaseGuardCleanupRef.current = useRenderElement.NOOP;
535
- doc.removeEventListener('pointerdown', restore, true);
536
- doc.removeEventListener('click', restore, true);
533
+ function enableDismissAfterRelease() {
534
+ // Safari can dispatch outside-press for the same swipe-open gesture
535
+ // after release, so defer re-enabling dismissal to the next macrotask.
536
+ releaseDismissTimeout.start(0, () => {
537
537
  store.context.outsidePressEnabledRef.current = true;
538
+ });
539
+ }
540
+ function resolvePopupSize() {
541
+ const popupElement = store.context.popupRef.current;
542
+ if (!popupElement) {
543
+ return null;
538
544
  }
539
-
540
- // The pointerup that ends a swipe-open gesture synthesizes a `click`. When the drag released
541
- // outside the popup (e.g. it was dragged past the popup's size), that click would be treated as
542
- // an outside press and immediately dismiss the drawer that was just opened. Keep outside-press
543
- // dismissal disabled until the next interaction that isn't that release click: a deliberate
544
- // outside press starts with a `pointerdown`, and a click-only activation (keyboard or
545
- // assistive tech) is distinguishable from a physical release. This is deterministic, unlike
546
- // re-enabling on a timer that can race the synthesized click and dismiss at random.
547
- //
548
- // `restore` runs in document capture, ahead of floating-ui's own outside-press check (which
549
- // happens on the event target, after capture), so the triggering press still dismisses.
550
- releaseGuardCleanupRef.current = restore;
551
- doc.addEventListener('pointerdown', restore, true);
552
- doc.addEventListener('click', restore, true);
553
- });
554
- function getPopupSize(popupElement) {
555
545
  const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
556
546
  const size = isHorizontal ? popupElement.offsetWidth : popupElement.offsetHeight;
557
547
  if (size <= 0) {
@@ -559,15 +549,15 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
559
549
  }
560
550
  return size;
561
551
  }
562
- function resolvePopupSize() {
563
- const popupElement = store.context.popupRef.current;
564
- return popupElement ? getPopupSize(popupElement) : null;
565
- }
566
- function resolveClosedOffset(popupElement) {
567
- const offset = getPopupSize(popupElement);
552
+ function resolveClosedOffset() {
553
+ const offset = resolvePopupSize();
568
554
  if (offset == null) {
569
555
  return null;
570
556
  }
557
+ const popupElement = store.context.popupRef.current;
558
+ if (!popupElement) {
559
+ return offset;
560
+ }
571
561
  const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
572
562
  const transform = ScrollAreaViewport.getElementTransform(popupElement);
573
563
  const transformOffset = isHorizontal ? transform.x : transform.y;
@@ -584,6 +574,9 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
584
574
  return popupSize * DEFAULT_SWIPE_OPEN_RATIO;
585
575
  }
586
576
  function applySwipeMovement() {
577
+ if (!swipeActive) {
578
+ return;
579
+ }
587
580
  const popupElement = store.context.popupRef.current;
588
581
  if (!popupElement) {
589
582
  return;
@@ -592,10 +585,10 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
592
585
  return;
593
586
  }
594
587
  if (closedOffsetRef.current == null) {
595
- closedOffsetRef.current = resolveClosedOffset(popupElement);
588
+ closedOffsetRef.current = resolveClosedOffset();
596
589
  }
597
590
  const closedOffset = closedOffsetRef.current;
598
- if (closedOffset === null) {
591
+ if (!closedOffset || !Number.isFinite(closedOffset) || closedOffset <= 0) {
599
592
  return;
600
593
  }
601
594
  const {
@@ -616,7 +609,6 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
616
609
  popupElement.style.setProperty(ScrollAreaViewport.DrawerPopupCssVars.swipeMovementX, `${movementX}px`);
617
610
  popupElement.style.setProperty(ScrollAreaViewport.DrawerPopupCssVars.swipeMovementY, `${movementY}px`);
618
611
  popupElement.setAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swiping, '');
619
- swipePopupElementRef.current = popupElement;
620
612
  if (popupTransitionRef.current === null) {
621
613
  popupTransitionRef.current = popupElement.style.transition;
622
614
  }
@@ -624,7 +616,6 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
624
616
  const backdropElement = store.context.backdropRef.current;
625
617
  if (backdropElement) {
626
618
  backdropElement.setAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swiping, '');
627
- swipeBackdropElementRef.current = backdropElement;
628
619
  backdropElement.style.setProperty(ScrollAreaViewport.DrawerBackdropCssVars.swipeProgress, `${backdropProgress}`);
629
620
  if (openProgress > 0 && frontmostHeight > 0) {
630
621
  backdropElement.style.setProperty(ScrollAreaViewport.DrawerPopupCssVars.height, `${frontmostHeight}px`);
@@ -637,11 +628,10 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
637
628
  frontmostHeight: openProgress > 0 ? frontmostHeight : 0
638
629
  });
639
630
  appliedSwipeStylesRef.current = true;
640
- swipeAreaActiveRef.current = true;
641
631
  }
642
632
  const clearSwipeStyles = useScrollLock.useStableCallback(() => {
643
- const popupElement = swipePopupElementRef.current;
644
- if (popupElement) {
633
+ const popupElement = store.context.popupRef.current;
634
+ if (popupElement && appliedSwipeStylesRef.current) {
645
635
  popupElement.style.removeProperty(ScrollAreaViewport.DrawerPopupCssVars.swipeMovementX);
646
636
  popupElement.style.removeProperty(ScrollAreaViewport.DrawerPopupCssVars.swipeMovementY);
647
637
  popupElement.removeAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swiping);
@@ -650,7 +640,7 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
650
640
  popupElement.style.transition = popupTransitionRef.current;
651
641
  popupTransitionRef.current = null;
652
642
  }
653
- const backdropElement = swipeBackdropElementRef.current;
643
+ const backdropElement = store.context.backdropRef.current;
654
644
  if (backdropElement) {
655
645
  backdropElement.removeAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swiping);
656
646
  backdropElement.style.setProperty(ScrollAreaViewport.DrawerBackdropCssVars.swipeProgress, '0');
@@ -661,16 +651,19 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
661
651
  frontmostHeight: 0
662
652
  });
663
653
  appliedSwipeStylesRef.current = false;
664
- swipePopupElementRef.current = null;
665
- swipeBackdropElementRef.current = null;
666
- swipeAreaActiveRef.current = false;
667
654
  });
668
655
  function openDrawer(event) {
656
+ if (store.select('open')) {
657
+ return;
658
+ }
669
659
  openedBySwipeRef.current = true;
670
- store.setOpen(true, useScrollLock.createChangeEventDetails(useScrollLock.swipe, event, swipeAreaRef.current));
660
+ store.setOpen(true, useScrollLock.createChangeEventDetails(useScrollLock.swipe, event, swipeAreaRef.current ?? undefined));
671
661
  }
672
662
  function closeDrawer(event) {
673
- store.setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.swipe, event, swipeAreaRef.current));
663
+ if (!store.select('open')) {
664
+ return;
665
+ }
666
+ store.setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.swipe, event, swipeAreaRef.current ?? undefined));
674
667
  }
675
668
  function resetSwipeInteractionState() {
676
669
  swipeStartEventRef.current = null;
@@ -713,10 +706,10 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
713
706
  return;
714
707
  }
715
708
  const displacement = ScrollAreaViewport.getDisplacement(resolvedSwipeDirection, details.deltaX, details.deltaY);
716
- if (!openedBySwipeRef.current && displacement < MIN_SWIPE_START_DISTANCE) {
709
+ if (displacement < MIN_SWIPE_START_DISTANCE && !openedBySwipeRef.current) {
717
710
  return;
718
711
  }
719
- if (!openedBySwipeRef.current && !store.select('open')) {
712
+ if (!openedBySwipeRef.current) {
720
713
  openDrawer(swipeStartEventRef.current);
721
714
  }
722
715
  applySwipeMovement();
@@ -732,14 +725,14 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
732
725
  const displacement = ScrollAreaViewport.getDisplacement(resolvedSwipeDirection, deltaX, deltaY);
733
726
  const releaseVelocity = ScrollAreaViewport.getDisplacement(resolvedSwipeDirection, releaseVelocityX, releaseVelocityY);
734
727
  const threshold = resolveSwipeOpenThreshold();
735
- const hasEnoughDistance = displacement >= threshold;
728
+ const hasEnoughDistance = threshold != null && displacement >= threshold;
736
729
  const hasEnoughVelocity = releaseVelocity >= VELOCITY_THRESHOLD;
737
- const shouldOpen = direction === resolvedSwipeDirection && (hasEnoughDistance || hasEnoughVelocity) && !disabled;
730
+ const shouldOpen = threshold != null && direction === resolvedSwipeDirection && (hasEnoughDistance || hasEnoughVelocity) && !disabled;
738
731
  if (shouldOpen) {
739
732
  if (!store.select('open')) {
740
733
  openDrawer(event);
741
734
  }
742
- } else if (openedBySwipeRef.current && store.select('open')) {
735
+ } else if (openedBySwipeRef.current) {
743
736
  closeDrawer(event);
744
737
  }
745
738
  finishSwipeInteraction();
@@ -750,29 +743,16 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
750
743
  const swipePointerProps = swipe.getPointerProps();
751
744
  const swipeTouchProps = swipe.getTouchProps();
752
745
  const resetSwipe = swipe.reset;
753
-
754
- // The commit that opens the drawer re-renders the popup, resetting `--swipe-movement-*` to `0px`
755
- // (the viewport isn't swiping). Re-assert after the DOM mutation but before paint. No deps: must
756
- // run on every commit the swipe area participates in.
757
- useScrollLock.useIsoLayoutEffect(() => {
758
- if (swipeActive && appliedSwipeStylesRef.current) {
759
- applySwipeMovement();
760
- }
761
- });
762
- useScrollLock.useIsoLayoutEffect(() => {
746
+ React__namespace.useEffect(() => {
763
747
  if (!enabled) {
764
- if (swipeActive) {
765
- enableDismissAfterRelease();
766
- }
767
748
  resetSwipe();
768
749
  resetDragDelta();
769
750
  clearSwipeStyles();
770
751
  resetSwipeInteractionState();
771
752
  }
772
- }, [clearSwipeStyles, enableDismissAfterRelease, enabled, resetDragDelta, resetSwipe, swipeActive]);
753
+ }, [clearSwipeStyles, enabled, resetDragDelta, resetSwipe]);
773
754
  React__namespace.useEffect(() => {
774
755
  return () => {
775
- releaseGuardCleanupRef.current();
776
756
  store.context.outsidePressEnabledRef.current = true;
777
757
  };
778
758
  }, [store]);
@@ -785,7 +765,7 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
785
765
  return useRenderElement.useRenderElement('div', componentProps, {
786
766
  state,
787
767
  ref: [forwardedRef, swipeAreaRef, registerTrigger],
788
- stateAttributesMapping: stateAttributesMapping$1,
768
+ stateAttributesMapping: stateAttributesMapping$3,
789
769
  props: [{
790
770
  role: 'presentation',
791
771
  'aria-hidden': true,
@@ -845,15 +825,6 @@ const KEYBOARD_VISIBILITY_MARGIN = 16;
845
825
  // keyboard overlap, so the field can be scrolled clear of the keyboard instead of
846
826
  // ending up flush against it. Only applied when there is actual overlap.
847
827
  const KEYBOARD_SCROLL_SLACK = 48;
848
- // Cadence of the settle-watching realign passes after focus moves with the keyboard open:
849
- // long enough for a smooth scroll to show progress between passes, short enough to recover
850
- // quickly from a scroll canceled by WebKit's reveal; the pass count covers CSS transitions
851
- // reacting to the focus change (e.g. a 260ms footer resize) with room to spare.
852
- const KEYBOARD_REALIGN_INTERVAL = 150;
853
- const KEYBOARD_REALIGN_MAX_PASSES = 4;
854
- // Frames the alignment waits for the scroll destination to stop moving (layout reacting to
855
- // the focus change, e.g. a footer resizing over a CSS transition) before scrolling anyway.
856
- const KEYBOARD_SETTLE_FRAME_LIMIT = 60;
857
828
  const INPUT_TAP_MOVE_THRESHOLD = 10;
858
829
  const INPUT_TAP_HIT_SLOP = 16;
859
830
  const KEYBOARD_INPUT_TYPES = new Set(['email', 'number', 'password', 'search', 'tel', 'text', 'url']);
@@ -877,10 +848,11 @@ function DrawerVirtualKeyboardProvider(props) {
877
848
  const {
878
849
  children
879
850
  } = props;
880
- const store = ScrollAreaViewport.useDialogRootContext();
851
+ const {
852
+ store
853
+ } = ScrollAreaViewport.useDialogRootContext();
881
854
  const open = store.useState('open');
882
855
  const mounted = store.useState('mounted');
883
- const modal = store.useState('modal');
884
856
  const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');
885
857
  const viewportElement = store.useState('viewportElement');
886
858
 
@@ -893,9 +865,7 @@ function DrawerVirtualKeyboardProvider(props) {
893
865
  const keyboardTouchStartRef = React__namespace.useRef(null);
894
866
  const focusedKeyboardTargetRef = React__namespace.useRef(null);
895
867
  const keyboardScrollAdjustmentRef = React__namespace.useRef(null);
896
- const programmaticKeyboardFocusRef = React__namespace.useRef(false);
897
868
  const keyboardFocusFrame = useScrollLock.useAnimationFrame();
898
- const keyboardRealignTimeout = useScrollLock.useTimeout();
899
869
  const restoreKeyboardScrollAdjustment = useScrollLock.useStableCallback(() => {
900
870
  const adjustment = keyboardScrollAdjustmentRef.current;
901
871
  if (!adjustment) {
@@ -922,7 +892,7 @@ function DrawerVirtualKeyboardProvider(props) {
922
892
  adjustment = null;
923
893
  }
924
894
  if (!adjustment) {
925
- const styles = useScrollLock.getComputedStyle(element);
895
+ const styles = floatingUi_utils.getComputedStyle(element);
926
896
  adjustment = {
927
897
  element,
928
898
  overflowAnchor: element.style.overflowAnchor,
@@ -938,7 +908,7 @@ function DrawerVirtualKeyboardProvider(props) {
938
908
  element.style.scrollPaddingBottom = `${adjustment.computedScrollPaddingBottom + KEYBOARD_VISIBILITY_MARGIN}px`;
939
909
  });
940
910
  const animateKeyboardScroll = useScrollLock.useStableCallback((element, scrollTop) => {
941
- const win = useScrollLock.getWindow(element);
911
+ const win = floatingUi_utils.getWindow(element);
942
912
  const behavior = win.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ? 'auto' : 'smooth';
943
913
  element.scrollTo({
944
914
  top: scrollTop,
@@ -961,102 +931,48 @@ function DrawerVirtualKeyboardProvider(props) {
961
931
  return undefined;
962
932
  }
963
933
  const doc = useScrollLock.ownerDocument(rootElement);
964
- const win = useScrollLock.getWindow(rootElement);
934
+ const win = floatingUi_utils.getWindow(rootElement);
965
935
  const visualViewport = win.visualViewport;
966
-
967
- // Alignment scroll bookkeeping: destination stability, whether a scroll was issued,
968
- // and the last observed progress so delayed passes can distinguish moving from stalled.
969
- let keyboardScrollElement = null;
970
- let keyboardScrollDestination = 0;
971
- let keyboardScrollChecks = 0;
972
- let keyboardScrollObserved = -1;
973
936
  const setDrawerKeyboardInset = inset => {
974
937
  rootElement.style.setProperty(DrawerViewportCssVars.keyboardInset, `${Math.max(0, Math.ceil(inset))}px`);
975
938
  };
939
+ const resetDrawerKeyboardInset = () => {
940
+ setDrawerKeyboardInset(0);
941
+ };
976
942
  const clearFocusedKeyboardTarget = () => {
977
943
  focusedKeyboardTargetRef.current = null;
978
- keyboardScrollElement = null;
979
- setDrawerKeyboardInset(0);
944
+ resetDrawerKeyboardInset();
980
945
  restoreKeyboardScrollAdjustment();
981
946
  keyboardFocusFrame.cancel();
982
- keyboardRealignTimeout.clear();
983
- };
984
-
985
- // WebKit's native reveal scroll can move the page even while the scroll lock hides
986
- // overflow (e.g. when the software keyboard's previous/next field arrows move focus).
987
- // While the drawer is modal, any window scroll during keyboard interaction is spurious,
988
- // so pin the page to the position it had when the drawer opened.
989
- const baseScrollX = win.scrollX;
990
- const baseScrollY = win.scrollY;
991
- const restoreWindowScroll = () => {
992
- if (modal !== true || nestedDrawerOpen || !focusedKeyboardTargetRef.current || getKeyboardVisualViewport(win) == null) {
993
- return false;
994
- }
995
- if (win.scrollX !== baseScrollX || win.scrollY !== baseScrollY) {
996
- // Force an instant jump: the two-argument form defaults `behavior` to `auto`, which
997
- // obeys the page's `scroll-behavior`, so a global `scroll-behavior: smooth` would
998
- // animate the restore. The measurements that follow assume the page is already back
999
- // at rest, and a smooth restore also re-emits `scroll`, re-entering this handler.
1000
- win.scrollTo({
1001
- left: baseScrollX,
1002
- top: baseScrollY,
1003
- behavior: 'instant'
1004
- });
1005
- return true;
1006
- }
1007
- return false;
1008
- };
1009
-
1010
- // Focus moved by the drawer itself goes through `focusKeyboardInputWithoutPageScroll`,
1011
- // but native focus changes (the iOS keyboard's previous/next field arrows) commit
1012
- // WebKit's reveal scroll before `focusin` reaches us — cancelling it afterwards
1013
- // visibly jitters the sheet. `focusout` on the outgoing field fires before focus
1014
- // lands, so override the incoming field's geometry there and restore it in `focusin`.
1015
- let restorePreemptedFocus = null;
1016
- const consumePreemptedFocus = () => {
1017
- restorePreemptedFocus?.();
1018
- restorePreemptedFocus = null;
1019
- };
1020
- const preemptFocusReveal = (target, keyboardViewport) => {
1021
- consumePreemptedFocus();
1022
-
1023
- // Native focus carries no preventScroll, so a fake off-screen rect would make WebKit
1024
- // scroll the drawer's own scroll containers thousands of pixels chasing it. Presenting
1025
- // the field as already centered in the visible band above the keyboard instead makes
1026
- // both the in-page reveal and the viewport pan no-ops.
1027
- const rect = target.getBoundingClientRect();
1028
- restorePreemptedFocus = overrideGeometryDuringFocus(target, (keyboardViewport.top + keyboardViewport.bottom - rect.top - rect.bottom) / 2);
1029
947
  };
1030
948
  const alignFocusedKeyboardTarget = () => {
1031
- // If focus never lands on a preempted target, the focusout-scheduled alignment still
1032
- // restores it on the next frame before paint.
1033
- consumePreemptedFocus();
1034
949
  const target = focusedKeyboardTargetRef.current;
1035
950
  // If the focused field is removed from the DOM without firing `focusout` (e.g. it is
1036
951
  // conditionally rendered away), any applied scroll slack is restored here on the next
1037
952
  // focus/viewport event or when the drawer closes. This self-corrects rather than
1038
953
  // tracking each field's lifecycle.
1039
954
  if (nestedDrawerOpen || !target || !useScrollLock.contains(rootElement, target)) {
1040
- setDrawerKeyboardInset(0);
955
+ resetDrawerKeyboardInset();
1041
956
  restoreKeyboardScrollAdjustment();
1042
957
  return;
1043
958
  }
1044
-
1045
- // Undo any reveal scroll WebKit applied to the locked page before measuring, so the
1046
- // keyboard inset and alignment are computed against the resting viewport.
1047
- restoreWindowScroll();
1048
959
  const keyboardViewport = getKeyboardVisualViewport(win);
1049
960
  if (!keyboardViewport) {
1050
- setDrawerKeyboardInset(0);
961
+ resetDrawerKeyboardInset();
1051
962
  restoreKeyboardScrollAdjustment();
1052
963
  return;
1053
964
  }
1054
- setDrawerKeyboardInset(Math.max(0, win.innerHeight - keyboardViewport.bottom));
965
+ setDrawerKeyboardInset(getDrawerKeyboardInset(win, keyboardViewport));
1055
966
  const scrollTarget = findKeyboardScrollTarget(target, rootElement);
1056
967
  if (!scrollTarget) {
1057
968
  restoreKeyboardScrollAdjustment();
1058
969
  return;
1059
970
  }
971
+ if (!scrollTarget.isConnected || !useScrollLock.contains(rootElement, scrollTarget)) {
972
+ resetDrawerKeyboardInset();
973
+ restoreKeyboardScrollAdjustment();
974
+ return;
975
+ }
1060
976
  const scrollTargetRect = scrollTarget.getBoundingClientRect();
1061
977
  const clippedBottom = Math.min(scrollTargetRect.bottom, keyboardViewport.bottom);
1062
978
  const overlap = Math.max(0, scrollTargetRect.bottom - keyboardViewport.bottom);
@@ -1072,70 +988,14 @@ function DrawerVirtualKeyboardProvider(props) {
1072
988
  return;
1073
989
  }
1074
990
  const targetRect = target.getBoundingClientRect();
1075
- const nextScrollTop = scrollTarget.scrollTop + (targetRect.top + targetRect.bottom - visibleTop - visibleBottom) / 2;
1076
- const destination = Math.round(scrollEdges.clamp(nextScrollTop, 0, maxScrollTop));
1077
- const settled = keyboardScrollElement === scrollTarget && Math.abs(keyboardScrollDestination - destination) <= 1;
1078
- if (!settled) {
1079
- // Commit the scroll only once the destination holds across two consecutive checks.
1080
- // Layout may still be reacting to the focus change (e.g. a footer sized by
1081
- // `:focus-within` resizing over a transition), and scrolling toward a
1082
- // mid-transition destination overshoots, then visibly pulls back once corrected.
1083
- // The destination is scroll-position-invariant, so an in-flight scroll of the
1084
- // container itself never defers the commit.
1085
- const checks = keyboardScrollElement === scrollTarget ? keyboardScrollChecks + 1 : 1;
1086
- keyboardScrollElement = scrollTarget;
1087
- keyboardScrollDestination = destination;
1088
- keyboardScrollChecks = checks;
1089
- keyboardScrollObserved = -1;
1090
- // Re-check next frame; give up and scroll anyway if layout never settles.
1091
- if (checks <= KEYBOARD_SETTLE_FRAME_LIMIT) {
1092
- keyboardFocusFrame.request(alignFocusedKeyboardTarget);
1093
- return;
1094
- }
1095
- } else if (keyboardScrollObserved >= 0) {
1096
- // A scroll toward this destination is already out. Leave it alone while it has
1097
- // arrived or is still progressing — re-issuing restarts the smooth-scroll easing
1098
- // curve, a visible stutter on every realign pass — and re-issue only when it
1099
- // stalled short (WebKit cancels in-flight smooth scrolls when its native reveal
1100
- // for the focus resolves).
1101
- const current = scrollTarget.scrollTop;
1102
- if (Math.abs(current - destination) <= 1) {
1103
- return;
1104
- }
1105
- if (current !== keyboardScrollObserved) {
1106
- keyboardScrollObserved = current;
1107
- return;
1108
- }
1109
- }
1110
- keyboardScrollElement = scrollTarget;
1111
- keyboardScrollDestination = destination;
1112
- keyboardScrollChecks = 0;
1113
- keyboardScrollObserved = scrollTarget.scrollTop;
1114
- animateKeyboardScroll(scrollTarget, destination);
991
+ const targetCenter = (targetRect.top + targetRect.bottom) / 2;
992
+ const visibleCenter = (visibleTop + visibleBottom) / 2;
993
+ const nextScrollTop = scrollTarget.scrollTop + targetCenter - visibleCenter;
994
+ animateKeyboardScroll(scrollTarget, scrollEdges.clamp(nextScrollTop, 0, maxScrollTop));
1115
995
  };
1116
996
  const scheduleKeyboardFocusAlignment = () => {
1117
997
  keyboardFocusFrame.request(alignFocusedKeyboardTarget);
1118
998
  };
1119
-
1120
- // When focus moves with the keyboard already up, no viewport resize events follow to
1121
- // re-run alignment, and the single frame-scheduled pass is unreliable: WebKit resolves
1122
- // the reveal for the focus asynchronously (a UI-process round trip) and cancels any
1123
- // in-flight scroll the pass started when it lands, and layout reacting to the focus
1124
- // change (e.g. a footer sized by `:focus-within` and the keyboard inset) settles only
1125
- // after its transition, so the pass measures stale geometry. Re-align on an interval
1126
- // until both settle; the alignment's scroll bookkeeping observes before re-issuing,
1127
- // so passes that find the scroll on course are inert.
1128
- const scheduleDelayedKeyboardRealign = () => {
1129
- let remainingPasses = KEYBOARD_REALIGN_MAX_PASSES;
1130
- const realign = () => {
1131
- alignFocusedKeyboardTarget();
1132
- remainingPasses -= 1;
1133
- if (remainingPasses > 0) {
1134
- keyboardRealignTimeout.start(KEYBOARD_REALIGN_INTERVAL, realign);
1135
- }
1136
- };
1137
- keyboardRealignTimeout.start(KEYBOARD_REALIGN_INTERVAL, realign);
1138
- };
1139
999
  const captureFocusedKeyboardTarget = eventTarget => {
1140
1000
  if (nestedDrawerOpen) {
1141
1001
  return false;
@@ -1147,56 +1007,16 @@ function DrawerVirtualKeyboardProvider(props) {
1147
1007
  if (!target || !useScrollLock.contains(rootElement, target)) {
1148
1008
  return false;
1149
1009
  }
1150
-
1151
- // A new field starts a fresh alignment; the scroll bookkeeping must not carry
1152
- // over from the previous field's scroll.
1153
- if (focusedKeyboardTargetRef.current !== target) {
1154
- keyboardScrollElement = null;
1155
- }
1156
1010
  focusedKeyboardTargetRef.current = target;
1157
1011
  return true;
1158
1012
  };
1159
1013
  const handleFocusIn = event => {
1160
- // The programmatic transition is over once focus lands, which happens before
1161
- // `.focus()` returns. Any later `focusout` is the consumer's own — an `onFocus`
1162
- // handler that blurs or moves focus — and must be reconciled rather than
1163
- // suppressed, so stop suppressing here instead of waiting for `.focus()` to return.
1164
- programmaticKeyboardFocusRef.current = false;
1165
- consumePreemptedFocus();
1166
- if (!captureFocusedKeyboardTarget(useScrollLock.getTarget(event))) {
1167
- // Focus landed outside any drawer keyboard input. The `focusout` on the previous
1168
- // field normally clears the tracked target, but the tap path suppresses that
1169
- // `focusout` via `programmaticKeyboardFocusRef`, so a consumer `onFocus` handler that
1170
- // redirects focus out of the drawer would otherwise leave a stale target holding its
1171
- // inset, slack, and pending realign. Reconcile against the real focus here.
1172
- // A handler that ends focus entirely emits no `focusin` to reconcile from; that case
1173
- // is covered by clearing the suppression flag above so its `focusout` is handled.
1174
- clearFocusedKeyboardTarget();
1175
- return;
1176
- }
1177
-
1178
- // Covers every way focus can land with the keyboard already up: native moves (the
1179
- // keyboard's previous/next arrows) and the tap path (which suppresses `focusout`
1180
- // handling via the programmatic flag).
1181
- if (getKeyboardVisualViewport(win) != null) {
1182
- scheduleDelayedKeyboardRealign();
1014
+ if (captureFocusedKeyboardTarget(useScrollLock.getTarget(event))) {
1015
+ scheduleKeyboardFocusAlignment();
1183
1016
  }
1184
- scheduleKeyboardFocusAlignment();
1185
1017
  };
1186
1018
  const handleFocusOut = event => {
1187
- // The blur inside `focusKeyboardInputWithoutPageScroll` is followed synchronously by
1188
- // a re-focus; clearing state here would drop the keyboard inset for a frame.
1189
- if (programmaticKeyboardFocusRef.current) {
1190
- return;
1191
- }
1192
1019
  if (captureFocusedKeyboardTarget(event.relatedTarget)) {
1193
- const target = focusedKeyboardTargetRef.current;
1194
- const keyboardViewport = getKeyboardVisualViewport(win);
1195
- // The delayed realign passes are scheduled by the `focusin` that follows once
1196
- // focus lands on the captured target.
1197
- if (target && keyboardViewport) {
1198
- preemptFocusReveal(target, keyboardViewport);
1199
- }
1200
1020
  scheduleKeyboardFocusAlignment();
1201
1021
  return;
1202
1022
  }
@@ -1211,36 +1031,25 @@ function DrawerVirtualKeyboardProvider(props) {
1211
1031
  if (visualViewport) {
1212
1032
  cleanupListeners.push(useScrollLock.addEventListener(visualViewport, 'resize', handleViewportUpdate), useScrollLock.addEventListener(visualViewport, 'scroll', handleViewportUpdate));
1213
1033
  }
1214
- const handleWindowScroll = () => {
1215
- if (restoreWindowScroll()) {
1216
- // Recompute the keyboard inset once the page is back at rest; measurements taken
1217
- // mid-reveal (a non-zero visual viewport offset) inflate it and push the popup up.
1218
- scheduleKeyboardFocusAlignment();
1219
- }
1220
- };
1221
-
1222
- // Once the user puts a finger down they own the scroll position. Stop both the frame settle
1223
- // loop, which can center after changing layout settles, and the delayed passes, which can't
1224
- // distinguish a user scroll from a reveal scroll WebKit canceled. A later focus or viewport
1225
- // change reschedules alignment if it's still needed.
1226
- const cancelKeyboardRealignOnPointerDown = () => {
1227
- keyboardFocusFrame.cancel();
1228
- keyboardRealignTimeout.clear();
1229
- keyboardScrollElement = null;
1230
- };
1231
- cleanupListeners.push(useScrollLock.addEventListener(doc, 'focusin', handleFocusIn, true), useScrollLock.addEventListener(doc, 'focusout', handleFocusOut, true), useScrollLock.addEventListener(win, 'scroll', handleWindowScroll), useScrollLock.addEventListener(doc, 'pointerdown', cancelKeyboardRealignOnPointerDown, true));
1034
+ cleanupListeners.push(useScrollLock.addEventListener(doc, 'focusin', handleFocusIn, true), useScrollLock.addEventListener(doc, 'focusout', handleFocusOut, true));
1232
1035
  if (captureFocusedKeyboardTarget(useScrollLock.activeElement(doc))) {
1233
1036
  scheduleKeyboardFocusAlignment();
1234
1037
  }
1235
1038
  return () => {
1236
1039
  cleanupListeners.forEach(cleanup => cleanup());
1237
- consumePreemptedFocus();
1238
1040
  clearFocusedKeyboardTarget();
1239
1041
  rootElement.style.removeProperty(DrawerViewportCssVars.keyboardInset);
1240
1042
  };
1241
- }, [animateKeyboardScroll, keyboardFocusFrame, keyboardRealignTimeout, modal, mounted, nestedDrawerOpen, open, restoreKeyboardScrollAdjustment, rootElement, setKeyboardScrollSlack]);
1043
+ }, [animateKeyboardScroll, keyboardFocusFrame, mounted, nestedDrawerOpen, open, restoreKeyboardScrollAdjustment, rootElement, setKeyboardScrollSlack]);
1242
1044
  const onTouchStart = useScrollLock.useStableCallback(event => {
1045
+ if (!open || !mounted || nestedDrawerOpen) {
1046
+ resetTouchTrackingState();
1047
+ return;
1048
+ }
1243
1049
  const touch = event.touches[0];
1050
+ if (!touch) {
1051
+ return;
1052
+ }
1244
1053
  pendingKeyboardFocusMovedRef.current = false;
1245
1054
  keyboardTouchStartRef.current = {
1246
1055
  x: touch.clientX,
@@ -1266,9 +1075,9 @@ function DrawerVirtualKeyboardProvider(props) {
1266
1075
  return;
1267
1076
  }
1268
1077
  const touch = event.changedTouches[0] ?? event.touches[0];
1269
- const root = rootElement.getRootNode();
1078
+ const doc = useScrollLock.ownerDocument(event.currentTarget);
1270
1079
  const nativeEventTarget = useScrollLock.getTarget(event.nativeEvent);
1271
- const pointTarget = touch ? resolveKeyboardTouchTargetFromPoint(root, touch.clientX, touch.clientY) : null;
1080
+ const pointTarget = touch ? resolveKeyboardTouchTargetFromPoint(doc, touch.clientX, touch.clientY) : null;
1272
1081
 
1273
1082
  // The lift point landed on another interactive/label element; let its native tap
1274
1083
  // through instead of stealing it for the touchstart input.
@@ -1286,7 +1095,7 @@ function DrawerVirtualKeyboardProvider(props) {
1286
1095
  clickTarget: keyboardClickTarget,
1287
1096
  focusTarget: keyboardFocusTarget
1288
1097
  } = keyboardTarget;
1289
- const win = useScrollLock.getWindow(keyboardFocusTarget);
1098
+ const win = floatingUi_utils.getWindow(keyboardFocusTarget);
1290
1099
 
1291
1100
  // While pinch-zoomed, keyboard alignment is suspended; let native behavior
1292
1101
  // handle focus and caret placement instead of blurring and re-focusing.
@@ -1297,24 +1106,15 @@ function DrawerVirtualKeyboardProvider(props) {
1297
1106
 
1298
1107
  // Already focused with the keyboard up: let the native tap through so it can
1299
1108
  // reposition the caret, rather than blurring and re-focusing the same input.
1300
- if (useScrollLock.activeElement(useScrollLock.ownerDocument(keyboardFocusTarget)) === keyboardFocusTarget && (!win.visualViewport || getKeyboardVisualViewport(win) != null)) {
1109
+ if (useScrollLock.activeElement(useScrollLock.ownerDocument(keyboardFocusTarget)) === keyboardFocusTarget && isKeyboardVisualViewportOpen(win)) {
1301
1110
  resetTouchTrackingState();
1302
1111
  return;
1303
1112
  }
1304
1113
 
1305
1114
  // iOS only opens the software keyboard when focus happens synchronously
1306
- // inside the touch gesture. The flag suppresses the `focusout` cleanup the
1307
- // intermediate blur would otherwise trigger, so the keyboard inset isn't
1308
- // dropped for a frame between the blur and the re-focus. It is cleared by the
1309
- // `focusin` that lands, so only that blur is suppressed; the `finally` is the
1310
- // backstop for a target that never takes focus.
1115
+ // inside the touch gesture.
1311
1116
  event.preventDefault();
1312
- programmaticKeyboardFocusRef.current = true;
1313
- try {
1314
- focusKeyboardInputWithoutPageScroll(keyboardFocusTarget);
1315
- } finally {
1316
- programmaticKeyboardFocusRef.current = false;
1317
- }
1117
+ focusKeyboardInputWithoutPageScroll(keyboardFocusTarget);
1318
1118
  // Preventing the touchend default also suppresses the compatibility mouse
1319
1119
  // events, including `click`; redispatch an untrusted replacement on the
1320
1120
  // original tap target so click handlers still run with the tap coordinates.
@@ -1339,7 +1139,7 @@ function isKeyboardInputElement(element) {
1339
1139
  if (element.isContentEditable) {
1340
1140
  return true;
1341
1141
  }
1342
- const win = useScrollLock.getWindow(element);
1142
+ const win = floatingUi_utils.getWindow(element);
1343
1143
  if (element instanceof win.HTMLTextAreaElement || element instanceof win.HTMLInputElement && KEYBOARD_INPUT_TYPES.has(element.type)) {
1344
1144
  // Disabled controls can't focus or open the keyboard, so tap-to-focus must skip them —
1345
1145
  // otherwise the dispatched click fires handlers a native tap on a disabled control never would.
@@ -1348,7 +1148,7 @@ function isKeyboardInputElement(element) {
1348
1148
  return false;
1349
1149
  }
1350
1150
  function resolveKeyboardInputTarget(target) {
1351
- if (!useScrollLock.isHTMLElement(target)) {
1151
+ if (!floatingUi_utils.isHTMLElement(target)) {
1352
1152
  return null;
1353
1153
  }
1354
1154
  if (isKeyboardInputElement(target)) {
@@ -1356,19 +1156,16 @@ function resolveKeyboardInputTarget(target) {
1356
1156
  }
1357
1157
  const label = target.closest('label');
1358
1158
  const control = label?.control ?? null;
1359
- return useScrollLock.isHTMLElement(control) && isKeyboardInputElement(control) ? control : null;
1159
+ return floatingUi_utils.isHTMLElement(control) && isKeyboardInputElement(control) ? control : null;
1360
1160
  }
1361
1161
  function resolveKeyboardTouchTarget(target) {
1362
- if (!useScrollLock.isHTMLElement(target)) {
1363
- return null;
1364
- }
1365
1162
  const focusTarget = resolveKeyboardInputTarget(target);
1366
1163
  if (!focusTarget) {
1367
1164
  return null;
1368
1165
  }
1369
1166
  return {
1370
1167
  focusTarget,
1371
- clickTarget: target
1168
+ clickTarget: floatingUi_utils.isHTMLElement(target) ? target : focusTarget
1372
1169
  };
1373
1170
  }
1374
1171
 
@@ -1381,16 +1178,14 @@ function getContentEditableHost(element) {
1381
1178
  }
1382
1179
  return host;
1383
1180
  }
1384
- function resolveKeyboardTouchTargetFromPoint(root, clientX, clientY) {
1385
- const exactTarget = ScrollAreaViewport.getElementAtPoint(root, clientX, clientY);
1386
- if (useScrollLock.isHTMLElement(exactTarget)) {
1387
- const exactKeyboardTarget = resolveKeyboardInputTarget(exactTarget);
1388
- if (exactKeyboardTarget) {
1389
- return {
1390
- focusTarget: exactKeyboardTarget,
1391
- clickTarget: exactTarget
1392
- };
1393
- }
1181
+ function resolveKeyboardTouchTargetFromPoint(doc, clientX, clientY) {
1182
+ const exactTarget = ScrollAreaViewport.getElementAtPoint(doc, clientX, clientY);
1183
+ const exactKeyboardTarget = resolveKeyboardInputTarget(exactTarget);
1184
+ if (exactKeyboardTarget) {
1185
+ return {
1186
+ focusTarget: exactKeyboardTarget,
1187
+ clickTarget: floatingUi_utils.isHTMLElement(exactTarget) ? exactTarget : exactKeyboardTarget
1188
+ };
1394
1189
  }
1395
1190
 
1396
1191
  // Probing nearby points compensates for iOS retargeting taps while the page reacts
@@ -1403,7 +1198,7 @@ function resolveKeyboardTouchTargetFromPoint(root, clientX, clientY) {
1403
1198
  return KEYBOARD_TAP_BLOCKED;
1404
1199
  }
1405
1200
  for (const [offsetX, offsetY] of [[0, INPUT_TAP_HIT_SLOP], [0, -INPUT_TAP_HIT_SLOP], [INPUT_TAP_HIT_SLOP, 0], [-INPUT_TAP_HIT_SLOP, 0]]) {
1406
- const keyboardTarget = resolveKeyboardInputTarget(ScrollAreaViewport.getElementAtPoint(root, clientX + offsetX, clientY + offsetY));
1201
+ const keyboardTarget = resolveKeyboardInputTarget(ScrollAreaViewport.getElementAtPoint(doc, clientX + offsetX, clientY + offsetY));
1407
1202
  if (keyboardTarget) {
1408
1203
  return {
1409
1204
  focusTarget: keyboardTarget,
@@ -1414,7 +1209,7 @@ function resolveKeyboardTouchTargetFromPoint(root, clientX, clientY) {
1414
1209
  return null;
1415
1210
  }
1416
1211
  function dispatchKeyboardClick(target, touch) {
1417
- const win = useScrollLock.getWindow(target);
1212
+ const win = floatingUi_utils.getWindow(target);
1418
1213
  const ClickEvent = win.PointerEvent ?? win.MouseEvent;
1419
1214
  target.dispatchEvent(new ClickEvent('click', {
1420
1215
  bubbles: true,
@@ -1427,10 +1222,15 @@ function dispatchKeyboardClick(target, touch) {
1427
1222
  }
1428
1223
  function focusKeyboardInputWithoutPageScroll(target) {
1429
1224
  const wasFocused = useScrollLock.activeElement(useScrollLock.ownerDocument(target)) === target;
1225
+ const previousOpacity = target.style.opacity;
1226
+ const previousTransform = target.style.transform;
1227
+ const previousTransition = target.style.transition;
1228
+
1430
1229
  // iOS Safari can still scroll the page for transformed sheets even with preventScroll.
1431
- // Move the input off-screen only for the synchronous focus call; `preventScroll`
1432
- // suppresses the in-page ancestor scrolling that would otherwise chase the fake rect.
1433
- const restoreStyles = overrideGeometryDuringFocus(target, -2000);
1230
+ // Move the input off-screen only for the synchronous focus call.
1231
+ target.style.transition = 'none';
1232
+ target.style.opacity = '0';
1233
+ target.style.transform = 'translateY(-2000px)';
1434
1234
  try {
1435
1235
  if (wasFocused) {
1436
1236
  target.blur();
@@ -1439,25 +1239,10 @@ function focusKeyboardInputWithoutPageScroll(target) {
1439
1239
  preventScroll: true
1440
1240
  });
1441
1241
  } finally {
1442
- restoreStyles();
1443
- }
1444
- }
1445
-
1446
- // Overrides the painted geometry WebKit samples when an element gains focus. The rect is
1447
- // hidden (opacity) rather than detached so layout is unaffected; the caller must restore
1448
- // synchronously before the next paint.
1449
- function overrideGeometryDuringFocus(target, translateY) {
1450
- const previousOpacity = target.style.opacity;
1451
- const previousTransform = target.style.transform;
1452
- const previousTransition = target.style.transition;
1453
- target.style.transition = 'none';
1454
- target.style.opacity = '0';
1455
- target.style.transform = `translateY(${translateY}px)`;
1456
- return () => {
1457
1242
  target.style.opacity = previousOpacity;
1458
1243
  target.style.transform = previousTransform;
1459
1244
  target.style.transition = previousTransition;
1460
- };
1245
+ }
1461
1246
  }
1462
1247
  function findKeyboardScrollTarget(target, root) {
1463
1248
  // Start at the parent: scrolling the focused field's own content (an overflowing
@@ -1466,8 +1251,8 @@ function findKeyboardScrollTarget(target, root) {
1466
1251
  // the drawer body scroller. Prefer an already-scrollable ancestor, then fall back to one
1467
1252
  // that only becomes scrollable once keyboard slack is added (overflow intent without
1468
1253
  // current overflow).
1469
- const scrollStart = useScrollLock.getParentNode(target);
1470
- return scrollable.findScrollableTouchTarget(scrollStart, root, 'vertical') ?? scrollable.findScrollableTouchTarget(scrollStart, root, 'vertical', true);
1254
+ const scrollStart = floatingUi_utils.getParentNode(target);
1255
+ return ScrollAreaViewport.findScrollableTouchTarget(scrollStart, root, 'vertical') ?? ScrollAreaViewport.findScrollableTouchTarget(scrollStart, root, 'vertical', true);
1471
1256
  }
1472
1257
  function getKeyboardVisualViewport(win) {
1473
1258
  const visualViewport = win.visualViewport;
@@ -1485,22 +1270,11 @@ function getKeyboardVisualViewport(win) {
1485
1270
  bottom: Math.min(win.innerHeight, top + visualViewport.height)
1486
1271
  };
1487
1272
  }
1488
-
1489
- /**
1490
- * Controls a Drawer imperatively and associates detached `Drawer.Trigger` components with a
1491
- * `Drawer.Root`. Create one with `Drawer.createHandle()` and pass it to the `handle` prop of the
1492
- * root and of any triggers rendered outside of it.
1493
- *
1494
- * The imperative methods take effect only while a root using this handle is mounted; calls made
1495
- * before a root attaches (or after it unmounts) are ignored.
1496
- */
1497
- class DrawerHandle extends DrawerDescription.DialogHandle {}
1498
-
1499
- /**
1500
- * Creates a new handle to connect a Drawer.Root with detached Drawer.Trigger components.
1501
- */
1502
- function createDrawerHandle() {
1503
- return new DrawerHandle();
1273
+ function getDrawerKeyboardInset(win, keyboardViewport) {
1274
+ return Math.max(0, win.innerHeight - keyboardViewport.bottom);
1275
+ }
1276
+ function isKeyboardVisualViewportOpen(win) {
1277
+ return !win.visualViewport || getKeyboardVisualViewport(win) != null;
1504
1278
  }
1505
1279
 
1506
1280
  var index_parts$5 = /*#__PURE__*/Object.freeze({
@@ -1509,7 +1283,7 @@ var index_parts$5 = /*#__PURE__*/Object.freeze({
1509
1283
  Close: DrawerTitle.DrawerClose,
1510
1284
  Content: ScrollAreaViewport.DrawerContent,
1511
1285
  Description: DrawerDescription.DrawerDescription,
1512
- Handle: DrawerHandle,
1286
+ Handle: DrawerDescription.DialogHandle,
1513
1287
  Indent: DrawerIndent,
1514
1288
  IndentBackground: DrawerIndentBackground,
1515
1289
  Popup: ScrollAreaViewport.DrawerPopup,
@@ -1521,7 +1295,7 @@ var index_parts$5 = /*#__PURE__*/Object.freeze({
1521
1295
  Trigger: ScrollAreaViewport.DrawerTrigger,
1522
1296
  Viewport: ScrollAreaViewport.DrawerViewport,
1523
1297
  VirtualKeyboardProvider: DrawerVirtualKeyboardProvider,
1524
- createHandle: createDrawerHandle
1298
+ createHandle: DrawerDescription.createDialogHandle
1525
1299
  });
1526
1300
 
1527
1301
  /**
@@ -1590,7 +1364,7 @@ const MenuCheckboxItem = /*#__PURE__*/React__namespace.forwardRef(function MenuC
1590
1364
  id: idProp,
1591
1365
  label,
1592
1366
  nativeButton = false,
1593
- disabled: disabledProp = false,
1367
+ disabled = false,
1594
1368
  closeOnClick = false,
1595
1369
  checked: checkedProp,
1596
1370
  defaultChecked,
@@ -1599,7 +1373,6 @@ const MenuCheckboxItem = /*#__PURE__*/React__namespace.forwardRef(function MenuC
1599
1373
  ...elementProps
1600
1374
  } = componentProps;
1601
1375
  const listItem = useCompositeListItem.useCompositeListItem({
1602
- guess: true,
1603
1376
  label
1604
1377
  });
1605
1378
  const menuPositionerContext = MenuSubmenuTrigger.useMenuPositionerContext(true);
@@ -1607,8 +1380,6 @@ const MenuCheckboxItem = /*#__PURE__*/React__namespace.forwardRef(function MenuC
1607
1380
  const {
1608
1381
  store
1609
1382
  } = MenuSubmenuTrigger.useMenuRootContext();
1610
- const rootDisabled = store.useState('disabled');
1611
- const disabled = disabledProp || rootDisabled;
1612
1383
  const highlighted = store.useState('isActive', listItem.index);
1613
1384
  const itemProps = store.useState('itemProps');
1614
1385
  const [checked, setChecked] = useScrollLock.useControlled({
@@ -1637,7 +1408,7 @@ const MenuCheckboxItem = /*#__PURE__*/React__namespace.forwardRef(function MenuC
1637
1408
  }), [disabled, highlighted, checked]);
1638
1409
  function handleClick(event) {
1639
1410
  const details = useScrollLock.createChangeEventDetails(useScrollLock.itemPress, event.nativeEvent, undefined, {
1640
- preventUnmountOnClose: useRenderElement.NOOP
1411
+ preventUnmountOnClose() {}
1641
1412
  });
1642
1413
  onCheckedChange?.(!checked, details);
1643
1414
  if (details.isCanceled) {
@@ -1680,7 +1451,6 @@ const MenuCheckboxItemIndicator = /*#__PURE__*/React__namespace.forwardRef(funct
1680
1451
  const indicatorRef = React__namespace.useRef(null);
1681
1452
  const {
1682
1453
  transitionStatus,
1683
- mounted,
1684
1454
  setMounted
1685
1455
  } = useScrollLock.useTransitionStatus(item.checked);
1686
1456
  useScrollLock.useOpenChangeComplete({
@@ -1706,22 +1476,22 @@ const MenuCheckboxItemIndicator = /*#__PURE__*/React__namespace.forwardRef(funct
1706
1476
  'aria-hidden': true,
1707
1477
  ...elementProps
1708
1478
  },
1709
- enabled: keepMounted || mounted
1479
+ enabled: keepMounted || item.checked
1710
1480
  });
1711
1481
  return element;
1712
1482
  });
1713
1483
  if (process.env.NODE_ENV !== "production") MenuCheckboxItemIndicator.displayName = "MenuCheckboxItemIndicator";
1714
1484
 
1715
1485
  function getCssDimensions(element) {
1716
- const css = useScrollLock.getComputedStyle(element);
1486
+ const css = floatingUi_utils.getComputedStyle(element);
1717
1487
  // In testing environments, the `width` and `height` properties are empty
1718
1488
  // strings for SVG elements, returning NaN. Fallback to `0` in this case.
1719
1489
  let width = parseFloat(css.width) || 0;
1720
1490
  let height = parseFloat(css.height) || 0;
1721
- const hasOffset = useScrollLock.isHTMLElement(element);
1491
+ const hasOffset = floatingUi_utils.isHTMLElement(element);
1722
1492
  const offsetWidth = hasOffset ? element.offsetWidth : width;
1723
1493
  const offsetHeight = hasOffset ? element.offsetHeight : height;
1724
- const shouldFallback = useScrollLock.round(width) !== offsetWidth || useScrollLock.round(height) !== offsetHeight;
1494
+ const shouldFallback = floatingUi_utils.round(width) !== offsetWidth || floatingUi_utils.round(height) !== offsetHeight;
1725
1495
  if (shouldFallback) {
1726
1496
  width = offsetWidth;
1727
1497
  height = offsetHeight;
@@ -1746,14 +1516,30 @@ function usePopupAutoResize(parameters) {
1746
1516
  side,
1747
1517
  direction
1748
1518
  } = parameters;
1749
- const runOnceAnimationsFinish = useScrollLock.useAnimationsFinished(popupElement, true);
1519
+ const runOnceAnimationsFinish = useScrollLock.useAnimationsFinished(popupElement, true, false);
1750
1520
  const animationFrame = useScrollLock.useAnimationFrame();
1751
1521
  const committedDimensionsRef = React__namespace.useRef(null);
1752
1522
  const isInitialRenderRef = React__namespace.useRef(true);
1753
1523
  const restoreAnchoringStylesRef = React__namespace.useRef(useRenderElement.NOOP);
1754
1524
  const onMeasureLayout = useScrollLock.useStableCallback(onMeasureLayoutParam);
1755
1525
  const onMeasureLayoutComplete = useScrollLock.useStableCallback(onMeasureLayoutCompleteParam);
1756
- const anchoringStyles = React__namespace.useMemo(() => getPopupAnchoringStyles(side, direction), [side, direction]);
1526
+ const anchoringStyles = React__namespace.useMemo(() => {
1527
+ // Ensure popup size transitions correctly when anchored to `bottom` (side=top) or `right` (side=left).
1528
+ let isOriginSide = side === 'top';
1529
+ let isPhysicalLeft = side === 'left';
1530
+ if (direction === 'rtl') {
1531
+ isOriginSide = isOriginSide || side === 'inline-end';
1532
+ isPhysicalLeft = isPhysicalLeft || side === 'inline-end';
1533
+ } else {
1534
+ isOriginSide = isOriginSide || side === 'inline-start';
1535
+ isPhysicalLeft = isPhysicalLeft || side === 'inline-start';
1536
+ }
1537
+ return isOriginSide ? {
1538
+ position: 'absolute',
1539
+ [side === 'top' ? 'bottom' : 'top']: '0',
1540
+ [isPhysicalLeft ? 'right' : 'left']: '0'
1541
+ } : useRenderElement.EMPTY_OBJECT;
1542
+ }, [side, direction]);
1757
1543
  useScrollLock.useIsoLayoutEffect(() => {
1758
1544
  // Reset the state when the popup is closed.
1759
1545
  if (!mounted) {
@@ -1829,19 +1615,6 @@ function usePopupAutoResize(parameters) {
1829
1615
  };
1830
1616
  }, [content, popupElement, positionerElement, runOnceAnimationsFinish, animationFrame, mounted, onMeasureLayout, onMeasureLayoutComplete, anchoringStyles]);
1831
1617
  }
1832
- function getPopupAnchoringStyles(side, direction) {
1833
- // Ensure popup size transitions correctly when anchored to `bottom` (side=top) or `right` (side=left).
1834
- const isPhysicalTop = side === 'top';
1835
- const isPhysicalLeft = side === 'left' || side === (direction === 'rtl' ? 'inline-end' : 'inline-start');
1836
- if (!isPhysicalTop && !isPhysicalLeft) {
1837
- return useRenderElement.EMPTY_OBJECT;
1838
- }
1839
- return {
1840
- position: 'absolute',
1841
- [isPhysicalTop ? 'bottom' : 'top']: '0',
1842
- [isPhysicalLeft ? 'right' : 'left']: '0'
1843
- };
1844
- }
1845
1618
  function overrideElementStyle(element, property, value) {
1846
1619
  const originalValue = element.style.getPropertyValue(property);
1847
1620
  element.style.setProperty(property, value);
@@ -1871,80 +1644,6 @@ function setPositionerCssSize(positionerElement, size) {
1871
1644
  positionerElement.style.setProperty('--positioner-height', height);
1872
1645
  }
1873
1646
 
1874
- const adaptiveOrigin = {
1875
- name: 'adaptiveOrigin',
1876
- async fn(state) {
1877
- const {
1878
- x: rawX,
1879
- y: rawY,
1880
- rects: {
1881
- floating: floatRect
1882
- },
1883
- elements: {
1884
- floating
1885
- },
1886
- platform,
1887
- strategy,
1888
- placement
1889
- } = state;
1890
- const win = useScrollLock.getWindow(floating);
1891
- const styles = win.getComputedStyle(floating);
1892
- const hasTransition = styles.transitionDuration !== '0s' && styles.transitionDuration !== '';
1893
- if (!hasTransition) {
1894
- return {
1895
- x: rawX,
1896
- y: rawY,
1897
- data: useCompositeListItem.DEFAULT_SIDES
1898
- };
1899
- }
1900
- const offsetParent = await platform.getOffsetParent?.(floating);
1901
- let offsetDimensions = {
1902
- width: 0,
1903
- height: 0
1904
- };
1905
-
1906
- // For fixed strategy, prefer visualViewport if available
1907
- if (strategy === 'fixed' && win?.visualViewport) {
1908
- offsetDimensions = {
1909
- width: win.visualViewport.width,
1910
- height: win.visualViewport.height
1911
- };
1912
- } else if (offsetParent === win) {
1913
- const doc = useScrollLock.ownerDocument(floating);
1914
- offsetDimensions = {
1915
- width: doc.documentElement.clientWidth,
1916
- height: doc.documentElement.clientHeight
1917
- };
1918
- } else if (await platform.isElement?.(offsetParent)) {
1919
- offsetDimensions = await platform.getDimensions(offsetParent);
1920
- }
1921
- const currentSide = useScrollLock.getSide(placement);
1922
- let x = rawX;
1923
- let y = rawY;
1924
- if (currentSide === 'left') {
1925
- x = offsetDimensions.width - (rawX + floatRect.width);
1926
- }
1927
- if (currentSide === 'top') {
1928
- y = offsetDimensions.height - (rawY + floatRect.height);
1929
- }
1930
- const sideX = currentSide === 'left' ? 'right' : useCompositeListItem.DEFAULT_SIDES.sideX;
1931
- const sideY = currentSide === 'top' ? 'bottom' : useCompositeListItem.DEFAULT_SIDES.sideY;
1932
- return {
1933
- x,
1934
- y,
1935
- data: {
1936
- sideX,
1937
- sideY
1938
- }
1939
- };
1940
- }
1941
- };
1942
-
1943
- const popupViewportStateMapping = {
1944
- activationDirection: value => value ? {
1945
- 'data-activation-direction': value
1946
- } : null
1947
- };
1948
1647
  /**
1949
1648
  * Builds morphing viewport containers for popups that animate between trigger-based content.
1950
1649
  * Handles previous-content snapshots, auto-resize, and state attributes for transitions.
@@ -1953,6 +1652,7 @@ function usePopupViewport(parameters) {
1953
1652
  const {
1954
1653
  store,
1955
1654
  side,
1655
+ cssVars,
1956
1656
  children
1957
1657
  } = parameters;
1958
1658
  const direction = useScrollLock.useDirection();
@@ -1972,15 +1672,14 @@ function usePopupViewport(parameters) {
1972
1672
  const [newTriggerOffset, setNewTriggerOffset] = React__namespace.useState(null);
1973
1673
  const currentContainerRef = React__namespace.useRef(null);
1974
1674
  const previousContainerRef = React__namespace.useRef(null);
1975
- const onAnimationsFinished = useScrollLock.useAnimationsFinished(currentContainerRef, true);
1675
+ const onAnimationsFinished = useScrollLock.useAnimationsFinished(currentContainerRef, true, false);
1976
1676
  const cleanupFrame = useScrollLock.useAnimationFrame();
1977
- const cleanupControllerRef = React__namespace.useRef(null);
1978
1677
  const [previousContentDimensions, setPreviousContentDimensions] = React__namespace.useState(null);
1979
1678
  const [showStartingStyleAttribute, setShowStartingStyleAttribute] = React__namespace.useState(false);
1980
1679
  useScrollLock.useIsoLayoutEffect(() => {
1981
- store.set('adaptiveOrigin', adaptiveOrigin);
1680
+ store.set('hasViewport', true);
1982
1681
  return () => {
1983
- store.set('adaptiveOrigin', undefined);
1682
+ store.set('hasViewport', false);
1984
1683
  };
1985
1684
  }, [store]);
1986
1685
  const handleMeasureLayout = useScrollLock.useStableCallback(() => {
@@ -1996,16 +1695,6 @@ function usePopupViewport(parameters) {
1996
1695
  setPreviousContentDimensions(previousDimensions);
1997
1696
  }
1998
1697
  });
1999
- const armViewportCleanup = useScrollLock.useStableCallback(() => {
2000
- cleanupControllerRef.current?.abort();
2001
- const controller = new AbortController();
2002
- cleanupControllerRef.current = controller;
2003
- onAnimationsFinished(() => {
2004
- setPreviousContentNode(null);
2005
- setPreviousContentDimensions(null);
2006
- capturedNodeRef.current = null;
2007
- }, controller.signal);
2008
- });
2009
1698
  const lastHandledTriggerRef = React__namespace.useRef(null);
2010
1699
  useScrollLock.useIsoLayoutEffect(() => {
2011
1700
  if (!open || !mounted) {
@@ -2023,33 +1712,19 @@ function usePopupViewport(parameters) {
2023
1712
  // so we can pass it to the style hook for animation purposes.
2024
1713
  const offset = calculateRelativePosition(previousActiveTrigger, activeTrigger);
2025
1714
  setNewTriggerOffset(offset);
1715
+ cleanupFrame.request(() => {
1716
+ ReactDOM__namespace.flushSync(() => {
1717
+ setShowStartingStyleAttribute(false);
1718
+ });
1719
+ onAnimationsFinished(() => {
1720
+ setPreviousContentNode(null);
1721
+ setPreviousContentDimensions(null);
1722
+ capturedNodeRef.current = null;
1723
+ });
1724
+ });
2026
1725
  lastHandledTriggerRef.current = activeTrigger;
2027
1726
  }
2028
- }, [activeTrigger, previousActiveTrigger]);
2029
-
2030
- // Arm cleanup after a trigger change, and re-arm it if the current container remounts
2031
- // mid-transition when a lagging payload bumps `currentContentKey`. The remount discards
2032
- // the running entry animation (and with transition-style CSS the replacement mounts at
2033
- // final styles with no animation at all), so re-run the starting-style choreography —
2034
- // otherwise the watcher either strands or fires before the previous container's exit
2035
- // animation finishes.
2036
- useScrollLock.useIsoLayoutEffect(() => {
2037
- if (previousContentNode == null) {
2038
- return;
2039
- }
2040
-
2041
- // Abort the stale watcher synchronously. The remount cancels the old container's
2042
- // animations, and the resulting promise rejection would otherwise run the cleanup
2043
- // in a microtask before the re-armed watcher below is in place.
2044
- cleanupControllerRef.current?.abort();
2045
- setShowStartingStyleAttribute(true);
2046
- cleanupFrame.request(() => {
2047
- ReactDOM__namespace.flushSync(() => {
2048
- setShowStartingStyleAttribute(false);
2049
- });
2050
- armViewportCleanup();
2051
- });
2052
- }, [currentContentKey, previousContentNode, armViewportCleanup, cleanupFrame]);
1727
+ }, [activeTrigger, previousActiveTrigger, previousContentNode, onAnimationsFinished, cleanupFrame]);
2053
1728
 
2054
1729
  // Capture a clone of the current content DOM subtree when not transitioning.
2055
1730
  // We can't store previous React nodes as they may be stateful; instead we capture DOM clones for visual continuity.
@@ -2086,8 +1761,8 @@ function usePopupViewport(parameters) {
2086
1761
  ref: previousContainerRef,
2087
1762
  style: {
2088
1763
  ...(previousContentDimensions ? {
2089
- '--popup-width': `${previousContentDimensions.width}px`,
2090
- '--popup-height': `${previousContentDimensions.height}px`
1764
+ [cssVars.popupWidth]: `${previousContentDimensions.width}px`,
1765
+ [cssVars.popupHeight]: `${previousContentDimensions.height}px`
2091
1766
  } : null),
2092
1767
  position: 'absolute'
2093
1768
  },
@@ -2212,6 +1887,28 @@ function usePopupContentKey(activeTriggerId, payload) {
2212
1887
  return `${activeTriggerId ?? 'current'}-${contentKey}`;
2213
1888
  }
2214
1889
 
1890
+ let MenuViewportCssVars = /*#__PURE__*/function (MenuViewportCssVars) {
1891
+ /**
1892
+ * The width of the parent popup.
1893
+ * This variable is placed on the 'previous' container and stores the width of the popup when the previous content was rendered.
1894
+ * It can be used to freeze the dimensions of the popup when animating between different content.
1895
+ */
1896
+ MenuViewportCssVars["popupWidth"] = "--popup-width";
1897
+ /**
1898
+ * The height of the parent popup.
1899
+ * This variable is placed on the 'previous' container and stores the height of the popup when the previous content was rendered.
1900
+ * It can be used to freeze the dimensions of the popup when animating between different content.
1901
+ */
1902
+ MenuViewportCssVars["popupHeight"] = "--popup-height";
1903
+ return MenuViewportCssVars;
1904
+ }({});
1905
+
1906
+ const stateAttributesMapping$2 = {
1907
+ activationDirection: value => value ? {
1908
+ 'data-activation-direction': value
1909
+ } : null
1910
+ };
1911
+
2215
1912
  /**
2216
1913
  * A viewport for displaying content transitions.
2217
1914
  * This component is only required if one popup can be opened by multiple triggers, its content
@@ -2241,6 +1938,7 @@ const MenuViewport = /*#__PURE__*/React__namespace.forwardRef(function MenuViewp
2241
1938
  } = usePopupViewport({
2242
1939
  store,
2243
1940
  side,
1941
+ cssVars: MenuViewportCssVars,
2244
1942
  children
2245
1943
  });
2246
1944
  const state = {
@@ -2254,50 +1952,47 @@ const MenuViewport = /*#__PURE__*/React__namespace.forwardRef(function MenuViewp
2254
1952
  props: [elementProps, {
2255
1953
  children: childrenToRender
2256
1954
  }],
2257
- stateAttributesMapping: popupViewportStateMapping
1955
+ stateAttributesMapping: stateAttributesMapping$2
2258
1956
  });
2259
1957
  });
2260
1958
  if (process.env.NODE_ENV !== "production") MenuViewport.displayName = "MenuViewport";
2261
1959
 
2262
- /**
2263
- * Controls a Menu imperatively and associates detached `Menu.Trigger` components with a `Menu.Root`.
2264
- * Create one with `Menu.createHandle()` and pass it to the `handle` prop of the root and of any
2265
- * triggers rendered outside of it.
2266
- *
2267
- * The imperative methods take effect only while a root using this handle is mounted; calls made
2268
- * before a root attaches (or after it unmounts) are ignored.
2269
- */
2270
- class MenuHandle extends DrawerDescription.BasePopupHandle {
1960
+ class MenuHandle {
1961
+ /**
1962
+ * Internal store holding the menu's state.
1963
+ * @internal
1964
+ */
1965
+
2271
1966
  constructor() {
2272
- super(MenuSubmenuTrigger.createNullMenuStore(), 'Menu');
1967
+ this.store = new MenuSubmenuTrigger.MenuStore();
2273
1968
  }
2274
1969
 
2275
1970
  /**
2276
1971
  * Opens the menu and associates it with the trigger with the given id.
1972
+ * The trigger must be a Menu.Trigger component with this handle passed as a prop.
2277
1973
  *
2278
- * This method should only be called in an event handler or an effect (not during rendering).
2279
- *
2280
- * @param triggerId ID of the trigger to associate with the menu. The trigger must be a matching
2281
- * `Menu.Trigger` with this handle passed as a prop.
1974
+ * @param triggerId ID of the trigger to associate with the menu.
2282
1975
  */
2283
1976
  open(triggerId) {
2284
- this.openByTrigger(triggerId);
1977
+ const triggerElement = triggerId ? this.store.context.triggerElements.getById(triggerId) : undefined;
1978
+ if (triggerId && !triggerElement) {
1979
+ throw new Error(process.env.NODE_ENV !== "production" ? `Base UI: MenuHandle.open: No trigger found with id "${triggerId}".` : useRenderElement.formatErrorMessage(83, triggerId));
1980
+ }
1981
+ this.store.setOpen(true, useScrollLock.createChangeEventDetails('imperative-action', undefined, triggerElement));
2285
1982
  }
2286
1983
 
2287
1984
  /**
2288
1985
  * Closes the menu.
2289
- *
2290
- * This method should only be called in an event handler or an effect (not during rendering).
2291
1986
  */
2292
1987
  close() {
2293
- this.closePopup();
1988
+ this.store.setOpen(false, useScrollLock.createChangeEventDetails('imperative-action', undefined, undefined));
2294
1989
  }
2295
1990
 
2296
1991
  /**
2297
- * Whether the menu is currently open. Returns `false` while no root is attached to the handle.
1992
+ * Indicates whether the menu is currently open.
2298
1993
  */
2299
1994
  get isOpen() {
2300
- return this.attachedStore?.select('open') ?? false;
1995
+ return this.store.select('open');
2301
1996
  }
2302
1997
  }
2303
1998
 
@@ -2344,6 +2039,20 @@ function useScrollAreaScrollbarContext() {
2344
2039
  return context;
2345
2040
  }
2346
2041
 
2042
+ let ScrollAreaScrollbarCssVars = /*#__PURE__*/function (ScrollAreaScrollbarCssVars) {
2043
+ /**
2044
+ * The scroll area thumb's height.
2045
+ * @type {number}
2046
+ */
2047
+ ScrollAreaScrollbarCssVars["scrollAreaThumbHeight"] = "--scroll-area-thumb-height";
2048
+ /**
2049
+ * The scroll area thumb's width.
2050
+ * @type {number}
2051
+ */
2052
+ ScrollAreaScrollbarCssVars["scrollAreaThumbWidth"] = "--scroll-area-thumb-width";
2053
+ return ScrollAreaScrollbarCssVars;
2054
+ }({});
2055
+
2347
2056
  /**
2348
2057
  * A vertical or horizontal scrollbar for the scroll area.
2349
2058
  * Renders a `<div>` element.
@@ -2364,6 +2073,7 @@ const ScrollAreaScrollbar = /*#__PURE__*/React__namespace.forwardRef(function Sc
2364
2073
  scrollingX,
2365
2074
  scrollingY,
2366
2075
  hiddenState,
2076
+ overflowEdges,
2367
2077
  scrollbarYRef,
2368
2078
  scrollbarXRef,
2369
2079
  viewportRef,
@@ -2372,37 +2082,43 @@ const ScrollAreaScrollbar = /*#__PURE__*/React__namespace.forwardRef(function Sc
2372
2082
  handlePointerDown,
2373
2083
  handlePointerUp,
2374
2084
  handleScroll,
2375
- disableViewportSnap,
2376
2085
  rootId,
2377
2086
  thumbSize,
2378
- hasMeasuredScrollbar,
2379
- viewportState
2087
+ hasMeasuredScrollbar
2380
2088
  } = ScrollAreaViewport.useScrollAreaRootContext();
2381
- const vertical = orientation === 'vertical';
2382
2089
  const state = {
2383
- ...viewportState,
2384
2090
  hovering,
2385
- scrolling: vertical ? scrollingY : scrollingX,
2386
- orientation
2091
+ scrolling: {
2092
+ horizontal: scrollingX,
2093
+ vertical: scrollingY
2094
+ }[orientation],
2095
+ orientation,
2096
+ hasOverflowX: !hiddenState.x,
2097
+ hasOverflowY: !hiddenState.y,
2098
+ overflowXStart: overflowEdges.xStart,
2099
+ overflowXEnd: overflowEdges.xEnd,
2100
+ overflowYStart: overflowEdges.yStart,
2101
+ overflowYEnd: overflowEdges.yEnd,
2102
+ cornerHidden: hiddenState.corner
2387
2103
  };
2388
2104
  const direction = useScrollLock.useDirection();
2389
2105
  const hideTrackUntilMeasured = !hasMeasuredScrollbar && !keepMounted;
2390
- const isHidden = vertical ? hiddenState.y : hiddenState.x;
2106
+ const isHidden = orientation === 'vertical' ? hiddenState.y : hiddenState.x;
2391
2107
  const shouldRender = keepMounted || !isHidden;
2392
2108
  React__namespace.useEffect(() => {
2393
2109
  if (!shouldRender) {
2394
2110
  return undefined;
2395
2111
  }
2396
2112
  const viewportEl = viewportRef.current;
2397
- const scrollbarEl = vertical ? scrollbarYRef.current : scrollbarXRef.current;
2113
+ const scrollbarEl = orientation === 'vertical' ? scrollbarYRef.current : scrollbarXRef.current;
2398
2114
  if (!scrollbarEl) {
2399
2115
  return undefined;
2400
2116
  }
2401
2117
  function handleWheel(event) {
2402
- if (!viewportEl || event.ctrlKey) {
2118
+ if (!viewportEl || !scrollbarEl || event.ctrlKey) {
2403
2119
  return;
2404
2120
  }
2405
- const horizontal = !vertical;
2121
+ const horizontal = orientation === 'horizontal';
2406
2122
  const scrollProperty = horizontal ? 'scrollLeft' : 'scrollTop';
2407
2123
  const delta = horizontal ? event.deltaX : event.deltaY;
2408
2124
  if (delta === 0) {
@@ -2429,7 +2145,7 @@ const ScrollAreaScrollbar = /*#__PURE__*/React__namespace.forwardRef(function Sc
2429
2145
  return useScrollLock.addEventListener(scrollbarEl, 'wheel', handleWheel, {
2430
2146
  passive: false
2431
2147
  });
2432
- }, [direction, handleScroll, vertical, scrollbarXRef, scrollbarYRef, shouldRender, viewportRef]);
2148
+ }, [direction, handleScroll, orientation, scrollbarXRef, scrollbarYRef, shouldRender, viewportRef]);
2433
2149
  const props = {
2434
2150
  ...(rootId && {
2435
2151
  'data-id': `${rootId}-scrollbar`
@@ -2439,55 +2155,58 @@ const ScrollAreaScrollbar = /*#__PURE__*/React__namespace.forwardRef(function Sc
2439
2155
  return;
2440
2156
  }
2441
2157
  const target = useScrollLock.getTarget(event.nativeEvent);
2442
- const thumbEl = vertical ? thumbYRef.current : thumbXRef.current;
2158
+ const thumb = orientation === 'vertical' ? thumbYRef.current : thumbXRef.current;
2443
2159
 
2444
2160
  // Ignore clicks on thumb, including cases where React retargets the
2445
2161
  // synthetic event to the track host across a shadow boundary.
2446
- if (thumbEl && useScrollLock.contains(thumbEl, target)) {
2447
- return;
2448
- }
2449
- const viewportEl = viewportRef.current;
2450
- if (!viewportEl) {
2451
- return;
2452
- }
2453
- const scrollbarEl = vertical ? scrollbarYRef.current : scrollbarXRef.current;
2454
- if (!thumbEl || !scrollbarEl) {
2162
+ if (thumb && useScrollLock.contains(thumb, target)) {
2455
2163
  return;
2456
2164
  }
2457
- const axis = vertical ? 'y' : 'x';
2458
- const thumbOffset = ScrollAreaViewport.getOffset(thumbEl, 'margin', axis);
2459
- const scrollbarOffset = ScrollAreaViewport.getOffset(scrollbarEl, 'padding', axis);
2460
- const thumbSizePx = vertical ? thumbEl.offsetHeight : thumbEl.offsetWidth;
2461
- const trackRect = scrollbarEl.getBoundingClientRect();
2462
- const clickPosition = vertical ? event.clientY - trackRect.top - thumbSizePx / 2 - scrollbarOffset + thumbOffset / 2 : event.clientX - trackRect.left - thumbSizePx / 2 - scrollbarOffset + thumbOffset / 2;
2463
- const scrollableSize = vertical ? viewportEl.scrollHeight : viewportEl.scrollWidth;
2464
- const viewportSize = vertical ? viewportEl.clientHeight : viewportEl.clientWidth;
2465
- const trackSize = vertical ? scrollbarEl.offsetHeight : scrollbarEl.offsetWidth;
2466
- const maxThumbOffset = trackSize - thumbSizePx - scrollbarOffset - thumbOffset;
2467
- // A short or heavily padded track can drive `maxThumbOffset` to zero or
2468
- // negative once the thumb hits its `MIN_THUMB_SIZE` floor. Dividing by it
2469
- // would yield a non-finite (`Infinity`/`NaN`) or inverted scroll position.
2470
- if (maxThumbOffset <= 0) {
2165
+ if (!viewportRef.current) {
2471
2166
  return;
2472
2167
  }
2473
- const scrollRatio = clickPosition / maxThumbOffset;
2474
- const maxScrollDistance = scrollableSize - viewportSize;
2475
-
2476
- // Disable snapping before the jump-to-click assignment, or the
2477
- // assigned position quantizes to the nearest snap point and the thumb
2478
- // stays offset from the pointer for the whole drag. `handlePointerDown`
2479
- // below re-runs this as a guarded no-op for the thumb-drag path.
2480
- disableViewportSnap();
2481
- if (vertical) {
2482
- viewportEl.scrollTop = scrollRatio * maxScrollDistance;
2483
- } else if (direction === 'rtl') {
2484
- viewportEl.scrollLeft = -(1 - scrollRatio) * maxScrollDistance;
2485
- } else {
2486
- viewportEl.scrollLeft = scrollRatio * maxScrollDistance;
2168
+
2169
+ // Handle Y-axis (vertical) scroll
2170
+ if (thumbYRef.current && scrollbarYRef.current && orientation === 'vertical') {
2171
+ const thumbYOffset = ScrollAreaViewport.getOffset(thumbYRef.current, 'margin', 'y');
2172
+ const scrollbarYOffset = ScrollAreaViewport.getOffset(scrollbarYRef.current, 'padding', 'y');
2173
+ const thumbHeight = thumbYRef.current.offsetHeight;
2174
+ const trackRectY = scrollbarYRef.current.getBoundingClientRect();
2175
+ const clickY = event.clientY - trackRectY.top - thumbHeight / 2 - scrollbarYOffset + thumbYOffset / 2;
2176
+ const scrollableContentHeight = viewportRef.current.scrollHeight;
2177
+ const viewportHeight = viewportRef.current.clientHeight;
2178
+ const maxThumbOffsetY = scrollbarYRef.current.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset;
2179
+ const scrollRatioY = clickY / maxThumbOffsetY;
2180
+ const newScrollTop = scrollRatioY * (scrollableContentHeight - viewportHeight);
2181
+ viewportRef.current.scrollTop = newScrollTop;
2182
+ }
2183
+ if (thumbXRef.current && scrollbarXRef.current && orientation === 'horizontal') {
2184
+ const thumbXOffset = ScrollAreaViewport.getOffset(thumbXRef.current, 'margin', 'x');
2185
+ const scrollbarXOffset = ScrollAreaViewport.getOffset(scrollbarXRef.current, 'padding', 'x');
2186
+ const thumbWidth = thumbXRef.current.offsetWidth;
2187
+ const trackRectX = scrollbarXRef.current.getBoundingClientRect();
2188
+ const clickX = event.clientX - trackRectX.left - thumbWidth / 2 - scrollbarXOffset + thumbXOffset / 2;
2189
+ const scrollableContentWidth = viewportRef.current.scrollWidth;
2190
+ const viewportWidth = viewportRef.current.clientWidth;
2191
+ const maxThumbOffsetX = scrollbarXRef.current.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset;
2192
+ const scrollRatioX = clickX / maxThumbOffsetX;
2193
+ let newScrollLeft;
2194
+ if (direction === 'rtl') {
2195
+ // In RTL, invert the scroll direction
2196
+ newScrollLeft = (1 - scrollRatioX) * (scrollableContentWidth - viewportWidth);
2197
+
2198
+ // Adjust for browsers that use negative scrollLeft in RTL
2199
+ if (viewportRef.current.scrollLeft <= 0) {
2200
+ newScrollLeft = -newScrollLeft;
2201
+ }
2202
+ } else {
2203
+ newScrollLeft = scrollRatioX * (scrollableContentWidth - viewportWidth);
2204
+ }
2205
+ viewportRef.current.scrollLeft = newScrollLeft;
2487
2206
  }
2488
2207
  handleScroll({
2489
- x: viewportEl.scrollLeft,
2490
- y: viewportEl.scrollTop
2208
+ x: viewportRef.current.scrollLeft,
2209
+ y: viewportRef.current.scrollTop
2491
2210
  });
2492
2211
  handlePointerDown(event);
2493
2212
  },
@@ -2501,30 +2220,34 @@ const ScrollAreaScrollbar = /*#__PURE__*/React__namespace.forwardRef(function Sc
2501
2220
  WebkitUserSelect: 'none',
2502
2221
  userSelect: 'none',
2503
2222
  visibility: hideTrackUntilMeasured ? 'hidden' : undefined,
2504
- ...(vertical ? {
2223
+ ...(orientation === 'vertical' && {
2505
2224
  top: 0,
2506
- bottom: 'var(--scroll-area-corner-height)',
2225
+ bottom: `var(${ScrollAreaViewport.ScrollAreaRootCssVars.scrollAreaCornerHeight})`,
2507
2226
  insetInlineEnd: 0,
2508
- ['--scroll-area-thumb-height']: `${thumbSize.height}px`
2509
- } : {
2227
+ [ScrollAreaScrollbarCssVars.scrollAreaThumbHeight]: `${thumbSize.height}px`
2228
+ }),
2229
+ ...(orientation === 'horizontal' && {
2510
2230
  insetInlineStart: 0,
2511
- insetInlineEnd: 'var(--scroll-area-corner-width)',
2231
+ insetInlineEnd: `var(${ScrollAreaViewport.ScrollAreaRootCssVars.scrollAreaCornerWidth})`,
2512
2232
  bottom: 0,
2513
- ['--scroll-area-thumb-width']: `${thumbSize.width}px`
2233
+ [ScrollAreaScrollbarCssVars.scrollAreaThumbWidth]: `${thumbSize.width}px`
2514
2234
  })
2515
2235
  }
2516
2236
  };
2517
2237
  const element = useRenderElement.useRenderElement('div', componentProps, {
2518
- ref: [forwardedRef, vertical ? scrollbarYRef : scrollbarXRef],
2238
+ ref: [forwardedRef, orientation === 'vertical' ? scrollbarYRef : scrollbarXRef],
2519
2239
  state,
2520
2240
  props: [props, elementProps],
2521
2241
  stateAttributesMapping: ScrollAreaViewport.scrollAreaStateAttributesMapping
2522
2242
  });
2243
+ const contextValue = React__namespace.useMemo(() => ({
2244
+ orientation
2245
+ }), [orientation]);
2523
2246
  if (!shouldRender) {
2524
2247
  return null;
2525
2248
  }
2526
2249
  return /*#__PURE__*/jsxRuntime.jsx(ScrollAreaScrollbarContext.Provider, {
2527
- value: orientation,
2250
+ value: contextValue,
2528
2251
  children: element
2529
2252
  });
2530
2253
  });
@@ -2612,30 +2335,43 @@ const ScrollAreaThumb = /*#__PURE__*/React__namespace.forwardRef(function Scroll
2612
2335
  handlePointerDown,
2613
2336
  handlePointerMove,
2614
2337
  handlePointerUp,
2338
+ setScrollingX,
2339
+ setScrollingY,
2615
2340
  scrollingX,
2616
2341
  scrollingY,
2617
2342
  hasMeasuredScrollbar
2618
2343
  } = ScrollAreaViewport.useScrollAreaRootContext();
2619
- const orientation = useScrollAreaScrollbarContext();
2620
- const vertical = orientation === 'vertical';
2344
+ const {
2345
+ orientation
2346
+ } = useScrollAreaScrollbarContext();
2621
2347
  const state = {
2622
- scrolling: vertical ? scrollingY : scrollingX,
2348
+ scrolling: orientation === 'horizontal' ? scrollingX : scrollingY,
2623
2349
  orientation
2624
2350
  };
2351
+ function endDrag(event) {
2352
+ if (orientation === 'vertical') {
2353
+ setScrollingY(false);
2354
+ }
2355
+ if (orientation === 'horizontal') {
2356
+ setScrollingX(false);
2357
+ }
2358
+ handlePointerUp(event);
2359
+ }
2625
2360
  const element = useRenderElement.useRenderElement('div', componentProps, {
2626
- ref: [forwardedRef, vertical ? thumbYRef : thumbXRef],
2361
+ ref: [forwardedRef, orientation === 'vertical' ? thumbYRef : thumbXRef],
2627
2362
  state,
2628
2363
  props: [{
2629
2364
  onPointerDown: handlePointerDown,
2630
2365
  onPointerMove: handlePointerMove,
2631
- onPointerUp: handlePointerUp,
2632
- onPointerCancel: handlePointerUp,
2366
+ onPointerUp: endDrag,
2367
+ onPointerCancel: endDrag,
2633
2368
  style: {
2634
2369
  visibility: hasMeasuredScrollbar ? undefined : 'hidden',
2635
- ...(vertical ? {
2636
- height: 'var(--scroll-area-thumb-height)'
2637
- } : {
2638
- width: 'var(--scroll-area-thumb-width)'
2370
+ ...(orientation === 'vertical' && {
2371
+ height: `var(${ScrollAreaScrollbarCssVars.scrollAreaThumbHeight})`
2372
+ }),
2373
+ ...(orientation === 'horizontal' && {
2374
+ width: `var(${ScrollAreaScrollbarCssVars.scrollAreaThumbWidth})`
2639
2375
  })
2640
2376
  }
2641
2377
  }, elementProps]
@@ -2693,34 +2429,34 @@ var index_parts$3 = /*#__PURE__*/Object.freeze({
2693
2429
 
2694
2430
  var index_parts$2 = /*#__PURE__*/Object.freeze({
2695
2431
  __proto__: null,
2696
- Arrow: ComboboxSeparator.ComboboxArrow,
2697
- Backdrop: ComboboxSeparator.ComboboxBackdrop,
2698
- Chip: ComboboxSeparator.ComboboxChip,
2699
- ChipRemove: ComboboxSeparator.ComboboxChipRemove,
2700
- Chips: ComboboxSeparator.ComboboxChips,
2701
- Clear: ComboboxSeparator.ComboboxClear,
2702
- Collection: ComboboxSeparator.ComboboxCollection,
2703
- Empty: ComboboxSeparator.ComboboxEmpty,
2704
- Group: ComboboxSeparator.ComboboxGroup,
2705
- GroupLabel: ComboboxSeparator.ComboboxGroupLabel,
2706
- Icon: ComboboxSeparator.ComboboxIcon,
2707
- Input: ComboboxSeparator.ComboboxInput,
2708
- InputGroup: ComboboxSeparator.ComboboxInputGroup,
2709
- Item: ComboboxSeparator.ComboboxItem,
2710
- ItemIndicator: ComboboxSeparator.ComboboxItemIndicator,
2711
- Label: ComboboxSeparator.ComboboxLabel,
2712
- List: ComboboxSeparator.ComboboxList,
2713
- Popup: ComboboxSeparator.ComboboxPopup,
2714
- Portal: ComboboxSeparator.ComboboxPortal,
2715
- Positioner: ComboboxSeparator.ComboboxPositioner,
2716
- Root: ComboboxSeparator.ComboboxRoot,
2717
- Row: ComboboxSeparator.ComboboxRow,
2718
- Separator: ComboboxSeparator.ComboboxSeparator,
2719
- Status: ComboboxSeparator.ComboboxStatus,
2720
- Trigger: ComboboxSeparator.ComboboxTrigger,
2721
- Value: ComboboxSeparator.ComboboxValue,
2722
- useFilter: ComboboxSeparator.useComboboxFilter,
2723
- useFilteredItems: ComboboxSeparator.useFilteredItems
2432
+ Arrow: ComboboxChipRemove.ComboboxArrow,
2433
+ Backdrop: ComboboxChipRemove.ComboboxBackdrop,
2434
+ Chip: ComboboxChipRemove.ComboboxChip,
2435
+ ChipRemove: ComboboxChipRemove.ComboboxChipRemove,
2436
+ Chips: ComboboxChipRemove.ComboboxChips,
2437
+ Clear: ComboboxChipRemove.ComboboxClear,
2438
+ Collection: ComboboxChipRemove.ComboboxCollection,
2439
+ Empty: ComboboxChipRemove.ComboboxEmpty,
2440
+ Group: ComboboxChipRemove.ComboboxGroup,
2441
+ GroupLabel: ComboboxChipRemove.ComboboxGroupLabel,
2442
+ Icon: ComboboxChipRemove.ComboboxIcon,
2443
+ Input: ComboboxChipRemove.ComboboxInput,
2444
+ InputGroup: ComboboxChipRemove.ComboboxInputGroup,
2445
+ Item: ComboboxChipRemove.ComboboxItem,
2446
+ ItemIndicator: ComboboxChipRemove.ComboboxItemIndicator,
2447
+ Label: ComboboxChipRemove.ComboboxLabel,
2448
+ List: ComboboxChipRemove.ComboboxList,
2449
+ Popup: ComboboxChipRemove.ComboboxPopup,
2450
+ Portal: ComboboxChipRemove.ComboboxPortal,
2451
+ Positioner: ComboboxChipRemove.ComboboxPositioner,
2452
+ Root: ComboboxChipRemove.ComboboxRoot,
2453
+ Row: ComboboxChipRemove.ComboboxRow,
2454
+ Separator: Separator.Separator,
2455
+ Status: ComboboxChipRemove.ComboboxStatus,
2456
+ Trigger: ComboboxChipRemove.ComboboxTrigger,
2457
+ Value: ComboboxChipRemove.ComboboxValue,
2458
+ useFilter: ComboboxChipRemove.useComboboxFilter,
2459
+ useFilteredItems: ComboboxChipRemove.useFilteredItems
2724
2460
  });
2725
2461
 
2726
2462
  const NumberFieldScrubAreaContext = /*#__PURE__*/React__namespace.createContext(undefined);
@@ -2735,7 +2471,7 @@ function useNumberFieldScrubAreaContext() {
2735
2471
 
2736
2472
  // Calculates the bounds the virtual cursor wraps within, as absolute edge coordinates.
2737
2473
  function getViewportRect(teleportDistance, scrubAreaEl) {
2738
- const win = useScrollLock.getWindow(scrubAreaEl);
2474
+ const win = floatingUi_utils.getWindow(scrubAreaEl);
2739
2475
  if (teleportDistance != null) {
2740
2476
  const rect = scrubAreaEl.getBoundingClientRect();
2741
2477
  return {
@@ -2762,6 +2498,22 @@ function getViewportRect(teleportDistance, scrubAreaEl) {
2762
2498
  };
2763
2499
  }
2764
2500
 
2501
+ // This lets us invert the scale of the cursor to match the OS scale, in which the cursor doesn't
2502
+ // scale with the content on pinch-zoom.
2503
+ function subscribeToVisualViewportResize(element, visualScaleRef) {
2504
+ const vV = floatingUi_utils.getWindow(element).visualViewport;
2505
+ if (!vV) {
2506
+ return () => {};
2507
+ }
2508
+ function handleVisualResize() {
2509
+ if (vV) {
2510
+ visualScaleRef.current = vV.scale;
2511
+ }
2512
+ }
2513
+ handleVisualResize();
2514
+ return useScrollLock.addEventListener(vV, 'resize', handleVisualResize);
2515
+ }
2516
+
2765
2517
  const SCRUB_AREA_STYLE = {
2766
2518
  touchAction: 'none',
2767
2519
  WebkitUserSelect: 'none',
@@ -2787,6 +2539,8 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2787
2539
  const {
2788
2540
  state,
2789
2541
  setIsScrubbing: setRootScrubbing,
2542
+ disabled,
2543
+ readOnly,
2790
2544
  inputRef,
2791
2545
  incrementValue,
2792
2546
  allowInputSyncRef,
@@ -2795,10 +2549,6 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2795
2549
  lastChangedValueRef,
2796
2550
  valueRef
2797
2551
  } = NumberFieldInput.useNumberFieldRootContext();
2798
- const {
2799
- disabled,
2800
- readOnly
2801
- } = state;
2802
2552
  const scrubAreaRef = React__namespace.useRef(null);
2803
2553
  const isScrubbingRef = React__namespace.useRef(false);
2804
2554
  const didMoveRef = React__namespace.useRef(false);
@@ -2808,15 +2558,21 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2808
2558
  x: 0,
2809
2559
  y: 0
2810
2560
  });
2561
+ const visualScaleRef = React__namespace.useRef(1);
2811
2562
  const exitPointerLockTimeout = useScrollLock.useTimeout();
2812
2563
  const [isTouchInput, setIsTouchInput] = React__namespace.useState(false);
2813
2564
  const [isPointerLockDenied, setIsPointerLockDenied] = React__namespace.useState(false);
2814
2565
  const [isScrubbing, setIsScrubbing] = React__namespace.useState(false);
2815
- function updateCursorTransform(virtualCursor, x, y) {
2816
- // Invert the visual viewport scale so the cursor matches the OS cursor, which doesn't
2817
- // scale with the content on pinch-zoom.
2818
- const scale = useScrollLock.getWindow(virtualCursor).visualViewport?.scale ?? 1;
2819
- virtualCursor.style.transform = `translate3d(${x}px,${y}px,0) scale(${1 / scale})`;
2566
+ React__namespace.useEffect(() => {
2567
+ if (!isScrubbing || !scrubAreaCursorRef.current) {
2568
+ return undefined;
2569
+ }
2570
+ return subscribeToVisualViewportResize(scrubAreaCursorRef.current, visualScaleRef);
2571
+ }, [isScrubbing]);
2572
+ function updateCursorTransform(x, y) {
2573
+ if (scrubAreaCursorRef.current) {
2574
+ scrubAreaCursorRef.current.style.transform = `translate3d(${x}px,${y}px,0) scale(${1 / visualScaleRef.current})`;
2575
+ }
2820
2576
  }
2821
2577
  const onScrub = useScrollLock.useStableCallback(({
2822
2578
  movementX,
@@ -2829,23 +2585,24 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2829
2585
  }
2830
2586
  const rect = getViewportRect(teleportDistance, scrubAreaEl);
2831
2587
  const coords = virtualCursorCoords.current;
2832
-
2833
- // Wrap the cursor to the opposite edge when its center crosses a viewport bound.
2834
- const wrap = (coord, halfSize, low, high) => {
2835
- if (coord + halfSize < low) {
2836
- return high - halfSize;
2837
- }
2838
- if (coord + halfSize > high) {
2839
- return low - halfSize;
2840
- }
2841
- return coord;
2842
- };
2843
2588
  const newCoords = {
2844
- x: wrap(Math.round(coords.x + movementX), virtualCursor.offsetWidth / 2, rect.left, rect.right),
2845
- y: wrap(Math.round(coords.y + movementY), virtualCursor.offsetHeight / 2, rect.top, rect.bottom)
2589
+ x: Math.round(coords.x + movementX),
2590
+ y: Math.round(coords.y + movementY)
2846
2591
  };
2592
+ const cursorWidth = virtualCursor.offsetWidth;
2593
+ const cursorHeight = virtualCursor.offsetHeight;
2594
+ if (newCoords.x + cursorWidth / 2 < rect.left) {
2595
+ newCoords.x = rect.right - cursorWidth / 2;
2596
+ } else if (newCoords.x + cursorWidth / 2 > rect.right) {
2597
+ newCoords.x = rect.left - cursorWidth / 2;
2598
+ }
2599
+ if (newCoords.y + cursorHeight / 2 < rect.top) {
2600
+ newCoords.y = rect.bottom - cursorHeight / 2;
2601
+ } else if (newCoords.y + cursorHeight / 2 > rect.bottom) {
2602
+ newCoords.y = rect.top - cursorHeight / 2;
2603
+ }
2847
2604
  virtualCursorCoords.current = newCoords;
2848
- updateCursorTransform(virtualCursor, newCoords.x, newCoords.y);
2605
+ updateCursorTransform(newCoords.x, newCoords.y);
2849
2606
  });
2850
2607
  const onScrubbingChange = useScrollLock.useStableCallback((scrubbingValue, {
2851
2608
  clientX,
@@ -2864,7 +2621,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2864
2621
  y: clientY - virtualCursor.offsetHeight / 2
2865
2622
  };
2866
2623
  virtualCursorCoords.current = initialCoords;
2867
- updateCursorTransform(virtualCursor, initialCoords.x, initialCoords.y);
2624
+ updateCursorTransform(initialCoords.x, initialCoords.y);
2868
2625
  });
2869
2626
  React__namespace.useEffect(function registerGlobalScrubbingEventListeners() {
2870
2627
  // Only listen while actively scrubbing; avoids unrelated pointerup events committing.
@@ -2888,7 +2645,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2888
2645
  const pointerDownTarget = pointerDownTargetRef.current;
2889
2646
  const input = inputRef.current;
2890
2647
  if (!didMoveRef.current && pointerDownTarget != null && input) {
2891
- pointerDownTarget.dispatchEvent(new (useScrollLock.getWindow(input).MouseEvent)('click', {
2648
+ pointerDownTarget.dispatchEvent(new (floatingUi_utils.getWindow(input).MouseEvent)('click', {
2892
2649
  bubbles: true,
2893
2650
  cancelable: true
2894
2651
  }));
@@ -2906,9 +2663,6 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2906
2663
  }
2907
2664
  }
2908
2665
  function handleScrubPointerMove(event) {
2909
- // The effects below can tear down and re-run without unmounting (`<Activity>`), which
2910
- // clears the ref while `isScrubbing` stays `true` and re-attaches this listener. The ref
2911
- // is the source of truth for whether a pointer is actually down.
2912
2666
  if (!isScrubbingRef.current) {
2913
2667
  return;
2914
2668
  }
@@ -2937,7 +2691,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2937
2691
  }
2938
2692
  }
2939
2693
  }
2940
- const win = useScrollLock.getWindow(inputRef.current);
2694
+ const win = floatingUi_utils.getWindow(inputRef.current);
2941
2695
  const unsubscribe = useScrollLock.mergeCleanups(useScrollLock.addEventListener(win, 'pointerup', handleScrubPointerUp, true), useScrollLock.addEventListener(win, 'pointermove', handleScrubPointerMove, true));
2942
2696
  return () => {
2943
2697
  exitPointerLockTimeout.clear();
@@ -2976,7 +2730,8 @@ const NumberFieldScrubArea = /*#__PURE__*/React__namespace.forwardRef(function N
2976
2730
  role: 'presentation',
2977
2731
  style: SCRUB_AREA_STYLE,
2978
2732
  async onPointerDown(event) {
2979
- if (event.defaultPrevented || readOnly || event.button || disabled) {
2733
+ const isMainButton = !event.button || event.button === 0;
2734
+ if (event.defaultPrevented || readOnly || !isMainButton || disabled) {
2980
2735
  return;
2981
2736
  }
2982
2737
  const isTouch = event.pointerType === 'touch';
@@ -3116,8 +2871,7 @@ const SelectLabel = /*#__PURE__*/React__namespace.forwardRef(function SelectLabe
3116
2871
  id: defaultLabelId,
3117
2872
  fallbackControlId: triggerElement?.id ?? rootId,
3118
2873
  setLabelId(nextLabelId) {
3119
- const resolvedLabelId = typeof nextLabelId === 'function' ? nextLabelId(store.state.labelId) : nextLabelId;
3120
- store.set('labelId', resolvedLabelId);
2874
+ store.set('labelId', nextLabelId);
3121
2875
  }
3122
2876
  });
3123
2877
  return useRenderElement.useRenderElement('div', componentProps, {
@@ -3162,7 +2916,7 @@ const SelectIcon = /*#__PURE__*/React__namespace.forwardRef(function SelectIcon(
3162
2916
  });
3163
2917
  if (process.env.NODE_ENV !== "production") SelectIcon.displayName = "SelectIcon";
3164
2918
 
3165
- const stateAttributesMapping = {
2919
+ const stateAttributesMapping$1 = {
3166
2920
  ...useScrollLock.popupStateMapping,
3167
2921
  ...useScrollLock.transitionStatusMapping
3168
2922
  };
@@ -3201,12 +2955,17 @@ const SelectBackdrop = /*#__PURE__*/React__namespace.forwardRef(function SelectB
3201
2955
  WebkitUserSelect: 'none'
3202
2956
  }
3203
2957
  }, elementProps],
3204
- stateAttributesMapping
2958
+ stateAttributesMapping: stateAttributesMapping$1
3205
2959
  });
3206
2960
  return element;
3207
2961
  });
3208
2962
  if (process.env.NODE_ENV !== "production") SelectBackdrop.displayName = "SelectBackdrop";
3209
2963
 
2964
+ const stateAttributesMapping = {
2965
+ ...useScrollLock.popupStateMapping,
2966
+ ...useScrollLock.transitionStatusMapping
2967
+ };
2968
+
3210
2969
  /**
3211
2970
  * Displays an element positioned against the select popup anchor.
3212
2971
  * Renders a `<div>` element.
@@ -3245,7 +3004,7 @@ const SelectArrow = /*#__PURE__*/React__namespace.forwardRef(function SelectArro
3245
3004
  style: arrowStyles,
3246
3005
  'aria-hidden': true
3247
3006
  }, elementProps],
3248
- stateAttributesMapping: useScrollLock.popupTransitionStateMapping
3007
+ stateAttributesMapping
3249
3008
  });
3250
3009
  if (alignItemWithTriggerActive) {
3251
3010
  return null;
@@ -3263,7 +3022,7 @@ const SelectScrollArrow = /*#__PURE__*/React__namespace.forwardRef(function Sele
3263
3022
  className,
3264
3023
  style,
3265
3024
  direction,
3266
- keepMounted,
3025
+ keepMounted = false,
3267
3026
  ...elementProps
3268
3027
  } = componentProps;
3269
3028
  const isUp = direction === 'up';
@@ -3294,10 +3053,12 @@ const SelectScrollArrow = /*#__PURE__*/React__namespace.forwardRef(function Sele
3294
3053
  } = useScrollLock.useTransitionStatus(visible);
3295
3054
  useScrollLock.useIsoLayoutEffect(() => {
3296
3055
  scrollArrowsMountedCountRef.current += 1;
3297
- store.set('hasScrollArrows', true);
3056
+ if (!store.state.hasScrollArrows) {
3057
+ store.set('hasScrollArrows', true);
3058
+ }
3298
3059
  return () => {
3299
3060
  scrollArrowsMountedCountRef.current = Math.max(0, scrollArrowsMountedCountRef.current - 1);
3300
- if (scrollArrowsMountedCountRef.current === 0) {
3061
+ if (scrollArrowsMountedCountRef.current === 0 && store.state.hasScrollArrows) {
3301
3062
  store.set('hasScrollArrows', false);
3302
3063
  }
3303
3064
  };
@@ -3334,7 +3095,7 @@ const SelectScrollArrow = /*#__PURE__*/React__namespace.forwardRef(function Sele
3334
3095
  return;
3335
3096
  }
3336
3097
  store.set('activeIndex', null);
3337
- handleScrollArrowVisibility(scroller);
3098
+ handleScrollArrowVisibility();
3338
3099
  const maxScrollTop = scrollEdges.getMaxScrollOffset(scroller.scrollHeight, scroller.clientHeight);
3339
3100
  const scrollTop = scrollEdges.normalizeScrollOffset(scroller.scrollTop, maxScrollTop);
3340
3101
  const isScrolledToEdge = scrollTop === (isUp ? 0 : maxScrollTop);
@@ -3342,6 +3103,11 @@ const SelectScrollArrow = /*#__PURE__*/React__namespace.forwardRef(function Sele
3342
3103
  if (scrollTop !== scroller.scrollTop) {
3343
3104
  scroller.scrollTop = scrollTop;
3344
3105
  }
3106
+
3107
+ // Fallback when there are no items registered yet.
3108
+ if (items.length === 0) {
3109
+ store.set(isUp ? 'scrollUpArrowVisible' : 'scrollDownArrowVisible', !isScrolledToEdge);
3110
+ }
3345
3111
  if (isScrolledToEdge) {
3346
3112
  timeout.clear();
3347
3113
  return;
@@ -3430,14 +3196,6 @@ const SelectScrollUpArrow = /*#__PURE__*/React__namespace.forwardRef(function Se
3430
3196
  });
3431
3197
  if (process.env.NODE_ENV !== "production") SelectScrollUpArrow.displayName = "SelectScrollUpArrow";
3432
3198
 
3433
- /**
3434
- * A visual separator between items or groups.
3435
- * Renders a `<div>` element.
3436
- *
3437
- * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
3438
- */
3439
- const SelectSeparator = ComboboxSeparator.ListboxSeparator;
3440
-
3441
3199
  var index_parts = /*#__PURE__*/Object.freeze({
3442
3200
  __proto__: null,
3443
3201
  Arrow: SelectArrow,
@@ -3456,7 +3214,7 @@ var index_parts = /*#__PURE__*/Object.freeze({
3456
3214
  Root: SelectGroupLabel.SelectRoot,
3457
3215
  ScrollDownArrow: SelectScrollDownArrow,
3458
3216
  ScrollUpArrow: SelectScrollUpArrow,
3459
- Separator: SelectSeparator,
3217
+ Separator: Separator.Separator,
3460
3218
  Trigger: SelectGroupLabel.SelectTrigger,
3461
3219
  Value: SelectGroupLabel.SelectValue
3462
3220
  });