@primer/behaviors 0.0.0-202342222036 → 0.0.0-20240222105352
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.d.ts +2 -2
- package/dist/cjs/anchored-position.js +12 -13
- package/dist/cjs/dimensions.d.ts +2 -2
- package/dist/cjs/dimensions.js +3 -3
- package/dist/cjs/focus-trap.js +1 -1
- package/dist/cjs/focus-zone.d.ts +5 -4
- package/dist/cjs/focus-zone.js +12 -7
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/polyfills/event-listener-signal.js +2 -2
- package/dist/cjs/utils/index.js +5 -1
- package/dist/cjs/utils/iterate-focusable-elements.js +8 -3
- package/dist/esm/anchored-position.d.ts +2 -2
- package/dist/esm/anchored-position.js +12 -13
- package/dist/esm/dimensions.d.ts +2 -2
- package/dist/esm/dimensions.js +3 -3
- package/dist/esm/focus-trap.js +1 -1
- package/dist/esm/focus-zone.d.ts +5 -4
- package/dist/esm/focus-zone.js +11 -6
- package/dist/esm/polyfills/event-listener-signal.js +2 -2
- package/dist/esm/utils/iterate-focusable-elements.js +8 -3
- package/package.json +26 -23
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export type AnchorAlignment = 'start' | 'center' | 'end';
|
|
2
|
+
export type AnchorSide = 'inside-top' | 'inside-bottom' | 'inside-left' | 'inside-right' | 'inside-center' | 'outside-top' | 'outside-bottom' | 'outside-left' | 'outside-right';
|
|
3
3
|
export interface PositionSettings {
|
|
4
4
|
side: AnchorSide;
|
|
5
5
|
align: AnchorAlignment;
|
|
@@ -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,7 +20,7 @@ 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
|
}
|
|
@@ -40,7 +40,7 @@ function getPositionedParent(element) {
|
|
|
40
40
|
function isOnTopLayer(element) {
|
|
41
41
|
var _a;
|
|
42
42
|
if (element.tagName === 'DIALOG') {
|
|
43
|
-
return
|
|
43
|
+
return element.matches(':modal');
|
|
44
44
|
}
|
|
45
45
|
try {
|
|
46
46
|
if (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString())) {
|
|
@@ -48,14 +48,13 @@ function isOnTopLayer(element) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
catch (_b) {
|
|
51
|
-
return false;
|
|
52
51
|
}
|
|
53
|
-
return
|
|
52
|
+
return element.matches(':fullscreen');
|
|
54
53
|
}
|
|
55
54
|
function getClippingRect(element) {
|
|
56
55
|
let parentNode = element;
|
|
57
56
|
while (parentNode !== null) {
|
|
58
|
-
if (parentNode
|
|
57
|
+
if (!(parentNode instanceof Element)) {
|
|
59
58
|
break;
|
|
60
59
|
}
|
|
61
60
|
const parentNodeStyle = getComputedStyle(parentNode);
|
|
@@ -71,13 +70,13 @@ function getClippingRect(element) {
|
|
|
71
70
|
elemStyle.borderTopWidth,
|
|
72
71
|
elemStyle.borderLeftWidth,
|
|
73
72
|
elemStyle.borderRightWidth,
|
|
74
|
-
elemStyle.borderBottomWidth
|
|
73
|
+
elemStyle.borderBottomWidth,
|
|
75
74
|
].map(v => parseInt(v, 10) || 0);
|
|
76
75
|
return {
|
|
77
76
|
top: elemRect.top + borderTop,
|
|
78
77
|
left: elemRect.left + borderLeft,
|
|
79
78
|
width: elemRect.width - borderRight - borderLeft,
|
|
80
|
-
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity)
|
|
79
|
+
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity),
|
|
81
80
|
};
|
|
82
81
|
}
|
|
83
82
|
const positionDefaults = {
|
|
@@ -85,7 +84,7 @@ const positionDefaults = {
|
|
|
85
84
|
align: 'start',
|
|
86
85
|
anchorOffset: 4,
|
|
87
86
|
alignmentOffset: 4,
|
|
88
|
-
allowOutOfBounds: false
|
|
87
|
+
allowOutOfBounds: false,
|
|
89
88
|
};
|
|
90
89
|
function getDefaultSettings(settings = {}) {
|
|
91
90
|
var _a, _b, _c, _d, _e;
|
|
@@ -96,7 +95,7 @@ function getDefaultSettings(settings = {}) {
|
|
|
96
95
|
align,
|
|
97
96
|
anchorOffset: (_c = settings.anchorOffset) !== null && _c !== void 0 ? _c : (side === 'inside-center' ? 0 : positionDefaults.anchorOffset),
|
|
98
97
|
alignmentOffset: (_d = settings.alignmentOffset) !== null && _d !== void 0 ? _d : (align !== 'center' && side.startsWith('inside') ? positionDefaults.alignmentOffset : 0),
|
|
99
|
-
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds
|
|
98
|
+
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds,
|
|
100
99
|
};
|
|
101
100
|
}
|
|
102
101
|
function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingRect, anchorRect, { side, align, allowOutOfBounds, anchorOffset, alignmentOffset }) {
|
|
@@ -104,7 +103,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
104
103
|
top: viewportRect.top - relativePosition.top,
|
|
105
104
|
left: viewportRect.left - relativePosition.left,
|
|
106
105
|
width: viewportRect.width,
|
|
107
|
-
height: viewportRect.height
|
|
106
|
+
height: viewportRect.height,
|
|
108
107
|
};
|
|
109
108
|
let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
|
|
110
109
|
let anchorSide = side;
|
|
@@ -151,7 +150,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
151
150
|
}
|
|
152
151
|
if (alternateOrder && positionAttempt < alternateOrder.length) {
|
|
153
152
|
if (pos.top + floatingRect.height > viewportRect.height + relativeViewportRect.top) {
|
|
154
|
-
pos.top = viewportRect.height + relativeViewportRect.top - floatingRect.height;
|
|
153
|
+
pos.top = Math.max(viewportRect.height + relativeViewportRect.top - floatingRect.height, 0);
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
156
|
}
|
package/dist/cjs/dimensions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type Dimensions = {
|
|
2
2
|
top: number;
|
|
3
3
|
left: number;
|
|
4
4
|
bottom: number;
|
|
@@ -6,7 +6,7 @@ declare type Dimensions = {
|
|
|
6
6
|
height?: number;
|
|
7
7
|
width?: number;
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
type Offset = {
|
|
10
10
|
top: number;
|
|
11
11
|
left: number;
|
|
12
12
|
};
|
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
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import { IterateFocusableElements } from './utils/iterate-focusable-elements.js';
|
|
2
|
+
export type Direction = 'previous' | 'next' | 'start' | 'end';
|
|
3
|
+
export type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'Backspace';
|
|
3
4
|
export declare enum FocusKeys {
|
|
4
5
|
ArrowHorizontal = 1,
|
|
5
6
|
ArrowVertical = 2,
|
|
@@ -16,7 +17,7 @@ export declare enum FocusKeys {
|
|
|
16
17
|
WASD = 96,
|
|
17
18
|
All = 511
|
|
18
19
|
}
|
|
19
|
-
export
|
|
20
|
+
export type FocusZoneSettings = IterateFocusableElements & {
|
|
20
21
|
focusOutBehavior?: 'stop' | 'wrap';
|
|
21
22
|
getNextFocusable?: (direction: Direction, from: Element | undefined, event: KeyboardEvent) => HTMLElement | undefined;
|
|
22
23
|
focusableElementFilter?: (element: HTMLElement) => boolean;
|
|
@@ -26,7 +27,7 @@ export interface FocusZoneSettings {
|
|
|
26
27
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
27
28
|
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
28
29
|
preventScroll?: boolean;
|
|
29
|
-
}
|
|
30
|
+
};
|
|
30
31
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
31
32
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
|
32
33
|
export declare const activeDescendantActivatedIndirectly = "activated-indirectly";
|
package/dist/cjs/focus-zone.js
CHANGED
|
@@ -22,7 +22,7 @@ var FocusKeys;
|
|
|
22
22
|
FocusKeys[FocusKeys["HJKL"] = 12] = "HJKL";
|
|
23
23
|
FocusKeys[FocusKeys["WASD"] = 96] = "WASD";
|
|
24
24
|
FocusKeys[FocusKeys["All"] = 511] = "All";
|
|
25
|
-
})(FocusKeys
|
|
25
|
+
})(FocusKeys || (exports.FocusKeys = FocusKeys = {}));
|
|
26
26
|
const KEY_TO_BIT = {
|
|
27
27
|
ArrowLeft: FocusKeys.ArrowHorizontal,
|
|
28
28
|
ArrowDown: FocusKeys.ArrowVertical,
|
|
@@ -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];
|
|
@@ -244,28 +244,33 @@ function focusZone(container, settings) {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
-
|
|
247
|
+
const iterateFocusableElementsOptions = {
|
|
248
|
+
reverse: settings === null || settings === void 0 ? void 0 : settings.reverse,
|
|
249
|
+
strict: settings === null || settings === void 0 ? void 0 : settings.strict,
|
|
250
|
+
onlyTabbable: settings === null || settings === void 0 ? void 0 : settings.onlyTabbable,
|
|
251
|
+
};
|
|
252
|
+
beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(container, iterateFocusableElementsOptions));
|
|
248
253
|
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
249
254
|
updateFocusedElement(initialElement);
|
|
250
255
|
const observer = new MutationObserver(mutations => {
|
|
251
256
|
for (const mutation of mutations) {
|
|
252
257
|
for (const removedNode of mutation.removedNodes) {
|
|
253
258
|
if (removedNode instanceof HTMLElement) {
|
|
254
|
-
endFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(removedNode));
|
|
259
|
+
endFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(removedNode, iterateFocusableElementsOptions));
|
|
255
260
|
}
|
|
256
261
|
}
|
|
257
262
|
}
|
|
258
263
|
for (const mutation of mutations) {
|
|
259
264
|
for (const addedNode of mutation.addedNodes) {
|
|
260
265
|
if (addedNode instanceof HTMLElement) {
|
|
261
|
-
beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(addedNode));
|
|
266
|
+
beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(addedNode, iterateFocusableElementsOptions));
|
|
262
267
|
}
|
|
263
268
|
}
|
|
264
269
|
}
|
|
265
270
|
});
|
|
266
271
|
observer.observe(container, {
|
|
267
272
|
subtree: true,
|
|
268
|
-
childList: true
|
|
273
|
+
childList: true,
|
|
269
274
|
});
|
|
270
275
|
const controller = new AbortController();
|
|
271
276
|
const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
package/dist/cjs/utils/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -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)) {
|
|
@@ -46,16 +46,21 @@ function isFocusable(elem, strict = false) {
|
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
48
|
if (strict) {
|
|
49
|
+
const style = getComputedStyle(elem);
|
|
49
50
|
const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
|
|
50
|
-
const visibilityInert = ['hidden', 'collapse'].includes(
|
|
51
|
+
const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
|
|
52
|
+
const displayInert = style.display === 'none' || !elem.offsetParent;
|
|
51
53
|
const clientRectsInert = elem.getClientRects().length === 0;
|
|
52
|
-
if (sizeInert || visibilityInert || clientRectsInert) {
|
|
54
|
+
if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
|
|
53
55
|
return false;
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
if (elem.getAttribute('tabindex') != null) {
|
|
57
59
|
return true;
|
|
58
60
|
}
|
|
61
|
+
if (elem.getAttribute('contenteditable') === 'true' || elem.getAttribute('contenteditable') === 'plaintext-only') {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
59
64
|
if (elem instanceof HTMLAnchorElement && elem.getAttribute('href') == null) {
|
|
60
65
|
return false;
|
|
61
66
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export type AnchorAlignment = 'start' | 'center' | 'end';
|
|
2
|
+
export type AnchorSide = 'inside-top' | 'inside-bottom' | 'inside-left' | 'inside-right' | 'inside-center' | 'outside-top' | 'outside-bottom' | 'outside-left' | 'outside-right';
|
|
3
3
|
export interface PositionSettings {
|
|
4
4
|
side: AnchorSide;
|
|
5
5
|
align: AnchorAlignment;
|
|
@@ -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,7 +17,7 @@ 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
|
}
|
|
@@ -36,7 +36,7 @@ function getPositionedParent(element) {
|
|
|
36
36
|
function isOnTopLayer(element) {
|
|
37
37
|
var _a;
|
|
38
38
|
if (element.tagName === 'DIALOG') {
|
|
39
|
-
return
|
|
39
|
+
return element.matches(':modal');
|
|
40
40
|
}
|
|
41
41
|
try {
|
|
42
42
|
if (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString())) {
|
|
@@ -44,14 +44,13 @@ function isOnTopLayer(element) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
catch (_b) {
|
|
47
|
-
return false;
|
|
48
47
|
}
|
|
49
|
-
return
|
|
48
|
+
return element.matches(':fullscreen');
|
|
50
49
|
}
|
|
51
50
|
function getClippingRect(element) {
|
|
52
51
|
let parentNode = element;
|
|
53
52
|
while (parentNode !== null) {
|
|
54
|
-
if (parentNode
|
|
53
|
+
if (!(parentNode instanceof Element)) {
|
|
55
54
|
break;
|
|
56
55
|
}
|
|
57
56
|
const parentNodeStyle = getComputedStyle(parentNode);
|
|
@@ -67,13 +66,13 @@ function getClippingRect(element) {
|
|
|
67
66
|
elemStyle.borderTopWidth,
|
|
68
67
|
elemStyle.borderLeftWidth,
|
|
69
68
|
elemStyle.borderRightWidth,
|
|
70
|
-
elemStyle.borderBottomWidth
|
|
69
|
+
elemStyle.borderBottomWidth,
|
|
71
70
|
].map(v => parseInt(v, 10) || 0);
|
|
72
71
|
return {
|
|
73
72
|
top: elemRect.top + borderTop,
|
|
74
73
|
left: elemRect.left + borderLeft,
|
|
75
74
|
width: elemRect.width - borderRight - borderLeft,
|
|
76
|
-
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity)
|
|
75
|
+
height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity),
|
|
77
76
|
};
|
|
78
77
|
}
|
|
79
78
|
const positionDefaults = {
|
|
@@ -81,7 +80,7 @@ const positionDefaults = {
|
|
|
81
80
|
align: 'start',
|
|
82
81
|
anchorOffset: 4,
|
|
83
82
|
alignmentOffset: 4,
|
|
84
|
-
allowOutOfBounds: false
|
|
83
|
+
allowOutOfBounds: false,
|
|
85
84
|
};
|
|
86
85
|
function getDefaultSettings(settings = {}) {
|
|
87
86
|
var _a, _b, _c, _d, _e;
|
|
@@ -92,7 +91,7 @@ function getDefaultSettings(settings = {}) {
|
|
|
92
91
|
align,
|
|
93
92
|
anchorOffset: (_c = settings.anchorOffset) !== null && _c !== void 0 ? _c : (side === 'inside-center' ? 0 : positionDefaults.anchorOffset),
|
|
94
93
|
alignmentOffset: (_d = settings.alignmentOffset) !== null && _d !== void 0 ? _d : (align !== 'center' && side.startsWith('inside') ? positionDefaults.alignmentOffset : 0),
|
|
95
|
-
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds
|
|
94
|
+
allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds,
|
|
96
95
|
};
|
|
97
96
|
}
|
|
98
97
|
function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingRect, anchorRect, { side, align, allowOutOfBounds, anchorOffset, alignmentOffset }) {
|
|
@@ -100,7 +99,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
100
99
|
top: viewportRect.top - relativePosition.top,
|
|
101
100
|
left: viewportRect.left - relativePosition.left,
|
|
102
101
|
width: viewportRect.width,
|
|
103
|
-
height: viewportRect.height
|
|
102
|
+
height: viewportRect.height,
|
|
104
103
|
};
|
|
105
104
|
let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
|
|
106
105
|
let anchorSide = side;
|
|
@@ -147,7 +146,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
147
146
|
}
|
|
148
147
|
if (alternateOrder && positionAttempt < alternateOrder.length) {
|
|
149
148
|
if (pos.top + floatingRect.height > viewportRect.height + relativeViewportRect.top) {
|
|
150
|
-
pos.top = viewportRect.height + relativeViewportRect.top - floatingRect.height;
|
|
149
|
+
pos.top = Math.max(viewportRect.height + relativeViewportRect.top - floatingRect.height, 0);
|
|
151
150
|
}
|
|
152
151
|
}
|
|
153
152
|
}
|
package/dist/esm/dimensions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type Dimensions = {
|
|
2
2
|
top: number;
|
|
3
3
|
left: number;
|
|
4
4
|
bottom: number;
|
|
@@ -6,7 +6,7 @@ declare type Dimensions = {
|
|
|
6
6
|
height?: number;
|
|
7
7
|
width?: number;
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
type Offset = {
|
|
10
10
|
top: number;
|
|
11
11
|
left: number;
|
|
12
12
|
};
|
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
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import { IterateFocusableElements } from './utils/iterate-focusable-elements.js';
|
|
2
|
+
export type Direction = 'previous' | 'next' | 'start' | 'end';
|
|
3
|
+
export type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'Backspace';
|
|
3
4
|
export declare enum FocusKeys {
|
|
4
5
|
ArrowHorizontal = 1,
|
|
5
6
|
ArrowVertical = 2,
|
|
@@ -16,7 +17,7 @@ export declare enum FocusKeys {
|
|
|
16
17
|
WASD = 96,
|
|
17
18
|
All = 511
|
|
18
19
|
}
|
|
19
|
-
export
|
|
20
|
+
export type FocusZoneSettings = IterateFocusableElements & {
|
|
20
21
|
focusOutBehavior?: 'stop' | 'wrap';
|
|
21
22
|
getNextFocusable?: (direction: Direction, from: Element | undefined, event: KeyboardEvent) => HTMLElement | undefined;
|
|
22
23
|
focusableElementFilter?: (element: HTMLElement) => boolean;
|
|
@@ -26,7 +27,7 @@ export interface FocusZoneSettings {
|
|
|
26
27
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
27
28
|
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
28
29
|
preventScroll?: boolean;
|
|
29
|
-
}
|
|
30
|
+
};
|
|
30
31
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
31
32
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
|
32
33
|
export declare const activeDescendantActivatedIndirectly = "activated-indirectly";
|
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];
|
|
@@ -241,28 +241,33 @@ export function focusZone(container, settings) {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
|
-
|
|
244
|
+
const iterateFocusableElementsOptions = {
|
|
245
|
+
reverse: settings === null || settings === void 0 ? void 0 : settings.reverse,
|
|
246
|
+
strict: settings === null || settings === void 0 ? void 0 : settings.strict,
|
|
247
|
+
onlyTabbable: settings === null || settings === void 0 ? void 0 : settings.onlyTabbable,
|
|
248
|
+
};
|
|
249
|
+
beginFocusManagement(...iterateFocusableElements(container, iterateFocusableElementsOptions));
|
|
245
250
|
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
246
251
|
updateFocusedElement(initialElement);
|
|
247
252
|
const observer = new MutationObserver(mutations => {
|
|
248
253
|
for (const mutation of mutations) {
|
|
249
254
|
for (const removedNode of mutation.removedNodes) {
|
|
250
255
|
if (removedNode instanceof HTMLElement) {
|
|
251
|
-
endFocusManagement(...iterateFocusableElements(removedNode));
|
|
256
|
+
endFocusManagement(...iterateFocusableElements(removedNode, iterateFocusableElementsOptions));
|
|
252
257
|
}
|
|
253
258
|
}
|
|
254
259
|
}
|
|
255
260
|
for (const mutation of mutations) {
|
|
256
261
|
for (const addedNode of mutation.addedNodes) {
|
|
257
262
|
if (addedNode instanceof HTMLElement) {
|
|
258
|
-
beginFocusManagement(...iterateFocusableElements(addedNode));
|
|
263
|
+
beginFocusManagement(...iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
|
|
259
264
|
}
|
|
260
265
|
}
|
|
261
266
|
}
|
|
262
267
|
});
|
|
263
268
|
observer.observe(container, {
|
|
264
269
|
subtree: true,
|
|
265
|
-
childList: true
|
|
270
|
+
childList: true,
|
|
266
271
|
});
|
|
267
272
|
const controller = new AbortController();
|
|
268
273
|
const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
|
|
@@ -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)) {
|
|
@@ -41,16 +41,21 @@ export function isFocusable(elem, strict = false) {
|
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
if (strict) {
|
|
44
|
+
const style = getComputedStyle(elem);
|
|
44
45
|
const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
|
|
45
|
-
const visibilityInert = ['hidden', 'collapse'].includes(
|
|
46
|
+
const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
|
|
47
|
+
const displayInert = style.display === 'none' || !elem.offsetParent;
|
|
46
48
|
const clientRectsInert = elem.getClientRects().length === 0;
|
|
47
|
-
if (sizeInert || visibilityInert || clientRectsInert) {
|
|
49
|
+
if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
|
|
48
50
|
return false;
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
if (elem.getAttribute('tabindex') != null) {
|
|
52
54
|
return true;
|
|
53
55
|
}
|
|
56
|
+
if (elem.getAttribute('contenteditable') === 'true' || elem.getAttribute('contenteditable') === 'plaintext-only') {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
54
59
|
if (elem instanceof HTMLAnchorElement && elem.getAttribute('href') == null) {
|
|
55
60
|
return false;
|
|
56
61
|
}
|
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-20240222105352",
|
|
4
4
|
"description": "Shared behaviors for JavaScript components",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -30,17 +30,17 @@
|
|
|
30
30
|
"dist/cjs/focus-trap.js"
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
|
-
"lint": "eslint src/",
|
|
34
|
-
"test": "npm run jest && npm run lint",
|
|
35
|
-
"test:watch": "jest --watch",
|
|
36
|
-
"jest": "jest",
|
|
37
33
|
"clean": "rm -rf dist",
|
|
38
|
-
"prebuild": "npm run clean",
|
|
39
34
|
"build": "npm run build:esm && npm run build:cjs",
|
|
40
35
|
"build:esm": "tsc",
|
|
41
36
|
"build:cjs": "tsc --module commonjs --outDir dist/cjs",
|
|
37
|
+
"lint": "eslint src/",
|
|
38
|
+
"test": "jest",
|
|
39
|
+
"test:watch": "jest --watch",
|
|
40
|
+
"prebuild": "npm run clean",
|
|
41
|
+
"release": "npm run build && changeset publish",
|
|
42
42
|
"size-limit": "npm run build && size-limit",
|
|
43
|
-
"
|
|
43
|
+
"type-check": "tsc --noEmit"
|
|
44
44
|
},
|
|
45
45
|
"repository": {
|
|
46
46
|
"type": "git",
|
|
@@ -66,23 +66,26 @@
|
|
|
66
66
|
}
|
|
67
67
|
],
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@changesets/changelog-github": "^0.
|
|
69
|
+
"@changesets/changelog-github": "^0.5.0",
|
|
70
70
|
"@changesets/cli": "^2.18.1",
|
|
71
|
-
"@github/prettier-config": "0.0.
|
|
72
|
-
"@size-limit/preset-small-lib": "^
|
|
73
|
-
"@testing-library/react": "^
|
|
74
|
-
"@testing-library/user-event": "^
|
|
75
|
-
"@types/jest": "^
|
|
76
|
-
"@types/
|
|
77
|
-
"
|
|
71
|
+
"@github/prettier-config": "^0.0.6",
|
|
72
|
+
"@size-limit/preset-small-lib": "^8.2.4",
|
|
73
|
+
"@testing-library/react": "^14.0.0",
|
|
74
|
+
"@testing-library/user-event": "^14.5.1",
|
|
75
|
+
"@types/jest": "^29.5.11",
|
|
76
|
+
"@types/node": "^20.10.5",
|
|
77
|
+
"@types/react": "^18.2.23",
|
|
78
|
+
"esbuild": "^0.20.0",
|
|
78
79
|
"esbuild-jest": "^0.5.0",
|
|
79
|
-
"eslint": "^8.
|
|
80
|
-
"eslint-plugin-github": "^4.
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
80
|
+
"eslint": "^8.50.0",
|
|
81
|
+
"eslint-plugin-github": "^4.10.0",
|
|
82
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
83
|
+
"jest": "^29.7.0",
|
|
84
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
85
|
+
"prettier": "^3.0.3",
|
|
86
|
+
"react": "^18.2.0",
|
|
87
|
+
"react-dom": "^18.2.0",
|
|
88
|
+
"size-limit": "^8.2.4",
|
|
89
|
+
"typescript": "^5.2.2"
|
|
87
90
|
}
|
|
88
91
|
}
|