@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/solid.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createSignal, onMount, onCleanup, createMemo } from 'solid-js';
2
- import { jsxs, jsx } from 'solid-js/jsx-runtime';
1
+ import { insert, memo, use, effect, setAttribute, template } from 'solid-js/web';
2
+ import { createSignal, onMount, onCleanup, createEffect, on, createMemo } from 'solid-js';
3
3
 
4
4
  var __defProp = Object.defineProperty;
5
5
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -23,6 +23,18 @@ var SheetStack = class {
23
23
  this.entries.splice(idx, 1);
24
24
  this.recompute();
25
25
  }
26
+ promote(id) {
27
+ const idx = this.entries.findIndex((e) => e.id === id);
28
+ if (idx === -1) return;
29
+ if (idx !== this.entries.length - 1) {
30
+ const [entry] = this.entries.splice(idx, 1);
31
+ this.entries.push(entry);
32
+ }
33
+ this.recompute();
34
+ }
35
+ update() {
36
+ this.recompute();
37
+ }
26
38
  size() {
27
39
  return this.entries.length;
28
40
  }
@@ -30,9 +42,24 @@ var SheetStack = class {
30
42
  this.entries = [];
31
43
  }
32
44
  recompute() {
45
+ let top = null;
46
+ const open = [];
47
+ for (const entry of this.entries) {
48
+ if (!entry.isOpen || entry.isOpen()) {
49
+ top = entry;
50
+ open.push(entry);
51
+ }
52
+ }
53
+ if (!top && this.entries.length > 0) {
54
+ top = this.entries[this.entries.length - 1];
55
+ }
33
56
  this.entries.forEach((entry, i) => {
34
57
  entry.setZIndex(BASE_Z + i * STEP);
35
- entry.setIsTop(i === this.entries.length - 1);
58
+ entry.setIsTop(entry === top);
59
+ if (entry.setDepth) {
60
+ const openIdx = open.indexOf(entry);
61
+ entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
62
+ }
36
63
  });
37
64
  }
38
65
  };
@@ -317,14 +344,17 @@ function installAutoCollapse(deps) {
317
344
  const fire = () => {
318
345
  if (deps.isDestroyed()) return;
319
346
  if (deps.isDragging()) return;
320
- const allowed = deps.getAllowedIds();
321
- const target = allowed.find((id) => {
347
+ const active = deps.resolveSnap(deps.getActiveId());
348
+ if (!active || active.size === 0) return;
349
+ let target = null;
350
+ for (const id of deps.getAllowedIds()) {
322
351
  const snap = deps.resolveSnap(id);
323
- return snap !== null && snap.size > 0;
324
- });
352
+ if (!snap || snap.size <= 0) continue;
353
+ if (!target || snap.size < target.size) target = snap;
354
+ }
325
355
  if (!target) return;
326
- if (target === deps.getActiveId()) return;
327
- deps.snapTo(target);
356
+ if (target.id === active.id || target.size >= active.size) return;
357
+ deps.snapTo(target.id);
328
358
  };
329
359
  const reset = () => {
330
360
  clearTimer();
@@ -335,6 +365,7 @@ function installAutoCollapse(deps) {
335
365
  }, ms);
336
366
  };
337
367
  const unsubscribe = deps.on("snap", () => reset());
