@primer/behaviors 1.8.4-rc.c90131c → 1.9.0-rc.8649bfb

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 CHANGED
@@ -7,10 +7,10 @@ Shared behaviors for JavaScript components
7
7
 
8
8
  ## Documentation
9
9
 
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)
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
+ ```
@@ -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";
@@ -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 = (_d = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _d !== void 0 ? _d : false;
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
- updateFocusedElement(initialElement);
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 = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.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
- container.addEventListener('mousemove', ({ target }) => {
311
- if (!(target instanceof Node)) {
312
- return;
313
- }
314
- const focusableElement = focusableElements.find(element => element.contains(target));
315
- if (focusableElement) {
316
- updateFocusedElement(focusableElement);
317
- }
318
- }, { signal, capture: true });
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
- updateFocusedElement(getFirstFocusableElement());
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;
@@ -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";
@@ -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 = (_d = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _d !== void 0 ? _d : false;
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
- updateFocusedElement(initialElement);
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 = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.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
- container.addEventListener('mousemove', ({ target }) => {
309
- if (!(target instanceof Node)) {
310
- return;
311
- }
312
- const focusableElement = focusableElements.find(element => element.contains(target));
313
- if (focusableElement) {
314
- updateFocusedElement(focusableElement);
315
- }
316
- }, { signal, capture: true });
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
- updateFocusedElement(getFirstFocusableElement());
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.8.4-rc.c90131c",
3
+ "version": "1.9.0-rc.8649bfb",
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": "^11.1.4",
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
- "esbuild": "^0.25.0",
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": "^11.1.4",
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
  }