@tamagui/focus-scope 2.7.6 → 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.
Files changed (69) hide show
  1. package/dist/cjs/FocusScope.cjs +346 -351
  2. package/dist/cjs/FocusScope.native.cjs +37 -0
  3. package/dist/cjs/FocusScope.native.js +21 -27
  4. package/dist/cjs/FocusScope.native.js.map +1 -1
  5. package/dist/cjs/FocusScopeController.cjs +48 -54
  6. package/dist/cjs/FocusScopeController.native.cjs +76 -0
  7. package/dist/cjs/FocusScopeController.native.js +50 -55
  8. package/dist/cjs/FocusScopeController.native.js.map +1 -1
  9. package/dist/cjs/index.cjs +10 -11
  10. package/dist/cjs/index.native.cjs +22 -0
  11. package/dist/cjs/index.native.js +10 -10
  12. package/dist/cjs/index.native.js.map +1 -1
  13. package/dist/cjs/types.cjs +10 -11
  14. package/dist/cjs/types.native.cjs +19 -0
  15. package/dist/cjs/types.native.js +10 -10
  16. package/dist/cjs/types.native.js.map +1 -1
  17. package/dist/esm/FocusScope.mjs +331 -328
  18. package/dist/esm/FocusScope.mjs.map +1 -1
  19. package/dist/esm/FocusScope.native.js +6 -3
  20. package/dist/esm/FocusScope.native.js.map +1 -1
  21. package/dist/esm/FocusScopeController.mjs +29 -27
  22. package/dist/esm/FocusScopeController.mjs.map +1 -1
  23. package/dist/esm/FocusScopeController.native.js +32 -30
  24. package/dist/esm/FocusScopeController.native.js.map +1 -1
  25. package/dist/esm/index.js +3 -3
  26. package/dist/esm/index.mjs +3 -3
  27. package/dist/esm/index.native.js +3 -3
  28. package/dist/esm/types.mjs +0 -2
  29. package/dist/esm/types.native.js +0 -2
  30. package/dist/jsx/FocusScope.mjs +331 -328
  31. package/dist/jsx/FocusScope.mjs.map +1 -1
  32. package/dist/jsx/FocusScope.native.cjs +37 -0
  33. package/dist/jsx/FocusScope.native.js +21 -27
  34. package/dist/jsx/FocusScope.native.js.map +1 -1
  35. package/dist/jsx/FocusScopeController.mjs +29 -27
  36. package/dist/jsx/FocusScopeController.mjs.map +1 -1
  37. package/dist/jsx/FocusScopeController.native.cjs +76 -0
  38. package/dist/jsx/FocusScopeController.native.js +50 -55
  39. package/dist/jsx/FocusScopeController.native.js.map +1 -1
  40. package/dist/jsx/index.js +3 -3
  41. package/dist/jsx/index.mjs +3 -3
  42. package/dist/jsx/index.native.cjs +22 -0
  43. package/dist/jsx/index.native.js +10 -10
  44. package/dist/jsx/index.native.js.map +1 -1
  45. package/dist/jsx/types.mjs +0 -2
  46. package/dist/jsx/types.native.cjs +19 -0
  47. package/dist/jsx/types.native.js +10 -10
  48. package/dist/jsx/types.native.js.map +1 -1
  49. package/package.json +9 -8
  50. package/src/FocusScope.native.tsx +2 -1
  51. package/src/FocusScope.tsx +62 -28
  52. package/src/FocusScopeController.tsx +3 -0
  53. package/src/types.tsx +16 -11
  54. package/types/FocusScope.d.ts +3 -4
  55. package/types/FocusScope.d.ts.map +1 -1
  56. package/types/FocusScope.native.d.ts +1 -2
  57. package/types/FocusScope.native.d.ts.map +1 -1
  58. package/types/FocusScopeController.d.ts +1 -1
  59. package/types/FocusScopeController.d.ts.map +1 -1
  60. package/types/types.d.ts +15 -9
  61. package/types/types.d.ts.map +1 -1
  62. package/dist/esm/index.js.map +0 -1
  63. package/dist/esm/index.mjs.map +0 -1
  64. package/dist/esm/index.native.js.map +0 -1
  65. package/dist/esm/types.mjs.map +0 -1
  66. package/dist/esm/types.native.js.map +0 -1
  67. package/dist/jsx/index.js.map +0 -1
  68. package/dist/jsx/index.mjs.map +0 -1
  69. package/dist/jsx/types.mjs.map +0 -1
