@surdeddd/bottom-sheet 0.2.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.
Files changed (49) hide show
  1. package/README.md +51 -21
  2. package/dist/{BottomSheetEngine-CIT9Htti.d.cts → BottomSheetEngine-CAymrH-P.d.cts} +67 -1
  3. package/dist/{BottomSheetEngine-Df9MwBbm.d.ts → BottomSheetEngine-DcqcuudN.d.ts} +67 -1
  4. package/dist/element.cjs +699 -116
  5. package/dist/element.d.cts +7 -2
  6. package/dist/element.d.ts +7 -2
  7. package/dist/element.iife.global.js +2 -2
  8. package/dist/element.js +699 -116
  9. package/dist/index.cjs +715 -145
  10. package/dist/index.d.cts +17 -3
  11. package/dist/index.d.ts +17 -3
  12. package/dist/index.js +713 -146
  13. package/dist/integrations/formik.d.cts +1 -1
  14. package/dist/integrations/formik.d.ts +1 -1
  15. package/dist/integrations/react-hook-form.d.cts +1 -1
  16. package/dist/integrations/react-hook-form.d.ts +1 -1
  17. package/dist/overlay.cjs +168 -35
  18. package/dist/overlay.d.cts +13 -0
  19. package/dist/overlay.d.ts +13 -0
  20. package/dist/overlay.js +168 -35
  21. package/dist/preact.d.cts +1 -1
  22. package/dist/preact.d.ts +1 -1
  23. package/dist/qwik.cjs +619 -119
  24. package/dist/qwik.d.cts +2 -2
  25. package/dist/qwik.d.ts +2 -2
  26. package/dist/qwik.js +620 -120
  27. package/dist/react.cjs +821 -191
  28. package/dist/react.d.cts +20 -3
  29. package/dist/react.d.ts +20 -3
  30. package/dist/react.js +821 -191
  31. package/dist/solid.cjs +656 -161
  32. package/dist/solid.d.cts +263 -4
  33. package/dist/solid.d.ts +263 -4
  34. package/dist/solid.js +657 -162
  35. package/dist/solid.source.jsx +3215 -0
  36. package/dist/styles.css +10 -10
  37. package/dist/svelte-core.cjs +3149 -0
  38. package/dist/svelte-core.js +3147 -0
  39. package/dist/svelte-src/BottomSheet.svelte +210 -0
  40. package/dist/svelte-src/index.js +2 -0
  41. package/dist/svelte.cjs +618 -124
  42. package/dist/svelte.d.cts +100 -0
  43. package/dist/svelte.d.ts +7 -3
  44. package/dist/svelte.js +619 -125
  45. package/dist/vue.cjs +612 -119
  46. package/dist/vue.d.cts +78 -0
  47. package/dist/vue.d.ts +6 -2
  48. package/dist/vue.js +612 -119
  49. package/package.json +103 -43
package/dist/svelte.cjs CHANGED
@@ -46,6 +46,18 @@ var SheetStack = class {
46
46
  this.entries.splice(idx, 1);
47
47
  this.recompute();
48
48
  }
49
+ promote(id) {
50
+ const idx = this.entries.findIndex((e) => e.id === id);
51
+ if (idx === -1) return;
52
+ if (idx !== this.entries.length - 1) {
53
+ const [entry] = this.entries.splice(idx, 1);
54
+ this.entries.push(entry);
55
+ }
56
+ this.recompute();
57
+ }
58
+ update() {
59
+ this.recompute();
60
+ }
49
61
  size() {
50
62
  return this.entries.length;
51
63
  }
@@ -53,9 +65,24 @@ var SheetStack = class {
53
65
  this.entries = [];
54
66
  }
55
67
  recompute() {
68
+ let top = null;
69
+ const open = [];
70
+ for (const entry of this.entries) {
71
+ if (!entry.isOpen || entry.isOpen()) {
72
+ top = entry;
73
+ open.push(entry);
74
+ }
75
+ }
76
+ if (!top && this.entries.length > 0) {
77
+ top = this.entries[this.entries.length - 1];
78
+ }
56
79
  this.entries.forEach((entry, i) => {
57
80
  entry.setZIndex(BASE_Z + i * STEP);
58
- entry.setIsTop(i === this.entries.length - 1);
81
+ entry.setIsTop(entry === top);
82
+ if (entry.setDepth) {
83
+ const openIdx = open.indexOf(entry);
84
+ entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
85
+ }
59
86
  });
60
87
  }
61
88
  };
@@ -340,14 +367,17 @@ function installAutoCollapse(deps) {
340
367
  const fire = () => {
341
368
  if (deps.isDestroyed()) return;
342
369
  if (deps.isDragging()) return;
343
- const allowed = deps.getAllowedIds();
344
- const target = allowed.find((id) => {
370
+ const active = deps.resolveSnap(deps.getActiveId());
371
+ if (!active || active.size === 0) return;
372
+ let target = null;
373
+ for (const id of deps.getAllowedIds()) {
345
374
  const snap = deps.resolveSnap(id);
346
- return snap !== null && snap.size > 0;
347
- });
375
+ if (!snap || snap.size <= 0) continue;
376
+ if (!target || snap.size < target.size) target = snap;
377
+ }
348
378
  if (!target) return;
349
- if (target === deps.getActiveId()) return;
350
- deps.snapTo(target);
379
+ if (target.id === active.id || target.size >= active.size) return;
380
+ deps.snapTo(target.id);
351
381
  };
352
382
  const reset2 = () => {
353
383
  clearTimer();
@@ -358,6 +388,7 @@ function installAutoCollapse(deps) {
358
388
  }, ms);
359
389
  };
360
390
  const unsubscribe = deps.on("snap", () => reset2());
