@y14e/portal 1.1.1 → 1.2.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 +49 -16
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +49 -16
- 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,54 @@ var Portal = class {
|
|
|
381
380
|
if (!this.#host.contains(active)) {
|
|
382
381
|
return;
|
|
383
382
|
}
|
|
384
|
-
|
|
383
|
+
this.#update();
|
|
384
|
+
const focusables = this.#getFocusables();
|
|
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 = /* @__PURE__ */ new Set([
|
|
404
|
+
...getFocusables(this.#host, { composed: true }),
|
|
405
|
+
...this.#getFocusables()
|
|
406
|
+
]);
|
|
407
|
+
this.#focusables.forEach((focusable) => {
|
|
408
|
+
if (current.has(focusable)) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (focusable.isConnected) {
|
|
412
|
+
const index = this.#tabIndexes.get(focusable);
|
|
413
|
+
if (index == null) {
|
|
414
|
+
focusable.removeAttribute("tabindex");
|
|
415
|
+
} else {
|
|
416
|
+
focusable.setAttribute("tabindex", index);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
this.#focusables.delete(focusable);
|
|
420
|
+
this.#tabIndexes.delete(focusable);
|
|
421
|
+
});
|
|
422
|
+
current.forEach((c) => {
|
|
423
|
+
if (this.#focusables.has(c)) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
this.#focusables.add(c);
|
|
427
|
+
this.#tabIndexes.set(c, c.getAttribute("tabindex"));
|
|
428
|
+
c.setAttribute("tabindex", "-1");
|
|
429
|
+
});
|
|
430
|
+
}
|
|
400
431
|
#createSentinel() {
|
|
401
432
|
const sentinel = document.createElement("span");
|
|
402
433
|
sentinel.setAttribute("aria-hidden", "true");
|
|
@@ -408,7 +439,9 @@ var Portal = class {
|
|
|
408
439
|
#getFocusables() {
|
|
409
440
|
return getFocusables(this.#host, {
|
|
410
441
|
composed: true,
|
|
411
|
-
include: (element) =>
|
|
442
|
+
include: (element) => {
|
|
443
|
+
return this.#focusables.has(element);
|
|
444
|
+
}
|
|
412
445
|
});
|
|
413
446
|
}
|
|
414
447
|
#moveFocus(direction) {
|
|
@@ -445,7 +478,7 @@ function getActiveElement2() {
|
|
|
445
478
|
* Lightweight DOM portal (teleport) utility with fully focus management.
|
|
446
479
|
* Designed for accessible dialogs, menus, overlays, popovers.
|
|
447
480
|
*
|
|
448
|
-
* @version 1.
|
|
481
|
+
* @version 1.2.0
|
|
449
482
|
* @author Yusuke Kamiyamane
|
|
450
483
|
* @license MIT
|
|
451
484
|
* @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.
|
|
6
|
+
* @version 1.2.0
|
|
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.
|
|
6
|
+
* @version 1.2.0
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
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,54 @@ var Portal = class {
|
|
|
379
378
|
if (!this.#host.contains(active)) {
|
|
380
379
|
return;
|
|
381
380
|
}
|
|
382
|
-
|
|
381
|
+
this.#update();
|
|
382
|
+
const focusables = this.#getFocusables();
|
|
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 = /* @__PURE__ */ new Set([
|
|
402
|
+
...getFocusables(this.#host, { composed: true }),
|
|
403
|
+
...this.#getFocusables()
|
|
404
|
+
]);
|
|
405
|
+
this.#focusables.forEach((focusable) => {
|
|
406
|
+
if (current.has(focusable)) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (focusable.isConnected) {
|
|
410
|
+
const index = this.#tabIndexes.get(focusable);
|
|
411
|
+
if (index == null) {
|
|
412
|
+
focusable.removeAttribute("tabindex");
|
|
413
|
+
} else {
|
|
414
|
+
focusable.setAttribute("tabindex", index);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
this.#focusables.delete(focusable);
|
|
418
|
+
this.#tabIndexes.delete(focusable);
|
|
419
|
+
});
|
|
420
|
+
current.forEach((c) => {
|
|
421
|
+
if (this.#focusables.has(c)) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
this.#focusables.add(c);
|
|
425
|
+
this.#tabIndexes.set(c, c.getAttribute("tabindex"));
|
|
426
|
+
c.setAttribute("tabindex", "-1");
|
|
427
|
+
});
|
|
428
|
+
}
|
|
398
429
|
#createSentinel() {
|
|
399
430
|
const sentinel = document.createElement("span");
|
|
400
431
|
sentinel.setAttribute("aria-hidden", "true");
|
|
@@ -406,7 +437,9 @@ var Portal = class {
|
|
|
406
437
|
#getFocusables() {
|
|
407
438
|
return getFocusables(this.#host, {
|
|
408
439
|
composed: true,
|
|
409
|
-
include: (element) =>
|
|
440
|
+
include: (element) => {
|
|
441
|
+
return this.#focusables.has(element);
|
|
442
|
+
}
|
|
410
443
|
});
|
|
411
444
|
}
|
|
412
445
|
#moveFocus(direction) {
|
|
@@ -443,7 +476,7 @@ function getActiveElement2() {
|
|
|
443
476
|
* Lightweight DOM portal (teleport) utility with fully focus management.
|
|
444
477
|
* Designed for accessible dialogs, menus, overlays, popovers.
|
|
445
478
|
*
|
|
446
|
-
* @version 1.
|
|
479
|
+
* @version 1.2.0
|
|
447
480
|
* @author Yusuke Kamiyamane
|
|
448
481
|
* @license MIT
|
|
449
482
|
* @copyright Copyright (c) Yusuke Kamiyamane
|