@y14e/portal 1.2.3 → 1.2.5

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
@@ -1,5 +1,36 @@
1
1
  'use strict';
2
2
 
3
+ // node_modules/@y14e/attributes-utils/dist/index.js
4
+ var snapshots = /* @__PURE__ */ new WeakMap();
5
+ function restoreAttributes(elements) {
6
+ for (const element of elements) {
7
+ const snapshot = snapshots.get(element);
8
+ if (!snapshot) {
9
+ continue;
10
+ }
11
+ for (const [attribute, value] of snapshot.entries()) {
12
+ if (value === null) {
13
+ element.removeAttribute(attribute);
14
+ } else {
15
+ element.setAttribute(attribute, value);
16
+ }
17
+ }
18
+ snapshots.delete(element);
19
+ }
20
+ }
21
+ function saveAttributes(elements, attributes) {
22
+ elements.forEach((element) => {
23
+ let snapshot = snapshots.get(element);
24
+ if (!snapshot) {
25
+ snapshot = /* @__PURE__ */ new Map();
26
+ snapshots.set(element, snapshot);
27
+ }
28
+ attributes.forEach((attribute) => {
29
+ snapshot.set(attribute, element.getAttribute(attribute));
30
+ });
31
+ });
32
+ }
33
+
3
34
  // node_modules/power-focusable/dist/index.js
4
35
  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
