@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/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(i === this.entries.length - 1);
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
  };
@@ -362,14 +389,17 @@ function installAutoCollapse(deps) {
362
389
  const fire = () => {
363
390
  if (deps.isDestroyed()) return;
364
391
  if (deps.isDragging()) return;
365
- const allowed = deps.getAllowedIds();
366
- const target = allowed.find((id) => {
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()) {
367
396
  const snap = deps.resolveSnap(id);
368
- return snap !== null && snap.size > 0;
369
- });
397
+ if (!snap || snap.size <= 0) continue;
398
+ if (!target || snap.size < target.size) target = snap;
399
+ }
370
400
  if (!target) return;
371
- if (target === deps.getActiveId()) return;
372
- deps.snapTo(target);
401
+ if (target.id === active.id || target.size >= active.size) return;
402
+ deps.snapTo(target.id);
373
403
  };
374
404
  const reset = () => {
375
405
  clearTimer();
@@ -380,6 +410,7 @@ function installAutoCollapse(deps) {
380
410
  }, ms);
381
411
  };
382
412
  const unsubscribe = deps.on("snap", () => reset());
413
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
383
414
  return () => {
384
415
  clearTimer();
385
416
  unsubscribe();
@@ -396,6 +427,7 @@ function notifyLinkedSheets(sheets, self) {
396
427
  for (const linked of sheets) {
397
428
  if (linked === self) continue;
398
429
  try {
430
+ if (linked.state.size === 0) continue;
399
431
  const ids = linked.getAllowedIds();
400
432
  const target = ids.find((id) => id !== "closed");
401
433
  if (target && target !== linked.state.activeId) {
@@ -446,7 +478,7 @@ function installRoute(deps) {
446
478
  const onPop = (event) => {
447
479
  if (deps.isDestroyed()) return;
448
480
  const state = event.state;
449
- if (state && state.__bsRouted === target && deps.getSize() > 0) {
481
+ if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
450
482
  routePushed = false;
451
483
  void deps.close();
452
484
  }
@@ -614,13 +646,17 @@ function installResizeObserver(deps) {
614
646
  if (viewportSize2 > 0 && viewportSize2 < max) {
615
647
  deps.setMaxAxisSize(viewportSize2);
616
648
  }
649
+ const wasAnimating = deps.isAnimating?.() ?? false;
617
650
  deps.cancelInFlight();
618
651
  deps.newCycle();
619
652
  const current = deps.resolveActiveSnap();
620
653
  const newMax = deps.getMaxAxisSize();
621
654
  const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
622
655
  deps.setSize(targetSize);
623
- if (current && !deps.isDragging()) deps.applySize(targetSize);
656
+ if (current && !deps.isDragging()) {
657
+ deps.applySize(targetSize);
658
+ if (wasAnimating) deps.resyncAfterCancel?.();
659
+ }
624
660
  };
625
661
  let resizeObserver = null;
626
662
  if (typeof ResizeObserver !== "undefined") {
@@ -762,7 +798,7 @@ var init_devWarn = __esm({
762
798
  }
763
799
  });
764
800
 
765
- // src/core/controllers/scrim-controller.ts
801
+ // src/core/primitives/overlay-position.ts
766
802
  function resolveSheetAnchoredStyle(mode, position, inset) {
767
803
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
768
804
  switch (mode) {
@@ -784,14 +820,68 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
784
820
  return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
785
821
  }
786
822
  }
787
- function applySheetAnchoredPosition(ws, mode, position, inset) {
788
- const style = resolveSheetAnchoredStyle(mode, position, inset);
789
- if (style.top !== void 0) ws.top = style.top;
790
- if (style.bottom !== void 0) ws.bottom = style.bottom;
791
- if (style.left !== void 0) ws.left = style.left;
792
- if (style.right !== void 0) ws.right = style.right;
793
- if (style.transform !== void 0) ws.transform = style.transform;
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
+ }
794
878
  }
879
+ var init_overlay_position = __esm({
880
+ "src/core/primitives/overlay-position.ts"() {
881
+ }
882
+ });
883
+
884
+ // src/core/controllers/scrim-controller.ts
795
885
  var VALID_CSS_LENGTH, ScrimController;
796
886
  var init_scrim_controller = __esm({
797
887
  "src/core/controllers/scrim-controller.ts"() {
@@ -800,6 +890,7 @@ var init_scrim_controller = __esm({
800
890
  init_hot_path_thresholds();
801
891
  init_opacity_dedup();
802
892
  init_devWarn();
893
+ init_overlay_position();
803
894
  VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
804
895
  ScrimController = class {
805
896
  constructor(deps, opts) {
@@ -811,6 +902,8 @@ var init_scrim_controller = __esm({
811
902
  __publicField(this, "getAllowedIds");
812
903
  __publicField(this, "getResolvedSnaps");
813
904
  __publicField(this, "snapToFn");
905
+ __publicField(this, "closeFn");
906
+ __publicField(this, "scrimInteractive", false);
814
907
  __publicField(this, "scrimMode");
815
908
  __publicField(this, "backdropRange");
816
909
  __publicField(this, "screenRange");
@@ -831,6 +924,7 @@ var init_scrim_controller = __esm({
831
924
  this.getAllowedIds = deps.getAllowedIds;
832
925
  this.getResolvedSnaps = deps.getResolvedSnaps;
833
926
  this.snapToFn = deps.snapTo;
927
+ this.closeFn = deps.close;
834
928
  this.scrimMode = opts.scrimMode ?? "full";
835
929
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
836
930
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
@@ -972,11 +1066,15 @@ var init_scrim_controller = __esm({
972
1066
  }
973
1067
  const onClick = () => {
974
1068
  if (this.isDestroyed()) return;
1069
+ if (this.closeFn) {
1070
+ this.closeFn();
1071
+ return;
1072
+ }
975
1073
  const allowed = this.getAllowedIds();
976
1074
  const snaps = this.getResolvedSnaps();
977
1075
  const fallback = allowed.find((id) => {
978
1076
  const snap = findById(id, snaps);
979
- return snap !== null && snap.size > 0;
1077
+ return snap !== null && snap.size === 0;
980
1078
  });
981
1079
  if (fallback) this.snapToFn(fallback);
982
1080
  };
@@ -993,6 +1091,7 @@ var init_scrim_controller = __esm({
993
1091
  if (this.isDestroyed()) return;
994
1092
  if (enabled === this.scrimEnabled) return;
995
1093
  this.scrimEnabled = enabled;
1094
+ this.invalidateOpacityCache();
996
1095
  if (!enabled) {
997
1096
  this.savedScrimRanges = {
998
1097
  screen: this.screenRange,
@@ -1016,7 +1115,12 @@ var init_scrim_controller = __esm({
1016
1115
  blur: cfg.blur ?? null,
1017
1116
  interactive: cfg.interactive
1018
1117
  });
1019
- this.screenRange = cfg.range;
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
+ }
1020
1124
  }
1021
1125
  const noop = () => {
1022
1126
  };
@@ -1066,59 +1170,7 @@ var init_scrim_controller = __esm({
1066
1170
  wrapper.className = "bs-scrim-overlay";
1067
1171
  const ws = wrapper.style;
1068
1172
  ws.position = "absolute";
1069
- switch (position) {
1070
- case "top-left":
1071
- ws.top = inset;
1072
- ws.left = inset;
1073
- break;
1074
- case "top-center":
1075
- ws.top = inset;
1076
- ws.left = "50%";
1077
- ws.transform = "translateX(-50%)";
1078
- break;
1079
- case "top-right":
1080
- ws.top = inset;
1081
- ws.right = inset;
1082
- break;
1083
- case "center-left":
1084
- ws.top = "50%";
1085
- ws.left = inset;
1086
- ws.transform = "translateY(-50%)";
1087
- break;
1088
- case "center":
1089
- ws.top = "50%";
1090
- ws.left = "50%";
1091
- ws.transform = "translate(-50%, -50%)";
1092
- break;
1093
- case "center-right":
1094
- ws.top = "50%";
1095
- ws.right = inset;
1096
- ws.transform = "translateY(-50%)";
1097
- break;
1098
- case "bottom-left":
1099
- ws.bottom = inset;
1100
- ws.left = inset;
1101
- break;
1102
- case "bottom-center":
1103
- ws.bottom = inset;
1104
- ws.left = "50%";
1105
- ws.transform = "translateX(-50%)";
1106
- break;
1107
- case "bottom-right":
1108
- ws.bottom = inset;
1109
- ws.right = inset;
1110
- break;
1111
- case "sheet-top-left":
1112
- case "sheet-top-center":
1113
- case "sheet-top-right":
1114
- applySheetAnchoredPosition(
1115
- ws,
1116
- this.mode,
1117
- position,
1118
- inset
1119
- );
1120
- break;
1121
- }
1173
+ applyOverlayPosition(ws, this.mode, position, inset);
1122
1174
  if (interactive) {
1123
1175
  ws.pointerEvents = "auto";
1124
1176
  }
@@ -1177,6 +1229,7 @@ var init_scrim_controller = __esm({
1177
1229
  }
1178
1230
  }
1179
1231
  if (opts.interactive !== void 0) {
1232
+ this.scrimInteractive = opts.interactive;
1180
1233
  s.pointerEvents = opts.interactive ? "auto" : "none";
1181
1234
  }
1182
1235
  }
@@ -1191,14 +1244,14 @@ var init_scrim_controller = __esm({
1191
1244
  const s = this.screenComponent.style;
1192
1245
  s.position = "fixed";
1193
1246
  s.inset = insetByMode[this.mode];
1194
- s.pointerEvents = "none";
1247
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1195
1248
  }
1196
1249
  };
1197
1250
  }
1198
1251
  });
1199
1252
 
1200
1253
  // src/core/animation/animation.ts
1201
- var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween;
1254
+ var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween, prefersReducedMotion;
1202
1255
  var init_animation = __esm({
1203
1256
  "src/core/animation/animation.ts"() {
1204
1257
  easeOutBack = (t) => {
@@ -1250,6 +1303,10 @@ var init_animation = __esm({
1250
1303
  promise
1251
1304
  };
1252
1305
  };
1306
+ prefersReducedMotion = () => {
1307
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1308
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1309
+ };
1253
1310
  }
1254
1311
  });
1255
1312
 
@@ -1285,14 +1342,20 @@ var init_spring = __esm({
1285
1342
  if (cancelled) return;
1286
1343
  const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
1287
1344
  lastTime = now;
1288
- const subDt = Math.min(dt, 1 / 240);
1289
- let stepsLeft = Math.max(1, Math.ceil(dt / subDt));
1345
+ const steps = Math.max(1, Math.ceil(dt * 240));
1346
+ const subDt = dt / steps;
1347
+ let stepsLeft = steps;
1290
1348
  while (stepsLeft-- > 0) {
1291
1349
  const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1292
1350
  const accel = force / cfg.mass;
1293
1351
  v += accel * subDt;
1294
1352
  x += v * subDt;
1295
1353
  }
1354
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1355
+ onUpdate(to, 0);
1356
+ resolve();
1357
+ return;
1358
+ }
1296
1359
  onUpdate(x, v);
1297
1360
  const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1298
1361
  if (isAtRest) {
@@ -1461,7 +1524,7 @@ var init_animation_runner = __esm({
1461
1524
  });
1462
1525
 
1463
1526
  // src/core/lifecycle/focusTrap.ts
1464
- var FOCUSABLE, isVisible, focusables, installFocusTrap;
1527
+ var FOCUSABLE, isVisible, focusables, trapStack, installFocusTrap;
1465
1528
  var init_focusTrap = __esm({
1466
1529
  "src/core/lifecycle/focusTrap.ts"() {
1467
1530
  FOCUSABLE = [
@@ -1486,9 +1549,13 @@ var init_focusTrap = __esm({
1486
1549
  focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
1487
1550
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1488
1551
  );
1552
+ trapStack = [];
1489
1553
  installFocusTrap = (container, options = {}) => {
1490
1554
  if (typeof document === "undefined") return () => {
1491
1555
  };
1556
+ const token = {};
1557
+ trapStack.push(token);
1558
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1492
1559
  const previouslyFocused = document.activeElement;
1493
1560
  const focusInitial = () => {
1494
1561
  const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
@@ -1496,6 +1563,7 @@ var init_focusTrap = __esm({
1496
1563
  };
1497
1564
  focusInitial();
1498
1565
  const handleKey = (e) => {
1566
+ if (!isActive()) return;
1499
1567
  if (e.key === "Escape" && options.onEscape) {
1500
1568
  options.onEscape();
1501
1569
  return;
@@ -1518,6 +1586,7 @@ var init_focusTrap = __esm({
1518
1586
  }
1519
1587
  };
1520
1588
  const handleFocusIn = (e) => {
1589
+ if (!isActive()) return;
1521
1590
  const target = e.target;
1522
1591
  if (target && !container.contains(target)) {
1523
1592
  const list = focusables(container);
@@ -1530,6 +1599,8 @@ var init_focusTrap = __esm({
1530
1599
  return () => {
1531
1600
  if (released) return;
1532
1601
  released = true;
1602
+ const idx = trapStack.indexOf(token);
1603
+ if (idx !== -1) trapStack.splice(idx, 1);
1533
1604
  document.removeEventListener("keydown", handleKey, true);
1534
1605
  document.removeEventListener("focusin", handleFocusIn, true);
1535
1606
  if (previouslyFocused && document.contains(previouslyFocused)) {
@@ -1592,11 +1663,17 @@ function createInertSiblings(rootProvider) {
1592
1663
  return {
1593
1664
  apply() {
1594
1665
  if (typeof document === "undefined") return;
1595
- const root = rootProvider();
1596
- const parent = root.parentElement ?? document.body;
1666
+ let top = rootProvider();
1667
+ while (top.parentElement && top.parentElement !== document.body) {
1668
+ top = top.parentElement;
1669
+ }
1670
+ const parent = top.parentElement ?? document.body;
1597
1671
  for (const child of Array.from(parent.children)) {
1598
- if (child === root) continue;
1672
+ if (child === top) continue;
1599
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
+ }
1600
1677
  child.setAttribute("inert", "");
1601
1678
  tracked.push(child);
1602
1679
  }
@@ -1622,7 +1699,6 @@ var init_lifecycle_controller = __esm({
1622
1699
  LifecycleController = class {
1623
1700
  constructor(deps, opts) {
1624
1701
  __publicField(this, "element");
1625
- __publicField(this, "closeFn");
1626
1702
  __publicField(this, "focusTrapEnabled");
1627
1703
  __publicField(this, "initialFocus");
1628
1704
  __publicField(this, "closeOnEscape");
@@ -1636,7 +1712,6 @@ var init_lifecycle_controller = __esm({
1636
1712
  __publicField(this, "installed", false);
1637
1713
  __publicField(this, "destroyed", false);
1638
1714
  this.element = deps.element;
1639
- this.closeFn = deps.close;
1640
1715
  this.focusTrapEnabled = opts.focusTrap ?? false;
1641
1716
  this.initialFocus = opts.initialFocus;
1642
1717
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1646,6 +1721,9 @@ var init_lifecycle_controller = __esm({
1646
1721
  this.returnFocus = opts.returnFocus;
1647
1722
  this.inertSiblings = createInertSiblings(() => this.element);
1648
1723
  }
1724
+ get isInstalled() {
1725
+ return this.installed;
1726
+ }
1649
1727
  install() {
1650
1728
  if (this.destroyed) return;
1651
1729
  this.installed = true;
@@ -1654,8 +1732,7 @@ var init_lifecycle_controller = __esm({
1654
1732
  }
1655
1733
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1656
1734
  this.releaseFocusTrap = installFocusTrap(this.element, {
1657
- initialFocus: this.initialFocus,
1658
- onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
1735
+ initialFocus: this.initialFocus
1659
1736
  });
1660
1737
  }
1661
1738
  if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
@@ -1818,12 +1895,12 @@ var init_gestures = __esm({
1818
1895
  // src/core/primitives/rubber-band.ts
1819
1896
  function rubberBand(overshoot, maxAxisSize) {
1820
1897
  const cap = Math.min(maxAxisSize * 0.15, 80);
1821
- return Math.sign(overshoot) * Math.pow(Math.abs(overshoot), RUBBER_BAND_FACTOR) * Math.min(6, cap / Math.max(Math.abs(overshoot), 1));
1898
+ if (cap <= 0) return 0;
1899
+ const abs = Math.abs(overshoot);
1900
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1822
1901
  }
1823
- var RUBBER_BAND_FACTOR;
1824
1902
  var init_rubber_band = __esm({
1825
1903
  "src/core/primitives/rubber-band.ts"() {
1826
- RUBBER_BAND_FACTOR = 0.55;
1827
1904
  }
1828
1905
  });
1829
1906
 
@@ -1919,6 +1996,10 @@ var init_gesture_controller = __esm({
1919
1996
  this.isDragging_ = false;
1920
1997
  this.keyboardDismissed = false;
1921
1998
  this.deps.getRoot()?.removeAttribute("data-dragging");
1999
+ this.deps.emit("dragend", {
2000
+ size: this.deps.getDragContext().size,
2001
+ velocity: 0
2002
+ });
1922
2003
  const restoreTo = this.dragStartSize;
1923
2004
  void this.deps.animateTo(restoreTo, 0);
1924
2005
  }
@@ -2003,6 +2084,306 @@ var init_scroll_cache = __esm({
2003
2084
  }
2004
2085
  });
2005
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
+
2006
2387
  // src/core/primitives/engine-options.ts
2007
2388
  function resolveEngineOptions(opts, extras) {
2008
2389
  const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
@@ -2083,6 +2464,8 @@ var init_BottomSheetEngine = __esm({
2083
2464
  init_aria_slider_writer();
2084
2465
  init_hot_path_thresholds();
2085
2466
  init_scroll_cache();
2467
+ init_sheet_anchors();
2468
+ init_scrim_stages();
2086
2469
  init_engine_options();
2087
2470
  init_opacity_dedup();
2088
2471
  HAPTIC_DURATION_MS = 8;
@@ -2151,6 +2534,12 @@ var init_BottomSheetEngine = __esm({
2151
2534
  __publicField(this, "linkedSheets", []);
2152
2535
  __publicField(this, "scrollCache");
2153
2536
  __publicField(this, "teardowns", new TeardownStack());
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);
2154
2543
  __publicField(this, "transformTemplate");
2155
2544
  const resolved = resolveEngineOptions(opts, {
2156
2545
  shouldApplyInertSiblings: () => {
@@ -2184,6 +2573,9 @@ var init_BottomSheetEngine = __esm({
2184
2573
  getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
2185
2574
  snapTo: (id) => {
2186
2575
  void this.snapTo(id);
2576
+ },
2577
+ close: () => {
2578
+ void this.close();
2187
2579
  }
2188
2580
  },
2189
2581
  resolved.scrim
@@ -2207,6 +2599,7 @@ var init_BottomSheetEngine = __esm({
2207
2599
  );
2208
2600
  this.closeOnBack = resolved.closeOnBack;
2209
2601
  this.routedTo = opts.routedTo;
2602
+ this.stackEffectEnabled = opts.stackEffect ?? false;
2210
2603
  this.snapPointsRaw = opts.snapPoints;
2211
2604
  this.persistKey = opts.persistKey;
2212
2605
  if (opts.linkedSheets) {
@@ -2219,7 +2612,7 @@ var init_BottomSheetEngine = __esm({
2219
2612
  this.snapPointsRaw,
2220
2613
  resolved.initialAllowed,
2221
2614
  this.mode,
2222
- () => this.handle.offsetHeight,
2615
+ () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2223
2616
  (maxAxisSize) => {
2224
2617
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2225
2618
  }
@@ -2429,11 +2822,6 @@ var init_BottomSheetEngine = __esm({
2429
2822
  }
2430
2823
  setAllowed(ids, snap) {
2431
2824
  if (this.destroyed) return;
2432
- const willSnap = snap !== void 0 && ids.includes(snap) || !ids.includes(this.activeId) && ids.length > 0;
2433
- if (!willSnap) {
2434
- this.animation.cancel();
2435
- this.newCycle();
2436
- }
2437
2825
  this.snaps.setAllowedIds(ids);
2438
2826
  this.updateAriaSlider();
2439
2827
  if (snap && ids.includes(snap)) {
@@ -2473,6 +2861,79 @@ var init_BottomSheetEngine = __esm({
2473
2861
  setScrimOverlay(opts) {
2474
2862
  return this.scrim.setScrimOverlay(opts);
2475
2863
  }
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
+ }
2476
2937
  getScrimState() {
2477
2938
  return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
2478
2939
  }
@@ -2559,24 +3020,45 @@ var init_BottomSheetEngine = __esm({
2559
3020
  getAllowedRange() {
2560
3021
  return this.snaps.getAllowedRange();
2561
3022
  }
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
+ }
2562
3028
  updateAriaSlider() {
2563
- this.aria.setValue(this.snaps.getAllowedIds(), this.activeId);
3029
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
2564
3030
  }
2565
3031
  registerInStack() {
2566
3032
  this.teardowns.add(sheetStack.push({
2567
3033
  id: this.id,
2568
3034
  setZIndex: (z) => {
3035
+ this.stackZ = z;
2569
3036
  this.element.style.zIndex = String(z);
2570
3037
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3038
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
2571
3039
  },
2572
3040
  setIsTop: (isTop) => {
2573
3041
  this.isTopSheet = isTop;
2574
3042
  if (this.backdrop) {
2575
3043
  this.backdrop.style.display = isTop ? "" : "none";
2576
3044
  }
2577
- }
3045
+ },
3046
+ isOpen: () => this.size > 0,
3047
+ setDepth: (depth) => this.applyStackDepth(depth)
2578
3048
  }));
2579
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
+ }
2580
3062
  attach() {
2581
3063
  this.gesture = new GestureController({
2582
3064
  handle: this.handle,
@@ -2636,7 +3118,7 @@ var init_BottomSheetEngine = __esm({
2636
3118
  container: this.scrollContainer,
2637
3119
  isDragging: () => this.gesture?.isDragging ?? false,
2638
3120
  isAnimating: () => this.animation.isAnimating,
2639
- getAllowedIds: () => this.snaps.getAllowedIds(),
3121
+ getAllowedIds: () => this.allowedIdsBySize(),
2640
3122
  getActiveId: () => this.activeId,
2641
3123
  snapTo: (id) => {
2642
3124
  void this.snapTo(id);
@@ -2665,11 +3147,13 @@ var init_BottomSheetEngine = __esm({
2665
3147
  },
2666
3148
  newCycle: () => {
2667
3149
  this.newCycle();
2668
- }
3150
+ },
3151
+ isAnimating: () => this.animation.isAnimating,
3152
+ resyncAfterCancel: () => this.resyncAfterResize()
2669
3153
  }));
2670
3154
  if (this.lifecycle.closeOnEscape) {
2671
3155
  const onKey = (e) => {
2672
- if (this.destroyed) return;
3156
+ if (this.destroyed || e.defaultPrevented) return;
2673
3157
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2674
3158
  void this.close();
2675
3159
  }
@@ -2701,7 +3185,7 @@ var init_BottomSheetEngine = __esm({
2701
3185
  handle: this.handle,
2702
3186
  mode: this.mode,
2703
3187
  isDestroyed: () => this.destroyed,
2704
- getAllowedIds: () => this.snaps.getAllowedIds(),
3188
+ getAllowedIds: () => this.allowedIdsBySize(),
2705
3189
  getActiveId: () => this.activeId,
2706
3190
  snapTo: (id) => {
2707
3191
  void this.snapTo(id);
@@ -2722,6 +3206,7 @@ var init_BottomSheetEngine = __esm({
2722
3206
  });
2723
3207
  if (!target) return;
2724
3208
  const previousId = this.activeId;
3209
+ if (target.id === previousId && target.size === this.size) return;
2725
3210
  if (this.emitBeforeSnap(target, previousId)) {
2726
3211
  const restore = this.snaps.findById(previousId);
2727
3212
  if (restore) {
@@ -2730,6 +3215,7 @@ var init_BottomSheetEngine = __esm({
2730
3215
  return;
2731
3216
  }
2732
3217
  const previousSize = this.size;
3218
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
2733
3219
  this.scrollCache.cache(previousId, previousSize, target.size);
2734
3220
  this.activeId = target.id;
2735
3221
  void this.animation.animateTo(target.size, velocity).then(() => {
@@ -2738,9 +3224,10 @@ var init_BottomSheetEngine = __esm({
2738
3224
  this.updateAriaSlider();
2739
3225
  this.emit("snap", { id: target.id, size: target.size });
2740
3226
  this.haptic();
2741
- if (previousId === "closed" && target.size > 0) {
3227
+ if (previousSnapSize === 0 && target.size > 0) {
2742
3228
  this.emit("open", { id: target.id });
2743
3229
  this.handleOpen();
3230
+ notifyLinkedSheets(this.linkedSheets, this);
2744
3231
  }
2745
3232
  if (target.size === 0) {
2746
3233
  this.emit("close", void 0);
@@ -2766,6 +3253,9 @@ var init_BottomSheetEngine = __esm({
2766
3253
  if (this.scrimParent) {
2767
3254
  this.scrimParent.style.setProperty("--bs-size", `${size}px`);
2768
3255
  }
3256
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3257
+ this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3258
+ }
2769
3259
  }
2770
3260
  const progress = this.computeProgress(size);
2771
3261
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -2774,6 +3264,9 @@ var init_BottomSheetEngine = __esm({
2774
3264
  );
2775
3265
  if (progressChanged) {
2776
3266
  style.setProperty("--bs-progress", String(progress));
3267
+ if (this.anchorHost) {
3268
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3269
+ }
2777
3270
  }
2778
3271
  this.scrim.applyOpacity(progress, progressChanged);
2779
3272
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -2788,6 +3281,7 @@ var init_BottomSheetEngine = __esm({
2788
3281
  return Math.min(Math.max((size - min) / (max - min), 0), 1);
2789
3282
  }
2790
3283
  handleOpen() {
3284
+ sheetStack.promote(this.id);
2791
3285
  try {
2792
3286
  this.lifecycle.install();
2793
3287
  } catch (err) {
@@ -2801,8 +3295,24 @@ var init_BottomSheetEngine = __esm({
2801
3295
  });
2802
3296
  }
2803
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
+ }
2804
3313
  handleClose() {
2805
3314
  this.lifecycle.release();
3315
+ sheetStack.update();
2806
3316
  }
2807
3317
  };
2808
3318
  }
@@ -2812,7 +3322,7 @@ var init_BottomSheetEngine = __esm({
2812
3322
  var baseStyles;
2813
3323
  var init_baseStyles = __esm({
2814
3324
  "src/web-component/baseStyles.ts"() {
2815
- baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--bs-shadow:0 -3px 12px rgba(0,0,0,0.12);--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);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);box-shadow: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;box-shadow: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);box-shadow: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);box-shadow:-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;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: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}}`;
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}}`;
2816
3326
  }
2817
3327
  });
2818
3328
 
@@ -2822,7 +3332,7 @@ __export(BottomSheetElement_exports, {
2822
3332
  BottomSheetElement: () => BottomSheetElement,
2823
3333
  defineBottomSheet: () => defineBottomSheet
2824
3334
  });
2825
- 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, BottomSheetElement, registered, defineBottomSheet;
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;
2826
3336
  var init_BottomSheetElement = __esm({
2827
3337
  "src/web-component/BottomSheetElement.ts"() {
2828
3338
  init_BottomSheetEngine();
@@ -2837,6 +3347,8 @@ var init_BottomSheetElement = __esm({
2837
3347
  ATTR_FOCUS_TRAP = "focus-trap";
2838
3348
  ATTR_CLOSE_ON_ESCAPE = "close-on-escape";
2839
3349
  ATTR_LOCK_BODY_SCROLL = "lock-body-scroll";
3350
+ ATTR_SHEET_LABEL = "sheet-label";
3351
+ ATTR_STACK_EFFECT = "stack-effect";
2840
3352
  isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
2841
3353
  DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
2842
3354
  parseSnapPoints = (raw) => {
@@ -2866,6 +3378,44 @@ var init_BottomSheetElement = __esm({
2866
3378
  parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
2867
3379
  parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
2868
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
+ };
2869
3419
  buildSlot = (name) => {
2870
3420
  const slot = document.createElement("slot");
2871
3421
  slot.name = name;
@@ -2935,7 +3485,9 @@ var init_BottomSheetElement = __esm({
2935
3485
  }
2936
3486
  };
2937
3487
  };
2938
- BottomSheetElement = class extends HTMLElement {
3488
+ BaseHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
3489
+ };
3490
+ BottomSheetElement = class extends BaseHTMLElement {
2939
3491
  constructor() {
2940
3492
  super();
2941
3493
  __publicField(this, "engine", null);
@@ -2943,17 +3495,12 @@ var init_BottomSheetElement = __esm({
2943
3495
  __publicField(this, "offHandlers", []);
2944
3496
  __publicField(this, "refs");
2945
3497
  __publicField(this, "backdropClickHandler", null);
3498
+ __publicField(this, "stylesheetLinked", false);
3499
+ __publicField(this, "declarativeAnchors", []);
2946
3500
  this.root = this.attachShadow({ mode: "open" });
2947
3501
  const baseStyle = document.createElement("style");
2948
3502
  baseStyle.textContent = baseStyles;
2949
3503
  this.root.appendChild(baseStyle);
2950
- const stylesheet = this.getAttribute(ATTR_STYLESHEET);
2951
- if (stylesheet) {
2952
- const link = document.createElement("link");
2953
- link.rel = "stylesheet";
2954
- link.href = stylesheet;
2955
- this.root.appendChild(link);
2956
- }
2957
3504
  const inlineStyle = document.createElement("style");
2958
3505
  inlineStyle.textContent = [
2959
3506
  ":host { display: contents; }",
@@ -2981,10 +3528,22 @@ var init_BottomSheetElement = __esm({
2981
3528
  ATTR_ANIMATION,
2982
3529
  ATTR_FOCUS_TRAP,
2983
3530
  ATTR_CLOSE_ON_ESCAPE,
2984
- ATTR_LOCK_BODY_SCROLL
3531
+ ATTR_LOCK_BODY_SCROLL,
3532
+ ATTR_SHEET_LABEL,
3533
+ ATTR_STACK_EFFECT
2985
3534
  ];
2986
3535
  }
2987
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
+ }
2988
3547
  this.initEngine();
2989
3548
  }
2990
3549
  disconnectedCallback() {
@@ -3007,6 +3566,10 @@ var init_BottomSheetElement = __esm({
3007
3566
  }
3008
3567
  this.engine?.destroy();
3009
3568
  this.engine = null;
3569
+ for (const el of this.declarativeAnchors) {
3570
+ this.appendChild(el);
3571
+ }
3572
+ this.declarativeAnchors = [];
3010
3573
  }
3011
3574
  initEngine() {
3012
3575
  const { sheet, handle, content, backdrop, announcer } = this.refs;
@@ -3018,6 +3581,10 @@ var init_BottomSheetElement = __esm({
3018
3581
  } else {
3019
3582
  sheet.removeAttribute("aria-modal");
3020
3583
  }
3584
+ sheet.setAttribute(
3585
+ "aria-label",
3586
+ this.getAttribute(ATTR_SHEET_LABEL) ?? "Bottom sheet"
3587
+ );
3021
3588
  const showBackdrop = this.getAttribute(ATTR_BACKDROP) !== "false";
3022
3589
  backdrop.style.display = showBackdrop ? "" : "none";
3023
3590
  if (showBackdrop) {
@@ -3037,11 +3604,20 @@ var init_BottomSheetElement = __esm({
3037
3604
  animation: parseAnimation(this.getAttribute(ATTR_ANIMATION)),
3038
3605
  focusTrap,
3039
3606
  closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
3040
- 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"
3041
3609
  };
3042
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
+ }
3043
3618
  this.offHandlers = [
3044
3619
  this.engine.on("snap", (payload) => {
3620
+ sheet.setAttribute("data-active", payload.id);
3045
3621
  announcer.textContent = payload.id;
3046
3622
  this.dispatchEvent(new CustomEvent("snap", { detail: payload }));
3047
3623
  }),
@@ -3071,15 +3647,22 @@ var init_BottomSheetElement = __esm({
3071
3647
  setAllowed(ids, snap) {
3072
3648
  this.engine?.setAllowed(ids, snap);
3073
3649
  }
3650
+ addAnchor(opts) {
3651
+ return this.engine?.addAnchor(opts) ?? (() => {
3652
+ });
3653
+ }
3654
+ setScrimStages(opts) {
3655
+ return this.engine?.setScrimStages(opts) ?? (() => {
3656
+ });
3657
+ }
3074
3658
  get sheetState() {
3075
3659
  return this.engine?.state ?? null;
3076
3660
  }
3077
3661
  };
3078
- registered = false;
3079
3662
  defineBottomSheet = (tag = "bottom-sheet") => {
3080
- if (registered || customElements.get(tag)) return;
3081
- customElements.define(tag, BottomSheetElement);
3082
- registered = true;
3663
+ if (typeof customElements === "undefined" || customElements.get(tag)) return;
3664
+ customElements.define(tag, class extends BottomSheetElement {
3665
+ });
3083
3666
  };
3084
3667
  }
3085
3668
  });