@y14e/portal 1.2.14 → 1.2.16

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.16';
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.16';
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.16/+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.16';
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
  }
@@ -466,9 +455,7 @@ var Portal = class {
466
455
  });
467
456
  next && focusElement(next);
468
457
  }
469
- return;
470
- }
471
- if (current === this.#exitSentinel) {
458
+ } else if (current === this.#exitSentinel) {
472
459
  if (this.#host.contains(before)) {
473
460
  this.#moveFocus("next");
474
461
  return;
@@ -484,7 +471,6 @@ var Portal = class {
484
471
  });
485
472
  previous && focusElement(previous);
486
473
  }
487
- return;
488
474
  }
489
475
  };
490
476
  #onKeyDown = (event) => {
@@ -501,18 +487,17 @@ var Portal = class {
501
487
  }
502
488
  this.#update();
503
489
  const focusables = this.#getFocusables();
504
- if (!focusables.length) {
490
+ if (focusables.length) {
491
+ const index = focusables.indexOf(active);
492
+ if (index !== -1) {
493
+ event.preventDefault();
494
+ const focusable = focusables[index + (shiftKey ? -1 : 1)];
495
+ focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
496
+ }
497
+ } else {
505
498
  event.preventDefault();
506
499
  this.#moveFocus(shiftKey ? "previous" : "next");
507
- return;
508
- }
509
- const index = focusables.indexOf(active);
510
- if (index === -1) {
511
- return;
512
500
  }
513
- event.preventDefault();
514
- const focusable = focusables[index + (shiftKey ? -1 : 1)];
515
- focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
516
501
  };
517
502
  #update() {
518
503
  const current = /* @__PURE__ */ new Set([
@@ -520,19 +505,17 @@ var Portal = class {
520
505
  ...getFocusables(this.#host, { composed: true })
521
506
  ]);
522
507
  for (const focusable of this.#focusables) {
523
- if (current.has(focusable)) {
524
- continue;
508
+ if (!current.has(focusable)) {
509
+ focusable.isConnected && restoreAttributes([focusable]);
510
+ this.#focusables.delete(focusable);
525
511
  }
526
- focusable.isConnected && restoreAttributes([focusable]);
527
- this.#focusables.delete(focusable);
528
512
  }
529
513
  for (const focusable of current) {
530
- if (this.#focusables.has(focusable)) {
531
- continue;
514
+ if (!this.#focusables.has(focusable)) {
515
+ this.#focusables.add(focusable);
516
+ saveAttributes([focusable], ["tabindex"]);
517
+ focusable.setAttribute("tabindex", "-1");
532
518
  }
533
- this.#focusables.add(focusable);
534
- saveAttributes([focusable], ["tabindex"]);
535
- focusable.setAttribute("tabindex", "-1");
536
519
  }
537
520
  }
538
521
  #createSentinel() {
@@ -589,7 +572,7 @@ function getActiveElement2() {
589
572
  * Lightweight DOM portal (teleport) utility with fully focus management.
590
573
  * Designed for accessible dialogs, menus, overlays, popovers.
591
574
  *
592
- * @version 1.2.14
575
+ * @version 1.2.16
593
576
  * @author Yusuke Kamiyamane
594
577
  * @license MIT
595
578
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -601,7 +584,7 @@ function getActiveElement2() {
601
584
  (**
602
585
  * Attributes Utils
603
586
  *
604
- * @version 1.1.1
587
+ * @version 1.1.2
605
588
  * @author Yusuke Kamiyamane
606
589
  * @license MIT
607
590
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -614,7 +597,7 @@ power-focusable/dist/index.js:
614
597
  * High-precision focus management utility with full composed tree support.
615
598
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
616
599
  *
617
- * @version 4.3.2
600
+ * @version 4.3.3
618
601
  * @author Yusuke Kamiyamane
619
602
  * @license MIT
620
603
  * @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.16
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.16
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
  }
@@ -464,9 +453,7 @@ var Portal = class {
464
453
  });
465
454
  next && focusElement(next);
466
455
  }
467
- return;
468
- }
469
- if (current === this.#exitSentinel) {
456
+ } else if (current === this.#exitSentinel) {
470
457
  if (this.#host.contains(before)) {
471
458
  this.#moveFocus("next");
472
459
  return;
@@ -482,7 +469,6 @@ var Portal = class {
482
469
  });
483
470
  previous && focusElement(previous);
484
471
  }
485
- return;
486
472
  }
487
473
  };
488
474
  #onKeyDown = (event) => {
@@ -499,18 +485,17 @@ var Portal = class {
499
485
  }
500
486
  this.#update();
501
487
  const focusables = this.#getFocusables();
502
- if (!focusables.length) {
488
+ if (focusables.length) {
489
+ const index = focusables.indexOf(active);
490
+ if (index !== -1) {
491
+ event.preventDefault();
492
+ const focusable = focusables[index + (shiftKey ? -1 : 1)];
493
+ focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
494
+ }
495
+ } else {
503
496
  event.preventDefault();
504
497
  this.#moveFocus(shiftKey ? "previous" : "next");
505
- return;
506
- }
507
- const index = focusables.indexOf(active);
508
- if (index === -1) {
509
- return;
510
498
  }
511
- event.preventDefault();
512
- const focusable = focusables[index + (shiftKey ? -1 : 1)];
513
- focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
514
499
  };
515
500
  #update() {
516
501
  const current = /* @__PURE__ */ new Set([
@@ -518,19 +503,17 @@ var Portal = class {
518
503
  ...getFocusables(this.#host, { composed: true })
519
504
  ]);
520
505
  for (const focusable of this.#focusables) {
521
- if (current.has(focusable)) {
522
- continue;
506
+ if (!current.has(focusable)) {
507
+ focusable.isConnected && restoreAttributes([focusable]);
508
+ this.#focusables.delete(focusable);
523
509
  }
524
- focusable.isConnected && restoreAttributes([focusable]);
525
- this.#focusables.delete(focusable);
526
510
  }
527
511
  for (const focusable of current) {
528
- if (this.#focusables.has(focusable)) {
529
- continue;
512
+ if (!this.#focusables.has(focusable)) {
513
+ this.#focusables.add(focusable);
514
+ saveAttributes([focusable], ["tabindex"]);
515
+ focusable.setAttribute("tabindex", "-1");
530
516
  }
531
- this.#focusables.add(focusable);
532
- saveAttributes([focusable], ["tabindex"]);
533
- focusable.setAttribute("tabindex", "-1");
534
517
  }
535
518
  }
536
519
  #createSentinel() {
@@ -587,7 +570,7 @@ function getActiveElement2() {
587
570
  * Lightweight DOM portal (teleport) utility with fully focus management.
588
571
  * Designed for accessible dialogs, menus, overlays, popovers.
589
572
  *
590
- * @version 1.2.14
573
+ * @version 1.2.16
591
574
  * @author Yusuke Kamiyamane
592
575
  * @license MIT
593
576
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -599,7 +582,7 @@ function getActiveElement2() {
599
582
  (**
600
583
  * Attributes Utils
601
584
  *
602
- * @version 1.1.1
585
+ * @version 1.1.2
603
586
  * @author Yusuke Kamiyamane
604
587
  * @license MIT
605
588
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -612,7 +595,7 @@ power-focusable/dist/index.js:
612
595
  * High-precision focus management utility with full composed tree support.
613
596
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
614
597
  *
615
- * @version 4.3.2
598
+ * @version 4.3.3
616
599
  * @author Yusuke Kamiyamane
617
600
  * @license MIT
618
601
  * @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.16",
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
  },