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