@jobber/components 8.23.0 → 8.23.1

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 (134) hide show
  1. package/dist/Autocomplete/index.cjs +1 -1
  2. package/dist/Autocomplete/index.mjs +1 -1
  3. package/dist/Card/index.cjs +4 -4
  4. package/dist/Card/index.mjs +4 -4
  5. package/dist/Chip/index.cjs +1 -1
  6. package/dist/Chip/index.mjs +1 -1
  7. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +1 -1
  8. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +1 -1
  9. package/dist/Chips/InternalChipDismissible/index.cjs +1 -1
  10. package/dist/Chips/InternalChipDismissible/index.mjs +1 -1
  11. package/dist/Chips/index.cjs +1 -1
  12. package/dist/Chips/index.mjs +1 -1
  13. package/dist/ComboboxChipRemove-cjs.js +668 -379
  14. package/dist/ComboboxChipRemove-es.js +508 -219
  15. package/dist/ConfirmationModal/index.cjs +1 -1
  16. package/dist/ConfirmationModal/index.mjs +1 -1
  17. package/dist/DataDump/index.cjs +4 -4
  18. package/dist/DataDump/index.mjs +4 -4
  19. package/dist/DataList/components/DataListActions/index.cjs +1 -1
  20. package/dist/DataList/components/DataListActions/index.mjs +1 -1
  21. package/dist/DataList/components/DataListBulkActions/index.cjs +1 -1
  22. package/dist/DataList/components/DataListBulkActions/index.mjs +1 -1
  23. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +1 -1
  24. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +1 -1
  25. package/dist/DataList/components/DataListFilters/index.cjs +1 -1
  26. package/dist/DataList/components/DataListFilters/index.mjs +1 -1
  27. package/dist/DataList/components/DataListHeader/index.cjs +1 -1
  28. package/dist/DataList/components/DataListHeader/index.mjs +1 -1
  29. package/dist/DataList/components/DataListItem/index.cjs +1 -1
  30. package/dist/DataList/components/DataListItem/index.mjs +1 -1
  31. package/dist/DataList/components/DataListItemActions/index.cjs +1 -1
  32. package/dist/DataList/components/DataListItemActions/index.mjs +1 -1
  33. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +1 -1
  34. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +1 -1
  35. package/dist/DataList/components/DataListItems/index.cjs +1 -1
  36. package/dist/DataList/components/DataListItems/index.mjs +1 -1
  37. package/dist/DataList/components/DataListLayout/index.cjs +1 -1
  38. package/dist/DataList/components/DataListLayout/index.mjs +1 -1
  39. package/dist/DataList/components/DataListLayoutActions/index.cjs +1 -1
  40. package/dist/DataList/components/DataListLayoutActions/index.mjs +1 -1
  41. package/dist/DataList/index.cjs +1 -1
  42. package/dist/DataList/index.mjs +1 -1
  43. package/dist/DatePicker/index.cjs +1 -1
  44. package/dist/DatePicker/index.mjs +1 -1
  45. package/dist/Dialog/index.cjs +3 -3
  46. package/dist/Dialog/index.mjs +3 -3
  47. package/dist/DrawerDescription-cjs.js +32 -33
  48. package/dist/DrawerDescription-es.js +19 -20
  49. package/dist/DrawerTitle-cjs.js +7 -5
  50. package/dist/DrawerTitle-es.js +7 -5
  51. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +1 -1
  52. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +1 -1
  53. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +1 -1
  54. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +1 -1
  55. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +1 -1
  56. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +1 -1
  57. package/dist/FilterPicker/index.cjs +1 -1
  58. package/dist/FilterPicker/index.mjs +1 -1
  59. package/dist/FormFieldWrapper-cjs.js +2 -2
  60. package/dist/FormFieldWrapper-es.js +2 -2
  61. package/dist/FormatFile/index.cjs +1 -1
  62. package/dist/FormatFile/index.mjs +1 -1
  63. package/dist/Gallery/index.cjs +1 -1
  64. package/dist/Gallery/index.mjs +1 -1
  65. package/dist/InputDate/index.cjs +1 -1
  66. package/dist/InputDate/index.mjs +1 -1
  67. package/dist/InputNumberExperimental-cjs.js +319 -102
  68. package/dist/InputNumberExperimental-es.js +321 -104
  69. package/dist/LightBox/index.cjs +1 -1
  70. package/dist/LightBox/index.mjs +1 -1
  71. package/dist/Menu/index.cjs +4 -4
  72. package/dist/Menu/index.mjs +4 -4
  73. package/dist/MenuSubmenuTrigger-cjs.js +545 -498
  74. package/dist/MenuSubmenuTrigger-es.js +490 -443
  75. package/dist/Modal/index.cjs +1 -1
  76. package/dist/Modal/index.mjs +1 -1
  77. package/dist/NumberFieldInput-cjs.js +609 -496
  78. package/dist/NumberFieldInput-es.js +612 -498
  79. package/dist/Page/index.cjs +4 -4
  80. package/dist/Page/index.mjs +4 -4
  81. package/dist/Popover/index.cjs +1 -1
  82. package/dist/Popover/index.mjs +1 -1
  83. package/dist/ScrollAreaViewport-cjs.js +326 -601
  84. package/dist/ScrollAreaViewport-es.js +278 -548
  85. package/dist/SelectGroupLabel-cjs.js +494 -528
  86. package/dist/SelectGroupLabel-es.js +411 -445
  87. package/dist/Separator-cjs.js +1 -0
  88. package/dist/Separator-es.js +1 -0
  89. package/dist/Tooltip/index.cjs +1 -1
  90. package/dist/Tooltip/index.mjs +1 -1
  91. package/dist/docs/InputTime/InputTime.md +8 -0
  92. package/dist/docs/SelectPrimitive/SelectPrimitive.md +38 -25
  93. package/dist/floating-ui.react-cjs.js +586 -10
  94. package/dist/floating-ui.react-dom-cjs.js +92 -92
  95. package/dist/floating-ui.react-dom-es.js +1 -1
  96. package/dist/floating-ui.react-es.js +580 -4
  97. package/dist/floating-ui.utils-cjs.js +159 -0
  98. package/dist/floating-ui.utils-es.js +136 -0
  99. package/dist/index.cjs +4 -4
  100. package/dist/index.mjs +4 -4
  101. package/dist/{ToolbarRootContext-cjs.js → isElementDisabled-cjs.js} +5 -0
  102. package/dist/{ToolbarRootContext-es.js → isElementDisabled-es.js} +5 -1
  103. package/dist/primitives/BottomSheet/index.cjs +3 -3
  104. package/dist/primitives/BottomSheet/index.mjs +3 -3
  105. package/dist/primitives/ComboboxPrimitive/index.cjs +2 -2
  106. package/dist/primitives/ComboboxPrimitive/index.mjs +2 -2
  107. package/dist/primitives/InputNumberExperimental/index.cjs +1 -1
  108. package/dist/primitives/InputNumberExperimental/index.mjs +1 -1
  109. package/dist/primitives/SelectPrimitive/index.cjs +5 -5
  110. package/dist/primitives/SelectPrimitive/index.mjs +5 -5
  111. package/dist/primitives/index.cjs +4 -4
  112. package/dist/primitives/index.mjs +4 -4
  113. package/dist/resolveAriaLabelledBy-cjs.js +5 -0
  114. package/dist/resolveAriaLabelledBy-es.js +5 -1
  115. package/dist/{CSPContext-cjs.js → scrollEdges-cjs.js} +30 -2
  116. package/dist/{CSPContext-es.js → scrollEdges-es.js} +28 -3
  117. package/dist/styles.css +9 -3
  118. package/dist/unstyledPrimitives/index.cjs +1217 -639
  119. package/dist/unstyledPrimitives/index.mjs +1163 -585
  120. package/dist/useButton-cjs.js +283 -317
  121. package/dist/useButton-es.js +277 -298
  122. package/dist/useCompositeListItem-cjs.js +328 -279
  123. package/dist/useCompositeListItem-es.js +293 -246
  124. package/dist/useLabel-cjs.js +11 -1
  125. package/dist/useLabel-es.js +11 -2
  126. package/dist/useLabelableId-cjs.js +37 -91
  127. package/dist/useLabelableId-es.js +37 -90
  128. package/dist/useRenderElement-cjs.js +81 -26
  129. package/dist/useRenderElement-es.js +81 -27
  130. package/dist/{InternalBackdrop-cjs.js → useScrollLock-cjs.js} +1286 -1220
  131. package/dist/{InternalBackdrop-es.js → useScrollLock-es.js} +1250 -1207
  132. package/package.json +3 -3
  133. package/dist/index.esm-cjs.js +0 -738
  134. package/dist/index.esm-es.js +0 -712
@@ -2,9 +2,9 @@
2
2
 
3
3
  var React = require('react');
4
4
  var useRenderElement = require('./useRenderElement-cjs.js');
5
- var InternalBackdrop = require('./InternalBackdrop-cjs.js');
5
+ var useScrollLock = require('./useScrollLock-cjs.js');
6
6
  var useButton = require('./useButton-cjs.js');
7
- var CSPContext = require('./CSPContext-cjs.js');
7
+ var scrollEdges = require('./scrollEdges-cjs.js');
8
8
  var clamp = require('./clamp-cjs.js');
9
9
  var jsxRuntime = require('react/jsx-runtime');
10
10
  var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
