@y14e/portal 1.0.6 → 1.1.0
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 +45 -22
- package/dist/index.d.cts +2 -7
- package/dist/index.d.ts +2 -7
- package/dist/index.js +46 -22
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -295,7 +295,8 @@ var Portal = class {
|
|
|
295
295
|
#container;
|
|
296
296
|
#entranceSentinel;
|
|
297
297
|
#exitSentinel;
|
|
298
|
-
#
|
|
298
|
+
#focusables = /* @__PURE__ */ new Set();
|
|
299
|
+
#tabIndexes = /* @__PURE__ */ new Map();
|
|
299
300
|
#controller = null;
|
|
300
301
|
#isDestroyed = false;
|
|
301
302
|
constructor(host, container) {
|
|
@@ -312,10 +313,7 @@ var Portal = class {
|
|
|
312
313
|
this.#isDestroyed = true;
|
|
313
314
|
this.#controller?.abort();
|
|
314
315
|
this.#controller = null;
|
|
315
|
-
this.#
|
|
316
|
-
if (!this.#tabIndexes.has(focusable)) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
316
|
+
this.#focusables.forEach((focusable) => {
|
|
319
317
|
const index = this.#tabIndexes.get(focusable);
|
|
320
318
|
if (index == null) {
|
|
321
319
|
focusable.removeAttribute("tabindex");
|
|
@@ -323,6 +321,8 @@ var Portal = class {
|
|
|
323
321
|
focusable.setAttribute("tabindex", index);
|
|
324
322
|
}
|
|
325
323
|
});
|
|
324
|
+
this.#focusables.clear();
|
|
325
|
+
this.#tabIndexes.clear();
|
|
326
326
|
this.#exitSentinel.after(this.#host);
|
|
327
327
|
this.#entranceSentinel.remove();
|
|
328
328
|
this.#exitSentinel.remove();
|
|
@@ -332,10 +332,7 @@ var Portal = class {
|
|
|
332
332
|
this.#host.before(this.#entranceSentinel);
|
|
333
333
|
this.#entranceSentinel.after(this.#exitSentinel);
|
|
334
334
|
this.#container.append(this.#host);
|
|
335
|
-
this.#
|
|
336
|
-
this.#tabIndexes.set(focusable, focusable.getAttribute("tabindex"));
|
|
337
|
-
focusable.setAttribute("tabindex", "-1");
|
|
338
|
-
});
|
|
335
|
+
this.#update();
|
|
339
336
|
this.#controller = new AbortController();
|
|
340
337
|
const { signal } = this.#controller;
|
|
341
338
|
document.addEventListener("focusin", this.#onFocusIn, {
|
|
@@ -358,14 +355,16 @@ var Portal = class {
|
|
|
358
355
|
if (this.#host.contains(before)) {
|
|
359
356
|
this.#moveFocus("previous");
|
|
360
357
|
} else {
|
|
361
|
-
|
|
358
|
+
this.#update();
|
|
359
|
+
const first = [...this.#focusables][0];
|
|
362
360
|
first && focusElement(first);
|
|
363
361
|
}
|
|
364
362
|
} else if (current === this.#exitSentinel) {
|
|
365
363
|
if (this.#host.contains(before)) {
|
|
366
364
|
this.#moveFocus("next");
|
|
367
365
|
} else {
|
|
368
|
-
|
|
366
|
+
this.#update();
|
|
367
|
+
const last = [...this.#focusables].at(-1);
|
|
369
368
|
last && focusElement(last);
|
|
370
369
|
}
|
|
371
370
|
}
|
|
@@ -381,22 +380,53 @@ var Portal = class {
|
|
|
381
380
|
if (!this.#host.contains(active)) {
|
|
382
381
|
return;
|
|
383
382
|
}
|
|
384
|
-
|
|
383
|
+
this.#update();
|
|
384
|
+
const focusables = [...this.#focusables];
|
|
385
|
+
if (!focusables.length) {
|
|
385
386
|
event.preventDefault();
|
|
386
387
|
(event.shiftKey ? this.#entranceSentinel : this.#exitSentinel).focus();
|
|
388
|
+
return;
|
|
387
389
|
}
|
|
388
|
-
const index =
|
|
390
|
+
const index = focusables.indexOf(active);
|
|
389
391
|
if (index === -1) {
|
|
390
392
|
return;
|
|
391
393
|
}
|
|
392
394
|
event.preventDefault();
|
|
393
|
-
const focusable =
|
|
395
|
+
const focusable = focusables[index + (event.shiftKey ? -1 : 1)];
|
|
394
396
|
if (focusable) {
|
|
395
397
|
focusElement(focusable);
|
|
396
398
|
} else {
|
|
397
399
|
(event.shiftKey ? this.#entranceSentinel : this.#exitSentinel).focus();
|
|
398
400
|
}
|
|
399
401
|
};
|
|
402
|
+
#update() {
|
|
403
|
+
const current = new Set(
|
|
404
|
+
getFocusables(this.#host, { composed: true })
|
|
405
|
+
);
|
|
406
|
+
this.#focusables.forEach((focusable) => {
|
|
407
|
+
if (current.has(focusable)) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
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
|
+
}
|
|
417
|
+
}
|
|
418
|
+
this.#focusables.delete(focusable);
|
|
419
|
+
this.#tabIndexes.delete(focusable);
|
|
420
|
+
});
|
|
421
|
+
const active = getActiveElement2();
|
|
422
|
+
current.forEach((c) => {
|
|
423
|
+
if (!this.#tabIndexes.has(c)) {
|
|
424
|
+
this.#tabIndexes.set(c, c.getAttribute("tabindex"));
|
|
425
|
+
}
|
|
426
|
+
c.setAttribute("tabindex", c === active ? "0" : "-1");
|
|
427
|
+
this.#focusables.add(c);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
400
430
|
#createSentinel() {
|
|
401
431
|
const sentinel = document.createElement("span");
|
|
402
432
|
sentinel.setAttribute("aria-hidden", "true");
|
|
@@ -405,12 +435,6 @@ var Portal = class {
|
|
|
405
435
|
sentinel.style.cssText += VISUALLY_HIDDEN_CSS;
|
|
406
436
|
return sentinel;
|
|
407
437
|
}
|
|
408
|
-
#getFocusables() {
|
|
409
|
-
return getFocusables(this.#host, {
|
|
410
|
-
composed: true,
|
|
411
|
-
include: (element) => this.#tabIndexes.has(element)
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
438
|
#moveFocus(direction) {
|
|
415
439
|
const options = {
|
|
416
440
|
anchor: direction === "previous" ? this.#entranceSentinel : this.#exitSentinel,
|
|
@@ -445,7 +469,7 @@ function getActiveElement2() {
|
|
|
445
469
|
* Lightweight DOM portal (teleport) utility with fully focus management.
|
|
446
470
|
* Designed for accessible dialogs, menus, overlays, popovers.
|
|
447
471
|
*
|
|
448
|
-
* @version 1.0
|
|
472
|
+
* @version 1.1.0
|
|
449
473
|
* @author Yusuke Kamiyamane
|
|
450
474
|
* @license MIT
|
|
451
475
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -467,5 +491,4 @@ power-focusable/dist/index.js:
|
|
|
467
491
|
*)
|
|
468
492
|
*/
|
|
469
493
|
|
|
470
|
-
exports.Portal = Portal;
|
|
471
494
|
exports.createPortal = createPortal;
|
package/dist/index.d.cts
CHANGED
|
@@ -3,17 +3,12 @@
|
|
|
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.0
|
|
6
|
+
* @version 1.1.0
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
10
10
|
* @see {@link https://github.com/y14e/portal}
|
|
11
11
|
*/
|
|
12
12
|
declare function createPortal(host: Element, container?: HTMLElement): () => void;
|
|
13
|
-
declare class Portal {
|
|
14
|
-
#private;
|
|
15
|
-
constructor(host: Element, container: Element);
|
|
16
|
-
destroy(): void;
|
|
17
|
-
}
|
|
18
13
|
|
|
19
|
-
export {
|
|
14
|
+
export { createPortal };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,17 +3,12 @@
|
|
|
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.0
|
|
6
|
+
* @version 1.1.0
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
10
10
|
* @see {@link https://github.com/y14e/portal}
|
|
11
11
|
*/
|
|
12
12
|
declare function createPortal(host: Element, container?: HTMLElement): () => void;
|
|
13
|
-
declare class Portal {
|
|
14
|
-
#private;
|
|
15
|
-
constructor(host: Element, container: Element);
|
|
16
|
-
destroy(): void;
|
|
17
|
-
}
|
|
18
13
|
|
|
19
|
-
export {
|
|
14
|
+
export { createPortal };
|
package/dist/index.js
CHANGED
|
@@ -293,7 +293,8 @@ var Portal = class {
|
|
|
293
293
|
#container;
|
|
294
294
|
#entranceSentinel;
|
|
295
295
|
#exitSentinel;
|
|
296
|
-
#
|
|
296
|
+
#focusables = /* @__PURE__ */ new Set();
|
|
297
|
+
#tabIndexes = /* @__PURE__ */ new Map();
|
|
297
298
|
#controller = null;
|
|
298
299
|
#isDestroyed = false;
|
|
299
300
|
constructor(host, container) {
|
|
@@ -310,10 +311,7 @@ var Portal = class {
|
|
|
310
311
|
this.#isDestroyed = true;
|
|
311
312
|
this.#controller?.abort();
|
|
312
313
|
this.#controller = null;
|
|
313
|
-
this.#
|
|
314
|
-
if (!this.#tabIndexes.has(focusable)) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
314
|
+
this.#focusables.forEach((focusable) => {
|
|
317
315
|
const index = this.#tabIndexes.get(focusable);
|
|
318
316
|
if (index == null) {
|
|
319
317
|
focusable.removeAttribute("tabindex");
|
|
@@ -321,6 +319,8 @@ var Portal = class {
|
|
|
321
319
|
focusable.setAttribute("tabindex", index);
|
|
322
320
|
}
|
|
323
321
|
});
|
|
322
|
+
this.#focusables.clear();
|
|
323
|
+
this.#tabIndexes.clear();
|
|
324
324
|
this.#exitSentinel.after(this.#host);
|
|
325
325
|
this.#entranceSentinel.remove();
|
|
326
326
|
this.#exitSentinel.remove();
|
|
@@ -330,10 +330,7 @@ var Portal = class {
|
|
|
330
330
|
this.#host.before(this.#entranceSentinel);
|
|
331
331
|
this.#entranceSentinel.after(this.#exitSentinel);
|
|
332
332
|
this.#container.append(this.#host);
|
|
333
|
-
this.#
|
|
334
|
-
this.#tabIndexes.set(focusable, focusable.getAttribute("tabindex"));
|
|
335
|
-
focusable.setAttribute("tabindex", "-1");
|
|
336
|
-
});
|
|
333
|
+
this.#update();
|
|
337
334
|
this.#controller = new AbortController();
|
|
338
335
|
const { signal } = this.#controller;
|
|
339
336
|
document.addEventListener("focusin", this.#onFocusIn, {
|
|
@@ -356,14 +353,16 @@ var Portal = class {
|
|
|
356
353
|
if (this.#host.contains(before)) {
|
|
357
354
|
this.#moveFocus("previous");
|
|
358
355
|
} else {
|
|
359
|
-
|
|
356
|
+
this.#update();
|
|
357
|
+
const first = [...this.#focusables][0];
|
|
360
358
|
first && focusElement(first);
|
|
361
359
|
}
|
|
362
360
|
} else if (current === this.#exitSentinel) {
|
|
363
361
|
if (this.#host.contains(before)) {
|
|
364
362
|
this.#moveFocus("next");
|
|
365
363
|
} else {
|
|
366
|
-
|
|
364
|
+
this.#update();
|
|
365
|
+
const last = [...this.#focusables].at(-1);
|
|
367
366
|
last && focusElement(last);
|
|
368
367
|
}
|
|
369
368
|
}
|
|
@@ -379,22 +378,53 @@ var Portal = class {
|
|
|
379
378
|
if (!this.#host.contains(active)) {
|
|
380
379
|
return;
|
|
381
380
|
}
|
|
382
|
-
|
|
381
|
+
this.#update();
|
|
382
|
+
const focusables = [...this.#focusables];
|
|
383
|
+
if (!focusables.length) {
|
|
383
384
|
event.preventDefault();
|
|
384
385
|
(event.shiftKey ? this.#entranceSentinel : this.#exitSentinel).focus();
|
|
386
|
+
return;
|
|
385
387
|
}
|
|
386
|
-
const index =
|
|
388
|
+
const index = focusables.indexOf(active);
|
|
387
389
|
if (index === -1) {
|
|
388
390
|
return;
|
|
389
391
|
}
|
|
390
392
|
event.preventDefault();
|
|
391
|
-
const focusable =
|
|
393
|
+
const focusable = focusables[index + (event.shiftKey ? -1 : 1)];
|
|
392
394
|
if (focusable) {
|
|
393
395
|
focusElement(focusable);
|
|
394
396
|
} else {
|
|
395
397
|
(event.shiftKey ? this.#entranceSentinel : this.#exitSentinel).focus();
|
|
396
398
|
}
|
|
397
399
|
};
|
|
400
|
+
#update() {
|
|
401
|
+
const current = new Set(
|
|
402
|
+
getFocusables(this.#host, { composed: true })
|
|
403
|
+
);
|
|
404
|
+
this.#focusables.forEach((focusable) => {
|
|
405
|
+
if (current.has(focusable)) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (focusable.isConnected) {
|
|
409
|
+
const index = this.#tabIndexes.get(focusable);
|
|
410
|
+
if (index == null) {
|
|
411
|
+
focusable.removeAttribute("tabindex");
|
|
412
|
+
} else {
|
|
413
|
+
focusable.setAttribute("tabindex", index);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
this.#focusables.delete(focusable);
|
|
417
|
+
this.#tabIndexes.delete(focusable);
|
|
418
|
+
});
|
|
419
|
+
const active = getActiveElement2();
|
|
420
|
+
current.forEach((c) => {
|
|
421
|
+
if (!this.#tabIndexes.has(c)) {
|
|
422
|
+
this.#tabIndexes.set(c, c.getAttribute("tabindex"));
|
|
423
|
+
}
|
|
424
|
+
c.setAttribute("tabindex", c === active ? "0" : "-1");
|
|
425
|
+
this.#focusables.add(c);
|
|
426
|
+
});
|
|
427
|
+
}
|
|
398
428
|
#createSentinel() {
|
|
399
429
|
const sentinel = document.createElement("span");
|
|
400
430
|
sentinel.setAttribute("aria-hidden", "true");
|
|
@@ -403,12 +433,6 @@ var Portal = class {
|
|
|
403
433
|
sentinel.style.cssText += VISUALLY_HIDDEN_CSS;
|
|
404
434
|
return sentinel;
|
|
405
435
|
}
|
|
406
|
-
#getFocusables() {
|
|
407
|
-
return getFocusables(this.#host, {
|
|
408
|
-
composed: true,
|
|
409
|
-
include: (element) => this.#tabIndexes.has(element)
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
436
|
#moveFocus(direction) {
|
|
413
437
|
const options = {
|
|
414
438
|
anchor: direction === "previous" ? this.#entranceSentinel : this.#exitSentinel,
|
|
@@ -443,7 +467,7 @@ function getActiveElement2() {
|
|
|
443
467
|
* Lightweight DOM portal (teleport) utility with fully focus management.
|
|
444
468
|
* Designed for accessible dialogs, menus, overlays, popovers.
|
|
445
469
|
*
|
|
446
|
-
* @version 1.0
|
|
470
|
+
* @version 1.1.0
|
|
447
471
|
* @author Yusuke Kamiyamane
|
|
448
472
|
* @license MIT
|
|
449
473
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -465,4 +489,4 @@ power-focusable/dist/index.js:
|
|
|
465
489
|
*)
|
|
466
490
|
*/
|
|
467
491
|
|
|
468
|
-
export {
|
|
492
|
+
export { createPortal };
|