@@ -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
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: true
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
- 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;
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
- // If the importer is in node compatibility mode or this is not an ESM
24
- // file that has been converted to a CommonJS file using a Babel-
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
- FocusScope: () => FocusScope,
37
- useFocusScope: () => useFocusScope
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 AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount";
48
- const AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount";
49
- const EVENT_OPTIONS = {
50
- bubbles: false,
51
- cancelable: true
52
- };
53
- const FocusScope = React.forwardRef(function FocusScope2({
54
- __scopeFocusScope,
55
- ...props
56
- }, forwardedRef) {
57
- const context = (0, import_FocusScopeController.useFocusScopeControllerContext)("FocusScope", __scopeFocusScope, {
58
- warn: false,
59
- fallback: {}
60
- });
61
- const mergedProps = {
62
- ...props,
63
- enabled: context.enabled ?? props.enabled,
64
- loop: context.loop ?? props.loop,
65
- trapped: context.trapped ?? props.trapped,
66
- onMountAutoFocus: context.onMountAutoFocus ?? props.onMountAutoFocus,
67
- onUnmountAutoFocus: context.onUnmountAutoFocus ?? props.onUnmountAutoFocus,
68
- forceUnmount: context.forceUnmount ?? props.forceUnmount,
69
- focusOnIdle: context.focusOnIdle ?? props.focusOnIdle
70
- };
71
- const childProps = useFocusScope(mergedProps, forwardedRef);
72
- if (typeof mergedProps.children === "function") {
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
- const controller = new AbortController();
81
- let rafId = null;
82
- function scheduleRefocus() {
83
- if (rafId) return;
84
- const elementToFocus = lastFocusedElementRef.current;
85
- rafId = requestAnimationFrame(() => {
86
- rafId = null;
87
- if (focusScope.paused) return;
88
- if (focusScope.stopped) return;
89
- if (!container.isConnected) return;
90
- if (!container.contains(document.activeElement)) {
91
- focus(elementToFocus);
92
- }
93
- });
94
- }
95
- function handleFocusIn(event) {
96
- if (focusScope.paused) return;
97
- const target = event.target;
98
- if (container.contains(target)) {
99
- target?.addEventListener("blur", handleBlur, {
100
- signal: controller.signal
101
- });
102
- lastFocusedElementRef.current = target;
103
- } else if (target?.contains(container)) {} else {
104
- scheduleRefocus();
105
- }
106
- }
107
- function handleFocusOut(event) {
108
- controller.abort();
109
- if (focusScope.paused) return;
110
- const relatedTarget = event.relatedTarget;
111
- if (relatedTarget === null) return;
112
- if (relatedTarget.contains(container)) return;
113
- if (!container.contains(relatedTarget)) {
114
- scheduleRefocus();
115
- }
116
- }
117
- function handleBlur() {}
118
- document.addEventListener("focusin", handleFocusIn);
119
- document.addEventListener("focusout", handleFocusOut);
120
- return () => {
121
- if (rafId) {
122
- cancelAnimationFrame(rafId);
123
- rafId = null;
124
- }
125
- controller.abort();
126
- document.removeEventListener("focusin", handleFocusIn);
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
- const {
132
- loop = false,
133
- enabled = true,
134
- trapped = false,
135
- onMountAutoFocus: onMountAutoFocusProp,
136
- onUnmountAutoFocus: onUnmountAutoFocusProp,
137
- forceUnmount,
138
- focusOnIdle = true,
139
- children,
140
- ...scopeProps
141
- } = props;
142
- const [container, setContainer] = React.useState(null);
143
- const containerRef = React.useRef(null);
144
- const onMountAutoFocus = (0, import_use_event.useEvent)(onMountAutoFocusProp);
145
- const onUnmountAutoFocus = (0, import_use_event.useEvent)(onUnmountAutoFocusProp);
146
- const lastFocusedElementRef = React.useRef(null);
147
- const focusScopeRef = React.useRef({
148
- paused: false,
149
- stopped: false,
150
- // set to true when cleanup starts, signals trap to stop
151
- pause() {
152
- this.paused = true;
153
- },
154
- resume() {
155
- this.paused = false;
156
- },
157
- stop() {
158
- this.stopped = true;
159
- }
160
- });
161
- const focusScope = focusScopeRef.current;
162
- const trapCleanupRef = React.useRef(null);
163
- const setContainerRef = React.useCallback(node => {
164
- if (trapCleanupRef.current) {
165
- trapCleanupRef.current();
166
- trapCleanupRef.current = null;
167
- }
168
- containerRef.current = node;
169
- if (import_constants.isWeb && node && enabled && trapped) {
170
- trapCleanupRef.current = setupFocusTrap(node, lastFocusedElementRef, focusScopeRef.current);
171
- }
172
- setContainer(node);
173
- }, [enabled, trapped]);
174
- const composedRefs = (0, import_compose_refs.useComposedRefs)(forwardedRef, setContainerRef);
175
- (0, import_constants.useIsomorphicLayoutEffect)(() => {
176
- if (!trapped) {
177
- focusScope.stopped = true;
178
- if (trapCleanupRef.current) {
179
- trapCleanupRef.current();
180
- trapCleanupRef.current = null;
181
- }
182
- }
183
- return () => {
184
- if (trapCleanupRef.current) {
185
- trapCleanupRef.current();
186
- trapCleanupRef.current = null;
187
- }
188
- };
189
- }, [trapped, focusScope]);
190
- (0, import_use_async.useAsyncEffect)(async signal => {
191
- if (!enabled) return;
192
- if (!container) return;
193
- if (forceUnmount) return;
194
- if (trapped) {
195
- focusScope.stopped = false;
196
- }
197
- focusScopesStack.add(focusScope);
198
- const previouslyFocusedElement = document.activeElement;
199
- const hasFocusedCandidate = container.contains(previouslyFocusedElement) && previouslyFocusedElement !== null && !isHidden(previouslyFocusedElement, {
200
- upTo: container
201
- });
202
- if (!hasFocusedCandidate) {
203
- const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);
204
- container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
205
- container.dispatchEvent(mountEvent);
206
- if (!mountEvent.defaultPrevented) {
207
- if (focusOnIdle) {
208
- await (0, import_use_async.idle)(signal, typeof focusOnIdle == "object" ? focusOnIdle : {
209
- // we can't wait too long or else user can take an action and then we focus
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, {
217
- upTo: container
218
- }));
219
- focusFirst(visibleCandidates, {
220
- select: true
221
- });
222
- if (visibleCandidates.length > 0) {
223
- lastFocusedElementRef.current = visibleCandidates[0];
224
- } else {
225
- lastFocusedElementRef.current = container;
226
- }
227
- if (document.activeElement === previouslyFocusedElement && visibleCandidates.length === 0) {
228
- focus(container);
229
- }
230
- }
231
- }
232
- return () => {
233
- focusScope.stop();
234
- container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
235
- const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);
236
- container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
237
- container.dispatchEvent(unmountEvent);
238
- if (!unmountEvent.defaultPrevented) {
239
- const currentFocus = document.activeElement;
240
- const focusHasMovedOutside = currentFocus && currentFocus !== document.body && currentFocus !== container && !container.contains(currentFocus);
241
- if (!focusHasMovedOutside) {
242
- focus(previouslyFocusedElement ?? document.body, {
243
- select: true
244
- });
245
- }
246
- }
247
- container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
248
- focusScopesStack.remove(focusScope);
249
- };
250
- }, [enabled, container, forceUnmount, onMountAutoFocus, onUnmountAutoFocus, focusScope, focusOnIdle]);
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, {
268
- select: true
269
- });
270
- } else if (event.shiftKey && focusedElement === first) {
271
- event.preventDefault();
272
- if (loop) focus(last, {
273
- select: true
274
- });
275
- }
276
- }
277
- }
278
- }, [loop, trapped, focusScope.paused, enabled, container]);
279
- React.useEffect(() => {
280
- if (!container) return;
281
- if (!trapped) return;
282
- if (!loop) return;
283
- if (!enabled) return;
284
- const handleKeyDownCapture = event => {
285
- if (event.key === "Tab") {
286
- handleKeyDown(event);
287
- }
288
- };
289
- container.addEventListener("keydown", handleKeyDownCapture, true);
290
- return () => {
291
- container.removeEventListener("keydown", handleKeyDownCapture, true);
292
- };
293
- }, [container, trapped, loop, enabled, handleKeyDown]);
294
- const existingOnKeyDown = scopeProps.onKeyDown;
295
- const composedOnKeyDown = React.useCallback(event => {
296
- existingOnKeyDown?.(event);
297
- }, [existingOnKeyDown]);
298
- return {
299
- ...scopeProps,
300
- ref: composedRefs,
301
- onKeyDown: composedOnKeyDown
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
- select = false
306
- } = {}) {
307
- const previouslyFocusedElement = document.activeElement;
308
- for (const candidate of candidates) {
309
- focus(candidate, {
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
- const candidates = getTabbableCandidates(container);
317
- const first = findVisible(candidates, container);
318
- const last = findVisible(candidates.reverse(), container);
319
- return [first, last];
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
- const nodes = [];
323
- const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
324
- acceptNode: node => {
325
- const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden";
326
- if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;
327
- return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
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
- for (const element of elements) {
335
- if (!isHidden(element, {
336
- upTo: container
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
- upTo
342
- }) {
343
- if (getComputedStyle(node).visibility === "hidden") return true;
344
- while (node) {
345
- if (upTo !== void 0 && node === upTo) return false;
346
- if (getComputedStyle(node).display === "none") return true;
347
- node = node.parentElement;
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
- return element instanceof HTMLInputElement && "select" in element;
351
+ return element instanceof HTMLInputElement && "select" in element;
353
352
  }
354
- function focus(element, {
355
- select = false
356
- } = {}) {
357
- if (element?.focus) {
358
- const previouslyFocusedElement = document.activeElement;
359
- try {
360
- element.focus({
361
- preventScroll: true
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
- let stack = [];
370
- return {
371
- add(focusScope) {
372
- const activeFocusScope = stack[0];
373
- if (focusScope !== activeFocusScope) {
374
- activeFocusScope?.pause();
375
- }
376
- stack = arrayRemove(stack, focusScope);
377
- stack.unshift(focusScope);
378
- },
379
- remove(focusScope) {
380
- stack = arrayRemove(stack, focusScope);
381
- stack[0]?.resume();
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
- const updatedArray = [...array];
387
- const index = updatedArray.indexOf(item);
388
- if (index !== -1) {
389
- updatedArray.splice(index, 1);
390
- }
391
- return updatedArray;
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
- return items.filter(item => item.tagName !== "A");
395
- }
389
+ return items.filter((item) => item.tagName !== "A");
390
+ }