@teamturing/react-kit 2.11.0 → 2.13.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/dist/core/Button/index.d.ts +2 -2
- package/dist/core/IconButton/index.d.ts +2 -2
- package/dist/core/Overlay/index.d.ts +18 -0
- package/dist/core/OverlayPopper/index.d.ts +16 -0
- package/dist/hook/useFocusTrap.d.ts +13 -0
- package/dist/hook/useFocusZone.d.ts +15 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2523 -164
- package/esm/core/Button/index.js +2 -2
- package/esm/core/Dialog/index.js +8 -36
- package/esm/core/IconButton/index.js +1 -1
- package/esm/core/Overlay/index.js +92 -0
- package/esm/core/OverlayPopper/index.js +69 -0
- package/esm/hook/useFocusTrap.js +39 -0
- package/esm/hook/useFocusZone.js +35 -0
- package/esm/index.js +6 -0
- package/esm/node_modules/@floating-ui/core/dist/floating-ui.core.js +475 -0
- package/esm/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +599 -0
- package/esm/node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.js +229 -0
- package/esm/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +121 -0
- package/esm/node_modules/@floating-ui/utils/dom/dist/floating-ui.utils.dom.js +128 -0
- package/esm/node_modules/@primer/behaviors/dist/esm/focus-trap.js +105 -0
- package/esm/node_modules/@primer/behaviors/dist/esm/focus-zone.js +436 -0
- package/esm/node_modules/@primer/behaviors/dist/esm/polyfills/event-listener-signal.js +38 -0
- package/esm/node_modules/@primer/behaviors/dist/esm/utils/iterate-focusable-elements.js +65 -0
- package/esm/node_modules/@primer/behaviors/dist/esm/utils/unique-id.js +6 -0
- package/esm/node_modules/@primer/behaviors/dist/esm/utils/user-agent.js +9 -0
- package/package.json +5 -2
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { computePosition } from '../../dom/dist/floating-ui.dom.js';
|
|
2
|
+
export { autoUpdate, platform } from '../../dom/dist/floating-ui.dom.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { useLayoutEffect, useEffect } from 'react';
|
|
5
|
+
import * as ReactDOM from 'react-dom';
|
|
6
|
+
|
|
7
|
+
var index = typeof document !== 'undefined' ? useLayoutEffect : useEffect;
|
|
8
|
+
|
|
9
|
+
// Fork of `fast-deep-equal` that only does the comparisons we need and compares
|
|
10
|
+
// functions
|
|
11
|
+
function deepEqual(a, b) {
|
|
12
|
+
if (a === b) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (typeof a !== typeof b) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (typeof a === 'function' && a.toString() === b.toString()) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
let length, i, keys;
|
|
22
|
+
if (a && b && typeof a == 'object') {
|
|
23
|
+
if (Array.isArray(a)) {
|
|
24
|
+
length = a.length;
|
|
25
|
+
if (length != b.length) return false;
|
|
26
|
+
for (i = length; i-- !== 0;) {
|
|
27
|
+
if (!deepEqual(a[i], b[i])) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
keys = Object.keys(a);
|
|
34
|
+
length = keys.length;
|
|
35
|
+
if (length !== Object.keys(b).length) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
for (i = length; i-- !== 0;) {
|
|
39
|
+
if (!{}.hasOwnProperty.call(b, keys[i])) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (i = length; i-- !== 0;) {
|
|
44
|
+
const key = keys[i];
|
|
45
|
+
if (key === '_owner' && a.$$typeof) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (!deepEqual(a[key], b[key])) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return a !== a && b !== b;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getDPR(element) {
|
|
58
|
+
if (typeof window === 'undefined') {
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
const win = element.ownerDocument.defaultView || window;
|
|
62
|
+
return win.devicePixelRatio || 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function roundByDPR(element, value) {
|
|
66
|
+
const dpr = getDPR(element);
|
|
67
|
+
return Math.round(value * dpr) / dpr;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function useLatestRef(value) {
|
|
71
|
+
const ref = React.useRef(value);
|
|
72
|
+
index(() => {
|
|
73
|
+
ref.current = value;
|
|
74
|
+
});
|
|
75
|
+
return ref;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Provides data to position a floating element.
|
|
80
|
+
* @see https://floating-ui.com/docs/react
|
|
81
|
+
*/
|
|
82
|
+
function useFloating(options) {
|
|
83
|
+
if (options === void 0) {
|
|
84
|
+
options = {};
|
|
85
|
+
}
|
|
86
|
+
const {
|
|
87
|
+
placement = 'bottom',
|
|
88
|
+
strategy = 'absolute',
|
|
89
|
+
middleware = [],
|
|
90
|
+
platform,
|
|
91
|
+
elements: {
|
|
92
|
+
reference: externalReference,
|
|
93
|
+
floating: externalFloating
|
|
94
|
+
} = {},
|
|
95
|
+
transform = true,
|
|
96
|
+
whileElementsMounted,
|
|
97
|
+
open
|
|
98
|
+
} = options;
|
|
99
|
+
const [data, setData] = React.useState({
|
|
100
|
+
x: 0,
|
|
101
|
+
y: 0,
|
|
102
|
+
strategy,
|
|
103
|
+
placement,
|
|
104
|
+
middlewareData: {},
|
|
105
|
+
isPositioned: false
|
|
106
|
+
});
|
|
107
|
+
const [latestMiddleware, setLatestMiddleware] = React.useState(middleware);
|
|
108
|
+
if (!deepEqual(latestMiddleware, middleware)) {
|
|
109
|
+
setLatestMiddleware(middleware);
|
|
110
|
+
}
|
|
111
|
+
const [_reference, _setReference] = React.useState(null);
|
|
112
|
+
const [_floating, _setFloating] = React.useState(null);
|
|
113
|
+
const setReference = React.useCallback(node => {
|
|
114
|
+
if (node != referenceRef.current) {
|
|
115
|
+
referenceRef.current = node;
|
|
116
|
+
_setReference(node);
|
|
117
|
+
}
|
|
118
|
+
}, [_setReference]);
|
|
119
|
+
const setFloating = React.useCallback(node => {
|
|
120
|
+
if (node !== floatingRef.current) {
|
|
121
|
+
floatingRef.current = node;
|
|
122
|
+
_setFloating(node);
|
|
123
|
+
}
|
|
124
|
+
}, [_setFloating]);
|
|
125
|
+
const referenceEl = externalReference || _reference;
|
|
126
|
+
const floatingEl = externalFloating || _floating;
|
|
127
|
+
const referenceRef = React.useRef(null);
|
|
128
|
+
const floatingRef = React.useRef(null);
|
|
129
|
+
const dataRef = React.useRef(data);
|
|
130
|
+
const whileElementsMountedRef = useLatestRef(whileElementsMounted);
|
|
131
|
+
const platformRef = useLatestRef(platform);
|
|
132
|
+
const update = React.useCallback(() => {
|
|
133
|
+
if (!referenceRef.current || !floatingRef.current) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const config = {
|
|
137
|
+
placement,
|
|
138
|
+
strategy,
|
|
139
|
+
middleware: latestMiddleware
|
|
140
|
+
};
|
|
141
|
+
if (platformRef.current) {
|
|
142
|
+
config.platform = platformRef.current;
|
|
143
|
+
}
|
|
144
|
+
computePosition(referenceRef.current, floatingRef.current, config).then(data => {
|
|
145
|
+
const fullData = {
|
|
146
|
+
...data,
|
|
147
|
+
isPositioned: true
|
|
148
|
+
};
|
|
149
|
+
if (isMountedRef.current && !deepEqual(dataRef.current, fullData)) {
|
|
150
|
+
dataRef.current = fullData;
|
|
151
|
+
ReactDOM.flushSync(() => {
|
|
152
|
+
setData(fullData);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}, [latestMiddleware, placement, strategy, platformRef]);
|
|
157
|
+
index(() => {
|
|
158
|
+
if (open === false && dataRef.current.isPositioned) {
|
|
159
|
+
dataRef.current.isPositioned = false;
|
|
160
|
+
setData(data => ({
|
|
161
|
+
...data,
|
|
162
|
+
isPositioned: false
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
}, [open]);
|
|
166
|
+
const isMountedRef = React.useRef(false);
|
|
167
|
+
index(() => {
|
|
168
|
+
isMountedRef.current = true;
|
|
169
|
+
return () => {
|
|
170
|
+
isMountedRef.current = false;
|
|
171
|
+
};
|
|
172
|
+
}, []);
|
|
173
|
+
index(() => {
|
|
174
|
+
if (referenceEl) referenceRef.current = referenceEl;
|
|
175
|
+
if (floatingEl) floatingRef.current = floatingEl;
|
|
176
|
+
if (referenceEl && floatingEl) {
|
|
177
|
+
if (whileElementsMountedRef.current) {
|
|
178
|
+
return whileElementsMountedRef.current(referenceEl, floatingEl, update);
|
|
179
|
+
} else {
|
|
180
|
+
update();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}, [referenceEl, floatingEl, update, whileElementsMountedRef]);
|
|
184
|
+
const refs = React.useMemo(() => ({
|
|
185
|
+
reference: referenceRef,
|
|
186
|
+
floating: floatingRef,
|
|
187
|
+
setReference,
|
|
188
|
+
setFloating
|
|
189
|
+
}), [setReference, setFloating]);
|
|
190
|
+
const elements = React.useMemo(() => ({
|
|
191
|
+
reference: referenceEl,
|
|
192
|
+
floating: floatingEl
|
|
193
|
+
}), [referenceEl, floatingEl]);
|
|
194
|
+
const floatingStyles = React.useMemo(() => {
|
|
195
|
+
const initialStyles = {
|
|
196
|
+
position: strategy,
|
|
197
|
+
left: 0,
|
|
198
|
+
top: 0
|
|
199
|
+
};
|
|
200
|
+
if (!elements.floating) {
|
|
201
|
+
return initialStyles;
|
|
202
|
+
}
|
|
203
|
+
const x = roundByDPR(elements.floating, data.x);
|
|
204
|
+
const y = roundByDPR(elements.floating, data.y);
|
|
205
|
+
if (transform) {
|
|
206
|
+
return {
|
|
207
|
+
...initialStyles,
|
|
208
|
+
transform: "translate(" + x + "px, " + y + "px)",
|
|
209
|
+
...(getDPR(elements.floating) >= 1.5 && {
|
|
210
|
+
willChange: 'transform'
|
|
211
|
+
})
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
position: strategy,
|
|
216
|
+
left: x,
|
|
217
|
+
top: y
|
|
218
|
+
};
|
|
219
|
+
}, [strategy, transform, elements.floating, data.x, data.y]);
|
|
220
|
+
return React.useMemo(() => ({
|
|
221
|
+
...data,
|
|
222
|
+
update,
|
|
223
|
+
refs,
|
|
224
|
+
elements,
|
|
225
|
+
floatingStyles
|
|
226
|
+
}), [data, update, refs, elements, floatingStyles]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export { computePosition, useFloating };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const min = Math.min;
|
|
2
|
+
const max = Math.max;
|
|
3
|
+
const round = Math.round;
|
|
4
|
+
const floor = Math.floor;
|
|
5
|
+
const createCoords = v => ({
|
|
6
|
+
x: v,
|
|
7
|
+
y: v
|
|
8
|
+
});
|
|
9
|
+
const oppositeSideMap = {
|
|
10
|
+
left: 'right',
|
|
11
|
+
right: 'left',
|
|
12
|
+
bottom: 'top',
|
|
13
|
+
top: 'bottom'
|
|
14
|
+
};
|
|
15
|
+
const oppositeAlignmentMap = {
|
|
16
|
+
start: 'end',
|
|
17
|
+
end: 'start'
|
|
18
|
+
};
|
|
19
|
+
function clamp(start, value, end) {
|
|
20
|
+
return max(start, min(value, end));
|
|
21
|
+
}
|
|
22
|
+
function evaluate(value, param) {
|
|
23
|
+
return typeof value === 'function' ? value(param) : value;
|
|
24
|
+
}
|
|
25
|
+
function getSide(placement) {
|
|
26
|
+
return placement.split('-')[0];
|
|
27
|
+
}
|
|
28
|
+
function getAlignment(placement) {
|
|
29
|
+
return placement.split('-')[1];
|
|
30
|
+
}
|
|
31
|
+
function getOppositeAxis(axis) {
|
|
32
|
+
return axis === 'x' ? 'y' : 'x';
|
|
33
|
+
}
|
|
34
|
+
function getAxisLength(axis) {
|
|
35
|
+
return axis === 'y' ? 'height' : 'width';
|
|
36
|
+
}
|
|
37
|
+
function getSideAxis(placement) {
|
|
38
|
+
return ['top', 'bottom'].includes(getSide(placement)) ? 'y' : 'x';
|
|
39
|
+
}
|
|
40
|
+
function getAlignmentAxis(placement) {
|
|
41
|
+
return getOppositeAxis(getSideAxis(placement));
|
|
42
|
+
}
|
|
43
|
+
function getAlignmentSides(placement, rects, rtl) {
|
|
44
|
+
if (rtl === void 0) {
|
|
45
|
+
rtl = false;
|
|
46
|
+
}
|
|
47
|
+
const alignment = getAlignment(placement);
|
|
48
|
+
const alignmentAxis = getAlignmentAxis(placement);
|
|
49
|
+
const length = getAxisLength(alignmentAxis);
|
|
50
|
+
let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';
|
|
51
|
+
if (rects.reference[length] > rects.floating[length]) {
|
|
52
|
+
mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
|
|
53
|
+
}
|
|
54
|
+
return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
|
|
55
|
+
}
|
|
56
|
+
function getExpandedPlacements(placement) {
|
|
57
|
+
const oppositePlacement = getOppositePlacement(placement);
|
|
58
|
+
return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
|
|
59
|
+
}
|
|
60
|
+
function getOppositeAlignmentPlacement(placement) {
|
|
61
|
+
return placement.replace(/start|end/g, alignment => oppositeAlignmentMap[alignment]);
|
|
62
|
+
}
|
|
63
|
+
function getSideList(side, isStart, rtl) {
|
|
64
|
+
const lr = ['left', 'right'];
|
|
65
|
+
const rl = ['right', 'left'];
|
|
66
|
+
const tb = ['top', 'bottom'];
|
|
67
|
+
const bt = ['bottom', 'top'];
|
|
68
|
+
switch (side) {
|
|
69
|
+
case 'top':
|
|
70
|
+
case 'bottom':
|
|
71
|
+
if (rtl) return isStart ? rl : lr;
|
|
72
|
+
return isStart ? lr : rl;
|
|
73
|
+
case 'left':
|
|
74
|
+
case 'right':
|
|
75
|
+
return isStart ? tb : bt;
|
|
76
|
+
default:
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
|
|
81
|
+
const alignment = getAlignment(placement);
|
|
82
|
+
let list = getSideList(getSide(placement), direction === 'start', rtl);
|
|
83
|
+
if (alignment) {
|
|
84
|
+
list = list.map(side => side + "-" + alignment);
|
|
85
|
+
if (flipAlignment) {
|
|
86
|
+
list = list.concat(list.map(getOppositeAlignmentPlacement));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return list;
|
|
90
|
+
}
|
|
91
|
+
function getOppositePlacement(placement) {
|
|
92
|
+
return placement.replace(/left|right|bottom|top/g, side => oppositeSideMap[side]);
|
|
93
|
+
}
|
|
94
|
+
function expandPaddingObject(padding) {
|
|
95
|
+
return {
|
|
96
|
+
top: 0,
|
|
97
|
+
right: 0,
|
|
98
|
+
bottom: 0,
|
|
99
|
+
left: 0,
|
|
100
|
+
...padding
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function getPaddingObject(padding) {
|
|
104
|
+
return typeof padding !== 'number' ? expandPaddingObject(padding) : {
|
|
105
|
+
top: padding,
|
|
106
|
+
right: padding,
|
|
107
|
+
bottom: padding,
|
|
108
|
+
left: padding
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function rectToClientRect(rect) {
|
|
112
|
+
return {
|
|
113
|
+
...rect,
|
|
114
|
+
top: rect.y,
|
|
115
|
+
left: rect.x,
|
|
116
|
+
right: rect.x + rect.width,
|
|
117
|
+
bottom: rect.y + rect.height
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { clamp, createCoords, evaluate, expandPaddingObject, floor, getAlignment, getAlignmentAxis, getAlignmentSides, getAxisLength, getExpandedPlacements, getOppositeAlignmentPlacement, getOppositeAxis, getOppositeAxisPlacements, getOppositePlacement, getPaddingObject, getSide, getSideAxis, max, min, rectToClientRect, round };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
function getNodeName(node) {
|
|
2
|
+
if (isNode(node)) {
|
|
3
|
+
return (node.nodeName || '').toLowerCase();
|
|
4
|
+
}
|
|
5
|
+
// Mocked nodes in testing environments may not be instances of Node. By
|
|
6
|
+
// returning `#document` an infinite loop won't occur.
|
|
7
|
+
// https://github.com/floating-ui/floating-ui/issues/2317
|
|
8
|
+
return '#document';
|
|
9
|
+
}
|
|
10
|
+
function getWindow(node) {
|
|
11
|
+
var _node$ownerDocument;
|
|
12
|
+
return (node == null ? void 0 : (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
|
|
13
|
+
}
|
|
14
|
+
function getDocumentElement(node) {
|
|
15
|
+
var _ref;
|
|
16
|
+
return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
|
|
17
|
+
}
|
|
18
|
+
function isNode(value) {
|
|
19
|
+
return value instanceof Node || value instanceof getWindow(value).Node;
|
|
20
|
+
}
|
|
21
|
+
function isElement(value) {
|
|
22
|
+
return value instanceof Element || value instanceof getWindow(value).Element;
|
|
23
|
+
}
|
|
24
|
+
function isHTMLElement(value) {
|
|
25
|
+
return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
|
|
26
|
+
}
|
|
27
|
+
function isShadowRoot(value) {
|
|
28
|
+
// Browsers without `ShadowRoot` support.
|
|
29
|
+
if (typeof ShadowRoot === 'undefined') {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
|
|
33
|
+
}
|
|
34
|
+
function isOverflowElement(element) {
|
|
35
|
+
const {
|
|
36
|
+
overflow,
|
|
37
|
+
overflowX,
|
|
38
|
+
overflowY,
|
|
39
|
+
display
|
|
40
|
+
} = getComputedStyle(element);
|
|
41
|
+
return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
|
|
42
|
+
}
|
|
43
|
+
function isTableElement(element) {
|
|
44
|
+
return ['table', 'td', 'th'].includes(getNodeName(element));
|
|
45
|
+
}
|
|
46
|
+
function isContainingBlock(element) {
|
|
47
|
+
const webkit = isWebKit();
|
|
48
|
+
const css = getComputedStyle(element);
|
|
49
|
+
|
|
50
|
+
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
|
|
51
|
+
return css.transform !== 'none' || css.perspective !== 'none' || (css.containerType ? css.containerType !== 'normal' : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== 'none' : false) || !webkit && (css.filter ? css.filter !== 'none' : false) || ['transform', 'perspective', 'filter'].some(value => (css.willChange || '').includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => (css.contain || '').includes(value));
|
|
52
|
+
}
|
|
53
|
+
function getContainingBlock(element) {
|
|
54
|
+
let currentNode = getParentNode(element);
|
|
55
|
+
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
|
|
56
|
+
if (isContainingBlock(currentNode)) {
|
|
57
|
+
return currentNode;
|
|
58
|
+
} else {
|
|
59
|
+
currentNode = getParentNode(currentNode);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function isWebKit() {
|
|
65
|
+
if (typeof CSS === 'undefined' || !CSS.supports) return false;
|
|
66
|
+
return CSS.supports('-webkit-backdrop-filter', 'none');
|
|
67
|
+
}
|
|
68
|
+
function isLastTraversableNode(node) {
|
|
69
|
+
return ['html', 'body', '#document'].includes(getNodeName(node));
|
|
70
|
+
}
|
|
71
|
+
function getComputedStyle(element) {
|
|
72
|
+
return getWindow(element).getComputedStyle(element);
|
|
73
|
+
}
|
|
74
|
+
function getNodeScroll(element) {
|
|
75
|
+
if (isElement(element)) {
|
|
76
|
+
return {
|
|
77
|
+
scrollLeft: element.scrollLeft,
|
|
78
|
+
scrollTop: element.scrollTop
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
scrollLeft: element.pageXOffset,
|
|
83
|
+
scrollTop: element.pageYOffset
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function getParentNode(node) {
|
|
87
|
+
if (getNodeName(node) === 'html') {
|
|
88
|
+
return node;
|
|
89
|
+
}
|
|
90
|
+
const result =
|
|
91
|
+
// Step into the shadow DOM of the parent of a slotted node.
|
|
92
|
+
node.assignedSlot ||
|
|
93
|
+
// DOM Element detected.
|
|
94
|
+
node.parentNode ||
|
|
95
|
+
// ShadowRoot detected.
|
|
96
|
+
isShadowRoot(node) && node.host ||
|
|
97
|
+
// Fallback.
|
|
98
|
+
getDocumentElement(node);
|
|
99
|
+
return isShadowRoot(result) ? result.host : result;
|
|
100
|
+
}
|
|
101
|
+
function getNearestOverflowAncestor(node) {
|
|
102
|
+
const parentNode = getParentNode(node);
|
|
103
|
+
if (isLastTraversableNode(parentNode)) {
|
|
104
|
+
return node.ownerDocument ? node.ownerDocument.body : node.body;
|
|
105
|
+
}
|
|
106
|
+
if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
|
|
107
|
+
return parentNode;
|
|
108
|
+
}
|
|
109
|
+
return getNearestOverflowAncestor(parentNode);
|
|
110
|
+
}
|
|
111
|
+
function getOverflowAncestors(node, list, traverseIframes) {
|
|
112
|
+
var _node$ownerDocument2;
|
|
113
|
+
if (list === void 0) {
|
|
114
|
+
list = [];
|
|
115
|
+
}
|
|
116
|
+
if (traverseIframes === void 0) {
|
|
117
|
+
traverseIframes = true;
|
|
118
|
+
}
|
|
119
|
+
const scrollableAncestor = getNearestOverflowAncestor(node);
|
|
120
|
+
const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
|
|
121
|
+
const win = getWindow(scrollableAncestor);
|
|
122
|
+
if (isBody) {
|
|
123
|
+
return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], win.frameElement && traverseIframes ? getOverflowAncestors(win.frameElement) : []);
|
|
124
|
+
}
|
|
125
|
+
return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { getComputedStyle, getContainingBlock, getDocumentElement, getNearestOverflowAncestor, getNodeName, getNodeScroll, getOverflowAncestors, getParentNode, getWindow, isContainingBlock, isElement, isHTMLElement, isLastTraversableNode, isNode, isOverflowElement, isShadowRoot, isTableElement, isWebKit };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { getFocusableChild, isTabbable } from './utils/iterate-focusable-elements.js';
|
|
2
|
+
import { polyfill } from './polyfills/event-listener-signal.js';
|
|
3
|
+
|
|
4
|
+
polyfill();
|
|
5
|
+
const suspendedTrapStack = [];
|
|
6
|
+
let activeTrap = undefined;
|
|
7
|
+
function tryReactivate() {
|
|
8
|
+
const trapToReactivate = suspendedTrapStack.pop();
|
|
9
|
+
if (trapToReactivate) {
|
|
10
|
+
focusTrap(trapToReactivate.container, trapToReactivate.initialFocus, trapToReactivate.originalSignal);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function followSignal(signal) {
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
signal.addEventListener('abort', () => {
|
|
16
|
+
controller.abort();
|
|
17
|
+
});
|
|
18
|
+
return controller;
|
|
19
|
+
}
|
|
20
|
+
function focusTrap(container, initialFocus, abortSignal) {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
|
|
23
|
+
container.setAttribute('data-focus-trap', 'active');
|
|
24
|
+
const sentinelStart = document.createElement('span');
|
|
25
|
+
sentinelStart.setAttribute('class', 'sentinel');
|
|
26
|
+
sentinelStart.setAttribute('tabindex', '0');
|
|
27
|
+
sentinelStart.setAttribute('aria-hidden', 'true');
|
|
28
|
+
sentinelStart.onfocus = () => {
|
|
29
|
+
const lastFocusableChild = getFocusableChild(container, true);
|
|
30
|
+
lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
|
|
31
|
+
};
|
|
32
|
+
const sentinelEnd = document.createElement('span');
|
|
33
|
+
sentinelEnd.setAttribute('class', 'sentinel');
|
|
34
|
+
sentinelEnd.setAttribute('tabindex', '0');
|
|
35
|
+
sentinelEnd.setAttribute('aria-hidden', 'true');
|
|
36
|
+
sentinelEnd.onfocus = () => {
|
|
37
|
+
const firstFocusableChild = getFocusableChild(container);
|
|
38
|
+
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
39
|
+
};
|
|
40
|
+
container.prepend(sentinelStart);
|
|
41
|
+
container.append(sentinelEnd);
|
|
42
|
+
let lastFocusedChild = undefined;
|
|
43
|
+
function ensureTrapZoneHasFocus(focusedElement) {
|
|
44
|
+
if (focusedElement instanceof HTMLElement && document.contains(container)) {
|
|
45
|
+
if (container.contains(focusedElement)) {
|
|
46
|
+
lastFocusedChild = focusedElement;
|
|
47
|
+
return;
|
|
48
|
+
} else {
|
|
49
|
+
if (lastFocusedChild && isTabbable(lastFocusedChild) && container.contains(lastFocusedChild)) {
|
|
50
|
+
lastFocusedChild.focus();
|
|
51
|
+
return;
|
|
52
|
+
} else if (initialFocus && container.contains(initialFocus)) {
|
|
53
|
+
initialFocus.focus();
|
|
54
|
+
return;
|
|
55
|
+
} else {
|
|
56
|
+
const firstFocusableChild = getFocusableChild(container);
|
|
57
|
+
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const wrappingController = followSignal(signal);
|
|
64
|
+
if (activeTrap) {
|
|
65
|
+
const suspendedTrap = activeTrap;
|
|
66
|
+
activeTrap.container.setAttribute('data-focus-trap', 'suspended');
|
|
67
|
+
activeTrap.controller.abort();
|
|
68
|
+
suspendedTrapStack.push(suspendedTrap);
|
|
69
|
+
}
|
|
70
|
+
wrappingController.signal.addEventListener('abort', () => {
|
|
71
|
+
activeTrap = undefined;
|
|
72
|
+
});
|
|
73
|
+
signal.addEventListener('abort', () => {
|
|
74
|
+
container.removeAttribute('data-focus-trap');
|
|
75
|
+
const sentinels = container.getElementsByClassName('sentinel');
|
|
76
|
+
while (sentinels.length > 0) sentinels[0].remove();
|
|
77
|
+
const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
|
|
78
|
+
if (suspendedTrapIndex >= 0) {
|
|
79
|
+
suspendedTrapStack.splice(suspendedTrapIndex, 1);
|
|
80
|
+
}
|
|
81
|
+
tryReactivate();
|
|
82
|
+
});
|
|
83
|
+
document.addEventListener('focus', event => {
|
|
84
|
+
ensureTrapZoneHasFocus(event.target);
|
|
85
|
+
}, {
|
|
86
|
+
signal: wrappingController.signal,
|
|
87
|
+
capture: true
|
|
88
|
+
});
|
|
89
|
+
ensureTrapZoneHasFocus(document.activeElement);
|
|
90
|
+
activeTrap = {
|
|
91
|
+
container,
|
|
92
|
+
controller: wrappingController,
|
|
93
|
+
initialFocus,
|
|
94
|
+
originalSignal: signal
|
|
95
|
+
};
|
|
96
|
+
const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
|
|
97
|
+
if (suspendedTrapIndex >= 0) {
|
|
98
|
+
suspendedTrapStack.splice(suspendedTrapIndex, 1);
|
|
99
|
+
}
|
|
100
|
+
if (!abortSignal) {
|
|
101
|
+
return controller;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { focusTrap };
|