@y14e/portal 1.2.26 → 1.2.28

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
@@ -13,11 +13,11 @@ npm i @y14e/portal
13
13
  import { createPortal } from '@y14e/portal';
14
14
 
15
15
  // CDNs
16
- import { createPortal } from 'https://esm.sh/@y14e/portal@1.2.26';
16
+ import { createPortal } from 'https://esm.sh/@y14e/portal@1.2.28';
17
17
  // or
18
- import { createPortal } from 'https://cdn.jsdelivr.net/npm/@y14e/portal@1.2.26/+esm';
18
+ import { createPortal } from 'https://cdn.jsdelivr.net/npm/@y14e/portal@1.2.28/+esm';
19
19
  // or
20
- import { createPortal } from 'https://esm.unpkg.com/@y14e/portal@1.2.26';
20
+ import { createPortal } from 'https://esm.unpkg.com/@y14e/portal@1.2.28';
21
21
  ```
22
22
 
23
23
  ## 📦 APIs
@@ -388,7 +388,6 @@ var Portal = class {
388
388
  #exitSentinel;
389
389
  #focusables = /* @__PURE__ */ new Set();
390
390
  #controller = null;
391
- #timer;
392
391
  #isDestroyed = false;
393
392
  constructor(host, container) {
394
393
  this.#host = host;
@@ -408,10 +407,6 @@ var Portal = class {
408
407
  this.#isDestroyed = true;
409
408
  this.#controller?.abort();
410
409
  this.#controller = null;
411
- if (this.#timer !== void 0) {
412
- cancelAnimationFrame(this.#timer);
413
- this.#timer = void 0;
414
- }
415
410
  restoreAttributes([...this.#focusables]);
416
411
  this.#focusables.clear();
417
412
  this.#exitSentinel.after(this.#host);
@@ -426,48 +421,37 @@ var Portal = class {
426
421
  this.#update();
427
422
  this.#controller = new AbortController();
428
423
  const { signal } = this.#controller;
429
- document.addEventListener("focusin", this.#onFocusIn, { signal });
430
- document.addEventListener("keydown", this.#onKeyDown, { signal });
424
+ [this.#entranceSentinel, this.#exitSentinel].forEach((sentinel) => {
425
+ sentinel.addEventListener("focus", this.#onFocus, { signal });
426
+ });
427
+ if (!(this.#host instanceof HTMLElement)) {
428
+ return;
429
+ }
430
+ this.#host.addEventListener("keydown", this.#onKeyDown, { signal });
431
431
  this.#host.setAttribute("data-portaled", "");
432
432
  }
433
- #onFocusIn = (event) => {
434
- const current = event.target;
433
+ #onFocus = (event) => {
434
+ const sentinel = event.target;
435
435
  const previous = event.relatedTarget;
436
436
  if (!(previous instanceof Element)) {
437
437
  return;
438
438
  }
439
- if (current === this.#entranceSentinel) {
439
+ if (sentinel === this.#entranceSentinel) {
440
440
  if (this.#host.contains(previous)) {
441
441
  this.#moveFocus("previous");
442
442
  return;
443
443
  }
444
444
  this.#update();
445
445
  const first = [...this.#focusables][0];
446
- if (first) {
447
- focusElement(first);
448
- } else {
449
- const next = getNextFocusable(document.body, {
450
- anchor: this.#exitSentinel,
451
- composed: true
452
- });
453
- next && focusElement(next);
454
- }
455
- } else if (current === this.#exitSentinel) {
446
+ first ? focusElement(first) : this.#moveFocus("next");
447
+ } else {
456
448
  if (this.#host.contains(previous)) {
457
449
  this.#moveFocus("next");
458
450
  return;
459
451
  }
460
452
  this.#update();
461
453
  const last = [...this.#focusables].at(-1);
462
- if (last) {
463
- focusElement(last);
464
- } else {
465
- const previous2 = getPreviousFocusable(document.body, {
466
- anchor: this.#entranceSentinel,
467
- composed: true
468
- });
469
- previous2 && focusElement(previous2);
470
- }
454
+ last ? focusElement(last) : this.#moveFocus("previous");
471
455
  }
472
456
  };
473
457
  #onKeyDown = (event) => {
@@ -479,9 +463,6 @@ var Portal = class {
479
463
  if (!(active instanceof Element)) {
480
464
  return;
481
465
  }
482
- if (!this.#host.contains(active)) {
483
- return;
484
- }
485
466
  this.#update();
486
467
  const focusables = this.#getFocusables();
487
468
  if (focusables.length) {
@@ -524,13 +505,7 @@ var Portal = class {
524
505
  return sentinel;
525
506
  }
526
507
  #focusSentinel(isPrevious) {
527
- const sentinel = isPrevious ? this.#entranceSentinel : this.#exitSentinel;
528
- if (isPrevious) {
529
- sentinel.focus();
530
- } else {
531
- this.#timer && cancelAnimationFrame(this.#timer);
532
- this.#timer = requestAnimationFrame(() => sentinel.focus());
533
- }
508
+ (isPrevious ? this.#entranceSentinel : this.#exitSentinel).focus();
534
509
  }
535
510
  #getFocusables() {
536
511
  return getFocusables(this.#host, {
@@ -562,7 +537,7 @@ function containsComposed2(container, element) {
562
537
  * Lightweight DOM portal (teleport) utility with fully focus management.
563
538
  * Designed for accessible dialogs, menus, overlays, popovers.
564
539
  *
565
- * @version 1.2.26
540
+ * @version 1.2.28
566
541
  * @author Yusuke Kamiyamane
567
542
  * @license MIT
568
543
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -386,7 +386,6 @@ var Portal = class {
386
386
  #exitSentinel;
387
387
  #focusables = /* @__PURE__ */ new Set();
388
388
  #controller = null;
389
- #timer;
390
389
  #isDestroyed = false;
391
390
  constructor(host, container) {
392
391
  this.#host = host;
@@ -406,10 +405,6 @@ var Portal = class {
406
405
  this.#isDestroyed = true;
407
406
  this.#controller?.abort();
408
407
  this.#controller = null;
409
- if (this.#timer !== void 0) {
410
- cancelAnimationFrame(this.#timer);
411
- this.#timer = void 0;
412
- }
413
408
  restoreAttributes([...this.#focusables]);
414
409
  this.#focusables.clear();
415
410
  this.#exitSentinel.after(this.#host);
@@ -424,48 +419,37 @@ var Portal = class {
424
419
  this.#update();
425
420
  this.#controller = new AbortController();
426
421
  const { signal } = this.#controller;
427
- document.addEventListener("focusin", this.#onFocusIn, { signal });
428
- document.addEventListener("keydown", this.#onKeyDown, { signal });
422
+ [this.#entranceSentinel, this.#exitSentinel].forEach((sentinel) => {
423
+ sentinel.addEventListener("focus", this.#onFocus, { signal });
424
+ });
425
+ if (!(this.#host instanceof HTMLElement)) {
426
+ return;
427
+ }
428
+ this.#host.addEventListener("keydown", this.#onKeyDown, { signal });
429
429
  this.#host.setAttribute("data-portaled", "");
430
430
  }
431
- #onFocusIn = (event) => {
432
- const current = event.target;
431
+ #onFocus = (event) => {
432
+ const sentinel = event.target;
433
433
  const previous = event.relatedTarget;
434
434
  if (!(previous instanceof Element)) {
435
435
  return;
436
436
  }
437
- if (current === this.#entranceSentinel) {
437
+ if (sentinel === this.#entranceSentinel) {
438
438
  if (this.#host.contains(previous)) {
439
439
  this.#moveFocus("previous");
440
440
  return;
441
441
  }
442
442
  this.#update();
443
443
  const first = [...this.#focusables][0];
444
- if (first) {
445
- focusElement(first);
446
- } else {
447
- const next = getNextFocusable(document.body, {
448
- anchor: this.#exitSentinel,
449
- composed: true
450
- });
451
- next && focusElement(next);
452
- }
453
- } else if (current === this.#exitSentinel) {
444
+ first ? focusElement(first) : this.#moveFocus("next");
445
+ } else {
454
446
  if (this.#host.contains(previous)) {
455
447
  this.#moveFocus("next");
456
448
  return;
457
449
  }
458
450
  this.#update();
459
451
  const last = [...this.#focusables].at(-1);
460
- if (last) {
461
- focusElement(last);
462
- } else {
463
- const previous2 = getPreviousFocusable(document.body, {
464
- anchor: this.#entranceSentinel,
465
- composed: true
466
- });
467
- previous2 && focusElement(previous2);
468
- }
452
+ last ? focusElement(last) : this.#moveFocus("previous");
469
453
  }
470
454
  };
471
455
  #onKeyDown = (event) => {
@@ -477,9 +461,6 @@ var Portal = class {
477
461
  if (!(active instanceof Element)) {
478
462
  return;
479
463
  }
480
- if (!this.#host.contains(active)) {
481
- return;
482
- }
483
464
  this.#update();
484
465
  const focusables = this.#getFocusables();
485
466
  if (focusables.length) {
@@ -522,13 +503,7 @@ var Portal = class {
522
503
  return sentinel;
523
504
  }
524
505
  #focusSentinel(isPrevious) {
525
- const sentinel = isPrevious ? this.#entranceSentinel : this.#exitSentinel;
526
- if (isPrevious) {
527
- sentinel.focus();
528
- } else {
529
- this.#timer && cancelAnimationFrame(this.#timer);
530
- this.#timer = requestAnimationFrame(() => sentinel.focus());
531
- }
506
+ (isPrevious ? this.#entranceSentinel : this.#exitSentinel).focus();
532
507
  }
533
508
  #getFocusables() {
534
509
  return getFocusables(this.#host, {
@@ -560,7 +535,7 @@ function containsComposed2(container, element) {
560
535
  * Lightweight DOM portal (teleport) utility with fully focus management.
561
536
  * Designed for accessible dialogs, menus, overlays, popovers.
562
537
  *
563
- * @version 1.2.26
538
+ * @version 1.2.28
564
539
  * @author Yusuke Kamiyamane
565
540
  * @license MIT
566
541
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.cjs CHANGED
@@ -31,7 +31,6 @@ var Portal = class {
31
31
  #exitSentinel;
32
32
  #focusables = /* @__PURE__ */ new Set();
33
33
  #controller = null;
34
- #timer;
35
34
  #isDestroyed = false;
36
35
  constructor(host, container) {
37
36
  this.#host = host;
@@ -51,10 +50,6 @@ var Portal = class {
51
50
  this.#isDestroyed = true;
52
51
  this.#controller?.abort();
53
52
  this.#controller = null;
54
- if (this.#timer !== void 0) {
55
- cancelAnimationFrame(this.#timer);
56
- this.#timer = void 0;
57
- }
58
53
  attributesUtils.restoreAttributes([...this.#focusables]);
59
54
  this.#focusables.clear();
60
55
  this.#exitSentinel.after(this.#host);
@@ -69,48 +64,37 @@ var Portal = class {
69
64
  this.#update();
70
65
  this.#controller = new AbortController();
71
66
  const { signal } = this.#controller;
72
- document.addEventListener("focusin", this.#onFocusIn, { signal });
73
- document.addEventListener("keydown", this.#onKeyDown, { signal });
67
+ [this.#entranceSentinel, this.#exitSentinel].forEach((sentinel) => {
68
+ sentinel.addEventListener("focus", this.#onFocus, { signal });
69
+ });
70
+ if (!(this.#host instanceof HTMLElement)) {
71
+ return;
72
+ }
73
+ this.#host.addEventListener("keydown", this.#onKeyDown, { signal });
74
74
  this.#host.setAttribute("data-portaled", "");
75
75
  }
76
- #onFocusIn = (event) => {
77
- const current = event.target;
76
+ #onFocus = (event) => {
77
+ const sentinel = event.target;
78
78
  const previous = event.relatedTarget;
79
79
  if (!(previous instanceof Element)) {
80
80
  return;
81
81
  }
82
- if (current === this.#entranceSentinel) {
82
+ if (sentinel === this.#entranceSentinel) {
83
83
  if (this.#host.contains(previous)) {
84
84
  this.#moveFocus("previous");
85
85
  return;
86
86
  }
87
87
  this.#update();
88
88
  const first = [...this.#focusables][0];
89
- if (first) {
90
- powerFocusable.focusElement(first);
91
- } else {
92
- const next = powerFocusable.getNextFocusable(document.body, {
93
- anchor: this.#exitSentinel,
94
- composed: true
95
- });
96
- next && powerFocusable.focusElement(next);
97
- }
98
- } else if (current === this.#exitSentinel) {
89
+ first ? powerFocusable.focusElement(first) : this.#moveFocus("next");
90
+ } else {
99
91
  if (this.#host.contains(previous)) {
100
92
  this.#moveFocus("next");
101
93
  return;
102
94
  }
103
95
  this.#update();
104
96
  const last = [...this.#focusables].at(-1);
105
- if (last) {
106
- powerFocusable.focusElement(last);
107
- } else {
108
- const previous2 = powerFocusable.getPreviousFocusable(document.body, {
109
- anchor: this.#entranceSentinel,
110
- composed: true
111
- });
112
- previous2 && powerFocusable.focusElement(previous2);
113
- }
97
+ last ? powerFocusable.focusElement(last) : this.#moveFocus("previous");
114
98
  }
115
99
  };
116
100
  #onKeyDown = (event) => {
@@ -122,9 +106,6 @@ var Portal = class {
122
106
  if (!(active instanceof Element)) {
123
107
  return;
124
108
  }
125
- if (!this.#host.contains(active)) {
126
- return;
127
- }
128
109
  this.#update();
129
110
  const focusables = this.#getFocusables();
130
111
  if (focusables.length) {
@@ -167,13 +148,7 @@ var Portal = class {
167
148
  return sentinel;
168
149
  }
169
150
  #focusSentinel(isPrevious) {
170
- const sentinel = isPrevious ? this.#entranceSentinel : this.#exitSentinel;
171
- if (isPrevious) {
172
- sentinel.focus();
173
- } else {
174
- this.#timer && cancelAnimationFrame(this.#timer);
175
- this.#timer = requestAnimationFrame(() => sentinel.focus());
176
- }
151
+ (isPrevious ? this.#entranceSentinel : this.#exitSentinel).focus();
177
152
  }
178
153
  #getFocusables() {
179
154
  return powerFocusable.getFocusables(this.#host, {
@@ -205,7 +180,7 @@ function containsComposed(container, element) {
205
180
  * Lightweight DOM portal (teleport) utility with fully focus management.
206
181
  * Designed for accessible dialogs, menus, overlays, popovers.
207
182
  *
208
- * @version 1.2.26
183
+ * @version 1.2.28
209
184
  * @author Yusuke Kamiyamane
210
185
  * @license MIT
211
186
  * @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.26
6
+ * @version 1.2.28
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.26
6
+ * @version 1.2.28
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { restoreAttributes, saveAttributes } from '@y14e/attributes-utils';
2
- import { focusElement, getNextFocusable, getPreviousFocusable, getActiveElement, getFocusables } from 'power-focusable';
2
+ import { focusElement, getActiveElement, getFocusables, getPreviousFocusable, getNextFocusable } from 'power-focusable';
3
3
 
4
4
  // src/index.ts
5
5
  var VISUALLY_HIDDEN_CSS = `border: 0; clip: rect(0, 0, 0, 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; user-select: none; white-space: nowrap; width: 1px;`;
@@ -29,7 +29,6 @@ var Portal = class {
29
29
  #exitSentinel;
30
30
  #focusables = /* @__PURE__ */ new Set();
31
31
  #controller = null;
32
- #timer;
33
32
  #isDestroyed = false;
34
33
  constructor(host, container) {
35
34
  this.#host = host;
@@ -49,10 +48,6 @@ var Portal = class {
49
48
  this.#isDestroyed = true;
50
49
  this.#controller?.abort();
51
50
  this.#controller = null;
52
- if (this.#timer !== void 0) {
53
- cancelAnimationFrame(this.#timer);
54
- this.#timer = void 0;
55
- }
56
51
  restoreAttributes([...this.#focusables]);
57
52
  this.#focusables.clear();
58
53
  this.#exitSentinel.after(this.#host);
@@ -67,48 +62,37 @@ var Portal = class {
67
62
  this.#update();
68
63
  this.#controller = new AbortController();
69
64
  const { signal } = this.#controller;
70
- document.addEventListener("focusin", this.#onFocusIn, { signal });
71
- document.addEventListener("keydown", this.#onKeyDown, { signal });
65
+ [this.#entranceSentinel, this.#exitSentinel].forEach((sentinel) => {
66
+ sentinel.addEventListener("focus", this.#onFocus, { signal });
67
+ });
68
+ if (!(this.#host instanceof HTMLElement)) {
69
+ return;
70
+ }
71
+ this.#host.addEventListener("keydown", this.#onKeyDown, { signal });
72
72
  this.#host.setAttribute("data-portaled", "");
73
73
  }
74
- #onFocusIn = (event) => {
75
- const current = event.target;
74
+ #onFocus = (event) => {
75
+ const sentinel = event.target;
76
76
  const previous = event.relatedTarget;
77
77
  if (!(previous instanceof Element)) {
78
78
  return;
79
79
  }
80
- if (current === this.#entranceSentinel) {
80
+ if (sentinel === this.#entranceSentinel) {
81
81
  if (this.#host.contains(previous)) {
82
82
  this.#moveFocus("previous");
83
83
  return;
84
84
  }
85
85
  this.#update();
86
86
  const first = [...this.#focusables][0];
87
- if (first) {
88
- focusElement(first);
89
- } else {
90
- const next = getNextFocusable(document.body, {
91
- anchor: this.#exitSentinel,
92
- composed: true
93
- });
94
- next && focusElement(next);
95
- }
96
- } else if (current === this.#exitSentinel) {
87
+ first ? focusElement(first) : this.#moveFocus("next");
88
+ } else {
97
89
  if (this.#host.contains(previous)) {
98
90
  this.#moveFocus("next");
99
91
  return;
100
92
  }
101
93
  this.#update();
102
94
  const last = [...this.#focusables].at(-1);
103
- if (last) {
104
- focusElement(last);
105
- } else {
106
- const previous2 = getPreviousFocusable(document.body, {
107
- anchor: this.#entranceSentinel,
108
- composed: true
109
- });
110
- previous2 && focusElement(previous2);
111
- }
95
+ last ? focusElement(last) : this.#moveFocus("previous");
112
96
  }
113
97
  };
114
98
  #onKeyDown = (event) => {
@@ -120,9 +104,6 @@ var Portal = class {
120
104
  if (!(active instanceof Element)) {
121
105
  return;
122
106
  }
123
- if (!this.#host.contains(active)) {
124
- return;
125
- }
126
107
  this.#update();
127
108
  const focusables = this.#getFocusables();
128
109
  if (focusables.length) {
@@ -165,13 +146,7 @@ var Portal = class {
165
146
  return sentinel;
166
147
  }
167
148
  #focusSentinel(isPrevious) {
168
- const sentinel = isPrevious ? this.#entranceSentinel : this.#exitSentinel;
169
- if (isPrevious) {
170
- sentinel.focus();
171
- } else {
172
- this.#timer && cancelAnimationFrame(this.#timer);
173
- this.#timer = requestAnimationFrame(() => sentinel.focus());
174
- }
149
+ (isPrevious ? this.#entranceSentinel : this.#exitSentinel).focus();
175
150
  }
176
151
  #getFocusables() {
177
152
  return getFocusables(this.#host, {
@@ -203,7 +178,7 @@ function containsComposed(container, element) {
203
178
  * Lightweight DOM portal (teleport) utility with fully focus management.
204
179
  * Designed for accessible dialogs, menus, overlays, popovers.
205
180
  *
206
- * @version 1.2.26
181
+ * @version 1.2.28
207
182
  * @author Yusuke Kamiyamane
208
183
  * @license MIT
209
184
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/portal",
3
- "version": "1.2.26",
3
+ "version": "1.2.28",
4
4
  "description": "Lightweight DOM portal (teleport) utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",