power-focusable 4.3.6 → 4.3.8

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
@@ -24,11 +24,11 @@ import {
24
24
  } from 'power-focusable';
25
25
 
26
26
  // CDNs
27
- import { ... } 'https://esm.sh/power-focusable@4.3.6';
27
+ import { ... } 'https://esm.sh/power-focusable@4.3.8';
28
28
  // or
29
- import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable@4.3.6/+esm';
29
+ import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable@4.3.8/+esm';
30
30
  // or
31
- import { ... } 'https://esm.unpkg.com/power-focusable@4.3.6';
31
+ import { ... } 'https://esm.unpkg.com/power-focusable@4.3.8';
32
32
  ```
33
33
 
34
34
  ## 🪄 Options
package/dist/index.cjs CHANGED
@@ -4,22 +4,49 @@
4
4
  var FOCUSABLE_SELECTOR = `:is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])`;
5
5
  function createFocusTrap(container = document.body) {
6
6
  if (!(container instanceof Element)) {
7
- throw new TypeError("Invalid container element");
7
+ console.warn("Invalid container element");
8
+ return () => {
9
+ };
8
10
  }
9
- focusElement(container);
10
- if (getActiveElement() !== container) {
11
- const first = getFocusables(container, { composed: true })[0];
12
- first && focusElement(first);
11
+ const trap = new FocusTrap(container);
12
+ return () => trap.destroy();
13
+ }
14
+ var FocusTrap = class {
15
+ #container;
16
+ #controller = null;
17
+ #isDestroyed = false;
18
+ constructor(container) {
19
+ this.#container = container;
20
+ this.#initialize();
21
+ }
22
+ destroy() {
23
+ if (this.#isDestroyed) {
24
+ return;
25
+ }
26
+ this.#isDestroyed = true;
27
+ this.#controller?.abort();
28
+ this.#controller = null;
29
+ }
30
+ #initialize() {
31
+ this.#controller = new AbortController();
32
+ this.#container.addEventListener("keydown", this.#onKeyDown, {
33
+ signal: this.#controller.signal
34
+ });
35
+ focusElement(this.#container);
36
+ if (getActiveElement() !== this.#container) {
37
+ const first = getFocusables(this.#container, { composed: true })[0];
38
+ first && focusElement(first);
39
+ }
13
40
  }
14
- function onKeyDown(event) {
15
- if (!event.composedPath().includes(container)) {
41
+ #onKeyDown = (event) => {
42
+ if (!(event instanceof KeyboardEvent)) {
16
43
  return;
17
44
  }
18
45
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
19
46
  if (key !== "Tab" || altKey || ctrlKey || metaKey) {
20
47
  return;
21
48
  }
22
- const focusable = getRelativeFocusable(container, shiftKey ? -1 : 1, {
49
+ const focusable = getRelativeFocusable(this.#container, shiftKey ? -1 : 1, {
23
50
  composed: true,
24
51
  wrap: true
25
52
  });
@@ -27,16 +54,8 @@ function createFocusTrap(container = document.body) {
27
54
  event.preventDefault();
28
55
  focusElement(focusable);
29
56
  }
30
- }
31
- let controller = new AbortController();
32
- document.addEventListener("keydown", onKeyDown, {
33
- signal: controller.signal
34
- });
35
- return () => {
36
- controller?.abort();
37
- controller = null;
38
57
  };
39
- }
58
+ };
40
59
  function getFocusables(container = document.body, options = {}) {
41
60
  if (!(container instanceof Element)) {
42
61
  console.warn("Invalid container element. Fallback: <body> element.");
@@ -457,7 +476,7 @@ function isUngroupedRadio(element) {
457
476
  * High-precision focus management utility with full composed tree support.
458
477
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
459
478
  *
460
- * @version 4.3.6
479
+ * @version 4.3.8
461
480
  * @author Yusuke Kamiyamane
462
481
  * @license MIT
463
482
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@
3
3
  * High-precision focus management utility with full composed tree support.
4
4
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
5
5
  *
6
- * @version 4.3.6
6
+ * @version 4.3.8
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * High-precision focus management utility with full composed tree support.
4
4
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
5
5
  *
6
- * @version 4.3.6
6
+ * @version 4.3.8
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -2,22 +2,49 @@
2
2
  var FOCUSABLE_SELECTOR = `:is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])`;
3
3
  function createFocusTrap(container = document.body) {
4
4
  if (!(container instanceof Element)) {
5
- throw new TypeError("Invalid container element");
5
+ console.warn("Invalid container element");
6
+ return () => {
7
+ };
6
8
  }
7
- focusElement(container);
8
- if (getActiveElement() !== container) {
9
- const first = getFocusables(container, { composed: true })[0];
10
- first && focusElement(first);
9
+ const trap = new FocusTrap(container);
10
+ return () => trap.destroy();
11
+ }
12
+ var FocusTrap = class {
13
+ #container;
14
+ #controller = null;
15
+ #isDestroyed = false;
16
+ constructor(container) {
17
+ this.#container = container;
18
+ this.#initialize();
19
+ }
20
+ destroy() {
21
+ if (this.#isDestroyed) {
22
+ return;
23
+ }
24
+ this.#isDestroyed = true;
25
+ this.#controller?.abort();
26
+ this.#controller = null;
27
+ }
28
+ #initialize() {
29
+ this.#controller = new AbortController();
30
+ this.#container.addEventListener("keydown", this.#onKeyDown, {
31
+ signal: this.#controller.signal
32
+ });
33
+ focusElement(this.#container);
34
+ if (getActiveElement() !== this.#container) {
35
+ const first = getFocusables(this.#container, { composed: true })[0];
36
+ first && focusElement(first);
37
+ }
11
38
  }
12
- function onKeyDown(event) {
13
- if (!event.composedPath().includes(container)) {
39
+ #onKeyDown = (event) => {
40
+ if (!(event instanceof KeyboardEvent)) {
14
41
  return;
15
42
  }
16
43
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
17
44
  if (key !== "Tab" || altKey || ctrlKey || metaKey) {
18
45
  return;
19
46
  }
20
- const focusable = getRelativeFocusable(container, shiftKey ? -1 : 1, {
47
+ const focusable = getRelativeFocusable(this.#container, shiftKey ? -1 : 1, {
21
48
  composed: true,
22
49
  wrap: true
23
50
  });
@@ -25,16 +52,8 @@ function createFocusTrap(container = document.body) {
25
52
  event.preventDefault();
26
53
  focusElement(focusable);
27
54
  }
28
- }
29
- let controller = new AbortController();
30
- document.addEventListener("keydown", onKeyDown, {
31
- signal: controller.signal
32
- });
33
- return () => {
34
- controller?.abort();
35
- controller = null;
36
55
  };
37
- }
56
+ };
38
57
  function getFocusables(container = document.body, options = {}) {
39
58
  if (!(container instanceof Element)) {
40
59
  console.warn("Invalid container element. Fallback: <body> element.");
@@ -455,7 +474,7 @@ function isUngroupedRadio(element) {
455
474
  * High-precision focus management utility with full composed tree support.
456
475
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
457
476
  *
458
- * @version 4.3.6
477
+ * @version 4.3.8
459
478
  * @author Yusuke Kamiyamane
460
479
  * @license MIT
461
480
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "power-focusable",
3
- "version": "4.3.6",
3
+ "version": "4.3.8",
4
4
  "description": "High-precision focus management utility with full composed tree support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",