power-focusable 3.1.0 → 4.0.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 CHANGED
@@ -35,14 +35,14 @@ import { ... } 'https://unpkg.com/power-focusable/dist/index.js';
35
35
 
36
36
  ```ts
37
37
  interface PowerFocusableOptions {
38
- active?: Element | null; // default: document.activeElement
38
+ anchor?: Element | null; // default: document.activeElement
39
39
  composed?: boolean; // default: false
40
40
  filter?: (element: Element) => boolean; // default: () => true
41
41
  wrap?: boolean; // default: false
42
42
  }
43
43
  ```
44
44
 
45
- ### `active`
45
+ ### `anchor`
46
46
 
47
47
  Specifies the starting element.
48
48
 
@@ -109,7 +109,7 @@ getNextFocusable(container);
109
109
  // container (optional): Element (default: <body>)
110
110
 
111
111
  // Specifies the starting element
112
- getNextFocusable(container, { active: document.querySelector('.button') });
112
+ getNextFocusable(container, { anchor: document.querySelector('.button') });
113
113
 
114
114
  // Traverses the composed tree (including shadow DOM; slower)
115
115
  getNextFocusable(container, { composed: true });
@@ -132,7 +132,7 @@ getPreviousFocusable(container);
132
132
  // container (optional): Element (default: <body>)
133
133
 
134
134
  // Specifies the starting element
135
- getPreviousFocusable(container, { active: document.querySelector('.button') });
135
+ getPreviousFocusable(container, { anchor: document.querySelector('.button') });
136
136
 
137
137
  // Traverses the composed tree (including shadow DOM; slower)
138
138
  getPreviousFocusable(container, { composed: true });
