@y14e/portal 1.2.14 → 1.2.15

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
@@ -10,14 +10,14 @@ npm i @y14e/portal
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import { createPortal } from '@y14e/portal@1.2.14';
13
+ import { createPortal } from '@y14e/portal@1.2.15';
14
14
 
15
15
  // CDNs
16
- import { createPortal } from 'https://esm.sh/@y14e/portal@1.2.14';
16
+ import { createPortal } from 'https://esm.sh/@y14e/portal@1.2.15';
17
17
  // or
18
- import { createPortal } from 'https://cdn.jsdelivr.net/npm/@y14e/portal@1.2.14/+esm';
18
+ import { createPortal } from 'https://cdn.jsdelivr.net/npm/@y14e/portal@1.2.15/+esm';
19
19
  // or
20
- import { createPortal } from 'https://esm.unpkg.com/@y14e/portal@1.2.14';
20
+ import { createPortal } from 'https://esm.unpkg.com/@y14e/portal@1.2.15';
21
21
  ```
22
22
 
23
23
  ## 📦 APIs
package/dist/index.cjs CHANGED
@@ -68,21 +68,16 @@ function getFocusables(container = document.body, options = {}) {
68
68
  const elements = [];
69
69
  if (composed || include) {
70
70
  let traverse2 = function(node) {
71
- if (node instanceof Element) {
72
- if (isFocusable(node, {
73
- skipNegativeTabIndexCheck,
74
- skipVisibilityCheck
75
- }) || include?.(node)) {
76
- elements[elements.length] = node;
77
- }
71
+ if (!(node instanceof Element)) {
72
+ return;
73
+ }
74
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
75
+ elements[elements.length] = node;
78
76
  }
79
77
  const children = getComposedChildren(node);
80
78
  for (let i = 0, l = children.length; i < l; i++) {
81
79
  const child = children[i];
82
- if (!child) {
83
- continue;
84
- }
85
- traverse2(child);
80
+ child && traverse2(child);
86
81
  }
87
82
  };
88
83
  traverse2(container);
@@ -90,10 +85,7 @@ function getFocusables(container = document.body, options = {}) {
90
85
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
91
86
  for (let i = 0, l = candidates.length; i < l; i++) {
92
87
  const candidate = candidates[i];
93
- if (!(candidate instanceof Element)) {
94
- continue;
95
- }
96
- if (isFocusable(candidate, {
88
+ if (candidate && isFocusable(candidate, {
97
89
  skipNegativeTabIndexCheck,
98
90
  skipVisibilityCheck
99
91
  })) {
@@ -280,23 +272,19 @@ function normalizeRadioGroup(elements) {
280
272
  for (const group of map.values()) {
281
273
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
282
274
  }
283
- return elements.filter((element) => {
284
- if (isUngroupedRadio(element)) {
285
- return placeholder.has(element);
286
- }
287
- return true;
288
- });
275
+ return elements.filter(
276
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
277
+ );
289
278
  }
290
279
  function sortByTabIndex(elements) {
291
280
  const ordered = [];
292
281
  const natural = [];
293
282
  for (let i = 0, l = elements.length; i < l; i++) {
294
283
  const element = elements[i];
295
- if (!element) {
296
- continue;
284
+ if (element) {
285
+ const target = getTabIndex(element) > 0 ? ordered : natural;
286
+ target[target.length] = element;
297
287
  }
298
- const target = getTabIndex(element) > 0 ? ordered : natural;
299
- target[target.length] = element;
300
288
  }
301
289
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
302
290
  let count = 0;
@@ -314,8 +302,9 @@ function containsComposed(container, element) {
314
302
  while (current) {
315
303
  if (current === container) {
316
304
  return true;
305
+ } else {
306
+ current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
317
307
  }
318
- current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
319
308
  }
320
309
  return false;
321
310
  }
@@ -501,18 +490,17 @@ var Portal = class {
501
490
  }
502
491
  this.#update();
503
492
  const focusables = this.#getFocusables();
504
- if (!focusables.length) {
493
+ if (focusables.length) {
494
+ const index = focusables.indexOf(active);
495
+ if (index !== -1) {
496
+ event.preventDefault();
497
+ const focusable = focusables[index + (shiftKey ? -1 : 1)];
498
+ focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
499
+ }
500
+ } else {
505
501
  event.preventDefault();
506
502
  this.#moveFocus(shiftKey ? "previous" : "next");
507
- return;
508
- }
509
- const index = focusables.indexOf(active);
510
- if (index === -1) {
511
- return;
512
503
  }
513
- event.preventDefault();
514
- const focusable = focusables[index + (shiftKey ? -1 : 1)];
515
- focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
516
504
  };
517
505
  #update() {
518
506
  const current = /* @__PURE__ */ new Set([
@@ -520,19 +508,17 @@ var Portal = class {
520
508
  ...getFocusables(this.#host, { composed: true })
521
509
  ]);
522
510
  for (const focusable of this.#focusables) {
523
- if (current.has(focusable)) {
524
- continue;
511
+ if (!current.has(focusable)) {
512
+ focusable.isConnected && restoreAttributes([focusable]);
513
+ this.#focusables.delete(focusable);
525
514
  }
526
- focusable.isConnected && restoreAttributes([focusable]);
527
- this.#focusables.delete(focusable);
528
515
  }
529
516
  for (const focusable of current) {
530
- if (this.#focusables.has(focusable)) {
531
- continue;
517
+ if (!this.#focusables.has(focusable)) {
518
+ this.#focusables.add(focusable);
519
+ saveAttributes([focusable], ["tabindex"]);
520
+ focusable.setAttribute("tabindex", "-1");
532
521
  }
533
- this.#focusables.add(focusable);
534
- saveAttributes([focusable], ["tabindex"]);
535
- focusable.setAttribute("tabindex", "-1");
536
522
  }
537
523
  }
538
524
  #createSentinel() {
@@ -589,7 +575,7 @@ function getActiveElement2() {
589
575
  * Lightweight DOM portal (teleport) utility with fully focus management.
590
576
  * Designed for accessible dialogs, menus, overlays, popovers.
591
577
  *
592
- * @version 1.2.14
578
+ * @version 1.2.15
593
579
  * @author Yusuke Kamiyamane
594
580
  * @license MIT
595
581
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -601,7 +587,7 @@ function getActiveElement2() {
601
587
  (**
602
588
  * Attributes Utils
603
589
  *
604
- * @version 1.1.1
590
+ * @version 1.1.2
605
591
  * @author Yusuke Kamiyamane
606
592
  * @license MIT
607
593
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -614,7 +600,7 @@ power-focusable/dist/index.js:
614
600
  * High-precision focus management utility with full composed tree support.
615
601
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
616
602
  *
617
- * @version 4.3.2
603
+ * @version 4.3.3
618
604
  * @author Yusuke Kamiyamane
619
605
  * @license MIT
620
606
  * @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.2.14
6
+ * @version 1.2.15
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.2.14
6
+ * @version 1.2.15
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -66,21 +66,16 @@ function getFocusables(container = document.body, options = {}) {
66
66
  const elements = [];
67
67
  if (composed || include) {
68
68
  let traverse2 = function(node) {
69
- if (node instanceof Element) {
70
- if (isFocusable(node, {
71
- skipNegativeTabIndexCheck,
72
- skipVisibilityCheck
73
- }) || include?.(node)) {
74
- elements[elements.length] = node;
75
- }
69
+ if (!(node instanceof Element)) {
70
+ return;
71
+ }
72
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
73
+ elements[elements.length] = node;
76
74
  }
77
75
  const children = getComposedChildren(node);
78
76
  for (let i = 0, l = children.length; i < l; i++) {
79
77
  const child = children[i];
80
- if (!child) {
81
- continue;
82
- }
83
- traverse2(child);
78
+ child && traverse2(child);
84
79
  }
85
80
  };
86
81
  traverse2(container);
@@ -88,10 +83,7 @@ function getFocusables(container = document.body, options = {}) {
88
83
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
89
84
  for (let i = 0, l = candidates.length; i < l; i++) {
90
85
  const candidate = candidates[i];
91
- if (!(candidate instanceof Element)) {
92
- continue;
93
- }
94
- if (isFocusable(candidate, {
86
+ if (candidate && isFocusable(candidate, {
95
87
  skipNegativeTabIndexCheck,
96
88
  skipVisibilityCheck
97
89
  })) {
@@ -278,23 +270,19 @@ function normalizeRadioGroup(elements) {
278
270
  for (const group of map.values()) {
279
271
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
280
272
  }
281
- return elements.filter((element) => {
282
- if (isUngroupedRadio(element)) {
283
- return placeholder.has(element);
284
- }
285
- return true;
286
- });
273
+ return elements.filter(
274
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
275
+ );
287
276
  }
288
277
  function sortByTabIndex(elements) {
289
278
  const ordered = [];
290
279
  const natural = [];
291
280
  for (let i = 0, l = elements.length; i < l; i++) {
292
281
  const element = elements[i];
293
- if (!element) {
294
- continue;
282
+ if (element) {
283
+ const target = getTabIndex(element) > 0 ? ordered : natural;
284
+ target[target.length] = element;
295
285
  }
296
- const target = getTabIndex(element) > 0 ? ordered : natural;
297
- target[target.length] = element;
298
286
  }
299
287
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
300
288
  let count = 0;
@@ -312,8 +300,9 @@ function containsComposed(container, element) {
312
300
  while (current) {
313
301
  if (current === container) {
314
302
  return true;
303
+ } else {
304
+ current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
315
305
  }
316
- current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
317
306
  }
318
307
  return false;
319
308
  }
@@ -499,18 +488,17 @@ var Portal = class {
499
488
  }
500
489
  this.#update();
501
490
  const focusables = this.#getFocusables();
502
- if (!focusables.length) {
491
+ if (focusables.length) {
492
+ const index = focusables.indexOf(active);
493
+ if (index !== -1) {
494
+ event.preventDefault();
495
+ const focusable = focusables[index + (shiftKey ? -1 : 1)];
496
+ focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
497
+ }
498
+ } else {
503
499
  event.preventDefault();
504
500
  this.#moveFocus(shiftKey ? "previous" : "next");
505
- return;
506
- }
507
- const index = focusables.indexOf(active);
508
- if (index === -1) {
509
- return;
510
501
  }
511
- event.preventDefault();
512
- const focusable = focusables[index + (shiftKey ? -1 : 1)];
513
- focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
514
502
  };
515
503
  #update() {
516
504
  const current = /* @__PURE__ */ new Set([
@@ -518,19 +506,17 @@ var Portal = class {
518
506
  ...getFocusables(this.#host, { composed: true })
519
507
  ]);
520
508
  for (const focusable of this.#focusables) {
521
- if (current.has(focusable)) {
522
- continue;
509
+ if (!current.has(focusable)) {
510
+ focusable.isConnected && restoreAttributes([focusable]);
511
+ this.#focusables.delete(focusable);
523
512
  }
524
- focusable.isConnected && restoreAttributes([focusable]);
525
- this.#focusables.delete(focusable);
526
513
  }
527
514
  for (const focusable of current) {
528
- if (this.#focusables.has(focusable)) {
529
- continue;
515
+ if (!this.#focusables.has(focusable)) {
516
+ this.#focusables.add(focusable);
517
+ saveAttributes([focusable], ["tabindex"]);
518
+ focusable.setAttribute("tabindex", "-1");
530
519
  }
531
- this.#focusables.add(focusable);
532
- saveAttributes([focusable], ["tabindex"]);
533
- focusable.setAttribute("tabindex", "-1");
534
520
  }
535
521
  }
536
522
  #createSentinel() {
@@ -587,7 +573,7 @@ function getActiveElement2() {
587
573
  * Lightweight DOM portal (teleport) utility with fully focus management.
588
574
  * Designed for accessible dialogs, menus, overlays, popovers.
589
575
  *
590
- * @version 1.2.14
576
+ * @version 1.2.15
591
577
  * @author Yusuke Kamiyamane
592
578
  * @license MIT
593
579
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -599,7 +585,7 @@ function getActiveElement2() {
599
585
  (**
600
586
  * Attributes Utils
601
587
  *
602
- * @version 1.1.1
588
+ * @version 1.1.2
603
589
  * @author Yusuke Kamiyamane
604
590
  * @license MIT
605
591
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -612,7 +598,7 @@ power-focusable/dist/index.js:
612
598
  * High-precision focus management utility with full composed tree support.
613
599
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
614
600
  *
615
- * @version 4.3.2
601
+ * @version 4.3.3
616
602
  * @author Yusuke Kamiyamane
617
603
  * @license MIT
618
604
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/portal",
3
- "version": "1.2.14",
3
+ "version": "1.2.15",
4
4
  "description": "Lightweight DOM portal (teleport) utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -47,9 +47,9 @@
47
47
  },
48
48
  "homepage": "https://github.com/y14e/portal#readme",
49
49
  "devDependencies": {
50
- "@y14e/attributes-utils": "^1.1.1",
50
+ "@y14e/attributes-utils": "^1.1.2",
51
51
  "bun-types": "latest",
52
- "power-focusable": "^4.3.2",
52
+ "power-focusable": "^4.3.3",
53
53
  "tsup": "^8.0.0",
54
54
  "typescript": "^5.6.0"
55
55
  },