368
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
338
369
  return () => {
339
370
  clearTimer();
340
371
  unsubscribe();
@@ -347,6 +378,7 @@ function notifyLinkedSheets(sheets, self) {
347
378
  for (const linked of sheets) {
348
379
  if (linked === self) continue;
349
380
  try {
381
+ if (linked.state.size === 0) continue;
350
382
  const ids = linked.getAllowedIds();
351
383
  const target = ids.find((id) => id !== "closed");
352
384
  if (target && target !== linked.state.activeId) {
@@ -393,7 +425,7 @@ function installRoute(deps) {
393
425
  const onPop = (event) => {
394
426
  if (deps.isDestroyed()) return;
395
427
  const state = event.state;
396
- if (state && state.__bsRouted === target && deps.getSize() > 0) {
428
+ if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
397
429
  routePushed = false;
398
430
  void deps.close();
399
431
  }
@@ -547,13 +579,17 @@ function installResizeObserver(deps) {
547
579
  if (viewportSize2 > 0 && viewportSize2 < max) {
548
580
  deps.setMaxAxisSize(viewportSize2);
549
581
  }
582
+ const wasAnimating = deps.isAnimating?.() ?? false;
550
583
  deps.cancelInFlight();
551
584
  deps.newCycle();
552
585
  const current = deps.resolveActiveSnap();
553
586
  const newMax = deps.getMaxAxisSize();
554
587
  const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
555
588
  deps.setSize(targetSize);
556
- if (current && !deps.isDragging()) deps.applySize(targetSize);
589
+ if (current && !deps.isDragging()) {
590
+ deps.applySize(targetSize);
591
+ if (wasAnimating) deps.resyncAfterCancel?.();
592
+ }
557
593
  };
558
594
  let resizeObserver = null;
559
595
  if (typeof ResizeObserver !== "undefined") {
@@ -668,6 +704,85 @@ function devWarn(...args) {
668
704
  }
669
705
  }
670
706
 
707
+ // src/core/primitives/overlay-position.ts
708
+ function resolveSheetAnchoredStyle(mode, position, inset) {
709
+ const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
710
+ switch (mode) {
711
+ case "bottom":
712
+ if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
713
+ if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
714
+ return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
715
+ case "top":
716
+ if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
717
+ if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
718
+ return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
719
+ case "left":
720
+ if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
721
+ if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
722
+ return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
723
+ case "right":
724
+ if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
725
+ if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
726
+ return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
727
+ }
728
+ }
729
+ function applyOverlayPosition(ws, mode, position, inset) {
730
+ switch (position) {
731
+ case "top-left":
732
+ ws.top = inset;
733
+ ws.left = inset;
734
+ break;
735
+ case "top-center":
736
+ ws.top = inset;
737
+ ws.left = "50%";
738
+ ws.transform = "translateX(-50%)";
739
+ break;
740
+ case "top-right":
741
+ ws.top = inset;
742
+ ws.right = inset;
743
+ break;
744
+ case "center-left":
745
+ ws.top = "50%";
746
+ ws.left = inset;
747
+ ws.transform = "translateY(-50%)";
748
+ break;
749
+ case "center":
750
+ ws.top = "50%";
751
+ ws.left = "50%";
752
+ ws.transform = "translate(-50%, -50%)";
753
+ break;
754
+ case "center-right":
755
+ ws.top = "50%";
756
+ ws.right = inset;
757
+ ws.transform = "translateY(-50%)";
758
+ break;
759
+ case "bottom-left":
760
+ ws.bottom = inset;
761
+ ws.left = inset;
762
+ break;
763
+ case "bottom-center":
764
+ ws.bottom = inset;
765
+ ws.left = "50%";
766
+ ws.transform = "translateX(-50%)";
767
+ break;
768
+ case "bottom-right":
769
+ ws.bottom = inset;
770
+ ws.right = inset;
771
+ break;
772
+ case "sheet-top-left":
773
+ case "sheet-top-center":
774
+ case "sheet-top-right": {
775
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
776
+ if (style.top !== void 0) ws.top = style.top;
777
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
778
+ if (style.left !== void 0) ws.left = style.left;
779
+ if (style.right !== void 0) ws.right = style.right;
780
+ if (style.transform !== void 0) ws.transform = style.transform;
781
+ break;
782
+ }
783
+ }
784
+ }
785
+
671
786
  // src/core/controllers/scrim-controller.ts
672
787
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
673
788
  var ScrimController = class {
@@ -680,6 +795,8 @@ var ScrimController = class {
680
795
  __publicField(this, "getAllowedIds");
681
796
  __publicField(this, "getResolvedSnaps");
682
797
  __publicField(this, "snapToFn");
798
+ __publicField(this, "closeFn");
799
+ __publicField(this, "scrimInteractive", false);
683
800
  __publicField(this, "scrimMode");
684
801
  __publicField(this, "backdropRange");
685
802
  __publicField(this, "screenRange");
@@ -700,6 +817,7 @@ var ScrimController = class {
700
817
  this.getAllowedIds = deps.getAllowedIds;
701
818
  this.getResolvedSnaps = deps.getResolvedSnaps;
702
819
  this.snapToFn = deps.snapTo;
820
+ this.closeFn = deps.close;
703
821
  this.scrimMode = opts.scrimMode ?? "full";
704
822
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
705
823
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
@@ -841,11 +959,15 @@ var ScrimController = class {
841
959
  }
842
960
  const onClick = () => {
843
961
  if (this.isDestroyed()) return;
962
+ if (this.closeFn) {
963
+ this.closeFn();
964
+ return;
965
+ }
844
966
  const allowed = this.getAllowedIds();
845
967
  const snaps = this.getResolvedSnaps();
846
968
  const fallback = allowed.find((id) => {
847
969
  const snap = findById(id, snaps);
848
- return snap !== null && snap.size > 0;
970
+ return snap !== null && snap.size === 0;
849
971
  });
850
972
  if (fallback) this.snapToFn(fallback);
851
973
  };
@@ -862,6 +984,7 @@ var ScrimController = class {
862
984
  if (this.isDestroyed()) return;
863
985
  if (enabled === this.scrimEnabled) return;
864
986
  this.scrimEnabled = enabled;
987
+ this.invalidateOpacityCache();
865
988
  if (!enabled) {
866
989
  this.savedScrimRanges = {
867
990
  screen: this.screenRange,
@@ -885,7 +1008,12 @@ var ScrimController = class {
885
1008
  blur: cfg.blur ?? null,
886
1009
  interactive: cfg.interactive
887
1010
  });
888
- this.screenRange = cfg.range;
1011
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1012
+ this.savedScrimRanges = { screen: cfg.range, backdrop: cfg.range };
1013
+ } else {
1014
+ this.screenRange = cfg.range;
1015
+ this.backdropRange = cfg.range;
1016
+ }
889
1017
  }
890
1018
  const noop = () => {
891
1019
  };
@@ -935,59 +1063,7 @@ var ScrimController = class {
935
1063
  wrapper.className = "bs-scrim-overlay";
936
1064
  const ws = wrapper.style;
937
1065
  ws.position = "absolute";
938
- switch (position) {
939
- case "top-left":
940
- ws.top = inset;
941
- ws.left = inset;
942
- break;
943
- case "top-center":
944
- ws.top = inset;
945
- ws.left = "50%";
946
- ws.transform = "translateX(-50%)";
947
- break;
948
- case "top-right":
949
- ws.top = inset;
950
- ws.right = inset;
951
- break;
952
- case "center-left":
953
- ws.top = "50%";
954
- ws.left = inset;
955
- ws.transform = "translateY(-50%)";
956
- break;
957
- case "center":
958
- ws.top = "50%";
959
- ws.left = "50%";
960
- ws.transform = "translate(-50%, -50%)";
961
- break;
962
- case "center-right":
963
- ws.top = "50%";
964
- ws.right = inset;
965
- ws.transform = "translateY(-50%)";
966
- break;
967
- case "bottom-left":
968
- ws.bottom = inset;
969
- ws.left = inset;
970
- break;
971
- case "bottom-center":
972
- ws.bottom = inset;
973
- ws.left = "50%";
974
- ws.transform = "translateX(-50%)";
975
- break;
976
- case "bottom-right":
977
- ws.bottom = inset;
978
- ws.right = inset;
979
- break;
980
- case "sheet-top-left":
981
- case "sheet-top-center":
982
- case "sheet-top-right":
983
- applySheetAnchoredPosition(
984
- ws,
985
- this.mode,
986
- position,
987
- inset
988
- );
989
- break;
990
- }
1066
+ applyOverlayPosition(ws, this.mode, position, inset);
991
1067
  if (interactive) {
992
1068
  ws.pointerEvents = "auto";
993
1069
  }
@@ -1046,6 +1122,7 @@ var ScrimController = class {
1046
1122
  }
1047
1123
  }
1048
1124
  if (opts.interactive !== void 0) {
1125
+ this.scrimInteractive = opts.interactive;
1049
1126
  s.pointerEvents = opts.interactive ? "auto" : "none";
1050
1127
  }
1051
1128
  }
@@ -1060,38 +1137,9 @@ var ScrimController = class {
1060
1137
  const s = this.screenComponent.style;
1061
1138
  s.position = "fixed";
1062
1139
  s.inset = insetByMode[this.mode];
1063
- s.pointerEvents = "none";
1140
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1064
1141
  }
1065
1142
  };
1066
- function resolveSheetAnchoredStyle(mode, position, inset) {
1067
- const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
1068
- switch (mode) {
1069
- case "bottom":
1070
- if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
1071
- if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
1072
- return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
1073
- case "top":
1074
- if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
1075
- if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
1076
- return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
1077
- case "left":
1078
- if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
1079
- if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
1080
- return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
1081
- case "right":
1082
- if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
1083
- if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
1084
- return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
1085
- }
1086
- }
1087
- function applySheetAnchoredPosition(ws, mode, position, inset) {
1088
- const style = resolveSheetAnchoredStyle(mode, position, inset);
1089
- if (style.top !== void 0) ws.top = style.top;
1090
- if (style.bottom !== void 0) ws.bottom = style.bottom;
1091
- if (style.left !== void 0) ws.left = style.left;
1092
- if (style.right !== void 0) ws.right = style.right;
1093
- if (style.transform !== void 0) ws.transform = style.transform;
1094
- }
1095
1143
 
1096
1144
  // src/core/animation/animation.ts
1097
1145
  var easeOutBack = (t) => {
@@ -1143,6 +1191,10 @@ var tween = ({
1143
1191
  promise
1144
1192
  };
1145
1193
  };
1194
+ var prefersReducedMotion = () => {
1195
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1196
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1197
+ };
1146
1198
 
1147
1199
  // src/core/animation/spring.ts
1148
1200
  var DEFAULT_SPRING = {
@@ -1173,14 +1225,20 @@ var runSpring = ({
1173
1225
  if (cancelled) return;
1174
1226
  const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
1175
1227
  lastTime = now;
1176
- const subDt = Math.min(dt, 1 / 240);
1177
- let stepsLeft = Math.max(1, Math.ceil(dt / subDt));
1228
+ const steps = Math.max(1, Math.ceil(dt * 240));
1229
+ const subDt = dt / steps;
1230
+ let stepsLeft = steps;
1178
1231
  while (stepsLeft-- > 0) {
1179
1232
  const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1180
1233
  const accel = force / cfg.mass;
1181
1234
  v += accel * subDt;
1182
1235
  x += v * subDt;
1183
1236
  }
1237
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1238
+ onUpdate(to, 0);
1239
+ resolve();
1240
+ return;
1241
+ }
1184
1242
  onUpdate(x, v);
1185
1243
  const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1186
1244
  if (isAtRest) {
@@ -1355,9 +1413,13 @@ var isVisible = (el) => {
1355
1413
  var focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
1356
1414
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1357
1415
  );
1416
+ var trapStack = [];
1358
1417
  var installFocusTrap = (container, options = {}) => {
1359
1418
  if (typeof document === "undefined") return () => {
1360
1419
  };
1420
+ const token = {};
1421
+ trapStack.push(token);
1422
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1361
1423
  const previouslyFocused = document.activeElement;
1362
1424
  const focusInitial = () => {
1363
1425
  const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
@@ -1365,6 +1427,7 @@ var installFocusTrap = (container, options = {}) => {
1365
1427
  };
1366
1428
  focusInitial();
1367
1429
  const handleKey = (e) => {
1430
+ if (!isActive()) return;
1368
1431
  if (e.key === "Escape" && options.onEscape) {
1369
1432
  options.onEscape();
1370
1433
  return;
@@ -1387,6 +1450,7 @@ var installFocusTrap = (container, options = {}) => {
1387
1450
  }
1388
1451
  };
1389
1452
  const handleFocusIn = (e) => {
1453
+ if (!isActive()) return;
1390
1454
  const target = e.target;
1391
1455
  if (target && !container.contains(target)) {
1392
1456
  const list = focusables(container);
@@ -1399,6 +1463,8 @@ var installFocusTrap = (container, options = {}) => {
1399
1463
  return () => {
1400
1464
  if (released) return;
1401
1465
  released = true;
1466
+ const idx = trapStack.indexOf(token);
1467
+ if (idx !== -1) trapStack.splice(idx, 1);
1402
1468
  document.removeEventListener("keydown", handleKey, true);
1403
1469
  document.removeEventListener("focusin", handleFocusIn, true);
1404
1470
  if (previouslyFocused && document.contains(previouslyFocused)) {
@@ -1454,11 +1520,17 @@ function createInertSiblings(rootProvider) {
1454
1520
  return {
1455
1521
  apply() {
1456
1522
  if (typeof document === "undefined") return;
1457
- const root = rootProvider();
1458
- const parent = root.parentElement ?? document.body;
1523
+ let top = rootProvider();
1524
+ while (top.parentElement && top.parentElement !== document.body) {
1525
+ top = top.parentElement;
1526
+ }
1527
+ const parent = top.parentElement ?? document.body;
1459
1528
  for (const child of Array.from(parent.children)) {
1460
- if (child === root) continue;
1529
+ if (child === top) continue;
1461
1530
  if (child.hasAttribute("inert")) continue;
1531
+ if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1532
+ continue;
1533
+ }
1462
1534
  child.setAttribute("inert", "");
1463
1535
  tracked.push(child);
1464
1536
  }
@@ -1474,7 +1546,6 @@ function createInertSiblings(rootProvider) {
1474
1546
  var LifecycleController = class {
1475
1547
  constructor(deps, opts) {
1476
1548
  __publicField(this, "element");
1477
- __publicField(this, "closeFn");
1478
1549
  __publicField(this, "focusTrapEnabled");
1479
1550
  __publicField(this, "initialFocus");
1480
1551
  __publicField(this, "closeOnEscape");
@@ -1488,7 +1559,6 @@ var LifecycleController = class {
1488
1559
  __publicField(this, "installed", false);
1489
1560
  __publicField(this, "destroyed", false);
1490
1561
  this.element = deps.element;
1491
- this.closeFn = deps.close;
1492
1562
  this.focusTrapEnabled = opts.focusTrap ?? false;
1493
1563
  this.initialFocus = opts.initialFocus;
1494
1564
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1498,6 +1568,9 @@ var LifecycleController = class {
1498
1568
  this.returnFocus = opts.returnFocus;
1499
1569
  this.inertSiblings = createInertSiblings(() => this.element);
1500
1570
  }
1571
+ get isInstalled() {
1572
+ return this.installed;
1573
+ }
1501
1574
  install() {
1502
1575
  if (this.destroyed) return;
1503
1576
  this.installed = true;
@@ -1506,8 +1579,7 @@ var LifecycleController = class {
1506
1579
  }
1507
1580
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1508
1581
  this.releaseFocusTrap = installFocusTrap(this.element, {
1509
- initialFocus: this.initialFocus,
1510
- onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
1582
+ initialFocus: this.initialFocus
1511
1583
  });
1512
1584
  }
1513
1585
  if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
@@ -1661,10 +1733,11 @@ var installGestures = (handle, mode, callbacks) => {
1661
1733
  };
1662
1734
 
1663
1735
  // src/core/primitives/rubber-band.ts
1664
- var RUBBER_BAND_FACTOR = 0.55;
1665
1736
  function rubberBand(overshoot, maxAxisSize) {
1666
1737
  const cap = Math.min(maxAxisSize * 0.15, 80);
1667
- return Math.sign(overshoot) * Math.pow(Math.abs(overshoot), RUBBER_BAND_FACTOR) * Math.min(6, cap / Math.max(Math.abs(overshoot), 1));
1738
+ if (cap <= 0) return 0;
1739
+ const abs = Math.abs(overshoot);
1740
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1668
1741
  }
1669
1742
 
1670
1743
  // src/core/features/soft-keyboard.ts
@@ -1749,6 +1822,10 @@ var GestureController = class {
1749
1822
  this.isDragging_ = false;
1750
1823
  this.keyboardDismissed = false;
1751
1824
  this.deps.getRoot()?.removeAttribute("data-dragging");
1825
+ this.deps.emit("dragend", {
1826
+ size: this.deps.getDragContext().size,
1827
+ velocity: 0
1828
+ });
1752
1829
  const restoreTo = this.dragStartSize;
1753
1830
  void this.deps.animateTo(restoreTo, 0);
1754
1831
  }
@@ -1822,6 +1899,287 @@ function createScrollCache(deps) {
1822
1899
  };
1823
1900
  }
1824
1901
 
1902
+ // src/core/primitives/anchor-animations.ts
1903
+ var DEFAULT_DURATION3 = 200;
1904
+ var DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
1905
+ var PRESET_FRAMES = {
1906
+ fade: {
1907
+ enter: [{ opacity: 0 }, { opacity: 1 }],
1908
+ exit: [{ opacity: 1 }, { opacity: 0 }]
1909
+ },
1910
+ scale: {
1911
+ enter: [
1912
+ { opacity: 0, transform: "scale(0.85)" },
1913
+ { opacity: 1, transform: "scale(1)" }
1914
+ ],
1915
+ exit: [
1916
+ { opacity: 1, transform: "scale(1)" },
1917
+ { opacity: 0, transform: "scale(0.85)" }
1918
+ ]
1919
+ },
1920
+ slide: {
1921
+ enter: [
1922
+ { opacity: 0, transform: "translateY(10px)" },
1923
+ { opacity: 1, transform: "translateY(0)" }
1924
+ ],
1925
+ exit: [
1926
+ { opacity: 1, transform: "translateY(0)" },
1927
+ { opacity: 0, transform: "translateY(10px)" }
1928
+ ]
1929
+ },
1930
+ pop: {
1931
+ enter: [
1932
+ { opacity: 0, transform: "scale(0.6)", offset: 0 },
1933
+ { opacity: 1, transform: "scale(1.06)", offset: 0.7 },
1934
+ { opacity: 1, transform: "scale(1)", offset: 1 }
1935
+ ],
1936
+ exit: [
1937
+ { opacity: 1, transform: "scale(1)" },
1938
+ { opacity: 0, transform: "scale(0.6)" }
1939
+ ]
1940
+ }
1941
+ };
1942
+ var INSTANT = {
1943
+ finished: Promise.resolve(),
1944
+ cancel: () => {
1945
+ }
1946
+ };
1947
+ function resolveTransitionFrames(spec, phase) {
1948
+ const fallback = { frames: null, duration: 0, easing: DEFAULT_EASING };
1949
+ if (spec === "none") return fallback;
1950
+ if (spec === void 0 || typeof spec === "string") {
1951
+ const preset2 = PRESET_FRAMES[spec ?? "fade"];
1952
+ return {
1953
+ frames: preset2[phase],
1954
+ duration: DEFAULT_DURATION3,
1955
+ easing: DEFAULT_EASING
1956
+ };
1957
+ }
1958
+ const custom = phase === "enter" ? spec.enter : spec.exit;
1959
+ if (custom) {
1960
+ return {
1961
+ frames: custom,
1962
+ duration: spec.duration ?? DEFAULT_DURATION3,
1963
+ easing: spec.easing ?? DEFAULT_EASING
1964
+ };
1965
+ }
1966
+ if (spec.preset === "none") return fallback;
1967
+ const preset = PRESET_FRAMES[spec.preset ?? "fade"];
1968
+ return {
1969
+ frames: preset[phase],
1970
+ duration: spec.duration ?? DEFAULT_DURATION3,
1971
+ easing: spec.easing ?? DEFAULT_EASING
1972
+ };
1973
+ }
1974
+ function runAnchorTransition(el, spec, phase) {
1975
+ const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
1976
+ if (respectReduced && prefersReducedMotion()) return INSTANT;
1977
+ const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
1978
+ if (!frames || duration <= 0) return INSTANT;
1979
+ if (typeof el.animate !== "function") return INSTANT;
1980
+ const animation = el.animate(frames, {
1981
+ duration,
1982
+ easing,
1983
+ fill: "both"
1984
+ });
1985
+ const finished = animation.finished.then(() => void 0).catch(() => void 0);
1986
+ return {
1987
+ finished,
1988
+ cancel: () => {
1989
+ try {
1990
+ animation.cancel();
1991
+ } catch {
1992
+ return;
1993
+ }
1994
+ }
1995
+ };
1996
+ }
1997
+
1998
+ // src/core/features/sheet-anchors.ts
1999
+ function attachAnchor(deps, opts) {
2000
+ const doc = deps.host.ownerDocument;
2001
+ const wrapper = doc.createElement("div");
2002
+ wrapper.className = "bs-anchor";
2003
+ const interactive = opts.interactive ?? true;
2004
+ const position = opts.position ?? "sheet-top-right";
2005
+ const isDock = position === "dock-bottom" || position === "dock-top";
2006
+ const ws = wrapper.style;
2007
+ ws.position = "fixed";
2008
+ if (isDock) {
2009
+ ws.left = "0";
2010
+ ws.right = "0";
2011
+ if (position === "dock-bottom") {
2012
+ ws.bottom = "0";
2013
+ } else {
2014
+ ws.top = "0";
2015
+ }
2016
+ } else {
2017
+ applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2018
+ }
2019
+ if (opts.fadeRange) {
2020
+ const [r0, r1] = opts.fadeRange;
2021
+ const span = Math.max(r1 - r0, 1e-4);
2022
+ ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
2023
+ }
2024
+ wrapper.appendChild(opts.element);
2025
+ deps.host.appendChild(wrapper);
2026
+ let visible = null;
2027
+ let inFlight = null;
2028
+ const shouldShow = (state) => {
2029
+ if (typeof opts.showOn === "function") return opts.showOn(state);
2030
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2031
+ return isDock || state.size > 0;
2032
+ };
2033
+ const applyVisibility = (next, animate) => {
2034
+ if (next === visible) return;
2035
+ visible = next;
2036
+ inFlight?.cancel();
2037
+ inFlight = null;
2038
+ wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
2039
+ if (next) {
2040
+ wrapper.style.visibility = "";
2041
+ if (animate) {
2042
+ inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
2043
+ }
2044
+ return;
2045
+ }
2046
+ if (!animate) {
2047
+ wrapper.style.visibility = "hidden";
2048
+ return;
2049
+ }
2050
+ const handle = runAnchorTransition(opts.element, opts.animation, "exit");
2051
+ inFlight = handle;
2052
+ void handle.finished.then(() => {
2053
+ if (inFlight === handle && visible === false) {
2054
+ wrapper.style.visibility = "hidden";
2055
+ }
2056
+ });
2057
+ };
2058
+ const evaluate = (animate) => {
2059
+ if (deps.isDestroyed()) return;
2060
+ applyVisibility(shouldShow(deps.getState()), animate);
2061
+ };
2062
+ const offs = [
2063
+ deps.on("snap", () => evaluate(true)),
2064
+ deps.on("open", () => evaluate(true)),
2065
+ deps.on("close", () => evaluate(true))
2066
+ ];
2067
+ evaluate(false);
2068
+ return {
2069
+ wrapper,
2070
+ detach: () => {
2071
+ offs.forEach((off) => off());
2072
+ inFlight?.cancel();
2073
+ inFlight = null;
2074
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
2075
+ },
2076
+ syncZ: (z) => {
2077
+ wrapper.style.zIndex = String(z);
2078
+ }
2079
+ };
2080
+ }
2081
+
2082
+ // src/core/features/scrim-stages.ts
2083
+ var matchesId = (def, activeId) => {
2084
+ if (def.for === void 0) return false;
2085
+ return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
2086
+ };
2087
+ var matchesRange = (def, progress) => {
2088
+ if (!def.forRange) return false;
2089
+ const [from, to] = def.forRange;
2090
+ return progress >= from && progress <= to;
2091
+ };
2092
+ function installScrimStages(deps, opts) {
2093
+ const doc = deps.host.ownerDocument;
2094
+ const entries = opts.stages.map((def) => {
2095
+ const wrapper = doc.createElement("div");
2096
+ wrapper.className = "bs-scrim-stage";
2097
+ const ws = wrapper.style;
2098
+ ws.position = "absolute";
2099
+ applyOverlayPosition(
2100
+ ws,
2101
+ deps.mode,
2102
+ def.position ?? opts.position ?? "center",
2103
+ def.inset ?? opts.inset ?? "16px"
2104
+ );
2105
+ ws.visibility = "hidden";
2106
+ ws.pointerEvents = "none";
2107
+ wrapper.appendChild(def.element);
2108
+ deps.host.appendChild(wrapper);
2109
+ return { def, wrapper, inFlight: null };
2110
+ });
2111
+ let active = null;
2112
+ const resolveActive = () => {
2113
+ const state = deps.getState();
2114
+ if (state.size === 0) {
2115
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2116
+ }
2117
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2118
+ };
2119
+ const hide = (entry, animate) => {
2120
+ entry.inFlight?.cancel();
2121
+ entry.wrapper.style.pointerEvents = "none";
2122
+ if (!animate) {
2123
+ entry.inFlight = null;
2124
+ entry.wrapper.style.visibility = "hidden";
2125
+ return;
2126
+ }
2127
+ const handle = runAnchorTransition(
2128
+ entry.def.element,
2129
+ entry.def.animation ?? opts.animation,
2130
+ "exit"
2131
+ );
2132
+ entry.inFlight = handle;
2133
+ void handle.finished.then(() => {
2134
+ if (entry.inFlight === handle && active !== entry) {
2135
+ entry.wrapper.style.visibility = "hidden";
2136
+ }
2137
+ });
2138
+ };
2139
+ const show = (entry, animate) => {
2140
+ entry.inFlight?.cancel();
2141
+ entry.inFlight = null;
2142
+ entry.wrapper.style.visibility = "";
2143
+ entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
2144
+ if (animate) {
2145
+ entry.inFlight = runAnchorTransition(
2146
+ entry.def.element,
2147
+ entry.def.animation ?? opts.animation,
2148
+ "enter"
2149
+ );
2150
+ }
2151
+ };
2152
+ const update = (animate) => {
2153
+ if (deps.isDestroyed()) return;
2154
+ const next = resolveActive();
2155
+ if (next === active) return;
2156
+ const prev = active;
2157
+ active = next;
2158
+ if (prev) hide(prev, animate);
2159
+ if (next) show(next, animate);
2160
+ };
2161
+ const offs = [
2162
+ deps.on("snap", () => update(true)),
2163
+ deps.on("open", () => update(true)),
2164
+ deps.on("close", () => update(true))
2165
+ ];
2166
+ if (entries.some((e) => e.def.forRange)) {
2167
+ offs.push(deps.on("progress", () => update(true)));
2168
+ }
2169
+ update(false);
2170
+ return () => {
2171
+ offs.forEach((off) => off());
2172
+ for (const entry of entries) {
2173
+ entry.inFlight?.cancel();
2174
+ entry.inFlight = null;
2175
+ if (entry.wrapper.parentNode) {
2176
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2177
+ }
2178
+ }
2179
+ active = null;
2180
+ };
2181
+ }
2182
+
1825
2183
  // src/core/primitives/engine-options.ts
1826
2184
  var DEFAULT_FLICK_VELOCITY = 0.65;
1827
2185
  var DEFAULT_DRAG_THRESHOLD = 18;
@@ -1938,6 +2296,12 @@ var BottomSheetEngine = class {
1938
2296
  __publicField(this, "linkedSheets", []);
1939
2297
  __publicField(this, "scrollCache");
1940
2298
  __publicField(this, "teardowns", new TeardownStack());
2299
+ __publicField(this, "anchors", []);
2300
+ __publicField(this, "anchorHost", null);
2301
+ __publicField(this, "stackZ", 100);
2302
+ __publicField(this, "detachScrimStages", null);
2303
+ __publicField(this, "stackEffectEnabled", false);
2304
+ __publicField(this, "stackEffectPrimed", false);
1941
2305
  __publicField(this, "transformTemplate");
1942
2306
  const resolved = resolveEngineOptions(opts, {
1943
2307
  shouldApplyInertSiblings: () => {
@@ -1971,6 +2335,9 @@ var BottomSheetEngine = class {
1971
2335
  getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
1972
2336
  snapTo: (id) => {
1973
2337
  void this.snapTo(id);
2338
+ },
2339
+ close: () => {
2340
+ void this.close();
1974
2341
  }
1975
2342
  },
1976
2343
  resolved.scrim
@@ -1994,6 +2361,7 @@ var BottomSheetEngine = class {
1994
2361
  );
1995
2362
  this.closeOnBack = resolved.closeOnBack;
1996
2363
  this.routedTo = opts.routedTo;
2364
+ this.stackEffectEnabled = opts.stackEffect ?? false;
1997
2365
  this.snapPointsRaw = opts.snapPoints;
1998
2366
  this.persistKey = opts.persistKey;
1999
2367
  if (opts.linkedSheets) {
@@ -2006,7 +2374,7 @@ var BottomSheetEngine = class {
2006
2374
  this.snapPointsRaw,
2007
2375
  resolved.initialAllowed,
2008
2376
  this.mode,
2009
- () => this.handle.offsetHeight,
2377
+ () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2010
2378
  (maxAxisSize) => {
2011
2379
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2012
2380
  }
@@ -2216,11 +2584,6 @@ var BottomSheetEngine = class {
2216
2584
  }
2217
2585
  setAllowed(ids, snap) {
2218
2586
  if (this.destroyed) return;
2219
- const willSnap = snap !== void 0 && ids.includes(snap) || !ids.includes(this.activeId) && ids.length > 0;
2220
- if (!willSnap) {
2221
- this.animation.cancel();
2222
- this.newCycle();
2223
- }
2224
2587
  this.snaps.setAllowedIds(ids);
2225
2588
  this.updateAriaSlider();
2226
2589
  if (snap && ids.includes(snap)) {
@@ -2260,6 +2623,79 @@ var BottomSheetEngine = class {
2260
2623
  setScrimOverlay(opts) {
2261
2624
  return this.scrim.setScrimOverlay(opts);
2262
2625
  }
2626
+ addAnchor(opts) {
2627
+ if (this.destroyed || typeof document === "undefined") return () => {
2628
+ };
2629
+ if (!this.anchorHost) {
2630
+ this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
2631
+ this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
2632
+ this.anchorHost.style.setProperty(
2633
+ "--bs-progress",
2634
+ String(this.computeProgress(this.size))
2635
+ );
2636
+ }
2637
+ const handle = attachAnchor(
2638
+ {
2639
+ mode: this.mode,
2640
+ host: this.anchorHost,
2641
+ getState: () => ({
2642
+ activeId: this.activeId,
2643
+ size: this.size,
2644
+ progress: this.computeProgress(this.size)
2645
+ }),
2646
+ on: (event, fn) => this.on(event, fn),
2647
+ isDestroyed: () => this.destroyed
2648
+ },
2649
+ opts
2650
+ );
2651
+ handle.syncZ(this.stackZ + 1);
2652
+ this.anchors.push(handle);
2653
+ const detach = () => {
2654
+ const idx = this.anchors.indexOf(handle);
2655
+ if (idx === -1) return;
2656
+ this.anchors.splice(idx, 1);
2657
+ handle.detach();
2658
+ };
2659
+ this.teardowns.add(detach);
2660
+ return detach;
2661
+ }
2662
+ setScrimStages(opts) {
2663
+ if (this.destroyed || typeof document === "undefined") return () => {
2664
+ };
2665
+ this.detachScrimStages?.();
2666
+ this.detachScrimStages = null;
2667
+ if (!opts) return () => {
2668
+ };
2669
+ const host = this.screenComponent?.parentElement;
2670
+ if (!host) {
2671
+ console.warn(
2672
+ "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
2673
+ );
2674
+ return () => {
2675
+ };
2676
+ }
2677
+ const detachInstalled = installScrimStages(
2678
+ {
2679
+ mode: this.mode,
2680
+ host,
2681
+ getState: () => ({
2682
+ activeId: this.activeId,
2683
+ size: this.size,
2684
+ progress: this.computeProgress(this.size)
2685
+ }),
2686
+ on: (event, fn) => this.on(event, fn),
2687
+ isDestroyed: () => this.destroyed
2688
+ },
2689
+ opts
2690
+ );
2691
+ const detach = () => {
2692
+ if (this.detachScrimStages === detach) this.detachScrimStages = null;
2693
+ detachInstalled();
2694
+ };
2695
+ this.detachScrimStages = detach;
2696
+ this.teardowns.add(() => this.detachScrimStages?.());
2697
+ return detach;
2698
+ }
2263
2699
  getScrimState() {
2264
2700
  return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
2265
2701
  }
@@ -2346,24 +2782,45 @@ var BottomSheetEngine = class {
2346
2782
  getAllowedRange() {
2347
2783
  return this.snaps.getAllowedRange();
2348
2784
  }
2785
+ allowedIdsBySize() {
2786
+ return this.snaps.getAllowedIds().slice().sort(
2787
+ (a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0)
2788
+ );
2789
+ }
2349
2790
  updateAriaSlider() {
2350
- this.aria.setValue(this.snaps.getAllowedIds(), this.activeId);
2791
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
2351
2792
  }
2352
2793
  registerInStack() {
2353
2794
  this.teardowns.add(sheetStack.push({
2354
2795
  id: this.id,
2355
2796
  setZIndex: (z) => {
2797
+ this.stackZ = z;
2356
2798
  this.element.style.zIndex = String(z);
2357
2799
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
2800
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
2358
2801
  },
2359
2802
  setIsTop: (isTop) => {
2360
2803
  this.isTopSheet = isTop;
2361
2804
  if (this.backdrop) {
2362
2805
  this.backdrop.style.display = isTop ? "" : "none";
2363
2806
  }
2364
- }
2807
+ },
2808
+ isOpen: () => this.size > 0,
2809
+ setDepth: (depth) => this.applyStackDepth(depth)
2365
2810
  }));
2366
2811
  }
2812
+ applyStackDepth(depth) {
2813
+ if (!this.stackEffectEnabled) return;
2814
+ this.element.setAttribute("data-stack-depth", String(depth));
2815
+ if (!this.stackEffectPrimed) {
2816
+ this.stackEffectPrimed = true;
2817
+ const isVertical = this.mode === "bottom" || this.mode === "top";
2818
+ this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
2819
+ this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
2820
+ }
2821
+ const scale = Math.max(1 - depth * 0.04, 0.86);
2822
+ this.element.style.scale = scale === 1 ? "" : String(scale);
2823
+ }
2367
2824
  attach() {
2368
2825
  this.gesture = new GestureController({
2369
2826
  handle: this.handle,
@@ -2423,7 +2880,7 @@ var BottomSheetEngine = class {
2423
2880
  container: this.scrollContainer,
2424
2881
  isDragging: () => this.gesture?.isDragging ?? false,
2425
2882
  isAnimating: () => this.animation.isAnimating,
2426
- getAllowedIds: () => this.snaps.getAllowedIds(),
2883
+ getAllowedIds: () => this.allowedIdsBySize(),
2427
2884
  getActiveId: () => this.activeId,
2428
2885
  snapTo: (id) => {
2429
2886
  void this.snapTo(id);
@@ -2452,11 +2909,13 @@ var BottomSheetEngine = class {
2452
2909
  },
2453
2910
  newCycle: () => {
2454
2911
  this.newCycle();
2455
- }
2912
+ },
2913
+ isAnimating: () => this.animation.isAnimating,
2914
+ resyncAfterCancel: () => this.resyncAfterResize()
2456
2915
  }));
2457
2916
  if (this.lifecycle.closeOnEscape) {
2458
2917
  const onKey = (e) => {
2459
- if (this.destroyed) return;
2918
+ if (this.destroyed || e.defaultPrevented) return;
2460
2919
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2461
2920
  void this.close();
2462
2921
  }
@@ -2488,7 +2947,7 @@ var BottomSheetEngine = class {
2488
2947
  handle: this.handle,
2489
2948
  mode: this.mode,
2490
2949
  isDestroyed: () => this.destroyed,
2491
- getAllowedIds: () => this.snaps.getAllowedIds(),
2950
+ getAllowedIds: () => this.allowedIdsBySize(),
2492
2951
  getActiveId: () => this.activeId,
2493
2952
  snapTo: (id) => {
2494
2953
  void this.snapTo(id);
@@ -2509,6 +2968,7 @@ var BottomSheetEngine = class {
2509
2968
  });
2510
2969
  if (!target) return;
2511
2970
  const previousId = this.activeId;
2971
+ if (target.id === previousId && target.size === this.size) return;
2512
2972
  if (this.emitBeforeSnap(target, previousId)) {
2513
2973
  const restore = this.snaps.findById(previousId);
2514
2974
  if (restore) {
@@ -2517,6 +2977,7 @@ var BottomSheetEngine = class {
2517
2977
  return;
2518
2978
  }
2519
2979
  const previousSize = this.size;
2980
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
2520
2981
  this.scrollCache.cache(previousId, previousSize, target.size);
2521
2982
  this.activeId = target.id;
2522
2983
  void this.animation.animateTo(target.size, velocity).then(() => {
@@ -2525,9 +2986,10 @@ var BottomSheetEngine = class {
2525
2986
  this.updateAriaSlider();
2526
2987
  this.emit("snap", { id: target.id, size: target.size });
2527
2988
  this.haptic();
2528
- if (previousId === "closed" && target.size > 0) {
2989
+ if (previousSnapSize === 0 && target.size > 0) {
2529
2990
  this.emit("open", { id: target.id });
2530
2991
  this.handleOpen();
2992
+ notifyLinkedSheets(this.linkedSheets, this);
2531
2993
  }
2532
2994
  if (target.size === 0) {
2533
2995
  this.emit("close", void 0);
@@ -2553,6 +3015,9 @@ var BottomSheetEngine = class {
2553
3015
  if (this.scrimParent) {
2554
3016
  this.scrimParent.style.setProperty("--bs-size", `${size}px`);
2555
3017
  }
3018
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3019
+ this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3020
+ }
2556
3021
  }
2557
3022
  const progress = this.computeProgress(size);
2558
3023
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -2561,6 +3026,9 @@ var BottomSheetEngine = class {
2561
3026
  );
2562
3027
  if (progressChanged) {
2563
3028
  style.setProperty("--bs-progress", String(progress));
3029
+ if (this.anchorHost) {
3030
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3031
+ }
2564
3032
  }
2565
3033
  this.scrim.applyOpacity(progress, progressChanged);
2566
3034
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -2575,6 +3043,7 @@ var BottomSheetEngine = class {
2575
3043
  return Math.min(Math.max((size - min) / (max - min), 0), 1);
2576
3044
  }
2577
3045
  handleOpen() {
3046
+ sheetStack.promote(this.id);
2578
3047
  try {
2579
3048
  this.lifecycle.install();
2580
3049
  } catch (err) {
@@ -2588,10 +3057,30 @@ var BottomSheetEngine = class {
2588
3057
  });
2589
3058
  }
2590
3059
  }
3060
+ resyncAfterResize() {
3061
+ if (this.destroyed) return;
3062
+ const target = this.snaps.findById(this.activeId);
3063
+ if (!target) return;
3064
+ this.updateAriaSlider();
3065
+ this.emit("snap", { id: target.id, size: target.size });
3066
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3067
+ this.emit("open", { id: target.id });
3068
+ this.handleOpen();
3069
+ notifyLinkedSheets(this.linkedSheets, this);
3070
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3071
+ this.emit("close", void 0);
3072
+ this.handleClose();
3073
+ }
3074
+ }
2591
3075
  handleClose() {
2592
3076
  this.lifecycle.release();
3077
+ sheetStack.update();
2593
3078
  }
2594
3079
  };
3080
+
3081
+ // src/solid/index.tsx
3082
+ var _tmpl$ = /* @__PURE__ */ template(`<div class=bs-root><div class=bs-screen></div><section class=bs-sheet role=dialog><div class=bs-handle role=slider tabindex=0 aria-label="Resize sheet"></div><div class=bs-content tabindex=0 role=region aria-label="Sheet content"></div><span class=bs-sr-only role=status aria-live=polite>`);
3083
+ var _tmpl$2 = /* @__PURE__ */ template(`<div class=bs-backdrop>`);
2595
3084
  var BottomSheet = (props) => {
2596
3085
  let sheetEl;
2597
3086
  let handleEl;
@@ -2633,7 +3122,9 @@ var BottomSheet = (props) => {
2633
3122
  const sync = () => {
2634
3123
  if (!engine) return;
2635
3124
  const s = engine.state;
2636
- setState({ ...s });
3125
+ setState({
3126
+ ...s
3127
+ });
2637
3128
  props.onChange?.(s);
2638
3129
  };
2639
3130
  sync();
@@ -2658,50 +3149,54 @@ var BottomSheet = (props) => {
2658
3149
  engine = null;
2659
3150
  props.engineRef?.(null);
2660
3151
  });
3152
+ createEffect(on(() => props.snapPoints, (points, prev) => {
3153
+ if (!engine || prev === void 0) return;
3154
+ engine.setSnapPoints(points, props.allowed);
3155
+ }, {
3156
+ defer: true
3157
+ }));
3158
+ createEffect(on(() => props.allowed, (allowed, prev) => {
3159
+ if (!engine || prev === void 0) return;
3160
+ if (allowed) engine.setAllowed(allowed);
3161
+ }, {
3162
+ defer: true
3163
+ }));
2661
3164
  const showBackdrop = createMemo(() => props.backdrop !== false);
2662
3165
  const mode = createMemo(() => props.mode ?? "bottom");
2663
3166
  const ariaLabel = createMemo(() => props.ariaLabel ?? "Bottom sheet");
2664
- return /* @__PURE__ */ jsxs("div", { class: "bs-root", children: [
2665
- showBackdrop() ? /* @__PURE__ */ jsx("div", { class: "bs-backdrop", ref: (el) => backdropEl = el }) : null,
2666
- /* @__PURE__ */ jsx("div", { class: "bs-screen", ref: (el) => scrimEl = el, children: props.screen }),
2667
- /* @__PURE__ */ jsxs(
2668
- "section",
2669
- {
2670
- class: "bs-sheet",
2671
- ref: (el) => sheetEl = el,
2672
- "data-mode": mode(),
2673
- "data-active": state().activeId,
2674
- role: "dialog",
2675
- "aria-modal": props.focusTrap ? "true" : void 0,
2676
- "aria-label": ariaLabel(),
2677
- children: [
2678
- /* @__PURE__ */ jsx(
2679
- "div",
2680
- {
2681
- class: "bs-handle",
2682
- ref: (el) => handleEl = el,
2683
- role: "slider",
2684
- tabIndex: 0,
2685
- "aria-label": "Resize sheet",
2686
- children: props.header
2687
- }
2688
- ),
2689
- /* @__PURE__ */ jsx(
2690
- "div",
2691
- {
2692
- class: "bs-content",
2693
- ref: (el) => contentEl = el,
2694
- tabIndex: 0,
2695
- role: "region",
2696
- "aria-label": "Sheet content",
2697
- children: props.children
2698
- }
2699
- ),
2700
- /* @__PURE__ */ jsx("span", { class: "bs-sr-only", role: "status", "aria-live": "polite", children: state().activeId })
2701
- ]
2702
- }
2703
- )
2704
- ] });
3167
+ return (() => {
3168
+ var _el$ = _tmpl$(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.firstChild, _el$5 = _el$4.nextSibling, _el$6 = _el$5.nextSibling;
3169
+ insert(_el$, (() => {
3170
+ var _c$ = memo(() => !!showBackdrop());
3171
+ return () => _c$() ? (() => {
3172
+ var _el$7 = _tmpl$2();
3173
+ use((el) => backdropEl = el, _el$7);
3174
+ return _el$7;
3175
+ })() : null;
3176
+ })(), _el$2);
3177
+ use((el) => scrimEl = el, _el$2);
3178
+ insert(_el$2, () => props.screen);
3179
+ use((el) => sheetEl = el, _el$3);
3180
+ use((el) => handleEl = el, _el$4);
3181
+ insert(_el$4, () => props.header);
3182
+ use((el) => contentEl = el, _el$5);
3183
+ insert(_el$5, () => props.children);
3184
+ insert(_el$6, () => state().activeId);
3185
+ effect((_p$) => {
3186
+ var _v$ = mode(), _v$2 = state().activeId, _v$3 = props.focusTrap ? "true" : void 0, _v$4 = ariaLabel();
3187
+ _v$ !== _p$.e && setAttribute(_el$3, "data-mode", _p$.e = _v$);
3188
+ _v$2 !== _p$.t && setAttribute(_el$3, "data-active", _p$.t = _v$2);
3189
+ _v$3 !== _p$.a && setAttribute(_el$3, "aria-modal", _p$.a = _v$3);
3190
+ _v$4 !== _p$.o && setAttribute(_el$3, "aria-label", _p$.o = _v$4);
3191
+ return _p$;
3192
+ }, {
3193
+ e: void 0,
3194
+ t: void 0,
3195
+ a: void 0,
3196
+ o: void 0
3197
+ });
3198
+ return _el$;
3199
+ })();
2705
3200
  };
2706
3201
 
2707
3202
  export { BottomSheet };