391
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset2();
361
392
  return () => {
362
393
  clearTimer();
363
394
  unsubscribe();
@@ -370,6 +401,7 @@ function notifyLinkedSheets(sheets, self) {
370
401
  for (const linked of sheets) {
371
402
  if (linked === self) continue;
372
403
  try {
404
+ if (linked.state.size === 0) continue;
373
405
  const ids = linked.getAllowedIds();
374
406
  const target = ids.find((id) => id !== "closed");
375
407
  if (target && target !== linked.state.activeId) {
@@ -416,7 +448,7 @@ function installRoute(deps) {
416
448
  const onPop = (event) => {
417
449
  if (deps.isDestroyed()) return;
418
450
  const state2 = event.state;
419
- if (state2 && state2.__bsRouted === target && deps.getSize() > 0) {
451
+ if ((!state2 || state2.__bsRouted !== target) && deps.getSize() > 0) {
420
452
  routePushed = false;
421
453
  void deps.close();
422
454
  }
@@ -570,13 +602,17 @@ function installResizeObserver(deps) {
570
602
  if (viewportSize2 > 0 && viewportSize2 < max) {
571
603
  deps.setMaxAxisSize(viewportSize2);
572
604
  }
605
+ const wasAnimating = deps.isAnimating?.() ?? false;
573
606
  deps.cancelInFlight();
574
607
  deps.newCycle();
575
608
  const current = deps.resolveActiveSnap();
576
609
  const newMax = deps.getMaxAxisSize();
577
610
  const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
578
611
  deps.setSize(targetSize);
579
- if (current && !deps.isDragging()) deps.applySize(targetSize);
612
+ if (current && !deps.isDragging()) {
613
+ deps.applySize(targetSize);
614
+ if (wasAnimating) deps.resyncAfterCancel?.();
615
+ }
580
616
  };
581
617
  let resizeObserver = null;
582
618
  if (typeof ResizeObserver !== "undefined") {
@@ -691,6 +727,85 @@ function devWarn(...args) {
691
727
  }
692
728
  }
693
729
 
730
+ // src/core/primitives/overlay-position.ts
731
+ function resolveSheetAnchoredStyle(mode, position, inset) {
732
+ const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
733
+ switch (mode) {
734
+ case "bottom":
735
+ if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
736
+ if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
737
+ return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
738
+ case "top":
739
+ if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
740
+ if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
741
+ return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
742
+ case "left":
743
+ if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
744
+ if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
745
+ return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
746
+ case "right":
747
+ if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
748
+ if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
749
+ return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
750
+ }
751
+ }
752
+ function applyOverlayPosition(ws, mode, position, inset) {
753
+ switch (position) {
754
+ case "top-left":
755
+ ws.top = inset;
756
+ ws.left = inset;
757
+ break;
758
+ case "top-center":
759
+ ws.top = inset;
760
+ ws.left = "50%";
761
+ ws.transform = "translateX(-50%)";
762
+ break;
763
+ case "top-right":
764
+ ws.top = inset;
765
+ ws.right = inset;
766
+ break;
767
+ case "center-left":
768
+ ws.top = "50%";
769
+ ws.left = inset;
770
+ ws.transform = "translateY(-50%)";
771
+ break;
772
+ case "center":
773
+ ws.top = "50%";
774
+ ws.left = "50%";
775
+ ws.transform = "translate(-50%, -50%)";
776
+ break;
777
+ case "center-right":
778
+ ws.top = "50%";
779
+ ws.right = inset;
780
+ ws.transform = "translateY(-50%)";
781
+ break;
782
+ case "bottom-left":
783
+ ws.bottom = inset;
784
+ ws.left = inset;
785
+ break;
786
+ case "bottom-center":
787
+ ws.bottom = inset;
788
+ ws.left = "50%";
789
+ ws.transform = "translateX(-50%)";
790
+ break;
791
+ case "bottom-right":
792
+ ws.bottom = inset;
793
+ ws.right = inset;
794
+ break;
795
+ case "sheet-top-left":
796
+ case "sheet-top-center":
797
+ case "sheet-top-right": {
798
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
799
+ if (style.top !== void 0) ws.top = style.top;
800
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
801
+ if (style.left !== void 0) ws.left = style.left;
802
+ if (style.right !== void 0) ws.right = style.right;
803
+ if (style.transform !== void 0) ws.transform = style.transform;
804
+ break;
805
+ }
806
+ }
807
+ }
808
+
694
809
  // src/core/controllers/scrim-controller.ts
695
810
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
696
811
  var ScrimController = class {
@@ -703,6 +818,8 @@ var ScrimController = class {
703
818
  __publicField(this, "getAllowedIds");
704
819
  __publicField(this, "getResolvedSnaps");
705
820
  __publicField(this, "snapToFn");
821
+ __publicField(this, "closeFn");
822
+ __publicField(this, "scrimInteractive", false);
706
823
  __publicField(this, "scrimMode");
707
824
  __publicField(this, "backdropRange");
708
825
  __publicField(this, "screenRange");
@@ -723,6 +840,7 @@ var ScrimController = class {
723
840
  this.getAllowedIds = deps.getAllowedIds;
724
841
  this.getResolvedSnaps = deps.getResolvedSnaps;
725
842
  this.snapToFn = deps.snapTo;
843
+ this.closeFn = deps.close;
726
844
  this.scrimMode = opts.scrimMode ?? "full";
727
845
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
728
846
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
@@ -864,11 +982,15 @@ var ScrimController = class {
864
982
  }
865
983
  const onClick = () => {
866
984
  if (this.isDestroyed()) return;
985
+ if (this.closeFn) {
986
+ this.closeFn();
987
+ return;
988
+ }
867
989
  const allowed = this.getAllowedIds();
868
990
  const snaps = this.getResolvedSnaps();
869
991
  const fallback = allowed.find((id) => {
870
992
  const snap = findById(id, snaps);
871
- return snap !== null && snap.size > 0;
993
+ return snap !== null && snap.size === 0;
872
994
  });
873
995
  if (fallback) this.snapToFn(fallback);
874
996
  };
@@ -885,6 +1007,7 @@ var ScrimController = class {
885
1007
  if (this.isDestroyed()) return;
886
1008
  if (enabled === this.scrimEnabled) return;
887
1009
  this.scrimEnabled = enabled;
1010
+ this.invalidateOpacityCache();
888
1011
  if (!enabled) {
889
1012
  this.savedScrimRanges = {
890
1013
  screen: this.screenRange,
@@ -908,7 +1031,12 @@ var ScrimController = class {
908
1031
  blur: cfg.blur ?? null,
909
1032
  interactive: cfg.interactive
910
1033
  });
911
- this.screenRange = cfg.range;
1034
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1035
+ this.savedScrimRanges = { screen: cfg.range, backdrop: cfg.range };
1036
+ } else {
1037
+ this.screenRange = cfg.range;
1038
+ this.backdropRange = cfg.range;
1039
+ }
912
1040
  }
913
1041
  const noop = () => {
914
1042
  };
@@ -958,59 +1086,7 @@ var ScrimController = class {
958
1086
  wrapper.className = "bs-scrim-overlay";
959
1087
  const ws = wrapper.style;
960
1088
  ws.position = "absolute";
961
- switch (position) {
962
- case "top-left":
963
- ws.top = inset;
964
- ws.left = inset;
965
- break;
966
- case "top-center":
967
- ws.top = inset;
968
- ws.left = "50%";
969
- ws.transform = "translateX(-50%)";
970
- break;
971
- case "top-right":
972
- ws.top = inset;
973
- ws.right = inset;
974
- break;
975
- case "center-left":
976
- ws.top = "50%";
977
- ws.left = inset;
978
- ws.transform = "translateY(-50%)";
979
- break;
980
- case "center":
981
- ws.top = "50%";
982
- ws.left = "50%";
983
- ws.transform = "translate(-50%, -50%)";
984
- break;
985
- case "center-right":
986
- ws.top = "50%";
987
- ws.right = inset;
988
- ws.transform = "translateY(-50%)";
989
- break;
990
- case "bottom-left":
991
- ws.bottom = inset;
992
- ws.left = inset;
993
- break;
994
- case "bottom-center":
995
- ws.bottom = inset;
996
- ws.left = "50%";
997
- ws.transform = "translateX(-50%)";
998
- break;
999
- case "bottom-right":
1000
- ws.bottom = inset;
1001
- ws.right = inset;
1002
- break;
1003
- case "sheet-top-left":
1004
- case "sheet-top-center":
1005
- case "sheet-top-right":
1006
- applySheetAnchoredPosition(
1007
- ws,
1008
- this.mode,
1009
- position,
1010
- inset
1011
- );
1012
- break;
1013
- }
1089
+ applyOverlayPosition(ws, this.mode, position, inset);
1014
1090
  if (interactive) {
1015
1091
  ws.pointerEvents = "auto";
1016
1092
  }
@@ -1069,6 +1145,7 @@ var ScrimController = class {
1069
1145
  }
1070
1146
  }
1071
1147
  if (opts.interactive !== void 0) {
1148
+ this.scrimInteractive = opts.interactive;
1072
1149
  s.pointerEvents = opts.interactive ? "auto" : "none";
1073
1150
  }
1074
1151
  }
@@ -1083,38 +1160,9 @@ var ScrimController = class {
1083
1160
  const s = this.screenComponent.style;
1084
1161
  s.position = "fixed";
1085
1162
  s.inset = insetByMode[this.mode];
1086
- s.pointerEvents = "none";
1163
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1087
1164
  }
1088
1165
  };
1089
- function resolveSheetAnchoredStyle(mode, position, inset) {
1090
- const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
1091
- switch (mode) {
1092
- case "bottom":
1093
- if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
1094
- if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
1095
- return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
1096
- case "top":
1097
- if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
1098
- if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
1099
- return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
1100
- case "left":
1101
- if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
1102
- if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
1103
- return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
1104
- case "right":
1105
- if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
1106
- if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
1107
- return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
1108
- }
1109
- }
1110
- function applySheetAnchoredPosition(ws, mode, position, inset) {
1111
- const style = resolveSheetAnchoredStyle(mode, position, inset);
1112
- if (style.top !== void 0) ws.top = style.top;
1113
- if (style.bottom !== void 0) ws.bottom = style.bottom;
1114
- if (style.left !== void 0) ws.left = style.left;
1115
- if (style.right !== void 0) ws.right = style.right;
1116
- if (style.transform !== void 0) ws.transform = style.transform;
1117
- }
1118
1166
 
1119
1167
  // src/core/animation/animation.ts
1120
1168
  var easeOutBack = (t) => {
@@ -1166,6 +1214,10 @@ var tween = ({
1166
1214
  promise
1167
1215
  };
1168
1216
  };
1217
+ var prefersReducedMotion = () => {
1218
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1219
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1220
+ };
1169
1221
 
1170
1222
  // src/core/animation/spring.ts
1171
1223
  var DEFAULT_SPRING = {
@@ -1196,14 +1248,20 @@ var runSpring = ({
1196
1248
  if (cancelled) return;
1197
1249
  const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
1198
1250
  lastTime = now;
1199
- const subDt = Math.min(dt, 1 / 240);
1200
- let stepsLeft = Math.max(1, Math.ceil(dt / subDt));
1251
+ const steps = Math.max(1, Math.ceil(dt * 240));
1252
+ const subDt = dt / steps;
1253
+ let stepsLeft = steps;
1201
1254
  while (stepsLeft-- > 0) {
1202
1255
  const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1203
1256
  const accel = force / cfg.mass;
1204
1257
  v += accel * subDt;
1205
1258
  x += v * subDt;
1206
1259
  }
1260
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1261
+ onUpdate(to, 0);
1262
+ resolve();
1263
+ return;
1264
+ }
1207
1265
  onUpdate(x, v);
1208
1266
  const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1209
1267
  if (isAtRest) {
@@ -1378,9 +1436,13 @@ var isVisible = (el) => {
1378
1436
  var focusables = (root2) => Array.from(root2.querySelectorAll(FOCUSABLE)).filter(
1379
1437
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1380
1438
  );
1439
+ var trapStack = [];
1381
1440
  var installFocusTrap = (container, options = {}) => {
1382
1441
  if (typeof document === "undefined") return () => {
1383
1442
  };
1443
+ const token = {};
1444
+ trapStack.push(token);
1445
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1384
1446
  const previouslyFocused = document.activeElement;
1385
1447
  const focusInitial = () => {
1386
1448
  const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
@@ -1388,6 +1450,7 @@ var installFocusTrap = (container, options = {}) => {
1388
1450
  };
1389
1451
  focusInitial();
1390
1452
  const handleKey = (e) => {
1453
+ if (!isActive()) return;
1391
1454
  if (e.key === "Escape" && options.onEscape) {
1392
1455
  options.onEscape();
1393
1456
  return;
@@ -1410,6 +1473,7 @@ var installFocusTrap = (container, options = {}) => {
1410
1473
  }
1411
1474
  };
1412
1475
  const handleFocusIn = (e) => {
1476
+ if (!isActive()) return;
1413
1477
  const target = e.target;
1414
1478
  if (target && !container.contains(target)) {
1415
1479
  const list = focusables(container);
@@ -1422,6 +1486,8 @@ var installFocusTrap = (container, options = {}) => {
1422
1486
  return () => {
1423
1487
  if (released) return;
1424
1488
  released = true;
1489
+ const idx = trapStack.indexOf(token);
1490
+ if (idx !== -1) trapStack.splice(idx, 1);
1425
1491
  document.removeEventListener("keydown", handleKey, true);
1426
1492
  document.removeEventListener("focusin", handleFocusIn, true);
1427
1493
  if (previouslyFocused && document.contains(previouslyFocused)) {
@@ -1477,11 +1543,17 @@ function createInertSiblings(rootProvider) {
1477
1543
  return {
1478
1544
  apply() {
1479
1545
  if (typeof document === "undefined") return;
1480
- const root2 = rootProvider();
1481
- const parent = root2.parentElement ?? document.body;
1546
+ let top = rootProvider();
1547
+ while (top.parentElement && top.parentElement !== document.body) {
1548
+ top = top.parentElement;
1549
+ }
1550
+ const parent = top.parentElement ?? document.body;
1482
1551
  for (const child2 of Array.from(parent.children)) {
1483
- if (child2 === root2) continue;
1552
+ if (child2 === top) continue;
1484
1553
  if (child2.hasAttribute("inert")) continue;
1554
+ if (child2.classList.contains("bs-backdrop") || child2.classList.contains("bs-screen") || child2.classList.contains("bs-root")) {
1555
+ continue;
1556
+ }
1485
1557
  child2.setAttribute("inert", "");
1486
1558
  tracked.push(child2);
1487
1559
  }
@@ -1497,7 +1569,6 @@ function createInertSiblings(rootProvider) {
1497
1569
  var LifecycleController = class {
1498
1570
  constructor(deps, opts) {
1499
1571
  __publicField(this, "element");
1500
- __publicField(this, "closeFn");
1501
1572
  __publicField(this, "focusTrapEnabled");
1502
1573
  __publicField(this, "initialFocus");
1503
1574
  __publicField(this, "closeOnEscape");
@@ -1511,7 +1582,6 @@ var LifecycleController = class {
1511
1582
  __publicField(this, "installed", false);
1512
1583
  __publicField(this, "destroyed", false);
1513
1584
  this.element = deps.element;
1514
- this.closeFn = deps.close;
1515
1585
  this.focusTrapEnabled = opts.focusTrap ?? false;
1516
1586
  this.initialFocus = opts.initialFocus;
1517
1587
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1521,6 +1591,9 @@ var LifecycleController = class {
1521
1591
  this.returnFocus = opts.returnFocus;
1522
1592
  this.inertSiblings = createInertSiblings(() => this.element);
1523
1593
  }
1594
+ get isInstalled() {
1595
+ return this.installed;
1596
+ }
1524
1597
  install() {
1525
1598
  if (this.destroyed) return;
1526
1599
  this.installed = true;
@@ -1529,8 +1602,7 @@ var LifecycleController = class {
1529
1602
  }
1530
1603
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1531
1604
  this.releaseFocusTrap = installFocusTrap(this.element, {
1532
- initialFocus: this.initialFocus,
1533
- onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
1605
+ initialFocus: this.initialFocus
1534
1606
  });
1535
1607
  }
1536
1608
  if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
@@ -1684,10 +1756,11 @@ var installGestures = (handle, mode, callbacks) => {
1684
1756
  };
1685
1757
 
1686
1758
  // src/core/primitives/rubber-band.ts
1687
- var RUBBER_BAND_FACTOR = 0.55;
1688
1759
  function rubberBand(overshoot, maxAxisSize) {
1689
1760
  const cap = Math.min(maxAxisSize * 0.15, 80);
1690
- return Math.sign(overshoot) * Math.pow(Math.abs(overshoot), RUBBER_BAND_FACTOR) * Math.min(6, cap / Math.max(Math.abs(overshoot), 1));
1761
+ if (cap <= 0) return 0;
1762
+ const abs = Math.abs(overshoot);
1763
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1691
1764
  }
1692
1765
 
1693
1766
  // src/core/features/soft-keyboard.ts
@@ -1772,6 +1845,10 @@ var GestureController = class {
1772
1845
  this.isDragging_ = false;
1773
1846
  this.keyboardDismissed = false;
1774
1847
  this.deps.getRoot()?.removeAttribute("data-dragging");
1848
+ this.deps.emit("dragend", {
1849
+ size: this.deps.getDragContext().size,
1850
+ velocity: 0
1851
+ });
1775
1852
  const restoreTo = this.dragStartSize;
1776
1853
  void this.deps.animateTo(restoreTo, 0);
1777
1854
  }
@@ -1845,6 +1922,287 @@ function createScrollCache(deps) {
1845
1922
  };
1846
1923
  }
1847
1924
 
1925
+ // src/core/primitives/anchor-animations.ts
1926
+ var DEFAULT_DURATION3 = 200;
1927
+ var DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
1928
+ var PRESET_FRAMES = {
1929
+ fade: {
1930
+ enter: [{ opacity: 0 }, { opacity: 1 }],
1931
+ exit: [{ opacity: 1 }, { opacity: 0 }]
1932
+ },
1933
+ scale: {
1934
+ enter: [
1935
+ { opacity: 0, transform: "scale(0.85)" },
1936
+ { opacity: 1, transform: "scale(1)" }
1937
+ ],
1938
+ exit: [
1939
+ { opacity: 1, transform: "scale(1)" },
1940
+ { opacity: 0, transform: "scale(0.85)" }
1941
+ ]
1942
+ },
1943
+ slide: {
1944
+ enter: [
1945
+ { opacity: 0, transform: "translateY(10px)" },
1946
+ { opacity: 1, transform: "translateY(0)" }
1947
+ ],
1948
+ exit: [
1949
+ { opacity: 1, transform: "translateY(0)" },
1950
+ { opacity: 0, transform: "translateY(10px)" }
1951
+ ]
1952
+ },
1953
+ pop: {
1954
+ enter: [
1955
+ { opacity: 0, transform: "scale(0.6)", offset: 0 },
1956
+ { opacity: 1, transform: "scale(1.06)", offset: 0.7 },
1957
+ { opacity: 1, transform: "scale(1)", offset: 1 }
1958
+ ],
1959
+ exit: [
1960
+ { opacity: 1, transform: "scale(1)" },
1961
+ { opacity: 0, transform: "scale(0.6)" }
1962
+ ]
1963
+ }
1964
+ };
1965
+ var INSTANT = {
1966
+ finished: Promise.resolve(),
1967
+ cancel: () => {
1968
+ }
1969
+ };
1970
+ function resolveTransitionFrames(spec, phase) {
1971
+ const fallback = { frames: null, duration: 0, easing: DEFAULT_EASING };
1972
+ if (spec === "none") return fallback;
1973
+ if (spec === void 0 || typeof spec === "string") {
1974
+ const preset2 = PRESET_FRAMES[spec ?? "fade"];
1975
+ return {
1976
+ frames: preset2[phase],
1977
+ duration: DEFAULT_DURATION3,
1978
+ easing: DEFAULT_EASING
1979
+ };
1980
+ }
1981
+ const custom = phase === "enter" ? spec.enter : spec.exit;
1982
+ if (custom) {
1983
+ return {
1984
+ frames: custom,
1985
+ duration: spec.duration ?? DEFAULT_DURATION3,
1986
+ easing: spec.easing ?? DEFAULT_EASING
1987
+ };
1988
+ }
1989
+ if (spec.preset === "none") return fallback;
1990
+ const preset = PRESET_FRAMES[spec.preset ?? "fade"];
1991
+ return {
1992
+ frames: preset[phase],
1993
+ duration: spec.duration ?? DEFAULT_DURATION3,
1994
+ easing: spec.easing ?? DEFAULT_EASING
1995
+ };
1996
+ }
1997
+ function runAnchorTransition(el, spec, phase) {
1998
+ const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
1999
+ if (respectReduced && prefersReducedMotion()) return INSTANT;
2000
+ const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
2001
+ if (!frames || duration <= 0) return INSTANT;
2002
+ if (typeof el.animate !== "function") return INSTANT;
2003
+ const animation = el.animate(frames, {
2004
+ duration,
2005
+ easing,
2006
+ fill: "both"
2007
+ });
2008
+ const finished = animation.finished.then(() => void 0).catch(() => void 0);
2009
+ return {
2010
+ finished,
2011
+ cancel: () => {
2012
+ try {
2013
+ animation.cancel();
2014
+ } catch {
2015
+ return;
2016
+ }
2017
+ }
2018
+ };
2019
+ }
2020
+
2021
+ // src/core/features/sheet-anchors.ts
2022
+ function attachAnchor(deps, opts) {
2023
+ const doc = deps.host.ownerDocument;
2024
+ const wrapper = doc.createElement("div");
2025
+ wrapper.className = "bs-anchor";
2026
+ const interactive = opts.interactive ?? true;
2027
+ const position = opts.position ?? "sheet-top-right";
2028
+ const isDock = position === "dock-bottom" || position === "dock-top";
2029
+ const ws = wrapper.style;
2030
+ ws.position = "fixed";
2031
+ if (isDock) {
2032
+ ws.left = "0";
2033
+ ws.right = "0";
2034
+ if (position === "dock-bottom") {
2035
+ ws.bottom = "0";
2036
+ } else {
2037
+ ws.top = "0";
2038
+ }
2039
+ } else {
2040
+ applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2041
+ }
2042
+ if (opts.fadeRange) {
2043
+ const [r0, r1] = opts.fadeRange;
2044
+ const span = Math.max(r1 - r0, 1e-4);
2045
+ ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
2046
+ }
2047
+ wrapper.appendChild(opts.element);
2048
+ deps.host.appendChild(wrapper);
2049
+ let visible = null;
2050
+ let inFlight = null;
2051
+ const shouldShow = (state2) => {
2052
+ if (typeof opts.showOn === "function") return opts.showOn(state2);
2053
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state2.activeId);
2054
+ return isDock || state2.size > 0;
2055
+ };
2056
+ const applyVisibility = (next, animate) => {
2057
+ if (next === visible) return;
2058
+ visible = next;
2059
+ inFlight?.cancel();
2060
+ inFlight = null;
2061
+ wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
2062
+ if (next) {
2063
+ wrapper.style.visibility = "";
2064
+ if (animate) {
2065
+ inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
2066
+ }
2067
+ return;
2068
+ }
2069
+ if (!animate) {
2070
+ wrapper.style.visibility = "hidden";
2071
+ return;
2072
+ }
2073
+ const handle = runAnchorTransition(opts.element, opts.animation, "exit");
2074
+ inFlight = handle;
2075
+ void handle.finished.then(() => {
2076
+ if (inFlight === handle && visible === false) {
2077
+ wrapper.style.visibility = "hidden";
2078
+ }
2079
+ });
2080
+ };
2081
+ const evaluate = (animate) => {
2082
+ if (deps.isDestroyed()) return;
2083
+ applyVisibility(shouldShow(deps.getState()), animate);
2084
+ };
2085
+ const offs = [
2086
+ deps.on("snap", () => evaluate(true)),
2087
+ deps.on("open", () => evaluate(true)),
2088
+ deps.on("close", () => evaluate(true))
2089
+ ];
2090
+ evaluate(false);
2091
+ return {
2092
+ wrapper,
2093
+ detach: () => {
2094
+ offs.forEach((off) => off());
2095
+ inFlight?.cancel();
2096
+ inFlight = null;
2097
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
2098
+ },
2099
+ syncZ: (z) => {
2100
+ wrapper.style.zIndex = String(z);
2101
+ }
2102
+ };
2103
+ }
2104
+
2105
+ // src/core/features/scrim-stages.ts
2106
+ var matchesId = (def, activeId) => {
2107
+ if (def.for === void 0) return false;
2108
+ return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
2109
+ };
2110
+ var matchesRange = (def, progress) => {
2111
+ if (!def.forRange) return false;
2112
+ const [from, to] = def.forRange;
2113
+ return progress >= from && progress <= to;
2114
+ };
2115
+ function installScrimStages(deps, opts) {
2116
+ const doc = deps.host.ownerDocument;
2117
+ const entries = opts.stages.map((def) => {
2118
+ const wrapper = doc.createElement("div");
2119
+ wrapper.className = "bs-scrim-stage";
2120
+ const ws = wrapper.style;
2121
+ ws.position = "absolute";
2122
+ applyOverlayPosition(
2123
+ ws,
2124
+ deps.mode,
2125
+ def.position ?? opts.position ?? "center",
2126
+ def.inset ?? opts.inset ?? "16px"
2127
+ );
2128
+ ws.visibility = "hidden";
2129
+ ws.pointerEvents = "none";
2130
+ wrapper.appendChild(def.element);
2131
+ deps.host.appendChild(wrapper);
2132
+ return { def, wrapper, inFlight: null };
2133
+ });
2134
+ let active = null;
2135
+ const resolveActive = () => {
2136
+ const state2 = deps.getState();
2137
+ if (state2.size === 0) {
2138
+ return entries.find((e) => matchesId(e.def, state2.activeId)) ?? null;
2139
+ }
2140
+ return entries.find((e) => matchesId(e.def, state2.activeId)) ?? entries.find((e) => matchesRange(e.def, state2.progress)) ?? null;
2141
+ };
2142
+ const hide = (entry, animate) => {
2143
+ entry.inFlight?.cancel();
2144
+ entry.wrapper.style.pointerEvents = "none";
2145
+ if (!animate) {
2146
+ entry.inFlight = null;
2147
+ entry.wrapper.style.visibility = "hidden";
2148
+ return;
2149
+ }
2150
+ const handle = runAnchorTransition(
2151
+ entry.def.element,
2152
+ entry.def.animation ?? opts.animation,
2153
+ "exit"
2154
+ );
2155
+ entry.inFlight = handle;
2156
+ void handle.finished.then(() => {
2157
+ if (entry.inFlight === handle && active !== entry) {
2158
+ entry.wrapper.style.visibility = "hidden";
2159
+ }
2160
+ });
2161
+ };
2162
+ const show = (entry, animate) => {
2163
+ entry.inFlight?.cancel();
2164
+ entry.inFlight = null;
2165
+ entry.wrapper.style.visibility = "";
2166
+ entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
2167
+ if (animate) {
2168
+ entry.inFlight = runAnchorTransition(
2169
+ entry.def.element,
2170
+ entry.def.animation ?? opts.animation,
2171
+ "enter"
2172
+ );
2173
+ }
2174
+ };
2175
+ const update = (animate) => {
2176
+ if (deps.isDestroyed()) return;
2177
+ const next = resolveActive();
2178
+ if (next === active) return;
2179
+ const prev = active;
2180
+ active = next;
2181
+ if (prev) hide(prev, animate);
2182
+ if (next) show(next, animate);
2183
+ };
2184
+ const offs = [
2185
+ deps.on("snap", () => update(true)),
2186
+ deps.on("open", () => update(true)),
2187
+ deps.on("close", () => update(true))
2188
+ ];
2189
+ if (entries.some((e) => e.def.forRange)) {
2190
+ offs.push(deps.on("progress", () => update(true)));
2191
+ }
2192
+ update(false);
2193
+ return () => {
2194
+ offs.forEach((off) => off());
2195
+ for (const entry of entries) {
2196
+ entry.inFlight?.cancel();
2197
+ entry.inFlight = null;
2198
+ if (entry.wrapper.parentNode) {
2199
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2200
+ }
2201
+ }
2202
+ active = null;
2203
+ };
2204
+ }
2205
+
1848
2206
  // src/core/primitives/engine-options.ts
1849
2207
  var DEFAULT_FLICK_VELOCITY = 0.65;
1850
2208
  var DEFAULT_DRAG_THRESHOLD = 18;
@@ -1961,6 +2319,12 @@ var BottomSheetEngine = class {
1961
2319
  __publicField(this, "linkedSheets", []);
1962
2320
  __publicField(this, "scrollCache");
1963
2321
  __publicField(this, "teardowns", new TeardownStack());
2322
+ __publicField(this, "anchors", []);
2323
+ __publicField(this, "anchorHost", null);
2324
+ __publicField(this, "stackZ", 100);
2325
+ __publicField(this, "detachScrimStages", null);
2326
+ __publicField(this, "stackEffectEnabled", false);
2327
+ __publicField(this, "stackEffectPrimed", false);
1964
2328
  __publicField(this, "transformTemplate");
1965
2329
  const resolved = resolveEngineOptions(opts, {
1966
2330
  shouldApplyInertSiblings: () => {
@@ -1994,6 +2358,9 @@ var BottomSheetEngine = class {
1994
2358
  getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
1995
2359
  snapTo: (id) => {
1996
2360
  void this.snapTo(id);
2361
+ },
2362
+ close: () => {
2363
+ void this.close();
1997
2364
  }
1998
2365
  },
1999
2366
  resolved.scrim
@@ -2017,6 +2384,7 @@ var BottomSheetEngine = class {
2017
2384
  );
2018
2385
  this.closeOnBack = resolved.closeOnBack;
2019
2386
  this.routedTo = opts.routedTo;
2387
+ this.stackEffectEnabled = opts.stackEffect ?? false;
2020
2388
  this.snapPointsRaw = opts.snapPoints;
2021
2389
  this.persistKey = opts.persistKey;
2022
2390
  if (opts.linkedSheets) {
@@ -2029,7 +2397,7 @@ var BottomSheetEngine = class {
2029
2397
  this.snapPointsRaw,
2030
2398
  resolved.initialAllowed,
2031
2399
  this.mode,
2032
- () => this.handle.offsetHeight,
2400
+ () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2033
2401
  (maxAxisSize) => {
2034
2402
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2035
2403
  }
@@ -2239,11 +2607,6 @@ var BottomSheetEngine = class {
2239
2607
  }
2240
2608
  setAllowed(ids, snap) {
2241
2609
  if (this.destroyed) return;
2242
- const willSnap = snap !== void 0 && ids.includes(snap) || !ids.includes(this.activeId) && ids.length > 0;
2243
- if (!willSnap) {
2244
- this.animation.cancel();
2245
- this.newCycle();
2246
- }
2247
2610
  this.snaps.setAllowedIds(ids);
2248
2611
  this.updateAriaSlider();
2249
2612
  if (snap && ids.includes(snap)) {
@@ -2283,6 +2646,79 @@ var BottomSheetEngine = class {
2283
2646
  setScrimOverlay(opts) {
2284
2647
  return this.scrim.setScrimOverlay(opts);
2285
2648
  }
2649
+ addAnchor(opts) {
2650
+ if (this.destroyed || typeof document === "undefined") return () => {
2651
+ };
2652
+ if (!this.anchorHost) {
2653
+ this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
2654
+ this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
2655
+ this.anchorHost.style.setProperty(
2656
+ "--bs-progress",
2657
+ String(this.computeProgress(this.size))
2658
+ );
2659
+ }
2660
+ const handle = attachAnchor(
2661
+ {
2662
+ mode: this.mode,
2663
+ host: this.anchorHost,
2664
+ getState: () => ({
2665
+ activeId: this.activeId,
2666
+ size: this.size,
2667
+ progress: this.computeProgress(this.size)
2668
+ }),
2669
+ on: (event, fn) => this.on(event, fn),
2670
+ isDestroyed: () => this.destroyed
2671
+ },
2672
+ opts
2673
+ );
2674
+ handle.syncZ(this.stackZ + 1);
2675
+ this.anchors.push(handle);
2676
+ const detach = () => {
2677
+ const idx = this.anchors.indexOf(handle);
2678
+ if (idx === -1) return;
2679
+ this.anchors.splice(idx, 1);
2680
+ handle.detach();
2681
+ };
2682
+ this.teardowns.add(detach);
2683
+ return detach;
2684
+ }
2685
+ setScrimStages(opts) {
2686
+ if (this.destroyed || typeof document === "undefined") return () => {
2687
+ };
2688
+ this.detachScrimStages?.();
2689
+ this.detachScrimStages = null;
2690
+ if (!opts) return () => {
2691
+ };
2692
+ const host = this.screenComponent?.parentElement;
2693
+ if (!host) {
2694
+ console.warn(
2695
+ "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
2696
+ );
2697
+ return () => {
2698
+ };
2699
+ }
2700
+ const detachInstalled = installScrimStages(
2701
+ {
2702
+ mode: this.mode,
2703
+ host,
2704
+ getState: () => ({
2705
+ activeId: this.activeId,
2706
+ size: this.size,
2707
+ progress: this.computeProgress(this.size)
2708
+ }),
2709
+ on: (event, fn) => this.on(event, fn),
2710
+ isDestroyed: () => this.destroyed
2711
+ },
2712
+ opts
2713
+ );
2714
+ const detach = () => {
2715
+ if (this.detachScrimStages === detach) this.detachScrimStages = null;
2716
+ detachInstalled();
2717
+ };
2718
+ this.detachScrimStages = detach;
2719
+ this.teardowns.add(() => this.detachScrimStages?.());
2720
+ return detach;
2721
+ }
2286
2722
  getScrimState() {
2287
2723
  return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
2288
2724
  }
@@ -2369,24 +2805,45 @@ var BottomSheetEngine = class {
2369
2805
  getAllowedRange() {
2370
2806
  return this.snaps.getAllowedRange();
2371
2807
  }
2808
+ allowedIdsBySize() {
2809
+ return this.snaps.getAllowedIds().slice().sort(
2810
+ (a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0)
2811
+ );
2812
+ }
2372
2813
  updateAriaSlider() {
2373
- this.aria.setValue(this.snaps.getAllowedIds(), this.activeId);
2814
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
2374
2815
  }
2375
2816
  registerInStack() {
2376
2817
  this.teardowns.add(sheetStack.push({
2377
2818
  id: this.id,
2378
2819
  setZIndex: (z) => {
2820
+ this.stackZ = z;
2379
2821
  this.element.style.zIndex = String(z);
2380
2822
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
2823
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
2381
2824
  },
2382
2825
  setIsTop: (isTop) => {
2383
2826
  this.isTopSheet = isTop;
2384
2827
  if (this.backdrop) {
2385
2828
  this.backdrop.style.display = isTop ? "" : "none";
2386
2829
  }
2387
- }
2830
+ },
2831
+ isOpen: () => this.size > 0,
2832
+ setDepth: (depth) => this.applyStackDepth(depth)
2388
2833
  }));
2389
2834
  }
2835
+ applyStackDepth(depth) {
2836
+ if (!this.stackEffectEnabled) return;
2837
+ this.element.setAttribute("data-stack-depth", String(depth));
2838
+ if (!this.stackEffectPrimed) {
2839
+ this.stackEffectPrimed = true;
2840
+ const isVertical = this.mode === "bottom" || this.mode === "top";
2841
+ this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
2842
+ this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
2843
+ }
2844
+ const scale = Math.max(1 - depth * 0.04, 0.86);
2845
+ this.element.style.scale = scale === 1 ? "" : String(scale);
2846
+ }
2390
2847
  attach() {
2391
2848
  this.gesture = new GestureController({
2392
2849
  handle: this.handle,
@@ -2446,7 +2903,7 @@ var BottomSheetEngine = class {
2446
2903
  container: this.scrollContainer,
2447
2904
  isDragging: () => this.gesture?.isDragging ?? false,
2448
2905
  isAnimating: () => this.animation.isAnimating,
2449
- getAllowedIds: () => this.snaps.getAllowedIds(),
2906
+ getAllowedIds: () => this.allowedIdsBySize(),
2450
2907
  getActiveId: () => this.activeId,
2451
2908
  snapTo: (id) => {
2452
2909
  void this.snapTo(id);
@@ -2475,11 +2932,13 @@ var BottomSheetEngine = class {
2475
2932
  },
2476
2933
  newCycle: () => {
2477
2934
  this.newCycle();
2478
- }
2935
+ },
2936
+ isAnimating: () => this.animation.isAnimating,
2937
+ resyncAfterCancel: () => this.resyncAfterResize()
2479
2938
  }));
2480
2939
  if (this.lifecycle.closeOnEscape) {
2481
2940
  const onKey = (e) => {
2482
- if (this.destroyed) return;
2941
+ if (this.destroyed || e.defaultPrevented) return;
2483
2942
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2484
2943
  void this.close();
2485
2944
  }
@@ -2511,7 +2970,7 @@ var BottomSheetEngine = class {
2511
2970
  handle: this.handle,
2512
2971
  mode: this.mode,
2513
2972
  isDestroyed: () => this.destroyed,
2514
- getAllowedIds: () => this.snaps.getAllowedIds(),
2973
+ getAllowedIds: () => this.allowedIdsBySize(),
2515
2974
  getActiveId: () => this.activeId,
2516
2975
  snapTo: (id) => {
2517
2976
  void this.snapTo(id);
@@ -2532,6 +2991,7 @@ var BottomSheetEngine = class {
2532
2991
  });
2533
2992
  if (!target) return;
2534
2993
  const previousId = this.activeId;
2994
+ if (target.id === previousId && target.size === this.size) return;
2535
2995
  if (this.emitBeforeSnap(target, previousId)) {
2536
2996
  const restore = this.snaps.findById(previousId);
2537
2997
  if (restore) {
@@ -2540,6 +3000,7 @@ var BottomSheetEngine = class {
2540
3000
  return;
2541
3001
  }
2542
3002
  const previousSize = this.size;
3003
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
2543
3004
  this.scrollCache.cache(previousId, previousSize, target.size);
2544
3005
  this.activeId = target.id;
2545
3006
  void this.animation.animateTo(target.size, velocity).then(() => {
@@ -2548,9 +3009,10 @@ var BottomSheetEngine = class {
2548
3009
  this.updateAriaSlider();
2549
3010
  this.emit("snap", { id: target.id, size: target.size });
2550
3011
  this.haptic();
2551
- if (previousId === "closed" && target.size > 0) {
3012
+ if (previousSnapSize === 0 && target.size > 0) {
2552
3013
  this.emit("open", { id: target.id });
2553
3014
  this.handleOpen();
3015
+ notifyLinkedSheets(this.linkedSheets, this);
2554
3016
  }
2555
3017
  if (target.size === 0) {
2556
3018
  this.emit("close", void 0);
@@ -2576,6 +3038,9 @@ var BottomSheetEngine = class {
2576
3038
  if (this.scrimParent) {
2577
3039
  this.scrimParent.style.setProperty("--bs-size", `${size}px`);
2578
3040
  }
3041
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3042
+ this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3043
+ }
2579
3044
  }
2580
3045
  const progress = this.computeProgress(size);
2581
3046
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -2584,6 +3049,9 @@ var BottomSheetEngine = class {
2584
3049
  );
2585
3050
  if (progressChanged) {
2586
3051
  style.setProperty("--bs-progress", String(progress));
3052
+ if (this.anchorHost) {
3053
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3054
+ }
2587
3055
  }
2588
3056
  this.scrim.applyOpacity(progress, progressChanged);
2589
3057
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -2598,6 +3066,7 @@ var BottomSheetEngine = class {
2598
3066
  return Math.min(Math.max((size - min) / (max - min), 0), 1);
2599
3067
  }
2600
3068
  handleOpen() {
3069
+ sheetStack.promote(this.id);
2601
3070
  try {
2602
3071
  this.lifecycle.install();
2603
3072
  } catch (err) {
@@ -2611,8 +3080,24 @@ var BottomSheetEngine = class {
2611
3080
  });
2612
3081
  }
2613
3082
  }
3083
+ resyncAfterResize() {
3084
+ if (this.destroyed) return;
3085
+ const target = this.snaps.findById(this.activeId);
3086
+ if (!target) return;
3087
+ this.updateAriaSlider();
3088
+ this.emit("snap", { id: target.id, size: target.size });
3089
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3090
+ this.emit("open", { id: target.id });
3091
+ this.handleOpen();
3092
+ notifyLinkedSheets(this.linkedSheets, this);
3093
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3094
+ this.emit("close", void 0);
3095
+ this.handleClose();
3096
+ }
3097
+ }
2614
3098
  handleClose() {
2615
3099
  this.lifecycle.release();
3100
+ sheetStack.update();
2616
3101
  }
2617
3102
  };
2618
3103
 
@@ -2630,17 +3115,18 @@ function createBottomSheet(opts) {
2630
3115
  const attach = (refs) => {
2631
3116
  if (engine) engine.destroy();
2632
3117
  const { onSnap, ...engineOpts } = opts;
2633
- engine = new BottomSheetEngine({ ...engineOpts, ...refs });
3118
+ const created = new BottomSheetEngine({ ...engineOpts, ...refs });
3119
+ engine = created;
2634
3120
  if (onSnap) {
2635
- engine.on("snap", (payload) => onSnap(payload.id));
3121
+ created.on("snap", (payload) => onSnap(payload.id));
2636
3122
  }
2637
3123
  for (const entry of pending) {
2638
- entry.engineUnsub = engine.on(entry.event, entry.fn);
3124
+ entry.engineUnsub = created.on(entry.event, entry.fn);
2639
3125
  }
2640
3126
  pending.length = 0;
2641
3127
  return () => {
2642
- engine?.destroy();
2643
- engine = null;
3128
+ created.destroy();
3129
+ if (engine === created) engine = null;
2644
3130
  };
2645
3131
  };
2646
3132
  return {
@@ -2664,6 +3150,14 @@ function createBottomSheet(opts) {
2664
3150
  open: (id) => engine?.open(id) ?? Promise.resolve(),
2665
3151
  close: () => engine?.close() ?? Promise.resolve(),
2666
3152
  setAllowed: (ids, snap) => engine?.setAllowed(ids, snap),
3153
+ setSnapPoints: (points, allowed) => engine?.setSnapPoints(points, allowed),
3154
+ setScrim: (scrimOpts) => engine?.setScrim(scrimOpts),
3155
+ setScrimOverlay: (overlayOpts) => engine?.setScrimOverlay(overlayOpts) ?? (() => {
3156
+ }),
3157
+ addAnchor: (anchorOpts) => engine?.addAnchor(anchorOpts) ?? (() => {
3158
+ }),
3159
+ setScrimStages: (stagesOpts) => engine?.setScrimStages(stagesOpts) ?? (() => {
3160
+ }),
2667
3161
  destroy: () => {
2668
3162
  engine?.destroy();
2669
3163
  engine = null;
@@ -2685,7 +3179,7 @@ function BottomSheet($$anchor, $$props) {
2685
3179
  let screenEl = $2__namespace.state(void 0);
2686
3180
  let viewState = $2__namespace.state($2__namespace.proxy({
2687
3181
  size: 0,
2688
- activeId: $$props.initial ?? $$props.snapPoints[0]?.id ?? "default",
3182
+ activeId: svelte.untrack(() => $$props.initial ?? $$props.snapPoints[0]?.id ?? "default"),
2689
3183
  isDragging: false,
2690
3184
  isAnimating: false,
2691
3185
  progress: 0