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