@jobber/components 8.23.0 → 8.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Autocomplete/index.cjs +1 -1
- package/dist/Autocomplete/index.mjs +1 -1
- package/dist/Card/index.cjs +4 -4
- package/dist/Card/index.mjs +4 -4
- package/dist/Chip/index.cjs +1 -1
- package/dist/Chip/index.mjs +1 -1
- package/dist/Chips/InternalChipDismissible/hooks/index.cjs +1 -1
- package/dist/Chips/InternalChipDismissible/hooks/index.mjs +1 -1
- package/dist/Chips/InternalChipDismissible/index.cjs +1 -1
- package/dist/Chips/InternalChipDismissible/index.mjs +1 -1
- package/dist/Chips/index.cjs +1 -1
- package/dist/Chips/index.mjs +1 -1
- package/dist/ComboboxChipRemove-cjs.js +668 -379
- package/dist/ComboboxChipRemove-es.js +508 -219
- package/dist/ConfirmationModal/index.cjs +1 -1
- package/dist/ConfirmationModal/index.mjs +1 -1
- package/dist/DataDump/index.cjs +4 -4
- package/dist/DataDump/index.mjs +4 -4
- package/dist/DataList/components/DataListActions/index.cjs +1 -1
- package/dist/DataList/components/DataListActions/index.mjs +1 -1
- package/dist/DataList/components/DataListBulkActions/index.cjs +1 -1
- package/dist/DataList/components/DataListBulkActions/index.mjs +1 -1
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +1 -1
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +1 -1
- package/dist/DataList/components/DataListFilters/index.cjs +1 -1
- package/dist/DataList/components/DataListFilters/index.mjs +1 -1
- package/dist/DataList/components/DataListHeader/index.cjs +1 -1
- package/dist/DataList/components/DataListHeader/index.mjs +1 -1
- package/dist/DataList/components/DataListItem/index.cjs +1 -1
- package/dist/DataList/components/DataListItem/index.mjs +1 -1
- package/dist/DataList/components/DataListItemActions/index.cjs +1 -1
- package/dist/DataList/components/DataListItemActions/index.mjs +1 -1
- package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +1 -1
- package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +1 -1
- package/dist/DataList/components/DataListItems/index.cjs +1 -1
- package/dist/DataList/components/DataListItems/index.mjs +1 -1
- package/dist/DataList/components/DataListLayout/index.cjs +1 -1
- package/dist/DataList/components/DataListLayout/index.mjs +1 -1
- package/dist/DataList/components/DataListLayoutActions/index.cjs +1 -1
- package/dist/DataList/components/DataListLayoutActions/index.mjs +1 -1
- package/dist/DataList/index.cjs +1 -1
- package/dist/DataList/index.mjs +1 -1
- package/dist/DatePicker/index.cjs +1 -1
- package/dist/DatePicker/index.mjs +1 -1
- package/dist/Dialog/index.cjs +3 -3
- package/dist/Dialog/index.mjs +3 -3
- package/dist/DrawerDescription-cjs.js +32 -33
- package/dist/DrawerDescription-es.js +19 -20
- package/dist/DrawerTitle-cjs.js +7 -5
- package/dist/DrawerTitle-es.js +7 -5
- package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +1 -1
- package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +1 -1
- package/dist/FilterPicker/components/FilterPickerContent/index.cjs +1 -1
- package/dist/FilterPicker/components/FilterPickerContent/index.mjs +1 -1
- package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +1 -1
- package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +1 -1
- package/dist/FilterPicker/index.cjs +1 -1
- package/dist/FilterPicker/index.mjs +1 -1
- package/dist/FormFieldWrapper-cjs.js +2 -2
- package/dist/FormFieldWrapper-es.js +2 -2
- package/dist/FormatFile/index.cjs +1 -1
- package/dist/FormatFile/index.mjs +1 -1
- package/dist/Gallery/index.cjs +1 -1
- package/dist/Gallery/index.mjs +1 -1
- package/dist/InputDate/index.cjs +1 -1
- package/dist/InputDate/index.mjs +1 -1
- package/dist/InputNumberExperimental-cjs.js +319 -102
- package/dist/InputNumberExperimental-es.js +321 -104
- package/dist/LightBox/index.cjs +1 -1
- package/dist/LightBox/index.mjs +1 -1
- package/dist/Menu/index.cjs +4 -4
- package/dist/Menu/index.mjs +4 -4
- package/dist/MenuSubmenuTrigger-cjs.js +545 -498
- package/dist/MenuSubmenuTrigger-es.js +490 -443
- package/dist/Modal/index.cjs +1 -1
- package/dist/Modal/index.mjs +1 -1
- package/dist/NumberFieldInput-cjs.js +609 -496
- package/dist/NumberFieldInput-es.js +612 -498
- package/dist/Page/index.cjs +4 -4
- package/dist/Page/index.mjs +4 -4
- package/dist/Popover/index.cjs +1 -1
- package/dist/Popover/index.mjs +1 -1
- package/dist/ScrollAreaViewport-cjs.js +326 -601
- package/dist/ScrollAreaViewport-es.js +278 -548
- package/dist/SelectGroupLabel-cjs.js +494 -528
- package/dist/SelectGroupLabel-es.js +411 -445
- package/dist/Separator-cjs.js +1 -0
- package/dist/Separator-es.js +1 -0
- package/dist/Tooltip/index.cjs +1 -1
- package/dist/Tooltip/index.mjs +1 -1
- package/dist/docs/InputTime/InputTime.md +8 -0
- package/dist/docs/SelectPrimitive/SelectPrimitive.md +38 -25
- package/dist/floating-ui.react-cjs.js +586 -10
- package/dist/floating-ui.react-dom-cjs.js +92 -92
- package/dist/floating-ui.react-dom-es.js +1 -1
- package/dist/floating-ui.react-es.js +580 -4
- package/dist/floating-ui.utils-cjs.js +159 -0
- package/dist/floating-ui.utils-es.js +136 -0
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{ToolbarRootContext-cjs.js → isElementDisabled-cjs.js} +5 -0
- package/dist/{ToolbarRootContext-es.js → isElementDisabled-es.js} +5 -1
- package/dist/primitives/BottomSheet/index.cjs +3 -3
- package/dist/primitives/BottomSheet/index.mjs +3 -3
- package/dist/primitives/ComboboxPrimitive/index.cjs +2 -2
- package/dist/primitives/ComboboxPrimitive/index.mjs +2 -2
- package/dist/primitives/InputNumberExperimental/index.cjs +1 -1
- package/dist/primitives/InputNumberExperimental/index.mjs +1 -1
- package/dist/primitives/SelectPrimitive/index.cjs +5 -5
- package/dist/primitives/SelectPrimitive/index.mjs +5 -5
- package/dist/primitives/index.cjs +4 -4
- package/dist/primitives/index.mjs +4 -4
- package/dist/resolveAriaLabelledBy-cjs.js +5 -0
- package/dist/resolveAriaLabelledBy-es.js +5 -1
- package/dist/{CSPContext-cjs.js → scrollEdges-cjs.js} +30 -2
- package/dist/{CSPContext-es.js → scrollEdges-es.js} +28 -3
- package/dist/styles.css +9 -3
- package/dist/unstyledPrimitives/index.cjs +1217 -639
- package/dist/unstyledPrimitives/index.mjs +1163 -585
- package/dist/useButton-cjs.js +283 -317
- package/dist/useButton-es.js +277 -298
- package/dist/useCompositeListItem-cjs.js +328 -279
- package/dist/useCompositeListItem-es.js +293 -246
- package/dist/useLabel-cjs.js +11 -1
- package/dist/useLabel-es.js +11 -2
- package/dist/useLabelableId-cjs.js +37 -91
- package/dist/useLabelableId-es.js +37 -90
- package/dist/useRenderElement-cjs.js +81 -26
- package/dist/useRenderElement-es.js +81 -27
- package/dist/{InternalBackdrop-cjs.js → useScrollLock-cjs.js} +1286 -1220
- package/dist/{InternalBackdrop-es.js → useScrollLock-es.js} +1250 -1207
- package/package.json +3 -3
- package/dist/index.esm-cjs.js +0 -738
- package/dist/index.esm-es.js +0 -712
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
|
-
var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
|
|
5
4
|
var useButton = require('./useButton-cjs.js');
|
|
6
|
-
var
|
|
5
|
+
var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
|
|
6
|
+
var useScrollLock = require('./useScrollLock-cjs.js');
|
|
7
7
|
var useRenderElement = require('./useRenderElement-cjs.js');
|
|
8
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
-
var
|
|
9
|
+
var floatingUi_utils = require('./floating-ui.utils-cjs.js');
|
|
10
10
|
var floatingUi_reactDom = require('./floating-ui.react-dom-cjs.js');
|
|
11
11
|
|
|
12
12
|
function _interopNamespaceDefault(e) {
|
|
@@ -35,22 +35,23 @@ function useFloatingRootContext(options) {
|
|
|
35
35
|
elements = {}
|
|
36
36
|
} = options;
|
|
37
37
|
const floatingId = useButton.useId();
|
|
38
|
-
const nested =
|
|
38
|
+
const nested = useScrollLock.useFloatingParentNodeId() != null;
|
|
39
39
|
if (process.env.NODE_ENV !== 'production') {
|
|
40
40
|
const optionDomReference = elements.reference;
|
|
41
41
|
if (optionDomReference && !floatingUi_utils_dom.isElement(optionDomReference)) {
|
|
42
42
|
console.error('Cannot pass a virtual element to the `elements.reference` option,', 'as it must be a real DOM element. Use `context.setPositionReference()`', 'instead.');
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
const store = useRenderElement.useRefWithInit(() => new
|
|
45
|
+
const store = useRenderElement.useRefWithInit(() => new useScrollLock.FloatingRootStore({
|
|
46
46
|
open,
|
|
47
|
+
transitionStatus: undefined,
|
|
47
48
|
onOpenChange,
|
|
48
49
|
referenceElement: elements.reference ?? null,
|
|
49
50
|
floatingElement: elements.floating ?? null,
|
|
50
|
-
triggerElements: new
|
|
51
|
+
triggerElements: new useScrollLock.PopupTriggerMap(),
|
|
51
52
|
floatingId,
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
syncOnly: false,
|
|
54
|
+
nested
|
|
54
55
|
})).current;
|
|
55
56
|
useButton.useIsoLayoutEffect(() => {
|
|
56
57
|
const valuesToSync = {
|
|
@@ -70,7 +71,6 @@ function useFloatingRootContext(options) {
|
|
|
70
71
|
}, [open, floatingId, elements.reference, elements.floating, store]);
|
|
71
72
|
store.context.onOpenChange = onOpenChange;
|
|
72
73
|
store.context.nested = nested;
|
|
73
|
-
store.context.noEmit = false;
|
|
74
74
|
return store;
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -83,30 +83,37 @@ function useFloating(options = {}) {
|
|
|
83
83
|
nodeId,
|
|
84
84
|
externalTree
|
|
85
85
|
} = options;
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
const internalStore = useFloatingRootContext(options);
|
|
87
|
+
const store = options.rootContext || internalStore;
|
|
88
|
+
const referenceElement = store.useState('referenceElement');
|
|
89
|
+
const floatingElement = store.useState('floatingElement');
|
|
90
|
+
const domReferenceElement = store.useState('domReferenceElement');
|
|
91
|
+
const open = store.useState('open');
|
|
92
|
+
const floatingId = store.useState('floatingId');
|
|
93
93
|
const [positionReference, setPositionReferenceRaw] = React__namespace.useState(null);
|
|
94
|
+
const [localDomReference, setLocalDomReference] = React__namespace.useState(undefined);
|
|
95
|
+
const [localFloatingElement, setLocalFloatingElement] = React__namespace.useState(undefined);
|
|
94
96
|
const domReferenceRef = React__namespace.useRef(null);
|
|
95
|
-
const tree =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}, [
|
|
97
|
+
const tree = useScrollLock.useFloatingTree(externalTree);
|
|
98
|
+
const storeElements = React__namespace.useMemo(() => ({
|
|
99
|
+
reference: referenceElement,
|
|
100
|
+
floating: floatingElement,
|
|
101
|
+
domReference: domReferenceElement
|
|
102
|
+
}), [referenceElement, floatingElement, domReferenceElement]);
|
|
101
103
|
const position = floatingUi_reactDom.useFloating({
|
|
102
104
|
...options,
|
|
103
105
|
elements: {
|
|
104
|
-
...
|
|
106
|
+
...storeElements,
|
|
105
107
|
...(positionReference && {
|
|
106
108
|
reference: positionReference
|
|
107
109
|
})
|
|
108
110
|
}
|
|
109
111
|
});
|
|
112
|
+
const localDomReferenceElement = floatingUi_utils_dom.isElement(localDomReference) ? localDomReference : null;
|
|
113
|
+
const syncedFloatingElement = localFloatingElement === undefined ? store.state.floatingElement : localFloatingElement;
|
|
114
|
+
store.useSyncedValue('referenceElement', localDomReference ?? null);
|
|
115
|
+
store.useSyncedValue('domReferenceElement', localDomReference === undefined ? domReferenceElement : localDomReferenceElement);
|
|
116
|
+
store.useSyncedValue('floatingElement', syncedFloatingElement);
|
|
110
117
|
const setPositionReference = React__namespace.useCallback(node => {
|
|
111
118
|
const computedPositionReference = floatingUi_utils_dom.isElement(node) ? {
|
|
112
119
|
getBoundingClientRect: () => node.getBoundingClientRect(),
|
|
@@ -118,11 +125,6 @@ function useFloating(options = {}) {
|
|
|
118
125
|
setPositionReferenceRaw(computedPositionReference);
|
|
119
126
|
position.refs.setReference(computedPositionReference);
|
|
120
127
|
}, [position.refs]);
|
|
121
|
-
const [localDomReference, setLocalDomReference] = React__namespace.useState(null);
|
|
122
|
-
const [localFloatingElement, setLocalFloatingElement] = React__namespace.useState(null);
|
|
123
|
-
rootContext.useSyncedValue('referenceElement', localDomReference);
|
|
124
|
-
rootContext.useSyncedValue('domReferenceElement', floatingUi_utils_dom.isElement(localDomReference) ? localDomReference : null);
|
|
125
|
-
rootContext.useSyncedValue('floatingElement', localFloatingElement);
|
|
126
128
|
const setReference = React__namespace.useCallback(node => {
|
|
127
129
|
if (floatingUi_utils_dom.isElement(node) || node === null) {
|
|
128
130
|
domReferenceRef.current = node;
|
|
@@ -152,24 +154,27 @@ function useFloating(options = {}) {
|
|
|
152
154
|
}), [position.refs, setReference, setFloating, setPositionReference]);
|
|
153
155
|
const elements = React__namespace.useMemo(() => ({
|
|
154
156
|
...position.elements,
|
|
155
|
-
domReference:
|
|
156
|
-
}), [position.elements,
|
|
157
|
-
const open = rootContext.useState('open');
|
|
158
|
-
const floatingId = rootContext.useState('floatingId');
|
|
157
|
+
domReference: domReferenceElement
|
|
158
|
+
}), [position.elements, domReferenceElement]);
|
|
159
159
|
const context = React__namespace.useMemo(() => ({
|
|
160
160
|
...position,
|
|
161
|
-
dataRef:
|
|
161
|
+
dataRef: store.context.dataRef,
|
|
162
162
|
open,
|
|
163
|
-
onOpenChange:
|
|
164
|
-
events:
|
|
163
|
+
onOpenChange: store.setOpen,
|
|
164
|
+
events: store.context.events,
|
|
165
165
|
floatingId,
|
|
166
166
|
refs,
|
|
167
167
|
elements,
|
|
168
168
|
nodeId,
|
|
169
|
-
rootStore:
|
|
170
|
-
}), [position, refs, elements, nodeId,
|
|
169
|
+
rootStore: store
|
|
170
|
+
}), [position, refs, elements, nodeId, store, open, floatingId]);
|
|
171
|
+
useButton.useIsoLayoutEffect(() => {
|
|
172
|
+
if (domReferenceElement) {
|
|
173
|
+
domReferenceRef.current = domReferenceElement;
|
|
174
|
+
}
|
|
175
|
+
}, [domReferenceElement]);
|
|
171
176
|
useButton.useIsoLayoutEffect(() => {
|
|
172
|
-
|
|
177
|
+
store.context.dataRef.current.floatingContext = context;
|
|
173
178
|
const node = tree?.nodesRef.current.find(n => n.id === nodeId);
|
|
174
179
|
if (node) {
|
|
175
180
|
node.context = context;
|
|
@@ -180,8 +185,8 @@ function useFloating(options = {}) {
|
|
|
180
185
|
context,
|
|
181
186
|
refs,
|
|
182
187
|
elements,
|
|
183
|
-
rootStore:
|
|
184
|
-
}), [position, refs, elements, context,
|
|
188
|
+
rootStore: store
|
|
189
|
+
}), [position, refs, elements, context, store]);
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
const ESCAPE = 'Escape';
|
|
@@ -196,24 +201,24 @@ function doSwitch(orientation, vertical, horizontal) {
|
|
|
196
201
|
}
|
|
197
202
|
}
|
|
198
203
|
function isMainOrientationKey(key, orientation) {
|
|
199
|
-
const vertical = key ===
|
|
200
|
-
const horizontal = key ===
|
|
204
|
+
const vertical = key === useScrollLock.ARROW_UP || key === useScrollLock.ARROW_DOWN;
|
|
205
|
+
const horizontal = key === useScrollLock.ARROW_LEFT || key === useScrollLock.ARROW_RIGHT;
|
|
201
206
|
return doSwitch(orientation, vertical, horizontal);
|
|
202
207
|
}
|
|
203
208
|
function isMainOrientationToEndKey(key, orientation, rtl) {
|
|
204
|
-
const vertical = key ===
|
|
205
|
-
const horizontal = rtl ? key ===
|
|
209
|
+
const vertical = key === useScrollLock.ARROW_DOWN;
|
|
210
|
+
const horizontal = rtl ? key === useScrollLock.ARROW_LEFT : key === useScrollLock.ARROW_RIGHT;
|
|
206
211
|
return doSwitch(orientation, vertical, horizontal) || key === 'Enter' || key === ' ' || key === '';
|
|
207
212
|
}
|
|
208
213
|
function isCrossOrientationOpenKey(key, orientation, rtl) {
|
|
209
|
-
const vertical = rtl ? key ===
|
|
210
|
-
const horizontal = key ===
|
|
214
|
+
const vertical = rtl ? key === useScrollLock.ARROW_LEFT : key === useScrollLock.ARROW_RIGHT;
|
|
215
|
+
const horizontal = key === useScrollLock.ARROW_DOWN;
|
|
211
216
|
return doSwitch(orientation, vertical, horizontal);
|
|
212
217
|
}
|
|
213
|
-
function isCrossOrientationCloseKey(key, orientation, rtl,
|
|
214
|
-
const vertical = rtl ? key ===
|
|
215
|
-
const horizontal = key ===
|
|
216
|
-
if (orientation === 'both' || orientation === 'horizontal' &&
|
|
218
|
+
function isCrossOrientationCloseKey(key, orientation, rtl, grid) {
|
|
219
|
+
const vertical = rtl ? key === useScrollLock.ARROW_RIGHT : key === useScrollLock.ARROW_LEFT;
|
|
220
|
+
const horizontal = key === useScrollLock.ARROW_UP;
|
|
221
|
+
if (orientation === 'both' || orientation === 'horizontal' && grid) {
|
|
217
222
|
return key === ESCAPE;
|
|
218
223
|
}
|
|
219
224
|
return doSwitch(orientation, vertical, horizontal);
|
|
@@ -224,11 +229,6 @@ function isCrossOrientationCloseKey(key, orientation, rtl, cols) {
|
|
|
224
229
|
* @see https://floating-ui.com/docs/useListNavigation
|
|
225
230
|
*/
|
|
226
231
|
function useListNavigation(context, props) {
|
|
227
|
-
const store = 'rootStore' in context ? context.rootStore : context;
|
|
228
|
-
const open = store.useState('open');
|
|
229
|
-
const floatingElement = store.useState('floatingElement');
|
|
230
|
-
const domReferenceElement = store.useState('domReferenceElement');
|
|
231
|
-
const dataRef = store.context.dataRef;
|
|
232
232
|
const {
|
|
233
233
|
listRef,
|
|
234
234
|
activeIndex,
|
|
@@ -246,11 +246,12 @@ function useListNavigation(context, props) {
|
|
|
246
246
|
disabledIndices = undefined,
|
|
247
247
|
orientation = 'vertical',
|
|
248
248
|
parentOrientation,
|
|
249
|
-
cols = 1,
|
|
250
249
|
id,
|
|
251
250
|
resetOnPointerLeave = true,
|
|
252
|
-
externalTree
|
|
251
|
+
externalTree,
|
|
252
|
+
grid: navigateGrid
|
|
253
253
|
} = props;
|
|
254
|
+
const isGrid = navigateGrid != null;
|
|
254
255
|
if (process.env.NODE_ENV !== 'production') {
|
|
255
256
|
if (allowEscape) {
|
|
256
257
|
if (!loopFocus) {
|
|
@@ -260,18 +261,20 @@ function useListNavigation(context, props) {
|
|
|
260
261
|
console.warn('`useListNavigation` must be virtual to allow escaping.');
|
|
261
262
|
}
|
|
262
263
|
}
|
|
263
|
-
if (orientation === 'vertical' &&
|
|
264
|
-
console.warn('In grid list navigation mode
|
|
264
|
+
if (orientation === 'vertical' && isGrid) {
|
|
265
|
+
console.warn('In grid list navigation mode, the `orientation` should', 'be either "horizontal" or "both".');
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
|
-
const
|
|
268
|
+
const store = 'rootStore' in context ? context.rootStore : context;
|
|
269
|
+
const open = store.useState('open');
|
|
270
|
+
const floatingElement = store.useState('floatingElement');
|
|
271
|
+
const domReferenceElement = store.useState('domReferenceElement');
|
|
272
|
+
const dataRef = store.context.dataRef;
|
|
273
|
+
const floatingFocusElement = useScrollLock.getFloatingFocusElement(floatingElement);
|
|
274
|
+
const typeableComboboxReference = useScrollLock.isTypeableCombobox(domReferenceElement);
|
|
268
275
|
const floatingFocusElementRef = useButton.useValueAsRef(floatingFocusElement);
|
|
269
|
-
const parentId =
|
|
270
|
-
const tree =
|
|
271
|
-
useButton.useIsoLayoutEffect(() => {
|
|
272
|
-
dataRef.current.orientation = orientation;
|
|
273
|
-
}, [dataRef, orientation]);
|
|
274
|
-
const typeableComboboxReference = useButton.isTypeableCombobox(domReferenceElement);
|
|
276
|
+
const parentId = useScrollLock.useFloatingParentNodeId();
|
|
277
|
+
const tree = useScrollLock.useFloatingTree(externalTree);
|
|
275
278
|
const focusItemOnOpenRef = React__namespace.useRef(focusItemOnOpen);
|
|
276
279
|
const indexRef = React__namespace.useRef(selectedIndex ?? -1);
|
|
277
280
|
const keyRef = React__namespace.useRef(null);
|
|
@@ -279,21 +282,23 @@ function useListNavigation(context, props) {
|
|
|
279
282
|
const onNavigate = useButton.useStableCallback(event => {
|
|
280
283
|
onNavigateProp(indexRef.current === -1 ? null : indexRef.current, event);
|
|
281
284
|
});
|
|
282
|
-
const previousOnNavigateRef = React__namespace.useRef(onNavigate);
|
|
283
285
|
const previousMountedRef = React__namespace.useRef(!!floatingElement);
|
|
284
286
|
const previousOpenRef = React__namespace.useRef(open);
|
|
285
287
|
const forceSyncFocusRef = React__namespace.useRef(false);
|
|
286
288
|
const forceScrollIntoViewRef = React__namespace.useRef(false);
|
|
289
|
+
const cancelQueuedFocusRef = React__namespace.useRef(null);
|
|
287
290
|
const disabledIndicesRef = useButton.useValueAsRef(disabledIndices);
|
|
288
291
|
const latestOpenRef = useButton.useValueAsRef(open);
|
|
289
292
|
const selectedIndexRef = useButton.useValueAsRef(selectedIndex);
|
|
290
293
|
const resetOnPointerLeaveRef = useButton.useValueAsRef(resetOnPointerLeave);
|
|
294
|
+
const focusFrame = useButton.useAnimationFrame();
|
|
295
|
+
const waitForListPopulatedFrame = useButton.useAnimationFrame();
|
|
291
296
|
const focusItem = useButton.useStableCallback(() => {
|
|
292
297
|
function runFocus(item) {
|
|
293
298
|
if (virtual) {
|
|
294
299
|
tree?.events.emit('virtualfocus', item);
|
|
295
300
|
} else {
|
|
296
|
-
|
|
301
|
+
cancelQueuedFocusRef.current = useScrollLock.enqueueFocus(item, {
|
|
297
302
|
sync: forceSyncFocusRef.current,
|
|
298
303
|
preventScroll: true
|
|
299
304
|
});
|
|
@@ -304,7 +309,7 @@ function useListNavigation(context, props) {
|
|
|
304
309
|
if (initialItem) {
|
|
305
310
|
runFocus(initialItem);
|
|
306
311
|
}
|
|
307
|
-
const scheduler = forceSyncFocusRef.current ?
|
|
312
|
+
const scheduler = forceSyncFocusRef.current ? callback => callback() : callback => focusFrame.request(callback);
|
|
308
313
|
scheduler(() => {
|
|
309
314
|
const waitedItem = listRef.current[indexRef.current] || initialItem;
|
|
310
315
|
if (!waitedItem) {
|
|
@@ -326,6 +331,9 @@ function useListNavigation(context, props) {
|
|
|
326
331
|
}
|
|
327
332
|
});
|
|
328
333
|
});
|
|
334
|
+
useButton.useIsoLayoutEffect(() => {
|
|
335
|
+
dataRef.current.orientation = orientation;
|
|
336
|
+
}, [dataRef, orientation]);
|
|
329
337
|
|
|
330
338
|
// Sync `selectedIndex` to be the `activeIndex` upon opening the floating
|
|
331
339
|
// element. Also, reset `activeIndex` upon closing the floating element.
|
|
@@ -342,11 +350,11 @@ function useListNavigation(context, props) {
|
|
|
342
350
|
onNavigate();
|
|
343
351
|
}
|
|
344
352
|
} else if (previousMountedRef.current) {
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
353
|
+
// Reset the active index when the list is no longer open and mounted (closing or
|
|
354
|
+
// unmounting). `onNavigate` is a stable callback that always forwards to the latest
|
|
355
|
+
// `onNavigate` prop.
|
|
348
356
|
indexRef.current = -1;
|
|
349
|
-
|
|
357
|
+
onNavigate();
|
|
350
358
|
}
|
|
351
359
|
}, [enabled, open, floatingElement, selectedIndex, onNavigate]);
|
|
352
360
|
|
|
@@ -384,25 +392,28 @@ function useListNavigation(context, props) {
|
|
|
384
392
|
// otherwise use rAF. Don't try more than twice, since something
|
|
385
393
|
// is wrong otherwise.
|
|
386
394
|
if (runs < 2) {
|
|
387
|
-
const scheduler = runs ?
|
|
395
|
+
const scheduler = runs ? callback => waitForListPopulatedFrame.request(callback) : queueMicrotask;
|
|
388
396
|
scheduler(waitForListPopulated);
|
|
389
397
|
}
|
|
390
398
|
runs += 1;
|
|
391
399
|
} else {
|
|
392
|
-
//
|
|
393
|
-
|
|
400
|
+
// Initially focus the first non-disabled item. `disabledIndices` is deliberately
|
|
401
|
+
// omitted here so attribute-disabled items (`disabled`/`aria-disabled`) are skipped
|
|
402
|
+
// on open even when the consumer passes an empty `disabledIndices` array. Passing it
|
|
403
|
+
// would regress that behavior (see mui/base-ui#2604).
|
|
404
|
+
indexRef.current = keyRef.current == null || isMainOrientationToEndKey(keyRef.current, orientation, rtl) || nested ? useScrollLock.getMinListIndex(listRef) : useScrollLock.getMaxListIndex(listRef);
|
|
394
405
|
keyRef.current = null;
|
|
395
406
|
onNavigate();
|
|
396
407
|
}
|
|
397
408
|
};
|
|
398
409
|
waitForListPopulated();
|
|
399
410
|
}
|
|
400
|
-
} else if (!
|
|
411
|
+
} else if (!useScrollLock.isIndexOutOfListBounds(listRef.current, activeIndex)) {
|
|
401
412
|
indexRef.current = activeIndex;
|
|
402
413
|
focusItem();
|
|
403
414
|
forceScrollIntoViewRef.current = false;
|
|
404
415
|
}
|
|
405
|
-
}, [enabled, open, floatingElement, activeIndex, selectedIndexRef, nested, listRef, orientation, rtl, onNavigate, focusItem,
|
|
416
|
+
}, [enabled, open, floatingElement, activeIndex, selectedIndexRef, nested, listRef, orientation, rtl, onNavigate, focusItem, waitForListPopulatedFrame]);
|
|
406
417
|
|
|
407
418
|
// Ensure the parent floating element has focus when a nested child closes
|
|
408
419
|
// to allow arrow key navigation to work after the pointer leaves the child.
|
|
@@ -412,16 +423,18 @@ function useListNavigation(context, props) {
|
|
|
412
423
|
}
|
|
413
424
|
const nodes = tree.nodesRef.current;
|
|
414
425
|
const parent = nodes.find(node => node.id === parentId)?.context?.elements.floating;
|
|
415
|
-
|
|
426
|
+
// `floatingElement` is null here (see the guard above), so resolve the owner document from an
|
|
427
|
+
// in-DOM element for realm-safety (shadow DOM/iframes): the reference element, falling back to
|
|
428
|
+
// the parent floating element when the reference is virtual (`domReferenceElement` is null).
|
|
429
|
+
const activeEl = useButton.activeElement(useButton.ownerDocument(domReferenceElement ?? parent ?? null));
|
|
416
430
|
const treeContainsActiveEl = nodes.some(node => node.context && useButton.contains(node.context.elements.floating, activeEl));
|
|
417
431
|
if (parent && !treeContainsActiveEl && isPointerModalityRef.current) {
|
|
418
432
|
parent.focus({
|
|
419
433
|
preventScroll: true
|
|
420
434
|
});
|
|
421
435
|
}
|
|
422
|
-
}, [enabled, floatingElement, tree, parentId, virtual]);
|
|
436
|
+
}, [enabled, floatingElement, domReferenceElement, tree, parentId, virtual]);
|
|
423
437
|
useButton.useIsoLayoutEffect(() => {
|
|
424
|
-
previousOnNavigateRef.current = onNavigate;
|
|
425
438
|
previousOpenRef.current = open;
|
|
426
439
|
previousMountedRef.current = !!floatingElement;
|
|
427
440
|
});
|
|
@@ -432,68 +445,22 @@ function useListNavigation(context, props) {
|
|
|
432
445
|
}
|
|
433
446
|
}, [open, focusItemOnOpen]);
|
|
434
447
|
const hasActiveIndex = activeIndex != null;
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
const index = listRef.current.indexOf(event.currentTarget);
|
|
441
|
-
if (index !== -1 && indexRef.current !== index) {
|
|
442
|
-
indexRef.current = index;
|
|
443
|
-
onNavigate(event);
|
|
444
|
-
}
|
|
448
|
+
const syncCurrentTarget = useButton.useStableCallback(event => {
|
|
449
|
+
if (!latestOpenRef.current) {
|
|
450
|
+
return;
|
|
445
451
|
}
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}) => currentTarget.focus({
|
|
454
|
-
preventScroll: true
|
|
455
|
-
}),
|
|
456
|
-
// Safari
|
|
457
|
-
onMouseMove(event) {
|
|
458
|
-
forceSyncFocusRef.current = true;
|
|
459
|
-
forceScrollIntoViewRef.current = false;
|
|
460
|
-
if (focusItemOnHover) {
|
|
461
|
-
syncCurrentTarget(event);
|
|
462
|
-
}
|
|
463
|
-
},
|
|
464
|
-
onPointerLeave(event) {
|
|
465
|
-
if (!latestOpenRef.current || !isPointerModalityRef.current || event.pointerType === 'touch') {
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
forceSyncFocusRef.current = true;
|
|
469
|
-
const relatedTarget = event.relatedTarget;
|
|
470
|
-
if (!focusItemOnHover || listRef.current.includes(relatedTarget)) {
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
if (!resetOnPointerLeaveRef.current) {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
InternalBackdrop.enqueueFocus(null, {
|
|
477
|
-
sync: true
|
|
478
|
-
});
|
|
479
|
-
indexRef.current = -1;
|
|
480
|
-
onNavigate(event);
|
|
481
|
-
if (!virtual) {
|
|
482
|
-
const floatingFocusEl = floatingFocusElementRef.current;
|
|
483
|
-
const activeEl = useButton.activeElement(useButton.ownerDocument(floatingFocusEl));
|
|
484
|
-
if (floatingFocusEl && useButton.contains(floatingFocusEl, activeEl)) {
|
|
485
|
-
floatingFocusEl.focus({
|
|
486
|
-
preventScroll: true
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
return itemProps;
|
|
493
|
-
}, [latestOpenRef, floatingFocusElementRef, focusItemOnHover, listRef, onNavigate, resetOnPointerLeaveRef, virtual]);
|
|
494
|
-
const getParentOrientation = React__namespace.useCallback(() => {
|
|
452
|
+
const index = listRef.current.indexOf(event.currentTarget);
|
|
453
|
+
if (index !== -1 && (indexRef.current !== index || activeIndex !== index)) {
|
|
454
|
+
indexRef.current = index;
|
|
455
|
+
onNavigate(event);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
const getParentOrientation = useButton.useStableCallback(() => {
|
|
495
459
|
return parentOrientation ?? tree?.nodesRef.current.find(node => node.id === parentId)?.context?.dataRef?.current.orientation;
|
|
496
|
-
}
|
|
460
|
+
});
|
|
461
|
+
const getMinEnabledIndex = useButton.useStableCallback(() => {
|
|
462
|
+
return useScrollLock.getMinListIndex(listRef, disabledIndicesRef.current);
|
|
463
|
+
});
|
|
497
464
|
const commonOnKeyDown = useButton.useStableCallback(event => {
|
|
498
465
|
isPointerModalityRef.current = false;
|
|
499
466
|
forceSyncFocusRef.current = true;
|
|
@@ -512,7 +479,7 @@ function useListNavigation(context, props) {
|
|
|
512
479
|
if (!latestOpenRef.current && event.currentTarget === floatingFocusElementRef.current) {
|
|
513
480
|
return;
|
|
514
481
|
}
|
|
515
|
-
if (nested && isCrossOrientationCloseKey(event.key, orientation, rtl,
|
|
482
|
+
if (nested && isCrossOrientationCloseKey(event.key, orientation, rtl, isGrid)) {
|
|
516
483
|
// If the nested list's close key is also the parent navigation key,
|
|
517
484
|
// let the parent navigate. Otherwise, stop propagating the event.
|
|
518
485
|
if (!isMainOrientationKey(event.key, getParentOrientation())) {
|
|
@@ -529,8 +496,8 @@ function useListNavigation(context, props) {
|
|
|
529
496
|
return;
|
|
530
497
|
}
|
|
531
498
|
const currentIndex = indexRef.current;
|
|
532
|
-
const minIndex =
|
|
533
|
-
const maxIndex =
|
|
499
|
+
const minIndex = useScrollLock.getMinListIndex(listRef, disabledIndices);
|
|
500
|
+
const maxIndex = useScrollLock.getMaxListIndex(listRef, disabledIndices);
|
|
534
501
|
if (!typeableComboboxReference) {
|
|
535
502
|
if (event.key === 'Home') {
|
|
536
503
|
useButton.stopEvent(event);
|
|
@@ -544,41 +511,10 @@ function useListNavigation(context, props) {
|
|
|
544
511
|
}
|
|
545
512
|
}
|
|
546
513
|
|
|
547
|
-
// Grid navigation
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}, () => ({
|
|
552
|
-
width: 1,
|
|
553
|
-
height: 1
|
|
554
|
-
}));
|
|
555
|
-
// To calculate movements on the grid, we use hypothetical cell indices
|
|
556
|
-
// as if every item was 1x1, then convert back to real indices.
|
|
557
|
-
const cellMap = InternalBackdrop.createGridCellMap(sizes, cols);
|
|
558
|
-
const minGridIndex = cellMap.findIndex(index => index != null && !InternalBackdrop.isListIndexDisabled(listRef, index, disabledIndices));
|
|
559
|
-
// last enabled index
|
|
560
|
-
const maxGridIndex = cellMap.reduce((foundIndex, index, cellIndex) => index != null && !InternalBackdrop.isListIndexDisabled(listRef, index, disabledIndices) ? cellIndex : foundIndex, -1);
|
|
561
|
-
const index = cellMap[InternalBackdrop.getGridNavigatedIndex({
|
|
562
|
-
current: cellMap.map(itemIndex => itemIndex != null ? listRef.current[itemIndex] : null)
|
|
563
|
-
}, {
|
|
564
|
-
event,
|
|
565
|
-
orientation,
|
|
566
|
-
loopFocus,
|
|
567
|
-
rtl,
|
|
568
|
-
cols,
|
|
569
|
-
// treat undefined (empty grid spaces) as disabled indices so we
|
|
570
|
-
// don't end up in them
|
|
571
|
-
disabledIndices: InternalBackdrop.getGridCellIndices([...((typeof disabledIndices !== 'function' ? disabledIndices : null) || listRef.current.map((_, listIndex) => InternalBackdrop.isListIndexDisabled(listRef, listIndex, disabledIndices) ? listIndex : undefined)), undefined], cellMap),
|
|
572
|
-
minIndex: minGridIndex,
|
|
573
|
-
maxIndex: maxGridIndex,
|
|
574
|
-
prevIndex: InternalBackdrop.getGridCellIndexOfCorner(indexRef.current > maxIndex ? minIndex : indexRef.current, sizes, cellMap, cols,
|
|
575
|
-
// use a corner matching the edge closest to the direction
|
|
576
|
-
// we're moving in so we don't end up in the same item. Prefer
|
|
577
|
-
// top/left over bottom/right.
|
|
578
|
-
// eslint-disable-next-line no-nested-ternary
|
|
579
|
-
event.key === useButton.ARROW_DOWN ? 'bl' : event.key === (rtl ? useButton.ARROW_LEFT : useButton.ARROW_RIGHT) ? 'tr' : 'tl'),
|
|
580
|
-
stopEvent: true
|
|
581
|
-
})];
|
|
514
|
+
// Grid navigation is injected by grid-capable consumers so non-grid
|
|
515
|
+
// consumers (menu, select) tree-shake the grid helpers out.
|
|
516
|
+
if (navigateGrid != null) {
|
|
517
|
+
const index = navigateGrid(event, indexRef.current, listRef, orientation, loopFocus, rtl, disabledIndices, minIndex, maxIndex);
|
|
582
518
|
if (index != null) {
|
|
583
519
|
indexRef.current = index;
|
|
584
520
|
onNavigate(event);
|
|
@@ -607,13 +543,13 @@ function useListNavigation(context, props) {
|
|
|
607
543
|
indexRef.current = minIndex;
|
|
608
544
|
}
|
|
609
545
|
} else {
|
|
610
|
-
indexRef.current =
|
|
546
|
+
indexRef.current = useScrollLock.findNonDisabledListIndex(listRef.current, {
|
|
611
547
|
startingIndex: currentIndex,
|
|
612
548
|
disabledIndices
|
|
613
549
|
});
|
|
614
550
|
}
|
|
615
551
|
} else {
|
|
616
|
-
indexRef.current = Math.min(maxIndex,
|
|
552
|
+
indexRef.current = Math.min(maxIndex, useScrollLock.findNonDisabledListIndex(listRef.current, {
|
|
617
553
|
startingIndex: currentIndex,
|
|
618
554
|
disabledIndices
|
|
619
555
|
}));
|
|
@@ -628,25 +564,73 @@ function useListNavigation(context, props) {
|
|
|
628
564
|
indexRef.current = maxIndex;
|
|
629
565
|
}
|
|
630
566
|
} else {
|
|
631
|
-
indexRef.current =
|
|
567
|
+
indexRef.current = useScrollLock.findNonDisabledListIndex(listRef.current, {
|
|
632
568
|
startingIndex: currentIndex,
|
|
633
569
|
decrement: true,
|
|
634
570
|
disabledIndices
|
|
635
571
|
});
|
|
636
572
|
}
|
|
637
573
|
} else {
|
|
638
|
-
indexRef.current = Math.max(minIndex,
|
|
574
|
+
indexRef.current = Math.max(minIndex, useScrollLock.findNonDisabledListIndex(listRef.current, {
|
|
639
575
|
startingIndex: currentIndex,
|
|
640
576
|
decrement: true,
|
|
641
577
|
disabledIndices
|
|
642
578
|
}));
|
|
643
579
|
}
|
|
644
|
-
if (
|
|
580
|
+
if (useScrollLock.isIndexOutOfListBounds(listRef.current, indexRef.current)) {
|
|
645
581
|
indexRef.current = -1;
|
|
646
582
|
}
|
|
647
583
|
onNavigate(event);
|
|
648
584
|
}
|
|
649
585
|
});
|
|
586
|
+
const item = React__namespace.useMemo(() => {
|
|
587
|
+
const itemProps = {
|
|
588
|
+
onFocus(event) {
|
|
589
|
+
forceSyncFocusRef.current = true;
|
|
590
|
+
syncCurrentTarget(event);
|
|
591
|
+
},
|
|
592
|
+
onClick: ({
|
|
593
|
+
currentTarget
|
|
594
|
+
}) => currentTarget.focus({
|
|
595
|
+
preventScroll: true
|
|
596
|
+
}),
|
|
597
|
+
// Safari
|
|
598
|
+
onMouseMove(event) {
|
|
599
|
+
forceSyncFocusRef.current = true;
|
|
600
|
+
forceScrollIntoViewRef.current = false;
|
|
601
|
+
if (focusItemOnHover) {
|
|
602
|
+
syncCurrentTarget(event);
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
onPointerLeave(event) {
|
|
606
|
+
if (!latestOpenRef.current || !isPointerModalityRef.current || event.pointerType === 'touch') {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
forceSyncFocusRef.current = true;
|
|
610
|
+
const relatedTarget = event.relatedTarget;
|
|
611
|
+
if (!focusItemOnHover || listRef.current.includes(relatedTarget)) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (!resetOnPointerLeaveRef.current) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
cancelQueuedFocusRef.current?.();
|
|
618
|
+
cancelQueuedFocusRef.current = null;
|
|
619
|
+
indexRef.current = -1;
|
|
620
|
+
onNavigate(event);
|
|
621
|
+
if (!virtual) {
|
|
622
|
+
const floatingFocusEl = floatingFocusElementRef.current;
|
|
623
|
+
const activeEl = useButton.activeElement(useButton.ownerDocument(floatingFocusEl));
|
|
624
|
+
if (floatingFocusEl && useButton.contains(floatingFocusEl, activeEl)) {
|
|
625
|
+
floatingFocusEl.focus({
|
|
626
|
+
preventScroll: true
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
return itemProps;
|
|
633
|
+
}, [syncCurrentTarget, latestOpenRef, floatingFocusElementRef, focusItemOnHover, listRef, onNavigate, resetOnPointerLeaveRef, virtual]);
|
|
650
634
|
const ariaActiveDescendantProp = React__namespace.useMemo(() => {
|
|
651
635
|
return virtual && open && hasActiveIndex && {
|
|
652
636
|
'aria-activedescendant': `${id}-${activeIndex}`
|
|
@@ -681,6 +665,9 @@ function useListNavigation(context, props) {
|
|
|
681
665
|
};
|
|
682
666
|
}, [ariaActiveDescendantProp, commonOnKeyDown, floatingFocusElementRef, orientation, typeableComboboxReference, store, open, virtual, domReferenceElement]);
|
|
683
667
|
const trigger = React__namespace.useMemo(() => {
|
|
668
|
+
function openOnNavigationKeyDown(event) {
|
|
669
|
+
store.setOpen(true, useButton.createChangeEventDetails(useButton.listNavigation, event.nativeEvent, event.currentTarget));
|
|
670
|
+
}
|
|
684
671
|
function checkVirtualMouse(event) {
|
|
685
672
|
if (focusItemOnOpen === 'auto' && useButton.isVirtualClick(event.nativeEvent)) {
|
|
686
673
|
focusItemOnOpenRef.current = !virtual;
|
|
@@ -719,10 +706,10 @@ function useListNavigation(context, props) {
|
|
|
719
706
|
if (isParentCrossOpenKey) {
|
|
720
707
|
useButton.stopEvent(event);
|
|
721
708
|
if (currentOpen) {
|
|
722
|
-
indexRef.current =
|
|
709
|
+
indexRef.current = getMinEnabledIndex();
|
|
723
710
|
onNavigate(event);
|
|
724
711
|
} else {
|
|
725
|
-
|
|
712
|
+
openOnNavigationKeyDown(event);
|
|
726
713
|
}
|
|
727
714
|
}
|
|
728
715
|
return undefined;
|
|
@@ -733,7 +720,7 @@ function useListNavigation(context, props) {
|
|
|
733
720
|
}
|
|
734
721
|
useButton.stopEvent(event);
|
|
735
722
|
if (!currentOpen && openOnArrowKeyDown) {
|
|
736
|
-
|
|
723
|
+
openOnNavigationKeyDown(event);
|
|
737
724
|
} else {
|
|
738
725
|
commonOnKeyDown(event);
|
|
739
726
|
}
|
|
@@ -754,7 +741,7 @@ function useListNavigation(context, props) {
|
|
|
754
741
|
onMouseDown: checkVirtualMouse,
|
|
755
742
|
onClick: checkVirtualMouse
|
|
756
743
|
};
|
|
757
|
-
}, [commonOnKeyDown,
|
|
744
|
+
}, [commonOnKeyDown, focusItemOnOpen, getMinEnabledIndex, nested, onNavigate, store, openOnArrowKeyDown, orientation, getParentOrientation, rtl, selectedIndexRef, virtual]);
|
|
758
745
|
const reference = React__namespace.useMemo(() => {
|
|
759
746
|
return {
|
|
760
747
|
...ariaActiveDescendantProp,
|
|
@@ -775,65 +762,48 @@ function useListNavigation(context, props) {
|
|
|
775
762
|
* @see https://floating-ui.com/docs/useTypeahead
|
|
776
763
|
*/
|
|
777
764
|
function useTypeahead(context, props) {
|
|
778
|
-
const store = 'rootStore' in context ? context.rootStore : context;
|
|
779
|
-
const dataRef = store.context.dataRef;
|
|
780
|
-
const open = store.useState('open');
|
|
781
765
|
const {
|
|
782
766
|
listRef,
|
|
783
767
|
elementsRef,
|
|
784
768
|
activeIndex,
|
|
785
769
|
onMatch: onMatchProp,
|
|
786
|
-
|
|
770
|
+
disabledIndices,
|
|
771
|
+
onTyping,
|
|
787
772
|
enabled = true,
|
|
788
773
|
resetMs = 750,
|
|
789
774
|
selectedIndex = null
|
|
790
775
|
} = props;
|
|
776
|
+
const store = 'rootStore' in context ? context.rootStore : context;
|
|
777
|
+
const open = store.useState('open');
|
|
791
778
|
const timeout = useButton.useTimeout();
|
|
792
779
|
const stringRef = React__namespace.useRef('');
|
|
793
780
|
const prevIndexRef = React__namespace.useRef(selectedIndex ?? activeIndex ?? -1);
|
|
794
781
|
const matchIndexRef = React__namespace.useRef(null);
|
|
795
|
-
useButton.useIsoLayoutEffect(() => {
|
|
796
|
-
if (!open && selectedIndex !== null) {
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
timeout.clear();
|
|
800
|
-
matchIndexRef.current = null;
|
|
801
|
-
if (stringRef.current !== '') {
|
|
802
|
-
stringRef.current = '';
|
|
803
|
-
}
|
|
804
|
-
}, [open, selectedIndex, timeout]);
|
|
805
|
-
useButton.useIsoLayoutEffect(() => {
|
|
806
|
-
// Sync arrow key navigation but not typeahead navigation.
|
|
807
|
-
if (open && stringRef.current === '') {
|
|
808
|
-
prevIndexRef.current = selectedIndex ?? activeIndex ?? -1;
|
|
809
|
-
}
|
|
810
|
-
}, [open, selectedIndex, activeIndex]);
|
|
811
|
-
const setTypingChange = useButton.useStableCallback(value => {
|
|
812
|
-
if (value) {
|
|
813
|
-
if (!dataRef.current.typing) {
|
|
814
|
-
dataRef.current.typing = value;
|
|
815
|
-
onTypingChange?.(value);
|
|
816
|
-
}
|
|
817
|
-
} else if (dataRef.current.typing) {
|
|
818
|
-
dataRef.current.typing = value;
|
|
819
|
-
onTypingChange?.(value);
|
|
820
|
-
}
|
|
821
|
-
});
|
|
822
782
|
const onKeyDown = useButton.useStableCallback(event => {
|
|
823
783
|
function isVisible(index) {
|
|
824
784
|
const element = elementsRef?.current[index];
|
|
825
|
-
return !element ||
|
|
785
|
+
return !element || useScrollLock.isElementVisible(element);
|
|
786
|
+
}
|
|
787
|
+
function isItemAvailable(index) {
|
|
788
|
+
if (!isVisible(index)) {
|
|
789
|
+
return false;
|
|
790
|
+
}
|
|
791
|
+
// Visibility is handled above; pass an empty element list so `isListIndexDisabled`
|
|
792
|
+
// resolves only the explicit `disabledIndices` (array/predicate) and skips its own
|
|
793
|
+
// visibility/attribute fallbacks. Consumers that don't opt in keep matching every
|
|
794
|
+
// visible item.
|
|
795
|
+
return disabledIndices == null || !useScrollLock.isListIndexDisabled(useRenderElement.EMPTY_ARRAY, index, disabledIndices);
|
|
826
796
|
}
|
|
827
797
|
function getMatchingIndex(list, string, startIndex = 0) {
|
|
828
798
|
if (list.length === 0) {
|
|
829
799
|
return -1;
|
|
830
800
|
}
|
|
831
801
|
const normalizedStartIndex = (startIndex % list.length + list.length) % list.length;
|
|
832
|
-
const lowerString = string.
|
|
802
|
+
const lowerString = string.toLowerCase();
|
|
833
803
|
for (let offset = 0; offset < list.length; offset += 1) {
|
|
834
804
|
const index = (normalizedStartIndex + offset) % list.length;
|
|
835
805
|
const text = list[index];
|
|
836
|
-
if (!text?.
|
|
806
|
+
if (!text?.toLowerCase().startsWith(lowerString) || !isItemAvailable(index)) {
|
|
837
807
|
continue;
|
|
838
808
|
}
|
|
839
809
|
return index;
|
|
@@ -844,11 +814,11 @@ function useTypeahead(context, props) {
|
|
|
844
814
|
if (stringRef.current.length > 0 && event.key === ' ') {
|
|
845
815
|
// Space should continue the in-progress typeahead session.
|
|
846
816
|
useButton.stopEvent(event);
|
|
847
|
-
|
|
817
|
+
onTyping?.(true);
|
|
848
818
|
}
|
|
849
819
|
if (stringRef.current.length > 0 && stringRef.current[0] !== ' ') {
|
|
850
820
|
if (getMatchingIndex(listContent, stringRef.current) === -1 && event.key !== ' ') {
|
|
851
|
-
|
|
821
|
+
onTyping?.(false);
|
|
852
822
|
}
|
|
853
823
|
}
|
|
854
824
|
if (listContent == null ||
|
|
@@ -860,7 +830,7 @@ function useTypeahead(context, props) {
|
|
|
860
830
|
}
|
|
861
831
|
if (open && event.key !== ' ') {
|
|
862
832
|
useButton.stopEvent(event);
|
|
863
|
-
|
|
833
|
+
onTyping?.(true);
|
|
864
834
|
}
|
|
865
835
|
|
|
866
836
|
// Capture whether this is a new typing session before mutating the string.
|
|
@@ -870,8 +840,10 @@ function useTypeahead(context, props) {
|
|
|
870
840
|
}
|
|
871
841
|
|
|
872
842
|
// Bail out if the list contains a word like "llama" or "aaron". TODO:
|
|
873
|
-
// allow it in this case, too.
|
|
874
|
-
|
|
843
|
+
// allow it in this case, too. Unavailable items are skipped while matching, so
|
|
844
|
+
// they must be ignored here as well — otherwise a hidden or disabled double-letter
|
|
845
|
+
// label would block rapid cycling through the available items.
|
|
846
|
+
const allowRapidSuccessionOfFirstLetter = listContent.every((text, index) => text && isItemAvailable(index) ? text[0]?.toLowerCase() !== text[1]?.toLowerCase() : true);
|
|
875
847
|
|
|
876
848
|
// Allows the user to cycle through items that start with the same letter
|
|
877
849
|
// in rapid succession.
|
|
@@ -883,7 +855,7 @@ function useTypeahead(context, props) {
|
|
|
883
855
|
timeout.start(resetMs, () => {
|
|
884
856
|
stringRef.current = '';
|
|
885
857
|
prevIndexRef.current = matchIndexRef.current;
|
|
886
|
-
|
|
858
|
+
onTyping?.(false);
|
|
887
859
|
});
|
|
888
860
|
|
|
889
861
|
// Compute the starting index for this search.
|
|
@@ -897,18 +869,17 @@ function useTypeahead(context, props) {
|
|
|
897
869
|
matchIndexRef.current = index;
|
|
898
870
|
} else if (event.key !== ' ') {
|
|
899
871
|
stringRef.current = '';
|
|
900
|
-
|
|
872
|
+
onTyping?.(false);
|
|
901
873
|
}
|
|
902
874
|
});
|
|
903
875
|
const onBlur = useButton.useStableCallback(event => {
|
|
904
876
|
const next = event.relatedTarget;
|
|
905
877
|
const currentDomReferenceElement = store.select('domReferenceElement');
|
|
906
878
|
const currentFloatingElement = store.select('floatingElement');
|
|
907
|
-
const
|
|
908
|
-
const withinFloating = useButton.contains(currentFloatingElement, next);
|
|
879
|
+
const withinComposite = useButton.contains(currentDomReferenceElement, next) || useButton.contains(currentFloatingElement, next);
|
|
909
880
|
|
|
910
881
|
// Keep the session if focus moves within the composite (reference <-> floating).
|
|
911
|
-
if (
|
|
882
|
+
if (withinComposite) {
|
|
912
883
|
return;
|
|
913
884
|
}
|
|
914
885
|
|
|
@@ -916,33 +887,44 @@ function useTypeahead(context, props) {
|
|
|
916
887
|
timeout.clear();
|
|
917
888
|
stringRef.current = '';
|
|
918
889
|
prevIndexRef.current = matchIndexRef.current;
|
|
919
|
-
|
|
890
|
+
onTyping?.(false);
|
|
920
891
|
});
|
|
921
|
-
|
|
892
|
+
useButton.useIsoLayoutEffect(() => {
|
|
893
|
+
if (!open && selectedIndex !== null) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
timeout.clear();
|
|
897
|
+
matchIndexRef.current = null;
|
|
898
|
+
if (stringRef.current !== '') {
|
|
899
|
+
stringRef.current = '';
|
|
900
|
+
}
|
|
901
|
+
}, [open, selectedIndex, timeout]);
|
|
902
|
+
useButton.useIsoLayoutEffect(() => {
|
|
903
|
+
// Sync arrow key navigation but not typeahead navigation.
|
|
904
|
+
if (open && stringRef.current === '') {
|
|
905
|
+
prevIndexRef.current = selectedIndex ?? activeIndex ?? -1;
|
|
906
|
+
}
|
|
907
|
+
}, [open, selectedIndex, activeIndex]);
|
|
908
|
+
const sharedProps = React__namespace.useMemo(() => ({
|
|
922
909
|
onKeyDown,
|
|
923
910
|
onBlur
|
|
924
911
|
}), [onKeyDown, onBlur]);
|
|
925
|
-
const floating = React__namespace.useMemo(() => {
|
|
926
|
-
return {
|
|
927
|
-
onKeyDown,
|
|
928
|
-
onBlur
|
|
929
|
-
};
|
|
930
|
-
}, [onKeyDown, onBlur]);
|
|
931
912
|
return React__namespace.useMemo(() => enabled ? {
|
|
932
|
-
reference,
|
|
933
|
-
floating
|
|
934
|
-
} : {}, [enabled,
|
|
913
|
+
reference: sharedProps,
|
|
914
|
+
floating: sharedProps
|
|
915
|
+
} : {}, [enabled, sharedProps]);
|
|
935
916
|
}
|
|
936
917
|
|
|
937
918
|
function getPseudoElementBounds(element) {
|
|
938
919
|
const elementRect = element.getBoundingClientRect();
|
|
920
|
+
const win = floatingUi_utils_dom.getWindow(element);
|
|
939
921
|
|
|
940
|
-
// Avoid "Not implemented: window.getComputedStyle(elt, pseudoElt)"
|
|
941
|
-
if (
|
|
922
|
+
// Avoid "Not implemented: window.getComputedStyle(elt, pseudoElt)" in jsdom.
|
|
923
|
+
if (useButton.jsdom) {
|
|
942
924
|
return elementRect;
|
|
943
925
|
}
|
|
944
|
-
const beforeStyles =
|
|
945
|
-
const afterStyles =
|
|
926
|
+
const beforeStyles = win.getComputedStyle(element, '::before');
|
|
927
|
+
const afterStyles = win.getComputedStyle(element, '::after');
|
|
946
928
|
const hasPseudoElements = beforeStyles.content !== 'none' || afterStyles.content !== 'none';
|
|
947
929
|
if (!hasPseudoElements) {
|
|
948
930
|
return elementRect;
|
|
@@ -1152,17 +1134,17 @@ const baseArrow = options => ({
|
|
|
1152
1134
|
element,
|
|
1153
1135
|
padding = 0,
|
|
1154
1136
|
offsetParent = 'real'
|
|
1155
|
-
} =
|
|
1137
|
+
} = floatingUi_utils.evaluate(options, state) || {};
|
|
1156
1138
|
if (element == null) {
|
|
1157
1139
|
return {};
|
|
1158
1140
|
}
|
|
1159
|
-
const paddingObject =
|
|
1141
|
+
const paddingObject = floatingUi_utils.getPaddingObject(padding);
|
|
1160
1142
|
const coords = {
|
|
1161
1143
|
x,
|
|
1162
1144
|
y
|
|
1163
1145
|
};
|
|
1164
|
-
const axis =
|
|
1165
|
-
const length =
|
|
1146
|
+
const axis = floatingUi_utils.getAlignmentAxis(placement);
|
|
1147
|
+
const length = floatingUi_utils.getAxisLength(axis);
|
|
1166
1148
|
const arrowDimensions = await platform.getDimensions(element);
|
|
1167
1149
|
const isYAxis = axis === 'y';
|
|
1168
1150
|
const minProp = isYAxis ? 'top' : 'left';
|
|
@@ -1190,13 +1172,13 @@ const baseArrow = options => ({
|
|
|
1190
1172
|
const min = minPadding;
|
|
1191
1173
|
const max = clientSize - arrowDimensions[length] - maxPadding;
|
|
1192
1174
|
const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
|
|
1193
|
-
const offset =
|
|
1175
|
+
const offset = floatingUi_utils.clamp(min, center, max);
|
|
1194
1176
|
|
|
1195
1177
|
// If the reference is small enough that the arrow's padding causes it to
|
|
1196
1178
|
// to point to nothing for an aligned placement, adjust the offset of the
|
|
1197
1179
|
// floating element itself. To ensure `shift()` continues to take action,
|
|
1198
1180
|
// a single reset is performed when this is true.
|
|
1199
|
-
const shouldAddOffset = !middlewareData.arrow &&
|
|
1181
|
+
const shouldAddOffset = !middlewareData.arrow && floatingUi_utils.getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
|
|
1200
1182
|
// eslint-disable-next-line no-nested-ternary
|
|
1201
1183
|
const alignmentOffset = shouldAddOffset ? center < min ? center - min : center - max : 0;
|
|
1202
1184
|
return {
|
|
@@ -1224,6 +1206,7 @@ const arrow = (options, deps) => ({
|
|
|
1224
1206
|
options: [options, deps]
|
|
1225
1207
|
});
|
|
1226
1208
|
|
|
1209
|
+
const nativeHideFn = floatingUi_reactDom.hide().fn;
|
|
1227
1210
|
const hide = {
|
|
1228
1211
|
name: 'hide',
|
|
1229
1212
|
async fn(state) {
|
|
@@ -1234,7 +1217,7 @@ const hide = {
|
|
|
1234
1217
|
y
|
|
1235
1218
|
} = state.rects.reference;
|
|
1236
1219
|
const anchorHidden = width === 0 && height === 0 && x === 0 && y === 0;
|
|
1237
|
-
const nativeHideResult = await
|
|
1220
|
+
const nativeHideResult = await nativeHideFn(state);
|
|
1238
1221
|
return {
|
|
1239
1222
|
data: {
|
|
1240
1223
|
referenceHidden: nativeHideResult.data?.referenceHidden || anchorHidden
|
|
@@ -1294,7 +1277,7 @@ const adaptiveOrigin = {
|
|
|
1294
1277
|
} else if (await platform.isElement?.(offsetParent)) {
|
|
1295
1278
|
offsetDimensions = await platform.getDimensions(offsetParent);
|
|
1296
1279
|
}
|
|
1297
|
-
const currentSide =
|
|
1280
|
+
const currentSide = floatingUi_utils.getSide(placement);
|
|
1298
1281
|
let x = rawX;
|
|
1299
1282
|
let y = rawY;
|
|
1300
1283
|
if (currentSide === 'left') {
|
|
@@ -1333,8 +1316,8 @@ function getOffsetData(state, sideParam, isRtl) {
|
|
|
1333
1316
|
placement
|
|
1334
1317
|
} = state;
|
|
1335
1318
|
const data = {
|
|
1336
|
-
side: getLogicalSide(sideParam,
|
|
1337
|
-
align:
|
|
1319
|
+
side: getLogicalSide(sideParam, floatingUi_utils.getSide(placement), isRtl),
|
|
1320
|
+
align: floatingUi_utils.getAlignment(placement) || 'center',
|
|
1338
1321
|
anchor: {
|
|
1339
1322
|
width: rects.reference.width,
|
|
1340
1323
|
height: rects.reference.height
|
|
@@ -1364,6 +1347,7 @@ function useAnchorPositioning(params) {
|
|
|
1364
1347
|
sticky = false,
|
|
1365
1348
|
arrowPadding = 5,
|
|
1366
1349
|
disableAnchorTracking = false,
|
|
1350
|
+
inline: inlineMiddleware,
|
|
1367
1351
|
// Private parameters
|
|
1368
1352
|
keepMounted = false,
|
|
1369
1353
|
floatingRootContext,
|
|
@@ -1386,7 +1370,8 @@ function useAnchorPositioning(params) {
|
|
|
1386
1370
|
const anchorFnCallback = useButton.useStableCallback(anchorFn);
|
|
1387
1371
|
const anchorDep = anchorFn ? anchorFnCallback : anchor;
|
|
1388
1372
|
const anchorValueRef = useButton.useValueAsRef(anchor);
|
|
1389
|
-
const
|
|
1373
|
+
const mountedRef = useButton.useValueAsRef(mounted);
|
|
1374
|
+
const direction = useScrollLock.useDirection();
|
|
1390
1375
|
const isRtl = direction === 'rtl';
|
|
1391
1376
|
const side = mountSide || {
|
|
1392
1377
|
top: 'top',
|
|
@@ -1437,7 +1422,11 @@ function useAnchorPositioning(params) {
|
|
|
1437
1422
|
const alignOffsetRef = useButton.useValueAsRef(alignOffset);
|
|
1438
1423
|
const sideOffsetDep = typeof sideOffset !== 'function' ? sideOffset : 0;
|
|
1439
1424
|
const alignOffsetDep = typeof alignOffset !== 'function' ? alignOffset : 0;
|
|
1440
|
-
const middleware = [
|
|
1425
|
+
const middleware = [];
|
|
1426
|
+
if (inlineMiddleware) {
|
|
1427
|
+
middleware.push(inlineMiddleware);
|
|
1428
|
+
}
|
|
1429
|
+
middleware.push(floatingUi_reactDom.offset(state => {
|
|
1441
1430
|
const data = getOffsetData(state, sideParam, isRtl);
|
|
1442
1431
|
const sideAxis = typeof sideOffsetRef.current === 'function' ? sideOffsetRef.current(data) : sideOffsetRef.current;
|
|
1443
1432
|
const alignAxis = typeof alignOffsetRef.current === 'function' ? alignOffsetRef.current(data) : alignOffsetRef.current;
|
|
@@ -1446,7 +1435,7 @@ function useAnchorPositioning(params) {
|
|
|
1446
1435
|
crossAxis: alignAxis,
|
|
1447
1436
|
alignmentAxis: alignAxis
|
|
1448
1437
|
};
|
|
1449
|
-
}, [sideOffsetDep, alignOffsetDep, isRtl, sideParam])
|
|
1438
|
+
}, [sideOffsetDep, alignOffsetDep, isRtl, sideParam]));
|
|
1450
1439
|
const shiftDisabled = collisionAvoidanceAlign === 'none' && collisionAvoidanceSide !== 'shift';
|
|
1451
1440
|
const crossAxisShiftEnabled = !shiftDisabled && (sticky || shiftCrossAxis || collisionAvoidanceSide === 'shift');
|
|
1452
1441
|
const flipMiddleware = collisionAvoidanceSide === 'none' ? null : floatingUi_reactDom.flip({
|
|
@@ -1485,7 +1474,7 @@ function useAnchorPositioning(params) {
|
|
|
1485
1474
|
width,
|
|
1486
1475
|
height
|
|
1487
1476
|
} = arrowRef.current.getBoundingClientRect();
|
|
1488
|
-
const sideAxis =
|
|
1477
|
+
const sideAxis = floatingUi_utils.getSideAxis(floatingUi_utils.getSide(limitData.placement));
|
|
1489
1478
|
const arrowSize = sideAxis === 'y' ? width : height;
|
|
1490
1479
|
const offsetAmount = sideAxis === 'y' ? collisionPadding.left + collisionPadding.right : collisionPadding.top + collisionPadding.bottom;
|
|
1491
1480
|
return {
|
|
@@ -1511,12 +1500,15 @@ function useAnchorPositioning(params) {
|
|
|
1511
1500
|
availableHeight,
|
|
1512
1501
|
rects
|
|
1513
1502
|
}) {
|
|
1503
|
+
if (!mountedRef.current) {
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1514
1506
|
const floatingStyle = floating.style;
|
|
1515
1507
|
floatingStyle.setProperty('--available-width', `${availableWidth}px`);
|
|
1516
1508
|
floatingStyle.setProperty('--available-height', `${availableHeight}px`);
|
|
1517
1509
|
|
|
1518
1510
|
// Snap anchor dimensions to device pixels to ensure the popup's visual width matches the anchor's one.
|
|
1519
|
-
const dpr =
|
|
1511
|
+
const dpr = floatingUi_utils_dom.getWindow(floating).devicePixelRatio || 1;
|
|
1520
1512
|
const {
|
|
1521
1513
|
x,
|
|
1522
1514
|
y,
|
|
@@ -1528,10 +1520,10 @@ function useAnchorPositioning(params) {
|
|
|
1528
1520
|
floatingStyle.setProperty('--anchor-width', `${anchorWidth}px`);
|
|
1529
1521
|
floatingStyle.setProperty('--anchor-height', `${anchorHeight}px`);
|
|
1530
1522
|
}
|
|
1531
|
-
}), arrow(
|
|
1523
|
+
}), arrow(state => ({
|
|
1532
1524
|
// `transform-origin` calculations rely on an element existing. If the arrow hasn't been set,
|
|
1533
1525
|
// we'll create a fake element.
|
|
1534
|
-
element: arrowRef.current ||
|
|
1526
|
+
element: arrowRef.current || useButton.ownerDocument(state.elements.floating).createElement('div'),
|
|
1535
1527
|
padding: arrowPadding,
|
|
1536
1528
|
offsetParent: 'floating'
|
|
1537
1529
|
}), [arrowPadding]), {
|
|
@@ -1544,8 +1536,8 @@ function useAnchorPositioning(params) {
|
|
|
1544
1536
|
rects,
|
|
1545
1537
|
y
|
|
1546
1538
|
} = state;
|
|
1547
|
-
const currentRenderedSide =
|
|
1548
|
-
const currentRenderedAxis =
|
|
1539
|
+
const currentRenderedSide = floatingUi_utils.getSide(renderedPlacement);
|
|
1540
|
+
const currentRenderedAxis = floatingUi_utils.getSideAxis(currentRenderedSide);
|
|
1549
1541
|
const arrowEl = arrowRef.current;
|
|
1550
1542
|
const arrowX = middlewareData.arrow?.x || 0;
|
|
1551
1543
|
const arrowY = middlewareData.arrow?.y || 0;
|
|
@@ -1575,7 +1567,8 @@ function useAnchorPositioning(params) {
|
|
|
1575
1567
|
floatingRootContext.update({
|
|
1576
1568
|
referenceElement: null,
|
|
1577
1569
|
floatingElement: null,
|
|
1578
|
-
domReferenceElement: null
|
|
1570
|
+
domReferenceElement: null,
|
|
1571
|
+
positionReference: null
|
|
1579
1572
|
});
|
|
1580
1573
|
}
|
|
1581
1574
|
}, [mounted, floatingRootContext]);
|
|
@@ -1596,6 +1589,7 @@ function useAnchorPositioning(params) {
|
|
|
1596
1589
|
floatingStyles: originalFloatingStyles
|
|
1597
1590
|
} = useFloating({
|
|
1598
1591
|
rootContext: floatingRootContext,
|
|
1592
|
+
open: keepMounted ? mounted : undefined,
|
|
1599
1593
|
placement,
|
|
1600
1594
|
middleware,
|
|
1601
1595
|
strategy: positionMethod,
|
|
@@ -1656,21 +1650,19 @@ function useAnchorPositioning(params) {
|
|
|
1656
1650
|
}
|
|
1657
1651
|
}, [mounted, refs, anchorDep, anchorValueRef]);
|
|
1658
1652
|
React__namespace.useEffect(() => {
|
|
1659
|
-
if (keepMounted && mounted && elements.
|
|
1660
|
-
return floatingUi_reactDom.autoUpdate(elements.
|
|
1653
|
+
if (keepMounted && mounted && elements.reference && elements.floating) {
|
|
1654
|
+
return floatingUi_reactDom.autoUpdate(elements.reference, elements.floating, update, autoUpdateOptions);
|
|
1661
1655
|
}
|
|
1662
1656
|
return undefined;
|
|
1663
1657
|
}, [keepMounted, mounted, elements, update, autoUpdateOptions]);
|
|
1664
|
-
const renderedSide =
|
|
1658
|
+
const renderedSide = floatingUi_utils.getSide(renderedPlacement);
|
|
1665
1659
|
const logicalRenderedSide = getLogicalSide(sideParam, renderedSide, isRtl);
|
|
1666
|
-
const renderedAlign =
|
|
1660
|
+
const renderedAlign = floatingUi_utils.getAlignment(renderedPlacement) || 'center';
|
|
1667
1661
|
const anchorHidden = Boolean(middlewareData.hide?.referenceHidden);
|
|
1668
1662
|
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
* the size of the popup dynamically to avoid unwanted flipping when typing.
|
|
1673
|
-
*/
|
|
1663
|
+
// Locks the flip (makes it "sticky") so it doesn't prefer a given placement
|
|
1664
|
+
// and flips back lazily, not eagerly. Ideal for filtered lists that change
|
|
1665
|
+
// the size of the popup dynamically to avoid unwanted flipping when typing.
|
|
1674
1666
|
useButton.useIsoLayoutEffect(() => {
|
|
1675
1667
|
if (lazyFlip && mounted && isPositioned) {
|
|
1676
1668
|
setMountSide(renderedSide);
|
|
@@ -1702,7 +1694,62 @@ function isRef(param) {
|
|
|
1702
1694
|
}
|
|
1703
1695
|
|
|
1704
1696
|
function getDisabledMountTransitionStyles(transitionStatus) {
|
|
1705
|
-
return transitionStatus === 'starting' ?
|
|
1697
|
+
return transitionStatus === 'starting' ? useScrollLock.DISABLED_TRANSITIONS_STYLE : useRenderElement.EMPTY_OBJECT;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
/**
|
|
1701
|
+
* Renders the shared outer Positioner element used by popup components.
|
|
1702
|
+
* Applies the common role, hidden state, transition styles, state attributes, and optional inert styling.
|
|
1703
|
+
*/
|
|
1704
|
+
function usePositioner(componentProps, state, {
|
|
1705
|
+
styles,
|
|
1706
|
+
transitionStatus,
|
|
1707
|
+
props,
|
|
1708
|
+
refs,
|
|
1709
|
+
hidden,
|
|
1710
|
+
inert = false
|
|
1711
|
+
}) {
|
|
1712
|
+
const style = {
|
|
1713
|
+
...styles
|
|
1714
|
+
};
|
|
1715
|
+
if (inert) {
|
|
1716
|
+
style.pointerEvents = 'none';
|
|
1717
|
+
}
|
|
1718
|
+
return useRenderElement.useRenderElement('div', componentProps, {
|
|
1719
|
+
state,
|
|
1720
|
+
ref: refs,
|
|
1721
|
+
props: [{
|
|
1722
|
+
role: 'presentation',
|
|
1723
|
+
hidden,
|
|
1724
|
+
style
|
|
1725
|
+
}, getDisabledMountTransitionStyles(transitionStatus), props],
|
|
1726
|
+
stateAttributesMapping: useScrollLock.popupStateMapping
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// Touch-opened popups normally avoid scroll locking so users can still swipe outside to dismiss.
|
|
1731
|
+
// This hook re-enables scroll lock only when the popup is effectively full-width.
|
|
1732
|
+
// Treat popups with up to 20px of total horizontal gutter as full-width so common ~10px side
|
|
1733
|
+
// padding still locks scroll, since that leaves too little outside space for a reliable swipe.
|
|
1734
|
+
const VIEWPORT_WIDTH_TOLERANCE_PX = 20;
|
|
1735
|
+
|
|
1736
|
+
/**
|
|
1737
|
+
* Manages scroll lock for anchored popups. For non-touch opens, scroll lock is applied when
|
|
1738
|
+
* enabled. For touch opens, scroll lock is applied only when the positioner width is effectively
|
|
1739
|
+
* viewport-sized.
|
|
1740
|
+
*/
|
|
1741
|
+
function useAnchoredPopupScrollLock(enabled, touchOpen, positionerElement, referenceElement) {
|
|
1742
|
+
const [touchOpenShouldLockScroll, setTouchOpenShouldLockScroll] = React__namespace.useState(false);
|
|
1743
|
+
useButton.useIsoLayoutEffect(() => {
|
|
1744
|
+
if (!enabled || !touchOpen || positionerElement == null) {
|
|
1745
|
+
setTouchOpenShouldLockScroll(false);
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
const viewportWidth = useButton.ownerDocument(positionerElement).documentElement.clientWidth;
|
|
1749
|
+
const popupWidth = positionerElement.offsetWidth;
|
|
1750
|
+
setTouchOpenShouldLockScroll(viewportWidth > 0 && popupWidth > 0 && popupWidth >= viewportWidth - VIEWPORT_WIDTH_TOLERANCE_PX);
|
|
1751
|
+
}, [enabled, touchOpen, positionerElement]);
|
|
1752
|
+
useScrollLock.useScrollLock(enabled && (!touchOpen || touchOpenShouldLockScroll), referenceElement);
|
|
1706
1753
|
}
|
|
1707
1754
|
|
|
1708
1755
|
let IndexGuessBehavior = /*#__PURE__*/function (IndexGuessBehavior) {
|
|
@@ -1774,10 +1821,10 @@ function useCompositeListItem(params = {}) {
|
|
|
1774
1821
|
}
|
|
1775
1822
|
});
|
|
1776
1823
|
}, [externalIndex, subscribeMapChange, setIndex]);
|
|
1777
|
-
return
|
|
1824
|
+
return {
|
|
1778
1825
|
ref,
|
|
1779
1826
|
index
|
|
1780
|
-
}
|
|
1827
|
+
};
|
|
1781
1828
|
}
|
|
1782
1829
|
|
|
1783
1830
|
exports.CompositeList = CompositeList;
|
|
@@ -1786,7 +1833,9 @@ exports.adaptiveOrigin = adaptiveOrigin;
|
|
|
1786
1833
|
exports.getDisabledMountTransitionStyles = getDisabledMountTransitionStyles;
|
|
1787
1834
|
exports.getPseudoElementBounds = getPseudoElementBounds;
|
|
1788
1835
|
exports.useAnchorPositioning = useAnchorPositioning;
|
|
1836
|
+
exports.useAnchoredPopupScrollLock = useAnchoredPopupScrollLock;
|
|
1789
1837
|
exports.useCompositeListItem = useCompositeListItem;
|
|
1790
1838
|
exports.useFloatingRootContext = useFloatingRootContext;
|
|
1791
1839
|
exports.useListNavigation = useListNavigation;
|
|
1840
|
+
exports.usePositioner = usePositioner;
|
|
1792
1841
|
exports.useTypeahead = useTypeahead;
|