@tamagui/focus-scope 2.7.7 → 3.0.0-beta.1093.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/cjs/FocusScope.cjs +345 -351
- package/dist/cjs/FocusScope.native.cjs +36 -0
- package/dist/cjs/FocusScope.native.js +20 -27
- package/dist/cjs/FocusScope.native.js.map +1 -1
- package/dist/cjs/FocusScopeController.cjs +47 -54
- package/dist/cjs/FocusScopeController.native.cjs +75 -0
- package/dist/cjs/FocusScopeController.native.js +49 -55
- package/dist/cjs/FocusScopeController.native.js.map +1 -1
- package/dist/cjs/index.cjs +9 -11
- package/dist/cjs/index.native.cjs +21 -0
- package/dist/cjs/index.native.js +9 -10
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/types.cjs +9 -11
- package/dist/cjs/types.native.cjs +18 -0
- package/dist/cjs/types.native.js +9 -10
- package/dist/cjs/types.native.js.map +1 -1
- package/dist/esm/FocusScope.mjs +331 -328
- package/dist/esm/FocusScope.mjs.map +1 -1
- package/dist/esm/FocusScope.native.js +6 -3
- package/dist/esm/FocusScope.native.js.map +1 -1
- package/dist/esm/FocusScopeController.mjs +29 -27
- package/dist/esm/FocusScopeController.mjs.map +1 -1
- package/dist/esm/FocusScopeController.native.js +32 -30
- package/dist/esm/FocusScopeController.native.js.map +1 -1
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.mjs +3 -3
- package/dist/esm/index.native.js +3 -3
- package/dist/esm/types.mjs +0 -2
- package/dist/esm/types.native.js +0 -2
- package/dist/jsx/FocusScope.mjs +331 -328
- package/dist/jsx/FocusScope.mjs.map +1 -1
- package/dist/jsx/FocusScope.native.cjs +36 -0
- package/dist/jsx/FocusScope.native.js +20 -27
- package/dist/jsx/FocusScope.native.js.map +1 -1
- package/dist/jsx/FocusScopeController.mjs +29 -27
- package/dist/jsx/FocusScopeController.mjs.map +1 -1
- package/dist/jsx/FocusScopeController.native.cjs +75 -0
- package/dist/jsx/FocusScopeController.native.js +49 -55
- package/dist/jsx/FocusScopeController.native.js.map +1 -1
- package/dist/jsx/index.js +3 -3
- package/dist/jsx/index.mjs +3 -3
- package/dist/jsx/index.native.cjs +21 -0
- package/dist/jsx/index.native.js +9 -10
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/types.mjs +0 -2
- package/dist/jsx/types.native.cjs +18 -0
- package/dist/jsx/types.native.js +9 -10
- package/dist/jsx/types.native.js.map +1 -1
- package/package.json +11 -9
- package/src/FocusScope.native.tsx +2 -1
- package/src/FocusScope.tsx +62 -28
- package/src/FocusScopeController.tsx +3 -0
- package/src/types.tsx +16 -11
- package/types/FocusScope.d.ts +3 -4
- package/types/FocusScope.d.ts.map +1 -1
- package/types/FocusScope.native.d.ts +1 -2
- package/types/FocusScope.native.d.ts.map +1 -1
- package/types/FocusScopeController.d.ts +1 -1
- package/types/FocusScopeController.d.ts.map +1 -1
- package/types/types.d.ts +15 -9
- package/types/types.d.ts.map +1 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/index.mjs.map +0 -1
- package/dist/esm/index.native.js.map +0 -1
- package/dist/esm/types.mjs.map +0 -1
- package/dist/esm/types.native.js.map +0 -1
- package/dist/jsx/index.js.map +0 -1
- package/dist/jsx/index.mjs.map +0 -1
- package/dist/jsx/types.mjs.map +0 -1
package/dist/cjs/FocusScope.cjs
CHANGED
|
@@ -5,391 +5,385 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
5
5
|
var __getProtoOf = Object.getPrototypeOf;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
7
|
var __export = (target, all) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
for (var name in all) __defProp(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true
|
|
11
|
+
});
|
|
12
12
|
};
|
|
13
13
|
var __copyProps = (to, from, except, desc) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
16
|
+
get: () => from[key],
|
|
17
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
21
|
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
28
|
-
value: mod,
|
|
29
|
-
enumerable: true
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
30
25
|
}) : target, mod));
|
|
31
|
-
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
32
|
-
value: true
|
|
33
|
-
}), mod);
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
34
27
|
var FocusScope_exports = {};
|
|
35
28
|
__export(FocusScope_exports, {
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
FocusScope: () => FocusScope,
|
|
30
|
+
useFocusScope: () => useFocusScope
|
|
38
31
|
});
|
|
39
32
|
module.exports = __toCommonJS(FocusScope_exports);
|
|
40
33
|
var import_compose_refs = require("@tamagui/compose-refs");
|
|
41
34
|
var import_constants = require("@tamagui/constants");
|
|
35
|
+
var import_core = require("@tamagui/core");
|
|
42
36
|
var import_use_async = require("@tamagui/use-async");
|
|
43
37
|
var import_use_event = require("@tamagui/use-event");
|
|
44
38
|
var React = __toESM(require("react"), 1);
|
|
45
39
|
var import_FocusScopeController = require("./FocusScopeController.cjs");
|
|
46
40
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
};
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
|
|
74
|
-
children: mergedProps.children(childProps)
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
return React.cloneElement(React.Children.only(mergedProps.children), childProps);
|
|
41
|
+
const FOCUS_SCOPE_STYLE = { display: "contents" };
|
|
42
|
+
const FocusScope = (0, import_compose_refs.createRefComponent)(function FocusScope2({ __scopeFocusScope, ...props }, forwardedRef) {
|
|
43
|
+
const context = (0, import_FocusScopeController.useFocusScopeControllerContext)("FocusScope", __scopeFocusScope, {
|
|
44
|
+
warn: false,
|
|
45
|
+
fallback: {}
|
|
46
|
+
});
|
|
47
|
+
const mergedProps = {
|
|
48
|
+
...props,
|
|
49
|
+
enabled: context.enabled ?? props.enabled,
|
|
50
|
+
loop: context.loop ?? props.loop,
|
|
51
|
+
trapped: context.trapped ?? props.trapped,
|
|
52
|
+
noFocus: context.noFocus ?? props.noFocus,
|
|
53
|
+
onMountAutoFocus: context.onMountAutoFocus ?? props.onMountAutoFocus,
|
|
54
|
+
onUnmountAutoFocus: context.onUnmountAutoFocus ?? props.onUnmountAutoFocus,
|
|
55
|
+
forceUnmount: context.forceUnmount ?? props.forceUnmount,
|
|
56
|
+
focusOnIdle: context.focusOnIdle ?? props.focusOnIdle
|
|
57
|
+
};
|
|
58
|
+
const childProps = useFocusScope(mergedProps, forwardedRef);
|
|
59
|
+
const { ref, style, ...scopeProps } = childProps;
|
|
60
|
+
const children = mergedProps.children;
|
|
61
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
62
|
+
...scopeProps,
|
|
63
|
+
ref,
|
|
64
|
+
style: getFocusScopeStyle(style),
|
|
65
|
+
children
|
|
66
|
+
});
|
|
78
67
|
});
|
|
68
|
+
function getFocusScopeStyle(style) {
|
|
69
|
+
if (!style) return FOCUS_SCOPE_STYLE;
|
|
70
|
+
return {
|
|
71
|
+
...style,
|
|
72
|
+
display: "contents"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
79
75
|
function setupFocusTrap(container, lastFocusedElementRef, focusScope) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
document.removeEventListener("focusout", handleFocusOut);
|
|
128
|
-
};
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
let rafId = null;
|
|
78
|
+
function scheduleRefocus() {
|
|
79
|
+
if (rafId) return;
|
|
80
|
+
const elementToFocus = lastFocusedElementRef.current;
|
|
81
|
+
rafId = requestAnimationFrame(() => {
|
|
82
|
+
rafId = null;
|
|
83
|
+
if (focusScope.paused) return;
|
|
84
|
+
if (focusScope.stopped) return;
|
|
85
|
+
if (!container.isConnected) return;
|
|
86
|
+
if (!container.contains(document.activeElement)) {
|
|
87
|
+
focus(elementToFocus);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function handleFocusIn(event) {
|
|
92
|
+
if (focusScope.paused) return;
|
|
93
|
+
const target = event.target;
|
|
94
|
+
if (container.contains(target)) {
|
|
95
|
+
target?.addEventListener("blur", handleBlur, { signal: controller.signal });
|
|
96
|
+
lastFocusedElementRef.current = target;
|
|
97
|
+
} else if (target?.contains(container)) {} else {
|
|
98
|
+
scheduleRefocus();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function handleFocusOut(event) {
|
|
102
|
+
controller.abort();
|
|
103
|
+
if (focusScope.paused) return;
|
|
104
|
+
const relatedTarget = event.relatedTarget;
|
|
105
|
+
if (relatedTarget === null) return;
|
|
106
|
+
if (relatedTarget.contains(container)) return;
|
|
107
|
+
if (!container.contains(relatedTarget)) {
|
|
108
|
+
scheduleRefocus();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function handleBlur() {}
|
|
112
|
+
document.addEventListener("focusin", handleFocusIn);
|
|
113
|
+
document.addEventListener("focusout", handleFocusOut);
|
|
114
|
+
return () => {
|
|
115
|
+
if (rafId) {
|
|
116
|
+
cancelAnimationFrame(rafId);
|
|
117
|
+
rafId = null;
|
|
118
|
+
}
|
|
119
|
+
controller.abort();
|
|
120
|
+
document.removeEventListener("focusin", handleFocusIn);
|
|
121
|
+
document.removeEventListener("focusout", handleFocusOut);
|
|
122
|
+
};
|
|
129
123
|
}
|
|
130
124
|
function useFocusScope(props, forwardedRef) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
125
|
+
const { loop = false, enabled = true, trapped: trappedProp = false, noFocus = false, onMountAutoFocus: onMountAutoFocusProp, onUnmountAutoFocus: onUnmountAutoFocusProp, forceUnmount, focusOnIdle = true, children: _children, asChild: _asChild, ...scopeProps } = props;
|
|
126
|
+
const trapped = noFocus ? false : trappedProp;
|
|
127
|
+
const [container, setContainer] = React.useState(null);
|
|
128
|
+
const containerRef = React.useRef(null);
|
|
129
|
+
const onMountAutoFocus = (0, import_use_event.useEvent)(onMountAutoFocusProp);
|
|
130
|
+
const onUnmountAutoFocus = (0, import_use_event.useEvent)(onUnmountAutoFocusProp);
|
|
131
|
+
const lastFocusedElementRef = React.useRef(null);
|
|
132
|
+
const focusScopeRef = React.useRef({
|
|
133
|
+
paused: false,
|
|
134
|
+
stopped: false,
|
|
135
|
+
pause() {
|
|
136
|
+
this.paused = true;
|
|
137
|
+
},
|
|
138
|
+
resume() {
|
|
139
|
+
this.paused = false;
|
|
140
|
+
},
|
|
141
|
+
stop() {
|
|
142
|
+
this.stopped = true;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const focusScope = focusScopeRef.current;
|
|
146
|
+
const trapCleanupRef = React.useRef(null);
|
|
147
|
+
const setContainerRef = React.useCallback((node) => {
|
|
148
|
+
if (trapCleanupRef.current) {
|
|
149
|
+
trapCleanupRef.current();
|
|
150
|
+
trapCleanupRef.current = null;
|
|
151
|
+
}
|
|
152
|
+
containerRef.current = node;
|
|
153
|
+
if (import_constants.isWeb && node && enabled && trapped) {
|
|
154
|
+
trapCleanupRef.current = setupFocusTrap(node, lastFocusedElementRef, focusScopeRef.current);
|
|
155
|
+
}
|
|
156
|
+
setContainer(node);
|
|
157
|
+
}, [enabled, trapped]);
|
|
158
|
+
const composedRefs = (0, import_compose_refs.useComposedRefs)(forwardedRef, setContainerRef);
|
|
159
|
+
(0, import_constants.useIsomorphicLayoutEffect)(() => {
|
|
160
|
+
if (!trapped) {
|
|
161
|
+
focusScope.stopped = true;
|
|
162
|
+
if (trapCleanupRef.current) {
|
|
163
|
+
trapCleanupRef.current();
|
|
164
|
+
trapCleanupRef.current = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return () => {
|
|
168
|
+
if (trapCleanupRef.current) {
|
|
169
|
+
trapCleanupRef.current();
|
|
170
|
+
trapCleanupRef.current = null;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}, [trapped, focusScope]);
|
|
174
|
+
(0, import_constants.useIsomorphicLayoutEffect)(() => {
|
|
175
|
+
if (!import_constants.isWeb) return;
|
|
176
|
+
if (!enabled || !noFocus || !container) return;
|
|
177
|
+
const blurActiveElement = () => {
|
|
178
|
+
const active = document.activeElement;
|
|
179
|
+
if (active && active !== document.body && typeof active.blur === "function") {
|
|
180
|
+
active.blur();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
blurActiveElement();
|
|
184
|
+
document.addEventListener("focusin", blurActiveElement, true);
|
|
185
|
+
return () => {
|
|
186
|
+
document.removeEventListener("focusin", blurActiveElement, true);
|
|
187
|
+
};
|
|
188
|
+
}, [
|
|
189
|
+
enabled,
|
|
190
|
+
noFocus,
|
|
191
|
+
container
|
|
192
|
+
]);
|
|
193
|
+
(0, import_use_async.useAsyncEffect)(async (signal) => {
|
|
194
|
+
if (!enabled) return;
|
|
195
|
+
if (!container) return;
|
|
196
|
+
if (forceUnmount) return;
|
|
197
|
+
if (noFocus) return;
|
|
198
|
+
if (trapped) {
|
|
199
|
+
focusScope.stopped = false;
|
|
200
|
+
}
|
|
201
|
+
focusScopesStack.add(focusScope);
|
|
202
|
+
const previouslyFocusedElement = document.activeElement;
|
|
203
|
+
const hasFocusedCandidate = container.contains(previouslyFocusedElement) && previouslyFocusedElement !== null && !isHidden(previouslyFocusedElement, { upTo: container });
|
|
204
|
+
if (!hasFocusedCandidate) {
|
|
205
|
+
const details = (0, import_core.createChangeEventDetails)("trigger-focus");
|
|
206
|
+
onMountAutoFocus?.(details);
|
|
207
|
+
if (!details.isCanceled) {
|
|
208
|
+
if (focusOnIdle) {
|
|
209
|
+
await (0, import_use_async.idle)(signal, typeof focusOnIdle == "object" ? focusOnIdle : {
|
|
210
|
+
max: 200,
|
|
211
|
+
min: typeof focusOnIdle == "number" ? focusOnIdle : 16
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
const allCandidates = getTabbableCandidates(container);
|
|
215
|
+
const linkedRemoved = removeLinks(allCandidates);
|
|
216
|
+
const visibleCandidates = linkedRemoved.filter((candidate) => !isHidden(candidate, { upTo: container }));
|
|
217
|
+
focusFirst(visibleCandidates, { select: true });
|
|
218
|
+
if (visibleCandidates.length > 0) {
|
|
219
|
+
lastFocusedElementRef.current = visibleCandidates[0];
|
|
220
|
+
} else {
|
|
221
|
+
lastFocusedElementRef.current = container;
|
|
222
|
+
}
|
|
223
|
+
if (document.activeElement === previouslyFocusedElement && visibleCandidates.length === 0) {
|
|
224
|
+
focus(container);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return () => {
|
|
229
|
+
focusScope.stop();
|
|
230
|
+
const details = (0, import_core.createChangeEventDetails)("focus-out");
|
|
231
|
+
onUnmountAutoFocus?.(details);
|
|
232
|
+
if (!details.isCanceled) {
|
|
233
|
+
const currentFocus = document.activeElement;
|
|
234
|
+
const focusHasMovedOutside = currentFocus && currentFocus !== document.body && currentFocus !== container && !container.contains(currentFocus);
|
|
235
|
+
if (!focusHasMovedOutside) {
|
|
236
|
+
focus(previouslyFocusedElement ?? document.body, { select: true });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
focusScopesStack.remove(focusScope);
|
|
240
|
+
};
|
|
241
|
+
}, [
|
|
242
|
+
enabled,
|
|
243
|
+
container,
|
|
244
|
+
forceUnmount,
|
|
245
|
+
noFocus,
|
|
246
|
+
onMountAutoFocus,
|
|
247
|
+
onUnmountAutoFocus,
|
|
248
|
+
focusScope,
|
|
249
|
+
focusOnIdle
|
|
250
|
+
]);
|
|
251
|
+
const handleKeyDown = React.useCallback((event) => {
|
|
252
|
+
if (!trapped) return;
|
|
253
|
+
if (!loop) return;
|
|
254
|
+
if (focusScope.paused) return;
|
|
255
|
+
if (!enabled) return;
|
|
256
|
+
if (!container) return;
|
|
257
|
+
const isTabKey = event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
258
|
+
const focusedElement = document.activeElement;
|
|
259
|
+
if (isTabKey && focusedElement) {
|
|
260
|
+
const [first, last] = getTabbableEdges(container);
|
|
261
|
+
const hasTabbableElementsInside = first && last;
|
|
262
|
+
if (!hasTabbableElementsInside) {
|
|
263
|
+
if (focusedElement === container) event.preventDefault();
|
|
264
|
+
} else {
|
|
265
|
+
if (!event.shiftKey && focusedElement === last) {
|
|
266
|
+
event.preventDefault();
|
|
267
|
+
if (loop) focus(first, { select: true });
|
|
268
|
+
} else if (event.shiftKey && focusedElement === first) {
|
|
269
|
+
event.preventDefault();
|
|
270
|
+
if (loop) focus(last, { select: true });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}, [
|
|
275
|
+
loop,
|
|
276
|
+
trapped,
|
|
277
|
+
focusScope.paused,
|
|
278
|
+
enabled,
|
|
279
|
+
container
|
|
280
|
+
]);
|
|
281
|
+
React.useEffect(() => {
|
|
282
|
+
if (!container) return;
|
|
283
|
+
if (!trapped) return;
|
|
284
|
+
if (!loop) return;
|
|
285
|
+
if (!enabled) return;
|
|
286
|
+
const handleKeyDownCapture = (event) => {
|
|
287
|
+
if (event.key === "Tab") {
|
|
288
|
+
handleKeyDown(event);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
container.addEventListener("keydown", handleKeyDownCapture, true);
|
|
292
|
+
return () => {
|
|
293
|
+
container.removeEventListener("keydown", handleKeyDownCapture, true);
|
|
294
|
+
};
|
|
295
|
+
}, [
|
|
296
|
+
container,
|
|
297
|
+
trapped,
|
|
298
|
+
loop,
|
|
299
|
+
enabled,
|
|
300
|
+
handleKeyDown
|
|
301
|
+
]);
|
|
302
|
+
const existingOnKeyDown = scopeProps.onKeyDown;
|
|
303
|
+
const composedOnKeyDown = React.useCallback((event) => {
|
|
304
|
+
existingOnKeyDown?.(event);
|
|
305
|
+
}, [existingOnKeyDown]);
|
|
306
|
+
return {
|
|
307
|
+
...scopeProps,
|
|
308
|
+
ref: composedRefs,
|
|
309
|
+
onKeyDown: composedOnKeyDown
|
|
310
|
+
};
|
|
303
311
|
}
|
|
304
|
-
function focusFirst(candidates, {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
select
|
|
311
|
-
});
|
|
312
|
-
if (document.activeElement !== previouslyFocusedElement) return;
|
|
313
|
-
}
|
|
312
|
+
function focusFirst(candidates, { select = false } = {}) {
|
|
313
|
+
const previouslyFocusedElement = document.activeElement;
|
|
314
|
+
for (const candidate of candidates) {
|
|
315
|
+
focus(candidate, { select });
|
|
316
|
+
if (document.activeElement !== previouslyFocusedElement) return;
|
|
317
|
+
}
|
|
314
318
|
}
|
|
315
319
|
function getTabbableEdges(container) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
+
const candidates = getTabbableCandidates(container);
|
|
321
|
+
const first = findVisible(candidates, container);
|
|
322
|
+
const last = findVisible(candidates.reverse(), container);
|
|
323
|
+
return [first, last];
|
|
320
324
|
}
|
|
321
325
|
function getTabbableCandidates(container) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
while (walker.nextNode()) nodes.push(walker.currentNode);
|
|
331
|
-
return nodes;
|
|
326
|
+
const nodes = [];
|
|
327
|
+
const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { acceptNode: (node) => {
|
|
328
|
+
const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden";
|
|
329
|
+
if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;
|
|
330
|
+
return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
|
|
331
|
+
} });
|
|
332
|
+
while (walker.nextNode()) nodes.push(walker.currentNode);
|
|
333
|
+
return nodes;
|
|
332
334
|
}
|
|
333
335
|
function findVisible(elements, container) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
})) return element;
|
|
338
|
-
}
|
|
336
|
+
for (const element of elements) {
|
|
337
|
+
if (!isHidden(element, { upTo: container })) return element;
|
|
338
|
+
}
|
|
339
339
|
}
|
|
340
|
-
function isHidden(node, {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
return false;
|
|
340
|
+
function isHidden(node, { upTo }) {
|
|
341
|
+
if (getComputedStyle(node).visibility === "hidden") return true;
|
|
342
|
+
while (node) {
|
|
343
|
+
if (upTo !== void 0 && node === upTo) return false;
|
|
344
|
+
if (getComputedStyle(node).display === "none") return true;
|
|
345
|
+
node = node.parentElement;
|
|
346
|
+
}
|
|
347
|
+
return false;
|
|
350
348
|
}
|
|
351
349
|
function isSelectableInput(element) {
|
|
352
|
-
|
|
350
|
+
return element instanceof HTMLInputElement && "select" in element;
|
|
353
351
|
}
|
|
354
|
-
function focus(element, {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
});
|
|
363
|
-
if (element !== previouslyFocusedElement && isSelectableInput(element) && select) element.select();
|
|
364
|
-
} catch (error) {}
|
|
365
|
-
}
|
|
352
|
+
function focus(element, { select = false } = {}) {
|
|
353
|
+
if (element?.focus) {
|
|
354
|
+
const previouslyFocusedElement = document.activeElement;
|
|
355
|
+
try {
|
|
356
|
+
element.focus({ preventScroll: true });
|
|
357
|
+
if (element !== previouslyFocusedElement && isSelectableInput(element) && select) element.select();
|
|
358
|
+
} catch (error) {}
|
|
359
|
+
}
|
|
366
360
|
}
|
|
367
361
|
const focusScopesStack = createFocusScopesStack();
|
|
368
362
|
function createFocusScopesStack() {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
363
|
+
let stack = [];
|
|
364
|
+
return {
|
|
365
|
+
add(focusScope) {
|
|
366
|
+
const activeFocusScope = stack[0];
|
|
367
|
+
if (focusScope !== activeFocusScope) {
|
|
368
|
+
activeFocusScope?.pause();
|
|
369
|
+
}
|
|
370
|
+
stack = arrayRemove(stack, focusScope);
|
|
371
|
+
stack.unshift(focusScope);
|
|
372
|
+
},
|
|
373
|
+
remove(focusScope) {
|
|
374
|
+
stack = arrayRemove(stack, focusScope);
|
|
375
|
+
stack[0]?.resume();
|
|
376
|
+
}
|
|
377
|
+
};
|
|
384
378
|
}
|
|
385
379
|
function arrayRemove(array, item) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
380
|
+
const updatedArray = [...array];
|
|
381
|
+
const index = updatedArray.indexOf(item);
|
|
382
|
+
if (index !== -1) {
|
|
383
|
+
updatedArray.splice(index, 1);
|
|
384
|
+
}
|
|
385
|
+
return updatedArray;
|
|
392
386
|
}
|
|
393
387
|
function removeLinks(items) {
|
|
394
|
-
|
|
395
|
-
}
|
|
388
|
+
return items.filter((item) => item.tagName !== "A");
|
|
389
|
+
}
|