@jobber/components 9.5.3 → 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Combobox.d.ts +1 -0
- package/Combobox.js +17 -0
- package/dist/Autocomplete/index.cjs +0 -2
- package/dist/Autocomplete/index.mjs +0 -2
- package/dist/Autocomplete-cjs.js +4 -5
- package/dist/Autocomplete-es.js +1 -2
- package/dist/Card/index.cjs +2 -2
- package/dist/Card/index.mjs +2 -2
- package/dist/Chip/index.cjs +0 -2
- package/dist/Chip/index.mjs +0 -2
- package/dist/Chips/InternalChipDismissible/hooks/index.cjs +0 -2
- package/dist/Chips/InternalChipDismissible/hooks/index.mjs +0 -2
- package/dist/Chips/InternalChipDismissible/index.cjs +0 -2
- package/dist/Chips/InternalChipDismissible/index.mjs +0 -2
- package/dist/Chips/index.cjs +0 -2
- package/dist/Chips/index.mjs +0 -2
- package/dist/Combobox/Combobox.contentSlots.d.ts +5 -0
- package/dist/Combobox/Combobox.context.d.ts +34 -0
- package/dist/Combobox/Combobox.d.ts +69 -0
- package/dist/Combobox/Combobox.footer.d.ts +7 -0
- package/dist/Combobox/Combobox.items.d.ts +25 -0
- package/dist/Combobox/Combobox.responsive.d.ts +54 -0
- package/dist/Combobox/Combobox.selectAll.d.ts +4 -0
- package/dist/Combobox/Combobox.slotFactory.d.ts +7 -0
- package/dist/Combobox/Combobox.testUtils.d.ts +12 -0
- package/dist/Combobox/Combobox.types.d.ts +166 -0
- package/dist/Combobox/index.cjs +35 -0
- package/dist/Combobox/index.d.ts +2 -0
- package/dist/Combobox/index.mjs +29 -0
- package/dist/Combobox-cjs.js +675 -0
- package/dist/Combobox-es.js +673 -0
- package/dist/ComboboxPrimitive-cjs.js +88 -47
- package/dist/ComboboxPrimitive-es.js +39 -23
- package/dist/{ComboboxChipRemove-cjs.js → ComboboxSeparator-cjs.js} +501 -455
- package/dist/{ComboboxChipRemove-es.js → ComboboxSeparator-es.js} +500 -456
- package/dist/ConfirmationModal/index.cjs +0 -2
- package/dist/ConfirmationModal/index.mjs +0 -2
- package/dist/ContentBlock/index.cjs +1 -0
- package/dist/ContentBlock/index.mjs +1 -0
- package/dist/ContentBlock-cjs.js +2 -18
- package/dist/ContentBlock-es.js +1 -17
- package/dist/DataDump/index.cjs +2 -2
- package/dist/DataDump/index.mjs +2 -2
- package/dist/DataList/components/DataListActions/index.cjs +0 -2
- package/dist/DataList/components/DataListActions/index.mjs +0 -2
- package/dist/DataList/components/DataListBulkActions/index.cjs +0 -2
- package/dist/DataList/components/DataListBulkActions/index.mjs +0 -2
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +0 -2
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +0 -2
- package/dist/DataList/components/DataListFilters/index.cjs +0 -2
- package/dist/DataList/components/DataListFilters/index.mjs +0 -2
- package/dist/DataList/components/DataListHeader/index.cjs +0 -2
- package/dist/DataList/components/DataListHeader/index.mjs +0 -2
- package/dist/DataList/components/DataListItem/index.cjs +0 -2
- package/dist/DataList/components/DataListItem/index.mjs +0 -2
- package/dist/DataList/components/DataListItemActions/index.cjs +0 -2
- package/dist/DataList/components/DataListItemActions/index.mjs +0 -2
- package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +0 -2
- package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +0 -2
- package/dist/DataList/components/DataListItems/index.cjs +0 -2
- package/dist/DataList/components/DataListItems/index.mjs +0 -2
- package/dist/DataList/components/DataListLayout/index.cjs +0 -2
- package/dist/DataList/components/DataListLayout/index.mjs +0 -2
- package/dist/DataList/components/DataListLayoutActions/index.cjs +0 -2
- package/dist/DataList/components/DataListLayoutActions/index.mjs +0 -2
- package/dist/DataList/index.cjs +0 -2
- package/dist/DataList/index.mjs +0 -2
- package/dist/DatePicker/index.cjs +0 -2
- package/dist/DatePicker/index.mjs +0 -2
- package/dist/DatePicker-cjs.js +4 -5
- package/dist/DatePicker-es.js +1 -2
- package/dist/Dialog/index.cjs +2 -2
- package/dist/Dialog/index.mjs +2 -2
- package/dist/DrawerDescription-cjs.js +268 -99
- package/dist/DrawerDescription-es.js +270 -103
- package/dist/DrawerTitle-cjs.js +2 -6
- package/dist/DrawerTitle-es.js +3 -7
- package/dist/FieldDescription-cjs.js +184 -181
- package/dist/FieldDescription-es.js +187 -184
- package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +0 -2
- package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +0 -2
- package/dist/FilterPicker/components/FilterPickerContent/index.cjs +0 -2
- package/dist/FilterPicker/components/FilterPickerContent/index.mjs +0 -2
- package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +0 -2
- package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +0 -2
- package/dist/FilterPicker/index.cjs +0 -2
- package/dist/FilterPicker/index.mjs +0 -2
- package/dist/FilterPickerContent-cjs.js +4 -5
- package/dist/FilterPickerContent-es.js +1 -2
- package/dist/FormatFile/index.cjs +0 -2
- package/dist/FormatFile/index.mjs +0 -2
- package/dist/Gallery/index.cjs +0 -2
- package/dist/Gallery/index.mjs +0 -2
- package/dist/InputDate/index.cjs +0 -2
- package/dist/InputDate/index.mjs +0 -2
- package/dist/InputNumber/index.cjs +2 -2
- package/dist/InputNumber/index.mjs +2 -2
- package/dist/InputNumber/types.d.ts +5 -0
- package/dist/InputNumber-cjs.js +2 -2
- package/dist/InputNumber-es.js +2 -2
- package/dist/LightBox/index.cjs +0 -2
- package/dist/LightBox/index.mjs +0 -2
- package/dist/Menu/index.cjs +2 -2
- package/dist/Menu/index.mjs +2 -2
- package/dist/Menu-cjs.js +4 -5
- package/dist/Menu-es.js +1 -2
- package/dist/MenuSubmenuTrigger-cjs.js +206 -164
- package/dist/MenuSubmenuTrigger-es.js +194 -152
- package/dist/Modal/index.cjs +0 -2
- package/dist/Modal/index.mjs +1 -3
- package/dist/NumberFieldInput-cjs.js +187 -308
- package/dist/NumberFieldInput-es.js +187 -308
- package/dist/Page/index.cjs +2 -2
- package/dist/Page/index.mjs +2 -2
- package/dist/Popover/index.cjs +0 -2
- package/dist/Popover/index.mjs +0 -2
- package/dist/Popover-cjs.js +7 -8
- package/dist/Popover-es.js +1 -2
- package/dist/ScrollAreaViewport-cjs.js +747 -1093
- package/dist/ScrollAreaViewport-es.js +734 -1081
- package/dist/Select/index.cjs +2 -2
- package/dist/Select/index.mjs +2 -2
- package/dist/SelectGroupLabel-cjs.js +207 -306
- package/dist/SelectGroupLabel-es.js +203 -302
- package/dist/SelectPrimitive-cjs.js +11 -17
- package/dist/SelectPrimitive-es.js +1 -7
- package/dist/Tooltip/index.cjs +0 -2
- package/dist/Tooltip/index.mjs +0 -2
- package/dist/Tooltip-cjs.js +6 -7
- package/dist/Tooltip-es.js +1 -2
- package/dist/buttonRenderAdapter-cjs.js +2 -1
- package/dist/buttonRenderAdapter-es.js +2 -1
- package/dist/dialogReturnFocus-cjs.js +1 -1
- package/dist/dialogReturnFocus-es.js +2 -2
- package/dist/docs/InputNumber/InputNumber.md +2 -2
- package/dist/docs/SelectPrimitive/SelectPrimitive.md +1 -1
- package/dist/floating-ui.react-cjs.js +2557 -185
- package/dist/floating-ui.react-es.js +2512 -149
- package/dist/getMappedBreakpointWidth-cjs.js +21 -0
- package/dist/getMappedBreakpointWidth-es.js +18 -0
- package/dist/index.cjs +15 -10
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +14 -10
- package/dist/mergeClassName-cjs.js +12 -0
- package/dist/mergeClassName-es.js +10 -0
- package/dist/primitives/BottomSheet/index.cjs +1 -1
- package/dist/primitives/BottomSheet/index.mjs +1 -1
- package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.d.ts +2 -2
- package/dist/primitives/ComboboxPrimitive/index.cjs +4 -5
- package/dist/primitives/ComboboxPrimitive/index.mjs +4 -5
- package/dist/primitives/SelectPrimitive/index.cjs +1 -2
- package/dist/primitives/SelectPrimitive/index.mjs +1 -2
- package/dist/primitives/index.cjs +3 -3
- package/dist/primitives/index.mjs +3 -3
- package/dist/scrollEdges-cjs.js +1 -11
- package/dist/scrollEdges-es.js +1 -11
- package/dist/scrollable-cjs.js +58 -0
- package/dist/scrollable-es.js +54 -0
- package/dist/styles.css +759 -42
- package/dist/unstyledPrimitives/index.cjs +720 -478
- package/dist/unstyledPrimitives/index.mjs +663 -421
- package/dist/useCompositeListItem-cjs.js +2108 -361
- package/dist/useCompositeListItem-es.js +2076 -331
- package/dist/useLabel-cjs.js +2 -3
- package/dist/useLabel-es.js +2 -3
- package/dist/useRenderElement-cjs.js +9 -7
- package/dist/useRenderElement-es.js +9 -7
- package/dist/useScrollLock-cjs.js +819 -344
- package/dist/useScrollLock-es.js +732 -296
- package/dist/useScrollToActive-cjs.js +4 -5
- package/dist/useScrollToActive-es.js +1 -2
- package/dist/utils/meta/meta.json +43 -1
- package/package.json +8 -3
- package/dist/floating-ui.react-dom-cjs.js +0 -2103
- package/dist/floating-ui.react-dom-es.js +0 -2072
- package/dist/floating-ui.utils-cjs.js +0 -342
- package/dist/floating-ui.utils-es.js +0 -300
|
@@ -5,8 +5,8 @@ var useRenderElement = require('./useRenderElement-cjs.js');
|
|
|
5
5
|
var useScrollLock = require('./useScrollLock-cjs.js');
|
|
6
6
|
var scrollEdges = require('./scrollEdges-cjs.js');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
-
var floatingUi_utils = require('./floating-ui.utils-cjs.js');
|
|
9
8
|
var ReactDOM = require('react-dom');
|
|
9
|
+
var scrollable = require('./scrollable-cjs.js');
|
|
10
10
|
|
|
11
11
|
function _interopNamespaceDefault(e) {
|
|
12
12
|
var n = Object.create(null);
|
|
@@ -80,12 +80,12 @@ function createPopupFloatingRootContext(triggerElements, floatingId, nested = fa
|
|
|
80
80
|
onOpenChange: undefined
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
|
-
const activeTriggerIdSelector =
|
|
84
|
-
const openSelector =
|
|
85
|
-
const popupIdSelector =
|
|
83
|
+
const activeTriggerIdSelector = state => state.triggerIdProp ?? state.activeTriggerId;
|
|
84
|
+
const openSelector = state => state.openProp ?? state.open;
|
|
85
|
+
const popupIdSelector = state => {
|
|
86
86
|
const popupId = state.popupElement?.id ?? state.floatingId;
|
|
87
87
|
return popupId || undefined;
|
|
88
|
-
}
|
|
88
|
+
};
|
|
89
89
|
function triggerOwnsOpenPopup(state, triggerId) {
|
|
90
90
|
return triggerId !== undefined && openSelector(state) && activeTriggerIdSelector(state) === triggerId;
|
|
91
91
|
}
|
|
@@ -97,47 +97,81 @@ function triggerOwnsOpenPopupOrIsOnlyTrigger(state, triggerId) {
|
|
|
97
97
|
}
|
|
98
98
|
const popupStoreSelectors = {
|
|
99
99
|
open: openSelector,
|
|
100
|
-
mounted:
|
|
101
|
-
transitionStatus:
|
|
102
|
-
floatingRootContext:
|
|
103
|
-
triggerCount:
|
|
104
|
-
preventUnmountingOnClose:
|
|
105
|
-
payload:
|
|
100
|
+
mounted: state => state.mounted,
|
|
101
|
+
transitionStatus: state => state.transitionStatus,
|
|
102
|
+
floatingRootContext: state => state.floatingRootContext,
|
|
103
|
+
triggerCount: state => state.triggerCount,
|
|
104
|
+
preventUnmountingOnClose: state => state.preventUnmountingOnClose,
|
|
105
|
+
payload: state => state.payload,
|
|
106
106
|
activeTriggerId: activeTriggerIdSelector,
|
|
107
|
-
activeTriggerElement:
|
|
107
|
+
activeTriggerElement: state => state.mounted ? state.activeTriggerElement : null,
|
|
108
108
|
popupId: popupIdSelector,
|
|
109
109
|
/**
|
|
110
110
|
* Whether the trigger with the given ID was used to open the popup.
|
|
111
111
|
*/
|
|
112
|
-
isTriggerActive:
|
|
112
|
+
isTriggerActive: (state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId,
|
|
113
113
|
/**
|
|
114
114
|
* Whether the popup is open and was activated by a trigger with the given ID.
|
|
115
115
|
*/
|
|
116
|
-
isOpenedByTrigger:
|
|
116
|
+
isOpenedByTrigger: (state, triggerId) => triggerOwnsOpenPopup(state, triggerId),
|
|
117
117
|
/**
|
|
118
118
|
* Whether the popup is mounted and was activated by a trigger with the given ID.
|
|
119
119
|
*/
|
|
120
|
-
isMountedByTrigger:
|
|
121
|
-
triggerProps:
|
|
120
|
+
isMountedByTrigger: (state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId && state.mounted,
|
|
121
|
+
triggerProps: (state, isActive) => isActive ? state.activeTriggerProps : state.inactiveTriggerProps,
|
|
122
122
|
/**
|
|
123
123
|
* Popup id for the trigger that currently owns the open popup.
|
|
124
124
|
*/
|
|
125
|
-
triggerPopupId:
|
|
126
|
-
popupProps:
|
|
127
|
-
popupElement:
|
|
128
|
-
positionerElement:
|
|
125
|
+
triggerPopupId: (state, triggerId) => triggerOwnsOpenPopupOrIsOnlyTrigger(state, triggerId) ? popupIdSelector(state) : undefined,
|
|
126
|
+
popupProps: state => state.popupProps,
|
|
127
|
+
popupElement: state => state.popupElement,
|
|
128
|
+
positionerElement: state => state.positionerElement
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Store members a detached handle-backed trigger reads or invokes for trigger registration and data
|
|
133
|
+
* forwarding. `set`/`update` are included only for trigger-count and trigger-data bookkeeping; on a
|
|
134
|
+
* detached (inert) store they are intentionally no-ops, so a write through them is not guaranteed to
|
|
135
|
+
* be durable. Component handle-store views Pick these from their concrete store (preserving its
|
|
136
|
+
* context and selectors) and add any component-specific trigger-invoked members such as `setOpen`.
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The subset of a popup store that trigger registration and data forwarding rely on. Narrow enough
|
|
141
|
+
* that an inert store can be passed while detached.
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Reads the store currently exposed by a popup handle and subscribes to store-pointer changes.
|
|
146
|
+
* Detached triggers use this to follow a handle as a root attaches or detaches: while no root is
|
|
147
|
+
* attached, the handle exposes its fallback store; once a root attaches, subscribers re-render and
|
|
148
|
+
* read from the live root store.
|
|
149
|
+
*
|
|
150
|
+
* Returns `undefined` when no handle is provided so callers can fall back to their root context.
|
|
151
|
+
*
|
|
152
|
+
* @param handle The popup handle to read from, or `undefined` when the trigger is not handle-bound.
|
|
153
|
+
*/
|
|
154
|
+
function usePopupHandleStore(handle) {
|
|
155
|
+
const subscribe = React__namespace.useCallback(listener => {
|
|
156
|
+
if (handle === undefined) {
|
|
157
|
+
return useRenderElement.NOOP;
|
|
158
|
+
}
|
|
159
|
+
return handle.subscribeStore(listener);
|
|
160
|
+
}, [handle]);
|
|
161
|
+
const getSnapshot = React__namespace.useCallback(() => {
|
|
162
|
+
return handle === undefined ? undefined : handle.store;
|
|
163
|
+
}, [handle]);
|
|
164
|
+
return useScrollLock.shimExports.useSyncExternalStore(subscribe, getSnapshot, () => handle?.serverStore);
|
|
165
|
+
}
|
|
166
|
+
|
|
133
167
|
const DialogRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
134
168
|
if (process.env.NODE_ENV !== "production") DialogRootContext.displayName = "DialogRootContext";
|
|
135
169
|
function useDialogRootContext(optional) {
|
|
136
|
-
const
|
|
137
|
-
if (optional
|
|
170
|
+
const store = React__namespace.useContext(DialogRootContext);
|
|
171
|
+
if (!optional && store === undefined) {
|
|
138
172
|
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: DialogRootContext is missing. Dialog parts must be placed within <Dialog.Root>.' : useRenderElement.formatErrorMessage(27));
|
|
139
173
|
}
|
|
140
|
-
return
|
|
174
|
+
return store;
|
|
141
175
|
}
|
|
142
176
|
|
|
143
177
|
const DialogPortalContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
@@ -150,14 +184,26 @@ function useDialogPortalContext() {
|
|
|
150
184
|
return value;
|
|
151
185
|
}
|
|
152
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Shared by `Dialog.Popup` and `Dialog.Viewport`, whose states have the same shape.
|
|
189
|
+
* `nested` is not mapped: unmapped `true` booleans already render as `data-nested`.
|
|
190
|
+
*/
|
|
191
|
+
const dialogStateAttributesMapping = {
|
|
192
|
+
...useScrollLock.popupStateMapping,
|
|
193
|
+
...useScrollLock.transitionStatusMapping,
|
|
194
|
+
nestedDialogOpen(value) {
|
|
195
|
+
return value ? {
|
|
196
|
+
'data-nested-dialog-open': ''
|
|
197
|
+
} : null;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
153
201
|
const DialogPortal = /*#__PURE__*/React__namespace.forwardRef(function DialogPortal(props, forwardedRef) {
|
|
154
202
|
const {
|
|
155
203
|
keepMounted = false,
|
|
156
204
|
...portalProps
|
|
157
205
|
} = props;
|
|
158
|
-
const
|
|
159
|
-
store
|
|
160
|
-
} = useDialogRootContext();
|
|
206
|
+
const store = useDialogRootContext();
|
|
161
207
|
const mounted = store.useState('mounted');
|
|
162
208
|
const modal = store.useState('modal');
|
|
163
209
|
const open = store.useState('open');
|
|
@@ -179,25 +225,6 @@ const DialogPortal = /*#__PURE__*/React__namespace.forwardRef(function DialogPor
|
|
|
179
225
|
});
|
|
180
226
|
if (process.env.NODE_ENV !== "production") DialogPortal.displayName = "DialogPortal";
|
|
181
227
|
|
|
182
|
-
function useDialogRoot(params) {
|
|
183
|
-
const {
|
|
184
|
-
store,
|
|
185
|
-
actionsRef
|
|
186
|
-
} = params;
|
|
187
|
-
const open = store.useState('open');
|
|
188
|
-
useScrollLock.usePopupRootSync(store, open);
|
|
189
|
-
useScrollLock.useImplicitActiveTrigger(store);
|
|
190
|
-
const {
|
|
191
|
-
forceUnmount
|
|
192
|
-
} = useScrollLock.useOpenStateTransitions(open, store);
|
|
193
|
-
const handleImperativeClose = React__namespace.useCallback(() => {
|
|
194
|
-
store.setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction));
|
|
195
|
-
}, [store]);
|
|
196
|
-
React__namespace.useImperativeHandle(actionsRef, () => ({
|
|
197
|
-
unmount: forceUnmount,
|
|
198
|
-
close: handleImperativeClose
|
|
199
|
-
}), [forceUnmount, handleImperativeClose]);
|
|
200
|
-
}
|
|
201
228
|
function DialogInteractions({
|
|
202
229
|
store,
|
|
203
230
|
parentContext,
|
|
@@ -233,8 +260,18 @@ function DialogInteractions({
|
|
|
233
260
|
if ('button' in event && event.button !== 0) {
|
|
234
261
|
return false;
|
|
235
262
|
}
|
|
236
|
-
if ('touches' in event
|
|
237
|
-
|
|
263
|
+
if ('touches' in event) {
|
|
264
|
+
// Outside press can be handled on `touchend`, where the lifted point is
|
|
265
|
+
// reported in `changedTouches` and `touches` contains any remaining
|
|
266
|
+
// active points. Treat it as a single-finger tap only when exactly one
|
|
267
|
+
// touch ended and no other fingers are still down.
|
|
268
|
+
if (event.type === 'touchend') {
|
|
269
|
+
if (event.changedTouches.length !== 1 || event.touches.length !== 0) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
} else if (event.touches.length !== 1) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
238
275
|
}
|
|
239
276
|
const target = useScrollLock.getTarget(event);
|
|
240
277
|
if (isTopmost && !disablePointerDismissal) {
|
|
@@ -242,7 +279,9 @@ function DialogInteractions({
|
|
|
242
279
|
// This supports multiple modal dialogs that aren't nested in the React tree:
|
|
243
280
|
// https://github.com/mui/base-ui/issues/1320
|
|
244
281
|
if (modal) {
|
|
245
|
-
|
|
282
|
+
const internalBackdrop = store.context.internalBackdropRef.current;
|
|
283
|
+
const backdrop = store.context.backdropRef.current;
|
|
284
|
+
return internalBackdrop || backdrop ? internalBackdrop === target || backdrop === target || useScrollLock.contains(target, popupElement) && !target?.hasAttribute('data-base-ui-portal') : true;
|
|
246
285
|
}
|
|
247
286
|
return true;
|
|
248
287
|
}
|
|
@@ -253,73 +292,82 @@ function DialogInteractions({
|
|
|
253
292
|
useScrollLock.useScrollLock(open && modal === true, popupElement);
|
|
254
293
|
|
|
255
294
|
// Listen for nested open/close events on this store to maintain the counts.
|
|
295
|
+
// A close notification is an open notification with zeroed counts.
|
|
256
296
|
store.useContextCallback('onNestedDialogOpen', (dialogCount, drawerCount) => {
|
|
257
297
|
setOwnNestedOpenDialogs(dialogCount);
|
|
258
298
|
setOwnNestedOpenDrawers(drawerCount);
|
|
259
299
|
});
|
|
260
|
-
store.useContextCallback('onNestedDialogClose', () => {
|
|
261
|
-
setOwnNestedOpenDialogs(0);
|
|
262
|
-
setOwnNestedOpenDrawers(0);
|
|
263
|
-
});
|
|
264
300
|
|
|
265
301
|
// Notify parent of our open/close state using parent callbacks, if any
|
|
266
|
-
|
|
267
|
-
if (parentContext?.onNestedDialogOpen
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
302
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
303
|
+
if (parentContext?.onNestedDialogOpen) {
|
|
304
|
+
if (open) {
|
|
305
|
+
parentContext.onNestedDialogOpen(ownNestedOpenDialogs + 1, ownNestedOpenDrawers + (isDrawer ? 1 : 0));
|
|
306
|
+
} else {
|
|
307
|
+
parentContext.onNestedDialogOpen(0, 0);
|
|
308
|
+
}
|
|
272
309
|
}
|
|
273
310
|
return () => {
|
|
274
|
-
if (parentContext?.
|
|
275
|
-
parentContext.
|
|
311
|
+
if (parentContext?.onNestedDialogOpen && open) {
|
|
312
|
+
parentContext.onNestedDialogOpen(0, 0);
|
|
276
313
|
}
|
|
277
314
|
};
|
|
278
315
|
}, [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
316
|
useScrollLock.usePopupInteractionProps(store, {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
317
|
+
// `enabled` is not passed to `useDismiss`, so its props are always defined,
|
|
318
|
+
// and `trigger` is the same object as `reference`.
|
|
319
|
+
activeTriggerProps: dismiss.reference,
|
|
320
|
+
inactiveTriggerProps: dismiss.trigger,
|
|
321
|
+
// DialogPopup and DrawerPopup spread `FOCUSABLE_POPUP_PROPS` directly, so
|
|
322
|
+
// this only needs to carry the dismiss handlers.
|
|
323
|
+
popupProps: dismiss.floating,
|
|
288
324
|
nestedOpenDialogCount: ownNestedOpenDialogs,
|
|
289
325
|
nestedOpenDrawerCount: ownNestedOpenDrawers
|
|
290
326
|
});
|
|
291
327
|
return null;
|
|
292
328
|
}
|
|
293
329
|
|
|
330
|
+
/**
|
|
331
|
+
* A `ReactStore` whose state never changes.
|
|
332
|
+
*
|
|
333
|
+
* Useful for fallback stores that need to support normal store reads while detached from the
|
|
334
|
+
* component that owns real state. Context values may still contain mutable refs or maps.
|
|
335
|
+
*/
|
|
336
|
+
class NullStore extends useScrollLock.ReactStore {
|
|
337
|
+
// `update`/`set`/`notifyAll` funnel through `setState` in the base `Store`, so overriding
|
|
338
|
+
// `setState` alone would neutralize them today. They are overridden explicitly so the store stays
|
|
339
|
+
// inert even if a future base-class change stops routing a mutator through `setState`.
|
|
340
|
+
setState(_newState) {}
|
|
341
|
+
update(_changes) {}
|
|
342
|
+
set(_key, _value) {}
|
|
343
|
+
notifyAll() {}
|
|
344
|
+
}
|
|
345
|
+
|
|
294
346
|
const selectors = {
|
|
295
347
|
...popupStoreSelectors,
|
|
296
|
-
modal:
|
|
297
|
-
nested:
|
|
298
|
-
nestedOpenDialogCount:
|
|
299
|
-
nestedOpenDrawerCount:
|
|
300
|
-
disablePointerDismissal:
|
|
301
|
-
openMethod:
|
|
302
|
-
descriptionElementId:
|
|
303
|
-
titleElementId:
|
|
304
|
-
viewportElement:
|
|
305
|
-
role:
|
|
348
|
+
modal: state => state.modal,
|
|
349
|
+
nested: state => state.nested,
|
|
350
|
+
nestedOpenDialogCount: state => state.nestedOpenDialogCount,
|
|
351
|
+
nestedOpenDrawerCount: state => state.nestedOpenDrawerCount,
|
|
352
|
+
disablePointerDismissal: state => state.disablePointerDismissal,
|
|
353
|
+
openMethod: state => state.openMethod,
|
|
354
|
+
descriptionElementId: state => state.descriptionElementId,
|
|
355
|
+
titleElementId: state => state.titleElementId,
|
|
356
|
+
viewportElement: state => state.viewportElement,
|
|
357
|
+
role: state => state.role
|
|
306
358
|
};
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* The subset of `DialogStore` that detached handle-backed triggers rely on. Both the real
|
|
362
|
+
* `DialogStore` and the inert fallback store satisfy it, so a trigger can read from whichever
|
|
363
|
+
* store the handle currently exposes.
|
|
364
|
+
*/
|
|
365
|
+
|
|
307
366
|
class DialogStore extends useScrollLock.ReactStore {
|
|
308
|
-
constructor(initialState, floatingId, nested
|
|
367
|
+
constructor(initialState, floatingId, nested) {
|
|
309
368
|
const triggerElements = new useScrollLock.PopupTriggerMap();
|
|
310
|
-
const state = createInitialState(initialState);
|
|
311
|
-
state
|
|
312
|
-
super(state, {
|
|
313
|
-
popupRef: /*#__PURE__*/React__namespace.createRef(),
|
|
314
|
-
backdropRef: /*#__PURE__*/React__namespace.createRef(),
|
|
315
|
-
internalBackdropRef: /*#__PURE__*/React__namespace.createRef(),
|
|
316
|
-
outsidePressEnabledRef: {
|
|
317
|
-
current: true
|
|
318
|
-
},
|
|
319
|
-
triggerElements,
|
|
320
|
-
onOpenChange: undefined,
|
|
321
|
-
onOpenChangeComplete: undefined
|
|
322
|
-
}, selectors);
|
|
369
|
+
const state = createInitialState(initialState, triggerElements, floatingId, nested);
|
|
370
|
+
super(state, createInitialContext(triggerElements), selectors);
|
|
323
371
|
}
|
|
324
372
|
setOpen = (nextOpen, eventDetails) => {
|
|
325
373
|
eventDetails.preventUnmountOnClose = () => {
|
|
@@ -341,20 +389,22 @@ class DialogStore extends useScrollLock.ReactStore {
|
|
|
341
389
|
useScrollLock.setPopupOpenState(updatedState, nextOpen, eventDetails.trigger);
|
|
342
390
|
this.update(updatedState);
|
|
343
391
|
};
|
|
344
|
-
|
|
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 */
|
|
392
|
+
}
|
|
348
393
|
|
|
349
|
-
|
|
350
|
-
|
|
394
|
+
/**
|
|
395
|
+
* Creates the inert fallback store used by detached handle-backed triggers while no
|
|
396
|
+
* `Dialog.Root` is attached. It preserves a dialog-specific trigger registry in context so
|
|
397
|
+
* detached triggers can register before migrating to the live root store.
|
|
398
|
+
*/
|
|
399
|
+
function createNullDialogStore() {
|
|
400
|
+
const triggerElements = new useScrollLock.PopupTriggerMap();
|
|
401
|
+
return new NullStore(Object.freeze(createInitialState(undefined, triggerElements)), Object.freeze(createInitialContext(triggerElements)), selectors);
|
|
351
402
|
}
|
|
352
|
-
function createInitialState(initialState =
|
|
353
|
-
|
|
403
|
+
function createInitialState(initialState, triggerElements, floatingId, nested = false) {
|
|
404
|
+
const state = {
|
|
354
405
|
...createInitialPopupStoreState(),
|
|
355
406
|
modal: true,
|
|
356
407
|
disablePointerDismissal: false,
|
|
357
|
-
popupElement: null,
|
|
358
408
|
viewportElement: null,
|
|
359
409
|
descriptionElementId: undefined,
|
|
360
410
|
titleElementId: undefined,
|
|
@@ -365,9 +415,24 @@ function createInitialState(initialState = {}) {
|
|
|
365
415
|
role: 'dialog',
|
|
366
416
|
...initialState
|
|
367
417
|
};
|
|
418
|
+
state.floatingRootContext = createPopupFloatingRootContext(triggerElements, floatingId, nested);
|
|
419
|
+
return state;
|
|
420
|
+
}
|
|
421
|
+
function createInitialContext(triggerElements) {
|
|
422
|
+
return {
|
|
423
|
+
popupRef: /*#__PURE__*/React__namespace.createRef(),
|
|
424
|
+
backdropRef: /*#__PURE__*/React__namespace.createRef(),
|
|
425
|
+
internalBackdropRef: /*#__PURE__*/React__namespace.createRef(),
|
|
426
|
+
outsidePressEnabledRef: {
|
|
427
|
+
current: true
|
|
428
|
+
},
|
|
429
|
+
triggerElements,
|
|
430
|
+
onOpenChange: undefined,
|
|
431
|
+
onOpenChangeComplete: undefined
|
|
432
|
+
};
|
|
368
433
|
}
|
|
369
434
|
|
|
370
|
-
function useRenderDialogRoot(
|
|
435
|
+
function useRenderDialogRoot(mode, props) {
|
|
371
436
|
const {
|
|
372
437
|
children,
|
|
373
438
|
open: openProp,
|
|
@@ -386,38 +451,26 @@ function useRenderDialogRoot(props, mode = 'dialog') {
|
|
|
386
451
|
const modal = isAlertDialog ? true : modalProp;
|
|
387
452
|
const disablePointerDismissal = isAlertDialog || disablePointerDismissalProp;
|
|
388
453
|
const role = isAlertDialog ? 'alertdialog' : 'dialog';
|
|
389
|
-
const
|
|
390
|
-
const nested =
|
|
454
|
+
const parentStore = useDialogRootContext(true);
|
|
455
|
+
const nested = parentStore != null;
|
|
391
456
|
const rootState = {
|
|
392
457
|
modal,
|
|
393
458
|
disablePointerDismissal,
|
|
394
459
|
nested,
|
|
395
460
|
role
|
|
396
461
|
};
|
|
397
|
-
|
|
462
|
+
|
|
463
|
+
// The store is owned by this Root instance and created exactly once. It is not tied to the handle:
|
|
464
|
+
// the handle attaches to it, so swapping the handle re-attaches rather than recreating state.
|
|
465
|
+
// Default values are only initial values; controlled values and root state are synced after creation.
|
|
466
|
+
// Dialogs pass the popup element to Floating UI as the floating element (`treatPopupAsFloatingElement`).
|
|
467
|
+
const store = useScrollLock.usePopupRootStore((floatingId, floatingNested) => new DialogStore({
|
|
398
468
|
open: defaultOpen,
|
|
399
469
|
openProp,
|
|
400
470
|
activeTriggerId: defaultTriggerIdProp,
|
|
401
471
|
triggerIdProp,
|
|
402
472
|
...rootState
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
// Support initially open state when uncontrolled
|
|
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);
|
|
419
|
-
}
|
|
420
|
-
});
|
|
473
|
+
}, floatingId, floatingNested), true);
|
|
421
474
|
store.useControlledProp('openProp', openProp);
|
|
422
475
|
store.useControlledProp('triggerIdProp', triggerIdProp);
|
|
423
476
|
store.useSyncedValues(rootState);
|
|
@@ -426,83 +479,31 @@ function useRenderDialogRoot(props, mode = 'dialog') {
|
|
|
426
479
|
const open = store.useState('open');
|
|
427
480
|
const mounted = store.useState('mounted');
|
|
428
481
|
const payload = store.useState('payload');
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
482
|
+
useScrollLock.usePopupRootSync(store, open);
|
|
483
|
+
useScrollLock.useImplicitActiveTrigger(store);
|
|
484
|
+
const {
|
|
485
|
+
forceUnmount
|
|
486
|
+
} = useScrollLock.useOpenStateTransitions(open, store);
|
|
487
|
+
React__namespace.useImperativeHandle(actionsRef, () => ({
|
|
488
|
+
unmount: forceUnmount,
|
|
489
|
+
close: () => store.setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction))
|
|
490
|
+
}), [forceUnmount, store]);
|
|
433
491
|
const shouldRenderInteractions = open || mounted;
|
|
434
|
-
|
|
435
|
-
store
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
payload
|
|
447
|
-
}) : children]
|
|
448
|
-
})
|
|
492
|
+
return /*#__PURE__*/jsxRuntime.jsxs(DialogRootContext.Provider, {
|
|
493
|
+
value: store,
|
|
494
|
+
children: [handle && /*#__PURE__*/jsxRuntime.jsx(useScrollLock.PopupHandleAttachment, {
|
|
495
|
+
handle: handle,
|
|
496
|
+
store: store
|
|
497
|
+
}), shouldRenderInteractions && /*#__PURE__*/jsxRuntime.jsx(DialogInteractions, {
|
|
498
|
+
store: store,
|
|
499
|
+
parentContext: parentStore?.context,
|
|
500
|
+
isDrawer: isDrawer
|
|
501
|
+
}), typeof children === 'function' ? children({
|
|
502
|
+
payload
|
|
503
|
+
}) : children]
|
|
449
504
|
});
|
|
450
505
|
}
|
|
451
506
|
|
|
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
|
-
|
|
463
|
-
let DialogViewportDataAttributes = function (DialogViewportDataAttributes) {
|
|
464
|
-
/**
|
|
465
|
-
* Present when the dialog is open.
|
|
466
|
-
*/
|
|
467
|
-
DialogViewportDataAttributes[DialogViewportDataAttributes["open"] = useScrollLock.CommonPopupDataAttributes.open] = "open";
|
|
468
|
-
/**
|
|
469
|
-
* Present when the dialog is closed.
|
|
470
|
-
*/
|
|
471
|
-
DialogViewportDataAttributes[DialogViewportDataAttributes["closed"] = useScrollLock.CommonPopupDataAttributes.closed] = "closed";
|
|
472
|
-
/**
|
|
473
|
-
* Present when the dialog is animating in.
|
|
474
|
-
*/
|
|
475
|
-
DialogViewportDataAttributes[DialogViewportDataAttributes["startingStyle"] = useScrollLock.CommonPopupDataAttributes.startingStyle] = "startingStyle";
|
|
476
|
-
/**
|
|
477
|
-
* Present when the dialog is animating out.
|
|
478
|
-
*/
|
|
479
|
-
DialogViewportDataAttributes[DialogViewportDataAttributes["endingStyle"] = useScrollLock.CommonPopupDataAttributes.endingStyle] = "endingStyle";
|
|
480
|
-
/**
|
|
481
|
-
* Present when the dialog is nested within another dialog.
|
|
482
|
-
*/
|
|
483
|
-
DialogViewportDataAttributes["nested"] = "data-nested";
|
|
484
|
-
/**
|
|
485
|
-
* Present when the dialog has other open dialogs nested within it.
|
|
486
|
-
*/
|
|
487
|
-
DialogViewportDataAttributes["nestedDialogOpen"] = "data-nested-dialog-open";
|
|
488
|
-
return DialogViewportDataAttributes;
|
|
489
|
-
}({});
|
|
490
|
-
|
|
491
|
-
const stateAttributesMapping$2 = {
|
|
492
|
-
...useScrollLock.popupStateMapping,
|
|
493
|
-
...useScrollLock.transitionStatusMapping,
|
|
494
|
-
nested(value) {
|
|
495
|
-
return value ? {
|
|
496
|
-
[DialogViewportDataAttributes.nested]: ''
|
|
497
|
-
} : null;
|
|
498
|
-
},
|
|
499
|
-
nestedDialogOpen(value) {
|
|
500
|
-
return value ? {
|
|
501
|
-
[DialogViewportDataAttributes.nestedDialogOpen]: ''
|
|
502
|
-
} : null;
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
|
|
506
507
|
/**
|
|
507
508
|
* A positioning container for the dialog popup that can be made scrollable.
|
|
508
509
|
* Renders a `<div>` element.
|
|
@@ -518,9 +519,7 @@ const DialogViewport = /*#__PURE__*/React__namespace.forwardRef(function DialogV
|
|
|
518
519
|
...elementProps
|
|
519
520
|
} = componentProps;
|
|
520
521
|
const keepMounted = useDialogPortalContext();
|
|
521
|
-
const
|
|
522
|
-
store
|
|
523
|
-
} = useDialogRootContext();
|
|
522
|
+
const store = useDialogRootContext();
|
|
524
523
|
const open = store.useState('open');
|
|
525
524
|
const nested = store.useState('nested');
|
|
526
525
|
const transitionStatus = store.useState('transitionStatus');
|
|
@@ -539,7 +538,7 @@ const DialogViewport = /*#__PURE__*/React__namespace.forwardRef(function DialogV
|
|
|
539
538
|
enabled: shouldRender,
|
|
540
539
|
state,
|
|
541
540
|
ref: [forwardedRef, setViewportElement],
|
|
542
|
-
stateAttributesMapping:
|
|
541
|
+
stateAttributesMapping: dialogStateAttributesMapping,
|
|
543
542
|
props: [{
|
|
544
543
|
role: 'presentation',
|
|
545
544
|
hidden: !mounted,
|
|
@@ -570,8 +569,9 @@ const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTr
|
|
|
570
569
|
handle,
|
|
571
570
|
...elementProps
|
|
572
571
|
} = componentProps;
|
|
573
|
-
const
|
|
574
|
-
const
|
|
572
|
+
const dialogRootStore = useDialogRootContext(true);
|
|
573
|
+
const handleStore = usePopupHandleStore(handle);
|
|
574
|
+
const store = handleStore ?? dialogRootStore;
|
|
575
575
|
if (!store) {
|
|
576
576
|
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Dialog.Trigger> must be used within <Dialog.Root> or provided with a handle.' : useRenderElement.formatErrorMessage(79));
|
|
577
577
|
}
|
|
@@ -593,9 +593,7 @@ const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTr
|
|
|
593
593
|
disabled,
|
|
594
594
|
native: nativeButton
|
|
595
595
|
});
|
|
596
|
-
const click = useScrollLock.useClick(floatingContext
|
|
597
|
-
enabled: floatingContext != null
|
|
598
|
-
});
|
|
596
|
+
const click = useScrollLock.useClick(floatingContext);
|
|
599
597
|
const interactionTypeProps = useScrollLock.useOpenMethodTriggerProps(() => store.select('open'), interactionType => {
|
|
600
598
|
store.set('openMethod', interactionType);
|
|
601
599
|
});
|
|
@@ -667,11 +665,6 @@ let DrawerBackdropCssVars = /*#__PURE__*/function (DrawerBackdropCssVars) {
|
|
|
667
665
|
return DrawerBackdropCssVars;
|
|
668
666
|
}({});
|
|
669
667
|
|
|
670
|
-
const stateAttributesMapping$1 = {
|
|
671
|
-
...useScrollLock.popupStateMapping,
|
|
672
|
-
...useScrollLock.transitionStatusMapping
|
|
673
|
-
};
|
|
674
|
-
|
|
675
668
|
/**
|
|
676
669
|
* An overlay displayed beneath the popup.
|
|
677
670
|
* Renders a `<div>` element.
|
|
@@ -686,9 +679,7 @@ const DrawerBackdrop = /*#__PURE__*/React__namespace.forwardRef(function DrawerB
|
|
|
686
679
|
forceRender = false,
|
|
687
680
|
...elementProps
|
|
688
681
|
} = componentProps;
|
|
689
|
-
const
|
|
690
|
-
store
|
|
691
|
-
} = useDialogRootContext();
|
|
682
|
+
const store = useDialogRootContext();
|
|
692
683
|
const open = store.useState('open');
|
|
693
684
|
const nested = store.useState('nested');
|
|
694
685
|
const mounted = store.useState('mounted');
|
|
@@ -700,7 +691,7 @@ const DrawerBackdrop = /*#__PURE__*/React__namespace.forwardRef(function DrawerB
|
|
|
700
691
|
return useRenderElement.useRenderElement('div', componentProps, {
|
|
701
692
|
state,
|
|
702
693
|
ref: [store.context.backdropRef, forwardedRef],
|
|
703
|
-
stateAttributesMapping:
|
|
694
|
+
stateAttributesMapping: useScrollLock.popupTransitionStateMapping,
|
|
704
695
|
props: [{
|
|
705
696
|
role: 'presentation',
|
|
706
697
|
hidden: !mounted,
|
|
@@ -744,9 +735,8 @@ if (process.env.NODE_ENV !== "production") DrawerContent.displayName = "DrawerCo
|
|
|
744
735
|
|
|
745
736
|
const DrawerProviderContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
746
737
|
if (process.env.NODE_ENV !== "production") DrawerProviderContext.displayName = "DrawerProviderContext";
|
|
747
|
-
function useDrawerProviderContext(
|
|
748
|
-
|
|
749
|
-
return context;
|
|
738
|
+
function useDrawerProviderContext() {
|
|
739
|
+
return React__namespace.useContext(DrawerProviderContext);
|
|
750
740
|
}
|
|
751
741
|
|
|
752
742
|
let DrawerPopupDataAttributes = function (DrawerPopupDataAttributes) {
|
|
@@ -759,7 +749,7 @@ let DrawerPopupDataAttributes = function (DrawerPopupDataAttributes) {
|
|
|
759
749
|
*/
|
|
760
750
|
DrawerPopupDataAttributes[DrawerPopupDataAttributes["closed"] = useScrollLock.CommonPopupDataAttributes.closed] = "closed";
|
|
761
751
|
/**
|
|
762
|
-
* Present when the drawer
|
|
752
|
+
* Present when the drawer begins animating in.
|
|
763
753
|
*/
|
|
764
754
|
DrawerPopupDataAttributes[DrawerPopupDataAttributes["startingStyle"] = useScrollLock.CommonPopupDataAttributes.startingStyle] = "startingStyle";
|
|
765
755
|
/**
|
|
@@ -798,7 +788,7 @@ const DrawerRootContext = /*#__PURE__*/React__namespace.createContext(undefined)
|
|
|
798
788
|
if (process.env.NODE_ENV !== "production") DrawerRootContext.displayName = "DrawerRootContext";
|
|
799
789
|
function useDrawerRootContext(optional) {
|
|
800
790
|
const drawerRootContext = React__namespace.useContext(DrawerRootContext);
|
|
801
|
-
if (optional
|
|
791
|
+
if (optional !== true && drawerRootContext === undefined) {
|
|
802
792
|
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: DrawerRootContext is missing. Drawer parts must be placed within <Drawer.Root>.' : useRenderElement.formatErrorMessage(90));
|
|
803
793
|
}
|
|
804
794
|
return drawerRootContext;
|
|
@@ -839,22 +829,24 @@ function resolveSnapPointValue(snapPoint, viewportHeight, rootFontSize) {
|
|
|
839
829
|
}
|
|
840
830
|
return null;
|
|
841
831
|
}
|
|
842
|
-
|
|
843
|
-
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Returns the index of the value closest to `target`, or `-1` if `values` is empty.
|
|
835
|
+
*/
|
|
836
|
+
function closestSnapPointIndex(values, target) {
|
|
837
|
+
let closestIndex = -1;
|
|
844
838
|
let closestDistance = Infinity;
|
|
845
|
-
for (
|
|
846
|
-
const distance = Math.abs(
|
|
839
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
840
|
+
const distance = Math.abs(values[index] - target);
|
|
847
841
|
if (distance < closestDistance) {
|
|
848
842
|
closestDistance = distance;
|
|
849
|
-
|
|
843
|
+
closestIndex = index;
|
|
850
844
|
}
|
|
851
845
|
}
|
|
852
|
-
return
|
|
846
|
+
return closestIndex;
|
|
853
847
|
}
|
|
854
848
|
function useDrawerSnapPoints() {
|
|
855
|
-
const
|
|
856
|
-
store
|
|
857
|
-
} = useDialogRootContext();
|
|
849
|
+
const store = useDialogRootContext();
|
|
858
850
|
const {
|
|
859
851
|
snapPoints,
|
|
860
852
|
activeSnapPoint,
|
|
@@ -867,12 +859,7 @@ function useDrawerSnapPoints() {
|
|
|
867
859
|
const measureViewportHeight = useScrollLock.useStableCallback(() => {
|
|
868
860
|
const doc = useScrollLock.ownerDocument(viewportElement);
|
|
869
861
|
const html = doc.documentElement;
|
|
870
|
-
|
|
871
|
-
setViewportHeight(viewportElement.offsetHeight);
|
|
872
|
-
}
|
|
873
|
-
if (!viewportElement) {
|
|
874
|
-
setViewportHeight(html.clientHeight);
|
|
875
|
-
}
|
|
862
|
+
setViewportHeight(viewportElement ? viewportElement.offsetHeight : html.clientHeight);
|
|
876
863
|
const fontSize = parseFloat(getComputedStyle(html).fontSize);
|
|
877
864
|
if (Number.isFinite(fontSize)) {
|
|
878
865
|
setRootFontSize(fontSize);
|
|
@@ -894,12 +881,9 @@ function useDrawerSnapPoints() {
|
|
|
894
881
|
return [];
|
|
895
882
|
}
|
|
896
883
|
const maxHeight = Math.min(popupHeight, viewportHeight);
|
|
897
|
-
if (!Number.isFinite(maxHeight) || maxHeight <= 0) {
|
|
898
|
-
return [];
|
|
899
|
-
}
|
|
900
884
|
const resolved = snapPoints.map(value => {
|
|
901
885
|
const resolvedHeight = resolveSnapPointValue(value, viewportHeight, rootFontSize);
|
|
902
|
-
if (resolvedHeight === null
|
|
886
|
+
if (resolvedHeight === null) {
|
|
903
887
|
return null;
|
|
904
888
|
}
|
|
905
889
|
const clampedHeight = scrollEdges.clamp(resolvedHeight, 0, maxHeight);
|
|
@@ -927,9 +911,6 @@ function useDrawerSnapPoints() {
|
|
|
927
911
|
return deduped;
|
|
928
912
|
}, [popupHeight, rootFontSize, snapPoints, viewportHeight]);
|
|
929
913
|
const resolvedActiveSnapPoint = React__namespace.useMemo(() => {
|
|
930
|
-
if (activeSnapPoint === undefined) {
|
|
931
|
-
return resolvedSnapPoints[0];
|
|
932
|
-
}
|
|
933
914
|
if (activeSnapPoint === null) {
|
|
934
915
|
return undefined;
|
|
935
916
|
}
|
|
@@ -939,11 +920,11 @@ function useDrawerSnapPoints() {
|
|
|
939
920
|
}
|
|
940
921
|
const maxHeight = Math.min(popupHeight, viewportHeight);
|
|
941
922
|
const resolvedHeight = resolveSnapPointValue(activeSnapPoint, viewportHeight, rootFontSize);
|
|
942
|
-
if (resolvedHeight === null
|
|
923
|
+
if (resolvedHeight === null) {
|
|
943
924
|
return undefined;
|
|
944
925
|
}
|
|
945
926
|
const clampedHeight = scrollEdges.clamp(resolvedHeight, 0, maxHeight);
|
|
946
|
-
return
|
|
927
|
+
return resolvedSnapPoints[closestSnapPointIndex(resolvedSnapPoints.map(point => point.height), clampedHeight)];
|
|
947
928
|
}, [activeSnapPoint, popupHeight, resolvedSnapPoints, rootFontSize, viewportHeight]);
|
|
948
929
|
return {
|
|
949
930
|
snapPoints,
|
|
@@ -958,9 +939,8 @@ function useDrawerSnapPoints() {
|
|
|
958
939
|
|
|
959
940
|
const DrawerViewportContext = /*#__PURE__*/React__namespace.createContext(null);
|
|
960
941
|
if (process.env.NODE_ENV !== "production") DrawerViewportContext.displayName = "DrawerViewportContext";
|
|
961
|
-
function useDrawerViewportContext(
|
|
962
|
-
|
|
963
|
-
return context;
|
|
942
|
+
function useDrawerViewportContext() {
|
|
943
|
+
return React__namespace.useContext(DrawerViewportContext);
|
|
964
944
|
}
|
|
965
945
|
|
|
966
946
|
let drawerSwipeVarsRegistered = false;
|
|
@@ -1018,8 +998,7 @@ function removeCSSVariableInheritance$1() {
|
|
|
1018
998
|
drawerSwipeVarsRegistered = true;
|
|
1019
999
|
}
|
|
1020
1000
|
const stateAttributesMapping = {
|
|
1021
|
-
...useScrollLock.
|
|
1022
|
-
...useScrollLock.transitionStatusMapping,
|
|
1001
|
+
...useScrollLock.popupTransitionStateMapping,
|
|
1023
1002
|
expanded(value) {
|
|
1024
1003
|
return value ? {
|
|
1025
1004
|
[DrawerPopupDataAttributes.expanded]: ''
|
|
@@ -1036,9 +1015,9 @@ const stateAttributesMapping = {
|
|
|
1036
1015
|
} : null;
|
|
1037
1016
|
},
|
|
1038
1017
|
swipeDirection(value) {
|
|
1039
|
-
return
|
|
1018
|
+
return {
|
|
1040
1019
|
[DrawerPopupDataAttributes.swipeDirection]: value
|
|
1041
|
-
}
|
|
1020
|
+
};
|
|
1042
1021
|
},
|
|
1043
1022
|
swiping(value) {
|
|
1044
1023
|
return value ? {
|
|
@@ -1062,9 +1041,7 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
|
|
|
1062
1041
|
initialFocus,
|
|
1063
1042
|
...elementProps
|
|
1064
1043
|
} = componentProps;
|
|
1065
|
-
const
|
|
1066
|
-
store
|
|
1067
|
-
} = useDialogRootContext();
|
|
1044
|
+
const store = useDialogRootContext();
|
|
1068
1045
|
const popupRef = store.context.popupRef;
|
|
1069
1046
|
const {
|
|
1070
1047
|
swipeDirection,
|
|
@@ -1103,6 +1080,8 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
|
|
|
1103
1080
|
const swipeStrength = swipe?.swipeStrength ?? null;
|
|
1104
1081
|
const [popupHeight, setPopupHeight] = React__namespace.useState(0);
|
|
1105
1082
|
const popupHeightRef = React__namespace.useRef(0);
|
|
1083
|
+
|
|
1084
|
+
/* istanbul ignore else -- process.env.NODE_ENV is a build-time constant. */
|
|
1106
1085
|
if (process.env.NODE_ENV !== 'production') {
|
|
1107
1086
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
1108
1087
|
React__namespace.useEffect(() => {
|
|
@@ -1185,7 +1164,7 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
|
|
|
1185
1164
|
}
|
|
1186
1165
|
};
|
|
1187
1166
|
}, [nestedSwipeProgressStore, popupRef]);
|
|
1188
|
-
|
|
1167
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
1189
1168
|
if (!open) {
|
|
1190
1169
|
return undefined;
|
|
1191
1170
|
}
|
|
@@ -1194,7 +1173,7 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
|
|
|
1194
1173
|
notifyParentFrontmostHeight?.(0);
|
|
1195
1174
|
};
|
|
1196
1175
|
}, [frontmostHeight, open, notifyParentFrontmostHeight]);
|
|
1197
|
-
|
|
1176
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
1198
1177
|
if (!notifyParentHasNestedDrawer) {
|
|
1199
1178
|
return undefined;
|
|
1200
1179
|
}
|
|
@@ -1238,7 +1217,7 @@ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopu
|
|
|
1238
1217
|
let dragStyles = swipe ? swipe.getDragStyles() : useRenderElement.EMPTY_OBJECT;
|
|
1239
1218
|
if (shouldApplySnapPoints && swipeDirection === 'down') {
|
|
1240
1219
|
const baseOffset = activeSnapPointOffset ?? 0;
|
|
1241
|
-
const movementValue = Number.parseFloat(String(dragStyles[DrawerPopupCssVars.swipeMovementY]
|
|
1220
|
+
const movementValue = Number.parseFloat(String(dragStyles[DrawerPopupCssVars.swipeMovementY]));
|
|
1242
1221
|
if (swiping && Number.isFinite(movementValue)) {
|
|
1243
1222
|
dragStyles = {
|
|
1244
1223
|
...dragStyles,
|
|
@@ -1348,6 +1327,7 @@ function DrawerRoot(props) {
|
|
|
1348
1327
|
state: 'snapPoint'
|
|
1349
1328
|
});
|
|
1350
1329
|
const isNestedDrawerOpenRef = React__namespace.useRef(false);
|
|
1330
|
+
const swipeAreaActiveRef = React__namespace.useRef(false);
|
|
1351
1331
|
const setActiveSnapPoint = useScrollLock.useStableCallback((nextSnapPoint, eventDetails) => {
|
|
1352
1332
|
const resolvedEventDetails = eventDetails ?? useScrollLock.createChangeEventDetails(useScrollLock.none);
|
|
1353
1333
|
onSnapPointChange?.(nextSnapPoint, resolvedEventDetails);
|
|
@@ -1407,6 +1387,7 @@ function DrawerRoot(props) {
|
|
|
1407
1387
|
});
|
|
1408
1388
|
const contextValue = React__namespace.useMemo(() => ({
|
|
1409
1389
|
swipeDirection,
|
|
1390
|
+
swipeAreaActiveRef,
|
|
1410
1391
|
snapToSequentialPoints,
|
|
1411
1392
|
snapPoints,
|
|
1412
1393
|
activeSnapPoint: resolvedActiveSnapPoint,
|
|
@@ -1425,30 +1406,28 @@ function DrawerRoot(props) {
|
|
|
1425
1406
|
notifyParentSwipingChange,
|
|
1426
1407
|
notifyParentSwipeProgressChange,
|
|
1427
1408
|
notifyParentHasNestedDrawer
|
|
1428
|
-
}), [resolvedActiveSnapPoint, frontmostHeight, hasNestedDrawer, nestedSwiping, nestedSwipeProgressStore, notifyParentHasNestedDrawer, notifyParentSwipeProgressChange, notifyParentSwipingChange, notifyParentFrontmostHeight, onNestedDrawerPresenceChange, onNestedFrontmostHeightChange, onNestedSwipeProgressChange, onNestedSwipingChange, onPopupHeightChange, popupHeight, setActiveSnapPoint, snapPoints, snapToSequentialPoints, swipeDirection]);
|
|
1409
|
+
}), [resolvedActiveSnapPoint, frontmostHeight, hasNestedDrawer, nestedSwiping, nestedSwipeProgressStore, notifyParentHasNestedDrawer, notifyParentSwipeProgressChange, notifyParentSwipingChange, notifyParentFrontmostHeight, onNestedDrawerPresenceChange, onNestedFrontmostHeightChange, onNestedSwipeProgressChange, onNestedSwipingChange, onPopupHeightChange, popupHeight, setActiveSnapPoint, snapPoints, snapToSequentialPoints, swipeAreaActiveRef, swipeDirection]);
|
|
1429
1410
|
const resolvedChildren = typeof children === 'function' ? payload => /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
|
|
1430
1411
|
children: [_DrawerProviderReport || (_DrawerProviderReport = /*#__PURE__*/jsxRuntime.jsx(DrawerProviderReporter, {})), children(payload)]
|
|
1431
1412
|
}) : /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
|
|
1432
1413
|
children: [_DrawerProviderReport2 || (_DrawerProviderReport2 = /*#__PURE__*/jsxRuntime.jsx(DrawerProviderReporter, {})), children]
|
|
1433
1414
|
});
|
|
1415
|
+
const dialog = useRenderDialogRoot('drawer', {
|
|
1416
|
+
open: openProp,
|
|
1417
|
+
defaultOpen,
|
|
1418
|
+
onOpenChange: handleOpenChange,
|
|
1419
|
+
onOpenChangeComplete,
|
|
1420
|
+
disablePointerDismissal,
|
|
1421
|
+
modal,
|
|
1422
|
+
actionsRef,
|
|
1423
|
+
handle,
|
|
1424
|
+
triggerId: triggerIdProp,
|
|
1425
|
+
defaultTriggerId: defaultTriggerIdProp,
|
|
1426
|
+
children: resolvedChildren
|
|
1427
|
+
});
|
|
1434
1428
|
return /*#__PURE__*/jsxRuntime.jsx(DrawerRootContext.Provider, {
|
|
1435
1429
|
value: contextValue,
|
|
1436
|
-
children:
|
|
1437
|
-
value: true,
|
|
1438
|
-
children: /*#__PURE__*/jsxRuntime.jsx(DialogRoot, {
|
|
1439
|
-
open: openProp,
|
|
1440
|
-
defaultOpen: defaultOpen,
|
|
1441
|
-
onOpenChange: handleOpenChange,
|
|
1442
|
-
onOpenChangeComplete: onOpenChangeComplete,
|
|
1443
|
-
disablePointerDismissal: disablePointerDismissal,
|
|
1444
|
-
modal: modal,
|
|
1445
|
-
actionsRef: actionsRef,
|
|
1446
|
-
handle: handle,
|
|
1447
|
-
triggerId: triggerIdProp,
|
|
1448
|
-
defaultTriggerId: defaultTriggerIdProp,
|
|
1449
|
-
children: resolvedChildren
|
|
1450
|
-
})
|
|
1451
|
-
})
|
|
1430
|
+
children: dialog
|
|
1452
1431
|
});
|
|
1453
1432
|
}
|
|
1454
1433
|
function createNestedSwipeProgressStore() {
|
|
@@ -1475,36 +1454,32 @@ function createNestedSwipeProgressStore() {
|
|
|
1475
1454
|
};
|
|
1476
1455
|
}
|
|
1477
1456
|
function DrawerProviderReporter() {
|
|
1478
|
-
const drawerId = useScrollLock.useId();
|
|
1479
1457
|
const providerContext = useDrawerProviderContext();
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1458
|
+
const store = useDialogRootContext(false);
|
|
1459
|
+
const setDrawerOpen = providerContext?.setDrawerOpen;
|
|
1460
|
+
const removeDrawer = providerContext?.removeDrawer;
|
|
1483
1461
|
const open = store.useState('open');
|
|
1484
1462
|
const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');
|
|
1485
1463
|
const popupElement = store.useState('popupElement');
|
|
1486
1464
|
const isTopmost = nestedOpenDialogCount === 0;
|
|
1487
|
-
|
|
1488
|
-
if (!
|
|
1465
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
1466
|
+
if (!removeDrawer) {
|
|
1489
1467
|
return undefined;
|
|
1490
1468
|
}
|
|
1491
1469
|
return () => {
|
|
1492
|
-
|
|
1470
|
+
removeDrawer(store);
|
|
1493
1471
|
};
|
|
1494
|
-
}, [
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
}
|
|
1499
|
-
providerContext?.setDrawerOpen(drawerId, open);
|
|
1500
|
-
}, [drawerId, open, providerContext]);
|
|
1472
|
+
}, [removeDrawer, store]);
|
|
1473
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
1474
|
+
setDrawerOpen?.(store, open);
|
|
1475
|
+
}, [open, setDrawerOpen, store]);
|
|
1501
1476
|
React__namespace.useEffect(() => {
|
|
1502
1477
|
// CloseWatcher enables the Android back gesture (Chromium-only).
|
|
1503
1478
|
// Keep this Android-only for now to avoid interfering with Escape/nesting semantics on desktop due to `useDismiss`.
|
|
1504
1479
|
if (!open || !isTopmost || !useScrollLock.android) {
|
|
1505
1480
|
return undefined;
|
|
1506
1481
|
}
|
|
1507
|
-
const win =
|
|
1482
|
+
const win = useScrollLock.getWindow(popupElement);
|
|
1508
1483
|
const CloseWatcherCtor = win.CloseWatcher;
|
|
1509
1484
|
if (!CloseWatcherCtor) {
|
|
1510
1485
|
return undefined;
|
|
@@ -1525,53 +1500,11 @@ function DrawerProviderReporter() {
|
|
|
1525
1500
|
return null;
|
|
1526
1501
|
}
|
|
1527
1502
|
|
|
1528
|
-
|
|
1529
|
-
//
|
|
1530
|
-
//
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
if (axis === 'vertical') {
|
|
1534
|
-
const overflowY = style.overflowY;
|
|
1535
|
-
if (overflowY !== 'auto' && overflowY !== 'scroll') {
|
|
1536
|
-
return false;
|
|
1537
|
-
}
|
|
1538
|
-
return allowOverflowIntent ? element.clientHeight > 0 : element.scrollHeight > element.clientHeight;
|
|
1539
|
-
}
|
|
1540
|
-
const overflowX = style.overflowX;
|
|
1541
|
-
if (overflowX !== 'auto' && overflowX !== 'scroll') {
|
|
1542
|
-
return false;
|
|
1543
|
-
}
|
|
1544
|
-
return allowOverflowIntent ? element.clientWidth > 0 : element.scrollWidth > element.clientWidth;
|
|
1545
|
-
}
|
|
1546
|
-
function hasScrollableAncestor(target, root, axes) {
|
|
1547
|
-
// `getParentNode` crosses shadow boundaries (and slots), so a target inside a shadow root
|
|
1548
|
-
// still walks up to scrollable ancestors in the light DOM.
|
|
1549
|
-
let node = target;
|
|
1550
|
-
while (floatingUi_utils.isHTMLElement(node) && node !== root && !floatingUi_utils.isLastTraversableNode(node)) {
|
|
1551
|
-
for (const axis of axes) {
|
|
1552
|
-
if (isScrollable(node, axis)) {
|
|
1553
|
-
return true;
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
node = floatingUi_utils.getParentNode(node);
|
|
1557
|
-
}
|
|
1558
|
-
return false;
|
|
1559
|
-
}
|
|
1560
|
-
function findScrollableTouchTarget(target, root, axis = 'vertical', allowOverflowIntent = false) {
|
|
1561
|
-
// `getParentNode` crosses shadow boundaries (and slots), so a target inside a shadow root
|
|
1562
|
-
// still reaches a scrollable ancestor in the light DOM.
|
|
1563
|
-
let node = floatingUi_utils.isHTMLElement(target) ? target : null;
|
|
1564
|
-
while (floatingUi_utils.isHTMLElement(node) && node !== root && !floatingUi_utils.isLastTraversableNode(node)) {
|
|
1565
|
-
if (isScrollable(node, axis, allowOverflowIntent)) {
|
|
1566
|
-
return node;
|
|
1567
|
-
}
|
|
1568
|
-
node = floatingUi_utils.getParentNode(node);
|
|
1569
|
-
}
|
|
1570
|
-
return isScrollable(root, axis, allowOverflowIntent) ? root : null;
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
function getElementAtPoint(doc, x, y) {
|
|
1574
|
-
return typeof doc?.elementFromPoint === 'function' ? doc.elementFromPoint(x, y) : null;
|
|
1503
|
+
// `Document.elementFromPoint` retargets shadow content to the shadow host, which then fails
|
|
1504
|
+
// `contains()` checks against a popup inside that shadow root, so callers pass `getRootNode()`
|
|
1505
|
+
// (a document or a shadow root) rather than `ownerDocument()`.
|
|
1506
|
+
function getElementAtPoint(root, x, y) {
|
|
1507
|
+
return typeof root?.elementFromPoint === 'function' ? root.elementFromPoint(x, y) : null;
|
|
1575
1508
|
}
|
|
1576
1509
|
|
|
1577
1510
|
const DEFAULT_SWIPE_THRESHOLD = 40;
|
|
@@ -1596,7 +1529,7 @@ function getDisplacement(direction, deltaX, deltaY) {
|
|
|
1596
1529
|
}
|
|
1597
1530
|
}
|
|
1598
1531
|
function getElementTransform(element) {
|
|
1599
|
-
const computedStyle =
|
|
1532
|
+
const computedStyle = useScrollLock.getWindow(element).getComputedStyle(element);
|
|
1600
1533
|
const transform = computedStyle.transform;
|
|
1601
1534
|
let translateX = 0;
|
|
1602
1535
|
let translateY = 0;
|
|
@@ -1718,6 +1651,7 @@ function useSwipeDismiss(options) {
|
|
|
1718
1651
|
});
|
|
1719
1652
|
const swipeProgressRef = React__namespace.useRef(0);
|
|
1720
1653
|
const swipeThresholdRef = React__namespace.useRef(swipeThresholdDefault);
|
|
1654
|
+
const swipeThresholdFunctionRef = React__namespace.useRef(null);
|
|
1721
1655
|
const swipeStartTimeRef = React__namespace.useRef(null);
|
|
1722
1656
|
const lastDragSampleRef = React__namespace.useRef(null);
|
|
1723
1657
|
const lastDragVelocityRef = React__namespace.useRef({
|
|
@@ -1739,15 +1673,12 @@ function useSwipeDismiss(options) {
|
|
|
1739
1673
|
if (!direction) {
|
|
1740
1674
|
return;
|
|
1741
1675
|
}
|
|
1742
|
-
if (typeof swipeThresholdProp !== 'function') {
|
|
1743
|
-
swipeThresholdRef.current = swipeThresholdDefault;
|
|
1744
|
-
return;
|
|
1745
|
-
}
|
|
1746
1676
|
const element = elementRef.current;
|
|
1747
|
-
|
|
1677
|
+
const thresholdFunction = swipeThresholdFunctionRef.current;
|
|
1678
|
+
if (!element || !thresholdFunction) {
|
|
1748
1679
|
return;
|
|
1749
1680
|
}
|
|
1750
|
-
const value =
|
|
1681
|
+
const value = thresholdFunction({
|
|
1751
1682
|
element,
|
|
1752
1683
|
direction
|
|
1753
1684
|
});
|
|
@@ -1825,6 +1756,7 @@ function useSwipeDismiss(options) {
|
|
|
1825
1756
|
setDragDismissed(false);
|
|
1826
1757
|
updateSwipeProgress(0);
|
|
1827
1758
|
swipeThresholdRef.current = swipeThresholdDefault;
|
|
1759
|
+
swipeThresholdFunctionRef.current = null;
|
|
1828
1760
|
dragStartPosRef.current = {
|
|
1829
1761
|
x: 0,
|
|
1830
1762
|
y: 0
|
|
@@ -1865,11 +1797,6 @@ function useSwipeDismiss(options) {
|
|
|
1865
1797
|
lastProgressDetailsRef.current = null;
|
|
1866
1798
|
syncDragStyles(false);
|
|
1867
1799
|
}, [setSwiping, swipeThresholdDefault, syncDragStyles, updateSwipeProgress]);
|
|
1868
|
-
React__namespace.useEffect(() => {
|
|
1869
|
-
if (typeof swipeThresholdProp !== 'function') {
|
|
1870
|
-
swipeThresholdRef.current = swipeThresholdDefault;
|
|
1871
|
-
}
|
|
1872
|
-
}, [swipeThresholdDefault, swipeThresholdProp]);
|
|
1873
1800
|
function getPrimaryPointerPosition(event) {
|
|
1874
1801
|
if ('touches' in event) {
|
|
1875
1802
|
const touch = event.touches[0];
|
|
@@ -1890,19 +1817,19 @@ function useSwipeDismiss(options) {
|
|
|
1890
1817
|
return event.pointerType === 'touch';
|
|
1891
1818
|
}
|
|
1892
1819
|
function getTargetAtPoint(position, nativeEvent) {
|
|
1893
|
-
const
|
|
1894
|
-
const elementAtPoint = getElementAtPoint(
|
|
1820
|
+
const root = elementRef.current?.getRootNode();
|
|
1821
|
+
const elementAtPoint = getElementAtPoint(root, position.x, position.y);
|
|
1895
1822
|
const target = elementAtPoint ?? useScrollLock.getTarget(nativeEvent);
|
|
1896
1823
|
return target;
|
|
1897
1824
|
}
|
|
1898
1825
|
function findGestureScrollableTouchTarget(target, root) {
|
|
1899
1826
|
if (hasHorizontal && !hasVertical) {
|
|
1900
|
-
return findScrollableTouchTarget(target, root, 'horizontal');
|
|
1827
|
+
return scrollable.findScrollableTouchTarget(target, root, 'horizontal');
|
|
1901
1828
|
}
|
|
1902
1829
|
if (hasVertical && !hasHorizontal) {
|
|
1903
|
-
return findScrollableTouchTarget(target, root, 'vertical');
|
|
1830
|
+
return scrollable.findScrollableTouchTarget(target, root, 'vertical');
|
|
1904
1831
|
}
|
|
1905
|
-
return findScrollableTouchTarget(target, root, 'vertical') ?? findScrollableTouchTarget(target, root, 'horizontal');
|
|
1832
|
+
return scrollable.findScrollableTouchTarget(target, root, 'vertical') ?? scrollable.findScrollableTouchTarget(target, root, 'horizontal');
|
|
1906
1833
|
}
|
|
1907
1834
|
function startSwipeAtPosition(event, position, startOptions) {
|
|
1908
1835
|
swipeFromScrollableRef.current = false;
|
|
@@ -1923,7 +1850,7 @@ function useSwipeDismiss(options) {
|
|
|
1923
1850
|
const element = elementRef.current;
|
|
1924
1851
|
if (ignoreScrollableAncestors && element && target && scrollAxes.length > 0) {
|
|
1925
1852
|
const ignoreAncestors = startOptions?.ignoreScrollableAncestors ?? false;
|
|
1926
|
-
if (!ignoreAncestors && hasScrollableAncestor(target, element, scrollAxes)) {
|
|
1853
|
+
if (!ignoreAncestors && scrollable.hasScrollableAncestor(target, element, scrollAxes)) {
|
|
1927
1854
|
return false;
|
|
1928
1855
|
}
|
|
1929
1856
|
}
|
|
@@ -1934,6 +1861,8 @@ function useSwipeDismiss(options) {
|
|
|
1934
1861
|
swipeStartTimeRef.current = getValidTimeStamp(event.timeStamp);
|
|
1935
1862
|
swipeCancelBaselineRef.current = position;
|
|
1936
1863
|
lastMovePosRef.current = position;
|
|
1864
|
+
swipeThresholdRef.current = swipeThresholdDefault;
|
|
1865
|
+
swipeThresholdFunctionRef.current = typeof swipeThresholdProp === 'function' ? swipeThresholdProp : null;
|
|
1937
1866
|
if (element) {
|
|
1938
1867
|
elementSizeRef.current = {
|
|
1939
1868
|
width: element.offsetWidth,
|
|
@@ -1998,12 +1927,9 @@ function useSwipeDismiss(options) {
|
|
|
1998
1927
|
onCancel?.(event.nativeEvent);
|
|
1999
1928
|
}
|
|
2000
1929
|
function applyDirectionalDamping(deltaX, deltaY) {
|
|
2001
|
-
const exponent = value => value
|
|
1930
|
+
const exponent = value => Math.sign(value) * Math.abs(value) ** 0.5;
|
|
2002
1931
|
const dampAxis = (delta, allowNegative, allowPositive) => {
|
|
2003
|
-
if (!allowNegative && delta < 0) {
|
|
2004
|
-
return exponent(delta);
|
|
2005
|
-
}
|
|
2006
|
-
if (!allowPositive && delta > 0) {
|
|
1932
|
+
if (!allowNegative && delta < 0 || !allowPositive && delta > 0) {
|
|
2007
1933
|
return exponent(delta);
|
|
2008
1934
|
}
|
|
2009
1935
|
return delta;
|
|
@@ -2016,29 +1942,16 @@ function useSwipeDismiss(options) {
|
|
|
2016
1942
|
};
|
|
2017
1943
|
}
|
|
2018
1944
|
function canSwipeFromScrollEdgeOnPendingMove(scrollTarget, deltaX, deltaY) {
|
|
1945
|
+
// Swiping toward the axis start edge (down/right) is allowed when scrolled to the start;
|
|
1946
|
+
// toward the end edge (up/left) when scrolled to the end.
|
|
1947
|
+
const canSwipeOnAxis = (delta, scrollOffset, maxScrollOffset, allowTowardStart, allowTowardEnd) => delta > 0 && scrollOffset <= 0 && allowTowardStart || delta < 0 && scrollOffset >= Math.max(0, maxScrollOffset) && allowTowardEnd;
|
|
2019
1948
|
const absDeltaX = Math.abs(deltaX);
|
|
2020
1949
|
const absDeltaY = Math.abs(deltaY);
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
const movingDown = deltaY > 0;
|
|
2027
|
-
const movingUp = deltaY < 0;
|
|
2028
|
-
const canSwipeDown = movingDown && atTop && allowDown;
|
|
2029
|
-
const canSwipeUp = movingUp && atBottom && allowUp;
|
|
2030
|
-
return canSwipeDown || canSwipeUp;
|
|
2031
|
-
}
|
|
2032
|
-
const useHorizontalAxis = hasHorizontal && deltaX !== 0 && (!hasVertical || absDeltaX > absDeltaY);
|
|
2033
|
-
if (useHorizontalAxis) {
|
|
2034
|
-
const maxScrollLeft = Math.max(0, scrollTarget.scrollWidth - scrollTarget.clientWidth);
|
|
2035
|
-
const atLeft = scrollTarget.scrollLeft <= 0;
|
|
2036
|
-
const atRight = scrollTarget.scrollLeft >= maxScrollLeft;
|
|
2037
|
-
const movingRight = deltaX > 0;
|
|
2038
|
-
const movingLeft = deltaX < 0;
|
|
2039
|
-
const canSwipeRight = movingRight && atLeft && allowRight;
|
|
2040
|
-
const canSwipeLeft = movingLeft && atRight && allowLeft;
|
|
2041
|
-
return canSwipeRight || canSwipeLeft;
|
|
1950
|
+
if (hasVertical && deltaY !== 0 && (!hasHorizontal || absDeltaY >= absDeltaX)) {
|
|
1951
|
+
return canSwipeOnAxis(deltaY, scrollTarget.scrollTop, scrollTarget.scrollHeight - scrollTarget.clientHeight, allowDown, allowUp);
|
|
1952
|
+
}
|
|
1953
|
+
if (hasHorizontal && deltaX !== 0 && (!hasVertical || absDeltaX > absDeltaY)) {
|
|
1954
|
+
return canSwipeOnAxis(deltaX, scrollTarget.scrollLeft, scrollTarget.scrollWidth - scrollTarget.clientWidth, allowRight, allowLeft);
|
|
2042
1955
|
}
|
|
2043
1956
|
return null;
|
|
2044
1957
|
}
|
|
@@ -2059,7 +1972,7 @@ function useSwipeDismiss(options) {
|
|
|
2059
1972
|
pendingSwipeRef.current = true;
|
|
2060
1973
|
pendingSwipeStartPosRef.current = startPos;
|
|
2061
1974
|
swipeFromScrollableRef.current = false;
|
|
2062
|
-
sawPrimaryButtonsOnMoveRef.current =
|
|
1975
|
+
sawPrimaryButtonsOnMoveRef.current = !('touches' in event);
|
|
2063
1976
|
const allowedToStart = canStart ? canStart(startPos, {
|
|
2064
1977
|
nativeEvent: event.nativeEvent,
|
|
2065
1978
|
direction: primaryDirection
|
|
@@ -2087,14 +2000,21 @@ function useSwipeDismiss(options) {
|
|
|
2087
2000
|
event.preventDefault();
|
|
2088
2001
|
}
|
|
2089
2002
|
if (isFirstPointerMoveRef.current) {
|
|
2090
|
-
// Adjust the starting position to the current position on the first move
|
|
2091
|
-
// to account for the delay between pointerdown and the first pointermove on iOS.
|
|
2092
|
-
dragStartPosRef.current = position;
|
|
2093
|
-
const moveTime = getValidTimeStamp(event.timeStamp);
|
|
2094
|
-
if (moveTime !== null) {
|
|
2095
|
-
swipeStartTimeRef.current = moveTime;
|
|
2096
|
-
}
|
|
2097
2003
|
isFirstPointerMoveRef.current = false;
|
|
2004
|
+
// Reset the drag origin to the first move's position to absorb the gap between the press and
|
|
2005
|
+
// the first move event — notably on iOS touch, where the first `touchmove` arrives already
|
|
2006
|
+
// offset from the `touchstart` — which would otherwise make the dragged element jump. This
|
|
2007
|
+
// only matters when an element follows the pointer; when `trackDrag` is false (e.g. the
|
|
2008
|
+
// swipe-area, which only opens the drawer) keep the original press position so a quick flick
|
|
2009
|
+
// still registers: on a low-refresh-rate display the whole travel can land in this single
|
|
2010
|
+
// first move, and discarding it would drop the gesture.
|
|
2011
|
+
if (trackDrag) {
|
|
2012
|
+
dragStartPosRef.current = position;
|
|
2013
|
+
const moveTime = getValidTimeStamp(event.timeStamp);
|
|
2014
|
+
if (moveTime !== null) {
|
|
2015
|
+
swipeStartTimeRef.current = moveTime;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2098
2018
|
}
|
|
2099
2019
|
const clientX = position.x;
|
|
2100
2020
|
const clientY = position.y;
|
|
@@ -2182,51 +2102,45 @@ function useSwipeDismiss(options) {
|
|
|
2182
2102
|
newOffsetY += dampedDelta.y;
|
|
2183
2103
|
}
|
|
2184
2104
|
}
|
|
2105
|
+
|
|
2106
|
+
// Only rewrite drag styles when the drag offset actually changed. `syncDragStyles` writes the
|
|
2107
|
+
// raw (undamped) frozen transform and movement vars, relying on the consumer's `onProgress`
|
|
2108
|
+
// to overwrite them with damped styles — but `updateSwipeProgress` dedupes unchanged
|
|
2109
|
+
// deltas and skips `onProgress`. A move that doesn't change the offset (e.g. the cursor
|
|
2110
|
+
// pinned at a screen edge during an off-screen drag, jittering only on the ignored axis)
|
|
2111
|
+
// would otherwise reinstate the raw styles with no correction, jumping the element to the
|
|
2112
|
+
// undamped position.
|
|
2113
|
+
const previousOffset = dragOffsetRef.current;
|
|
2114
|
+
const offsetChanged = newOffsetX !== previousOffset.x || newOffsetY !== previousOffset.y;
|
|
2185
2115
|
dragOffsetRef.current = {
|
|
2186
2116
|
x: newOffsetX,
|
|
2187
2117
|
y: newOffsetY
|
|
2188
2118
|
};
|
|
2189
|
-
|
|
2119
|
+
if (offsetChanged) {
|
|
2120
|
+
syncDragStyles(true);
|
|
2121
|
+
}
|
|
2190
2122
|
recordDragSample({
|
|
2191
2123
|
x: newOffsetX,
|
|
2192
2124
|
y: newOffsetY
|
|
2193
2125
|
}, getValidTimeStamp(event.timeStamp));
|
|
2194
2126
|
const dragDeltaX = newOffsetX - initialTransformRef.current.x;
|
|
2195
2127
|
const dragDeltaY = newOffsetY - initialTransformRef.current.y;
|
|
2196
|
-
const
|
|
2197
|
-
const progressDirection = primaryDirection ?? intendedSwipeDirectionRef.current;
|
|
2198
|
-
if (!progressDirection) {
|
|
2199
|
-
updateSwipeProgress(0, {
|
|
2200
|
-
deltaX: dragDeltaX,
|
|
2201
|
-
deltaY: dragDeltaY,
|
|
2202
|
-
direction: swipeDirectionDetails
|
|
2203
|
-
});
|
|
2204
|
-
return;
|
|
2205
|
-
}
|
|
2206
|
-
const size = progressDirection === 'left' || progressDirection === 'right' ? elementSizeRef.current.width : elementSizeRef.current.height;
|
|
2207
|
-
const scale = initialTransformRef.current.scale || 1;
|
|
2208
|
-
if (size <= 0 || scale <= 0) {
|
|
2209
|
-
updateSwipeProgress(0, {
|
|
2210
|
-
deltaX: dragDeltaX,
|
|
2211
|
-
deltaY: dragDeltaY,
|
|
2212
|
-
direction: swipeDirectionDetails
|
|
2213
|
-
});
|
|
2214
|
-
return;
|
|
2215
|
-
}
|
|
2216
|
-
const progressDisplacement = getDisplacement(progressDirection, newOffsetX - initialTransformRef.current.x, newOffsetY - initialTransformRef.current.y);
|
|
2217
|
-
if (progressDisplacement <= 0) {
|
|
2218
|
-
updateSwipeProgress(0, {
|
|
2219
|
-
deltaX: dragDeltaX,
|
|
2220
|
-
deltaY: dragDeltaY,
|
|
2221
|
-
direction: swipeDirectionDetails
|
|
2222
|
-
});
|
|
2223
|
-
return;
|
|
2224
|
-
}
|
|
2225
|
-
updateSwipeProgress(progressDisplacement / (size * scale), {
|
|
2128
|
+
const progressDetails = {
|
|
2226
2129
|
deltaX: dragDeltaX,
|
|
2227
2130
|
deltaY: dragDeltaY,
|
|
2228
|
-
direction:
|
|
2229
|
-
}
|
|
2131
|
+
direction: intendedSwipeDirectionRef.current
|
|
2132
|
+
};
|
|
2133
|
+
let progress = 0;
|
|
2134
|
+
const progressDirection = primaryDirection ?? intendedSwipeDirectionRef.current;
|
|
2135
|
+
if (progressDirection) {
|
|
2136
|
+
const size = progressDirection === 'left' || progressDirection === 'right' ? elementSizeRef.current.width : elementSizeRef.current.height;
|
|
2137
|
+
const scale = initialTransformRef.current.scale || 1;
|
|
2138
|
+
const progressDisplacement = getDisplacement(progressDirection, dragDeltaX, dragDeltaY);
|
|
2139
|
+
if (size > 0 && scale > 0 && progressDisplacement > 0) {
|
|
2140
|
+
progress = progressDisplacement / (size * scale);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
updateSwipeProgress(progress, progressDetails);
|
|
2230
2144
|
}
|
|
2231
2145
|
const handleEnd = useScrollLock.useStableCallback(event => {
|
|
2232
2146
|
if (!enabled) {
|
|
@@ -2314,33 +2228,9 @@ function useSwipeDismiss(options) {
|
|
|
2314
2228
|
dismissDirection = intendedSwipeDirectionRef.current ?? primaryDirection;
|
|
2315
2229
|
} else {
|
|
2316
2230
|
for (const direction of directions) {
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
shouldClose = true;
|
|
2321
|
-
dismissDirection = 'right';
|
|
2322
|
-
}
|
|
2323
|
-
break;
|
|
2324
|
-
case 'left':
|
|
2325
|
-
if (deltaX < -swipeThresholdRef.current) {
|
|
2326
|
-
shouldClose = true;
|
|
2327
|
-
dismissDirection = 'left';
|
|
2328
|
-
}
|
|
2329
|
-
break;
|
|
2330
|
-
case 'down':
|
|
2331
|
-
if (deltaY > swipeThresholdRef.current) {
|
|
2332
|
-
shouldClose = true;
|
|
2333
|
-
dismissDirection = 'down';
|
|
2334
|
-
}
|
|
2335
|
-
break;
|
|
2336
|
-
case 'up':
|
|
2337
|
-
if (deltaY < -swipeThresholdRef.current) {
|
|
2338
|
-
shouldClose = true;
|
|
2339
|
-
dismissDirection = 'up';
|
|
2340
|
-
}
|
|
2341
|
-
break;
|
|
2342
|
-
}
|
|
2343
|
-
if (shouldClose) {
|
|
2231
|
+
if (getDisplacement(direction, deltaX, deltaY) > swipeThresholdRef.current) {
|
|
2232
|
+
shouldClose = true;
|
|
2233
|
+
dismissDirection = direction;
|
|
2344
2234
|
break;
|
|
2345
2235
|
}
|
|
2346
2236
|
}
|
|
@@ -2482,25 +2372,29 @@ function useSwipeDismiss(options) {
|
|
|
2482
2372
|
});
|
|
2483
2373
|
});
|
|
2484
2374
|
const getDragStyles = React__namespace.useCallback(() => {
|
|
2375
|
+
// Read `isSwipingRef`, not the lagging `isSwiping` state, to match the imperative writer
|
|
2376
|
+
// `syncDragStyles`. Otherwise a render that commits before `setSwiping(true)` flushes strips the
|
|
2377
|
+
// transform it just wrote, flashing the popup to its resting position for a frame.
|
|
2378
|
+
const swiping = isSwipingRef.current;
|
|
2485
2379
|
const dragOffset = dragOffsetRef.current;
|
|
2486
2380
|
const initialTransform = initialTransformRef.current;
|
|
2487
2381
|
const deltaX = dragOffset.x - initialTransform.x;
|
|
2488
2382
|
const deltaY = dragOffset.y - initialTransform.y;
|
|
2489
|
-
if (!
|
|
2383
|
+
if (!swiping && deltaX === 0 && deltaY === 0 && !dragDismissed) {
|
|
2490
2384
|
return {
|
|
2491
2385
|
[movementCssVars.x]: '0px',
|
|
2492
2386
|
[movementCssVars.y]: '0px'
|
|
2493
2387
|
};
|
|
2494
2388
|
}
|
|
2495
2389
|
return {
|
|
2496
|
-
transition:
|
|
2390
|
+
transition: swiping ? 'none' : undefined,
|
|
2497
2391
|
// While swiping, freeze the element at its current visual transform so it doesn't snap to the
|
|
2498
2392
|
// end position.
|
|
2499
|
-
transform:
|
|
2393
|
+
transform: swiping ? getDragTransform(dragOffset, initialTransform.scale) : undefined,
|
|
2500
2394
|
[movementCssVars.x]: `${deltaX}px`,
|
|
2501
2395
|
[movementCssVars.y]: `${deltaY}px`
|
|
2502
2396
|
};
|
|
2503
|
-
}, [dragDismissed,
|
|
2397
|
+
}, [dragDismissed, movementCssVars]);
|
|
2504
2398
|
const getPointerProps = React__namespace.useCallback(() => {
|
|
2505
2399
|
if (!enabled) {
|
|
2506
2400
|
return {};
|
|
@@ -2560,6 +2454,8 @@ const MIN_SWIPE_RELEASE_DURATION_MS = 80;
|
|
|
2560
2454
|
const MAX_SWIPE_RELEASE_DURATION_MS = 360;
|
|
2561
2455
|
const MIN_SWIPE_RELEASE_SCALAR = 0.1;
|
|
2562
2456
|
const MAX_SWIPE_RELEASE_SCALAR = 1;
|
|
2457
|
+
const AXIS_LOCK_SLOP = 6;
|
|
2458
|
+
const AXIS_LOCK_BIAS = 2;
|
|
2563
2459
|
const DRAWER_CONTENT_SELECTOR = `[${DRAWER_CONTENT_ATTRIBUTE}]`;
|
|
2564
2460
|
/**
|
|
2565
2461
|
* A positioning container for the drawer popup that can be made scrollable.
|
|
@@ -2575,9 +2471,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2575
2471
|
children,
|
|
2576
2472
|
...elementProps
|
|
2577
2473
|
} = props;
|
|
2578
|
-
const
|
|
2579
|
-
store
|
|
2580
|
-
} = useDialogRootContext();
|
|
2474
|
+
const store = useDialogRootContext();
|
|
2581
2475
|
const popupRef = store.context.popupRef;
|
|
2582
2476
|
const backdropRef = store.context.backdropRef;
|
|
2583
2477
|
const {
|
|
@@ -2585,7 +2479,8 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2585
2479
|
notifyParentSwipingChange,
|
|
2586
2480
|
notifyParentSwipeProgressChange,
|
|
2587
2481
|
frontmostHeight,
|
|
2588
|
-
snapToSequentialPoints
|
|
2482
|
+
snapToSequentialPoints,
|
|
2483
|
+
swipeAreaActiveRef
|
|
2589
2484
|
} = useDrawerRootContext();
|
|
2590
2485
|
const providerContext = useDrawerProviderContext();
|
|
2591
2486
|
const {
|
|
@@ -2619,30 +2514,13 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2619
2514
|
const touchScrollStateRef = React__namespace.useRef(null);
|
|
2620
2515
|
const virtualKeyboard = useDrawerVirtualKeyboardContext();
|
|
2621
2516
|
const snapPointRange = React__namespace.useMemo(() => {
|
|
2622
|
-
if (!snapPoints || snapPoints.length < 2) {
|
|
2623
|
-
return null;
|
|
2624
|
-
}
|
|
2625
|
-
if (swipeDirection !== 'down' && swipeDirection !== 'up') {
|
|
2626
|
-
return null;
|
|
2627
|
-
}
|
|
2628
|
-
if (resolvedSnapPoints.length < 2) {
|
|
2629
|
-
return null;
|
|
2630
|
-
}
|
|
2631
|
-
const offsets = resolvedSnapPoints.map(point => point.offset).filter(offset => Number.isFinite(offset)).sort((a, b) => a - b);
|
|
2632
|
-
if (offsets.length < 2) {
|
|
2517
|
+
if (!snapPoints || snapPoints.length < 2 || resolvedSnapPoints.length < 2 || swipeDirection !== 'down' && swipeDirection !== 'up') {
|
|
2633
2518
|
return null;
|
|
2634
2519
|
}
|
|
2520
|
+
const offsets = resolvedSnapPoints.map(point => point.offset).sort((a, b) => a - b);
|
|
2635
2521
|
const minOffset = offsets[0];
|
|
2636
2522
|
const nextOffset = offsets[1];
|
|
2637
|
-
const
|
|
2638
|
-
let range = nextOffset - minOffset;
|
|
2639
|
-
if (!Number.isFinite(range) || range <= 0) {
|
|
2640
|
-
const fallbackRange = maxOffset - minOffset;
|
|
2641
|
-
if (!Number.isFinite(fallbackRange) || fallbackRange <= 0) {
|
|
2642
|
-
return null;
|
|
2643
|
-
}
|
|
2644
|
-
range = fallbackRange;
|
|
2645
|
-
}
|
|
2523
|
+
const range = nextOffset - minOffset;
|
|
2646
2524
|
return {
|
|
2647
2525
|
minOffset,
|
|
2648
2526
|
range
|
|
@@ -2661,7 +2539,8 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2661
2539
|
return [swipeDirection];
|
|
2662
2540
|
}, [snapPoints, swipeDirection]);
|
|
2663
2541
|
const setSwipeDismissed = useScrollLock.useStableCallback(dismissed => {
|
|
2664
|
-
|
|
2542
|
+
popupRef.current?.toggleAttribute(DrawerPopupDataAttributes.swipeDismiss, dismissed);
|
|
2543
|
+
backdropRef.current?.toggleAttribute(DrawerPopupDataAttributes.swipeDismiss, dismissed);
|
|
2665
2544
|
});
|
|
2666
2545
|
const clearSwipeRelease = useScrollLock.useStableCallback(() => {
|
|
2667
2546
|
setSwipeDismissed(false);
|
|
@@ -2675,11 +2554,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2675
2554
|
nestedSwipeActiveRef.current = false;
|
|
2676
2555
|
notifyParentSwipingChange?.(false);
|
|
2677
2556
|
});
|
|
2678
|
-
const applySwipeProgress = useScrollLock.useStableCallback(({
|
|
2679
|
-
resolvedProgress,
|
|
2680
|
-
shouldTrackProgress,
|
|
2681
|
-
notifyParent
|
|
2682
|
-
}) => {
|
|
2557
|
+
const applySwipeProgress = useScrollLock.useStableCallback((resolvedProgress, shouldTrackProgress, notifyParent) => {
|
|
2683
2558
|
const isActive = open && !nested && shouldTrackProgress;
|
|
2684
2559
|
const swipeProgress = isActive ? resolvedProgress : 0;
|
|
2685
2560
|
const nestedSwipeProgress = open && shouldTrackProgress ? resolvedProgress : 0;
|
|
@@ -2697,78 +2572,48 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2697
2572
|
if (!backdropElement) {
|
|
2698
2573
|
return;
|
|
2699
2574
|
}
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
return;
|
|
2704
|
-
}
|
|
2705
|
-
backdropElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, `${swipeProgress}`);
|
|
2706
|
-
if (frontmostHeight > 0) {
|
|
2575
|
+
const showProgress = isActive && swipeProgress > 0;
|
|
2576
|
+
backdropElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, showProgress ? `${swipeProgress}` : '0');
|
|
2577
|
+
if (showProgress && frontmostHeight > 0) {
|
|
2707
2578
|
backdropElement.style.setProperty(DrawerPopupCssVars.height, `${frontmostHeight}px`);
|
|
2708
2579
|
} else {
|
|
2709
2580
|
backdropElement.style.removeProperty(DrawerPopupCssVars.height);
|
|
2710
2581
|
}
|
|
2711
2582
|
});
|
|
2712
|
-
function resolveSwipeRelease({
|
|
2713
|
-
direction
|
|
2714
|
-
|
|
2715
|
-
deltaY,
|
|
2716
|
-
velocityX,
|
|
2717
|
-
velocityY,
|
|
2718
|
-
releaseVelocityX,
|
|
2719
|
-
releaseVelocityY
|
|
2720
|
-
}) {
|
|
2721
|
-
if (!direction) {
|
|
2722
|
-
return null;
|
|
2723
|
-
}
|
|
2724
|
-
const popupElement = store.context.popupRef.current;
|
|
2725
|
-
if (!popupElement) {
|
|
2726
|
-
return null;
|
|
2727
|
-
}
|
|
2728
|
-
const size = direction === 'left' || direction === 'right' ? popupElement.offsetWidth : popupElement.offsetHeight;
|
|
2729
|
-
if (!Number.isFinite(size) || size <= 0) {
|
|
2583
|
+
function resolveSwipeRelease(popupElement, direction, deltaX, deltaY, velocityX, velocityY, releaseVelocityX, releaseVelocityY) {
|
|
2584
|
+
const size = getBaseSwipeSize(popupElement, direction);
|
|
2585
|
+
if (size <= 0) {
|
|
2730
2586
|
return null;
|
|
2731
2587
|
}
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
} else if (direction === 'up') {
|
|
2738
|
-
baseOffset = -snapPointBaseOffset;
|
|
2739
|
-
}
|
|
2740
|
-
const translation = baseOffset + axisDelta;
|
|
2741
|
-
const translationAlongDirection = direction === 'left' || direction === 'up' ? -translation : translation;
|
|
2588
|
+
|
|
2589
|
+
// The snap point base offset shifts the popup along the dismiss direction for both
|
|
2590
|
+
// `down` (+offset) and `up` (-offset), so it always adds to the directional translation.
|
|
2591
|
+
const snapPointBaseOffset = (direction === 'down' || direction === 'up') && snapPoints && snapPoints.length > 0 ? activeSnapPointOffset ?? 0 : 0;
|
|
2592
|
+
const translationAlongDirection = snapPointBaseOffset + getDisplacement(direction, deltaX, deltaY);
|
|
2742
2593
|
const remainingDistance = Math.max(0, size - translationAlongDirection);
|
|
2743
|
-
if (
|
|
2594
|
+
if (remainingDistance <= 0) {
|
|
2744
2595
|
return null;
|
|
2745
2596
|
}
|
|
2746
|
-
const
|
|
2747
|
-
const
|
|
2748
|
-
|
|
2749
|
-
const directionalVelocity = direction === 'left' || direction === 'up' ? -resolvedVelocity : resolvedVelocity;
|
|
2750
|
-
if (!Number.isFinite(directionalVelocity) || directionalVelocity <= MIN_SWIPE_RELEASE_VELOCITY) {
|
|
2597
|
+
const releaseVelocity = getDisplacement(direction, releaseVelocityX, releaseVelocityY);
|
|
2598
|
+
const directionalVelocity = Math.abs(releaseVelocity) > 0 ? releaseVelocity : getDisplacement(direction, velocityX, velocityY);
|
|
2599
|
+
if (directionalVelocity <= MIN_SWIPE_RELEASE_VELOCITY) {
|
|
2751
2600
|
return null;
|
|
2752
2601
|
}
|
|
2753
2602
|
const clampedVelocity = scrollEdges.clamp(directionalVelocity, MIN_SWIPE_RELEASE_VELOCITY, MAX_SWIPE_RELEASE_VELOCITY);
|
|
2603
|
+
// The gesture hook supplies finite deltas and velocities. The guards above keep the remaining
|
|
2604
|
+
// distance and divisor positive, so the duration stays within [MIN, MAX] and the resulting
|
|
2605
|
+
// scalar within (0, 1].
|
|
2754
2606
|
const durationMs = scrollEdges.clamp(remainingDistance / clampedVelocity, MIN_SWIPE_RELEASE_DURATION_MS, MAX_SWIPE_RELEASE_DURATION_MS);
|
|
2755
|
-
if (!Number.isFinite(durationMs)) {
|
|
2756
|
-
return null;
|
|
2757
|
-
}
|
|
2758
2607
|
const normalizedDuration = (durationMs - MIN_SWIPE_RELEASE_DURATION_MS) / (MAX_SWIPE_RELEASE_DURATION_MS - MIN_SWIPE_RELEASE_DURATION_MS);
|
|
2759
|
-
|
|
2760
|
-
if (!Number.isFinite(durationScalar) || durationScalar <= 0) {
|
|
2761
|
-
return null;
|
|
2762
|
-
}
|
|
2763
|
-
return durationScalar;
|
|
2608
|
+
return MIN_SWIPE_RELEASE_SCALAR + normalizedDuration * (MAX_SWIPE_RELEASE_SCALAR - MIN_SWIPE_RELEASE_SCALAR);
|
|
2764
2609
|
}
|
|
2765
2610
|
function updateNestedSwipeActive(details) {
|
|
2766
2611
|
if (nestedSwipeActiveRef.current || !details) {
|
|
2767
2612
|
return;
|
|
2768
2613
|
}
|
|
2769
2614
|
const direction = details.direction ?? swipeDirection;
|
|
2770
|
-
const delta = direction
|
|
2771
|
-
if (
|
|
2615
|
+
const delta = getDisplacement(direction, details.deltaX, details.deltaY);
|
|
2616
|
+
if (Math.abs(delta) < MIN_SWIPE_THRESHOLD) {
|
|
2772
2617
|
return;
|
|
2773
2618
|
}
|
|
2774
2619
|
nestedSwipeActiveRef.current = true;
|
|
@@ -2785,20 +2630,17 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2785
2630
|
y: DrawerPopupCssVars.swipeMovementY
|
|
2786
2631
|
},
|
|
2787
2632
|
onSwipeStart(event) {
|
|
2788
|
-
if ('touches' in event ||
|
|
2633
|
+
if ('touches' in event || event.pointerType === 'touch') {
|
|
2789
2634
|
return;
|
|
2790
2635
|
}
|
|
2791
2636
|
const popupElement = popupRef.current;
|
|
2792
|
-
if (!popupElement) {
|
|
2793
|
-
return;
|
|
2794
|
-
}
|
|
2795
2637
|
const doc = useScrollLock.ownerDocument(popupElement);
|
|
2796
2638
|
const selection = doc.getSelection?.();
|
|
2797
2639
|
if (!selection || selection.isCollapsed) {
|
|
2798
2640
|
return;
|
|
2799
2641
|
}
|
|
2800
|
-
const anchorElement =
|
|
2801
|
-
const focusElement =
|
|
2642
|
+
const anchorElement = useScrollLock.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
|
|
2643
|
+
const focusElement = useScrollLock.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
|
|
2802
2644
|
if (!useScrollLock.contains(popupElement, anchorElement) && !useScrollLock.contains(popupElement, focusElement)) {
|
|
2803
2645
|
return;
|
|
2804
2646
|
}
|
|
@@ -2823,54 +2665,38 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2823
2665
|
return false;
|
|
2824
2666
|
}
|
|
2825
2667
|
const doc = popupElement.ownerDocument;
|
|
2826
|
-
const elementAtPoint = getElementAtPoint(
|
|
2668
|
+
const elementAtPoint = getElementAtPoint(popupElement.getRootNode(), position.x, position.y);
|
|
2827
2669
|
if (!elementAtPoint || !useScrollLock.contains(popupElement, elementAtPoint)) {
|
|
2828
2670
|
return false;
|
|
2829
2671
|
}
|
|
2830
2672
|
const nativeEvent = details.nativeEvent;
|
|
2831
|
-
const touchLike = 'touches' in nativeEvent ||
|
|
2673
|
+
const touchLike = 'touches' in nativeEvent || nativeEvent.pointerType === 'touch';
|
|
2832
2674
|
if (touchLike && shouldIgnoreSwipeForTextSelection(doc, popupElement)) {
|
|
2833
2675
|
return false;
|
|
2834
2676
|
}
|
|
2835
|
-
if (nativeEvent.type === 'touchstart' && isSwipeIgnoredTarget(elementAtPoint)) {
|
|
2836
|
-
return false;
|
|
2837
|
-
}
|
|
2838
2677
|
return true;
|
|
2839
2678
|
},
|
|
2840
2679
|
onProgress(progress, details) {
|
|
2841
2680
|
updateNestedSwipeActive(details);
|
|
2842
2681
|
const hasSnapPoints = Boolean(snapPoints && snapPoints.length > 0);
|
|
2843
|
-
if (swipingRef.current && swipeDirection === 'down' && hasSnapPoints && details
|
|
2682
|
+
if (swipingRef.current && swipeDirection === 'down' && hasSnapPoints && details) {
|
|
2844
2683
|
const popupElement = store.context.popupRef.current;
|
|
2845
2684
|
if (popupElement) {
|
|
2846
2685
|
popupElement.style.removeProperty('transform');
|
|
2847
2686
|
popupElement.style.setProperty(DrawerPopupCssVars.swipeMovementY, `${getSnapPointSwipeMovement(activeSnapPointOffset ?? 0, details.deltaY)}px`);
|
|
2848
2687
|
}
|
|
2849
2688
|
}
|
|
2850
|
-
const currentDirection = details?.direction ?? swipe.swipeDirection;
|
|
2851
|
-
const isDismissSwipe = currentDirection === undefined || currentDirection === swipeDirection;
|
|
2852
|
-
const isVerticalSwipe = swipeDirection === 'down' || swipeDirection === 'up';
|
|
2853
|
-
const shouldTrackProgress = hasSnapPoints && isVerticalSwipe || !hasSnapPoints || swipeDirection === 'left' || swipeDirection === 'right' || isDismissSwipe;
|
|
2854
2689
|
let resolvedProgress = progress;
|
|
2855
2690
|
if (snapPointRange && popupHeight > 0) {
|
|
2691
|
+
const baseOffset = activeSnapPointOffset ?? snapPointRange.minOffset;
|
|
2692
|
+
const offsetToProgress = nextOffset => scrollEdges.clamp((nextOffset - snapPointRange.minOffset) / snapPointRange.range, 0, 1);
|
|
2856
2693
|
if (details && Number.isFinite(details.deltaY)) {
|
|
2857
|
-
|
|
2858
|
-
const nextOffset = scrollEdges.clamp(baseOffset + details.deltaY, 0, popupHeight);
|
|
2859
|
-
resolvedProgress = scrollEdges.clamp((nextOffset - snapPointRange.minOffset) / snapPointRange.range, 0, 1);
|
|
2694
|
+
resolvedProgress = offsetToProgress(scrollEdges.clamp(baseOffset + details.deltaY, 0, popupHeight));
|
|
2860
2695
|
} else if (snapPointProgress !== null) {
|
|
2861
2696
|
resolvedProgress = snapPointProgress;
|
|
2862
|
-
} else if (currentDirection === 'down' || currentDirection === 'up') {
|
|
2863
|
-
const displacement = progress * popupHeight;
|
|
2864
|
-
const baseOffset = activeSnapPointOffset ?? snapPointRange.minOffset;
|
|
2865
|
-
const nextOffset = currentDirection === 'down' ? baseOffset + displacement : baseOffset - displacement;
|
|
2866
|
-
resolvedProgress = scrollEdges.clamp((nextOffset - snapPointRange.minOffset) / snapPointRange.range, 0, 1);
|
|
2867
2697
|
}
|
|
2868
2698
|
}
|
|
2869
|
-
applySwipeProgress(
|
|
2870
|
-
resolvedProgress,
|
|
2871
|
-
shouldTrackProgress,
|
|
2872
|
-
notifyParent: true
|
|
2873
|
-
});
|
|
2699
|
+
applySwipeProgress(resolvedProgress, true, true);
|
|
2874
2700
|
},
|
|
2875
2701
|
onRelease({
|
|
2876
2702
|
event,
|
|
@@ -2882,30 +2708,21 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2882
2708
|
releaseVelocityX,
|
|
2883
2709
|
releaseVelocityY
|
|
2884
2710
|
}) {
|
|
2885
|
-
const
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
releaseVelocityY
|
|
2892
|
-
};
|
|
2711
|
+
const popupElement = store.context.popupRef.current;
|
|
2712
|
+
if (!popupElement) {
|
|
2713
|
+
clearSwipeRelease();
|
|
2714
|
+
return undefined;
|
|
2715
|
+
}
|
|
2716
|
+
const releasePopupElement = popupElement;
|
|
2893
2717
|
function startSwipeRelease(resolvedDirection) {
|
|
2894
2718
|
// Start ending transition styles earlier and synchronously to prevent a period where
|
|
2895
2719
|
// the popup appears stuck on release before the actual closing animation starts.
|
|
2896
|
-
const popupElement = store.context.popupRef.current;
|
|
2897
|
-
if (!popupElement) {
|
|
2898
|
-
return;
|
|
2899
|
-
}
|
|
2900
2720
|
finishNestedSwipe();
|
|
2901
2721
|
setSwipeDismissed(true);
|
|
2902
|
-
|
|
2903
|
-
|
|
2722
|
+
releasePopupElement.style.removeProperty('transition');
|
|
2723
|
+
releasePopupElement.setAttribute(useScrollLock.TransitionStatusDataAttributes.endingStyle, '');
|
|
2904
2724
|
ReactDOM__namespace.flushSync(() => {
|
|
2905
|
-
setSwipeRelease(resolveSwipeRelease(
|
|
2906
|
-
direction: resolvedDirection,
|
|
2907
|
-
...swipeReleasePayload
|
|
2908
|
-
}));
|
|
2725
|
+
setSwipeRelease(resolveSwipeRelease(releasePopupElement, resolvedDirection, deltaX, deltaY, velocityX, velocityY, releaseVelocityX, releaseVelocityY));
|
|
2909
2726
|
});
|
|
2910
2727
|
}
|
|
2911
2728
|
if (!snapPoints || snapPoints.length === 0) {
|
|
@@ -2913,29 +2730,16 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2913
2730
|
clearSwipeRelease();
|
|
2914
2731
|
return undefined;
|
|
2915
2732
|
}
|
|
2916
|
-
const
|
|
2917
|
-
if (!element) {
|
|
2918
|
-
clearSwipeRelease();
|
|
2919
|
-
return undefined;
|
|
2920
|
-
}
|
|
2921
|
-
const baseThreshold = getBaseSwipeThreshold(element, direction);
|
|
2922
|
-
const delta = direction === 'left' || direction === 'right' ? deltaX : deltaY;
|
|
2923
|
-
if (!Number.isFinite(delta)) {
|
|
2924
|
-
clearSwipeRelease();
|
|
2925
|
-
return undefined;
|
|
2926
|
-
}
|
|
2927
|
-
const directionalDelta = direction === 'left' || direction === 'up' ? -delta : delta;
|
|
2733
|
+
const directionalDelta = getDisplacement(direction, deltaX, deltaY);
|
|
2928
2734
|
if (directionalDelta <= 0) {
|
|
2929
2735
|
clearSwipeRelease();
|
|
2930
2736
|
return false;
|
|
2931
2737
|
}
|
|
2932
|
-
|
|
2933
|
-
const directionalVelocity = direction === 'left' || direction === 'up' ? -velocity : velocity;
|
|
2934
|
-
if (directionalVelocity >= FAST_SWIPE_VELOCITY && directionalDelta > 0) {
|
|
2738
|
+
if (getDisplacement(direction, velocityX, velocityY) >= FAST_SWIPE_VELOCITY) {
|
|
2935
2739
|
startSwipeRelease(direction);
|
|
2936
2740
|
return true;
|
|
2937
2741
|
}
|
|
2938
|
-
const shouldClose = directionalDelta >
|
|
2742
|
+
const shouldClose = directionalDelta > getBaseSwipeThreshold(releasePopupElement, direction);
|
|
2939
2743
|
if (shouldClose) {
|
|
2940
2744
|
startSwipeRelease(direction);
|
|
2941
2745
|
} else {
|
|
@@ -2947,20 +2751,20 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2947
2751
|
clearSwipeRelease();
|
|
2948
2752
|
return undefined;
|
|
2949
2753
|
}
|
|
2950
|
-
if (!popupHeight
|
|
2754
|
+
if (!popupHeight) {
|
|
2951
2755
|
clearSwipeRelease();
|
|
2952
|
-
return
|
|
2756
|
+
return false;
|
|
2953
2757
|
}
|
|
2954
|
-
|
|
2955
|
-
if (!Number.isFinite(dragDelta)) {
|
|
2758
|
+
if (resolvedSnapPoints.length === 0) {
|
|
2956
2759
|
clearSwipeRelease();
|
|
2957
2760
|
return undefined;
|
|
2958
2761
|
}
|
|
2762
|
+
const dragDelta = swipeDirection === 'down' ? deltaY : -deltaY;
|
|
2959
2763
|
const dragDirection = Math.sign(dragDelta);
|
|
2960
2764
|
const releaseDirectionalVelocity = swipeDirection === 'down' ? releaseVelocityY : -releaseVelocityY;
|
|
2961
2765
|
const fallbackDirectionalVelocity = swipeDirection === 'down' ? velocityY : -velocityY;
|
|
2962
|
-
let resolvedDirectionalVelocity =
|
|
2963
|
-
if (dragDirection !== 0 && Math.abs(dragDelta) >= MIN_SWIPE_THRESHOLD
|
|
2766
|
+
let resolvedDirectionalVelocity = releaseDirectionalVelocity;
|
|
2767
|
+
if (dragDirection !== 0 && Math.abs(dragDelta) >= MIN_SWIPE_THRESHOLD) {
|
|
2964
2768
|
const velocityDirection = Math.sign(resolvedDirectionalVelocity);
|
|
2965
2769
|
if (velocityDirection !== 0 && velocityDirection !== dragDirection) {
|
|
2966
2770
|
// Ignore touch reversals that would otherwise flip the snap decision.
|
|
@@ -2969,39 +2773,20 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
2969
2773
|
}
|
|
2970
2774
|
const currentOffset = activeSnapPointOffset ?? 0;
|
|
2971
2775
|
const dragTargetOffset = scrollEdges.clamp(currentOffset + dragDelta, 0, popupHeight);
|
|
2972
|
-
const velocityOffset =
|
|
2776
|
+
const velocityOffset = Math.abs(resolvedDirectionalVelocity) >= SNAP_VELOCITY_THRESHOLD ? scrollEdges.clamp(resolvedDirectionalVelocity, -MAX_SNAP_VELOCITY, MAX_SNAP_VELOCITY) * SNAP_VELOCITY_MULTIPLIER : 0;
|
|
2973
2777
|
const targetOffset = snapToSequentialPoints ? dragTargetOffset : scrollEdges.clamp(dragTargetOffset + velocityOffset, 0, popupHeight);
|
|
2974
2778
|
const snapPointEventDetails = useScrollLock.createChangeEventDetails(useScrollLock.swipe, event);
|
|
2975
2779
|
const closeFromSnapPoints = () => {
|
|
2976
2780
|
pendingSwipeCloseSnapPointRef.current = activeSnapPoint;
|
|
2977
|
-
setActiveSnapPoint
|
|
2781
|
+
setActiveSnapPoint(null, snapPointEventDetails);
|
|
2978
2782
|
startSwipeRelease(swipeDirection);
|
|
2979
2783
|
return true;
|
|
2980
2784
|
};
|
|
2981
2785
|
if (snapToSequentialPoints) {
|
|
2982
2786
|
const orderedSnapPoints = [...resolvedSnapPoints].sort((first, second) => first.offset - second.offset);
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
}
|
|
2987
|
-
let currentIndex = 0;
|
|
2988
|
-
let closestDistance = Math.abs(currentOffset - orderedSnapPoints[0].offset);
|
|
2989
|
-
for (let index = 1; index < orderedSnapPoints.length; index += 1) {
|
|
2990
|
-
const distance = Math.abs(currentOffset - orderedSnapPoints[index].offset);
|
|
2991
|
-
if (distance < closestDistance) {
|
|
2992
|
-
closestDistance = distance;
|
|
2993
|
-
currentIndex = index;
|
|
2994
|
-
}
|
|
2995
|
-
}
|
|
2996
|
-
let targetSnapPoint = orderedSnapPoints[0];
|
|
2997
|
-
closestDistance = Math.abs(targetOffset - targetSnapPoint.offset);
|
|
2998
|
-
for (const snapPoint of orderedSnapPoints) {
|
|
2999
|
-
const distance = Math.abs(targetOffset - snapPoint.offset);
|
|
3000
|
-
if (distance < closestDistance) {
|
|
3001
|
-
closestDistance = distance;
|
|
3002
|
-
targetSnapPoint = snapPoint;
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
2787
|
+
const orderedOffsets = orderedSnapPoints.map(point => point.offset);
|
|
2788
|
+
const currentIndex = closestSnapPointIndex(orderedOffsets, currentOffset);
|
|
2789
|
+
let targetSnapPoint = orderedSnapPoints[closestSnapPointIndex(orderedOffsets, targetOffset)];
|
|
3005
2790
|
const velocityDirection = Math.sign(resolvedDirectionalVelocity);
|
|
3006
2791
|
const shouldAdvance = dragDirection !== 0 && velocityDirection !== 0 && velocityDirection === dragDirection && Math.abs(resolvedDirectionalVelocity) >= SNAP_VELOCITY_THRESHOLD;
|
|
3007
2792
|
let effectiveTargetOffset = targetOffset;
|
|
@@ -3018,34 +2803,24 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3018
2803
|
return closeFromSnapPoints();
|
|
3019
2804
|
}
|
|
3020
2805
|
}
|
|
3021
|
-
const
|
|
3022
|
-
const closeDistance = Math.abs(effectiveTargetOffset - closeOffset);
|
|
2806
|
+
const closeDistance = Math.abs(effectiveTargetOffset - popupHeight);
|
|
3023
2807
|
const snapDistance = Math.abs(effectiveTargetOffset - targetSnapPoint.offset);
|
|
3024
2808
|
if (closeDistance < snapDistance) {
|
|
3025
2809
|
return closeFromSnapPoints();
|
|
3026
2810
|
}
|
|
3027
|
-
setActiveSnapPoint
|
|
2811
|
+
setActiveSnapPoint(targetSnapPoint.value, snapPointEventDetails);
|
|
3028
2812
|
clearSwipeRelease();
|
|
3029
2813
|
return false;
|
|
3030
2814
|
}
|
|
3031
2815
|
if (resolvedDirectionalVelocity >= FAST_SWIPE_VELOCITY && dragDelta > 0) {
|
|
3032
2816
|
return closeFromSnapPoints();
|
|
3033
2817
|
}
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
const distance = Math.abs(targetOffset - snapPoint.offset);
|
|
3038
|
-
if (distance < closestDistance) {
|
|
3039
|
-
closestDistance = distance;
|
|
3040
|
-
closestSnapPoint = snapPoint;
|
|
3041
|
-
}
|
|
3042
|
-
}
|
|
3043
|
-
const closeOffset = popupHeight;
|
|
3044
|
-
const closeDistance = Math.abs(targetOffset - closeOffset);
|
|
3045
|
-
if (closeDistance < closestDistance) {
|
|
2818
|
+
const closestSnapPoint = resolvedSnapPoints[closestSnapPointIndex(resolvedSnapPoints.map(point => point.offset), targetOffset)];
|
|
2819
|
+
const closeDistance = Math.abs(targetOffset - popupHeight);
|
|
2820
|
+
if (closeDistance < Math.abs(targetOffset - closestSnapPoint.offset)) {
|
|
3046
2821
|
return closeFromSnapPoints();
|
|
3047
2822
|
}
|
|
3048
|
-
setActiveSnapPoint
|
|
2823
|
+
setActiveSnapPoint(closestSnapPoint.value, snapPointEventDetails);
|
|
3049
2824
|
clearSwipeRelease();
|
|
3050
2825
|
return false;
|
|
3051
2826
|
},
|
|
@@ -3064,7 +2839,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3064
2839
|
if (dismissEventDetails.isCanceled) {
|
|
3065
2840
|
const pendingSnapPoint = pendingSwipeCloseSnapPointRef.current;
|
|
3066
2841
|
if (pendingSnapPoint !== undefined) {
|
|
3067
|
-
setActiveSnapPoint
|
|
2842
|
+
setActiveSnapPoint(pendingSnapPoint, useScrollLock.createChangeEventDetails(useScrollLock.swipe, event));
|
|
3068
2843
|
}
|
|
3069
2844
|
pendingSwipeCloseSnapPointRef.current = undefined;
|
|
3070
2845
|
resetSwipeRef.current?.();
|
|
@@ -3086,7 +2861,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3086
2861
|
// Parent rejected: revert animation and restore snap point.
|
|
3087
2862
|
const pendingSnapPoint = pendingSwipeCloseSnapPointRef.current;
|
|
3088
2863
|
if (pendingSnapPoint !== undefined) {
|
|
3089
|
-
setActiveSnapPoint
|
|
2864
|
+
setActiveSnapPoint(pendingSnapPoint, useScrollLock.createChangeEventDetails(useScrollLock.swipe, savedEvent));
|
|
3090
2865
|
}
|
|
3091
2866
|
pendingSwipeCloseSnapPointRef.current = undefined;
|
|
3092
2867
|
clearSwipeRelease();
|
|
@@ -3116,42 +2891,18 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3116
2891
|
}
|
|
3117
2892
|
const resolvedRootElement = rootElement;
|
|
3118
2893
|
const doc = useScrollLock.ownerDocument(resolvedRootElement);
|
|
3119
|
-
|
|
3120
|
-
function handleNativeTouchMove(event) {
|
|
3121
|
-
// The virtual keyboard provider observes the move to tell a tap apart from a drag.
|
|
3122
|
-
// It must run even when the swipe gesture below claims the event with
|
|
3123
|
-
// `stopPropagation()`, which would otherwise prevent React's delegated handlers
|
|
3124
|
-
// (and the provider) from ever seeing the move.
|
|
3125
|
-
virtualKeyboard?.onTouchMove(event);
|
|
3126
|
-
if (ignoreTouchSwipeRef.current) {
|
|
3127
|
-
return;
|
|
3128
|
-
}
|
|
3129
|
-
const touchState = touchScrollStateRef.current;
|
|
3130
|
-
const touch = event.touches[0];
|
|
3131
|
-
if (!touch || !touchState) {
|
|
3132
|
-
return;
|
|
3133
|
-
}
|
|
2894
|
+
function processTouchMove(event, touchState, touch) {
|
|
3134
2895
|
const drawerAxisDelta = isVerticalScrollAxis ? touch.clientY - touchState.lastY : touch.clientX - touchState.lastX;
|
|
3135
2896
|
|
|
3136
|
-
// Preserve native range interaction by never locking touchmove for range inputs.
|
|
3137
|
-
if (isEventOnRangeInput(event, win)) {
|
|
3138
|
-
touchState.allowSwipe = false;
|
|
3139
|
-
updateTouchScrollPosition(touchState, touch);
|
|
3140
|
-
return;
|
|
3141
|
-
}
|
|
3142
|
-
|
|
3143
2897
|
// Avoid blocking pinch zoom or text selection adjustments on iOS Safari.
|
|
3144
2898
|
if (event.touches.length === 2) {
|
|
3145
|
-
updateTouchScrollPosition(touchState, touch);
|
|
3146
2899
|
return;
|
|
3147
2900
|
}
|
|
3148
2901
|
const allowTouchMove = shouldIgnoreSwipeForTextSelection(doc, resolvedRootElement);
|
|
3149
2902
|
if (allowTouchMove || !open || !mounted || nestedDrawerOpen) {
|
|
3150
|
-
updateTouchScrollPosition(touchState, touch);
|
|
3151
2903
|
return;
|
|
3152
2904
|
}
|
|
3153
|
-
if (
|
|
3154
|
-
updateTouchScrollPosition(touchState, touch);
|
|
2905
|
+
if (shouldYieldTouchMove(touchState, event, touch, isVerticalScrollAxis)) {
|
|
3155
2906
|
return;
|
|
3156
2907
|
}
|
|
3157
2908
|
const scrollTarget = touchState.scrollTarget;
|
|
@@ -3163,27 +2914,21 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3163
2914
|
// move through React re-rasterizes the popup content on every frame.
|
|
3164
2915
|
event.stopPropagation();
|
|
3165
2916
|
moveSwipeNative(event, resolvedRootElement);
|
|
3166
|
-
updateTouchScrollPosition(touchState, touch);
|
|
3167
2917
|
return;
|
|
3168
2918
|
}
|
|
3169
|
-
|
|
3170
|
-
if (!hasScrollableContent) {
|
|
2919
|
+
if (!hasScrollableContentOnAxis(scrollTarget, scrollAxis)) {
|
|
3171
2920
|
// If the scroll container doesn't overflow on the drawer axis, prevent the window from
|
|
3172
2921
|
// scrolling instead.
|
|
3173
2922
|
if (event.cancelable) {
|
|
3174
2923
|
event.preventDefault();
|
|
3175
2924
|
}
|
|
3176
2925
|
event.stopPropagation();
|
|
3177
|
-
updateTouchScrollPosition(touchState, touch);
|
|
3178
2926
|
return;
|
|
3179
2927
|
}
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
const canSwipeFromScrollEdge = canSwipeFromScrollEdgeOnMove(scrollTarget, scrollAxis, swipeDirection, delta);
|
|
2928
|
+
if (drawerAxisDelta !== 0) {
|
|
2929
|
+
const canSwipeFromScrollEdge = canSwipeFromScrollEdgeOnMove(scrollTarget, scrollAxis, swipeDirection, drawerAxisDelta);
|
|
3183
2930
|
if (!touchState.allowSwipe) {
|
|
3184
|
-
if (
|
|
3185
|
-
touchState.allowSwipe = false;
|
|
3186
|
-
} else if (canSwipeFromScrollEdge) {
|
|
2931
|
+
if (event.cancelable && canSwipeFromScrollEdge) {
|
|
3187
2932
|
touchState.allowSwipe = true;
|
|
3188
2933
|
event.preventDefault();
|
|
3189
2934
|
} else {
|
|
@@ -3197,6 +2942,22 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3197
2942
|
event.stopPropagation();
|
|
3198
2943
|
moveSwipeNative(event, resolvedRootElement);
|
|
3199
2944
|
}
|
|
2945
|
+
}
|
|
2946
|
+
function handleNativeTouchMove(event) {
|
|
2947
|
+
// The virtual keyboard provider observes the move to tell a tap apart from a drag.
|
|
2948
|
+
// It must run even when the swipe gesture below claims the event with
|
|
2949
|
+
// `stopPropagation()`, which would otherwise prevent React's delegated handlers
|
|
2950
|
+
// (and the provider) from ever seeing the move.
|
|
2951
|
+
virtualKeyboard?.onTouchMove(event);
|
|
2952
|
+
if (ignoreTouchSwipeRef.current) {
|
|
2953
|
+
return;
|
|
2954
|
+
}
|
|
2955
|
+
const touchState = touchScrollStateRef.current;
|
|
2956
|
+
const touch = event.touches[0];
|
|
2957
|
+
if (!touch || !touchState) {
|
|
2958
|
+
return;
|
|
2959
|
+
}
|
|
2960
|
+
processTouchMove(event, touchState, touch);
|
|
3200
2961
|
updateTouchScrollPosition(touchState, touch);
|
|
3201
2962
|
}
|
|
3202
2963
|
return useScrollLock.addEventListener(doc, 'touchmove', handleNativeTouchMove, {
|
|
@@ -3204,18 +2965,13 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3204
2965
|
capture: true
|
|
3205
2966
|
});
|
|
3206
2967
|
}, [mounted, nestedDrawerOpen, open, popupElementState, isVerticalScrollAxis, scrollAxis, swipeDirection, moveSwipeNative, viewportElement, virtualKeyboard]);
|
|
3207
|
-
|
|
2968
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
3208
2969
|
if (!snapPointRange || swipe.swiping) {
|
|
3209
2970
|
return;
|
|
3210
2971
|
}
|
|
3211
|
-
|
|
3212
|
-
applySwipeProgress({
|
|
3213
|
-
resolvedProgress,
|
|
3214
|
-
shouldTrackProgress: true,
|
|
3215
|
-
notifyParent: false
|
|
3216
|
-
});
|
|
2972
|
+
applySwipeProgress(!open || nested ? 0 : snapPointProgress ?? 0, true, false);
|
|
3217
2973
|
}, [applySwipeProgress, frontmostHeight, nested, notifyParentSwipeProgressChange, open, snapPointProgress, snapPointRange, swipe.swiping, store, visualStateStore]);
|
|
3218
|
-
|
|
2974
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
3219
2975
|
if (!notifyParentSwipeProgressChange) {
|
|
3220
2976
|
return undefined;
|
|
3221
2977
|
}
|
|
@@ -3226,13 +2982,19 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3226
2982
|
notifyParentSwipeProgressChange(0);
|
|
3227
2983
|
};
|
|
3228
2984
|
}, [notifyParentSwipeProgressChange, open]);
|
|
3229
|
-
|
|
2985
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
3230
2986
|
if (open) {
|
|
3231
|
-
resetSwipe
|
|
2987
|
+
// Skip `resetSwipe` while `Drawer.SwipeArea` is driving the open: it zeroes the popup's
|
|
2988
|
+
// `--swipe-movement-*` (via `syncDragStyles(false)`), flashing it fully open for a frame.
|
|
2989
|
+
// `clearSwipeRelease` doesn't touch those vars, so always run it to clear any leftover
|
|
2990
|
+
// release state from a prior dismiss (e.g. when the popup is kept mounted).
|
|
2991
|
+
if (!swipeAreaActiveRef.current) {
|
|
2992
|
+
resetSwipe();
|
|
2993
|
+
}
|
|
3232
2994
|
clearSwipeRelease();
|
|
3233
2995
|
}
|
|
3234
|
-
}, [clearSwipeRelease, open, resetSwipe]);
|
|
3235
|
-
|
|
2996
|
+
}, [clearSwipeRelease, open, resetSwipe, swipeAreaActiveRef]);
|
|
2997
|
+
useScrollLock.useIsoLayoutEffect(() => {
|
|
3236
2998
|
const backdropElement = backdropRef.current;
|
|
3237
2999
|
return () => {
|
|
3238
3000
|
visualStateStore?.set({
|
|
@@ -3257,12 +3019,19 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3257
3019
|
swipeStrength: swipeRelease ?? null,
|
|
3258
3020
|
setSwipeDismissed
|
|
3259
3021
|
}), [setSwipeDismissed, swipe.getDragStyles, swipe.swiping, swipeRelease]);
|
|
3260
|
-
function
|
|
3261
|
-
ignoreTouchSwipeRef.current =
|
|
3022
|
+
function resetTouchSwipeState(ignoreSwipe) {
|
|
3023
|
+
ignoreTouchSwipeRef.current = ignoreSwipe;
|
|
3262
3024
|
touchScrollStateRef.current = null;
|
|
3025
|
+
}
|
|
3026
|
+
function resetTouchTrackingState() {
|
|
3027
|
+
resetTouchSwipeState(false);
|
|
3263
3028
|
lastPointerTypeRef.current = '';
|
|
3264
3029
|
ignoreNextTouchStartFromPenRef.current = false;
|
|
3265
3030
|
}
|
|
3031
|
+
function handlePointerEnd(event) {
|
|
3032
|
+
lastPointerTypeRef.current = '';
|
|
3033
|
+
return event.pointerType !== 'touch';
|
|
3034
|
+
}
|
|
3266
3035
|
return /*#__PURE__*/jsxRuntime.jsx(DialogViewport, {
|
|
3267
3036
|
ref: forwardedRef,
|
|
3268
3037
|
className: className,
|
|
@@ -3275,8 +3044,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3275
3044
|
if (!open || !mounted || nestedDrawerOpen) {
|
|
3276
3045
|
return;
|
|
3277
3046
|
}
|
|
3278
|
-
const
|
|
3279
|
-
const elementAtPoint = getElementAtPoint(doc, event.clientX, event.clientY);
|
|
3047
|
+
const elementAtPoint = getElementAtPoint(event.currentTarget.getRootNode(), event.clientX, event.clientY);
|
|
3280
3048
|
if (isSwipeIgnoredTarget(elementAtPoint) || isDrawerContentTarget(elementAtPoint)) {
|
|
3281
3049
|
return;
|
|
3282
3050
|
}
|
|
@@ -3292,34 +3060,24 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3292
3060
|
swipePointerProps.onPointerMove?.(event);
|
|
3293
3061
|
},
|
|
3294
3062
|
onPointerUp(event) {
|
|
3295
|
-
if (
|
|
3296
|
-
|
|
3297
|
-
}
|
|
3298
|
-
if (event.pointerType === 'touch') {
|
|
3299
|
-
return;
|
|
3063
|
+
if (handlePointerEnd(event)) {
|
|
3064
|
+
swipePointerProps.onPointerUp?.(event);
|
|
3300
3065
|
}
|
|
3301
|
-
swipePointerProps.onPointerUp?.(event);
|
|
3302
3066
|
},
|
|
3303
3067
|
onPointerCancel(event) {
|
|
3304
|
-
if (
|
|
3305
|
-
|
|
3306
|
-
}
|
|
3307
|
-
if (event.pointerType === 'touch') {
|
|
3308
|
-
return;
|
|
3068
|
+
if (handlePointerEnd(event)) {
|
|
3069
|
+
swipePointerProps.onPointerCancel?.(event);
|
|
3309
3070
|
}
|
|
3310
|
-
swipePointerProps.onPointerCancel?.(event);
|
|
3311
3071
|
},
|
|
3312
3072
|
onTouchStart(event) {
|
|
3313
3073
|
const startedFromPenPointerDown = lastPointerTypeRef.current === 'pen' && ignoreNextTouchStartFromPenRef.current;
|
|
3314
3074
|
if (startedFromPenPointerDown) {
|
|
3315
3075
|
ignoreNextTouchStartFromPenRef.current = false;
|
|
3316
|
-
|
|
3317
|
-
touchScrollStateRef.current = null;
|
|
3076
|
+
resetTouchSwipeState(false);
|
|
3318
3077
|
return;
|
|
3319
3078
|
}
|
|
3320
3079
|
if (!open || !mounted || nestedDrawerOpen) {
|
|
3321
|
-
|
|
3322
|
-
touchScrollStateRef.current = null;
|
|
3080
|
+
resetTouchSwipeState(false);
|
|
3323
3081
|
return;
|
|
3324
3082
|
}
|
|
3325
3083
|
const touch = event.touches[0];
|
|
@@ -3327,32 +3085,25 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3327
3085
|
return;
|
|
3328
3086
|
}
|
|
3329
3087
|
if (isReactTouchEventOnRangeInput(event)) {
|
|
3330
|
-
|
|
3331
|
-
touchScrollStateRef.current = null;
|
|
3088
|
+
resetTouchSwipeState(false);
|
|
3332
3089
|
return;
|
|
3333
3090
|
}
|
|
3334
|
-
const
|
|
3335
|
-
const elementAtPoint = getElementAtPoint(
|
|
3336
|
-
const rootElement = viewportElement ?? popupElementState;
|
|
3091
|
+
const rootElement = event.currentTarget;
|
|
3092
|
+
const elementAtPoint = getElementAtPoint(rootElement.getRootNode(), touch.clientX, touch.clientY);
|
|
3337
3093
|
const eventTarget = useScrollLock.getTarget(event.nativeEvent);
|
|
3338
|
-
const target =
|
|
3339
|
-
if (
|
|
3340
|
-
|
|
3341
|
-
touchScrollStateRef.current = null;
|
|
3094
|
+
const target = useScrollLock.isElement(eventTarget) ? eventTarget : rootElement;
|
|
3095
|
+
if (!useScrollLock.contains(rootElement, target)) {
|
|
3096
|
+
resetTouchSwipeState(true);
|
|
3342
3097
|
return;
|
|
3343
3098
|
}
|
|
3344
3099
|
virtualKeyboard?.onTouchStart(event);
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
touchScrollStateRef.current = null;
|
|
3100
|
+
if (isSwipeIgnoredTarget(elementAtPoint)) {
|
|
3101
|
+
resetTouchSwipeState(true);
|
|
3348
3102
|
return;
|
|
3349
3103
|
}
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
scrollTarget = findScrollableTouchTarget(target, rootElement, scrollAxis);
|
|
3354
|
-
hasCrossAxisScrollableContent = findScrollableTouchTarget(target, rootElement, crossScrollAxis) != null;
|
|
3355
|
-
}
|
|
3104
|
+
ignoreTouchSwipeRef.current = false;
|
|
3105
|
+
const scrollTarget = scrollable.findScrollableTouchTarget(target, rootElement, scrollAxis);
|
|
3106
|
+
const hasCrossAxisScrollableContent = scrollable.findScrollableTouchTarget(target, rootElement, crossScrollAxis) != null;
|
|
3356
3107
|
let allowSwipe = null;
|
|
3357
3108
|
if (scrollTarget) {
|
|
3358
3109
|
const canSwipeFromEdge = isAtSwipeStartEdge(scrollTarget, scrollAxis, swipeDirection);
|
|
@@ -3366,7 +3117,8 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3366
3117
|
scrollTarget,
|
|
3367
3118
|
hasCrossAxisScrollableContent,
|
|
3368
3119
|
allowSwipe,
|
|
3369
|
-
preserveNativeCrossAxisScroll: false
|
|
3120
|
+
preserveNativeCrossAxisScroll: false,
|
|
3121
|
+
drawerAxisAttributed: false
|
|
3370
3122
|
};
|
|
3371
3123
|
swipeTouchProps.onTouchStart?.(event);
|
|
3372
3124
|
},
|
|
@@ -3391,24 +3143,8 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
|
|
|
3391
3143
|
});
|
|
3392
3144
|
});
|
|
3393
3145
|
if (process.env.NODE_ENV !== "production") DrawerViewport.displayName = "DrawerViewport";
|
|
3394
|
-
function setSwipeDismissedElements(popupElement, backdropElement, dismissed) {
|
|
3395
|
-
if (dismissed) {
|
|
3396
|
-
popupElement?.setAttribute(DrawerPopupDataAttributes.swipeDismiss, '');
|
|
3397
|
-
backdropElement?.setAttribute(DrawerPopupDataAttributes.swipeDismiss, '');
|
|
3398
|
-
return;
|
|
3399
|
-
}
|
|
3400
|
-
popupElement?.removeAttribute(DrawerPopupDataAttributes.swipeDismiss);
|
|
3401
|
-
backdropElement?.removeAttribute(DrawerPopupDataAttributes.swipeDismiss);
|
|
3402
|
-
}
|
|
3403
3146
|
function setBackdropSwipingAttribute(backdropElement, swiping) {
|
|
3404
|
-
|
|
3405
|
-
return;
|
|
3406
|
-
}
|
|
3407
|
-
if (swiping) {
|
|
3408
|
-
backdropElement.setAttribute(DrawerPopupDataAttributes.swiping, '');
|
|
3409
|
-
return;
|
|
3410
|
-
}
|
|
3411
|
-
backdropElement.removeAttribute(DrawerPopupDataAttributes.swiping);
|
|
3147
|
+
backdropElement?.toggleAttribute(DrawerPopupDataAttributes.swiping, swiping);
|
|
3412
3148
|
}
|
|
3413
3149
|
function isSwipeIgnoredTarget(target) {
|
|
3414
3150
|
return Boolean(target?.closest(useScrollLock.BASE_UI_SWIPE_IGNORE_SELECTOR));
|
|
@@ -3416,28 +3152,26 @@ function isSwipeIgnoredTarget(target) {
|
|
|
3416
3152
|
function isDrawerContentTarget(target) {
|
|
3417
3153
|
return Boolean(target?.closest(DRAWER_CONTENT_SELECTOR));
|
|
3418
3154
|
}
|
|
3155
|
+
function getBaseSwipeSize(element, direction) {
|
|
3156
|
+
return direction === 'left' || direction === 'right' ? element.offsetWidth : element.offsetHeight;
|
|
3157
|
+
}
|
|
3419
3158
|
function getBaseSwipeThreshold(element, direction) {
|
|
3420
|
-
|
|
3421
|
-
return Math.max(size * 0.5, MIN_SWIPE_THRESHOLD);
|
|
3159
|
+
return Math.max(getBaseSwipeSize(element, direction) * 0.5, MIN_SWIPE_THRESHOLD);
|
|
3422
3160
|
}
|
|
3423
3161
|
function isRangeInput(target, win) {
|
|
3424
3162
|
return target instanceof win.HTMLInputElement && target.type === 'range';
|
|
3425
3163
|
}
|
|
3426
3164
|
function isTextSelectionControl(target) {
|
|
3427
|
-
if (!floatingUi_utils.isElement(target)) {
|
|
3428
|
-
return false;
|
|
3429
|
-
}
|
|
3430
3165
|
return target.tagName === 'INPUT' || target.tagName === 'TEXTAREA';
|
|
3431
3166
|
}
|
|
3432
3167
|
function hasExpandedSelectionWithinTarget(selection, target) {
|
|
3433
|
-
const anchorElement =
|
|
3434
|
-
const focusElement =
|
|
3168
|
+
const anchorElement = useScrollLock.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
|
|
3169
|
+
const focusElement = useScrollLock.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
|
|
3435
3170
|
return selection.containsNode(target, true) || useScrollLock.contains(target, anchorElement) || useScrollLock.contains(target, focusElement);
|
|
3436
3171
|
}
|
|
3437
3172
|
function shouldIgnoreSwipeForTextSelection(doc, rootElement) {
|
|
3438
3173
|
const activeEl = useScrollLock.activeElement(doc);
|
|
3439
|
-
|
|
3440
|
-
if (activeElementWithinRoot && isTextSelectionControl(activeEl)) {
|
|
3174
|
+
if (activeEl && useScrollLock.contains(rootElement, activeEl) && isTextSelectionControl(activeEl)) {
|
|
3441
3175
|
const {
|
|
3442
3176
|
selectionStart,
|
|
3443
3177
|
selectionEnd
|
|
@@ -3453,38 +3187,60 @@ function shouldIgnoreSwipeForTextSelection(doc, rootElement) {
|
|
|
3453
3187
|
return hasExpandedSelectionWithinTarget(selection, rootElement);
|
|
3454
3188
|
}
|
|
3455
3189
|
function isEventOnRangeInput(event, win) {
|
|
3456
|
-
|
|
3457
|
-
if (composedPath) {
|
|
3458
|
-
return composedPath.some(pathTarget => isRangeInput(pathTarget, win));
|
|
3459
|
-
}
|
|
3460
|
-
return isRangeInput(useScrollLock.getTarget(event), win);
|
|
3190
|
+
return event.composedPath().some(pathTarget => isRangeInput(pathTarget, win));
|
|
3461
3191
|
}
|
|
3462
3192
|
function isReactTouchEventOnRangeInput(event) {
|
|
3463
|
-
return isEventOnRangeInput(event.nativeEvent,
|
|
3193
|
+
return isEventOnRangeInput(event.nativeEvent, useScrollLock.getWindow(event.currentTarget));
|
|
3464
3194
|
}
|
|
3465
3195
|
function updateTouchScrollPosition(touchState, touch) {
|
|
3466
3196
|
touchState.lastX = touch.clientX;
|
|
3467
3197
|
touchState.lastY = touch.clientY;
|
|
3468
3198
|
}
|
|
3469
|
-
|
|
3199
|
+
|
|
3200
|
+
/**
|
|
3201
|
+
* Arbitrates a touchmove between the drawer swipe and a native cross-axis scroll.
|
|
3202
|
+
* Returns `true` when the move must be left alone — either because the cross axis already won the
|
|
3203
|
+
* gesture, or because neither axis has passed the slop yet and the gesture cannot be attributed.
|
|
3204
|
+
*/
|
|
3205
|
+
function shouldYieldTouchMove(touchState, event, touch, isVerticalScrollAxis) {
|
|
3470
3206
|
if (touchState.preserveNativeCrossAxisScroll) {
|
|
3471
3207
|
return true;
|
|
3472
3208
|
}
|
|
3473
|
-
|
|
3209
|
+
|
|
3210
|
+
// Attribution happens once per gesture. Re-arbitrating after the drawer axis has won would let
|
|
3211
|
+
// the pre-attribution branches below fire mid-drag (the slop is measured from the touch origin,
|
|
3212
|
+
// which is never re-baselined), freezing the popup and dropping `preventDefault()`.
|
|
3213
|
+
if (touchState.drawerAxisAttributed || touchState.allowSwipe === true || !touchState.hasCrossAxisScrollableContent) {
|
|
3474
3214
|
return false;
|
|
3475
3215
|
}
|
|
3216
|
+
|
|
3217
|
+
// A non-cancelable touchmove means the browser has already committed the gesture to a native
|
|
3218
|
+
// scroll; claiming it for the swipe would drag the popup alongside the scrolling content.
|
|
3219
|
+
if (!event.cancelable) {
|
|
3220
|
+
touchState.preserveNativeCrossAxisScroll = true;
|
|
3221
|
+
return true;
|
|
3222
|
+
}
|
|
3476
3223
|
const drawerAxisGestureDelta = isVerticalScrollAxis ? touch.clientY - touchState.startY : touch.clientX - touchState.startX;
|
|
3477
3224
|
const crossAxisGestureDelta = isVerticalScrollAxis ? touch.clientX - touchState.startX : touch.clientY - touchState.startY;
|
|
3478
3225
|
const absDrawerAxisGestureDelta = Math.abs(drawerAxisGestureDelta);
|
|
3479
3226
|
const absCrossAxisGestureDelta = Math.abs(crossAxisGestureDelta);
|
|
3480
|
-
if (absCrossAxisGestureDelta
|
|
3227
|
+
if (absCrossAxisGestureDelta >= AXIS_LOCK_SLOP && absCrossAxisGestureDelta > absDrawerAxisGestureDelta + AXIS_LOCK_BIAS) {
|
|
3228
|
+
touchState.preserveNativeCrossAxisScroll = true;
|
|
3229
|
+
return true;
|
|
3230
|
+
}
|
|
3231
|
+
if (absDrawerAxisGestureDelta >= AXIS_LOCK_SLOP) {
|
|
3232
|
+
touchState.drawerAxisAttributed = true;
|
|
3481
3233
|
return false;
|
|
3482
3234
|
}
|
|
3483
|
-
|
|
3235
|
+
|
|
3236
|
+
// Neither axis has traveled past the slop yet, so the gesture cannot be attributed. Leave the
|
|
3237
|
+
// event alone: on iOS, `preventDefault()` on the first cancelable touchmove cancels native
|
|
3238
|
+
// scrolling for the entire gesture, which would lock a cross-axis scroll that only passes the
|
|
3239
|
+
// slop on a later move.
|
|
3484
3240
|
return true;
|
|
3485
3241
|
}
|
|
3486
3242
|
function hasScrollableContentOnAxis(scrollTarget, axis) {
|
|
3487
|
-
return
|
|
3243
|
+
return getScrollMetrics(scrollTarget, axis).max > 0;
|
|
3488
3244
|
}
|
|
3489
3245
|
function getScrollMetrics(scrollTarget, axis) {
|
|
3490
3246
|
if (axis === 'vertical') {
|
|
@@ -3501,48 +3257,23 @@ function getScrollMetrics(scrollTarget, axis) {
|
|
|
3501
3257
|
};
|
|
3502
3258
|
}
|
|
3503
3259
|
function isAtSwipeStartEdge(scrollTarget, axis, direction) {
|
|
3260
|
+
const dismissFromStartEdge = shouldDismissFromStartEdge(direction, axis);
|
|
3504
3261
|
const {
|
|
3505
3262
|
offset,
|
|
3506
3263
|
max
|
|
3507
3264
|
} = getScrollMetrics(scrollTarget, axis);
|
|
3508
|
-
const dismissFromStartEdge = shouldDismissFromStartEdge(direction, axis);
|
|
3509
|
-
if (dismissFromStartEdge === null) {
|
|
3510
|
-
return false;
|
|
3511
|
-
}
|
|
3512
3265
|
return dismissFromStartEdge ? offset <= 0 : offset >= max;
|
|
3513
3266
|
}
|
|
3514
3267
|
function canSwipeFromScrollEdgeOnMove(scrollTarget, axis, direction, delta) {
|
|
3515
|
-
const {
|
|
3516
|
-
offset,
|
|
3517
|
-
max
|
|
3518
|
-
} = getScrollMetrics(scrollTarget, axis);
|
|
3519
3268
|
const dismissFromStartEdge = shouldDismissFromStartEdge(direction, axis);
|
|
3520
|
-
if (dismissFromStartEdge === null) {
|
|
3521
|
-
return false;
|
|
3522
|
-
}
|
|
3523
3269
|
const movingTowardDismiss = dismissFromStartEdge ? delta > 0 : delta < 0;
|
|
3524
3270
|
if (!movingTowardDismiss) {
|
|
3525
3271
|
return false;
|
|
3526
3272
|
}
|
|
3527
|
-
return
|
|
3273
|
+
return isAtSwipeStartEdge(scrollTarget, axis, direction);
|
|
3528
3274
|
}
|
|
3529
3275
|
function shouldDismissFromStartEdge(direction, axis) {
|
|
3530
|
-
|
|
3531
|
-
if (direction === 'down') {
|
|
3532
|
-
return true;
|
|
3533
|
-
}
|
|
3534
|
-
if (direction === 'up') {
|
|
3535
|
-
return false;
|
|
3536
|
-
}
|
|
3537
|
-
return null;
|
|
3538
|
-
}
|
|
3539
|
-
if (direction === 'right') {
|
|
3540
|
-
return true;
|
|
3541
|
-
}
|
|
3542
|
-
if (direction === 'left') {
|
|
3543
|
-
return false;
|
|
3544
|
-
}
|
|
3545
|
-
return null;
|
|
3276
|
+
return axis === 'vertical' ? direction === 'down' : direction === 'right';
|
|
3546
3277
|
}
|
|
3547
3278
|
|
|
3548
3279
|
const ScrollAreaRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
@@ -3555,20 +3286,6 @@ function useScrollAreaRootContext() {
|
|
|
3555
3286
|
return context;
|
|
3556
3287
|
}
|
|
3557
3288
|
|
|
3558
|
-
let ScrollAreaRootCssVars = /*#__PURE__*/function (ScrollAreaRootCssVars) {
|
|
3559
|
-
/**
|
|
3560
|
-
* The scroll area's corner height.
|
|
3561
|
-
* @type {number}
|
|
3562
|
-
*/
|
|
3563
|
-
ScrollAreaRootCssVars["scrollAreaCornerHeight"] = "--scroll-area-corner-height";
|
|
3564
|
-
/**
|
|
3565
|
-
* The scroll area's corner width.
|
|
3566
|
-
* @type {number}
|
|
3567
|
-
*/
|
|
3568
|
-
ScrollAreaRootCssVars["scrollAreaCornerWidth"] = "--scroll-area-corner-width";
|
|
3569
|
-
return ScrollAreaRootCssVars;
|
|
3570
|
-
}({});
|
|
3571
|
-
|
|
3572
3289
|
const SCROLL_TIMEOUT = 500;
|
|
3573
3290
|
const MIN_THUMB_SIZE = 16;
|
|
3574
3291
|
|
|
@@ -3577,108 +3294,28 @@ function getOffset(element, prop, axis) {
|
|
|
3577
3294
|
return 0;
|
|
3578
3295
|
}
|
|
3579
3296
|
const styles = getComputedStyle(element);
|
|
3580
|
-
const
|
|
3297
|
+
const key = `${prop}${axis === 'x' ? 'Inline' : 'Block'}`;
|
|
3298
|
+
const start = parseFloat(styles[`${key}Start`]);
|
|
3581
3299
|
|
|
3582
3300
|
// Safari misreports `marginInlineEnd` in RTL.
|
|
3583
3301
|
// We have to assume the start/end values are symmetrical, which is likely.
|
|
3584
3302
|
if (axis === 'x' && prop === 'margin') {
|
|
3585
|
-
return
|
|
3303
|
+
return start * 2;
|
|
3586
3304
|
}
|
|
3587
|
-
return
|
|
3305
|
+
return start + parseFloat(styles[`${key}End`]);
|
|
3588
3306
|
}
|
|
3589
3307
|
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
*/
|
|
3595
|
-
ScrollAreaScrollbarDataAttributes["orientation"] = "data-orientation";
|
|
3596
|
-
/**
|
|
3597
|
-
* Present when the pointer is over the scroll area.
|
|
3598
|
-
*/
|
|
3599
|
-
ScrollAreaScrollbarDataAttributes["hovering"] = "data-hovering";
|
|
3600
|
-
/**
|
|
3601
|
-
* Present when the user scrolls inside the scroll area.
|
|
3602
|
-
*/
|
|
3603
|
-
ScrollAreaScrollbarDataAttributes["scrolling"] = "data-scrolling";
|
|
3604
|
-
/**
|
|
3605
|
-
* Present when the scroll area content is wider than the viewport.
|
|
3606
|
-
*/
|
|
3607
|
-
ScrollAreaScrollbarDataAttributes["hasOverflowX"] = "data-has-overflow-x";
|
|
3608
|
-
/**
|
|
3609
|
-
* Present when the scroll area content is taller than the viewport.
|
|
3610
|
-
*/
|
|
3611
|
-
ScrollAreaScrollbarDataAttributes["hasOverflowY"] = "data-has-overflow-y";
|
|
3612
|
-
/**
|
|
3613
|
-
* Present when there is overflow on the horizontal start side.
|
|
3614
|
-
*/
|
|
3615
|
-
ScrollAreaScrollbarDataAttributes["overflowXStart"] = "data-overflow-x-start";
|
|
3616
|
-
/**
|
|
3617
|
-
* Present when there is overflow on the horizontal end side.
|
|
3618
|
-
*/
|
|
3619
|
-
ScrollAreaScrollbarDataAttributes["overflowXEnd"] = "data-overflow-x-end";
|
|
3620
|
-
/**
|
|
3621
|
-
* Present when there is overflow on the vertical start side.
|
|
3622
|
-
*/
|
|
3623
|
-
ScrollAreaScrollbarDataAttributes["overflowYStart"] = "data-overflow-y-start";
|
|
3624
|
-
/**
|
|
3625
|
-
* Present when there is overflow on the vertical end side.
|
|
3626
|
-
*/
|
|
3627
|
-
ScrollAreaScrollbarDataAttributes["overflowYEnd"] = "data-overflow-y-end";
|
|
3628
|
-
return ScrollAreaScrollbarDataAttributes;
|
|
3629
|
-
}({});
|
|
3630
|
-
|
|
3631
|
-
let ScrollAreaRootDataAttributes = /*#__PURE__*/function (ScrollAreaRootDataAttributes) {
|
|
3632
|
-
/**
|
|
3633
|
-
* Present when the user scrolls inside the scroll area.
|
|
3634
|
-
*/
|
|
3635
|
-
ScrollAreaRootDataAttributes["scrolling"] = "data-scrolling";
|
|
3636
|
-
/**
|
|
3637
|
-
* Present when the scroll area content is wider than the viewport.
|
|
3638
|
-
*/
|
|
3639
|
-
ScrollAreaRootDataAttributes["hasOverflowX"] = "data-has-overflow-x";
|
|
3640
|
-
/**
|
|
3641
|
-
* Present when the scroll area content is taller than the viewport.
|
|
3642
|
-
*/
|
|
3643
|
-
ScrollAreaRootDataAttributes["hasOverflowY"] = "data-has-overflow-y";
|
|
3644
|
-
/**
|
|
3645
|
-
* Present when there is overflow on the horizontal start side.
|
|
3646
|
-
*/
|
|
3647
|
-
ScrollAreaRootDataAttributes["overflowXStart"] = "data-overflow-x-start";
|
|
3648
|
-
/**
|
|
3649
|
-
* Present when there is overflow on the horizontal end side.
|
|
3650
|
-
*/
|
|
3651
|
-
ScrollAreaRootDataAttributes["overflowXEnd"] = "data-overflow-x-end";
|
|
3652
|
-
/**
|
|
3653
|
-
* Present when there is overflow on the vertical start side.
|
|
3654
|
-
*/
|
|
3655
|
-
ScrollAreaRootDataAttributes["overflowYStart"] = "data-overflow-y-start";
|
|
3656
|
-
/**
|
|
3657
|
-
* Present when there is overflow on the vertical end side.
|
|
3658
|
-
*/
|
|
3659
|
-
ScrollAreaRootDataAttributes["overflowYEnd"] = "data-overflow-y-end";
|
|
3660
|
-
return ScrollAreaRootDataAttributes;
|
|
3661
|
-
}({});
|
|
3662
|
-
|
|
3308
|
+
// Data-attribute strings inlined so `ScrollAreaRootDataAttributes` tree-shakes out.
|
|
3309
|
+
const attr = name => value => value ? {
|
|
3310
|
+
[name]: ''
|
|
3311
|
+
} : null;
|
|
3663
3312
|
const scrollAreaStateAttributesMapping = {
|
|
3664
|
-
hasOverflowX:
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
overflowXStart: value => value ? {
|
|
3671
|
-
[ScrollAreaRootDataAttributes.overflowXStart]: ''
|
|
3672
|
-
} : null,
|
|
3673
|
-
overflowXEnd: value => value ? {
|
|
3674
|
-
[ScrollAreaRootDataAttributes.overflowXEnd]: ''
|
|
3675
|
-
} : null,
|
|
3676
|
-
overflowYStart: value => value ? {
|
|
3677
|
-
[ScrollAreaRootDataAttributes.overflowYStart]: ''
|
|
3678
|
-
} : null,
|
|
3679
|
-
overflowYEnd: value => value ? {
|
|
3680
|
-
[ScrollAreaRootDataAttributes.overflowYEnd]: ''
|
|
3681
|
-
} : null,
|
|
3313
|
+
hasOverflowX: attr('data-has-overflow-x'),
|
|
3314
|
+
hasOverflowY: attr('data-has-overflow-y'),
|
|
3315
|
+
overflowXStart: attr('data-overflow-x-start'),
|
|
3316
|
+
overflowXEnd: attr('data-overflow-x-end'),
|
|
3317
|
+
overflowYStart: attr('data-overflow-y-start'),
|
|
3318
|
+
overflowYEnd: attr('data-overflow-y-end'),
|
|
3682
3319
|
cornerHidden: () => null
|
|
3683
3320
|
};
|
|
3684
3321
|
|
|
@@ -3744,99 +3381,145 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
|
|
|
3744
3381
|
const thumbYRef = React__namespace.useRef(null);
|
|
3745
3382
|
const thumbXRef = React__namespace.useRef(null);
|
|
3746
3383
|
const cornerRef = React__namespace.useRef(null);
|
|
3747
|
-
const
|
|
3384
|
+
const activePointerIdRef = React__namespace.useRef(null);
|
|
3748
3385
|
const startYRef = React__namespace.useRef(0);
|
|
3749
3386
|
const startXRef = React__namespace.useRef(0);
|
|
3750
3387
|
const startScrollTopRef = React__namespace.useRef(0);
|
|
3751
3388
|
const startScrollLeftRef = React__namespace.useRef(0);
|
|
3752
3389
|
const currentOrientationRef = React__namespace.useRef('vertical');
|
|
3753
3390
|
const scrollPositionRef = React__namespace.useRef(DEFAULT_COORDS);
|
|
3391
|
+
const savedSnapTypeRef = React__namespace.useRef(null);
|
|
3392
|
+
function startScrolling(vertical) {
|
|
3393
|
+
const setScrolling = vertical ? setScrollingY : setScrollingX;
|
|
3394
|
+
const timeout = vertical ? scrollYTimeout : scrollXTimeout;
|
|
3395
|
+
setScrolling(true);
|
|
3396
|
+
timeout.start(SCROLL_TIMEOUT, () => {
|
|
3397
|
+
setScrolling(false);
|
|
3398
|
+
});
|
|
3399
|
+
}
|
|
3754
3400
|
const handleScroll = useScrollLock.useStableCallback(scrollPosition => {
|
|
3755
3401
|
const offsetX = scrollPosition.x - scrollPositionRef.current.x;
|
|
3756
3402
|
const offsetY = scrollPosition.y - scrollPositionRef.current.y;
|
|
3757
3403
|
scrollPositionRef.current = scrollPosition;
|
|
3758
3404
|
if (offsetY !== 0) {
|
|
3759
|
-
|
|
3760
|
-
scrollYTimeout.start(SCROLL_TIMEOUT, () => {
|
|
3761
|
-
setScrollingY(false);
|
|
3762
|
-
});
|
|
3405
|
+
startScrolling(true);
|
|
3763
3406
|
}
|
|
3764
3407
|
if (offsetX !== 0) {
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3408
|
+
startScrolling(false);
|
|
3409
|
+
}
|
|
3410
|
+
});
|
|
3411
|
+
|
|
3412
|
+
// CSS scroll snap forces every programmatic scroll to land on a snap
|
|
3413
|
+
// point, making thumb dragging jump between snap points. Native
|
|
3414
|
+
// scrollbars suppress snapping while dragging, so disable it until the
|
|
3415
|
+
// pointer is released; restoring the value re-snaps the viewport. The
|
|
3416
|
+
// save is guarded so a second pointer during an active drag can't
|
|
3417
|
+
// clobber the saved value with `none`.
|
|
3418
|
+
const disableViewportSnap = useScrollLock.useStableCallback(() => {
|
|
3419
|
+
const viewportEl = viewportRef.current;
|
|
3420
|
+
if (viewportEl && savedSnapTypeRef.current === null) {
|
|
3421
|
+
savedSnapTypeRef.current = viewportEl.style.scrollSnapType;
|
|
3422
|
+
viewportEl.style.scrollSnapType = 'none';
|
|
3769
3423
|
}
|
|
3770
3424
|
});
|
|
3771
3425
|
const handlePointerDown = useScrollLock.useStableCallback(event => {
|
|
3772
3426
|
if (event.button !== 0) {
|
|
3773
3427
|
return;
|
|
3774
3428
|
}
|
|
3775
|
-
|
|
3429
|
+
if (activePointerIdRef.current !== null) {
|
|
3430
|
+
const activeThumb = currentOrientationRef.current === 'vertical' ? thumbYRef.current : thumbXRef.current;
|
|
3431
|
+
// A live drag holds capture for the active pointer — ignore other pointers.
|
|
3432
|
+
// No capture means the release went missing entirely (silent capture drop
|
|
3433
|
+
// with an id that never reappears, e.g. a lost touch contact), so let the
|
|
3434
|
+
// new pointer take over the latch instead of leaving dragging dead.
|
|
3435
|
+
if (activeThumb?.hasPointerCapture(activePointerIdRef.current)) {
|
|
3436
|
+
return;
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
activePointerIdRef.current = event.pointerId;
|
|
3776
3440
|
startYRef.current = event.clientY;
|
|
3777
3441
|
startXRef.current = event.clientX;
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
if (
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
thumbXRef.current.setPointerCapture(event.pointerId);
|
|
3442
|
+
// Literal instead of `ScrollAreaScrollbarDataAttributes.orientation`: referencing an
|
|
3443
|
+
// enum member retains its whole object in the bundle, so the strings are inlined and
|
|
3444
|
+
// the enums kept for docs only.
|
|
3445
|
+
currentOrientationRef.current = event.currentTarget.getAttribute('data-orientation');
|
|
3446
|
+
const viewportEl = viewportRef.current;
|
|
3447
|
+
if (viewportEl) {
|
|
3448
|
+
startScrollTopRef.current = viewportEl.scrollTop;
|
|
3449
|
+
startScrollLeftRef.current = viewportEl.scrollLeft;
|
|
3450
|
+
disableViewportSnap();
|
|
3788
3451
|
}
|
|
3452
|
+
const thumb = currentOrientationRef.current === 'vertical' ? thumbYRef.current : thumbXRef.current;
|
|
3453
|
+
thumb?.setPointerCapture(event.pointerId);
|
|
3789
3454
|
});
|
|
3790
|
-
const
|
|
3791
|
-
if (
|
|
3455
|
+
const handlePointerUp = useScrollLock.useStableCallback(event => {
|
|
3456
|
+
if (event.pointerId !== activePointerIdRef.current) {
|
|
3792
3457
|
return;
|
|
3793
3458
|
}
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
const scrollbarYOffset = getOffset(scrollbarYRef.current, 'padding', 'y');
|
|
3803
|
-
const thumbYOffset = getOffset(thumbYRef.current, 'margin', 'y');
|
|
3804
|
-
const thumbHeight = thumbYRef.current.offsetHeight;
|
|
3805
|
-
const maxThumbOffsetY = scrollbarYRef.current.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset;
|
|
3806
|
-
const scrollRatioY = deltaY / maxThumbOffsetY;
|
|
3807
|
-
viewportRef.current.scrollTop = startScrollTopRef.current + scrollRatioY * (scrollableContentHeight - viewportHeight);
|
|
3808
|
-
event.preventDefault();
|
|
3809
|
-
setScrollingY(true);
|
|
3810
|
-
scrollYTimeout.start(SCROLL_TIMEOUT, () => {
|
|
3811
|
-
setScrollingY(false);
|
|
3812
|
-
});
|
|
3813
|
-
}
|
|
3814
|
-
if (thumbXRef.current && scrollbarXRef.current && currentOrientationRef.current === 'horizontal') {
|
|
3815
|
-
const scrollbarXOffset = getOffset(scrollbarXRef.current, 'padding', 'x');
|
|
3816
|
-
const thumbXOffset = getOffset(thumbXRef.current, 'margin', 'x');
|
|
3817
|
-
const thumbWidth = thumbXRef.current.offsetWidth;
|
|
3818
|
-
const maxThumbOffsetX = scrollbarXRef.current.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset;
|
|
3819
|
-
const scrollRatioX = deltaX / maxThumbOffsetX;
|
|
3820
|
-
viewportRef.current.scrollLeft = startScrollLeftRef.current + scrollRatioX * (scrollableContentWidth - viewportWidth);
|
|
3821
|
-
event.preventDefault();
|
|
3822
|
-
setScrollingX(true);
|
|
3823
|
-
scrollXTimeout.start(SCROLL_TIMEOUT, () => {
|
|
3824
|
-
setScrollingX(false);
|
|
3825
|
-
});
|
|
3459
|
+
activePointerIdRef.current = null;
|
|
3460
|
+
// Clear the drag's scrolling state immediately rather than waiting for the
|
|
3461
|
+
// `SCROLL_TIMEOUT` timer armed by the last drag move, so every release path
|
|
3462
|
+
// (real, `pointercancel`, or the missed-release fallback) behaves the same.
|
|
3463
|
+
(currentOrientationRef.current === 'vertical' ? setScrollingY : setScrollingX)(false);
|
|
3464
|
+
if (savedSnapTypeRef.current !== null) {
|
|
3465
|
+
if (viewportRef.current) {
|
|
3466
|
+
viewportRef.current.style.scrollSnapType = savedSnapTypeRef.current;
|
|
3826
3467
|
}
|
|
3468
|
+
savedSnapTypeRef.current = null;
|
|
3827
3469
|
}
|
|
3828
|
-
|
|
3829
|
-
const handlePointerUp = useScrollLock.useStableCallback(event => {
|
|
3830
|
-
thumbDraggingRef.current = false;
|
|
3831
|
-
|
|
3470
|
+
const thumb = currentOrientationRef.current === 'vertical' ? thumbYRef.current : thumbXRef.current;
|
|
3832
3471
|
// `pointercancel` releases capture implicitly, so guard against releasing a
|
|
3833
3472
|
// capture we no longer hold (which would throw).
|
|
3834
|
-
if (
|
|
3835
|
-
|
|
3473
|
+
if (thumb?.hasPointerCapture(event.pointerId)) {
|
|
3474
|
+
thumb.releasePointerCapture(event.pointerId);
|
|
3836
3475
|
}
|
|
3837
|
-
|
|
3838
|
-
|
|
3476
|
+
});
|
|
3477
|
+
const handlePointerMove = useScrollLock.useStableCallback(event => {
|
|
3478
|
+
if (event.pointerId !== activePointerIdRef.current) {
|
|
3479
|
+
return;
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
// The release can go missing entirely (e.g. the browser drops pointer
|
|
3483
|
+
// capture while the scrollbar is hidden mid-drag), leaving the drag
|
|
3484
|
+
// latched so a buttonless hover over the thumb scrolls the viewport.
|
|
3485
|
+
// Treat a move without the primary button held (`buttons` bit 1 unset)
|
|
3486
|
+
// as the missed release.
|
|
3487
|
+
if (event.buttons % 2 === 0) {
|
|
3488
|
+
handlePointerUp(event);
|
|
3489
|
+
return;
|
|
3839
3490
|
}
|
|
3491
|
+
const viewportEl = viewportRef.current;
|
|
3492
|
+
if (!viewportEl) {
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
3495
|
+
const vertical = currentOrientationRef.current === 'vertical';
|
|
3496
|
+
const thumbEl = vertical ? thumbYRef.current : thumbXRef.current;
|
|
3497
|
+
const scrollbarEl = vertical ? scrollbarYRef.current : scrollbarXRef.current;
|
|
3498
|
+
if (!thumbEl || !scrollbarEl) {
|
|
3499
|
+
return;
|
|
3500
|
+
}
|
|
3501
|
+
const axis = vertical ? 'y' : 'x';
|
|
3502
|
+
const scrollbarOffset = getOffset(scrollbarEl, 'padding', axis);
|
|
3503
|
+
const thumbOffset = getOffset(thumbEl, 'margin', axis);
|
|
3504
|
+
const thumbSizePx = vertical ? thumbEl.offsetHeight : thumbEl.offsetWidth;
|
|
3505
|
+
const trackSize = vertical ? scrollbarEl.offsetHeight : scrollbarEl.offsetWidth;
|
|
3506
|
+
const maxThumbOffset = trackSize - thumbSizePx - scrollbarOffset - thumbOffset;
|
|
3507
|
+
// A short or heavily padded track can drive `maxThumbOffset` to zero or
|
|
3508
|
+
// negative once the thumb hits its `MIN_THUMB_SIZE` floor. Dividing by it
|
|
3509
|
+
// would yield a non-finite (`Infinity`/`NaN`) or inverted scroll position.
|
|
3510
|
+
const delta = vertical ? event.clientY - startYRef.current : event.clientX - startXRef.current;
|
|
3511
|
+
const scrollRatio = maxThumbOffset <= 0 ? 0 : delta / maxThumbOffset;
|
|
3512
|
+
const scrollableSize = vertical ? viewportEl.scrollHeight : viewportEl.scrollWidth;
|
|
3513
|
+
const viewportSize = vertical ? viewportEl.clientHeight : viewportEl.clientWidth;
|
|
3514
|
+
const startScroll = vertical ? startScrollTopRef.current : startScrollLeftRef.current;
|
|
3515
|
+
const nextScroll = startScroll + scrollRatio * (scrollableSize - viewportSize);
|
|
3516
|
+
if (vertical) {
|
|
3517
|
+
viewportEl.scrollTop = nextScroll;
|
|
3518
|
+
} else {
|
|
3519
|
+
viewportEl.scrollLeft = nextScroll;
|
|
3520
|
+
}
|
|
3521
|
+
event.preventDefault();
|
|
3522
|
+
startScrolling(vertical);
|
|
3840
3523
|
});
|
|
3841
3524
|
function handleTouchModalityChange(event) {
|
|
3842
3525
|
setTouchModality(event.pointerType === 'touch');
|
|
@@ -3868,8 +3551,8 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
|
|
|
3868
3551
|
},
|
|
3869
3552
|
style: {
|
|
3870
3553
|
position: 'relative',
|
|
3871
|
-
[
|
|
3872
|
-
[
|
|
3554
|
+
['--scroll-area-corner-height']: `${cornerSize.height}px`,
|
|
3555
|
+
['--scroll-area-corner-width']: `${cornerSize.width}px`
|
|
3873
3556
|
}
|
|
3874
3557
|
};
|
|
3875
3558
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
@@ -3883,6 +3566,7 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
|
|
|
3883
3566
|
handlePointerMove,
|
|
3884
3567
|
handlePointerUp,
|
|
3885
3568
|
handleScroll,
|
|
3569
|
+
disableViewportSnap,
|
|
3886
3570
|
cornerSize,
|
|
3887
3571
|
setCornerSize,
|
|
3888
3572
|
thumbSize,
|
|
@@ -3892,13 +3576,10 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
|
|
|
3892
3576
|
touchModality,
|
|
3893
3577
|
cornerRef,
|
|
3894
3578
|
scrollingX,
|
|
3895
|
-
setScrollingX,
|
|
3896
3579
|
scrollingY,
|
|
3897
|
-
setScrollingY,
|
|
3898
3580
|
hovering,
|
|
3899
3581
|
setHovering,
|
|
3900
3582
|
viewportRef,
|
|
3901
|
-
rootRef,
|
|
3902
3583
|
scrollbarYRef,
|
|
3903
3584
|
scrollbarXRef,
|
|
3904
3585
|
thumbYRef,
|
|
@@ -3915,7 +3596,7 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
|
|
|
3915
3596
|
yStart,
|
|
3916
3597
|
yEnd
|
|
3917
3598
|
}
|
|
3918
|
-
}), [handlePointerDown, handlePointerMove, handlePointerUp, handleScroll, cornerSize, thumbSize, hasMeasuredScrollbar, touchModality, scrollingX,
|
|
3599
|
+
}), [handlePointerDown, handlePointerMove, handlePointerUp, handleScroll, disableViewportSnap, cornerSize, thumbSize, hasMeasuredScrollbar, touchModality, scrollingX, scrollingY, hovering, rootId, hiddenState, overflowEdges, state, xStart, xEnd, yStart, yEnd]);
|
|
3919
3600
|
return /*#__PURE__*/jsxRuntime.jsxs(ScrollAreaRootContext.Provider, {
|
|
3920
3601
|
value: contextValue,
|
|
3921
3602
|
children: [!disableStyleElements && scrollEdges.styleDisableScrollbar.getElement(nonce), element]
|
|
@@ -3923,20 +3604,17 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
|
|
|
3923
3604
|
});
|
|
3924
3605
|
if (process.env.NODE_ENV !== "production") ScrollAreaRoot.displayName = "ScrollAreaRoot";
|
|
3925
3606
|
function normalizeOverflowEdgeThreshold(threshold) {
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
yEnd: value
|
|
3933
|
-
};
|
|
3934
|
-
}
|
|
3607
|
+
const thresholds = typeof threshold === 'number' ? {
|
|
3608
|
+
xStart: threshold,
|
|
3609
|
+
xEnd: threshold,
|
|
3610
|
+
yStart: threshold,
|
|
3611
|
+
yEnd: threshold
|
|
3612
|
+
} : threshold;
|
|
3935
3613
|
return {
|
|
3936
|
-
xStart: Math.max(0,
|
|
3937
|
-
xEnd: Math.max(0,
|
|
3938
|
-
yStart: Math.max(0,
|
|
3939
|
-
yEnd: Math.max(0,
|
|
3614
|
+
xStart: Math.max(0, thresholds?.xStart || 0),
|
|
3615
|
+
xEnd: Math.max(0, thresholds?.xEnd || 0),
|
|
3616
|
+
yStart: Math.max(0, thresholds?.yStart || 0),
|
|
3617
|
+
yEnd: Math.max(0, thresholds?.yEnd || 0)
|
|
3940
3618
|
};
|
|
3941
3619
|
}
|
|
3942
3620
|
|
|
@@ -3950,30 +3628,10 @@ function useScrollAreaViewportContext() {
|
|
|
3950
3628
|
return context;
|
|
3951
3629
|
}
|
|
3952
3630
|
|
|
3953
|
-
|
|
3954
|
-
/**
|
|
3955
|
-
* The distance from the horizontal start edge in pixels.
|
|
3956
|
-
* @type {number}
|
|
3957
|
-
*/
|
|
3958
|
-
ScrollAreaViewportCssVars["scrollAreaOverflowXStart"] = "--scroll-area-overflow-x-start";
|
|
3959
|
-
/**
|
|
3960
|
-
* The distance from the horizontal end edge in pixels.
|
|
3961
|
-
* @type {number}
|
|
3962
|
-
*/
|
|
3963
|
-
ScrollAreaViewportCssVars["scrollAreaOverflowXEnd"] = "--scroll-area-overflow-x-end";
|
|
3964
|
-
/**
|
|
3965
|
-
* The distance from the vertical start edge in pixels.
|
|
3966
|
-
* @type {number}
|
|
3967
|
-
*/
|
|
3968
|
-
ScrollAreaViewportCssVars["scrollAreaOverflowYStart"] = "--scroll-area-overflow-y-start";
|
|
3969
|
-
/**
|
|
3970
|
-
* The distance from the vertical end edge in pixels.
|
|
3971
|
-
* @type {number}
|
|
3972
|
-
*/
|
|
3973
|
-
ScrollAreaViewportCssVars["scrollAreaOverflowYEnd"] = "--scroll-area-overflow-y-end";
|
|
3974
|
-
return ScrollAreaViewportCssVars;
|
|
3975
|
-
}({});
|
|
3631
|
+
const OVERFLOW_EDGE_VARS = ['--scroll-area-overflow-x-start', '--scroll-area-overflow-x-end', '--scroll-area-overflow-y-start', '--scroll-area-overflow-y-end'];
|
|
3976
3632
|
|
|
3633
|
+
// Module-level flag to ensure we only register the CSS properties once,
|
|
3634
|
+
// regardless of how many Scroll Area components are mounted.
|
|
3977
3635
|
let scrollAreaOverflowVarsRegistered = false;
|
|
3978
3636
|
|
|
3979
3637
|
/**
|
|
@@ -3992,7 +3650,7 @@ function removeCSSVariableInheritance() {
|
|
|
3992
3650
|
return;
|
|
3993
3651
|
}
|
|
3994
3652
|
if (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {
|
|
3995
|
-
|
|
3653
|
+
OVERFLOW_EDGE_VARS.forEach(name => {
|
|
3996
3654
|
try {
|
|
3997
3655
|
CSS.registerProperty({
|
|
3998
3656
|
name,
|
|
@@ -4036,12 +3694,11 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
|
|
|
4036
3694
|
hiddenState,
|
|
4037
3695
|
setHasMeasuredScrollbar,
|
|
4038
3696
|
handleScroll,
|
|
3697
|
+
touchModality,
|
|
4039
3698
|
setHovering,
|
|
4040
3699
|
setOverflowEdges,
|
|
4041
|
-
overflowEdges,
|
|
4042
3700
|
overflowEdgeThreshold,
|
|
4043
|
-
|
|
4044
|
-
scrollingY
|
|
3701
|
+
viewportState
|
|
4045
3702
|
} = useScrollAreaRootContext();
|
|
4046
3703
|
const direction = useScrollLock.useDirection();
|
|
4047
3704
|
const programmaticScrollRef = React__namespace.useRef(true);
|
|
@@ -4086,18 +3743,12 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
|
|
|
4086
3743
|
let scrollLeftFromStart = 0;
|
|
4087
3744
|
let scrollLeftFromEnd = 0;
|
|
4088
3745
|
if (!scrollbarXHidden) {
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
rawScrollLeftFromStart = scrollEdges.clamp(-scrollLeft, 0, maxScrollLeft);
|
|
4092
|
-
} else {
|
|
4093
|
-
rawScrollLeftFromStart = scrollEdges.clamp(scrollLeft, 0, maxScrollLeft);
|
|
4094
|
-
}
|
|
4095
|
-
scrollLeftFromStart = scrollEdges.normalizeScrollOffset(rawScrollLeftFromStart, maxScrollLeft);
|
|
3746
|
+
// `normalizeScrollOffset` clamps internally.
|
|
3747
|
+
scrollLeftFromStart = scrollEdges.normalizeScrollOffset(direction === 'rtl' ? -scrollLeft : scrollLeft, maxScrollLeft);
|
|
4096
3748
|
scrollLeftFromEnd = maxScrollLeft - scrollLeftFromStart;
|
|
4097
3749
|
}
|
|
4098
|
-
const
|
|
4099
|
-
const
|
|
4100
|
-
const scrollTopFromEnd = !scrollbarYHidden ? maxScrollTop - scrollTopFromStart : 0;
|
|
3750
|
+
const scrollTopFromStart = scrollbarYHidden ? 0 : scrollEdges.normalizeScrollOffset(scrollTop, maxScrollTop);
|
|
3751
|
+
const scrollTopFromEnd = scrollbarYHidden ? 0 : maxScrollTop - scrollTopFromStart;
|
|
4101
3752
|
const nextWidth = scrollbarXHidden ? 0 : viewportWidth;
|
|
4102
3753
|
const nextHeight = scrollbarYHidden ? 0 : viewportHeight;
|
|
4103
3754
|
let nextCornerWidth = 0;
|
|
@@ -4122,76 +3773,53 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
|
|
|
4122
3773
|
const maxNextHeight = scrollbarYEl ? Math.min(scrollbarYEl.offsetHeight - cornerHeightOffset, idealNextHeight) : idealNextHeight;
|
|
4123
3774
|
const clampedNextWidth = Math.max(MIN_THUMB_SIZE, maxNextWidth * ratioX);
|
|
4124
3775
|
const clampedNextHeight = Math.max(MIN_THUMB_SIZE, maxNextHeight * ratioY);
|
|
4125
|
-
setThumbSize(prevSize => {
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
return {
|
|
4130
|
-
width: clampedNextWidth,
|
|
4131
|
-
height: clampedNextHeight
|
|
4132
|
-
};
|
|
4133
|
-
});
|
|
3776
|
+
setThumbSize(prevSize => pickState(prevSize, {
|
|
3777
|
+
width: clampedNextWidth,
|
|
3778
|
+
height: clampedNextHeight
|
|
3779
|
+
}));
|
|
4134
3780
|
|
|
4135
3781
|
// Handle Y (vertical) scroll
|
|
4136
3782
|
if (scrollbarYEl && thumbYEl) {
|
|
4137
3783
|
const maxThumbOffsetY = scrollbarYEl.offsetHeight - clampedNextHeight - scrollbarYOffset - thumbYOffset;
|
|
4138
|
-
const
|
|
4139
|
-
const scrollRatioY = scrollRangeY === 0 ? 0 : scrollTop / scrollRangeY;
|
|
4140
|
-
|
|
4141
|
-
// In Safari, don't allow it to go negative or too far as `scrollTop` considers the rubber
|
|
4142
|
-
// band effect.
|
|
4143
|
-
const thumbOffsetY = Math.min(maxThumbOffsetY, Math.max(0, scrollRatioY * maxThumbOffsetY));
|
|
3784
|
+
const thumbOffsetY = applyOverscrollThumb(thumbYEl, '--scroll-area-thumb-height', scrollTop, maxScrollTop, scrollableContentHeight, clampedNextHeight, maxThumbOffsetY);
|
|
4144
3785
|
thumbYEl.style.transform = `translate3d(0,${thumbOffsetY}px,0)`;
|
|
4145
3786
|
}
|
|
4146
3787
|
|
|
4147
3788
|
// Handle X (horizontal) scroll
|
|
4148
3789
|
if (scrollbarXEl && thumbXEl) {
|
|
4149
3790
|
const maxThumbOffsetX = scrollbarXEl.offsetWidth - clampedNextWidth - scrollbarXOffset - thumbXOffset;
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
viewportEl.style.setProperty(cssVar, `${value}px`);
|
|
4161
|
-
}
|
|
3791
|
+
// RTL scrolls from 0 down to `-maxScrollLeft`; measure from the inline start edge so the
|
|
3792
|
+
// overscroll math is direction-agnostic, then flip the resulting offset back below.
|
|
3793
|
+
const scrollFromStart = direction === 'rtl' ? -scrollLeft : scrollLeft;
|
|
3794
|
+
const offsetX = applyOverscrollThumb(thumbXEl, '--scroll-area-thumb-width', scrollFromStart, maxScrollLeft, scrollableContentWidth, clampedNextWidth, maxThumbOffsetX);
|
|
3795
|
+
thumbXEl.style.transform = `translate3d(${direction === 'rtl' ? -offsetX : offsetX}px,0,0)`;
|
|
3796
|
+
}
|
|
3797
|
+
const overflowMetricsPx = [scrollLeftFromStart, scrollLeftFromEnd, scrollTopFromStart, scrollTopFromEnd];
|
|
3798
|
+
OVERFLOW_EDGE_VARS.forEach((cssVar, index) => {
|
|
3799
|
+
viewportEl.style.setProperty(cssVar, `${overflowMetricsPx[index]}px`);
|
|
3800
|
+
});
|
|
4162
3801
|
if (cornerEl) {
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
}
|
|
4174
|
-
}
|
|
4175
|
-
setHiddenState(prevState => mergeHiddenState(prevState, nextHiddenState));
|
|
3802
|
+
// Bail when the size is unchanged (like `setThumbSize` above); otherwise a
|
|
3803
|
+
// fresh object literal on every scroll frame rebuilds the root context and
|
|
3804
|
+
// re-renders every scroll-area part.
|
|
3805
|
+
// `nextCornerWidth`/`nextCornerHeight` stay 0 when either scrollbar is hidden.
|
|
3806
|
+
setCornerSize(prevSize => pickState(prevSize, {
|
|
3807
|
+
width: nextCornerWidth,
|
|
3808
|
+
height: nextCornerHeight
|
|
3809
|
+
}));
|
|
3810
|
+
}
|
|
3811
|
+
setHiddenState(prevState => pickState(prevState, nextHiddenState));
|
|
4176
3812
|
const nextOverflowEdges = {
|
|
4177
3813
|
xStart: !scrollbarXHidden && scrollLeftFromStart > overflowEdgeThreshold.xStart,
|
|
4178
3814
|
xEnd: !scrollbarXHidden && scrollLeftFromEnd > overflowEdgeThreshold.xEnd,
|
|
4179
3815
|
yStart: !scrollbarYHidden && scrollTopFromStart > overflowEdgeThreshold.yStart,
|
|
4180
3816
|
yEnd: !scrollbarYHidden && scrollTopFromEnd > overflowEdgeThreshold.yEnd
|
|
4181
3817
|
};
|
|
4182
|
-
setOverflowEdges(prev =>
|
|
4183
|
-
if (prev.xStart === nextOverflowEdges.xStart && prev.xEnd === nextOverflowEdges.xEnd && prev.yStart === nextOverflowEdges.yStart && prev.yEnd === nextOverflowEdges.yEnd) {
|
|
4184
|
-
return prev;
|
|
4185
|
-
}
|
|
4186
|
-
return nextOverflowEdges;
|
|
4187
|
-
});
|
|
3818
|
+
setOverflowEdges(prev => pickState(prev, nextOverflowEdges));
|
|
4188
3819
|
});
|
|
4189
3820
|
useScrollLock.useIsoLayoutEffect(() => {
|
|
4190
|
-
if (!viewportRef.current) {
|
|
4191
|
-
return;
|
|
4192
|
-
}
|
|
4193
3821
|
removeCSSVariableInheritance();
|
|
4194
|
-
}, [
|
|
3822
|
+
}, []);
|
|
4195
3823
|
useScrollLock.useIsoLayoutEffect(() => {
|
|
4196
3824
|
// Wait for scrollbar and thumb refs after hidden-state toggles, refresh math on direction
|
|
4197
3825
|
// flips, and re-evaluate overflow edges when the threshold changes.
|
|
@@ -4234,6 +3862,10 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
|
|
|
4234
3862
|
if (animations.length === 0) {
|
|
4235
3863
|
return;
|
|
4236
3864
|
}
|
|
3865
|
+
|
|
3866
|
+
// `allSettled` never rejects, but `computeThumbPosition` can still run against a
|
|
3867
|
+
// torn-down tree once the animations resolve. Swallow instead of leaking an unhandled
|
|
3868
|
+
// rejection; `void` alone would only silence the floating-promise lint.
|
|
4237
3869
|
Promise.allSettled(animations.map(animation => animation.finished)).then(computeThumbPosition).catch(() => {});
|
|
4238
3870
|
});
|
|
4239
3871
|
return () => {
|
|
@@ -4261,7 +3893,13 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
|
|
|
4261
3893
|
return;
|
|
4262
3894
|
}
|
|
4263
3895
|
computeThumbPosition();
|
|
4264
|
-
|
|
3896
|
+
|
|
3897
|
+
// WebKit consumes a touch that catches an in-flight momentum scroll or
|
|
3898
|
+
// rubber-band bounce without dispatching any DOM events for the whole
|
|
3899
|
+
// gesture (not even `touchstart`), so scrolls cannot be attributed to
|
|
3900
|
+
// the user through events. Treat every scroll in touch modality as
|
|
3901
|
+
// user-driven instead.
|
|
3902
|
+
if (touchModality || !programmaticScrollRef.current) {
|
|
4265
3903
|
handleScroll({
|
|
4266
3904
|
x: viewportRef.current.scrollLeft,
|
|
4267
3905
|
y: viewportRef.current.scrollTop
|
|
@@ -4279,21 +3917,10 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
|
|
|
4279
3917
|
});
|
|
4280
3918
|
},
|
|
4281
3919
|
onWheel: handleUserInteraction,
|
|
4282
|
-
onTouchMove: handleUserInteraction,
|
|
4283
3920
|
onPointerMove: handleUserInteraction,
|
|
4284
3921
|
onPointerEnter: handleUserInteraction,
|
|
4285
3922
|
onKeyDown: handleUserInteraction
|
|
4286
3923
|
};
|
|
4287
|
-
const viewportState = React__namespace.useMemo(() => ({
|
|
4288
|
-
scrolling: scrollingX || scrollingY,
|
|
4289
|
-
hasOverflowX: !hiddenState.x,
|
|
4290
|
-
hasOverflowY: !hiddenState.y,
|
|
4291
|
-
overflowXStart: overflowEdges.xStart,
|
|
4292
|
-
overflowXEnd: overflowEdges.xEnd,
|
|
4293
|
-
overflowYStart: overflowEdges.yStart,
|
|
4294
|
-
overflowYEnd: overflowEdges.yEnd,
|
|
4295
|
-
cornerHidden: hiddenState.corner
|
|
4296
|
-
}), [scrollingX, scrollingY, hiddenState.x, hiddenState.y, hiddenState.corner, overflowEdges]);
|
|
4297
3924
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
4298
3925
|
ref: [forwardedRef, viewportRef],
|
|
4299
3926
|
state: viewportState,
|
|
@@ -4318,16 +3945,40 @@ function getHiddenState(viewport) {
|
|
|
4318
3945
|
corner: y || x
|
|
4319
3946
|
};
|
|
4320
3947
|
}
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
3948
|
+
|
|
3949
|
+
/**
|
|
3950
|
+
* Returns `prev` when `next` is shallow-equal to it so setState bails out and
|
|
3951
|
+
* scroll-frame updates don't rebuild the root context.
|
|
3952
|
+
*/
|
|
3953
|
+
function pickState(prev, next) {
|
|
3954
|
+
for (const key in next) {
|
|
3955
|
+
if (prev[key] !== next[key]) {
|
|
3956
|
+
return next;
|
|
3957
|
+
}
|
|
4324
3958
|
}
|
|
4325
|
-
return
|
|
3959
|
+
return prev;
|
|
3960
|
+
}
|
|
3961
|
+
|
|
3962
|
+
/**
|
|
3963
|
+
* Sizes the thumb and returns its axis offset. On overscroll (Safari rubber-band only) it shrinks
|
|
3964
|
+
* against the pinned edge, damped by `content / (content + overscroll)` to match native feedback;
|
|
3965
|
+
* the size flows through the thumb-size variable so the resting `var(...)` still applies.
|
|
3966
|
+
*/
|
|
3967
|
+
function applyOverscrollThumb(thumbEl, sizeVar, scrollFromStart, maxScroll, content, size, maxThumbOffset) {
|
|
3968
|
+
const clamped = scrollEdges.clamp(scrollFromStart, 0, maxScroll);
|
|
3969
|
+
const overscroll = scrollFromStart - clamped;
|
|
3970
|
+
const nextSize = Math.max(MIN_THUMB_SIZE, size * content / (content + Math.abs(overscroll)));
|
|
3971
|
+
|
|
3972
|
+
// Passing an empty string removes the override, restoring the resting `var(...)` size.
|
|
3973
|
+
thumbEl.style.setProperty(sizeVar, overscroll ? `${nextSize}px` : '');
|
|
3974
|
+
|
|
3975
|
+
// Slide proportionally; at the end edge push down by the shrink so the thumb stays pinned to
|
|
3976
|
+
// it, while a start overscroll pins to offset 0.
|
|
3977
|
+
const offset = maxScroll ? clamped / maxScroll * maxThumbOffset : 0;
|
|
3978
|
+
return offset + (overscroll > 0 ? size - nextSize : 0);
|
|
4326
3979
|
}
|
|
4327
3980
|
|
|
4328
3981
|
exports.DialogPortal = DialogPortal;
|
|
4329
|
-
exports.DialogRoot = DialogRoot;
|
|
4330
|
-
exports.DialogStore = DialogStore;
|
|
4331
3982
|
exports.DialogTrigger = DialogTrigger;
|
|
4332
3983
|
exports.DialogViewport = DialogViewport;
|
|
4333
3984
|
exports.DrawerBackdrop = DrawerBackdrop;
|
|
@@ -4342,11 +3993,12 @@ exports.DrawerRoot = DrawerRoot;
|
|
|
4342
3993
|
exports.DrawerTrigger = DrawerTrigger;
|
|
4343
3994
|
exports.DrawerViewport = DrawerViewport;
|
|
4344
3995
|
exports.DrawerVirtualKeyboardContext = DrawerVirtualKeyboardContext;
|
|
3996
|
+
exports.NullStore = NullStore;
|
|
4345
3997
|
exports.ScrollAreaRoot = ScrollAreaRoot;
|
|
4346
|
-
exports.ScrollAreaRootCssVars = ScrollAreaRootCssVars;
|
|
4347
3998
|
exports.ScrollAreaViewport = ScrollAreaViewport;
|
|
4348
3999
|
exports.createInitialPopupStoreState = createInitialPopupStoreState;
|
|
4349
|
-
exports.
|
|
4000
|
+
exports.createNullDialogStore = createNullDialogStore;
|
|
4001
|
+
exports.dialogStateAttributesMapping = dialogStateAttributesMapping;
|
|
4350
4002
|
exports.getDisplacement = getDisplacement;
|
|
4351
4003
|
exports.getElementAtPoint = getElementAtPoint;
|
|
4352
4004
|
exports.getElementTransform = getElementTransform;
|
|
@@ -4357,6 +4009,8 @@ exports.useDialogPortalContext = useDialogPortalContext;
|
|
|
4357
4009
|
exports.useDialogRootContext = useDialogRootContext;
|
|
4358
4010
|
exports.useDrawerProviderContext = useDrawerProviderContext;
|
|
4359
4011
|
exports.useDrawerRootContext = useDrawerRootContext;
|
|
4012
|
+
exports.usePopupHandleStore = usePopupHandleStore;
|
|
4013
|
+
exports.useRenderDialogRoot = useRenderDialogRoot;
|
|
4360
4014
|
exports.useScrollAreaRootContext = useScrollAreaRootContext;
|
|
4361
4015
|
exports.useScrollAreaViewportContext = useScrollAreaViewportContext;
|
|
4362
4016
|
exports.useSwipeDismiss = useSwipeDismiss;
|