@primer/behaviors 0.0.0-2022349143 → 0.0.0-2022920191756
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 +1 -0
- package/dist/cjs/anchored-position.js +30 -1
- package/dist/cjs/dimensions.d.ts +19 -0
- package/dist/cjs/dimensions.js +135 -0
- package/dist/cjs/focus-trap.d.ts +1 -1
- package/dist/cjs/focus-trap.js +13 -8
- package/dist/cjs/focus-zone.d.ts +1 -0
- package/dist/cjs/focus-zone.js +7 -6
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/utils/iterate-focusable-elements.d.ts +0 -1
- package/dist/cjs/utils/iterate-focusable-elements.js +1 -7
- package/dist/esm/anchored-position.d.ts +1 -0
- package/dist/esm/anchored-position.js +30 -1
- package/dist/esm/dimensions.d.ts +19 -0
- package/dist/esm/dimensions.js +128 -0
- package/dist/esm/focus-trap.d.ts +1 -1
- package/dist/esm/focus-trap.js +14 -9
- package/dist/esm/focus-zone.d.ts +1 -0
- package/dist/esm/focus-zone.js +7 -6
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/utils/iterate-focusable-elements.d.ts +0 -1
- package/dist/esm/utils/iterate-focusable-elements.js +0 -5
- package/package.json +10 -16
- package/utils/package.json +2 -2
- package/dist/cjs/components/modal-dialog.d.ts +0 -18
- package/dist/cjs/components/modal-dialog.js +0 -115
- package/dist/esm/components/modal-dialog.d.ts +0 -18
- package/dist/esm/components/modal-dialog.js +0 -113
|
@@ -11,5 +11,6 @@ export interface AnchorPosition {
|
|
|
11
11
|
top: number;
|
|
12
12
|
left: number;
|
|
13
13
|
anchorSide: AnchorSide;
|
|
14
|
+
anchorAlign: AnchorAlignment;
|
|
14
15
|
}
|
|
15
16
|
export declare function getAnchoredPosition(floatingElement: Element, anchorElement: Element | DOMRect, settings?: Partial<PositionSettings>): AnchorPosition;
|
|
@@ -7,6 +7,11 @@ const alternateOrders = {
|
|
|
7
7
|
'outside-left': ['outside-right', 'outside-bottom', 'outside-top', 'outside-bottom'],
|
|
8
8
|
'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom']
|
|
9
9
|
};
|
|
10
|
+
const alternateAlignments = {
|
|
11
|
+
start: ['end', 'center'],
|
|
12
|
+
end: ['start', 'center'],
|
|
13
|
+
center: ['end', 'start']
|
|
14
|
+
};
|
|
10
15
|
function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
|
|
11
16
|
const parentElement = getPositionedParent(floatingElement);
|
|
12
17
|
const clippingRect = getClippingRect(parentElement);
|
|
@@ -86,6 +91,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
86
91
|
};
|
|
87
92
|
let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
|
|
88
93
|
let anchorSide = side;
|
|
94
|
+
let anchorAlign = align;
|
|
89
95
|
pos.top -= relativePosition.top;
|
|
90
96
|
pos.left -= relativePosition.left;
|
|
91
97
|
if (!allowOutOfBounds) {
|
|
@@ -103,6 +109,20 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
103
109
|
anchorSide = nextSide;
|
|
104
110
|
}
|
|
105
111
|
}
|
|
112
|
+
const alternateAlignment = alternateAlignments[align];
|
|
113
|
+
let alignmentAttempt = 0;
|
|
114
|
+
if (alternateAlignment) {
|
|
115
|
+
let prevAlign = align;
|
|
116
|
+
while (alignmentAttempt < alternateAlignment.length &&
|
|
117
|
+
shouldRecalculateAlignment(prevAlign, pos, relativeViewportRect, floatingRect)) {
|
|
118
|
+
const nextAlign = alternateAlignment[alignmentAttempt++];
|
|
119
|
+
prevAlign = nextAlign;
|
|
120
|
+
pos = calculatePosition(floatingRect, anchorRect, anchorSide, nextAlign, anchorOffset, alignmentOffset);
|
|
121
|
+
pos.top -= relativePosition.top;
|
|
122
|
+
pos.left -= relativePosition.left;
|
|
123
|
+
anchorAlign = nextAlign;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
106
126
|
if (pos.top < relativeViewportRect.top) {
|
|
107
127
|
pos.top = relativeViewportRect.top;
|
|
108
128
|
}
|
|
@@ -118,7 +138,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
118
138
|
}
|
|
119
139
|
}
|
|
120
140
|
}
|
|
121
|
-
return Object.assign(Object.assign({}, pos), { anchorSide });
|
|
141
|
+
return Object.assign(Object.assign({}, pos), { anchorSide, anchorAlign });
|
|
122
142
|
}
|
|
123
143
|
function calculatePosition(elementDimensions, anchorPosition, side, align, anchorOffset, alignmentOffset) {
|
|
124
144
|
const anchorRight = anchorPosition.left + anchorPosition.width;
|
|
@@ -208,3 +228,12 @@ function shouldRecalculatePosition(side, currentPos, containerDimensions, elemen
|
|
|
208
228
|
currentPos.left + elementDimensions.width > containerDimensions.width + containerDimensions.left);
|
|
209
229
|
}
|
|
210
230
|
}
|
|
231
|
+
function shouldRecalculateAlignment(align, currentPos, containerDimensions, elementDimensions) {
|
|
232
|
+
if (align === 'end') {
|
|
233
|
+
return currentPos.left < containerDimensions.left;
|
|
234
|
+
}
|
|
235
|
+
else if (align === 'start' || align === 'center') {
|
|
236
|
+
return (currentPos.left + elementDimensions.width > containerDimensions.left + containerDimensions.width ||
|
|
237
|
+
currentPos.left < containerDimensions.left);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare type Dimensions = {
|
|
2
|
+
top: number;
|
|
3
|
+
left: number;
|
|
4
|
+
bottom: number;
|
|
5
|
+
right: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
width?: number;
|
|
8
|
+
};
|
|
9
|
+
declare type Offset = {
|
|
10
|
+
top: number;
|
|
11
|
+
left: number;
|
|
12
|
+
};
|
|
13
|
+
export declare function offset(element: HTMLElement): Offset;
|
|
14
|
+
export declare function overflowParent(targetElement: HTMLElement): HTMLElement | null | undefined;
|
|
15
|
+
export declare function overflowOffset(element: HTMLElement, targetContainer: Document | HTMLElement | null): Dimensions | undefined;
|
|
16
|
+
export declare function positionedOffset(targetElement: HTMLElement, container: HTMLElement | Document | Window | null): (Dimensions & {
|
|
17
|
+
_container: HTMLElement;
|
|
18
|
+
}) | undefined;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.positionedOffset = exports.overflowOffset = exports.overflowParent = exports.offset = void 0;
|
|
4
|
+
function offset(element) {
|
|
5
|
+
const rect = element.getBoundingClientRect();
|
|
6
|
+
return {
|
|
7
|
+
top: rect.top + window.pageYOffset,
|
|
8
|
+
left: rect.left + window.pageXOffset
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
exports.offset = offset;
|
|
12
|
+
function overflowParent(targetElement) {
|
|
13
|
+
let element = targetElement;
|
|
14
|
+
const document = element.ownerDocument;
|
|
15
|
+
if (!document) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!element.offsetParent) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const HTMLElement = document.defaultView.HTMLElement;
|
|
22
|
+
if (element === document.body) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
while (element !== document.body) {
|
|
26
|
+
if (element.parentElement instanceof HTMLElement) {
|
|
27
|
+
element = element.parentElement;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const { position, overflowY, overflowX } = getComputedStyle(element);
|
|
33
|
+
if (position === 'fixed' ||
|
|
34
|
+
overflowY === 'auto' ||
|
|
35
|
+
overflowX === 'auto' ||
|
|
36
|
+
overflowY === 'scroll' ||
|
|
37
|
+
overflowX === 'scroll') {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return element instanceof Document ? null : element;
|
|
42
|
+
}
|
|
43
|
+
exports.overflowParent = overflowParent;
|
|
44
|
+
function overflowOffset(element, targetContainer) {
|
|
45
|
+
let container = targetContainer;
|
|
46
|
+
const document = element.ownerDocument;
|
|
47
|
+
if (!document) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const documentElement = document.documentElement;
|
|
51
|
+
if (!documentElement) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (element === documentElement) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const elementOffset = positionedOffset(element, container);
|
|
58
|
+
if (!elementOffset) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
container = elementOffset._container;
|
|
62
|
+
const scroll = container === document.documentElement && document.defaultView
|
|
63
|
+
? {
|
|
64
|
+
top: document.defaultView.pageYOffset,
|
|
65
|
+
left: document.defaultView.pageXOffset
|
|
66
|
+
}
|
|
67
|
+
: {
|
|
68
|
+
top: container.scrollTop,
|
|
69
|
+
left: container.scrollLeft
|
|
70
|
+
};
|
|
71
|
+
const top = elementOffset.top - scroll.top;
|
|
72
|
+
const left = elementOffset.left - scroll.left;
|
|
73
|
+
const height = container.clientHeight;
|
|
74
|
+
const width = container.clientWidth;
|
|
75
|
+
const bottom = height - (top + element.offsetHeight);
|
|
76
|
+
const right = width - (left + element.offsetWidth);
|
|
77
|
+
return { top, left, bottom, right, height, width };
|
|
78
|
+
}
|
|
79
|
+
exports.overflowOffset = overflowOffset;
|
|
80
|
+
function positionedOffset(targetElement, container) {
|
|
81
|
+
let element = targetElement;
|
|
82
|
+
const document = element.ownerDocument;
|
|
83
|
+
if (!document) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const documentElement = document.documentElement;
|
|
87
|
+
if (!documentElement) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const HTMLElement = document.defaultView.HTMLElement;
|
|
91
|
+
let top = 0;
|
|
92
|
+
let left = 0;
|
|
93
|
+
const height = element.offsetHeight;
|
|
94
|
+
const width = element.offsetWidth;
|
|
95
|
+
while (!(element === document.body || element === container)) {
|
|
96
|
+
top += element.offsetTop || 0;
|
|
97
|
+
left += element.offsetLeft || 0;
|
|
98
|
+
if (element.offsetParent instanceof HTMLElement) {
|
|
99
|
+
element = element.offsetParent;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let scrollHeight;
|
|
106
|
+
let scrollWidth;
|
|
107
|
+
let measuredContainer;
|
|
108
|
+
if (!container ||
|
|
109
|
+
container === document ||
|
|
110
|
+
container === document.defaultView ||
|
|
111
|
+
container === document.documentElement ||
|
|
112
|
+
container === document.body) {
|
|
113
|
+
measuredContainer = documentElement;
|
|
114
|
+
scrollHeight = getDocumentHeight(document.body, documentElement);
|
|
115
|
+
scrollWidth = getDocumentWidth(document.body, documentElement);
|
|
116
|
+
}
|
|
117
|
+
else if (container instanceof HTMLElement) {
|
|
118
|
+
measuredContainer = container;
|
|
119
|
+
scrollHeight = container.scrollHeight;
|
|
120
|
+
scrollWidth = container.scrollWidth;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const bottom = scrollHeight - (top + height);
|
|
126
|
+
const right = scrollWidth - (left + width);
|
|
127
|
+
return { top, left, bottom, right, _container: measuredContainer };
|
|
128
|
+
}
|
|
129
|
+
exports.positionedOffset = positionedOffset;
|
|
130
|
+
function getDocumentHeight(documentBody, documentElement) {
|
|
131
|
+
return Math.max(documentBody.scrollHeight, documentElement.scrollHeight, documentBody.offsetHeight, documentElement.offsetHeight, documentElement.clientHeight);
|
|
132
|
+
}
|
|
133
|
+
function getDocumentWidth(documentBody, documentElement) {
|
|
134
|
+
return Math.max(documentBody.scrollWidth, documentElement.scrollWidth, documentBody.offsetWidth, documentElement.offsetWidth, documentElement.clientWidth);
|
|
135
|
+
}
|
package/dist/cjs/focus-trap.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function focusTrap(container: HTMLElement,
|
|
1
|
+
export declare function focusTrap(container: HTMLElement, initialFocus?: HTMLElement, abortSignal?: AbortSignal): AbortController | undefined;
|
package/dist/cjs/focus-trap.js
CHANGED
|
@@ -9,7 +9,7 @@ let activeTrap = undefined;
|
|
|
9
9
|
function tryReactivate() {
|
|
10
10
|
const trapToReactivate = suspendedTrapStack.pop();
|
|
11
11
|
if (trapToReactivate) {
|
|
12
|
-
focusTrap(trapToReactivate.container, trapToReactivate.
|
|
12
|
+
focusTrap(trapToReactivate.container, trapToReactivate.initialFocus, trapToReactivate.originalSignal);
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
function followSignal(signal) {
|
|
@@ -19,7 +19,9 @@ function followSignal(signal) {
|
|
|
19
19
|
});
|
|
20
20
|
return controller;
|
|
21
21
|
}
|
|
22
|
-
function focusTrap(container,
|
|
22
|
+
function focusTrap(container, initialFocus, abortSignal) {
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
|
|
23
25
|
container.setAttribute('data-focus-trap', 'active');
|
|
24
26
|
const sentinelStart = document.createElement('span');
|
|
25
27
|
sentinelStart.setAttribute('class', 'sentinel');
|
|
@@ -48,22 +50,22 @@ function focusTrap(container, abortSignal, initialFocus) {
|
|
|
48
50
|
}
|
|
49
51
|
else {
|
|
50
52
|
if (lastFocusedChild && (0, iterate_focusable_elements_js_1.isTabbable)(lastFocusedChild) && container.contains(lastFocusedChild)) {
|
|
51
|
-
|
|
53
|
+
lastFocusedChild.focus();
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
54
56
|
else if (initialFocus && container.contains(initialFocus)) {
|
|
55
|
-
|
|
57
|
+
initialFocus.focus();
|
|
56
58
|
return;
|
|
57
59
|
}
|
|
58
60
|
else {
|
|
59
61
|
const firstFocusableChild = (0, iterate_focusable_elements_js_1.getFocusableChild)(container);
|
|
60
|
-
|
|
62
|
+
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
61
63
|
return;
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
|
-
const wrappingController = followSignal(
|
|
68
|
+
const wrappingController = followSignal(signal);
|
|
67
69
|
if (activeTrap) {
|
|
68
70
|
const suspendedTrap = activeTrap;
|
|
69
71
|
activeTrap.container.setAttribute('data-focus-trap', 'suspended');
|
|
@@ -73,7 +75,7 @@ function focusTrap(container, abortSignal, initialFocus) {
|
|
|
73
75
|
wrappingController.signal.addEventListener('abort', () => {
|
|
74
76
|
activeTrap = undefined;
|
|
75
77
|
});
|
|
76
|
-
|
|
78
|
+
signal.addEventListener('abort', () => {
|
|
77
79
|
container.removeAttribute('data-focus-trap');
|
|
78
80
|
const sentinels = container.getElementsByClassName('sentinel');
|
|
79
81
|
while (sentinels.length > 0)
|
|
@@ -92,11 +94,14 @@ function focusTrap(container, abortSignal, initialFocus) {
|
|
|
92
94
|
container,
|
|
93
95
|
controller: wrappingController,
|
|
94
96
|
initialFocus,
|
|
95
|
-
originalSignal:
|
|
97
|
+
originalSignal: signal
|
|
96
98
|
};
|
|
97
99
|
const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
|
|
98
100
|
if (suspendedTrapIndex >= 0) {
|
|
99
101
|
suspendedTrapStack.splice(suspendedTrapIndex, 1);
|
|
100
102
|
}
|
|
103
|
+
if (!abortSignal) {
|
|
104
|
+
return controller;
|
|
105
|
+
}
|
|
101
106
|
}
|
|
102
107
|
exports.focusTrap = focusTrap;
|
package/dist/cjs/focus-zone.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface FocusZoneSettings {
|
|
|
24
24
|
activeDescendantControl?: HTMLElement;
|
|
25
25
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
26
26
|
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
27
|
+
preventScroll?: boolean;
|
|
27
28
|
}
|
|
28
29
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
29
30
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
package/dist/cjs/focus-zone.js
CHANGED
|
@@ -124,7 +124,7 @@ exports.activeDescendantActivatedDirectly = 'activated-directly';
|
|
|
124
124
|
exports.activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
125
125
|
exports.hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
126
126
|
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 @@ 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
|
}
|
|
@@ -243,7 +244,7 @@ function focusZone(container, settings) {
|
|
|
243
244
|
childList: true
|
|
244
245
|
});
|
|
245
246
|
const controller = new AbortController();
|
|
246
|
-
const signal = (
|
|
247
|
+
const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
|
|
247
248
|
signal.addEventListener('abort', () => {
|
|
248
249
|
endFocusManagement(...focusableElements);
|
|
249
250
|
});
|
|
@@ -256,7 +257,7 @@ function focusZone(container, settings) {
|
|
|
256
257
|
if (activeDescendantControl) {
|
|
257
258
|
container.addEventListener('focusin', event => {
|
|
258
259
|
if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
|
|
259
|
-
activeDescendantControl.focus();
|
|
260
|
+
activeDescendantControl.focus({ preventScroll });
|
|
260
261
|
updateFocusedElement(event.target);
|
|
261
262
|
}
|
|
262
263
|
});
|
|
@@ -300,7 +301,7 @@ function focusZone(container, settings) {
|
|
|
300
301
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
301
302
|
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
|
|
302
303
|
const targetElement = focusableElements[targetElementIndex];
|
|
303
|
-
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus();
|
|
304
|
+
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
|
|
304
305
|
return;
|
|
305
306
|
}
|
|
306
307
|
else {
|
|
@@ -312,7 +313,7 @@ function focusZone(container, settings) {
|
|
|
312
313
|
const elementToFocus = focusInStrategy(event.relatedTarget);
|
|
313
314
|
const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
|
|
314
315
|
if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
|
|
315
|
-
elementToFocus.focus();
|
|
316
|
+
elementToFocus.focus({ preventScroll });
|
|
316
317
|
return;
|
|
317
318
|
}
|
|
318
319
|
else {
|
|
@@ -397,7 +398,7 @@ function focusZone(container, settings) {
|
|
|
397
398
|
}
|
|
398
399
|
else if (nextElementToFocus) {
|
|
399
400
|
lastKeyboardFocusDirection = direction;
|
|
400
|
-
nextElementToFocus.focus();
|
|
401
|
+
nextElementToFocus.focus({ preventScroll });
|
|
401
402
|
}
|
|
402
403
|
if (event.key !== 'Tab' || nextElementToFocus) {
|
|
403
404
|
event.preventDefault();
|
package/dist/cjs/index.d.ts
CHANGED
package/dist/cjs/index.js
CHANGED
|
@@ -14,3 +14,4 @@ __exportStar(require("./anchored-position.js"), exports);
|
|
|
14
14
|
__exportStar(require("./focus-trap.js"), exports);
|
|
15
15
|
__exportStar(require("./focus-zone.js"), exports);
|
|
16
16
|
__exportStar(require("./scroll-into-view.js"), exports);
|
|
17
|
+
__exportStar(require("./dimensions.js"), exports);
|
|
@@ -4,7 +4,6 @@ export interface IterateFocusableElements {
|
|
|
4
4
|
onlyTabbable?: boolean;
|
|
5
5
|
}
|
|
6
6
|
export declare function iterateFocusableElements(container: HTMLElement, options?: IterateFocusableElements): Generator<HTMLElement, undefined, undefined>;
|
|
7
|
-
export declare function focusIfNeeded(elem?: HTMLElement): void;
|
|
8
7
|
export declare function getFocusableChild(container: HTMLElement, lastChild?: boolean): HTMLElement | undefined;
|
|
9
8
|
export declare function isFocusable(elem: HTMLElement, strict?: boolean): boolean;
|
|
10
9
|
export declare function isTabbable(elem: HTMLElement, strict?: boolean): boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isTabbable = exports.isFocusable = exports.getFocusableChild = exports.
|
|
3
|
+
exports.isTabbable = exports.isFocusable = exports.getFocusableChild = exports.iterateFocusableElements = void 0;
|
|
4
4
|
function* iterateFocusableElements(container, options = {}) {
|
|
5
5
|
var _a, _b;
|
|
6
6
|
const strict = (_a = options.strict) !== null && _a !== void 0 ? _a : false;
|
|
@@ -32,12 +32,6 @@ function* iterateFocusableElements(container, options = {}) {
|
|
|
32
32
|
return undefined;
|
|
33
33
|
}
|
|
34
34
|
exports.iterateFocusableElements = iterateFocusableElements;
|
|
35
|
-
function focusIfNeeded(elem) {
|
|
36
|
-
if (document.activeElement !== elem) {
|
|
37
|
-
elem === null || elem === void 0 ? void 0 : elem.focus();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
exports.focusIfNeeded = focusIfNeeded;
|
|
41
35
|
function getFocusableChild(container, lastChild = false) {
|
|
42
36
|
return iterateFocusableElements(container, { reverse: lastChild, strict: true, onlyTabbable: true }).next().value;
|
|
43
37
|
}
|
|
@@ -11,5 +11,6 @@ export interface AnchorPosition {
|
|
|
11
11
|
top: number;
|
|
12
12
|
left: number;
|
|
13
13
|
anchorSide: AnchorSide;
|
|
14
|
+
anchorAlign: AnchorAlignment;
|
|
14
15
|
}
|
|
15
16
|
export declare function getAnchoredPosition(floatingElement: Element, anchorElement: Element | DOMRect, settings?: Partial<PositionSettings>): AnchorPosition;
|
|
@@ -4,6 +4,11 @@ const alternateOrders = {
|
|
|
4
4
|
'outside-left': ['outside-right', 'outside-bottom', 'outside-top', 'outside-bottom'],
|
|
5
5
|
'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom']
|
|
6
6
|
};
|
|
7
|
+
const alternateAlignments = {
|
|
8
|
+
start: ['end', 'center'],
|
|
9
|
+
end: ['start', 'center'],
|
|
10
|
+
center: ['end', 'start']
|
|
11
|
+
};
|
|
7
12
|
export function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
|
|
8
13
|
const parentElement = getPositionedParent(floatingElement);
|
|
9
14
|
const clippingRect = getClippingRect(parentElement);
|
|
@@ -82,6 +87,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
82
87
|
};
|
|
83
88
|
let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
|
|
84
89
|
let anchorSide = side;
|
|
90
|
+
let anchorAlign = align;
|
|
85
91
|
pos.top -= relativePosition.top;
|
|
86
92
|
pos.left -= relativePosition.left;
|
|
87
93
|
if (!allowOutOfBounds) {
|
|
@@ -99,6 +105,20 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
99
105
|
anchorSide = nextSide;
|
|
100
106
|
}
|
|
101
107
|
}
|
|
108
|
+
const alternateAlignment = alternateAlignments[align];
|
|
109
|
+
let alignmentAttempt = 0;
|
|
110
|
+
if (alternateAlignment) {
|
|
111
|
+
let prevAlign = align;
|
|
112
|
+
while (alignmentAttempt < alternateAlignment.length &&
|
|
113
|
+
shouldRecalculateAlignment(prevAlign, pos, relativeViewportRect, floatingRect)) {
|
|
114
|
+
const nextAlign = alternateAlignment[alignmentAttempt++];
|
|
115
|
+
prevAlign = nextAlign;
|
|
116
|
+
pos = calculatePosition(floatingRect, anchorRect, anchorSide, nextAlign, anchorOffset, alignmentOffset);
|
|
117
|
+
pos.top -= relativePosition.top;
|
|
118
|
+
pos.left -= relativePosition.left;
|
|
119
|
+
anchorAlign = nextAlign;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
102
122
|
if (pos.top < relativeViewportRect.top) {
|
|
103
123
|
pos.top = relativeViewportRect.top;
|
|
104
124
|
}
|
|
@@ -114,7 +134,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
|
|
|
114
134
|
}
|
|
115
135
|
}
|
|
116
136
|
}
|
|
117
|
-
return Object.assign(Object.assign({}, pos), { anchorSide });
|
|
137
|
+
return Object.assign(Object.assign({}, pos), { anchorSide, anchorAlign });
|
|
118
138
|
}
|
|
119
139
|
function calculatePosition(elementDimensions, anchorPosition, side, align, anchorOffset, alignmentOffset) {
|
|
120
140
|
const anchorRight = anchorPosition.left + anchorPosition.width;
|
|
@@ -204,3 +224,12 @@ function shouldRecalculatePosition(side, currentPos, containerDimensions, elemen
|
|
|
204
224
|
currentPos.left + elementDimensions.width > containerDimensions.width + containerDimensions.left);
|
|
205
225
|
}
|
|
206
226
|
}
|
|
227
|
+
function shouldRecalculateAlignment(align, currentPos, containerDimensions, elementDimensions) {
|
|
228
|
+
if (align === 'end') {
|
|
229
|
+
return currentPos.left < containerDimensions.left;
|
|
230
|
+
}
|
|
231
|
+
else if (align === 'start' || align === 'center') {
|
|
232
|
+
return (currentPos.left + elementDimensions.width > containerDimensions.left + containerDimensions.width ||
|
|
233
|
+
currentPos.left < containerDimensions.left);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare type Dimensions = {
|
|
2
|
+
top: number;
|
|
3
|
+
left: number;
|
|
4
|
+
bottom: number;
|
|
5
|
+
right: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
width?: number;
|
|
8
|
+
};
|
|
9
|
+
declare type Offset = {
|
|
10
|
+
top: number;
|
|
11
|
+
left: number;
|
|
12
|
+
};
|
|
13
|
+
export declare function offset(element: HTMLElement): Offset;
|
|
14
|
+
export declare function overflowParent(targetElement: HTMLElement): HTMLElement | null | undefined;
|
|
15
|
+
export declare function overflowOffset(element: HTMLElement, targetContainer: Document | HTMLElement | null): Dimensions | undefined;
|
|
16
|
+
export declare function positionedOffset(targetElement: HTMLElement, container: HTMLElement | Document | Window | null): (Dimensions & {
|
|
17
|
+
_container: HTMLElement;
|
|
18
|
+
}) | undefined;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export function offset(element) {
|
|
2
|
+
const rect = element.getBoundingClientRect();
|
|
3
|
+
return {
|
|
4
|
+
top: rect.top + window.pageYOffset,
|
|
5
|
+
left: rect.left + window.pageXOffset
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function overflowParent(targetElement) {
|
|
9
|
+
let element = targetElement;
|
|
10
|
+
const document = element.ownerDocument;
|
|
11
|
+
if (!document) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (!element.offsetParent) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const HTMLElement = document.defaultView.HTMLElement;
|
|
18
|
+
if (element === document.body) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
while (element !== document.body) {
|
|
22
|
+
if (element.parentElement instanceof HTMLElement) {
|
|
23
|
+
element = element.parentElement;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const { position, overflowY, overflowX } = getComputedStyle(element);
|
|
29
|
+
if (position === 'fixed' ||
|
|
30
|
+
overflowY === 'auto' ||
|
|
31
|
+
overflowX === 'auto' ||
|
|
32
|
+
overflowY === 'scroll' ||
|
|
33
|
+
overflowX === 'scroll') {
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return element instanceof Document ? null : element;
|
|
38
|
+
}
|
|
39
|
+
export function overflowOffset(element, targetContainer) {
|
|
40
|
+
let container = targetContainer;
|
|
41
|
+
const document = element.ownerDocument;
|
|
42
|
+
if (!document) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const documentElement = document.documentElement;
|
|
46
|
+
if (!documentElement) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (element === documentElement) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const elementOffset = positionedOffset(element, container);
|
|
53
|
+
if (!elementOffset) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
container = elementOffset._container;
|
|
57
|
+
const scroll = container === document.documentElement && document.defaultView
|
|
58
|
+
? {
|
|
59
|
+
top: document.defaultView.pageYOffset,
|
|
60
|
+
left: document.defaultView.pageXOffset
|
|
61
|
+
}
|
|
62
|
+
: {
|
|
63
|
+
top: container.scrollTop,
|
|
64
|
+
left: container.scrollLeft
|
|
65
|
+
};
|
|
66
|
+
const top = elementOffset.top - scroll.top;
|
|
67
|
+
const left = elementOffset.left - scroll.left;
|
|
68
|
+
const height = container.clientHeight;
|
|
69
|
+
const width = container.clientWidth;
|
|
70
|
+
const bottom = height - (top + element.offsetHeight);
|
|
71
|
+
const right = width - (left + element.offsetWidth);
|
|
72
|
+
return { top, left, bottom, right, height, width };
|
|
73
|
+
}
|
|
74
|
+
export function positionedOffset(targetElement, container) {
|
|
75
|
+
let element = targetElement;
|
|
76
|
+
const document = element.ownerDocument;
|
|
77
|
+
if (!document) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const documentElement = document.documentElement;
|
|
81
|
+
if (!documentElement) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const HTMLElement = document.defaultView.HTMLElement;
|
|
85
|
+
let top = 0;
|
|
86
|
+
let left = 0;
|
|
87
|
+
const height = element.offsetHeight;
|
|
88
|
+
const width = element.offsetWidth;
|
|
89
|
+
while (!(element === document.body || element === container)) {
|
|
90
|
+
top += element.offsetTop || 0;
|
|
91
|
+
left += element.offsetLeft || 0;
|
|
92
|
+
if (element.offsetParent instanceof HTMLElement) {
|
|
93
|
+
element = element.offsetParent;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let scrollHeight;
|
|
100
|
+
let scrollWidth;
|
|
101
|
+
let measuredContainer;
|
|
102
|
+
if (!container ||
|
|
103
|
+
container === document ||
|
|
104
|
+
container === document.defaultView ||
|
|
105
|
+
container === document.documentElement ||
|
|
106
|
+
container === document.body) {
|
|
107
|
+
measuredContainer = documentElement;
|
|
108
|
+
scrollHeight = getDocumentHeight(document.body, documentElement);
|
|
109
|
+
scrollWidth = getDocumentWidth(document.body, documentElement);
|
|
110
|
+
}
|
|
111
|
+
else if (container instanceof HTMLElement) {
|
|
112
|
+
measuredContainer = container;
|
|
113
|
+
scrollHeight = container.scrollHeight;
|
|
114
|
+
scrollWidth = container.scrollWidth;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const bottom = scrollHeight - (top + height);
|
|
120
|
+
const right = scrollWidth - (left + width);
|
|
121
|
+
return { top, left, bottom, right, _container: measuredContainer };
|
|
122
|
+
}
|
|
123
|
+
function getDocumentHeight(documentBody, documentElement) {
|
|
124
|
+
return Math.max(documentBody.scrollHeight, documentElement.scrollHeight, documentBody.offsetHeight, documentElement.offsetHeight, documentElement.clientHeight);
|
|
125
|
+
}
|
|
126
|
+
function getDocumentWidth(documentBody, documentElement) {
|
|
127
|
+
return Math.max(documentBody.scrollWidth, documentElement.scrollWidth, documentBody.offsetWidth, documentElement.offsetWidth, documentElement.clientWidth);
|
|
128
|
+
}
|
package/dist/esm/focus-trap.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function focusTrap(container: HTMLElement,
|
|
1
|
+
export declare function focusTrap(container: HTMLElement, initialFocus?: HTMLElement, abortSignal?: AbortSignal): AbortController | undefined;
|
package/dist/esm/focus-trap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getFocusableChild, isTabbable } from './utils/iterate-focusable-elements.js';
|
|
2
2
|
import { polyfill as eventListenerSignalPolyfill } from './polyfills/event-listener-signal.js';
|
|
3
3
|
eventListenerSignalPolyfill();
|
|
4
4
|
const suspendedTrapStack = [];
|
|
@@ -6,7 +6,7 @@ let activeTrap = undefined;
|
|
|
6
6
|
function tryReactivate() {
|
|
7
7
|
const trapToReactivate = suspendedTrapStack.pop();
|
|
8
8
|
if (trapToReactivate) {
|
|
9
|
-
focusTrap(trapToReactivate.container, trapToReactivate.
|
|
9
|
+
focusTrap(trapToReactivate.container, trapToReactivate.initialFocus, trapToReactivate.originalSignal);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
function followSignal(signal) {
|
|
@@ -16,7 +16,9 @@ function followSignal(signal) {
|
|
|
16
16
|
});
|
|
17
17
|
return controller;
|
|
18
18
|
}
|
|
19
|
-
export function focusTrap(container,
|
|
19
|
+
export function focusTrap(container, initialFocus, abortSignal) {
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
|
|
20
22
|
container.setAttribute('data-focus-trap', 'active');
|
|
21
23
|
const sentinelStart = document.createElement('span');
|
|
22
24
|
sentinelStart.setAttribute('class', 'sentinel');
|
|
@@ -45,22 +47,22 @@ export function focusTrap(container, abortSignal, initialFocus) {
|
|
|
45
47
|
}
|
|
46
48
|
else {
|
|
47
49
|
if (lastFocusedChild && isTabbable(lastFocusedChild) && container.contains(lastFocusedChild)) {
|
|
48
|
-
|
|
50
|
+
lastFocusedChild.focus();
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
53
|
else if (initialFocus && container.contains(initialFocus)) {
|
|
52
|
-
|
|
54
|
+
initialFocus.focus();
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
else {
|
|
56
58
|
const firstFocusableChild = getFocusableChild(container);
|
|
57
|
-
|
|
59
|
+
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
|
-
const wrappingController = followSignal(
|
|
65
|
+
const wrappingController = followSignal(signal);
|
|
64
66
|
if (activeTrap) {
|
|
65
67
|
const suspendedTrap = activeTrap;
|
|
66
68
|
activeTrap.container.setAttribute('data-focus-trap', 'suspended');
|
|
@@ -70,7 +72,7 @@ export function focusTrap(container, abortSignal, initialFocus) {
|
|
|
70
72
|
wrappingController.signal.addEventListener('abort', () => {
|
|
71
73
|
activeTrap = undefined;
|
|
72
74
|
});
|
|
73
|
-
|
|
75
|
+
signal.addEventListener('abort', () => {
|
|
74
76
|
container.removeAttribute('data-focus-trap');
|
|
75
77
|
const sentinels = container.getElementsByClassName('sentinel');
|
|
76
78
|
while (sentinels.length > 0)
|
|
@@ -89,10 +91,13 @@ export function focusTrap(container, abortSignal, initialFocus) {
|
|
|
89
91
|
container,
|
|
90
92
|
controller: wrappingController,
|
|
91
93
|
initialFocus,
|
|
92
|
-
originalSignal:
|
|
94
|
+
originalSignal: signal
|
|
93
95
|
};
|
|
94
96
|
const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
|
|
95
97
|
if (suspendedTrapIndex >= 0) {
|
|
96
98
|
suspendedTrapStack.splice(suspendedTrapIndex, 1);
|
|
97
99
|
}
|
|
100
|
+
if (!abortSignal) {
|
|
101
|
+
return controller;
|
|
102
|
+
}
|
|
98
103
|
}
|
package/dist/esm/focus-zone.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface FocusZoneSettings {
|
|
|
24
24
|
activeDescendantControl?: HTMLElement;
|
|
25
25
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
26
26
|
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
27
|
+
preventScroll?: boolean;
|
|
27
28
|
}
|
|
28
29
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
29
30
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
package/dist/esm/focus-zone.js
CHANGED
|
@@ -121,7 +121,7 @@ export const activeDescendantActivatedDirectly = 'activated-directly';
|
|
|
121
121
|
export const activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
122
122
|
export const hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
123
123
|
export function focusZone(container, settings) {
|
|
124
|
-
var _a, _b, _c, _d;
|
|
124
|
+
var _a, _b, _c, _d, _e;
|
|
125
125
|
const focusableElements = [];
|
|
126
126
|
const savedTabIndex = new WeakMap();
|
|
127
127
|
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;
|
|
@@ -130,6 +130,7 @@ export function focusZone(container, settings) {
|
|
|
130
130
|
const activeDescendantControl = settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl;
|
|
131
131
|
const activeDescendantCallback = settings === null || settings === void 0 ? void 0 : settings.onActiveDescendantChanged;
|
|
132
132
|
let currentFocusedElement;
|
|
133
|
+
const preventScroll = (_d = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _d !== void 0 ? _d : false;
|
|
133
134
|
function getFirstFocusableElement() {
|
|
134
135
|
return focusableElements[0];
|
|
135
136
|
}
|
|
@@ -240,7 +241,7 @@ export function focusZone(container, settings) {
|
|
|
240
241
|
childList: true
|
|
241
242
|
});
|
|
242
243
|
const controller = new AbortController();
|
|
243
|
-
const signal = (
|
|
244
|
+
const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
|
|
244
245
|
signal.addEventListener('abort', () => {
|
|
245
246
|
endFocusManagement(...focusableElements);
|
|
246
247
|
});
|
|
@@ -253,7 +254,7 @@ export function focusZone(container, settings) {
|
|
|
253
254
|
if (activeDescendantControl) {
|
|
254
255
|
container.addEventListener('focusin', event => {
|
|
255
256
|
if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
|
|
256
|
-
activeDescendantControl.focus();
|
|
257
|
+
activeDescendantControl.focus({ preventScroll });
|
|
257
258
|
updateFocusedElement(event.target);
|
|
258
259
|
}
|
|
259
260
|
});
|
|
@@ -297,7 +298,7 @@ export function focusZone(container, settings) {
|
|
|
297
298
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
298
299
|
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
|
|
299
300
|
const targetElement = focusableElements[targetElementIndex];
|
|
300
|
-
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus();
|
|
301
|
+
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
|
|
301
302
|
return;
|
|
302
303
|
}
|
|
303
304
|
else {
|
|
@@ -309,7 +310,7 @@ export function focusZone(container, settings) {
|
|
|
309
310
|
const elementToFocus = focusInStrategy(event.relatedTarget);
|
|
310
311
|
const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
|
|
311
312
|
if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
|
|
312
|
-
elementToFocus.focus();
|
|
313
|
+
elementToFocus.focus({ preventScroll });
|
|
313
314
|
return;
|
|
314
315
|
}
|
|
315
316
|
else {
|
|
@@ -394,7 +395,7 @@ export function focusZone(container, settings) {
|
|
|
394
395
|
}
|
|
395
396
|
else if (nextElementToFocus) {
|
|
396
397
|
lastKeyboardFocusDirection = direction;
|
|
397
|
-
nextElementToFocus.focus();
|
|
398
|
+
nextElementToFocus.focus({ preventScroll });
|
|
398
399
|
}
|
|
399
400
|
if (event.key !== 'Tab' || nextElementToFocus) {
|
|
400
401
|
event.preventDefault();
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
|
@@ -4,7 +4,6 @@ export interface IterateFocusableElements {
|
|
|
4
4
|
onlyTabbable?: boolean;
|
|
5
5
|
}
|
|
6
6
|
export declare function iterateFocusableElements(container: HTMLElement, options?: IterateFocusableElements): Generator<HTMLElement, undefined, undefined>;
|
|
7
|
-
export declare function focusIfNeeded(elem?: HTMLElement): void;
|
|
8
7
|
export declare function getFocusableChild(container: HTMLElement, lastChild?: boolean): HTMLElement | undefined;
|
|
9
8
|
export declare function isFocusable(elem: HTMLElement, strict?: boolean): boolean;
|
|
10
9
|
export declare function isTabbable(elem: HTMLElement, strict?: boolean): boolean;
|
|
@@ -28,11 +28,6 @@ export function* iterateFocusableElements(container, options = {}) {
|
|
|
28
28
|
}
|
|
29
29
|
return undefined;
|
|
30
30
|
}
|
|
31
|
-
export function focusIfNeeded(elem) {
|
|
32
|
-
if (document.activeElement !== elem) {
|
|
33
|
-
elem === null || elem === void 0 ? void 0 : elem.focus();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
31
|
export function getFocusableChild(container, lastChild = false) {
|
|
37
32
|
return iterateFocusableElements(container, { reverse: lastChild, strict: true, onlyTabbable: true }).next().value;
|
|
38
33
|
}
|
package/package.json
CHANGED
|
@@ -1,39 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primer/behaviors",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-2022920191756",
|
|
4
4
|
"description": "Shared behaviors for JavaScript components",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"
|
|
9
|
+
"module": "./dist/esm/index.js",
|
|
10
|
+
"import": "./dist/esm/index.js",
|
|
10
11
|
"require": "./dist/cjs/index.js",
|
|
11
|
-
"
|
|
12
|
+
"types": "./dist/esm/index.d.ts"
|
|
12
13
|
},
|
|
13
14
|
"./utils": {
|
|
14
|
-
"
|
|
15
|
+
"module": "./dist/esm/utils/index.js",
|
|
16
|
+
"import": "./dist/esm/utils/index.js",
|
|
15
17
|
"require": "./dist/cjs/utils/index.js",
|
|
16
|
-
"
|
|
17
|
-
},
|
|
18
|
-
"./components/modal-dialog": {
|
|
19
|
-
"types": "./dist/components/modal-dialog.d.ts",
|
|
20
|
-
"require": "./dist/cjs/components/modal-dialog.js",
|
|
21
|
-
"module": "./dist/esm/components/modal-dialog.js"
|
|
18
|
+
"types": "./dist/esm/utils/index.d.ts"
|
|
22
19
|
}
|
|
23
20
|
},
|
|
24
|
-
"types": "dist/
|
|
21
|
+
"types": "dist/esm/index.d.ts",
|
|
25
22
|
"files": [
|
|
26
23
|
"dist",
|
|
27
|
-
"utils"
|
|
28
|
-
"modal-dialog"
|
|
24
|
+
"utils"
|
|
29
25
|
],
|
|
30
26
|
"sideEffects": [
|
|
31
27
|
"dist/esm/focus-zone.js",
|
|
32
28
|
"dist/esm/focus-trap.js",
|
|
33
29
|
"dist/cjs/focus-zone.js",
|
|
34
|
-
"dist/cjs/focus-trap.js"
|
|
35
|
-
"dist/esm/components/modal-dialog.js",
|
|
36
|
-
"dist/cjs/components/modal-dialog.js"
|
|
30
|
+
"dist/cjs/focus-trap.js"
|
|
37
31
|
],
|
|
38
32
|
"scripts": {
|
|
39
33
|
"lint": "eslint src/",
|
package/utils/package.json
CHANGED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
declare class ModalDialogElement extends HTMLElement {
|
|
2
|
-
#private;
|
|
3
|
-
get open(): boolean;
|
|
4
|
-
set open(value: boolean);
|
|
5
|
-
connectedCallback(): void;
|
|
6
|
-
disconnectedCallback(): void;
|
|
7
|
-
show(): void;
|
|
8
|
-
close(): void;
|
|
9
|
-
}
|
|
10
|
-
declare global {
|
|
11
|
-
interface Window {
|
|
12
|
-
ModalDialogElement: typeof ModalDialogElement;
|
|
13
|
-
}
|
|
14
|
-
interface HTMLElementTagNameMap {
|
|
15
|
-
'modal-dialog': ModalDialogElement;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export default ModalDialogElement;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
|
-
};
|
|
13
|
-
var _ModalDialogElement_instances, _ModalDialogElement_focusAbortController, _ModalDialogElement_abortController, _ModalDialogElement_openButton, _ModalDialogElement_overlayBackdrop_get, _ModalDialogElement_keydown;
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const iterate_focusable_elements_js_1 = require("../utils/iterate-focusable-elements.js");
|
|
16
|
-
const focus_trap_js_1 = require("../focus-trap.js");
|
|
17
|
-
class ModalDialogElement extends HTMLElement {
|
|
18
|
-
constructor() {
|
|
19
|
-
super(...arguments);
|
|
20
|
-
_ModalDialogElement_instances.add(this);
|
|
21
|
-
_ModalDialogElement_focusAbortController.set(this, new AbortController());
|
|
22
|
-
_ModalDialogElement_abortController.set(this, null);
|
|
23
|
-
_ModalDialogElement_openButton.set(this, void 0);
|
|
24
|
-
}
|
|
25
|
-
get open() {
|
|
26
|
-
return this.hasAttribute('open');
|
|
27
|
-
}
|
|
28
|
-
set open(value) {
|
|
29
|
-
var _a, _b;
|
|
30
|
-
if (value) {
|
|
31
|
-
if (this.open)
|
|
32
|
-
return;
|
|
33
|
-
this.setAttribute('open', '');
|
|
34
|
-
(_a = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _a === void 0 ? void 0 : _a.classList.remove('Overlay-hidden');
|
|
35
|
-
document.body.style.overflow = 'hidden';
|
|
36
|
-
if (__classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").signal.aborted) {
|
|
37
|
-
__classPrivateFieldSet(this, _ModalDialogElement_focusAbortController, new AbortController(), "f");
|
|
38
|
-
}
|
|
39
|
-
(0, focus_trap_js_1.focusTrap)(this, __classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").signal);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
if (!this.open)
|
|
43
|
-
return;
|
|
44
|
-
this.removeAttribute('open');
|
|
45
|
-
(_b = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _b === void 0 ? void 0 : _b.classList.add('Overlay-hidden');
|
|
46
|
-
document.body.style.overflow = 'initial';
|
|
47
|
-
__classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").abort();
|
|
48
|
-
(0, iterate_focusable_elements_js_1.focusIfNeeded)(__classPrivateFieldGet(this, _ModalDialogElement_openButton, "f"));
|
|
49
|
-
__classPrivateFieldSet(this, _ModalDialogElement_openButton, undefined, "f");
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
connectedCallback() {
|
|
53
|
-
if (!this.hasAttribute('role'))
|
|
54
|
-
this.setAttribute('role', 'dialog');
|
|
55
|
-
const { signal } = (__classPrivateFieldSet(this, _ModalDialogElement_abortController, new AbortController(), "f"));
|
|
56
|
-
this.ownerDocument.addEventListener('click', event => {
|
|
57
|
-
const target = event.target;
|
|
58
|
-
const clickOutsideDialog = target.closest(this.tagName) !== this;
|
|
59
|
-
const button = target === null || target === void 0 ? void 0 : target.closest('button');
|
|
60
|
-
if (!button) {
|
|
61
|
-
if (clickOutsideDialog) {
|
|
62
|
-
this.close();
|
|
63
|
-
}
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
let dialogId = button.getAttribute('data-close-dialog-id');
|
|
67
|
-
if (dialogId === this.id) {
|
|
68
|
-
this.close();
|
|
69
|
-
}
|
|
70
|
-
dialogId = button.getAttribute('data-show-dialog-id');
|
|
71
|
-
if (dialogId === this.id) {
|
|
72
|
-
event.stopPropagation();
|
|
73
|
-
__classPrivateFieldSet(this, _ModalDialogElement_openButton, button, "f");
|
|
74
|
-
this.show();
|
|
75
|
-
}
|
|
76
|
-
}, { signal });
|
|
77
|
-
this.addEventListener('keydown', e => __classPrivateFieldGet(this, _ModalDialogElement_instances, "m", _ModalDialogElement_keydown).call(this, e));
|
|
78
|
-
}
|
|
79
|
-
disconnectedCallback() {
|
|
80
|
-
var _a;
|
|
81
|
-
(_a = __classPrivateFieldGet(this, _ModalDialogElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
|
82
|
-
}
|
|
83
|
-
show() {
|
|
84
|
-
this.open = true;
|
|
85
|
-
}
|
|
86
|
-
close() {
|
|
87
|
-
this.open = false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
_ModalDialogElement_focusAbortController = new WeakMap(), _ModalDialogElement_abortController = new WeakMap(), _ModalDialogElement_openButton = new WeakMap(), _ModalDialogElement_instances = new WeakSet(), _ModalDialogElement_overlayBackdrop_get = function _ModalDialogElement_overlayBackdrop_get() {
|
|
91
|
-
var _a;
|
|
92
|
-
if ((_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.classList.contains('Overlay-backdrop')) {
|
|
93
|
-
return this.parentElement;
|
|
94
|
-
}
|
|
95
|
-
return null;
|
|
96
|
-
}, _ModalDialogElement_keydown = function _ModalDialogElement_keydown(event) {
|
|
97
|
-
if (!(event instanceof KeyboardEvent))
|
|
98
|
-
return;
|
|
99
|
-
if (event.isComposing)
|
|
100
|
-
return;
|
|
101
|
-
switch (event.key) {
|
|
102
|
-
case 'Escape':
|
|
103
|
-
if (this.open) {
|
|
104
|
-
this.close();
|
|
105
|
-
event.preventDefault();
|
|
106
|
-
event.stopPropagation();
|
|
107
|
-
}
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
exports.default = ModalDialogElement;
|
|
112
|
-
if (!window.customElements.get('modal-dialog')) {
|
|
113
|
-
window.ModalDialogElement = ModalDialogElement;
|
|
114
|
-
window.customElements.define('modal-dialog', ModalDialogElement);
|
|
115
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
declare class ModalDialogElement extends HTMLElement {
|
|
2
|
-
#private;
|
|
3
|
-
get open(): boolean;
|
|
4
|
-
set open(value: boolean);
|
|
5
|
-
connectedCallback(): void;
|
|
6
|
-
disconnectedCallback(): void;
|
|
7
|
-
show(): void;
|
|
8
|
-
close(): void;
|
|
9
|
-
}
|
|
10
|
-
declare global {
|
|
11
|
-
interface Window {
|
|
12
|
-
ModalDialogElement: typeof ModalDialogElement;
|
|
13
|
-
}
|
|
14
|
-
interface HTMLElementTagNameMap {
|
|
15
|
-
'modal-dialog': ModalDialogElement;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export default ModalDialogElement;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
-
};
|
|
12
|
-
var _ModalDialogElement_instances, _ModalDialogElement_focusAbortController, _ModalDialogElement_abortController, _ModalDialogElement_openButton, _ModalDialogElement_overlayBackdrop_get, _ModalDialogElement_keydown;
|
|
13
|
-
import { focusIfNeeded } from '../utils/iterate-focusable-elements.js';
|
|
14
|
-
import { focusTrap } from '../focus-trap.js';
|
|
15
|
-
class ModalDialogElement extends HTMLElement {
|
|
16
|
-
constructor() {
|
|
17
|
-
super(...arguments);
|
|
18
|
-
_ModalDialogElement_instances.add(this);
|
|
19
|
-
_ModalDialogElement_focusAbortController.set(this, new AbortController());
|
|
20
|
-
_ModalDialogElement_abortController.set(this, null);
|
|
21
|
-
_ModalDialogElement_openButton.set(this, void 0);
|
|
22
|
-
}
|
|
23
|
-
get open() {
|
|
24
|
-
return this.hasAttribute('open');
|
|
25
|
-
}
|
|
26
|
-
set open(value) {
|
|
27
|
-
var _a, _b;
|
|
28
|
-
if (value) {
|
|
29
|
-
if (this.open)
|
|
30
|
-
return;
|
|
31
|
-
this.setAttribute('open', '');
|
|
32
|
-
(_a = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _a === void 0 ? void 0 : _a.classList.remove('Overlay-hidden');
|
|
33
|
-
document.body.style.overflow = 'hidden';
|
|
34
|
-
if (__classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").signal.aborted) {
|
|
35
|
-
__classPrivateFieldSet(this, _ModalDialogElement_focusAbortController, new AbortController(), "f");
|
|
36
|
-
}
|
|
37
|
-
focusTrap(this, __classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").signal);
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
if (!this.open)
|
|
41
|
-
return;
|
|
42
|
-
this.removeAttribute('open');
|
|
43
|
-
(_b = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _b === void 0 ? void 0 : _b.classList.add('Overlay-hidden');
|
|
44
|
-
document.body.style.overflow = 'initial';
|
|
45
|
-
__classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").abort();
|
|
46
|
-
focusIfNeeded(__classPrivateFieldGet(this, _ModalDialogElement_openButton, "f"));
|
|
47
|
-
__classPrivateFieldSet(this, _ModalDialogElement_openButton, undefined, "f");
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
connectedCallback() {
|
|
51
|
-
if (!this.hasAttribute('role'))
|
|
52
|
-
this.setAttribute('role', 'dialog');
|
|
53
|
-
const { signal } = (__classPrivateFieldSet(this, _ModalDialogElement_abortController, new AbortController(), "f"));
|
|
54
|
-
this.ownerDocument.addEventListener('click', event => {
|
|
55
|
-
const target = event.target;
|
|
56
|
-
const clickOutsideDialog = target.closest(this.tagName) !== this;
|
|
57
|
-
const button = target === null || target === void 0 ? void 0 : target.closest('button');
|
|
58
|
-
if (!button) {
|
|
59
|
-
if (clickOutsideDialog) {
|
|
60
|
-
this.close();
|
|
61
|
-
}
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
let dialogId = button.getAttribute('data-close-dialog-id');
|
|
65
|
-
if (dialogId === this.id) {
|
|
66
|
-
this.close();
|
|
67
|
-
}
|
|
68
|
-
dialogId = button.getAttribute('data-show-dialog-id');
|
|
69
|
-
if (dialogId === this.id) {
|
|
70
|
-
event.stopPropagation();
|
|
71
|
-
__classPrivateFieldSet(this, _ModalDialogElement_openButton, button, "f");
|
|
72
|
-
this.show();
|
|
73
|
-
}
|
|
74
|
-
}, { signal });
|
|
75
|
-
this.addEventListener('keydown', e => __classPrivateFieldGet(this, _ModalDialogElement_instances, "m", _ModalDialogElement_keydown).call(this, e));
|
|
76
|
-
}
|
|
77
|
-
disconnectedCallback() {
|
|
78
|
-
var _a;
|
|
79
|
-
(_a = __classPrivateFieldGet(this, _ModalDialogElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
|
80
|
-
}
|
|
81
|
-
show() {
|
|
82
|
-
this.open = true;
|
|
83
|
-
}
|
|
84
|
-
close() {
|
|
85
|
-
this.open = false;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
_ModalDialogElement_focusAbortController = new WeakMap(), _ModalDialogElement_abortController = new WeakMap(), _ModalDialogElement_openButton = new WeakMap(), _ModalDialogElement_instances = new WeakSet(), _ModalDialogElement_overlayBackdrop_get = function _ModalDialogElement_overlayBackdrop_get() {
|
|
89
|
-
var _a;
|
|
90
|
-
if ((_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.classList.contains('Overlay-backdrop')) {
|
|
91
|
-
return this.parentElement;
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
94
|
-
}, _ModalDialogElement_keydown = function _ModalDialogElement_keydown(event) {
|
|
95
|
-
if (!(event instanceof KeyboardEvent))
|
|
96
|
-
return;
|
|
97
|
-
if (event.isComposing)
|
|
98
|
-
return;
|
|
99
|
-
switch (event.key) {
|
|
100
|
-
case 'Escape':
|
|
101
|
-
if (this.open) {
|
|
102
|
-
this.close();
|
|
103
|
-
event.preventDefault();
|
|
104
|
-
event.stopPropagation();
|
|
105
|
-
}
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
export default ModalDialogElement;
|
|
110
|
-
if (!window.customElements.get('modal-dialog')) {
|
|
111
|
-
window.ModalDialogElement = ModalDialogElement;
|
|
112
|
-
window.customElements.define('modal-dialog', ModalDialogElement);
|
|
113
|
-
}
|