@primer/behaviors 1.8.4 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/dist/cjs/focus-zone.d.ts +2 -1
- package/dist/cjs/focus-zone.js +22 -16
- package/dist/esm/focus-zone.d.ts +2 -1
- package/dist/esm/focus-zone.mjs +22 -16
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@ Shared behaviors for JavaScript components
|
|
|
7
7
|
|
|
8
8
|
## Documentation
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
- [Anchored Position](/docs/anchored-position.md)
|
|
11
|
+
- [Focus Trap](/docs/focus-trap.md)
|
|
12
|
+
- [Focus Zone](/docs/focus-zone.md)
|
|
13
|
+
- [Scroll Into View](/docs/scroll-into-view.md)
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -18,8 +18,14 @@ Shared behaviors for JavaScript components
|
|
|
18
18
|
npm install @primer/behaviors
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
or
|
|
21
|
+
or
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
yarn add @primer/behaviors
|
|
25
25
|
```
|
|
26
|
+
|
|
27
|
+
## Storybook
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run storybook
|
|
31
|
+
```
|
package/dist/cjs/focus-zone.d.ts
CHANGED
|
@@ -25,8 +25,9 @@ export type FocusZoneSettings = IterateFocusableElements & {
|
|
|
25
25
|
abortSignal?: AbortSignal;
|
|
26
26
|
activeDescendantControl?: HTMLElement;
|
|
27
27
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
28
|
-
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
28
|
+
focusInStrategy?: 'first' | 'closest' | 'previous' | 'initial' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
29
29
|
preventScroll?: boolean;
|
|
30
|
+
ignoreHoverEvents?: boolean;
|
|
30
31
|
};
|
|
31
32
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
32
33
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
package/dist/cjs/focus-zone.js
CHANGED
|
@@ -129,7 +129,7 @@ const activeDescendantActivatedDirectly = 'activated-directly';
|
|
|
129
129
|
const activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
130
130
|
const hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
131
131
|
function focusZone(container, settings) {
|
|
132
|
-
var _a, _b, _c, _d, _e;
|
|
132
|
+
var _a, _b, _c, _d, _e, _f;
|
|
133
133
|
const focusableElements = [];
|
|
134
134
|
const savedTabIndex = new WeakMap();
|
|
135
135
|
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) ? exports.FocusKeys.ArrowAll : exports.FocusKeys.ArrowVertical) | exports.FocusKeys.HomeAndEnd;
|
|
@@ -137,8 +137,10 @@ function focusZone(container, settings) {
|
|
|
137
137
|
const focusInStrategy = (_c = settings === null || settings === void 0 ? void 0 : settings.focusInStrategy) !== null && _c !== void 0 ? _c : 'previous';
|
|
138
138
|
const activeDescendantControl = settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl;
|
|
139
139
|
const activeDescendantCallback = settings === null || settings === void 0 ? void 0 : settings.onActiveDescendantChanged;
|
|
140
|
+
const ignoreHoverEvents = (_d = settings === null || settings === void 0 ? void 0 : settings.ignoreHoverEvents) !== null && _d !== void 0 ? _d : false;
|
|
140
141
|
let currentFocusedElement;
|
|
141
|
-
const preventScroll = (
|
|
142
|
+
const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
|
|
143
|
+
const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
|
|
142
144
|
function getFirstFocusableElement() {
|
|
143
145
|
return focusableElements[0];
|
|
144
146
|
}
|
|
@@ -202,7 +204,7 @@ function focusZone(container, settings) {
|
|
|
202
204
|
}
|
|
203
205
|
element.setAttribute('tabindex', '-1');
|
|
204
206
|
}
|
|
205
|
-
if (!currentFocusedElement) {
|
|
207
|
+
if (!currentFocusedElement && !preventInitialFocus) {
|
|
206
208
|
updateFocusedElement(getFirstFocusableElement());
|
|
207
209
|
}
|
|
208
210
|
}
|
|
@@ -256,7 +258,8 @@ function focusZone(container, settings) {
|
|
|
256
258
|
};
|
|
257
259
|
beginFocusManagement(...iterateFocusableElements.iterateFocusableElements(container, iterateFocusableElementsOptions));
|
|
258
260
|
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
259
|
-
|
|
261
|
+
if (!preventInitialFocus)
|
|
262
|
+
updateFocusedElement(initialElement);
|
|
260
263
|
const observer = new MutationObserver(mutations => {
|
|
261
264
|
for (const mutation of mutations) {
|
|
262
265
|
for (const removedNode of mutation.removedNodes) {
|
|
@@ -290,7 +293,7 @@ function focusZone(container, settings) {
|
|
|
290
293
|
attributeOldValue: true,
|
|
291
294
|
});
|
|
292
295
|
const controller = new AbortController();
|
|
293
|
-
const signal = (
|
|
296
|
+
const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
|
|
294
297
|
signal.addEventListener('abort', () => {
|
|
295
298
|
endFocusManagement(...focusableElements);
|
|
296
299
|
});
|
|
@@ -307,18 +310,21 @@ function focusZone(container, settings) {
|
|
|
307
310
|
updateFocusedElement(event.target);
|
|
308
311
|
}
|
|
309
312
|
}, { signal });
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
313
|
+
if (!ignoreHoverEvents) {
|
|
314
|
+
container.addEventListener('mousemove', ({ target }) => {
|
|
315
|
+
if (!(target instanceof Node)) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const focusableElement = focusableElements.find(element => element.contains(target));
|
|
319
|
+
if (focusableElement) {
|
|
320
|
+
updateFocusedElement(focusableElement);
|
|
321
|
+
}
|
|
322
|
+
}, { signal, capture: true });
|
|
323
|
+
}
|
|
319
324
|
activeDescendantControl.addEventListener('focusin', () => {
|
|
320
325
|
if (!currentFocusedElement) {
|
|
321
|
-
|
|
326
|
+
if (!preventInitialFocus)
|
|
327
|
+
updateFocusedElement(getFirstFocusableElement());
|
|
322
328
|
}
|
|
323
329
|
else {
|
|
324
330
|
setActiveDescendant(undefined, currentFocusedElement);
|
|
@@ -386,7 +392,7 @@ function focusZone(container, settings) {
|
|
|
386
392
|
}
|
|
387
393
|
function getCurrentFocusedIndex() {
|
|
388
394
|
if (!currentFocusedElement) {
|
|
389
|
-
return 0;
|
|
395
|
+
return preventInitialFocus ? -1 : 0;
|
|
390
396
|
}
|
|
391
397
|
const focusedIndex = focusableElements.indexOf(currentFocusedElement);
|
|
392
398
|
const fallbackIndex = currentFocusedElement === container ? -1 : 0;
|
package/dist/esm/focus-zone.d.ts
CHANGED
|
@@ -25,8 +25,9 @@ export type FocusZoneSettings = IterateFocusableElements & {
|
|
|
25
25
|
abortSignal?: AbortSignal;
|
|
26
26
|
activeDescendantControl?: HTMLElement;
|
|
27
27
|
onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
|
|
28
|
-
focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
28
|
+
focusInStrategy?: 'first' | 'closest' | 'previous' | 'initial' | ((previousFocusedElement: Element) => HTMLElement | undefined);
|
|
29
29
|
preventScroll?: boolean;
|
|
30
|
+
ignoreHoverEvents?: boolean;
|
|
30
31
|
};
|
|
31
32
|
export declare const isActiveDescendantAttribute = "data-is-active-descendant";
|
|
32
33
|
export declare const activeDescendantActivatedDirectly = "activated-directly";
|
package/dist/esm/focus-zone.mjs
CHANGED
|
@@ -127,7 +127,7 @@ const activeDescendantActivatedDirectly = 'activated-directly';
|
|
|
127
127
|
const activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
128
128
|
const hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
129
129
|
function focusZone(container, settings) {
|
|
130
|
-
var _a, _b, _c, _d, _e;
|
|
130
|
+
var _a, _b, _c, _d, _e, _f;
|
|
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;
|
|
@@ -135,8 +135,10 @@ function focusZone(container, settings) {
|
|
|
135
135
|
const focusInStrategy = (_c = settings === null || settings === void 0 ? void 0 : settings.focusInStrategy) !== null && _c !== void 0 ? _c : 'previous';
|
|
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
|
+
const ignoreHoverEvents = (_d = settings === null || settings === void 0 ? void 0 : settings.ignoreHoverEvents) !== null && _d !== void 0 ? _d : false;
|
|
138
139
|
let currentFocusedElement;
|
|
139
|
-
const preventScroll = (
|
|
140
|
+
const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
|
|
141
|
+
const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
|
|
140
142
|
function getFirstFocusableElement() {
|
|
141
143
|
return focusableElements[0];
|
|
142
144
|
}
|
|
@@ -200,7 +202,7 @@ function focusZone(container, settings) {
|
|
|
200
202
|
}
|
|
201
203
|
element.setAttribute('tabindex', '-1');
|
|
202
204
|
}
|
|
203
|
-
if (!currentFocusedElement) {
|
|
205
|
+
if (!currentFocusedElement && !preventInitialFocus) {
|
|
204
206
|
updateFocusedElement(getFirstFocusableElement());
|
|
205
207
|
}
|
|
206
208
|
}
|
|
@@ -254,7 +256,8 @@ function focusZone(container, settings) {
|
|
|
254
256
|
};
|
|
255
257
|
beginFocusManagement(...iterateFocusableElements(container, iterateFocusableElementsOptions));
|
|
256
258
|
const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
|
|
257
|
-
|
|
259
|
+
if (!preventInitialFocus)
|
|
260
|
+
updateFocusedElement(initialElement);
|
|
258
261
|
const observer = new MutationObserver(mutations => {
|
|
259
262
|
for (const mutation of mutations) {
|
|
260
263
|
for (const removedNode of mutation.removedNodes) {
|
|
@@ -288,7 +291,7 @@ function focusZone(container, settings) {
|
|
|
288
291
|
attributeOldValue: true,
|
|
289
292
|
});
|
|
290
293
|
const controller = new AbortController();
|
|
291
|
-
const signal = (
|
|
294
|
+
const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
|
|
292
295
|
signal.addEventListener('abort', () => {
|
|
293
296
|
endFocusManagement(...focusableElements);
|
|
294
297
|
});
|
|
@@ -305,18 +308,21 @@ function focusZone(container, settings) {
|
|
|
305
308
|
updateFocusedElement(event.target);
|
|
306
309
|
}
|
|
307
310
|
}, { signal });
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
311
|
+
if (!ignoreHoverEvents) {
|
|
312
|
+
container.addEventListener('mousemove', ({ target }) => {
|
|
313
|
+
if (!(target instanceof Node)) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const focusableElement = focusableElements.find(element => element.contains(target));
|
|
317
|
+
if (focusableElement) {
|
|
318
|
+
updateFocusedElement(focusableElement);
|
|
319
|
+
}
|
|
320
|
+
}, { signal, capture: true });
|
|
321
|
+
}
|
|
317
322
|
activeDescendantControl.addEventListener('focusin', () => {
|
|
318
323
|
if (!currentFocusedElement) {
|
|
319
|
-
|
|
324
|
+
if (!preventInitialFocus)
|
|
325
|
+
updateFocusedElement(getFirstFocusableElement());
|
|
320
326
|
}
|
|
321
327
|
else {
|
|
322
328
|
setActiveDescendant(undefined, currentFocusedElement);
|
|
@@ -384,7 +390,7 @@ function focusZone(container, settings) {
|
|
|
384
390
|
}
|
|
385
391
|
function getCurrentFocusedIndex() {
|
|
386
392
|
if (!currentFocusedElement) {
|
|
387
|
-
return 0;
|
|
393
|
+
return preventInitialFocus ? -1 : 0;
|
|
388
394
|
}
|
|
389
395
|
const focusedIndex = focusableElements.indexOf(currentFocusedElement);
|
|
390
396
|
const fallbackIndex = currentFocusedElement === container ? -1 : 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primer/behaviors",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Shared behaviors for JavaScript components",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/cjs/index.js",
|
|
@@ -43,7 +43,9 @@
|
|
|
43
43
|
"prebuild": "npm run clean",
|
|
44
44
|
"release": "npm run build && changeset publish",
|
|
45
45
|
"size-limit": "npm run build && size-limit",
|
|
46
|
-
"type-check": "tsc --noEmit"
|
|
46
|
+
"type-check": "tsc --noEmit",
|
|
47
|
+
"storybook": "storybook dev -p 6006 --no-open",
|
|
48
|
+
"build-storybook": "storybook build"
|
|
47
49
|
},
|
|
48
50
|
"repository": {
|
|
49
51
|
"type": "git",
|
|
@@ -75,17 +77,21 @@
|
|
|
75
77
|
"@github/prettier-config": "^0.0.6",
|
|
76
78
|
"@rollup/plugin-typescript": "^12.1.0",
|
|
77
79
|
"@rollup/wasm-node": "^4.19.1",
|
|
78
|
-
"@size-limit/preset-small-lib": "^
|
|
80
|
+
"@size-limit/preset-small-lib": "^12.0.0",
|
|
81
|
+
"@storybook/react-vite": "^10.0.8",
|
|
79
82
|
"@testing-library/react": "^16.0.0",
|
|
80
83
|
"@testing-library/user-event": "^14.5.1",
|
|
81
84
|
"@types/jest": "^30.0.0",
|
|
82
85
|
"@types/node": "^24.0.10",
|
|
83
86
|
"@types/react": "^19.0.1",
|
|
84
|
-
"
|
|
87
|
+
"@types/react-dom": "^19.2.3",
|
|
88
|
+
"clsx": "^2.1.1",
|
|
89
|
+
"esbuild": "^0.27.0",
|
|
85
90
|
"esbuild-jest": "^0.5.0",
|
|
86
91
|
"eslint": "^8.50.0",
|
|
87
92
|
"eslint-plugin-github": "^5.0.0",
|
|
88
93
|
"eslint-plugin-prettier": "^5.0.0",
|
|
94
|
+
"eslint-plugin-storybook": "^10.0.8",
|
|
89
95
|
"jest": "^30.0.4",
|
|
90
96
|
"jest-environment-jsdom": "^30.0.4",
|
|
91
97
|
"prettier": "^3.0.3",
|
|
@@ -93,7 +99,8 @@
|
|
|
93
99
|
"react-dom": "^19.0.0",
|
|
94
100
|
"rimraf": "^6.0.1",
|
|
95
101
|
"rollup": "^4.18.0",
|
|
96
|
-
"size-limit": "^
|
|
102
|
+
"size-limit": "^12.0.0",
|
|
103
|
+
"storybook": "^10.0.8",
|
|
97
104
|
"tslib": "^2.8.1",
|
|
98
105
|
"typescript": "^5.2.2"
|
|
99
106
|
}
|