@jobber/components 9.6.1 → 9.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Autocomplete/index.cjs +2 -0
- package/dist/Autocomplete/index.mjs +2 -0
- package/dist/Autocomplete-cjs.js +5 -4
- package/dist/Autocomplete-es.js +2 -1
- package/dist/Card/index.cjs +2 -1
- package/dist/Card/index.mjs +2 -1
- package/dist/Chip/index.cjs +2 -0
- package/dist/Chip/index.mjs +2 -0
- package/dist/Chips/InternalChipDismissible/hooks/index.cjs +2 -0
- package/dist/Chips/InternalChipDismissible/hooks/index.mjs +2 -0
- package/dist/Chips/InternalChipDismissible/index.cjs +2 -0
- package/dist/Chips/InternalChipDismissible/index.mjs +2 -0
- package/dist/Chips/index.cjs +2 -0
- package/dist/Chips/index.mjs +2 -0
- package/dist/Combobox/index.cjs +4 -2
- package/dist/Combobox/index.mjs +4 -2
- package/dist/{ComboboxSeparator-cjs.js → ComboboxChipRemove-cjs.js} +455 -501
- package/dist/{ComboboxSeparator-es.js → ComboboxChipRemove-es.js} +456 -500
- package/dist/ComboboxPrimitive-cjs.js +30 -29
- package/dist/ComboboxPrimitive-es.js +3 -2
- package/dist/ConfirmationModal/index.cjs +2 -0
- package/dist/ConfirmationModal/index.mjs +2 -0
- package/dist/DataDump/index.cjs +2 -1
- package/dist/DataDump/index.mjs +2 -1
- package/dist/DataList/components/DataListActions/index.cjs +2 -0
- package/dist/DataList/components/DataListActions/index.mjs +2 -0
- package/dist/DataList/components/DataListBulkActions/index.cjs +2 -0
- package/dist/DataList/components/DataListBulkActions/index.mjs +2 -0
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +2 -0
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +2 -0
- package/dist/DataList/components/DataListFilters/index.cjs +2 -0
- package/dist/DataList/components/DataListFilters/index.mjs +2 -0
- package/dist/DataList/components/DataListHeader/index.cjs +2 -0
- package/dist/DataList/components/DataListHeader/index.mjs +2 -0
- package/dist/DataList/components/DataListItem/index.cjs +2 -0
- package/dist/DataList/components/DataListItem/index.mjs +2 -0
- package/dist/DataList/components/DataListItemActions/index.cjs +2 -0
- package/dist/DataList/components/DataListItemActions/index.mjs +2 -0
- package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +2 -0
- package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +2 -0
- package/dist/DataList/components/DataListItems/index.cjs +2 -0
- package/dist/DataList/components/DataListItems/index.mjs +2 -0
- package/dist/DataList/components/DataListLayout/index.cjs +2 -0
- package/dist/DataList/components/DataListLayout/index.mjs +2 -0
- package/dist/DataList/components/DataListLayoutActions/index.cjs +2 -0
- package/dist/DataList/components/DataListLayoutActions/index.mjs +2 -0
- package/dist/DataList/index.cjs +2 -0
- package/dist/DataList/index.mjs +2 -0
- package/dist/DatePicker/index.cjs +2 -0
- package/dist/DatePicker/index.mjs +2 -0
- package/dist/DatePicker-cjs.js +5 -4
- package/dist/DatePicker-es.js +2 -1
- package/dist/Dialog/index.cjs +2 -1
- package/dist/Dialog/index.mjs +2 -1
- package/dist/DrawerDescription-cjs.js +99 -268
- package/dist/DrawerDescription-es.js +103 -270
- package/dist/DrawerTitle-cjs.js +6 -2
- package/dist/DrawerTitle-es.js +7 -3
- package/dist/FieldDescription-cjs.js +181 -184
- package/dist/FieldDescription-es.js +184 -187
- package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +2 -0
- package/dist/FilterPicker/components/FilterPickerContent/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerContent/index.mjs +2 -0
- package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +2 -0
- package/dist/FilterPicker/index.cjs +2 -0
- package/dist/FilterPicker/index.mjs +2 -0
- package/dist/FilterPickerContent-cjs.js +5 -4
- package/dist/FilterPickerContent-es.js +2 -1
- package/dist/FormatFile/index.cjs +2 -0
- package/dist/FormatFile/index.mjs +2 -0
- package/dist/Gallery/index.cjs +2 -0
- package/dist/Gallery/index.mjs +2 -0
- package/dist/InputDate/index.cjs +2 -0
- package/dist/InputDate/index.mjs +2 -0
- package/dist/InputNumber/index.cjs +2 -1
- package/dist/InputNumber/index.mjs +2 -1
- package/dist/LightBox/index.cjs +2 -0
- package/dist/LightBox/index.mjs +2 -0
- package/dist/Menu/index.cjs +2 -1
- package/dist/Menu/index.mjs +2 -1
- package/dist/Menu-cjs.js +5 -4
- package/dist/Menu-es.js +2 -1
- package/dist/MenuSubmenuTrigger-cjs.js +164 -206
- package/dist/MenuSubmenuTrigger-es.js +152 -194
- package/dist/Modal/index.cjs +2 -0
- package/dist/Modal/index.mjs +3 -1
- package/dist/NumberFieldInput-cjs.js +308 -187
- package/dist/NumberFieldInput-es.js +308 -187
- package/dist/Page/index.cjs +2 -1
- package/dist/Page/index.mjs +2 -1
- package/dist/Popover/index.cjs +2 -0
- package/dist/Popover/index.mjs +2 -0
- package/dist/Popover-cjs.js +8 -7
- package/dist/Popover-es.js +2 -1
- package/dist/ScrollAreaViewport-cjs.js +1093 -747
- package/dist/ScrollAreaViewport-es.js +1081 -734
- package/dist/Select/index.cjs +2 -1
- package/dist/Select/index.mjs +2 -1
- package/dist/SelectGroupLabel-cjs.js +306 -207
- package/dist/SelectGroupLabel-es.js +302 -203
- package/dist/Tooltip/index.cjs +2 -0
- package/dist/Tooltip/index.mjs +2 -0
- package/dist/Tooltip-cjs.js +7 -6
- package/dist/Tooltip-es.js +2 -1
- package/dist/dialogReturnFocus-cjs.js +1 -1
- package/dist/dialogReturnFocus-es.js +2 -2
- package/dist/floating-ui.react-cjs.js +192 -2564
- package/dist/floating-ui.react-dom-cjs.js +2103 -0
- package/dist/floating-ui.react-dom-es.js +2072 -0
- package/dist/floating-ui.react-es.js +156 -2519
- package/dist/floating-ui.utils-cjs.js +342 -0
- package/dist/floating-ui.utils-es.js +300 -0
- package/dist/index.cjs +4 -3
- package/dist/index.mjs +4 -3
- package/dist/primitives/BottomSheet/index.cjs +1 -1
- package/dist/primitives/BottomSheet/index.mjs +1 -1
- package/dist/primitives/ComboboxPrimitive/index.cjs +4 -2
- package/dist/primitives/ComboboxPrimitive/index.mjs +4 -2
- package/dist/primitives/SelectPrimitive/index.cjs +2 -0
- package/dist/primitives/SelectPrimitive/index.mjs +2 -0
- package/dist/primitives/index.cjs +3 -2
- package/dist/primitives/index.mjs +3 -2
- package/dist/scrollEdges-cjs.js +11 -1
- package/dist/scrollEdges-es.js +11 -1
- package/dist/unstyledPrimitives/index.cjs +478 -720
- package/dist/unstyledPrimitives/index.mjs +421 -663
- package/dist/useCompositeListItem-cjs.js +361 -2108
- package/dist/useCompositeListItem-es.js +331 -2076
- package/dist/useLabel-cjs.js +3 -2
- package/dist/useLabel-es.js +3 -2
- package/dist/useRenderElement-cjs.js +7 -9
- package/dist/useRenderElement-es.js +7 -9
- package/dist/useScrollLock-cjs.js +344 -819
- package/dist/useScrollLock-es.js +296 -732
- package/dist/useScrollToActive-cjs.js +5 -4
- package/dist/useScrollToActive-es.js +2 -1
- package/package.json +3 -3
- package/dist/scrollable-cjs.js +0 -58
- package/dist/scrollable-es.js +0 -54
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { A as useCoreFilter, B as AriaCombobox, D as useComboboxInputValueContext, e as ComboboxTrigger, d as ComboboxInputGroup, p as ComboboxItem, l as ComboboxArrow, i as ComboboxBackdrop, x as ComboboxClear, v as ComboboxCollection, w as ComboboxEmpty, n as ComboboxGroup, o as ComboboxGroupLabel, m as ComboboxIcon, c as ComboboxInput, f as ComboboxList, k as ComboboxPopup, h as ComboboxPortal, j as ComboboxPositioner, u as ComboboxRow, g as ComboboxStatus, z as useFilteredItems, s as ComboboxChip, t as ComboboxChipRemove, r as ComboboxChips, q as ComboboxItemIndicator, a as ComboboxLabel, C as ComboboxRoot, b as ComboboxValue, y as useComboboxFilter } from '../ComboboxChipRemove-es.js';
|
|
3
|
+
import { m as stringifyAsLabel, z as useCompositeListItem, b as useFieldRootContext, f as fieldValidityMapping, J as getDefaultLabelId } from '../useCompositeListItem-es.js';
|
|
4
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { bn as pointer, a as useIsoLayoutEffect, b as useStableCallback, C as CommonPopupDataAttributes, k as useTimeout, u as useBaseUiId, b7 as useTriggerRegistration, d as createChangeEventDetails, bh as swipe, a5 as useAnimationFrame, o as ownerDocument, s as addEventListener, D as activeElement, _ as contains, g as getTarget, aG as isInteractiveElement, p as popupStateMapping, n as useControlled, a9 as itemPress, m as useTransitionStatus, f as useOpenChangeComplete, aJ as useAnimationsFinished, a4 as useDirection, a2 as inertValue, ax as mergeCleanups, bk as gecko, bo as scrub, a6 as webkit, E as createGenericEventDetails, P as useStore, b8 as triggerOpenStateMapping, t as transitionStatusMapping } from '../useScrollLock-es.js';
|
|
6
|
+
import { S as Separator } from '../Separator-es.js';
|
|
7
|
+
import { a as DialogBackdrop, c as DialogDescription, e as DialogHandle, b as DialogPopup, d as createDialogHandle, D as DrawerDescription } from '../DrawerDescription-es.js';
|
|
7
8
|
import { c as DialogClose, b as DialogTitle, a as DrawerClose, D as DrawerTitle } from '../DrawerTitle-es.js';
|
|
8
|
-
import {
|
|
9
|
-
import { u as useRenderElement,
|
|
9
|
+
import { j as DialogPortal, h as DialogRoot, i as DialogTrigger, k as DialogViewport, o as useDrawerProviderContext, q as DrawerBackdropCssVars, r as DrawerPopupCssVars, s as DrawerProviderContext, u as useDialogRootContext, t as useDrawerRootContext, v as useSwipeDismiss, w as getDisplacement, x as DrawerPopupDataAttributes, y as getElementTransform, z as DrawerVirtualKeyboardContext, A as getElementAtPoint, B as findScrollableTouchTarget, c as DrawerBackdrop, g as DrawerContent, e as DrawerPopup, b as DrawerPortal, a as DrawerRoot, D as DrawerTrigger, d as DrawerViewport, C as useScrollAreaRootContext, E as getOffset, F as ScrollAreaRootCssVars, G as scrollAreaStateAttributesMapping, H as useScrollAreaViewportContext, S as ScrollAreaRoot, f as ScrollAreaViewport } from '../ScrollAreaViewport-es.js';
|
|
10
|
+
import { u as useRenderElement, f as formatErrorMessage, N as NOOP, E as EMPTY_OBJECT } from '../useRenderElement-es.js';
|
|
11
|
+
import { K as getWindow, h as getComputedStyle, i as isHTMLElement, e as getParentNode, G as round } from '../floating-ui.utils-es.js';
|
|
10
12
|
import { c as clamp, g as getMaxScrollOffset, n as normalizeScrollOffset, S as SCROLL_EDGE_TOLERANCE_PX } from '../scrollEdges-es.js';
|
|
11
|
-
import { f as
|
|
12
|
-
import { u as useMenuRootContext, o as useMenuPositionerContext, p as useMenuItem, R as REGULAR_ITEM, q as itemMapping, r as createNullMenuStore, h as MenuBackdrop, l as MenuGroup, n as MenuGroupLabel, b as MenuItem, a as MenuLinkItem, j as MenuPopup, g as MenuPortal, i as MenuPositioner, k as MenuRadioGroup, c as MenuRadioItem, d as MenuRadioItemIndicator, f as MenuRoot, m as MenuSubmenuRoot, e as MenuSubmenuTrigger, M as MenuTrigger } from '../MenuSubmenuTrigger-es.js';
|
|
13
|
+
import { u as useMenuRootContext, o as useMenuPositionerContext, p as useMenuItem, R as REGULAR_ITEM, q as itemMapping, r as MenuStore, h as MenuBackdrop, l as MenuGroup, n as MenuGroupLabel, b as MenuItem, a as MenuLinkItem, j as MenuPopup, g as MenuPortal, i as MenuPositioner, k as MenuRadioGroup, c as MenuRadioItem, d as MenuRadioItemIndicator, f as MenuRoot, m as MenuSubmenuRoot, e as MenuSubmenuTrigger, M as MenuTrigger } from '../MenuSubmenuTrigger-es.js';
|
|
13
14
|
import * as ReactDOM from 'react-dom';
|
|
14
15
|
import { m as usePreviousValue, n as useSelectRootContext, s as selectors, o as useSelectPositionerContext, j as SelectGroup, k as SelectGroupLabel, g as SelectItem, i as SelectItemIndicator, h as SelectItemText, f as SelectList, e as SelectPopup, a as SelectPortal, d as SelectPositioner, S as SelectRoot, b as SelectTrigger, c as SelectValue } from '../SelectGroupLabel-es.js';
|
|
15
|
-
import {
|
|
16
|
-
import { u as useNumberFieldRootContext, s as stateAttributesMapping$4, d as NumberFieldDecrement, a as NumberFieldGroup, c as NumberFieldIncrement, b as NumberFieldInput, N as NumberFieldRoot } from '../NumberFieldInput-es.js';
|
|
16
|
+
import { u as useNumberFieldRootContext, s as stateAttributesMapping$6, d as NumberFieldDecrement, a as NumberFieldGroup, c as NumberFieldIncrement, b as NumberFieldInput, N as NumberFieldRoot } from '../NumberFieldInput-es.js';
|
|
17
17
|
import { u as useLabel } from '../useLabel-es.js';
|
|
18
18
|
import '../stringifyLocale-es.js';
|
|
19
|
+
import '../floating-ui.react-dom-es.js';
|
|
19
20
|
|
|
20
21
|
function AutocompleteRoot(props) {
|
|
21
22
|
const {
|
|
@@ -50,9 +51,7 @@ function AutocompleteRoot(props) {
|
|
|
50
51
|
} else {
|
|
51
52
|
resolvedInputValue = internalValue;
|
|
52
53
|
}
|
|
53
|
-
const collator = useCoreFilter(
|
|
54
|
-
locale: other.locale
|
|
55
|
-
});
|
|
54
|
+
const collator = useCoreFilter();
|
|
56
55
|
const baseFilter = React.useMemo(() => {
|
|
57
56
|
if (other.filter !== undefined) {
|
|
58
57
|
return other.filter;
|
|
@@ -85,7 +84,15 @@ function AutocompleteRoot(props) {
|
|
|
85
84
|
if (eventDetails.reason === pointer) {
|
|
86
85
|
return;
|
|
87
86
|
}
|
|
88
|
-
|
|
87
|
+
if (enableInline) {
|
|
88
|
+
if (highlightedValue == null) {
|
|
89
|
+
setInlineInputValue('');
|
|
90
|
+
} else {
|
|
91
|
+
setInlineInputValue(stringifyAsLabel(highlightedValue, itemToStringValue));
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
setInlineInputValue('');
|
|
95
|
+
}
|
|
89
96
|
}
|
|
90
97
|
return /*#__PURE__*/jsx(AriaCombobox, {
|
|
91
98
|
...other,
|
|
@@ -144,14 +151,6 @@ const AutocompleteInputGroup = ComboboxInputGroup;
|
|
|
144
151
|
*/
|
|
145
152
|
const AutocompleteItem = ComboboxItem;
|
|
146
153
|
|
|
147
|
-
/**
|
|
148
|
-
* A visual separator between items or groups.
|
|
149
|
-
* Renders a `<div>` element.
|
|
150
|
-
*
|
|
151
|
-
* Documentation: [Base UI Autocomplete](https://base-ui.com/react/components/autocomplete)
|
|
152
|
-
*/
|
|
153
|
-
const AutocompleteSeparator = ListboxSeparator;
|
|
154
|
-
|
|
155
154
|
var index_parts$7 = /*#__PURE__*/Object.freeze({
|
|
156
155
|
__proto__: null,
|
|
157
156
|
Arrow: ComboboxArrow,
|
|
@@ -171,7 +170,7 @@ var index_parts$7 = /*#__PURE__*/Object.freeze({
|
|
|
171
170
|
Positioner: ComboboxPositioner,
|
|
172
171
|
Root: AutocompleteRoot,
|
|
173
172
|
Row: ComboboxRow,
|
|
174
|
-
Separator:
|
|
173
|
+
Separator: Separator,
|
|
175
174
|
Status: ComboboxStatus,
|
|
176
175
|
Trigger: AutocompleteTrigger,
|
|
177
176
|
Value: AutocompleteValue,
|
|
@@ -194,7 +193,7 @@ var index_parts$6 = /*#__PURE__*/Object.freeze({
|
|
|
194
193
|
createHandle: createDialogHandle
|
|
195
194
|
});
|
|
196
195
|
|
|
197
|
-
const stateAttributesMapping$
|
|
196
|
+
const stateAttributesMapping$5 = {
|
|
198
197
|
active(value) {
|
|
199
198
|
if (value) {
|
|
200
199
|
return {
|
|
@@ -265,12 +264,12 @@ const DrawerIndent = /*#__PURE__*/React.forwardRef(function DrawerIndent(compone
|
|
|
265
264
|
[DrawerBackdropCssVars.swipeProgress]: '0'
|
|
266
265
|
}
|
|
267
266
|
}, elementProps],
|
|
268
|
-
stateAttributesMapping: stateAttributesMapping$
|
|
267
|
+
stateAttributesMapping: stateAttributesMapping$5
|
|
269
268
|
});
|
|
270
269
|
});
|
|
271
270
|
if (process.env.NODE_ENV !== "production") DrawerIndent.displayName = "DrawerIndent";
|
|
272
271
|
|
|
273
|
-
const stateAttributesMapping$
|
|
272
|
+
const stateAttributesMapping$4 = {
|
|
274
273
|
active(value) {
|
|
275
274
|
if (value) {
|
|
276
275
|
return {
|
|
@@ -305,7 +304,7 @@ const DrawerIndentBackground = /*#__PURE__*/React.forwardRef(function DrawerInde
|
|
|
305
304
|
ref: forwardedRef,
|
|
306
305
|
state,
|
|
307
306
|
props: elementProps,
|
|
308
|
-
stateAttributesMapping: stateAttributesMapping$
|
|
307
|
+
stateAttributesMapping: stateAttributesMapping$4
|
|
309
308
|
});
|
|
310
309
|
});
|
|
311
310
|
if (process.env.NODE_ENV !== "production") DrawerIndentBackground.displayName = "DrawerIndentBackground";
|
|
@@ -314,26 +313,37 @@ function DrawerProvider(props) {
|
|
|
314
313
|
const {
|
|
315
314
|
children
|
|
316
315
|
} = props;
|
|
317
|
-
const [
|
|
316
|
+
const [openById, setOpenById] = React.useState(() => new Map());
|
|
318
317
|
const [visualStateStore] = React.useState(createVisualStateStore);
|
|
319
|
-
const setDrawerOpen = useStableCallback((
|
|
320
|
-
|
|
321
|
-
|
|
318
|
+
const setDrawerOpen = useStableCallback((drawerId, open) => {
|
|
319
|
+
setOpenById(prev => {
|
|
320
|
+
const prevOpen = prev.get(drawerId);
|
|
321
|
+
if (prevOpen === open) {
|
|
322
322
|
return prev;
|
|
323
323
|
}
|
|
324
|
-
const next = new
|
|
325
|
-
|
|
326
|
-
next.add(drawer);
|
|
327
|
-
} else {
|
|
328
|
-
next.delete(drawer);
|
|
329
|
-
}
|
|
324
|
+
const next = new Map(prev);
|
|
325
|
+
next.set(drawerId, open);
|
|
330
326
|
return next;
|
|
331
327
|
});
|
|
332
328
|
});
|
|
333
|
-
const removeDrawer = useStableCallback(
|
|
334
|
-
|
|
329
|
+
const removeDrawer = useStableCallback(drawerId => {
|
|
330
|
+
setOpenById(prev => {
|
|
331
|
+
if (!prev.has(drawerId)) {
|
|
332
|
+
return prev;
|
|
333
|
+
}
|
|
334
|
+
const next = new Map(prev);
|
|
335
|
+
next.delete(drawerId);
|
|
336
|
+
return next;
|
|
337
|
+
});
|
|
335
338
|
});
|
|
336
|
-
const active =
|
|
339
|
+
const active = React.useMemo(() => {
|
|
340
|
+
for (const open of openById.values()) {
|
|
341
|
+
if (open) {
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}, [openById]);
|
|
337
347
|
const contextValue = React.useMemo(() => ({
|
|
338
348
|
setDrawerOpen,
|
|
339
349
|
removeDrawer,
|
|
@@ -423,7 +433,7 @@ const SWIPE_AREA_SWIPING_HOOK = {
|
|
|
423
433
|
const SWIPE_AREA_DISABLED_HOOK = {
|
|
424
434
|
[DrawerSwipeAreaDataAttributes.disabled]: ''
|
|
425
435
|
};
|
|
426
|
-
const stateAttributesMapping$
|
|
436
|
+
const stateAttributesMapping$3 = {
|
|
427
437
|
open(value) {
|
|
428
438
|
return value ? SWIPE_AREA_OPEN_HOOK : SWIPE_AREA_CLOSED_HOOK;
|
|
429
439
|
},
|
|
@@ -431,9 +441,9 @@ const stateAttributesMapping$1 = {
|
|
|
431
441
|
return value ? SWIPE_AREA_SWIPING_HOOK : null;
|
|
432
442
|
},
|
|
433
443
|
swipeDirection(value) {
|
|
434
|
-
return {
|
|
444
|
+
return value ? {
|
|
435
445
|
[DrawerSwipeAreaDataAttributes.swipeDirection]: value
|
|
436
|
-
};
|
|
446
|
+
} : null;
|
|
437
447
|
},
|
|
438
448
|
disabled(value) {
|
|
439
449
|
return value ? SWIPE_AREA_DISABLED_HOOK : null;
|
|
@@ -464,14 +474,16 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
464
474
|
swipeDirection: swipeDirectionProp,
|
|
465
475
|
...elementProps
|
|
466
476
|
} = componentProps;
|
|
467
|
-
const
|
|
477
|
+
const {
|
|
478
|
+
store
|
|
479
|
+
} = useDialogRootContext();
|
|
468
480
|
const {
|
|
469
481
|
swipeDirection,
|
|
470
|
-
frontmostHeight
|
|
471
|
-
swipeAreaActiveRef
|
|
482
|
+
frontmostHeight
|
|
472
483
|
} = useDrawerRootContext();
|
|
473
484
|
const providerContext = useDrawerProviderContext();
|
|
474
485
|
const [swipeActive, setSwipeActive] = React.useState(false);
|
|
486
|
+
const releaseDismissTimeout = useTimeout();
|
|
475
487
|
const swipeAreaRef = React.useRef(null);
|
|
476
488
|
const swipeStartEventRef = React.useRef(null);
|
|
477
489
|
const openedBySwipeRef = React.useRef(false);
|
|
@@ -481,10 +493,7 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
481
493
|
});
|
|
482
494
|
const closedOffsetRef = React.useRef(null);
|
|
483
495
|
const appliedSwipeStylesRef = React.useRef(false);
|
|
484
|
-
const swipePopupElementRef = React.useRef(null);
|
|
485
|
-
const swipeBackdropElementRef = React.useRef(null);
|
|
486
496
|
const popupTransitionRef = React.useRef(null);
|
|
487
|
-
const releaseGuardCleanupRef = React.useRef(NOOP);
|
|
488
497
|
const swipeAreaId = useBaseUiId(componentProps.id);
|
|
489
498
|
const registerTrigger = useTriggerRegistration(swipeAreaId, store);
|
|
490
499
|
const open = store.useState('open');
|
|
@@ -496,40 +505,21 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
496
505
|
const dismissDirection = oppositeSwipeDirection[resolvedSwipeDirection];
|
|
497
506
|
const enabled = !disabled && (!open || swipeActive);
|
|
498
507
|
function disableDismissForSwipe() {
|
|
499
|
-
|
|
508
|
+
releaseDismissTimeout.clear();
|
|
500
509
|
store.context.outsidePressEnabledRef.current = false;
|
|
501
510
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
// The gesture's trailing release click is the one physical click with no `pointerdown` of
|
|
507
|
-
// its own. Ignore it and keep waiting, so it cannot dismiss the drawer it just opened,
|
|
508
|
-
// while a click-only activation (keyboard or assistive tech) still re-enables in time.
|
|
509
|
-
if (event?.type === 'click' && event.detail !== 0 && !isVirtualClick(event)) {
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
releaseGuardCleanupRef.current = NOOP;
|
|
513
|
-
doc.removeEventListener('pointerdown', restore, true);
|
|
514
|
-
doc.removeEventListener('click', restore, true);
|
|
511
|
+
function enableDismissAfterRelease() {
|
|
512
|
+
// Safari can dispatch outside-press for the same swipe-open gesture
|
|
513
|
+
// after release, so defer re-enabling dismissal to the next macrotask.
|
|
514
|
+
releaseDismissTimeout.start(0, () => {
|
|
515
515
|
store.context.outsidePressEnabledRef.current = true;
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
function resolvePopupSize() {
|
|
519
|
+
const popupElement = store.context.popupRef.current;
|
|
520
|
+
if (!popupElement) {
|
|
521
|
+
return null;
|
|
516
522
|
}
|
|
517
|
-
|
|
518
|
-
// The pointerup that ends a swipe-open gesture synthesizes a `click`. When the drag released
|
|
519
|
-
// outside the popup (e.g. it was dragged past the popup's size), that click would be treated as
|
|
520
|
-
// an outside press and immediately dismiss the drawer that was just opened. Keep outside-press
|
|
521
|
-
// dismissal disabled until the next interaction that isn't that release click: a deliberate
|
|
522
|
-
// outside press starts with a `pointerdown`, and a click-only activation (keyboard or
|
|
523
|
-
// assistive tech) is distinguishable from a physical release. This is deterministic, unlike
|
|
524
|
-
// re-enabling on a timer that can race the synthesized click and dismiss at random.
|
|
525
|
-
//
|
|
526
|
-
// `restore` runs in document capture, ahead of floating-ui's own outside-press check (which
|
|
527
|
-
// happens on the event target, after capture), so the triggering press still dismisses.
|
|
528
|
-
releaseGuardCleanupRef.current = restore;
|
|
529
|
-
doc.addEventListener('pointerdown', restore, true);
|
|
530
|
-
doc.addEventListener('click', restore, true);
|
|
531
|
-
});
|
|
532
|
-
function getPopupSize(popupElement) {
|
|
533
523
|
const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
|
|
534
524
|
const size = isHorizontal ? popupElement.offsetWidth : popupElement.offsetHeight;
|
|
535
525
|
if (size <= 0) {
|
|
@@ -537,15 +527,15 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
537
527
|
}
|
|
538
528
|
return size;
|
|
539
529
|
}
|
|
540
|
-
function
|
|
541
|
-
const
|
|
542
|
-
return popupElement ? getPopupSize(popupElement) : null;
|
|
543
|
-
}
|
|
544
|
-
function resolveClosedOffset(popupElement) {
|
|
545
|
-
const offset = getPopupSize(popupElement);
|
|
530
|
+
function resolveClosedOffset() {
|
|
531
|
+
const offset = resolvePopupSize();
|
|
546
532
|
if (offset == null) {
|
|
547
533
|
return null;
|
|
548
534
|
}
|
|
535
|
+
const popupElement = store.context.popupRef.current;
|
|
536
|
+
if (!popupElement) {
|
|
537
|
+
return offset;
|
|
538
|
+
}
|
|
549
539
|
const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
|
|
550
540
|
const transform = getElementTransform(popupElement);
|
|
551
541
|
const transformOffset = isHorizontal ? transform.x : transform.y;
|
|
@@ -562,6 +552,9 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
562
552
|
return popupSize * DEFAULT_SWIPE_OPEN_RATIO;
|
|
563
553
|
}
|
|
564
554
|
function applySwipeMovement() {
|
|
555
|
+
if (!swipeActive) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
565
558
|
const popupElement = store.context.popupRef.current;
|
|
566
559
|
if (!popupElement) {
|
|
567
560
|
return;
|
|
@@ -570,10 +563,10 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
570
563
|
return;
|
|
571
564
|
}
|
|
572
565
|
if (closedOffsetRef.current == null) {
|
|
573
|
-
closedOffsetRef.current = resolveClosedOffset(
|
|
566
|
+
closedOffsetRef.current = resolveClosedOffset();
|
|
574
567
|
}
|
|
575
568
|
const closedOffset = closedOffsetRef.current;
|
|
576
|
-
if (closedOffset
|
|
569
|
+
if (!closedOffset || !Number.isFinite(closedOffset) || closedOffset <= 0) {
|
|
577
570
|
return;
|
|
578
571
|
}
|
|
579
572
|
const {
|
|
@@ -594,7 +587,6 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
594
587
|
popupElement.style.setProperty(DrawerPopupCssVars.swipeMovementX, `${movementX}px`);
|
|
595
588
|
popupElement.style.setProperty(DrawerPopupCssVars.swipeMovementY, `${movementY}px`);
|
|
596
589
|
popupElement.setAttribute(DrawerPopupDataAttributes.swiping, '');
|
|
597
|
-
swipePopupElementRef.current = popupElement;
|
|
598
590
|
if (popupTransitionRef.current === null) {
|
|
599
591
|
popupTransitionRef.current = popupElement.style.transition;
|
|
600
592
|
}
|
|
@@ -602,7 +594,6 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
602
594
|
const backdropElement = store.context.backdropRef.current;
|
|
603
595
|
if (backdropElement) {
|
|
604
596
|
backdropElement.setAttribute(DrawerPopupDataAttributes.swiping, '');
|
|
605
|
-
swipeBackdropElementRef.current = backdropElement;
|
|
606
597
|
backdropElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, `${backdropProgress}`);
|
|
607
598
|
if (openProgress > 0 && frontmostHeight > 0) {
|
|
608
599
|
backdropElement.style.setProperty(DrawerPopupCssVars.height, `${frontmostHeight}px`);
|
|
@@ -615,11 +606,10 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
615
606
|
frontmostHeight: openProgress > 0 ? frontmostHeight : 0
|
|
616
607
|
});
|
|
617
608
|
appliedSwipeStylesRef.current = true;
|
|
618
|
-
swipeAreaActiveRef.current = true;
|
|
619
609
|
}
|
|
620
610
|
const clearSwipeStyles = useStableCallback(() => {
|
|
621
|
-
const popupElement =
|
|
622
|
-
if (popupElement) {
|
|
611
|
+
const popupElement = store.context.popupRef.current;
|
|
612
|
+
if (popupElement && appliedSwipeStylesRef.current) {
|
|
623
613
|
popupElement.style.removeProperty(DrawerPopupCssVars.swipeMovementX);
|
|
624
614
|
popupElement.style.removeProperty(DrawerPopupCssVars.swipeMovementY);
|
|
625
615
|
popupElement.removeAttribute(DrawerPopupDataAttributes.swiping);
|
|
@@ -628,7 +618,7 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
628
618
|
popupElement.style.transition = popupTransitionRef.current;
|
|
629
619
|
popupTransitionRef.current = null;
|
|
630
620
|
}
|
|
631
|
-
const backdropElement =
|
|
621
|
+
const backdropElement = store.context.backdropRef.current;
|
|
632
622
|
if (backdropElement) {
|
|
633
623
|
backdropElement.removeAttribute(DrawerPopupDataAttributes.swiping);
|
|
634
624
|
backdropElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, '0');
|
|
@@ -639,16 +629,19 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
639
629
|
frontmostHeight: 0
|
|
640
630
|
});
|
|
641
631
|
appliedSwipeStylesRef.current = false;
|
|
642
|
-
swipePopupElementRef.current = null;
|
|
643
|
-
swipeBackdropElementRef.current = null;
|
|
644
|
-
swipeAreaActiveRef.current = false;
|
|
645
632
|
});
|
|
646
633
|
function openDrawer(event) {
|
|
634
|
+
if (store.select('open')) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
647
637
|
openedBySwipeRef.current = true;
|
|
648
|
-
store.setOpen(true, createChangeEventDetails(swipe, event, swipeAreaRef.current));
|
|
638
|
+
store.setOpen(true, createChangeEventDetails(swipe, event, swipeAreaRef.current ?? undefined));
|
|
649
639
|
}
|
|
650
640
|
function closeDrawer(event) {
|
|
651
|
-
store.
|
|
641
|
+
if (!store.select('open')) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
store.setOpen(false, createChangeEventDetails(swipe, event, swipeAreaRef.current ?? undefined));
|
|
652
645
|
}
|
|
653
646
|
function resetSwipeInteractionState() {
|
|
654
647
|
swipeStartEventRef.current = null;
|
|
@@ -691,10 +684,10 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
691
684
|
return;
|
|
692
685
|
}
|
|
693
686
|
const displacement = getDisplacement(resolvedSwipeDirection, details.deltaX, details.deltaY);
|
|
694
|
-
if (
|
|
687
|
+
if (displacement < MIN_SWIPE_START_DISTANCE && !openedBySwipeRef.current) {
|
|
695
688
|
return;
|
|
696
689
|
}
|
|
697
|
-
if (!openedBySwipeRef.current
|
|
690
|
+
if (!openedBySwipeRef.current) {
|
|
698
691
|
openDrawer(swipeStartEventRef.current);
|
|
699
692
|
}
|
|
700
693
|
applySwipeMovement();
|
|
@@ -710,14 +703,14 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
710
703
|
const displacement = getDisplacement(resolvedSwipeDirection, deltaX, deltaY);
|
|
711
704
|
const releaseVelocity = getDisplacement(resolvedSwipeDirection, releaseVelocityX, releaseVelocityY);
|
|
712
705
|
const threshold = resolveSwipeOpenThreshold();
|
|
713
|
-
const hasEnoughDistance = displacement >= threshold;
|
|
706
|
+
const hasEnoughDistance = threshold != null && displacement >= threshold;
|
|
714
707
|
const hasEnoughVelocity = releaseVelocity >= VELOCITY_THRESHOLD;
|
|
715
|
-
const shouldOpen = direction === resolvedSwipeDirection && (hasEnoughDistance || hasEnoughVelocity) && !disabled;
|
|
708
|
+
const shouldOpen = threshold != null && direction === resolvedSwipeDirection && (hasEnoughDistance || hasEnoughVelocity) && !disabled;
|
|
716
709
|
if (shouldOpen) {
|
|
717
710
|
if (!store.select('open')) {
|
|
718
711
|
openDrawer(event);
|
|
719
712
|
}
|
|
720
|
-
} else if (openedBySwipeRef.current
|
|
713
|
+
} else if (openedBySwipeRef.current) {
|
|
721
714
|
closeDrawer(event);
|
|
722
715
|
}
|
|
723
716
|
finishSwipeInteraction();
|
|
@@ -728,29 +721,16 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
728
721
|
const swipePointerProps = swipe$1.getPointerProps();
|
|
729
722
|
const swipeTouchProps = swipe$1.getTouchProps();
|
|
730
723
|
const resetSwipe = swipe$1.reset;
|
|
731
|
-
|
|
732
|
-
// The commit that opens the drawer re-renders the popup, resetting `--swipe-movement-*` to `0px`
|
|
733
|
-
// (the viewport isn't swiping). Re-assert after the DOM mutation but before paint. No deps: must
|
|
734
|
-
// run on every commit the swipe area participates in.
|
|
735
|
-
useIsoLayoutEffect(() => {
|
|
736
|
-
if (swipeActive && appliedSwipeStylesRef.current) {
|
|
737
|
-
applySwipeMovement();
|
|
738
|
-
}
|
|
739
|
-
});
|
|
740
|
-
useIsoLayoutEffect(() => {
|
|
724
|
+
React.useEffect(() => {
|
|
741
725
|
if (!enabled) {
|
|
742
|
-
if (swipeActive) {
|
|
743
|
-
enableDismissAfterRelease();
|
|
744
|
-
}
|
|
745
726
|
resetSwipe();
|
|
746
727
|
resetDragDelta();
|
|
747
728
|
clearSwipeStyles();
|
|
748
729
|
resetSwipeInteractionState();
|
|
749
730
|
}
|
|
750
|
-
}, [clearSwipeStyles,
|
|
731
|
+
}, [clearSwipeStyles, enabled, resetDragDelta, resetSwipe]);
|
|
751
732
|
React.useEffect(() => {
|
|
752
733
|
return () => {
|
|
753
|
-
releaseGuardCleanupRef.current();
|
|
754
734
|
store.context.outsidePressEnabledRef.current = true;
|
|
755
735
|
};
|
|
756
736
|
}, [store]);
|
|
@@ -763,7 +743,7 @@ const DrawerSwipeArea = /*#__PURE__*/React.forwardRef(function DrawerSwipeArea(c
|
|
|
763
743
|
return useRenderElement('div', componentProps, {
|
|
764
744
|
state,
|
|
765
745
|
ref: [forwardedRef, swipeAreaRef, registerTrigger],
|
|
766
|
-
stateAttributesMapping: stateAttributesMapping$
|
|
746
|
+
stateAttributesMapping: stateAttributesMapping$3,
|
|
767
747
|
props: [{
|
|
768
748
|
role: 'presentation',
|
|
769
749
|
'aria-hidden': true,
|
|
@@ -823,15 +803,6 @@ const KEYBOARD_VISIBILITY_MARGIN = 16;
|
|
|
823
803
|
// keyboard overlap, so the field can be scrolled clear of the keyboard instead of
|
|
824
804
|
// ending up flush against it. Only applied when there is actual overlap.
|
|
825
805
|
const KEYBOARD_SCROLL_SLACK = 48;
|
|
826
|
-
// Cadence of the settle-watching realign passes after focus moves with the keyboard open:
|
|
827
|
-
// long enough for a smooth scroll to show progress between passes, short enough to recover
|
|
828
|
-
// quickly from a scroll canceled by WebKit's reveal; the pass count covers CSS transitions
|
|
829
|
-
// reacting to the focus change (e.g. a 260ms footer resize) with room to spare.
|
|
830
|
-
const KEYBOARD_REALIGN_INTERVAL = 150;
|
|
831
|
-
const KEYBOARD_REALIGN_MAX_PASSES = 4;
|
|
832
|
-
// Frames the alignment waits for the scroll destination to stop moving (layout reacting to
|
|
833
|
-
// the focus change, e.g. a footer resizing over a CSS transition) before scrolling anyway.
|
|
834
|
-
const KEYBOARD_SETTLE_FRAME_LIMIT = 60;
|
|
835
806
|
const INPUT_TAP_MOVE_THRESHOLD = 10;
|
|
836
807
|
const INPUT_TAP_HIT_SLOP = 16;
|
|
837
808
|
const KEYBOARD_INPUT_TYPES = new Set(['email', 'number', 'password', 'search', 'tel', 'text', 'url']);
|
|
@@ -855,10 +826,11 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
855
826
|
const {
|
|
856
827
|
children
|
|
857
828
|
} = props;
|
|
858
|
-
const
|
|
829
|
+
const {
|
|
830
|
+
store
|
|
831
|
+
} = useDialogRootContext();
|
|
859
832
|
const open = store.useState('open');
|
|
860
833
|
const mounted = store.useState('mounted');
|
|
861
|
-
const modal = store.useState('modal');
|
|
862
834
|
const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');
|
|
863
835
|
const viewportElement = store.useState('viewportElement');
|
|
864
836
|
|
|
@@ -871,9 +843,7 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
871
843
|
const keyboardTouchStartRef = React.useRef(null);
|
|
872
844
|
const focusedKeyboardTargetRef = React.useRef(null);
|
|
873
845
|
const keyboardScrollAdjustmentRef = React.useRef(null);
|
|
874
|
-
const programmaticKeyboardFocusRef = React.useRef(false);
|
|
875
846
|
const keyboardFocusFrame = useAnimationFrame();
|
|
876
|
-
const keyboardRealignTimeout = useTimeout();
|
|
877
847
|
const restoreKeyboardScrollAdjustment = useStableCallback(() => {
|
|
878
848
|
const adjustment = keyboardScrollAdjustmentRef.current;
|
|
879
849
|
if (!adjustment) {
|
|
@@ -941,100 +911,46 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
941
911
|
const doc = ownerDocument(rootElement);
|
|
942
912
|
const win = getWindow(rootElement);
|
|
943
913
|
const visualViewport = win.visualViewport;
|
|
944
|
-
|
|
945
|
-
// Alignment scroll bookkeeping: destination stability, whether a scroll was issued,
|
|
946
|
-
// and the last observed progress so delayed passes can distinguish moving from stalled.
|
|
947
|
-
let keyboardScrollElement = null;
|
|
948
|
-
let keyboardScrollDestination = 0;
|
|
949
|
-
let keyboardScrollChecks = 0;
|
|
950
|
-
let keyboardScrollObserved = -1;
|
|
951
914
|
const setDrawerKeyboardInset = inset => {
|
|
952
915
|
rootElement.style.setProperty(DrawerViewportCssVars.keyboardInset, `${Math.max(0, Math.ceil(inset))}px`);
|
|
953
916
|
};
|
|
917
|
+
const resetDrawerKeyboardInset = () => {
|
|
918
|
+
setDrawerKeyboardInset(0);
|
|
919
|
+
};
|
|
954
920
|
const clearFocusedKeyboardTarget = () => {
|
|
955
921
|
focusedKeyboardTargetRef.current = null;
|
|
956
|
-
|
|
957
|
-
setDrawerKeyboardInset(0);
|
|
922
|
+
resetDrawerKeyboardInset();
|
|
958
923
|
restoreKeyboardScrollAdjustment();
|
|
959
924
|
keyboardFocusFrame.cancel();
|
|
960
|
-
keyboardRealignTimeout.clear();
|
|
961
|
-
};
|
|
962
|
-
|
|
963
|
-
// WebKit's native reveal scroll can move the page even while the scroll lock hides
|
|
964
|
-
// overflow (e.g. when the software keyboard's previous/next field arrows move focus).
|
|
965
|
-
// While the drawer is modal, any window scroll during keyboard interaction is spurious,
|
|
966
|
-
// so pin the page to the position it had when the drawer opened.
|
|
967
|
-
const baseScrollX = win.scrollX;
|
|
968
|
-
const baseScrollY = win.scrollY;
|
|
969
|
-
const restoreWindowScroll = () => {
|
|
970
|
-
if (modal !== true || nestedDrawerOpen || !focusedKeyboardTargetRef.current || getKeyboardVisualViewport(win) == null) {
|
|
971
|
-
return false;
|
|
972
|
-
}
|
|
973
|
-
if (win.scrollX !== baseScrollX || win.scrollY !== baseScrollY) {
|
|
974
|
-
// Force an instant jump: the two-argument form defaults `behavior` to `auto`, which
|
|
975
|
-
// obeys the page's `scroll-behavior`, so a global `scroll-behavior: smooth` would
|
|
976
|
-
// animate the restore. The measurements that follow assume the page is already back
|
|
977
|
-
// at rest, and a smooth restore also re-emits `scroll`, re-entering this handler.
|
|
978
|
-
win.scrollTo({
|
|
979
|
-
left: baseScrollX,
|
|
980
|
-
top: baseScrollY,
|
|
981
|
-
behavior: 'instant'
|
|
982
|
-
});
|
|
983
|
-
return true;
|
|
984
|
-
}
|
|
985
|
-
return false;
|
|
986
|
-
};
|
|
987
|
-
|
|
988
|
-
// Focus moved by the drawer itself goes through `focusKeyboardInputWithoutPageScroll`,
|
|
989
|
-
// but native focus changes (the iOS keyboard's previous/next field arrows) commit
|
|
990
|
-
// WebKit's reveal scroll before `focusin` reaches us — cancelling it afterwards
|
|
991
|
-
// visibly jitters the sheet. `focusout` on the outgoing field fires before focus
|
|
992
|
-
// lands, so override the incoming field's geometry there and restore it in `focusin`.
|
|
993
|
-
let restorePreemptedFocus = null;
|
|
994
|
-
const consumePreemptedFocus = () => {
|
|
995
|
-
restorePreemptedFocus?.();
|
|
996
|
-
restorePreemptedFocus = null;
|
|
997
|
-
};
|
|
998
|
-
const preemptFocusReveal = (target, keyboardViewport) => {
|
|
999
|
-
consumePreemptedFocus();
|
|
1000
|
-
|
|
1001
|
-
// Native focus carries no preventScroll, so a fake off-screen rect would make WebKit
|
|
1002
|
-
// scroll the drawer's own scroll containers thousands of pixels chasing it. Presenting
|
|
1003
|
-
// the field as already centered in the visible band above the keyboard instead makes
|
|
1004
|
-
// both the in-page reveal and the viewport pan no-ops.
|
|
1005
|
-
const rect = target.getBoundingClientRect();
|
|
1006
|
-
restorePreemptedFocus = overrideGeometryDuringFocus(target, (keyboardViewport.top + keyboardViewport.bottom - rect.top - rect.bottom) / 2);
|
|
1007
925
|
};
|
|
1008
926
|
const alignFocusedKeyboardTarget = () => {
|
|
1009
|
-
// If focus never lands on a preempted target, the focusout-scheduled alignment still
|
|
1010
|
-
// restores it on the next frame before paint.
|
|
1011
|
-
consumePreemptedFocus();
|
|
1012
927
|
const target = focusedKeyboardTargetRef.current;
|
|
1013
928
|
// If the focused field is removed from the DOM without firing `focusout` (e.g. it is
|
|
1014
929
|
// conditionally rendered away), any applied scroll slack is restored here on the next
|
|
1015
930
|
// focus/viewport event or when the drawer closes. This self-corrects rather than
|
|
1016
931
|
// tracking each field's lifecycle.
|
|
1017
932
|
if (nestedDrawerOpen || !target || !contains(rootElement, target)) {
|
|
1018
|
-
|
|
933
|
+
resetDrawerKeyboardInset();
|
|
1019
934
|
restoreKeyboardScrollAdjustment();
|
|
1020
935
|
return;
|
|
1021
936
|
}
|
|
1022
|
-
|
|
1023
|
-
// Undo any reveal scroll WebKit applied to the locked page before measuring, so the
|
|
1024
|
-
// keyboard inset and alignment are computed against the resting viewport.
|
|
1025
|
-
restoreWindowScroll();
|
|
1026
937
|
const keyboardViewport = getKeyboardVisualViewport(win);
|
|
1027
938
|
if (!keyboardViewport) {
|
|
1028
|
-
|
|
939
|
+
resetDrawerKeyboardInset();
|
|
1029
940
|
restoreKeyboardScrollAdjustment();
|
|
1030
941
|
return;
|
|
1031
942
|
}
|
|
1032
|
-
setDrawerKeyboardInset(
|
|
943
|
+
setDrawerKeyboardInset(getDrawerKeyboardInset(win, keyboardViewport));
|
|
1033
944
|
const scrollTarget = findKeyboardScrollTarget(target, rootElement);
|
|
1034
945
|
if (!scrollTarget) {
|
|
1035
946
|
restoreKeyboardScrollAdjustment();
|
|
1036
947
|
return;
|
|
1037
948
|
}
|
|
949
|
+
if (!scrollTarget.isConnected || !contains(rootElement, scrollTarget)) {
|
|
950
|
+
resetDrawerKeyboardInset();
|
|
951
|
+
restoreKeyboardScrollAdjustment();
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
1038
954
|
const scrollTargetRect = scrollTarget.getBoundingClientRect();
|
|
1039
955
|
const clippedBottom = Math.min(scrollTargetRect.bottom, keyboardViewport.bottom);
|
|
1040
956
|
const overlap = Math.max(0, scrollTargetRect.bottom - keyboardViewport.bottom);
|
|
@@ -1050,70 +966,14 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
1050
966
|
return;
|
|
1051
967
|
}
|
|
1052
968
|
const targetRect = target.getBoundingClientRect();
|
|
1053
|
-
const
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
// Commit the scroll only once the destination holds across two consecutive checks.
|
|
1058
|
-
// Layout may still be reacting to the focus change (e.g. a footer sized by
|
|
1059
|
-
// `:focus-within` resizing over a transition), and scrolling toward a
|
|
1060
|
-
// mid-transition destination overshoots, then visibly pulls back once corrected.
|
|
1061
|
-
// The destination is scroll-position-invariant, so an in-flight scroll of the
|
|
1062
|
-
// container itself never defers the commit.
|
|
1063
|
-
const checks = keyboardScrollElement === scrollTarget ? keyboardScrollChecks + 1 : 1;
|
|
1064
|
-
keyboardScrollElement = scrollTarget;
|
|
1065
|
-
keyboardScrollDestination = destination;
|
|
1066
|
-
keyboardScrollChecks = checks;
|
|
1067
|
-
keyboardScrollObserved = -1;
|
|
1068
|
-
// Re-check next frame; give up and scroll anyway if layout never settles.
|
|
1069
|
-
if (checks <= KEYBOARD_SETTLE_FRAME_LIMIT) {
|
|
1070
|
-
keyboardFocusFrame.request(alignFocusedKeyboardTarget);
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
} else if (keyboardScrollObserved >= 0) {
|
|
1074
|
-
// A scroll toward this destination is already out. Leave it alone while it has
|
|
1075
|
-
// arrived or is still progressing — re-issuing restarts the smooth-scroll easing
|
|
1076
|
-
// curve, a visible stutter on every realign pass — and re-issue only when it
|
|
1077
|
-
// stalled short (WebKit cancels in-flight smooth scrolls when its native reveal
|
|
1078
|
-
// for the focus resolves).
|
|
1079
|
-
const current = scrollTarget.scrollTop;
|
|
1080
|
-
if (Math.abs(current - destination) <= 1) {
|
|
1081
|
-
return;
|
|
1082
|
-
}
|
|
1083
|
-
if (current !== keyboardScrollObserved) {
|
|
1084
|
-
keyboardScrollObserved = current;
|
|
1085
|
-
return;
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
keyboardScrollElement = scrollTarget;
|
|
1089
|
-
keyboardScrollDestination = destination;
|
|
1090
|
-
keyboardScrollChecks = 0;
|
|
1091
|
-
keyboardScrollObserved = scrollTarget.scrollTop;
|
|
1092
|
-
animateKeyboardScroll(scrollTarget, destination);
|
|
969
|
+
const targetCenter = (targetRect.top + targetRect.bottom) / 2;
|
|
970
|
+
const visibleCenter = (visibleTop + visibleBottom) / 2;
|
|
971
|
+
const nextScrollTop = scrollTarget.scrollTop + targetCenter - visibleCenter;
|
|
972
|
+
animateKeyboardScroll(scrollTarget, clamp(nextScrollTop, 0, maxScrollTop));
|
|
1093
973
|
};
|
|
1094
974
|
const scheduleKeyboardFocusAlignment = () => {
|
|
1095
975
|
keyboardFocusFrame.request(alignFocusedKeyboardTarget);
|
|
1096
976
|
};
|
|
1097
|
-
|
|
1098
|
-
// When focus moves with the keyboard already up, no viewport resize events follow to
|
|
1099
|
-
// re-run alignment, and the single frame-scheduled pass is unreliable: WebKit resolves
|
|
1100
|
-
// the reveal for the focus asynchronously (a UI-process round trip) and cancels any
|
|
1101
|
-
// in-flight scroll the pass started when it lands, and layout reacting to the focus
|
|
1102
|
-
// change (e.g. a footer sized by `:focus-within` and the keyboard inset) settles only
|
|
1103
|
-
// after its transition, so the pass measures stale geometry. Re-align on an interval
|
|
1104
|
-
// until both settle; the alignment's scroll bookkeeping observes before re-issuing,
|
|
1105
|
-
// so passes that find the scroll on course are inert.
|
|
1106
|
-
const scheduleDelayedKeyboardRealign = () => {
|
|
1107
|
-
let remainingPasses = KEYBOARD_REALIGN_MAX_PASSES;
|
|
1108
|
-
const realign = () => {
|
|
1109
|
-
alignFocusedKeyboardTarget();
|
|
1110
|
-
remainingPasses -= 1;
|
|
1111
|
-
if (remainingPasses > 0) {
|
|
1112
|
-
keyboardRealignTimeout.start(KEYBOARD_REALIGN_INTERVAL, realign);
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
keyboardRealignTimeout.start(KEYBOARD_REALIGN_INTERVAL, realign);
|
|
1116
|
-
};
|
|
1117
977
|
const captureFocusedKeyboardTarget = eventTarget => {
|
|
1118
978
|
if (nestedDrawerOpen) {
|
|
1119
979
|
return false;
|
|
@@ -1125,56 +985,16 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
1125
985
|
if (!target || !contains(rootElement, target)) {
|
|
1126
986
|
return false;
|
|
1127
987
|
}
|
|
1128
|
-
|
|
1129
|
-
// A new field starts a fresh alignment; the scroll bookkeeping must not carry
|
|
1130
|
-
// over from the previous field's scroll.
|
|
1131
|
-
if (focusedKeyboardTargetRef.current !== target) {
|
|
1132
|
-
keyboardScrollElement = null;
|
|
1133
|
-
}
|
|
1134
988
|
focusedKeyboardTargetRef.current = target;
|
|
1135
989
|
return true;
|
|
1136
990
|
};
|
|
1137
991
|
const handleFocusIn = event => {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
// handler that blurs or moves focus — and must be reconciled rather than
|
|
1141
|
-
// suppressed, so stop suppressing here instead of waiting for `.focus()` to return.
|
|
1142
|
-
programmaticKeyboardFocusRef.current = false;
|
|
1143
|
-
consumePreemptedFocus();
|
|
1144
|
-
if (!captureFocusedKeyboardTarget(getTarget(event))) {
|
|
1145
|
-
// Focus landed outside any drawer keyboard input. The `focusout` on the previous
|
|
1146
|
-
// field normally clears the tracked target, but the tap path suppresses that
|
|
1147
|
-
// `focusout` via `programmaticKeyboardFocusRef`, so a consumer `onFocus` handler that
|
|
1148
|
-
// redirects focus out of the drawer would otherwise leave a stale target holding its
|
|
1149
|
-
// inset, slack, and pending realign. Reconcile against the real focus here.
|
|
1150
|
-
// A handler that ends focus entirely emits no `focusin` to reconcile from; that case
|
|
1151
|
-
// is covered by clearing the suppression flag above so its `focusout` is handled.
|
|
1152
|
-
clearFocusedKeyboardTarget();
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
// Covers every way focus can land with the keyboard already up: native moves (the
|
|
1157
|
-
// keyboard's previous/next arrows) and the tap path (which suppresses `focusout`
|
|
1158
|
-
// handling via the programmatic flag).
|
|
1159
|
-
if (getKeyboardVisualViewport(win) != null) {
|
|
1160
|
-
scheduleDelayedKeyboardRealign();
|
|
992
|
+
if (captureFocusedKeyboardTarget(getTarget(event))) {
|
|
993
|
+
scheduleKeyboardFocusAlignment();
|
|
1161
994
|
}
|
|
1162
|
-
scheduleKeyboardFocusAlignment();
|
|
1163
995
|
};
|
|
1164
996
|
const handleFocusOut = event => {
|
|
1165
|
-
// The blur inside `focusKeyboardInputWithoutPageScroll` is followed synchronously by
|
|
1166
|
-
// a re-focus; clearing state here would drop the keyboard inset for a frame.
|
|
1167
|
-
if (programmaticKeyboardFocusRef.current) {
|
|
1168
|
-
return;
|
|
1169
|
-
}
|
|
1170
997
|
if (captureFocusedKeyboardTarget(event.relatedTarget)) {
|
|
1171
|
-
const target = focusedKeyboardTargetRef.current;
|
|
1172
|
-
const keyboardViewport = getKeyboardVisualViewport(win);
|
|
1173
|
-
// The delayed realign passes are scheduled by the `focusin` that follows once
|
|
1174
|
-
// focus lands on the captured target.
|
|
1175
|
-
if (target && keyboardViewport) {
|
|
1176
|
-
preemptFocusReveal(target, keyboardViewport);
|
|
1177
|
-
}
|
|
1178
998
|
scheduleKeyboardFocusAlignment();
|
|
1179
999
|
return;
|
|
1180
1000
|
}
|
|
@@ -1189,36 +1009,25 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
1189
1009
|
if (visualViewport) {
|
|
1190
1010
|
cleanupListeners.push(addEventListener(visualViewport, 'resize', handleViewportUpdate), addEventListener(visualViewport, 'scroll', handleViewportUpdate));
|
|
1191
1011
|
}
|
|
1192
|
-
|
|
1193
|
-
if (restoreWindowScroll()) {
|
|
1194
|
-
// Recompute the keyboard inset once the page is back at rest; measurements taken
|
|
1195
|
-
// mid-reveal (a non-zero visual viewport offset) inflate it and push the popup up.
|
|
1196
|
-
scheduleKeyboardFocusAlignment();
|
|
1197
|
-
}
|
|
1198
|
-
};
|
|
1199
|
-
|
|
1200
|
-
// Once the user puts a finger down they own the scroll position. Stop both the frame settle
|
|
1201
|
-
// loop, which can center after changing layout settles, and the delayed passes, which can't
|
|
1202
|
-
// distinguish a user scroll from a reveal scroll WebKit canceled. A later focus or viewport
|
|
1203
|
-
// change reschedules alignment if it's still needed.
|
|
1204
|
-
const cancelKeyboardRealignOnPointerDown = () => {
|
|
1205
|
-
keyboardFocusFrame.cancel();
|
|
1206
|
-
keyboardRealignTimeout.clear();
|
|
1207
|
-
keyboardScrollElement = null;
|
|
1208
|
-
};
|
|
1209
|
-
cleanupListeners.push(addEventListener(doc, 'focusin', handleFocusIn, true), addEventListener(doc, 'focusout', handleFocusOut, true), addEventListener(win, 'scroll', handleWindowScroll), addEventListener(doc, 'pointerdown', cancelKeyboardRealignOnPointerDown, true));
|
|
1012
|
+
cleanupListeners.push(addEventListener(doc, 'focusin', handleFocusIn, true), addEventListener(doc, 'focusout', handleFocusOut, true));
|
|
1210
1013
|
if (captureFocusedKeyboardTarget(activeElement(doc))) {
|
|
1211
1014
|
scheduleKeyboardFocusAlignment();
|
|
1212
1015
|
}
|
|
1213
1016
|
return () => {
|
|
1214
1017
|
cleanupListeners.forEach(cleanup => cleanup());
|
|
1215
|
-
consumePreemptedFocus();
|
|
1216
1018
|
clearFocusedKeyboardTarget();
|
|
1217
1019
|
rootElement.style.removeProperty(DrawerViewportCssVars.keyboardInset);
|
|
1218
1020
|
};
|
|
1219
|
-
}, [animateKeyboardScroll, keyboardFocusFrame,
|
|
1021
|
+
}, [animateKeyboardScroll, keyboardFocusFrame, mounted, nestedDrawerOpen, open, restoreKeyboardScrollAdjustment, rootElement, setKeyboardScrollSlack]);
|
|
1220
1022
|
const onTouchStart = useStableCallback(event => {
|
|
1023
|
+
if (!open || !mounted || nestedDrawerOpen) {
|
|
1024
|
+
resetTouchTrackingState();
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1221
1027
|
const touch = event.touches[0];
|
|
1028
|
+
if (!touch) {
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1222
1031
|
pendingKeyboardFocusMovedRef.current = false;
|
|
1223
1032
|
keyboardTouchStartRef.current = {
|
|
1224
1033
|
x: touch.clientX,
|
|
@@ -1244,9 +1053,9 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
1244
1053
|
return;
|
|
1245
1054
|
}
|
|
1246
1055
|
const touch = event.changedTouches[0] ?? event.touches[0];
|
|
1247
|
-
const
|
|
1056
|
+
const doc = ownerDocument(event.currentTarget);
|
|
1248
1057
|
const nativeEventTarget = getTarget(event.nativeEvent);
|
|
1249
|
-
const pointTarget = touch ? resolveKeyboardTouchTargetFromPoint(
|
|
1058
|
+
const pointTarget = touch ? resolveKeyboardTouchTargetFromPoint(doc, touch.clientX, touch.clientY) : null;
|
|
1250
1059
|
|
|
1251
1060
|
// The lift point landed on another interactive/label element; let its native tap
|
|
1252
1061
|
// through instead of stealing it for the touchstart input.
|
|
@@ -1275,24 +1084,15 @@ function DrawerVirtualKeyboardProvider(props) {
|
|
|
1275
1084
|
|
|
1276
1085
|
// Already focused with the keyboard up: let the native tap through so it can
|
|
1277
1086
|
// reposition the caret, rather than blurring and re-focusing the same input.
|
|
1278
|
-
if (activeElement(ownerDocument(keyboardFocusTarget)) === keyboardFocusTarget && (
|
|
1087
|
+
if (activeElement(ownerDocument(keyboardFocusTarget)) === keyboardFocusTarget && isKeyboardVisualViewportOpen(win)) {
|
|
1279
1088
|
resetTouchTrackingState();
|
|
1280
1089
|
return;
|
|
1281
1090
|
}
|
|
1282
1091
|
|
|
1283
1092
|
// iOS only opens the software keyboard when focus happens synchronously
|
|
1284
|
-
// inside the touch gesture.
|
|
1285
|
-
// intermediate blur would otherwise trigger, so the keyboard inset isn't
|
|
1286
|
-
// dropped for a frame between the blur and the re-focus. It is cleared by the
|
|
1287
|
-
// `focusin` that lands, so only that blur is suppressed; the `finally` is the
|
|
1288
|
-
// backstop for a target that never takes focus.
|
|
1093
|
+
// inside the touch gesture.
|
|
1289
1094
|
event.preventDefault();
|
|
1290
|
-
|
|
1291
|
-
try {
|
|
1292
|
-
focusKeyboardInputWithoutPageScroll(keyboardFocusTarget);
|
|
1293
|
-
} finally {
|
|
1294
|
-
programmaticKeyboardFocusRef.current = false;
|
|
1295
|
-
}
|
|
1095
|
+
focusKeyboardInputWithoutPageScroll(keyboardFocusTarget);
|
|
1296
1096
|
// Preventing the touchend default also suppresses the compatibility mouse
|
|
1297
1097
|
// events, including `click`; redispatch an untrusted replacement on the
|
|
1298
1098
|
// original tap target so click handlers still run with the tap coordinates.
|
|
@@ -1337,16 +1137,13 @@ function resolveKeyboardInputTarget(target) {
|
|
|
1337
1137
|
return isHTMLElement(control) && isKeyboardInputElement(control) ? control : null;
|
|
1338
1138
|
}
|
|
1339
1139
|
function resolveKeyboardTouchTarget(target) {
|
|
1340
|
-
if (!isHTMLElement(target)) {
|
|
1341
|
-
return null;
|
|
1342
|
-
}
|
|
1343
1140
|
const focusTarget = resolveKeyboardInputTarget(target);
|
|
1344
1141
|
if (!focusTarget) {
|
|
1345
1142
|
return null;
|
|
1346
1143
|
}
|
|
1347
1144
|
return {
|
|
1348
1145
|
focusTarget,
|
|
1349
|
-
clickTarget: target
|
|
1146
|
+
clickTarget: isHTMLElement(target) ? target : focusTarget
|
|
1350
1147
|
};
|
|
1351
1148
|
}
|
|
1352
1149
|
|
|
@@ -1359,16 +1156,14 @@ function getContentEditableHost(element) {
|
|
|
1359
1156
|
}
|
|
1360
1157
|
return host;
|
|
1361
1158
|
}
|
|
1362
|
-
function resolveKeyboardTouchTargetFromPoint(
|
|
1363
|
-
const exactTarget = getElementAtPoint(
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1159
|
+
function resolveKeyboardTouchTargetFromPoint(doc, clientX, clientY) {
|
|
1160
|
+
const exactTarget = getElementAtPoint(doc, clientX, clientY);
|
|
1161
|
+
const exactKeyboardTarget = resolveKeyboardInputTarget(exactTarget);
|
|
1162
|
+
if (exactKeyboardTarget) {
|
|
1163
|
+
return {
|
|
1164
|
+
focusTarget: exactKeyboardTarget,
|
|
1165
|
+
clickTarget: isHTMLElement(exactTarget) ? exactTarget : exactKeyboardTarget
|
|
1166
|
+
};
|
|
1372
1167
|
}
|
|
1373
1168
|
|
|
1374
1169
|
// Probing nearby points compensates for iOS retargeting taps while the page reacts
|
|
@@ -1381,7 +1176,7 @@ function resolveKeyboardTouchTargetFromPoint(root, clientX, clientY) {
|
|
|
1381
1176
|
return KEYBOARD_TAP_BLOCKED;
|
|
1382
1177
|
}
|
|
1383
1178
|
for (const [offsetX, offsetY] of [[0, INPUT_TAP_HIT_SLOP], [0, -INPUT_TAP_HIT_SLOP], [INPUT_TAP_HIT_SLOP, 0], [-INPUT_TAP_HIT_SLOP, 0]]) {
|
|
1384
|
-
const keyboardTarget = resolveKeyboardInputTarget(getElementAtPoint(
|
|
1179
|
+
const keyboardTarget = resolveKeyboardInputTarget(getElementAtPoint(doc, clientX + offsetX, clientY + offsetY));
|
|
1385
1180
|
if (keyboardTarget) {
|
|
1386
1181
|
return {
|
|
1387
1182
|
focusTarget: keyboardTarget,
|
|
@@ -1405,10 +1200,15 @@ function dispatchKeyboardClick(target, touch) {
|
|
|
1405
1200
|
}
|
|
1406
1201
|
function focusKeyboardInputWithoutPageScroll(target) {
|
|
1407
1202
|
const wasFocused = activeElement(ownerDocument(target)) === target;
|
|
1203
|
+
const previousOpacity = target.style.opacity;
|
|
1204
|
+
const previousTransform = target.style.transform;
|
|
1205
|
+
const previousTransition = target.style.transition;
|
|
1206
|
+
|
|
1408
1207
|
// iOS Safari can still scroll the page for transformed sheets even with preventScroll.
|
|
1409
|
-
// Move the input off-screen only for the synchronous focus call
|
|
1410
|
-
|
|
1411
|
-
|
|
1208
|
+
// Move the input off-screen only for the synchronous focus call.
|
|
1209
|
+
target.style.transition = 'none';
|
|
1210
|
+
target.style.opacity = '0';
|
|
1211
|
+
target.style.transform = 'translateY(-2000px)';
|
|
1412
1212
|
try {
|
|
1413
1213
|
if (wasFocused) {
|
|
1414
1214
|
target.blur();
|
|
@@ -1417,25 +1217,10 @@ function focusKeyboardInputWithoutPageScroll(target) {
|
|
|
1417
1217
|
preventScroll: true
|
|
1418
1218
|
});
|
|
1419
1219
|
} finally {
|
|
1420
|
-
restoreStyles();
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
// Overrides the painted geometry WebKit samples when an element gains focus. The rect is
|
|
1425
|
-
// hidden (opacity) rather than detached so layout is unaffected; the caller must restore
|
|
1426
|
-
// synchronously before the next paint.
|
|
1427
|
-
function overrideGeometryDuringFocus(target, translateY) {
|
|
1428
|
-
const previousOpacity = target.style.opacity;
|
|
1429
|
-
const previousTransform = target.style.transform;
|
|
1430
|
-
const previousTransition = target.style.transition;
|
|
1431
|
-
target.style.transition = 'none';
|
|
1432
|
-
target.style.opacity = '0';
|
|
1433
|
-
target.style.transform = `translateY(${translateY}px)`;
|
|
1434
|
-
return () => {
|
|
1435
1220
|
target.style.opacity = previousOpacity;
|
|
1436
1221
|
target.style.transform = previousTransform;
|
|
1437
1222
|
target.style.transition = previousTransition;
|
|
1438
|
-
}
|
|
1223
|
+
}
|
|
1439
1224
|
}
|
|
1440
1225
|
function findKeyboardScrollTarget(target, root) {
|
|
1441
1226
|
// Start at the parent: scrolling the focused field's own content (an overflowing
|
|
@@ -1463,22 +1248,11 @@ function getKeyboardVisualViewport(win) {
|
|
|
1463
1248
|
bottom: Math.min(win.innerHeight, top + visualViewport.height)
|
|
1464
1249
|
};
|
|
1465
1250
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
*
|
|
1472
|
-
* The imperative methods take effect only while a root using this handle is mounted; calls made
|
|
1473
|
-
* before a root attaches (or after it unmounts) are ignored.
|
|
1474
|
-
*/
|
|
1475
|
-
class DrawerHandle extends DialogHandle {}
|
|
1476
|
-
|
|
1477
|
-
/**
|
|
1478
|
-
* Creates a new handle to connect a Drawer.Root with detached Drawer.Trigger components.
|
|
1479
|
-
*/
|
|
1480
|
-
function createDrawerHandle() {
|
|
1481
|
-
return new DrawerHandle();
|
|
1251
|
+
function getDrawerKeyboardInset(win, keyboardViewport) {
|
|
1252
|
+
return Math.max(0, win.innerHeight - keyboardViewport.bottom);
|
|
1253
|
+
}
|
|
1254
|
+
function isKeyboardVisualViewportOpen(win) {
|
|
1255
|
+
return !win.visualViewport || getKeyboardVisualViewport(win) != null;
|
|
1482
1256
|
}
|
|
1483
1257
|
|
|
1484
1258
|
var index_parts$5 = /*#__PURE__*/Object.freeze({
|
|
@@ -1487,7 +1261,7 @@ var index_parts$5 = /*#__PURE__*/Object.freeze({
|
|
|
1487
1261
|
Close: DrawerClose,
|
|
1488
1262
|
Content: DrawerContent,
|
|
1489
1263
|
Description: DrawerDescription,
|
|
1490
|
-
Handle:
|
|
1264
|
+
Handle: DialogHandle,
|
|
1491
1265
|
Indent: DrawerIndent,
|
|
1492
1266
|
IndentBackground: DrawerIndentBackground,
|
|
1493
1267
|
Popup: DrawerPopup,
|
|
@@ -1499,7 +1273,7 @@ var index_parts$5 = /*#__PURE__*/Object.freeze({
|
|
|
1499
1273
|
Trigger: DrawerTrigger,
|
|
1500
1274
|
Viewport: DrawerViewport,
|
|
1501
1275
|
VirtualKeyboardProvider: DrawerVirtualKeyboardProvider,
|
|
1502
|
-
createHandle:
|
|
1276
|
+
createHandle: createDialogHandle
|
|
1503
1277
|
});
|
|
1504
1278
|
|
|
1505
1279
|
/**
|
|
@@ -1568,7 +1342,7 @@ const MenuCheckboxItem = /*#__PURE__*/React.forwardRef(function MenuCheckboxItem
|
|
|
1568
1342
|
id: idProp,
|
|
1569
1343
|
label,
|
|
1570
1344
|
nativeButton = false,
|
|
1571
|
-
disabled
|
|
1345
|
+
disabled = false,
|
|
1572
1346
|
closeOnClick = false,
|
|
1573
1347
|
checked: checkedProp,
|
|
1574
1348
|
defaultChecked,
|
|
@@ -1577,7 +1351,6 @@ const MenuCheckboxItem = /*#__PURE__*/React.forwardRef(function MenuCheckboxItem
|
|
|
1577
1351
|
...elementProps
|
|
1578
1352
|
} = componentProps;
|
|
1579
1353
|
const listItem = useCompositeListItem({
|
|
1580
|
-
guess: true,
|
|
1581
1354
|
label
|
|
1582
1355
|
});
|
|
1583
1356
|
const menuPositionerContext = useMenuPositionerContext(true);
|
|
@@ -1585,8 +1358,6 @@ const MenuCheckboxItem = /*#__PURE__*/React.forwardRef(function MenuCheckboxItem
|
|
|
1585
1358
|
const {
|
|
1586
1359
|
store
|
|
1587
1360
|
} = useMenuRootContext();
|
|
1588
|
-
const rootDisabled = store.useState('disabled');
|
|
1589
|
-
const disabled = disabledProp || rootDisabled;
|
|
1590
1361
|
const highlighted = store.useState('isActive', listItem.index);
|
|
1591
1362
|
const itemProps = store.useState('itemProps');
|
|
1592
1363
|
const [checked, setChecked] = useControlled({
|
|
@@ -1615,7 +1386,7 @@ const MenuCheckboxItem = /*#__PURE__*/React.forwardRef(function MenuCheckboxItem
|
|
|
1615
1386
|
}), [disabled, highlighted, checked]);
|
|
1616
1387
|
function handleClick(event) {
|
|
1617
1388
|
const details = createChangeEventDetails(itemPress, event.nativeEvent, undefined, {
|
|
1618
|
-
preventUnmountOnClose
|
|
1389
|
+
preventUnmountOnClose() {}
|
|
1619
1390
|
});
|
|
1620
1391
|
onCheckedChange?.(!checked, details);
|
|
1621
1392
|
if (details.isCanceled) {
|
|
@@ -1658,7 +1429,6 @@ const MenuCheckboxItemIndicator = /*#__PURE__*/React.forwardRef(function MenuChe
|
|
|
1658
1429
|
const indicatorRef = React.useRef(null);
|
|
1659
1430
|
const {
|
|
1660
1431
|
transitionStatus,
|
|
1661
|
-
mounted,
|
|
1662
1432
|
setMounted
|
|
1663
1433
|
} = useTransitionStatus(item.checked);
|
|
1664
1434
|
useOpenChangeComplete({
|
|
@@ -1684,7 +1454,7 @@ const MenuCheckboxItemIndicator = /*#__PURE__*/React.forwardRef(function MenuChe
|
|
|
1684
1454
|
'aria-hidden': true,
|
|
1685
1455
|
...elementProps
|
|
1686
1456
|
},
|
|
1687
|
-
enabled: keepMounted ||
|
|
1457
|
+
enabled: keepMounted || item.checked
|
|
1688
1458
|
});
|
|
1689
1459
|
return element;
|
|
1690
1460
|
});
|
|
@@ -1724,14 +1494,30 @@ function usePopupAutoResize(parameters) {
|
|
|
1724
1494
|
side,
|
|
1725
1495
|
direction
|
|
1726
1496
|
} = parameters;
|
|
1727
|
-
const runOnceAnimationsFinish = useAnimationsFinished(popupElement, true);
|
|
1497
|
+
const runOnceAnimationsFinish = useAnimationsFinished(popupElement, true, false);
|
|
1728
1498
|
const animationFrame = useAnimationFrame();
|
|
1729
1499
|
const committedDimensionsRef = React.useRef(null);
|
|
1730
1500
|
const isInitialRenderRef = React.useRef(true);
|
|
1731
1501
|
const restoreAnchoringStylesRef = React.useRef(NOOP);
|
|
1732
1502
|
const onMeasureLayout = useStableCallback(onMeasureLayoutParam);
|
|
1733
1503
|
const onMeasureLayoutComplete = useStableCallback(onMeasureLayoutCompleteParam);
|
|
1734
|
-
const anchoringStyles = React.useMemo(() =>
|
|
1504
|
+
const anchoringStyles = React.useMemo(() => {
|
|
1505
|
+
// Ensure popup size transitions correctly when anchored to `bottom` (side=top) or `right` (side=left).
|
|
1506
|
+
let isOriginSide = side === 'top';
|
|
1507
|
+
let isPhysicalLeft = side === 'left';
|
|
1508
|
+
if (direction === 'rtl') {
|
|
1509
|
+
isOriginSide = isOriginSide || side === 'inline-end';
|
|
1510
|
+
isPhysicalLeft = isPhysicalLeft || side === 'inline-end';
|
|
1511
|
+
} else {
|
|
1512
|
+
isOriginSide = isOriginSide || side === 'inline-start';
|
|
1513
|
+
isPhysicalLeft = isPhysicalLeft || side === 'inline-start';
|
|
1514
|
+
}
|
|
1515
|
+
return isOriginSide ? {
|
|
1516
|
+
position: 'absolute',
|
|
1517
|
+
[side === 'top' ? 'bottom' : 'top']: '0',
|
|
1518
|
+
[isPhysicalLeft ? 'right' : 'left']: '0'
|
|
1519
|
+
} : EMPTY_OBJECT;
|
|
1520
|
+
}, [side, direction]);
|
|
1735
1521
|
useIsoLayoutEffect(() => {
|
|
1736
1522
|
// Reset the state when the popup is closed.
|
|
1737
1523
|
if (!mounted) {
|
|
@@ -1807,19 +1593,6 @@ function usePopupAutoResize(parameters) {
|
|
|
1807
1593
|
};
|
|
1808
1594
|
}, [content, popupElement, positionerElement, runOnceAnimationsFinish, animationFrame, mounted, onMeasureLayout, onMeasureLayoutComplete, anchoringStyles]);
|
|
1809
1595
|
}
|
|
1810
|
-
function getPopupAnchoringStyles(side, direction) {
|
|
1811
|
-
// Ensure popup size transitions correctly when anchored to `bottom` (side=top) or `right` (side=left).
|
|
1812
|
-
const isPhysicalTop = side === 'top';
|
|
1813
|
-
const isPhysicalLeft = side === 'left' || side === (direction === 'rtl' ? 'inline-end' : 'inline-start');
|
|
1814
|
-
if (!isPhysicalTop && !isPhysicalLeft) {
|
|
1815
|
-
return EMPTY_OBJECT;
|
|
1816
|
-
}
|
|
1817
|
-
return {
|
|
1818
|
-
position: 'absolute',
|
|
1819
|
-
[isPhysicalTop ? 'bottom' : 'top']: '0',
|
|
1820
|
-
[isPhysicalLeft ? 'right' : 'left']: '0'
|
|
1821
|
-
};
|
|
1822
|
-
}
|
|
1823
1596
|
function overrideElementStyle(element, property, value) {
|
|
1824
1597
|
const originalValue = element.style.getPropertyValue(property);
|
|
1825
1598
|
element.style.setProperty(property, value);
|
|
@@ -1849,80 +1622,6 @@ function setPositionerCssSize(positionerElement, size) {
|
|
|
1849
1622
|
positionerElement.style.setProperty('--positioner-height', height);
|
|
1850
1623
|
}
|
|
1851
1624
|
|
|
1852
|
-
const adaptiveOrigin = {
|
|
1853
|
-
name: 'adaptiveOrigin',
|
|
1854
|
-
async fn(state) {
|
|
1855
|
-
const {
|
|
1856
|
-
x: rawX,
|
|
1857
|
-
y: rawY,
|
|
1858
|
-
rects: {
|
|
1859
|
-
floating: floatRect
|
|
1860
|
-
},
|
|
1861
|
-
elements: {
|
|
1862
|
-
floating
|
|
1863
|
-
},
|
|
1864
|
-
platform,
|
|
1865
|
-
strategy,
|
|
1866
|
-
placement
|
|
1867
|
-
} = state;
|
|
1868
|
-
const win = getWindow(floating);
|
|
1869
|
-
const styles = win.getComputedStyle(floating);
|
|
1870
|
-
const hasTransition = styles.transitionDuration !== '0s' && styles.transitionDuration !== '';
|
|
1871
|
-
if (!hasTransition) {
|
|
1872
|
-
return {
|
|
1873
|
-
x: rawX,
|
|
1874
|
-
y: rawY,
|
|
1875
|
-
data: DEFAULT_SIDES
|
|
1876
|
-
};
|
|
1877
|
-
}
|
|
1878
|
-
const offsetParent = await platform.getOffsetParent?.(floating);
|
|
1879
|
-
let offsetDimensions = {
|
|
1880
|
-
width: 0,
|
|
1881
|
-
height: 0
|
|
1882
|
-
};
|
|
1883
|
-
|
|
1884
|
-
// For fixed strategy, prefer visualViewport if available
|
|
1885
|
-
if (strategy === 'fixed' && win?.visualViewport) {
|
|
1886
|
-
offsetDimensions = {
|
|
1887
|
-
width: win.visualViewport.width,
|
|
1888
|
-
height: win.visualViewport.height
|
|
1889
|
-
};
|
|
1890
|
-
} else if (offsetParent === win) {
|
|
1891
|
-
const doc = ownerDocument(floating);
|
|
1892
|
-
offsetDimensions = {
|
|
1893
|
-
width: doc.documentElement.clientWidth,
|
|
1894
|
-
height: doc.documentElement.clientHeight
|
|
1895
|
-
};
|
|
1896
|
-
} else if (await platform.isElement?.(offsetParent)) {
|
|
1897
|
-
offsetDimensions = await platform.getDimensions(offsetParent);
|
|
1898
|
-
}
|
|
1899
|
-
const currentSide = getSide(placement);
|
|
1900
|
-
let x = rawX;
|
|
1901
|
-
let y = rawY;
|
|
1902
|
-
if (currentSide === 'left') {
|
|
1903
|
-
x = offsetDimensions.width - (rawX + floatRect.width);
|
|
1904
|
-
}
|
|
1905
|
-
if (currentSide === 'top') {
|
|
1906
|
-
y = offsetDimensions.height - (rawY + floatRect.height);
|
|
1907
|
-
}
|
|
1908
|
-
const sideX = currentSide === 'left' ? 'right' : DEFAULT_SIDES.sideX;
|
|
1909
|
-
const sideY = currentSide === 'top' ? 'bottom' : DEFAULT_SIDES.sideY;
|
|
1910
|
-
return {
|
|
1911
|
-
x,
|
|
1912
|
-
y,
|
|
1913
|
-
data: {
|
|
1914
|
-
sideX,
|
|
1915
|
-
sideY
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
};
|
|
1920
|
-
|
|
1921
|
-
const popupViewportStateMapping = {
|
|
1922
|
-
activationDirection: value => value ? {
|
|
1923
|
-
'data-activation-direction': value
|
|
1924
|
-
} : null
|
|
1925
|
-
};
|
|
1926
1625
|
/**
|
|
1927
1626
|
* Builds morphing viewport containers for popups that animate between trigger-based content.
|
|
1928
1627
|
* Handles previous-content snapshots, auto-resize, and state attributes for transitions.
|
|
@@ -1931,6 +1630,7 @@ function usePopupViewport(parameters) {
|
|
|
1931
1630
|
const {
|
|
1932
1631
|
store,
|
|
1933
1632
|
side,
|
|
1633
|
+
cssVars,
|
|
1934
1634
|
children
|
|
1935
1635
|
} = parameters;
|
|
1936
1636
|
const direction = useDirection();
|
|
@@ -1950,15 +1650,14 @@ function usePopupViewport(parameters) {
|
|
|
1950
1650
|
const [newTriggerOffset, setNewTriggerOffset] = React.useState(null);
|
|
1951
1651
|
const currentContainerRef = React.useRef(null);
|
|
1952
1652
|
const previousContainerRef = React.useRef(null);
|
|
1953
|
-
const onAnimationsFinished = useAnimationsFinished(currentContainerRef, true);
|
|
1653
|
+
const onAnimationsFinished = useAnimationsFinished(currentContainerRef, true, false);
|
|
1954
1654
|
const cleanupFrame = useAnimationFrame();
|
|
1955
|
-
const cleanupControllerRef = React.useRef(null);
|
|
1956
1655
|
const [previousContentDimensions, setPreviousContentDimensions] = React.useState(null);
|
|
1957
1656
|
const [showStartingStyleAttribute, setShowStartingStyleAttribute] = React.useState(false);
|
|
1958
1657
|
useIsoLayoutEffect(() => {
|
|
1959
|
-
store.set('
|
|
1658
|
+
store.set('hasViewport', true);
|
|
1960
1659
|
return () => {
|
|
1961
|
-
store.set('
|
|
1660
|
+
store.set('hasViewport', false);
|
|
1962
1661
|
};
|
|
1963
1662
|
}, [store]);
|
|
1964
1663
|
const handleMeasureLayout = useStableCallback(() => {
|
|
@@ -1974,16 +1673,6 @@ function usePopupViewport(parameters) {
|
|
|
1974
1673
|
setPreviousContentDimensions(previousDimensions);
|
|
1975
1674
|
}
|
|
1976
1675
|
});
|
|
1977
|
-
const armViewportCleanup = useStableCallback(() => {
|
|
1978
|
-
cleanupControllerRef.current?.abort();
|
|
1979
|
-
const controller = new AbortController();
|
|
1980
|
-
cleanupControllerRef.current = controller;
|
|
1981
|
-
onAnimationsFinished(() => {
|
|
1982
|
-
setPreviousContentNode(null);
|
|
1983
|
-
setPreviousContentDimensions(null);
|
|
1984
|
-
capturedNodeRef.current = null;
|
|
1985
|
-
}, controller.signal);
|
|
1986
|
-
});
|
|
1987
1676
|
const lastHandledTriggerRef = React.useRef(null);
|
|
1988
1677
|
useIsoLayoutEffect(() => {
|
|
1989
1678
|
if (!open || !mounted) {
|
|
@@ -2001,33 +1690,19 @@ function usePopupViewport(parameters) {
|
|
|
2001
1690
|
// so we can pass it to the style hook for animation purposes.
|
|
2002
1691
|
const offset = calculateRelativePosition(previousActiveTrigger, activeTrigger);
|
|
2003
1692
|
setNewTriggerOffset(offset);
|
|
1693
|
+
cleanupFrame.request(() => {
|
|
1694
|
+
ReactDOM.flushSync(() => {
|
|
1695
|
+
setShowStartingStyleAttribute(false);
|
|
1696
|
+
});
|
|
1697
|
+
onAnimationsFinished(() => {
|
|
1698
|
+
setPreviousContentNode(null);
|
|
1699
|
+
setPreviousContentDimensions(null);
|
|
1700
|
+
capturedNodeRef.current = null;
|
|
1701
|
+
});
|
|
1702
|
+
});
|
|
2004
1703
|
lastHandledTriggerRef.current = activeTrigger;
|
|
2005
1704
|
}
|
|
2006
|
-
}, [activeTrigger, previousActiveTrigger]);
|
|
2007
|
-
|
|
2008
|
-
// Arm cleanup after a trigger change, and re-arm it if the current container remounts
|
|
2009
|
-
// mid-transition when a lagging payload bumps `currentContentKey`. The remount discards
|
|
2010
|
-
// the running entry animation (and with transition-style CSS the replacement mounts at
|
|
2011
|
-
// final styles with no animation at all), so re-run the starting-style choreography —
|
|
2012
|
-
// otherwise the watcher either strands or fires before the previous container's exit
|
|
2013
|
-
// animation finishes.
|
|
2014
|
-
useIsoLayoutEffect(() => {
|
|
2015
|
-
if (previousContentNode == null) {
|
|
2016
|
-
return;
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
// Abort the stale watcher synchronously. The remount cancels the old container's
|
|
2020
|
-
// animations, and the resulting promise rejection would otherwise run the cleanup
|
|
2021
|
-
// in a microtask before the re-armed watcher below is in place.
|
|
2022
|
-
cleanupControllerRef.current?.abort();
|
|
2023
|
-
setShowStartingStyleAttribute(true);
|
|
2024
|
-
cleanupFrame.request(() => {
|
|
2025
|
-
ReactDOM.flushSync(() => {
|
|
2026
|
-
setShowStartingStyleAttribute(false);
|
|
2027
|
-
});
|
|
2028
|
-
armViewportCleanup();
|
|
2029
|
-
});
|
|
2030
|
-
}, [currentContentKey, previousContentNode, armViewportCleanup, cleanupFrame]);
|
|
1705
|
+
}, [activeTrigger, previousActiveTrigger, previousContentNode, onAnimationsFinished, cleanupFrame]);
|
|
2031
1706
|
|
|
2032
1707
|
// Capture a clone of the current content DOM subtree when not transitioning.
|
|
2033
1708
|
// We can't store previous React nodes as they may be stateful; instead we capture DOM clones for visual continuity.
|
|
@@ -2064,8 +1739,8 @@ function usePopupViewport(parameters) {
|
|
|
2064
1739
|
ref: previousContainerRef,
|
|
2065
1740
|
style: {
|
|
2066
1741
|
...(previousContentDimensions ? {
|
|
2067
|
-
|
|
2068
|
-
|
|
1742
|
+
[cssVars.popupWidth]: `${previousContentDimensions.width}px`,
|
|
1743
|
+
[cssVars.popupHeight]: `${previousContentDimensions.height}px`
|
|
2069
1744
|
} : null),
|
|
2070
1745
|
position: 'absolute'
|
|
2071
1746
|
},
|
|
@@ -2190,6 +1865,28 @@ function usePopupContentKey(activeTriggerId, payload) {
|
|
|
2190
1865
|
return `${activeTriggerId ?? 'current'}-${contentKey}`;
|
|
2191
1866
|
}
|
|
2192
1867
|
|
|
1868
|
+
let MenuViewportCssVars = /*#__PURE__*/function (MenuViewportCssVars) {
|
|
1869
|
+
/**
|
|
1870
|
+
* The width of the parent popup.
|
|
1871
|
+
* This variable is placed on the 'previous' container and stores the width of the popup when the previous content was rendered.
|
|
1872
|
+
* It can be used to freeze the dimensions of the popup when animating between different content.
|
|
1873
|
+
*/
|
|
1874
|
+
MenuViewportCssVars["popupWidth"] = "--popup-width";
|
|
1875
|
+
/**
|
|
1876
|
+
* The height of the parent popup.
|
|
1877
|
+
* This variable is placed on the 'previous' container and stores the height of the popup when the previous content was rendered.
|
|
1878
|
+
* It can be used to freeze the dimensions of the popup when animating between different content.
|
|
1879
|
+
*/
|
|
1880
|
+
MenuViewportCssVars["popupHeight"] = "--popup-height";
|
|
1881
|
+
return MenuViewportCssVars;
|
|
1882
|
+
}({});
|
|
1883
|
+
|
|
1884
|
+
const stateAttributesMapping$2 = {
|
|
1885
|
+
activationDirection: value => value ? {
|
|
1886
|
+
'data-activation-direction': value
|
|
1887
|
+
} : null
|
|
1888
|
+
};
|
|
1889
|
+
|
|
2193
1890
|
/**
|
|
2194
1891
|
* A viewport for displaying content transitions.
|
|
2195
1892
|
* This component is only required if one popup can be opened by multiple triggers, its content
|
|
@@ -2219,6 +1916,7 @@ const MenuViewport = /*#__PURE__*/React.forwardRef(function MenuViewport(compone
|
|
|
2219
1916
|
} = usePopupViewport({
|
|
2220
1917
|
store,
|
|
2221
1918
|
side,
|
|
1919
|
+
cssVars: MenuViewportCssVars,
|
|
2222
1920
|
children
|
|
2223
1921
|
});
|
|
2224
1922
|
const state = {
|
|
@@ -2232,50 +1930,47 @@ const MenuViewport = /*#__PURE__*/React.forwardRef(function MenuViewport(compone
|
|
|
2232
1930
|
props: [elementProps, {
|
|
2233
1931
|
children: childrenToRender
|
|
2234
1932
|
}],
|
|
2235
|
-
stateAttributesMapping:
|
|
1933
|
+
stateAttributesMapping: stateAttributesMapping$2
|
|
2236
1934
|
});
|
|
2237
1935
|
});
|
|
2238
1936
|
if (process.env.NODE_ENV !== "production") MenuViewport.displayName = "MenuViewport";
|
|
2239
1937
|
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
* before a root attaches (or after it unmounts) are ignored.
|
|
2247
|
-
*/
|
|
2248
|
-
class MenuHandle extends BasePopupHandle {
|
|
1938
|
+
class MenuHandle {
|
|
1939
|
+
/**
|
|
1940
|
+
* Internal store holding the menu's state.
|
|
1941
|
+
* @internal
|
|
1942
|
+
*/
|
|
1943
|
+
|
|
2249
1944
|
constructor() {
|
|
2250
|
-
|
|
1945
|
+
this.store = new MenuStore();
|
|
2251
1946
|
}
|
|
2252
1947
|
|
|
2253
1948
|
/**
|
|
2254
1949
|
* Opens the menu and associates it with the trigger with the given id.
|
|
1950
|
+
* The trigger must be a Menu.Trigger component with this handle passed as a prop.
|
|
2255
1951
|
*
|
|
2256
|
-
*
|
|
2257
|
-
*
|
|
2258
|
-
* @param triggerId ID of the trigger to associate with the menu. The trigger must be a matching
|
|
2259
|
-
* `Menu.Trigger` with this handle passed as a prop.
|
|
1952
|
+
* @param triggerId ID of the trigger to associate with the menu.
|
|
2260
1953
|
*/
|
|
2261
1954
|
open(triggerId) {
|
|
2262
|
-
this.
|
|
1955
|
+
const triggerElement = triggerId ? this.store.context.triggerElements.getById(triggerId) : undefined;
|
|
1956
|
+
if (triggerId && !triggerElement) {
|
|
1957
|
+
throw new Error(process.env.NODE_ENV !== "production" ? `Base UI: MenuHandle.open: No trigger found with id "${triggerId}".` : formatErrorMessage(83, triggerId));
|
|
1958
|
+
}
|
|
1959
|
+
this.store.setOpen(true, createChangeEventDetails('imperative-action', undefined, triggerElement));
|
|
2263
1960
|
}
|
|
2264
1961
|
|
|
2265
1962
|
/**
|
|
2266
1963
|
* Closes the menu.
|
|
2267
|
-
*
|
|
2268
|
-
* This method should only be called in an event handler or an effect (not during rendering).
|
|
2269
1964
|
*/
|
|
2270
1965
|
close() {
|
|
2271
|
-
this.
|
|
1966
|
+
this.store.setOpen(false, createChangeEventDetails('imperative-action', undefined, undefined));
|
|
2272
1967
|
}
|
|
2273
1968
|
|
|
2274
1969
|
/**
|
|
2275
|
-
*
|
|
1970
|
+
* Indicates whether the menu is currently open.
|
|
2276
1971
|
*/
|
|
2277
1972
|
get isOpen() {
|
|
2278
|
-
return this.
|
|
1973
|
+
return this.store.select('open');
|
|
2279
1974
|
}
|
|
2280
1975
|
}
|
|
2281
1976
|
|
|
@@ -2322,6 +2017,20 @@ function useScrollAreaScrollbarContext() {
|
|
|
2322
2017
|
return context;
|
|
2323
2018
|
}
|
|
2324
2019
|
|
|
2020
|
+
let ScrollAreaScrollbarCssVars = /*#__PURE__*/function (ScrollAreaScrollbarCssVars) {
|
|
2021
|
+
/**
|
|
2022
|
+
* The scroll area thumb's height.
|
|
2023
|
+
* @type {number}
|
|
2024
|
+
*/
|
|
2025
|
+
ScrollAreaScrollbarCssVars["scrollAreaThumbHeight"] = "--scroll-area-thumb-height";
|
|
2026
|
+
/**
|
|
2027
|
+
* The scroll area thumb's width.
|
|
2028
|
+
* @type {number}
|
|
2029
|
+
*/
|
|
2030
|
+
ScrollAreaScrollbarCssVars["scrollAreaThumbWidth"] = "--scroll-area-thumb-width";
|
|
2031
|
+
return ScrollAreaScrollbarCssVars;
|
|
2032
|
+
}({});
|
|
2033
|
+
|
|
2325
2034
|
/**
|
|
2326
2035
|
* A vertical or horizontal scrollbar for the scroll area.
|
|
2327
2036
|
* Renders a `<div>` element.
|
|
@@ -2342,6 +2051,7 @@ const ScrollAreaScrollbar = /*#__PURE__*/React.forwardRef(function ScrollAreaScr
|
|
|
2342
2051
|
scrollingX,
|
|
2343
2052
|
scrollingY,
|
|
2344
2053
|
hiddenState,
|
|
2054
|
+
overflowEdges,
|
|
2345
2055
|
scrollbarYRef,
|
|
2346
2056
|
scrollbarXRef,
|
|
2347
2057
|
viewportRef,
|
|
@@ -2350,37 +2060,43 @@ const ScrollAreaScrollbar = /*#__PURE__*/React.forwardRef(function ScrollAreaScr
|
|
|
2350
2060
|
handlePointerDown,
|
|
2351
2061
|
handlePointerUp,
|
|
2352
2062
|
handleScroll,
|
|
2353
|
-
disableViewportSnap,
|
|
2354
2063
|
rootId,
|
|
2355
2064
|
thumbSize,
|
|
2356
|
-
hasMeasuredScrollbar
|
|
2357
|
-
viewportState
|
|
2065
|
+
hasMeasuredScrollbar
|
|
2358
2066
|
} = useScrollAreaRootContext();
|
|
2359
|
-
const vertical = orientation === 'vertical';
|
|
2360
2067
|
const state = {
|
|
2361
|
-
...viewportState,
|
|
2362
2068
|
hovering,
|
|
2363
|
-
scrolling:
|
|
2364
|
-
|
|
2069
|
+
scrolling: {
|
|
2070
|
+
horizontal: scrollingX,
|
|
2071
|
+
vertical: scrollingY
|
|
2072
|
+
}[orientation],
|
|
2073
|
+
orientation,
|
|
2074
|
+
hasOverflowX: !hiddenState.x,
|
|
2075
|
+
hasOverflowY: !hiddenState.y,
|
|
2076
|
+
overflowXStart: overflowEdges.xStart,
|
|
2077
|
+
overflowXEnd: overflowEdges.xEnd,
|
|
2078
|
+
overflowYStart: overflowEdges.yStart,
|
|
2079
|
+
overflowYEnd: overflowEdges.yEnd,
|
|
2080
|
+
cornerHidden: hiddenState.corner
|
|
2365
2081
|
};
|
|
2366
2082
|
const direction = useDirection();
|
|
2367
2083
|
const hideTrackUntilMeasured = !hasMeasuredScrollbar && !keepMounted;
|
|
2368
|
-
const isHidden = vertical ? hiddenState.y : hiddenState.x;
|
|
2084
|
+
const isHidden = orientation === 'vertical' ? hiddenState.y : hiddenState.x;
|
|
2369
2085
|
const shouldRender = keepMounted || !isHidden;
|
|
2370
2086
|
React.useEffect(() => {
|
|
2371
2087
|
if (!shouldRender) {
|
|
2372
2088
|
return undefined;
|
|
2373
2089
|
}
|
|
2374
2090
|
const viewportEl = viewportRef.current;
|
|
2375
|
-
const scrollbarEl = vertical ? scrollbarYRef.current : scrollbarXRef.current;
|
|
2091
|
+
const scrollbarEl = orientation === 'vertical' ? scrollbarYRef.current : scrollbarXRef.current;
|
|
2376
2092
|
if (!scrollbarEl) {
|
|
2377
2093
|
return undefined;
|
|
2378
2094
|
}
|
|
2379
2095
|
function handleWheel(event) {
|
|
2380
|
-
if (!viewportEl || event.ctrlKey) {
|
|
2096
|
+
if (!viewportEl || !scrollbarEl || event.ctrlKey) {
|
|
2381
2097
|
return;
|
|
2382
2098
|
}
|
|
2383
|
-
const horizontal =
|
|
2099
|
+
const horizontal = orientation === 'horizontal';
|
|
2384
2100
|
const scrollProperty = horizontal ? 'scrollLeft' : 'scrollTop';
|
|
2385
2101
|
const delta = horizontal ? event.deltaX : event.deltaY;
|
|
2386
2102
|
if (delta === 0) {
|
|
@@ -2407,7 +2123,7 @@ const ScrollAreaScrollbar = /*#__PURE__*/React.forwardRef(function ScrollAreaScr
|
|
|
2407
2123
|
return addEventListener(scrollbarEl, 'wheel', handleWheel, {
|
|
2408
2124
|
passive: false
|
|
2409
2125
|
});
|
|
2410
|
-
}, [direction, handleScroll,
|
|
2126
|
+
}, [direction, handleScroll, orientation, scrollbarXRef, scrollbarYRef, shouldRender, viewportRef]);
|
|
2411
2127
|
const props = {
|
|
2412
2128
|
...(rootId && {
|
|
2413
2129
|
'data-id': `${rootId}-scrollbar`
|
|
@@ -2417,55 +2133,58 @@ const ScrollAreaScrollbar = /*#__PURE__*/React.forwardRef(function ScrollAreaScr
|
|
|
2417
2133
|
return;
|
|
2418
2134
|
}
|
|
2419
2135
|
const target = getTarget(event.nativeEvent);
|
|
2420
|
-
const
|
|
2136
|
+
const thumb = orientation === 'vertical' ? thumbYRef.current : thumbXRef.current;
|
|
2421
2137
|
|
|
2422
2138
|
// Ignore clicks on thumb, including cases where React retargets the
|
|
2423
2139
|
// synthetic event to the track host across a shadow boundary.
|
|
2424
|
-
if (
|
|
2425
|
-
return;
|
|
2426
|
-
}
|
|
2427
|
-
const viewportEl = viewportRef.current;
|
|
2428
|
-
if (!viewportEl) {
|
|
2429
|
-
return;
|
|
2430
|
-
}
|
|
2431
|
-
const scrollbarEl = vertical ? scrollbarYRef.current : scrollbarXRef.current;
|
|
2432
|
-
if (!thumbEl || !scrollbarEl) {
|
|
2140
|
+
if (thumb && contains(thumb, target)) {
|
|
2433
2141
|
return;
|
|
2434
2142
|
}
|
|
2435
|
-
|
|
2436
|
-
const thumbOffset = getOffset(thumbEl, 'margin', axis);
|
|
2437
|
-
const scrollbarOffset = getOffset(scrollbarEl, 'padding', axis);
|
|
2438
|
-
const thumbSizePx = vertical ? thumbEl.offsetHeight : thumbEl.offsetWidth;
|
|
2439
|
-
const trackRect = scrollbarEl.getBoundingClientRect();
|
|
2440
|
-
const clickPosition = vertical ? event.clientY - trackRect.top - thumbSizePx / 2 - scrollbarOffset + thumbOffset / 2 : event.clientX - trackRect.left - thumbSizePx / 2 - scrollbarOffset + thumbOffset / 2;
|
|
2441
|
-
const scrollableSize = vertical ? viewportEl.scrollHeight : viewportEl.scrollWidth;
|
|
2442
|
-
const viewportSize = vertical ? viewportEl.clientHeight : viewportEl.clientWidth;
|
|
2443
|
-
const trackSize = vertical ? scrollbarEl.offsetHeight : scrollbarEl.offsetWidth;
|
|
2444
|
-
const maxThumbOffset = trackSize - thumbSizePx - scrollbarOffset - thumbOffset;
|
|
2445
|
-
// A short or heavily padded track can drive `maxThumbOffset` to zero or
|
|
2446
|
-
// negative once the thumb hits its `MIN_THUMB_SIZE` floor. Dividing by it
|
|
2447
|
-
// would yield a non-finite (`Infinity`/`NaN`) or inverted scroll position.
|
|
2448
|
-
if (maxThumbOffset <= 0) {
|
|
2143
|
+
if (!viewportRef.current) {
|
|
2449
2144
|
return;
|
|
2450
2145
|
}
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2146
|
+
|
|
2147
|
+
// Handle Y-axis (vertical) scroll
|
|
2148
|
+
if (thumbYRef.current && scrollbarYRef.current && orientation === 'vertical') {
|
|
2149
|
+
const thumbYOffset = getOffset(thumbYRef.current, 'margin', 'y');
|
|
2150
|
+
const scrollbarYOffset = getOffset(scrollbarYRef.current, 'padding', 'y');
|
|
2151
|
+
const thumbHeight = thumbYRef.current.offsetHeight;
|
|
2152
|
+
const trackRectY = scrollbarYRef.current.getBoundingClientRect();
|
|
2153
|
+
const clickY = event.clientY - trackRectY.top - thumbHeight / 2 - scrollbarYOffset + thumbYOffset / 2;
|
|
2154
|
+
const scrollableContentHeight = viewportRef.current.scrollHeight;
|
|
2155
|
+
const viewportHeight = viewportRef.current.clientHeight;
|
|
2156
|
+
const maxThumbOffsetY = scrollbarYRef.current.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset;
|
|
2157
|
+
const scrollRatioY = clickY / maxThumbOffsetY;
|
|
2158
|
+
const newScrollTop = scrollRatioY * (scrollableContentHeight - viewportHeight);
|
|
2159
|
+
viewportRef.current.scrollTop = newScrollTop;
|
|
2160
|
+
}
|
|
2161
|
+
if (thumbXRef.current && scrollbarXRef.current && orientation === 'horizontal') {
|
|
2162
|
+
const thumbXOffset = getOffset(thumbXRef.current, 'margin', 'x');
|
|
2163
|
+
const scrollbarXOffset = getOffset(scrollbarXRef.current, 'padding', 'x');
|
|
2164
|
+
const thumbWidth = thumbXRef.current.offsetWidth;
|
|
2165
|
+
const trackRectX = scrollbarXRef.current.getBoundingClientRect();
|
|
2166
|
+
const clickX = event.clientX - trackRectX.left - thumbWidth / 2 - scrollbarXOffset + thumbXOffset / 2;
|
|
2167
|
+
const scrollableContentWidth = viewportRef.current.scrollWidth;
|
|
2168
|
+
const viewportWidth = viewportRef.current.clientWidth;
|
|
2169
|
+
const maxThumbOffsetX = scrollbarXRef.current.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset;
|
|
2170
|
+
const scrollRatioX = clickX / maxThumbOffsetX;
|
|
2171
|
+
let newScrollLeft;
|
|
2172
|
+
if (direction === 'rtl') {
|
|
2173
|
+
// In RTL, invert the scroll direction
|
|
2174
|
+
newScrollLeft = (1 - scrollRatioX) * (scrollableContentWidth - viewportWidth);
|
|
2175
|
+
|
|
2176
|
+
// Adjust for browsers that use negative scrollLeft in RTL
|
|
2177
|
+
if (viewportRef.current.scrollLeft <= 0) {
|
|
2178
|
+
newScrollLeft = -newScrollLeft;
|
|
2179
|
+
}
|
|
2180
|
+
} else {
|
|
2181
|
+
newScrollLeft = scrollRatioX * (scrollableContentWidth - viewportWidth);
|
|
2182
|
+
}
|
|
2183
|
+
viewportRef.current.scrollLeft = newScrollLeft;
|
|
2465
2184
|
}
|
|
2466
2185
|
handleScroll({
|
|
2467
|
-
x:
|
|
2468
|
-
y:
|
|
2186
|
+
x: viewportRef.current.scrollLeft,
|
|
2187
|
+
y: viewportRef.current.scrollTop
|
|
2469
2188
|
});
|
|
2470
2189
|
handlePointerDown(event);
|
|
2471
2190
|
},
|
|
@@ -2479,30 +2198,34 @@ const ScrollAreaScrollbar = /*#__PURE__*/React.forwardRef(function ScrollAreaScr
|
|
|
2479
2198
|
WebkitUserSelect: 'none',
|
|
2480
2199
|
userSelect: 'none',
|
|
2481
2200
|
visibility: hideTrackUntilMeasured ? 'hidden' : undefined,
|
|
2482
|
-
...(vertical
|
|
2201
|
+
...(orientation === 'vertical' && {
|
|
2483
2202
|
top: 0,
|
|
2484
|
-
bottom:
|
|
2203
|
+
bottom: `var(${ScrollAreaRootCssVars.scrollAreaCornerHeight})`,
|
|
2485
2204
|
insetInlineEnd: 0,
|
|
2486
|
-
[
|
|
2487
|
-
}
|
|
2205
|
+
[ScrollAreaScrollbarCssVars.scrollAreaThumbHeight]: `${thumbSize.height}px`
|
|
2206
|
+
}),
|
|
2207
|
+
...(orientation === 'horizontal' && {
|
|
2488
2208
|
insetInlineStart: 0,
|
|
2489
|
-
insetInlineEnd:
|
|
2209
|
+
insetInlineEnd: `var(${ScrollAreaRootCssVars.scrollAreaCornerWidth})`,
|
|
2490
2210
|
bottom: 0,
|
|
2491
|
-
[
|
|
2211
|
+
[ScrollAreaScrollbarCssVars.scrollAreaThumbWidth]: `${thumbSize.width}px`
|
|
2492
2212
|
})
|
|
2493
2213
|
}
|
|
2494
2214
|
};
|
|
2495
2215
|
const element = useRenderElement('div', componentProps, {
|
|
2496
|
-
ref: [forwardedRef, vertical ? scrollbarYRef : scrollbarXRef],
|
|
2216
|
+
ref: [forwardedRef, orientation === 'vertical' ? scrollbarYRef : scrollbarXRef],
|
|
2497
2217
|
state,
|
|
2498
2218
|
props: [props, elementProps],
|
|
2499
2219
|
stateAttributesMapping: scrollAreaStateAttributesMapping
|
|
2500
2220
|
});
|
|
2221
|
+
const contextValue = React.useMemo(() => ({
|
|
2222
|
+
orientation
|
|
2223
|
+
}), [orientation]);
|
|
2501
2224
|
if (!shouldRender) {
|
|
2502
2225
|
return null;
|
|
2503
2226
|
}
|
|
2504
2227
|
return /*#__PURE__*/jsx(ScrollAreaScrollbarContext.Provider, {
|
|
2505
|
-
value:
|
|
2228
|
+
value: contextValue,
|
|
2506
2229
|
children: element
|
|
2507
2230
|
});
|
|
2508
2231
|
});
|
|
@@ -2590,30 +2313,43 @@ const ScrollAreaThumb = /*#__PURE__*/React.forwardRef(function ScrollAreaThumb(c
|
|
|
2590
2313
|
handlePointerDown,
|
|
2591
2314
|
handlePointerMove,
|
|
2592
2315
|
handlePointerUp,
|
|
2316
|
+
setScrollingX,
|
|
2317
|
+
setScrollingY,
|
|
2593
2318
|
scrollingX,
|
|
2594
2319
|
scrollingY,
|
|
2595
2320
|
hasMeasuredScrollbar
|
|
2596
2321
|
} = useScrollAreaRootContext();
|
|
2597
|
-
const
|
|
2598
|
-
|
|
2322
|
+
const {
|
|
2323
|
+
orientation
|
|
2324
|
+
} = useScrollAreaScrollbarContext();
|
|
2599
2325
|
const state = {
|
|
2600
|
-
scrolling:
|
|
2326
|
+
scrolling: orientation === 'horizontal' ? scrollingX : scrollingY,
|
|
2601
2327
|
orientation
|
|
2602
2328
|
};
|
|
2329
|
+
function endDrag(event) {
|
|
2330
|
+
if (orientation === 'vertical') {
|
|
2331
|
+
setScrollingY(false);
|
|
2332
|
+
}
|
|
2333
|
+
if (orientation === 'horizontal') {
|
|
2334
|
+
setScrollingX(false);
|
|
2335
|
+
}
|
|
2336
|
+
handlePointerUp(event);
|
|
2337
|
+
}
|
|
2603
2338
|
const element = useRenderElement('div', componentProps, {
|
|
2604
|
-
ref: [forwardedRef, vertical ? thumbYRef : thumbXRef],
|
|
2339
|
+
ref: [forwardedRef, orientation === 'vertical' ? thumbYRef : thumbXRef],
|
|
2605
2340
|
state,
|
|
2606
2341
|
props: [{
|
|
2607
2342
|
onPointerDown: handlePointerDown,
|
|
2608
2343
|
onPointerMove: handlePointerMove,
|
|
2609
|
-
onPointerUp:
|
|
2610
|
-
onPointerCancel:
|
|
2344
|
+
onPointerUp: endDrag,
|
|
2345
|
+
onPointerCancel: endDrag,
|
|
2611
2346
|
style: {
|
|
2612
2347
|
visibility: hasMeasuredScrollbar ? undefined : 'hidden',
|
|
2613
|
-
...(vertical
|
|
2614
|
-
height:
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2348
|
+
...(orientation === 'vertical' && {
|
|
2349
|
+
height: `var(${ScrollAreaScrollbarCssVars.scrollAreaThumbHeight})`
|
|
2350
|
+
}),
|
|
2351
|
+
...(orientation === 'horizontal' && {
|
|
2352
|
+
width: `var(${ScrollAreaScrollbarCssVars.scrollAreaThumbWidth})`
|
|
2617
2353
|
})
|
|
2618
2354
|
}
|
|
2619
2355
|
}, elementProps]
|
|
@@ -2693,7 +2429,7 @@ var index_parts$2 = /*#__PURE__*/Object.freeze({
|
|
|
2693
2429
|
Positioner: ComboboxPositioner,
|
|
2694
2430
|
Root: ComboboxRoot,
|
|
2695
2431
|
Row: ComboboxRow,
|
|
2696
|
-
Separator:
|
|
2432
|
+
Separator: Separator,
|
|
2697
2433
|
Status: ComboboxStatus,
|
|
2698
2434
|
Trigger: ComboboxTrigger,
|
|
2699
2435
|
Value: ComboboxValue,
|
|
@@ -2740,6 +2476,22 @@ function getViewportRect(teleportDistance, scrubAreaEl) {
|
|
|
2740
2476
|
};
|
|
2741
2477
|
}
|
|
2742
2478
|
|
|
2479
|
+
// This lets us invert the scale of the cursor to match the OS scale, in which the cursor doesn't
|
|
2480
|
+
// scale with the content on pinch-zoom.
|
|
2481
|
+
function subscribeToVisualViewportResize(element, visualScaleRef) {
|
|
2482
|
+
const vV = getWindow(element).visualViewport;
|
|
2483
|
+
if (!vV) {
|
|
2484
|
+
return () => {};
|
|
2485
|
+
}
|
|
2486
|
+
function handleVisualResize() {
|
|
2487
|
+
if (vV) {
|
|
2488
|
+
visualScaleRef.current = vV.scale;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
handleVisualResize();
|
|
2492
|
+
return addEventListener(vV, 'resize', handleVisualResize);
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2743
2495
|
const SCRUB_AREA_STYLE = {
|
|
2744
2496
|
touchAction: 'none',
|
|
2745
2497
|
WebkitUserSelect: 'none',
|
|
@@ -2765,6 +2517,8 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2765
2517
|
const {
|
|
2766
2518
|
state,
|
|
2767
2519
|
setIsScrubbing: setRootScrubbing,
|
|
2520
|
+
disabled,
|
|
2521
|
+
readOnly,
|
|
2768
2522
|
inputRef,
|
|
2769
2523
|
incrementValue,
|
|
2770
2524
|
allowInputSyncRef,
|
|
@@ -2773,10 +2527,6 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2773
2527
|
lastChangedValueRef,
|
|
2774
2528
|
valueRef
|
|
2775
2529
|
} = useNumberFieldRootContext();
|
|
2776
|
-
const {
|
|
2777
|
-
disabled,
|
|
2778
|
-
readOnly
|
|
2779
|
-
} = state;
|
|
2780
2530
|
const scrubAreaRef = React.useRef(null);
|
|
2781
2531
|
const isScrubbingRef = React.useRef(false);
|
|
2782
2532
|
const didMoveRef = React.useRef(false);
|
|
@@ -2786,15 +2536,21 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2786
2536
|
x: 0,
|
|
2787
2537
|
y: 0
|
|
2788
2538
|
});
|
|
2539
|
+
const visualScaleRef = React.useRef(1);
|
|
2789
2540
|
const exitPointerLockTimeout = useTimeout();
|
|
2790
2541
|
const [isTouchInput, setIsTouchInput] = React.useState(false);
|
|
2791
2542
|
const [isPointerLockDenied, setIsPointerLockDenied] = React.useState(false);
|
|
2792
2543
|
const [isScrubbing, setIsScrubbing] = React.useState(false);
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2544
|
+
React.useEffect(() => {
|
|
2545
|
+
if (!isScrubbing || !scrubAreaCursorRef.current) {
|
|
2546
|
+
return undefined;
|
|
2547
|
+
}
|
|
2548
|
+
return subscribeToVisualViewportResize(scrubAreaCursorRef.current, visualScaleRef);
|
|
2549
|
+
}, [isScrubbing]);
|
|
2550
|
+
function updateCursorTransform(x, y) {
|
|
2551
|
+
if (scrubAreaCursorRef.current) {
|
|
2552
|
+
scrubAreaCursorRef.current.style.transform = `translate3d(${x}px,${y}px,0) scale(${1 / visualScaleRef.current})`;
|
|
2553
|
+
}
|
|
2798
2554
|
}
|
|
2799
2555
|
const onScrub = useStableCallback(({
|
|
2800
2556
|
movementX,
|
|
@@ -2807,23 +2563,24 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2807
2563
|
}
|
|
2808
2564
|
const rect = getViewportRect(teleportDistance, scrubAreaEl);
|
|
2809
2565
|
const coords = virtualCursorCoords.current;
|
|
2810
|
-
|
|
2811
|
-
// Wrap the cursor to the opposite edge when its center crosses a viewport bound.
|
|
2812
|
-
const wrap = (coord, halfSize, low, high) => {
|
|
2813
|
-
if (coord + halfSize < low) {
|
|
2814
|
-
return high - halfSize;
|
|
2815
|
-
}
|
|
2816
|
-
if (coord + halfSize > high) {
|
|
2817
|
-
return low - halfSize;
|
|
2818
|
-
}
|
|
2819
|
-
return coord;
|
|
2820
|
-
};
|
|
2821
2566
|
const newCoords = {
|
|
2822
|
-
x:
|
|
2823
|
-
y:
|
|
2567
|
+
x: Math.round(coords.x + movementX),
|
|
2568
|
+
y: Math.round(coords.y + movementY)
|
|
2824
2569
|
};
|
|
2570
|
+
const cursorWidth = virtualCursor.offsetWidth;
|
|
2571
|
+
const cursorHeight = virtualCursor.offsetHeight;
|
|
2572
|
+
if (newCoords.x + cursorWidth / 2 < rect.left) {
|
|
2573
|
+
newCoords.x = rect.right - cursorWidth / 2;
|
|
2574
|
+
} else if (newCoords.x + cursorWidth / 2 > rect.right) {
|
|
2575
|
+
newCoords.x = rect.left - cursorWidth / 2;
|
|
2576
|
+
}
|
|
2577
|
+
if (newCoords.y + cursorHeight / 2 < rect.top) {
|
|
2578
|
+
newCoords.y = rect.bottom - cursorHeight / 2;
|
|
2579
|
+
} else if (newCoords.y + cursorHeight / 2 > rect.bottom) {
|
|
2580
|
+
newCoords.y = rect.top - cursorHeight / 2;
|
|
2581
|
+
}
|
|
2825
2582
|
virtualCursorCoords.current = newCoords;
|
|
2826
|
-
updateCursorTransform(
|
|
2583
|
+
updateCursorTransform(newCoords.x, newCoords.y);
|
|
2827
2584
|
});
|
|
2828
2585
|
const onScrubbingChange = useStableCallback((scrubbingValue, {
|
|
2829
2586
|
clientX,
|
|
@@ -2842,7 +2599,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2842
2599
|
y: clientY - virtualCursor.offsetHeight / 2
|
|
2843
2600
|
};
|
|
2844
2601
|
virtualCursorCoords.current = initialCoords;
|
|
2845
|
-
updateCursorTransform(
|
|
2602
|
+
updateCursorTransform(initialCoords.x, initialCoords.y);
|
|
2846
2603
|
});
|
|
2847
2604
|
React.useEffect(function registerGlobalScrubbingEventListeners() {
|
|
2848
2605
|
// Only listen while actively scrubbing; avoids unrelated pointerup events committing.
|
|
@@ -2884,9 +2641,6 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2884
2641
|
}
|
|
2885
2642
|
}
|
|
2886
2643
|
function handleScrubPointerMove(event) {
|
|
2887
|
-
// The effects below can tear down and re-run without unmounting (`<Activity>`), which
|
|
2888
|
-
// clears the ref while `isScrubbing` stays `true` and re-attaches this listener. The ref
|
|
2889
|
-
// is the source of truth for whether a pointer is actually down.
|
|
2890
2644
|
if (!isScrubbingRef.current) {
|
|
2891
2645
|
return;
|
|
2892
2646
|
}
|
|
@@ -2954,7 +2708,8 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2954
2708
|
role: 'presentation',
|
|
2955
2709
|
style: SCRUB_AREA_STYLE,
|
|
2956
2710
|
async onPointerDown(event) {
|
|
2957
|
-
|
|
2711
|
+
const isMainButton = !event.button || event.button === 0;
|
|
2712
|
+
if (event.defaultPrevented || readOnly || !isMainButton || disabled) {
|
|
2958
2713
|
return;
|
|
2959
2714
|
}
|
|
2960
2715
|
const isTouch = event.pointerType === 'touch';
|
|
@@ -2993,7 +2748,7 @@ const NumberFieldScrubArea = /*#__PURE__*/React.forwardRef(function NumberFieldS
|
|
|
2993
2748
|
ref: [forwardedRef, scrubAreaRef],
|
|
2994
2749
|
state,
|
|
2995
2750
|
props: [defaultProps, elementProps],
|
|
2996
|
-
stateAttributesMapping: stateAttributesMapping$
|
|
2751
|
+
stateAttributesMapping: stateAttributesMapping$6
|
|
2997
2752
|
});
|
|
2998
2753
|
const contextValue = React.useMemo(() => ({
|
|
2999
2754
|
isScrubbing,
|
|
@@ -3050,7 +2805,7 @@ const NumberFieldScrubAreaCursor = /*#__PURE__*/React.forwardRef(function Number
|
|
|
3050
2805
|
role: 'presentation',
|
|
3051
2806
|
style: CURSOR_STYLE
|
|
3052
2807
|
}, elementProps],
|
|
3053
|
-
stateAttributesMapping: stateAttributesMapping$
|
|
2808
|
+
stateAttributesMapping: stateAttributesMapping$6
|
|
3054
2809
|
});
|
|
3055
2810
|
return element && /*#__PURE__*/ReactDOM.createPortal(element, ownerDocument(domElement).body);
|
|
3056
2811
|
});
|
|
@@ -3094,8 +2849,7 @@ const SelectLabel = /*#__PURE__*/React.forwardRef(function SelectLabel(component
|
|
|
3094
2849
|
id: defaultLabelId,
|
|
3095
2850
|
fallbackControlId: triggerElement?.id ?? rootId,
|
|
3096
2851
|
setLabelId(nextLabelId) {
|
|
3097
|
-
|
|
3098
|
-
store.set('labelId', resolvedLabelId);
|
|
2852
|
+
store.set('labelId', nextLabelId);
|
|
3099
2853
|
}
|
|
3100
2854
|
});
|
|
3101
2855
|
return useRenderElement('div', componentProps, {
|
|
@@ -3140,7 +2894,7 @@ const SelectIcon = /*#__PURE__*/React.forwardRef(function SelectIcon(componentPr
|
|
|
3140
2894
|
});
|
|
3141
2895
|
if (process.env.NODE_ENV !== "production") SelectIcon.displayName = "SelectIcon";
|
|
3142
2896
|
|
|
3143
|
-
const stateAttributesMapping = {
|
|
2897
|
+
const stateAttributesMapping$1 = {
|
|
3144
2898
|
...popupStateMapping,
|
|
3145
2899
|
...transitionStatusMapping
|
|
3146
2900
|
};
|
|
@@ -3179,12 +2933,17 @@ const SelectBackdrop = /*#__PURE__*/React.forwardRef(function SelectBackdrop(com
|
|
|
3179
2933
|
WebkitUserSelect: 'none'
|
|
3180
2934
|
}
|
|
3181
2935
|
}, elementProps],
|
|
3182
|
-
stateAttributesMapping
|
|
2936
|
+
stateAttributesMapping: stateAttributesMapping$1
|
|
3183
2937
|
});
|
|
3184
2938
|
return element;
|
|
3185
2939
|
});
|
|
3186
2940
|
if (process.env.NODE_ENV !== "production") SelectBackdrop.displayName = "SelectBackdrop";
|
|
3187
2941
|
|
|
2942
|
+
const stateAttributesMapping = {
|
|
2943
|
+
...popupStateMapping,
|
|
2944
|
+
...transitionStatusMapping
|
|
2945
|
+
};
|
|
2946
|
+
|
|
3188
2947
|
/**
|
|
3189
2948
|
* Displays an element positioned against the select popup anchor.
|
|
3190
2949
|
* Renders a `<div>` element.
|
|
@@ -3223,7 +2982,7 @@ const SelectArrow = /*#__PURE__*/React.forwardRef(function SelectArrow(component
|
|
|
3223
2982
|
style: arrowStyles,
|
|
3224
2983
|
'aria-hidden': true
|
|
3225
2984
|
}, elementProps],
|
|
3226
|
-
stateAttributesMapping
|
|
2985
|
+
stateAttributesMapping
|
|
3227
2986
|
});
|
|
3228
2987
|
if (alignItemWithTriggerActive) {
|
|
3229
2988
|
return null;
|
|
@@ -3241,7 +3000,7 @@ const SelectScrollArrow = /*#__PURE__*/React.forwardRef(function SelectScrollArr
|
|
|
3241
3000
|
className,
|
|
3242
3001
|
style,
|
|
3243
3002
|
direction,
|
|
3244
|
-
keepMounted,
|
|
3003
|
+
keepMounted = false,
|
|
3245
3004
|
...elementProps
|
|
3246
3005
|
} = componentProps;
|
|
3247
3006
|
const isUp = direction === 'up';
|
|
@@ -3272,10 +3031,12 @@ const SelectScrollArrow = /*#__PURE__*/React.forwardRef(function SelectScrollArr
|
|
|
3272
3031
|
} = useTransitionStatus(visible);
|
|
3273
3032
|
useIsoLayoutEffect(() => {
|
|
3274
3033
|
scrollArrowsMountedCountRef.current += 1;
|
|
3275
|
-
store.
|
|
3034
|
+
if (!store.state.hasScrollArrows) {
|
|
3035
|
+
store.set('hasScrollArrows', true);
|
|
3036
|
+
}
|
|
3276
3037
|
return () => {
|
|
3277
3038
|
scrollArrowsMountedCountRef.current = Math.max(0, scrollArrowsMountedCountRef.current - 1);
|
|
3278
|
-
if (scrollArrowsMountedCountRef.current === 0) {
|
|
3039
|
+
if (scrollArrowsMountedCountRef.current === 0 && store.state.hasScrollArrows) {
|
|
3279
3040
|
store.set('hasScrollArrows', false);
|
|
3280
3041
|
}
|
|
3281
3042
|
};
|
|
@@ -3312,7 +3073,7 @@ const SelectScrollArrow = /*#__PURE__*/React.forwardRef(function SelectScrollArr
|
|
|
3312
3073
|
return;
|
|
3313
3074
|
}
|
|
3314
3075
|
store.set('activeIndex', null);
|
|
3315
|
-
handleScrollArrowVisibility(
|
|
3076
|
+
handleScrollArrowVisibility();
|
|
3316
3077
|
const maxScrollTop = getMaxScrollOffset(scroller.scrollHeight, scroller.clientHeight);
|
|
3317
3078
|
const scrollTop = normalizeScrollOffset(scroller.scrollTop, maxScrollTop);
|
|
3318
3079
|
const isScrolledToEdge = scrollTop === (isUp ? 0 : maxScrollTop);
|
|
@@ -3320,6 +3081,11 @@ const SelectScrollArrow = /*#__PURE__*/React.forwardRef(function SelectScrollArr
|
|
|
3320
3081
|
if (scrollTop !== scroller.scrollTop) {
|
|
3321
3082
|
scroller.scrollTop = scrollTop;
|
|
3322
3083
|
}
|
|
3084
|
+
|
|
3085
|
+
// Fallback when there are no items registered yet.
|
|
3086
|
+
if (items.length === 0) {
|
|
3087
|
+
store.set(isUp ? 'scrollUpArrowVisible' : 'scrollDownArrowVisible', !isScrolledToEdge);
|
|
3088
|
+
}
|
|
3323
3089
|
if (isScrolledToEdge) {
|
|
3324
3090
|
timeout.clear();
|
|
3325
3091
|
return;
|
|
@@ -3408,14 +3174,6 @@ const SelectScrollUpArrow = /*#__PURE__*/React.forwardRef(function SelectScrollU
|
|
|
3408
3174
|
});
|
|
3409
3175
|
if (process.env.NODE_ENV !== "production") SelectScrollUpArrow.displayName = "SelectScrollUpArrow";
|
|
3410
3176
|
|
|
3411
|
-
/**
|
|
3412
|
-
* A visual separator between items or groups.
|
|
3413
|
-
* Renders a `<div>` element.
|
|
3414
|
-
*
|
|
3415
|
-
* Documentation: [Base UI Select](https://base-ui.com/react/components/select)
|
|
3416
|
-
*/
|
|
3417
|
-
const SelectSeparator = ListboxSeparator;
|
|
3418
|
-
|
|
3419
3177
|
var index_parts = /*#__PURE__*/Object.freeze({
|
|
3420
3178
|
__proto__: null,
|
|
3421
3179
|
Arrow: SelectArrow,
|
|
@@ -3434,7 +3192,7 @@ var index_parts = /*#__PURE__*/Object.freeze({
|
|
|
3434
3192
|
Root: SelectRoot,
|
|
3435
3193
|
ScrollDownArrow: SelectScrollDownArrow,
|
|
3436
3194
|
ScrollUpArrow: SelectScrollUpArrow,
|
|
3437
|
-
Separator:
|
|
3195
|
+
Separator: Separator,
|
|
3438
3196
|
Trigger: SelectTrigger,
|
|
3439
3197
|
Value: SelectValue
|
|
3440
3198
|
});
|