@surdeddd/bottom-sheet 0.1.0 → 0.3.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/README.md +57 -21
- package/dist/BottomSheetEngine-CAymrH-P.d.cts +285 -0
- package/dist/BottomSheetEngine-DcqcuudN.d.ts +285 -0
- package/dist/element.cjs +775 -537
- package/dist/element.d.cts +9 -57
- package/dist/element.d.ts +9 -57
- package/dist/element.iife.global.js +2 -2
- package/dist/element.js +775 -537
- package/dist/index.cjs +760 -600
- package/dist/index.d.cts +18 -85
- package/dist/index.d.ts +18 -85
- package/dist/index.js +758 -601
- package/dist/integrations/formik.d.cts +2 -8
- package/dist/integrations/formik.d.ts +2 -8
- package/dist/integrations/react-hook-form.d.cts +2 -11
- package/dist/integrations/react-hook-form.d.ts +2 -11
- package/dist/overlay.cjs +168 -122
- package/dist/overlay.d.cts +14 -137
- package/dist/overlay.d.ts +14 -137
- package/dist/overlay.js +168 -122
- package/dist/preact.d.cts +2 -2
- package/dist/preact.d.ts +2 -2
- package/dist/preact.js +0 -1
- package/dist/qwik.cjs +667 -519
- package/dist/qwik.d.cts +3 -24
- package/dist/qwik.d.ts +3 -24
- package/dist/qwik.js +668 -520
- package/dist/react.cjs +873 -648
- package/dist/react.d.cts +34 -159
- package/dist/react.d.ts +34 -159
- package/dist/react.js +873 -648
- package/dist/solid.cjs +703 -560
- package/dist/solid.d.cts +264 -32
- package/dist/solid.d.ts +264 -32
- package/dist/solid.js +704 -561
- package/dist/solid.source.jsx +3215 -0
- package/dist/styles.css +37 -112
- package/dist/svelte-core.cjs +3149 -0
- package/dist/svelte-core.js +3147 -0
- package/dist/svelte-src/BottomSheet.svelte +210 -0
- package/dist/svelte-src/index.js +2 -0
- package/dist/svelte.cjs +718 -547
- package/dist/svelte.d.cts +100 -0
- package/dist/svelte.d.ts +15 -4
- package/dist/svelte.js +719 -548
- package/dist/themes/ios.css +0 -3
- package/dist/themes/material.css +0 -3
- package/dist/themes/vercel.css +0 -3
- package/dist/{transform-Brkf-wzo.d.cts → transform-jVfAikE9.d.cts} +0 -1
- package/dist/{transform-Brkf-wzo.d.ts → transform-jVfAikE9.d.ts} +0 -1
- package/dist/vue.cjs +677 -526
- package/dist/vue.d.cts +78 -0
- package/dist/vue.d.ts +12 -2
- package/dist/vue.js +678 -527
- package/package.json +105 -45
- package/dist/BottomSheetEngine-CvSVbo6c.d.cts +0 -704
- package/dist/BottomSheetEngine-DbO8rW_3.d.ts +0 -704
package/dist/element.cjs
CHANGED
|
@@ -33,6 +33,18 @@ var init_sheetStack = __esm({
|
|
|
33
33
|
this.entries.splice(idx, 1);
|
|
34
34
|
this.recompute();
|
|
35
35
|
}
|
|
36
|
+
promote(id) {
|
|
37
|
+
const idx = this.entries.findIndex((e) => e.id === id);
|
|
38
|
+
if (idx === -1) return;
|
|
39
|
+
if (idx !== this.entries.length - 1) {
|
|
40
|
+
const [entry] = this.entries.splice(idx, 1);
|
|
41
|
+
this.entries.push(entry);
|
|
42
|
+
}
|
|
43
|
+
this.recompute();
|
|
44
|
+
}
|
|
45
|
+
update() {
|
|
46
|
+
this.recompute();
|
|
47
|
+
}
|
|
36
48
|
size() {
|
|
37
49
|
return this.entries.length;
|
|
38
50
|
}
|
|
@@ -40,9 +52,24 @@ var init_sheetStack = __esm({
|
|
|
40
52
|
this.entries = [];
|
|
41
53
|
}
|
|
42
54
|
recompute() {
|
|
55
|
+
let top = null;
|
|
56
|
+
const open = [];
|
|
57
|
+
for (const entry of this.entries) {
|
|
58
|
+
if (!entry.isOpen || entry.isOpen()) {
|
|
59
|
+
top = entry;
|
|
60
|
+
open.push(entry);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!top && this.entries.length > 0) {
|
|
64
|
+
top = this.entries[this.entries.length - 1];
|
|
65
|
+
}
|
|
43
66
|
this.entries.forEach((entry, i) => {
|
|
44
67
|
entry.setZIndex(BASE_Z + i * STEP);
|
|
45
|
-
entry.setIsTop(
|
|
68
|
+
entry.setIsTop(entry === top);
|
|
69
|
+
if (entry.setDepth) {
|
|
70
|
+
const openIdx = open.indexOf(entry);
|
|
71
|
+
entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
|
|
72
|
+
}
|
|
46
73
|
});
|
|
47
74
|
}
|
|
48
75
|
};
|
|
@@ -272,11 +299,6 @@ var init_snap_resolver = __esm({
|
|
|
272
299
|
this.allowedIds = allowed ?? raw.map((p) => p.id);
|
|
273
300
|
this.recompute();
|
|
274
301
|
}
|
|
275
|
-
/**
|
|
276
|
-
* Re-resolve raw → resolved against the current viewport / fit-measure,
|
|
277
|
-
* recompute maxAxisSize, invalidate range cache, notify engine. Called on
|
|
278
|
-
* resize, orientationchange, visualViewport changes, and snap-list swaps.
|
|
279
|
-
*/
|
|
280
302
|
recompute() {
|
|
281
303
|
this.resolved = resolveSnapList(this.raw, this.mode, this.measureFit);
|
|
282
304
|
this.maxAxisSize = this.resolved.reduce(
|
|
@@ -286,15 +308,10 @@ var init_snap_resolver = __esm({
|
|
|
286
308
|
this.rangeCache = null;
|
|
287
309
|
this.onMaxAxisSizeChange?.(this.maxAxisSize);
|
|
288
310
|
}
|
|
289
|
-
/** Replace the raw snap list and recompute. Engine calls on `setSnapPoints`. */
|
|
290
311
|
setRaw(raw) {
|
|
291
312
|
this.raw = raw;
|
|
292
313
|
this.recompute();
|
|
293
314
|
}
|
|
294
|
-
/**
|
|
295
|
-
* Replace the allow-list. Range cache depends on `(resolvedSnaps ×
|
|
296
|
-
* allowedIds)` so flipping ids alone invalidates without a full recompute.
|
|
297
|
-
*/
|
|
298
315
|
setAllowedIds(ids) {
|
|
299
316
|
this.allowedIds = ids;
|
|
300
317
|
this.rangeCache = null;
|
|
@@ -302,11 +319,6 @@ var init_snap_resolver = __esm({
|
|
|
302
319
|
findById(id) {
|
|
303
320
|
return findById(id, this.resolved);
|
|
304
321
|
}
|
|
305
|
-
/**
|
|
306
|
-
* Pure drag-settle target resolution. Engine passes the gesture-specific
|
|
307
|
-
* fields (delta, velocity, pointerKind, flickVelocity, dragThreshold) plus
|
|
308
|
-
* `size`/`activeId` since those are still engine-owned.
|
|
309
|
-
*/
|
|
310
322
|
findDragSettleTarget(args) {
|
|
311
323
|
return findDragSettleTarget({
|
|
312
324
|
...args,
|
|
@@ -315,18 +327,9 @@ var init_snap_resolver = __esm({
|
|
|
315
327
|
maxAxisSize: this.maxAxisSize
|
|
316
328
|
});
|
|
317
329
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Memoised allowed-range query. Twice-per-drag-frame (gesture onMove +
|
|
320
|
-
* applySize→computeProgress); cache collapses 2× iteration of resolvedSnaps
|
|
321
|
-
* to one recompute per geometry/allow-list change.
|
|
322
|
-
*/
|
|
323
330
|
getAllowedRange() {
|
|
324
331
|
return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
|
|
325
332
|
}
|
|
326
|
-
/**
|
|
327
|
-
* Read-only view of resolved snaps. Returned as `readonly` to discourage
|
|
328
|
-
* accidental mutation by feature install sites that close over this list.
|
|
329
|
-
*/
|
|
330
333
|
getResolvedSnaps() {
|
|
331
334
|
return this.resolved;
|
|
332
335
|
}
|
|
@@ -336,18 +339,6 @@ var init_snap_resolver = __esm({
|
|
|
336
339
|
getMaxAxisSize() {
|
|
337
340
|
return this.maxAxisSize;
|
|
338
341
|
}
|
|
339
|
-
/**
|
|
340
|
-
* External clamp override. The resize-observer feature compares
|
|
341
|
-
* `getMaxAxisSize()` against the live viewport and shrinks if the largest
|
|
342
|
-
* resolved snap exceeds it (mobile address-bar collapse / soft-keyboard
|
|
343
|
-
* window resize). The next `recompute()` overwrites this clamp from the
|
|
344
|
-
* resolved-snap reduction.
|
|
345
|
-
*
|
|
346
|
-
* Does NOT invalidate the range cache — viewport clamping doesn't change
|
|
347
|
-
* `(resolvedSnaps × allowedIds) → range`. DOES fire `onMaxAxisSizeChange`
|
|
348
|
-
* so the resolver owns the layout-axis style write end-to-end (symmetric
|
|
349
|
-
* with `recompute()` — feature call sites no longer duplicate the write).
|
|
350
|
-
*/
|
|
351
342
|
setMaxAxisSize(n) {
|
|
352
343
|
this.maxAxisSize = n;
|
|
353
344
|
this.onMaxAxisSizeChange?.(n);
|
|
@@ -400,14 +391,17 @@ function installAutoCollapse(deps) {
|
|
|
400
391
|
const fire = () => {
|
|
401
392
|
if (deps.isDestroyed()) return;
|
|
402
393
|
if (deps.isDragging()) return;
|
|
403
|
-
const
|
|
404
|
-
|
|
394
|
+
const active = deps.resolveSnap(deps.getActiveId());
|
|
395
|
+
if (!active || active.size === 0) return;
|
|
396
|
+
let target = null;
|
|
397
|
+
for (const id of deps.getAllowedIds()) {
|
|
405
398
|
const snap = deps.resolveSnap(id);
|
|
406
|
-
|
|
407
|
-
|
|
399
|
+
if (!snap || snap.size <= 0) continue;
|
|
400
|
+
if (!target || snap.size < target.size) target = snap;
|
|
401
|
+
}
|
|
408
402
|
if (!target) return;
|
|
409
|
-
if (target ===
|
|
410
|
-
deps.snapTo(target);
|
|
403
|
+
if (target.id === active.id || target.size >= active.size) return;
|
|
404
|
+
deps.snapTo(target.id);
|
|
411
405
|
};
|
|
412
406
|
const reset = () => {
|
|
413
407
|
clearTimer();
|
|
@@ -418,6 +412,7 @@ function installAutoCollapse(deps) {
|
|
|
418
412
|
}, ms);
|
|
419
413
|
};
|
|
420
414
|
const unsubscribe = deps.on("snap", () => reset());
|
|
415
|
+
if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
|
|
421
416
|
return () => {
|
|
422
417
|
clearTimer();
|
|
423
418
|
unsubscribe();
|
|
@@ -434,6 +429,7 @@ function notifyLinkedSheets(sheets, self) {
|
|
|
434
429
|
for (const linked of sheets) {
|
|
435
430
|
if (linked === self) continue;
|
|
436
431
|
try {
|
|
432
|
+
if (linked.state.size === 0) continue;
|
|
437
433
|
const ids = linked.getAllowedIds();
|
|
438
434
|
const target = ids.find((id) => id !== "closed");
|
|
439
435
|
if (target && target !== linked.state.activeId) {
|
|
@@ -484,7 +480,7 @@ function installRoute(deps) {
|
|
|
484
480
|
const onPop = (event) => {
|
|
485
481
|
if (deps.isDestroyed()) return;
|
|
486
482
|
const state = event.state;
|
|
487
|
-
if (state
|
|
483
|
+
if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
|
|
488
484
|
routePushed = false;
|
|
489
485
|
void deps.close();
|
|
490
486
|
}
|
|
@@ -652,13 +648,17 @@ function installResizeObserver(deps) {
|
|
|
652
648
|
if (viewportSize2 > 0 && viewportSize2 < max) {
|
|
653
649
|
deps.setMaxAxisSize(viewportSize2);
|
|
654
650
|
}
|
|
651
|
+
const wasAnimating = deps.isAnimating?.() ?? false;
|
|
655
652
|
deps.cancelInFlight();
|
|
656
653
|
deps.newCycle();
|
|
657
654
|
const current = deps.resolveActiveSnap();
|
|
658
655
|
const newMax = deps.getMaxAxisSize();
|
|
659
656
|
const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
|
|
660
657
|
deps.setSize(targetSize);
|
|
661
|
-
if (current && !deps.isDragging())
|
|
658
|
+
if (current && !deps.isDragging()) {
|
|
659
|
+
deps.applySize(targetSize);
|
|
660
|
+
if (wasAnimating) deps.resyncAfterCancel?.();
|
|
661
|
+
}
|
|
662
662
|
};
|
|
663
663
|
let resizeObserver = null;
|
|
664
664
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -771,36 +771,17 @@ var init_opacity_dedup = __esm({
|
|
|
771
771
|
constructor() {
|
|
772
772
|
__publicField(this, "last", -1);
|
|
773
773
|
}
|
|
774
|
-
/**
|
|
775
|
-
* Returns `true` (and updates the cached value) if `next` differs from the
|
|
776
|
-
* cached `last` by more than `epsilon`. Returns `false` (and leaves the
|
|
777
|
-
* cache untouched) when the delta is within epsilon — caller skips the
|
|
778
|
-
* CSSOM write.
|
|
779
|
-
*/
|
|
780
774
|
shouldWrite(next, epsilon) {
|
|
781
775
|
if (Math.abs(next - this.last) <= epsilon) return false;
|
|
782
776
|
this.last = next;
|
|
783
777
|
return true;
|
|
784
778
|
}
|
|
785
|
-
/**
|
|
786
|
-
* Force the next `shouldWrite` to pass. Resets the cache to `-1` so any
|
|
787
|
-
* non-negative `next` exceeds the epsilon comparison. Called from setters
|
|
788
|
-
* that mutate state under the cache (range/mode/enabled flips that change
|
|
789
|
-
* the progress→opacity mapping but don't move `progress` itself).
|
|
790
|
-
*/
|
|
791
779
|
invalidate() {
|
|
792
780
|
this.last = -1;
|
|
793
781
|
}
|
|
794
|
-
/**
|
|
795
|
-
* Record `value` as the last-written without performing a write. Used
|
|
796
|
-
* when a setter writes a literal value through a different path (e.g.
|
|
797
|
-
* `setScrimMode("off")` writes `opacity: "0"` inline) and wants future
|
|
798
|
-
* `shouldWrite` calls to short-circuit against that known cached state.
|
|
799
|
-
*/
|
|
800
782
|
setLastWritten(value) {
|
|
801
783
|
this.last = value;
|
|
802
784
|
}
|
|
803
|
-
/** Read-only accessor for diagnostic / dedup-aware peek. */
|
|
804
785
|
get value() {
|
|
805
786
|
return this.last;
|
|
806
787
|
}
|
|
@@ -808,45 +789,131 @@ var init_opacity_dedup = __esm({
|
|
|
808
789
|
}
|
|
809
790
|
});
|
|
810
791
|
|
|
792
|
+
// src/core/primitives/devWarn.ts
|
|
793
|
+
function devWarn(...args) {
|
|
794
|
+
if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
|
|
795
|
+
console.warn(...args);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
var init_devWarn = __esm({
|
|
799
|
+
"src/core/primitives/devWarn.ts"() {
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
// src/core/primitives/overlay-position.ts
|
|
804
|
+
function resolveSheetAnchoredStyle(mode, position, inset) {
|
|
805
|
+
const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
|
|
806
|
+
switch (mode) {
|
|
807
|
+
case "bottom":
|
|
808
|
+
if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
|
|
809
|
+
if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
|
|
810
|
+
return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
|
|
811
|
+
case "top":
|
|
812
|
+
if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
|
|
813
|
+
if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
|
|
814
|
+
return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
|
|
815
|
+
case "left":
|
|
816
|
+
if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
|
|
817
|
+
if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
|
|
818
|
+
return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
|
|
819
|
+
case "right":
|
|
820
|
+
if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
|
|
821
|
+
if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
|
|
822
|
+
return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
function applyOverlayPosition(ws, mode, position, inset) {
|
|
826
|
+
switch (position) {
|
|
827
|
+
case "top-left":
|
|
828
|
+
ws.top = inset;
|
|
829
|
+
ws.left = inset;
|
|
830
|
+
break;
|
|
831
|
+
case "top-center":
|
|
832
|
+
ws.top = inset;
|
|
833
|
+
ws.left = "50%";
|
|
834
|
+
ws.transform = "translateX(-50%)";
|
|
835
|
+
break;
|
|
836
|
+
case "top-right":
|
|
837
|
+
ws.top = inset;
|
|
838
|
+
ws.right = inset;
|
|
839
|
+
break;
|
|
840
|
+
case "center-left":
|
|
841
|
+
ws.top = "50%";
|
|
842
|
+
ws.left = inset;
|
|
843
|
+
ws.transform = "translateY(-50%)";
|
|
844
|
+
break;
|
|
845
|
+
case "center":
|
|
846
|
+
ws.top = "50%";
|
|
847
|
+
ws.left = "50%";
|
|
848
|
+
ws.transform = "translate(-50%, -50%)";
|
|
849
|
+
break;
|
|
850
|
+
case "center-right":
|
|
851
|
+
ws.top = "50%";
|
|
852
|
+
ws.right = inset;
|
|
853
|
+
ws.transform = "translateY(-50%)";
|
|
854
|
+
break;
|
|
855
|
+
case "bottom-left":
|
|
856
|
+
ws.bottom = inset;
|
|
857
|
+
ws.left = inset;
|
|
858
|
+
break;
|
|
859
|
+
case "bottom-center":
|
|
860
|
+
ws.bottom = inset;
|
|
861
|
+
ws.left = "50%";
|
|
862
|
+
ws.transform = "translateX(-50%)";
|
|
863
|
+
break;
|
|
864
|
+
case "bottom-right":
|
|
865
|
+
ws.bottom = inset;
|
|
866
|
+
ws.right = inset;
|
|
867
|
+
break;
|
|
868
|
+
case "sheet-top-left":
|
|
869
|
+
case "sheet-top-center":
|
|
870
|
+
case "sheet-top-right": {
|
|
871
|
+
const style = resolveSheetAnchoredStyle(mode, position, inset);
|
|
872
|
+
if (style.top !== void 0) ws.top = style.top;
|
|
873
|
+
if (style.bottom !== void 0) ws.bottom = style.bottom;
|
|
874
|
+
if (style.left !== void 0) ws.left = style.left;
|
|
875
|
+
if (style.right !== void 0) ws.right = style.right;
|
|
876
|
+
if (style.transform !== void 0) ws.transform = style.transform;
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
var init_overlay_position = __esm({
|
|
882
|
+
"src/core/primitives/overlay-position.ts"() {
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
|
|
811
886
|
// src/core/controllers/scrim-controller.ts
|
|
812
|
-
var ScrimController;
|
|
887
|
+
var VALID_CSS_LENGTH, ScrimController;
|
|
813
888
|
var init_scrim_controller = __esm({
|
|
814
889
|
"src/core/controllers/scrim-controller.ts"() {
|
|
815
890
|
init_types();
|
|
816
891
|
init_snap_points();
|
|
817
892
|
init_hot_path_thresholds();
|
|
818
893
|
init_opacity_dedup();
|
|
894
|
+
init_devWarn();
|
|
895
|
+
init_overlay_position();
|
|
896
|
+
VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
|
|
819
897
|
ScrimController = class {
|
|
820
898
|
constructor(deps, opts) {
|
|
821
|
-
// ── DOM refs (mirror engine's; cached for hot path) ─────────────────
|
|
822
899
|
__publicField(this, "screenComponent");
|
|
823
900
|
__publicField(this, "backdrop");
|
|
824
901
|
__publicField(this, "mode");
|
|
825
|
-
// ── Engine callbacks ────────────────────────────────────────────────
|
|
826
902
|
__publicField(this, "isDestroyed");
|
|
827
903
|
__publicField(this, "isTopSheet");
|
|
828
904
|
__publicField(this, "getAllowedIds");
|
|
829
905
|
__publicField(this, "getResolvedSnaps");
|
|
830
906
|
__publicField(this, "snapToFn");
|
|
831
|
-
|
|
907
|
+
__publicField(this, "closeFn");
|
|
908
|
+
__publicField(this, "scrimInteractive", false);
|
|
832
909
|
__publicField(this, "scrimMode");
|
|
833
910
|
__publicField(this, "backdropRange");
|
|
834
911
|
__publicField(this, "screenRange");
|
|
835
|
-
// setScrimEnabled toggle — gated through `scrimEnabled` rather than nuking
|
|
836
|
-
// ranges so re-enable can restore prior values without a remount. Public
|
|
837
|
-
// so tests / introspection can read it without an `as any` cast.
|
|
838
912
|
__publicField(this, "scrimEnabled", true);
|
|
839
913
|
__publicField(this, "savedScrimRanges", null);
|
|
840
|
-
// ── Tap-to-close ────────────────────────────────────────────────────
|
|
841
914
|
__publicField(this, "scrimTapToCloseEnabled", false);
|
|
842
915
|
__publicField(this, "detachScrimTap", null);
|
|
843
|
-
// ── Scrim overlay slot (positioned floating element) ────────────────
|
|
844
916
|
__publicField(this, "scrimOverlayTeardown", null);
|
|
845
|
-
// ── Hot-path opacity dedup caches (per-frame writes) ────────────────
|
|
846
|
-
// Numeric sentinels migrated to WriteSentinel primitive — single
|
|
847
|
-
// invalidation surface, shared with the engine's size/progress sentinels.
|
|
848
|
-
// The two string-typed sentinels (`Pointer`, `Display`) keep their inline
|
|
849
|
-
// pattern: equality on string union, no epsilon, different reset semantics.
|
|
850
917
|
__publicField(this, "backdropOpacitySentinel", new WriteSentinel());
|
|
851
918
|
__publicField(this, "screenOpacitySentinel", new WriteSentinel());
|
|
852
919
|
__publicField(this, "lastBackdropPointer", "");
|
|
@@ -859,6 +926,7 @@ var init_scrim_controller = __esm({
|
|
|
859
926
|
this.getAllowedIds = deps.getAllowedIds;
|
|
860
927
|
this.getResolvedSnaps = deps.getResolvedSnaps;
|
|
861
928
|
this.snapToFn = deps.snapTo;
|
|
929
|
+
this.closeFn = deps.close;
|
|
862
930
|
this.scrimMode = opts.scrimMode ?? "full";
|
|
863
931
|
const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
|
|
864
932
|
this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
|
|
@@ -881,41 +949,15 @@ var init_scrim_controller = __esm({
|
|
|
881
949
|
}
|
|
882
950
|
}
|
|
883
951
|
}
|
|
884
|
-
/**
|
|
885
|
-
* Invalidate the opacity dedup caches. Called from any setter that mutates
|
|
886
|
-
* range/mode/enabled — opacity is a pure function of (progress, range,
|
|
887
|
-
* enabled), so a config change makes the cached values stale even if
|
|
888
|
-
* `progress` itself didn't move. Next `applyOpacity(progress, false)` call
|
|
889
|
-
* sees the -1 sentinel and re-runs the math instead of short-circuiting.
|
|
890
|
-
*
|
|
891
|
-
* Public for the engine: `setSnapPoints` mutates resolved geometry which
|
|
892
|
-
* shifts `progress→opacity` mapping under the cache; engine calls this to
|
|
893
|
-
* keep the next render frame correct on a static sheet.
|
|
894
|
-
* @internal
|
|
895
|
-
*/
|
|
896
952
|
invalidateOpacityCache() {
|
|
897
953
|
this.backdropOpacitySentinel.invalidate();
|
|
898
954
|
this.screenOpacitySentinel.invalidate();
|
|
899
955
|
}
|
|
900
|
-
/** Engine calls this when its `attach()` runs — installs scrim-tap-click if requested. */
|
|
901
956
|
attach() {
|
|
902
957
|
if (this.scrimTapToCloseEnabled) {
|
|
903
958
|
this.setScrimTapToClose(true);
|
|
904
959
|
}
|
|
905
960
|
}
|
|
906
|
-
// ── Hot-path: opacity computation called per-frame from engine.applySize ──
|
|
907
|
-
/**
|
|
908
|
-
* Per-frame opacity writer. Computes backdrop + screen opacity from the
|
|
909
|
-
* sheet's progress (0..1 within allowed range), gated by sub-pixel dedup
|
|
910
|
-
* caches so steady-state frames don't trigger style recalc.
|
|
911
|
-
*
|
|
912
|
-
* `progressChanged` is the engine's already-computed dedup gate — when
|
|
913
|
-
* `false`, opacity is by construction a pure function of `progress` so
|
|
914
|
-
* cached values are still valid; we short-circuit the Math.min/max +
|
|
915
|
-
* division work entirely. Setters that mutate ranges/mode/enabled
|
|
916
|
-
* invalidate the cache by writing -1 sentinels so the next call re-runs
|
|
917
|
-
* the math.
|
|
918
|
-
*/
|
|
919
961
|
applyOpacity(progress, progressChanged) {
|
|
920
962
|
if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
|
|
921
963
|
return;
|
|
@@ -961,80 +1003,30 @@ var init_scrim_controller = __esm({
|
|
|
961
1003
|
}
|
|
962
1004
|
}
|
|
963
1005
|
}
|
|
964
|
-
// ── Public setters (engine forwards these 1:1) ─────────────────────
|
|
965
|
-
/**
|
|
966
|
-
* Replace the [start, end] progress window in which the backdrop fades 0→1.
|
|
967
|
-
* @param range - Tuple of two progress values in 0..1 (start <= end).
|
|
968
|
-
* @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
|
|
969
|
-
* @example
|
|
970
|
-
* // Backdrop only starts darkening after the sheet crosses 30% open.
|
|
971
|
-
* engine.setBackdropRange([0.3, 1]);
|
|
972
|
-
*/
|
|
973
1006
|
setBackdropRange(range, applySize) {
|
|
974
1007
|
if (this.isDestroyed()) return;
|
|
975
1008
|
this.backdropRange = range;
|
|
976
1009
|
this.invalidateOpacityCache();
|
|
977
1010
|
applySize();
|
|
978
1011
|
}
|
|
979
|
-
/**
|
|
980
|
-
* Replace the [start, end] progress window in which the screen scrim fades 0→1.
|
|
981
|
-
* @param range - Tuple of two progress values in 0..1 (start <= end).
|
|
982
|
-
* @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
|
|
983
|
-
* @example
|
|
984
|
-
* // Scrim is fully opaque only when the sheet is past 50% — softer feel.
|
|
985
|
-
* engine.setScreenRange([0, 0.5]);
|
|
986
|
-
*/
|
|
987
1012
|
setScreenRange(range, applySize) {
|
|
988
1013
|
if (this.isDestroyed()) return;
|
|
989
1014
|
this.screenRange = range;
|
|
990
1015
|
this.invalidateOpacityCache();
|
|
991
1016
|
applySize();
|
|
992
1017
|
}
|
|
993
|
-
/**
|
|
994
|
-
* Set the scrim background color. Pass `null`/`undefined` to clear the
|
|
995
|
-
* inline write and let consumer CSS take over.
|
|
996
|
-
* @param color - Any valid CSS color string, or `null`/`undefined` to clear.
|
|
997
|
-
* @example
|
|
998
|
-
* engine.setScrimColor("rgba(0, 0, 0, 0.6)");
|
|
999
|
-
* engine.setScrimColor(null); // hand back to consumer CSS
|
|
1000
|
-
*/
|
|
1001
1018
|
setScrimColor(color) {
|
|
1002
1019
|
if (this.isDestroyed()) return;
|
|
1003
1020
|
this.applyScrimStyles({ color: color === void 0 ? null : color });
|
|
1004
1021
|
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Set the scrim backdrop-filter blur radius. Pass `null`/`undefined` to clear.
|
|
1007
|
-
* @param blur - CSS length (e.g. `"8px"`) consumed as `blur(<value>)`, or
|
|
1008
|
-
* `null`/`undefined` to remove the filter.
|
|
1009
|
-
* @example
|
|
1010
|
-
* engine.setScrimBlur("12px"); // frosted-glass effect
|
|
1011
|
-
* engine.setScrimBlur(null); // disable blur
|
|
1012
|
-
*/
|
|
1013
1022
|
setScrimBlur(blur) {
|
|
1014
1023
|
if (this.isDestroyed()) return;
|
|
1015
1024
|
this.applyScrimStyles({ blur: blur === void 0 ? null : blur });
|
|
1016
1025
|
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Toggle whether the scrim layer captures pointer events.
|
|
1019
|
-
* @param interactive - `true` → `pointer-events: auto`; `false` → `none`.
|
|
1020
|
-
* @example
|
|
1021
|
-
* engine.setScrimInteractive(true); // scrim swallows clicks
|
|
1022
|
-
* engine.setScrimInteractive(false); // clicks pass through to content below
|
|
1023
|
-
*/
|
|
1024
1026
|
setScrimInteractive(interactive) {
|
|
1025
1027
|
if (this.isDestroyed()) return;
|
|
1026
1028
|
this.applyScrimStyles({ interactive });
|
|
1027
1029
|
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Switch scrim positioning at runtime.
|
|
1030
|
-
* @param mode - `"full"` (consumer CSS), `"above-sheet"` (engine-pinned inset
|
|
1031
|
-
* so only the area NOT covered by the sheet is dimmed), or
|
|
1032
|
-
* `"off"` (display:none + opacity:0 — no remount needed).
|
|
1033
|
-
* @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
|
|
1034
|
-
* @example
|
|
1035
|
-
* engine.setScrimMode("above-sheet"); // dim only the area above the sheet
|
|
1036
|
-
* engine.setScrimMode("off"); // disable scrim layer
|
|
1037
|
-
*/
|
|
1038
1030
|
setScrimMode(mode, applySize) {
|
|
1039
1031
|
if (this.isDestroyed()) return;
|
|
1040
1032
|
if (mode === this.scrimMode) return;
|
|
@@ -1064,15 +1056,6 @@ var init_scrim_controller = __esm({
|
|
|
1064
1056
|
}
|
|
1065
1057
|
applySize();
|
|
1066
1058
|
}
|
|
1067
|
-
/**
|
|
1068
|
-
* Enable or disable click-on-scrim → snap to first non-zero allowed snap.
|
|
1069
|
-
* Auto-promotes `pointer-events: auto` when scrim is non-interactive so the
|
|
1070
|
-
* click listener actually fires.
|
|
1071
|
-
* @param enabled - `true` installs the click listener; `false` removes it.
|
|
1072
|
-
* @example
|
|
1073
|
-
* engine.setScrimTapToClose(true); // tap outside sheet collapses it
|
|
1074
|
-
* engine.setScrimTapToClose(false); // require explicit close button
|
|
1075
|
-
*/
|
|
1076
1059
|
setScrimTapToClose(enabled) {
|
|
1077
1060
|
if (this.isDestroyed()) return;
|
|
1078
1061
|
if (!this.screenComponent) return;
|
|
@@ -1085,11 +1068,15 @@ var init_scrim_controller = __esm({
|
|
|
1085
1068
|
}
|
|
1086
1069
|
const onClick = () => {
|
|
1087
1070
|
if (this.isDestroyed()) return;
|
|
1071
|
+
if (this.closeFn) {
|
|
1072
|
+
this.closeFn();
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1088
1075
|
const allowed = this.getAllowedIds();
|
|
1089
1076
|
const snaps = this.getResolvedSnaps();
|
|
1090
1077
|
const fallback = allowed.find((id) => {
|
|
1091
1078
|
const snap = findById(id, snaps);
|
|
1092
|
-
return snap !== null && snap.size
|
|
1079
|
+
return snap !== null && snap.size === 0;
|
|
1093
1080
|
});
|
|
1094
1081
|
if (fallback) this.snapToFn(fallback);
|
|
1095
1082
|
};
|
|
@@ -1102,20 +1089,11 @@ var init_scrim_controller = __esm({
|
|
|
1102
1089
|
this.scrimTapToCloseEnabled = false;
|
|
1103
1090
|
}
|
|
1104
1091
|
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Toggle the scrim layer without remounting. Disabling stashes current
|
|
1107
|
-
* `screenRange`/`backdropRange` and degenerates them to `[1, 1]` (opacity
|
|
1108
|
-
* formula → 0 across the whole range); re-enabling restores the saved values.
|
|
1109
|
-
* @param enabled - `false` hides scrim, `true` restores prior ranges.
|
|
1110
|
-
* @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
|
|
1111
|
-
* @example
|
|
1112
|
-
* engine.setScrimEnabled(false); // hide scrim, ranges remembered
|
|
1113
|
-
* engine.setScrimEnabled(true); // restore prior ranges (no remount)
|
|
1114
|
-
*/
|
|
1115
1092
|
setScrimEnabled(enabled, applySize) {
|
|
1116
1093
|
if (this.isDestroyed()) return;
|
|
1117
1094
|
if (enabled === this.scrimEnabled) return;
|
|
1118
1095
|
this.scrimEnabled = enabled;
|
|
1096
|
+
this.invalidateOpacityCache();
|
|
1119
1097
|
if (!enabled) {
|
|
1120
1098
|
this.savedScrimRanges = {
|
|
1121
1099
|
screen: this.screenRange,
|
|
@@ -1130,20 +1108,6 @@ var init_scrim_controller = __esm({
|
|
|
1130
1108
|
}
|
|
1131
1109
|
applySize();
|
|
1132
1110
|
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Batch-update multiple scrim fields with a single applySize() at the end.
|
|
1135
|
-
* If `preset` is set, it seeds color/blur/interactive/range first; explicit
|
|
1136
|
-
* fields override.
|
|
1137
|
-
* @param opts - Partial scrim config: preset, mode, enabled, tapToClose,
|
|
1138
|
-
* color, blur, interactive, range.
|
|
1139
|
-
* @param applySize - Engine-supplied recompute hook; runs ONCE per call.
|
|
1140
|
-
* @example
|
|
1141
|
-
* engine.setScrim({
|
|
1142
|
-
* preset: "frosted",
|
|
1143
|
-
* mode: "above-sheet",
|
|
1144
|
-
* tapToClose: true,
|
|
1145
|
-
* });
|
|
1146
|
-
*/
|
|
1147
1111
|
setScrim(opts, applySize) {
|
|
1148
1112
|
if (this.isDestroyed()) return;
|
|
1149
1113
|
if (opts.preset) {
|
|
@@ -1153,7 +1117,12 @@ var init_scrim_controller = __esm({
|
|
|
1153
1117
|
blur: cfg.blur ?? null,
|
|
1154
1118
|
interactive: cfg.interactive
|
|
1155
1119
|
});
|
|
1156
|
-
this.
|
|
1120
|
+
if (!this.scrimEnabled && this.savedScrimRanges) {
|
|
1121
|
+
this.savedScrimRanges = { screen: cfg.range, backdrop: cfg.range };
|
|
1122
|
+
} else {
|
|
1123
|
+
this.screenRange = cfg.range;
|
|
1124
|
+
this.backdropRange = cfg.range;
|
|
1125
|
+
}
|
|
1157
1126
|
}
|
|
1158
1127
|
const noop = () => {
|
|
1159
1128
|
};
|
|
@@ -1166,27 +1135,18 @@ var init_scrim_controller = __esm({
|
|
|
1166
1135
|
interactive: opts.interactive
|
|
1167
1136
|
});
|
|
1168
1137
|
if (opts.range !== void 0) {
|
|
1169
|
-
this.
|
|
1138
|
+
if (!this.scrimEnabled && this.savedScrimRanges) {
|
|
1139
|
+
this.savedScrimRanges = {
|
|
1140
|
+
screen: opts.range,
|
|
1141
|
+
backdrop: this.savedScrimRanges.backdrop
|
|
1142
|
+
};
|
|
1143
|
+
} else {
|
|
1144
|
+
this.screenRange = opts.range;
|
|
1145
|
+
}
|
|
1170
1146
|
}
|
|
1171
1147
|
this.invalidateOpacityCache();
|
|
1172
1148
|
applySize();
|
|
1173
1149
|
}
|
|
1174
|
-
/**
|
|
1175
|
-
* Inject a positioned floating element into the scrim layer (e.g. a close
|
|
1176
|
-
* button anchored top-right). Replaces any prior overlay so repeated calls
|
|
1177
|
-
* (reactive effects re-running) don't accumulate wrappers.
|
|
1178
|
-
* @param opts - Children element, optional `inset` (default `"16px"`),
|
|
1179
|
-
* `position` (9-way grid, default `"top-right"`), and
|
|
1180
|
-
* `interactive` flag (default `true`).
|
|
1181
|
-
* @returns Teardown function that removes the wrapper from the DOM.
|
|
1182
|
-
* @example
|
|
1183
|
-
* const teardown = engine.setScrimOverlay({
|
|
1184
|
-
* children: closeBtn,
|
|
1185
|
-
* position: "top-right",
|
|
1186
|
-
* inset: "20px",
|
|
1187
|
-
* });
|
|
1188
|
-
* // later: teardown(); // remove the floating button
|
|
1189
|
-
*/
|
|
1190
1150
|
setScrimOverlay(opts) {
|
|
1191
1151
|
if (this.isDestroyed()) return () => {
|
|
1192
1152
|
};
|
|
@@ -1194,61 +1154,38 @@ var init_scrim_controller = __esm({
|
|
|
1194
1154
|
};
|
|
1195
1155
|
this.scrimOverlayTeardown?.();
|
|
1196
1156
|
const screen = this.screenComponent;
|
|
1197
|
-
const
|
|
1157
|
+
const insetRaw = opts.inset ?? "16px";
|
|
1158
|
+
const inset = VALID_CSS_LENGTH.test(insetRaw) ? insetRaw : "16px";
|
|
1159
|
+
if (inset !== insetRaw) {
|
|
1160
|
+
devWarn(
|
|
1161
|
+
`[bottom-sheet] setScrimOverlay: invalid inset ${JSON.stringify(insetRaw)}; expected a CSS length like "16px" / "1rem" / "5%". Falling back to "16px".`
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
if (opts.children.ownerDocument && opts.children.ownerDocument !== screen.ownerDocument) {
|
|
1165
|
+
devWarn(
|
|
1166
|
+
"[bottom-sheet] setScrimOverlay: children belong to a different document than the scrim. Cross-document adoption can drop event listeners; construct children via the same document as the sheet root."
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1198
1169
|
const position = opts.position ?? "top-right";
|
|
1199
1170
|
const interactive = opts.interactive ?? true;
|
|
1200
1171
|
const wrapper = screen.ownerDocument.createElement("div");
|
|
1201
1172
|
wrapper.className = "bs-scrim-overlay";
|
|
1202
1173
|
const ws = wrapper.style;
|
|
1203
1174
|
ws.position = "absolute";
|
|
1204
|
-
|
|
1205
|
-
case "top-left":
|
|
1206
|
-
ws.top = inset;
|
|
1207
|
-
ws.left = inset;
|
|
1208
|
-
break;
|
|
1209
|
-
case "top-center":
|
|
1210
|
-
ws.top = inset;
|
|
1211
|
-
ws.left = "50%";
|
|
1212
|
-
ws.transform = "translateX(-50%)";
|
|
1213
|
-
break;
|
|
1214
|
-
case "top-right":
|
|
1215
|
-
ws.top = inset;
|
|
1216
|
-
ws.right = inset;
|
|
1217
|
-
break;
|
|
1218
|
-
case "center-left":
|
|
1219
|
-
ws.top = "50%";
|
|
1220
|
-
ws.left = inset;
|
|
1221
|
-
ws.transform = "translateY(-50%)";
|
|
1222
|
-
break;
|
|
1223
|
-
case "center":
|
|
1224
|
-
ws.top = "50%";
|
|
1225
|
-
ws.left = "50%";
|
|
1226
|
-
ws.transform = "translate(-50%, -50%)";
|
|
1227
|
-
break;
|
|
1228
|
-
case "center-right":
|
|
1229
|
-
ws.top = "50%";
|
|
1230
|
-
ws.right = inset;
|
|
1231
|
-
ws.transform = "translateY(-50%)";
|
|
1232
|
-
break;
|
|
1233
|
-
case "bottom-left":
|
|
1234
|
-
ws.bottom = inset;
|
|
1235
|
-
ws.left = inset;
|
|
1236
|
-
break;
|
|
1237
|
-
case "bottom-center":
|
|
1238
|
-
ws.bottom = inset;
|
|
1239
|
-
ws.left = "50%";
|
|
1240
|
-
ws.transform = "translateX(-50%)";
|
|
1241
|
-
break;
|
|
1242
|
-
case "bottom-right":
|
|
1243
|
-
ws.bottom = inset;
|
|
1244
|
-
ws.right = inset;
|
|
1245
|
-
break;
|
|
1246
|
-
}
|
|
1175
|
+
applyOverlayPosition(ws, this.mode, position, inset);
|
|
1247
1176
|
if (interactive) {
|
|
1248
1177
|
ws.pointerEvents = "auto";
|
|
1249
1178
|
}
|
|
1250
1179
|
wrapper.appendChild(opts.children);
|
|
1251
|
-
screen.
|
|
1180
|
+
const host = screen.parentElement;
|
|
1181
|
+
if (!host) {
|
|
1182
|
+
devWarn(
|
|
1183
|
+
"[bottom-sheet] setScrimOverlay: scrim element has no parent, skipping overlay injection. Mount the sheet root before calling setScrimOverlay."
|
|
1184
|
+
);
|
|
1185
|
+
return () => {
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
host.appendChild(wrapper);
|
|
1252
1189
|
const teardown = () => {
|
|
1253
1190
|
if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
|
|
1254
1191
|
if (this.scrimOverlayTeardown === teardown) {
|
|
@@ -1258,7 +1195,6 @@ var init_scrim_controller = __esm({
|
|
|
1258
1195
|
this.scrimOverlayTeardown = teardown;
|
|
1259
1196
|
return teardown;
|
|
1260
1197
|
}
|
|
1261
|
-
/** Tear down toggle-state listeners + remove any injected overlay wrapper. */
|
|
1262
1198
|
destroy() {
|
|
1263
1199
|
this.detachScrimTap?.();
|
|
1264
1200
|
this.detachScrimTap = null;
|
|
@@ -1277,8 +1213,6 @@ var init_scrim_controller = __esm({
|
|
|
1277
1213
|
this.lastBackdropPointer = "";
|
|
1278
1214
|
this.lastScreenDisplay = "";
|
|
1279
1215
|
}
|
|
1280
|
-
// ── Private helpers ────────────────────────────────────────────────
|
|
1281
|
-
/** Centralized scrim style writer — atomic 3-property write across all setters. */
|
|
1282
1216
|
applyScrimStyles(opts) {
|
|
1283
1217
|
if (!this.screenComponent) return;
|
|
1284
1218
|
const s = this.screenComponent.style;
|
|
@@ -1297,10 +1231,10 @@ var init_scrim_controller = __esm({
|
|
|
1297
1231
|
}
|
|
1298
1232
|
}
|
|
1299
1233
|
if (opts.interactive !== void 0) {
|
|
1234
|
+
this.scrimInteractive = opts.interactive;
|
|
1300
1235
|
s.pointerEvents = opts.interactive ? "auto" : "none";
|
|
1301
1236
|
}
|
|
1302
1237
|
}
|
|
1303
|
-
/** Pin the screen overlay to the area NOT occupied by the sheet. */
|
|
1304
1238
|
applyAboveSheetInset() {
|
|
1305
1239
|
if (!this.screenComponent) return;
|
|
1306
1240
|
const insetByMode = {
|
|
@@ -1312,14 +1246,14 @@ var init_scrim_controller = __esm({
|
|
|
1312
1246
|
const s = this.screenComponent.style;
|
|
1313
1247
|
s.position = "fixed";
|
|
1314
1248
|
s.inset = insetByMode[this.mode];
|
|
1315
|
-
s.pointerEvents = "none";
|
|
1249
|
+
s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
|
|
1316
1250
|
}
|
|
1317
1251
|
};
|
|
1318
1252
|
}
|
|
1319
1253
|
});
|
|
1320
1254
|
|
|
1321
1255
|
// src/core/animation/animation.ts
|
|
1322
|
-
var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween;
|
|
1256
|
+
var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween, prefersReducedMotion;
|
|
1323
1257
|
var init_animation = __esm({
|
|
1324
1258
|
"src/core/animation/animation.ts"() {
|
|
1325
1259
|
easeOutBack = (t) => {
|
|
@@ -1371,6 +1305,10 @@ var init_animation = __esm({
|
|
|
1371
1305
|
promise
|
|
1372
1306
|
};
|
|
1373
1307
|
};
|
|
1308
|
+
prefersReducedMotion = () => {
|
|
1309
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
1310
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1311
|
+
};
|
|
1374
1312
|
}
|
|
1375
1313
|
});
|
|
1376
1314
|
|
|
@@ -1406,14 +1344,20 @@ var init_spring = __esm({
|
|
|
1406
1344
|
if (cancelled) return;
|
|
1407
1345
|
const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
|
|
1408
1346
|
lastTime = now;
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1347
|
+
const steps = Math.max(1, Math.ceil(dt * 240));
|
|
1348
|
+
const subDt = dt / steps;
|
|
1349
|
+
let stepsLeft = steps;
|
|
1411
1350
|
while (stepsLeft-- > 0) {
|
|
1412
1351
|
const force = -cfg.stiffness * (x - to) - cfg.damping * v;
|
|
1413
1352
|
const accel = force / cfg.mass;
|
|
1414
1353
|
v += accel * subDt;
|
|
1415
1354
|
x += v * subDt;
|
|
1416
1355
|
}
|
|
1356
|
+
if (!Number.isFinite(x) || !Number.isFinite(v)) {
|
|
1357
|
+
onUpdate(to, 0);
|
|
1358
|
+
resolve();
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1417
1361
|
onUpdate(x, v);
|
|
1418
1362
|
const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
|
|
1419
1363
|
if (isAtRest) {
|
|
@@ -1491,13 +1435,9 @@ var init_animation_runner = __esm({
|
|
|
1491
1435
|
__publicField(this, "duration");
|
|
1492
1436
|
__publicField(this, "easing");
|
|
1493
1437
|
__publicField(this, "respectReducedMotion");
|
|
1494
|
-
/** Live-tracked via mq listener so animateTo skips matchMedia per snap. */
|
|
1495
1438
|
__publicField(this, "reducedMotion", false);
|
|
1496
|
-
/** Disposes the matchMedia change listener — drained from destroy(). */
|
|
1497
1439
|
__publicField(this, "detachReducedMotion", null);
|
|
1498
|
-
/** Computed once — both inputs (option + API presence) stable for life. */
|
|
1499
1440
|
__publicField(this, "viewTransitionsAvailable");
|
|
1500
|
-
/** In-flight handles. Public for tests / debugging only — engine drives via cancel(). */
|
|
1501
1441
|
__publicField(this, "currentTween", null);
|
|
1502
1442
|
__publicField(this, "currentSpring", null);
|
|
1503
1443
|
this.element = deps.element;
|
|
@@ -1528,26 +1468,13 @@ var init_animation_runner = __esm({
|
|
|
1528
1468
|
}
|
|
1529
1469
|
}
|
|
1530
1470
|
}
|
|
1531
|
-
/**
|
|
1532
|
-
* Live derived flag — `true` whenever a spring/tween handle is in flight.
|
|
1533
|
-
* Reading the canonical handles (instead of mirroring into a separate
|
|
1534
|
-
* boolean) eliminates the reduced-motion early-return latent bug where
|
|
1535
|
-
* the flag stayed true forever, and removes the cross-controller
|
|
1536
|
-
* setIsAnimating callback.
|
|
1537
|
-
*/
|
|
1538
1471
|
get isAnimating() {
|
|
1539
1472
|
return this.currentTween !== null || this.currentSpring !== null;
|
|
1540
1473
|
}
|
|
1541
|
-
/** Cancel any in-flight tween/spring without touching engine state. */
|
|
1542
1474
|
cancel() {
|
|
1543
1475
|
this.currentTween?.cancel();
|
|
1544
1476
|
this.currentSpring?.cancel();
|
|
1545
1477
|
}
|
|
1546
|
-
/**
|
|
1547
|
-
* Animate from current size to `target`. Resolves when the animation
|
|
1548
|
-
* completes; resolves early if cancelled mid-flight (caller checks an
|
|
1549
|
-
* abort signal post-await for cycle-invariant correctness).
|
|
1550
|
-
*/
|
|
1551
1478
|
async animateTo(target, velocityPxPerMs) {
|
|
1552
1479
|
if (target === this.getSize()) return;
|
|
1553
1480
|
this.currentTween?.cancel();
|
|
@@ -1588,7 +1515,6 @@ var init_animation_runner = __esm({
|
|
|
1588
1515
|
if (!this.isDragging()) this.element.style.willChange = "auto";
|
|
1589
1516
|
}
|
|
1590
1517
|
}
|
|
1591
|
-
/** Tear down the matchMedia listener. Engine calls from destroy(). */
|
|
1592
1518
|
destroy() {
|
|
1593
1519
|
this.detachReducedMotion?.();
|
|
1594
1520
|
this.detachReducedMotion = null;
|
|
@@ -1600,7 +1526,7 @@ var init_animation_runner = __esm({
|
|
|
1600
1526
|
});
|
|
1601
1527
|
|
|
1602
1528
|
// src/core/lifecycle/focusTrap.ts
|
|
1603
|
-
var FOCUSABLE, isVisible, focusables, installFocusTrap;
|
|
1529
|
+
var FOCUSABLE, isVisible, focusables, trapStack, installFocusTrap;
|
|
1604
1530
|
var init_focusTrap = __esm({
|
|
1605
1531
|
"src/core/lifecycle/focusTrap.ts"() {
|
|
1606
1532
|
FOCUSABLE = [
|
|
@@ -1625,9 +1551,13 @@ var init_focusTrap = __esm({
|
|
|
1625
1551
|
focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
|
|
1626
1552
|
(el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
|
|
1627
1553
|
);
|
|
1554
|
+
trapStack = [];
|
|
1628
1555
|
installFocusTrap = (container, options = {}) => {
|
|
1629
1556
|
if (typeof document === "undefined") return () => {
|
|
1630
1557
|
};
|
|
1558
|
+
const token = {};
|
|
1559
|
+
trapStack.push(token);
|
|
1560
|
+
const isActive = () => trapStack[trapStack.length - 1] === token;
|
|
1631
1561
|
const previouslyFocused = document.activeElement;
|
|
1632
1562
|
const focusInitial = () => {
|
|
1633
1563
|
const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
|
|
@@ -1635,6 +1565,7 @@ var init_focusTrap = __esm({
|
|
|
1635
1565
|
};
|
|
1636
1566
|
focusInitial();
|
|
1637
1567
|
const handleKey = (e) => {
|
|
1568
|
+
if (!isActive()) return;
|
|
1638
1569
|
if (e.key === "Escape" && options.onEscape) {
|
|
1639
1570
|
options.onEscape();
|
|
1640
1571
|
return;
|
|
@@ -1657,6 +1588,7 @@ var init_focusTrap = __esm({
|
|
|
1657
1588
|
}
|
|
1658
1589
|
};
|
|
1659
1590
|
const handleFocusIn = (e) => {
|
|
1591
|
+
if (!isActive()) return;
|
|
1660
1592
|
const target = e.target;
|
|
1661
1593
|
if (target && !container.contains(target)) {
|
|
1662
1594
|
const list = focusables(container);
|
|
@@ -1669,6 +1601,8 @@ var init_focusTrap = __esm({
|
|
|
1669
1601
|
return () => {
|
|
1670
1602
|
if (released) return;
|
|
1671
1603
|
released = true;
|
|
1604
|
+
const idx = trapStack.indexOf(token);
|
|
1605
|
+
if (idx !== -1) trapStack.splice(idx, 1);
|
|
1672
1606
|
document.removeEventListener("keydown", handleKey, true);
|
|
1673
1607
|
document.removeEventListener("focusin", handleFocusIn, true);
|
|
1674
1608
|
if (previouslyFocused && document.contains(previouslyFocused)) {
|
|
@@ -1731,11 +1665,17 @@ function createInertSiblings(rootProvider) {
|
|
|
1731
1665
|
return {
|
|
1732
1666
|
apply() {
|
|
1733
1667
|
if (typeof document === "undefined") return;
|
|
1734
|
-
|
|
1735
|
-
|
|
1668
|
+
let top = rootProvider();
|
|
1669
|
+
while (top.parentElement && top.parentElement !== document.body) {
|
|
1670
|
+
top = top.parentElement;
|
|
1671
|
+
}
|
|
1672
|
+
const parent = top.parentElement ?? document.body;
|
|
1736
1673
|
for (const child of Array.from(parent.children)) {
|
|
1737
|
-
if (child ===
|
|
1674
|
+
if (child === top) continue;
|
|
1738
1675
|
if (child.hasAttribute("inert")) continue;
|
|
1676
|
+
if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1739
1679
|
child.setAttribute("inert", "");
|
|
1740
1680
|
tracked.push(child);
|
|
1741
1681
|
}
|
|
@@ -1761,7 +1701,6 @@ var init_lifecycle_controller = __esm({
|
|
|
1761
1701
|
LifecycleController = class {
|
|
1762
1702
|
constructor(deps, opts) {
|
|
1763
1703
|
__publicField(this, "element");
|
|
1764
|
-
__publicField(this, "closeFn");
|
|
1765
1704
|
__publicField(this, "focusTrapEnabled");
|
|
1766
1705
|
__publicField(this, "initialFocus");
|
|
1767
1706
|
__publicField(this, "closeOnEscape");
|
|
@@ -1770,16 +1709,11 @@ var init_lifecycle_controller = __esm({
|
|
|
1770
1709
|
__publicField(this, "inertSiblings");
|
|
1771
1710
|
__publicField(this, "shouldApplyInertSiblings");
|
|
1772
1711
|
__publicField(this, "returnFocus");
|
|
1773
|
-
/** Toggle-state teardowns — fire on close, NOT in destroy-only stack. */
|
|
1774
1712
|
__publicField(this, "releaseFocusTrap", null);
|
|
1775
1713
|
__publicField(this, "releaseScrollLock", null);
|
|
1776
|
-
/** True between install() and release(). Guards release()/destroy() from
|
|
1777
|
-
* re-running side effects (notably `returnFocus.focus()`) on already-
|
|
1778
|
-
* released or never-installed instances. */
|
|
1779
1714
|
__publicField(this, "installed", false);
|
|
1780
1715
|
__publicField(this, "destroyed", false);
|
|
1781
1716
|
this.element = deps.element;
|
|
1782
|
-
this.closeFn = deps.close;
|
|
1783
1717
|
this.focusTrapEnabled = opts.focusTrap ?? false;
|
|
1784
1718
|
this.initialFocus = opts.initialFocus;
|
|
1785
1719
|
this.closeOnEscape = opts.closeOnEscape ?? true;
|
|
@@ -1789,14 +1723,9 @@ var init_lifecycle_controller = __esm({
|
|
|
1789
1723
|
this.returnFocus = opts.returnFocus;
|
|
1790
1724
|
this.inertSiblings = createInertSiblings(() => this.element);
|
|
1791
1725
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
* doesn't double-install. Sets `installed=true` BEFORE the first side
|
|
1796
|
-
* effect so a partial throw (detached / shadow-DOM target) leaves the
|
|
1797
|
-
* caller's `release()` rollback path armed to undo whatever did succeed.
|
|
1798
|
-
* No-op when destroyed.
|
|
1799
|
-
*/
|
|
1726
|
+
get isInstalled() {
|
|
1727
|
+
return this.installed;
|
|
1728
|
+
}
|
|
1800
1729
|
install() {
|
|
1801
1730
|
if (this.destroyed) return;
|
|
1802
1731
|
this.installed = true;
|
|
@@ -1805,30 +1734,17 @@ var init_lifecycle_controller = __esm({
|
|
|
1805
1734
|
}
|
|
1806
1735
|
if (this.focusTrapEnabled && !this.releaseFocusTrap) {
|
|
1807
1736
|
this.releaseFocusTrap = installFocusTrap(this.element, {
|
|
1808
|
-
initialFocus: this.initialFocus
|
|
1809
|
-
onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
|
|
1737
|
+
initialFocus: this.initialFocus
|
|
1810
1738
|
});
|
|
1811
1739
|
}
|
|
1812
1740
|
if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
|
|
1813
1741
|
this.inertSiblings.apply();
|
|
1814
1742
|
}
|
|
1815
1743
|
}
|
|
1816
|
-
/**
|
|
1817
|
-
* Update the post-release focus target. Overlay re-binds this when the
|
|
1818
|
-
* consumer changes `returnFocus` after construction; the new value applies
|
|
1819
|
-
* on the next `release()`. No-op when destroyed.
|
|
1820
|
-
*/
|
|
1821
1744
|
setReturnFocus(target) {
|
|
1822
1745
|
if (this.destroyed) return;
|
|
1823
1746
|
this.returnFocus = target;
|
|
1824
1747
|
}
|
|
1825
|
-
/**
|
|
1826
|
-
* Release all install-time teardowns. Short-circuits when not installed,
|
|
1827
|
-
* so `close() → destroy()` (each routing through here) only restores
|
|
1828
|
-
* focus once. The `installed` guard lets `inertSiblings.remove()` and
|
|
1829
|
-
* the focus-trap/scroll-lock teardown nullings stay simple — they were
|
|
1830
|
-
* already idempotent, but `returnFocus.focus()` was firing on every call.
|
|
1831
|
-
*/
|
|
1832
1748
|
release() {
|
|
1833
1749
|
if (!this.installed) return;
|
|
1834
1750
|
this.installed = false;
|
|
@@ -1842,12 +1758,6 @@ var init_lifecycle_controller = __esm({
|
|
|
1842
1758
|
target?.focus?.();
|
|
1843
1759
|
}
|
|
1844
1760
|
}
|
|
1845
|
-
/**
|
|
1846
|
-
* Final teardown — releases any still-installed listeners and seals the
|
|
1847
|
-
* controller. After destroy(), install() is a no-op and setReturnFocus()
|
|
1848
|
-
* is rejected, so post-destroy mutations can't queue up a stale focus
|
|
1849
|
-
* restore.
|
|
1850
|
-
*/
|
|
1851
1761
|
destroy() {
|
|
1852
1762
|
this.release();
|
|
1853
1763
|
this.destroyed = true;
|
|
@@ -1862,12 +1772,10 @@ var init_gestures = __esm({
|
|
|
1862
1772
|
"src/core/gestures.ts"() {
|
|
1863
1773
|
isAxisVertical = (mode) => mode === "bottom" || mode === "top";
|
|
1864
1774
|
SampleRing = class {
|
|
1865
|
-
// current count, ≤ capacity
|
|
1866
1775
|
constructor(capacity) {
|
|
1867
1776
|
__publicField(this, "slots");
|
|
1868
1777
|
__publicField(this, "capacity");
|
|
1869
1778
|
__publicField(this, "head", 0);
|
|
1870
|
-
// next write index
|
|
1871
1779
|
__publicField(this, "size", 0);
|
|
1872
1780
|
this.capacity = capacity;
|
|
1873
1781
|
this.slots = new Array(capacity);
|
|
@@ -1880,12 +1788,6 @@ var init_gestures = __esm({
|
|
|
1880
1788
|
this.head = (this.head + 1) % this.capacity;
|
|
1881
1789
|
if (this.size < this.capacity) this.size++;
|
|
1882
1790
|
}
|
|
1883
|
-
/**
|
|
1884
|
-
* Drop oldest entries whose timestamp is below `cutoff`, but never shrink
|
|
1885
|
-
* below `minKeep`. Mirrors the original
|
|
1886
|
-
* `while (samples.length > minKeep && samples[0].t < cutoff) samples.shift()`
|
|
1887
|
-
* but without copying — just advances the logical tail.
|
|
1888
|
-
*/
|
|
1889
1791
|
pruneBefore(cutoff, minKeep) {
|
|
1890
1792
|
while (this.size > minKeep) {
|
|
1891
1793
|
const oldestIdx = (this.head - this.size + this.capacity) % this.capacity;
|
|
@@ -1995,12 +1897,12 @@ var init_gestures = __esm({
|
|
|
1995
1897
|
// src/core/primitives/rubber-band.ts
|
|
1996
1898
|
function rubberBand(overshoot, maxAxisSize) {
|
|
1997
1899
|
const cap = Math.min(maxAxisSize * 0.15, 80);
|
|
1998
|
-
|
|
1900
|
+
if (cap <= 0) return 0;
|
|
1901
|
+
const abs = Math.abs(overshoot);
|
|
1902
|
+
return Math.sign(overshoot) * (cap * abs / (abs + cap));
|
|
1999
1903
|
}
|
|
2000
|
-
var RUBBER_BAND_FACTOR;
|
|
2001
1904
|
var init_rubber_band = __esm({
|
|
2002
1905
|
"src/core/primitives/rubber-band.ts"() {
|
|
2003
|
-
RUBBER_BAND_FACTOR = 0.55;
|
|
2004
1906
|
}
|
|
2005
1907
|
});
|
|
2006
1908
|
|
|
@@ -2038,28 +1940,14 @@ var init_gesture_controller = __esm({
|
|
|
2038
1940
|
__publicField(this, "detach", null);
|
|
2039
1941
|
__publicField(this, "isDragging_", false);
|
|
2040
1942
|
__publicField(this, "dragStartSize", 0);
|
|
2041
|
-
// Only "touch" gestures are eligible for soft-keyboard dismissal —
|
|
2042
|
-
// mouse/trackpad users don't have a keyboard to dismiss.
|
|
2043
1943
|
__publicField(this, "currentPointerKind", "touch");
|
|
2044
|
-
// One blur per drag — flipping focus every move event races the keyboard's
|
|
2045
|
-
// own animations on iOS and makes the page jitter.
|
|
2046
1944
|
__publicField(this, "keyboardDismissed", false);
|
|
2047
|
-
// Reused payload object for the "drag" event — mutated each onMove instead
|
|
2048
|
-
// of allocating a fresh `{size, delta}` per frame. At 60-120Hz a long drag
|
|
2049
|
-
// would otherwise churn tens of KB through the GC. Mirrors browser
|
|
2050
|
-
// PointerEvent semantics: consumers must NOT retain the reference across
|
|
2051
|
-
// frames (clone if needed). See SheetEventMap.drag JSDoc.
|
|
2052
1945
|
__publicField(this, "dragPayload", { size: 0, delta: 0 });
|
|
2053
1946
|
this.deps = deps;
|
|
2054
1947
|
}
|
|
2055
|
-
/** True between `dragstart` and `dragend`/`onCancel`. */
|
|
2056
1948
|
get isDragging() {
|
|
2057
1949
|
return this.isDragging_;
|
|
2058
1950
|
}
|
|
2059
|
-
/**
|
|
2060
|
-
* Bind pointer listeners on the handle and return a teardown. Engine pushes
|
|
2061
|
-
* the teardown into its TeardownStack so destroy() drains it once.
|
|
2062
|
-
*/
|
|
2063
1951
|
install() {
|
|
2064
1952
|
if (this.detach) return this.detach;
|
|
2065
1953
|
this.detach = installGestures(this.deps.handle, this.deps.mode, {
|
|
@@ -2110,17 +1998,16 @@ var init_gesture_controller = __esm({
|
|
|
2110
1998
|
this.isDragging_ = false;
|
|
2111
1999
|
this.keyboardDismissed = false;
|
|
2112
2000
|
this.deps.getRoot()?.removeAttribute("data-dragging");
|
|
2001
|
+
this.deps.emit("dragend", {
|
|
2002
|
+
size: this.deps.getDragContext().size,
|
|
2003
|
+
velocity: 0
|
|
2004
|
+
});
|
|
2113
2005
|
const restoreTo = this.dragStartSize;
|
|
2114
2006
|
void this.deps.animateTo(restoreTo, 0);
|
|
2115
2007
|
}
|
|
2116
2008
|
});
|
|
2117
2009
|
return this.detach;
|
|
2118
2010
|
}
|
|
2119
|
-
/**
|
|
2120
|
-
* Force-clear drag visual state. Engine calls from destroy() when destroyed
|
|
2121
|
-
* mid-drag — gesture detach won't synthesize an onCancel, so we have to
|
|
2122
|
-
* roll back the data-attribute + will-change ourselves.
|
|
2123
|
-
*/
|
|
2124
2011
|
forceClearDragState() {
|
|
2125
2012
|
if (!this.isDragging_) return;
|
|
2126
2013
|
this.isDragging_ = false;
|
|
@@ -2144,11 +2031,6 @@ var init_aria_slider_writer = __esm({
|
|
|
2144
2031
|
mode === "bottom" || mode === "top" ? "vertical" : "horizontal"
|
|
2145
2032
|
);
|
|
2146
2033
|
}
|
|
2147
|
-
/**
|
|
2148
|
-
* Update slider position. `allowedIds` length determines `aria-valuemax`;
|
|
2149
|
-
* `activeId` indexes into it for `aria-valuenow` + `aria-valuetext`.
|
|
2150
|
-
* Empty list → strip every dynamic attr so AT skips the handle entirely.
|
|
2151
|
-
*/
|
|
2152
2034
|
setValue(allowedIds, activeId) {
|
|
2153
2035
|
if (allowedIds.length === 0) {
|
|
2154
2036
|
this.handle.removeAttribute("aria-valuemin");
|
|
@@ -2204,6 +2086,306 @@ var init_scroll_cache = __esm({
|
|
|
2204
2086
|
}
|
|
2205
2087
|
});
|
|
2206
2088
|
|
|
2089
|
+
// src/core/primitives/anchor-animations.ts
|
|
2090
|
+
function resolveTransitionFrames(spec, phase) {
|
|
2091
|
+
const fallback = { frames: null, duration: 0, easing: DEFAULT_EASING };
|
|
2092
|
+
if (spec === "none") return fallback;
|
|
2093
|
+
if (spec === void 0 || typeof spec === "string") {
|
|
2094
|
+
const preset2 = PRESET_FRAMES[spec ?? "fade"];
|
|
2095
|
+
return {
|
|
2096
|
+
frames: preset2[phase],
|
|
2097
|
+
duration: DEFAULT_DURATION3,
|
|
2098
|
+
easing: DEFAULT_EASING
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
const custom = phase === "enter" ? spec.enter : spec.exit;
|
|
2102
|
+
if (custom) {
|
|
2103
|
+
return {
|
|
2104
|
+
frames: custom,
|
|
2105
|
+
duration: spec.duration ?? DEFAULT_DURATION3,
|
|
2106
|
+
easing: spec.easing ?? DEFAULT_EASING
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
if (spec.preset === "none") return fallback;
|
|
2110
|
+
const preset = PRESET_FRAMES[spec.preset ?? "fade"];
|
|
2111
|
+
return {
|
|
2112
|
+
frames: preset[phase],
|
|
2113
|
+
duration: spec.duration ?? DEFAULT_DURATION3,
|
|
2114
|
+
easing: spec.easing ?? DEFAULT_EASING
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
function runAnchorTransition(el, spec, phase) {
|
|
2118
|
+
const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
|
|
2119
|
+
if (respectReduced && prefersReducedMotion()) return INSTANT;
|
|
2120
|
+
const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
|
|
2121
|
+
if (!frames || duration <= 0) return INSTANT;
|
|
2122
|
+
if (typeof el.animate !== "function") return INSTANT;
|
|
2123
|
+
const animation = el.animate(frames, {
|
|
2124
|
+
duration,
|
|
2125
|
+
easing,
|
|
2126
|
+
fill: "both"
|
|
2127
|
+
});
|
|
2128
|
+
const finished = animation.finished.then(() => void 0).catch(() => void 0);
|
|
2129
|
+
return {
|
|
2130
|
+
finished,
|
|
2131
|
+
cancel: () => {
|
|
2132
|
+
try {
|
|
2133
|
+
animation.cancel();
|
|
2134
|
+
} catch {
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
var DEFAULT_DURATION3, DEFAULT_EASING, PRESET_FRAMES, INSTANT;
|
|
2141
|
+
var init_anchor_animations = __esm({
|
|
2142
|
+
"src/core/primitives/anchor-animations.ts"() {
|
|
2143
|
+
init_animation();
|
|
2144
|
+
DEFAULT_DURATION3 = 200;
|
|
2145
|
+
DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
2146
|
+
PRESET_FRAMES = {
|
|
2147
|
+
fade: {
|
|
2148
|
+
enter: [{ opacity: 0 }, { opacity: 1 }],
|
|
2149
|
+
exit: [{ opacity: 1 }, { opacity: 0 }]
|
|
2150
|
+
},
|
|
2151
|
+
scale: {
|
|
2152
|
+
enter: [
|
|
2153
|
+
{ opacity: 0, transform: "scale(0.85)" },
|
|
2154
|
+
{ opacity: 1, transform: "scale(1)" }
|
|
2155
|
+
],
|
|
2156
|
+
exit: [
|
|
2157
|
+
{ opacity: 1, transform: "scale(1)" },
|
|
2158
|
+
{ opacity: 0, transform: "scale(0.85)" }
|
|
2159
|
+
]
|
|
2160
|
+
},
|
|
2161
|
+
slide: {
|
|
2162
|
+
enter: [
|
|
2163
|
+
{ opacity: 0, transform: "translateY(10px)" },
|
|
2164
|
+
{ opacity: 1, transform: "translateY(0)" }
|
|
2165
|
+
],
|
|
2166
|
+
exit: [
|
|
2167
|
+
{ opacity: 1, transform: "translateY(0)" },
|
|
2168
|
+
{ opacity: 0, transform: "translateY(10px)" }
|
|
2169
|
+
]
|
|
2170
|
+
},
|
|
2171
|
+
pop: {
|
|
2172
|
+
enter: [
|
|
2173
|
+
{ opacity: 0, transform: "scale(0.6)", offset: 0 },
|
|
2174
|
+
{ opacity: 1, transform: "scale(1.06)", offset: 0.7 },
|
|
2175
|
+
{ opacity: 1, transform: "scale(1)", offset: 1 }
|
|
2176
|
+
],
|
|
2177
|
+
exit: [
|
|
2178
|
+
{ opacity: 1, transform: "scale(1)" },
|
|
2179
|
+
{ opacity: 0, transform: "scale(0.6)" }
|
|
2180
|
+
]
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
INSTANT = {
|
|
2184
|
+
finished: Promise.resolve(),
|
|
2185
|
+
cancel: () => {
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
|
|
2191
|
+
// src/core/features/sheet-anchors.ts
|
|
2192
|
+
function attachAnchor(deps, opts) {
|
|
2193
|
+
const doc = deps.host.ownerDocument;
|
|
2194
|
+
const wrapper = doc.createElement("div");
|
|
2195
|
+
wrapper.className = "bs-anchor";
|
|
2196
|
+
const interactive = opts.interactive ?? true;
|
|
2197
|
+
const position = opts.position ?? "sheet-top-right";
|
|
2198
|
+
const isDock = position === "dock-bottom" || position === "dock-top";
|
|
2199
|
+
const ws = wrapper.style;
|
|
2200
|
+
ws.position = "fixed";
|
|
2201
|
+
if (isDock) {
|
|
2202
|
+
ws.left = "0";
|
|
2203
|
+
ws.right = "0";
|
|
2204
|
+
if (position === "dock-bottom") {
|
|
2205
|
+
ws.bottom = "0";
|
|
2206
|
+
} else {
|
|
2207
|
+
ws.top = "0";
|
|
2208
|
+
}
|
|
2209
|
+
} else {
|
|
2210
|
+
applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
|
|
2211
|
+
}
|
|
2212
|
+
if (opts.fadeRange) {
|
|
2213
|
+
const [r0, r1] = opts.fadeRange;
|
|
2214
|
+
const span = Math.max(r1 - r0, 1e-4);
|
|
2215
|
+
ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
|
|
2216
|
+
}
|
|
2217
|
+
wrapper.appendChild(opts.element);
|
|
2218
|
+
deps.host.appendChild(wrapper);
|
|
2219
|
+
let visible = null;
|
|
2220
|
+
let inFlight = null;
|
|
2221
|
+
const shouldShow = (state) => {
|
|
2222
|
+
if (typeof opts.showOn === "function") return opts.showOn(state);
|
|
2223
|
+
if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
|
|
2224
|
+
return isDock || state.size > 0;
|
|
2225
|
+
};
|
|
2226
|
+
const applyVisibility = (next, animate) => {
|
|
2227
|
+
if (next === visible) return;
|
|
2228
|
+
visible = next;
|
|
2229
|
+
inFlight?.cancel();
|
|
2230
|
+
inFlight = null;
|
|
2231
|
+
wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
|
|
2232
|
+
if (next) {
|
|
2233
|
+
wrapper.style.visibility = "";
|
|
2234
|
+
if (animate) {
|
|
2235
|
+
inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
|
|
2236
|
+
}
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2239
|
+
if (!animate) {
|
|
2240
|
+
wrapper.style.visibility = "hidden";
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
const handle = runAnchorTransition(opts.element, opts.animation, "exit");
|
|
2244
|
+
inFlight = handle;
|
|
2245
|
+
void handle.finished.then(() => {
|
|
2246
|
+
if (inFlight === handle && visible === false) {
|
|
2247
|
+
wrapper.style.visibility = "hidden";
|
|
2248
|
+
}
|
|
2249
|
+
});
|
|
2250
|
+
};
|
|
2251
|
+
const evaluate = (animate) => {
|
|
2252
|
+
if (deps.isDestroyed()) return;
|
|
2253
|
+
applyVisibility(shouldShow(deps.getState()), animate);
|
|
2254
|
+
};
|
|
2255
|
+
const offs = [
|
|
2256
|
+
deps.on("snap", () => evaluate(true)),
|
|
2257
|
+
deps.on("open", () => evaluate(true)),
|
|
2258
|
+
deps.on("close", () => evaluate(true))
|
|
2259
|
+
];
|
|
2260
|
+
evaluate(false);
|
|
2261
|
+
return {
|
|
2262
|
+
wrapper,
|
|
2263
|
+
detach: () => {
|
|
2264
|
+
offs.forEach((off) => off());
|
|
2265
|
+
inFlight?.cancel();
|
|
2266
|
+
inFlight = null;
|
|
2267
|
+
if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
|
|
2268
|
+
},
|
|
2269
|
+
syncZ: (z) => {
|
|
2270
|
+
wrapper.style.zIndex = String(z);
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
var init_sheet_anchors = __esm({
|
|
2275
|
+
"src/core/features/sheet-anchors.ts"() {
|
|
2276
|
+
init_overlay_position();
|
|
2277
|
+
init_anchor_animations();
|
|
2278
|
+
}
|
|
2279
|
+
});
|
|
2280
|
+
|
|
2281
|
+
// src/core/features/scrim-stages.ts
|
|
2282
|
+
function installScrimStages(deps, opts) {
|
|
2283
|
+
const doc = deps.host.ownerDocument;
|
|
2284
|
+
const entries = opts.stages.map((def) => {
|
|
2285
|
+
const wrapper = doc.createElement("div");
|
|
2286
|
+
wrapper.className = "bs-scrim-stage";
|
|
2287
|
+
const ws = wrapper.style;
|
|
2288
|
+
ws.position = "absolute";
|
|
2289
|
+
applyOverlayPosition(
|
|
2290
|
+
ws,
|
|
2291
|
+
deps.mode,
|
|
2292
|
+
def.position ?? opts.position ?? "center",
|
|
2293
|
+
def.inset ?? opts.inset ?? "16px"
|
|
2294
|
+
);
|
|
2295
|
+
ws.visibility = "hidden";
|
|
2296
|
+
ws.pointerEvents = "none";
|
|
2297
|
+
wrapper.appendChild(def.element);
|
|
2298
|
+
deps.host.appendChild(wrapper);
|
|
2299
|
+
return { def, wrapper, inFlight: null };
|
|
2300
|
+
});
|
|
2301
|
+
let active = null;
|
|
2302
|
+
const resolveActive = () => {
|
|
2303
|
+
const state = deps.getState();
|
|
2304
|
+
if (state.size === 0) {
|
|
2305
|
+
return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
|
|
2306
|
+
}
|
|
2307
|
+
return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
|
|
2308
|
+
};
|
|
2309
|
+
const hide = (entry, animate) => {
|
|
2310
|
+
entry.inFlight?.cancel();
|
|
2311
|
+
entry.wrapper.style.pointerEvents = "none";
|
|
2312
|
+
if (!animate) {
|
|
2313
|
+
entry.inFlight = null;
|
|
2314
|
+
entry.wrapper.style.visibility = "hidden";
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const handle = runAnchorTransition(
|
|
2318
|
+
entry.def.element,
|
|
2319
|
+
entry.def.animation ?? opts.animation,
|
|
2320
|
+
"exit"
|
|
2321
|
+
);
|
|
2322
|
+
entry.inFlight = handle;
|
|
2323
|
+
void handle.finished.then(() => {
|
|
2324
|
+
if (entry.inFlight === handle && active !== entry) {
|
|
2325
|
+
entry.wrapper.style.visibility = "hidden";
|
|
2326
|
+
}
|
|
2327
|
+
});
|
|
2328
|
+
};
|
|
2329
|
+
const show = (entry, animate) => {
|
|
2330
|
+
entry.inFlight?.cancel();
|
|
2331
|
+
entry.inFlight = null;
|
|
2332
|
+
entry.wrapper.style.visibility = "";
|
|
2333
|
+
entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
|
|
2334
|
+
if (animate) {
|
|
2335
|
+
entry.inFlight = runAnchorTransition(
|
|
2336
|
+
entry.def.element,
|
|
2337
|
+
entry.def.animation ?? opts.animation,
|
|
2338
|
+
"enter"
|
|
2339
|
+
);
|
|
2340
|
+
}
|
|
2341
|
+
};
|
|
2342
|
+
const update = (animate) => {
|
|
2343
|
+
if (deps.isDestroyed()) return;
|
|
2344
|
+
const next = resolveActive();
|
|
2345
|
+
if (next === active) return;
|
|
2346
|
+
const prev = active;
|
|
2347
|
+
active = next;
|
|
2348
|
+
if (prev) hide(prev, animate);
|
|
2349
|
+
if (next) show(next, animate);
|
|
2350
|
+
};
|
|
2351
|
+
const offs = [
|
|
2352
|
+
deps.on("snap", () => update(true)),
|
|
2353
|
+
deps.on("open", () => update(true)),
|
|
2354
|
+
deps.on("close", () => update(true))
|
|
2355
|
+
];
|
|
2356
|
+
if (entries.some((e) => e.def.forRange)) {
|
|
2357
|
+
offs.push(deps.on("progress", () => update(true)));
|
|
2358
|
+
}
|
|
2359
|
+
update(false);
|
|
2360
|
+
return () => {
|
|
2361
|
+
offs.forEach((off) => off());
|
|
2362
|
+
for (const entry of entries) {
|
|
2363
|
+
entry.inFlight?.cancel();
|
|
2364
|
+
entry.inFlight = null;
|
|
2365
|
+
if (entry.wrapper.parentNode) {
|
|
2366
|
+
entry.wrapper.parentNode.removeChild(entry.wrapper);
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
active = null;
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
var matchesId, matchesRange;
|
|
2373
|
+
var init_scrim_stages = __esm({
|
|
2374
|
+
"src/core/features/scrim-stages.ts"() {
|
|
2375
|
+
init_overlay_position();
|
|
2376
|
+
init_anchor_animations();
|
|
2377
|
+
matchesId = (def, activeId) => {
|
|
2378
|
+
if (def.for === void 0) return false;
|
|
2379
|
+
return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
|
|
2380
|
+
};
|
|
2381
|
+
matchesRange = (def, progress) => {
|
|
2382
|
+
if (!def.forRange) return false;
|
|
2383
|
+
const [from, to] = def.forRange;
|
|
2384
|
+
return progress >= from && progress <= to;
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
|
|
2207
2389
|
// src/core/primitives/engine-options.ts
|
|
2208
2390
|
function resolveEngineOptions(opts, extras) {
|
|
2209
2391
|
const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
|
|
@@ -2226,9 +2408,6 @@ function resolveEngineOptions(opts, extras) {
|
|
|
2226
2408
|
scrimMode: opts.scrimMode,
|
|
2227
2409
|
scrimColor: opts.scrimColor,
|
|
2228
2410
|
scrimBlur: opts.scrimBlur,
|
|
2229
|
-
// Auto-promote pointer-events when tap-to-close is on so the click
|
|
2230
|
-
// listener actually receives events. Caller can still force `false`
|
|
2231
|
-
// explicitly to opt out — `??` only kicks in for `undefined`.
|
|
2232
2411
|
scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
|
|
2233
2412
|
scrimTapToClose: opts.scrimTapToClose,
|
|
2234
2413
|
scrimPreset: opts.scrimPreset,
|
|
@@ -2249,10 +2428,6 @@ function resolveEngineOptions(opts, extras) {
|
|
|
2249
2428
|
closeOnEscape: opts.closeOnEscape,
|
|
2250
2429
|
lockBodyScroll: opts.lockBodyScroll,
|
|
2251
2430
|
inertSiblings: opts.inertSiblings,
|
|
2252
|
-
// Folded in from `extras` so the lifecycle bag is fully assembled at
|
|
2253
|
-
// the call site — engine no longer needs a spread+override at
|
|
2254
|
-
// controller construction. When `extras` is omitted, the field stays
|
|
2255
|
-
// undefined and the controller uses its own default predicate.
|
|
2256
2431
|
shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
|
|
2257
2432
|
}
|
|
2258
2433
|
};
|
|
@@ -2291,6 +2466,8 @@ var init_BottomSheetEngine = __esm({
|
|
|
2291
2466
|
init_aria_slider_writer();
|
|
2292
2467
|
init_hot_path_thresholds();
|
|
2293
2468
|
init_scroll_cache();
|
|
2469
|
+
init_sheet_anchors();
|
|
2470
|
+
init_scrim_stages();
|
|
2294
2471
|
init_engine_options();
|
|
2295
2472
|
init_opacity_dedup();
|
|
2296
2473
|
HAPTIC_DURATION_MS = 8;
|
|
@@ -2321,6 +2498,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
2321
2498
|
__publicField(this, "scrollContainer");
|
|
2322
2499
|
__publicField(this, "backdrop");
|
|
2323
2500
|
__publicField(this, "screenComponent");
|
|
2501
|
+
__publicField(this, "scrimParent", null);
|
|
2324
2502
|
__publicField(this, "mode");
|
|
2325
2503
|
__publicField(this, "flickVelocity");
|
|
2326
2504
|
__publicField(this, "dragThreshold");
|
|
@@ -2331,40 +2509,22 @@ var init_BottomSheetEngine = __esm({
|
|
|
2331
2509
|
__publicField(this, "lifecycle");
|
|
2332
2510
|
__publicField(this, "closeOnBack");
|
|
2333
2511
|
__publicField(this, "routedTo");
|
|
2334
|
-
// Engine retains `snapPointsRaw` for the "find a closed-id" query in
|
|
2335
|
-
// close() (raw input, not resolved geometry). Resolver owns geometry.
|
|
2336
2512
|
__publicField(this, "snapPointsRaw");
|
|
2337
2513
|
__publicField(this, "snaps");
|
|
2338
2514
|
__publicField(this, "activeId");
|
|
2339
2515
|
__publicField(this, "size", 0);
|
|
2340
|
-
// Constructed in attach(); a plugin's install() could read `state` before
|
|
2341
|
-
// attach completes if the constructor throws partway through. State getter
|
|
2342
|
-
// null-guards via `this.gesture?.isDragging ?? false`.
|
|
2343
2516
|
__publicField(this, "gesture");
|
|
2344
2517
|
__publicField(this, "rootEl", null);
|
|
2345
2518
|
__publicField(this, "destroyed", false);
|
|
2346
|
-
// In-flight ViewTransition handle. A second snapTo while a VT is in flight
|
|
2347
|
-
// would otherwise let Chromium reject the second's promise and flicker —
|
|
2348
|
-
// newCycle() calls skipTransition() to abort cleanly.
|
|
2349
2519
|
__publicField(this, "currentViewTransition", null);
|
|
2350
|
-
// Each cycle captures the signal at entry; when a newer cycle aborts the
|
|
2351
|
-
// controller, the captured signal flips to `aborted=true` and post-await
|
|
2352
|
-
// guards short-circuit (prevents stale snap/open/close emits).
|
|
2353
2520
|
__publicField(this, "currentAbort", new AbortController());
|
|
2354
|
-
// Sub-pixel `--bs-size` deltas and <1% `--bs-progress` deltas are not
|
|
2355
|
-
// visually distinguishable; gating CSSOM writes drops 30-50% of writes
|
|
2356
|
-
// during the spring's settle tail.
|
|
2357
2521
|
__publicField(this, "sizeWriteSentinel", new WriteSentinel());
|
|
2358
2522
|
__publicField(this, "progressWriteSentinel", new WriteSentinel());
|
|
2359
2523
|
__publicField(this, "isTopSheet", true);
|
|
2360
|
-
// Pooled payload — identity documented in SheetEventMap.progress JSDoc;
|
|
2361
|
-
// consumers must not retain across frames.
|
|
2362
2524
|
__publicField(this, "progressPayload", {
|
|
2363
2525
|
value: 0,
|
|
2364
2526
|
size: 0
|
|
2365
2527
|
});
|
|
2366
|
-
// Pooled buffer for `getDragContext()` — onMove calls this 60-120×/s.
|
|
2367
|
-
// Inner `range` object identity is also stable (mutated in place).
|
|
2368
2528
|
__publicField(this, "dragContextBuf", {
|
|
2369
2529
|
size: 0,
|
|
2370
2530
|
maxAxisSize: 0,
|
|
@@ -2375,13 +2535,13 @@ var init_BottomSheetEngine = __esm({
|
|
|
2375
2535
|
__publicField(this, "persistKey");
|
|
2376
2536
|
__publicField(this, "linkedSheets", []);
|
|
2377
2537
|
__publicField(this, "scrollCache");
|
|
2378
|
-
// Single LIFO stack for ALL destroy-only teardowns. Plugins (added last)
|
|
2379
|
-
// tear down first before the infrastructure they depend on. Toggle-semantic
|
|
2380
|
-
// listeners (focus-trap / scroll-lock / scrim tap-to-close) stay as named
|
|
2381
|
-
// fields on their controllers.
|
|
2382
2538
|
__publicField(this, "teardowns", new TeardownStack());
|
|
2383
|
-
|
|
2384
|
-
|
|
2539
|
+
__publicField(this, "anchors", []);
|
|
2540
|
+
__publicField(this, "anchorHost", null);
|
|
2541
|
+
__publicField(this, "stackZ", 100);
|
|
2542
|
+
__publicField(this, "detachScrimStages", null);
|
|
2543
|
+
__publicField(this, "stackEffectEnabled", false);
|
|
2544
|
+
__publicField(this, "stackEffectPrimed", false);
|
|
2385
2545
|
__publicField(this, "transformTemplate");
|
|
2386
2546
|
const resolved = resolveEngineOptions(opts, {
|
|
2387
2547
|
shouldApplyInertSiblings: () => {
|
|
@@ -2396,7 +2556,8 @@ var init_BottomSheetEngine = __esm({
|
|
|
2396
2556
|
this.handle = opts.handle ?? opts.element;
|
|
2397
2557
|
this.scrollContainer = opts.scrollContainer;
|
|
2398
2558
|
this.backdrop = opts.backdrop;
|
|
2399
|
-
this.screenComponent = opts.screenComponent;
|
|
2559
|
+
this.screenComponent = opts.scrim ?? opts.screenComponent;
|
|
2560
|
+
this.scrimParent = this.screenComponent?.parentElement ?? null;
|
|
2400
2561
|
this.mode = resolved.mode;
|
|
2401
2562
|
this.transformTemplate = buildTransformTemplate(this.mode);
|
|
2402
2563
|
this.aria = new AriaSliderWriter(this.handle, this.mode);
|
|
@@ -2414,6 +2575,9 @@ var init_BottomSheetEngine = __esm({
|
|
|
2414
2575
|
getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
|
|
2415
2576
|
snapTo: (id) => {
|
|
2416
2577
|
void this.snapTo(id);
|
|
2578
|
+
},
|
|
2579
|
+
close: () => {
|
|
2580
|
+
void this.close();
|
|
2417
2581
|
}
|
|
2418
2582
|
},
|
|
2419
2583
|
resolved.scrim
|
|
@@ -2424,8 +2588,6 @@ var init_BottomSheetEngine = __esm({
|
|
|
2424
2588
|
getRootEl: () => this.rootEl,
|
|
2425
2589
|
applySize: (size) => this.applySize(size),
|
|
2426
2590
|
getSize: () => this.size,
|
|
2427
|
-
// Optional chain protects synthetic test paths that drive animateTo
|
|
2428
|
-
// before attach() (gesture is constructed there).
|
|
2429
2591
|
isDragging: () => this.gesture?.isDragging ?? false
|
|
2430
2592
|
},
|
|
2431
2593
|
resolved.animation
|
|
@@ -2439,6 +2601,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
2439
2601
|
);
|
|
2440
2602
|
this.closeOnBack = resolved.closeOnBack;
|
|
2441
2603
|
this.routedTo = opts.routedTo;
|
|
2604
|
+
this.stackEffectEnabled = opts.stackEffect ?? false;
|
|
2442
2605
|
this.snapPointsRaw = opts.snapPoints;
|
|
2443
2606
|
this.persistKey = opts.persistKey;
|
|
2444
2607
|
if (opts.linkedSheets) {
|
|
@@ -2451,7 +2614,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
2451
2614
|
this.snapPointsRaw,
|
|
2452
2615
|
resolved.initialAllowed,
|
|
2453
2616
|
this.mode,
|
|
2454
|
-
() => this.handle.offsetHeight,
|
|
2617
|
+
() => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
|
|
2455
2618
|
(maxAxisSize) => {
|
|
2456
2619
|
this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
|
|
2457
2620
|
}
|
|
@@ -2530,21 +2693,6 @@ var init_BottomSheetEngine = __esm({
|
|
|
2530
2693
|
on(event, fn) {
|
|
2531
2694
|
return this.bus.on(event, fn);
|
|
2532
2695
|
}
|
|
2533
|
-
/**
|
|
2534
|
-
* Register a plugin. `install` runs synchronously; if it returns a function,
|
|
2535
|
-
* it's captured for teardown during `destroy()`. No-op if already destroyed
|
|
2536
|
-
* — installing into a torn-down engine would leak listeners.
|
|
2537
|
-
*
|
|
2538
|
-
* Throws from `plugin.install` are caught and rethrown via `queueMicrotask`
|
|
2539
|
-
* so a buggy plugin can't take the engine down with it: the engine stays
|
|
2540
|
-
* usable, sibling plugins still install, and the error surfaces in dev tools
|
|
2541
|
-
* on the next tick instead of being swallowed. Mirrors the per-fn isolation
|
|
2542
|
-
* model used for teardowns.
|
|
2543
|
-
*
|
|
2544
|
-
* Plugins that accept the second `scope` argument get transactional cleanup:
|
|
2545
|
-
* push partial-install cleanups via `scope.add(fn)` and the engine drains
|
|
2546
|
-
* them on throw, OR merges them into destroy-time teardowns on success.
|
|
2547
|
-
*/
|
|
2548
2696
|
use(plugin) {
|
|
2549
2697
|
if (this.destroyed) {
|
|
2550
2698
|
console.warn(
|
|
@@ -2554,9 +2702,6 @@ var init_BottomSheetEngine = __esm({
|
|
|
2554
2702
|
}
|
|
2555
2703
|
const scoped = [];
|
|
2556
2704
|
const scope = {
|
|
2557
|
-
// typeof check — not just truthiness — so a JS-only plugin pushing a
|
|
2558
|
-
// bogus value is silently rejected instead of queueing a confusing
|
|
2559
|
-
// TypeError on destroy() drain.
|
|
2560
2705
|
add: (fn) => {
|
|
2561
2706
|
if (typeof fn === "function") scoped.push(fn);
|
|
2562
2707
|
}
|
|
@@ -2585,36 +2730,13 @@ var init_BottomSheetEngine = __esm({
|
|
|
2585
2730
|
}
|
|
2586
2731
|
return this;
|
|
2587
2732
|
}
|
|
2588
|
-
/**
|
|
2589
|
-
* Replace the linked-sheets list at runtime. Use when peer engines aren't
|
|
2590
|
-
* available at construction time (mounted later in the tree). The wiring
|
|
2591
|
-
* to the `open` event is independent of this list — it reads `linkedSheets`
|
|
2592
|
-
* lazily on emit, so this setter is a simple field swap.
|
|
2593
|
-
*/
|
|
2594
2733
|
setLinkedSheets(sheets) {
|
|
2595
2734
|
if (this.destroyed) return;
|
|
2596
2735
|
this.linkedSheets = sheets;
|
|
2597
2736
|
}
|
|
2598
|
-
/** Read-only view of the current allow-list, for cross-sheet target negotiation. */
|
|
2599
2737
|
getAllowedIds() {
|
|
2600
2738
|
return this.snaps.getAllowedIds().slice();
|
|
2601
2739
|
}
|
|
2602
|
-
/**
|
|
2603
|
-
* Animate to a snap point by id. Resolves when the animation completes.
|
|
2604
|
-
*
|
|
2605
|
-
* Two call shapes:
|
|
2606
|
-
* ```ts
|
|
2607
|
-
* engine.snapTo("full"); // default velocity
|
|
2608
|
-
* engine.snapTo("full", 0.5); // px/ms initial velocity
|
|
2609
|
-
* engine.snapTo("full", { velocity: 0.5, signal }); // abortable
|
|
2610
|
-
* ```
|
|
2611
|
-
*
|
|
2612
|
-
* When `opts.signal` is provided, calling `controller.abort()` cancels the
|
|
2613
|
-
* in-flight tween/spring and the returned promise resolves without firing
|
|
2614
|
-
* `snap`. The captured `AbortSignal` is checked after each await so external
|
|
2615
|
-
* aborts ride the same cycle invariant as internal ones (resize, destroy,
|
|
2616
|
-
* concurrent snapTo).
|
|
2617
|
-
*/
|
|
2618
2740
|
async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
|
|
2619
2741
|
if (this.destroyed) return;
|
|
2620
2742
|
const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
|
|
@@ -2673,16 +2795,6 @@ var init_BottomSheetEngine = __esm({
|
|
|
2673
2795
|
this.handleClose();
|
|
2674
2796
|
}
|
|
2675
2797
|
}
|
|
2676
|
-
/**
|
|
2677
|
-
* Drive the sheet to an arbitrary px size, bypassing snap matching.
|
|
2678
|
-
* Useful for preview scrubs, animation chaining, or programmatic gestures.
|
|
2679
|
-
* Animates with the configured spring/tween. Resolves when the animation
|
|
2680
|
-
* completes. Does NOT update `activeId` — call `snapTo(id)` after to commit.
|
|
2681
|
-
*
|
|
2682
|
-
* Unlike `snapTo`, this does not fire `before-snap` (it isn't a snap commit)
|
|
2683
|
-
* nor `snap` (the sheet isn't settling to a known snap). `progress` events
|
|
2684
|
-
* still fire via applySize. Target is clamped to `[0, maxAxisSize]`.
|
|
2685
|
-
*/
|
|
2686
2798
|
async dragTo(targetSize, velocityOrOpts = 0) {
|
|
2687
2799
|
if (this.destroyed) return;
|
|
2688
2800
|
const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
|
|
@@ -2712,28 +2824,13 @@ var init_BottomSheetEngine = __esm({
|
|
|
2712
2824
|
}
|
|
2713
2825
|
setAllowed(ids, snap) {
|
|
2714
2826
|
if (this.destroyed) return;
|
|
2715
|
-
const willSnap = snap !== void 0 && ids.includes(snap) || !ids.includes(this.activeId) && ids.length > 0;
|
|
2716
|
-
if (!willSnap) {
|
|
2717
|
-
this.animation.cancel();
|
|
2718
|
-
this.newCycle();
|
|
2719
|
-
}
|
|
2720
2827
|
this.snaps.setAllowedIds(ids);
|
|
2721
2828
|
this.updateAriaSlider();
|
|
2722
2829
|
if (snap && ids.includes(snap)) {
|
|
2723
|
-
void this.snapTo(
|
|
2724
|
-
snap,
|
|
2725
|
-
0,
|
|
2726
|
-
/* internal */
|
|
2727
|
-
true
|
|
2728
|
-
);
|
|
2830
|
+
void this.snapTo(snap, 0, true);
|
|
2729
2831
|
} else if (!ids.includes(this.activeId)) {
|
|
2730
2832
|
const fallback = ids[0];
|
|
2731
|
-
if (fallback) void this.snapTo(
|
|
2732
|
-
fallback,
|
|
2733
|
-
0,
|
|
2734
|
-
/* internal */
|
|
2735
|
-
true
|
|
2736
|
-
);
|
|
2833
|
+
if (fallback) void this.snapTo(fallback, 0, true);
|
|
2737
2834
|
}
|
|
2738
2835
|
}
|
|
2739
2836
|
setBackdropRange(range) {
|
|
@@ -2751,21 +2848,6 @@ var init_BottomSheetEngine = __esm({
|
|
|
2751
2848
|
setScrimInteractive(interactive) {
|
|
2752
2849
|
this.scrim.setScrimInteractive(interactive);
|
|
2753
2850
|
}
|
|
2754
|
-
/**
|
|
2755
|
-
* Batch-update multiple scrim fields in one call. Apply order is:
|
|
2756
|
-
* 1. `preset` — fills baseline color/blur/range/interactive.
|
|
2757
|
-
* 2. `mode` — re-positions the scrim (fixed inset / clear / hide).
|
|
2758
|
-
* 3. `enabled` — flips the opacity contribution on/off.
|
|
2759
|
-
* 4. `tapToClose` — installs/tears the click listener.
|
|
2760
|
-
* 5. `color` / `blur` / `interactive` / `range` — explicit overrides.
|
|
2761
|
-
* 6. ONE `applySize` so the per-frame opacity stays consistent.
|
|
2762
|
-
*
|
|
2763
|
-
* Performance: collecting changes here (vs calling individual setters)
|
|
2764
|
-
* avoids repeated `applySize` calls during a multi-field swap. Setters
|
|
2765
|
-
* called here suppress their own internal `applySize` either by passing
|
|
2766
|
-
* through fast-paths (mode/enabled no-op when value unchanged) or by
|
|
2767
|
-
* being visual-only (color/blur/interactive don't influence opacity).
|
|
2768
|
-
*/
|
|
2769
2851
|
setScrim(opts) {
|
|
2770
2852
|
this.scrim.setScrim(opts, () => this.applySize(this.size));
|
|
2771
2853
|
}
|
|
@@ -2781,11 +2863,79 @@ var init_BottomSheetEngine = __esm({
|
|
|
2781
2863
|
setScrimOverlay(opts) {
|
|
2782
2864
|
return this.scrim.setScrimOverlay(opts);
|
|
2783
2865
|
}
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2866
|
+
addAnchor(opts) {
|
|
2867
|
+
if (this.destroyed || typeof document === "undefined") return () => {
|
|
2868
|
+
};
|
|
2869
|
+
if (!this.anchorHost) {
|
|
2870
|
+
this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
|
|
2871
|
+
this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
|
|
2872
|
+
this.anchorHost.style.setProperty(
|
|
2873
|
+
"--bs-progress",
|
|
2874
|
+
String(this.computeProgress(this.size))
|
|
2875
|
+
);
|
|
2876
|
+
}
|
|
2877
|
+
const handle = attachAnchor(
|
|
2878
|
+
{
|
|
2879
|
+
mode: this.mode,
|
|
2880
|
+
host: this.anchorHost,
|
|
2881
|
+
getState: () => ({
|
|
2882
|
+
activeId: this.activeId,
|
|
2883
|
+
size: this.size,
|
|
2884
|
+
progress: this.computeProgress(this.size)
|
|
2885
|
+
}),
|
|
2886
|
+
on: (event, fn) => this.on(event, fn),
|
|
2887
|
+
isDestroyed: () => this.destroyed
|
|
2888
|
+
},
|
|
2889
|
+
opts
|
|
2890
|
+
);
|
|
2891
|
+
handle.syncZ(this.stackZ + 1);
|
|
2892
|
+
this.anchors.push(handle);
|
|
2893
|
+
const detach = () => {
|
|
2894
|
+
const idx = this.anchors.indexOf(handle);
|
|
2895
|
+
if (idx === -1) return;
|
|
2896
|
+
this.anchors.splice(idx, 1);
|
|
2897
|
+
handle.detach();
|
|
2898
|
+
};
|
|
2899
|
+
this.teardowns.add(detach);
|
|
2900
|
+
return detach;
|
|
2901
|
+
}
|
|
2902
|
+
setScrimStages(opts) {
|
|
2903
|
+
if (this.destroyed || typeof document === "undefined") return () => {
|
|
2904
|
+
};
|
|
2905
|
+
this.detachScrimStages?.();
|
|
2906
|
+
this.detachScrimStages = null;
|
|
2907
|
+
if (!opts) return () => {
|
|
2908
|
+
};
|
|
2909
|
+
const host = this.screenComponent?.parentElement;
|
|
2910
|
+
if (!host) {
|
|
2911
|
+
console.warn(
|
|
2912
|
+
"[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
|
|
2913
|
+
);
|
|
2914
|
+
return () => {
|
|
2915
|
+
};
|
|
2916
|
+
}
|
|
2917
|
+
const detachInstalled = installScrimStages(
|
|
2918
|
+
{
|
|
2919
|
+
mode: this.mode,
|
|
2920
|
+
host,
|
|
2921
|
+
getState: () => ({
|
|
2922
|
+
activeId: this.activeId,
|
|
2923
|
+
size: this.size,
|
|
2924
|
+
progress: this.computeProgress(this.size)
|
|
2925
|
+
}),
|
|
2926
|
+
on: (event, fn) => this.on(event, fn),
|
|
2927
|
+
isDestroyed: () => this.destroyed
|
|
2928
|
+
},
|
|
2929
|
+
opts
|
|
2930
|
+
);
|
|
2931
|
+
const detach = () => {
|
|
2932
|
+
if (this.detachScrimStages === detach) this.detachScrimStages = null;
|
|
2933
|
+
detachInstalled();
|
|
2934
|
+
};
|
|
2935
|
+
this.detachScrimStages = detach;
|
|
2936
|
+
this.teardowns.add(() => this.detachScrimStages?.());
|
|
2937
|
+
return detach;
|
|
2938
|
+
}
|
|
2789
2939
|
getScrimState() {
|
|
2790
2940
|
return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
|
|
2791
2941
|
}
|
|
@@ -2831,24 +2981,11 @@ var init_BottomSheetEngine = __esm({
|
|
|
2831
2981
|
emit(event, payload) {
|
|
2832
2982
|
this.bus.emit(event, payload);
|
|
2833
2983
|
}
|
|
2834
|
-
/**
|
|
2835
|
-
* Abort the current cycle's signal and replace the controller. Returns the
|
|
2836
|
-
* fresh signal so the caller can capture it locally before any await — when
|
|
2837
|
-
* a future newCycle() / destroy aborts our controller, the captured signal
|
|
2838
|
-
* flips to aborted and post-await guards short-circuit.
|
|
2839
|
-
*/
|
|
2840
2984
|
newCycle() {
|
|
2841
2985
|
this.currentAbort.abort();
|
|
2842
2986
|
this.currentAbort = new AbortController();
|
|
2843
2987
|
return this.currentAbort.signal;
|
|
2844
2988
|
}
|
|
2845
|
-
/**
|
|
2846
|
-
* Fire `before-snap` and report whether a listener cancelled it. The
|
|
2847
|
-
* `cancel()` callback exposed to listeners is frozen after the synchronous
|
|
2848
|
-
* emit phase so async callers can't silently miss — they get a console
|
|
2849
|
-
* warning. Returns `true` when cancelled so callers can early-return /
|
|
2850
|
-
* spring-back without re-implementing the cancel/frozen pattern.
|
|
2851
|
-
*/
|
|
2852
2989
|
emitBeforeSnap(target, previousId) {
|
|
2853
2990
|
let cancelled = false;
|
|
2854
2991
|
let frozen = false;
|
|
@@ -2882,59 +3019,54 @@ var init_BottomSheetEngine = __esm({
|
|
|
2882
3019
|
this.snaps.setRaw(this.snapPointsRaw);
|
|
2883
3020
|
this.scrim.invalidateOpacityCache();
|
|
2884
3021
|
}
|
|
2885
|
-
/**
|
|
2886
|
-
* Memoised `allowedRange()` — delegates to SnapResolver which owns the cache.
|
|
2887
|
-
* Engine wrapper kept so existing call sites don't all need updating;
|
|
2888
|
-
* resolver-level memoization survives across method invocations.
|
|
2889
|
-
*/
|
|
2890
3022
|
getAllowedRange() {
|
|
2891
3023
|
return this.snaps.getAllowedRange();
|
|
2892
3024
|
}
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
* without it screen readers can't announce drag/snap progress to keyboard
|
|
2899
|
-
* + AT users (WCAG 4.1.2 Name, Role, Value).
|
|
2900
|
-
*
|
|
2901
|
-
* Engine does NOT set `role="slider"` itself — that's the adapter's or
|
|
2902
|
-
* consumer markup's responsibility. We only decorate with dynamic values.
|
|
2903
|
-
*
|
|
2904
|
-
* Empty allowed-list is malformed input (settable via `setAllowed([])`);
|
|
2905
|
-
* short-circuit to remove ARIA value attributes so screen readers see a
|
|
2906
|
-
* no-value slider rather than a degenerate `min=max=now=0` (WAI-ARIA 1.2
|
|
2907
|
-
* requires `valuemax > valuemin` for `role="slider"`).
|
|
2908
|
-
*/
|
|
3025
|
+
allowedIdsBySize() {
|
|
3026
|
+
return this.snaps.getAllowedIds().slice().sort(
|
|
3027
|
+
(a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0)
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
2909
3030
|
updateAriaSlider() {
|
|
2910
|
-
this.aria.setValue(this.
|
|
3031
|
+
this.aria.setValue(this.allowedIdsBySize(), this.activeId);
|
|
2911
3032
|
}
|
|
2912
3033
|
registerInStack() {
|
|
2913
3034
|
this.teardowns.add(sheetStack.push({
|
|
2914
3035
|
id: this.id,
|
|
2915
3036
|
setZIndex: (z) => {
|
|
3037
|
+
this.stackZ = z;
|
|
2916
3038
|
this.element.style.zIndex = String(z);
|
|
2917
3039
|
if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
|
|
3040
|
+
for (const anchor of this.anchors) anchor.syncZ(z + 1);
|
|
2918
3041
|
},
|
|
2919
3042
|
setIsTop: (isTop) => {
|
|
2920
3043
|
this.isTopSheet = isTop;
|
|
2921
3044
|
if (this.backdrop) {
|
|
2922
3045
|
this.backdrop.style.display = isTop ? "" : "none";
|
|
2923
3046
|
}
|
|
2924
|
-
}
|
|
3047
|
+
},
|
|
3048
|
+
isOpen: () => this.size > 0,
|
|
3049
|
+
setDepth: (depth) => this.applyStackDepth(depth)
|
|
2925
3050
|
}));
|
|
2926
3051
|
}
|
|
3052
|
+
applyStackDepth(depth) {
|
|
3053
|
+
if (!this.stackEffectEnabled) return;
|
|
3054
|
+
this.element.setAttribute("data-stack-depth", String(depth));
|
|
3055
|
+
if (!this.stackEffectPrimed) {
|
|
3056
|
+
this.stackEffectPrimed = true;
|
|
3057
|
+
const isVertical = this.mode === "bottom" || this.mode === "top";
|
|
3058
|
+
this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
|
|
3059
|
+
this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
|
|
3060
|
+
}
|
|
3061
|
+
const scale = Math.max(1 - depth * 0.04, 0.86);
|
|
3062
|
+
this.element.style.scale = scale === 1 ? "" : String(scale);
|
|
3063
|
+
}
|
|
2927
3064
|
attach() {
|
|
2928
3065
|
this.gesture = new GestureController({
|
|
2929
3066
|
handle: this.handle,
|
|
2930
3067
|
element: this.element,
|
|
2931
3068
|
mode: this.mode,
|
|
2932
3069
|
getRoot: () => this.rootEl,
|
|
2933
|
-
// Single coalesced reader — narrows the DI surface and removes 3 getter
|
|
2934
|
-
// dispatches per onMove frame at 60-120Hz. Returns a SHARED buffer
|
|
2935
|
-
// (this.dragContextBuf) — see field JSDoc for the retention contract.
|
|
2936
|
-
// Inner `range` object is mutated in place too so callers caching its
|
|
2937
|
-
// identity between frames stay valid.
|
|
2938
3070
|
getDragContext: () => {
|
|
2939
3071
|
const buf = this.dragContextBuf;
|
|
2940
3072
|
const r = this.getAllowedRange();
|
|
@@ -2988,7 +3120,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
2988
3120
|
container: this.scrollContainer,
|
|
2989
3121
|
isDragging: () => this.gesture?.isDragging ?? false,
|
|
2990
3122
|
isAnimating: () => this.animation.isAnimating,
|
|
2991
|
-
getAllowedIds: () => this.
|
|
3123
|
+
getAllowedIds: () => this.allowedIdsBySize(),
|
|
2992
3124
|
getActiveId: () => this.activeId,
|
|
2993
3125
|
snapTo: (id) => {
|
|
2994
3126
|
void this.snapTo(id);
|
|
@@ -3017,11 +3149,13 @@ var init_BottomSheetEngine = __esm({
|
|
|
3017
3149
|
},
|
|
3018
3150
|
newCycle: () => {
|
|
3019
3151
|
this.newCycle();
|
|
3020
|
-
}
|
|
3152
|
+
},
|
|
3153
|
+
isAnimating: () => this.animation.isAnimating,
|
|
3154
|
+
resyncAfterCancel: () => this.resyncAfterResize()
|
|
3021
3155
|
}));
|
|
3022
3156
|
if (this.lifecycle.closeOnEscape) {
|
|
3023
3157
|
const onKey = (e) => {
|
|
3024
|
-
if (this.destroyed) return;
|
|
3158
|
+
if (this.destroyed || e.defaultPrevented) return;
|
|
3025
3159
|
if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
|
|
3026
3160
|
void this.close();
|
|
3027
3161
|
}
|
|
@@ -3053,7 +3187,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
3053
3187
|
handle: this.handle,
|
|
3054
3188
|
mode: this.mode,
|
|
3055
3189
|
isDestroyed: () => this.destroyed,
|
|
3056
|
-
getAllowedIds: () => this.
|
|
3190
|
+
getAllowedIds: () => this.allowedIdsBySize(),
|
|
3057
3191
|
getActiveId: () => this.activeId,
|
|
3058
3192
|
snapTo: (id) => {
|
|
3059
3193
|
void this.snapTo(id);
|
|
@@ -3074,6 +3208,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
3074
3208
|
});
|
|
3075
3209
|
if (!target) return;
|
|
3076
3210
|
const previousId = this.activeId;
|
|
3211
|
+
if (target.id === previousId && target.size === this.size) return;
|
|
3077
3212
|
if (this.emitBeforeSnap(target, previousId)) {
|
|
3078
3213
|
const restore = this.snaps.findById(previousId);
|
|
3079
3214
|
if (restore) {
|
|
@@ -3082,6 +3217,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
3082
3217
|
return;
|
|
3083
3218
|
}
|
|
3084
3219
|
const previousSize = this.size;
|
|
3220
|
+
const previousSnapSize = this.snaps.findById(previousId)?.size;
|
|
3085
3221
|
this.scrollCache.cache(previousId, previousSize, target.size);
|
|
3086
3222
|
this.activeId = target.id;
|
|
3087
3223
|
void this.animation.animateTo(target.size, velocity).then(() => {
|
|
@@ -3090,9 +3226,10 @@ var init_BottomSheetEngine = __esm({
|
|
|
3090
3226
|
this.updateAriaSlider();
|
|
3091
3227
|
this.emit("snap", { id: target.id, size: target.size });
|
|
3092
3228
|
this.haptic();
|
|
3093
|
-
if (
|
|
3229
|
+
if (previousSnapSize === 0 && target.size > 0) {
|
|
3094
3230
|
this.emit("open", { id: target.id });
|
|
3095
3231
|
this.handleOpen();
|
|
3232
|
+
notifyLinkedSheets(this.linkedSheets, this);
|
|
3096
3233
|
}
|
|
3097
3234
|
if (target.size === 0) {
|
|
3098
3235
|
this.emit("close", void 0);
|
|
@@ -3100,7 +3237,6 @@ var init_BottomSheetEngine = __esm({
|
|
|
3100
3237
|
}
|
|
3101
3238
|
});
|
|
3102
3239
|
}
|
|
3103
|
-
/** Trigger a brief haptic tick (iOS Safari + Android Chrome support). */
|
|
3104
3240
|
haptic() {
|
|
3105
3241
|
if (typeof navigator !== "undefined" && "vibrate" in navigator) {
|
|
3106
3242
|
try {
|
|
@@ -3116,6 +3252,12 @@ var init_BottomSheetEngine = __esm({
|
|
|
3116
3252
|
style.transform = this.transformTemplate(offset);
|
|
3117
3253
|
if (this.sizeWriteSentinel.shouldWrite(size, SIZE_WRITE_EPSILON)) {
|
|
3118
3254
|
style.setProperty("--bs-size", `${size}px`);
|
|
3255
|
+
if (this.scrimParent) {
|
|
3256
|
+
this.scrimParent.style.setProperty("--bs-size", `${size}px`);
|
|
3257
|
+
}
|
|
3258
|
+
if (this.anchorHost && this.anchorHost !== this.scrimParent) {
|
|
3259
|
+
this.anchorHost.style.setProperty("--bs-size", `${size}px`);
|
|
3260
|
+
}
|
|
3119
3261
|
}
|
|
3120
3262
|
const progress = this.computeProgress(size);
|
|
3121
3263
|
const progressChanged = this.progressWriteSentinel.shouldWrite(
|
|
@@ -3124,6 +3266,9 @@ var init_BottomSheetEngine = __esm({
|
|
|
3124
3266
|
);
|
|
3125
3267
|
if (progressChanged) {
|
|
3126
3268
|
style.setProperty("--bs-progress", String(progress));
|
|
3269
|
+
if (this.anchorHost) {
|
|
3270
|
+
this.anchorHost.style.setProperty("--bs-progress", String(progress));
|
|
3271
|
+
}
|
|
3127
3272
|
}
|
|
3128
3273
|
this.scrim.applyOpacity(progress, progressChanged);
|
|
3129
3274
|
if (progressChanged && this.bus.listenerCount("progress") > 0) {
|
|
@@ -3138,6 +3283,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
3138
3283
|
return Math.min(Math.max((size - min) / (max - min), 0), 1);
|
|
3139
3284
|
}
|
|
3140
3285
|
handleOpen() {
|
|
3286
|
+
sheetStack.promote(this.id);
|
|
3141
3287
|
try {
|
|
3142
3288
|
this.lifecycle.install();
|
|
3143
3289
|
} catch (err) {
|
|
@@ -3151,8 +3297,24 @@ var init_BottomSheetEngine = __esm({
|
|
|
3151
3297
|
});
|
|
3152
3298
|
}
|
|
3153
3299
|
}
|
|
3300
|
+
resyncAfterResize() {
|
|
3301
|
+
if (this.destroyed) return;
|
|
3302
|
+
const target = this.snaps.findById(this.activeId);
|
|
3303
|
+
if (!target) return;
|
|
3304
|
+
this.updateAriaSlider();
|
|
3305
|
+
this.emit("snap", { id: target.id, size: target.size });
|
|
3306
|
+
if (target.size > 0 && !this.lifecycle.isInstalled) {
|
|
3307
|
+
this.emit("open", { id: target.id });
|
|
3308
|
+
this.handleOpen();
|
|
3309
|
+
notifyLinkedSheets(this.linkedSheets, this);
|
|
3310
|
+
} else if (target.size === 0 && this.lifecycle.isInstalled) {
|
|
3311
|
+
this.emit("close", void 0);
|
|
3312
|
+
this.handleClose();
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3154
3315
|
handleClose() {
|
|
3155
3316
|
this.lifecycle.release();
|
|
3317
|
+
sheetStack.update();
|
|
3156
3318
|
}
|
|
3157
3319
|
};
|
|
3158
3320
|
}
|
|
@@ -3162,7 +3324,7 @@ var init_BottomSheetEngine = __esm({
|
|
|
3162
3324
|
var baseStyles;
|
|
3163
3325
|
var init_baseStyles = __esm({
|
|
3164
3326
|
"src/web-component/baseStyles.ts"() {
|
|
3165
|
-
baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--bs-
|
|
3327
|
+
baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--bs-radius:20px;--bs-handle-color:#b5b3a8;--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-header-min-height:80px;--bs-backdrop-color:rgba(0,0,0,0.5);--bs-scrim-color:rgba(0,0,0,0.4);--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color);opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface);box-shadow:var(--bs-shadow,var(--bs-shadow-auto,none));z-index:var(--bs-z-sheet);overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius);border-top-right-radius:var(--bs-radius);border-top:1px solid var(--bs-surface-border);padding-bottom:env(safe-area-inset-bottom);--bs-shadow-auto:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba(0,0,0,calc(var(--bs-progress,0) * 0.15 + 0.12))}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-bottom:1px solid var(--bs-surface-border);flex-direction:column-reverse;--bs-shadow-auto:0 3px 12px rgba(0,0,0,0.12);padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-right:1px solid var(--bs-surface-border);--bs-shadow-auto:3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius);border-bottom-left-radius:var(--bs-radius);border-left:1px solid var(--bs-surface-border);--bs-shadow-auto:-3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:8px 16px 4px;cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface);-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius)}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color);border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface);z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;user-select:text;padding:0 16px 16px}.bs-screen{position:absolute;inset:0;pointer-events:none;z-index:var(--bs-z-scrim)}.bs-scrim-overlay{z-index:calc(var(--bs-z-sheet) + 1);pointer-events:none}.bs-scrim{background:var(--bs-scrim-color);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-scrim,.bs-root[data-animating="true"] .bs-scrim{transition:none}.bs-scrim{backdrop-filter:blur(var(--bs-scrim-blur));-webkit-backdrop-filter:blur(var(--bs-scrim-blur))}.bs-button-slot{position:absolute;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface:#1a1a1d;--bs-surface-border:rgba(255,255,255,0.08);--bs-handle-color:rgba(255,255,255,0.45);--bs-shadow-auto:0 -3px 16px rgba(0,0,0,0.5)}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,#ffffff);--ovl-surface-border:var(--bs-surface-border,rgba(0,0,0,0.08));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,20px);--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
|
|
3166
3328
|
}
|
|
3167
3329
|
});
|
|
3168
3330
|
|
|
@@ -3172,7 +3334,7 @@ __export(BottomSheetElement_exports, {
|
|
|
3172
3334
|
BottomSheetElement: () => exports.BottomSheetElement,
|
|
3173
3335
|
defineBottomSheet: () => exports.defineBottomSheet
|
|
3174
3336
|
});
|
|
3175
|
-
var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, buildSlot, buildShadowTree; exports.BottomSheetElement = void 0;
|
|
3337
|
+
var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions, buildSlot, buildShadowTree, BaseHTMLElement; exports.BottomSheetElement = void 0; exports.defineBottomSheet = void 0;
|
|
3176
3338
|
var init_BottomSheetElement = __esm({
|
|
3177
3339
|
"src/web-component/BottomSheetElement.ts"() {
|
|
3178
3340
|
init_BottomSheetEngine();
|
|
@@ -3187,6 +3349,8 @@ var init_BottomSheetElement = __esm({
|
|
|
3187
3349
|
ATTR_FOCUS_TRAP = "focus-trap";
|
|
3188
3350
|
ATTR_CLOSE_ON_ESCAPE = "close-on-escape";
|
|
3189
3351
|
ATTR_LOCK_BODY_SCROLL = "lock-body-scroll";
|
|
3352
|
+
ATTR_SHEET_LABEL = "sheet-label";
|
|
3353
|
+
ATTR_STACK_EFFECT = "stack-effect";
|
|
3190
3354
|
isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
|
|
3191
3355
|
DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
|
|
3192
3356
|
parseSnapPoints = (raw) => {
|
|
@@ -3216,6 +3380,44 @@ var init_BottomSheetElement = __esm({
|
|
|
3216
3380
|
parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
|
|
3217
3381
|
parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
|
|
3218
3382
|
parseList = (raw) => raw ? raw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
3383
|
+
ANCHOR_POSITIONS = [
|
|
3384
|
+
"top-left",
|
|
3385
|
+
"top-center",
|
|
3386
|
+
"top-right",
|
|
3387
|
+
"center-left",
|
|
3388
|
+
"center",
|
|
3389
|
+
"center-right",
|
|
3390
|
+
"bottom-left",
|
|
3391
|
+
"bottom-center",
|
|
3392
|
+
"bottom-right",
|
|
3393
|
+
"sheet-top-left",
|
|
3394
|
+
"sheet-top-center",
|
|
3395
|
+
"sheet-top-right",
|
|
3396
|
+
"dock-bottom",
|
|
3397
|
+
"dock-top"
|
|
3398
|
+
];
|
|
3399
|
+
ANCHOR_ANIMATIONS = [
|
|
3400
|
+
"fade",
|
|
3401
|
+
"scale",
|
|
3402
|
+
"slide",
|
|
3403
|
+
"pop",
|
|
3404
|
+
"none"
|
|
3405
|
+
];
|
|
3406
|
+
parseAnchorOptions = (el) => {
|
|
3407
|
+
const position = el.getAttribute("data-position");
|
|
3408
|
+
const animation = el.getAttribute("data-animation");
|
|
3409
|
+
const showOn = parseList(el.getAttribute("data-show-on"));
|
|
3410
|
+
const fadeRangeRaw = parseList(el.getAttribute("data-fade-range"));
|
|
3411
|
+
const fadeRange = fadeRangeRaw?.length === 2 && fadeRangeRaw.every((v) => Number.isFinite(Number(v))) ? [Number(fadeRangeRaw[0]), Number(fadeRangeRaw[1])] : void 0;
|
|
3412
|
+
return {
|
|
3413
|
+
position: position && ANCHOR_POSITIONS.includes(position) ? position : void 0,
|
|
3414
|
+
inset: el.getAttribute("data-inset") ?? void 0,
|
|
3415
|
+
showOn,
|
|
3416
|
+
fadeRange,
|
|
3417
|
+
interactive: el.getAttribute("data-interactive") !== "false",
|
|
3418
|
+
animation: animation && ANCHOR_ANIMATIONS.includes(animation) ? animation : void 0
|
|
3419
|
+
};
|
|
3420
|
+
};
|
|
3219
3421
|
buildSlot = (name) => {
|
|
3220
3422
|
const slot = document.createElement("slot");
|
|
3221
3423
|
slot.name = name;
|
|
@@ -3285,7 +3487,9 @@ var init_BottomSheetElement = __esm({
|
|
|
3285
3487
|
}
|
|
3286
3488
|
};
|
|
3287
3489
|
};
|
|
3288
|
-
|
|
3490
|
+
BaseHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
|
|
3491
|
+
};
|
|
3492
|
+
exports.BottomSheetElement = class extends BaseHTMLElement {
|
|
3289
3493
|
constructor() {
|
|
3290
3494
|
super();
|
|
3291
3495
|
__publicField(this, "engine", null);
|
|
@@ -3293,17 +3497,12 @@ var init_BottomSheetElement = __esm({
|
|
|
3293
3497
|
__publicField(this, "offHandlers", []);
|
|
3294
3498
|
__publicField(this, "refs");
|
|
3295
3499
|
__publicField(this, "backdropClickHandler", null);
|
|
3500
|
+
__publicField(this, "stylesheetLinked", false);
|
|
3501
|
+
__publicField(this, "declarativeAnchors", []);
|
|
3296
3502
|
this.root = this.attachShadow({ mode: "open" });
|
|
3297
3503
|
const baseStyle = document.createElement("style");
|
|
3298
3504
|
baseStyle.textContent = baseStyles;
|
|
3299
3505
|
this.root.appendChild(baseStyle);
|
|
3300
|
-
const stylesheet = this.getAttribute(ATTR_STYLESHEET);
|
|
3301
|
-
if (stylesheet) {
|
|
3302
|
-
const link = document.createElement("link");
|
|
3303
|
-
link.rel = "stylesheet";
|
|
3304
|
-
link.href = stylesheet;
|
|
3305
|
-
this.root.appendChild(link);
|
|
3306
|
-
}
|
|
3307
3506
|
const inlineStyle = document.createElement("style");
|
|
3308
3507
|
inlineStyle.textContent = [
|
|
3309
3508
|
":host { display: contents; }",
|
|
@@ -3331,10 +3530,22 @@ var init_BottomSheetElement = __esm({
|
|
|
3331
3530
|
ATTR_ANIMATION,
|
|
3332
3531
|
ATTR_FOCUS_TRAP,
|
|
3333
3532
|
ATTR_CLOSE_ON_ESCAPE,
|
|
3334
|
-
ATTR_LOCK_BODY_SCROLL
|
|
3533
|
+
ATTR_LOCK_BODY_SCROLL,
|
|
3534
|
+
ATTR_SHEET_LABEL,
|
|
3535
|
+
ATTR_STACK_EFFECT
|
|
3335
3536
|
];
|
|
3336
3537
|
}
|
|
3337
3538
|
connectedCallback() {
|
|
3539
|
+
if (!this.stylesheetLinked) {
|
|
3540
|
+
this.stylesheetLinked = true;
|
|
3541
|
+
const stylesheet = this.getAttribute(ATTR_STYLESHEET);
|
|
3542
|
+
if (stylesheet) {
|
|
3543
|
+
const link = document.createElement("link");
|
|
3544
|
+
link.rel = "stylesheet";
|
|
3545
|
+
link.href = stylesheet;
|
|
3546
|
+
this.root.appendChild(link);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3338
3549
|
this.initEngine();
|
|
3339
3550
|
}
|
|
3340
3551
|
disconnectedCallback() {
|
|
@@ -3345,6 +3556,9 @@ var init_BottomSheetElement = __esm({
|
|
|
3345
3556
|
this.teardownEngine();
|
|
3346
3557
|
this.initEngine();
|
|
3347
3558
|
}
|
|
3559
|
+
getEngine() {
|
|
3560
|
+
return this.engine;
|
|
3561
|
+
}
|
|
3348
3562
|
teardownEngine() {
|
|
3349
3563
|
this.offHandlers.forEach((off) => off());
|
|
3350
3564
|
this.offHandlers = [];
|
|
@@ -3354,6 +3568,10 @@ var init_BottomSheetElement = __esm({
|
|
|
3354
3568
|
}
|
|
3355
3569
|
this.engine?.destroy();
|
|
3356
3570
|
this.engine = null;
|
|
3571
|
+
for (const el of this.declarativeAnchors) {
|
|
3572
|
+
this.appendChild(el);
|
|
3573
|
+
}
|
|
3574
|
+
this.declarativeAnchors = [];
|
|
3357
3575
|
}
|
|
3358
3576
|
initEngine() {
|
|
3359
3577
|
const { sheet, handle, content, backdrop, announcer } = this.refs;
|
|
@@ -3365,6 +3583,10 @@ var init_BottomSheetElement = __esm({
|
|
|
3365
3583
|
} else {
|
|
3366
3584
|
sheet.removeAttribute("aria-modal");
|
|
3367
3585
|
}
|
|
3586
|
+
sheet.setAttribute(
|
|
3587
|
+
"aria-label",
|
|
3588
|
+
this.getAttribute(ATTR_SHEET_LABEL) ?? "Bottom sheet"
|
|
3589
|
+
);
|
|
3368
3590
|
const showBackdrop = this.getAttribute(ATTR_BACKDROP) !== "false";
|
|
3369
3591
|
backdrop.style.display = showBackdrop ? "" : "none";
|
|
3370
3592
|
if (showBackdrop) {
|
|
@@ -3376,7 +3598,7 @@ var init_BottomSheetElement = __esm({
|
|
|
3376
3598
|
handle,
|
|
3377
3599
|
scrollContainer: content,
|
|
3378
3600
|
backdrop: showBackdrop ? backdrop : void 0,
|
|
3379
|
-
|
|
3601
|
+
scrim: this.refs.screen,
|
|
3380
3602
|
mode,
|
|
3381
3603
|
snapPoints: parseSnapPoints(this.getAttribute(ATTR_SNAP_POINTS)),
|
|
3382
3604
|
allowed: parseList(this.getAttribute(ATTR_ALLOWED)),
|
|
@@ -3384,11 +3606,20 @@ var init_BottomSheetElement = __esm({
|
|
|
3384
3606
|
animation: parseAnimation(this.getAttribute(ATTR_ANIMATION)),
|
|
3385
3607
|
focusTrap,
|
|
3386
3608
|
closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
|
|
3387
|
-
lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false"
|
|
3609
|
+
lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false",
|
|
3610
|
+
stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true"
|
|
3388
3611
|
};
|
|
3389
3612
|
this.engine = new BottomSheetEngine(opts);
|
|
3613
|
+
sheet.setAttribute("data-active", this.engine.state.activeId);
|
|
3614
|
+
for (const el of Array.from(
|
|
3615
|
+
this.querySelectorAll('[slot="anchor"]')
|
|
3616
|
+
)) {
|
|
3617
|
+
this.declarativeAnchors.push(el);
|
|
3618
|
+
this.engine.addAnchor({ ...parseAnchorOptions(el), element: el });
|
|
3619
|
+
}
|
|
3390
3620
|
this.offHandlers = [
|
|
3391
3621
|
this.engine.on("snap", (payload) => {
|
|
3622
|
+
sheet.setAttribute("data-active", payload.id);
|
|
3392
3623
|
announcer.textContent = payload.id;
|
|
3393
3624
|
this.dispatchEvent(new CustomEvent("snap", { detail: payload }));
|
|
3394
3625
|
}),
|
|
@@ -3418,15 +3649,22 @@ var init_BottomSheetElement = __esm({
|
|
|
3418
3649
|
setAllowed(ids, snap) {
|
|
3419
3650
|
this.engine?.setAllowed(ids, snap);
|
|
3420
3651
|
}
|
|
3652
|
+
addAnchor(opts) {
|
|
3653
|
+
return this.engine?.addAnchor(opts) ?? (() => {
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3656
|
+
setScrimStages(opts) {
|
|
3657
|
+
return this.engine?.setScrimStages(opts) ?? (() => {
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3421
3660
|
get sheetState() {
|
|
3422
3661
|
return this.engine?.state ?? null;
|
|
3423
3662
|
}
|
|
3424
3663
|
};
|
|
3425
|
-
registered = false;
|
|
3426
3664
|
exports.defineBottomSheet = (tag = "bottom-sheet") => {
|
|
3427
|
-
if (
|
|
3428
|
-
customElements.define(tag, exports.BottomSheetElement
|
|
3429
|
-
|
|
3665
|
+
if (typeof customElements === "undefined" || customElements.get(tag)) return;
|
|
3666
|
+
customElements.define(tag, class extends exports.BottomSheetElement {
|
|
3667
|
+
});
|
|
3430
3668
|
};
|
|
3431
3669
|
}
|
|
3432
3670
|
});
|