@@ -28,170 +28,16 @@ function _interopNamespaceDefault(e) {
28
28
 
29
29
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
30
30
 
31
- /**
32
- * Returns a callback ref that registers/unregisters the trigger element in the store.
33
- *
34
- * @param store The Store instance where the trigger should be registered.
35
- */
36
- function useTriggerRegistration(id, store) {
37
- // Keep track of the currently registered element to unregister it on unmount or id change.
38
- const registeredElementIdRef = React__namespace.useRef(null);
39
- const registeredElementRef = React__namespace.useRef(null);
40
- return React__namespace.useCallback(element => {
41
- if (id === undefined) {
42
- return;
43
- }
44
- if (registeredElementIdRef.current !== null) {
45
- const registeredId = registeredElementIdRef.current;
46
- const registeredElement = registeredElementRef.current;
47
- const currentElement = store.context.triggerElements.getById(registeredId);
48
- if (registeredElement && currentElement === registeredElement) {
49
- store.context.triggerElements.delete(registeredId);
50
- }
51
- registeredElementIdRef.current = null;
52
- registeredElementRef.current = null;
53
- }
54
- if (element !== null) {
55
- registeredElementIdRef.current = id;
56
- registeredElementRef.current = element;
57
- store.context.triggerElements.add(id, element);
58
- }
59
- }, [store, id]);
60
- }
61
-
62
- /**
63
- * Sets up trigger data forwarding to the store.
64
- *
65
- * @param triggerId Id of the trigger.
66
- * @param triggerElement The trigger DOM element.
67
- * @param store The Store instance managing the popup state.
68
- * @param stateUpdates An object with state updates to apply when the trigger is active.
69
- */
70
- function useTriggerDataForwarding(triggerId, triggerElementRef, store, stateUpdates) {
71
- const isMountedByThisTrigger = store.useState('isMountedByTrigger', triggerId);
72
- const baseRegisterTrigger = useTriggerRegistration(triggerId, store);
73
- const registerTrigger = useButton.useStableCallback(element => {
74
- baseRegisterTrigger(element);
75
- if (!element || !store.select('open')) {
76
- return;
77
- }
78
- const activeTriggerId = store.select('activeTriggerId');
79
- if (activeTriggerId === triggerId) {
80
- store.update({
81
- activeTriggerElement: element,
82
- ...stateUpdates
83
- });
84
- return;
85
- }
86
- if (activeTriggerId == null) {
87
- // This runs when popup is open, but no active trigger is set.
88
- // It can happen when using controlled mode and the trigger is mounted after opening or if `triggerId` prop is not set explicitly.
89
- // In such cases the first trigger to run this code becomes the active trigger (store.select('activeTriggerId') should not return null after that).
90
- // This is mostly for compatibility with contained triggers where no explicit `triggerId` was required in controlled mode.
91
- store.update({
92
- activeTriggerId: triggerId,
93
- activeTriggerElement: element,
94
- ...stateUpdates
95
- });
96
- }
97
- });
98
- useButton.useIsoLayoutEffect(() => {
99
- if (isMountedByThisTrigger) {
100
- store.update({
101
- activeTriggerElement: triggerElementRef.current,
102
- ...stateUpdates
103
- });
104
- }
105
- // eslint-disable-next-line react-hooks/exhaustive-deps
106
- }, [isMountedByThisTrigger, store, triggerElementRef, ...Object.values(stateUpdates)]);
107
- return {
108
- registerTrigger,
109
- isMountedByThisTrigger
110
- };
111
- }
112
- /**
113
- * Ensures that when there's only one trigger element registered, it is set as the active trigger.
114
- * This allows controlled popups to work correctly without an explicit triggerId, maintaining compatibility
115
- * with the contained triggers.
116
- *
117
- * This should be called on the Root part.
118
- *
119
- * @param open Whether the popup is open.
120
- * @param store The Store instance managing the popup state.
121
- */
122
- function useImplicitActiveTrigger(store) {
123
- const open = store.useState('open');
124
- useButton.useIsoLayoutEffect(() => {
125
- if (open && !store.select('activeTriggerId') && store.context.triggerElements.size === 1) {
126
- const iteratorResult = store.context.triggerElements.entries().next();
127
- if (!iteratorResult.done) {
128
- const [implicitTriggerId, implicitTriggerElement] = iteratorResult.value;
129
- store.update({
130
- activeTriggerId: implicitTriggerId,
131
- activeTriggerElement: implicitTriggerElement
132
- });
133
- }
134
- }
135
- }, [open, store]);
136
- }
137
-
138
- /**
139
- * Mangages the mounted state of the popup.
140
- * Sets up the transition status listeners and handles unmounting when needed.
141
- * Updates the `mounted` and `transitionStatus` states in the store.
142
- *
143
- * @param open Whether the popup is open.
144
- * @param store The Store instance managing the popup state.
145
- * @param onUnmount Optional callback to be called when the popup is unmounted.
146
- *
147
- * @returns A function to forcibly unmount the popup.
148
- */
149
- function useOpenStateTransitions(open, store, onUnmount) {
150
- const {
151
- mounted,
152
- setMounted,
153
- transitionStatus
154
- } = useButton.useTransitionStatus(open);
155
- store.useSyncedValues({
156
- mounted,
157
- transitionStatus
158
- });
159
- const forceUnmount = useButton.useStableCallback(() => {
160
- setMounted(false);
161
- store.update({
162
- activeTriggerId: null,
163
- activeTriggerElement: null,
164
- mounted: false
165
- });
166
- onUnmount?.();
167
- store.context.onOpenChangeComplete?.(false);
168
- });
169
- const preventUnmountingOnClose = store.useState('preventUnmountingOnClose');
170
- useButton.useOpenChangeComplete({
171
- enabled: !preventUnmountingOnClose,
172
- open,
173
- ref: store.context.popupRef,
174
- onComplete() {
175
- if (!open) {
176
- forceUnmount();
177
- }
178
- }
179
- });
180
- return {
181
- forceUnmount,
182
- transitionStatus
183
- };
184
- }
185
-
186
31
  function getEmptyRootContext() {
187
- return new InternalBackdrop.FloatingRootStore({
32
+ return new useScrollLock.FloatingRootStore({
188
33
  open: false,
34
+ transitionStatus: undefined,
189
35
  floatingElement: null,
190
36
  referenceElement: null,
191
- triggerElements: new InternalBackdrop.PopupTriggerMap(),
192
- floatingId: '',
37
+ triggerElements: new useScrollLock.PopupTriggerMap(),
38
+ floatingId: undefined,
39
+ syncOnly: false,
193
40
  nested: false,
194
- noEmit: false,
195
41
  onOpenChange: undefined
196
42
  });
197
43
  }
@@ -205,8 +51,10 @@ function createInitialPopupStoreState() {
205
51
  open: false,
206
52
  openProp: undefined,
207
53
  mounted: false,
208
- transitionStatus: 'idle',
54
+ transitionStatus: undefined,
209
55
  floatingRootContext: getEmptyRootContext(),
56
+ floatingId: undefined,
57
+ triggerCount: 0,
210
58
  preventUnmountingOnClose: false,
211
59
  payload: undefined,
212
60
  activeTriggerId: null,
@@ -219,192 +67,69 @@ function createInitialPopupStoreState() {
219
67
  popupProps: useRenderElement.EMPTY_OBJECT
220
68
  };
221
69
  }
222
- const activeTriggerIdSelector = InternalBackdrop.createSelector(state => state.triggerIdProp ?? state.activeTriggerId);
70
+ function createPopupFloatingRootContext(triggerElements, floatingId, nested = false) {
71
+ return new useScrollLock.FloatingRootStore({
72
+ open: false,
73
+ transitionStatus: undefined,
74
+ floatingElement: null,
75
+ referenceElement: null,
76
+ triggerElements,
77
+ floatingId,
78
+ syncOnly: true,
79
+ nested,
80
+ onOpenChange: undefined
81
+ });
82
+ }
83
+ const activeTriggerIdSelector = useScrollLock.createSelector(state => state.triggerIdProp ?? state.activeTriggerId);
84
+ const openSelector = useScrollLock.createSelector(state => state.openProp ?? state.open);
85
+ const popupIdSelector = useScrollLock.createSelector(state => {
86
+ const popupId = state.popupElement?.id ?? state.floatingId;
87
+ return popupId || undefined;
88
+ });
89
+ function triggerOwnsOpenPopup(state, triggerId) {
90
+ return triggerId !== undefined && openSelector(state) && activeTriggerIdSelector(state) === triggerId;
91
+ }
92
+ function triggerOwnsOpenPopupOrIsOnlyTrigger(state, triggerId) {
93
+ if (triggerOwnsOpenPopup(state, triggerId)) {
94
+ return true;
95
+ }
96
+ return triggerId !== undefined && openSelector(state) && activeTriggerIdSelector(state) == null && state.triggerCount === 1;
97
+ }
223
98
  const popupStoreSelectors = {
224
- open: InternalBackdrop.createSelector(state => state.openProp ?? state.open),
225
- mounted: InternalBackdrop.createSelector(state => state.mounted),
226
- transitionStatus: InternalBackdrop.createSelector(state => state.transitionStatus),
227
- floatingRootContext: InternalBackdrop.createSelector(state => state.floatingRootContext),
228
- preventUnmountingOnClose: InternalBackdrop.createSelector(state => state.preventUnmountingOnClose),
229
- payload: InternalBackdrop.createSelector(state => state.payload),
99
+ open: openSelector,
100
+ mounted: useScrollLock.createSelector(state => state.mounted),
101
+ transitionStatus: useScrollLock.createSelector(state => state.transitionStatus),
102
+ floatingRootContext: useScrollLock.createSelector(state => state.floatingRootContext),
103
+ triggerCount: useScrollLock.createSelector(state => state.triggerCount),
104
+ preventUnmountingOnClose: useScrollLock.createSelector(state => state.preventUnmountingOnClose),
105
+ payload: useScrollLock.createSelector(state => state.payload),
230
106
  activeTriggerId: activeTriggerIdSelector,
231
- activeTriggerElement: InternalBackdrop.createSelector(state => state.mounted ? state.activeTriggerElement : null),
107
+ activeTriggerElement: useScrollLock.createSelector(state => state.mounted ? state.activeTriggerElement : null),
108
+ popupId: popupIdSelector,
232
109
  /**
233
110
  * Whether the trigger with the given ID was used to open the popup.
234
111
  */
235
- isTriggerActive: InternalBackdrop.createSelector((state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId),
112
+ isTriggerActive: useScrollLock.createSelector((state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId),
236
113
  /**
237
114
  * Whether the popup is open and was activated by a trigger with the given ID.
238
115
  */
239
- isOpenedByTrigger: InternalBackdrop.createSelector((state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId && state.open),
116
+ isOpenedByTrigger: useScrollLock.createSelector((state, triggerId) => triggerOwnsOpenPopup(state, triggerId)),
240
117
  /**
241
118
  * Whether the popup is mounted and was activated by a trigger with the given ID.
242
119
  */
243
- isMountedByTrigger: InternalBackdrop.createSelector((state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId && state.mounted),
244
- triggerProps: InternalBackdrop.createSelector((state, isActive) => isActive ? state.activeTriggerProps : state.inactiveTriggerProps),
245
- popupProps: InternalBackdrop.createSelector(state => state.popupProps),
246
- popupElement: InternalBackdrop.createSelector(state => state.popupElement),
247
- positionerElement: InternalBackdrop.createSelector(state => state.positionerElement)
120
+ isMountedByTrigger: useScrollLock.createSelector((state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId && state.mounted),
121
+ triggerProps: useScrollLock.createSelector((state, isActive) => isActive ? state.activeTriggerProps : state.inactiveTriggerProps),
122
+ /**
123
+ * Popup id for the trigger that currently owns the open popup.
124
+ */
125
+ triggerPopupId: useScrollLock.createSelector((state, triggerId) => triggerOwnsOpenPopupOrIsOnlyTrigger(state, triggerId) ? popupIdSelector(state) : undefined),
126
+ popupProps: useScrollLock.createSelector(state => state.popupProps),
127
+ popupElement: useScrollLock.createSelector(state => state.popupElement),
128
+ positionerElement: useScrollLock.createSelector(state => state.positionerElement)
248
129
  };
249
130
 
250
- /**
251
- * Initializes a FloatingRootStore that is kept in sync with the provided PopupStore.
252
- * The new instance is created only once and updated on every render.
253
- */
254
- function useSyncedFloatingRootContext(options) {
255
- const {
256
- popupStore,
257
- noEmit = false,
258
- treatPopupAsFloatingElement = false,
259
- onOpenChange
260
- } = options;
261
- const floatingId = useButton.useId();
262
- const nested = InternalBackdrop.useFloatingParentNodeId() != null;
263
- const open = popupStore.useState('open');
264
- const referenceElement = popupStore.useState('activeTriggerElement');
265
- const floatingElement = popupStore.useState(treatPopupAsFloatingElement ? 'popupElement' : 'positionerElement');
266
- const triggerElements = popupStore.context.triggerElements;
267
- const store = useRenderElement.useRefWithInit(() => new InternalBackdrop.FloatingRootStore({
268
- open,
269
- referenceElement,
270
- floatingElement,
271
- triggerElements,
272
- onOpenChange,
273
- floatingId,
274
- nested,
275
- noEmit
276
- })).current;
277
- useButton.useIsoLayoutEffect(() => {
278
- const valuesToSync = {
279
- open,
280
- floatingId,
281
- referenceElement,
282
- floatingElement
283
- };
284
- if (floatingUi_utils_dom.isElement(referenceElement)) {
285
- valuesToSync.domReferenceElement = referenceElement;
286
- }
287
- if (store.state.positionReference === store.state.referenceElement) {
288
- valuesToSync.positionReference = referenceElement;
289
- }
290
- store.update(valuesToSync);
291
- }, [open, floatingId, referenceElement, floatingElement, store]);
292
-
293
- // TODO: When `setOpen` is a part of the PopupStore API, we don't need to sync it.
294
- store.context.onOpenChange = onOpenChange;
295
- store.context.nested = nested;
296
- store.context.noEmit = noEmit;
297
- return store;
298
- }
299
-
300
- const componentRoleToAriaRoleMap = new Map([['select', 'listbox'], ['combobox', 'listbox'], ['label', false]]);
301
-
302
- /**
303
- * Adds base screen reader props to the reference and floating elements for a
304
- * given floating element `role`.
305
- * @see https://floating-ui.com/docs/useRole
306
- */
307
- function useRole(context, props = {}) {
308
- const store = 'rootStore' in context ? context.rootStore : context;
309
- const open = store.useState('open');
310
- const defaultFloatingId = store.useState('floatingId');
311
- const domReference = store.useState('domReferenceElement');
312
- const floatingElement = store.useState('floatingElement');
313
- const {
314
- role = 'dialog'
315
- } = props;
316
- const defaultReferenceId = useButton.useId();
317
- const referenceId = domReference?.id || defaultReferenceId;
318
- const floatingId = React__namespace.useMemo(() => useButton.getFloatingFocusElement(floatingElement)?.id || defaultFloatingId, [floatingElement, defaultFloatingId]);
319
- const ariaRole = componentRoleToAriaRoleMap.get(role) ?? role;
320
- const parentId = InternalBackdrop.useFloatingParentNodeId();
321
- const isNested = parentId != null;
322
- const trigger = React__namespace.useMemo(() => {
323
- if (ariaRole === 'tooltip' || role === 'label') {
324
- return useRenderElement.EMPTY_OBJECT;
325
- }
326
- return {
327
- 'aria-haspopup': ariaRole === 'alertdialog' ? 'dialog' : ariaRole,
328
- 'aria-expanded': 'false',
329
- ...(ariaRole === 'listbox' && {
330
- role: 'combobox'
331
- }),
332
- ...(ariaRole === 'menu' && isNested && {
333
- role: 'menuitem'
334
- }),
335
- ...(role === 'select' && {
336
- 'aria-autocomplete': 'none'
337
- }),
338
- ...(role === 'combobox' && {
339
- 'aria-autocomplete': 'list'
340
- })
341
- };
342
- }, [ariaRole, isNested, role]);
343
- const reference = React__namespace.useMemo(() => {
344
- if (ariaRole === 'tooltip' || role === 'label') {
345
- return {
346
- [`aria-${role === 'label' ? 'labelledby' : 'describedby'}`]: open ? floatingId : undefined
347
- };
348
- }
349
- const triggerProps = trigger;
350
- return {
351
- ...triggerProps,
352
- 'aria-expanded': open ? 'true' : 'false',
353
- 'aria-controls': open ? floatingId : undefined,
354
- ...(ariaRole === 'menu' && {
355
- id: referenceId
356
- })
357
- };
358
- }, [ariaRole, floatingId, open, referenceId, role, trigger]);
359
- const floating = React__namespace.useMemo(() => {
360
- const floatingProps = {
361
- id: floatingId,
362
- ...(ariaRole && {
363
- role: ariaRole
364
- })
365
- };
366
- if (ariaRole === 'tooltip' || role === 'label') {
367
- return floatingProps;
368
- }
369
- return {
370
- ...floatingProps,
371
- ...(ariaRole === 'menu' && {
372
- 'aria-labelledby': referenceId
373
- })
374
- };
375
- }, [ariaRole, floatingId, referenceId, role]);
376
- const item = React__namespace.useCallback(({
377
- active,
378
- selected
379
- }) => {
380
- const commonProps = {
381
- role: 'option',
382
- ...(active && {
383
- id: `${floatingId}-fui-option`
384
- })
385
- };
386
-
387
- // For `menu`, we are unable to tell if the item is a `menuitemradio`
388
- // or `menuitemcheckbox`. For backwards-compatibility reasons, also
389
- // avoid defaulting to `menuitem` as it may overwrite custom role props.
390
- switch (role) {
391
- case 'select':
392
- case 'combobox':
393
- return {
394
- ...commonProps,
395
- 'aria-selected': selected
396
- };
397
- }
398
- return {};
399
- }, [floatingId, role]);
400
- return React__namespace.useMemo(() => ({
401
- reference,
402
- floating,
403
- item,
404
- trigger
405
- }), [reference, floating, trigger, item]);
406
- }
407
-
131
+ const IsDrawerContext = /*#__PURE__*/React__namespace.createContext(false);
132
+ if (process.env.NODE_ENV !== "production") IsDrawerContext.displayName = "IsDrawerContext";
408
133
  const DialogRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
409
134
  if (process.env.NODE_ENV !== "production") DialogRootContext.displayName = "DialogRootContext";
410
135
  function useDialogRootContext(optional) {
@@ -442,12 +167,12 @@ const DialogPortal = /*#__PURE__*/React__namespace.forwardRef(function DialogPor
442
167
  }
443
168
  return /*#__PURE__*/jsxRuntime.jsx(DialogPortalContext.Provider, {
444
169
  value: keepMounted,
445
- children: /*#__PURE__*/jsxRuntime.jsxs(InternalBackdrop.FloatingPortal, {
170
+ children: /*#__PURE__*/jsxRuntime.jsxs(useScrollLock.FloatingPortal, {
446
171
  ref: forwardedRef,
447
172
  ...portalProps,
448
- children: [mounted && modal === true && /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.InternalBackdrop, {
173
+ children: [mounted && modal === true && /*#__PURE__*/jsxRuntime.jsx(useScrollLock.InternalBackdrop, {
449
174
  ref: store.context.internalBackdropRef,
450
- inert: InternalBackdrop.inertValue(!open)
175
+ inert: useScrollLock.inertValue(!open)
451
176
  }), props.children]
452
177
  })
453
178
  });
@@ -457,45 +182,36 @@ if (process.env.NODE_ENV !== "production") DialogPortal.displayName = "DialogPor
457
182
  function useDialogRoot(params) {
458
183
  const {
459
184
  store,
460
- parentContext,
461
185
  actionsRef
462
186
  } = params;
463
187
  const open = store.useState('open');
464
- const disablePointerDismissal = store.useState('disablePointerDismissal');
465
- const modal = store.useState('modal');
466
- const popupElement = store.useState('popupElement');
467
- const {
468
- openMethod,
469
- triggerProps
470
- } = InternalBackdrop.useOpenInteractionType(open);
471
- useImplicitActiveTrigger(store);
188
+ useScrollLock.usePopupRootSync(store, open);
189
+ useScrollLock.useImplicitActiveTrigger(store);
472
190
  const {
473
191
  forceUnmount
474
- } = useOpenStateTransitions(open, store);
475
- const createDialogEventDetails = useButton.useStableCallback(reason => {
476
- const details = useButton.createChangeEventDetails(reason);
477
- details.preventUnmountOnClose = () => {
478
- store.set('preventUnmountingOnClose', true);
479
- };
480
- return details;
481
- });
192
+ } = useScrollLock.useOpenStateTransitions(open, store);
482
193
  const handleImperativeClose = React__namespace.useCallback(() => {
483
- store.setOpen(false, createDialogEventDetails(useButton.imperativeAction));
484
- }, [store, createDialogEventDetails]);
194
+ store.setOpen(false, useButton.createChangeEventDetails(useButton.imperativeAction));
195
+ }, [store]);
485
196
  React__namespace.useImperativeHandle(actionsRef, () => ({
486
197
  unmount: forceUnmount,
487
198
  close: handleImperativeClose
488
199
  }), [forceUnmount, handleImperativeClose]);
489
- const floatingRootContext = useSyncedFloatingRootContext({
490
- popupStore: store,
491
- onOpenChange: store.setOpen,
492
- treatPopupAsFloatingElement: true,
493
- noEmit: true
494
- });
200
+ }
201
+ function DialogInteractions({
202
+ store,
203
+ parentContext,
204
+ isDrawer
205
+ }) {
206
+ const open = store.useState('open');
207
+ const disablePointerDismissal = store.useState('disablePointerDismissal');
208
+ const modal = store.useState('modal');
209
+ const popupElement = store.useState('popupElement');
210
+ const floatingRootContext = store.useState('floatingRootContext');
495
211
  const [ownNestedOpenDialogs, setOwnNestedOpenDialogs] = React__namespace.useState(0);
212
+ const [ownNestedOpenDrawers, setOwnNestedOpenDrawers] = React__namespace.useState(0);
496
213
  const isTopmost = ownNestedOpenDialogs === 0;
497
- const role = useRole(floatingRootContext);
498
- const dismiss = InternalBackdrop.useDismiss(floatingRootContext, {
214
+ const dismiss = useScrollLock.useDismiss(floatingRootContext, {
499
215
  outsidePressEvent() {
500
216
  if (store.context.internalBackdropRef.current || store.context.backdropRef.current) {
501
217
  return 'intentional';
@@ -522,12 +238,11 @@ function useDialogRoot(params) {
522
238
  }
523
239
  const target = useButton.getTarget(event);
524
240
  if (isTopmost && !disablePointerDismissal) {
525
- const eventTarget = target;
526
241
  // Only close if the click occurred on the dialog's owning backdrop.
527
242
  // This supports multiple modal dialogs that aren't nested in the React tree:
528
243
  // https://github.com/mui/base-ui/issues/1320
529
244
  if (modal) {
530
- return store.context.internalBackdropRef.current || store.context.backdropRef.current ? store.context.internalBackdropRef.current === eventTarget || store.context.backdropRef.current === eventTarget || useButton.contains(eventTarget, popupElement) && !eventTarget?.hasAttribute('data-base-ui-portal') : true;
245
+ return store.context.internalBackdropRef.current || store.context.backdropRef.current ? store.context.internalBackdropRef.current === target || store.context.backdropRef.current === target || useButton.contains(target, popupElement) && !target?.hasAttribute('data-base-ui-portal') : true;
531
246
  }
532
247
  return true;
533
248
  }
@@ -535,25 +250,22 @@ function useDialogRoot(params) {
535
250
  },
536
251
  escapeKey: isTopmost
537
252
  });
538
- InternalBackdrop.useScrollLock(open && modal === true, popupElement);
539
- const {
540
- getReferenceProps,
541
- getFloatingProps,
542
- getTriggerProps
543
- } = InternalBackdrop.useInteractions([role, dismiss]);
253
+ useScrollLock.useScrollLock(open && modal === true, popupElement);
544
254
 
545
- // Listen for nested open/close events on this store to maintain the count
546
- store.useContextCallback('onNestedDialogOpen', ownChildrenCount => {
547
- setOwnNestedOpenDialogs(ownChildrenCount + 1);
255
+ // Listen for nested open/close events on this store to maintain the counts.
256
+ store.useContextCallback('onNestedDialogOpen', (dialogCount, drawerCount) => {
257
+ setOwnNestedOpenDialogs(dialogCount);
258
+ setOwnNestedOpenDrawers(drawerCount);
548
259
  });
549
260
  store.useContextCallback('onNestedDialogClose', () => {
550
261
  setOwnNestedOpenDialogs(0);
262
+ setOwnNestedOpenDrawers(0);
551
263
  });
552
264
 
553
265
  // Notify parent of our open/close state using parent callbacks, if any
554
266
  React__namespace.useEffect(() => {
555
267
  if (parentContext?.onNestedDialogOpen && open) {
556
- parentContext.onNestedDialogOpen(ownNestedOpenDialogs);
268
+ parentContext.onNestedDialogOpen(ownNestedOpenDialogs + 1, ownNestedOpenDrawers + (isDrawer ? 1 : 0));
557
269
  }
558
270
  if (parentContext?.onNestedDialogClose && !open) {
559
271
  parentContext.onNestedDialogClose();
@@ -563,42 +275,48 @@ function useDialogRoot(params) {
563
275
  parentContext.onNestedDialogClose();
564
276
  }
565
277
  };
566
- }, [open, parentContext, ownNestedOpenDialogs]);
567
- const activeTriggerProps = React__namespace.useMemo(() => getReferenceProps(triggerProps), [getReferenceProps, triggerProps]);
568
- const inactiveTriggerProps = React__namespace.useMemo(() => getTriggerProps(triggerProps), [getTriggerProps, triggerProps]);
569
- const popupProps = React__namespace.useMemo(() => getFloatingProps(), [getFloatingProps]);
570
- store.useSyncedValues({
571
- openMethod,
278
+ }, [isDrawer, open, ownNestedOpenDialogs, ownNestedOpenDrawers, parentContext]);
279
+ const activeTriggerProps = dismiss.reference ?? useRenderElement.EMPTY_OBJECT;
280
+ const inactiveTriggerProps = dismiss.trigger ?? useRenderElement.EMPTY_OBJECT;
281
+ // Consumers (DialogPopup/DrawerPopup) already spread `FOCUSABLE_POPUP_PROPS`
282
+ // directly, so the popup props only need to carry the dismiss handlers.
283
+ const popupProps = dismiss.floating ?? useRenderElement.EMPTY_OBJECT;
284
+ useScrollLock.usePopupInteractionProps(store, {
572
285
  activeTriggerProps,
573
286
  inactiveTriggerProps,
574
287
  popupProps,
575
- floatingRootContext,
576
- nestedOpenDialogCount: ownNestedOpenDialogs
288
+ nestedOpenDialogCount: ownNestedOpenDialogs,
289
+ nestedOpenDrawerCount: ownNestedOpenDrawers
577
290
  });
291
+ return null;
578
292
  }
579
293
 
580
294
  const selectors = {
581
295
  ...popupStoreSelectors,
582
- modal: InternalBackdrop.createSelector(state => state.modal),
583
- nested: InternalBackdrop.createSelector(state => state.nested),
584
- nestedOpenDialogCount: InternalBackdrop.createSelector(state => state.nestedOpenDialogCount),
585
- disablePointerDismissal: InternalBackdrop.createSelector(state => state.disablePointerDismissal),
586
- openMethod: InternalBackdrop.createSelector(state => state.openMethod),
587
- descriptionElementId: InternalBackdrop.createSelector(state => state.descriptionElementId),
588
- titleElementId: InternalBackdrop.createSelector(state => state.titleElementId),
589
- viewportElement: InternalBackdrop.createSelector(state => state.viewportElement),
590
- role: InternalBackdrop.createSelector(state => state.role)
296
+ modal: useScrollLock.createSelector(state => state.modal),
297
+ nested: useScrollLock.createSelector(state => state.nested),
298
+ nestedOpenDialogCount: useScrollLock.createSelector(state => state.nestedOpenDialogCount),
299
+ nestedOpenDrawerCount: useScrollLock.createSelector(state => state.nestedOpenDrawerCount),
300
+ disablePointerDismissal: useScrollLock.createSelector(state => state.disablePointerDismissal),
301
+ openMethod: useScrollLock.createSelector(state => state.openMethod),
302
+ descriptionElementId: useScrollLock.createSelector(state => state.descriptionElementId),
303
+ titleElementId: useScrollLock.createSelector(state => state.titleElementId),
304
+ viewportElement: useScrollLock.createSelector(state => state.viewportElement),
305
+ role: useScrollLock.createSelector(state => state.role)
591
306
  };
592
- class DialogStore extends InternalBackdrop.ReactStore {
593
- constructor(initialState) {
594
- super(createInitialState(initialState), {
307
+ class DialogStore extends useScrollLock.ReactStore {
308
+ constructor(initialState, floatingId, nested = false) {
309
+ const triggerElements = new useScrollLock.PopupTriggerMap();
310
+ const state = createInitialState(initialState);
311
+ state.floatingRootContext = createPopupFloatingRootContext(triggerElements, floatingId, nested);
312
+ super(state, {
595
313
  popupRef: /*#__PURE__*/React__namespace.createRef(),
596
314
  backdropRef: /*#__PURE__*/React__namespace.createRef(),
597
315
  internalBackdropRef: /*#__PURE__*/React__namespace.createRef(),
598
316
  outsidePressEnabledRef: {
599
317
  current: true
600
318
  },
601
- triggerElements: new InternalBackdrop.PopupTriggerMap(),
319
+ triggerElements,
602
320
  onOpenChange: undefined,
603
321
  onOpenChangeComplete: undefined
604
322
  }, selectors);
@@ -616,26 +334,20 @@ class DialogStore extends InternalBackdrop.ReactStore {
616
334
  if (eventDetails.isCanceled) {
617
335
  return;
618
336
  }
619
- const details = {
620
- open: nextOpen,
621
- nativeEvent: eventDetails.event,
622
- reason: eventDetails.reason,
623
- nested: this.state.nested
624
- };
625
- this.state.floatingRootContext.context.events?.emit('openchange', details);
337
+ this.state.floatingRootContext.dispatchOpenChange(nextOpen, eventDetails);
626
338
  const updatedState = {
627
339
  open: nextOpen
628
340
  };
629
-
630
- // If a popup is closing, the `trigger` may be null.
631
- // We want to keep the previous value so that exit animations are played and focus is returned correctly.
632
- const newTriggerId = eventDetails.trigger?.id ?? null;
633
- if (newTriggerId || nextOpen) {
634
- updatedState.activeTriggerId = newTriggerId;
635
- updatedState.activeTriggerElement = eventDetails.trigger ?? null;
636
- }
341
+ useScrollLock.setPopupOpenState(updatedState, nextOpen, eventDetails.trigger);
637
342
  this.update(updatedState);
638
343
  };
344
+ static useStore(externalStore, initialState) {
345
+ /* eslint-disable react-hooks/rules-of-hooks */
346
+ const store = useScrollLock.usePopupStore(externalStore, (floatingId, nested) => new DialogStore(initialState, floatingId, nested), true).store;
347
+ /* eslint-enable react-hooks/rules-of-hooks */
348
+
349
+ return store;
350
+ }
639
351
  }
640
352
  function createInitialState(initialState = {}) {
641
353
  return {
@@ -649,82 +361,105 @@ function createInitialState(initialState = {}) {
649
361
  openMethod: null,
650
362
  nested: false,
651
363
  nestedOpenDialogCount: 0,
364
+ nestedOpenDrawerCount: 0,
652
365
  role: 'dialog',
653
366
  ...initialState
654
367
  };
655
368
  }
656
369
 
657
- /**
658
- * Groups all parts of the dialog.
659
- * Doesn’t render its own HTML element.
660
- *
661
- * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
662
- */
663
- function DialogRoot(props) {
370
+ function useRenderDialogRoot(props, mode = 'dialog') {
664
371
  const {
665
372
  children,
666
373
  open: openProp,
667
374
  defaultOpen = false,
668
375
  onOpenChange,
669
376
  onOpenChangeComplete,
670
- disablePointerDismissal = false,
671
- modal = true,
377
+ disablePointerDismissal: disablePointerDismissalProp = false,
378
+ modal: modalProp = true,
672
379
  actionsRef,
673
380
  handle,
674
381
  triggerId: triggerIdProp,
675
382
  defaultTriggerId: defaultTriggerIdProp = null
676
383
  } = props;
384
+ const isDrawer = mode === 'drawer';
385
+ const isAlertDialog = mode === 'alert-dialog';
386
+ const modal = isAlertDialog ? true : modalProp;
387
+ const disablePointerDismissal = isAlertDialog || disablePointerDismissalProp;
388
+ const role = isAlertDialog ? 'alertdialog' : 'dialog';
677
389
  const parentDialogRootContext = useDialogRootContext(true);
678
390
  const nested = Boolean(parentDialogRootContext);
679
- const store = useRenderElement.useRefWithInit(() => {
680
- return handle?.store ?? new DialogStore({
681
- open: defaultOpen,
682
- openProp,
683
- activeTriggerId: defaultTriggerIdProp,
684
- triggerIdProp,
685
- modal,
686
- disablePointerDismissal,
687
- nested
688
- });
689
- }).current;
391
+ const rootState = {
392
+ modal,
393
+ disablePointerDismissal,
394
+ nested,
395
+ role
396
+ };
397
+ const store = DialogStore.useStore(handle?.store, {
398
+ open: defaultOpen,
399
+ openProp,
400
+ activeTriggerId: defaultTriggerIdProp,
401
+ triggerIdProp,
402
+ ...rootState
403
+ });
690
404
 
691
405
  // Support initially open state when uncontrolled
692
- InternalBackdrop.useOnFirstRender(() => {
693
- if (openProp === undefined && store.state.open === false && defaultOpen === true) {
694
- store.update({
695
- open: true,
696
- activeTriggerId: defaultTriggerIdProp
697
- });
406
+ useScrollLock.useOnFirstRender(() => {
407
+ const nextState = openProp === undefined && store.state.open === false && defaultOpen === true ? {
408
+ open: true,
409
+ activeTriggerId: defaultTriggerIdProp
410
+ } : null;
411
+ if (isAlertDialog) {
412
+ // Handles can reuse plain Dialog stores; alert dialog invariants must exist immediately.
413
+ store.update(nextState ? {
414
+ ...rootState,
415
+ ...nextState
416
+ } : rootState);
417
+ } else if (nextState) {
418
+ store.update(nextState);
698
419
  }
699
420
  });
700
421
  store.useControlledProp('openProp', openProp);
701
422
  store.useControlledProp('triggerIdProp', triggerIdProp);
702
- store.useSyncedValues({
703
- disablePointerDismissal,
704
- nested,
705
- modal
706
- });
423
+ store.useSyncedValues(rootState);
707
424
  store.useContextCallback('onOpenChange', onOpenChange);
708
425
  store.useContextCallback('onOpenChangeComplete', onOpenChangeComplete);
426
+ const open = store.useState('open');
427
+ const mounted = store.useState('mounted');
709
428
  const payload = store.useState('payload');
710
429
  useDialogRoot({
711
430
  store,
712
- actionsRef,
713
- parentContext: parentDialogRootContext?.store.context,
714
- onOpenChange,
715
- triggerIdProp
431
+ actionsRef
716
432
  });
433
+ const shouldRenderInteractions = open || mounted;
717
434
  const contextValue = React__namespace.useMemo(() => ({
718
435
  store
719
436
  }), [store]);
720
- return /*#__PURE__*/jsxRuntime.jsx(DialogRootContext.Provider, {
721
- value: contextValue,
722
- children: typeof children === 'function' ? children({
723
- payload
724
- }) : children
437
+ return /*#__PURE__*/jsxRuntime.jsx(IsDrawerContext.Provider, {
438
+ value: false,
439
+ children: /*#__PURE__*/jsxRuntime.jsxs(DialogRootContext.Provider, {
440
+ value: contextValue,
441
+ children: [shouldRenderInteractions && /*#__PURE__*/jsxRuntime.jsx(DialogInteractions, {
442
+ store: store,
443
+ parentContext: parentDialogRootContext?.store.context,
444
+ isDrawer: isDrawer
445
+ }), typeof children === 'function' ? children({
446
+ payload
447
+ }) : children]
448
+ })
725
449
  });
726
450
  }
727
451
 
452
+ /**
453
+ * Groups all parts of the dialog.
454
+ * Doesn't render its own HTML element.
455
+ *
456
+ * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
457
+ */
458
+ function DialogRoot(props) {
459
+ const mode = React__namespace.useContext(IsDrawerContext) ? 'drawer' : 'dialog';
460
+ return useRenderDialogRoot(props, mode);
461
+ }
462
+
728
463
  /**
729
464
  * A button that opens the dialog.
730
465
  * Renders a `<button>` element.
@@ -735,6 +470,7 @@ const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTr
735
470
  const {
736
471
  render,
737
472
  className,
473
+ style,
738
474
  disabled = false,
739
475
  nativeButton = true,
740
476
  id: idProp,
@@ -750,11 +486,12 @@ const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTr
750
486
  const thisTriggerId = useButton.useBaseUiId(idProp);
751
487
  const floatingContext = store.useState('floatingRootContext');
752
488
  const isOpenedByThisTrigger = store.useState('isOpenedByTrigger', thisTriggerId);
489
+ const popupId = store.useState('triggerPopupId', thisTriggerId);
753
490
  const triggerElementRef = React__namespace.useRef(null);
754
491
  const {
755
492
  registerTrigger,
756
493
  isMountedByThisTrigger
757
- } = useTriggerDataForwarding(thisTriggerId, triggerElementRef, store, {
494
+ } = useScrollLock.useTriggerDataForwarding(thisTriggerId, triggerElementRef, store, {
758
495
  payload
759
496
  });
760
497
  const {
@@ -764,10 +501,12 @@ const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTr
764
501
  disabled,
765
502
  native: nativeButton
766
503
  });
767
- const click = InternalBackdrop.useClick(floatingContext, {
504
+ const click = useScrollLock.useClick(floatingContext, {
768
505
  enabled: floatingContext != null
769
506
  });
770
- const localInteractionProps = InternalBackdrop.useInteractions([click]);
507
+ const interactionTypeProps = useScrollLock.useOpenMethodTriggerProps(() => store.select('open'), interactionType => {
508
+ store.set('openMethod', interactionType);
509
+ });
771
510
  const state = {
772
511
  disabled,
773
512
  open: isOpenedByThisTrigger
@@ -776,11 +515,14 @@ const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTr
776
515
  return useRenderElement.useRenderElement('button', componentProps, {
777
516
  state,
778
517
  ref: [buttonRef, forwardedRef, registerTrigger, triggerElementRef],
779
- props: [localInteractionProps.getReferenceProps(), rootTriggerProps, {
780
- [InternalBackdrop.CLICK_TRIGGER_IDENTIFIER]: '',
781
- id: thisTriggerId
518
+ props: [click.reference, rootTriggerProps, interactionTypeProps, {
519
+ [useScrollLock.CLICK_TRIGGER_IDENTIFIER]: '',
520
+ id: thisTriggerId,
521
+ 'aria-haspopup': 'dialog',
522
+ 'aria-expanded': isOpenedByThisTrigger,
523
+ 'aria-controls': popupId
782
524
  }, elementProps, getButtonProps],
783
- stateAttributesMapping: InternalBackdrop.triggerOpenStateMapping
525
+ stateAttributesMapping: useScrollLock.triggerOpenStateMapping
784
526
  });
785
527
  });
786
528
  if (process.env.NODE_ENV !== "production") DialogTrigger.displayName = "DialogTrigger";
@@ -834,7 +576,7 @@ let DrawerBackdropCssVars = /*#__PURE__*/function (DrawerBackdropCssVars) {
834
576
  }({});
835
577
 
836
578
  const stateAttributesMapping$1 = {
837
- ...InternalBackdrop.popupStateMapping,
579
+ ...useScrollLock.popupStateMapping,
838
580
  ...useButton.transitionStatusMapping
839
581
  };
840
582
 
@@ -848,6 +590,7 @@ const DrawerBackdrop = /*#__PURE__*/React__namespace.forwardRef(function DrawerB
848
590
  const {
849
591
  render,
850
592
  className,
593
+ style,
851
594
  forceRender = false,
852
595
  ...elementProps
853
596
  } = componentProps;
@@ -894,6 +637,7 @@ const DrawerContent = /*#__PURE__*/React__namespace.forwardRef(function DrawerCo
894
637
  const {
895
638
  render,
896
639
  className,
640
+ style,
897
641
  ...elementProps
898
642
  } = componentProps;
899
643
  useDialogRootContext();
@@ -917,19 +661,19 @@ let DrawerPopupDataAttributes = function (DrawerPopupDataAttributes) {
917
661
  /**
918
662
  * Present when the drawer is open.
919
663
  */
920
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["open"] = InternalBackdrop.CommonPopupDataAttributes.open] = "open";
664
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["open"] = useScrollLock.CommonPopupDataAttributes.open] = "open";
921
665
  /**
922
666
  * Present when the drawer is closed.
923
667
  */
924
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["closed"] = InternalBackdrop.CommonPopupDataAttributes.closed] = "closed";
668
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["closed"] = useScrollLock.CommonPopupDataAttributes.closed] = "closed";
925
669
  /**
926
670
  * Present when the drawer is animating in.
927
671
  */
928
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["startingStyle"] = InternalBackdrop.CommonPopupDataAttributes.startingStyle] = "startingStyle";
672
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["startingStyle"] = useScrollLock.CommonPopupDataAttributes.startingStyle] = "startingStyle";
929
673
  /**
930
674
  * Present when the drawer is animating out.
931
675
  */
932
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["endingStyle"] = InternalBackdrop.CommonPopupDataAttributes.endingStyle] = "endingStyle";
676
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["endingStyle"] = useScrollLock.CommonPopupDataAttributes.endingStyle] = "endingStyle";
933
677
  /**
934
678
  * Present when the drawer is at the expanded (full-height) snap point.
935
679
  */
@@ -968,6 +712,17 @@ function useDrawerRootContext(optional) {
968
712
  return drawerRootContext;
969
713
  }
970
714
 
715
+ /**
716
+ * Resolves the vertical swipe movement for a snap point, applying square-root damping once the drag
717
+ * overshoots the fully-open edge (`nextOffset < 0`) so the popup resists travelling past it.
718
+ */
719
+ function getSnapPointSwipeMovement(baseOffset, movementValue) {
720
+ const nextOffset = baseOffset + movementValue;
721
+ if (nextOffset >= 0) {
722
+ return movementValue;
723
+ }
724
+ return -Math.sqrt(-nextOffset) - baseOffset;
725
+ }
971
726
  function resolveSnapPointValue(snapPoint, viewportHeight, rootFontSize) {
972
727
  if (!Number.isFinite(viewportHeight) || viewportHeight <= 0) {
973
728
  return null;
@@ -1121,7 +876,6 @@ let drawerSwipeVarsRegistered = false;
1121
876
  /**
1122
877
  * Removes inheritance of high-frequency drawer swipe CSS variables, which
1123
878
  * reduces style recalculation cost in complex drawers with deep subtrees.
1124
- * Child elements that need these values can still opt-in by using `inherit`.
1125
879
  * See https://motion.dev/blog/web-animation-performance-tier-list
1126
880
  * under the "Improving CSS variable performance" section.
1127
881
  */
@@ -1172,7 +926,7 @@ function removeCSSVariableInheritance$1() {
1172
926
  drawerSwipeVarsRegistered = true;
1173
927
  }
1174
928
  const stateAttributesMapping = {
1175
- ...InternalBackdrop.popupStateMapping,
929
+ ...useScrollLock.popupStateMapping,
1176
930
  ...useButton.transitionStatusMapping,
1177
931
  expanded(value) {
1178
932
  return value ? {
@@ -1209,15 +963,17 @@ const stateAttributesMapping = {
1209
963
  */
1210
964
  const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopup(componentProps, forwardedRef) {
1211
965
  const {
966
+ render,
1212
967
  className,
968
+ style,
1213
969
  finalFocus,
1214
970
  initialFocus,
1215
- render,
1216
971
  ...elementProps
1217
972
  } = componentProps;
1218
973
  const {
1219
974
  store
1220
975
  } = useDialogRootContext();
976
+ const popupRef = store.context.popupRef;
1221
977
  const {
1222
978
  swipeDirection,
1223
979
  frontmostHeight,
@@ -1235,26 +991,39 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
1235
991
  const modal = store.useState('modal');
1236
992
  const mounted = store.useState('mounted');
1237
993
  const nested = store.useState('nested');
1238
- const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');
994
+ const nestedOpenDrawerCount = store.useState('nestedOpenDrawerCount');
1239
995
  const transitionStatus = store.useState('transitionStatus');
1240
996
  const open = store.useState('open');
1241
997
  const openMethod = store.useState('openMethod');
1242
998
  const titleElementId = store.useState('titleElementId');
1243
999
  const role = store.useState('role');
1244
- const nestedDrawerOpen = nestedOpenDialogCount > 0;
1000
+ const floatingId = floatingRootContext.useState('floatingId');
1001
+ const popupId = elementProps.id ?? floatingId;
1245
1002
  const swipe = useDrawerViewportContext();
1246
- const swiping = swipe?.swiping ?? false;
1247
- const swipeStrength = swipe?.swipeStrength ?? null;
1003
+ useDialogPortalContext();
1248
1004
  const {
1249
1005
  snapPoints,
1250
1006
  activeSnapPoint,
1251
1007
  activeSnapPointOffset
1252
1008
  } = useDrawerSnapPoints();
1253
- useDialogPortalContext();
1009
+ const nestedDrawerOpen = nestedOpenDrawerCount > 0;
1010
+ const swiping = swipe?.swiping ?? false;
1011
+ const swipeStrength = swipe?.swipeStrength ?? null;
1254
1012
  const [popupHeight, setPopupHeight] = React__namespace.useState(0);
1255
1013
  const popupHeightRef = React__namespace.useRef(0);
1014
+ if (process.env.NODE_ENV !== 'production') {
1015
+ // eslint-disable-next-line react-hooks/rules-of-hooks
1016
+ React__namespace.useEffect(() => {
1017
+ if (swipe) {
1018
+ return;
1019
+ }
1020
+ const ownerStackMessage = useButton.SafeReact.captureOwnerStack?.() || '';
1021
+ const message = '<Drawer.Popup> expected to be rendered within <Drawer.Viewport>. Omitting the ' + 'viewport disables drawer swipe handling and touch scroll locking. Wrap ' + '<Drawer.Popup> in <Drawer.Viewport>.';
1022
+ useButton.error(`${message}${ownerStackMessage}`);
1023
+ }, [swipe]);
1024
+ }
1256
1025
  const measureHeight = useButton.useStableCallback(() => {
1257
- const popupElement = store.context.popupRef.current;
1026
+ const popupElement = popupRef.current;
1258
1027
  if (!popupElement) {
1259
1028
  return;
1260
1029
  }
@@ -1286,7 +1055,7 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
1286
1055
  onPopupHeightChange(0);
1287
1056
  return undefined;
1288
1057
  }
1289
- const popupElement = store.context.popupRef.current;
1058
+ const popupElement = popupRef.current;
1290
1059
  if (!popupElement) {
1291
1060
  return undefined;
1292
1061
  }
@@ -1300,9 +1069,8 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
1300
1069
  return () => {
1301
1070
  resizeObserver.disconnect();
1302
1071
  };
1303
- }, [measureHeight, mounted, nestedDrawerOpen, onPopupHeightChange, store.context.popupRef]);
1072
+ }, [measureHeight, mounted, nestedDrawerOpen, onPopupHeightChange, popupRef]);
1304
1073
  useButton.useIsoLayoutEffect(() => {
1305
- const popupRef = store.context.popupRef;
1306
1074
  const syncNestedSwipeProgress = () => {
1307
1075
  const popupElement = popupRef.current;
1308
1076
  if (!popupElement) {
@@ -1317,14 +1085,14 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
1317
1085
  };
1318
1086
  syncNestedSwipeProgress();
1319
1087
  const unsubscribe = nestedSwipeProgressStore.subscribe(syncNestedSwipeProgress);
1088
+ const popupElement = popupRef.current;
1320
1089
  return () => {
1321
1090
  unsubscribe();
1322
- const popupElement = popupRef.current;
1323
1091
  if (popupElement) {
1324
1092
  popupElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, '0');
1325
1093
  }
1326
1094
  };
1327
- }, [nestedSwipeProgressStore, store.context.popupRef]);
1095
+ }, [nestedSwipeProgressStore, popupRef]);
1328
1096
  React__namespace.useEffect(() => {
1329
1097
  if (!open) {
1330
1098
  return undefined;
@@ -1346,14 +1114,15 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
1346
1114
  }, [notifyParentHasNestedDrawer, open, transitionStatus]);
1347
1115
  useButton.useOpenChangeComplete({
1348
1116
  open,
1349
- ref: store.context.popupRef,
1117
+ ref: popupRef,
1350
1118
  onComplete() {
1351
1119
  if (open) {
1352
1120
  store.context.onOpenChangeComplete?.(true);
1353
1121
  }
1354
1122
  }
1355
1123
  });
1356
- const resolvedInitialFocus = initialFocus === undefined ? store.context.popupRef : initialFocus;
1124
+ const resolvedInitialFocus = initialFocus === undefined ? popupRef : initialFocus;
1125
+ const setPopupElement = store.useStateSetter('popupElement');
1357
1126
  const state = {
1358
1127
  open,
1359
1128
  nested,
@@ -1378,16 +1147,11 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
1378
1147
  if (shouldApplySnapPoints && swipeDirection === 'down') {
1379
1148
  const baseOffset = activeSnapPointOffset ?? 0;
1380
1149
  const movementValue = Number.parseFloat(String(dragStyles[DrawerPopupCssVars.swipeMovementY] ?? 0));
1381
- const nextOffset = Number.isFinite(movementValue) ? baseOffset + movementValue : baseOffset;
1382
- const shouldDamp = nextOffset < 0;
1383
- if (swiping && shouldDamp && Number.isFinite(movementValue)) {
1384
- const overshoot = Math.abs(nextOffset);
1385
- const dampedOffset = -Math.sqrt(overshoot);
1386
- const dampedMovement = dampedOffset - baseOffset;
1150
+ if (swiping && Number.isFinite(movementValue)) {
1387
1151
  dragStyles = {
1388
1152
  ...dragStyles,
1389
1153
  transform: undefined,
1390
- [DrawerPopupCssVars.swipeMovementY]: `${dampedMovement}px`
1154
+ [DrawerPopupCssVars.swipeMovementY]: `${getSnapPointSwipeMovement(baseOffset, movementValue)}px`
1391
1155
  };
1392
1156
  } else {
1393
1157
  dragStyles = {
@@ -1399,30 +1163,31 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
1399
1163
  const element = useRenderElement.useRenderElement('div', componentProps, {
1400
1164
  state,
1401
1165
  props: [rootPopupProps, {
1166
+ id: popupId,
1402
1167
  'aria-labelledby': titleElementId,
1403
1168
  'aria-describedby': descriptionElementId,
1404
1169
  role,
1405
- tabIndex: -1,
1170
+ ...useScrollLock.FOCUSABLE_POPUP_PROPS,
1406
1171
  hidden: !mounted,
1407
1172
  onKeyDown(event) {
1408
- if (CSPContext.COMPOSITE_KEYS.has(event.key)) {
1173
+ if (scrollEdges.COMPOSITE_KEYS.has(event.key)) {
1409
1174
  event.stopPropagation();
1410
1175
  }
1411
1176
  },
1412
1177
  style: {
1413
1178
  ...dragStyles,
1414
1179
  [DrawerBackdropCssVars.swipeProgress]: '0',
1415
- [DrawerPopupCssVars.nestedDrawers]: nestedOpenDialogCount,
1180
+ [DrawerPopupCssVars.nestedDrawers]: nestedOpenDrawerCount,
1416
1181
  [DrawerPopupCssVars.height]: popupHeightCssVarValue,
1417
1182
  [DrawerPopupCssVars.snapPointOffset]: typeof snapPointOffsetValue === 'number' ? `${snapPointOffsetValue}px` : '0px',
1418
1183
  [DrawerPopupCssVars.frontmostHeight]: frontmostHeight ? `${frontmostHeight}px` : undefined,
1419
1184
  [DrawerPopupCssVars.swipeStrength]: typeof swipeStrength === 'number' && Number.isFinite(swipeStrength) && swipeStrength > 0 ? `${swipeStrength}` : '1'
1420
1185
  }
1421
1186
  }, elementProps],
1422
- ref: [forwardedRef, store.context.popupRef, store.useStateSetter('popupElement')],
1187
+ ref: [forwardedRef, popupRef, setPopupElement],
1423
1188
  stateAttributesMapping
1424
1189
  });
1425
- return /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.FloatingFocusManager, {
1190
+ return /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingFocusManager, {
1426
1191
  context: floatingRootContext,
1427
1192
  openInteractionType: openMethod,
1428
1193
  disabled: !mounted,
@@ -1470,9 +1235,8 @@ function DrawerRoot(props) {
1470
1235
  snapPoints,
1471
1236
  snapPoint: snapPointProp,
1472
1237
  defaultSnapPoint,
1473
- onSnapPointChange: onSnapPointChangeProp
1238
+ onSnapPointChange
1474
1239
  } = props;
1475
- const onSnapPointChange = useButton.useStableCallback(onSnapPointChangeProp);
1476
1240
  const parentDrawerRootContext = useDrawerRootContext(true);
1477
1241
  const notifyParentSwipeProgressChange = parentDrawerRootContext?.onNestedSwipeProgressChange;
1478
1242
  const notifyParentFrontmostHeight = parentDrawerRootContext?.onNestedFrontmostHeightChange;
@@ -1577,18 +1341,21 @@ function DrawerRoot(props) {
1577
1341
  });
1578
1342
  return /*#__PURE__*/jsxRuntime.jsx(DrawerRootContext.Provider, {
1579
1343
  value: contextValue,
1580
- children: /*#__PURE__*/jsxRuntime.jsx(DialogRoot, {
1581
- open: openProp,
1582
- defaultOpen: defaultOpen,
1583
- onOpenChange: handleOpenChange,
1584
- onOpenChangeComplete: onOpenChangeComplete,
1585
- disablePointerDismissal: disablePointerDismissal,
1586
- modal: modal,
1587
- actionsRef: actionsRef,
1588
- handle: handle,
1589
- triggerId: triggerIdProp,
1590
- defaultTriggerId: defaultTriggerIdProp,
1591
- children: resolvedChildren
1344
+ children: /*#__PURE__*/jsxRuntime.jsx(IsDrawerContext.Provider, {
1345
+ value: true,
1346
+ children: /*#__PURE__*/jsxRuntime.jsx(DialogRoot, {
1347
+ open: openProp,
1348
+ defaultOpen: defaultOpen,
1349
+ onOpenChange: handleOpenChange,
1350
+ onOpenChangeComplete: onOpenChangeComplete,
1351
+ disablePointerDismissal: disablePointerDismissal,
1352
+ modal: modal,
1353
+ actionsRef: actionsRef,
1354
+ handle: handle,
1355
+ triggerId: triggerIdProp,
1356
+ defaultTriggerId: defaultTriggerIdProp,
1357
+ children: resolvedChildren
1358
+ })
1592
1359
  })
1593
1360
  });
1594
1361
  }
@@ -1618,10 +1385,12 @@ function createNestedSwipeProgressStore() {
1618
1385
  function DrawerProviderReporter() {
1619
1386
  const drawerId = useButton.useId();
1620
1387
  const providerContext = useDrawerProviderContext();
1621
- const dialogRootContext = useDialogRootContext(false);
1622
- const open = dialogRootContext.store.useState('open');
1623
- const nestedOpenDialogCount = dialogRootContext.store.useState('nestedOpenDialogCount');
1624
- const popupElement = dialogRootContext.store.useState('popupElement');
1388
+ const {
1389
+ store
1390
+ } = useDialogRootContext(false);
1391
+ const open = store.useState('open');
1392
+ const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');
1393
+ const popupElement = store.useState('popupElement');
1625
1394
  const isTopmost = nestedOpenDialogCount === 0;
1626
1395
  React__namespace.useEffect(() => {
1627
1396
  if (!providerContext || drawerId == null) {
@@ -1640,7 +1409,7 @@ function DrawerProviderReporter() {
1640
1409
  React__namespace.useEffect(() => {
1641
1410
  // CloseWatcher enables the Android back gesture (Chromium-only).
1642
1411
  // Keep this Android-only for now to avoid interfering with Escape/nesting semantics on desktop due to `useDismiss`.
1643
- if (!open || !isTopmost || !useButton.isAndroid) {
1412
+ if (!open || !isTopmost || !useButton.android) {
1644
1413
  return undefined;
1645
1414
  }
1646
1415
  const win = floatingUi_utils_dom.getWindow(popupElement);
@@ -1649,18 +1418,18 @@ function DrawerProviderReporter() {
1649
1418
  return undefined;
1650
1419
  }
1651
1420
  function handleCloseWatcher(event) {
1652
- if (!dialogRootContext.store.select('open')) {
1421
+ if (!store.select('open')) {
1653
1422
  return;
1654
1423
  }
1655
- dialogRootContext.store.setOpen(false, useButton.createChangeEventDetails(useButton.closeWatcher, event));
1424
+ store.setOpen(false, useButton.createChangeEventDetails(useButton.closeWatcher, event));
1656
1425
  }
1657
1426
  const closeWatcher = new CloseWatcherCtor();
1658
- closeWatcher.addEventListener('close', handleCloseWatcher);
1427
+ const unsubscribe = useButton.addEventListener(closeWatcher, 'close', handleCloseWatcher);
1659
1428
  return () => {
1660
- closeWatcher.removeEventListener('close', handleCloseWatcher);
1429
+ unsubscribe();
1661
1430
  closeWatcher.destroy();
1662
1431
  };
1663
- }, [dialogRootContext.store, isTopmost, open, popupElement]);
1432
+ }, [store, isTopmost, open, popupElement]);
1664
1433
  return null;
1665
1434
  }
1666
1435
 
@@ -1839,16 +1608,22 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
1839
1608
  render,
1840
1609
  className,
1841
1610
  overflowEdgeThreshold: overflowEdgeThresholdProp,
1611
+ style,
1842
1612
  ...elementProps
1843
1613
  } = componentProps;
1844
- const overflowEdgeThreshold = normalizeOverflowEdgeThreshold(overflowEdgeThresholdProp);
1614
+ const {
1615
+ xStart,
1616
+ xEnd,
1617
+ yStart,
1618
+ yEnd
1619
+ } = normalizeOverflowEdgeThreshold(overflowEdgeThresholdProp);
1845
1620
  const rootId = useButton.useBaseUiId();
1846
1621
  const scrollYTimeout = useButton.useTimeout();
1847
1622
  const scrollXTimeout = useButton.useTimeout();
1848
1623
  const {
1849
1624
  nonce,
1850
1625
  disableStyleElements
1851
- } = CSPContext.useCSPContext();
1626
+ } = scrollEdges.useCSPContext();
1852
1627
  const [hovering, setHovering] = React__namespace.useState(false);
1853
1628
  const [scrollingX, setScrollingX] = React__namespace.useState(false);
1854
1629
  const [scrollingY, setScrollingY] = React__namespace.useState(false);
@@ -1949,10 +1724,13 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
1949
1724
  });
1950
1725
  const handlePointerUp = useButton.useStableCallback(event => {
1951
1726
  thumbDraggingRef.current = false;
1952
- if (thumbYRef.current && currentOrientationRef.current === 'vertical') {
1727
+
1728
+ // `pointercancel` releases capture implicitly, so guard against releasing a
1729
+ // capture we no longer hold (which would throw).
1730
+ if (thumbYRef.current && currentOrientationRef.current === 'vertical' && thumbYRef.current.hasPointerCapture(event.pointerId)) {
1953
1731
  thumbYRef.current.releasePointerCapture(event.pointerId);
1954
1732
  }
1955
- if (thumbXRef.current && currentOrientationRef.current === 'horizontal') {
1733
+ if (thumbXRef.current && currentOrientationRef.current === 'horizontal' && thumbXRef.current.hasPointerCapture(event.pointerId)) {
1956
1734
  thumbXRef.current.releasePointerCapture(event.pointerId);
1957
1735
  }
1958
1736
  });
@@ -2027,11 +1805,16 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
2027
1805
  overflowEdges,
2028
1806
  setOverflowEdges,
2029
1807
  viewportState: state,
2030
- overflowEdgeThreshold
2031
- }), [handlePointerDown, handlePointerMove, handlePointerUp, handleScroll, cornerSize, thumbSize, hasMeasuredScrollbar, touchModality, scrollingX, setScrollingX, scrollingY, setScrollingY, hovering, setHovering, rootId, hiddenState, overflowEdges, state, overflowEdgeThreshold]);
1808
+ overflowEdgeThreshold: {
1809
+ xStart,
1810
+ xEnd,
1811
+ yStart,
1812
+ yEnd
1813
+ }
1814
+ }), [handlePointerDown, handlePointerMove, handlePointerUp, handleScroll, cornerSize, thumbSize, hasMeasuredScrollbar, touchModality, scrollingX, setScrollingX, scrollingY, setScrollingY, hovering, setHovering, rootId, hiddenState, overflowEdges, state, xStart, xEnd, yStart, yEnd]);
2032
1815
  return /*#__PURE__*/jsxRuntime.jsxs(ScrollAreaRootContext.Provider, {
2033
1816
  value: contextValue,
2034
- children: [!disableStyleElements && CSPContext.styleDisableScrollbar.getElement(nonce), element]
1817
+ children: [!disableStyleElements && scrollEdges.styleDisableScrollbar.getElement(nonce), element]
2035
1818
  });
2036
1819
  });
2037
1820
  if (process.env.NODE_ENV !== "production") ScrollAreaRoot.displayName = "ScrollAreaRoot";
@@ -2063,27 +1846,6 @@ function useScrollAreaViewportContext() {
2063
1846
  return context;
2064
1847
  }
2065
1848
 
2066
- /**
2067
- * Executes a callback when an element becomes visible.
2068
- */
2069
- function onVisible(element, callback) {
2070
- if (typeof IntersectionObserver === 'undefined') {
2071
- return () => {};
2072
- }
2073
- const observer = new IntersectionObserver(entries => {
2074
- entries.forEach(entry => {
2075
- if (entry.intersectionRatio > 0) {
2076
- callback();
2077
- observer.disconnect();
2078
- }
2079
- });
2080
- });
2081
- observer.observe(element);
2082
- return () => {
2083
- observer.disconnect();
2084
- };
2085
- }
2086
-
2087
1849
  let ScrollAreaViewportCssVars = /*#__PURE__*/function (ScrollAreaViewportCssVars) {
2088
1850
  /**
2089
1851
  * The distance from the horizontal start edge in pixels.
@@ -2108,30 +1870,7 @@ let ScrollAreaViewportCssVars = /*#__PURE__*/function (ScrollAreaViewportCssVars
2108
1870
  return ScrollAreaViewportCssVars;
2109
1871
  }({});
2110
1872
 
2111
- const SCROLL_EDGE_TOLERANCE_PX = 1;
2112
- function normalizeScrollOffset(value, max) {
2113
- if (max <= 0) {
2114
- return 0;
2115
- }
2116
- const clamped = clamp.clamp(value, 0, max);
2117
- const startDistance = clamped;
2118
- const endDistance = max - clamped;
2119
- const withinStartTolerance = startDistance <= SCROLL_EDGE_TOLERANCE_PX;
2120
- const withinEndTolerance = endDistance <= SCROLL_EDGE_TOLERANCE_PX;
2121
- if (withinStartTolerance && withinEndTolerance) {
2122
- return startDistance <= endDistance ? 0 : max;
2123
- }
2124
- if (withinStartTolerance) {
2125
- return 0;
2126
- }
2127
- if (withinEndTolerance) {
2128
- return max;
2129
- }
2130
- return clamped;
2131
- }
2132
-
2133
1873
  let scrollAreaOverflowVarsRegistered = false;
2134
- const HAS_RESIZE_OBSERVER = typeof ResizeObserver !== 'undefined';
2135
1874
 
2136
1875
  /**
2137
1876
  * Removes inheritance of the scroll area overflow CSS variables, which
@@ -2145,7 +1884,7 @@ function removeCSSVariableInheritance() {
2145
1884
  if (scrollAreaOverflowVarsRegistered ||
2146
1885
  // When `inherits: false`, specifying `inherit` on child elements doesn't work
2147
1886
  // in Safari. To let CSS features work correctly, this optimization must be skipped.
2148
- useButton.isWebKit) {
1887
+ useScrollLock.webkit) {
2149
1888
  return;
2150
1889
  }
2151
1890
  if (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {
@@ -2175,6 +1914,7 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2175
1914
  const {
2176
1915
  render,
2177
1916
  className,
1917
+ style,
2178
1918
  ...elementProps
2179
1919
  } = componentProps;
2180
1920
  const {
@@ -2199,7 +1939,7 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2199
1939
  scrollingX,
2200
1940
  scrollingY
2201
1941
  } = useScrollAreaRootContext();
2202
- const direction = InternalBackdrop.useDirection();
1942
+ const direction = useScrollLock.useDirection();
2203
1943
  const programmaticScrollRef = React__namespace.useRef(true);
2204
1944
  const lastMeasuredViewportMetricsRef = React__namespace.useRef([NaN, NaN, NaN, NaN]);
2205
1945
  const scrollEndTimeout = useButton.useTimeout();
@@ -2248,11 +1988,11 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2248
1988
  } else {
2249
1989
  rawScrollLeftFromStart = clamp.clamp(scrollLeft, 0, maxScrollLeft);
2250
1990
  }
2251
- scrollLeftFromStart = normalizeScrollOffset(rawScrollLeftFromStart, maxScrollLeft);
1991
+ scrollLeftFromStart = scrollEdges.normalizeScrollOffset(rawScrollLeftFromStart, maxScrollLeft);
2252
1992
  scrollLeftFromEnd = maxScrollLeft - scrollLeftFromStart;
2253
1993
  }
2254
1994
  const rawScrollTopFromStart = !scrollbarYHidden ? clamp.clamp(scrollTop, 0, maxScrollTop) : 0;
2255
- const scrollTopFromStart = !scrollbarYHidden ? normalizeScrollOffset(rawScrollTopFromStart, maxScrollTop) : 0;
1995
+ const scrollTopFromStart = !scrollbarYHidden ? scrollEdges.normalizeScrollOffset(rawScrollTopFromStart, maxScrollTop) : 0;
2256
1996
  const scrollTopFromEnd = !scrollbarYHidden ? maxScrollTop - scrollTopFromStart : 0;
2257
1997
  const nextWidth = scrollbarXHidden ? 0 : viewportWidth;
2258
1998
  const nextHeight = scrollbarYHidden ? 0 : viewportHeight;
@@ -2344,20 +2084,15 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2344
2084
  });
2345
2085
  useButton.useIsoLayoutEffect(() => {
2346
2086
  if (!viewportRef.current) {
2347
- return undefined;
2087
+ return;
2348
2088
  }
2349
2089
  removeCSSVariableInheritance();
2350
- if (HAS_RESIZE_OBSERVER) {
2351
- return undefined;
2352
- }
2353
- return onVisible(viewportRef.current, () => {
2354
- computeThumbPosition();
2355
- });
2356
- }, [computeThumbPosition, viewportRef]);
2090
+ }, [viewportRef]);
2357
2091
  useButton.useIsoLayoutEffect(() => {
2358
- // Wait for scrollbar and thumb refs after hidden-state toggles, and refresh math on direction flips.
2092
+ // Wait for scrollbar and thumb refs after hidden-state toggles, refresh math on direction
2093
+ // flips, and re-evaluate overflow edges when the threshold changes.
2359
2094
  queueMicrotask(computeThumbPosition);
2360
- }, [computeThumbPosition, hiddenState, direction]);
2095
+ }, [computeThumbPosition, hiddenState, direction, overflowEdgeThreshold.xStart, overflowEdgeThreshold.xEnd, overflowEdgeThreshold.yStart, overflowEdgeThreshold.yEnd]);
2361
2096
  useButton.useIsoLayoutEffect(() => {
2362
2097
  // `onMouseEnter` doesn't fire upon load, so we need to check if the viewport is already
2363
2098
  // being hovered.
@@ -2365,13 +2100,13 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2365
2100
  setHovering(true);
2366
2101
  }
2367
2102
  }, [viewportRef, setHovering]);
2368
- React__namespace.useEffect(() => {
2103
+ useButton.useIsoLayoutEffect(() => {
2369
2104
  const viewport = viewportRef.current;
2370
- if (!HAS_RESIZE_OBSERVER || !viewport) {
2105
+ if (typeof ResizeObserver === 'undefined' || !viewport) {
2371
2106
  return undefined;
2372
2107
  }
2373
2108
  let hasInitialized = false;
2374
- const ro = new ResizeObserver(() => {
2109
+ const resizeObserver = new ResizeObserver(() => {
2375
2110
  // Avoid duplicate mount-time recompute when observer data matches what the mount
2376
2111
  // scheduling pass already measured. If dimensions changed before the first observer
2377
2112
  // delivery, keep the recompute so overflow transitions stay in sync.
@@ -2384,15 +2119,10 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2384
2119
  }
2385
2120
  computeThumbPosition();
2386
2121
  });
2387
- ro.observe(viewport);
2122
+ resizeObserver.observe(viewport);
2388
2123
 
2389
- // If there are animations in the viewport, wait for them to finish and then recompute the thumb position.
2390
- // This is necessary when the viewport contains a Dialog that is animating its popup on open
2391
- // and the popup is using a transform for the animation, which affects the size of the viewport.
2392
- // Without this, the thumb position will be incorrect until scrolling (i.e. if the scrollbar shows
2393
- // on hover, the thumb has an incorrect size).
2394
- // We assume the user is using `onOpenChangeComplete` to hide the scrollbar
2395
- // until animations complete because otherwise the scrollbar would show the thumb resizing mid-animation.
2124
+ // Wait for subtree animations to finish, then recompute thumb geometry that
2125
+ // may have been affected by transform-based animations.
2396
2126
  waitForAnimationsTimeout.start(0, () => {
2397
2127
  const animations = viewport.getAnimations({
2398
2128
  subtree: true
@@ -2403,7 +2133,7 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2403
2133
  Promise.allSettled(animations.map(animation => animation.finished)).then(computeThumbPosition).catch(() => {});
2404
2134
  });
2405
2135
  return () => {
2406
- ro.disconnect();
2136
+ resizeObserver.disconnect();
2407
2137
  waitForAnimationsTimeout.clear();
2408
2138
  };
2409
2139
  }, [computeThumbPosition, viewportRef, waitForAnimationsTimeout]);
@@ -2418,7 +2148,7 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2418
2148
  // https://accessibilityinsights.io/info-examples/web/scrollable-region-focusable/
2419
2149
  // Keep non-scrollable viewports out of tab order.
2420
2150
  tabIndex: hiddenState.x && hiddenState.y ? -1 : 0,
2421
- className: CSPContext.styleDisableScrollbar.className,
2151
+ className: scrollEdges.styleDisableScrollbar.className,
2422
2152
  style: {
2423
2153
  overflow: 'scroll'
2424
2154
  },
@@ -2512,6 +2242,7 @@ exports.ScrollAreaRootCssVars = ScrollAreaRootCssVars;
2512
2242
  exports.ScrollAreaViewport = ScrollAreaViewport;
2513
2243
  exports.createInitialPopupStoreState = createInitialPopupStoreState;
2514
2244
  exports.getOffset = getOffset;
2245
+ exports.getSnapPointSwipeMovement = getSnapPointSwipeMovement;
2515
2246
  exports.popupStoreSelectors = popupStoreSelectors;
2516
2247
  exports.scrollAreaStateAttributesMapping = scrollAreaStateAttributesMapping;
2517
2248
  exports.useDialogPortalContext = useDialogPortalContext;
@@ -2519,11 +2250,5 @@ exports.useDialogRootContext = useDialogRootContext;
2519
2250
  exports.useDrawerProviderContext = useDrawerProviderContext;
2520
2251
  exports.useDrawerRootContext = useDrawerRootContext;
2521
2252
  exports.useDrawerSnapPoints = useDrawerSnapPoints;
2522
- exports.useImplicitActiveTrigger = useImplicitActiveTrigger;
2523
- exports.useOpenStateTransitions = useOpenStateTransitions;
2524
- exports.useRole = useRole;
2525
2253
  exports.useScrollAreaRootContext = useScrollAreaRootContext;
2526
2254
  exports.useScrollAreaViewportContext = useScrollAreaViewportContext;
2527
- exports.useSyncedFloatingRootContext = useSyncedFloatingRootContext;
2528
- exports.useTriggerDataForwarding = useTriggerDataForwarding;
2529
- exports.useTriggerRegistration = useTriggerRegistration;