@primer/behaviors 0.0.0-2022927173910 → 0.0.0-20231722480
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/focus-zone.d.ts +1 -0
- package/dist/cjs/focus-zone.js +31 -9
- package/dist/esm/focus-zone.d.ts +1 -0
- package/dist/esm/focus-zone.js +31 -9
- package/package.json +2 -2
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
|
@@ -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'));
|
|
@@ -198,9 +198,31 @@ function focusZone(container, settings) {
|
|
|
198
198
|
element.setAttribute('tabindex', '-1');
|
|
199
199
|
}
|
|
200
200
|
if (!currentFocusedElement) {
|
|
201
|
-
|
|
201
|
+
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
202
|
+
updateFocusedElement(initialElement);
|
|
202
203
|
}
|
|
203
204
|
}
|
|
205
|
+
function findInsertionIndex(elementsToInsert) {
|
|
206
|
+
const firstElementToInsert = elementsToInsert[0];
|
|
207
|
+
if (focusableElements.length === 0)
|
|
208
|
+
return 0;
|
|
209
|
+
let iMin = 0;
|
|
210
|
+
let iMax = focusableElements.length - 1;
|
|
211
|
+
while (iMin <= iMax) {
|
|
212
|
+
const i = Math.floor((iMin + iMax) / 2);
|
|
213
|
+
const element = focusableElements[i];
|
|
214
|
+
if (followsInDocument(firstElementToInsert, element)) {
|
|
215
|
+
iMax = i - 1;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
iMin = i + 1;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return iMin;
|
|
222
|
+
}
|
|
223
|
+
function followsInDocument(first, second) {
|
|
224
|
+
return (second.compareDocumentPosition(first) & Node.DOCUMENT_POSITION_PRECEDING) > 0;
|
|
225
|
+
}
|
|
204
226
|
function endFocusManagement(...elements) {
|
|
205
227
|
for (const element of elements) {
|
|
206
228
|
const focusableElementIndex = focusableElements.indexOf(element);
|
|
@@ -246,7 +268,7 @@ function focusZone(container, settings) {
|
|
|
246
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();
|
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
|
@@ -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'));
|
|
@@ -195,9 +195,31 @@ export function focusZone(container, settings) {
|
|
|
195
195
|
element.setAttribute('tabindex', '-1');
|
|
196
196
|
}
|
|
197
197
|
if (!currentFocusedElement) {
|
|
198
|
-
|
|
198
|
+
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
199
|
+
updateFocusedElement(initialElement);
|
|
199
200
|
}
|
|
200
201
|
}
|
|
202
|
+
function findInsertionIndex(elementsToInsert) {
|
|
203
|
+
const firstElementToInsert = elementsToInsert[0];
|
|
204
|
+
if (focusableElements.length === 0)
|
|
205
|
+
return 0;
|
|
206
|
+
let iMin = 0;
|
|
207
|
+
let iMax = focusableElements.length - 1;
|
|
208
|
+
while (iMin <= iMax) {
|
|
209
|
+
const i = Math.floor((iMin + iMax) / 2);
|
|
210
|
+
const element = focusableElements[i];
|
|
211
|
+
if (followsInDocument(firstElementToInsert, element)) {
|
|
212
|
+
iMax = i - 1;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
iMin = i + 1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return iMin;
|
|
219
|
+
}
|
|
220
|
+
function followsInDocument(first, second) {
|
|
221
|
+
return (second.compareDocumentPosition(first) & Node.DOCUMENT_POSITION_PRECEDING) > 0;
|
|
222
|
+
}
|
|
201
223
|
function endFocusManagement(...elements) {
|
|
202
224
|
for (const element of elements) {
|
|
203
225
|
const focusableElementIndex = focusableElements.indexOf(element);
|
|
@@ -243,7 +265,7 @@ export function focusZone(container, settings) {
|
|
|
243
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();
|
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-20231722480",
|
|
4
4
|
"description": "Shared behaviors for JavaScript components",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -74,7 +74,7 @@
|
|
|
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
79
|
"eslint": "^8.3.0",
|
|
80
80
|
"eslint-plugin-github": "^4.3.5",
|