@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.cjs CHANGED
@@ -33,6 +33,18 @@ var init_sheetStack = __esm({
33
33
  this.entries.splice(idx, 1);
34
34
  this.recompute();
35
35
  }
36
+ promote(id) {
37
+ const idx = this.entries.findIndex((e) => e.id === id);
38
+ if (idx === -1) return;
39
+ if (idx !== this.entries.length - 1) {
40
+ const [entry] = this.entries.splice(idx, 1);
41
+ this.entries.push(entry);
42
+ }
43
+ this.recompute();
44
+ }
45
+ update() {
46
+ this.recompute();
47
+ }
36
48
  size() {
37
49
  return this.entries.length;
38
50
  }
@@ -40,9 +52,24 @@ var init_sheetStack = __esm({
40
52
  this.entries = [];
41
53
  }
42
54
  recompute() {
55
+ let top = null;
56
+ const open = [];
57
+ for (const entry of this.entries) {
58
+ if (!entry.isOpen || entry.isOpen()) {
59
+ top = entry;
60
+ open.push(entry);
61
+ }
62
+ }
63
+ if (!top && this.entries.length > 0) {
64
+ top = this.entries[this.entries.length - 1];
65
+ }
43
66
  this.entries.forEach((entry, i) => {
44
67
  entry.setZIndex(BASE_Z + i * STEP);
45
- entry.setIsTop(i === this.entries.length - 1);
68
+ entry.setIsTop(entry === top);
69
+ if (entry.setDepth) {
70
+ const openIdx = open.indexOf(entry);
71
+ entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
72
+ }
46
73
  });
47
74
  }
48
75
  };
@@ -364,14 +391,17 @@ function installAutoCollapse(deps) {
364
391
  const fire = () => {
365
392
  if (deps.isDestroyed()) return;
366
393
  if (deps.isDragging()) return;
367
- const allowed = deps.getAllowedIds();
368
- const target = allowed.find((id) => {
394
+ const active = deps.resolveSnap(deps.getActiveId());
395
+ if (!active || active.size === 0) return;
396
+ let target = null;
397
+ for (const id of deps.getAllowedIds()) {
369
398
  const snap = deps.resolveSnap(id);
370
- return snap !== null && snap.size > 0;
371
- });
399
+ if (!snap || snap.size <= 0) continue;
400
+ if (!target || snap.size < target.size) target = snap;
401
+ }
372
402
  if (!target) return;
373
- if (target === deps.getActiveId()) return;
374
- deps.snapTo(target);
403
+ if (target.id === active.id || target.size >= active.size) return;
404
+ deps.snapTo(target.id);
375
405
  };
376
406
  const reset = () => {
377
407
  clearTimer();
@@ -382,6 +412,7 @@ function installAutoCollapse(deps) {
382
412
  }, ms);
383
413
  };
384
414
  const unsubscribe = deps.on("snap", () => reset());
415
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
385
416
  return () => {
386
417
  clearTimer();
387
418
  unsubscribe();
@@ -398,6 +429,7 @@ function notifyLinkedSheets(sheets, self) {
398
429
  for (const linked of sheets) {
399
430
  if (linked === self) continue;
400
431
  try {
432
+ if (linked.state.size === 0) continue;
401
433
  const ids = linked.getAllowedIds();
402
434
  const target = ids.find((id) => id !== "closed");
403
435
  if (target && target !== linked.state.activeId) {
@@ -448,7 +480,7 @@ function installRoute(deps) {
448
480
  const onPop = (event) => {
449
481
  if (deps.isDestroyed()) return;
450
482
  const state = event.state;
451
- if (state && state.__bsRouted === target && deps.getSize() > 0) {
483
+ if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
452
484
  routePushed = false;
453
485
  void deps.close();
454
486
  }
@@ -616,13 +648,17 @@ function installResizeObserver(deps) {
616
648
  if (viewportSize2 > 0 && viewportSize2 < max) {
617
649
  deps.setMaxAxisSize(viewportSize2);
618
650
  }
651
+ const wasAnimating = deps.isAnimating?.() ?? false;
619
652
  deps.cancelInFlight();
620
653
  deps.newCycle();
621
654
  const current = deps.resolveActiveSnap();
622
655
  const newMax = deps.getMaxAxisSize();
623
656
  const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
624
657
  deps.setSize(targetSize);
625
- if (current && !deps.isDragging()) deps.applySize(targetSize);
658
+ if (current && !deps.isDragging()) {
659
+ deps.applySize(targetSize);
660
+ if (wasAnimating) deps.resyncAfterCancel?.();
661
+ }
626
662
  };
627
663
  let resizeObserver = null;
628
664
  if (typeof ResizeObserver !== "undefined") {
@@ -764,7 +800,7 @@ var init_devWarn = __esm({
764
800
  }
765
801
  });
766
802
 
767
- // src/core/controllers/scrim-controller.ts
803
+ // src/core/primitives/overlay-position.ts
768
804
  function resolveSheetAnchoredStyle(mode, position, inset) {
769
805
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
770
806
  switch (mode) {
@@ -786,14 +822,68 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
786
822
  return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
787
823
  }
788
824
  }
789
- function applySheetAnchoredPosition(ws, mode, position, inset) {
790
- const style = resolveSheetAnchoredStyle(mode, position, inset);
791
- if (style.top !== void 0) ws.top = style.top;
792
- if (style.bottom !== void 0) ws.bottom = style.bottom;
793
- if (style.left !== void 0) ws.left = style.left;
794
- if (style.right !== void 0) ws.right = style.right;
795
- if (style.transform !== void 0) ws.transform = style.transform;
825
+ function applyOverlayPosition(ws, mode, position, inset) {
826
+ switch (position) {
827
+ case "top-left":
828
+ ws.top = inset;
829
+ ws.left = inset;
830
+ break;
831
+ case "top-center":
832
+ ws.top = inset;
833
+ ws.left = "50%";
834
+ ws.transform = "translateX(-50%)";
835
+ break;
836
+ case "top-right":
837
+ ws.top = inset;
838
+ ws.right = inset;
839
+ break;
840
+ case "center-left":
841
+ ws.top = "50%";
842
+ ws.left = inset;
843
+ ws.transform = "translateY(-50%)";
844
+ break;
845
+ case "center":
846
+ ws.top = "50%";
847
+ ws.left = "50%";
848
+ ws.transform = "translate(-50%, -50%)";
849
+ break;
850
+ case "center-right":
851
+ ws.top = "50%";
852
+ ws.right = inset;
853
+ ws.transform = "translateY(-50%)";
854
+ break;
855
+ case "bottom-left":
856
+ ws.bottom = inset;
857
+ ws.left = inset;
858
+ break;
859
+ case "bottom-center":
860
+ ws.bottom = inset;
861
+ ws.left = "50%";
862
+ ws.transform = "translateX(-50%)";
863
+ break;
864
+ case "bottom-right":
865
+ ws.bottom = inset;
866
+ ws.right = inset;
867
+ break;
868
+ case "sheet-top-left":
869
+ case "sheet-top-center":
870
+ case "sheet-top-right": {
871
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
872
+ if (style.top !== void 0) ws.top = style.top;
873
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
874
+ if (style.left !== void 0) ws.left = style.left;
875
+ if (style.right !== void 0) ws.right = style.right;
876
+ if (style.transform !== void 0) ws.transform = style.transform;
877
+ break;
878
+ }
879
+ }
796
880
  }
881
+ var init_overlay_position = __esm({
882
+ "src/core/primitives/overlay-position.ts"() {
883
+ }
884
+ });
885
+
886
+ // src/core/controllers/scrim-controller.ts
797
887
  var VALID_CSS_LENGTH, ScrimController;
798
888
  var init_scrim_controller = __esm({
799
889
  "src/core/controllers/scrim-controller.ts"() {
@@ -802,6 +892,7 @@ var init_scrim_controller = __esm({
802
892
  init_hot_path_thresholds();
803
893
  init_opacity_dedup();
804
894
  init_devWarn();
895
+ init_overlay_position();
805
896
  VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
806
897
  ScrimController = class {
807
898
  constructor(deps, opts) {
@@ -813,6 +904,8 @@ var init_scrim_controller = __esm({
813
904
  __publicField(this, "getAllowedIds");
814
905
  __publicField(this, "getResolvedSnaps");
815
906
  __publicField(this, "snapToFn");
907
+ __publicField(this, "closeFn");
908
+ __publicField(this, "scrimInteractive", false);
816
909
  __publicField(this, "scrimMode");
817
910
  __publicField(this, "backdropRange");
818
911
  __publicField(this, "screenRange");
@@ -833,6 +926,7 @@ var init_scrim_controller = __esm({
833
926
  this.getAllowedIds = deps.getAllowedIds;
834
927
  this.getResolvedSnaps = deps.getResolvedSnaps;
835
928
  this.snapToFn = deps.snapTo;
929
+ this.closeFn = deps.close;
836
930
  this.scrimMode = opts.scrimMode ?? "full";
837
931
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
838
932
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
@@ -974,11 +1068,15 @@ var init_scrim_controller = __esm({
974
1068
  }
975
1069
  const onClick = () => {
976
1070
  if (this.isDestroyed()) return;
1071
+ if (this.closeFn) {
1072
+ this.closeFn();
1073
+ return;
1074
+ }
977
1075
  const allowed = this.getAllowedIds();
978
1076
  const snaps = this.getResolvedSnaps();
979
1077
  const fallback = allowed.find((id) => {
980
1078
  const snap = findById(id, snaps);
981
- return snap !== null && snap.size > 0;
1079
+ return snap !== null && snap.size === 0;
982
1080
  });
983
1081
  if (fallback) this.snapToFn(fallback);
984
1082
  };
@@ -995,6 +1093,7 @@ var init_scrim_controller = __esm({
995
1093
  if (this.isDestroyed()) return;
996
1094
  if (enabled === this.scrimEnabled) return;
997
1095
  this.scrimEnabled = enabled;
1096
+ this.invalidateOpacityCache();
998
1097
  if (!enabled) {
999
1098
  this.savedScrimRanges = {
1000
1099
  screen: this.screenRange,
@@ -1018,7 +1117,12 @@ var init_scrim_controller = __esm({
1018
1117
  blur: cfg.blur ?? null,
1019
1118
  interactive: cfg.interactive
1020
1119
  });
1021
- this.screenRange = cfg.range;
1120
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1121
+ this.savedScrimRanges = { screen: cfg.range, backdrop: cfg.range };
1122
+ } else {
1123
+ this.screenRange = cfg.range;
1124
+ this.backdropRange = cfg.range;
1125
+ }
1022
1126
  }
1023
1127
  const noop = () => {
1024
1128
  };
@@ -1068,59 +1172,7 @@ var init_scrim_controller = __esm({
1068
1172
  wrapper.className = "bs-scrim-overlay";
1069
1173
  const ws = wrapper.style;
1070
1174
  ws.position = "absolute";
1071
- switch (position) {
1072
- case "top-left":
1073
- ws.top = inset;
1074
- ws.left = inset;
1075
- break;
1076
- case "top-center":
1077
- ws.top = inset;
1078
- ws.left = "50%";
1079
- ws.transform = "translateX(-50%)";
1080
- break;
1081
- case "top-right":
1082
- ws.top = inset;
1083
- ws.right = inset;
1084
- break;
1085
- case "center-left":
1086
- ws.top = "50%";
1087
- ws.left = inset;
1088
- ws.transform = "translateY(-50%)";
1089
- break;
1090
- case "center":
1091
- ws.top = "50%";
1092
- ws.left = "50%";
1093
- ws.transform = "translate(-50%, -50%)";
1094
- break;
1095
- case "center-right":
1096
- ws.top = "50%";
1097
- ws.right = inset;
1098
- ws.transform = "translateY(-50%)";
1099
- break;
1100
- case "bottom-left":
1101
- ws.bottom = inset;
1102
- ws.left = inset;
1103
- break;
1104
- case "bottom-center":
1105
- ws.bottom = inset;
1106
- ws.left = "50%";
1107
- ws.transform = "translateX(-50%)";
1108
- break;
1109
- case "bottom-right":
1110
- ws.bottom = inset;
1111
- ws.right = inset;
1112
- break;
1113
- case "sheet-top-left":
1114
- case "sheet-top-center":
1115
- case "sheet-top-right":
1116
- applySheetAnchoredPosition(
1117
- ws,
1118
- this.mode,
1119
- position,
1120
- inset
1121
- );
1122
- break;
1123
- }
1175
+ applyOverlayPosition(ws, this.mode, position, inset);
1124
1176
  if (interactive) {
1125
1177
  ws.pointerEvents = "auto";
1126
1178
  }
@@ -1179,6 +1231,7 @@ var init_scrim_controller = __esm({
1179
1231
  }
1180
1232
  }
1181
1233
  if (opts.interactive !== void 0) {
1234
+ this.scrimInteractive = opts.interactive;
1182
1235
  s.pointerEvents = opts.interactive ? "auto" : "none";
1183
1236
  }
1184
1237
  }
@@ -1193,14 +1246,14 @@ var init_scrim_controller = __esm({
1193
1246
  const s = this.screenComponent.style;
1194
1247
  s.position = "fixed";
1195
1248
  s.inset = insetByMode[this.mode];
1196
- s.pointerEvents = "none";
1249
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1197
1250
  }
1198
1251
  };
1199
1252
  }
1200
1253
  });
1201
1254
 
1202
1255
  // src/core/animation/animation.ts
1203
- var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween;
1256
+ var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween, prefersReducedMotion;
1204
1257
  var init_animation = __esm({
1205
1258
  "src/core/animation/animation.ts"() {
1206
1259
  easeOutBack = (t) => {
@@ -1252,6 +1305,10 @@ var init_animation = __esm({
1252
1305
  promise
1253
1306
  };
1254
1307
  };
1308
+ prefersReducedMotion = () => {
1309
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1310
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1311
+ };
1255
1312
  }
1256
1313
  });
1257
1314
 
@@ -1287,14 +1344,20 @@ var init_spring = __esm({
1287
1344
  if (cancelled) return;
1288
1345
  const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
1289
1346
  lastTime = now;
1290
- const subDt = Math.min(dt, 1 / 240);
1291
- let stepsLeft = Math.max(1, Math.ceil(dt / subDt));
1347
+ const steps = Math.max(1, Math.ceil(dt * 240));
1348
+ const subDt = dt / steps;
1349
+ let stepsLeft = steps;
1292
1350
  while (stepsLeft-- > 0) {
1293
1351
  const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1294
1352
  const accel = force / cfg.mass;
1295
1353
  v += accel * subDt;
1296
1354
  x += v * subDt;
1297
1355
  }
1356
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1357
+ onUpdate(to, 0);
1358
+ resolve();
1359
+ return;
1360
+ }
1298
1361
  onUpdate(x, v);
1299
1362
  const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1300
1363
  if (isAtRest) {
@@ -1463,7 +1526,7 @@ var init_animation_runner = __esm({
1463
1526
  });
1464
1527
 
1465
1528
  // src/core/lifecycle/focusTrap.ts
1466
- var FOCUSABLE, isVisible, focusables, installFocusTrap;
1529
+ var FOCUSABLE, isVisible, focusables, trapStack, installFocusTrap;
1467
1530
  var init_focusTrap = __esm({
1468
1531
  "src/core/lifecycle/focusTrap.ts"() {
1469
1532
  FOCUSABLE = [
@@ -1488,9 +1551,13 @@ var init_focusTrap = __esm({
1488
1551
  focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
1489
1552
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1490
1553
  );
1554
+ trapStack = [];
1491
1555
  installFocusTrap = (container, options = {}) => {
1492
1556
  if (typeof document === "undefined") return () => {
1493
1557
  };
1558
+ const token = {};
1559
+ trapStack.push(token);
1560
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1494
1561
  const previouslyFocused = document.activeElement;
1495
1562
  const focusInitial = () => {
1496
1563
  const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
@@ -1498,6 +1565,7 @@ var init_focusTrap = __esm({
1498
1565
  };
1499
1566
  focusInitial();
1500
1567
  const handleKey = (e) => {
1568
+ if (!isActive()) return;
1501
1569
  if (e.key === "Escape" && options.onEscape) {
1502
1570
  options.onEscape();
1503
1571
  return;
@@ -1520,6 +1588,7 @@ var init_focusTrap = __esm({
1520
1588
  }
1521
1589
  };
1522
1590
  const handleFocusIn = (e) => {
1591
+ if (!isActive()) return;
1523
1592
  const target = e.target;
1524
1593
  if (target && !container.contains(target)) {
1525
1594
  const list = focusables(container);
@@ -1532,6 +1601,8 @@ var init_focusTrap = __esm({
1532
1601
  return () => {
1533
1602
  if (released) return;
1534
1603
  released = true;
1604
+ const idx = trapStack.indexOf(token);
1605
+ if (idx !== -1) trapStack.splice(idx, 1);
1535
1606
  document.removeEventListener("keydown", handleKey, true);
1536
1607
  document.removeEventListener("focusin", handleFocusIn, true);
1537
1608
  if (previouslyFocused && document.contains(previouslyFocused)) {
@@ -1594,11 +1665,17 @@ function createInertSiblings(rootProvider) {
1594
1665
  return {
1595
1666
  apply() {
1596
1667
  if (typeof document === "undefined") return;
1597
- const root = rootProvider();
1598
- const parent = root.parentElement ?? document.body;
1668
+ let top = rootProvider();
1669
+ while (top.parentElement && top.parentElement !== document.body) {
1670
+ top = top.parentElement;
1671
+ }
1672
+ const parent = top.parentElement ?? document.body;
1599
1673
  for (const child of Array.from(parent.children)) {
1600
- if (child === root) continue;
1674
+ if (child === top) continue;
1601
1675
  if (child.hasAttribute("inert")) continue;
1676
+ if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1677
+ continue;
1678
+ }
1602
1679
  child.setAttribute("inert", "");
1603
1680
  tracked.push(child);
1604
1681
  }
@@ -1624,7 +1701,6 @@ var init_lifecycle_controller = __esm({
1624
1701
  LifecycleController = class {
1625
1702
  constructor(deps, opts) {
1626
1703
  __publicField(this, "element");
1627
- __publicField(this, "closeFn");
1628
1704
  __publicField(this, "focusTrapEnabled");
1629
1705
  __publicField(this, "initialFocus");
1630
1706
  __publicField(this, "closeOnEscape");
@@ -1638,7 +1714,6 @@ var init_lifecycle_controller = __esm({
1638
1714
  __publicField(this, "installed", false);
1639
1715
  __publicField(this, "destroyed", false);
1640
1716
  this.element = deps.element;
1641
- this.closeFn = deps.close;
1642
1717
  this.focusTrapEnabled = opts.focusTrap ?? false;
1643
1718
  this.initialFocus = opts.initialFocus;
1644
1719
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1648,6 +1723,9 @@ var init_lifecycle_controller = __esm({
1648
1723
  this.returnFocus = opts.returnFocus;
1649
1724
  this.inertSiblings = createInertSiblings(() => this.element);
1650
1725
  }
1726
+ get isInstalled() {
1727
+ return this.installed;
1728
+ }
1651
1729
  install() {
1652
1730
  if (this.destroyed) return;
1653
1731
  this.installed = true;
@@ -1656,8 +1734,7 @@ var init_lifecycle_controller = __esm({
1656
1734
  }
1657
1735
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1658
1736
  this.releaseFocusTrap = installFocusTrap(this.element, {
1659
- initialFocus: this.initialFocus,
1660
- onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
1737
+ initialFocus: this.initialFocus
1661
1738
  });
1662
1739
  }
1663
1740
  if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
@@ -1820,12 +1897,12 @@ var init_gestures = __esm({
1820
1897
  // src/core/primitives/rubber-band.ts
1821
1898
  function rubberBand(overshoot, maxAxisSize) {
1822
1899
  const cap = Math.min(maxAxisSize * 0.15, 80);
1823
- return Math.sign(overshoot) * Math.pow(Math.abs(overshoot), RUBBER_BAND_FACTOR) * Math.min(6, cap / Math.max(Math.abs(overshoot), 1));
1900
+ if (cap <= 0) return 0;
1901
+ const abs = Math.abs(overshoot);
1902
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1824
1903
  }
1825
- var RUBBER_BAND_FACTOR;
1826
1904
  var init_rubber_band = __esm({
1827
1905
  "src/core/primitives/rubber-band.ts"() {
1828
- RUBBER_BAND_FACTOR = 0.55;
1829
1906
  }
1830
1907
  });
1831
1908
 
@@ -1921,6 +1998,10 @@ var init_gesture_controller = __esm({
1921
1998
  this.isDragging_ = false;
1922
1999
  this.keyboardDismissed = false;
1923
2000
  this.deps.getRoot()?.removeAttribute("data-dragging");
2001
+ this.deps.emit("dragend", {
2002
+ size: this.deps.getDragContext().size,
2003
+ velocity: 0
2004
+ });
1924
2005
  const restoreTo = this.dragStartSize;
1925
2006
  void this.deps.animateTo(restoreTo, 0);
1926
2007
  }
@@ -2005,6 +2086,306 @@ var init_scroll_cache = __esm({
2005
2086
  }
2006
2087
  });
2007
2088
 
2089
+ // src/core/primitives/anchor-animations.ts
2090
+ function resolveTransitionFrames(spec, phase) {
2091
+ const fallback = { frames: null, duration: 0, easing: DEFAULT_EASING };
2092
+ if (spec === "none") return fallback;
2093
+ if (spec === void 0 || typeof spec === "string") {
2094
+ const preset2 = PRESET_FRAMES[spec ?? "fade"];
2095
+ return {
2096
+ frames: preset2[phase],
2097
+ duration: DEFAULT_DURATION3,
2098
+ easing: DEFAULT_EASING
2099
+ };
2100
+ }
2101
+ const custom = phase === "enter" ? spec.enter : spec.exit;
2102
+ if (custom) {
2103
+ return {
2104
+ frames: custom,
2105
+ duration: spec.duration ?? DEFAULT_DURATION3,
2106
+ easing: spec.easing ?? DEFAULT_EASING
2107
+ };
2108
+ }
2109
+ if (spec.preset === "none") return fallback;
2110
+ const preset = PRESET_FRAMES[spec.preset ?? "fade"];
2111
+ return {
2112
+ frames: preset[phase],
2113
+ duration: spec.duration ?? DEFAULT_DURATION3,
2114
+ easing: spec.easing ?? DEFAULT_EASING
2115
+ };
2116
+ }
2117
+ function runAnchorTransition(el, spec, phase) {
2118
+ const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
2119
+ if (respectReduced && prefersReducedMotion()) return INSTANT;
2120
+ const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
2121
+ if (!frames || duration <= 0) return INSTANT;
2122
+ if (typeof el.animate !== "function") return INSTANT;
2123
+ const animation = el.animate(frames, {
2124
+ duration,
2125
+ easing,
2126
+ fill: "both"
2127
+ });
2128
+ const finished = animation.finished.then(() => void 0).catch(() => void 0);
2129
+ return {
2130
+ finished,
2131
+ cancel: () => {
2132
+ try {
2133
+ animation.cancel();
2134
+ } catch {
2135
+ return;
2136
+ }
2137
+ }
2138
+ };
2139
+ }
2140
+ var DEFAULT_DURATION3, DEFAULT_EASING, PRESET_FRAMES, INSTANT;
2141
+ var init_anchor_animations = __esm({
2142
+ "src/core/primitives/anchor-animations.ts"() {
2143
+ init_animation();
2144
+ DEFAULT_DURATION3 = 200;
2145
+ DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
2146
+ PRESET_FRAMES = {
2147
+ fade: {
2148
+ enter: [{ opacity: 0 }, { opacity: 1 }],
2149
+ exit: [{ opacity: 1 }, { opacity: 0 }]
2150
+ },
2151
+ scale: {
2152
+ enter: [
2153
+ { opacity: 0, transform: "scale(0.85)" },
2154
+ { opacity: 1, transform: "scale(1)" }
2155
+ ],
2156
+ exit: [
2157
+ { opacity: 1, transform: "scale(1)" },
2158
+ { opacity: 0, transform: "scale(0.85)" }
2159
+ ]
2160
+ },
2161
+ slide: {
2162
+ enter: [
2163
+ { opacity: 0, transform: "translateY(10px)" },
2164
+ { opacity: 1, transform: "translateY(0)" }
2165
+ ],
2166
+ exit: [
2167
+ { opacity: 1, transform: "translateY(0)" },
2168
+ { opacity: 0, transform: "translateY(10px)" }
2169
+ ]
2170
+ },
2171
+ pop: {
2172
+ enter: [
2173
+ { opacity: 0, transform: "scale(0.6)", offset: 0 },
2174
+ { opacity: 1, transform: "scale(1.06)", offset: 0.7 },
2175
+ { opacity: 1, transform: "scale(1)", offset: 1 }
2176
+ ],
2177
+ exit: [
2178
+ { opacity: 1, transform: "scale(1)" },
2179
+ { opacity: 0, transform: "scale(0.6)" }
2180
+ ]
2181
+ }
2182
+ };
2183
+ INSTANT = {
2184
+ finished: Promise.resolve(),
2185
+ cancel: () => {
2186
+ }
2187
+ };
2188
+ }
2189
+ });
2190
+
2191
+ // src/core/features/sheet-anchors.ts
2192
+ function attachAnchor(deps, opts) {
2193
+ const doc = deps.host.ownerDocument;
2194
+ const wrapper = doc.createElement("div");
2195
+ wrapper.className = "bs-anchor";
2196
+ const interactive = opts.interactive ?? true;
2197
+ const position = opts.position ?? "sheet-top-right";
2198
+ const isDock = position === "dock-bottom" || position === "dock-top";
2199
+ const ws = wrapper.style;
2200
+ ws.position = "fixed";
2201
+ if (isDock) {
2202
+ ws.left = "0";
2203
+ ws.right = "0";
2204
+ if (position === "dock-bottom") {
2205
+ ws.bottom = "0";
2206
+ } else {
2207
+ ws.top = "0";
2208
+ }
2209
+ } else {
2210
+ applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2211
+ }
2212
+ if (opts.fadeRange) {
2213
+ const [r0, r1] = opts.fadeRange;
2214
+ const span = Math.max(r1 - r0, 1e-4);
2215
+ ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
2216
+ }
2217
+ wrapper.appendChild(opts.element);
2218
+ deps.host.appendChild(wrapper);
2219
+ let visible = null;
2220
+ let inFlight = null;
2221
+ const shouldShow = (state) => {
2222
+ if (typeof opts.showOn === "function") return opts.showOn(state);
2223
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2224
+ return isDock || state.size > 0;
2225
+ };
2226
+ const applyVisibility = (next, animate) => {
2227
+ if (next === visible) return;
2228
+ visible = next;
2229
+ inFlight?.cancel();
2230
+ inFlight = null;
2231
+ wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
2232
+ if (next) {
2233
+ wrapper.style.visibility = "";
2234
+ if (animate) {
2235
+ inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
2236
+ }
2237
+ return;
2238
+ }
2239
+ if (!animate) {
2240
+ wrapper.style.visibility = "hidden";
2241
+ return;
2242
+ }
2243
+ const handle = runAnchorTransition(opts.element, opts.animation, "exit");
2244
+ inFlight = handle;
2245
+ void handle.finished.then(() => {
2246
+ if (inFlight === handle && visible === false) {
2247
+ wrapper.style.visibility = "hidden";
2248
+ }
2249
+ });
2250
+ };
2251
+ const evaluate = (animate) => {
2252
+ if (deps.isDestroyed()) return;
2253
+ applyVisibility(shouldShow(deps.getState()), animate);
2254
+ };
2255
+ const offs = [
2256
+ deps.on("snap", () => evaluate(true)),
2257
+ deps.on("open", () => evaluate(true)),
2258
+ deps.on("close", () => evaluate(true))
2259
+ ];
2260
+ evaluate(false);
2261
+ return {
2262
+ wrapper,
2263
+ detach: () => {
2264
+ offs.forEach((off) => off());
2265
+ inFlight?.cancel();
2266
+ inFlight = null;
2267
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
2268
+ },
2269
+ syncZ: (z) => {
2270
+ wrapper.style.zIndex = String(z);
2271
+ }
2272
+ };
2273
+ }
2274
+ var init_sheet_anchors = __esm({
2275
+ "src/core/features/sheet-anchors.ts"() {
2276
+ init_overlay_position();
2277
+ init_anchor_animations();
2278
+ }
2279
+ });
2280
+
2281
+ // src/core/features/scrim-stages.ts
2282
+ function installScrimStages(deps, opts) {
2283
+ const doc = deps.host.ownerDocument;
2284
+ const entries = opts.stages.map((def) => {
2285
+ const wrapper = doc.createElement("div");
2286
+ wrapper.className = "bs-scrim-stage";
2287
+ const ws = wrapper.style;
2288
+ ws.position = "absolute";
2289
+ applyOverlayPosition(
2290
+ ws,
2291
+ deps.mode,
2292
+ def.position ?? opts.position ?? "center",
2293
+ def.inset ?? opts.inset ?? "16px"
2294
+ );
2295
+ ws.visibility = "hidden";
2296
+ ws.pointerEvents = "none";
2297
+ wrapper.appendChild(def.element);
2298
+ deps.host.appendChild(wrapper);
2299
+ return { def, wrapper, inFlight: null };
2300
+ });
2301
+ let active = null;
2302
+ const resolveActive = () => {
2303
+ const state = deps.getState();
2304
+ if (state.size === 0) {
2305
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2306
+ }
2307
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2308
+ };
2309
+ const hide = (entry, animate) => {
2310
+ entry.inFlight?.cancel();
2311
+ entry.wrapper.style.pointerEvents = "none";
2312
+ if (!animate) {
2313
+ entry.inFlight = null;
2314
+ entry.wrapper.style.visibility = "hidden";
2315
+ return;
2316
+ }
2317
+ const handle = runAnchorTransition(
2318
+ entry.def.element,
2319
+ entry.def.animation ?? opts.animation,
2320
+ "exit"
2321
+ );
2322
+ entry.inFlight = handle;
2323
+ void handle.finished.then(() => {
2324
+ if (entry.inFlight === handle && active !== entry) {
2325
+ entry.wrapper.style.visibility = "hidden";
2326
+ }
2327
+ });
2328
+ };
2329
+ const show = (entry, animate) => {
2330
+ entry.inFlight?.cancel();
2331
+ entry.inFlight = null;
2332
+ entry.wrapper.style.visibility = "";
2333
+ entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
2334
+ if (animate) {
2335
+ entry.inFlight = runAnchorTransition(
2336
+ entry.def.element,
2337
+ entry.def.animation ?? opts.animation,
2338
+ "enter"
2339
+ );
2340
+ }
2341
+ };
2342
+ const update = (animate) => {
2343
+ if (deps.isDestroyed()) return;
2344
+ const next = resolveActive();
2345
+ if (next === active) return;
2346
+ const prev = active;
2347
+ active = next;
2348
+ if (prev) hide(prev, animate);
2349
+ if (next) show(next, animate);
2350
+ };
2351
+ const offs = [
2352
+ deps.on("snap", () => update(true)),
2353
+ deps.on("open", () => update(true)),
2354
+ deps.on("close", () => update(true))
2355
+ ];
2356
+ if (entries.some((e) => e.def.forRange)) {
2357
+ offs.push(deps.on("progress", () => update(true)));
2358
+ }
2359
+ update(false);
2360
+ return () => {
2361
+ offs.forEach((off) => off());
2362
+ for (const entry of entries) {
2363
+ entry.inFlight?.cancel();
2364
+ entry.inFlight = null;
2365
+ if (entry.wrapper.parentNode) {
2366
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2367
+ }
2368
+ }
2369
+ active = null;
2370
+ };
2371
+ }
2372
+ var matchesId, matchesRange;
2373
+ var init_scrim_stages = __esm({
2374
+ "src/core/features/scrim-stages.ts"() {
2375
+ init_overlay_position();
2376
+ init_anchor_animations();
2377
+ matchesId = (def, activeId) => {
2378
+ if (def.for === void 0) return false;
2379
+ return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
2380
+ };
2381
+ matchesRange = (def, progress) => {
2382
+ if (!def.forRange) return false;
2383
+ const [from, to] = def.forRange;
2384
+ return progress >= from && progress <= to;
2385
+ };
2386
+ }
2387
+ });
2388
+
2008
2389
  // src/core/primitives/engine-options.ts
2009
2390
  function resolveEngineOptions(opts, extras) {
2010
2391
  const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
@@ -2085,6 +2466,8 @@ var init_BottomSheetEngine = __esm({
2085
2466
  init_aria_slider_writer();
2086
2467
  init_hot_path_thresholds();
2087
2468
  init_scroll_cache();
2469
+ init_sheet_anchors();
2470
+ init_scrim_stages();
2088
2471
  init_engine_options();
2089
2472
  init_opacity_dedup();
2090
2473
  HAPTIC_DURATION_MS = 8;
@@ -2153,6 +2536,12 @@ var init_BottomSheetEngine = __esm({
2153
2536
  __publicField(this, "linkedSheets", []);
2154
2537
  __publicField(this, "scrollCache");
2155
2538
  __publicField(this, "teardowns", new TeardownStack());
2539
+ __publicField(this, "anchors", []);
2540
+ __publicField(this, "anchorHost", null);
2541
+ __publicField(this, "stackZ", 100);
2542
+ __publicField(this, "detachScrimStages", null);
2543
+ __publicField(this, "stackEffectEnabled", false);
2544
+ __publicField(this, "stackEffectPrimed", false);
2156
2545
  __publicField(this, "transformTemplate");
2157
2546
  const resolved = resolveEngineOptions(opts, {
2158
2547
  shouldApplyInertSiblings: () => {
@@ -2186,6 +2575,9 @@ var init_BottomSheetEngine = __esm({
2186
2575
  getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
2187
2576
  snapTo: (id) => {
2188
2577
  void this.snapTo(id);
2578
+ },
2579
+ close: () => {
2580
+ void this.close();
2189
2581
  }
2190
2582
  },
2191
2583
  resolved.scrim
@@ -2209,6 +2601,7 @@ var init_BottomSheetEngine = __esm({
2209
2601
  );
2210
2602
  this.closeOnBack = resolved.closeOnBack;
2211
2603
  this.routedTo = opts.routedTo;
2604
+ this.stackEffectEnabled = opts.stackEffect ?? false;
2212
2605
  this.snapPointsRaw = opts.snapPoints;
2213
2606
  this.persistKey = opts.persistKey;
2214
2607
  if (opts.linkedSheets) {
@@ -2221,7 +2614,7 @@ var init_BottomSheetEngine = __esm({
2221
2614
  this.snapPointsRaw,
2222
2615
  resolved.initialAllowed,
2223
2616
  this.mode,
2224
- () => this.handle.offsetHeight,
2617
+ () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2225
2618
  (maxAxisSize) => {
2226
2619
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2227
2620
  }
@@ -2431,11 +2824,6 @@ var init_BottomSheetEngine = __esm({
2431
2824
  }
2432
2825
  setAllowed(ids, snap) {
2433
2826
  if (this.destroyed) return;
2434
- const willSnap = snap !== void 0 && ids.includes(snap) || !ids.includes(this.activeId) && ids.length > 0;
2435
- if (!willSnap) {
2436
- this.animation.cancel();
2437
- this.newCycle();
2438
- }
2439
2827
  this.snaps.setAllowedIds(ids);
2440
2828
  this.updateAriaSlider();
2441
2829
  if (snap && ids.includes(snap)) {
@@ -2475,6 +2863,79 @@ var init_BottomSheetEngine = __esm({
2475
2863
  setScrimOverlay(opts) {
2476
2864
  return this.scrim.setScrimOverlay(opts);
2477
2865
  }
2866
+ addAnchor(opts) {
2867
+ if (this.destroyed || typeof document === "undefined") return () => {
2868
+ };
2869
+ if (!this.anchorHost) {
2870
+ this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
2871
+ this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
2872
+ this.anchorHost.style.setProperty(
2873
+ "--bs-progress",
2874
+ String(this.computeProgress(this.size))
2875
+ );
2876
+ }
2877
+ const handle = attachAnchor(
2878
+ {
2879
+ mode: this.mode,
2880
+ host: this.anchorHost,
2881
+ getState: () => ({
2882
+ activeId: this.activeId,
2883
+ size: this.size,
2884
+ progress: this.computeProgress(this.size)
2885
+ }),
2886
+ on: (event, fn) => this.on(event, fn),
2887
+ isDestroyed: () => this.destroyed
2888
+ },
2889
+ opts
2890
+ );
2891
+ handle.syncZ(this.stackZ + 1);
2892
+ this.anchors.push(handle);
2893
+ const detach = () => {
2894
+ const idx = this.anchors.indexOf(handle);
2895
+ if (idx === -1) return;
2896
+ this.anchors.splice(idx, 1);
2897
+ handle.detach();
2898
+ };
2899
+ this.teardowns.add(detach);
2900
+ return detach;
2901
+ }
2902
+ setScrimStages(opts) {
2903
+ if (this.destroyed || typeof document === "undefined") return () => {
2904
+ };
2905
+ this.detachScrimStages?.();
2906
+ this.detachScrimStages = null;
2907
+ if (!opts) return () => {
2908
+ };
2909
+ const host = this.screenComponent?.parentElement;
2910
+ if (!host) {
2911
+ console.warn(
2912
+ "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
2913
+ );
2914
+ return () => {
2915
+ };
2916
+ }
2917
+ const detachInstalled = installScrimStages(
2918
+ {
2919
+ mode: this.mode,
2920
+ host,
2921
+ getState: () => ({
2922
+ activeId: this.activeId,
2923
+ size: this.size,
2924
+ progress: this.computeProgress(this.size)
2925
+ }),
2926
+ on: (event, fn) => this.on(event, fn),
2927
+ isDestroyed: () => this.destroyed
2928
+ },
2929
+ opts
2930
+ );
2931
+ const detach = () => {
2932
+ if (this.detachScrimStages === detach) this.detachScrimStages = null;
2933
+ detachInstalled();
2934
+ };
2935
+ this.detachScrimStages = detach;
2936
+ this.teardowns.add(() => this.detachScrimStages?.());
2937
+ return detach;
2938
+ }
2478
2939
  getScrimState() {
2479
2940
  return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
2480
2941
  }
@@ -2561,24 +3022,45 @@ var init_BottomSheetEngine = __esm({
2561
3022
  getAllowedRange() {
2562
3023
  return this.snaps.getAllowedRange();
2563
3024
  }
3025
+ allowedIdsBySize() {
3026
+ return this.snaps.getAllowedIds().slice().sort(
3027
+ (a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0)
3028
+ );
3029
+ }
2564
3030
  updateAriaSlider() {
2565
- this.aria.setValue(this.snaps.getAllowedIds(), this.activeId);
3031
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
2566
3032
  }
2567
3033
  registerInStack() {
2568
3034
  this.teardowns.add(sheetStack.push({
2569
3035
  id: this.id,
2570
3036
  setZIndex: (z) => {
3037
+ this.stackZ = z;
2571
3038
  this.element.style.zIndex = String(z);
2572
3039
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3040
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
2573
3041
  },
2574
3042
  setIsTop: (isTop) => {
2575
3043
  this.isTopSheet = isTop;
2576
3044
  if (this.backdrop) {
2577
3045
  this.backdrop.style.display = isTop ? "" : "none";
2578
3046
  }
2579
- }
3047
+ },
3048
+ isOpen: () => this.size > 0,
3049
+ setDepth: (depth) => this.applyStackDepth(depth)
2580
3050
  }));
2581
3051
  }
3052
+ applyStackDepth(depth) {
3053
+ if (!this.stackEffectEnabled) return;
3054
+ this.element.setAttribute("data-stack-depth", String(depth));
3055
+ if (!this.stackEffectPrimed) {
3056
+ this.stackEffectPrimed = true;
3057
+ const isVertical = this.mode === "bottom" || this.mode === "top";
3058
+ this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
3059
+ this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
3060
+ }
3061
+ const scale = Math.max(1 - depth * 0.04, 0.86);
3062
+ this.element.style.scale = scale === 1 ? "" : String(scale);
3063
+ }
2582
3064
  attach() {
2583
3065
  this.gesture = new GestureController({
2584
3066
  handle: this.handle,
@@ -2638,7 +3120,7 @@ var init_BottomSheetEngine = __esm({
2638
3120
  container: this.scrollContainer,
2639
3121
  isDragging: () => this.gesture?.isDragging ?? false,
2640
3122
  isAnimating: () => this.animation.isAnimating,
2641
- getAllowedIds: () => this.snaps.getAllowedIds(),
3123
+ getAllowedIds: () => this.allowedIdsBySize(),
2642
3124
  getActiveId: () => this.activeId,
2643
3125
  snapTo: (id) => {
2644
3126
  void this.snapTo(id);
@@ -2667,11 +3149,13 @@ var init_BottomSheetEngine = __esm({
2667
3149
  },
2668
3150
  newCycle: () => {
2669
3151
  this.newCycle();
2670
- }
3152
+ },
3153
+ isAnimating: () => this.animation.isAnimating,
3154
+ resyncAfterCancel: () => this.resyncAfterResize()
2671
3155
  }));
2672
3156
  if (this.lifecycle.closeOnEscape) {
2673
3157
  const onKey = (e) => {
2674
- if (this.destroyed) return;
3158
+ if (this.destroyed || e.defaultPrevented) return;
2675
3159
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2676
3160
  void this.close();
2677
3161
  }
@@ -2703,7 +3187,7 @@ var init_BottomSheetEngine = __esm({
2703
3187
  handle: this.handle,
2704
3188
  mode: this.mode,
2705
3189
  isDestroyed: () => this.destroyed,
2706
- getAllowedIds: () => this.snaps.getAllowedIds(),
3190
+ getAllowedIds: () => this.allowedIdsBySize(),
2707
3191
  getActiveId: () => this.activeId,
2708
3192
  snapTo: (id) => {
2709
3193
  void this.snapTo(id);
@@ -2724,6 +3208,7 @@ var init_BottomSheetEngine = __esm({
2724
3208
  });
2725
3209
  if (!target) return;
2726
3210
  const previousId = this.activeId;
3211
+ if (target.id === previousId && target.size === this.size) return;
2727
3212
  if (this.emitBeforeSnap(target, previousId)) {
2728
3213
  const restore = this.snaps.findById(previousId);
2729
3214
  if (restore) {
@@ -2732,6 +3217,7 @@ var init_BottomSheetEngine = __esm({
2732
3217
  return;
2733
3218
  }
2734
3219
  const previousSize = this.size;
3220
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
2735
3221
  this.scrollCache.cache(previousId, previousSize, target.size);
2736
3222
  this.activeId = target.id;
2737
3223
  void this.animation.animateTo(target.size, velocity).then(() => {
@@ -2740,9 +3226,10 @@ var init_BottomSheetEngine = __esm({
2740
3226
  this.updateAriaSlider();
2741
3227
  this.emit("snap", { id: target.id, size: target.size });
2742
3228
  this.haptic();
2743
- if (previousId === "closed" && target.size > 0) {
3229
+ if (previousSnapSize === 0 && target.size > 0) {
2744
3230
  this.emit("open", { id: target.id });
2745
3231
  this.handleOpen();
3232
+ notifyLinkedSheets(this.linkedSheets, this);
2746
3233
  }
2747
3234
  if (target.size === 0) {
2748
3235
  this.emit("close", void 0);
@@ -2768,6 +3255,9 @@ var init_BottomSheetEngine = __esm({
2768
3255
  if (this.scrimParent) {
2769
3256
  this.scrimParent.style.setProperty("--bs-size", `${size}px`);
2770
3257
  }
3258
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3259
+ this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3260
+ }
2771
3261
  }
2772
3262
  const progress = this.computeProgress(size);
2773
3263
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -2776,6 +3266,9 @@ var init_BottomSheetEngine = __esm({
2776
3266
  );
2777
3267
  if (progressChanged) {
2778
3268
  style.setProperty("--bs-progress", String(progress));
3269
+ if (this.anchorHost) {
3270
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3271
+ }
2779
3272
  }
2780
3273
  this.scrim.applyOpacity(progress, progressChanged);
2781
3274
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -2790,6 +3283,7 @@ var init_BottomSheetEngine = __esm({
2790
3283
  return Math.min(Math.max((size - min) / (max - min), 0), 1);
2791
3284
  }
2792
3285
  handleOpen() {
3286
+ sheetStack.promote(this.id);
2793
3287
  try {
2794
3288
  this.lifecycle.install();
2795
3289
  } catch (err) {
@@ -2803,8 +3297,24 @@ var init_BottomSheetEngine = __esm({
2803
3297
  });
2804
3298
  }
2805
3299
  }
3300
+ resyncAfterResize() {
3301
+ if (this.destroyed) return;
3302
+ const target = this.snaps.findById(this.activeId);
3303
+ if (!target) return;
3304
+ this.updateAriaSlider();
3305
+ this.emit("snap", { id: target.id, size: target.size });
3306
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3307
+ this.emit("open", { id: target.id });
3308
+ this.handleOpen();
3309
+ notifyLinkedSheets(this.linkedSheets, this);
3310
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3311
+ this.emit("close", void 0);
3312
+ this.handleClose();
3313
+ }
3314
+ }
2806
3315
  handleClose() {
2807
3316
  this.lifecycle.release();
3317
+ sheetStack.update();
2808
3318
  }
2809
3319
  };
2810
3320
  }
@@ -2814,7 +3324,7 @@ var init_BottomSheetEngine = __esm({
2814
3324
  var baseStyles;
2815
3325
  var init_baseStyles = __esm({
2816
3326
  "src/web-component/baseStyles.ts"() {
2817
- 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}}`;
3327
+ baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--bs-radius:20px;--bs-handle-color:#b5b3a8;--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-header-min-height:80px;--bs-backdrop-color:rgba(0,0,0,0.5);--bs-scrim-color:rgba(0,0,0,0.4);--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color);opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface);box-shadow:var(--bs-shadow,var(--bs-shadow-auto,none));z-index:var(--bs-z-sheet);overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius);border-top-right-radius:var(--bs-radius);border-top:1px solid var(--bs-surface-border);padding-bottom:env(safe-area-inset-bottom);--bs-shadow-auto:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba(0,0,0,calc(var(--bs-progress,0) * 0.15 + 0.12))}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-bottom:1px solid var(--bs-surface-border);flex-direction:column-reverse;--bs-shadow-auto:0 3px 12px rgba(0,0,0,0.12);padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-right:1px solid var(--bs-surface-border);--bs-shadow-auto:3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius);border-bottom-left-radius:var(--bs-radius);border-left:1px solid var(--bs-surface-border);--bs-shadow-auto:-3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:8px 16px 4px;cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface);-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius)}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color);border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface);z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;user-select:text;padding:0 16px 16px}.bs-screen{position:absolute;inset:0;pointer-events:none;z-index:var(--bs-z-scrim)}.bs-scrim-overlay{z-index:calc(var(--bs-z-sheet) + 1);pointer-events:none}.bs-scrim{background:var(--bs-scrim-color);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-scrim,.bs-root[data-animating="true"] .bs-scrim{transition:none}.bs-scrim{backdrop-filter:blur(var(--bs-scrim-blur));-webkit-backdrop-filter:blur(var(--bs-scrim-blur))}.bs-button-slot{position:absolute;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface:#1a1a1d;--bs-surface-border:rgba(255,255,255,0.08);--bs-handle-color:rgba(255,255,255,0.45);--bs-shadow-auto:0 -3px 16px rgba(0,0,0,0.5)}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,#ffffff);--ovl-surface-border:var(--bs-surface-border,rgba(0,0,0,0.08));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,20px);--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
2818
3328
  }
2819
3329
  });
2820
3330
 
@@ -2824,7 +3334,7 @@ __export(BottomSheetElement_exports, {
2824
3334
  BottomSheetElement: () => exports.BottomSheetElement,
2825
3335
  defineBottomSheet: () => exports.defineBottomSheet
2826
3336
  });
2827
- var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, buildSlot, buildShadowTree; exports.BottomSheetElement = void 0; var registered; exports.defineBottomSheet = void 0;
3337
+ var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions, buildSlot, buildShadowTree, BaseHTMLElement; exports.BottomSheetElement = void 0; exports.defineBottomSheet = void 0;
2828
3338
  var init_BottomSheetElement = __esm({
2829
3339
  "src/web-component/BottomSheetElement.ts"() {
2830
3340
  init_BottomSheetEngine();
@@ -2839,6 +3349,8 @@ var init_BottomSheetElement = __esm({
2839
3349
  ATTR_FOCUS_TRAP = "focus-trap";
2840
3350
  ATTR_CLOSE_ON_ESCAPE = "close-on-escape";
2841
3351
  ATTR_LOCK_BODY_SCROLL = "lock-body-scroll";
3352
+ ATTR_SHEET_LABEL = "sheet-label";
3353
+ ATTR_STACK_EFFECT = "stack-effect";
2842
3354
  isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
2843
3355
  DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
2844
3356
  parseSnapPoints = (raw) => {
@@ -2868,6 +3380,44 @@ var init_BottomSheetElement = __esm({
2868
3380
  parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
2869
3381
  parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
2870
3382
  parseList = (raw) => raw ? raw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
3383
+ ANCHOR_POSITIONS = [
3384
+ "top-left",
3385
+ "top-center",
3386
+ "top-right",
3387
+ "center-left",
3388
+ "center",
3389
+ "center-right",
3390
+ "bottom-left",
3391
+ "bottom-center",
3392
+ "bottom-right",
3393
+ "sheet-top-left",
3394
+ "sheet-top-center",
3395
+ "sheet-top-right",
3396
+ "dock-bottom",
3397
+ "dock-top"
3398
+ ];
3399
+ ANCHOR_ANIMATIONS = [
3400
+ "fade",
3401
+ "scale",
3402
+ "slide",
3403
+ "pop",
3404
+ "none"
3405
+ ];
3406
+ parseAnchorOptions = (el) => {
3407
+ const position = el.getAttribute("data-position");
3408
+ const animation = el.getAttribute("data-animation");
3409
+ const showOn = parseList(el.getAttribute("data-show-on"));
3410
+ const fadeRangeRaw = parseList(el.getAttribute("data-fade-range"));
3411
+ const fadeRange = fadeRangeRaw?.length === 2 && fadeRangeRaw.every((v) => Number.isFinite(Number(v))) ? [Number(fadeRangeRaw[0]), Number(fadeRangeRaw[1])] : void 0;
3412
+ return {
3413
+ position: position && ANCHOR_POSITIONS.includes(position) ? position : void 0,
3414
+ inset: el.getAttribute("data-inset") ?? void 0,
3415
+ showOn,
3416
+ fadeRange,
3417
+ interactive: el.getAttribute("data-interactive") !== "false",
3418
+ animation: animation && ANCHOR_ANIMATIONS.includes(animation) ? animation : void 0
3419
+ };
3420
+ };
2871
3421
  buildSlot = (name) => {
2872
3422
  const slot = document.createElement("slot");
2873
3423
  slot.name = name;
@@ -2937,7 +3487,9 @@ var init_BottomSheetElement = __esm({
2937
3487
  }
2938
3488
  };
2939
3489
  };
2940
- exports.BottomSheetElement = class extends HTMLElement {
3490
+ BaseHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
3491
+ };
3492
+ exports.BottomSheetElement = class extends BaseHTMLElement {
2941
3493
  constructor() {
2942
3494
  super();
2943
3495
  __publicField(this, "engine", null);
@@ -2945,17 +3497,12 @@ var init_BottomSheetElement = __esm({
2945
3497
  __publicField(this, "offHandlers", []);
2946
3498
  __publicField(this, "refs");
2947
3499
  __publicField(this, "backdropClickHandler", null);
3500
+ __publicField(this, "stylesheetLinked", false);
3501
+ __publicField(this, "declarativeAnchors", []);
2948
3502
  this.root = this.attachShadow({ mode: "open" });
2949
3503
  const baseStyle = document.createElement("style");
2950
3504
  baseStyle.textContent = baseStyles;
2951
3505
  this.root.appendChild(baseStyle);
2952
- const stylesheet = this.getAttribute(ATTR_STYLESHEET);
2953
- if (stylesheet) {
2954
- const link = document.createElement("link");
2955
- link.rel = "stylesheet";
2956
- link.href = stylesheet;
2957
- this.root.appendChild(link);
2958
- }
2959
3506
  const inlineStyle = document.createElement("style");
2960
3507
  inlineStyle.textContent = [
2961
3508
  ":host { display: contents; }",
@@ -2983,10 +3530,22 @@ var init_BottomSheetElement = __esm({
2983
3530
  ATTR_ANIMATION,
2984
3531
  ATTR_FOCUS_TRAP,
2985
3532
  ATTR_CLOSE_ON_ESCAPE,
2986
- ATTR_LOCK_BODY_SCROLL
3533
+ ATTR_LOCK_BODY_SCROLL,
3534
+ ATTR_SHEET_LABEL,
3535
+ ATTR_STACK_EFFECT
2987
3536
  ];
2988
3537
  }
2989
3538
  connectedCallback() {
3539
+ if (!this.stylesheetLinked) {
3540
+ this.stylesheetLinked = true;
3541
+ const stylesheet = this.getAttribute(ATTR_STYLESHEET);
3542
+ if (stylesheet) {
3543
+ const link = document.createElement("link");
3544
+ link.rel = "stylesheet";
3545
+ link.href = stylesheet;
3546
+ this.root.appendChild(link);
3547
+ }
3548
+ }
2990
3549
  this.initEngine();
2991
3550
  }
2992
3551
  disconnectedCallback() {
@@ -3009,6 +3568,10 @@ var init_BottomSheetElement = __esm({
3009
3568
  }
3010
3569
  this.engine?.destroy();
3011
3570
  this.engine = null;
3571
+ for (const el of this.declarativeAnchors) {
3572
+ this.appendChild(el);
3573
+ }
3574
+ this.declarativeAnchors = [];
3012
3575
  }
3013
3576
  initEngine() {
3014
3577
  const { sheet, handle, content, backdrop, announcer } = this.refs;
@@ -3020,6 +3583,10 @@ var init_BottomSheetElement = __esm({
3020
3583
  } else {
3021
3584
  sheet.removeAttribute("aria-modal");
3022
3585
  }
3586
+ sheet.setAttribute(
3587
+ "aria-label",
3588
+ this.getAttribute(ATTR_SHEET_LABEL) ?? "Bottom sheet"
3589
+ );
3023
3590
  const showBackdrop = this.getAttribute(ATTR_BACKDROP) !== "false";
3024
3591
  backdrop.style.display = showBackdrop ? "" : "none";
3025
3592
  if (showBackdrop) {
@@ -3039,11 +3606,20 @@ var init_BottomSheetElement = __esm({
3039
3606
  animation: parseAnimation(this.getAttribute(ATTR_ANIMATION)),
3040
3607
  focusTrap,
3041
3608
  closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
3042
- lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false"
3609
+ lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false",
3610
+ stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true"
3043
3611
  };
3044
3612
  this.engine = new BottomSheetEngine(opts);
3613
+ sheet.setAttribute("data-active", this.engine.state.activeId);
3614
+ for (const el of Array.from(
3615
+ this.querySelectorAll('[slot="anchor"]')
3616
+ )) {
3617
+ this.declarativeAnchors.push(el);
3618
+ this.engine.addAnchor({ ...parseAnchorOptions(el), element: el });
3619
+ }
3045
3620
  this.offHandlers = [
3046
3621
  this.engine.on("snap", (payload) => {
3622
+ sheet.setAttribute("data-active", payload.id);
3047
3623
  announcer.textContent = payload.id;
3048
3624
  this.dispatchEvent(new CustomEvent("snap", { detail: payload }));
3049
3625
  }),
@@ -3073,15 +3649,22 @@ var init_BottomSheetElement = __esm({
3073
3649
  setAllowed(ids, snap) {
3074
3650
  this.engine?.setAllowed(ids, snap);
3075
3651
  }
3652
+ addAnchor(opts) {
3653
+ return this.engine?.addAnchor(opts) ?? (() => {
3654
+ });
3655
+ }
3656
+ setScrimStages(opts) {
3657
+ return this.engine?.setScrimStages(opts) ?? (() => {
3658
+ });
3659
+ }
3076
3660
  get sheetState() {
3077
3661
  return this.engine?.state ?? null;
3078
3662
  }
3079
3663
  };
3080
- registered = false;
3081
3664
  exports.defineBottomSheet = (tag = "bottom-sheet") => {
3082
- if (registered || customElements.get(tag)) return;
3083
- customElements.define(tag, exports.BottomSheetElement);
3084
- registered = true;
3665
+ if (typeof customElements === "undefined" || customElements.get(tag)) return;
3666
+ customElements.define(tag, class extends exports.BottomSheetElement {
3667
+ });
3085
3668
  };
3086
3669
  }
3087
3670
  });