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