@mortenbrudvik/waveui 0.10.0 → 0.11.0
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/README.md +357 -72
- package/dist/components/button/Toolbar.cjs +1 -1
- package/dist/components/button/Toolbar.cjs.map +1 -1
- package/dist/components/button/Toolbar.mjs +25 -30
- package/dist/components/button/Toolbar.mjs.map +1 -1
- package/dist/components/button/Toolbar.navigation.cjs +2 -0
- package/dist/components/button/Toolbar.navigation.cjs.map +1 -0
- package/dist/components/button/Toolbar.navigation.mjs +17 -0
- package/dist/components/button/Toolbar.navigation.mjs.map +1 -0
- package/dist/components/data-display/Avatar.cjs +1 -1
- package/dist/components/data-display/Avatar.cjs.map +1 -1
- package/dist/components/data-display/Avatar.context.cjs +2 -0
- package/dist/components/data-display/Avatar.context.cjs.map +1 -0
- package/dist/components/data-display/Avatar.context.mjs +9 -0
- package/dist/components/data-display/Avatar.context.mjs.map +1 -0
- package/dist/components/data-display/Avatar.mjs +66 -61
- package/dist/components/data-display/Avatar.mjs.map +1 -1
- package/dist/components/data-display/InteractionTag.cjs +2 -0
- package/dist/components/data-display/InteractionTag.cjs.map +1 -0
- package/dist/components/data-display/InteractionTag.mjs +234 -0
- package/dist/components/data-display/InteractionTag.mjs.map +1 -0
- package/dist/components/data-display/Tag.cjs +1 -1
- package/dist/components/data-display/Tag.cjs.map +1 -1
- package/dist/components/data-display/Tag.mjs +222 -90
- package/dist/components/data-display/Tag.mjs.map +1 -1
- package/dist/components/data-display/TagGroup.cjs +2 -0
- package/dist/components/data-display/TagGroup.cjs.map +1 -0
- package/dist/components/data-display/TagGroup.context.cjs +2 -0
- package/dist/components/data-display/TagGroup.context.cjs.map +1 -0
- package/dist/components/data-display/TagGroup.context.mjs +113 -0
- package/dist/components/data-display/TagGroup.context.mjs.map +1 -0
- package/dist/components/data-display/TagGroup.mjs +149 -0
- package/dist/components/data-display/TagGroup.mjs.map +1 -0
- package/dist/components/data-display/tagStyles.cjs +2 -0
- package/dist/components/data-display/tagStyles.cjs.map +1 -0
- package/dist/components/data-display/tagStyles.mjs +108 -0
- package/dist/components/data-display/tagStyles.mjs.map +1 -0
- package/dist/components/feedback/Toaster.cjs +1 -1
- package/dist/components/feedback/Toaster.cjs.map +1 -1
- package/dist/components/feedback/Toaster.mjs +213 -181
- package/dist/components/feedback/Toaster.mjs.map +1 -1
- package/dist/components/input/Combobox.expand.cjs +1 -1
- package/dist/components/input/Combobox.expand.cjs.map +1 -1
- package/dist/components/input/Combobox.expand.mjs +7 -7
- package/dist/components/input/Combobox.expand.mjs.map +1 -1
- package/dist/components/input/Option.cjs +1 -1
- package/dist/components/input/Option.cjs.map +1 -1
- package/dist/components/input/Option.mjs +16 -11
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.cjs.map +1 -1
- package/dist/components/input/SwatchPicker.context.cjs.map +1 -1
- package/dist/components/input/SwatchPicker.context.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.mjs.map +1 -1
- package/dist/components/input/TagPicker.cjs +1 -1
- package/dist/components/input/TagPicker.cjs.map +1 -1
- package/dist/components/input/TagPicker.context.cjs +2 -0
- package/dist/components/input/TagPicker.context.cjs.map +1 -0
- package/dist/components/input/TagPicker.context.mjs +20 -0
- package/dist/components/input/TagPicker.context.mjs.map +1 -0
- package/dist/components/input/TagPicker.create.cjs +2 -0
- package/dist/components/input/TagPicker.create.cjs.map +1 -0
- package/dist/components/input/TagPicker.create.mjs +24 -0
- package/dist/components/input/TagPicker.create.mjs.map +1 -0
- package/dist/components/input/TagPicker.mjs +13 -279
- package/dist/components/input/TagPicker.mjs.map +1 -1
- package/dist/components/input/TagPicker.option.cjs +2 -0
- package/dist/components/input/TagPicker.option.cjs.map +1 -0
- package/dist/components/input/TagPicker.option.mjs +45 -0
- package/dist/components/input/TagPicker.option.mjs.map +1 -0
- package/dist/components/input/TagPicker.overflow.cjs +2 -0
- package/dist/components/input/TagPicker.overflow.cjs.map +1 -0
- package/dist/components/input/TagPicker.overflow.mjs +153 -0
- package/dist/components/input/TagPicker.overflow.mjs.map +1 -0
- package/dist/components/input/TagPicker.parts.cjs +2 -0
- package/dist/components/input/TagPicker.parts.cjs.map +1 -0
- package/dist/components/input/TagPicker.parts.mjs +425 -0
- package/dist/components/input/TagPicker.parts.mjs.map +1 -0
- package/dist/components/input/TagPicker.root.cjs +2 -0
- package/dist/components/input/TagPicker.root.cjs.map +1 -0
- package/dist/components/input/TagPicker.root.mjs +416 -0
- package/dist/components/input/TagPicker.root.mjs.map +1 -0
- package/dist/components/layout/Accordion.cjs.map +1 -1
- package/dist/components/layout/Accordion.mjs.map +1 -1
- package/dist/components/navigation/Hamburger.cjs +2 -0
- package/dist/components/navigation/Hamburger.cjs.map +1 -0
- package/dist/components/navigation/Hamburger.mjs +54 -0
- package/dist/components/navigation/Hamburger.mjs.map +1 -0
- package/dist/components/navigation/Nav.cjs +1 -1
- package/dist/components/navigation/Nav.cjs.map +1 -1
- package/dist/components/navigation/Nav.context.cjs +2 -0
- package/dist/components/navigation/Nav.context.cjs.map +1 -0
- package/dist/components/navigation/Nav.context.mjs +137 -0
- package/dist/components/navigation/Nav.context.mjs.map +1 -0
- package/dist/components/navigation/Nav.mjs +151 -258
- package/dist/components/navigation/Nav.mjs.map +1 -1
- package/dist/components/navigation/Nav.parts.cjs +2 -0
- package/dist/components/navigation/Nav.parts.cjs.map +1 -0
- package/dist/components/navigation/Nav.parts.mjs +77 -0
- package/dist/components/navigation/Nav.parts.mjs.map +1 -0
- package/dist/components/navigation/Nav.root.cjs +2 -0
- package/dist/components/navigation/Nav.root.cjs.map +1 -0
- package/dist/components/navigation/Nav.root.mjs +78 -0
- package/dist/components/navigation/Nav.root.mjs.map +1 -0
- package/dist/components/navigation/NavDrawer.cjs +2 -0
- package/dist/components/navigation/NavDrawer.cjs.map +1 -0
- package/dist/components/navigation/NavDrawer.context.cjs +2 -0
- package/dist/components/navigation/NavDrawer.context.cjs.map +1 -0
- package/dist/components/navigation/NavDrawer.context.mjs +11 -0
- package/dist/components/navigation/NavDrawer.context.mjs.map +1 -0
- package/dist/components/navigation/NavDrawer.mjs +179 -0
- package/dist/components/navigation/NavDrawer.mjs.map +1 -0
- package/dist/components/overlays/Dialog.cjs +1 -1
- package/dist/components/overlays/Dialog.cjs.map +1 -1
- package/dist/components/overlays/Dialog.mjs +190 -125
- package/dist/components/overlays/Dialog.mjs.map +1 -1
- package/dist/components/overlays/Dialog.shared.cjs +1 -1
- package/dist/components/overlays/Dialog.shared.cjs.map +1 -1
- package/dist/components/overlays/Dialog.shared.mjs +157 -81
- package/dist/components/overlays/Dialog.shared.mjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.cjs +2 -0
- package/dist/components/overlays/Dialog.slots.cjs.map +1 -0
- package/dist/components/overlays/Dialog.slots.mjs +129 -0
- package/dist/components/overlays/Dialog.slots.mjs.map +1 -0
- package/dist/components/overlays/Drawer.cjs +1 -1
- package/dist/components/overlays/Drawer.cjs.map +1 -1
- package/dist/components/overlays/Drawer.context.cjs +2 -0
- package/dist/components/overlays/Drawer.context.cjs.map +1 -0
- package/dist/components/overlays/Drawer.context.mjs +29 -0
- package/dist/components/overlays/Drawer.context.mjs.map +1 -0
- package/dist/components/overlays/Drawer.mjs +12 -187
- package/dist/components/overlays/Drawer.mjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.cjs +2 -0
- package/dist/components/overlays/Drawer.panel.cjs.map +1 -0
- package/dist/components/overlays/Drawer.panel.mjs +159 -0
- package/dist/components/overlays/Drawer.panel.mjs.map +1 -0
- package/dist/components/overlays/Drawer.parts.cjs +2 -0
- package/dist/components/overlays/Drawer.parts.cjs.map +1 -0
- package/dist/components/overlays/Drawer.parts.mjs +152 -0
- package/dist/components/overlays/Drawer.parts.mjs.map +1 -0
- package/dist/components/overlays/Drawer.root.cjs +2 -0
- package/dist/components/overlays/Drawer.root.cjs.map +1 -0
- package/dist/components/overlays/Drawer.root.mjs +207 -0
- package/dist/components/overlays/Drawer.root.mjs.map +1 -0
- package/dist/components/overlays/Drawer.scroll.cjs +2 -0
- package/dist/components/overlays/Drawer.scroll.cjs.map +1 -0
- package/dist/components/overlays/Drawer.scroll.mjs +42 -0
- package/dist/components/overlays/Drawer.scroll.mjs.map +1 -0
- package/dist/hooks/useDismiss.cjs.map +1 -1
- package/dist/hooks/useDismiss.mjs.map +1 -1
- package/dist/hooks/useFocusTrap.cjs +1 -1
- package/dist/hooks/useFocusTrap.cjs.map +1 -1
- package/dist/hooks/useFocusTrap.mjs +115 -98
- package/dist/hooks/useFocusTrap.mjs.map +1 -1
- package/dist/hooks/useModalLayer.cjs +1 -1
- package/dist/hooks/useModalLayer.cjs.map +1 -1
- package/dist/hooks/useModalLayer.mjs +35 -22
- package/dist/hooks/useModalLayer.mjs.map +1 -1
- package/dist/hooks/useRestoreFocus.cjs +1 -1
- package/dist/hooks/useRestoreFocus.cjs.map +1 -1
- package/dist/hooks/useRestoreFocus.mjs +91 -78
- package/dist/hooks/useRestoreFocus.mjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.cjs +1 -1
- package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.mjs +102 -78
- package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2226 -221
- package/dist/index.d.ts +2226 -221
- package/dist/index.mjs +115 -107
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +137 -77
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/icons.cjs +1 -1
- package/dist/lib/icons.cjs.map +1 -1
- package/dist/lib/icons.mjs +13 -6
- package/dist/lib/icons.mjs.map +1 -1
- package/dist/lib/layers.cjs +1 -1
- package/dist/lib/layers.cjs.map +1 -1
- package/dist/lib/layers.mjs +53 -47
- package/dist/lib/layers.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/styles/tokens.css +25 -0
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { getGlobalRegistry as e } from "../lib/globalRegistry.mjs";
|
|
3
3
|
import { focusElement as t, getFirstTabbable as n, getTabbableElements as r, isFocusable as i } from "../lib/focus.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { useInsertionEffect as
|
|
4
|
+
import { ALLOW_OUTSIDE_SELECTOR as a, compareLayers as o, getLayer as s, getLayerTreeElements as c, getOpenLayers as l, isDescendantLayer as u } from "../lib/layers.mjs";
|
|
5
|
+
import { useInsertionEffect as d, useLayoutEffect as f, useRef as p } from "react";
|
|
6
6
|
//#region src/hooks/useFocusTrap.ts
|
|
7
|
-
function
|
|
7
|
+
function m() {
|
|
8
8
|
return e("traps", () => ({
|
|
9
9
|
traps: [],
|
|
10
10
|
seq: 0,
|
|
11
11
|
listeners: null
|
|
12
12
|
}));
|
|
13
13
|
}
|
|
14
|
-
function
|
|
14
|
+
function h(e, t) {
|
|
15
15
|
let n = e.layerId, r = t.layerId;
|
|
16
16
|
if (n !== void 0 && r !== void 0 && n !== r) {
|
|
17
|
-
if (
|
|
18
|
-
if (
|
|
19
|
-
let e =
|
|
20
|
-
if (e && t) return
|
|
17
|
+
if (u(n, r)) return 1;
|
|
18
|
+
if (u(r, n)) return -1;
|
|
19
|
+
let e = s(n), t = s(r);
|
|
20
|
+
if (e && t) return o(e, t);
|
|
21
21
|
}
|
|
22
22
|
return e.seq - t.seq;
|
|
23
23
|
}
|
|
24
|
-
function
|
|
24
|
+
function g(e) {
|
|
25
25
|
let t = null;
|
|
26
|
-
for (let n of e.traps) (!t ||
|
|
26
|
+
for (let n of e.traps) (!t || h(n, t) > 0) && (t = n);
|
|
27
27
|
return t;
|
|
28
28
|
}
|
|
29
|
-
var
|
|
30
|
-
function
|
|
29
|
+
var _ = 4, v = 2;
|
|
30
|
+
function y(e) {
|
|
31
31
|
let t = e.container.ownerDocument, n = Array.from(t.querySelectorAll(e.allowOutsideSelector));
|
|
32
32
|
return n.filter((t) => !e.container.contains(t) && !t.contains(e.container) && !n.some((e) => e !== t && e.contains(t)));
|
|
33
33
|
}
|
|
34
|
-
function
|
|
34
|
+
function b(e, t) {
|
|
35
35
|
let n = r(e.container).map((e) => ({
|
|
36
36
|
el: e,
|
|
37
37
|
segment: 0
|
|
@@ -43,106 +43,106 @@ function y(e, t) {
|
|
|
43
43
|
});
|
|
44
44
|
}), n;
|
|
45
45
|
}
|
|
46
|
-
function b(e, t) {
|
|
47
|
-
return (e.compareDocumentPosition(t) & g) !== 0;
|
|
48
|
-
}
|
|
49
46
|
function x(e, t) {
|
|
50
|
-
return (e.compareDocumentPosition(t) & _) !== 0
|
|
47
|
+
return (e.compareDocumentPosition(t) & _) !== 0;
|
|
48
|
+
}
|
|
49
|
+
function S(e, t) {
|
|
50
|
+
return (e.compareDocumentPosition(t) & v) !== 0 && !t.contains(e);
|
|
51
51
|
}
|
|
52
|
-
function
|
|
52
|
+
function C(e, t, n, r) {
|
|
53
53
|
if (!r) {
|
|
54
|
-
let r = e.find((e) => e.segment === n &&
|
|
54
|
+
let r = e.find((e) => e.segment === n && x(t, e.el));
|
|
55
55
|
return r ? r.el : (e.find((e) => e.segment > n) ?? e[0]).el;
|
|
56
56
|
}
|
|
57
57
|
for (let r = e.length - 1; r >= 0; r--) {
|
|
58
58
|
let i = e[r];
|
|
59
|
-
if (i.segment === n &&
|
|
59
|
+
if (i.segment === n && S(t, i.el)) return i.el;
|
|
60
60
|
}
|
|
61
61
|
for (let t = e.length - 1; t >= 0; t--) if (e[t].segment < n) return e[t].el;
|
|
62
62
|
return e[e.length - 1].el;
|
|
63
63
|
}
|
|
64
|
-
function C(e, t) {
|
|
65
|
-
return e !== void 0 && s(e, { includeOwnElements: !1 }).some((e) => e.contains(t));
|
|
66
|
-
}
|
|
67
64
|
function w(e, t) {
|
|
65
|
+
return e !== void 0 && c(e, { includeOwnElements: !1 }).some((e) => e.contains(t));
|
|
66
|
+
}
|
|
67
|
+
function T(e, t) {
|
|
68
68
|
if (e.container.contains(t)) return !0;
|
|
69
69
|
let n = t.nodeType === 1 ? t : t.parentElement;
|
|
70
|
-
return n && n.closest(e.allowOutsideSelector) ? !0 :
|
|
70
|
+
return n && n.closest(e.allowOutsideSelector) ? !0 : w(e.layerId, t);
|
|
71
71
|
}
|
|
72
|
-
function
|
|
72
|
+
function E(e, t) {
|
|
73
73
|
let n = e.layerId;
|
|
74
74
|
if (n === void 0) return null;
|
|
75
75
|
let r = null;
|
|
76
|
-
for (let i of
|
|
77
|
-
if (i.id === n || !
|
|
78
|
-
let
|
|
79
|
-
s && (!r ||
|
|
76
|
+
for (let i of l()) {
|
|
77
|
+
if (i.id === n || !u(i.id, n)) continue;
|
|
78
|
+
let a = i.getAnchor(), s = i.getElements().find((n) => !!n && n.contains(t) && !e.container.contains(n) && !(a && n.contains(a)));
|
|
79
|
+
s && (!r || o(i, r.layer) > 0) && (r = {
|
|
80
80
|
layer: i,
|
|
81
81
|
region: s
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
return r;
|
|
85
85
|
}
|
|
86
|
-
function
|
|
86
|
+
function D(e, t) {
|
|
87
87
|
if (e.layerId === void 0) return null;
|
|
88
|
-
let n =
|
|
88
|
+
let n = c(e.layerId, { includeOwnElements: !1 }).filter((e) => e.contains(t));
|
|
89
89
|
return n.find((e) => !n.some((t) => t !== e && t.contains(e))) ?? null;
|
|
90
90
|
}
|
|
91
|
-
function
|
|
91
|
+
function O(e) {
|
|
92
92
|
for (let n of e) if (n && t(n)) return;
|
|
93
93
|
}
|
|
94
|
-
function
|
|
94
|
+
function k(e, t, n) {
|
|
95
95
|
let i = r(e), a = i.indexOf(t);
|
|
96
|
-
return a === -1 ? n ? !i.some((e) =>
|
|
96
|
+
return a === -1 ? n ? !i.some((e) => S(t, e)) : !i.some((e) => x(t, e)) : n ? a === 0 : a === i.length - 1;
|
|
97
97
|
}
|
|
98
|
-
function
|
|
99
|
-
let r =
|
|
100
|
-
|
|
98
|
+
function A(e, t, n) {
|
|
99
|
+
let r = b(e, t);
|
|
100
|
+
O([n ? r[r.length - 1]?.el : r[0]?.el, e.container]);
|
|
101
101
|
}
|
|
102
|
-
function
|
|
103
|
-
let i =
|
|
104
|
-
return i ? !
|
|
102
|
+
function j(e, t, n, r) {
|
|
103
|
+
let i = D(e, n);
|
|
104
|
+
return i ? !k(i, n, t.shiftKey) || (t.preventDefault(), A(e, r, t.shiftKey), !0) : !1;
|
|
105
105
|
}
|
|
106
|
-
function
|
|
106
|
+
function M(e, n, a, o) {
|
|
107
107
|
let s = a, c = n.shiftKey;
|
|
108
108
|
for (let l = 0; l < 10; l++) {
|
|
109
|
-
let l =
|
|
109
|
+
let l = E(e, s);
|
|
110
110
|
if (!l) return !1;
|
|
111
111
|
let { layer: u, region: d } = l;
|
|
112
|
-
if (s === a && !
|
|
112
|
+
if (s === a && !k(d, s, c)) return !0;
|
|
113
113
|
let f = u.getAnchor();
|
|
114
|
-
if (n.preventDefault(), !f || !f.isConnected) return
|
|
114
|
+
if (n.preventDefault(), !f || !f.isConnected) return A(e, o, c), !0;
|
|
115
115
|
if (c && i(f)) return t(f), !0;
|
|
116
116
|
if (e.container.contains(f)) {
|
|
117
|
-
let n =
|
|
118
|
-
return n.length === 0 ? (t(e.container), !0) : (
|
|
117
|
+
let n = b(e, o);
|
|
118
|
+
return n.length === 0 ? (t(e.container), !0) : (O([n.some((e) => e.el === f) ? P(n, f, c) : C(n, f, 0, c), e.container]), !0);
|
|
119
119
|
}
|
|
120
|
-
let p =
|
|
120
|
+
let p = E(e, f);
|
|
121
121
|
if (!p) {
|
|
122
|
-
let n =
|
|
123
|
-
if (!n) return
|
|
124
|
-
let i =
|
|
125
|
-
return i ? t(i) :
|
|
122
|
+
let n = D(e, f);
|
|
123
|
+
if (!n) return O([f, e.container]), !0;
|
|
124
|
+
let i = N(r(n), f, c);
|
|
125
|
+
return i ? t(i) : A(e, o, c), !0;
|
|
126
126
|
}
|
|
127
|
-
let m =
|
|
127
|
+
let m = N(r(p.region), f, c);
|
|
128
128
|
if (m) return t(m), !0;
|
|
129
129
|
s = f;
|
|
130
130
|
}
|
|
131
131
|
return !0;
|
|
132
132
|
}
|
|
133
|
-
function M(e, t, n) {
|
|
134
|
-
return n ? [...e].reverse().find((e) => x(t, e)) : e.find((e) => b(t, e) && !t.contains(e));
|
|
135
|
-
}
|
|
136
133
|
function N(e, t, n) {
|
|
134
|
+
return n ? [...e].reverse().find((e) => S(t, e)) : e.find((e) => x(t, e) && !t.contains(e));
|
|
135
|
+
}
|
|
136
|
+
function P(e, t, n) {
|
|
137
137
|
let r = e.findIndex((e) => e.el === t);
|
|
138
138
|
return e[((n ? r - 1 : r + 1) + e.length) % e.length].el;
|
|
139
139
|
}
|
|
140
|
-
function
|
|
140
|
+
function F(e, n) {
|
|
141
141
|
let { container: r } = e;
|
|
142
142
|
if (!r.isConnected) return;
|
|
143
|
-
let i = r.ownerDocument, a = i.activeElement, o = n.shiftKey, s =
|
|
144
|
-
if (a && !c && l === -1 && a !== i.body &&
|
|
145
|
-
let u =
|
|
143
|
+
let i = r.ownerDocument, a = i.activeElement, o = n.shiftKey, s = y(e), c = !!a && r.contains(a), l = a && !c ? s.findIndex((e) => e.contains(a)) : -1;
|
|
144
|
+
if (a && !c && l === -1 && a !== i.body && w(e.layerId, a) && (M(e, n, a, s) || j(e, n, a, s))) return;
|
|
145
|
+
let u = b(e, s);
|
|
146
146
|
if (u.length === 0) {
|
|
147
147
|
n.preventDefault(), t(r);
|
|
148
148
|
return;
|
|
@@ -160,71 +160,71 @@ function P(e, n) {
|
|
|
160
160
|
}
|
|
161
161
|
if (n.preventDefault(), a && (c || l !== -1)) {
|
|
162
162
|
let e = c ? 0 : l + 1;
|
|
163
|
-
t(
|
|
163
|
+
t(C(u, a, e, o));
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
166
166
|
t(u[o ? u.length - 1 : 0].el);
|
|
167
167
|
}
|
|
168
|
-
function
|
|
168
|
+
function I(e) {
|
|
169
169
|
if (e.key !== "Tab" || e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey || e.isComposing) return;
|
|
170
|
-
let t =
|
|
171
|
-
t &&
|
|
170
|
+
let t = g(m());
|
|
171
|
+
t && F(t, e);
|
|
172
172
|
}
|
|
173
|
-
function
|
|
174
|
-
let t =
|
|
173
|
+
function L(e) {
|
|
174
|
+
let t = g(m());
|
|
175
175
|
if (!t) return;
|
|
176
176
|
let n = e.target;
|
|
177
177
|
if (!n || typeof n.nodeType != "number") return;
|
|
178
178
|
let r = n;
|
|
179
179
|
if (r.nodeType !== 1) return;
|
|
180
180
|
let i = r;
|
|
181
|
-
if (
|
|
181
|
+
if (T(t, i)) {
|
|
182
182
|
t.lastFocused = i;
|
|
183
183
|
return;
|
|
184
184
|
}
|
|
185
|
-
|
|
185
|
+
R || queueMicrotask(() => B(i));
|
|
186
186
|
}
|
|
187
|
-
var
|
|
188
|
-
function
|
|
187
|
+
var R = !1;
|
|
188
|
+
function z(e) {
|
|
189
189
|
let t = e.activeElement;
|
|
190
190
|
return !t || t === e.body || t === e.documentElement;
|
|
191
191
|
}
|
|
192
|
-
function
|
|
192
|
+
function B(e) {
|
|
193
193
|
let t = e.ownerDocument, r = t.activeElement !== e;
|
|
194
|
-
if (r && !
|
|
195
|
-
let a =
|
|
194
|
+
if (r && !z(t)) return;
|
|
195
|
+
let a = g(m());
|
|
196
196
|
if (!a || !a.container.isConnected) return;
|
|
197
|
-
if (!r &&
|
|
197
|
+
if (!r && T(a, e)) {
|
|
198
198
|
a.lastFocused = e;
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
|
-
let o = a.lastFocused, s = !!o && o.isConnected &&
|
|
202
|
-
|
|
201
|
+
let o = a.lastFocused, s = !!o && o.isConnected && T(a, o) && i(o);
|
|
202
|
+
R = !0;
|
|
203
203
|
try {
|
|
204
|
-
|
|
204
|
+
O([
|
|
205
205
|
s ? o : null,
|
|
206
206
|
n(a.container),
|
|
207
207
|
a.container
|
|
208
208
|
]);
|
|
209
209
|
} finally {
|
|
210
|
-
|
|
210
|
+
R = !1;
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
function
|
|
213
|
+
function V(e) {
|
|
214
214
|
if (e.listeners || typeof document > "u") return;
|
|
215
215
|
let t = document;
|
|
216
|
-
t.addEventListener("keydown",
|
|
217
|
-
t.removeEventListener("keydown",
|
|
216
|
+
t.addEventListener("keydown", I), t.addEventListener("focusin", L, !0), e.listeners = { remove() {
|
|
217
|
+
t.removeEventListener("keydown", I), t.removeEventListener("focusin", L, !0);
|
|
218
218
|
} };
|
|
219
219
|
}
|
|
220
|
-
function
|
|
221
|
-
let t =
|
|
222
|
-
return t.traps.push(e),
|
|
220
|
+
function H(e) {
|
|
221
|
+
let t = m();
|
|
222
|
+
return t.traps.push(e), V(t), () => {
|
|
223
223
|
let n = t.traps.indexOf(e);
|
|
224
224
|
n !== -1 && t.traps.splice(n, 1), t.traps.length === 0 && (t.listeners?.remove(), t.listeners = null);
|
|
225
225
|
};
|
|
226
226
|
}
|
|
227
|
-
function
|
|
227
|
+
function U(e, t) {
|
|
228
228
|
let r = n(e);
|
|
229
229
|
return t === "container" ? [e] : t === void 0 || t === "first" ? [r, e] : [
|
|
230
230
|
typeof t == "function" ? t() : t.current,
|
|
@@ -232,13 +232,13 @@ function H(e, t) {
|
|
|
232
232
|
e
|
|
233
233
|
];
|
|
234
234
|
}
|
|
235
|
-
function
|
|
236
|
-
let { enabled: n } = t, r =
|
|
237
|
-
|
|
235
|
+
function W(e, t) {
|
|
236
|
+
let { enabled: n } = t, r = p(t);
|
|
237
|
+
d(() => {
|
|
238
238
|
r.current = t;
|
|
239
|
-
}),
|
|
239
|
+
}), f(() => {
|
|
240
240
|
if (!n || !e) return;
|
|
241
|
-
let t = r, i =
|
|
241
|
+
let t = r, i = m();
|
|
242
242
|
i.seq += 1;
|
|
243
243
|
let a = {
|
|
244
244
|
container: e,
|
|
@@ -250,17 +250,34 @@ function U(e, t) {
|
|
|
250
250
|
return t.current.allowOutsideSelector ?? "[data-wave-focus-trap-allow]";
|
|
251
251
|
},
|
|
252
252
|
lastFocused: null
|
|
253
|
-
}, o =
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
253
|
+
}, o = H(a), s = G(e, {
|
|
254
|
+
layerId: t.current.layerId,
|
|
255
|
+
initialFocus: t.current.initialFocus,
|
|
256
|
+
allowOutsideSelector: a.allowOutsideSelector
|
|
257
|
+
});
|
|
258
|
+
return s && (a.lastFocused = s), o;
|
|
259
|
+
}, [n, e]);
|
|
260
|
+
}
|
|
261
|
+
function G(e, t) {
|
|
262
|
+
let n = e.ownerDocument.activeElement;
|
|
263
|
+
if (n && (e.contains(n) || !n.closest(t.allowOutsideSelector) && w(t.layerId, n))) return n;
|
|
264
|
+
O(U(e, t.initialFocus));
|
|
265
|
+
let r = e.ownerDocument.activeElement;
|
|
266
|
+
return r && e.contains(r) ? r : null;
|
|
267
|
+
}
|
|
268
|
+
function K(e, t) {
|
|
269
|
+
let { enabled: n } = t, r = p(t);
|
|
270
|
+
d(() => {
|
|
271
|
+
r.current = t;
|
|
272
|
+
}), f(() => {
|
|
273
|
+
n && e && G(e, {
|
|
274
|
+
layerId: r.current.layerId,
|
|
275
|
+
initialFocus: r.current.initialFocus,
|
|
276
|
+
allowOutsideSelector: a
|
|
277
|
+
});
|
|
261
278
|
}, [n, e]);
|
|
262
279
|
}
|
|
263
280
|
//#endregion
|
|
264
|
-
export {
|
|
281
|
+
export { W as useFocusTrap, K as useInitialFocus };
|
|
265
282
|
|
|
266
283
|
//# sourceMappingURL=useFocusTrap.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusTrap.mjs","names":["getState","getGlobalRegistry","compareTraps","idA","a","idB","b","isDescendantLayer","layerA","getLayer","layerB","compareLayers","getActiveTrap","active","trap","state","FOLLOWING","PRECEDING","getAllowRegions","doc","all","region","other","getCycle","entries","getTabbableElements","regions","el","index","follows","reference","precedes","findFromPosition","backward","sameSegment","cycle","entry","segment","from","i","isInsideDescendantLayers","layerId","getLayerTreeElements","node","isInsideTrap","findDescendantLayer","found","layer","getOpenLayers","anchor","findPortalWrapper","containing","focusFirstPossible","candidate","candidates","focusElement","isAtRegionEdge","tabbables","focusCycleEdge","handlePortalWrapperTab","wrapper","event","handleDescendantLayerTab","depth","isFocusable","neighbour","outer","next","findNextTo","handleTab","container","inContainer","regionIndex","nextIndex","handleKeyDown","handleFocusIn","target","reclaiming","reclaimFocus","isFocusLost","lost","last","lastIsValid","getFirstTabbable","ensureListeners","pushTrap","resolveInitialFocus","first","initialFocus","useFocusTrap","options","latestRef","useRef","useInsertionEffect","useLayoutEffect","enabled","latest","release","now"],"sources":["../../src/hooks/useFocusTrap.ts"],"sourcesContent":["import { useInsertionEffect, useLayoutEffect, useRef } from 'react';\nimport type * as React from 'react';\nimport { getGlobalRegistry } from '../lib/globalRegistry';\nimport { focusElement, getFirstTabbable, getTabbableElements, isFocusable } from '../lib/focus';\nimport {\n ALLOW_OUTSIDE_SELECTOR,\n compareLayers,\n getLayer,\n getLayerTreeElements,\n getOpenLayers,\n isDescendantLayer,\n type LayerRecord,\n} from '../lib/layers';\n\n/** Options of {@link useFocusTrap}. */\nexport interface UseFocusTrapOptions {\n /** Whether the trap is active. */\n enabled: boolean;\n /**\n * The surface's dismiss layer; focus inside its descendant layers (portaled popovers opened from\n * inside) is allowed. The layer's own `refs` outside the container (its trigger) are not.\n */\n layerId?: string;\n /**\n * What to focus when the trap starts, unless focus is already inside (an `autoFocus` element\n * wins). @default 'first' (the first tabbable element, else the container)\n */\n initialFocus?:\n React.RefObject<HTMLElement | null> | 'first' | 'container' | (() => HTMLElement | null);\n /** Regions outside the container whose tabbables join the Tab cycle. @default ALLOW_OUTSIDE_SELECTOR */\n allowOutsideSelector?: string;\n}\n\ninterface TrapEntry {\n container: HTMLElement;\n seq: number;\n readonly layerId: string | undefined;\n readonly allowOutsideSelector: string;\n lastFocused: HTMLElement | null;\n}\n\ninterface TrapState {\n traps: TrapEntry[];\n seq: number;\n listeners: { remove(): void } | null;\n}\n\nfunction getState(): TrapState {\n return getGlobalRegistry<TrapState>('traps', () => ({ traps: [], seq: 0, listeners: null }));\n}\n\n// ---------------------------------------------------------------------------\n// Trap stack\n// ---------------------------------------------------------------------------\n\n/** `> 0` when trap `a` is above trap `b`: layer parentage/stacking first, then activation order. */\nfunction compareTraps(a: TrapEntry, b: TrapEntry): number {\n const idA = a.layerId;\n const idB = b.layerId;\n if (idA !== undefined && idB !== undefined && idA !== idB) {\n if (isDescendantLayer(idA, idB)) return 1;\n if (isDescendantLayer(idB, idA)) return -1;\n const layerA = getLayer(idA);\n const layerB = getLayer(idB);\n if (layerA && layerB) return compareLayers(layerA, layerB);\n }\n return a.seq - b.seq;\n}\n\nfunction getActiveTrap(state: TrapState): TrapEntry | null {\n let active: TrapEntry | null = null;\n for (const trap of state.traps) {\n if (!active || compareTraps(trap, active) > 0) active = trap;\n }\n return active;\n}\n\n// ---------------------------------------------------------------------------\n// Tab cycle\n// ---------------------------------------------------------------------------\n\ninterface CycleEntry {\n el: HTMLElement;\n /** 0 = the container, 1… = allow-listed regions in document order. */\n segment: number;\n}\n\nconst FOLLOWING = 4; // Node.DOCUMENT_POSITION_FOLLOWING\nconst PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING\n\n/** Top-level allow-listed regions outside the container. */\nfunction getAllowRegions(trap: TrapEntry): HTMLElement[] {\n const doc = trap.container.ownerDocument;\n const all = Array.from(doc.querySelectorAll<HTMLElement>(trap.allowOutsideSelector));\n return all.filter(\n (region) =>\n !trap.container.contains(region) &&\n !region.contains(trap.container) &&\n !all.some((other) => other !== region && other.contains(region)),\n );\n}\n\nfunction getCycle(trap: TrapEntry, regions: HTMLElement[]): CycleEntry[] {\n const entries: CycleEntry[] = getTabbableElements(trap.container).map((el) => ({\n el,\n segment: 0,\n }));\n regions.forEach((region, index) => {\n for (const el of getTabbableElements(region)) entries.push({ el, segment: index + 1 });\n });\n return entries;\n}\n\nfunction follows(reference: Node, el: Node): boolean {\n return (reference.compareDocumentPosition(el) & FOLLOWING) !== 0;\n}\n\nfunction precedes(reference: Node, el: Node): boolean {\n // An ancestor also \"precedes\"; it is not a place Tab goes back to.\n return (reference.compareDocumentPosition(el) & PRECEDING) !== 0 && !el.contains(reference);\n}\n\n/** The cycle entry Tab (or Shift+Tab) reaches from `from`, an element not in the cycle. */\nfunction findFromPosition(\n cycle: CycleEntry[],\n from: Element,\n segment: number,\n backward: boolean,\n): HTMLElement {\n if (!backward) {\n const sameSegment = cycle.find((entry) => entry.segment === segment && follows(from, entry.el));\n if (sameSegment) return sameSegment.el;\n const later = cycle.find((entry) => entry.segment > segment);\n return (later ?? cycle[0]).el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n const entry = cycle[i];\n if (entry.segment === segment && precedes(from, entry.el)) return entry.el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n if (cycle[i].segment < segment) return cycle[i].el;\n }\n return cycle[cycle.length - 1].el;\n}\n\n/**\n * Whether `node` is inside a **descendant** of the layer `layerId`: the portal wrappers registered\n * with it (overlays rendered inside the trapped surface) and the trees of its descendant layers.\n * The layer's own `getElements()` are left out — they usually include its trigger, which lives\n * outside the container (a DatePicker toggle) and must not count as inside the trap.\n */\nfunction isInsideDescendantLayers(layerId: string | undefined, node: Node): boolean {\n if (layerId === undefined) return false;\n return getLayerTreeElements(layerId, { includeOwnElements: false }).some((el) =>\n el.contains(node),\n );\n}\n\nfunction isInsideTrap(trap: TrapEntry, node: Node): boolean {\n if (trap.container.contains(node)) return true;\n const el = node.nodeType === 1 ? (node as Element) : node.parentElement;\n if (el && el.closest(trap.allowOutsideSelector)) return true;\n return isInsideDescendantLayers(trap.layerId, node);\n}\n\n/**\n * The open descendant layer of the trap's layer that `node` is in (the topmost when several\n * contain it), with the element of that layer containing `node` (its portaled surface). A layer's\n * trigger is not its surface: an element that is, or contains, the layer's anchor (an info\n * button, a tooltip's wrapper) belongs to the region around it — the container, the surface of\n * an enclosing descendant layer or a plain portal.\n */\nfunction findDescendantLayer(\n trap: TrapEntry,\n node: Node,\n): { layer: LayerRecord; region: HTMLElement } | null {\n const layerId = trap.layerId;\n if (layerId === undefined) return null;\n let found: { layer: LayerRecord; region: HTMLElement } | null = null;\n for (const layer of getOpenLayers()) {\n if (layer.id === layerId || !isDescendantLayer(layer.id, layerId)) continue;\n const anchor = layer.getAnchor();\n const region = layer\n .getElements()\n .find(\n (el): el is HTMLElement =>\n !!el &&\n el.contains(node) &&\n !trap.container.contains(el) &&\n !(anchor && el.contains(anchor)),\n );\n if (region && (!found || compareLayers(layer, found.layer) > 0)) found = { layer, region };\n }\n return found;\n}\n\n/**\n * The outermost element of the trap's layer tree (its own layer's elements left out) that contains\n * `node`. Asked once no descendant layer's surface contains `node`, this is the wrapper of a plain\n * `<Portal>` rendered inside the trapped surface (or inside one of its descendant layers).\n */\nfunction findPortalWrapper(trap: TrapEntry, node: Node): HTMLElement | null {\n if (trap.layerId === undefined) return null;\n const containing = getLayerTreeElements(trap.layerId, { includeOwnElements: false }).filter(\n (el) => el.contains(node),\n );\n return (\n containing.find((el) => !containing.some((other) => other !== el && other.contains(el))) ?? null\n );\n}\n\nfunction focusFirstPossible(candidates: Array<HTMLElement | null | undefined>): void {\n for (const candidate of candidates) {\n if (candidate && focusElement(candidate)) return;\n }\n}\n\n/** Whether Tab (Shift+Tab) from `from` leaves `region`: no tabbable of it follows (precedes). */\nfunction isAtRegionEdge(region: HTMLElement, from: HTMLElement, backward: boolean): boolean {\n const tabbables = getTabbableElements(region);\n const index = tabbables.indexOf(from);\n if (index === -1) {\n return backward\n ? !tabbables.some((el) => precedes(from, el))\n : !tabbables.some((el) => follows(from, el));\n }\n return backward ? index === 0 : index === tabbables.length - 1;\n}\n\n/** Leaving a region without an anchor: the first (Shift+Tab: last) element of the Tab cycle. */\nfunction focusCycleEdge(trap: TrapEntry, regions: HTMLElement[], backward: boolean): void {\n const cycle = getCycle(trap, regions);\n focusFirstPossible([backward ? cycle[cycle.length - 1]?.el : cycle[0]?.el, trap.container]);\n}\n\n/**\n * Tab inside a plain portal wrapper of the trap's layer tree (see {@link findPortalWrapper}):\n * native inside it; it has no anchor, so leaving it continues at the edge of the Tab cycle.\n * Returns `true` when it handled the event.\n */\nfunction handlePortalWrapperTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n const wrapper = findPortalWrapper(trap, active);\n if (!wrapper) return false;\n if (!isAtRegionEdge(wrapper, active, event.shiftKey)) return true; // native Tab inside it\n event.preventDefault();\n focusCycleEdge(trap, regions, event.shiftKey);\n return true;\n}\n\n/**\n * Tab inside a descendant layer: native inside it; leaving it forward goes to the tabbable after\n * the layer's anchor, backward to the anchor itself. Returns `true` when it handled the event.\n */\nfunction handleDescendantLayerTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n let from: HTMLElement = active;\n const backward = event.shiftKey;\n // Nested descendant layers: leave each one whose edge is reached, up to the container.\n for (let depth = 0; depth < 10; depth++) {\n const found = findDescendantLayer(trap, from);\n if (!found) return false;\n const { layer, region } = found;\n // Native Tab inside the layer.\n if (from === active && !isAtRegionEdge(region, from, backward)) return true;\n\n const anchor = layer.getAnchor();\n event.preventDefault();\n if (!anchor || !anchor.isConnected) {\n focusCycleEdge(trap, regions, backward);\n return true;\n }\n if (backward && isFocusable(anchor)) {\n focusElement(anchor);\n return true;\n }\n if (trap.container.contains(anchor)) {\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n focusElement(trap.container);\n return true;\n }\n const target = cycle.some((entry) => entry.el === anchor)\n ? neighbour(cycle, anchor, backward)\n : findFromPosition(cycle, anchor, 0, backward);\n focusFirstPossible([target, trap.container]);\n return true;\n }\n // The anchor lives in another descendant layer: continue from there.\n const outer = findDescendantLayer(trap, anchor);\n if (!outer) {\n // Or in a plain portal of the trap's tree: the element next to it there, else the edge of\n // the Tab cycle (as when Tab leaves that portal).\n const wrapper = findPortalWrapper(trap, anchor);\n if (!wrapper) {\n focusFirstPossible([anchor, trap.container]);\n return true;\n }\n const next = findNextTo(getTabbableElements(wrapper), anchor, backward);\n if (next) focusElement(next);\n else focusCycleEdge(trap, regions, backward);\n return true;\n }\n const next = findNextTo(getTabbableElements(outer.region), anchor, backward);\n if (next) {\n focusElement(next);\n return true;\n }\n from = anchor;\n }\n return true;\n}\n\n/** The tabbable after `anchor` (backward: before it) in `tabbables`, never one inside it. */\nfunction findNextTo(\n tabbables: HTMLElement[],\n anchor: HTMLElement,\n backward: boolean,\n): HTMLElement | undefined {\n return backward\n ? [...tabbables].reverse().find((el) => precedes(anchor, el))\n : tabbables.find((el) => follows(anchor, el) && !anchor.contains(el));\n}\n\nfunction neighbour(cycle: CycleEntry[], el: HTMLElement, backward: boolean): HTMLElement {\n const index = cycle.findIndex((entry) => entry.el === el);\n const next = backward ? index - 1 : index + 1;\n return cycle[(next + cycle.length) % cycle.length].el;\n}\n\nfunction handleTab(trap: TrapEntry, event: KeyboardEvent): void {\n const { container } = trap;\n if (!container.isConnected) return;\n const doc = container.ownerDocument;\n const active = doc.activeElement as HTMLElement | null;\n const backward = event.shiftKey;\n const regions = getAllowRegions(trap);\n\n const inContainer = !!active && container.contains(active);\n const regionIndex =\n active && !inContainer ? regions.findIndex((region) => region.contains(active)) : -1;\n\n if (active && !inContainer && regionIndex === -1 && active !== doc.body) {\n if (isInsideDescendantLayers(trap.layerId, active)) {\n if (handleDescendantLayerTab(trap, event, active, regions)) return;\n if (handlePortalWrapperTab(trap, event, active, regions)) return;\n }\n }\n\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n event.preventDefault();\n focusElement(container);\n return;\n }\n\n const index = active ? cycle.findIndex((entry) => entry.el === active) : -1;\n if (index !== -1) {\n const nextIndex = backward ? index - 1 : index + 1;\n if (nextIndex < 0 || nextIndex >= cycle.length) {\n event.preventDefault();\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n return;\n }\n // Inside one segment the browser's own Tab order is used; crossing segments is explicit.\n const next = cycle[nextIndex];\n if (next.segment !== cycle[index].segment) {\n event.preventDefault();\n focusElement(next.el);\n }\n return;\n }\n\n event.preventDefault();\n if (active && (inContainer || regionIndex !== -1)) {\n const segment = inContainer ? 0 : regionIndex + 1;\n focusElement(findFromPosition(cycle, active, segment, backward));\n return;\n }\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n}\n\n// ---------------------------------------------------------------------------\n// Document listeners\n// ---------------------------------------------------------------------------\n\nfunction handleKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Tab' || event.defaultPrevented) return;\n if (event.altKey || event.ctrlKey || event.metaKey || event.isComposing) return;\n const trap = getActiveTrap(getState());\n if (trap) handleTab(trap, event);\n}\n\nfunction handleFocusIn(event: FocusEvent): void {\n const trap = getActiveTrap(getState());\n if (!trap) return;\n const target = event.target as { nodeType?: unknown } | null;\n if (!target || typeof target.nodeType !== 'number') return;\n const node = target as Node;\n if (node.nodeType !== 1) return;\n const el = node as HTMLElement;\n if (isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n // Pulled out again by the focus being returned (another focus trap on the page): leave it there\n // rather than fight forever.\n if (reclaiming) return;\n // Not in this dispatch: React applies `autoFocus` in the layout phase of the commit that mounts\n // a surface, before the surface's portal wrapper and dismiss layer register and before its own\n // trap starts (a nested dialog, a confirm dialog over a drawer, a popover opened from a dialog).\n // Once that commit has run, the element is inside the trap that is active then.\n queueMicrotask(() => reclaimFocus(el));\n}\n\n/** Whether {@link reclaimFocus} is moving focus (its `focusin` events are dispatched meanwhile). */\nlet reclaiming = false;\n\n/** Whether focus is on no element: `<body>`, the document element or nothing. */\nfunction isFocusLost(doc: Document): boolean {\n const active = doc.activeElement;\n return !active || active === doc.body || active === doc.documentElement;\n}\n\n/**\n * Returns focus that landed on `el`, outside the trap, to the last focused element inside — also\n * when `el` lost it again before this ran (removed or blurred: focus is on `<body>` then, and no\n * later `focusin` would bring it back). Focus that moved on to another element is left to that\n * element's own check.\n */\nfunction reclaimFocus(el: HTMLElement): void {\n const doc = el.ownerDocument;\n const lost = doc.activeElement !== el;\n if (lost && !isFocusLost(doc)) return; // focus has moved on since\n const trap = getActiveTrap(getState());\n if (!trap || !trap.container.isConnected) return;\n if (!lost && isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n const last = trap.lastFocused;\n const lastIsValid = !!last && last.isConnected && isInsideTrap(trap, last) && isFocusable(last);\n reclaiming = true;\n try {\n focusFirstPossible([\n lastIsValid ? last : null,\n getFirstTabbable(trap.container),\n trap.container,\n ]);\n } finally {\n reclaiming = false;\n }\n}\n\nfunction ensureListeners(state: TrapState): void {\n if (state.listeners || typeof document === 'undefined') return;\n const doc = document;\n doc.addEventListener('keydown', handleKeyDown);\n doc.addEventListener('focusin', handleFocusIn, true);\n state.listeners = {\n remove() {\n doc.removeEventListener('keydown', handleKeyDown);\n doc.removeEventListener('focusin', handleFocusIn, true);\n },\n };\n}\n\nfunction pushTrap(entry: TrapEntry): () => void {\n const state = getState();\n state.traps.push(entry);\n ensureListeners(state);\n return () => {\n const index = state.traps.indexOf(entry);\n if (index !== -1) state.traps.splice(index, 1);\n if (state.traps.length === 0) {\n state.listeners?.remove();\n state.listeners = null;\n }\n };\n}\n\nfunction resolveInitialFocus(\n container: HTMLElement,\n initialFocus: UseFocusTrapOptions['initialFocus'],\n): Array<HTMLElement | null> {\n const first = getFirstTabbable(container);\n if (initialFocus === 'container') return [container];\n if (initialFocus === undefined || initialFocus === 'first') return [first, container];\n const chosen = typeof initialFocus === 'function' ? initialFocus() : initialFocus.current;\n return [chosen, first, container];\n}\n\n/**\n * Keeps keyboard focus inside `container` while `enabled` (modal dialogs, drawers, the DatePicker\n * calendar). Element-based: pass the surface held in state via a callback ref, so the trap starts\n * when the element appears.\n *\n * - **Initial focus** runs synchronously in the commit in which the container appears (no\n * `requestAnimationFrame`), and does nothing when focus is already inside (an `autoFocus`\n * element wins).\n * - **Tab / Shift+Tab** are handled by a document bubble-phase `keydown` listener that ignores\n * events whose default is already prevented, so a React handler inside (a menu closing on Tab, a\n * grid) can take the key first. Focus wraps at the edges, and from the container itself or a\n * non-tabbable element it continues in document order. Tabbable elements are computed on every\n * keydown (content may change; radio-group stops depend on focus), never cached.\n * - Tabbables inside `[data-wave-focus-trap-allow]` regions (the Toaster viewport) join the cycle.\n * - **Descendant layers** (a popover or menu opened from inside, identified through `layerId`):\n * Tab moves natively inside them; leaving one moves focus to the element after its anchor\n * (Shift+Tab: the anchor itself). Inside a plain `<Portal>` rendered in the surface (no layer,\n * no anchor) Tab is native too; leaving it wraps to the first (Shift+Tab: last) element. A\n * layer's trigger (its anchor, or an element around it such as a tooltip's wrapper) belongs to\n * the region it sits in, so Tab from an open info button or a button showing its tooltip moves\n * on as from any other element of that portal or popover.\n * - Focus that lands outside (not in the container, an allowed region or a descendant layer)\n * returns to the last focused element inside, also when that outside element loses it again\n * (removed or blurred, leaving focus on `<body>`). That is decided in a microtask, once the\n * current commit has run: an `autoFocus` element of a surface opened above the trap (a nested or\n * stacked dialog, a popover opened from the surface) gets focus before that surface's layer and\n * trap exist, and keeps it. The layer's own `refs` (its trigger, outside the container) are\n * outside: opening from the focused trigger still moves focus in, and focus moving back onto\n * the trigger is returned. Focus pulled out again while it is being returned (another focus\n * trap on the page) is left there.\n * - Traps form a stack (shared through the global registry): only the topmost acts.\n */\nexport function useFocusTrap(container: HTMLElement | null, options: UseFocusTrapOptions): void {\n const { enabled } = options;\n const latestRef = useRef(options);\n\n useInsertionEffect(() => {\n latestRef.current = options;\n });\n\n useLayoutEffect(() => {\n if (!enabled || !container) return;\n const latest = latestRef;\n const state = getState();\n state.seq += 1;\n const entry: TrapEntry = {\n container,\n seq: state.seq,\n get layerId() {\n return latest.current.layerId;\n },\n get allowOutsideSelector() {\n return latest.current.allowOutsideSelector ?? ALLOW_OUTSIDE_SELECTOR;\n },\n lastFocused: null,\n };\n const release = pushTrap(entry);\n\n // Initial focus is skipped only when focus is already inside the container or one of its\n // descendant layers (never for the layer's own trigger or an allow-listed toast).\n const active = container.ownerDocument.activeElement as HTMLElement | null;\n const focusInside =\n !!active &&\n (container.contains(active) ||\n (!active.closest(entry.allowOutsideSelector) &&\n isInsideDescendantLayers(latest.current.layerId, active)));\n if (focusInside) {\n entry.lastFocused = active;\n } else {\n focusFirstPossible(resolveInitialFocus(container, latest.current.initialFocus));\n const now = container.ownerDocument.activeElement as HTMLElement | null;\n if (now && container.contains(now)) entry.lastFocused = now;\n }\n return release;\n }, [enabled, container]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useFocusTrap.mjs","names":["getState","getGlobalRegistry","compareTraps","idA","a","idB","b","isDescendantLayer","layerA","getLayer","layerB","compareLayers","getActiveTrap","active","trap","state","FOLLOWING","PRECEDING","getAllowRegions","doc","all","region","other","getCycle","entries","getTabbableElements","regions","el","index","follows","reference","precedes","findFromPosition","backward","sameSegment","cycle","entry","segment","from","i","isInsideDescendantLayers","layerId","getLayerTreeElements","node","isInsideTrap","findDescendantLayer","found","layer","getOpenLayers","anchor","findPortalWrapper","containing","focusFirstPossible","candidate","candidates","focusElement","isAtRegionEdge","tabbables","focusCycleEdge","handlePortalWrapperTab","wrapper","event","handleDescendantLayerTab","depth","isFocusable","neighbour","outer","next","findNextTo","handleTab","container","inContainer","regionIndex","nextIndex","handleKeyDown","handleFocusIn","target","reclaiming","reclaimFocus","isFocusLost","lost","last","lastIsValid","getFirstTabbable","ensureListeners","pushTrap","resolveInitialFocus","first","initialFocus","useFocusTrap","options","latestRef","useRef","useInsertionEffect","useLayoutEffect","enabled","latest","release","focused","applyInitialFocus","now","useInitialFocus","ALLOW_OUTSIDE_SELECTOR"],"sources":["../../src/hooks/useFocusTrap.ts"],"sourcesContent":["import { useInsertionEffect, useLayoutEffect, useRef } from 'react';\nimport type * as React from 'react';\nimport { getGlobalRegistry } from '../lib/globalRegistry';\nimport { focusElement, getFirstTabbable, getTabbableElements, isFocusable } from '../lib/focus';\nimport {\n ALLOW_OUTSIDE_SELECTOR,\n compareLayers,\n getLayer,\n getLayerTreeElements,\n getOpenLayers,\n isDescendantLayer,\n type LayerRecord,\n} from '../lib/layers';\n\n/** Options of {@link useFocusTrap}. */\nexport interface UseFocusTrapOptions {\n /** Whether the trap is active. */\n enabled: boolean;\n /**\n * The surface's dismiss layer; focus inside its descendant layers (portaled popovers opened from\n * inside) is allowed. The layer's own `refs` outside the container (its trigger) are not.\n */\n layerId?: string;\n /**\n * What to focus when the trap starts, unless focus is already inside (an `autoFocus` element\n * wins). @default 'first' (the first tabbable element, else the container)\n */\n initialFocus?:\n React.RefObject<HTMLElement | null> | 'first' | 'container' | (() => HTMLElement | null);\n /** Regions outside the container whose tabbables join the Tab cycle. @default ALLOW_OUTSIDE_SELECTOR */\n allowOutsideSelector?: string;\n}\n\ninterface TrapEntry {\n container: HTMLElement;\n seq: number;\n readonly layerId: string | undefined;\n readonly allowOutsideSelector: string;\n lastFocused: HTMLElement | null;\n}\n\ninterface TrapState {\n traps: TrapEntry[];\n seq: number;\n listeners: { remove(): void } | null;\n}\n\nfunction getState(): TrapState {\n return getGlobalRegistry<TrapState>('traps', () => ({ traps: [], seq: 0, listeners: null }));\n}\n\n// ---------------------------------------------------------------------------\n// Trap stack\n// ---------------------------------------------------------------------------\n\n/** `> 0` when trap `a` is above trap `b`: layer parentage/stacking first, then activation order. */\nfunction compareTraps(a: TrapEntry, b: TrapEntry): number {\n const idA = a.layerId;\n const idB = b.layerId;\n if (idA !== undefined && idB !== undefined && idA !== idB) {\n if (isDescendantLayer(idA, idB)) return 1;\n if (isDescendantLayer(idB, idA)) return -1;\n const layerA = getLayer(idA);\n const layerB = getLayer(idB);\n if (layerA && layerB) return compareLayers(layerA, layerB);\n }\n return a.seq - b.seq;\n}\n\nfunction getActiveTrap(state: TrapState): TrapEntry | null {\n let active: TrapEntry | null = null;\n for (const trap of state.traps) {\n if (!active || compareTraps(trap, active) > 0) active = trap;\n }\n return active;\n}\n\n// ---------------------------------------------------------------------------\n// Tab cycle\n// ---------------------------------------------------------------------------\n\ninterface CycleEntry {\n el: HTMLElement;\n /** 0 = the container, 1… = allow-listed regions in document order. */\n segment: number;\n}\n\nconst FOLLOWING = 4; // Node.DOCUMENT_POSITION_FOLLOWING\nconst PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING\n\n/** Top-level allow-listed regions outside the container. */\nfunction getAllowRegions(trap: TrapEntry): HTMLElement[] {\n const doc = trap.container.ownerDocument;\n const all = Array.from(doc.querySelectorAll<HTMLElement>(trap.allowOutsideSelector));\n return all.filter(\n (region) =>\n !trap.container.contains(region) &&\n !region.contains(trap.container) &&\n !all.some((other) => other !== region && other.contains(region)),\n );\n}\n\nfunction getCycle(trap: TrapEntry, regions: HTMLElement[]): CycleEntry[] {\n const entries: CycleEntry[] = getTabbableElements(trap.container).map((el) => ({\n el,\n segment: 0,\n }));\n regions.forEach((region, index) => {\n for (const el of getTabbableElements(region)) entries.push({ el, segment: index + 1 });\n });\n return entries;\n}\n\nfunction follows(reference: Node, el: Node): boolean {\n return (reference.compareDocumentPosition(el) & FOLLOWING) !== 0;\n}\n\nfunction precedes(reference: Node, el: Node): boolean {\n // An ancestor also \"precedes\"; it is not a place Tab goes back to.\n return (reference.compareDocumentPosition(el) & PRECEDING) !== 0 && !el.contains(reference);\n}\n\n/** The cycle entry Tab (or Shift+Tab) reaches from `from`, an element not in the cycle. */\nfunction findFromPosition(\n cycle: CycleEntry[],\n from: Element,\n segment: number,\n backward: boolean,\n): HTMLElement {\n if (!backward) {\n const sameSegment = cycle.find((entry) => entry.segment === segment && follows(from, entry.el));\n if (sameSegment) return sameSegment.el;\n const later = cycle.find((entry) => entry.segment > segment);\n return (later ?? cycle[0]).el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n const entry = cycle[i];\n if (entry.segment === segment && precedes(from, entry.el)) return entry.el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n if (cycle[i].segment < segment) return cycle[i].el;\n }\n return cycle[cycle.length - 1].el;\n}\n\n/**\n * Whether `node` is inside a **descendant** of the layer `layerId`: the portal wrappers registered\n * with it (overlays rendered inside the trapped surface) and the trees of its descendant layers.\n * The layer's own `getElements()` are left out — they usually include its trigger, which lives\n * outside the container (a DatePicker toggle) and must not count as inside the trap.\n */\nfunction isInsideDescendantLayers(layerId: string | undefined, node: Node): boolean {\n if (layerId === undefined) return false;\n return getLayerTreeElements(layerId, { includeOwnElements: false }).some((el) =>\n el.contains(node),\n );\n}\n\nfunction isInsideTrap(trap: TrapEntry, node: Node): boolean {\n if (trap.container.contains(node)) return true;\n const el = node.nodeType === 1 ? (node as Element) : node.parentElement;\n if (el && el.closest(trap.allowOutsideSelector)) return true;\n return isInsideDescendantLayers(trap.layerId, node);\n}\n\n/**\n * The open descendant layer of the trap's layer that `node` is in (the topmost when several\n * contain it), with the element of that layer containing `node` (its portaled surface). A layer's\n * trigger is not its surface: an element that is, or contains, the layer's anchor (an info\n * button, a tooltip's wrapper) belongs to the region around it — the container, the surface of\n * an enclosing descendant layer or a plain portal.\n */\nfunction findDescendantLayer(\n trap: TrapEntry,\n node: Node,\n): { layer: LayerRecord; region: HTMLElement } | null {\n const layerId = trap.layerId;\n if (layerId === undefined) return null;\n let found: { layer: LayerRecord; region: HTMLElement } | null = null;\n for (const layer of getOpenLayers()) {\n if (layer.id === layerId || !isDescendantLayer(layer.id, layerId)) continue;\n const anchor = layer.getAnchor();\n const region = layer\n .getElements()\n .find(\n (el): el is HTMLElement =>\n !!el &&\n el.contains(node) &&\n !trap.container.contains(el) &&\n !(anchor && el.contains(anchor)),\n );\n if (region && (!found || compareLayers(layer, found.layer) > 0)) found = { layer, region };\n }\n return found;\n}\n\n/**\n * The outermost element of the trap's layer tree (its own layer's elements left out) that contains\n * `node`. Asked once no descendant layer's surface contains `node`, this is the wrapper of a plain\n * `<Portal>` rendered inside the trapped surface (or inside one of its descendant layers).\n */\nfunction findPortalWrapper(trap: TrapEntry, node: Node): HTMLElement | null {\n if (trap.layerId === undefined) return null;\n const containing = getLayerTreeElements(trap.layerId, { includeOwnElements: false }).filter(\n (el) => el.contains(node),\n );\n return (\n containing.find((el) => !containing.some((other) => other !== el && other.contains(el))) ?? null\n );\n}\n\nfunction focusFirstPossible(candidates: Array<HTMLElement | null | undefined>): void {\n for (const candidate of candidates) {\n if (candidate && focusElement(candidate)) return;\n }\n}\n\n/** Whether Tab (Shift+Tab) from `from` leaves `region`: no tabbable of it follows (precedes). */\nfunction isAtRegionEdge(region: HTMLElement, from: HTMLElement, backward: boolean): boolean {\n const tabbables = getTabbableElements(region);\n const index = tabbables.indexOf(from);\n if (index === -1) {\n return backward\n ? !tabbables.some((el) => precedes(from, el))\n : !tabbables.some((el) => follows(from, el));\n }\n return backward ? index === 0 : index === tabbables.length - 1;\n}\n\n/** Leaving a region without an anchor: the first (Shift+Tab: last) element of the Tab cycle. */\nfunction focusCycleEdge(trap: TrapEntry, regions: HTMLElement[], backward: boolean): void {\n const cycle = getCycle(trap, regions);\n focusFirstPossible([backward ? cycle[cycle.length - 1]?.el : cycle[0]?.el, trap.container]);\n}\n\n/**\n * Tab inside a plain portal wrapper of the trap's layer tree (see {@link findPortalWrapper}):\n * native inside it; it has no anchor, so leaving it continues at the edge of the Tab cycle.\n * Returns `true` when it handled the event.\n */\nfunction handlePortalWrapperTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n const wrapper = findPortalWrapper(trap, active);\n if (!wrapper) return false;\n if (!isAtRegionEdge(wrapper, active, event.shiftKey)) return true; // native Tab inside it\n event.preventDefault();\n focusCycleEdge(trap, regions, event.shiftKey);\n return true;\n}\n\n/**\n * Tab inside a descendant layer: native inside it; leaving it forward goes to the tabbable after\n * the layer's anchor, backward to the anchor itself. Returns `true` when it handled the event.\n */\nfunction handleDescendantLayerTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n let from: HTMLElement = active;\n const backward = event.shiftKey;\n // Nested descendant layers: leave each one whose edge is reached, up to the container.\n for (let depth = 0; depth < 10; depth++) {\n const found = findDescendantLayer(trap, from);\n if (!found) return false;\n const { layer, region } = found;\n // Native Tab inside the layer.\n if (from === active && !isAtRegionEdge(region, from, backward)) return true;\n\n const anchor = layer.getAnchor();\n event.preventDefault();\n if (!anchor || !anchor.isConnected) {\n focusCycleEdge(trap, regions, backward);\n return true;\n }\n if (backward && isFocusable(anchor)) {\n focusElement(anchor);\n return true;\n }\n if (trap.container.contains(anchor)) {\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n focusElement(trap.container);\n return true;\n }\n const target = cycle.some((entry) => entry.el === anchor)\n ? neighbour(cycle, anchor, backward)\n : findFromPosition(cycle, anchor, 0, backward);\n focusFirstPossible([target, trap.container]);\n return true;\n }\n // The anchor lives in another descendant layer: continue from there.\n const outer = findDescendantLayer(trap, anchor);\n if (!outer) {\n // Or in a plain portal of the trap's tree: the element next to it there, else the edge of\n // the Tab cycle (as when Tab leaves that portal).\n const wrapper = findPortalWrapper(trap, anchor);\n if (!wrapper) {\n focusFirstPossible([anchor, trap.container]);\n return true;\n }\n const next = findNextTo(getTabbableElements(wrapper), anchor, backward);\n if (next) focusElement(next);\n else focusCycleEdge(trap, regions, backward);\n return true;\n }\n const next = findNextTo(getTabbableElements(outer.region), anchor, backward);\n if (next) {\n focusElement(next);\n return true;\n }\n from = anchor;\n }\n return true;\n}\n\n/** The tabbable after `anchor` (backward: before it) in `tabbables`, never one inside it. */\nfunction findNextTo(\n tabbables: HTMLElement[],\n anchor: HTMLElement,\n backward: boolean,\n): HTMLElement | undefined {\n return backward\n ? [...tabbables].reverse().find((el) => precedes(anchor, el))\n : tabbables.find((el) => follows(anchor, el) && !anchor.contains(el));\n}\n\nfunction neighbour(cycle: CycleEntry[], el: HTMLElement, backward: boolean): HTMLElement {\n const index = cycle.findIndex((entry) => entry.el === el);\n const next = backward ? index - 1 : index + 1;\n return cycle[(next + cycle.length) % cycle.length].el;\n}\n\nfunction handleTab(trap: TrapEntry, event: KeyboardEvent): void {\n const { container } = trap;\n if (!container.isConnected) return;\n const doc = container.ownerDocument;\n const active = doc.activeElement as HTMLElement | null;\n const backward = event.shiftKey;\n const regions = getAllowRegions(trap);\n\n const inContainer = !!active && container.contains(active);\n const regionIndex =\n active && !inContainer ? regions.findIndex((region) => region.contains(active)) : -1;\n\n if (active && !inContainer && regionIndex === -1 && active !== doc.body) {\n if (isInsideDescendantLayers(trap.layerId, active)) {\n if (handleDescendantLayerTab(trap, event, active, regions)) return;\n if (handlePortalWrapperTab(trap, event, active, regions)) return;\n }\n }\n\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n event.preventDefault();\n focusElement(container);\n return;\n }\n\n const index = active ? cycle.findIndex((entry) => entry.el === active) : -1;\n if (index !== -1) {\n const nextIndex = backward ? index - 1 : index + 1;\n if (nextIndex < 0 || nextIndex >= cycle.length) {\n event.preventDefault();\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n return;\n }\n // Inside one segment the browser's own Tab order is used; crossing segments is explicit.\n const next = cycle[nextIndex];\n if (next.segment !== cycle[index].segment) {\n event.preventDefault();\n focusElement(next.el);\n }\n return;\n }\n\n event.preventDefault();\n if (active && (inContainer || regionIndex !== -1)) {\n const segment = inContainer ? 0 : regionIndex + 1;\n focusElement(findFromPosition(cycle, active, segment, backward));\n return;\n }\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n}\n\n// ---------------------------------------------------------------------------\n// Document listeners\n// ---------------------------------------------------------------------------\n\nfunction handleKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Tab' || event.defaultPrevented) return;\n if (event.altKey || event.ctrlKey || event.metaKey || event.isComposing) return;\n const trap = getActiveTrap(getState());\n if (trap) handleTab(trap, event);\n}\n\nfunction handleFocusIn(event: FocusEvent): void {\n const trap = getActiveTrap(getState());\n if (!trap) return;\n const target = event.target as { nodeType?: unknown } | null;\n if (!target || typeof target.nodeType !== 'number') return;\n const node = target as Node;\n if (node.nodeType !== 1) return;\n const el = node as HTMLElement;\n if (isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n // Pulled out again by the focus being returned (another focus trap on the page): leave it there\n // rather than fight forever.\n if (reclaiming) return;\n // Not in this dispatch: React applies `autoFocus` in the layout phase of the commit that mounts\n // a surface, before the surface's portal wrapper and dismiss layer register and before its own\n // trap starts (a nested dialog, a confirm dialog over a drawer, a popover opened from a dialog).\n // Once that commit has run, the element is inside the trap that is active then.\n queueMicrotask(() => reclaimFocus(el));\n}\n\n/** Whether {@link reclaimFocus} is moving focus (its `focusin` events are dispatched meanwhile). */\nlet reclaiming = false;\n\n/** Whether focus is on no element: `<body>`, the document element or nothing. */\nfunction isFocusLost(doc: Document): boolean {\n const active = doc.activeElement;\n return !active || active === doc.body || active === doc.documentElement;\n}\n\n/**\n * Returns focus that landed on `el`, outside the trap, to the last focused element inside — also\n * when `el` lost it again before this ran (removed or blurred: focus is on `<body>` then, and no\n * later `focusin` would bring it back). Focus that moved on to another element is left to that\n * element's own check.\n */\nfunction reclaimFocus(el: HTMLElement): void {\n const doc = el.ownerDocument;\n const lost = doc.activeElement !== el;\n if (lost && !isFocusLost(doc)) return; // focus has moved on since\n const trap = getActiveTrap(getState());\n if (!trap || !trap.container.isConnected) return;\n if (!lost && isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n const last = trap.lastFocused;\n const lastIsValid = !!last && last.isConnected && isInsideTrap(trap, last) && isFocusable(last);\n reclaiming = true;\n try {\n focusFirstPossible([\n lastIsValid ? last : null,\n getFirstTabbable(trap.container),\n trap.container,\n ]);\n } finally {\n reclaiming = false;\n }\n}\n\nfunction ensureListeners(state: TrapState): void {\n if (state.listeners || typeof document === 'undefined') return;\n const doc = document;\n doc.addEventListener('keydown', handleKeyDown);\n doc.addEventListener('focusin', handleFocusIn, true);\n state.listeners = {\n remove() {\n doc.removeEventListener('keydown', handleKeyDown);\n doc.removeEventListener('focusin', handleFocusIn, true);\n },\n };\n}\n\nfunction pushTrap(entry: TrapEntry): () => void {\n const state = getState();\n state.traps.push(entry);\n ensureListeners(state);\n return () => {\n const index = state.traps.indexOf(entry);\n if (index !== -1) state.traps.splice(index, 1);\n if (state.traps.length === 0) {\n state.listeners?.remove();\n state.listeners = null;\n }\n };\n}\n\nfunction resolveInitialFocus(\n container: HTMLElement,\n initialFocus: UseFocusTrapOptions['initialFocus'],\n): Array<HTMLElement | null> {\n const first = getFirstTabbable(container);\n if (initialFocus === 'container') return [container];\n if (initialFocus === undefined || initialFocus === 'first') return [first, container];\n const chosen = typeof initialFocus === 'function' ? initialFocus() : initialFocus.current;\n return [chosen, first, container];\n}\n\n/**\n * Keeps keyboard focus inside `container` while `enabled` (modal dialogs, drawers, the DatePicker\n * calendar). Element-based: pass the surface held in state via a callback ref, so the trap starts\n * when the element appears.\n *\n * - **Initial focus** runs synchronously in the commit in which the container appears (no\n * `requestAnimationFrame`), and does nothing when focus is already inside (an `autoFocus`\n * element wins).\n * - **Tab / Shift+Tab** are handled by a document bubble-phase `keydown` listener that ignores\n * events whose default is already prevented, so a React handler inside (a menu closing on Tab, a\n * grid) can take the key first. Focus wraps at the edges, and from the container itself or a\n * non-tabbable element it continues in document order. Tabbable elements are computed on every\n * keydown (content may change; radio-group stops depend on focus), never cached.\n * - Tabbables inside `[data-wave-focus-trap-allow]` regions (the Toaster viewport) join the cycle.\n * - **Descendant layers** (a popover or menu opened from inside, identified through `layerId`):\n * Tab moves natively inside them; leaving one moves focus to the element after its anchor\n * (Shift+Tab: the anchor itself). Inside a plain `<Portal>` rendered in the surface (no layer,\n * no anchor) Tab is native too; leaving it wraps to the first (Shift+Tab: last) element. A\n * layer's trigger (its anchor, or an element around it such as a tooltip's wrapper) belongs to\n * the region it sits in, so Tab from an open info button or a button showing its tooltip moves\n * on as from any other element of that portal or popover.\n * - Focus that lands outside (not in the container, an allowed region or a descendant layer)\n * returns to the last focused element inside, also when that outside element loses it again\n * (removed or blurred, leaving focus on `<body>`). That is decided in a microtask, once the\n * current commit has run: an `autoFocus` element of a surface opened above the trap (a nested or\n * stacked dialog, a popover opened from the surface) gets focus before that surface's layer and\n * trap exist, and keeps it. The layer's own `refs` (its trigger, outside the container) are\n * outside: opening from the focused trigger still moves focus in, and focus moving back onto\n * the trigger is returned. Focus pulled out again while it is being returned (another focus\n * trap on the page) is left there.\n * - Traps form a stack (shared through the global registry): only the topmost acts.\n */\nexport function useFocusTrap(container: HTMLElement | null, options: UseFocusTrapOptions): void {\n const { enabled } = options;\n const latestRef = useRef(options);\n\n useInsertionEffect(() => {\n latestRef.current = options;\n });\n\n useLayoutEffect(() => {\n if (!enabled || !container) return;\n const latest = latestRef;\n const state = getState();\n state.seq += 1;\n const entry: TrapEntry = {\n container,\n seq: state.seq,\n get layerId() {\n return latest.current.layerId;\n },\n get allowOutsideSelector() {\n return latest.current.allowOutsideSelector ?? ALLOW_OUTSIDE_SELECTOR;\n },\n lastFocused: null,\n };\n const release = pushTrap(entry);\n const focused = applyInitialFocus(container, {\n layerId: latest.current.layerId,\n initialFocus: latest.current.initialFocus,\n allowOutsideSelector: entry.allowOutsideSelector,\n });\n if (focused) entry.lastFocused = focused;\n return release;\n }, [enabled, container]);\n}\n\n/**\n * The initial focus of a surface (the trap's, and {@link useInitialFocus}'s): skipped when focus is\n * already inside the container or one of the descendant layers of `layerId` (never for the\n * layer's own trigger or an allow-listed toast), else `initialFocus` (default: the first tabbable\n * element), else the container. Returns the focused element when it is inside the container or\n * those layers (the trap's `lastFocused`), else `null`.\n */\nfunction applyInitialFocus(\n container: HTMLElement,\n options: {\n layerId: string | undefined;\n initialFocus: UseFocusTrapOptions['initialFocus'];\n allowOutsideSelector: string;\n },\n): HTMLElement | null {\n const active = container.ownerDocument.activeElement as HTMLElement | null;\n const focusInside =\n !!active &&\n (container.contains(active) ||\n (!active.closest(options.allowOutsideSelector) &&\n isInsideDescendantLayers(options.layerId, active)));\n if (focusInside) return active;\n focusFirstPossible(resolveInitialFocus(container, options.initialFocus));\n const now = container.ownerDocument.activeElement as HTMLElement | null;\n return now && container.contains(now) ? now : null;\n}\n\n/** Options of {@link useInitialFocus}. */\nexport interface UseInitialFocusOptions {\n /** Whether the surface is open: focus moves in when it becomes `true` with a container. */\n enabled: boolean;\n /** The surface's dismiss layer: focus already in one of its descendant layers stays there. */\n layerId?: string;\n /** What to focus first. @default 'first' (the first tabbable element, else the container) */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n}\n\n/**\n * The trap's initial focus without the trap (a non-modal Dialog or Drawer, which moves focus in\n * when it opens but does not keep it there): in the layout phase of the commit in which\n * `enabled && container` becomes true, does nothing when focus is already inside the container or\n * one of the layer's descendant layers (an `autoFocus` element wins), else focuses\n * `initialFocus` (default: the first tabbable element), else the container. `useFocusTrap` runs\n * the same code when it starts. It adds no listener.\n */\nexport function useInitialFocus(\n container: HTMLElement | null,\n options: UseInitialFocusOptions,\n): void {\n const { enabled } = options;\n const latestRef = useRef(options);\n\n useInsertionEffect(() => {\n latestRef.current = options;\n });\n\n useLayoutEffect(() => {\n if (!enabled || !container) return;\n applyInitialFocus(container, {\n layerId: latestRef.current.layerId,\n initialFocus: latestRef.current.initialFocus,\n allowOutsideSelector: ALLOW_OUTSIDE_SELECTOR,\n });\n }, [enabled, container]);\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";const e=require("./useDismiss.cjs"),t=require("./useFocusTrap.cjs"),n=require("./useRestoreFocus.cjs"),r=require("./useModalIsolation.cjs"),i=require("./useScrollLock.cjs");function a(a){let{container:o,initialFocus:
|
|
1
|
+
"use client";const e=require("./useDismiss.cjs"),t=require("./useFocusTrap.cjs"),n=require("./useRestoreFocus.cjs"),r=require("./useModalIsolation.cjs"),i=require("./useScrollLock.cjs");function a(a){let{container:o,modalType:s=`modal`,initialFocus:c,triggerRef:l,finalFocusRef:u,...d}=a,{open:f}=a,p=s===`non-modal`,m=f&&!p,h=e.useDismiss(p?{...d,kind:`dialog`,escape:`inside`,outsidePress:!1,focusOutside:!1}:{...d,kind:`modal`,...s===`alert`&&{outsidePress:!1}});return t.useFocusTrap(o,{enabled:m,layerId:h.layerId,initialFocus:c}),t.useInitialFocus(o,{enabled:f&&p,layerId:h.layerId,initialFocus:c}),r.useModalIsolation(m,{layerId:h.layerId,container:o}),i.useScrollLock(m),n.useRestoreFocus({enabled:f,container:o,triggerRef:l,finalFocusRef:u,requireFocusInside:p,layerId:h.layerId}),h}exports.useModalLayer=a;
|
|
2
2
|
//# sourceMappingURL=useModalLayer.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useModalLayer.cjs","names":["useDismiss","require_useDismiss","require_useFocusTrap","require_useRestoreFocus","require_useModalIsolation","require_useScrollLock","useModalLayer","dismissOptions","options","
|
|
1
|
+
{"version":3,"file":"useModalLayer.cjs","names":["useDismiss","require_useDismiss","require_useFocusTrap","require_useRestoreFocus","require_useModalIsolation","require_useScrollLock","useModalLayer","dismissOptions","options","nonModal","modalType","modal","open","layer","container"],"sources":["../../src/hooks/useModalLayer.ts"],"sourcesContent":["import type * as React from 'react';\nimport type { ModalType } from '../lib/types';\nimport { useDismiss, type DismissLayer, type UseDismissOptions } from './useDismiss';\nimport { useFocusTrap, useInitialFocus, type UseFocusTrapOptions } from './useFocusTrap';\nimport { useModalIsolation } from './useModalIsolation';\nimport { useRestoreFocus } from './useRestoreFocus';\nimport { useScrollLock } from './useScrollLock';\n\n/**\n * Options of {@link useModalLayer}: `useDismiss` options (the kind follows `modalType`) plus focus.\n */\nexport interface UseModalLayerOptions extends Omit<UseDismissOptions, 'kind'> {\n /** The modal surface, held in state via a callback ref. */\n container: HTMLElement | null;\n /**\n * `modal` (default) and `alert` register a `modal` layer, trap focus, isolate the page, lock its\n * scroll and restore focus (an `alert` layer ignores outside presses); `non-modal` registers a\n * `dialog` layer (`escape: 'inside'`, no outside press, no focus-outside), traps, isolates and\n * locks nothing, moves focus in on open with the trap's initial-focus rule and restores focus only\n * from inside (`requireFocusInside`).\n * @default 'modal'\n */\n modalType?: ModalType;\n /** What the focus trap (or a non-modal surface's initial focus) focuses first. @default 'first' */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n /** The trigger: preferred focus-restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override for the focus-restore target. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Everything a modal surface (Dialog, Drawer) needs, composed in the order that works:\n *\n * 1. `useDismiss({ kind: 'modal' })` — Escape and outside presses (the backdrop is outside the\n * content surface, so a click on it dismisses; a drag from the content does not).\n * 2. `useFocusTrap` — initial focus, Tab cycle incl. toasts and descendant layers.\n * 3. `useModalIsolation` — the rest of the page is `inert` (instead of `aria-modal`).\n * 4. `useScrollLock` — ref-counted page scroll lock.\n * 5. `useRestoreFocus` — the opener (or `triggerRef`/`finalFocusRef`) gets focus back.\n *\n * On close the trap is released before the isolation is lifted, and the isolation is lifted\n * before focus is restored (an inert opener cannot take focus). Wrap the surface in\n * `Portal layerId={layerId}` so layers opened inside the modal become its descendants.\n *\n * `modalType: 'alert'` composes the same with `outsidePress: false`. `modalType: 'non-modal'`\n * registers a `dialog` layer (Escape only from inside its tree, no outside press, no focus-outside\n * dismissal), keeps the trap, the isolation and the scroll lock off, moves focus in on open with\n * the trap's initial-focus rule (`useInitialFocus`) and restores focus only when it is inside the\n * surface (or in a layer opened from it) at close, or was lost to `<body>` from there. Every hook\n * is always called and keys on `open`: a `modalType` that changes while open applies at once (the\n * trap, isolation and lock follow). It leaves focus inside the surface where it is; focus outside\n * it moves in, as on open: a switch to `modal` or `alert` starts the trap (the page turns inert),\n * and a switch to `non-modal` runs the initial focus (focus in the toast region, which a trap lets\n * in, moves into the surface). The Tab order of a non-modal surface (its place after the opener)\n * is the component's.\n *\n * A surface that mounts through the presence core (Dialog, Drawer) passes its open state as\n * `open`, never its mount (`isMounted`), and its element as `container` also while it is mounted\n * closed: the layer, the trap, the isolation and the scroll lock end and focus is restored in the\n * commit that closes it, while the element still runs its exit motion (`inert`) or stays mounted\n * (`hidden`, with `unmountOnClose={false}`); the next open starts them again with the same\n * element.\n */\nexport function useModalLayer(options: UseModalLayerOptions): DismissLayer {\n const {\n container,\n modalType = 'modal',\n initialFocus,\n triggerRef,\n finalFocusRef,\n ...dismissOptions\n } = options;\n const { open } = options;\n const nonModal = modalType === 'non-modal';\n const modal = open && !nonModal;\n\n const layer = useDismiss(\n nonModal\n ? {\n ...dismissOptions,\n kind: 'dialog',\n escape: 'inside',\n outsidePress: false,\n focusOutside: false,\n }\n : {\n ...dismissOptions,\n kind: 'modal',\n ...(modalType === 'alert' && { outsidePress: false }),\n },\n );\n useFocusTrap(container, { enabled: modal, layerId: layer.layerId, initialFocus });\n useInitialFocus(container, { enabled: open && nonModal, layerId: layer.layerId, initialFocus });\n useModalIsolation(modal, { layerId: layer.layerId, container });\n useScrollLock(modal);\n useRestoreFocus({\n enabled: open,\n container,\n triggerRef,\n finalFocusRef,\n requireFocusInside: nonModal,\n layerId: layer.layerId,\n });\n\n return layer;\n}\n"],"mappings":"0LAgEA,SAAgBM,EAAc,EAA6C,CACzE,GAAM,CACJ,YACA,YAAY,QACZ,eACA,aACA,gBACA,GAAGC,GACDC,EACE,CAAE,QAASA,EACXC,EAAWC,IAAc,YACzBC,EAAQC,GAAQ,CAACH,EAEjBI,EAAQb,EAAAA,WACZS,EACI,CACE,GAAGF,EACH,KAAM,SACN,OAAQ,SACR,aAAc,GACd,aAAc,EAChB,EACA,CACE,GAAGA,EACH,KAAM,QACN,GAAIG,IAAc,SAAW,CAAE,aAAc,EAAM,CACrD,CACN,EAcA,OAbAR,EAAA,aAAaY,EAAW,CAAE,QAASH,EAAO,QAASE,EAAM,QAAS,cAAa,CAAC,EAChFX,EAAA,gBAAgBY,EAAW,CAAE,QAASF,GAAQH,EAAU,QAASI,EAAM,QAAS,cAAa,CAAC,EAC9FT,EAAA,kBAAkBO,EAAO,CAAE,QAASE,EAAM,QAAS,WAAU,CAAC,EAC9DR,EAAA,cAAcM,CAAK,EACnBR,EAAA,gBAAgB,CACd,QAASS,EACT,YACA,aACA,gBACA,mBAAoBH,EACpB,QAASI,EAAM,OACjB,CAAC,EAEMA,CACT"}
|
|
@@ -1,30 +1,43 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useDismiss as e } from "./useDismiss.mjs";
|
|
3
|
-
import { useFocusTrap as t } from "./useFocusTrap.mjs";
|
|
4
|
-
import { useRestoreFocus as
|
|
5
|
-
import { useModalIsolation as
|
|
6
|
-
import { useScrollLock as
|
|
3
|
+
import { useFocusTrap as t, useInitialFocus as n } from "./useFocusTrap.mjs";
|
|
4
|
+
import { useRestoreFocus as r } from "./useRestoreFocus.mjs";
|
|
5
|
+
import { useModalIsolation as i } from "./useModalIsolation.mjs";
|
|
6
|
+
import { useScrollLock as a } from "./useScrollLock.mjs";
|
|
7
7
|
//#region src/hooks/useModalLayer.ts
|
|
8
|
-
function
|
|
9
|
-
let { container:
|
|
10
|
-
...
|
|
11
|
-
kind: "
|
|
8
|
+
function o(o) {
|
|
9
|
+
let { container: s, modalType: c = "modal", initialFocus: l, triggerRef: u, finalFocusRef: d, ...f } = o, { open: p } = o, m = c === "non-modal", h = p && !m, g = e(m ? {
|
|
10
|
+
...f,
|
|
11
|
+
kind: "dialog",
|
|
12
|
+
escape: "inside",
|
|
13
|
+
outsidePress: !1,
|
|
14
|
+
focusOutside: !1
|
|
15
|
+
} : {
|
|
16
|
+
...f,
|
|
17
|
+
kind: "modal",
|
|
18
|
+
...c === "alert" && { outsidePress: !1 }
|
|
12
19
|
});
|
|
13
|
-
return t(
|
|
14
|
-
enabled:
|
|
15
|
-
layerId:
|
|
16
|
-
initialFocus:
|
|
17
|
-
}),
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
return t(s, {
|
|
21
|
+
enabled: h,
|
|
22
|
+
layerId: g.layerId,
|
|
23
|
+
initialFocus: l
|
|
24
|
+
}), n(s, {
|
|
25
|
+
enabled: p && m,
|
|
26
|
+
layerId: g.layerId,
|
|
27
|
+
initialFocus: l
|
|
28
|
+
}), i(h, {
|
|
29
|
+
layerId: g.layerId,
|
|
30
|
+
container: s
|
|
31
|
+
}), a(h), r({
|
|
32
|
+
enabled: p,
|
|
33
|
+
container: s,
|
|
34
|
+
triggerRef: u,
|
|
35
|
+
finalFocusRef: d,
|
|
36
|
+
requireFocusInside: m,
|
|
37
|
+
layerId: g.layerId
|
|
38
|
+
}), g;
|
|
26
39
|
}
|
|
27
40
|
//#endregion
|
|
28
|
-
export {
|
|
41
|
+
export { o as useModalLayer };
|
|
29
42
|
|
|
30
43
|
//# sourceMappingURL=useModalLayer.mjs.map
|