@y14e/portal 1.2.13 → 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 +4 -4
- package/dist/index.cjs +51 -49
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +51 -49
- package/package.json +3 -3
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
284
|
-
|
|
285
|
-
|
|
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 (
|
|
296
|
-
|
|
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
|
}
|
|
@@ -457,7 +446,15 @@ var Portal = class {
|
|
|
457
446
|
}
|
|
458
447
|
this.#update();
|
|
459
448
|
const first = [...this.#focusables][0];
|
|
460
|
-
|
|
449
|
+
if (first) {
|
|
450
|
+
focusElement(first);
|
|
451
|
+
} else {
|
|
452
|
+
const next = getNextFocusable(document.body, {
|
|
453
|
+
anchor: this.#exitSentinel,
|
|
454
|
+
composed: true
|
|
455
|
+
});
|
|
456
|
+
next && focusElement(next);
|
|
457
|
+
}
|
|
461
458
|
return;
|
|
462
459
|
}
|
|
463
460
|
if (current === this.#exitSentinel) {
|
|
@@ -467,7 +464,15 @@ var Portal = class {
|
|
|
467
464
|
}
|
|
468
465
|
this.#update();
|
|
469
466
|
const last = [...this.#focusables].at(-1);
|
|
470
|
-
|
|
467
|
+
if (last) {
|
|
468
|
+
focusElement(last);
|
|
469
|
+
} else {
|
|
470
|
+
const previous = getPreviousFocusable(document.body, {
|
|
471
|
+
anchor: this.#entranceSentinel,
|
|
472
|
+
composed: true
|
|
473
|
+
});
|
|
474
|
+
previous && focusElement(previous);
|
|
475
|
+
}
|
|
471
476
|
return;
|
|
472
477
|
}
|
|
473
478
|
};
|
|
@@ -485,18 +490,17 @@ var Portal = class {
|
|
|
485
490
|
}
|
|
486
491
|
this.#update();
|
|
487
492
|
const focusables = this.#getFocusables();
|
|
488
|
-
if (
|
|
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 {
|
|
489
501
|
event.preventDefault();
|
|
490
502
|
this.#moveFocus(shiftKey ? "previous" : "next");
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
const index = focusables.indexOf(active);
|
|
494
|
-
if (index === -1) {
|
|
495
|
-
return;
|
|
496
503
|
}
|
|
497
|
-
event.preventDefault();
|
|
498
|
-
const focusable = focusables[index + (shiftKey ? -1 : 1)];
|
|
499
|
-
focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
|
|
500
504
|
};
|
|
501
505
|
#update() {
|
|
502
506
|
const current = /* @__PURE__ */ new Set([
|
|
@@ -504,19 +508,17 @@ var Portal = class {
|
|
|
504
508
|
...getFocusables(this.#host, { composed: true })
|
|
505
509
|
]);
|
|
506
510
|
for (const focusable of this.#focusables) {
|
|
507
|
-
if (current.has(focusable)) {
|
|
508
|
-
|
|
511
|
+
if (!current.has(focusable)) {
|
|
512
|
+
focusable.isConnected && restoreAttributes([focusable]);
|
|
513
|
+
this.#focusables.delete(focusable);
|
|
509
514
|
}
|
|
510
|
-
focusable.isConnected && restoreAttributes([focusable]);
|
|
511
|
-
this.#focusables.delete(focusable);
|
|
512
515
|
}
|
|
513
516
|
for (const focusable of current) {
|
|
514
|
-
if (this.#focusables.has(focusable)) {
|
|
515
|
-
|
|
517
|
+
if (!this.#focusables.has(focusable)) {
|
|
518
|
+
this.#focusables.add(focusable);
|
|
519
|
+
saveAttributes([focusable], ["tabindex"]);
|
|
520
|
+
focusable.setAttribute("tabindex", "-1");
|
|
516
521
|
}
|
|
517
|
-
this.#focusables.add(focusable);
|
|
518
|
-
saveAttributes([focusable], ["tabindex"]);
|
|
519
|
-
focusable.setAttribute("tabindex", "-1");
|
|
520
522
|
}
|
|
521
523
|
}
|
|
522
524
|
#createSentinel() {
|
|
@@ -573,7 +575,7 @@ function getActiveElement2() {
|
|
|
573
575
|
* Lightweight DOM portal (teleport) utility with fully focus management.
|
|
574
576
|
* Designed for accessible dialogs, menus, overlays, popovers.
|
|
575
577
|
*
|
|
576
|
-
* @version 1.2.
|
|
578
|
+
* @version 1.2.15
|
|
577
579
|
* @author Yusuke Kamiyamane
|
|
578
580
|
* @license MIT
|
|
579
581
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -585,7 +587,7 @@ function getActiveElement2() {
|
|
|
585
587
|
(**
|
|
586
588
|
* Attributes Utils
|
|
587
589
|
*
|
|
588
|
-
* @version 1.1.
|
|
590
|
+
* @version 1.1.2
|
|
589
591
|
* @author Yusuke Kamiyamane
|
|
590
592
|
* @license MIT
|
|
591
593
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -598,7 +600,7 @@ power-focusable/dist/index.js:
|
|
|
598
600
|
* High-precision focus management utility with full composed tree support.
|
|
599
601
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
600
602
|
*
|
|
601
|
-
* @version 4.3.
|
|
603
|
+
* @version 4.3.3
|
|
602
604
|
* @author Yusuke Kamiyamane
|
|
603
605
|
* @license MIT
|
|
604
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.
|
|
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.
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
282
|
-
|
|
283
|
-
|
|
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 (
|
|
294
|
-
|
|
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
|
}
|
|
@@ -455,7 +444,15 @@ var Portal = class {
|
|
|
455
444
|
}
|
|
456
445
|
this.#update();
|
|
457
446
|
const first = [...this.#focusables][0];
|
|
458
|
-
|
|
447
|
+
if (first) {
|
|
448
|
+
focusElement(first);
|
|
449
|
+
} else {
|
|
450
|
+
const next = getNextFocusable(document.body, {
|
|
451
|
+
anchor: this.#exitSentinel,
|
|
452
|
+
composed: true
|
|
453
|
+
});
|
|
454
|
+
next && focusElement(next);
|
|
455
|
+
}
|
|
459
456
|
return;
|
|
460
457
|
}
|
|
461
458
|
if (current === this.#exitSentinel) {
|
|
@@ -465,7 +462,15 @@ var Portal = class {
|
|
|
465
462
|
}
|
|
466
463
|
this.#update();
|
|
467
464
|
const last = [...this.#focusables].at(-1);
|
|
468
|
-
|
|
465
|
+
if (last) {
|
|
466
|
+
focusElement(last);
|
|
467
|
+
} else {
|
|
468
|
+
const previous = getPreviousFocusable(document.body, {
|
|
469
|
+
anchor: this.#entranceSentinel,
|
|
470
|
+
composed: true
|
|
471
|
+
});
|
|
472
|
+
previous && focusElement(previous);
|
|
473
|
+
}
|
|
469
474
|
return;
|
|
470
475
|
}
|
|
471
476
|
};
|
|
@@ -483,18 +488,17 @@ var Portal = class {
|
|
|
483
488
|
}
|
|
484
489
|
this.#update();
|
|
485
490
|
const focusables = this.#getFocusables();
|
|
486
|
-
if (
|
|
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 {
|
|
487
499
|
event.preventDefault();
|
|
488
500
|
this.#moveFocus(shiftKey ? "previous" : "next");
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
const index = focusables.indexOf(active);
|
|
492
|
-
if (index === -1) {
|
|
493
|
-
return;
|
|
494
501
|
}
|
|
495
|
-
event.preventDefault();
|
|
496
|
-
const focusable = focusables[index + (shiftKey ? -1 : 1)];
|
|
497
|
-
focusable ? focusElement(focusable) : this.#focusSentinel(shiftKey);
|
|
498
502
|
};
|
|
499
503
|
#update() {
|
|
500
504
|
const current = /* @__PURE__ */ new Set([
|
|
@@ -502,19 +506,17 @@ var Portal = class {
|
|
|
502
506
|
...getFocusables(this.#host, { composed: true })
|
|
503
507
|
]);
|
|
504
508
|
for (const focusable of this.#focusables) {
|
|
505
|
-
if (current.has(focusable)) {
|
|
506
|
-
|
|
509
|
+
if (!current.has(focusable)) {
|
|
510
|
+
focusable.isConnected && restoreAttributes([focusable]);
|
|
511
|
+
this.#focusables.delete(focusable);
|
|
507
512
|
}
|
|
508
|
-
focusable.isConnected && restoreAttributes([focusable]);
|
|
509
|
-
this.#focusables.delete(focusable);
|
|
510
513
|
}
|
|
511
514
|
for (const focusable of current) {
|
|
512
|
-
if (this.#focusables.has(focusable)) {
|
|
513
|
-
|
|
515
|
+
if (!this.#focusables.has(focusable)) {
|
|
516
|
+
this.#focusables.add(focusable);
|
|
517
|
+
saveAttributes([focusable], ["tabindex"]);
|
|
518
|
+
focusable.setAttribute("tabindex", "-1");
|
|
514
519
|
}
|
|
515
|
-
this.#focusables.add(focusable);
|
|
516
|
-
saveAttributes([focusable], ["tabindex"]);
|
|
517
|
-
focusable.setAttribute("tabindex", "-1");
|
|
518
520
|
}
|
|
519
521
|
}
|
|
520
522
|
#createSentinel() {
|
|
@@ -571,7 +573,7 @@ function getActiveElement2() {
|
|
|
571
573
|
* Lightweight DOM portal (teleport) utility with fully focus management.
|
|
572
574
|
* Designed for accessible dialogs, menus, overlays, popovers.
|
|
573
575
|
*
|
|
574
|
-
* @version 1.2.
|
|
576
|
+
* @version 1.2.15
|
|
575
577
|
* @author Yusuke Kamiyamane
|
|
576
578
|
* @license MIT
|
|
577
579
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -583,7 +585,7 @@ function getActiveElement2() {
|
|
|
583
585
|
(**
|
|
584
586
|
* Attributes Utils
|
|
585
587
|
*
|
|
586
|
-
* @version 1.1.
|
|
588
|
+
* @version 1.1.2
|
|
587
589
|
* @author Yusuke Kamiyamane
|
|
588
590
|
* @license MIT
|
|
589
591
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -596,7 +598,7 @@ power-focusable/dist/index.js:
|
|
|
596
598
|
* High-precision focus management utility with full composed tree support.
|
|
597
599
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
598
600
|
*
|
|
599
|
-
* @version 4.3.
|
|
601
|
+
* @version 4.3.3
|
|
600
602
|
* @author Yusuke Kamiyamane
|
|
601
603
|
* @license MIT
|
|
602
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.
|
|
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.
|
|
50
|
+
"@y14e/attributes-utils": "^1.1.2",
|
|
51
51
|
"bun-types": "latest",
|
|
52
|
-
"power-focusable": "^4.3.
|
|
52
|
+
"power-focusable": "^4.3.3",
|
|
53
53
|
"tsup": "^8.0.0",
|
|
54
54
|
"typescript": "^5.6.0"
|
|
55
55
|
},
|