@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,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
|
-
var index_esm = require('./index.esm-cjs.js');
|
|
5
4
|
var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
|
|
6
|
-
var useRenderElement = require('./useRenderElement-cjs.js');
|
|
7
5
|
var useButton = require('./useButton-cjs.js');
|
|
6
|
+
var useRenderElement = require('./useRenderElement-cjs.js');
|
|
8
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
var floatingUi_utils = require('./floating-ui.utils-cjs.js');
|
|
9
9
|
var ReactDOM = require('react-dom');
|
|
10
10
|
|
|
11
11
|
function _interopNamespaceDefault(e) {
|
|
@@ -36,449 +36,24 @@ function useOnFirstRender(fn) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} catch {
|
|
48
|
-
}
|
|
49
|
-
if (isInputSameAsOutput) {
|
|
50
|
-
let stack = void 0;
|
|
51
|
-
try {
|
|
52
|
-
throw new Error();
|
|
53
|
-
} catch (e) {
|
|
54
|
-
({ stack } = e);
|
|
55
|
-
}
|
|
56
|
-
console.warn(
|
|
57
|
-
"The result function returned its own inputs without modification. e.g\n`createSelector([state => state.todos], todos => todos)`\nThis could lead to inefficient memoization and unnecessary re-renders.\nEnsure transformation logic is in the result function, and extraction logic is in the input selectors.",
|
|
58
|
-
{ stack }
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// src/devModeChecks/inputStabilityCheck.ts
|
|
65
|
-
var runInputStabilityCheck = (inputSelectorResultsObject, options, inputSelectorArgs) => {
|
|
66
|
-
const { memoize, memoizeOptions } = options;
|
|
67
|
-
const { inputSelectorResults, inputSelectorResultsCopy } = inputSelectorResultsObject;
|
|
68
|
-
const createAnEmptyObject = memoize(() => ({}), ...memoizeOptions);
|
|
69
|
-
const areInputSelectorResultsEqual = createAnEmptyObject.apply(null, inputSelectorResults) === createAnEmptyObject.apply(null, inputSelectorResultsCopy);
|
|
70
|
-
if (!areInputSelectorResultsEqual) {
|
|
71
|
-
let stack = void 0;
|
|
72
|
-
try {
|
|
73
|
-
throw new Error();
|
|
74
|
-
} catch (e) {
|
|
75
|
-
({ stack } = e);
|
|
76
|
-
}
|
|
77
|
-
console.warn(
|
|
78
|
-
"An input selector returned a different result when passed same arguments.\nThis means your output selector will likely run more frequently than intended.\nAvoid returning a new reference inside your input selector, e.g.\n`createSelector([state => state.todos.map(todo => todo.id)], todoIds => todoIds.length)`",
|
|
79
|
-
{
|
|
80
|
-
arguments: inputSelectorArgs,
|
|
81
|
-
firstInputs: inputSelectorResults,
|
|
82
|
-
secondInputs: inputSelectorResultsCopy,
|
|
83
|
-
stack
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// src/devModeChecks/setGlobalDevModeChecks.ts
|
|
90
|
-
var globalDevModeChecks = {
|
|
91
|
-
inputStabilityCheck: "once",
|
|
92
|
-
identityFunctionCheck: "once"
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// src/utils.ts
|
|
96
|
-
var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
|
|
97
|
-
function assertIsFunction(func, errorMessage = `expected a function, instead received ${typeof func}`) {
|
|
98
|
-
if (typeof func !== "function") {
|
|
99
|
-
throw new TypeError(errorMessage);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function assertIsObject(object, errorMessage = `expected an object, instead received ${typeof object}`) {
|
|
103
|
-
if (typeof object !== "object") {
|
|
104
|
-
throw new TypeError(errorMessage);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
function assertIsArrayOfFunctions(array, errorMessage = `expected all items to be functions, instead received the following types: `) {
|
|
108
|
-
if (!array.every((item) => typeof item === "function")) {
|
|
109
|
-
const itemTypes = array.map(
|
|
110
|
-
(item) => typeof item === "function" ? `function ${item.name || "unnamed"}()` : typeof item
|
|
111
|
-
).join(", ");
|
|
112
|
-
throw new TypeError(`${errorMessage}[${itemTypes}]`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
var ensureIsArray = (item) => {
|
|
116
|
-
return Array.isArray(item) ? item : [item];
|
|
117
|
-
};
|
|
118
|
-
function getDependencies(createSelectorArgs) {
|
|
119
|
-
const dependencies = Array.isArray(createSelectorArgs[0]) ? createSelectorArgs[0] : createSelectorArgs;
|
|
120
|
-
assertIsArrayOfFunctions(
|
|
121
|
-
dependencies,
|
|
122
|
-
`createSelector expects all input-selectors to be functions, but received the following types: `
|
|
123
|
-
);
|
|
124
|
-
return dependencies;
|
|
125
|
-
}
|
|
126
|
-
function collectInputSelectorResults(dependencies, inputSelectorArgs) {
|
|
127
|
-
const inputSelectorResults = [];
|
|
128
|
-
const { length } = dependencies;
|
|
129
|
-
for (let i = 0; i < length; i++) {
|
|
130
|
-
inputSelectorResults.push(dependencies[i].apply(null, inputSelectorArgs));
|
|
131
|
-
}
|
|
132
|
-
return inputSelectorResults;
|
|
133
|
-
}
|
|
134
|
-
var getDevModeChecksExecutionInfo = (firstRun, devModeChecks) => {
|
|
135
|
-
const { identityFunctionCheck, inputStabilityCheck } = {
|
|
136
|
-
...globalDevModeChecks,
|
|
137
|
-
...devModeChecks
|
|
138
|
-
};
|
|
139
|
-
return {
|
|
140
|
-
identityFunctionCheck: {
|
|
141
|
-
shouldRun: identityFunctionCheck === "always" || identityFunctionCheck === "once" && firstRun,
|
|
142
|
-
run: runIdentityFunctionCheck
|
|
143
|
-
},
|
|
144
|
-
inputStabilityCheck: {
|
|
145
|
-
shouldRun: inputStabilityCheck === "always" || inputStabilityCheck === "once" && firstRun,
|
|
146
|
-
run: runInputStabilityCheck
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
// src/lruMemoize.ts
|
|
152
|
-
function createSingletonCache(equals) {
|
|
153
|
-
let entry;
|
|
154
|
-
return {
|
|
155
|
-
get(key) {
|
|
156
|
-
if (entry && equals(entry.key, key)) {
|
|
157
|
-
return entry.value;
|
|
158
|
-
}
|
|
159
|
-
return NOT_FOUND;
|
|
160
|
-
},
|
|
161
|
-
put(key, value) {
|
|
162
|
-
entry = { key, value };
|
|
163
|
-
},
|
|
164
|
-
getEntries() {
|
|
165
|
-
return entry ? [entry] : [];
|
|
166
|
-
},
|
|
167
|
-
clear() {
|
|
168
|
-
entry = void 0;
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
function createLruCache(maxSize, equals) {
|
|
173
|
-
let entries = [];
|
|
174
|
-
function get(key) {
|
|
175
|
-
const cacheIndex = entries.findIndex((entry) => equals(key, entry.key));
|
|
176
|
-
if (cacheIndex > -1) {
|
|
177
|
-
const entry = entries[cacheIndex];
|
|
178
|
-
if (cacheIndex > 0) {
|
|
179
|
-
entries.splice(cacheIndex, 1);
|
|
180
|
-
entries.unshift(entry);
|
|
181
|
-
}
|
|
182
|
-
return entry.value;
|
|
183
|
-
}
|
|
184
|
-
return NOT_FOUND;
|
|
185
|
-
}
|
|
186
|
-
function put(key, value) {
|
|
187
|
-
if (get(key) === NOT_FOUND) {
|
|
188
|
-
entries.unshift({ key, value });
|
|
189
|
-
if (entries.length > maxSize) {
|
|
190
|
-
entries.pop();
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
function getEntries() {
|
|
195
|
-
return entries;
|
|
196
|
-
}
|
|
197
|
-
function clear() {
|
|
198
|
-
entries = [];
|
|
199
|
-
}
|
|
200
|
-
return { get, put, getEntries, clear };
|
|
201
|
-
}
|
|
202
|
-
var referenceEqualityCheck = (a, b) => a === b;
|
|
203
|
-
function createCacheKeyComparator(equalityCheck) {
|
|
204
|
-
return function areArgumentsShallowlyEqual(prev, next) {
|
|
205
|
-
if (prev === null || next === null || prev.length !== next.length) {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
const { length } = prev;
|
|
209
|
-
for (let i = 0; i < length; i++) {
|
|
210
|
-
if (!equalityCheck(prev[i], next[i])) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return true;
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
function lruMemoize(func, equalityCheckOrOptions) {
|
|
218
|
-
const providedOptions = typeof equalityCheckOrOptions === "object" ? equalityCheckOrOptions : { equalityCheck: equalityCheckOrOptions };
|
|
219
|
-
const {
|
|
220
|
-
equalityCheck = referenceEqualityCheck,
|
|
221
|
-
maxSize = 1,
|
|
222
|
-
resultEqualityCheck
|
|
223
|
-
} = providedOptions;
|
|
224
|
-
const comparator = createCacheKeyComparator(equalityCheck);
|
|
225
|
-
let resultsCount = 0;
|
|
226
|
-
const cache = maxSize <= 1 ? createSingletonCache(comparator) : createLruCache(maxSize, comparator);
|
|
227
|
-
function memoized() {
|
|
228
|
-
let value = cache.get(arguments);
|
|
229
|
-
if (value === NOT_FOUND) {
|
|
230
|
-
value = func.apply(null, arguments);
|
|
231
|
-
resultsCount++;
|
|
232
|
-
if (resultEqualityCheck) {
|
|
233
|
-
const entries = cache.getEntries();
|
|
234
|
-
const matchingEntry = entries.find(
|
|
235
|
-
(entry) => resultEqualityCheck(entry.value, value)
|
|
236
|
-
);
|
|
237
|
-
if (matchingEntry) {
|
|
238
|
-
value = matchingEntry.value;
|
|
239
|
-
resultsCount !== 0 && resultsCount--;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
cache.put(arguments, value);
|
|
243
|
-
}
|
|
244
|
-
return value;
|
|
245
|
-
}
|
|
246
|
-
memoized.clearCache = () => {
|
|
247
|
-
cache.clear();
|
|
248
|
-
memoized.resetResultsCount();
|
|
249
|
-
};
|
|
250
|
-
memoized.resultsCount = () => resultsCount;
|
|
251
|
-
memoized.resetResultsCount = () => {
|
|
252
|
-
resultsCount = 0;
|
|
253
|
-
};
|
|
254
|
-
return memoized;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// src/weakMapMemoize.ts
|
|
258
|
-
var StrongRef = class {
|
|
259
|
-
constructor(value) {
|
|
260
|
-
this.value = value;
|
|
261
|
-
}
|
|
262
|
-
deref() {
|
|
263
|
-
return this.value;
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
var Ref = typeof WeakRef !== "undefined" ? WeakRef : StrongRef;
|
|
267
|
-
var UNTERMINATED = 0;
|
|
268
|
-
var TERMINATED = 1;
|
|
269
|
-
function createCacheNode() {
|
|
270
|
-
return {
|
|
271
|
-
s: UNTERMINATED,
|
|
272
|
-
v: void 0,
|
|
273
|
-
o: null,
|
|
274
|
-
p: null
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
function weakMapMemoize(func, options = {}) {
|
|
278
|
-
let fnNode = createCacheNode();
|
|
279
|
-
const { resultEqualityCheck } = options;
|
|
280
|
-
let lastResult;
|
|
281
|
-
let resultsCount = 0;
|
|
282
|
-
function memoized() {
|
|
283
|
-
let cacheNode = fnNode;
|
|
284
|
-
const { length } = arguments;
|
|
285
|
-
for (let i = 0, l = length; i < l; i++) {
|
|
286
|
-
const arg = arguments[i];
|
|
287
|
-
if (typeof arg === "function" || typeof arg === "object" && arg !== null) {
|
|
288
|
-
let objectCache = cacheNode.o;
|
|
289
|
-
if (objectCache === null) {
|
|
290
|
-
cacheNode.o = objectCache = /* @__PURE__ */ new WeakMap();
|
|
291
|
-
}
|
|
292
|
-
const objectNode = objectCache.get(arg);
|
|
293
|
-
if (objectNode === void 0) {
|
|
294
|
-
cacheNode = createCacheNode();
|
|
295
|
-
objectCache.set(arg, cacheNode);
|
|
296
|
-
} else {
|
|
297
|
-
cacheNode = objectNode;
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
let primitiveCache = cacheNode.p;
|
|
301
|
-
if (primitiveCache === null) {
|
|
302
|
-
cacheNode.p = primitiveCache = /* @__PURE__ */ new Map();
|
|
303
|
-
}
|
|
304
|
-
const primitiveNode = primitiveCache.get(arg);
|
|
305
|
-
if (primitiveNode === void 0) {
|
|
306
|
-
cacheNode = createCacheNode();
|
|
307
|
-
primitiveCache.set(arg, cacheNode);
|
|
308
|
-
} else {
|
|
309
|
-
cacheNode = primitiveNode;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
const terminatedNode = cacheNode;
|
|
314
|
-
let result;
|
|
315
|
-
if (cacheNode.s === TERMINATED) {
|
|
316
|
-
result = cacheNode.v;
|
|
317
|
-
} else {
|
|
318
|
-
result = func.apply(null, arguments);
|
|
319
|
-
resultsCount++;
|
|
320
|
-
if (resultEqualityCheck) {
|
|
321
|
-
const lastResultValue = lastResult?.deref?.() ?? lastResult;
|
|
322
|
-
if (lastResultValue != null && resultEqualityCheck(lastResultValue, result)) {
|
|
323
|
-
result = lastResultValue;
|
|
324
|
-
resultsCount !== 0 && resultsCount--;
|
|
325
|
-
}
|
|
326
|
-
const needsWeakRef = typeof result === "object" && result !== null || typeof result === "function";
|
|
327
|
-
lastResult = needsWeakRef ? new Ref(result) : result;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
terminatedNode.s = TERMINATED;
|
|
331
|
-
terminatedNode.v = result;
|
|
332
|
-
return result;
|
|
333
|
-
}
|
|
334
|
-
memoized.clearCache = () => {
|
|
335
|
-
fnNode = createCacheNode();
|
|
336
|
-
memoized.resetResultsCount();
|
|
337
|
-
};
|
|
338
|
-
memoized.resultsCount = () => resultsCount;
|
|
339
|
-
memoized.resetResultsCount = () => {
|
|
340
|
-
resultsCount = 0;
|
|
341
|
-
};
|
|
342
|
-
return memoized;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// src/createSelectorCreator.ts
|
|
346
|
-
function createSelectorCreator(memoizeOrOptions, ...memoizeOptionsFromArgs) {
|
|
347
|
-
const createSelectorCreatorOptions = typeof memoizeOrOptions === "function" ? {
|
|
348
|
-
memoize: memoizeOrOptions,
|
|
349
|
-
memoizeOptions: memoizeOptionsFromArgs
|
|
350
|
-
} : memoizeOrOptions;
|
|
351
|
-
const createSelector2 = (...createSelectorArgs) => {
|
|
352
|
-
let recomputations = 0;
|
|
353
|
-
let dependencyRecomputations = 0;
|
|
354
|
-
let lastResult;
|
|
355
|
-
let directlyPassedOptions = {};
|
|
356
|
-
let resultFunc = createSelectorArgs.pop();
|
|
357
|
-
if (typeof resultFunc === "object") {
|
|
358
|
-
directlyPassedOptions = resultFunc;
|
|
359
|
-
resultFunc = createSelectorArgs.pop();
|
|
360
|
-
}
|
|
361
|
-
assertIsFunction(
|
|
362
|
-
resultFunc,
|
|
363
|
-
`createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]`
|
|
364
|
-
);
|
|
365
|
-
const combinedOptions = {
|
|
366
|
-
...createSelectorCreatorOptions,
|
|
367
|
-
...directlyPassedOptions
|
|
368
|
-
};
|
|
369
|
-
const {
|
|
370
|
-
memoize,
|
|
371
|
-
memoizeOptions = [],
|
|
372
|
-
argsMemoize = weakMapMemoize,
|
|
373
|
-
argsMemoizeOptions = [],
|
|
374
|
-
devModeChecks = {}
|
|
375
|
-
} = combinedOptions;
|
|
376
|
-
const finalMemoizeOptions = ensureIsArray(memoizeOptions);
|
|
377
|
-
const finalArgsMemoizeOptions = ensureIsArray(argsMemoizeOptions);
|
|
378
|
-
const dependencies = getDependencies(createSelectorArgs);
|
|
379
|
-
const memoizedResultFunc = memoize(function recomputationWrapper() {
|
|
380
|
-
recomputations++;
|
|
381
|
-
return resultFunc.apply(
|
|
382
|
-
null,
|
|
383
|
-
arguments
|
|
384
|
-
);
|
|
385
|
-
}, ...finalMemoizeOptions);
|
|
386
|
-
let firstRun = true;
|
|
387
|
-
const selector = argsMemoize(function dependenciesChecker() {
|
|
388
|
-
dependencyRecomputations++;
|
|
389
|
-
const inputSelectorResults = collectInputSelectorResults(
|
|
390
|
-
dependencies,
|
|
391
|
-
arguments
|
|
392
|
-
);
|
|
393
|
-
lastResult = memoizedResultFunc.apply(null, inputSelectorResults);
|
|
394
|
-
if (process.env.NODE_ENV !== "production") {
|
|
395
|
-
const { identityFunctionCheck, inputStabilityCheck } = getDevModeChecksExecutionInfo(firstRun, devModeChecks);
|
|
396
|
-
if (identityFunctionCheck.shouldRun) {
|
|
397
|
-
identityFunctionCheck.run(
|
|
398
|
-
resultFunc,
|
|
399
|
-
inputSelectorResults,
|
|
400
|
-
lastResult
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
if (inputStabilityCheck.shouldRun) {
|
|
404
|
-
const inputSelectorResultsCopy = collectInputSelectorResults(
|
|
405
|
-
dependencies,
|
|
406
|
-
arguments
|
|
407
|
-
);
|
|
408
|
-
inputStabilityCheck.run(
|
|
409
|
-
{ inputSelectorResults, inputSelectorResultsCopy },
|
|
410
|
-
{ memoize, memoizeOptions: finalMemoizeOptions },
|
|
411
|
-
arguments
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
if (firstRun)
|
|
415
|
-
firstRun = false;
|
|
416
|
-
}
|
|
417
|
-
return lastResult;
|
|
418
|
-
}, ...finalArgsMemoizeOptions);
|
|
419
|
-
return Object.assign(selector, {
|
|
420
|
-
resultFunc,
|
|
421
|
-
memoizedResultFunc,
|
|
422
|
-
dependencies,
|
|
423
|
-
dependencyRecomputations: () => dependencyRecomputations,
|
|
424
|
-
resetDependencyRecomputations: () => {
|
|
425
|
-
dependencyRecomputations = 0;
|
|
426
|
-
},
|
|
427
|
-
lastResult: () => lastResult,
|
|
428
|
-
recomputations: () => recomputations,
|
|
429
|
-
resetRecomputations: () => {
|
|
430
|
-
recomputations = 0;
|
|
431
|
-
},
|
|
432
|
-
memoize,
|
|
433
|
-
argsMemoize
|
|
434
|
-
});
|
|
435
|
-
};
|
|
436
|
-
Object.assign(createSelector2, {
|
|
437
|
-
withTypes: () => createSelector2
|
|
438
|
-
});
|
|
439
|
-
return createSelector2;
|
|
440
|
-
}
|
|
441
|
-
var createSelector$1 = /* @__PURE__ */ createSelectorCreator(weakMapMemoize);
|
|
442
|
-
|
|
443
|
-
// src/createStructuredSelector.ts
|
|
444
|
-
var createStructuredSelector = Object.assign(
|
|
445
|
-
(inputSelectorsObject, selectorCreator = createSelector$1) => {
|
|
446
|
-
assertIsObject(
|
|
447
|
-
inputSelectorsObject,
|
|
448
|
-
`createStructuredSelector expects first argument to be an object where each property is a selector, instead received a ${typeof inputSelectorsObject}`
|
|
449
|
-
);
|
|
450
|
-
const inputSelectorKeys = Object.keys(inputSelectorsObject);
|
|
451
|
-
const dependencies = inputSelectorKeys.map(
|
|
452
|
-
(key) => inputSelectorsObject[key]
|
|
453
|
-
);
|
|
454
|
-
const structuredSelector = selectorCreator(
|
|
455
|
-
dependencies,
|
|
456
|
-
(...inputSelectorResults) => {
|
|
457
|
-
return inputSelectorResults.reduce((composition, value, index) => {
|
|
458
|
-
composition[inputSelectorKeys[index]] = value;
|
|
459
|
-
return composition;
|
|
460
|
-
}, {});
|
|
461
|
-
}
|
|
462
|
-
);
|
|
463
|
-
return structuredSelector;
|
|
464
|
-
},
|
|
465
|
-
{ withTypes: () => createStructuredSelector }
|
|
466
|
-
);
|
|
467
|
-
|
|
468
|
-
/* eslint-disable no-underscore-dangle */ // __cacheKey__
|
|
39
|
+
/**
|
|
40
|
+
* The NoOptionalParams type is a utility type that checks if a function has optional or default parameters.
|
|
41
|
+
* If the function has optional or default parameters, it returns a string literal type with an error message.
|
|
42
|
+
* Otherwise, it returns the original function type.
|
|
43
|
+
*
|
|
44
|
+
* This is used to enforce that the combiner function passed to createSelector does not have optional or default parameters,
|
|
45
|
+
* as memoization relies on the Function.length property, which does not account for optional or default parameters.
|
|
46
|
+
*/
|
|
469
47
|
|
|
470
|
-
createSelectorCreator({
|
|
471
|
-
memoize: lruMemoize,
|
|
472
|
-
memoizeOptions: {
|
|
473
|
-
maxSize: 1,
|
|
474
|
-
equalityCheck: Object.is
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
48
|
/**
|
|
478
49
|
* Creates a selector function that can be used to derive values from the store's state.
|
|
479
|
-
*
|
|
50
|
+
*
|
|
51
|
+
* The combiner function can have up to three additional parameters, but it **cannot have optional or default parameters**.
|
|
52
|
+
*
|
|
480
53
|
* This function accepts up to six functions and combines them into a single selector function.
|
|
481
|
-
* The
|
|
54
|
+
* The resulting selector will take the state from the combined selectors and any additional parameters required by the combiner.
|
|
55
|
+
*
|
|
56
|
+
* The return type of the resulting selector is determined by the return type of the combiner function.
|
|
482
57
|
*
|
|
483
58
|
* @example
|
|
484
59
|
* const selector = createSelector(
|
|
@@ -491,7 +66,6 @@ createSelectorCreator({
|
|
|
491
66
|
* (state) => state.open,
|
|
492
67
|
* (disabled, open) => ({ disabled, open })
|
|
493
68
|
* );
|
|
494
|
-
*
|
|
495
69
|
*/
|
|
496
70
|
/* eslint-disable id-denylist */
|
|
497
71
|
const createSelector = (a, b, c, d, e, f, ...other) => {
|
|
@@ -541,6 +115,7 @@ const createSelector = (a, b, c, d, e, f, ...other) => {
|
|
|
541
115
|
}
|
|
542
116
|
return selector;
|
|
543
117
|
};
|
|
118
|
+
/* eslint-enable id-denylist */
|
|
544
119
|
|
|
545
120
|
var shim = {exports: {}};
|
|
546
121
|
|
|
@@ -1003,10 +578,9 @@ register({
|
|
|
1003
578
|
for (let i = 0; i < instance.syncHooks.length; i += 1) {
|
|
1004
579
|
const hook = instance.syncHooks[i];
|
|
1005
580
|
const value = hook.selector(hook.store.state, hook.a1, hook.a2, hook.a3);
|
|
1006
|
-
if (
|
|
581
|
+
if (!Object.is(hook.value, value)) {
|
|
1007
582
|
didChange = true;
|
|
1008
583
|
hook.value = value;
|
|
1009
|
-
hook.didChange = false;
|
|
1010
584
|
}
|
|
1011
585
|
}
|
|
1012
586
|
if (didChange) {
|
|
@@ -1057,8 +631,7 @@ function useStoreFast(store, selector, a1, a2, a3) {
|
|
|
1057
631
|
a1,
|
|
1058
632
|
a2,
|
|
1059
633
|
a3,
|
|
1060
|
-
value: selector(store.getSnapshot(), a1, a2, a3)
|
|
1061
|
-
didChange: false
|
|
634
|
+
value: selector(store.getSnapshot(), a1, a2, a3)
|
|
1062
635
|
};
|
|
1063
636
|
instance.syncHooks.push(hook);
|
|
1064
637
|
} else {
|
|
@@ -1072,7 +645,7 @@ function useStoreFast(store, selector, a1, a2, a3) {
|
|
|
1072
645
|
hook.a1 = a1;
|
|
1073
646
|
hook.a2 = a2;
|
|
1074
647
|
hook.a3 = a3;
|
|
1075
|
-
hook.
|
|
648
|
+
hook.value = selector(store.getSnapshot(), a1, a2, a3);
|
|
1076
649
|
}
|
|
1077
650
|
}
|
|
1078
651
|
return hook.value;
|
|
@@ -1224,11 +797,13 @@ class ReactStore extends Store {
|
|
|
1224
797
|
*/
|
|
1225
798
|
useSyncedValue(key, value) {
|
|
1226
799
|
React__namespace.useDebugValue(key);
|
|
800
|
+
// eslint-disable-next-line consistent-this
|
|
801
|
+
const store = this;
|
|
1227
802
|
useButton.useIsoLayoutEffect(() => {
|
|
1228
|
-
if (
|
|
1229
|
-
|
|
803
|
+
if (store.state[key] !== value) {
|
|
804
|
+
store.set(key, value);
|
|
1230
805
|
}
|
|
1231
|
-
}, [key, value]);
|
|
806
|
+
}, [store, key, value]);
|
|
1232
807
|
}
|
|
1233
808
|
|
|
1234
809
|
/**
|
|
@@ -1282,16 +857,18 @@ class ReactStore extends Store {
|
|
|
1282
857
|
*/
|
|
1283
858
|
useControlledProp(key, controlled) {
|
|
1284
859
|
React__namespace.useDebugValue(key);
|
|
860
|
+
// eslint-disable-next-line consistent-this
|
|
861
|
+
const store = this;
|
|
1285
862
|
const isControlled = controlled !== undefined;
|
|
1286
863
|
useButton.useIsoLayoutEffect(() => {
|
|
1287
|
-
if (isControlled && !Object.is(
|
|
864
|
+
if (isControlled && !Object.is(store.state[key], controlled)) {
|
|
1288
865
|
// Set the internal state to match the controlled value.
|
|
1289
|
-
|
|
1290
|
-
...
|
|
866
|
+
store.setState({
|
|
867
|
+
...store.state,
|
|
1291
868
|
[key]: controlled
|
|
1292
869
|
});
|
|
1293
870
|
}
|
|
1294
|
-
}, [key, controlled, isControlled]);
|
|
871
|
+
}, [store, key, controlled, isControlled]);
|
|
1295
872
|
if (process.env.NODE_ENV !== 'production') {
|
|
1296
873
|
// eslint-disable-next-line
|
|
1297
874
|
const cache = this.controlledValues ??= new Map();
|
|
@@ -1384,44 +961,169 @@ class ReactStore extends Store {
|
|
|
1384
961
|
}
|
|
1385
962
|
}
|
|
1386
963
|
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
return
|
|
964
|
+
/**
|
|
965
|
+
* Combines multiple cleanup functions into a single cleanup function.
|
|
966
|
+
*/
|
|
967
|
+
function mergeCleanups(...cleanups) {
|
|
968
|
+
return () => {
|
|
969
|
+
for (let i = 0; i < cleanups.length; i += 1) {
|
|
970
|
+
const cleanup = cleanups[i];
|
|
971
|
+
if (cleanup) {
|
|
972
|
+
cleanup();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
};
|
|
1392
976
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
977
|
+
|
|
978
|
+
// WebKit (Safari, all iOS browsers, GNOME Web). Distinguished from Blink by
|
|
979
|
+
// the legacy `-webkit-backdrop-filter` name — Blink forked from WebKit in 2013
|
|
980
|
+
// and only ships the unprefixed `backdrop-filter`.
|
|
981
|
+
/** WebKit: Safari, all iOS browsers, GNOME Web. Excludes Blink. */
|
|
982
|
+
const webkit = typeof CSS !== 'undefined' && !!CSS.supports?.('-webkit-backdrop-filter:none');
|
|
983
|
+
|
|
984
|
+
// Anchored to `!webkit` so engines are mutually exclusive by construction.
|
|
985
|
+
// Firefox-on-iOS uses WebKit (its UA marker is `FxiOS/`, not `Firefox/`); the
|
|
986
|
+
// `!webkit` prefix also defends against future iOS-browser UA changes that
|
|
987
|
+
// might inject `Firefox` into a WebKit-based UA.
|
|
988
|
+
/** Gecko: Firefox. */
|
|
989
|
+
const gecko = !webkit && useButton.lowerUserAgent.includes('firefox');
|
|
990
|
+
|
|
991
|
+
// All Chromium-based browsers ship `Chrome/` or `Chromium/` in their UA — both
|
|
992
|
+
// share the `chrom` prefix, so a single substring check covers Chrome, Edge,
|
|
993
|
+
// Opera, Brave, etc. Chrome-on-iOS uses `CriOS/` and stays WebKit. The positive
|
|
994
|
+
// UA check also makes this SSR-safe — an empty UA matches nothing.
|
|
995
|
+
/** Blink: Chrome, Edge, Opera, Brave, and other Chromium-based browsers. */
|
|
996
|
+
!webkit && useButton.lowerUserAgent.includes('chrom');
|
|
997
|
+
|
|
998
|
+
// Whether a screen reader is *actually* running cannot be detected. These flags
|
|
999
|
+
// identify platforms where a specific screen reader could be active. VoiceOver
|
|
1000
|
+
// is the system screen reader on Apple platforms and works with every browser
|
|
1001
|
+
// there, so the flag is purely an OS check; engine-specific quirks (e.g. the
|
|
1002
|
+
// NSAccessibility virtual-cursor focus path) should be gated at the call site.
|
|
1003
|
+
/**
|
|
1004
|
+
* The user *may* be using VoiceOver — actual activation is not detectable.
|
|
1005
|
+
* True on any Apple platform (macOS, iOS, iPadOS).
|
|
1006
|
+
*/
|
|
1007
|
+
const voiceOver = useButton.apple;
|
|
1008
|
+
|
|
1009
|
+
const FOCUSABLE_ATTRIBUTE = 'data-base-ui-focusable';
|
|
1010
|
+
const TYPEABLE_SELECTOR = "input:not([type='hidden']):not([disabled])," + "[contenteditable]:not([contenteditable='false']),textarea:not([disabled])";
|
|
1011
|
+
const ARROW_LEFT = 'ArrowLeft';
|
|
1012
|
+
const ARROW_RIGHT = 'ArrowRight';
|
|
1013
|
+
const ARROW_UP = 'ArrowUp';
|
|
1014
|
+
const ARROW_DOWN = 'ArrowDown';
|
|
1015
|
+
|
|
1016
|
+
function isTargetInsideEnabledTrigger(target, triggerElements) {
|
|
1017
|
+
if (!floatingUi_utils_dom.isElement(target)) {
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|
|
1020
|
+
const targetElement = target;
|
|
1021
|
+
if (triggerElements.hasElement(targetElement)) {
|
|
1022
|
+
return !targetElement.hasAttribute('data-trigger-disabled');
|
|
1023
|
+
}
|
|
1024
|
+
for (const [, trigger] of triggerElements.entries()) {
|
|
1025
|
+
if (useButton.contains(trigger, targetElement)) {
|
|
1026
|
+
return !trigger.hasAttribute('data-trigger-disabled');
|
|
1401
1027
|
}
|
|
1402
1028
|
}
|
|
1403
|
-
return
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
function isEventTargetWithin(event, node) {
|
|
1032
|
+
if (node == null) {
|
|
1033
|
+
return false;
|
|
1034
|
+
}
|
|
1035
|
+
if ('composedPath' in event) {
|
|
1036
|
+
return event.composedPath().includes(node);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// TS thinks `event` is of type never as it assumes all browsers support composedPath, but browsers without shadow dom don't
|
|
1040
|
+
const eventAgain = event;
|
|
1041
|
+
return eventAgain.target != null && node.contains(eventAgain.target);
|
|
1042
|
+
}
|
|
1043
|
+
function isRootElement(element) {
|
|
1044
|
+
return element.matches('html,body');
|
|
1045
|
+
}
|
|
1046
|
+
function isTypeableElement(element) {
|
|
1047
|
+
return floatingUi_utils_dom.isHTMLElement(element) && element.matches(TYPEABLE_SELECTOR);
|
|
1048
|
+
}
|
|
1049
|
+
function isInteractiveElement(element) {
|
|
1050
|
+
return element?.closest(`button,a[href],[role="button"],select,[tabindex]:not([tabindex="-1"]),${TYPEABLE_SELECTOR}`) != null;
|
|
1404
1051
|
}
|
|
1052
|
+
function isTypeableCombobox(element) {
|
|
1053
|
+
if (!element) {
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
return element.getAttribute('role') === 'combobox' && isTypeableElement(element);
|
|
1057
|
+
}
|
|
1058
|
+
function matchesFocusVisible(element) {
|
|
1059
|
+
// We don't want to block focus from working with `visibleOnly`
|
|
1060
|
+
// (JSDOM doesn't match `:focus-visible` when the element has `:focus`)
|
|
1061
|
+
if (!element || useButton.jsdom) {
|
|
1062
|
+
return true;
|
|
1063
|
+
}
|
|
1064
|
+
try {
|
|
1065
|
+
return element.matches(':focus-visible');
|
|
1066
|
+
} catch (_e) {
|
|
1067
|
+
return true;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
function getFloatingFocusElement(floatingElement) {
|
|
1071
|
+
if (!floatingElement) {
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
// Try to find the element that has `{...getFloatingProps()}` spread on it.
|
|
1075
|
+
// This indicates the floating element is acting as a positioning wrapper, and
|
|
1076
|
+
// so focus should be managed on the child element with the event handlers and
|
|
1077
|
+
// aria props.
|
|
1078
|
+
return floatingElement.hasAttribute(FOCUSABLE_ATTRIBUTE) ? floatingElement : floatingElement.querySelector(`[${FOCUSABLE_ATTRIBUTE}]`) || floatingElement;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const FocusGuard = /*#__PURE__*/React__namespace.forwardRef(function FocusGuard(props, ref) {
|
|
1082
|
+
const [role, setRole] = React__namespace.useState();
|
|
1083
|
+
useButton.useIsoLayoutEffect(() => {
|
|
1084
|
+
// Unlike NVDA and JAWS, VoiceOver's virtual cursor triggers `onFocus` as
|
|
1085
|
+
// it moves — but only on focusable/role-button elements through WebKit's
|
|
1086
|
+
// NSAccessibility path. Setting `role="button"` lets the focus trap catch
|
|
1087
|
+
// the cursor.
|
|
1088
|
+
if (voiceOver && webkit) {
|
|
1089
|
+
setRole('button');
|
|
1090
|
+
}
|
|
1091
|
+
}, []);
|
|
1092
|
+
const restProps = {
|
|
1093
|
+
tabIndex: 0,
|
|
1094
|
+
// Role is only for VoiceOver
|
|
1095
|
+
role
|
|
1096
|
+
};
|
|
1097
|
+
return /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
1098
|
+
...props,
|
|
1099
|
+
ref: ref,
|
|
1100
|
+
style: useButton.visuallyHidden,
|
|
1101
|
+
"aria-hidden": role ? undefined : true,
|
|
1102
|
+
...restProps,
|
|
1103
|
+
"data-base-ui-focus-guard": ""
|
|
1104
|
+
});
|
|
1105
|
+
});
|
|
1106
|
+
if (process.env.NODE_ENV !== "production") FocusGuard.displayName = "FocusGuard";
|
|
1405
1107
|
|
|
1406
1108
|
function isDifferentGridRow(index, cols, prevRow) {
|
|
1407
1109
|
return Math.floor(index / cols) !== prevRow;
|
|
1408
1110
|
}
|
|
1409
|
-
function isIndexOutOfListBounds(
|
|
1410
|
-
return index < 0 || index >=
|
|
1111
|
+
function isIndexOutOfListBounds(list, index) {
|
|
1112
|
+
return index < 0 || index >= list.length;
|
|
1411
1113
|
}
|
|
1412
1114
|
function getMinListIndex(listRef, disabledIndices) {
|
|
1413
|
-
return findNonDisabledListIndex(listRef, {
|
|
1115
|
+
return findNonDisabledListIndex(listRef.current, {
|
|
1414
1116
|
disabledIndices
|
|
1415
1117
|
});
|
|
1416
1118
|
}
|
|
1417
1119
|
function getMaxListIndex(listRef, disabledIndices) {
|
|
1418
|
-
return findNonDisabledListIndex(listRef, {
|
|
1120
|
+
return findNonDisabledListIndex(listRef.current, {
|
|
1419
1121
|
decrement: true,
|
|
1420
1122
|
startingIndex: listRef.current.length,
|
|
1421
1123
|
disabledIndices
|
|
1422
1124
|
});
|
|
1423
1125
|
}
|
|
1424
|
-
function findNonDisabledListIndex(
|
|
1126
|
+
function findNonDisabledListIndex(list, {
|
|
1425
1127
|
startingIndex = -1,
|
|
1426
1128
|
decrement = false,
|
|
1427
1129
|
disabledIndices,
|
|
@@ -1430,13 +1132,14 @@ function findNonDisabledListIndex(listRef, {
|
|
|
1430
1132
|
let index = startingIndex;
|
|
1431
1133
|
do {
|
|
1432
1134
|
index += decrement ? -amount : amount;
|
|
1433
|
-
} while (index >= 0 && index <=
|
|
1135
|
+
} while (index >= 0 && index <= list.length - 1 && isListIndexDisabled(list, index, disabledIndices));
|
|
1434
1136
|
return index;
|
|
1435
1137
|
}
|
|
1436
|
-
function getGridNavigatedIndex(
|
|
1138
|
+
function getGridNavigatedIndex(list, {
|
|
1437
1139
|
event,
|
|
1438
1140
|
orientation,
|
|
1439
1141
|
loopFocus,
|
|
1142
|
+
onLoop,
|
|
1440
1143
|
rtl,
|
|
1441
1144
|
cols,
|
|
1442
1145
|
disabledIndices,
|
|
@@ -1447,9 +1150,9 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1447
1150
|
}) {
|
|
1448
1151
|
let nextIndex = prevIndex;
|
|
1449
1152
|
let verticalDirection;
|
|
1450
|
-
if (event.key ===
|
|
1153
|
+
if (event.key === ARROW_UP) {
|
|
1451
1154
|
verticalDirection = 'up';
|
|
1452
|
-
} else if (event.key ===
|
|
1155
|
+
} else if (event.key === ARROW_DOWN) {
|
|
1453
1156
|
verticalDirection = 'down';
|
|
1454
1157
|
}
|
|
1455
1158
|
if (verticalDirection) {
|
|
@@ -1464,7 +1167,7 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1464
1167
|
{
|
|
1465
1168
|
let currentRowEl = null;
|
|
1466
1169
|
let currentRowIndex = -1;
|
|
1467
|
-
|
|
1170
|
+
list.forEach((el, idx) => {
|
|
1468
1171
|
if (el == null) {
|
|
1469
1172
|
return;
|
|
1470
1173
|
}
|
|
@@ -1495,7 +1198,7 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1495
1198
|
}
|
|
1496
1199
|
}
|
|
1497
1200
|
}
|
|
1498
|
-
const hasVirtualizedGaps = hasDomRows && visibleItemCount <
|
|
1201
|
+
const hasVirtualizedGaps = hasDomRows && visibleItemCount < list.length;
|
|
1499
1202
|
const verticalCols = inferredDomCols || cols;
|
|
1500
1203
|
const navigateVertically = direction => {
|
|
1501
1204
|
if (!hasDomRows || prevIndex === -1) {
|
|
@@ -1513,11 +1216,17 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1513
1216
|
return undefined;
|
|
1514
1217
|
}
|
|
1515
1218
|
nextRow = nextRow < 0 ? rows.length - 1 : 0;
|
|
1219
|
+
if (onLoop) {
|
|
1220
|
+
const clampedCol = Math.min(colInRow, rows[nextRow].length - 1);
|
|
1221
|
+
const targetItemIndex = rows[nextRow][clampedCol] ?? rows[nextRow][0];
|
|
1222
|
+
const returnedItemIndex = onLoop(event, prevIndex, targetItemIndex);
|
|
1223
|
+
nextRow = rowIndexMap[returnedItemIndex] ?? nextRow;
|
|
1224
|
+
}
|
|
1516
1225
|
}
|
|
1517
1226
|
const targetRow = rows[nextRow];
|
|
1518
1227
|
for (let col = Math.min(colInRow, targetRow.length - 1); col >= 0; col -= 1) {
|
|
1519
1228
|
const candidate = targetRow[col];
|
|
1520
|
-
if (!isListIndexDisabled(
|
|
1229
|
+
if (!isListIndexDisabled(list, candidate, disabledIndices)) {
|
|
1521
1230
|
return candidate;
|
|
1522
1231
|
}
|
|
1523
1232
|
}
|
|
@@ -1531,7 +1240,7 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1531
1240
|
const colInRow = prevIndex % verticalCols;
|
|
1532
1241
|
const rowStep = direction === 'up' ? -verticalCols : verticalCols;
|
|
1533
1242
|
const lastRowStart = maxIndex - maxIndex % verticalCols;
|
|
1534
|
-
const rowCount =
|
|
1243
|
+
const rowCount = floatingUi_utils.floor(maxIndex / verticalCols) + 1;
|
|
1535
1244
|
for (let rowStart = prevIndex - colInRow + rowStep, i = 0; i < rowCount; i += 1, rowStart += rowStep) {
|
|
1536
1245
|
if (rowStart < 0 || rowStart > maxIndex) {
|
|
1537
1246
|
if (!loopFocus) {
|
|
@@ -1541,7 +1250,7 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1541
1250
|
}
|
|
1542
1251
|
const rowEnd = Math.min(rowStart + verticalCols - 1, maxIndex);
|
|
1543
1252
|
for (let candidate = Math.min(rowStart + colInRow, rowEnd); candidate >= rowStart; candidate -= 1) {
|
|
1544
|
-
if (!isListIndexDisabled(
|
|
1253
|
+
if (!isListIndexDisabled(list, candidate, disabledIndices)) {
|
|
1545
1254
|
return candidate;
|
|
1546
1255
|
}
|
|
1547
1256
|
}
|
|
@@ -1557,7 +1266,7 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1557
1266
|
} else if (prevIndex === -1) {
|
|
1558
1267
|
nextIndex = verticalDirection === 'up' ? maxIndex : minIndex;
|
|
1559
1268
|
} else {
|
|
1560
|
-
nextIndex = findNonDisabledListIndex(
|
|
1269
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1561
1270
|
startingIndex: prevIndex,
|
|
1562
1271
|
amount: verticalCols,
|
|
1563
1272
|
decrement: verticalDirection === 'up',
|
|
@@ -1573,84 +1282,105 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1573
1282
|
} else {
|
|
1574
1283
|
nextIndex = maxCol > col ? offset : offset - verticalCols;
|
|
1575
1284
|
}
|
|
1285
|
+
if (onLoop) {
|
|
1286
|
+
nextIndex = onLoop(event, prevIndex, nextIndex);
|
|
1287
|
+
}
|
|
1576
1288
|
}
|
|
1577
1289
|
if (verticalDirection === 'down' && prevIndex + verticalCols > maxIndex) {
|
|
1578
|
-
nextIndex = findNonDisabledListIndex(
|
|
1290
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1579
1291
|
startingIndex: prevIndex % verticalCols - verticalCols,
|
|
1580
1292
|
amount: verticalCols,
|
|
1581
1293
|
disabledIndices
|
|
1582
1294
|
});
|
|
1295
|
+
if (onLoop) {
|
|
1296
|
+
nextIndex = onLoop(event, prevIndex, nextIndex);
|
|
1297
|
+
}
|
|
1583
1298
|
}
|
|
1584
1299
|
}
|
|
1585
1300
|
}
|
|
1586
|
-
if (isIndexOutOfListBounds(
|
|
1301
|
+
if (isIndexOutOfListBounds(list, nextIndex)) {
|
|
1587
1302
|
nextIndex = prevIndex;
|
|
1588
1303
|
}
|
|
1589
1304
|
}
|
|
1590
1305
|
|
|
1591
1306
|
// Remains on the same row/column.
|
|
1592
1307
|
if (orientation === 'both') {
|
|
1593
|
-
const prevRow =
|
|
1594
|
-
if (event.key === (rtl ?
|
|
1308
|
+
const prevRow = floatingUi_utils.floor(prevIndex / cols);
|
|
1309
|
+
if (event.key === (rtl ? ARROW_LEFT : ARROW_RIGHT)) {
|
|
1595
1310
|
if (stop) {
|
|
1596
1311
|
useButton.stopEvent(event);
|
|
1597
1312
|
}
|
|
1598
1313
|
if (prevIndex % cols !== cols - 1) {
|
|
1599
|
-
nextIndex = findNonDisabledListIndex(
|
|
1314
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1600
1315
|
startingIndex: prevIndex,
|
|
1601
1316
|
disabledIndices
|
|
1602
1317
|
});
|
|
1603
1318
|
if (loopFocus && isDifferentGridRow(nextIndex, cols, prevRow)) {
|
|
1604
|
-
nextIndex = findNonDisabledListIndex(
|
|
1319
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1605
1320
|
startingIndex: prevIndex - prevIndex % cols - 1,
|
|
1606
1321
|
disabledIndices
|
|
1607
1322
|
});
|
|
1323
|
+
if (onLoop) {
|
|
1324
|
+
nextIndex = onLoop(event, prevIndex, nextIndex);
|
|
1325
|
+
}
|
|
1608
1326
|
}
|
|
1609
1327
|
} else if (loopFocus) {
|
|
1610
|
-
nextIndex = findNonDisabledListIndex(
|
|
1328
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1611
1329
|
startingIndex: prevIndex - prevIndex % cols - 1,
|
|
1612
1330
|
disabledIndices
|
|
1613
1331
|
});
|
|
1332
|
+
if (onLoop) {
|
|
1333
|
+
nextIndex = onLoop(event, prevIndex, nextIndex);
|
|
1334
|
+
}
|
|
1614
1335
|
}
|
|
1615
1336
|
if (isDifferentGridRow(nextIndex, cols, prevRow)) {
|
|
1616
1337
|
nextIndex = prevIndex;
|
|
1617
1338
|
}
|
|
1618
1339
|
}
|
|
1619
|
-
if (event.key === (rtl ?
|
|
1340
|
+
if (event.key === (rtl ? ARROW_RIGHT : ARROW_LEFT)) {
|
|
1620
1341
|
if (stop) {
|
|
1621
1342
|
useButton.stopEvent(event);
|
|
1622
1343
|
}
|
|
1623
1344
|
if (prevIndex % cols !== 0) {
|
|
1624
|
-
nextIndex = findNonDisabledListIndex(
|
|
1345
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1625
1346
|
startingIndex: prevIndex,
|
|
1626
1347
|
decrement: true,
|
|
1627
1348
|
disabledIndices
|
|
1628
1349
|
});
|
|
1629
1350
|
if (loopFocus && isDifferentGridRow(nextIndex, cols, prevRow)) {
|
|
1630
|
-
nextIndex = findNonDisabledListIndex(
|
|
1351
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1631
1352
|
startingIndex: prevIndex + (cols - prevIndex % cols),
|
|
1632
1353
|
decrement: true,
|
|
1633
1354
|
disabledIndices
|
|
1634
1355
|
});
|
|
1356
|
+
if (onLoop) {
|
|
1357
|
+
nextIndex = onLoop(event, prevIndex, nextIndex);
|
|
1358
|
+
}
|
|
1635
1359
|
}
|
|
1636
1360
|
} else if (loopFocus) {
|
|
1637
|
-
nextIndex = findNonDisabledListIndex(
|
|
1361
|
+
nextIndex = findNonDisabledListIndex(list, {
|
|
1638
1362
|
startingIndex: prevIndex + (cols - prevIndex % cols),
|
|
1639
1363
|
decrement: true,
|
|
1640
1364
|
disabledIndices
|
|
1641
1365
|
});
|
|
1366
|
+
if (onLoop) {
|
|
1367
|
+
nextIndex = onLoop(event, prevIndex, nextIndex);
|
|
1368
|
+
}
|
|
1642
1369
|
}
|
|
1643
1370
|
if (isDifferentGridRow(nextIndex, cols, prevRow)) {
|
|
1644
1371
|
nextIndex = prevIndex;
|
|
1645
1372
|
}
|
|
1646
1373
|
}
|
|
1647
|
-
const lastRow =
|
|
1648
|
-
if (isIndexOutOfListBounds(
|
|
1374
|
+
const lastRow = floatingUi_utils.floor(maxIndex / cols) === prevRow;
|
|
1375
|
+
if (isIndexOutOfListBounds(list, nextIndex)) {
|
|
1649
1376
|
if (loopFocus && lastRow) {
|
|
1650
|
-
nextIndex = event.key === (rtl ?
|
|
1377
|
+
nextIndex = event.key === (rtl ? ARROW_RIGHT : ARROW_LEFT) ? maxIndex : findNonDisabledListIndex(list, {
|
|
1651
1378
|
startingIndex: prevIndex - prevIndex % cols - 1,
|
|
1652
1379
|
disabledIndices
|
|
1653
1380
|
});
|
|
1381
|
+
if (onLoop) {
|
|
1382
|
+
nextIndex = onLoop(event, prevIndex, nextIndex);
|
|
1383
|
+
}
|
|
1654
1384
|
} else {
|
|
1655
1385
|
nextIndex = prevIndex;
|
|
1656
1386
|
}
|
|
@@ -1658,80 +1388,12 @@ function getGridNavigatedIndex(listRef, {
|
|
|
1658
1388
|
}
|
|
1659
1389
|
return nextIndex;
|
|
1660
1390
|
}
|
|
1661
|
-
|
|
1662
|
-
/** For each cell index, gets the item index that occupies that cell */
|
|
1663
|
-
function createGridCellMap(sizes, cols, dense) {
|
|
1664
|
-
const cellMap = [];
|
|
1665
|
-
let startIndex = 0;
|
|
1666
|
-
sizes.forEach(({
|
|
1667
|
-
width,
|
|
1668
|
-
height
|
|
1669
|
-
}, index) => {
|
|
1670
|
-
if (width > cols) {
|
|
1671
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
1672
|
-
throw new Error(`[Floating UI]: Invalid grid - item width at index ${index} is greater than grid columns`);
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
let itemPlaced = false;
|
|
1676
|
-
while (!itemPlaced) {
|
|
1677
|
-
const targetCells = [];
|
|
1678
|
-
for (let i = 0; i < width; i += 1) {
|
|
1679
|
-
for (let j = 0; j < height; j += 1) {
|
|
1680
|
-
targetCells.push(startIndex + i + j * cols);
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
if (startIndex % cols + width <= cols && targetCells.every(cell => cellMap[cell] == null)) {
|
|
1684
|
-
targetCells.forEach(cell => {
|
|
1685
|
-
cellMap[cell] = index;
|
|
1686
|
-
});
|
|
1687
|
-
itemPlaced = true;
|
|
1688
|
-
} else {
|
|
1689
|
-
startIndex += 1;
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
});
|
|
1693
|
-
|
|
1694
|
-
// convert into a non-sparse array
|
|
1695
|
-
return [...cellMap];
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
/** Gets cell index of an item's corner or -1 when index is -1. */
|
|
1699
|
-
function getGridCellIndexOfCorner(index, sizes, cellMap, cols, corner) {
|
|
1700
|
-
if (index === -1) {
|
|
1701
|
-
return -1;
|
|
1702
|
-
}
|
|
1703
|
-
const firstCellIndex = cellMap.indexOf(index);
|
|
1704
|
-
const sizeItem = sizes[index];
|
|
1705
|
-
switch (corner) {
|
|
1706
|
-
case 'tl':
|
|
1707
|
-
return firstCellIndex;
|
|
1708
|
-
case 'tr':
|
|
1709
|
-
if (!sizeItem) {
|
|
1710
|
-
return firstCellIndex;
|
|
1711
|
-
}
|
|
1712
|
-
return firstCellIndex + sizeItem.width - 1;
|
|
1713
|
-
case 'bl':
|
|
1714
|
-
if (!sizeItem) {
|
|
1715
|
-
return firstCellIndex;
|
|
1716
|
-
}
|
|
1717
|
-
return firstCellIndex + (sizeItem.height - 1) * cols;
|
|
1718
|
-
case 'br':
|
|
1719
|
-
return cellMap.lastIndexOf(index);
|
|
1720
|
-
default:
|
|
1721
|
-
return -1;
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
/** Gets all cell indices that correspond to the specified indices */
|
|
1726
|
-
function getGridCellIndices(indices, cellMap) {
|
|
1727
|
-
return cellMap.flatMap((index, cellIndex) => indices.includes(index) ? [cellIndex] : []);
|
|
1728
|
-
}
|
|
1729
|
-
function isListIndexDisabled(listRef, index, disabledIndices) {
|
|
1391
|
+
function isListIndexDisabled(list, index, disabledIndices) {
|
|
1730
1392
|
const isExplicitlyDisabled = typeof disabledIndices === 'function' ? disabledIndices(index) : disabledIndices?.includes(index) ?? false;
|
|
1731
1393
|
if (isExplicitlyDisabled) {
|
|
1732
1394
|
return true;
|
|
1733
1395
|
}
|
|
1734
|
-
const element =
|
|
1396
|
+
const element = list[index];
|
|
1735
1397
|
if (!element) {
|
|
1736
1398
|
return false;
|
|
1737
1399
|
}
|
|
@@ -1740,44 +1402,172 @@ function isListIndexDisabled(listRef, index, disabledIndices) {
|
|
|
1740
1402
|
}
|
|
1741
1403
|
return !disabledIndices && (element.hasAttribute('disabled') || element.getAttribute('aria-disabled') === 'true');
|
|
1742
1404
|
}
|
|
1743
|
-
function
|
|
1744
|
-
return
|
|
1405
|
+
function isHiddenByStyles(styles) {
|
|
1406
|
+
return styles.visibility === 'hidden' || styles.visibility === 'collapse';
|
|
1407
|
+
}
|
|
1408
|
+
function isElementVisible(element, styles = element ? floatingUi_utils_dom.getComputedStyle(element) : null) {
|
|
1409
|
+
if (!element || !element.isConnected || !styles || isHiddenByStyles(styles)) {
|
|
1410
|
+
return false;
|
|
1411
|
+
}
|
|
1412
|
+
if (typeof element.checkVisibility === 'function') {
|
|
1413
|
+
return element.checkVisibility();
|
|
1414
|
+
}
|
|
1415
|
+
return styles.display !== 'none' && styles.display !== 'contents';
|
|
1745
1416
|
}
|
|
1746
1417
|
|
|
1747
|
-
const
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
// determines if the current environment is JSDOM-like.
|
|
1753
|
-
typeof ResizeObserver === 'function' && ResizeObserver.toString().includes('[native code]') ? 'full' : 'none'
|
|
1754
|
-
});
|
|
1755
|
-
function getTabbableIn(container, dir) {
|
|
1756
|
-
const list = index_esm.tabbable(container, getTabbableOptions());
|
|
1757
|
-
const len = list.length;
|
|
1758
|
-
if (len === 0) {
|
|
1759
|
-
return undefined;
|
|
1418
|
+
const CANDIDATE_SELECTOR = 'a[href],button,input,select,textarea,summary,details,iframe,object,embed,[tabindex],[contenteditable]:not([contenteditable="false"]),audio[controls],video[controls]';
|
|
1419
|
+
function getParentElement(element) {
|
|
1420
|
+
const assignedSlot = element.assignedSlot;
|
|
1421
|
+
if (assignedSlot) {
|
|
1422
|
+
return assignedSlot;
|
|
1760
1423
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
const
|
|
1765
|
-
return
|
|
1424
|
+
if (element.parentElement) {
|
|
1425
|
+
return element.parentElement;
|
|
1426
|
+
}
|
|
1427
|
+
const rootNode = element.getRootNode();
|
|
1428
|
+
return floatingUi_utils_dom.isShadowRoot(rootNode) ? rootNode.host : null;
|
|
1766
1429
|
}
|
|
1767
|
-
function
|
|
1768
|
-
|
|
1430
|
+
function getDetailsSummary(details) {
|
|
1431
|
+
for (const child of Array.from(details.children)) {
|
|
1432
|
+
if (floatingUi_utils_dom.getNodeName(child) === 'summary') {
|
|
1433
|
+
return child;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
return null;
|
|
1769
1437
|
}
|
|
1770
|
-
function
|
|
1771
|
-
|
|
1438
|
+
function isWithinOpenDetailsSummary(element, details) {
|
|
1439
|
+
const summary = getDetailsSummary(details);
|
|
1440
|
+
return !!summary && (element === summary || useButton.contains(summary, element));
|
|
1772
1441
|
}
|
|
1773
|
-
function
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1442
|
+
function isFocusableCandidate(element) {
|
|
1443
|
+
const nodeName = element ? floatingUi_utils_dom.getNodeName(element) : '';
|
|
1444
|
+
return element != null && element.matches(CANDIDATE_SELECTOR) && (nodeName !== 'summary' || element.parentElement != null && floatingUi_utils_dom.getNodeName(element.parentElement) === 'details' && getDetailsSummary(element.parentElement) === element) && (nodeName !== 'details' || getDetailsSummary(element) == null) && (nodeName !== 'input' || element.type !== 'hidden');
|
|
1445
|
+
}
|
|
1446
|
+
function isFocusableElement(element) {
|
|
1447
|
+
if (!isFocusableCandidate(element) || !element.isConnected || element.matches(':disabled')) {
|
|
1448
|
+
return false;
|
|
1449
|
+
}
|
|
1450
|
+
for (let current = element; current; current = getParentElement(current)) {
|
|
1451
|
+
const isAncestor = current !== element;
|
|
1452
|
+
const isSlot = floatingUi_utils_dom.getNodeName(current) === 'slot';
|
|
1453
|
+
if (current.hasAttribute('inert')) {
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
if (isAncestor && floatingUi_utils_dom.getNodeName(current) === 'details' && !current.open && !isWithinOpenDetailsSummary(element, current) || current.hasAttribute('hidden') || !isSlot && !isVisibleInTabbableTree(current, isAncestor)) {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
return true;
|
|
1461
|
+
}
|
|
1462
|
+
function isVisibleInTabbableTree(element, isAncestor) {
|
|
1463
|
+
const styles = floatingUi_utils_dom.getComputedStyle(element);
|
|
1464
|
+
if (!isAncestor) {
|
|
1465
|
+
return isElementVisible(element, styles);
|
|
1466
|
+
}
|
|
1467
|
+
return styles.display !== 'none';
|
|
1468
|
+
}
|
|
1469
|
+
function getTabIndex(element) {
|
|
1470
|
+
const tabIndex = element.tabIndex;
|
|
1471
|
+
if (tabIndex < 0) {
|
|
1472
|
+
const nodeName = floatingUi_utils_dom.getNodeName(element);
|
|
1473
|
+
if (nodeName === 'details' || nodeName === 'audio' || nodeName === 'video' || floatingUi_utils_dom.isHTMLElement(element) && element.isContentEditable) {
|
|
1474
|
+
return 0;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
return tabIndex;
|
|
1478
|
+
}
|
|
1479
|
+
function getNamedRadioInput(element) {
|
|
1480
|
+
if (floatingUi_utils_dom.getNodeName(element) !== 'input') {
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
const input = element;
|
|
1484
|
+
return input.type === 'radio' && input.name !== '' ? input : null;
|
|
1485
|
+
}
|
|
1486
|
+
function isTabbableRadio(element, candidates) {
|
|
1487
|
+
const input = getNamedRadioInput(element);
|
|
1488
|
+
if (!input) {
|
|
1489
|
+
return true;
|
|
1490
|
+
}
|
|
1491
|
+
const checkedRadio = candidates.find(candidate => {
|
|
1492
|
+
const radio = getNamedRadioInput(candidate);
|
|
1493
|
+
return radio?.name === input.name && radio.form === input.form && radio.checked;
|
|
1494
|
+
});
|
|
1495
|
+
if (checkedRadio) {
|
|
1496
|
+
return checkedRadio === input;
|
|
1497
|
+
}
|
|
1498
|
+
return candidates.find(candidate => {
|
|
1499
|
+
const radio = getNamedRadioInput(candidate);
|
|
1500
|
+
return radio?.name === input.name && radio.form === input.form;
|
|
1501
|
+
}) === input;
|
|
1502
|
+
}
|
|
1503
|
+
function getComposedChildren(container) {
|
|
1504
|
+
if (floatingUi_utils_dom.isHTMLElement(container) && floatingUi_utils_dom.getNodeName(container) === 'slot') {
|
|
1505
|
+
const assignedElements = container.assignedElements({
|
|
1506
|
+
flatten: true
|
|
1507
|
+
});
|
|
1508
|
+
if (assignedElements.length > 0) {
|
|
1509
|
+
return assignedElements;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (floatingUi_utils_dom.isHTMLElement(container) && container.shadowRoot) {
|
|
1513
|
+
return Array.from(container.shadowRoot.children);
|
|
1514
|
+
}
|
|
1515
|
+
return Array.from(container.children);
|
|
1516
|
+
}
|
|
1517
|
+
function appendCandidates(container, list) {
|
|
1518
|
+
getComposedChildren(container).forEach(child => {
|
|
1519
|
+
if (isFocusableCandidate(child)) {
|
|
1520
|
+
list.push(child);
|
|
1521
|
+
}
|
|
1522
|
+
appendCandidates(child, list);
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
function appendMatchingElements(container, selector, list) {
|
|
1526
|
+
getComposedChildren(container).forEach(child => {
|
|
1527
|
+
if (floatingUi_utils_dom.isHTMLElement(child) && child.matches(selector)) {
|
|
1528
|
+
list.push(child);
|
|
1529
|
+
}
|
|
1530
|
+
appendMatchingElements(child, selector, list);
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
function isTabbable(element) {
|
|
1534
|
+
return isFocusableElement(element) && getTabIndex(element) >= 0;
|
|
1535
|
+
}
|
|
1536
|
+
function focusable(container) {
|
|
1537
|
+
const candidates = [];
|
|
1538
|
+
appendCandidates(container, candidates);
|
|
1539
|
+
return candidates.filter(isFocusableElement);
|
|
1540
|
+
}
|
|
1541
|
+
function tabbable(container) {
|
|
1542
|
+
const candidates = focusable(container);
|
|
1543
|
+
return candidates.filter(element => getTabIndex(element) >= 0 && isTabbableRadio(element, candidates));
|
|
1544
|
+
}
|
|
1545
|
+
function getTabbableIn(container, dir) {
|
|
1546
|
+
const list = tabbable(container);
|
|
1547
|
+
const len = list.length;
|
|
1548
|
+
if (len === 0) {
|
|
1549
|
+
return undefined;
|
|
1550
|
+
}
|
|
1551
|
+
const active = useButton.activeElement(useButton.ownerDocument(container));
|
|
1552
|
+
const index = list.indexOf(active);
|
|
1553
|
+
// eslint-disable-next-line no-nested-ternary
|
|
1554
|
+
const nextIndex = index === -1 ? dir === 1 ? 0 : len - 1 : index + dir;
|
|
1555
|
+
return list[nextIndex];
|
|
1556
|
+
}
|
|
1557
|
+
function getNextTabbable(referenceElement) {
|
|
1558
|
+
return getTabbableIn(useButton.ownerDocument(referenceElement).body, 1) || referenceElement;
|
|
1559
|
+
}
|
|
1560
|
+
function getPreviousTabbable(referenceElement) {
|
|
1561
|
+
return getTabbableIn(useButton.ownerDocument(referenceElement).body, -1) || referenceElement;
|
|
1562
|
+
}
|
|
1563
|
+
function getTabbableNearElement(referenceElement, dir) {
|
|
1564
|
+
if (!referenceElement) {
|
|
1565
|
+
return null;
|
|
1566
|
+
}
|
|
1567
|
+
const list = tabbable(useButton.ownerDocument(referenceElement).body);
|
|
1568
|
+
const elementCount = list.length;
|
|
1569
|
+
if (elementCount === 0) {
|
|
1570
|
+
return null;
|
|
1781
1571
|
}
|
|
1782
1572
|
const index = list.indexOf(referenceElement);
|
|
1783
1573
|
if (index === -1) {
|
|
@@ -1798,14 +1588,15 @@ function isOutsideEvent(event, container) {
|
|
|
1798
1588
|
return !relatedTarget || !useButton.contains(containerElement, relatedTarget);
|
|
1799
1589
|
}
|
|
1800
1590
|
function disableFocusInside(container) {
|
|
1801
|
-
const tabbableElements =
|
|
1591
|
+
const tabbableElements = tabbable(container);
|
|
1802
1592
|
tabbableElements.forEach(element => {
|
|
1803
1593
|
element.dataset.tabindex = element.getAttribute('tabindex') || '';
|
|
1804
1594
|
element.setAttribute('tabindex', '-1');
|
|
1805
1595
|
});
|
|
1806
1596
|
}
|
|
1807
1597
|
function enableFocusInside(container) {
|
|
1808
|
-
const elements =
|
|
1598
|
+
const elements = [];
|
|
1599
|
+
appendMatchingElements(container, '[data-tabindex]', elements);
|
|
1809
1600
|
elements.forEach(element => {
|
|
1810
1601
|
const tabindex = element.dataset.tabindex;
|
|
1811
1602
|
delete element.dataset.tabindex;
|
|
@@ -1817,71 +1608,24 @@ function enableFocusInside(container) {
|
|
|
1817
1608
|
});
|
|
1818
1609
|
}
|
|
1819
1610
|
|
|
1820
|
-
|
|
1821
|
-
const PATIENT_CLICK_THRESHOLD = 500;
|
|
1822
|
-
const DISABLED_TRANSITIONS_STYLE = {
|
|
1823
|
-
style: {
|
|
1824
|
-
transition: 'none'
|
|
1825
|
-
}
|
|
1826
|
-
};
|
|
1827
|
-
const CLICK_TRIGGER_IDENTIFIER = 'data-base-ui-click-trigger';
|
|
1828
|
-
const BASE_UI_SWIPE_IGNORE_ATTRIBUTE = 'data-base-ui-swipe-ignore';
|
|
1829
|
-
const BASE_UI_SWIPE_IGNORE_SELECTOR = `[${BASE_UI_SWIPE_IGNORE_ATTRIBUTE}]`;
|
|
1830
|
-
|
|
1831
|
-
/**
|
|
1832
|
-
* Used for dropdowns that usually strictly prefer top/bottom placements and
|
|
1833
|
-
* use `var(--available-height)` to limit their height.
|
|
1834
|
-
*/
|
|
1835
|
-
const DROPDOWN_COLLISION_AVOIDANCE = {
|
|
1836
|
-
fallbackAxisSide: 'none'
|
|
1837
|
-
};
|
|
1838
|
-
|
|
1839
|
-
/**
|
|
1840
|
-
* Used by regular popups that usually aren't scrollable and are allowed to
|
|
1841
|
-
* freely flip to any axis of placement.
|
|
1842
|
-
*/
|
|
1843
|
-
const POPUP_COLLISION_AVOIDANCE = {
|
|
1844
|
-
fallbackAxisSide: 'end'
|
|
1845
|
-
};
|
|
1846
|
-
|
|
1847
|
-
/**
|
|
1848
|
-
* Special visually hidden styles for the aria-owns owner element to ensure owned element
|
|
1849
|
-
* accessibility in iOS/Safari/VoiceControl.
|
|
1850
|
-
* The owner element is an empty span, so most of the common visually hidden styles are not needed.
|
|
1851
|
-
* @see https://github.com/floating-ui/floating-ui/issues/3403
|
|
1852
|
-
*/
|
|
1853
|
-
const ownerVisuallyHidden = {
|
|
1854
|
-
clipPath: 'inset(50%)',
|
|
1855
|
-
position: 'fixed',
|
|
1856
|
-
top: 0,
|
|
1857
|
-
left: 0
|
|
1858
|
-
};
|
|
1611
|
+
/* eslint-disable @typescript-eslint/no-loop-func */
|
|
1859
1612
|
|
|
1860
|
-
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1613
|
+
function getNodeChildren(nodes, id, onlyOpenChildren = true) {
|
|
1614
|
+
const directChildren = nodes.filter(node => node.parentId === id);
|
|
1615
|
+
return directChildren.flatMap(child => [...(!onlyOpenChildren || child.context?.open ? [child] : []), ...getNodeChildren(nodes, child.id, onlyOpenChildren)]);
|
|
1616
|
+
}
|
|
1617
|
+
function getNodeAncestors(nodes, id) {
|
|
1618
|
+
let allAncestors = [];
|
|
1619
|
+
let currentParentId = nodes.find(node => node.id === id)?.parentId;
|
|
1620
|
+
while (currentParentId) {
|
|
1621
|
+
const currentNode = nodes.find(node => node.id === currentParentId);
|
|
1622
|
+
currentParentId = currentNode?.parentId;
|
|
1623
|
+
if (currentNode) {
|
|
1624
|
+
allAncestors = allAncestors.concat(currentNode);
|
|
1868
1625
|
}
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
// Role is only for VoiceOver
|
|
1873
|
-
role
|
|
1874
|
-
};
|
|
1875
|
-
return /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
1876
|
-
...props,
|
|
1877
|
-
ref: ref,
|
|
1878
|
-
style: useButton.visuallyHidden,
|
|
1879
|
-
"aria-hidden": role ? undefined : true,
|
|
1880
|
-
...restProps,
|
|
1881
|
-
"data-base-ui-focus-guard": ""
|
|
1882
|
-
});
|
|
1883
|
-
});
|
|
1884
|
-
if (process.env.NODE_ENV !== "production") FocusGuard.displayName = "FocusGuard";
|
|
1626
|
+
}
|
|
1627
|
+
return allAncestors;
|
|
1628
|
+
}
|
|
1885
1629
|
|
|
1886
1630
|
function createAttribute(name) {
|
|
1887
1631
|
return `data-base-ui-${name}`;
|
|
@@ -1891,20 +1635,30 @@ let rafId = 0;
|
|
|
1891
1635
|
function enqueueFocus(el, options = {}) {
|
|
1892
1636
|
const {
|
|
1893
1637
|
preventScroll = false,
|
|
1894
|
-
|
|
1895
|
-
|
|
1638
|
+
sync = false,
|
|
1639
|
+
shouldFocus
|
|
1896
1640
|
} = options;
|
|
1897
|
-
|
|
1898
|
-
|
|
1641
|
+
cancelAnimationFrame(rafId);
|
|
1642
|
+
function exec() {
|
|
1643
|
+
if (shouldFocus && !shouldFocus()) {
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
el?.focus({
|
|
1647
|
+
preventScroll
|
|
1648
|
+
});
|
|
1899
1649
|
}
|
|
1900
|
-
const exec = () => el?.focus({
|
|
1901
|
-
preventScroll
|
|
1902
|
-
});
|
|
1903
1650
|
if (sync) {
|
|
1904
1651
|
exec();
|
|
1905
|
-
|
|
1906
|
-
rafId = requestAnimationFrame(exec);
|
|
1652
|
+
return useRenderElement.NOOP;
|
|
1907
1653
|
}
|
|
1654
|
+
const currentRafId = requestAnimationFrame(exec);
|
|
1655
|
+
rafId = currentRafId;
|
|
1656
|
+
return () => {
|
|
1657
|
+
if (rafId === currentRafId) {
|
|
1658
|
+
cancelAnimationFrame(currentRafId);
|
|
1659
|
+
rafId = 0;
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1908
1662
|
}
|
|
1909
1663
|
|
|
1910
1664
|
// Modified to add conditional `aria-hidden` support:
|
|
@@ -1971,17 +1725,18 @@ const collectOutsideElements = (root, keepElements, stopElements) => {
|
|
|
1971
1725
|
return outside;
|
|
1972
1726
|
};
|
|
1973
1727
|
function applyAttributeToOthers(uncorrectedAvoidElements, body, ariaHidden, inert, {
|
|
1974
|
-
mark = true
|
|
1975
|
-
markerIgnoreElements = []
|
|
1728
|
+
mark = true
|
|
1976
1729
|
}) {
|
|
1977
|
-
|
|
1978
|
-
|
|
1730
|
+
let controlAttribute = null;
|
|
1731
|
+
if (inert) {
|
|
1732
|
+
controlAttribute = 'inert';
|
|
1733
|
+
} else if (ariaHidden) {
|
|
1734
|
+
controlAttribute = 'aria-hidden';
|
|
1735
|
+
}
|
|
1979
1736
|
let counterMap = null;
|
|
1980
1737
|
let uncontrolledElementsSet = null;
|
|
1981
1738
|
const avoidElements = correctElements(body, uncorrectedAvoidElements);
|
|
1982
|
-
const
|
|
1983
|
-
const markerIgnoreSet = new Set(markerIgnoreTargets);
|
|
1984
|
-
const markerTargets = mark ? collectOutsideElements(body, buildKeepSet(avoidElements), new Set(avoidElements)).filter(target => !markerIgnoreSet.has(target)) : [];
|
|
1739
|
+
const markerTargets = mark ? collectOutsideElements(body, buildKeepSet(avoidElements), new Set(avoidElements)) : [];
|
|
1985
1740
|
const hiddenElements = [];
|
|
1986
1741
|
const markedElements = [];
|
|
1987
1742
|
if (controlAttribute) {
|
|
@@ -2054,16 +1809,54 @@ function markOthers(avoidElements, options = {}) {
|
|
|
2054
1809
|
const {
|
|
2055
1810
|
ariaHidden = false,
|
|
2056
1811
|
inert = false,
|
|
2057
|
-
mark = true
|
|
2058
|
-
markerIgnoreElements = []
|
|
1812
|
+
mark = true
|
|
2059
1813
|
} = options;
|
|
2060
1814
|
const body = useButton.ownerDocument(avoidElements[0]).body;
|
|
2061
1815
|
return applyAttributeToOthers(avoidElements, body, ariaHidden, inert, {
|
|
2062
|
-
mark
|
|
2063
|
-
markerIgnoreElements
|
|
1816
|
+
mark
|
|
2064
1817
|
});
|
|
2065
1818
|
}
|
|
2066
1819
|
|
|
1820
|
+
const TYPEAHEAD_RESET_MS = 500;
|
|
1821
|
+
const PATIENT_CLICK_THRESHOLD = 500;
|
|
1822
|
+
const DISABLED_TRANSITIONS_STYLE = {
|
|
1823
|
+
style: {
|
|
1824
|
+
transition: 'none'
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
const CLICK_TRIGGER_IDENTIFIER = 'data-base-ui-click-trigger';
|
|
1828
|
+
const BASE_UI_SWIPE_IGNORE_ATTRIBUTE = 'data-base-ui-swipe-ignore';
|
|
1829
|
+
const BASE_UI_SWIPE_IGNORE_SELECTOR = `[${BASE_UI_SWIPE_IGNORE_ATTRIBUTE}]`;
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* Used for dropdowns that usually strictly prefer top/bottom placements and
|
|
1833
|
+
* use `var(--available-height)` to limit their height.
|
|
1834
|
+
*/
|
|
1835
|
+
const DROPDOWN_COLLISION_AVOIDANCE = {
|
|
1836
|
+
fallbackAxisSide: 'none'
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* Used by regular popups that usually aren't scrollable and are allowed to
|
|
1841
|
+
* freely flip to any axis of placement.
|
|
1842
|
+
*/
|
|
1843
|
+
const POPUP_COLLISION_AVOIDANCE = {
|
|
1844
|
+
fallbackAxisSide: 'end'
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
/**
|
|
1848
|
+
* Special visually hidden styles for the aria-owns owner element to ensure owned element
|
|
1849
|
+
* accessibility in iOS/Safari/VoiceControl.
|
|
1850
|
+
* The owner element is an empty span, so most of the common visually hidden styles are not needed.
|
|
1851
|
+
* @see https://github.com/floating-ui/floating-ui/issues/3403
|
|
1852
|
+
*/
|
|
1853
|
+
const ownerVisuallyHidden = {
|
|
1854
|
+
clipPath: 'inset(50%)',
|
|
1855
|
+
position: 'fixed',
|
|
1856
|
+
top: 0,
|
|
1857
|
+
left: 0
|
|
1858
|
+
};
|
|
1859
|
+
|
|
2067
1860
|
const PortalContext = /*#__PURE__*/React__namespace.createContext(null);
|
|
2068
1861
|
if (process.env.NODE_ENV !== "production") PortalContext.displayName = "PortalContext";
|
|
2069
1862
|
const usePortalContext = () => React__namespace.useContext(PortalContext);
|
|
@@ -2147,10 +1940,11 @@ function useFloatingPortalNode(props = {}) {
|
|
|
2147
1940
|
*/
|
|
2148
1941
|
const FloatingPortal = /*#__PURE__*/React__namespace.forwardRef(function FloatingPortal(componentProps, forwardedRef) {
|
|
2149
1942
|
const {
|
|
1943
|
+
render,
|
|
1944
|
+
className,
|
|
1945
|
+
style,
|
|
2150
1946
|
children,
|
|
2151
1947
|
container,
|
|
2152
|
-
className,
|
|
2153
|
-
render,
|
|
2154
1948
|
renderGuards,
|
|
2155
1949
|
...elementProps
|
|
2156
1950
|
} = componentProps;
|
|
@@ -2168,6 +1962,7 @@ const FloatingPortal = /*#__PURE__*/React__namespace.forwardRef(function Floatin
|
|
|
2168
1962
|
const beforeInsideRef = React__namespace.useRef(null);
|
|
2169
1963
|
const afterInsideRef = React__namespace.useRef(null);
|
|
2170
1964
|
const [focusManagerState, setFocusManagerState] = React__namespace.useState(null);
|
|
1965
|
+
const focusInsideDisabledRef = React__namespace.useRef(false);
|
|
2171
1966
|
const modal = focusManagerState?.modal;
|
|
2172
1967
|
const open = focusManagerState?.open;
|
|
2173
1968
|
const shouldRenderGuards = typeof renderGuards === 'boolean' ? renderGuards : !!focusManagerState && !focusManagerState.modal && focusManagerState.open && !!portalNode;
|
|
@@ -2183,26 +1978,30 @@ const FloatingPortal = /*#__PURE__*/React__namespace.forwardRef(function Floatin
|
|
|
2183
1978
|
// element outside or using the mouse.
|
|
2184
1979
|
function onFocus(event) {
|
|
2185
1980
|
if (portalNode && event.relatedTarget && isOutsideEvent(event)) {
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
1981
|
+
if (event.type === 'focusin') {
|
|
1982
|
+
if (focusInsideDisabledRef.current) {
|
|
1983
|
+
enableFocusInside(portalNode);
|
|
1984
|
+
focusInsideDisabledRef.current = false;
|
|
1985
|
+
}
|
|
1986
|
+
} else {
|
|
1987
|
+
disableFocusInside(portalNode);
|
|
1988
|
+
focusInsideDisabledRef.current = true;
|
|
1989
|
+
}
|
|
2189
1990
|
}
|
|
2190
1991
|
}
|
|
2191
1992
|
|
|
2192
1993
|
// Listen to the event on the capture phase so they run before the focus
|
|
2193
1994
|
// trap elements onFocus prop is called.
|
|
2194
|
-
|
|
2195
|
-
portalNode.addEventListener('focusout', onFocus, true);
|
|
2196
|
-
return () => {
|
|
2197
|
-
portalNode.removeEventListener('focusin', onFocus, true);
|
|
2198
|
-
portalNode.removeEventListener('focusout', onFocus, true);
|
|
2199
|
-
};
|
|
1995
|
+
return mergeCleanups(useButton.addEventListener(portalNode, 'focusin', onFocus, true), useButton.addEventListener(portalNode, 'focusout', onFocus, true));
|
|
2200
1996
|
}, [portalNode, modal]);
|
|
2201
|
-
|
|
2202
|
-
if (!portalNode || open) {
|
|
1997
|
+
useButton.useIsoLayoutEffect(() => {
|
|
1998
|
+
if (!portalNode || open !== true || !focusInsideDisabledRef.current) {
|
|
2203
1999
|
return;
|
|
2204
2000
|
}
|
|
2001
|
+
|
|
2002
|
+
// Restore tabbability before the focus manager's queued focus-on-open step runs.
|
|
2205
2003
|
enableFocusInside(portalNode);
|
|
2004
|
+
focusInsideDisabledRef.current = false;
|
|
2206
2005
|
}, [open, portalNode]);
|
|
2207
2006
|
const portalContextValue = React__namespace.useMemo(() => ({
|
|
2208
2007
|
beforeOutsideRef,
|
|
@@ -2374,7 +2173,7 @@ function FloatingTree(props) {
|
|
|
2374
2173
|
}
|
|
2375
2174
|
|
|
2376
2175
|
function getEventType(event, lastInteractionType) {
|
|
2377
|
-
const win = floatingUi_utils_dom.getWindow(event
|
|
2176
|
+
const win = floatingUi_utils_dom.getWindow(useButton.getTarget(event));
|
|
2378
2177
|
if (event instanceof win.KeyboardEvent) {
|
|
2379
2178
|
return 'keyboard';
|
|
2380
2179
|
}
|
|
@@ -2419,35 +2218,30 @@ function getFirstTabbableElement(container) {
|
|
|
2419
2218
|
if (!container) {
|
|
2420
2219
|
return null;
|
|
2421
2220
|
}
|
|
2422
|
-
|
|
2423
|
-
if (index_esm.isTabbable(container, tabbableOptions)) {
|
|
2221
|
+
if (isTabbable(container)) {
|
|
2424
2222
|
return container;
|
|
2425
2223
|
}
|
|
2426
|
-
return
|
|
2224
|
+
return tabbable(container)[0] || container;
|
|
2427
2225
|
}
|
|
2428
|
-
function
|
|
2429
|
-
if (
|
|
2430
|
-
return
|
|
2431
|
-
}
|
|
2432
|
-
if (typeof element.checkVisibility === 'function') {
|
|
2433
|
-
return element.checkVisibility();
|
|
2226
|
+
function handleTabIndex(floatingFocusElement) {
|
|
2227
|
+
if (floatingFocusElement.hasAttribute('tabindex') && !floatingFocusElement.hasAttribute('data-tabindex')) {
|
|
2228
|
+
return;
|
|
2434
2229
|
}
|
|
2435
|
-
|
|
2436
|
-
}
|
|
2437
|
-
function handleTabIndex(floatingFocusElement, orderRef) {
|
|
2438
|
-
if (!orderRef.current.includes('floating') && !floatingFocusElement.getAttribute('role')?.includes('dialog')) {
|
|
2230
|
+
if (!floatingFocusElement.getAttribute('role')?.includes('dialog')) {
|
|
2439
2231
|
return;
|
|
2440
2232
|
}
|
|
2441
|
-
const
|
|
2442
|
-
const focusableElements = index_esm.focusable(floatingFocusElement, options);
|
|
2233
|
+
const focusableElements = focusable(floatingFocusElement);
|
|
2443
2234
|
const tabbableContent = focusableElements.filter(element => {
|
|
2444
2235
|
const dataTabIndex = element.getAttribute('data-tabindex') || '';
|
|
2445
|
-
return
|
|
2236
|
+
return isTabbable(element) || element.hasAttribute('data-tabindex') && !dataTabIndex.startsWith('-');
|
|
2446
2237
|
});
|
|
2447
2238
|
const tabIndex = floatingFocusElement.getAttribute('tabindex');
|
|
2448
|
-
if (
|
|
2239
|
+
if (tabbableContent.length === 0) {
|
|
2449
2240
|
if (tabIndex !== '0') {
|
|
2450
2241
|
floatingFocusElement.setAttribute('tabindex', '0');
|
|
2242
|
+
// Mark our own write so the externally-managed early-return above doesn't
|
|
2243
|
+
// mistake it for a user-authored `tabindex` and freeze management.
|
|
2244
|
+
floatingFocusElement.setAttribute('data-tabindex', '0');
|
|
2451
2245
|
}
|
|
2452
2246
|
} else if (tabIndex !== '-1' || floatingFocusElement.hasAttribute('data-tabindex') && floatingFocusElement.getAttribute('data-tabindex') !== '-1') {
|
|
2453
2247
|
floatingFocusElement.setAttribute('tabindex', '-1');
|
|
@@ -2491,17 +2285,17 @@ function FloatingFocusManager(props) {
|
|
|
2491
2285
|
// aria-hidden should be applied to all nodes still. Further, the visually
|
|
2492
2286
|
// hidden dismiss button should only appear at the end of the list, not the
|
|
2493
2287
|
// start.
|
|
2494
|
-
const isUntrappedTypeableCombobox =
|
|
2495
|
-
const orderRef = React__namespace.useRef(['content']);
|
|
2288
|
+
const isUntrappedTypeableCombobox = isTypeableCombobox(domReference) && ignoreInitialFocus;
|
|
2496
2289
|
const initialFocusRef = useButton.useValueAsRef(initialFocus);
|
|
2497
2290
|
const returnFocusRef = useButton.useValueAsRef(returnFocus);
|
|
2498
2291
|
const openInteractionTypeRef = useButton.useValueAsRef(openInteractionType);
|
|
2292
|
+
const openRef = useButton.useValueAsRef(open);
|
|
2499
2293
|
const tree = useFloatingTree(externalTree);
|
|
2500
2294
|
const portalContext = usePortalContext();
|
|
2501
2295
|
const preventReturnFocusRef = React__namespace.useRef(false);
|
|
2502
2296
|
const isPointerDownRef = React__namespace.useRef(false);
|
|
2503
2297
|
const pointerDownOutsideRef = React__namespace.useRef(false);
|
|
2504
|
-
const
|
|
2298
|
+
const lastFocusedTabbableRef = React__namespace.useRef(null);
|
|
2505
2299
|
const closeTypeRef = React__namespace.useRef('');
|
|
2506
2300
|
const lastInteractionTypeRef = React__namespace.useRef('');
|
|
2507
2301
|
const beforeGuardRef = React__namespace.useRef(null);
|
|
@@ -2512,15 +2306,11 @@ function FloatingFocusManager(props) {
|
|
|
2512
2306
|
const pointerDownTimeout = useButton.useTimeout();
|
|
2513
2307
|
const restoreFocusFrame = useButton.useAnimationFrame();
|
|
2514
2308
|
const isInsidePortal = portalContext != null;
|
|
2515
|
-
const floatingFocusElement =
|
|
2309
|
+
const floatingFocusElement = getFloatingFocusElement(floating);
|
|
2516
2310
|
const getTabbableContent = useButton.useStableCallback((container = floatingFocusElement) => {
|
|
2517
|
-
return container ?
|
|
2311
|
+
return container ? tabbable(container) : [];
|
|
2518
2312
|
});
|
|
2519
2313
|
const getResolvedInsideElements = useButton.useStableCallback(() => getInsideElements?.().filter(element => element != null) ?? []);
|
|
2520
|
-
const getTabbableElements = useButton.useStableCallback(container => {
|
|
2521
|
-
const content = getTabbableContent(container);
|
|
2522
|
-
return orderRef.current.map(() => content).filter(Boolean).flat();
|
|
2523
|
-
});
|
|
2524
2314
|
|
|
2525
2315
|
// Prevent Tab from escaping the modal when there are no tabbable elements.
|
|
2526
2316
|
React__namespace.useEffect(() => {
|
|
@@ -2536,11 +2326,8 @@ function FloatingFocusManager(props) {
|
|
|
2536
2326
|
}
|
|
2537
2327
|
}
|
|
2538
2328
|
const doc = useButton.ownerDocument(floatingFocusElement);
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
doc.removeEventListener('keydown', onKeyDown);
|
|
2542
|
-
};
|
|
2543
|
-
}, [disabled, domReference, floatingFocusElement, modal, orderRef, isUntrappedTypeableCombobox, getTabbableContent, getTabbableElements]);
|
|
2329
|
+
return useButton.addEventListener(doc, 'keydown', onKeyDown);
|
|
2330
|
+
}, [disabled, floatingFocusElement, modal, isUntrappedTypeableCombobox, getTabbableContent]);
|
|
2544
2331
|
|
|
2545
2332
|
// Track pointer/keyboard interactions to disambiguate focus and outside presses.
|
|
2546
2333
|
React__namespace.useEffect(() => {
|
|
@@ -2559,22 +2346,21 @@ function FloatingFocusManager(props) {
|
|
|
2559
2346
|
lastInteractionTypeRef.current = event.pointerType || 'keyboard';
|
|
2560
2347
|
if (target?.closest(`[${CLICK_TRIGGER_IDENTIFIER}]`)) {
|
|
2561
2348
|
isPointerDownRef.current = true;
|
|
2349
|
+
// Reset on the next tick so a single click on a click-trigger doesn't
|
|
2350
|
+
// permanently suppress focus-out closing for the lifetime of the instance.
|
|
2351
|
+
pointerDownTimeout.start(0, () => {
|
|
2352
|
+
isPointerDownRef.current = false;
|
|
2353
|
+
});
|
|
2562
2354
|
}
|
|
2563
2355
|
}
|
|
2564
2356
|
function onKeyDown() {
|
|
2565
2357
|
lastInteractionTypeRef.current = 'keyboard';
|
|
2566
2358
|
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
doc.removeEventListener('pointerdown', onPointerDown, true);
|
|
2573
|
-
doc.removeEventListener('pointerup', clearPointerDownOutside, true);
|
|
2574
|
-
doc.removeEventListener('pointercancel', clearPointerDownOutside, true);
|
|
2575
|
-
doc.removeEventListener('keydown', onKeyDown, true);
|
|
2576
|
-
};
|
|
2577
|
-
}, [disabled, floating, domReference, floatingFocusElement, open, portalContext, getResolvedInsideElements]);
|
|
2359
|
+
return mergeCleanups(useButton.addEventListener(doc, 'pointerdown', onPointerDown, true), useButton.addEventListener(doc, 'pointerup', clearPointerDownOutside, true), useButton.addEventListener(doc, 'pointercancel', clearPointerDownOutside, true), useButton.addEventListener(doc, 'keydown', onKeyDown, true),
|
|
2360
|
+
// Avoid a stale `true` leaking into the next open (e.g. keep-mounted popups)
|
|
2361
|
+
// if the popup dismissed between pointerdown and pointerup.
|
|
2362
|
+
clearPointerDownOutside);
|
|
2363
|
+
}, [disabled, floating, domReference, floatingFocusElement, open, portalContext, pointerDownTimeout, getResolvedInsideElements]);
|
|
2578
2364
|
|
|
2579
2365
|
// Close on focus out and restore focus within the floating tree when needed.
|
|
2580
2366
|
React__namespace.useEffect(() => {
|
|
@@ -2592,29 +2378,34 @@ function FloatingFocusManager(props) {
|
|
|
2592
2378
|
}
|
|
2593
2379
|
function handleFocusIn(event) {
|
|
2594
2380
|
const target = useButton.getTarget(event);
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
if (tabbableIndex !== -1) {
|
|
2598
|
-
tabbableIndexRef.current = tabbableIndex;
|
|
2381
|
+
if (isTabbable(target)) {
|
|
2382
|
+
lastFocusedTabbableRef.current = target;
|
|
2599
2383
|
}
|
|
2600
2384
|
}
|
|
2601
2385
|
function handleFocusOutside(event) {
|
|
2602
2386
|
const relatedTarget = event.relatedTarget;
|
|
2603
2387
|
const currentTarget = event.currentTarget;
|
|
2604
2388
|
const target = useButton.getTarget(event);
|
|
2389
|
+
|
|
2390
|
+
// When focus is lost to the body (e.g. on a backdrop press), record the element that
|
|
2391
|
+
// had focus so a confirmation dialog opened while the body is focused can return focus
|
|
2392
|
+
// to it. Scoped to `modal` to avoid non-modal popups polluting the shared stack.
|
|
2393
|
+
if (modal && relatedTarget == null && target != null && useButton.contains(floating, target)) {
|
|
2394
|
+
addPreviouslyFocusedElement(target);
|
|
2395
|
+
}
|
|
2605
2396
|
queueMicrotask(() => {
|
|
2606
2397
|
const nodeId = getNodeId();
|
|
2607
2398
|
const triggers = store.context.triggerElements;
|
|
2608
2399
|
const insideElements = getResolvedInsideElements();
|
|
2609
2400
|
const isRelatedFocusGuard = relatedTarget?.hasAttribute(createAttribute('focus-guard')) && [beforeGuardRef.current, afterGuardRef.current, portalContext?.beforeInsideRef.current, portalContext?.afterInsideRef.current, portalContext?.beforeOutsideRef.current, portalContext?.afterOutsideRef.current, useButton.resolveRef(previousFocusableElement), useButton.resolveRef(nextFocusableElement)].includes(relatedTarget);
|
|
2610
|
-
const movedToUnrelatedNode = !(useButton.contains(domReference, relatedTarget) || useButton.contains(floating, relatedTarget) || useButton.contains(relatedTarget, floating) || useButton.contains(portalContext?.portalNode, relatedTarget) || insideElements.some(element => element === relatedTarget || useButton.contains(element, relatedTarget)) || relatedTarget != null && triggers.hasElement(relatedTarget) || triggers.hasMatchingElement(trigger => useButton.contains(trigger, relatedTarget)) || isRelatedFocusGuard || tree && (getNodeChildren(tree.nodesRef.current, nodeId).find(node => useButton.contains(node.context?.elements.floating, relatedTarget) || useButton.contains(node.context?.elements.domReference, relatedTarget)) || getNodeAncestors(tree.nodesRef.current, nodeId).find(node => [node.context?.elements.floating,
|
|
2401
|
+
const movedToUnrelatedNode = !(useButton.contains(domReference, relatedTarget) || useButton.contains(floating, relatedTarget) || useButton.contains(relatedTarget, floating) || useButton.contains(portalContext?.portalNode, relatedTarget) || insideElements.some(element => element === relatedTarget || useButton.contains(element, relatedTarget)) || relatedTarget != null && triggers.hasElement(relatedTarget) || triggers.hasMatchingElement(trigger => useButton.contains(trigger, relatedTarget)) || isRelatedFocusGuard || tree && (getNodeChildren(tree.nodesRef.current, nodeId).find(node => useButton.contains(node.context?.elements.floating, relatedTarget) || useButton.contains(node.context?.elements.domReference, relatedTarget)) || getNodeAncestors(tree.nodesRef.current, nodeId).find(node => [node.context?.elements.floating, getFloatingFocusElement(node.context?.elements.floating)].includes(relatedTarget) || node.context?.elements.domReference === relatedTarget)));
|
|
2611
2402
|
if (currentTarget === domReference && floatingFocusElement) {
|
|
2612
|
-
handleTabIndex(floatingFocusElement
|
|
2403
|
+
handleTabIndex(floatingFocusElement);
|
|
2613
2404
|
}
|
|
2614
2405
|
|
|
2615
2406
|
// Restore focus to the previous tabbable element index to prevent
|
|
2616
2407
|
// focus from being lost outside the floating tree.
|
|
2617
|
-
if (restoreFocus && currentTarget !== domReference && !
|
|
2408
|
+
if (restoreFocus && currentTarget !== domReference && !isElementVisible(target) && useButton.activeElement(doc) === doc.body) {
|
|
2618
2409
|
// Let `FloatingPortal` effect knows that focus is still inside the
|
|
2619
2410
|
// floating tree.
|
|
2620
2411
|
if (floatingUi_utils_dom.isHTMLElement(floatingFocusElement)) {
|
|
@@ -2632,9 +2423,9 @@ function FloatingFocusManager(props) {
|
|
|
2632
2423
|
return;
|
|
2633
2424
|
}
|
|
2634
2425
|
}
|
|
2635
|
-
const prevTabbableIndex = tabbableIndexRef.current;
|
|
2636
2426
|
const tabbableContent = getTabbableContent();
|
|
2637
|
-
const
|
|
2427
|
+
const prevTabbable = lastFocusedTabbableRef.current;
|
|
2428
|
+
const nodeToFocus = (prevTabbable && tabbableContent.includes(prevTabbable) ? prevTabbable : null) || tabbableContent[tabbableContent.length - 1] || floatingFocusElement;
|
|
2638
2429
|
if (floatingUi_utils_dom.isHTMLElement(nodeToFocus)) {
|
|
2639
2430
|
nodeToFocus.focus();
|
|
2640
2431
|
}
|
|
@@ -2672,38 +2463,11 @@ function FloatingFocusManager(props) {
|
|
|
2672
2463
|
});
|
|
2673
2464
|
}
|
|
2674
2465
|
const domReferenceElement = floatingUi_utils_dom.isHTMLElement(domReference) ? domReference : null;
|
|
2675
|
-
const cleanups = [];
|
|
2676
2466
|
if (!floating && !domReferenceElement) {
|
|
2677
2467
|
return undefined;
|
|
2678
2468
|
}
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
domReferenceElement.addEventListener('pointerdown', handlePointerDown);
|
|
2682
|
-
cleanups.push(() => {
|
|
2683
|
-
domReferenceElement.removeEventListener('focusout', handleFocusOutside);
|
|
2684
|
-
domReferenceElement.removeEventListener('pointerdown', handlePointerDown);
|
|
2685
|
-
});
|
|
2686
|
-
}
|
|
2687
|
-
if (floating) {
|
|
2688
|
-
floating.addEventListener('focusin', handleFocusIn);
|
|
2689
|
-
floating.addEventListener('focusout', handleFocusOutside);
|
|
2690
|
-
if (portalContext) {
|
|
2691
|
-
floating.addEventListener('focusout', markInsideReactTree, true);
|
|
2692
|
-
cleanups.push(() => {
|
|
2693
|
-
floating.removeEventListener('focusout', markInsideReactTree, true);
|
|
2694
|
-
});
|
|
2695
|
-
}
|
|
2696
|
-
cleanups.push(() => {
|
|
2697
|
-
floating.removeEventListener('focusin', handleFocusIn);
|
|
2698
|
-
floating.removeEventListener('focusout', handleFocusOutside);
|
|
2699
|
-
});
|
|
2700
|
-
}
|
|
2701
|
-
return () => {
|
|
2702
|
-
cleanups.forEach(cleanup => {
|
|
2703
|
-
cleanup();
|
|
2704
|
-
});
|
|
2705
|
-
};
|
|
2706
|
-
}, [disabled, domReference, floating, floatingFocusElement, modal, tree, portalContext, store, closeOnFocusOut, restoreFocus, getTabbableContent, isUntrappedTypeableCombobox, getNodeId, orderRef, dataRef, blurTimeout, pointerDownTimeout, restoreFocusFrame, nextFocusableElement, previousFocusableElement, getResolvedInsideElements]);
|
|
2469
|
+
return mergeCleanups(domReferenceElement && useButton.addEventListener(domReferenceElement, 'focusout', handleFocusOutside), domReferenceElement && useButton.addEventListener(domReferenceElement, 'pointerdown', handlePointerDown), floating && useButton.addEventListener(floating, 'focusin', handleFocusIn), floating && useButton.addEventListener(floating, 'focusout', handleFocusOutside), floating && portalContext && useButton.addEventListener(floating, 'focusout', markInsideReactTree, true));
|
|
2470
|
+
}, [disabled, domReference, floating, floatingFocusElement, modal, tree, portalContext, store, closeOnFocusOut, restoreFocus, getTabbableContent, isUntrappedTypeableCombobox, getNodeId, dataRef, blurTimeout, pointerDownTimeout, restoreFocusFrame, nextFocusableElement, previousFocusableElement, getResolvedInsideElements]);
|
|
2707
2471
|
|
|
2708
2472
|
// Hide everything outside the floating tree from assistive tech while open.
|
|
2709
2473
|
React__namespace.useEffect(() => {
|
|
@@ -2714,7 +2478,7 @@ function FloatingFocusManager(props) {
|
|
|
2714
2478
|
// Don't hide portals nested within the parent portal.
|
|
2715
2479
|
const portalNodes = Array.from(portalContext?.portalNode?.querySelectorAll(`[${createAttribute('portal')}]`) || []);
|
|
2716
2480
|
const ancestors = tree ? getNodeAncestors(tree.nodesRef.current, getNodeId()) : [];
|
|
2717
|
-
const rootAncestorComboboxDomReference = ancestors.find(node =>
|
|
2481
|
+
const rootAncestorComboboxDomReference = ancestors.find(node => isTypeableCombobox(node.context?.elements.domReference || null))?.context?.elements.domReference;
|
|
2718
2482
|
const controlInsideElements = [floating, ...portalNodes, beforeGuardRef.current, afterGuardRef.current, portalContext?.beforeOutsideRef.current, portalContext?.afterOutsideRef.current, ...getResolvedInsideElements()];
|
|
2719
2483
|
const insideElements = [...controlInsideElements, rootAncestorComboboxDomReference, useButton.resolveRef(previousFocusableElement), useButton.resolveRef(nextFocusableElement), isUntrappedTypeableCombobox ? domReference : null].filter(x => x != null);
|
|
2720
2484
|
const ariaHiddenCleanup = markOthers(insideElements, {
|
|
@@ -2727,7 +2491,7 @@ function FloatingFocusManager(props) {
|
|
|
2727
2491
|
markerCleanup();
|
|
2728
2492
|
ariaHiddenCleanup();
|
|
2729
2493
|
};
|
|
2730
|
-
}, [open, disabled, domReference, floating, modal,
|
|
2494
|
+
}, [open, disabled, domReference, floating, modal, portalContext, isUntrappedTypeableCombobox, tree, getNodeId, nextFocusableElement, previousFocusableElement, getResolvedInsideElements]);
|
|
2731
2495
|
|
|
2732
2496
|
// Focus the initial element when the floating element opens.
|
|
2733
2497
|
useButton.useIsoLayoutEffect(() => {
|
|
@@ -2739,7 +2503,6 @@ function FloatingFocusManager(props) {
|
|
|
2739
2503
|
|
|
2740
2504
|
// Wait for any layout effect state setters to execute to set `tabIndex`.
|
|
2741
2505
|
queueMicrotask(() => {
|
|
2742
|
-
const focusableElements = getTabbableElements(floatingFocusElement);
|
|
2743
2506
|
const initialFocusValueOrFn = initialFocusRef.current;
|
|
2744
2507
|
const resolvedInitialFocus = typeof initialFocusValueOrFn === 'function' ? initialFocusValueOrFn(openInteractionTypeRef.current || '') : initialFocusValueOrFn;
|
|
2745
2508
|
|
|
@@ -2747,22 +2510,44 @@ function FloatingFocusManager(props) {
|
|
|
2747
2510
|
if (resolvedInitialFocus === undefined || resolvedInitialFocus === false) {
|
|
2748
2511
|
return;
|
|
2749
2512
|
}
|
|
2513
|
+
const focusAlreadyInsideFloatingEl = useButton.contains(floatingFocusElement, previouslyFocusedElement);
|
|
2514
|
+
if (focusAlreadyInsideFloatingEl) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
let focusableElements = null;
|
|
2518
|
+
const getDefaultFocusElement = () => {
|
|
2519
|
+
if (focusableElements == null) {
|
|
2520
|
+
focusableElements = getTabbableContent(floatingFocusElement);
|
|
2521
|
+
}
|
|
2522
|
+
return focusableElements[0] || floatingFocusElement;
|
|
2523
|
+
};
|
|
2750
2524
|
let elToFocus;
|
|
2751
2525
|
if (resolvedInitialFocus === true || resolvedInitialFocus === null) {
|
|
2752
|
-
elToFocus =
|
|
2526
|
+
elToFocus = getDefaultFocusElement();
|
|
2753
2527
|
} else {
|
|
2754
2528
|
elToFocus = useButton.resolveRef(resolvedInitialFocus);
|
|
2755
2529
|
}
|
|
2756
|
-
elToFocus = elToFocus ||
|
|
2757
|
-
const
|
|
2758
|
-
if (focusAlreadyInsideFloatingEl) {
|
|
2759
|
-
return;
|
|
2760
|
-
}
|
|
2530
|
+
elToFocus = elToFocus || getDefaultFocusElement();
|
|
2531
|
+
const hadFocusInside = useButton.contains(floatingFocusElement, useButton.activeElement(doc));
|
|
2761
2532
|
enqueueFocus(elToFocus, {
|
|
2762
|
-
preventScroll: elToFocus === floatingFocusElement
|
|
2533
|
+
preventScroll: elToFocus === floatingFocusElement,
|
|
2534
|
+
shouldFocus() {
|
|
2535
|
+
// This focus is queued on the next animation frame. If the floating element has closed
|
|
2536
|
+
// before it runs — e.g. tabbing out of a kept-mounted popup — don't pull focus back
|
|
2537
|
+
// onto the initial element after it has legitimately moved elsewhere.
|
|
2538
|
+
if (!openRef.current) {
|
|
2539
|
+
return false;
|
|
2540
|
+
}
|
|
2541
|
+
if (hadFocusInside) {
|
|
2542
|
+
return true;
|
|
2543
|
+
}
|
|
2544
|
+
const currentActiveElement = useButton.activeElement(doc);
|
|
2545
|
+
const focusMovedInside = currentActiveElement !== elToFocus && useButton.contains(floatingFocusElement, currentActiveElement);
|
|
2546
|
+
return !focusMovedInside;
|
|
2547
|
+
}
|
|
2763
2548
|
});
|
|
2764
2549
|
});
|
|
2765
|
-
}, [disabled, open, floatingFocusElement,
|
|
2550
|
+
}, [disabled, open, floatingFocusElement, getTabbableContent, initialFocusRef, openInteractionTypeRef, openRef]);
|
|
2766
2551
|
|
|
2767
2552
|
// Track return focus targets and restore focus on unmount/close.
|
|
2768
2553
|
useButton.useIsoLayoutEffect(() => {
|
|
@@ -2770,8 +2555,11 @@ function FloatingFocusManager(props) {
|
|
|
2770
2555
|
return undefined;
|
|
2771
2556
|
}
|
|
2772
2557
|
const doc = useButton.ownerDocument(floatingFocusElement);
|
|
2773
|
-
const
|
|
2774
|
-
|
|
2558
|
+
const elementFocusedBeforeOpen = useButton.activeElement(doc);
|
|
2559
|
+
// Only nullish interaction types represent programmatic opens. The empty
|
|
2560
|
+
// string default is intentionally not treated as programmatic.
|
|
2561
|
+
const preferPreviousFocus = openInteractionTypeRef.current == null;
|
|
2562
|
+
addPreviouslyFocusedElement(elementFocusedBeforeOpen);
|
|
2775
2563
|
|
|
2776
2564
|
// Dismissing via outside press should always ignore `returnFocus` to
|
|
2777
2565
|
// prevent unwanted scrolling.
|
|
@@ -2791,7 +2579,7 @@ function FloatingFocusManager(props) {
|
|
|
2791
2579
|
preventReturnFocusRef.current = false;
|
|
2792
2580
|
} else {
|
|
2793
2581
|
let isPreventScrollSupported = false;
|
|
2794
|
-
|
|
2582
|
+
useButton.ownerDocument(floatingFocusElement).createElement('div').focus({
|
|
2795
2583
|
get preventScroll() {
|
|
2796
2584
|
isPreventScrollSupported = true;
|
|
2797
2585
|
return false;
|
|
@@ -2816,26 +2604,31 @@ function FloatingFocusManager(props) {
|
|
|
2816
2604
|
if (resolvedReturnFocusValue === null) {
|
|
2817
2605
|
resolvedReturnFocusValue = true;
|
|
2818
2606
|
}
|
|
2607
|
+
const referenceReturnElement = domReference?.isConnected ? domReference : null;
|
|
2608
|
+
const previousReturnElement = elementFocusedBeforeOpen?.isConnected && floatingUi_utils_dom.getNodeName(elementFocusedBeforeOpen) !== 'body' ? elementFocusedBeforeOpen : null;
|
|
2609
|
+
let defaultReturnElement = preferPreviousFocus ? previousReturnElement || referenceReturnElement : referenceReturnElement || previousReturnElement;
|
|
2610
|
+
if (!defaultReturnElement) {
|
|
2611
|
+
defaultReturnElement = getPreviouslyFocusedElement() || null;
|
|
2612
|
+
}
|
|
2819
2613
|
if (typeof resolvedReturnFocusValue === 'boolean') {
|
|
2820
|
-
|
|
2821
|
-
return el && el.isConnected ? el : null;
|
|
2614
|
+
return defaultReturnElement;
|
|
2822
2615
|
}
|
|
2823
|
-
|
|
2824
|
-
return useButton.resolveRef(resolvedReturnFocusValue) || fallback || null;
|
|
2616
|
+
return useButton.resolveRef(resolvedReturnFocusValue) || defaultReturnElement || null;
|
|
2825
2617
|
}
|
|
2826
2618
|
return () => {
|
|
2827
2619
|
events.off('openchange', onOpenChangeLocal);
|
|
2828
2620
|
const activeEl = useButton.activeElement(doc);
|
|
2829
2621
|
const insideElements = getResolvedInsideElements();
|
|
2830
2622
|
const isFocusInsideFloatingTree = useButton.contains(floating, activeEl) || insideElements.some(element => element === activeEl || useButton.contains(element, activeEl)) || tree && getNodeChildren(tree.nodesRef.current, getNodeId(), false).some(node => useButton.contains(node.context?.elements.floating, activeEl));
|
|
2623
|
+
|
|
2624
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2625
|
+
const returnFocusValueOrFn = returnFocusRef.current;
|
|
2831
2626
|
const returnElement = getReturnElement();
|
|
2832
2627
|
queueMicrotask(() => {
|
|
2833
2628
|
// This is `returnElement`, if it's tabbable, or its first tabbable child.
|
|
2834
2629
|
const tabbableReturnElement = getFirstTabbableElement(returnElement);
|
|
2835
|
-
const hasExplicitReturnFocus = typeof
|
|
2836
|
-
if (
|
|
2837
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2838
|
-
returnFocusRef.current && !preventReturnFocusRef.current && floatingUi_utils_dom.isHTMLElement(tabbableReturnElement) && (
|
|
2630
|
+
const hasExplicitReturnFocus = typeof returnFocusValueOrFn !== 'boolean';
|
|
2631
|
+
if (returnFocusValueOrFn && !preventReturnFocusRef.current && floatingUi_utils_dom.isHTMLElement(tabbableReturnElement) && (
|
|
2839
2632
|
// If the focus moved somewhere else after mount, avoid returning focus
|
|
2840
2633
|
// since it likely entered a different element which should be
|
|
2841
2634
|
// respected: https://github.com/floating-ui/floating-ui/issues/2607
|
|
@@ -2847,23 +2640,24 @@ function FloatingFocusManager(props) {
|
|
|
2847
2640
|
preventReturnFocusRef.current = false;
|
|
2848
2641
|
});
|
|
2849
2642
|
};
|
|
2850
|
-
}, [disabled, floating, floatingFocusElement, returnFocusRef,
|
|
2643
|
+
}, [disabled, floating, floatingFocusElement, returnFocusRef, openInteractionTypeRef, events, tree, domReference, getNodeId, getResolvedInsideElements]);
|
|
2851
2644
|
|
|
2852
2645
|
// Safari may randomly scroll to the bottom of the page if an input inside a popup has focus
|
|
2853
2646
|
// when the popup unmounts from the DOM.
|
|
2854
2647
|
// By blurring it before the popup unmounts, we can prevent this behavior.
|
|
2855
2648
|
useButton.useIsoLayoutEffect(() => {
|
|
2856
|
-
if (!
|
|
2649
|
+
if (!webkit || open || !floating) {
|
|
2857
2650
|
return;
|
|
2858
2651
|
}
|
|
2859
2652
|
const activeEl = useButton.activeElement(useButton.ownerDocument(floating));
|
|
2860
|
-
if (!floatingUi_utils_dom.isHTMLElement(activeEl) || !
|
|
2653
|
+
if (!floatingUi_utils_dom.isHTMLElement(activeEl) || !isTypeableElement(activeEl)) {
|
|
2861
2654
|
return;
|
|
2862
2655
|
}
|
|
2863
2656
|
if (useButton.contains(floating, activeEl)) {
|
|
2864
2657
|
activeEl.blur();
|
|
2865
2658
|
}
|
|
2866
2659
|
}, [open, floating]);
|
|
2660
|
+
|
|
2867
2661
|
// Synchronize the `context` & `modal` value to the FloatingPortal context.
|
|
2868
2662
|
// It will decide whether or not it needs to render its own guards.
|
|
2869
2663
|
useButton.useIsoLayoutEffect(() => {
|
|
@@ -2887,11 +2681,11 @@ function FloatingFocusManager(props) {
|
|
|
2887
2681
|
if (disabled || !floatingFocusElement) {
|
|
2888
2682
|
return undefined;
|
|
2889
2683
|
}
|
|
2890
|
-
handleTabIndex(floatingFocusElement
|
|
2684
|
+
handleTabIndex(floatingFocusElement);
|
|
2891
2685
|
return () => {
|
|
2892
2686
|
queueMicrotask(clearDisconnectedPreviouslyFocusedElements);
|
|
2893
2687
|
};
|
|
2894
|
-
}, [disabled, floatingFocusElement
|
|
2688
|
+
}, [disabled, floatingFocusElement]);
|
|
2895
2689
|
const shouldRenderGuards = !disabled && (modal ? !isUntrappedTypeableCombobox : true) && (isInsidePortal || modal);
|
|
2896
2690
|
return /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
|
|
2897
2691
|
children: [shouldRenderGuards && /*#__PURE__*/jsxRuntime.jsx(FocusGuard, {
|
|
@@ -2899,7 +2693,7 @@ function FloatingFocusManager(props) {
|
|
|
2899
2693
|
ref: mergedBeforeGuardRef,
|
|
2900
2694
|
onFocus: event => {
|
|
2901
2695
|
if (modal) {
|
|
2902
|
-
const els =
|
|
2696
|
+
const els = getTabbableContent();
|
|
2903
2697
|
enqueueFocus(els[els.length - 1]);
|
|
2904
2698
|
} else if (portalContext?.portalNode) {
|
|
2905
2699
|
preventReturnFocusRef.current = false;
|
|
@@ -2916,7 +2710,7 @@ function FloatingFocusManager(props) {
|
|
|
2916
2710
|
ref: mergedAfterGuardRef,
|
|
2917
2711
|
onFocus: event => {
|
|
2918
2712
|
if (modal) {
|
|
2919
|
-
enqueueFocus(
|
|
2713
|
+
enqueueFocus(getTabbableContent()[0]);
|
|
2920
2714
|
} else if (portalContext?.portalNode) {
|
|
2921
2715
|
if (closeOnFocusOut) {
|
|
2922
2716
|
preventReturnFocusRef.current = true;
|
|
@@ -2938,8 +2732,6 @@ function FloatingFocusManager(props) {
|
|
|
2938
2732
|
* @see https://floating-ui.com/docs/useClick
|
|
2939
2733
|
*/
|
|
2940
2734
|
function useClick(context, props = {}) {
|
|
2941
|
-
const store = 'rootStore' in context ? context.rootStore : context;
|
|
2942
|
-
const dataRef = store.context.dataRef;
|
|
2943
2735
|
const {
|
|
2944
2736
|
enabled = true,
|
|
2945
2737
|
event: eventOption = 'click',
|
|
@@ -2949,97 +2741,105 @@ function useClick(context, props = {}) {
|
|
|
2949
2741
|
touchOpenDelay = 0,
|
|
2950
2742
|
reason = useButton.triggerPress
|
|
2951
2743
|
} = props;
|
|
2744
|
+
const store = 'rootStore' in context ? context.rootStore : context;
|
|
2745
|
+
const dataRef = store.context.dataRef;
|
|
2952
2746
|
const pointerTypeRef = React__namespace.useRef(undefined);
|
|
2953
2747
|
const frame = useButton.useAnimationFrame();
|
|
2954
2748
|
const touchOpenTimeout = useButton.useTimeout();
|
|
2955
|
-
const reference = React__namespace.useMemo(() =>
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
// Ignore all buttons except for the "main" button.
|
|
2965
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
|
2966
|
-
if (event.button !== 0 || eventOption === 'click' || useButton.isMouseLikePointerType(pointerType, true) && ignoreMouse) {
|
|
2967
|
-
return;
|
|
2749
|
+
const reference = React__namespace.useMemo(() => {
|
|
2750
|
+
function setOpenWithTouchDelay(nextOpen, nativeEvent, target, pointerType) {
|
|
2751
|
+
const details = useButton.createChangeEventDetails(reason, nativeEvent, target);
|
|
2752
|
+
if (nextOpen && pointerType === 'touch' && touchOpenDelay > 0) {
|
|
2753
|
+
touchOpenTimeout.start(touchOpenDelay, () => {
|
|
2754
|
+
store.setOpen(true, details);
|
|
2755
|
+
});
|
|
2756
|
+
} else {
|
|
2757
|
+
store.setOpen(nextOpen, details);
|
|
2968
2758
|
}
|
|
2759
|
+
}
|
|
2760
|
+
function getNextOpen(open, currentTarget, isClickLikeOpenEvent) {
|
|
2969
2761
|
const openEvent = dataRef.current.openEvent;
|
|
2970
|
-
const
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
// Animations sometimes won't run on a typeable element if using a rAF.
|
|
2975
|
-
// Focus is always set on these elements. For touch, we may delay opening.
|
|
2976
|
-
if (useButton.isTypeableElement(nativeEvent.target)) {
|
|
2977
|
-
const details = useButton.createChangeEventDetails(reason, nativeEvent, nativeEvent.target);
|
|
2978
|
-
if (nextOpen && pointerType === 'touch' && touchOpenDelay > 0) {
|
|
2979
|
-
touchOpenTimeout.start(touchOpenDelay, () => {
|
|
2980
|
-
store.setOpen(true, details);
|
|
2981
|
-
});
|
|
2982
|
-
} else {
|
|
2983
|
-
store.setOpen(nextOpen, details);
|
|
2984
|
-
}
|
|
2985
|
-
return;
|
|
2762
|
+
const hasClickedOnInactiveTrigger = store.select('domReferenceElement') !== currentTarget;
|
|
2763
|
+
if (open && hasClickedOnInactiveTrigger) {
|
|
2764
|
+
// Moving between triggers should always open the newly active one.
|
|
2765
|
+
return true;
|
|
2986
2766
|
}
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
const eventCurrentTarget = event.currentTarget;
|
|
2991
|
-
|
|
2992
|
-
// Wait until focus is set on the element. This is an alternative to
|
|
2993
|
-
// `event.preventDefault()` to avoid :focus-visible from appearing when using a pointer.
|
|
2994
|
-
frame.request(() => {
|
|
2995
|
-
const details = useButton.createChangeEventDetails(reason, nativeEvent, eventCurrentTarget);
|
|
2996
|
-
if (nextOpen && pointerType === 'touch' && touchOpenDelay > 0) {
|
|
2997
|
-
touchOpenTimeout.start(touchOpenDelay, () => {
|
|
2998
|
-
store.setOpen(true, details);
|
|
2999
|
-
});
|
|
3000
|
-
} else {
|
|
3001
|
-
store.setOpen(nextOpen, details);
|
|
3002
|
-
}
|
|
3003
|
-
});
|
|
3004
|
-
},
|
|
3005
|
-
onClick(event) {
|
|
3006
|
-
if (eventOption === 'mousedown-only') {
|
|
3007
|
-
return;
|
|
2767
|
+
if (!open) {
|
|
2768
|
+
// A closed popup should open on the next press.
|
|
2769
|
+
return true;
|
|
3008
2770
|
}
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
return;
|
|
2771
|
+
if (!toggle) {
|
|
2772
|
+
// Non-toggle mode never closes on a repeated trigger press.
|
|
2773
|
+
return true;
|
|
3013
2774
|
}
|
|
3014
|
-
if (
|
|
3015
|
-
|
|
2775
|
+
if (openEvent && stickIfOpen) {
|
|
2776
|
+
// Preserve hover/focus-opened popups until the matching click-like event closes them.
|
|
2777
|
+
return !isClickLikeOpenEvent(openEvent.type);
|
|
3016
2778
|
}
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
2779
|
+
|
|
2780
|
+
// Otherwise, a repeated click toggles the popup closed.
|
|
2781
|
+
return false;
|
|
2782
|
+
}
|
|
2783
|
+
return {
|
|
2784
|
+
onPointerDown(event) {
|
|
2785
|
+
pointerTypeRef.current = event.pointerType;
|
|
2786
|
+
},
|
|
2787
|
+
onMouseDown(event) {
|
|
2788
|
+
const pointerType = pointerTypeRef.current;
|
|
2789
|
+
const nativeEvent = event.nativeEvent;
|
|
2790
|
+
const open = store.select('open');
|
|
2791
|
+
|
|
2792
|
+
// Ignore all buttons except for the "main" button.
|
|
2793
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
|
2794
|
+
if (event.button !== 0 || eventOption === 'click' || useButton.isMouseLikePointerType(pointerType, true) && ignoreMouse) {
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
const nextOpen = getNextOpen(open, event.currentTarget, openEventType => openEventType === 'click' || openEventType === 'mousedown');
|
|
2798
|
+
|
|
2799
|
+
// Animations sometimes won't run on a typeable element if using a rAF.
|
|
2800
|
+
// Focus is always set on these elements. For touch, we may delay opening.
|
|
2801
|
+
const target = useButton.getTarget(nativeEvent);
|
|
2802
|
+
if (isTypeableElement(target)) {
|
|
2803
|
+
setOpenWithTouchDelay(nextOpen, nativeEvent, target, pointerType);
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// Capture the currentTarget before the rAF.
|
|
2808
|
+
// as React sets it to null after the event handler completes.
|
|
2809
|
+
const eventCurrentTarget = event.currentTarget;
|
|
2810
|
+
|
|
2811
|
+
// Wait until focus is set on the element. This is an alternative to
|
|
2812
|
+
// `event.preventDefault()` to avoid :focus-visible from appearing when using a pointer.
|
|
2813
|
+
frame.request(() => {
|
|
2814
|
+
setOpenWithTouchDelay(nextOpen, nativeEvent, eventCurrentTarget, pointerType);
|
|
3025
2815
|
});
|
|
3026
|
-
}
|
|
3027
|
-
|
|
2816
|
+
},
|
|
2817
|
+
onClick(event) {
|
|
2818
|
+
if (eventOption === 'mousedown-only') {
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2821
|
+
const pointerType = pointerTypeRef.current;
|
|
2822
|
+
if (eventOption === 'mousedown' && pointerType) {
|
|
2823
|
+
pointerTypeRef.current = undefined;
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
if (useButton.isMouseLikePointerType(pointerType, true) && ignoreMouse) {
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2829
|
+
const open = store.select('open');
|
|
2830
|
+
const nextOpen = getNextOpen(open, event.currentTarget, openEventType => openEventType === 'click' || openEventType === 'mousedown' || openEventType === 'keydown' || openEventType === 'keyup');
|
|
2831
|
+
setOpenWithTouchDelay(nextOpen, event.nativeEvent, event.currentTarget, pointerType);
|
|
2832
|
+
},
|
|
2833
|
+
onKeyDown() {
|
|
2834
|
+
pointerTypeRef.current = undefined;
|
|
3028
2835
|
}
|
|
3029
|
-
}
|
|
3030
|
-
|
|
3031
|
-
pointerTypeRef.current = undefined;
|
|
3032
|
-
}
|
|
3033
|
-
}), [dataRef, eventOption, ignoreMouse, store, stickIfOpen, toggle, frame, touchOpenTimeout, touchOpenDelay, reason]);
|
|
2836
|
+
};
|
|
2837
|
+
}, [dataRef, eventOption, ignoreMouse, reason, store, stickIfOpen, toggle, frame, touchOpenTimeout, touchOpenDelay]);
|
|
3034
2838
|
return React__namespace.useMemo(() => enabled ? {
|
|
3035
2839
|
reference
|
|
3036
2840
|
} : useRenderElement.EMPTY_OBJECT, [enabled, reference]);
|
|
3037
2841
|
}
|
|
3038
2842
|
|
|
3039
|
-
const bubbleHandlerKeys = {
|
|
3040
|
-
intentional: 'onClick',
|
|
3041
|
-
sloppy: 'onPointerDown'
|
|
3042
|
-
};
|
|
3043
2843
|
function alwaysFalse() {
|
|
3044
2844
|
return false;
|
|
3045
2845
|
}
|
|
@@ -3055,35 +2855,36 @@ function normalizeProp(normalizable) {
|
|
|
3055
2855
|
* @see https://floating-ui.com/docs/useDismiss
|
|
3056
2856
|
*/
|
|
3057
2857
|
function useDismiss(context, props = {}) {
|
|
3058
|
-
const store = 'rootStore' in context ? context.rootStore : context;
|
|
3059
|
-
const open = store.useState('open');
|
|
3060
|
-
const floatingElement = store.useState('floatingElement');
|
|
3061
|
-
const {
|
|
3062
|
-
dataRef
|
|
3063
|
-
} = store.context;
|
|
3064
2858
|
const {
|
|
3065
2859
|
enabled = true,
|
|
3066
2860
|
escapeKey = true,
|
|
3067
2861
|
outsidePress: outsidePressProp = true,
|
|
3068
2862
|
outsidePressEvent = 'sloppy',
|
|
3069
2863
|
referencePress = alwaysFalse,
|
|
3070
|
-
referencePressEvent = 'sloppy',
|
|
3071
2864
|
bubbles,
|
|
3072
2865
|
externalTree
|
|
3073
2866
|
} = props;
|
|
2867
|
+
const store = 'rootStore' in context ? context.rootStore : context;
|
|
2868
|
+
const open = store.useState('open');
|
|
2869
|
+
const floatingElement = store.useState('floatingElement');
|
|
2870
|
+
const {
|
|
2871
|
+
dataRef
|
|
2872
|
+
} = store.context;
|
|
3074
2873
|
const tree = useFloatingTree(externalTree);
|
|
3075
2874
|
const outsidePressFn = useButton.useStableCallback(typeof outsidePressProp === 'function' ? outsidePressProp : () => false);
|
|
3076
2875
|
const outsidePress = typeof outsidePressProp === 'function' ? outsidePressFn : outsidePressProp;
|
|
3077
2876
|
const outsidePressEnabled = outsidePress !== false;
|
|
3078
2877
|
const getOutsidePressEventProp = useButton.useStableCallback(() => outsidePressEvent);
|
|
3079
|
-
const pressStartedInsideRef = React__namespace.useRef(false);
|
|
3080
|
-
const pressStartPreventedRef = React__namespace.useRef(false);
|
|
3081
|
-
// Ignore only the very next outside click after dragging from inside to outside.
|
|
3082
|
-
const suppressNextOutsideClickRef = React__namespace.useRef(false);
|
|
3083
2878
|
const {
|
|
3084
2879
|
escapeKey: escapeKeyBubbles,
|
|
3085
2880
|
outsidePress: outsidePressBubbles
|
|
3086
2881
|
} = normalizeProp(bubbles);
|
|
2882
|
+
const pressStartedInsideRef = React__namespace.useRef(false);
|
|
2883
|
+
const pressStartPreventedRef = React__namespace.useRef(false);
|
|
2884
|
+
// Ignore only the very next outside click after dragging from inside to outside.
|
|
2885
|
+
const suppressNextOutsideClickRef = React__namespace.useRef(false);
|
|
2886
|
+
const isComposingRef = React__namespace.useRef(false);
|
|
2887
|
+
const currentPointerTypeRef = React__namespace.useRef('');
|
|
3087
2888
|
const touchStateRef = React__namespace.useRef(null);
|
|
3088
2889
|
const cancelDismissOnEndTimeout = useButton.useTimeout();
|
|
3089
2890
|
const clearInsideReactTreeTimeout = useButton.useTimeout();
|
|
@@ -3091,9 +2892,20 @@ function useDismiss(context, props = {}) {
|
|
|
3091
2892
|
clearInsideReactTreeTimeout.clear();
|
|
3092
2893
|
dataRef.current.insideReactTree = false;
|
|
3093
2894
|
});
|
|
3094
|
-
const
|
|
3095
|
-
|
|
3096
|
-
|
|
2895
|
+
const hasBlockingChild = useButton.useStableCallback(bubbleKey => {
|
|
2896
|
+
const nodeId = dataRef.current.floatingContext?.nodeId;
|
|
2897
|
+
const children = tree ? getNodeChildren(tree.nodesRef.current, nodeId) : [];
|
|
2898
|
+
return children.some(child => child.context?.open && !child.context.dataRef.current[bubbleKey]);
|
|
2899
|
+
});
|
|
2900
|
+
const isEventWithinOwnElements = useButton.useStableCallback(event => {
|
|
2901
|
+
return isEventTargetWithin(event, store.select('floatingElement')) || isEventTargetWithin(event, store.select('domReferenceElement'));
|
|
2902
|
+
});
|
|
2903
|
+
const closeOnReferencePress = useButton.useStableCallback(event => {
|
|
2904
|
+
if (!referencePress()) {
|
|
2905
|
+
return;
|
|
2906
|
+
}
|
|
2907
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerPress, event.nativeEvent));
|
|
2908
|
+
});
|
|
3097
2909
|
const closeOnEscapeKeyDown = useButton.useStableCallback(event => {
|
|
3098
2910
|
if (!open || !enabled || !escapeKey || event.key !== 'Escape') {
|
|
3099
2911
|
return;
|
|
@@ -3104,24 +2916,15 @@ function useDismiss(context, props = {}) {
|
|
|
3104
2916
|
if (isComposingRef.current) {
|
|
3105
2917
|
return;
|
|
3106
2918
|
}
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
if (!escapeKeyBubbles) {
|
|
3110
|
-
if (children.length > 0) {
|
|
3111
|
-
let shouldDismiss = true;
|
|
3112
|
-
children.forEach(child => {
|
|
3113
|
-
if (child.context?.open && !child.context.dataRef.current.__escapeKeyBubbles) {
|
|
3114
|
-
shouldDismiss = false;
|
|
3115
|
-
}
|
|
3116
|
-
});
|
|
3117
|
-
if (!shouldDismiss) {
|
|
3118
|
-
return;
|
|
3119
|
-
}
|
|
3120
|
-
}
|
|
2919
|
+
if (!escapeKeyBubbles && hasBlockingChild('__escapeKeyBubbles')) {
|
|
2920
|
+
return;
|
|
3121
2921
|
}
|
|
3122
2922
|
const native = useButton.isReactEvent(event) ? event.nativeEvent : event;
|
|
3123
2923
|
const eventDetails = useButton.createChangeEventDetails(useButton.escapeKey, native);
|
|
3124
2924
|
store.setOpen(false, eventDetails);
|
|
2925
|
+
if (!eventDetails.isCanceled) {
|
|
2926
|
+
event.preventDefault();
|
|
2927
|
+
}
|
|
3125
2928
|
if (!escapeKeyBubbles && !eventDetails.isPropagationAllowed) {
|
|
3126
2929
|
event.stopPropagation();
|
|
3127
2930
|
}
|
|
@@ -3130,6 +2933,33 @@ function useDismiss(context, props = {}) {
|
|
|
3130
2933
|
dataRef.current.insideReactTree = true;
|
|
3131
2934
|
clearInsideReactTreeTimeout.start(0, clearInsideReactTree);
|
|
3132
2935
|
});
|
|
2936
|
+
const markPressStartedInsideReactTree = useButton.useStableCallback(event => {
|
|
2937
|
+
if (!open || !enabled || event.button !== 0) {
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
const target = useButton.getTarget(event.nativeEvent);
|
|
2941
|
+
|
|
2942
|
+
// Only treat presses that start within the floating DOM subtree as inside.
|
|
2943
|
+
// This avoids suppressing parent dismissal when interacting with nested portals.
|
|
2944
|
+
if (!useButton.contains(store.select('floatingElement'), target)) {
|
|
2945
|
+
return;
|
|
2946
|
+
}
|
|
2947
|
+
if (!pressStartedInsideRef.current) {
|
|
2948
|
+
pressStartedInsideRef.current = true;
|
|
2949
|
+
pressStartPreventedRef.current = false;
|
|
2950
|
+
}
|
|
2951
|
+
});
|
|
2952
|
+
const markInsidePressStartPrevented = useButton.useStableCallback(event => {
|
|
2953
|
+
if (!open || !enabled) {
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
if (!(event.defaultPrevented || event.nativeEvent.defaultPrevented)) {
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
if (pressStartedInsideRef.current) {
|
|
2960
|
+
pressStartPreventedRef.current = true;
|
|
2961
|
+
}
|
|
2962
|
+
});
|
|
3133
2963
|
React__namespace.useEffect(() => {
|
|
3134
2964
|
if (!open || !enabled) {
|
|
3135
2965
|
return undefined;
|
|
@@ -3149,7 +2979,7 @@ function useDismiss(context, props = {}) {
|
|
|
3149
2979
|
compositionTimeout.start(
|
|
3150
2980
|
// 0ms or 1ms don't work in Safari. 5ms appears to consistently work.
|
|
3151
2981
|
// Only apply to WebKit for the test to remain 0ms.
|
|
3152
|
-
|
|
2982
|
+
webkit ? 5 : 0, () => {
|
|
3153
2983
|
isComposingRef.current = false;
|
|
3154
2984
|
});
|
|
3155
2985
|
}
|
|
@@ -3181,11 +3011,17 @@ function useDismiss(context, props = {}) {
|
|
|
3181
3011
|
}
|
|
3182
3012
|
function isEventWithinFloatingTree(event) {
|
|
3183
3013
|
const nodeId = dataRef.current.floatingContext?.nodeId;
|
|
3184
|
-
const targetIsInsideChildren = tree && getNodeChildren(tree.nodesRef.current, nodeId).some(node =>
|
|
3185
|
-
return
|
|
3014
|
+
const targetIsInsideChildren = tree && getNodeChildren(tree.nodesRef.current, nodeId).some(node => isEventTargetWithin(event, node.context?.elements.floating));
|
|
3015
|
+
return isEventWithinOwnElements(event) || targetIsInsideChildren;
|
|
3186
3016
|
}
|
|
3187
3017
|
function closeOnPressOutside(event) {
|
|
3188
3018
|
if (shouldIgnoreEvent(event)) {
|
|
3019
|
+
// A new press began outside the floating element and its trigger. Clear any
|
|
3020
|
+
// leftover drag-out suppression so this press's eventual click can dismiss.
|
|
3021
|
+
if (event.type !== 'click' && !isEventWithinOwnElements(event)) {
|
|
3022
|
+
preventedPressSuppressionTimeout.clear();
|
|
3023
|
+
suppressNextOutsideClickRef.current = false;
|
|
3024
|
+
}
|
|
3189
3025
|
clearInsideReactTree();
|
|
3190
3026
|
return;
|
|
3191
3027
|
}
|
|
@@ -3195,11 +3031,8 @@ function useDismiss(context, props = {}) {
|
|
|
3195
3031
|
}
|
|
3196
3032
|
const target = useButton.getTarget(event);
|
|
3197
3033
|
const inertSelector = `[${createAttribute('inert')}]`;
|
|
3198
|
-
let markers = Array.from(useButton.ownerDocument(store.select('floatingElement')).querySelectorAll(inertSelector));
|
|
3199
3034
|
const targetRoot = floatingUi_utils_dom.isElement(target) ? target.getRootNode() : null;
|
|
3200
|
-
|
|
3201
|
-
markers = markers.concat(Array.from(targetRoot.querySelectorAll(inertSelector)));
|
|
3202
|
-
}
|
|
3035
|
+
const markers = Array.from((floatingUi_utils_dom.isShadowRoot(targetRoot) ? targetRoot : useButton.ownerDocument(store.select('floatingElement'))).querySelectorAll(inertSelector));
|
|
3203
3036
|
const triggers = store.context.triggerElements;
|
|
3204
3037
|
|
|
3205
3038
|
// If another trigger is clicked, don't close the floating element.
|
|
@@ -3217,7 +3050,7 @@ function useDismiss(context, props = {}) {
|
|
|
3217
3050
|
|
|
3218
3051
|
// Check if the click occurred on a third-party element injected after the
|
|
3219
3052
|
// floating element rendered.
|
|
3220
|
-
if (markers.length && floatingUi_utils_dom.isElement(target) && !
|
|
3053
|
+
if (markers.length && floatingUi_utils_dom.isElement(target) && !isRootElement(target) &&
|
|
3221
3054
|
// Clicked on a direct ancestor (e.g. FloatingOverlay).
|
|
3222
3055
|
!useButton.contains(target, store.select('floatingElement')) &&
|
|
3223
3056
|
// If the target root element contains none of the markers, then the
|
|
@@ -3264,30 +3097,20 @@ function useDismiss(context, props = {}) {
|
|
|
3264
3097
|
if (typeof outsidePress === 'function' && !outsidePress(event)) {
|
|
3265
3098
|
return;
|
|
3266
3099
|
}
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
if (children.length > 0) {
|
|
3270
|
-
let shouldDismiss = true;
|
|
3271
|
-
children.forEach(child => {
|
|
3272
|
-
if (child.context?.open && !child.context.dataRef.current.__outsidePressBubbles) {
|
|
3273
|
-
shouldDismiss = false;
|
|
3274
|
-
}
|
|
3275
|
-
});
|
|
3276
|
-
if (!shouldDismiss) {
|
|
3277
|
-
return;
|
|
3278
|
-
}
|
|
3100
|
+
if (hasBlockingChild('__outsidePressBubbles')) {
|
|
3101
|
+
return;
|
|
3279
3102
|
}
|
|
3280
3103
|
store.setOpen(false, useButton.createChangeEventDetails(useButton.outsidePress, event));
|
|
3281
3104
|
clearInsideReactTree();
|
|
3282
3105
|
}
|
|
3283
3106
|
function handlePointerDown(event) {
|
|
3284
|
-
if (getOutsidePressEvent() !== 'sloppy' || event.pointerType === 'touch' || !store.select('open') || !enabled ||
|
|
3107
|
+
if (getOutsidePressEvent() !== 'sloppy' || event.pointerType === 'touch' || !store.select('open') || !enabled || isEventWithinOwnElements(event)) {
|
|
3285
3108
|
return;
|
|
3286
3109
|
}
|
|
3287
3110
|
closeOnPressOutside(event);
|
|
3288
3111
|
}
|
|
3289
3112
|
function handleTouchStart(event) {
|
|
3290
|
-
if (getOutsidePressEvent() !== 'sloppy' || !store.select('open') || !enabled ||
|
|
3113
|
+
if (getOutsidePressEvent() !== 'sloppy' || !store.select('open') || !enabled || isEventWithinOwnElements(event)) {
|
|
3291
3114
|
return;
|
|
3292
3115
|
}
|
|
3293
3116
|
const touch = event.touches[0];
|
|
@@ -3307,14 +3130,19 @@ function useDismiss(context, props = {}) {
|
|
|
3307
3130
|
});
|
|
3308
3131
|
}
|
|
3309
3132
|
}
|
|
3310
|
-
function
|
|
3311
|
-
currentPointerTypeRef.current = 'touch';
|
|
3133
|
+
function addTargetEventListenerOnce(event, listener) {
|
|
3312
3134
|
const target = useButton.getTarget(event);
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
target?.removeEventListener(event.type, callback);
|
|
3135
|
+
if (!target) {
|
|
3136
|
+
return;
|
|
3316
3137
|
}
|
|
3317
|
-
|
|
3138
|
+
const unsubscribe = useButton.addEventListener(target, event.type, () => {
|
|
3139
|
+
listener(event);
|
|
3140
|
+
unsubscribe();
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
3143
|
+
function handleTouchStartCapture(event) {
|
|
3144
|
+
currentPointerTypeRef.current = 'touch';
|
|
3145
|
+
addTargetEventListenerOnce(event, handleTouchStart);
|
|
3318
3146
|
}
|
|
3319
3147
|
function closeOnPressOutsideCapture(event) {
|
|
3320
3148
|
cancelDismissOnEndTimeout.clear();
|
|
@@ -3324,16 +3152,13 @@ function useDismiss(context, props = {}) {
|
|
|
3324
3152
|
if (event.type === 'mousedown' && touchStateRef.current && !touchStateRef.current.dismissOnMouseDown) {
|
|
3325
3153
|
return;
|
|
3326
3154
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
handlePointerDown(event);
|
|
3155
|
+
addTargetEventListenerOnce(event, targetEvent => {
|
|
3156
|
+
if (targetEvent.type === 'pointerdown') {
|
|
3157
|
+
handlePointerDown(targetEvent);
|
|
3331
3158
|
} else {
|
|
3332
|
-
closeOnPressOutside(
|
|
3159
|
+
closeOnPressOutside(targetEvent);
|
|
3333
3160
|
}
|
|
3334
|
-
|
|
3335
|
-
}
|
|
3336
|
-
target?.addEventListener(event.type, callback);
|
|
3161
|
+
});
|
|
3337
3162
|
}
|
|
3338
3163
|
function handlePressEndCapture(event) {
|
|
3339
3164
|
if (!pressStartedInsideRef.current) {
|
|
@@ -3372,7 +3197,7 @@ function useDismiss(context, props = {}) {
|
|
|
3372
3197
|
clearInsideReactTree();
|
|
3373
3198
|
}
|
|
3374
3199
|
function handleTouchMove(event) {
|
|
3375
|
-
if (getOutsidePressEvent() !== 'sloppy' || !touchStateRef.current ||
|
|
3200
|
+
if (getOutsidePressEvent() !== 'sloppy' || !touchStateRef.current || isEventWithinOwnElements(event)) {
|
|
3376
3201
|
return;
|
|
3377
3202
|
}
|
|
3378
3203
|
const touch = event.touches[0];
|
|
@@ -3392,15 +3217,10 @@ function useDismiss(context, props = {}) {
|
|
|
3392
3217
|
}
|
|
3393
3218
|
}
|
|
3394
3219
|
function handleTouchMoveCapture(event) {
|
|
3395
|
-
|
|
3396
|
-
function callback() {
|
|
3397
|
-
handleTouchMove(event);
|
|
3398
|
-
target?.removeEventListener(event.type, callback);
|
|
3399
|
-
}
|
|
3400
|
-
target?.addEventListener(event.type, callback);
|
|
3220
|
+
addTargetEventListenerOnce(event, handleTouchMove);
|
|
3401
3221
|
}
|
|
3402
3222
|
function handleTouchEnd(event) {
|
|
3403
|
-
if (getOutsidePressEvent() !== 'sloppy' || !touchStateRef.current ||
|
|
3223
|
+
if (getOutsidePressEvent() !== 'sloppy' || !touchStateRef.current || isEventWithinOwnElements(event)) {
|
|
3404
3224
|
return;
|
|
3405
3225
|
}
|
|
3406
3226
|
if (touchStateRef.current.dismissOnTouchEnd) {
|
|
@@ -3410,97 +3230,24 @@ function useDismiss(context, props = {}) {
|
|
|
3410
3230
|
touchStateRef.current = null;
|
|
3411
3231
|
}
|
|
3412
3232
|
function handleTouchEndCapture(event) {
|
|
3413
|
-
|
|
3414
|
-
function callback() {
|
|
3415
|
-
handleTouchEnd(event);
|
|
3416
|
-
target?.removeEventListener(event.type, callback);
|
|
3417
|
-
}
|
|
3418
|
-
target?.addEventListener(event.type, callback);
|
|
3233
|
+
addTargetEventListenerOnce(event, handleTouchEnd);
|
|
3419
3234
|
}
|
|
3420
3235
|
const doc = useButton.ownerDocument(floatingElement);
|
|
3421
|
-
|
|
3422
|
-
doc.addEventListener('keydown', closeOnEscapeKeyDown);
|
|
3423
|
-
doc.addEventListener('compositionstart', handleCompositionStart);
|
|
3424
|
-
doc.addEventListener('compositionend', handleCompositionEnd);
|
|
3425
|
-
}
|
|
3426
|
-
if (outsidePressEnabled) {
|
|
3427
|
-
doc.addEventListener('click', closeOnPressOutsideCapture, true);
|
|
3428
|
-
doc.addEventListener('pointerdown', closeOnPressOutsideCapture, true);
|
|
3429
|
-
doc.addEventListener('pointerup', handlePressEndCapture, true);
|
|
3430
|
-
doc.addEventListener('pointercancel', handlePressEndCapture, true);
|
|
3431
|
-
doc.addEventListener('mousedown', closeOnPressOutsideCapture, true);
|
|
3432
|
-
doc.addEventListener('mouseup', handlePressEndCapture, true);
|
|
3433
|
-
doc.addEventListener('touchstart', handleTouchStartCapture, true);
|
|
3434
|
-
doc.addEventListener('touchmove', handleTouchMoveCapture, true);
|
|
3435
|
-
doc.addEventListener('touchend', handleTouchEndCapture, true);
|
|
3436
|
-
}
|
|
3236
|
+
const unsubscribe = mergeCleanups(escapeKey && mergeCleanups(useButton.addEventListener(doc, 'keydown', closeOnEscapeKeyDown), useButton.addEventListener(doc, 'compositionstart', handleCompositionStart), useButton.addEventListener(doc, 'compositionend', handleCompositionEnd)), outsidePressEnabled && mergeCleanups(useButton.addEventListener(doc, 'click', closeOnPressOutsideCapture, true), useButton.addEventListener(doc, 'pointerdown', closeOnPressOutsideCapture, true), useButton.addEventListener(doc, 'pointerup', handlePressEndCapture, true), useButton.addEventListener(doc, 'pointercancel', handlePressEndCapture, true), useButton.addEventListener(doc, 'mousedown', closeOnPressOutsideCapture, true), useButton.addEventListener(doc, 'mouseup', handlePressEndCapture, true), useButton.addEventListener(doc, 'touchstart', handleTouchStartCapture, true), useButton.addEventListener(doc, 'touchmove', handleTouchMoveCapture, true), useButton.addEventListener(doc, 'touchend', handleTouchEndCapture, true)));
|
|
3437
3237
|
return () => {
|
|
3438
|
-
|
|
3439
|
-
doc.removeEventListener('keydown', closeOnEscapeKeyDown);
|
|
3440
|
-
doc.removeEventListener('compositionstart', handleCompositionStart);
|
|
3441
|
-
doc.removeEventListener('compositionend', handleCompositionEnd);
|
|
3442
|
-
}
|
|
3443
|
-
if (outsidePressEnabled) {
|
|
3444
|
-
doc.removeEventListener('click', closeOnPressOutsideCapture, true);
|
|
3445
|
-
doc.removeEventListener('pointerdown', closeOnPressOutsideCapture, true);
|
|
3446
|
-
doc.removeEventListener('pointerup', handlePressEndCapture, true);
|
|
3447
|
-
doc.removeEventListener('pointercancel', handlePressEndCapture, true);
|
|
3448
|
-
doc.removeEventListener('mousedown', closeOnPressOutsideCapture, true);
|
|
3449
|
-
doc.removeEventListener('mouseup', handlePressEndCapture, true);
|
|
3450
|
-
doc.removeEventListener('touchstart', handleTouchStartCapture, true);
|
|
3451
|
-
doc.removeEventListener('touchmove', handleTouchMoveCapture, true);
|
|
3452
|
-
doc.removeEventListener('touchend', handleTouchEndCapture, true);
|
|
3453
|
-
}
|
|
3238
|
+
unsubscribe();
|
|
3454
3239
|
compositionTimeout.clear();
|
|
3455
3240
|
preventedPressSuppressionTimeout.clear();
|
|
3456
3241
|
resetPressStartState();
|
|
3457
3242
|
suppressNextOutsideClickRef.current = false;
|
|
3458
3243
|
};
|
|
3459
|
-
}, [dataRef, floatingElement, escapeKey, outsidePressEnabled, outsidePress, open, enabled, escapeKeyBubbles, outsidePressBubbles, closeOnEscapeKeyDown, clearInsideReactTree, getOutsidePressEventProp, tree, store, cancelDismissOnEndTimeout]);
|
|
3244
|
+
}, [dataRef, floatingElement, escapeKey, outsidePressEnabled, outsidePress, open, enabled, escapeKeyBubbles, outsidePressBubbles, closeOnEscapeKeyDown, clearInsideReactTree, getOutsidePressEventProp, hasBlockingChild, isEventWithinOwnElements, tree, store, cancelDismissOnEndTimeout]);
|
|
3460
3245
|
React__namespace.useEffect(clearInsideReactTree, [outsidePress, clearInsideReactTree]);
|
|
3461
3246
|
const reference = React__namespace.useMemo(() => ({
|
|
3462
3247
|
onKeyDown: closeOnEscapeKeyDown,
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
}
|
|
3467
|
-
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerPress, event.nativeEvent));
|
|
3468
|
-
},
|
|
3469
|
-
...(referencePressEvent !== 'intentional' && {
|
|
3470
|
-
onClick(event) {
|
|
3471
|
-
if (!isReferencePressEnabled()) {
|
|
3472
|
-
return;
|
|
3473
|
-
}
|
|
3474
|
-
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerPress, event.nativeEvent));
|
|
3475
|
-
}
|
|
3476
|
-
})
|
|
3477
|
-
}), [closeOnEscapeKeyDown, store, referencePressEvent, isReferencePressEnabled]);
|
|
3478
|
-
const markPressStartedInsideReactTree = useButton.useStableCallback(event => {
|
|
3479
|
-
if (!open || !enabled || event.button !== 0) {
|
|
3480
|
-
return;
|
|
3481
|
-
}
|
|
3482
|
-
const target = useButton.getTarget(event.nativeEvent);
|
|
3483
|
-
// Only treat presses that start within the floating DOM subtree as inside.
|
|
3484
|
-
// This avoids suppressing parent dismissal when interacting with nested portals.
|
|
3485
|
-
if (!useButton.contains(store.select('floatingElement'), target)) {
|
|
3486
|
-
return;
|
|
3487
|
-
}
|
|
3488
|
-
if (!pressStartedInsideRef.current) {
|
|
3489
|
-
pressStartedInsideRef.current = true;
|
|
3490
|
-
pressStartPreventedRef.current = false;
|
|
3491
|
-
}
|
|
3492
|
-
});
|
|
3493
|
-
const markInsidePressStartPrevented = useButton.useStableCallback(event => {
|
|
3494
|
-
if (!open || !enabled) {
|
|
3495
|
-
return;
|
|
3496
|
-
}
|
|
3497
|
-
if (!(event.defaultPrevented || event.nativeEvent.defaultPrevented)) {
|
|
3498
|
-
return;
|
|
3499
|
-
}
|
|
3500
|
-
if (pressStartedInsideRef.current) {
|
|
3501
|
-
pressStartPreventedRef.current = true;
|
|
3502
|
-
}
|
|
3503
|
-
});
|
|
3248
|
+
onPointerDown: closeOnReferencePress,
|
|
3249
|
+
onClick: closeOnReferencePress
|
|
3250
|
+
}), [closeOnEscapeKeyDown, closeOnReferencePress]);
|
|
3504
3251
|
const floating = React__namespace.useMemo(() => ({
|
|
3505
3252
|
onKeyDown: closeOnEscapeKeyDown,
|
|
3506
3253
|
// `onMouseDown` may be blocked if `event.preventDefault()` is called in
|
|
@@ -3530,6 +3277,7 @@ function useDismiss(context, props = {}) {
|
|
|
3530
3277
|
|
|
3531
3278
|
const selectors = {
|
|
3532
3279
|
open: createSelector(state => state.open),
|
|
3280
|
+
transitionStatus: createSelector(state => state.transitionStatus),
|
|
3533
3281
|
domReferenceElement: createSelector(state => state.domReferenceElement),
|
|
3534
3282
|
referenceElement: createSelector(state => state.positionReference ?? state.referenceElement),
|
|
3535
3283
|
floatingElement: createSelector(state => state.floatingElement),
|
|
@@ -3538,8 +3286,8 @@ const selectors = {
|
|
|
3538
3286
|
class FloatingRootStore extends ReactStore {
|
|
3539
3287
|
constructor(options) {
|
|
3540
3288
|
const {
|
|
3289
|
+
syncOnly,
|
|
3541
3290
|
nested,
|
|
3542
|
-
noEmit,
|
|
3543
3291
|
onOpenChange,
|
|
3544
3292
|
triggerElements,
|
|
3545
3293
|
...initialState
|
|
@@ -3555,11 +3303,38 @@ class FloatingRootStore extends ReactStore {
|
|
|
3555
3303
|
},
|
|
3556
3304
|
events: createEventEmitter(),
|
|
3557
3305
|
nested,
|
|
3558
|
-
noEmit,
|
|
3559
3306
|
triggerElements
|
|
3560
3307
|
}, selectors);
|
|
3308
|
+
this.syncOnly = syncOnly;
|
|
3561
3309
|
}
|
|
3562
3310
|
|
|
3311
|
+
/**
|
|
3312
|
+
* Syncs the event used by hover logic to distinguish hover-open from click-like interaction.
|
|
3313
|
+
*/
|
|
3314
|
+
syncOpenEvent = (newOpen, event) => {
|
|
3315
|
+
if (!newOpen || !this.state.open ||
|
|
3316
|
+
// Prevent a pending hover-open from overwriting a click-open event, while allowing
|
|
3317
|
+
// click events to upgrade a hover-open.
|
|
3318
|
+
event != null && useButton.isClickLikeEvent(event)) {
|
|
3319
|
+
this.context.dataRef.current.openEvent = newOpen ? event : undefined;
|
|
3320
|
+
}
|
|
3321
|
+
};
|
|
3322
|
+
|
|
3323
|
+
/**
|
|
3324
|
+
* Runs the root-owned side effects for an open state change.
|
|
3325
|
+
*/
|
|
3326
|
+
dispatchOpenChange = (newOpen, eventDetails) => {
|
|
3327
|
+
this.syncOpenEvent(newOpen, eventDetails.event);
|
|
3328
|
+
const details = {
|
|
3329
|
+
open: newOpen,
|
|
3330
|
+
reason: eventDetails.reason,
|
|
3331
|
+
nativeEvent: eventDetails.event,
|
|
3332
|
+
nested: this.context.nested,
|
|
3333
|
+
triggerElement: eventDetails.trigger
|
|
3334
|
+
};
|
|
3335
|
+
this.context.events.emit('openchange', details);
|
|
3336
|
+
};
|
|
3337
|
+
|
|
3563
3338
|
/**
|
|
3564
3339
|
* Emits the `openchange` event through the internal event emitter and calls the `onOpenChange` handler with the provided arguments.
|
|
3565
3340
|
*
|
|
@@ -3567,26 +3342,385 @@ class FloatingRootStore extends ReactStore {
|
|
|
3567
3342
|
* @param eventDetails Details about the event that triggered the open state change.
|
|
3568
3343
|
*/
|
|
3569
3344
|
setOpen = (newOpen, eventDetails) => {
|
|
3570
|
-
if (
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
useButton.isClickLikeEvent(eventDetails.event)) {
|
|
3574
|
-
this.context.dataRef.current.openEvent = newOpen ? eventDetails.event : undefined;
|
|
3575
|
-
}
|
|
3576
|
-
if (!this.context.noEmit) {
|
|
3577
|
-
const details = {
|
|
3578
|
-
open: newOpen,
|
|
3579
|
-
reason: eventDetails.reason,
|
|
3580
|
-
nativeEvent: eventDetails.event,
|
|
3581
|
-
nested: this.context.nested,
|
|
3582
|
-
triggerElement: eventDetails.trigger
|
|
3583
|
-
};
|
|
3584
|
-
this.context.events.emit('openchange', details);
|
|
3345
|
+
if (this.syncOnly) {
|
|
3346
|
+
this.context.onOpenChange?.(newOpen, eventDetails);
|
|
3347
|
+
return;
|
|
3585
3348
|
}
|
|
3349
|
+
this.dispatchOpenChange(newOpen, eventDetails);
|
|
3586
3350
|
this.context.onOpenChange?.(newOpen, eventDetails);
|
|
3587
3351
|
};
|
|
3588
3352
|
}
|
|
3589
3353
|
|
|
3354
|
+
/**
|
|
3355
|
+
* Keeps a FloatingRootStore in sync with the provided PopupStore.
|
|
3356
|
+
* Uses the provided FloatingRootStore when one exists, otherwise creates one once and updates it on every render.
|
|
3357
|
+
*/
|
|
3358
|
+
function useSyncedFloatingRootContext(options) {
|
|
3359
|
+
const {
|
|
3360
|
+
popupStore,
|
|
3361
|
+
treatPopupAsFloatingElement = false,
|
|
3362
|
+
floatingRootContext: floatingRootContextProp,
|
|
3363
|
+
floatingId,
|
|
3364
|
+
nested,
|
|
3365
|
+
onOpenChange
|
|
3366
|
+
} = options;
|
|
3367
|
+
const open = popupStore.useState('open');
|
|
3368
|
+
const referenceElement = popupStore.useState('activeTriggerElement');
|
|
3369
|
+
const floatingElement = popupStore.useState(treatPopupAsFloatingElement ? 'popupElement' : 'positionerElement');
|
|
3370
|
+
const triggerElements = popupStore.context.triggerElements;
|
|
3371
|
+
const handleOpenChange = onOpenChange;
|
|
3372
|
+
const internalStoreRef = React__namespace.useRef(null);
|
|
3373
|
+
if (floatingRootContextProp === undefined && internalStoreRef.current === null) {
|
|
3374
|
+
internalStoreRef.current = new FloatingRootStore({
|
|
3375
|
+
open,
|
|
3376
|
+
transitionStatus: undefined,
|
|
3377
|
+
referenceElement,
|
|
3378
|
+
floatingElement,
|
|
3379
|
+
triggerElements,
|
|
3380
|
+
onOpenChange: handleOpenChange,
|
|
3381
|
+
floatingId,
|
|
3382
|
+
syncOnly: true,
|
|
3383
|
+
nested
|
|
3384
|
+
});
|
|
3385
|
+
}
|
|
3386
|
+
const store = floatingRootContextProp ?? internalStoreRef.current;
|
|
3387
|
+
popupStore.useSyncedValue('floatingId', floatingId);
|
|
3388
|
+
useButton.useIsoLayoutEffect(() => {
|
|
3389
|
+
const valuesToSync = {
|
|
3390
|
+
open,
|
|
3391
|
+
floatingId,
|
|
3392
|
+
referenceElement,
|
|
3393
|
+
floatingElement
|
|
3394
|
+
};
|
|
3395
|
+
if (floatingUi_utils_dom.isElement(referenceElement)) {
|
|
3396
|
+
valuesToSync.domReferenceElement = referenceElement;
|
|
3397
|
+
}
|
|
3398
|
+
if (store.state.positionReference === store.state.referenceElement) {
|
|
3399
|
+
valuesToSync.positionReference = referenceElement;
|
|
3400
|
+
}
|
|
3401
|
+
store.update(valuesToSync);
|
|
3402
|
+
}, [open, floatingId, referenceElement, floatingElement, store]);
|
|
3403
|
+
|
|
3404
|
+
// Keep non-reactive context values fresh for interactions that call `store.setOpen`.
|
|
3405
|
+
store.context.onOpenChange = handleOpenChange;
|
|
3406
|
+
store.context.nested = nested;
|
|
3407
|
+
return store;
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
const FOCUSABLE_POPUP_PROPS = {
|
|
3411
|
+
tabIndex: -1,
|
|
3412
|
+
[FOCUSABLE_ATTRIBUTE]: ''
|
|
3413
|
+
};
|
|
3414
|
+
|
|
3415
|
+
/**
|
|
3416
|
+
* Returns the default `initialFocus` resolver for a popup. When opened by touch it focuses the
|
|
3417
|
+
* popup element itself to prevent the virtual keyboard from opening (required for Android
|
|
3418
|
+
* specifically; iOS handles this automatically). Otherwise it falls back to the default behavior.
|
|
3419
|
+
*/
|
|
3420
|
+
function createDefaultInitialFocus(popupRef) {
|
|
3421
|
+
return interactionType => interactionType === 'touch' ? popupRef.current : true;
|
|
3422
|
+
}
|
|
3423
|
+
function usePopupStore(externalStore, createStore, treatPopupAsFloatingElement = false) {
|
|
3424
|
+
const floatingId = useButton.useId();
|
|
3425
|
+
const nested = useFloatingParentNodeId() != null;
|
|
3426
|
+
const internalStoreRef = React__namespace.useRef(null);
|
|
3427
|
+
if (externalStore === undefined && internalStoreRef.current === null) {
|
|
3428
|
+
internalStoreRef.current = createStore(floatingId, nested);
|
|
3429
|
+
}
|
|
3430
|
+
const store = externalStore ?? internalStoreRef.current;
|
|
3431
|
+
useSyncedFloatingRootContext({
|
|
3432
|
+
popupStore: store,
|
|
3433
|
+
treatPopupAsFloatingElement,
|
|
3434
|
+
floatingRootContext: store.state.floatingRootContext,
|
|
3435
|
+
floatingId,
|
|
3436
|
+
nested,
|
|
3437
|
+
onOpenChange: store.setOpen
|
|
3438
|
+
});
|
|
3439
|
+
return {
|
|
3440
|
+
store,
|
|
3441
|
+
internalStore: internalStoreRef.current
|
|
3442
|
+
};
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
/**
|
|
3446
|
+
* Returns a callback ref that registers/unregisters the trigger element in the store.
|
|
3447
|
+
*
|
|
3448
|
+
* @param store The Store instance where the trigger should be registered.
|
|
3449
|
+
*/
|
|
3450
|
+
function useTriggerRegistration(id, store) {
|
|
3451
|
+
// Keep track of the currently registered element to unregister it on unmount or id change.
|
|
3452
|
+
const registeredElementIdRef = React__namespace.useRef(null);
|
|
3453
|
+
const registeredElementRef = React__namespace.useRef(null);
|
|
3454
|
+
return React__namespace.useCallback(element => {
|
|
3455
|
+
if (id === undefined) {
|
|
3456
|
+
return;
|
|
3457
|
+
}
|
|
3458
|
+
let shouldSyncTriggerCount = false;
|
|
3459
|
+
if (registeredElementIdRef.current !== null) {
|
|
3460
|
+
const registeredId = registeredElementIdRef.current;
|
|
3461
|
+
const registeredElement = registeredElementRef.current;
|
|
3462
|
+
const currentElement = store.context.triggerElements.getById(registeredId);
|
|
3463
|
+
if (registeredElement && currentElement === registeredElement) {
|
|
3464
|
+
store.context.triggerElements.delete(registeredId);
|
|
3465
|
+
shouldSyncTriggerCount = true;
|
|
3466
|
+
}
|
|
3467
|
+
registeredElementIdRef.current = null;
|
|
3468
|
+
registeredElementRef.current = null;
|
|
3469
|
+
}
|
|
3470
|
+
if (element !== null) {
|
|
3471
|
+
registeredElementIdRef.current = id;
|
|
3472
|
+
registeredElementRef.current = element;
|
|
3473
|
+
store.context.triggerElements.add(id, element);
|
|
3474
|
+
shouldSyncTriggerCount = true;
|
|
3475
|
+
}
|
|
3476
|
+
if (shouldSyncTriggerCount) {
|
|
3477
|
+
const triggerCount = store.context.triggerElements.size;
|
|
3478
|
+
if (store.select('open') && store.state.triggerCount !== triggerCount) {
|
|
3479
|
+
store.set('triggerCount', triggerCount);
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
}, [store, id]);
|
|
3483
|
+
}
|
|
3484
|
+
function setPopupOpenState(state, open, trigger, preventUnmountOnClose = false) {
|
|
3485
|
+
if (open) {
|
|
3486
|
+
// Opening starts a new close cycle, so clear any previous request to keep the popup mounted.
|
|
3487
|
+
state.preventUnmountingOnClose = false;
|
|
3488
|
+
} else if (preventUnmountOnClose) {
|
|
3489
|
+
state.preventUnmountingOnClose = true;
|
|
3490
|
+
}
|
|
3491
|
+
const triggerId = trigger?.id ?? null;
|
|
3492
|
+
|
|
3493
|
+
// If a popup is closing, the `trigger` may be undefined.
|
|
3494
|
+
// We want to keep the previous value so that exit animations are played and focus is returned correctly.
|
|
3495
|
+
if (triggerId || open) {
|
|
3496
|
+
state.activeTriggerId = triggerId;
|
|
3497
|
+
state.activeTriggerElement = trigger ?? null;
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
function attachPreventUnmountOnClose(eventDetails) {
|
|
3501
|
+
let preventUnmountOnClose = false;
|
|
3502
|
+
eventDetails.preventUnmountOnClose = () => {
|
|
3503
|
+
preventUnmountOnClose = true;
|
|
3504
|
+
};
|
|
3505
|
+
return () => preventUnmountOnClose;
|
|
3506
|
+
}
|
|
3507
|
+
function useInitialOpenSync(store, openProp, defaultOpen, defaultTriggerId) {
|
|
3508
|
+
useOnFirstRender(() => {
|
|
3509
|
+
if (openProp === undefined && store.state.open === false && defaultOpen) {
|
|
3510
|
+
// Avoid notifying detached trigger subscribers while the Root is rendering.
|
|
3511
|
+
store.state = {
|
|
3512
|
+
...store.state,
|
|
3513
|
+
open: true,
|
|
3514
|
+
activeTriggerId: defaultTriggerId,
|
|
3515
|
+
preventUnmountingOnClose: false
|
|
3516
|
+
};
|
|
3517
|
+
}
|
|
3518
|
+
});
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
/**
|
|
3522
|
+
* Sets up trigger data forwarding to the store.
|
|
3523
|
+
*
|
|
3524
|
+
* @param triggerId Id of the trigger.
|
|
3525
|
+
* @param triggerElementRef Ref for the trigger DOM element.
|
|
3526
|
+
* @param store The Store instance managing the popup state.
|
|
3527
|
+
* @param stateUpdates An object with state updates to apply when the trigger is active.
|
|
3528
|
+
*/
|
|
3529
|
+
function useTriggerDataForwarding(triggerId, triggerElementRef, store, stateUpdates) {
|
|
3530
|
+
const isMountedByThisTrigger = store.useState('isMountedByTrigger', triggerId);
|
|
3531
|
+
const baseRegisterTrigger = useTriggerRegistration(triggerId, store);
|
|
3532
|
+
const registerTrigger = useButton.useStableCallback(element => {
|
|
3533
|
+
baseRegisterTrigger(element);
|
|
3534
|
+
if (!element) {
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
const open = store.select('open');
|
|
3538
|
+
const activeTriggerId = store.select('activeTriggerId');
|
|
3539
|
+
if (activeTriggerId === triggerId) {
|
|
3540
|
+
store.update({
|
|
3541
|
+
activeTriggerElement: element,
|
|
3542
|
+
...(open ? stateUpdates : null)
|
|
3543
|
+
});
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
if (activeTriggerId == null && open) {
|
|
3547
|
+
// If a popup is already open, a detached trigger can mount before any active trigger
|
|
3548
|
+
// has been established. Claim the first registered trigger so trigger-owned focus
|
|
3549
|
+
// management and ARIA relationships work.
|
|
3550
|
+
store.update({
|
|
3551
|
+
activeTriggerId: triggerId,
|
|
3552
|
+
activeTriggerElement: element,
|
|
3553
|
+
...stateUpdates
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
});
|
|
3557
|
+
useButton.useIsoLayoutEffect(() => {
|
|
3558
|
+
if (isMountedByThisTrigger) {
|
|
3559
|
+
store.update({
|
|
3560
|
+
activeTriggerElement: triggerElementRef.current,
|
|
3561
|
+
...stateUpdates
|
|
3562
|
+
});
|
|
3563
|
+
}
|
|
3564
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3565
|
+
}, [isMountedByThisTrigger, store, triggerElementRef, ...Object.values(stateUpdates)]);
|
|
3566
|
+
return {
|
|
3567
|
+
registerTrigger,
|
|
3568
|
+
isMountedByThisTrigger
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
/**
|
|
3572
|
+
* Keeps trigger registration state synchronized while the popup is open.
|
|
3573
|
+
*
|
|
3574
|
+
* When a popup opens without an explicit trigger id and exactly one trigger is registered, that
|
|
3575
|
+
* trigger is claimed as the active trigger. When the active trigger id is still registered but its
|
|
3576
|
+
* element changed, the active element is refreshed. When the active trigger unregisters, the
|
|
3577
|
+
* default path preserves existing ownership so non-closing popup families do not silently claim a
|
|
3578
|
+
* different trigger while staying open.
|
|
3579
|
+
*
|
|
3580
|
+
* If `closeOnActiveTriggerUnmount` is enabled, unregistering the active trigger requests a close
|
|
3581
|
+
* after a microtask so a same-tick replacement trigger with the same id can register first.
|
|
3582
|
+
*
|
|
3583
|
+
* This should be called on the Root part.
|
|
3584
|
+
*
|
|
3585
|
+
* @param store The Store instance managing the popup state.
|
|
3586
|
+
* @param options Options for active trigger unmount behavior.
|
|
3587
|
+
*/
|
|
3588
|
+
function useImplicitActiveTrigger(store, options = {}) {
|
|
3589
|
+
const {
|
|
3590
|
+
closeOnActiveTriggerUnmount = false
|
|
3591
|
+
} = options;
|
|
3592
|
+
const open = store.useState('open');
|
|
3593
|
+
const reactiveTriggerCount = store.useState('triggerCount');
|
|
3594
|
+
useButton.useIsoLayoutEffect(() => {
|
|
3595
|
+
if (!open) {
|
|
3596
|
+
if (store.state.triggerCount !== 0) {
|
|
3597
|
+
store.set('triggerCount', 0);
|
|
3598
|
+
}
|
|
3599
|
+
return;
|
|
3600
|
+
}
|
|
3601
|
+
const triggerCount = store.context.triggerElements.size;
|
|
3602
|
+
const stateUpdates = {};
|
|
3603
|
+
if (store.state.triggerCount !== triggerCount) {
|
|
3604
|
+
stateUpdates.triggerCount = triggerCount;
|
|
3605
|
+
}
|
|
3606
|
+
const activeTriggerId = store.select('activeTriggerId');
|
|
3607
|
+
let lostActiveTriggerId = null;
|
|
3608
|
+
if (activeTriggerId) {
|
|
3609
|
+
const activeTriggerElement = store.context.triggerElements.getById(activeTriggerId);
|
|
3610
|
+
if (!activeTriggerElement) {
|
|
3611
|
+
lostActiveTriggerId = activeTriggerId;
|
|
3612
|
+
} else if (activeTriggerElement !== store.state.activeTriggerElement) {
|
|
3613
|
+
stateUpdates.activeTriggerElement = activeTriggerElement;
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
if (!lostActiveTriggerId && !activeTriggerId && triggerCount === 1) {
|
|
3617
|
+
const iteratorResult = store.context.triggerElements.entries().next();
|
|
3618
|
+
if (!iteratorResult.done) {
|
|
3619
|
+
const [implicitTriggerId, implicitTriggerElement] = iteratorResult.value;
|
|
3620
|
+
stateUpdates.activeTriggerId = implicitTriggerId;
|
|
3621
|
+
stateUpdates.activeTriggerElement = implicitTriggerElement;
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
if (stateUpdates.triggerCount !== undefined || stateUpdates.activeTriggerId !== undefined || stateUpdates.activeTriggerElement !== undefined) {
|
|
3625
|
+
store.update(stateUpdates);
|
|
3626
|
+
}
|
|
3627
|
+
if (lostActiveTriggerId) {
|
|
3628
|
+
if (closeOnActiveTriggerUnmount) {
|
|
3629
|
+
// Defer so a same-tick replacement trigger with the same id can register first.
|
|
3630
|
+
queueMicrotask(() => {
|
|
3631
|
+
if (store.select('open') && store.select('activeTriggerId') === lostActiveTriggerId && !store.context.triggerElements.getById(lostActiveTriggerId)) {
|
|
3632
|
+
const eventDetails = useButton.createChangeEventDetails(useButton.none);
|
|
3633
|
+
store.setOpen(false, eventDetails);
|
|
3634
|
+
// If closing is canceled, keep the previous active trigger ownership for the
|
|
3635
|
+
// still-open popup instead of claiming another trigger implicitly.
|
|
3636
|
+
if (!eventDetails.isCanceled) {
|
|
3637
|
+
store.update({
|
|
3638
|
+
activeTriggerId: null,
|
|
3639
|
+
activeTriggerElement: null
|
|
3640
|
+
});
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
});
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
}, [open, store, reactiveTriggerCount, closeOnActiveTriggerUnmount]);
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
/**
|
|
3650
|
+
* Manages the mounted state of the popup.
|
|
3651
|
+
* Sets up the transition status listeners and handles unmounting when needed.
|
|
3652
|
+
* Updates the `mounted`, `transitionStatus`, and `preventUnmountingOnClose` states in the store.
|
|
3653
|
+
*
|
|
3654
|
+
* @param open Whether the popup is open.
|
|
3655
|
+
* @param store The Store instance managing the popup state.
|
|
3656
|
+
* @param onUnmount Optional callback to be called when the popup is unmounted.
|
|
3657
|
+
*
|
|
3658
|
+
* @returns A function to forcibly unmount the popup.
|
|
3659
|
+
*/
|
|
3660
|
+
function useOpenStateTransitions(open, store, onUnmount) {
|
|
3661
|
+
const {
|
|
3662
|
+
mounted,
|
|
3663
|
+
setMounted,
|
|
3664
|
+
transitionStatus
|
|
3665
|
+
} = useButton.useTransitionStatus(open);
|
|
3666
|
+
const preventUnmountingOnClose = store.useState('preventUnmountingOnClose');
|
|
3667
|
+
// Opening starts a new close cycle. Clear during render so the close-completion hook below
|
|
3668
|
+
// reads the synchronized value on the same pass.
|
|
3669
|
+
const syncedPreventUnmountingOnClose = open ? false : preventUnmountingOnClose;
|
|
3670
|
+
store.useSyncedValues({
|
|
3671
|
+
mounted,
|
|
3672
|
+
transitionStatus,
|
|
3673
|
+
preventUnmountingOnClose: syncedPreventUnmountingOnClose
|
|
3674
|
+
});
|
|
3675
|
+
const forceUnmount = useButton.useStableCallback(() => {
|
|
3676
|
+
setMounted(false);
|
|
3677
|
+
store.update({
|
|
3678
|
+
activeTriggerId: null,
|
|
3679
|
+
activeTriggerElement: null,
|
|
3680
|
+
mounted: false,
|
|
3681
|
+
preventUnmountingOnClose: false
|
|
3682
|
+
});
|
|
3683
|
+
onUnmount?.();
|
|
3684
|
+
store.context.onOpenChangeComplete?.(false);
|
|
3685
|
+
});
|
|
3686
|
+
useButton.useOpenChangeComplete({
|
|
3687
|
+
enabled: mounted && !open && !syncedPreventUnmountingOnClose,
|
|
3688
|
+
open,
|
|
3689
|
+
ref: store.context.popupRef,
|
|
3690
|
+
onComplete() {
|
|
3691
|
+
if (!open) {
|
|
3692
|
+
forceUnmount();
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
});
|
|
3696
|
+
return {
|
|
3697
|
+
forceUnmount,
|
|
3698
|
+
transitionStatus
|
|
3699
|
+
};
|
|
3700
|
+
}
|
|
3701
|
+
function usePopupInteractionProps(store, statePart) {
|
|
3702
|
+
store.useSyncedValues(statePart);
|
|
3703
|
+
useButton.useIsoLayoutEffect(() => () => {
|
|
3704
|
+
store.update({
|
|
3705
|
+
activeTriggerProps: useRenderElement.EMPTY_OBJECT,
|
|
3706
|
+
inactiveTriggerProps: useRenderElement.EMPTY_OBJECT,
|
|
3707
|
+
popupProps: useRenderElement.EMPTY_OBJECT
|
|
3708
|
+
});
|
|
3709
|
+
}, [store]);
|
|
3710
|
+
}
|
|
3711
|
+
function usePopupRootSync(store, open) {
|
|
3712
|
+
useButton.useIsoLayoutEffect(() => {
|
|
3713
|
+
if (!open && store.state.openMethod !== null) {
|
|
3714
|
+
store.set('openMethod', null);
|
|
3715
|
+
}
|
|
3716
|
+
}, [open, store]);
|
|
3717
|
+
useButton.useIsoLayoutEffect(() => () => {
|
|
3718
|
+
if (store.state.openMethod !== null) {
|
|
3719
|
+
store.set('openMethod', null);
|
|
3720
|
+
}
|
|
3721
|
+
}, [store]);
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3590
3724
|
/**
|
|
3591
3725
|
* Data structure to keep track of popup trigger elements by their IDs.
|
|
3592
3726
|
* Uses both a set of Elements and a map of IDs to Elements for efficient lookups.
|
|
@@ -3616,6 +3750,8 @@ class PopupTriggerMap {
|
|
|
3616
3750
|
this.idMap.set(id, element);
|
|
3617
3751
|
if (process.env.NODE_ENV !== 'production') {
|
|
3618
3752
|
if (this.elementsSet.size !== this.idMap.size) {
|
|
3753
|
+
// TODO: fix mui/no-guarded-throw
|
|
3754
|
+
// eslint-disable-next-line mui/no-guarded-throw
|
|
3619
3755
|
throw new Error('Base UI: A trigger element cannot be registered under multiple IDs in PopupTriggerMap.');
|
|
3620
3756
|
}
|
|
3621
3757
|
}
|
|
@@ -3680,93 +3816,6 @@ class PopupTriggerMap {
|
|
|
3680
3816
|
}
|
|
3681
3817
|
}
|
|
3682
3818
|
|
|
3683
|
-
/**
|
|
3684
|
-
* Merges an array of interaction hooks' props into prop getters, allowing
|
|
3685
|
-
* event handler functions to be composed together without overwriting one
|
|
3686
|
-
* another.
|
|
3687
|
-
* @see https://floating-ui.com/docs/useInteractions
|
|
3688
|
-
*/
|
|
3689
|
-
function useInteractions(propsList = []) {
|
|
3690
|
-
const referenceDeps = propsList.map(key => key?.reference);
|
|
3691
|
-
const floatingDeps = propsList.map(key => key?.floating);
|
|
3692
|
-
const itemDeps = propsList.map(key => key?.item);
|
|
3693
|
-
const triggerDeps = propsList.map(key => key?.trigger);
|
|
3694
|
-
const getReferenceProps = React__namespace.useCallback(userProps => mergeProps(userProps, propsList, 'reference'),
|
|
3695
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3696
|
-
referenceDeps);
|
|
3697
|
-
const getFloatingProps = React__namespace.useCallback(userProps => mergeProps(userProps, propsList, 'floating'),
|
|
3698
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3699
|
-
floatingDeps);
|
|
3700
|
-
const getItemProps = React__namespace.useCallback(userProps => mergeProps(userProps, propsList, 'item'),
|
|
3701
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3702
|
-
itemDeps);
|
|
3703
|
-
const getTriggerProps = React__namespace.useCallback(userProps => mergeProps(userProps, propsList, 'trigger'),
|
|
3704
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3705
|
-
triggerDeps);
|
|
3706
|
-
return React__namespace.useMemo(() => ({
|
|
3707
|
-
getReferenceProps,
|
|
3708
|
-
getFloatingProps,
|
|
3709
|
-
getItemProps,
|
|
3710
|
-
getTriggerProps
|
|
3711
|
-
}), [getReferenceProps, getFloatingProps, getItemProps, getTriggerProps]);
|
|
3712
|
-
}
|
|
3713
|
-
|
|
3714
|
-
/* eslint-disable guard-for-in */
|
|
3715
|
-
|
|
3716
|
-
function mergeProps(userProps, propsList, elementKey) {
|
|
3717
|
-
const eventHandlers = new Map();
|
|
3718
|
-
const isItem = elementKey === 'item';
|
|
3719
|
-
const outputProps = {};
|
|
3720
|
-
if (elementKey === 'floating') {
|
|
3721
|
-
outputProps.tabIndex = -1;
|
|
3722
|
-
outputProps[useButton.FOCUSABLE_ATTRIBUTE] = '';
|
|
3723
|
-
}
|
|
3724
|
-
for (const key in userProps) {
|
|
3725
|
-
if (isItem && userProps) {
|
|
3726
|
-
if (key === useButton.ACTIVE_KEY || key === useButton.SELECTED_KEY) {
|
|
3727
|
-
continue;
|
|
3728
|
-
}
|
|
3729
|
-
}
|
|
3730
|
-
outputProps[key] = userProps[key];
|
|
3731
|
-
}
|
|
3732
|
-
for (let i = 0; i < propsList.length; i += 1) {
|
|
3733
|
-
let props;
|
|
3734
|
-
const propsOrGetProps = propsList[i]?.[elementKey];
|
|
3735
|
-
if (typeof propsOrGetProps === 'function') {
|
|
3736
|
-
props = userProps ? propsOrGetProps(userProps) : null;
|
|
3737
|
-
} else {
|
|
3738
|
-
props = propsOrGetProps;
|
|
3739
|
-
}
|
|
3740
|
-
if (!props) {
|
|
3741
|
-
continue;
|
|
3742
|
-
}
|
|
3743
|
-
mutablyMergeProps(outputProps, props, isItem, eventHandlers);
|
|
3744
|
-
}
|
|
3745
|
-
mutablyMergeProps(outputProps, userProps, isItem, eventHandlers);
|
|
3746
|
-
return outputProps;
|
|
3747
|
-
}
|
|
3748
|
-
function mutablyMergeProps(outputProps, props, isItem, eventHandlers) {
|
|
3749
|
-
for (const key in props) {
|
|
3750
|
-
const value = props[key];
|
|
3751
|
-
if (isItem && (key === useButton.ACTIVE_KEY || key === useButton.SELECTED_KEY)) {
|
|
3752
|
-
continue;
|
|
3753
|
-
}
|
|
3754
|
-
if (!key.startsWith('on')) {
|
|
3755
|
-
outputProps[key] = value;
|
|
3756
|
-
} else {
|
|
3757
|
-
if (!eventHandlers.has(key)) {
|
|
3758
|
-
eventHandlers.set(key, []);
|
|
3759
|
-
}
|
|
3760
|
-
if (typeof value === 'function') {
|
|
3761
|
-
eventHandlers.get(key)?.push(value);
|
|
3762
|
-
outputProps[key] = (...args) => {
|
|
3763
|
-
return eventHandlers.get(key)?.map(fn => fn(...args)).find(val => val !== undefined);
|
|
3764
|
-
};
|
|
3765
|
-
}
|
|
3766
|
-
}
|
|
3767
|
-
}
|
|
3768
|
-
}
|
|
3769
|
-
|
|
3770
3819
|
/**
|
|
3771
3820
|
* Provides a cross-browser way to determine the type of the pointer used to click.
|
|
3772
3821
|
* Safari and Firefox do not provide the PointerEvent to the click handler (they use MouseEvent) yet.
|
|
@@ -3803,38 +3852,54 @@ function useEnhancedClickHandler(handler) {
|
|
|
3803
3852
|
};
|
|
3804
3853
|
}
|
|
3805
3854
|
|
|
3806
|
-
|
|
3807
|
-
* Determines the interaction type (keyboard, mouse, touch, etc.) that opened the component.
|
|
3808
|
-
*
|
|
3809
|
-
* @param open The open state of the component.
|
|
3810
|
-
*/
|
|
3811
|
-
function useOpenInteractionType(open) {
|
|
3812
|
-
const [openMethod, setOpenMethod] = React__namespace.useState(null);
|
|
3855
|
+
function useOpenMethodTriggerProps(open, setOpenMethod) {
|
|
3813
3856
|
const handleTriggerClick = useButton.useStableCallback((_, interactionType) => {
|
|
3814
|
-
|
|
3857
|
+
const isOpen = typeof open === 'function' ? open() : open;
|
|
3858
|
+
if (!isOpen) {
|
|
3815
3859
|
setOpenMethod(interactionType || (
|
|
3816
3860
|
// On iOS Safari, the hitslop around touch targets means tapping outside an element's
|
|
3817
3861
|
// bounds does not fire `pointerdown` but does fire `mousedown`. The `interactionType`
|
|
3818
3862
|
// will be "" in that case.
|
|
3819
|
-
useButton.
|
|
3863
|
+
useButton.ios ? 'touch' : ''));
|
|
3820
3864
|
}
|
|
3821
3865
|
});
|
|
3866
|
+
const {
|
|
3867
|
+
onClick,
|
|
3868
|
+
onPointerDown
|
|
3869
|
+
} = useEnhancedClickHandler(handleTriggerClick);
|
|
3870
|
+
return React__namespace.useMemo(() => ({
|
|
3871
|
+
onClick,
|
|
3872
|
+
onPointerDown
|
|
3873
|
+
}), [onClick, onPointerDown]);
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
/**
|
|
3877
|
+
* Determines the interaction type (keyboard, mouse, touch, etc.) that opened the component.
|
|
3878
|
+
*
|
|
3879
|
+
* @param open The open state of the component.
|
|
3880
|
+
*/
|
|
3881
|
+
function useOpenInteractionType(open) {
|
|
3882
|
+
const [openMethod, setOpenMethod] = React__namespace.useState(null);
|
|
3883
|
+
const triggerProps = useOpenMethodTriggerProps(open, setOpenMethod);
|
|
3822
3884
|
useButton.useValueChanged(open, previousOpen => {
|
|
3823
3885
|
if (previousOpen && !open) {
|
|
3824
3886
|
setOpenMethod(null);
|
|
3825
3887
|
}
|
|
3826
3888
|
});
|
|
3827
|
-
const {
|
|
3828
|
-
onClick,
|
|
3829
|
-
onPointerDown
|
|
3830
|
-
} = useEnhancedClickHandler(handleTriggerClick);
|
|
3831
3889
|
return React__namespace.useMemo(() => ({
|
|
3832
3890
|
openMethod,
|
|
3833
|
-
triggerProps
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3891
|
+
triggerProps
|
|
3892
|
+
}), [openMethod, triggerProps]);
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
/**
|
|
3896
|
+
* @internal
|
|
3897
|
+
*/
|
|
3898
|
+
const DirectionContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
3899
|
+
if (process.env.NODE_ENV !== "production") DirectionContext.displayName = "DirectionContext";
|
|
3900
|
+
function useDirection() {
|
|
3901
|
+
const context = React__namespace.useContext(DirectionContext);
|
|
3902
|
+
return context?.direction ?? 'ltr';
|
|
3838
3903
|
}
|
|
3839
3904
|
|
|
3840
3905
|
let CommonPopupDataAttributes = function (CommonPopupDataAttributes) {
|
|
@@ -3928,16 +3993,6 @@ const popupStateMapping = {
|
|
|
3928
3993
|
}
|
|
3929
3994
|
};
|
|
3930
3995
|
|
|
3931
|
-
/**
|
|
3932
|
-
* @internal
|
|
3933
|
-
*/
|
|
3934
|
-
const DirectionContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
3935
|
-
if (process.env.NODE_ENV !== "production") DirectionContext.displayName = "DirectionContext";
|
|
3936
|
-
function useDirection() {
|
|
3937
|
-
const context = React__namespace.useContext(DirectionContext);
|
|
3938
|
-
return context?.direction ?? 'ltr';
|
|
3939
|
-
}
|
|
3940
|
-
|
|
3941
3996
|
function inertValue(value) {
|
|
3942
3997
|
if (useRenderElement.isReactVersionAtLeast(19)) {
|
|
3943
3998
|
return value;
|
|
@@ -3946,6 +4001,35 @@ function inertValue(value) {
|
|
|
3946
4001
|
return value ? 'true' : undefined;
|
|
3947
4002
|
}
|
|
3948
4003
|
|
|
4004
|
+
const InternalBackdrop = /*#__PURE__*/React__namespace.forwardRef(function InternalBackdrop(props, ref) {
|
|
4005
|
+
const {
|
|
4006
|
+
cutout,
|
|
4007
|
+
...otherProps
|
|
4008
|
+
} = props;
|
|
4009
|
+
let clipPath;
|
|
4010
|
+
if (cutout) {
|
|
4011
|
+
const rect = cutout.getBoundingClientRect();
|
|
4012
|
+
clipPath = `polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${rect.left}px ${rect.top}px,${rect.left}px ${rect.bottom}px,${rect.right}px ${rect.bottom}px,${rect.right}px ${rect.top}px,${rect.left}px ${rect.top}px)`;
|
|
4013
|
+
}
|
|
4014
|
+
return /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
4015
|
+
ref: ref,
|
|
4016
|
+
role: "presentation"
|
|
4017
|
+
// Ensures Floating UI's outside press detection runs, as it considers
|
|
4018
|
+
// it an element that existed when the popup rendered.
|
|
4019
|
+
,
|
|
4020
|
+
"data-base-ui-inert": "",
|
|
4021
|
+
...otherProps,
|
|
4022
|
+
style: {
|
|
4023
|
+
position: 'fixed',
|
|
4024
|
+
inset: 0,
|
|
4025
|
+
userSelect: 'none',
|
|
4026
|
+
WebkitUserSelect: 'none',
|
|
4027
|
+
clipPath
|
|
4028
|
+
}
|
|
4029
|
+
});
|
|
4030
|
+
});
|
|
4031
|
+
if (process.env.NODE_ENV !== "production") InternalBackdrop.displayName = "InternalBackdrop";
|
|
4032
|
+
|
|
3949
4033
|
let originalHtmlStyles = {};
|
|
3950
4034
|
let originalBodyStyles = {};
|
|
3951
4035
|
let originalHtmlScrollBehavior = '';
|
|
@@ -4010,7 +4094,7 @@ function preventScrollInsetScrollbars(referenceElement) {
|
|
|
4010
4094
|
const resizeFrame = useButton.AnimationFrame.create();
|
|
4011
4095
|
|
|
4012
4096
|
// Pinch-zoom in Safari causes a shift. Just don't lock scroll if there's any pinch-zoom.
|
|
4013
|
-
if (
|
|
4097
|
+
if (webkit && (win.visualViewport?.scale ?? 1) !== 1) {
|
|
4014
4098
|
return () => {};
|
|
4015
4099
|
}
|
|
4016
4100
|
function lockScroll() {
|
|
@@ -4104,16 +4188,15 @@ function preventScrollInsetScrollbars(referenceElement) {
|
|
|
4104
4188
|
resizeFrame.request(lockScroll);
|
|
4105
4189
|
}
|
|
4106
4190
|
lockScroll();
|
|
4107
|
-
|
|
4191
|
+
const unsubscribeResize = useButton.addEventListener(win, 'resize', handleResize);
|
|
4108
4192
|
return () => {
|
|
4109
4193
|
resizeFrame.cancel();
|
|
4110
4194
|
cleanup();
|
|
4111
|
-
// Sometimes this cleanup can
|
|
4112
|
-
//
|
|
4113
|
-
//
|
|
4114
|
-
// so we check for it to avoid test failures.
|
|
4195
|
+
// Sometimes this cleanup can run after test teardown because it is called
|
|
4196
|
+
// in a `setTimeout(fn, 0)`. Guard the returned cleanup to avoid calling
|
|
4197
|
+
// `removeEventListener` when it is no longer available in tests.
|
|
4115
4198
|
if (typeof win.removeEventListener === 'function') {
|
|
4116
|
-
|
|
4199
|
+
unsubscribeResize();
|
|
4117
4200
|
}
|
|
4118
4201
|
};
|
|
4119
4202
|
}
|
|
@@ -4154,7 +4237,7 @@ class ScrollLocker {
|
|
|
4154
4237
|
this.restore = useRenderElement.NOOP;
|
|
4155
4238
|
return;
|
|
4156
4239
|
}
|
|
4157
|
-
const hasOverlayScrollbars = useButton.
|
|
4240
|
+
const hasOverlayScrollbars = useButton.ios || !hasInsetScrollbars(referenceElement);
|
|
4158
4241
|
|
|
4159
4242
|
// On iOS, scroll locking does not work if the navbar is collapsed. Due to numerous
|
|
4160
4243
|
// side effects and bugs that arise on iOS, it must be researched extensively before
|
|
@@ -4182,51 +4265,16 @@ function useScrollLock(enabled = true, referenceElement = null) {
|
|
|
4182
4265
|
}, [enabled, referenceElement]);
|
|
4183
4266
|
}
|
|
4184
4267
|
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
} = props;
|
|
4190
|
-
let clipPath;
|
|
4191
|
-
if (cutout) {
|
|
4192
|
-
const rect = cutout?.getBoundingClientRect();
|
|
4193
|
-
clipPath = `polygon(
|
|
4194
|
-
0% 0%,
|
|
4195
|
-
100% 0%,
|
|
4196
|
-
100% 100%,
|
|
4197
|
-
0% 100%,
|
|
4198
|
-
0% 0%,
|
|
4199
|
-
${rect.left}px ${rect.top}px,
|
|
4200
|
-
${rect.left}px ${rect.bottom}px,
|
|
4201
|
-
${rect.right}px ${rect.bottom}px,
|
|
4202
|
-
${rect.right}px ${rect.top}px,
|
|
4203
|
-
${rect.left}px ${rect.top}px
|
|
4204
|
-
)`;
|
|
4205
|
-
}
|
|
4206
|
-
return /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
4207
|
-
ref: ref,
|
|
4208
|
-
role: "presentation"
|
|
4209
|
-
// Ensures Floating UI's outside press detection runs, as it considers
|
|
4210
|
-
// it an element that existed when the popup rendered.
|
|
4211
|
-
,
|
|
4212
|
-
"data-base-ui-inert": "",
|
|
4213
|
-
...otherProps,
|
|
4214
|
-
style: {
|
|
4215
|
-
position: 'fixed',
|
|
4216
|
-
inset: 0,
|
|
4217
|
-
userSelect: 'none',
|
|
4218
|
-
WebkitUserSelect: 'none',
|
|
4219
|
-
clipPath
|
|
4220
|
-
}
|
|
4221
|
-
});
|
|
4222
|
-
});
|
|
4223
|
-
if (process.env.NODE_ENV !== "production") InternalBackdrop.displayName = "InternalBackdrop";
|
|
4224
|
-
|
|
4268
|
+
exports.ARROW_DOWN = ARROW_DOWN;
|
|
4269
|
+
exports.ARROW_LEFT = ARROW_LEFT;
|
|
4270
|
+
exports.ARROW_RIGHT = ARROW_RIGHT;
|
|
4271
|
+
exports.ARROW_UP = ARROW_UP;
|
|
4225
4272
|
exports.BASE_UI_SWIPE_IGNORE_SELECTOR = BASE_UI_SWIPE_IGNORE_SELECTOR;
|
|
4226
4273
|
exports.CLICK_TRIGGER_IDENTIFIER = CLICK_TRIGGER_IDENTIFIER;
|
|
4227
4274
|
exports.CommonPopupDataAttributes = CommonPopupDataAttributes;
|
|
4228
4275
|
exports.DISABLED_TRANSITIONS_STYLE = DISABLED_TRANSITIONS_STYLE;
|
|
4229
4276
|
exports.DROPDOWN_COLLISION_AVOIDANCE = DROPDOWN_COLLISION_AVOIDANCE;
|
|
4277
|
+
exports.FOCUSABLE_POPUP_PROPS = FOCUSABLE_POPUP_PROPS;
|
|
4230
4278
|
exports.FloatingFocusManager = FloatingFocusManager;
|
|
4231
4279
|
exports.FloatingNode = FloatingNode;
|
|
4232
4280
|
exports.FloatingPortal = FloatingPortal;
|
|
@@ -4241,15 +4289,16 @@ exports.PopupTriggerMap = PopupTriggerMap;
|
|
|
4241
4289
|
exports.ReactStore = ReactStore;
|
|
4242
4290
|
exports.Store = Store;
|
|
4243
4291
|
exports.TYPEAHEAD_RESET_MS = TYPEAHEAD_RESET_MS;
|
|
4292
|
+
exports.attachPreventUnmountOnClose = attachPreventUnmountOnClose;
|
|
4244
4293
|
exports.createAttribute = createAttribute;
|
|
4245
|
-
exports.
|
|
4294
|
+
exports.createDefaultInitialFocus = createDefaultInitialFocus;
|
|
4246
4295
|
exports.createSelector = createSelector;
|
|
4247
4296
|
exports.enqueueFocus = enqueueFocus;
|
|
4248
4297
|
exports.fastComponent = fastComponent;
|
|
4249
4298
|
exports.fastComponentRef = fastComponentRef;
|
|
4250
4299
|
exports.findNonDisabledListIndex = findNonDisabledListIndex;
|
|
4251
|
-
exports.
|
|
4252
|
-
exports.
|
|
4300
|
+
exports.gecko = gecko;
|
|
4301
|
+
exports.getFloatingFocusElement = getFloatingFocusElement;
|
|
4253
4302
|
exports.getGridNavigatedIndex = getGridNavigatedIndex;
|
|
4254
4303
|
exports.getMaxListIndex = getMaxListIndex;
|
|
4255
4304
|
exports.getMinListIndex = getMinListIndex;
|
|
@@ -4260,10 +4309,17 @@ exports.getTabbableBeforeElement = getTabbableBeforeElement;
|
|
|
4260
4309
|
exports.inertValue = inertValue;
|
|
4261
4310
|
exports.isElementVisible = isElementVisible;
|
|
4262
4311
|
exports.isIndexOutOfListBounds = isIndexOutOfListBounds;
|
|
4312
|
+
exports.isInteractiveElement = isInteractiveElement;
|
|
4263
4313
|
exports.isListIndexDisabled = isListIndexDisabled;
|
|
4264
4314
|
exports.isOutsideEvent = isOutsideEvent;
|
|
4315
|
+
exports.isTargetInsideEnabledTrigger = isTargetInsideEnabledTrigger;
|
|
4316
|
+
exports.isTypeableCombobox = isTypeableCombobox;
|
|
4317
|
+
exports.isTypeableElement = isTypeableElement;
|
|
4318
|
+
exports.matchesFocusVisible = matchesFocusVisible;
|
|
4319
|
+
exports.mergeCleanups = mergeCleanups;
|
|
4265
4320
|
exports.popupStateMapping = popupStateMapping;
|
|
4266
4321
|
exports.pressableTriggerOpenStateMapping = pressableTriggerOpenStateMapping;
|
|
4322
|
+
exports.setPopupOpenState = setPopupOpenState;
|
|
4267
4323
|
exports.triggerOpenStateMapping = triggerOpenStateMapping;
|
|
4268
4324
|
exports.useClick = useClick;
|
|
4269
4325
|
exports.useDirection = useDirection;
|
|
@@ -4271,8 +4327,18 @@ exports.useDismiss = useDismiss;
|
|
|
4271
4327
|
exports.useFloatingNodeId = useFloatingNodeId;
|
|
4272
4328
|
exports.useFloatingParentNodeId = useFloatingParentNodeId;
|
|
4273
4329
|
exports.useFloatingTree = useFloatingTree;
|
|
4274
|
-
exports.
|
|
4330
|
+
exports.useImplicitActiveTrigger = useImplicitActiveTrigger;
|
|
4331
|
+
exports.useInitialOpenSync = useInitialOpenSync;
|
|
4275
4332
|
exports.useOnFirstRender = useOnFirstRender;
|
|
4276
4333
|
exports.useOpenInteractionType = useOpenInteractionType;
|
|
4334
|
+
exports.useOpenMethodTriggerProps = useOpenMethodTriggerProps;
|
|
4335
|
+
exports.useOpenStateTransitions = useOpenStateTransitions;
|
|
4336
|
+
exports.usePopupInteractionProps = usePopupInteractionProps;
|
|
4337
|
+
exports.usePopupRootSync = usePopupRootSync;
|
|
4338
|
+
exports.usePopupStore = usePopupStore;
|
|
4277
4339
|
exports.useScrollLock = useScrollLock;
|
|
4278
4340
|
exports.useStore = useStore;
|
|
4341
|
+
exports.useSyncedFloatingRootContext = useSyncedFloatingRootContext;
|
|
4342
|
+
exports.useTriggerDataForwarding = useTriggerDataForwarding;
|
|
4343
|
+
exports.useTriggerRegistration = useTriggerRegistration;
|
|
4344
|
+
exports.webkit = webkit;
|