36
  function getFocusables(container = document.body, options = {}) {
@@ -7,14 +38,17 @@ function getFocusables(container = document.body, options = {}) {
7
38
  console.warn("Invalid container element. Fallback: <body> element.");
8
39
  container = document.body;
9
40
  }
10
- const { composed = false } = options;
11
- let { filter, include } = options;
12
- if (filter && typeof filter !== "function") {
13
- console.warn("Invalid filter function");
41
+ let { composed = false, filter, include } = options;
42
+ if (typeof composed !== "boolean") {
43
+ console.warn("Invalid composed. Fallback: false.");
44
+ composed = false;
45
+ }
46
+ if (typeof filter !== "undefined" && typeof filter !== "function") {
47
+ console.warn("Invalid filter. Fallback: no filter function (undefined).");
14
48
  filter = void 0;
15
49
  }
16
- if (include && typeof include !== "function") {
17
- console.warn("Invalid include function");
50
+ if (typeof include !== "undefined" && typeof include !== "function") {
51
+ console.warn("Invalid include. Fallback: no include function (undefined).");
18
52
  include = void 0;
19
53
  }
20
54
  const elements = [];
@@ -51,17 +85,9 @@ function getFocusables(container = document.body, options = {}) {
51
85
  return filter ? unfiltered.filter(filter) : unfiltered;
52
86
  }
53
87
  function getNextFocusable(container = document.body, options = {}) {
54
- if (!(container instanceof Element)) {
55
- console.warn("Invalid container element. Fallback: <body> element.");
56
- container = document.body;
57
- }
58
88
  return getRelativeFocusable(container, 1, options);
59
89
  }
60
90
  function getPreviousFocusable(container = document.body, options = {}) {
61
- if (!(container instanceof Element)) {
62
- console.warn("Invalid container element. Fallback: <body> element.");
63
- container = document.body;
64
- }
65
91
  return getRelativeFocusable(container, -1, options);
66
92
  }
67
93
  function isFocusable(element) {
@@ -91,8 +117,17 @@ function isFocusable(element) {
91
117
  return true;
92
118
  }
93
119
  function getRelativeFocusable(container, offset, options) {
94
- let anchor = options.anchor ?? getActiveElement();
95
- const { composed = false, filter, include, wrap = false } = options;
120
+ if (!(container instanceof Element)) {
121
+ console.warn("Invalid container element. Fallback: <body> element.");
122
+ container = document.body;
123
+ }
124
+ let {
125
+ anchor = getActiveElement(),
126
+ composed = false,
127
+ filter,
128
+ include,
129
+ wrap = false
130
+ } = options;
96
131
  if (!(anchor instanceof Element)) {
97
132
  const active = getActiveElement();
98
133
  if (active instanceof Element) {
@@ -107,6 +142,22 @@ function getRelativeFocusable(container, offset, options) {
107
142
  console.warn("Anchor (active) element not within container");
108
143
  return null;
109
144
  }
145
+ if (typeof composed !== "boolean") {
146
+ console.warn("Invalid composed. Fallback: false.");
147
+ composed = false;
148
+ }
149
+ if (typeof filter !== "undefined" && typeof filter !== "function") {
150
+ console.warn("Invalid filter. Fallback: no filter function (undefined).");
151
+ filter = void 0;
152
+ }
153
+ if (typeof include !== "undefined" && typeof include !== "function") {
154
+ console.warn("Invalid include. Fallback: no include function (undefined).");
155
+ include = void 0;
156
+ }
157
+ if (typeof wrap !== "boolean") {
158
+ console.warn("Invalid wrap. Fallback: false.");
159
+ wrap = false;
160
+ }
110
161
  const focusables = getFocusables(container, { composed, filter, include });
111
162
  const { length } = focusables;
112
163
  if (!length) {
@@ -296,7 +347,6 @@ var Portal = class {
296
347
  #entranceSentinel;
297
348
  #exitSentinel;
298
349
  #focusables = /* @__PURE__ */ new Set();
299
- #tabIndexes = /* @__PURE__ */ new Map();
300
350
  #controller = null;
301
351
  #isDestroyed = false;
302
352
  constructor(host, container) {
@@ -313,16 +363,8 @@ var Portal = class {
313
363
  this.#isDestroyed = true;
314
364
  this.#controller?.abort();
315
365
  this.#controller = null;
316
- this.#focusables.forEach((focusable) => {
317
- const index = this.#tabIndexes.get(focusable);
318
- if (index == null) {
319
- focusable.removeAttribute("tabindex");
320
- } else {
321
- focusable.setAttribute("tabindex", index);
322
- }
323
- });
366
+ restoreAttributes([...this.#focusables]);
324
367
  this.#focusables.clear();
325
- this.#tabIndexes.clear();
326
368
  this.#exitSentinel.after(this.#host);
327
369
  this.#entranceSentinel.remove();
328
370
  this.#exitSentinel.remove();
@@ -405,29 +447,23 @@ var Portal = class {
405
447
  ...this.#getFocusables(),
406
448
  ...getFocusables(this.#host, { composed: true })
407
449
  ]);
408
- this.#focusables.forEach((focusable) => {
450
+ for (const focusable of this.#focusables) {
409
451
  if (current.has(focusable)) {
410
- return;
452
+ continue;
411
453
  }
412
454
  if (focusable.isConnected) {
413
- const index = this.#tabIndexes.get(focusable);
414
- if (index == null) {
415
- focusable.removeAttribute("tabindex");
416
- } else {
417
- focusable.setAttribute("tabindex", index);
418
- }
455
+ restoreAttributes([focusable]);
419
456
  }
420
457
  this.#focusables.delete(focusable);
421
- this.#tabIndexes.delete(focusable);
422
- });
423
- current.forEach((c) => {
458
+ }
459
+ for (const c of current) {
424
460
  if (this.#focusables.has(c)) {
425
- return;
461
+ continue;
426
462
  }
427
463
  this.#focusables.add(c);
428
- this.#tabIndexes.set(c, c.getAttribute("tabindex"));
464
+ saveAttributes([c], ["tabindex"]);
429
465
  c.setAttribute("tabindex", "-1");
430
- });
466
+ }
431
467
  }
432
468
  #createSentinel() {
433
469
  const sentinel = document.createElement("span");
@@ -482,7 +518,7 @@ function getActiveElement2() {
482
518
  * Lightweight DOM portal (teleport) utility with fully focus management.
483
519
  * Designed for accessible dialogs, menus, overlays, popovers.
484
520
  *
485
- * @version 1.2.3
521
+ * @version 1.2.5
486
522
  * @author Yusuke Kamiyamane
487
523
  * @license MIT
488
524
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -490,13 +526,24 @@ function getActiveElement2() {
490
526
  */
491
527
  /*! Bundled license information:
492
528
 
529
+ @y14e/attributes-utils/dist/index.js:
530
+ (**
531
+ * Attributes Utils
532
+ *
533
+ * @version 1.0.2
534
+ * @author Yusuke Kamiyamane
535
+ * @license MIT
536
+ * @copyright Copyright (c) Yusuke Kamiyamane
537
+ * @see {@link https://github.com/y14e/attributes-utils}
538
+ *)
539
+
493
540
  power-focusable/dist/index.js:
494
541
  (**
495
542
  * Power Focusable
496
543
  * High-precision focus management utility with full composed tree support.
497
544
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
498
545
  *
499
- * @version 4.1.5
546
+ * @version 4.1.7
500
547
  * @author Yusuke Kamiyamane
501
548
  * @license MIT
502
549
  * @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.3
6
+ * @version 1.2.5
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.3
6
+ * @version 1.2.5
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -1,3 +1,34 @@
1
+ // node_modules/@y14e/attributes-utils/dist/index.js
2
+ var snapshots = /* @__PURE__ */ new WeakMap();
3
+ function restoreAttributes(elements) {
4
+ for (const element of elements) {
5
+ const snapshot = snapshots.get(element);
6
+ if (!snapshot) {
7
+ continue;
8
+ }
9
+ for (const [attribute, value] of snapshot.entries()) {
10
+ if (value === null) {
11
+ element.removeAttribute(attribute);
12
+ } else {
13
+ element.setAttribute(attribute, value);
14
+ }
15
+ }
16
+ snapshots.delete(element);
17
+ }
18
+ }
19
+ function saveAttributes(elements, attributes) {
20
+ elements.forEach((element) => {
21
+ let snapshot = snapshots.get(element);
22
+ if (!snapshot) {
23
+ snapshot = /* @__PURE__ */ new Map();
24
+ snapshots.set(element, snapshot);
25
+ }
26
+ attributes.forEach((attribute) => {
27
+ snapshot.set(attribute, element.getAttribute(attribute));
28
+ });
29
+ });
30
+ }
31
+
1
32
  // node_modules/power-focusable/dist/index.js
2
33
  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
34
  function getFocusables(container = document.body, options = {}) {
@@ -5,14 +36,17 @@ function getFocusables(container = document.body, options = {}) {
5
36
  console.warn("Invalid container element. Fallback: <body> element.");
6
37
  container = document.body;
7
38
  }
8
- const { composed = false } = options;
9
- let { filter, include } = options;
10
- if (filter && typeof filter !== "function") {
11
- console.warn("Invalid filter function");
39
+ let { composed = false, filter, include } = options;
40
+ if (typeof composed !== "boolean") {
41
+ console.warn("Invalid composed. Fallback: false.");
42
+ composed = false;
43
+ }
44
+ if (typeof filter !== "undefined" && typeof filter !== "function") {
45
+ console.warn("Invalid filter. Fallback: no filter function (undefined).");
12
46
  filter = void 0;
13
47
  }
14
- if (include && typeof include !== "function") {
15
- console.warn("Invalid include function");
48
+ if (typeof include !== "undefined" && typeof include !== "function") {
49
+ console.warn("Invalid include. Fallback: no include function (undefined).");
16
50
  include = void 0;
17
51
  }
18
52
  const elements = [];
@@ -49,17 +83,9 @@ function getFocusables(container = document.body, options = {}) {
49
83
  return filter ? unfiltered.filter(filter) : unfiltered;
50
84
  }
51
85
  function getNextFocusable(container = document.body, options = {}) {
52
- if (!(container instanceof Element)) {
53
- console.warn("Invalid container element. Fallback: <body> element.");
54
- container = document.body;
55
- }
56
86
  return getRelativeFocusable(container, 1, options);
57
87
  }
58
88
  function getPreviousFocusable(container = document.body, options = {}) {
59
- if (!(container instanceof Element)) {
60
- console.warn("Invalid container element. Fallback: <body> element.");
61
- container = document.body;
62
- }
63
89
  return getRelativeFocusable(container, -1, options);
64
90
  }
65
91
  function isFocusable(element) {
@@ -89,8 +115,17 @@ function isFocusable(element) {
89
115
  return true;
90
116
  }
91
117
  function getRelativeFocusable(container, offset, options) {
92
- let anchor = options.anchor ?? getActiveElement();
93
- const { composed = false, filter, include, wrap = false } = options;
118
+ if (!(container instanceof Element)) {
119
+ console.warn("Invalid container element. Fallback: <body> element.");
120
+ container = document.body;
121
+ }
122
+ let {
123
+ anchor = getActiveElement(),
124
+ composed = false,
125
+ filter,
126
+ include,
127
+ wrap = false
128
+ } = options;
94
129
  if (!(anchor instanceof Element)) {
95
130
  const active = getActiveElement();
96
131
  if (active instanceof Element) {
@@ -105,6 +140,22 @@ function getRelativeFocusable(container, offset, options) {
105
140
  console.warn("Anchor (active) element not within container");
106
141
  return null;
107
142
  }
143
+ if (typeof composed !== "boolean") {
144
+ console.warn("Invalid composed. Fallback: false.");
145
+ composed = false;
146
+ }
147
+ if (typeof filter !== "undefined" && typeof filter !== "function") {
148
+ console.warn("Invalid filter. Fallback: no filter function (undefined).");
149
+ filter = void 0;
150
+ }
151
+ if (typeof include !== "undefined" && typeof include !== "function") {
152
+ console.warn("Invalid include. Fallback: no include function (undefined).");
153
+ include = void 0;
154
+ }
155
+ if (typeof wrap !== "boolean") {
156
+ console.warn("Invalid wrap. Fallback: false.");
157
+ wrap = false;
158
+ }
108
159
  const focusables = getFocusables(container, { composed, filter, include });
109
160
  const { length } = focusables;
110
161
  if (!length) {
@@ -294,7 +345,6 @@ var Portal = class {
294
345
  #entranceSentinel;
295
346
  #exitSentinel;
296
347
  #focusables = /* @__PURE__ */ new Set();
297
- #tabIndexes = /* @__PURE__ */ new Map();
298
348
  #controller = null;
299
349
  #isDestroyed = false;
300
350
  constructor(host, container) {
@@ -311,16 +361,8 @@ var Portal = class {
311
361
  this.#isDestroyed = true;
312
362
  this.#controller?.abort();
313
363
  this.#controller = null;
314
- this.#focusables.forEach((focusable) => {
315
- const index = this.#tabIndexes.get(focusable);
316
- if (index == null) {
317
- focusable.removeAttribute("tabindex");
318
- } else {
319
- focusable.setAttribute("tabindex", index);
320
- }
321
- });
364
+ restoreAttributes([...this.#focusables]);
322
365
  this.#focusables.clear();
323
- this.#tabIndexes.clear();
324
366
  this.#exitSentinel.after(this.#host);
325
367
  this.#entranceSentinel.remove();
326
368
  this.#exitSentinel.remove();
@@ -403,29 +445,23 @@ var Portal = class {
403
445
  ...this.#getFocusables(),
404
446
  ...getFocusables(this.#host, { composed: true })
405
447
  ]);
406
- this.#focusables.forEach((focusable) => {
448
+ for (const focusable of this.#focusables) {
407
449
  if (current.has(focusable)) {
408
- return;
450
+ continue;
409
451
  }
410
452
  if (focusable.isConnected) {
411
- const index = this.#tabIndexes.get(focusable);
412
- if (index == null) {
413
- focusable.removeAttribute("tabindex");
414
- } else {
415
- focusable.setAttribute("tabindex", index);
416
- }
453
+ restoreAttributes([focusable]);
417
454
  }
418
455
  this.#focusables.delete(focusable);
419
- this.#tabIndexes.delete(focusable);
420
- });
421
- current.forEach((c) => {
456
+ }
457
+ for (const c of current) {
422
458
  if (this.#focusables.has(c)) {
423
- return;
459
+ continue;
424
460
  }
425
461
  this.#focusables.add(c);
426
- this.#tabIndexes.set(c, c.getAttribute("tabindex"));
462
+ saveAttributes([c], ["tabindex"]);
427
463
  c.setAttribute("tabindex", "-1");
428
- });
464
+ }
429
465
  }
430
466
  #createSentinel() {
431
467
  const sentinel = document.createElement("span");
@@ -480,7 +516,7 @@ function getActiveElement2() {
480
516
  * Lightweight DOM portal (teleport) utility with fully focus management.
481
517
  * Designed for accessible dialogs, menus, overlays, popovers.
482
518
  *
483
- * @version 1.2.3
519
+ * @version 1.2.5
484
520
  * @author Yusuke Kamiyamane
485
521
  * @license MIT
486
522
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -488,13 +524,24 @@ function getActiveElement2() {
488
524
  */
489
525
  /*! Bundled license information:
490
526
 
527
+ @y14e/attributes-utils/dist/index.js:
528
+ (**
529
+ * Attributes Utils
530
+ *
531
+ * @version 1.0.2
532
+ * @author Yusuke Kamiyamane
533
+ * @license MIT
534
+ * @copyright Copyright (c) Yusuke Kamiyamane
535
+ * @see {@link https://github.com/y14e/attributes-utils}
536
+ *)
537
+
491
538
  power-focusable/dist/index.js:
492
539
  (**
493
540
  * Power Focusable
494
541
  * High-precision focus management utility with full composed tree support.
495
542
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
496
543
  *
497
- * @version 4.1.5
544
+ * @version 4.1.7
498
545
  * @author Yusuke Kamiyamane
499
546
  * @license MIT
500
547
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/portal",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "Lightweight DOM portal (teleport) utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -47,8 +47,9 @@
47
47
  },
48
48
  "homepage": "https://github.com/y14e/portal#readme",
49
49
  "devDependencies": {
50
+ "@y14e/attributes-utils": "^1.0.2",
50
51
  "bun-types": "latest",
51
- "power-focusable": "^4.1.5",
52
+ "power-focusable": "^4.1.7",
52
53
  "tsup": "^8.0.0",
53
54
  "typescript": "^5.6.0"
54
55
  },