@aurodesignsystem-dev/auro-drawer 0.0.0-pr176.0 → 0.0.0-pr180.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/demo/api.md +5 -5
- package/demo/api.min.js +269 -47
- package/demo/index.min.js +269 -47
- package/dist/{auro-drawer-Bsw9JkHh.js → auro-drawer-DdDsuo_W.js} +10 -10
- package/dist/index.d.ts +6 -1
- package/dist/index.js +1 -1
- package/dist/registered.js +1 -1
- package/package.json +2 -2
package/demo/api.md
CHANGED
|
@@ -20,11 +20,11 @@ The `auro-drawer` element provides users a way to implement an expandable drawer
|
|
|
20
20
|
|
|
21
21
|
### Methods
|
|
22
22
|
|
|
23
|
-
| Name | Parameters | Return | Description
|
|
24
|
-
| -------- | -------------------------------------------------------------------- | ------ |
|
|
25
|
-
| hide | `eventType` (any) | | Closes the native dialog.
|
|
26
|
-
| register | `name` (string) - The name of the element that you want to register. | | This will register this element with the browser.
|
|
27
|
-
| show | None | | Opens the native dialog inside the bib.<br><br>- `nested`: `setAttribute("open", "")` to anchor within the parent container.<br>- `!modal && !nested`: `showPopover()` to keep positional CSS intact<br>
|
|
23
|
+
| Name | Parameters | Return | Description |
|
|
24
|
+
| -------- | -------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
25
|
+
| hide | `eventType` (any) | | Closes the native dialog. |
|
|
26
|
+
| register | `name` (string) - The name of the element that you want to register. | | This will register this element with the browser. |
|
|
27
|
+
| show | None | | Opens the native dialog inside the bib.<br><br>- `nested`: `setAttribute("open", "")` to anchor within the parent container.<br>- `!modal && !nested`: `showPopover()` to keep positional CSS intact<br> without taking on `showModal()`'s native modal semantics — `dialog.open`<br> is not set and the background is never made inert. Browsers without the<br> Popover API fall back to `dialog.show()`, which does set `dialog.open`<br> but is likewise non-modal. Focus is still contained on every path:<br> auro-drawer-content installs a `FocusTrap` regardless of how the dialog<br> was opened, not just on the `showModal()` one.<br>- `modal && !nested`: `showModal()` for native focus containment and top-layer rendering. |
|
|
28
28
|
|
|
29
29
|
### Events
|
|
30
30
|
|
package/demo/api.min.js
CHANGED
|
@@ -2045,6 +2045,48 @@ const computePosition = (reference, floating, options) => {
|
|
|
2045
2045
|
});
|
|
2046
2046
|
};
|
|
2047
2047
|
|
|
2048
|
+
/**
|
|
2049
|
+
* Generates a v4 UUID with a three-tier fallback for insecure contexts.
|
|
2050
|
+
*
|
|
2051
|
+
* Tier 1 (secure contexts): crypto.randomUUID() — native, fully random.
|
|
2052
|
+
* Tier 2 (insecure contexts): crypto.getRandomValues() — available on all
|
|
2053
|
+
* modern browsers regardless of secure-context status.
|
|
2054
|
+
* Tier 3 (last resort, no crypto API): new Date().getTime() — lower entropy,
|
|
2055
|
+
* but sufficient for element IDs where collision risk is negligible.
|
|
2056
|
+
*
|
|
2057
|
+
* See: https://w3c.github.io/webcrypto/#dom-crypto-randomuuid (secure-context restriction)
|
|
2058
|
+
*/
|
|
2059
|
+
function generateUUID() {
|
|
2060
|
+
let uuid;
|
|
2061
|
+
|
|
2062
|
+
if (
|
|
2063
|
+
typeof crypto !== "undefined" &&
|
|
2064
|
+
typeof crypto.randomUUID === "function"
|
|
2065
|
+
) {
|
|
2066
|
+
uuid = crypto.randomUUID();
|
|
2067
|
+
} else if (
|
|
2068
|
+
typeof crypto !== "undefined" &&
|
|
2069
|
+
typeof crypto.getRandomValues === "function"
|
|
2070
|
+
) {
|
|
2071
|
+
const bytes = new Uint8Array(16);
|
|
2072
|
+
crypto.getRandomValues(bytes);
|
|
2073
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
2074
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
2075
|
+
const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0"));
|
|
2076
|
+
uuid = `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
|
|
2077
|
+
} else {
|
|
2078
|
+
let d = new Date().getTime();
|
|
2079
|
+
uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2080
|
+
const r = (d % 16) | 0;
|
|
2081
|
+
d = (d - r) / 16 || new Date().getTime();
|
|
2082
|
+
return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
// CSS selectors require IDs starting with a letter; replace a leading digit with 'a'.
|
|
2087
|
+
return /^[0-9]/.test(uuid) ? `a${uuid.slice(1)}` : uuid;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2048
2090
|
/* eslint-disable line-comment-position, no-inline-comments */
|
|
2049
2091
|
|
|
2050
2092
|
|
|
@@ -2332,30 +2374,61 @@ class AuroFloatingUI {
|
|
|
2332
2374
|
|
|
2333
2375
|
/**
|
|
2334
2376
|
* @private
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2377
|
+
* Mirrors `aria-modal` onto the `<dialog>` inside the bib, tracking the page
|
|
2378
|
+
* scroll lock: an overlay that holds the page hides it from assistive tech.
|
|
2379
|
+
*
|
|
2380
|
+
* The removal is called only from inside lockScroll()'s ownership guard: run
|
|
2381
|
+
* outside it, it fired on every autoUpdate tick of every NON-owning instance,
|
|
2382
|
+
* and a repositioning dropdown stripped `aria-modal` off an open modal dialog
|
|
2383
|
+
* it had nothing to do with. The write is unguarded by design — see the call
|
|
2384
|
+
* site — but is skipped when the attribute already reads correctly, so the
|
|
2385
|
+
* repeat costs no mutation record.
|
|
2386
|
+
* @param {Boolean} modal - True to mark the dialog modal; false to unmark it.
|
|
2337
2387
|
*/
|
|
2338
|
-
|
|
2388
|
+
mirrorAriaModal(modal) {
|
|
2339
2389
|
const element = this.element;
|
|
2390
|
+
const dialog = element?.bib
|
|
2391
|
+
? (element.bib?.shadowRoot || element.bib || element).querySelector(
|
|
2392
|
+
"dialog",
|
|
2393
|
+
)
|
|
2394
|
+
: undefined;
|
|
2340
2395
|
|
|
2341
|
-
if (!
|
|
2396
|
+
if (!dialog) {
|
|
2342
2397
|
return;
|
|
2343
2398
|
}
|
|
2344
2399
|
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
element.bib ||
|
|
2348
|
-
element
|
|
2349
|
-
).querySelector("dialog");
|
|
2350
|
-
if (dialog) {
|
|
2351
|
-
if (lock) {
|
|
2400
|
+
if (modal) {
|
|
2401
|
+
if (dialog.getAttribute("aria-modal") !== "true") {
|
|
2352
2402
|
dialog.setAttribute("aria-modal", "true");
|
|
2353
|
-
} else {
|
|
2354
|
-
dialog.removeAttribute("aria-modal");
|
|
2355
2403
|
}
|
|
2404
|
+
} else {
|
|
2405
|
+
dialog.removeAttribute("aria-modal");
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
/**
|
|
2410
|
+
* @private
|
|
2411
|
+
* Controls whether to lock the scrolling for the document's body.
|
|
2412
|
+
* @param {Boolean} lock - If true, locks the body's scrolling functionlity; otherwise, unlock.
|
|
2413
|
+
*/
|
|
2414
|
+
lockScroll(lock = true) {
|
|
2415
|
+
const element = this.element;
|
|
2416
|
+
|
|
2417
|
+
// Locking needs a bib to lock around; unlocking must not, or a bib that has
|
|
2418
|
+
// already been detached would leave the page frozen with no way back.
|
|
2419
|
+
if (lock && !element?.bib) {
|
|
2420
|
+
return;
|
|
2356
2421
|
}
|
|
2357
2422
|
|
|
2358
2423
|
if (lock) {
|
|
2424
|
+
// Re-asserted on every lock call rather than only on the unlocked ->
|
|
2425
|
+
// locked transition. If the bib's inner <dialog> is replaced while this
|
|
2426
|
+
// instance still holds the lock, the replacement would otherwise go
|
|
2427
|
+
// unmarked until a full unlock/lock cycle. Safe to repeat: it targets
|
|
2428
|
+
// this instance's own element, so it cannot reach another overlay's
|
|
2429
|
+
// dialog, and mirrorAriaModal() skips the write when already correct.
|
|
2430
|
+
this.mirrorAriaModal(true);
|
|
2431
|
+
|
|
2359
2432
|
if (!this._scrollLocked) {
|
|
2360
2433
|
this._scrollLocked = true;
|
|
2361
2434
|
this._savedScrollY = window.scrollY;
|
|
@@ -2413,6 +2486,8 @@ class AuroFloatingUI {
|
|
|
2413
2486
|
}
|
|
2414
2487
|
} else {
|
|
2415
2488
|
if (this._scrollLocked) {
|
|
2489
|
+
this.mirrorAriaModal(false);
|
|
2490
|
+
|
|
2416
2491
|
if (this._viewportHandler && window.visualViewport) {
|
|
2417
2492
|
window.visualViewport.removeEventListener(
|
|
2418
2493
|
"resize",
|
|
@@ -2525,15 +2600,47 @@ class AuroFloatingUI {
|
|
|
2525
2600
|
} else if (this.configureTrial < MAX_CONFIGURATION_COUNT) {
|
|
2526
2601
|
this.configureTrial += 1;
|
|
2527
2602
|
|
|
2528
|
-
|
|
2603
|
+
// The id is retained so disconnect() can cancel it. A retry that fires
|
|
2604
|
+
// after teardown re-enters this method with isPopoverVisible still true
|
|
2605
|
+
// — nothing clears it on the teardown path — and takes the page scroll
|
|
2606
|
+
// lock back, with no owner left to release it.
|
|
2607
|
+
//
|
|
2608
|
+
// This is not the rare timing race it reads as: the lookup above wants
|
|
2609
|
+
// a `.container` inside the BIB's own shadow root, which only a bib
|
|
2610
|
+
// that is itself a custom element has. A consumer whose bib is a plain
|
|
2611
|
+
// element (auro-dialog's `#bib` is a div, with its `.container` a
|
|
2612
|
+
// sibling in the host's shadow root) can never satisfy it, so it
|
|
2613
|
+
// retries the full MAX_CONFIGURATION_COUNT every time. Correcting the
|
|
2614
|
+
// lookup would change bib sizing for those consumers and is tracked
|
|
2615
|
+
// separately; cancelling the timer is what keeps it from freezing the
|
|
2616
|
+
// page.
|
|
2617
|
+
this._configureRetryId = setTimeout(() => {
|
|
2618
|
+
this._configureRetryId = undefined;
|
|
2529
2619
|
this.configureBibStrategy(value);
|
|
2530
2620
|
}, 0);
|
|
2531
2621
|
}
|
|
2532
2622
|
|
|
2533
2623
|
if (element.isPopoverVisible) {
|
|
2534
|
-
|
|
2624
|
+
// Positioning strategy is not scroll ownership. Both "fullscreen" and
|
|
2625
|
+
// "dialog" lay the bib over the page and trap focus inside it, so both
|
|
2626
|
+
// must freeze the page behind them (AB#1647843). Gating this on
|
|
2627
|
+
// "fullscreen" alone left dismissible desktop dialogs and drawers
|
|
2628
|
+
// scrollable, and actively released the lock on every reposition
|
|
2629
|
+
// (AB#1625424, AB#1625435). The nested "cover" strategy takes the else
|
|
2630
|
+
// branch and stays scrollable by design — it sits inside its parent
|
|
2631
|
+
// rather than over the page.
|
|
2632
|
+
this.lockScroll(true);
|
|
2535
2633
|
}
|
|
2536
2634
|
} else {
|
|
2635
|
+
// The mirror of the lock above: a strategy that does not own the page must
|
|
2636
|
+
// hand the lock back, not merely decline to take it. autoUpdate re-runs
|
|
2637
|
+
// this method on every resize tick, and getPositioningStrategy() flips
|
|
2638
|
+
// "fullscreen" -> "floating" across the breakpoint for dropdown behavior —
|
|
2639
|
+
// so an overlay that locked while narrow can land here while still open.
|
|
2640
|
+
// Without this, the page stays frozen behind a small floating bib until
|
|
2641
|
+
// hideBib() happens to run.
|
|
2642
|
+
this.lockScroll(false);
|
|
2643
|
+
|
|
2537
2644
|
element.bib.style.position = "";
|
|
2538
2645
|
element.bib.removeAttribute("isfullscreen");
|
|
2539
2646
|
element.isBibFullscreen = false;
|
|
@@ -2643,6 +2750,46 @@ class AuroFloatingUI {
|
|
|
2643
2750
|
this.hideBib("focusloss");
|
|
2644
2751
|
}
|
|
2645
2752
|
|
|
2753
|
+
/**
|
|
2754
|
+
* @private
|
|
2755
|
+
* Installs the handlers that dismiss an open overlay.
|
|
2756
|
+
*
|
|
2757
|
+
* Shared by showBib(), which opens the overlay, and configure(), which has to
|
|
2758
|
+
* put these back after a rewire tore them down under an overlay that is still
|
|
2759
|
+
* open. A non-modal overlay dismisses on outside click, focus loss and
|
|
2760
|
+
* Escape; a modal one instead swallows Escape so the native dialog's
|
|
2761
|
+
* CloseWatcher never sees it (AB#1613688).
|
|
2762
|
+
*/
|
|
2763
|
+
setupDismissHandlers() {
|
|
2764
|
+
const element = this.element;
|
|
2765
|
+
if (!element) {
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
if (!element.modal) {
|
|
2770
|
+
this.setupHideHandlers();
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
if (this.keyDownHandler) {
|
|
2775
|
+
document.removeEventListener("keydown", this.keyDownHandler);
|
|
2776
|
+
}
|
|
2777
|
+
this.keyDownHandler = (evt) => {
|
|
2778
|
+
if (evt.key === "Escape" && element.isPopoverVisible) {
|
|
2779
|
+
// Intercept at keydown so CloseWatcher never sees the keystroke.
|
|
2780
|
+
// Canceling `cancel` alone is insufficient — a second Esc with no
|
|
2781
|
+
// intervening user activation triggers the anti-trap and fires `close`
|
|
2782
|
+
// directly, bypassing any `cancel` preventDefault (AB#1613688).
|
|
2783
|
+
// stopImmediatePropagation is intentional: it prevents other document
|
|
2784
|
+
// keydown handlers (e.g. consumer code, auro-dialog) from also acting
|
|
2785
|
+
// on Escape while the modal owns the key.
|
|
2786
|
+
evt.preventDefault();
|
|
2787
|
+
evt.stopImmediatePropagation();
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
document.addEventListener("keydown", this.keyDownHandler);
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2646
2793
|
setupHideHandlers() {
|
|
2647
2794
|
const element = this.element;
|
|
2648
2795
|
if (!element) {
|
|
@@ -2815,27 +2962,7 @@ class AuroFloatingUI {
|
|
|
2815
2962
|
|
|
2816
2963
|
// prevent double showing: isPopovervisible gets first and showBib gets called later
|
|
2817
2964
|
if (!this.showing) {
|
|
2818
|
-
|
|
2819
|
-
this.setupHideHandlers();
|
|
2820
|
-
} else {
|
|
2821
|
-
if (this.keyDownHandler) {
|
|
2822
|
-
document.removeEventListener("keydown", this.keyDownHandler);
|
|
2823
|
-
}
|
|
2824
|
-
this.keyDownHandler = (evt) => {
|
|
2825
|
-
if (evt.key === "Escape" && element.isPopoverVisible) {
|
|
2826
|
-
// Intercept at keydown so CloseWatcher never sees the keystroke.
|
|
2827
|
-
// Canceling `cancel` alone is insufficient — a second Esc with no
|
|
2828
|
-
// intervening user activation triggers the anti-trap and fires `close`
|
|
2829
|
-
// directly, bypassing any `cancel` preventDefault (AB#1613688).
|
|
2830
|
-
// stopImmediatePropagation is intentional: it prevents other document
|
|
2831
|
-
// keydown handlers (e.g. consumer code, auro-dialog) from also acting
|
|
2832
|
-
// on Escape while the modal owns the key.
|
|
2833
|
-
evt.preventDefault();
|
|
2834
|
-
evt.stopImmediatePropagation();
|
|
2835
|
-
}
|
|
2836
|
-
};
|
|
2837
|
-
document.addEventListener("keydown", this.keyDownHandler);
|
|
2838
|
-
}
|
|
2965
|
+
this.setupDismissHandlers();
|
|
2839
2966
|
this.showing = true;
|
|
2840
2967
|
element.isPopoverVisible = true;
|
|
2841
2968
|
this.position();
|
|
@@ -3063,7 +3190,7 @@ class AuroFloatingUI {
|
|
|
3063
3190
|
|
|
3064
3191
|
this.id = element.getAttribute("id");
|
|
3065
3192
|
if (!this.id) {
|
|
3066
|
-
this.id =
|
|
3193
|
+
this.id = generateUUID();
|
|
3067
3194
|
element.setAttribute("id", this.id);
|
|
3068
3195
|
}
|
|
3069
3196
|
|
|
@@ -3075,7 +3202,35 @@ class AuroFloatingUI {
|
|
|
3075
3202
|
this.enableKeyboardHandling = enableKeyboardHandling;
|
|
3076
3203
|
|
|
3077
3204
|
this.eventPrefix = eventPrefix;
|
|
3078
|
-
|
|
3205
|
+
|
|
3206
|
+
// A retained scroll lock's saved body styles were captured for whichever
|
|
3207
|
+
// element held it, so only a same-element rewire may keep it. An actual
|
|
3208
|
+
// element swap has to take the full teardown, or this instance is left
|
|
3209
|
+
// holding a lock that no longer describes the page it points at — with no
|
|
3210
|
+
// owner able to release it (AB#1647843).
|
|
3211
|
+
const rewiringSameElement = this.element === elem;
|
|
3212
|
+
|
|
3213
|
+
if (!rewiringSameElement) {
|
|
3214
|
+
// Tear down against the outgoing element, before this.element is
|
|
3215
|
+
// repointed. Everything disconnect() releases is element-scoped:
|
|
3216
|
+
// aria-modal on that element's inner dialog, the bib transform saved for
|
|
3217
|
+
// it, its autoUpdate loop and its trigger listeners. Repointing first
|
|
3218
|
+
// aimed all of it at the incoming element — the outgoing dialog kept
|
|
3219
|
+
// aria-modal="true" for good, its bib stayed transformed, and the
|
|
3220
|
+
// incoming bib had the outgoing element's saved transform written onto
|
|
3221
|
+
// it. The trigger gate below read the incoming element's trigger, which
|
|
3222
|
+
// is not assigned until later in this method, so on a swap teardown was
|
|
3223
|
+
// usually skipped outright and the page stayed locked with no owner.
|
|
3224
|
+
if (this.element?.trigger) {
|
|
3225
|
+
this.disconnect({ teardown: true });
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
// disconnect() does not clear this.showing, and the rewire block at the
|
|
3229
|
+
// end of this method only runs for a same-element rewire. Left true, the
|
|
3230
|
+
// incoming element's first showBib() is silently dropped by its
|
|
3231
|
+
// `!this.showing` guard and the overlay never opens.
|
|
3232
|
+
this.showing = false;
|
|
3233
|
+
|
|
3079
3234
|
this.element = elem;
|
|
3080
3235
|
}
|
|
3081
3236
|
|
|
@@ -3088,8 +3243,10 @@ class AuroFloatingUI {
|
|
|
3088
3243
|
this.behavior = element.behavior;
|
|
3089
3244
|
}
|
|
3090
3245
|
|
|
3091
|
-
if (element.trigger) {
|
|
3092
|
-
|
|
3246
|
+
if (rewiringSameElement && element.trigger) {
|
|
3247
|
+
// Rewiring, not tearing down — an overlay that is open right now keeps
|
|
3248
|
+
// its page scroll lock across a trigger change.
|
|
3249
|
+
this.disconnect({ teardown: false });
|
|
3093
3250
|
}
|
|
3094
3251
|
element.trigger =
|
|
3095
3252
|
element.triggerElement ||
|
|
@@ -3118,11 +3275,61 @@ class AuroFloatingUI {
|
|
|
3118
3275
|
element.trigger.addEventListener("focus", this.handleEvent);
|
|
3119
3276
|
element.trigger.addEventListener("blur", this.handleEvent);
|
|
3120
3277
|
}
|
|
3278
|
+
|
|
3279
|
+
// disconnect() above stripped the dismissal handlers and killed the
|
|
3280
|
+
// autoUpdate loop; the trigger listeners re-registered here replace
|
|
3281
|
+
// neither. An overlay that is open right now would otherwise be left on
|
|
3282
|
+
// screen with outside-click, focus-loss and Escape all dead and its
|
|
3283
|
+
// position frozen — and, since the lock is deliberately retained, holding
|
|
3284
|
+
// the page with only its original trigger able to give it back.
|
|
3285
|
+
if (rewiringSameElement && this.showing) {
|
|
3286
|
+
this.setupDismissHandlers();
|
|
3287
|
+
|
|
3288
|
+
if (element.bib && (element.trigger || element.parentNode)) {
|
|
3289
|
+
element.cleanup = autoUpdate(
|
|
3290
|
+
element.trigger || element.parentNode,
|
|
3291
|
+
element.bib,
|
|
3292
|
+
() => {
|
|
3293
|
+
this.position();
|
|
3294
|
+
},
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3121
3298
|
}
|
|
3122
3299
|
|
|
3123
|
-
|
|
3300
|
+
/**
|
|
3301
|
+
* Tears the floater's connections down.
|
|
3302
|
+
*
|
|
3303
|
+
* Two callers with different intent share this: a real teardown, which must
|
|
3304
|
+
* release the page scroll lock, and configure(), which re-runs it purely to
|
|
3305
|
+
* rewire a changed trigger and must leave an open overlay's lock alone.
|
|
3306
|
+
* @param {Object} [options] - Teardown options.
|
|
3307
|
+
* @param {Boolean} [options.teardown] - False when only rewiring; keeps the
|
|
3308
|
+
* page scroll lock held for an overlay that is still open.
|
|
3309
|
+
* @returns {void}
|
|
3310
|
+
*/
|
|
3311
|
+
disconnect({ teardown = true } = {}) {
|
|
3124
3312
|
this.cleanupHideHandlers();
|
|
3125
3313
|
|
|
3314
|
+
// Cancel any pending configureBibStrategy() retry. It would otherwise fire
|
|
3315
|
+
// a macrotask after the unlock below, re-lock the page, and leave it frozen
|
|
3316
|
+
// with the floater already gone. Cancelled on a rewire too: configure()
|
|
3317
|
+
// re-runs position() itself, so a stale retry has nothing to contribute.
|
|
3318
|
+
if (this._configureRetryId !== undefined) {
|
|
3319
|
+
clearTimeout(this._configureRetryId);
|
|
3320
|
+
this._configureRetryId = undefined;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
// Tearing down while the bib is still open would otherwise strand
|
|
3324
|
+
// body{position:fixed} and leave the page permanently unscrollable —
|
|
3325
|
+
// hideBib() is the only other unlock, and it never runs in this path.
|
|
3326
|
+
// Skipped when merely rewiring: configure() routes through here on every
|
|
3327
|
+
// triggerElement change, and releasing the lock there would unlock the page
|
|
3328
|
+
// behind an overlay that is still open (AB#1625424, AB#1625435).
|
|
3329
|
+
if (teardown) {
|
|
3330
|
+
this.lockScroll(false);
|
|
3331
|
+
}
|
|
3332
|
+
|
|
3126
3333
|
const element = this.element;
|
|
3127
3334
|
if (!element) {
|
|
3128
3335
|
return;
|
|
@@ -3366,7 +3573,12 @@ class AuroFloater extends i$1 {
|
|
|
3366
3573
|
*
|
|
3367
3574
|
* - `nested`: `setAttribute("open", "")` to anchor within the parent container.
|
|
3368
3575
|
* - `!modal && !nested`: `showPopover()` to keep positional CSS intact
|
|
3369
|
-
*
|
|
3576
|
+
* without taking on `showModal()`'s native modal semantics — `dialog.open`
|
|
3577
|
+
* is not set and the background is never made inert. Browsers without the
|
|
3578
|
+
* Popover API fall back to `dialog.show()`, which does set `dialog.open`
|
|
3579
|
+
* but is likewise non-modal. Focus is still contained on every path:
|
|
3580
|
+
* auro-drawer-content installs a `FocusTrap` regardless of how the dialog
|
|
3581
|
+
* was opened, not just on the `showModal()` one.
|
|
3370
3582
|
* - `modal && !nested`: `showModal()` for native focus containment and top-layer rendering.
|
|
3371
3583
|
*/
|
|
3372
3584
|
async show() {
|
|
@@ -3426,15 +3638,17 @@ class AuroFloater extends i$1 {
|
|
|
3426
3638
|
// the deferred close fires (e.g. when the element is removed from the DOM).
|
|
3427
3639
|
const dialog = this.bib?.dialog;
|
|
3428
3640
|
if (dialog?.open) {
|
|
3429
|
-
// Modal and nested drawers
|
|
3641
|
+
// Modal and nested drawers (showModal() / setAttribute('open')), and
|
|
3642
|
+
// non-modal drawers on browsers without the Popover API (dialog.show()).
|
|
3430
3643
|
this._closeTimeout = setTimeout(() => {
|
|
3431
3644
|
if (dialog.open) {
|
|
3432
3645
|
dialog.close();
|
|
3433
3646
|
}
|
|
3434
3647
|
}, 300);
|
|
3435
3648
|
} else if (typeof dialog?.hidePopover === "function" && dialog.matches?.(':popover-open')) {
|
|
3436
|
-
// Non-modal drawers
|
|
3437
|
-
// so the popover stays in the top layer unless hidePopover()
|
|
3649
|
+
// Non-modal drawers opened via showPopover() — that path never sets
|
|
3650
|
+
// dialog.open, so the popover stays in the top layer unless hidePopover()
|
|
3651
|
+
// is called.
|
|
3438
3652
|
// Guard on hidePopover support: matches(':popover-open') throws a SyntaxError
|
|
3439
3653
|
// on browsers that don't implement the Popover API.
|
|
3440
3654
|
this._closeTimeout = setTimeout(() => {
|
|
@@ -4395,8 +4609,16 @@ class AuroDrawer extends AuroFloater {
|
|
|
4395
4609
|
this.bib.getAttribute("id"),
|
|
4396
4610
|
);
|
|
4397
4611
|
}
|
|
4398
|
-
// role="dialog"
|
|
4399
|
-
//
|
|
4612
|
+
// role="dialog" is provided natively by the <dialog> element — do not set it here.
|
|
4613
|
+
//
|
|
4614
|
+
// aria-modal is NOT, and the distinction matters: native modal semantics come
|
|
4615
|
+
// from how the dialog is opened, not from the element. Only `modal && !nested`
|
|
4616
|
+
// uses showModal(); `!modal && !nested` uses showPopover() (or dialog.show() on
|
|
4617
|
+
// browsers without the Popover API) and `nested` uses setAttribute("open"),
|
|
4618
|
+
// none of which is exposed as modal. auro-library's lockScroll() therefore sets aria-modal on the inner
|
|
4619
|
+
// <dialog> for any presentation that takes the page scroll lock, which is what
|
|
4620
|
+
// makes the dismissible drawer report its modality correctly. See auro-floater.js
|
|
4621
|
+
// show() for the three open paths.
|
|
4400
4622
|
}
|
|
4401
4623
|
|
|
4402
4624
|
/**
|