package/dist/index.cjs CHANGED
@@ -9,9 +9,7 @@ function createFocusTrap(container) {
9
9
  focus(container);
10
10
  if (getActiveElement() !== container) {
11
11
  const first = getFocusables(container, { composed: true })[0];
12
- if (first) {
13
- focus(first);
14
- }
12
+ first && focus(first);
15
13
  }
16
14
  function onKeyDown(event) {
17
15
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
@@ -121,9 +119,7 @@ function inertOutside(element) {
121
119
  if (!(node instanceof Element)) {
122
120
  return;
123
121
  }
124
- if (applyInert(node)) {
125
- elements.push(node);
126
- }
122
+ applyInert(node) && elements.push(node);
127
123
  });
128
124
  return () => {
129
125
  elements.forEach((element2) => {
@@ -159,7 +155,7 @@ function isFocusable(element) {
159
155
  }
160
156
  function getRelativeFocusable(container, offset, options) {
161
157
  const {
162
- active = getActiveElement(),
158
+ anchor = getActiveElement(),
163
159
  composed = false,
164
160
  filter = () => true,
165
161
  wrap = false
@@ -169,13 +165,13 @@ function getRelativeFocusable(container, offset, options) {
169
165
  if (!length) {
170
166
  return null;
171
167
  }
172
- if (!active || !containsComposed(container, active)) {
168
+ if (!anchor || !containsComposed(container, anchor)) {
173
169
  return null;
174
170
  }
175
- if (!(active instanceof Element)) {
171
+ if (!(anchor instanceof Element)) {
176
172
  return null;
177
173
  }
178
- const currentIndex = focusables.indexOf(active);
174
+ const currentIndex = focusables.indexOf(anchor);
179
175
  if (currentIndex === -1) {
180
176
  return null;
181
177
  }
@@ -335,9 +331,7 @@ function applyInert(element) {
335
331
  }
336
332
  const count = inertRefCounts.get(element) ?? 0;
337
333
  inertRefCounts.set(element, count + 1);
338
- if (count === 0) {
339
- setInert(element, true);
340
- }
334
+ count === 0 && element.toggleAttribute("inert", true);
341
335
  return true;
342
336
  }
343
337
  function restoreInert(element) {
@@ -347,15 +341,13 @@ function restoreInert(element) {
347
341
  }
348
342
  if (count === 1) {
349
343
  inertRefCounts.delete(element);
350
- setInert(element, false);
344
+ element.toggleAttribute("inert", false);
351
345
  return;
352
346
  }
353
347
  inertRefCounts.set(element, count - 1);
354
348
  }
355
349
  function focus(element) {
356
- if ("focus" in element && typeof element.focus === "function") {
357
- element.focus();
358
- }
350
+ "focus" in element && typeof element.focus === "function" && element.focus();
359
351
  }
360
352
  function getActiveElement() {
361
353
  let current = document.activeElement;
@@ -400,20 +392,13 @@ function isInert(element) {
400
392
  function isUngroupedRadio(element) {
401
393
  return element instanceof HTMLInputElement && element.type === "radio" && !!element.name;
402
394
  }
403
- function setInert(element, boolean) {
404
- if (boolean) {
405
- element.setAttribute("inert", "");
406
- } else {
407
- element.removeAttribute("inert");
408
- }
409
- }
410
395
  /**
411
396
  * Power Focusable
412
397
  * High-precision focus management utility with full composed tree support.
413
398
  * Handles complex focus rules including tabindex ordering, radio groups, inert,
414
399
  * and shadow DOM.
415
400
  *
416
- * @version 3.1.0
401
+ * @version 4.0.0
417
402
  * @author Yusuke Kamiyamane
418
403
  * @license MIT
419
404
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -4,23 +4,23 @@
4
4
  * Handles complex focus rules including tabindex ordering, radio groups, inert,
5
5
  * and shadow DOM.
6
6
  *
7
- * @version 3.1.0
7
+ * @version 4.0.0
8
8
  * @author Yusuke Kamiyamane
9
9
  * @license MIT
10
10
  * @copyright Copyright (c) Yusuke Kamiyamane
11
11
  * @see {@link https://github.com/y14e/power-focusable}
12
12
  */
13
13
  interface PowerFocusableOptions {
14
- readonly active?: Element | null;
14
+ readonly anchor?: Element | null;
15
15
  readonly composed?: boolean;
16
16
  readonly filter?: (element: Element) => boolean;
17
17
  readonly wrap?: boolean;
18
18
  }
19
19
  declare function createFocusTrap(container: Element): () => void;
20
- declare function getFocusables(container?: Element, options?: Omit<PowerFocusableOptions, 'active' | 'wrap'>): Element[];
20
+ declare function getFocusables(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): Element[];
21
21
  declare function getNextFocusable(container?: Element, options?: PowerFocusableOptions): Element | null;
22
22
  declare function getPreviousFocusable(container?: Element, options?: PowerFocusableOptions): Element | null;
23
- declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'active' | 'wrap'>): boolean;
23
+ declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): boolean;
24
24
  declare function inertOutside(element: Element): () => void;
25
25
  declare function isFocusable(element: Element): boolean;
26
26
 
package/dist/index.d.ts CHANGED
@@ -4,23 +4,23 @@
4
4
  * Handles complex focus rules including tabindex ordering, radio groups, inert,
5
5
  * and shadow DOM.
6
6
  *
7
- * @version 3.1.0
7
+ * @version 4.0.0
8
8
  * @author Yusuke Kamiyamane
9
9
  * @license MIT
10
10
  * @copyright Copyright (c) Yusuke Kamiyamane
11
11
  * @see {@link https://github.com/y14e/power-focusable}
12
12
  */
13
13
  interface PowerFocusableOptions {
14
- readonly active?: Element | null;
14
+ readonly anchor?: Element | null;
15
15
  readonly composed?: boolean;
16
16
  readonly filter?: (element: Element) => boolean;
17
17
  readonly wrap?: boolean;
18
18
  }
19
19
  declare function createFocusTrap(container: Element): () => void;
20
- declare function getFocusables(container?: Element, options?: Omit<PowerFocusableOptions, 'active' | 'wrap'>): Element[];
20
+ declare function getFocusables(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): Element[];
21
21
  declare function getNextFocusable(container?: Element, options?: PowerFocusableOptions): Element | null;
22
22
  declare function getPreviousFocusable(container?: Element, options?: PowerFocusableOptions): Element | null;
23
- declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'active' | 'wrap'>): boolean;
23
+ declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): boolean;
24
24
  declare function inertOutside(element: Element): () => void;
25
25
  declare function isFocusable(element: Element): boolean;
26
26
 
package/dist/index.js CHANGED
@@ -7,9 +7,7 @@ function createFocusTrap(container) {
7
7
  focus(container);
8
8
  if (getActiveElement() !== container) {
9
9
  const first = getFocusables(container, { composed: true })[0];
10
- if (first) {
11
- focus(first);
12
- }
10
+ first && focus(first);
13
11
  }
14
12
  function onKeyDown(event) {
15
13
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
@@ -119,9 +117,7 @@ function inertOutside(element) {
119
117
  if (!(node instanceof Element)) {
120
118
  return;
121
119
  }
122
- if (applyInert(node)) {
123
- elements.push(node);
124
- }
120
+ applyInert(node) && elements.push(node);
125
121
  });
126
122
  return () => {
127
123
  elements.forEach((element2) => {
@@ -157,7 +153,7 @@ function isFocusable(element) {
157
153
  }
158
154
  function getRelativeFocusable(container, offset, options) {
159
155
  const {
160
- active = getActiveElement(),
156
+ anchor = getActiveElement(),
161
157
  composed = false,
162
158
  filter = () => true,
163
159
  wrap = false
@@ -167,13 +163,13 @@ function getRelativeFocusable(container, offset, options) {
167
163
  if (!length) {
168
164
  return null;
169
165
  }
170
- if (!active || !containsComposed(container, active)) {
166
+ if (!anchor || !containsComposed(container, anchor)) {
171
167
  return null;
172
168
  }
173
- if (!(active instanceof Element)) {
169
+ if (!(anchor instanceof Element)) {
174
170
  return null;
175
171
  }
176
- const currentIndex = focusables.indexOf(active);
172
+ const currentIndex = focusables.indexOf(anchor);
177
173
  if (currentIndex === -1) {
178
174
  return null;
179
175
  }
@@ -333,9 +329,7 @@ function applyInert(element) {
333
329
  }
334
330
  const count = inertRefCounts.get(element) ?? 0;
335
331
  inertRefCounts.set(element, count + 1);
336
- if (count === 0) {
337
- setInert(element, true);
338
- }
332
+ count === 0 && element.toggleAttribute("inert", true);
339
333
  return true;
340
334
  }
341
335
  function restoreInert(element) {
@@ -345,15 +339,13 @@ function restoreInert(element) {
345
339
  }
346
340
  if (count === 1) {
347
341
  inertRefCounts.delete(element);
348
- setInert(element, false);
342
+ element.toggleAttribute("inert", false);
349
343
  return;
350
344
  }
351
345
  inertRefCounts.set(element, count - 1);
352
346
  }
353
347
  function focus(element) {
354
- if ("focus" in element && typeof element.focus === "function") {
355
- element.focus();
356
- }
348
+ "focus" in element && typeof element.focus === "function" && element.focus();
357
349
  }
358
350
  function getActiveElement() {
359
351
  let current = document.activeElement;
@@ -398,20 +390,13 @@ function isInert(element) {
398
390
  function isUngroupedRadio(element) {
399
391
  return element instanceof HTMLInputElement && element.type === "radio" && !!element.name;
400
392
  }
401
- function setInert(element, boolean) {
402
- if (boolean) {
403
- element.setAttribute("inert", "");
404
- } else {
405
- element.removeAttribute("inert");
406
- }
407
- }
408
393
  /**
409
394
  * Power Focusable
410
395
  * High-precision focus management utility with full composed tree support.
411
396
  * Handles complex focus rules including tabindex ordering, radio groups, inert,
412
397
  * and shadow DOM.
413
398
  *
414
- * @version 3.1.0
399
+ * @version 4.0.0
415
400
  * @author Yusuke Kamiyamane
416
401
  * @license MIT
417
402
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "power-focusable",
3
- "version": "3.1.0",
3
+ "version": "4.0.0",
4
4
  "description": "High-precision focus management utility with full composed tree support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",