@primer/behaviors 0.0.0-2022927173910 → 0.0.0-20230925221012
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/anchored-position.js +25 -8
- package/dist/cjs/dimensions.js +3 -3
- package/dist/cjs/focus-trap.js +1 -1
- package/dist/cjs/focus-zone.d.ts +1 -0
- package/dist/cjs/focus-zone.js +34 -12
- package/dist/cjs/polyfills/event-listener-signal.js +2 -2
- package/dist/cjs/utils/iterate-focusable-elements.js +1 -1
- package/dist/esm/anchored-position.js +25 -8
- package/dist/esm/dimensions.js +3 -3
- package/dist/esm/focus-trap.js +1 -1
- package/dist/esm/focus-zone.d.ts +1 -0
- package/dist/esm/focus-zone.js +34 -12
- package/dist/esm/polyfills/event-listener-signal.js +2 -2
- package/dist/esm/utils/iterate-focusable-elements.js +1 -1
- package/package.json +9 -8
|
@@ -5,12 +5,12 @@ const alternateOrders = {
|
|
|
5
5
|
'outside-top': ['outside-bottom', 'outside-right', 'outside-left', 'outside-bottom'],
|
|
6
6
|
'outside-bottom': ['outside-top', 'outside-right', 'outside-left', 'outside-bottom'],
|
|
7
7
|
'outside-left': ['outside-right', 'outside-bottom', 'outside-top', 'outside-bottom'],
|
|
8
|
-
'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom']
|
|
8
|
+
'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom'],
|
|
9
9
|
};
|
|
10
10
|
const alternateAlignments = {
|
|
11
11
|
start: ['end', 'center'],
|
|
12
12
|
end: ['start', 'center'],
|
|
13
|
-
center: ['end', 'start']
|
|
13
|
+
center: ['end', 'start'],
|
|
14
14
|
};
|
|
15
15
|
function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
|
|
16
16
|
const parentElement = getPositionedParent(floatingElement);
|
|
@@ -20,12 +20,14 @@ function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
|
|
|
20
20
|
const [borderTop, borderLeft] = [parentElementStyle.borderTopWidth, parentElementStyle.borderLeftWidth].map(v => parseInt(v, 10) || 0);
|
|
21
21
|
const relativeRect = {
|
|
22
22
|
top: parentElementRect.top + borderTop,
|
|
23
|
-
left: parentElementRect.left + borderLeft
|
|
23
|
+
left: parentElementRect.left + borderLeft,
|
|
24
24
|
};
|
|
25
25
|
return pureCalculateAnchoredPosition(clippingRect, relativeRect, floatingElement.getBoundingClientRect(), anchorElement instanceof Element ? anchorElement.getBoundingClientRect() : anchorElement, getDefaultSettings(settings));
|
|
26
26
|
}
|
|
27
27
|
exports.getAnchoredPosition = getAnchoredPosition;
|
|
28
28
|
function getPositionedParent(element) {
|
|
29
|
+
if (isOnTopLayer(element))
|
|
30
|
+
return document.body;
|
|
29
31
|
let parentNode = element.parentNode;
|
|
30
32
|
while (parentNode !== null) {
|
|
31
33
|
if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
|
|
@@ -35,6 +37,21 @@ function getPositionedParent(element) {
|
|
|
35
37
|
}
|
|
36
38
|
return document.body;
|
|
37
39
|
}
|
|
40
|
+
function isOnTopLayer(element) {
|
|
41
|
+
var _a;
|
|
42
|
+
if (element.tagName === 'DIALOG') {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
if (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString())) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (_b) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
38
55
|
function getClippingRect(element) {
|
|
39
56
|
let parentNode = element;
|
|
40
57
|
while (parentNode !== null) {
|
|
@@ -54,13 +71,13 @@ function getClippingRect(element) {
|
|
|
54
71
|
elemStyle.borderTopWidth,
|
|
55
72
|
elemStyle.borderLeftWidth,
|
|
56
73
|
elemStyle.borderRightWidth,
|
|
57
|
-
elemStyle.borderBottomWidth
|
|
74
|
+
elemStyle.borderBottomWidth,
|
|
58
75
|
].map(v => parseInt(v, 10) || 0);
|
|
59
76
|
return {
|
|
60
77
|
top: elemRect.top + borderTop,
|
|
61
78
|
left: elemRect.left + borderLeft,
|
|
62
79
|
width: elemRect.width - borderRight - borderLeft,
|
|
63
|
-
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity)
|
|
80
|
+
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity),
|
|
64
81
|
};
|
|
65
82
|
}
|
|
66
83
|
const positionDefaults = {
|
|
@@ -68,7 +85,7 @@ const positionDefaults = {
|
|
|
68
85
|
align: 'start',
|
|
69
86
|
anchorOffset: 4,
|
|
70
87
|
alignmentOffset: 4,
|
|
71
|
-
allowOutOfBounds: false
|
|
88
|
+
allowOutOfBounds: false,
|
|
72
89
|
};
|
|
73
90
|
function getDefaultSettings(settings = {}) {
|
|
74
91
|
var _a, _b, _c, _d, _e;
|
|
@@ -79,7 +96,7 @@ function getDefaultSettings(settings = {}) {
|
|
|
79
96
|
align,
|
|
80
97
|
anchorOffset: (_c = settings.anchorOffset) !== null && _c !== void 0 ? _c : (side === 'inside-center' ? 0 : positionDefaults.anchorOffset),
|
|
81
98
|
alignmentOffset: (_d = settings.alignmentOffset) !== null && _d !== void 0 ? _d : (align !== 'center' && side.startsWith('inside') ? positionDefaults.alignmentOffset : 0),
|
|
82
|
-
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds
|
|
99
|
+
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds,
|
|
83
100
|
};
|
|
84
101
|
}
|
|
85
102
|
function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingRect, anchorRect, { side, align, allowOutOfBounds, anchorOffset, alignmentOffset }) {
|
|
@@ -87,7 +104,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
87
104
|
top: viewportRect.top - relativePosition.top,
|
|
88
105
|
left: viewportRect.left - relativePosition.left,
|
|
89
106
|
width: viewportRect.width,
|
|
90
|
-
height: viewportRect.height
|
|
107
|
+
height: viewportRect.height,
|
|
91
108
|
};
|
|
92
109
|
let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
|
|
93
110
|
let anchorSide = side;
|
package/dist/cjs/dimensions.js
CHANGED
|
@@ -5,7 +5,7 @@ function offset(element) {
|
|
|
5
5
|
const rect = element.getBoundingClientRect();
|
|
6
6
|
return {
|
|
7
7
|
top: rect.top + window.pageYOffset,
|
|
8
|
-
left: rect.left + window.pageXOffset
|
|
8
|
+
left: rect.left + window.pageXOffset,
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
exports.offset = offset;
|
|
@@ -62,11 +62,11 @@ function overflowOffset(element, targetContainer) {
|
|
|
62
62
|
const scroll = container === document.documentElement && document.defaultView
|
|
63
63
|
? {
|
|
64
64
|
top: document.defaultView.pageYOffset,
|
|
65
|
-
left: document.defaultView.pageXOffset
|
|
65
|
+
left: document.defaultView.pageXOffset,
|
|
66
66
|
}
|
|
67
67
|
: {
|
|
68
68
|
top: container.scrollTop,
|
|
69
|
-
left: container.scrollLeft
|
|
69
|
+
left: container.scrollLeft,
|
|
70
70
|
};
|
|
71
71
|
const top = elementOffset.top - scroll.top;
|
|
72
72
|
const left = elementOffset.left - scroll.left;
|
package/dist/cjs/focus-trap.js
CHANGED
|
@@ -94,7 +94,7 @@ function focusTrap(container, initialFocus, abortSignal) {
|
|
|
94
94
|
container,
|
|
95
95
|
controller: wrappingController,
|
|
96
96
|
initialFocus,
|
|
97
|
-
originalSignal: signal
|
|
97
|
+
originalSignal: signal,
|
|
98
98
|
};
|
|
99
99
|
const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
|
|
100
100
|
if (suspendedTrapIndex >= 0) {
|
package/dist/cjs/focus-zone.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface FocusZoneSettings {
|
|
|
25
25
|
activeDescendantControl?: HTMLElement;
|
|
26
26
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
27
27
|
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
28
|
+
preventScroll?: boolean;
|
|
28
29
|
}
|
|
29
30
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
30
31
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
package/dist/cjs/focus-zone.js
CHANGED
|
@@ -41,7 +41,7 @@ const KEY_TO_BIT = {
|
|
|
41
41
|
End: FocusKeys.HomeAndEnd,
|
|
42
42
|
PageUp: FocusKeys.PageUpDown,
|
|
43
43
|
PageDown: FocusKeys.PageUpDown,
|
|
44
|
-
Backspace: FocusKeys.Backspace
|
|
44
|
+
Backspace: FocusKeys.Backspace,
|
|
45
45
|
};
|
|
46
46
|
const KEY_TO_DIRECTION = {
|
|
47
47
|
ArrowLeft: 'previous',
|
|
@@ -61,7 +61,7 @@ const KEY_TO_DIRECTION = {
|
|
|
61
61
|
End: 'end',
|
|
62
62
|
PageUp: 'start',
|
|
63
63
|
PageDown: 'end',
|
|
64
|
-
Backspace: 'previous'
|
|
64
|
+
Backspace: 'previous',
|
|
65
65
|
};
|
|
66
66
|
function getDirection(keyboardEvent) {
|
|
67
67
|
const direction = KEY_TO_DIRECTION[keyboardEvent.key];
|
|
@@ -127,7 +127,7 @@ exports.activeDescendantActivatedDirectly = 'activated-directly';
|
|
|
127
127
|
exports.activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
128
128
|
exports.hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
129
129
|
function focusZone(container, settings) {
|
|
130
|
-
var _a, _b, _c, _d;
|
|
130
|
+
var _a, _b, _c, _d, _e;
|
|
131
131
|
const focusableElements = [];
|
|
132
132
|
const savedTabIndex = new WeakMap();
|
|
133
133
|
const bindKeys = (_a = settings === null || settings === void 0 ? void 0 : settings.bindKeys) !== null && _a !== void 0 ? _a : ((settings === null || settings === void 0 ? void 0 : settings.getNextFocusable) ? FocusKeys.ArrowAll : FocusKeys.ArrowVertical) | FocusKeys.HomeAndEnd;
|
|
@@ -136,6 +136,7 @@ function focusZone(container, settings) {
|
|
|
136
136
|
const activeDescendantControl = settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl;
|
|
137
137
|
const activeDescendantCallback = settings === null || settings === void 0 ? void 0 : settings.onActiveDescendantChanged;
|
|
138
138
|
let currentFocusedElement;
|
|
139
|
+
const preventScroll = (_d = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _d !== void 0 ? _d : false;
|
|
139
140
|
function getFirstFocusableElement() {
|
|
140
141
|
return focusableElements[0];
|
|
141
142
|
}
|
|
@@ -189,8 +190,7 @@ function focusZone(container, settings) {
|
|
|
189
190
|
if (filteredElements.length === 0) {
|
|
190
191
|
return;
|
|
191
192
|
}
|
|
192
|
-
|
|
193
|
-
focusableElements.splice(insertIndex === -1 ? focusableElements.length : insertIndex, 0, ...filteredElements);
|
|
193
|
+
focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
|
|
194
194
|
for (const element of filteredElements) {
|
|
195
195
|
if (!savedTabIndex.has(element)) {
|
|
196
196
|
savedTabIndex.set(element, element.getAttribute('tabindex'));
|
|
@@ -201,6 +201,27 @@ function focusZone(container, settings) {
|
|
|
201
201
|
updateFocusedElement(getFirstFocusableElement());
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
+
function findInsertionIndex(elementsToInsert) {
|
|
205
|
+
const firstElementToInsert = elementsToInsert[0];
|
|
206
|
+
if (focusableElements.length === 0)
|
|
207
|
+
return 0;
|
|
208
|
+
let iMin = 0;
|
|
209
|
+
let iMax = focusableElements.length - 1;
|
|
210
|
+
while (iMin <= iMax) {
|
|
211
|
+
const i = Math.floor((iMin + iMax) / 2);
|
|
212
|
+
const element = focusableElements[i];
|
|
213
|
+
if (followsInDocument(firstElementToInsert, element)) {
|
|
214
|
+
iMax = i - 1;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
iMin = i + 1;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return iMin;
|
|
221
|
+
}
|
|
222
|
+
function followsInDocument(first, second) {
|
|
223
|
+
return (second.compareDocumentPosition(first) & Node.DOCUMENT_POSITION_PRECEDING) > 0;
|
|
224
|
+
}
|
|
204
225
|
function endFocusManagement(...elements) {
|
|
205
226
|
for (const element of elements) {
|
|
206
227
|
const focusableElementIndex = focusableElements.indexOf(element);
|
|
@@ -224,7 +245,8 @@ function focusZone(container, settings) {
|
|
|
224
245
|
}
|
|
225
246
|
}
|
|
226
247
|
beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(container));
|
|
227
|
-
|
|
248
|
+
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
249
|
+
updateFocusedElement(initialElement);
|
|
228
250
|
const observer = new MutationObserver(mutations => {
|
|
229
251
|
for (const mutation of mutations) {
|
|
230
252
|
for (const removedNode of mutation.removedNodes) {
|
|
@@ -243,10 +265,10 @@ function focusZone(container, settings) {
|
|
|
243
265
|
});
|
|
244
266
|
observer.observe(container, {
|
|
245
267
|
subtree: true,
|
|
246
|
-
childList: true
|
|
268
|
+
childList: true,
|
|
247
269
|
});
|
|
248
270
|
const controller = new AbortController();
|
|
249
|
-
const signal = (
|
|
271
|
+
const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
|
|
250
272
|
signal.addEventListener('abort', () => {
|
|
251
273
|
endFocusManagement(...focusableElements);
|
|
252
274
|
});
|
|
@@ -259,7 +281,7 @@ function focusZone(container, settings) {
|
|
|
259
281
|
if (activeDescendantControl) {
|
|
260
282
|
container.addEventListener('focusin', event => {
|
|
261
283
|
if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
|
|
262
|
-
activeDescendantControl.focus();
|
|
284
|
+
activeDescendantControl.focus({ preventScroll });
|
|
263
285
|
updateFocusedElement(event.target);
|
|
264
286
|
}
|
|
265
287
|
});
|
|
@@ -303,7 +325,7 @@ function focusZone(container, settings) {
|
|
|
303
325
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
304
326
|
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
|
|
305
327
|
const targetElement = focusableElements[targetElementIndex];
|
|
306
|
-
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus();
|
|
328
|
+
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
|
|
307
329
|
return;
|
|
308
330
|
}
|
|
309
331
|
else {
|
|
@@ -315,7 +337,7 @@ function focusZone(container, settings) {
|
|
|
315
337
|
const elementToFocus = focusInStrategy(event.relatedTarget);
|
|
316
338
|
const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
|
|
317
339
|
if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
|
|
318
|
-
elementToFocus.focus();
|
|
340
|
+
elementToFocus.focus({ preventScroll });
|
|
319
341
|
return;
|
|
320
342
|
}
|
|
321
343
|
else {
|
|
@@ -400,7 +422,7 @@ function focusZone(container, settings) {
|
|
|
400
422
|
}
|
|
401
423
|
else if (nextElementToFocus) {
|
|
402
424
|
lastKeyboardFocusDirection = direction;
|
|
403
|
-
nextElementToFocus.focus();
|
|
425
|
+
nextElementToFocus.focus({ preventScroll });
|
|
404
426
|
}
|
|
405
427
|
if (event.key !== 'Tab' || nextElementToFocus) {
|
|
406
428
|
event.preventDefault();
|
|
@@ -6,7 +6,7 @@ function* iterateFocusableElements(container, options = {}) {
|
|
|
6
6
|
const strict = (_a = options.strict) !== null && _a !== void 0 ? _a : false;
|
|
7
7
|
const acceptFn = ((_b = options.onlyTabbable) !== null && _b !== void 0 ? _b : false) ? isTabbable : isFocusable;
|
|
8
8
|
const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
|
|
9
|
-
acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
|
9
|
+
acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP,
|
|
10
10
|
});
|
|
11
11
|
let nextNode = null;
|
|
12
12
|
if (!options.reverse && acceptFn(container, strict)) {
|
|
@@ -2,12 +2,12 @@ const alternateOrders = {
|
|
|
2
2
|
'outside-top': ['outside-bottom', 'outside-right', 'outside-left', 'outside-bottom'],
|
|
3
3
|
'outside-bottom': ['outside-top', 'outside-right', 'outside-left', 'outside-bottom'],
|
|
4
4
|
'outside-left': ['outside-right', 'outside-bottom', 'outside-top', 'outside-bottom'],
|
|
5
|
-
'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom']
|
|
5
|
+
'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom'],
|
|
6
6
|
};
|
|
7
7
|
const alternateAlignments = {
|
|
8
8
|
start: ['end', 'center'],
|
|
9
9
|
end: ['start', 'center'],
|
|
10
|
-
center: ['end', 'start']
|
|
10
|
+
center: ['end', 'start'],
|
|
11
11
|
};
|
|
12
12
|
export function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
|
|
13
13
|
const parentElement = getPositionedParent(floatingElement);
|
|
@@ -17,11 +17,13 @@ export function getAnchoredPosition(floatingElement, anchorElement, settings = {
|
|
|
17
17
|
const [borderTop, borderLeft] = [parentElementStyle.borderTopWidth, parentElementStyle.borderLeftWidth].map(v => parseInt(v, 10) || 0);
|
|
18
18
|
const relativeRect = {
|
|
19
19
|
top: parentElementRect.top + borderTop,
|
|
20
|
-
left: parentElementRect.left + borderLeft
|
|
20
|
+
left: parentElementRect.left + borderLeft,
|
|
21
21
|
};
|
|
22
22
|
return pureCalculateAnchoredPosition(clippingRect, relativeRect, floatingElement.getBoundingClientRect(), anchorElement instanceof Element ? anchorElement.getBoundingClientRect() : anchorElement, getDefaultSettings(settings));
|
|
23
23
|
}
|
|
24
24
|
function getPositionedParent(element) {
|
|
25
|
+
if (isOnTopLayer(element))
|
|
26
|
+
return document.body;
|
|
25
27
|
let parentNode = element.parentNode;
|
|
26
28
|
while (parentNode !== null) {
|
|
27
29
|
if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
|
|
@@ -31,6 +33,21 @@ function getPositionedParent(element) {
|
|
|
31
33
|
}
|
|
32
34
|
return document.body;
|
|
33
35
|
}
|
|
36
|
+
function isOnTopLayer(element) {
|
|
37
|
+
var _a;
|
|
38
|
+
if (element.tagName === 'DIALOG') {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
if (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString())) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (_b) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
34
51
|
function getClippingRect(element) {
|
|
35
52
|
let parentNode = element;
|
|
36
53
|
while (parentNode !== null) {
|
|
@@ -50,13 +67,13 @@ function getClippingRect(element) {
|
|
|
50
67
|
elemStyle.borderTopWidth,
|
|
51
68
|
elemStyle.borderLeftWidth,
|
|
52
69
|
elemStyle.borderRightWidth,
|
|
53
|
-
elemStyle.borderBottomWidth
|
|
70
|
+
elemStyle.borderBottomWidth,
|
|
54
71
|
].map(v => parseInt(v, 10) || 0);
|
|
55
72
|
return {
|
|
56
73
|
top: elemRect.top + borderTop,
|
|
57
74
|
left: elemRect.left + borderLeft,
|
|
58
75
|
width: elemRect.width - borderRight - borderLeft,
|
|
59
|
-
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity)
|
|
76
|
+
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity),
|
|
60
77
|
};
|
|
61
78
|
}
|
|
62
79
|
const positionDefaults = {
|
|
@@ -64,7 +81,7 @@ const positionDefaults = {
|
|
|
64
81
|
align: 'start',
|
|
65
82
|
anchorOffset: 4,
|
|
66
83
|
alignmentOffset: 4,
|
|
67
|
-
allowOutOfBounds: false
|
|
84
|
+
allowOutOfBounds: false,
|
|
68
85
|
};
|
|
69
86
|
function getDefaultSettings(settings = {}) {
|
|
70
87
|
var _a, _b, _c, _d, _e;
|
|
@@ -75,7 +92,7 @@ function getDefaultSettings(settings = {}) {
|
|
|
75
92
|
align,
|
|
76
93
|
anchorOffset: (_c = settings.anchorOffset) !== null && _c !== void 0 ? _c : (side === 'inside-center' ? 0 : positionDefaults.anchorOffset),
|
|
77
94
|
alignmentOffset: (_d = settings.alignmentOffset) !== null && _d !== void 0 ? _d : (align !== 'center' && side.startsWith('inside') ? positionDefaults.alignmentOffset : 0),
|
|
78
|
-
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds
|
|
95
|
+
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds,
|
|
79
96
|
};
|
|
80
97
|
}
|
|
81
98
|
function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingRect, anchorRect, { side, align, allowOutOfBounds, anchorOffset, alignmentOffset }) {
|
|
@@ -83,7 +100,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
83
100
|
top: viewportRect.top - relativePosition.top,
|
|
84
101
|
left: viewportRect.left - relativePosition.left,
|
|
85
102
|
width: viewportRect.width,
|
|
86
|
-
height: viewportRect.height
|
|
103
|
+
height: viewportRect.height,
|
|
87
104
|
};
|
|
88
105
|
let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
|
|
89
106
|
let anchorSide = side;
|
package/dist/esm/dimensions.js
CHANGED
|
@@ -2,7 +2,7 @@ export function offset(element) {
|
|
|
2
2
|
const rect = element.getBoundingClientRect();
|
|
3
3
|
return {
|
|
4
4
|
top: rect.top + window.pageYOffset,
|
|
5
|
-
left: rect.left + window.pageXOffset
|
|
5
|
+
left: rect.left + window.pageXOffset,
|
|
6
6
|
};
|
|
7
7
|
}
|
|
8
8
|
export function overflowParent(targetElement) {
|
|
@@ -57,11 +57,11 @@ export function overflowOffset(element, targetContainer) {
|
|
|
57
57
|
const scroll = container === document.documentElement && document.defaultView
|
|
58
58
|
? {
|
|
59
59
|
top: document.defaultView.pageYOffset,
|
|
60
|
-
left: document.defaultView.pageXOffset
|
|
60
|
+
left: document.defaultView.pageXOffset,
|
|
61
61
|
}
|
|
62
62
|
: {
|
|
63
63
|
top: container.scrollTop,
|
|
64
|
-
left: container.scrollLeft
|
|
64
|
+
left: container.scrollLeft,
|
|
65
65
|
};
|
|
66
66
|
const top = elementOffset.top - scroll.top;
|
|
67
67
|
const left = elementOffset.left - scroll.left;
|
package/dist/esm/focus-trap.js
CHANGED
|
@@ -91,7 +91,7 @@ export function focusTrap(container, initialFocus, abortSignal) {
|
|
|
91
91
|
container,
|
|
92
92
|
controller: wrappingController,
|
|
93
93
|
initialFocus,
|
|
94
|
-
originalSignal: signal
|
|
94
|
+
originalSignal: signal,
|
|
95
95
|
};
|
|
96
96
|
const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
|
|
97
97
|
if (suspendedTrapIndex >= 0) {
|
package/dist/esm/focus-zone.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface FocusZoneSettings {
|
|
|
25
25
|
activeDescendantControl?: HTMLElement;
|
|
26
26
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
27
27
|
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
28
|
+
preventScroll?: boolean;
|
|
28
29
|
}
|
|
29
30
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
30
31
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
package/dist/esm/focus-zone.js
CHANGED
|
@@ -38,7 +38,7 @@ const KEY_TO_BIT = {
|
|
|
38
38
|
End: FocusKeys.HomeAndEnd,
|
|
39
39
|
PageUp: FocusKeys.PageUpDown,
|
|
40
40
|
PageDown: FocusKeys.PageUpDown,
|
|
41
|
-
Backspace: FocusKeys.Backspace
|
|
41
|
+
Backspace: FocusKeys.Backspace,
|
|
42
42
|
};
|
|
43
43
|
const KEY_TO_DIRECTION = {
|
|
44
44
|
ArrowLeft: 'previous',
|
|
@@ -58,7 +58,7 @@ const KEY_TO_DIRECTION = {
|
|
|
58
58
|
End: 'end',
|
|
59
59
|
PageUp: 'start',
|
|
60
60
|
PageDown: 'end',
|
|
61
|
-
Backspace: 'previous'
|
|
61
|
+
Backspace: 'previous',
|
|
62
62
|
};
|
|
63
63
|
function getDirection(keyboardEvent) {
|
|
64
64
|
const direction = KEY_TO_DIRECTION[keyboardEvent.key];
|
|
@@ -124,7 +124,7 @@ export const activeDescendantActivatedDirectly = 'activated-directly';
|
|
|
124
124
|
export const activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
125
125
|
export const hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
126
126
|
export function focusZone(container, settings) {
|
|
127
|
-
var _a, _b, _c, _d;
|
|
127
|
+
var _a, _b, _c, _d, _e;
|
|
128
128
|
const focusableElements = [];
|
|
129
129
|
const savedTabIndex = new WeakMap();
|
|
130
130
|
const bindKeys = (_a = settings === null || settings === void 0 ? void 0 : settings.bindKeys) !== null && _a !== void 0 ? _a : ((settings === null || settings === void 0 ? void 0 : settings.getNextFocusable) ? FocusKeys.ArrowAll : FocusKeys.ArrowVertical) | FocusKeys.HomeAndEnd;
|
|
@@ -133,6 +133,7 @@ export function focusZone(container, settings) {
|
|
|
133
133
|
const activeDescendantControl = settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl;
|
|
134
134
|
const activeDescendantCallback = settings === null || settings === void 0 ? void 0 : settings.onActiveDescendantChanged;
|
|
135
135
|
let currentFocusedElement;
|
|
136
|
+
const preventScroll = (_d = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _d !== void 0 ? _d : false;
|
|
136
137
|
function getFirstFocusableElement() {
|
|
137
138
|
return focusableElements[0];
|
|
138
139
|
}
|
|
@@ -186,8 +187,7 @@ export function focusZone(container, settings) {
|
|
|
186
187
|
if (filteredElements.length === 0) {
|
|
187
188
|
return;
|
|
188
189
|
}
|
|
189
|
-
|
|
190
|
-
focusableElements.splice(insertIndex === -1 ? focusableElements.length : insertIndex, 0, ...filteredElements);
|
|
190
|
+
focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
|
|
191
191
|
for (const element of filteredElements) {
|
|
192
192
|
if (!savedTabIndex.has(element)) {
|
|
193
193
|
savedTabIndex.set(element, element.getAttribute('tabindex'));
|
|
@@ -198,6 +198,27 @@ export function focusZone(container, settings) {
|
|
|
198
198
|
updateFocusedElement(getFirstFocusableElement());
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
function findInsertionIndex(elementsToInsert) {
|
|
202
|
+
const firstElementToInsert = elementsToInsert[0];
|
|
203
|
+
if (focusableElements.length === 0)
|
|
204
|
+
return 0;
|
|
205
|
+
let iMin = 0;
|
|
206
|
+
let iMax = focusableElements.length - 1;
|
|
207
|
+
while (iMin <= iMax) {
|
|
208
|
+
const i = Math.floor((iMin + iMax) / 2);
|
|
209
|
+
const element = focusableElements[i];
|
|
210
|
+
if (followsInDocument(firstElementToInsert, element)) {
|
|
211
|
+
iMax = i - 1;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
iMin = i + 1;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return iMin;
|
|
218
|
+
}
|
|
219
|
+
function followsInDocument(first, second) {
|
|
220
|
+
return (second.compareDocumentPosition(first) & Node.DOCUMENT_POSITION_PRECEDING) > 0;
|
|
221
|
+
}
|
|
201
222
|
function endFocusManagement(...elements) {
|
|
202
223
|
for (const element of elements) {
|
|
203
224
|
const focusableElementIndex = focusableElements.indexOf(element);
|
|
@@ -221,7 +242,8 @@ export function focusZone(container, settings) {
|
|
|
221
242
|
}
|
|
222
243
|
}
|
|
223
244
|
beginFocusManagement(...iterateFocusableElements(container));
|
|
224
|
-
|
|
245
|
+
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
246
|
+
updateFocusedElement(initialElement);
|
|
225
247
|
const observer = new MutationObserver(mutations => {
|
|
226
248
|
for (const mutation of mutations) {
|
|
227
249
|
for (const removedNode of mutation.removedNodes) {
|
|
@@ -240,10 +262,10 @@ export function focusZone(container, settings) {
|
|
|
240
262
|
});
|
|
241
263
|
observer.observe(container, {
|
|
242
264
|
subtree: true,
|
|
243
|
-
childList: true
|
|
265
|
+
childList: true,
|
|
244
266
|
});
|
|
245
267
|
const controller = new AbortController();
|
|
246
|
-
const signal = (
|
|
268
|
+
const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
|
|
247
269
|
signal.addEventListener('abort', () => {
|
|
248
270
|
endFocusManagement(...focusableElements);
|
|
249
271
|
});
|
|
@@ -256,7 +278,7 @@ export function focusZone(container, settings) {
|
|
|
256
278
|
if (activeDescendantControl) {
|
|
257
279
|
container.addEventListener('focusin', event => {
|
|
258
280
|
if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
|
|
259
|
-
activeDescendantControl.focus();
|
|
281
|
+
activeDescendantControl.focus({ preventScroll });
|
|
260
282
|
updateFocusedElement(event.target);
|
|
261
283
|
}
|
|
262
284
|
});
|
|
@@ -300,7 +322,7 @@ export function focusZone(container, settings) {
|
|
|
300
322
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
301
323
|
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
|
|
302
324
|
const targetElement = focusableElements[targetElementIndex];
|
|
303
|
-
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus();
|
|
325
|
+
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
|
|
304
326
|
return;
|
|
305
327
|
}
|
|
306
328
|
else {
|
|
@@ -312,7 +334,7 @@ export function focusZone(container, settings) {
|
|
|
312
334
|
const elementToFocus = focusInStrategy(event.relatedTarget);
|
|
313
335
|
const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
|
|
314
336
|
if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
|
|
315
|
-
elementToFocus.focus();
|
|
337
|
+
elementToFocus.focus({ preventScroll });
|
|
316
338
|
return;
|
|
317
339
|
}
|
|
318
340
|
else {
|
|
@@ -397,7 +419,7 @@ export function focusZone(container, settings) {
|
|
|
397
419
|
}
|
|
398
420
|
else if (nextElementToFocus) {
|
|
399
421
|
lastKeyboardFocusDirection = direction;
|
|
400
|
-
nextElementToFocus.focus();
|
|
422
|
+
nextElementToFocus.focus({ preventScroll });
|
|
401
423
|
}
|
|
402
424
|
if (event.key !== 'Tab' || nextElementToFocus) {
|
|
403
425
|
event.preventDefault();
|
|
@@ -3,7 +3,7 @@ export function* iterateFocusableElements(container, options = {}) {
|
|
|
3
3
|
const strict = (_a = options.strict) !== null && _a !== void 0 ? _a : false;
|
|
4
4
|
const acceptFn = ((_b = options.onlyTabbable) !== null && _b !== void 0 ? _b : false) ? isTabbable : isFocusable;
|
|
5
5
|
const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
|
|
6
|
-
acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
|
6
|
+
acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP,
|
|
7
7
|
});
|
|
8
8
|
let nextNode = null;
|
|
9
9
|
if (!options.reverse && acceptFn(container, strict)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primer/behaviors",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-20230925221012",
|
|
4
4
|
"description": "Shared behaviors for JavaScript components",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -68,21 +68,22 @@
|
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@changesets/changelog-github": "^0.4.2",
|
|
70
70
|
"@changesets/cli": "^2.18.1",
|
|
71
|
-
"@github/prettier-config": "0.0.
|
|
72
|
-
"@size-limit/preset-small-lib": "^
|
|
71
|
+
"@github/prettier-config": "^0.0.6",
|
|
72
|
+
"@size-limit/preset-small-lib": "^8.2.4",
|
|
73
73
|
"@testing-library/react": "^12.1.2",
|
|
74
74
|
"@testing-library/user-event": "^13.5.0",
|
|
75
75
|
"@types/jest": "^27.0.3",
|
|
76
76
|
"@types/react": "^17.0.37",
|
|
77
|
-
"esbuild": "^0.
|
|
77
|
+
"esbuild": "^0.15.16",
|
|
78
78
|
"esbuild-jest": "^0.5.0",
|
|
79
|
-
"eslint": "^8.
|
|
80
|
-
"eslint-plugin-github": "^4.
|
|
79
|
+
"eslint": "^8.50.0",
|
|
80
|
+
"eslint-plugin-github": "^4.10.0",
|
|
81
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
81
82
|
"jest": "^27.4.3",
|
|
82
|
-
"prettier": "^
|
|
83
|
+
"prettier": "^3.0.3",
|
|
83
84
|
"react": "^17.0.2",
|
|
84
85
|
"react-dom": "^17.0.2",
|
|
85
|
-
"size-limit": "^
|
|
86
|
+
"size-limit": "^8.2.4",
|
|
86
87
|
"typescript": "^4.5.2"
|
|
87
88
|
}
|
|
88
89
|
}
|