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