@y14e/portal 1.0.0 → 1.0.2

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/index.cjs CHANGED
@@ -7,9 +7,18 @@ function getFocusables(container = document.body, options = {}) {
7
7
  console.warn("Invalid container element. Fallback: <body> element.");
8
8
  container = document.body;
9
9
  }
10
- const { composed = false, filter = () => true, include } = options;
10
+ const { composed = false } = options;
11
+ let { filter, include } = options;
12
+ if (filter && typeof filter !== "function") {
13
+ console.warn("Invalid filter function. Fallback: undefined.");
14
+ filter = void 0;
15
+ }
16
+ if (include && typeof include !== "function") {
17
+ console.warn("Invalid include function. Fallback: undefined.");
18
+ include = void 0;
19
+ }
11
20
  const elements = [];
12
- if (composed || typeof include === "function") {
21
+ if (composed || include) {
13
22
  let traverse2 = function(node) {
14
23
  if (node instanceof Element) {
15
24
  if (isFocusable(node) || include?.(node)) {
@@ -38,7 +47,8 @@ function getFocusables(container = document.body, options = {}) {
38
47
  }
39
48
  }
40
49
  }
41
- return normalizeRadioGroup(sortByTabIndex(elements)).filter(filter);
50
+ const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
51
+ return filter ? unfiltered.filter(filter) : unfiltered;
42
52
  }
43
53
  function getNextFocusable(container = document.body, options = {}) {
44
54
  if (!(container instanceof Element)) {
@@ -81,22 +91,25 @@ function isFocusable(element) {
81
91
  return true;
82
92
  }
83
93
  function getRelativeFocusable(container, offset, options) {
84
- const {
85
- anchor = getActiveElement(),
86
- composed = false,
87
- filter = () => true,
88
- include,
89
- wrap = false
90
- } = options;
91
- const focusables = getFocusables(container, { composed, filter, include });
92
- const { length } = focusables;
93
- if (!length) {
94
- return null;
94
+ let anchor = options.anchor ?? getActiveElement();
95
+ const { composed = false, filter, include, wrap = false } = options;
96
+ if (!(anchor instanceof Element)) {
97
+ const active = getActiveElement();
98
+ if (active instanceof Element) {
99
+ console.warn("Invalid anchor element. Fallback: active element.");
100
+ anchor = active;
101
+ } else {
102
+ console.warn("Invalid anchor element");
103
+ return null;
104
+ }
95
105
  }
96
- if (!anchor || !containsComposed(container, anchor)) {
106
+ if (!containsComposed(container, anchor)) {
107
+ console.warn("Mismatch elements");
97
108
  return null;
98
109
  }
99
- if (!(anchor instanceof Element)) {
110
+ const focusables = getFocusables(container, { composed, filter, include });
111
+ const { length } = focusables;
112
+ if (!length) {
100
113
  return null;
101
114
  }
102
115
  const currentIndex = focusables.indexOf(anchor);
@@ -262,6 +275,9 @@ function createPortal(host, container = document.body) {
262
275
  console.warn("Invalid container element. Fallback: <body> element.");
263
276
  container = document.body;
264
277
  }
278
+ if (!containsComposed2(container, host)) {
279
+ throw new Error("Mismatch elements");
280
+ }
265
281
  const portal = new Portal(host, container);
266
282
  return () => portal.destroy();
267
283
  }
@@ -394,6 +410,16 @@ var Portal = class {
394
410
  focusable && focus(focusable);
395
411
  }
396
412
  };
413
+ function containsComposed2(container, element) {
414
+ let current = element;
415
+ while (current) {
416
+ if (current === container) {
417
+ return true;
418
+ }
419
+ current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
420
+ }
421
+ return false;
422
+ }
397
423
  function focus(element) {
398
424
  "focus" in element && typeof element.focus === "function" && element.focus();
399
425
  }
@@ -409,7 +435,7 @@ function getActiveElement2() {
409
435
  * Lightweight DOM portal (teleport) utility with fully focus management.
410
436
  * Designed for accessible dialogs, menus, overlays, popovers.
411
437
  *
412
- * @version 1.0.0
438
+ * @version 1.0.2
413
439
  * @author Yusuke Kamiyamane
414
440
  * @license MIT
415
441
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -423,7 +449,7 @@ power-focusable/dist/index.js:
423
449
  * High-precision focus management utility with full composed tree support.
424
450
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
425
451
  *
426
- * @version 4.1.0
452
+ * @version 4.1.3
427
453
  * @author Yusuke Kamiyamane
428
454
  * @license MIT
429
455
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Lightweight DOM portal (teleport) utility with fully focus management.
4
4
  * Designed for accessible dialogs, menus, overlays, popovers.
5
5
  *
6
- * @version 1.0.0
6
+ * @version 1.0.2
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
  * Lightweight DOM portal (teleport) utility with fully focus management.
4
4
  * Designed for accessible dialogs, menus, overlays, popovers.
5
5
  *
6
- * @version 1.0.0
6
+ * @version 1.0.2
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -5,9 +5,18 @@ function getFocusables(container = document.body, options = {}) {
5
5
  console.warn("Invalid container element. Fallback: <body> element.");
6
6
  container = document.body;
7
7
  }
8
- const { composed = false, filter = () => true, include } = options;
8
+ const { composed = false } = options;
9
+ let { filter, include } = options;
10
+ if (filter && typeof filter !== "function") {
11
+ console.warn("Invalid filter function. Fallback: undefined.");
12
+ filter = void 0;
13
+ }
14
+ if (include && typeof include !== "function") {
15
+ console.warn("Invalid include function. Fallback: undefined.");
16
+ include = void 0;
17
+ }
9
18
  const elements = [];
10
- if (composed || typeof include === "function") {
19
+ if (composed || include) {
11
20
  let traverse2 = function(node) {
12
21
  if (node instanceof Element) {
13
22
  if (isFocusable(node) || include?.(node)) {
@@ -36,7 +45,8 @@ function getFocusables(container = document.body, options = {}) {
36
45
  }
37
46
  }
38
47
  }
39
- return normalizeRadioGroup(sortByTabIndex(elements)).filter(filter);
48
+ const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
49
+ return filter ? unfiltered.filter(filter) : unfiltered;
40
50
  }
41
51
  function getNextFocusable(container = document.body, options = {}) {
42
52
  if (!(container instanceof Element)) {
@@ -79,22 +89,25 @@ function isFocusable(element) {
79
89
  return true;
80
90
  }
81
91
  function getRelativeFocusable(container, offset, options) {
82
- const {
83
- anchor = getActiveElement(),
84
- composed = false,
85
- filter = () => true,
86
- include,
87
- wrap = false
88
- } = options;
89
- const focusables = getFocusables(container, { composed, filter, include });
90
- const { length } = focusables;
91
- if (!length) {
92
- return null;
92
+ let anchor = options.anchor ?? getActiveElement();
93
+ const { composed = false, filter, include, wrap = false } = options;
94
+ if (!(anchor instanceof Element)) {
95
+ const active = getActiveElement();
96
+ if (active instanceof Element) {
97
+ console.warn("Invalid anchor element. Fallback: active element.");
98
+ anchor = active;
99
+ } else {
100
+ console.warn("Invalid anchor element");
101
+ return null;
102
+ }
93
103
  }
94
- if (!anchor || !containsComposed(container, anchor)) {
104
+ if (!containsComposed(container, anchor)) {
105
+ console.warn("Mismatch elements");
95
106
  return null;
96
107
  }
97
- if (!(anchor instanceof Element)) {
108
+ const focusables = getFocusables(container, { composed, filter, include });
109
+ const { length } = focusables;
110
+ if (!length) {
98
111
  return null;
99
112
  }
100
113
  const currentIndex = focusables.indexOf(anchor);
@@ -260,6 +273,9 @@ function createPortal(host, container = document.body) {
260
273
  console.warn("Invalid container element. Fallback: <body> element.");
261
274
  container = document.body;
262
275
  }
276
+ if (!containsComposed2(container, host)) {
277
+ throw new Error("Mismatch elements");
278
+ }
263
279
  const portal = new Portal(host, container);
264
280
  return () => portal.destroy();
265
281
  }
@@ -392,6 +408,16 @@ var Portal = class {
392
408
  focusable && focus(focusable);
393
409
  }
394
410
  };
411
+ function containsComposed2(container, element) {
412
+ let current = element;
413
+ while (current) {
414
+ if (current === container) {
415
+ return true;
416
+ }
417
+ current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
418
+ }
419
+ return false;
420
+ }
395
421
  function focus(element) {
396
422
  "focus" in element && typeof element.focus === "function" && element.focus();
397
423
  }
@@ -407,7 +433,7 @@ function getActiveElement2() {
407
433
  * Lightweight DOM portal (teleport) utility with fully focus management.
408
434
  * Designed for accessible dialogs, menus, overlays, popovers.
409
435
  *
410
- * @version 1.0.0
436
+ * @version 1.0.2
411
437
  * @author Yusuke Kamiyamane
412
438
  * @license MIT
413
439
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -421,7 +447,7 @@ power-focusable/dist/index.js:
421
447
  * High-precision focus management utility with full composed tree support.
422
448
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
423
449
  *
424
- * @version 4.1.0
450
+ * @version 4.1.3
425
451
  * @author Yusuke Kamiyamane
426
452
  * @license MIT
427
453
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/portal",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Lightweight DOM portal (teleport) utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -48,7 +48,7 @@
48
48
  "homepage": "https://github.com/y14e/portal#readme",
49
49
  "devDependencies": {
50
50
  "bun-types": "latest",
51
- "power-focusable": "^4.1.0",
51
+ "power-focusable": "^4.1.3",
52
52
  "tsup": "^8.0.0",
53
53
  "typescript": "^5.6.0"
54
54
  },