@surdeddd/bottom-sheet 0.2.0 → 0.4.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-BJrH7qY3.d.cts} +105 -3
  3. package/dist/{BottomSheetEngine-Df9MwBbm.d.ts → BottomSheetEngine-L05jGZvU.d.ts} +105 -3
  4. package/dist/element.cjs +1170 -196
  5. package/dist/element.d.cts +29 -5
  6. package/dist/element.d.ts +29 -5
  7. package/dist/element.iife.global.js +2 -2
  8. package/dist/element.js +1170 -196
  9. package/dist/index.cjs +1051 -220
  10. package/dist/index.d.cts +18 -3
  11. package/dist/index.d.ts +18 -3
  12. package/dist/index.js +1049 -221
  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 +181 -37
  18. package/dist/overlay.d.cts +13 -0
  19. package/dist/overlay.d.ts +13 -0
  20. package/dist/overlay.js +181 -37
  21. package/dist/preact.d.cts +1 -1
  22. package/dist/preact.d.ts +1 -1
  23. package/dist/qwik.cjs +966 -195
  24. package/dist/qwik.d.cts +4 -2
  25. package/dist/qwik.d.ts +4 -2
  26. package/dist/qwik.js +967 -196
  27. package/dist/react.cjs +1242 -272
  28. package/dist/react.d.cts +47 -5
  29. package/dist/react.d.ts +47 -5
  30. package/dist/react.js +1242 -272
  31. package/dist/solid.cjs +992 -236
  32. package/dist/solid.d.cts +299 -4
  33. package/dist/solid.d.ts +299 -4
  34. package/dist/solid.js +993 -237
  35. package/dist/solid.source.jsx +3476 -0
  36. package/dist/styles.css +67 -66
  37. package/dist/svelte-core.cjs +3415 -0
  38. package/dist/svelte-core.js +3413 -0
  39. package/dist/svelte-src/BottomSheet.svelte +308 -0
  40. package/dist/svelte-src/index.js +2 -0
  41. package/dist/svelte.cjs +1034 -207
  42. package/dist/svelte.d.cts +121 -0
  43. package/dist/svelte.d.ts +28 -3
  44. package/dist/svelte.js +1035 -208
  45. package/dist/vue.cjs +1191 -205
  46. package/dist/vue.d.cts +127 -0
  47. package/dist/vue.d.ts +57 -4
  48. package/dist/vue.js +1192 -206
  49. package/package.json +105 -45
package/dist/qwik.cjs CHANGED
@@ -25,16 +25,52 @@ 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
  }
43
+ depthOf(id) {
44
+ const open = [];
45
+ for (const entry of this.entries) {
46
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
47
+ }
48
+ const idx = open.findIndex((e) => e.id === id);
49
+ if (idx === -1) return 0;
50
+ return open.length - 1 - idx;
51
+ }
31
52
  clear() {
32
53
  this.entries = [];
33
54
  }
34
55
  recompute() {
56
+ let top = null;
57
+ const open = [];
58
+ for (const entry of this.entries) {
59
+ if (!entry.isOpen || entry.isOpen()) {
60
+ top = entry;
61
+ open.push(entry);
62
+ }
63
+ }
64
+ if (!top && this.entries.length > 0) {
65
+ top = this.entries[this.entries.length - 1];
66
+ }
35
67
  this.entries.forEach((entry, i) => {
36
68
  entry.setZIndex(BASE_Z + i * STEP);
37
- entry.setIsTop(i === this.entries.length - 1);
69
+ entry.setIsTop(entry === top);
70
+ if (entry.setDepth) {
71
+ const openIdx = open.indexOf(entry);
72
+ entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
73
+ }
38
74
  });
39
75
  }
40
76
  };
@@ -131,7 +167,9 @@ var PERCENT = /^(-?\d+(?:\.\d+)?)%$/;
131
167
  var resolveSnap = (point, mode, measureFit) => {
132
168
  if (typeof point === "number") return Math.max(0, point);
133
169
  if (point === "full") return viewportSize(mode);
134
- if (point === "fit") return Math.max(0, measureFit?.() ?? 0);
170
+ if (point === "fit" || point === "content") {
171
+ return Math.max(0, measureFit?.() ?? 0);
172
+ }
135
173
  const pctMatch = point.match(PERCENT);
136
174
  if (pctMatch) {
137
175
  const pct = parseFloat(pctMatch[1]) / 100;
@@ -319,14 +357,17 @@ function installAutoCollapse(deps) {
319
357
  const fire = () => {
320
358
  if (deps.isDestroyed()) return;
321
359
  if (deps.isDragging()) return;
322
- const allowed = deps.getAllowedIds();
323
- const target = allowed.find((id) => {
360
+ const active = deps.resolveSnap(deps.getActiveId());
361
+ if (!active || active.size === 0) return;
362
+ let target = null;
363
+ for (const id of deps.getAllowedIds()) {
324
364
  const snap = deps.resolveSnap(id);
325
- return snap !== null && snap.size > 0;
326
- });
365
+ if (!snap || snap.size <= 0) continue;
366
+ if (!target || snap.size < target.size) target = snap;
367
+ }
327
368
  if (!target) return;
328
- if (target === deps.getActiveId()) return;
329
- deps.snapTo(target);
369
+ if (target.id === active.id || target.size >= active.size) return;
370
+ deps.snapTo(target.id);
330
371
  };
331
372
  const reset = () => {
332
373
  clearTimer();
@@ -337,6 +378,7 @@ function installAutoCollapse(deps) {
337
378
  }, ms);
338
379
  };
339
380
  const unsubscribe = deps.on("snap", () => reset());
381
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
340
382
  return () => {
341
383
  clearTimer();
342
384
  unsubscribe();
@@ -349,6 +391,7 @@ function notifyLinkedSheets(sheets, self) {
349
391
  for (const linked of sheets) {
350
392
  if (linked === self) continue;
351
393
  try {
394
+ if (linked.state.size === 0) continue;
352
395
  const ids = linked.getAllowedIds();
353
396
  const target = ids.find((id) => id !== "closed");
354
397
  if (target && target !== linked.state.activeId) {
@@ -363,6 +406,42 @@ function notifyLinkedSheets(sheets, self) {
363
406
  }
364
407
 
365
408
  // src/core/features/route.ts
409
+ var closers = [];
410
+ var handlerInstalled = false;
411
+ var suppress = 0;
412
+ var ensureHandler = () => {
413
+ if (handlerInstalled || typeof window === "undefined") return;
414
+ handlerInstalled = true;
415
+ window.addEventListener("popstate", () => {
416
+ if (suppress > 0) {
417
+ suppress -= 1;
418
+ return;
419
+ }
420
+ let target = null;
421
+ for (let i = closers.length - 1; i >= 0; i -= 1) {
422
+ const c = closers[i];
423
+ if (!c.isOpen()) continue;
424
+ if (!target) target = c;
425
+ if (c.isTop()) {
426
+ target = c;
427
+ break;
428
+ }
429
+ }
430
+ if (!target) return;
431
+ target.consumed = true;
432
+ const idx = closers.indexOf(target);
433
+ if (idx !== -1) closers.splice(idx, 1);
434
+ target.close();
435
+ });
436
+ };
437
+ var programmaticBack = () => {
438
+ suppress += 1;
439
+ try {
440
+ history.back();
441
+ } catch {
442
+ suppress = Math.max(0, suppress - 1);
443
+ }
444
+ };
366
445
  function installRoute(deps) {
367
446
  if (typeof window === "undefined") {
368
447
  return () => {
@@ -372,82 +451,81 @@ function installRoute(deps) {
372
451
  return () => {
373
452
  };
374
453
  }
375
- let routePushed = false;
376
- let closeOnBackPushed = false;
377
- let detachPopstate = null;
378
- const popOurMarker = (key, expected) => {
379
- const state = history.state;
380
- if (state && state[key] === expected) {
381
- try {
382
- history.back();
383
- } catch {
384
- }
454
+ let pushed = false;
455
+ let closer = null;
456
+ const teardownEntry = () => {
457
+ if (closer) {
458
+ const idx = closers.indexOf(closer);
459
+ if (idx !== -1) closers.splice(idx, 1);
460
+ }
461
+ const wasConsumed = closer?.consumed ?? false;
462
+ closer = null;
463
+ if (pushed && !wasConsumed) {
464
+ pushed = false;
465
+ programmaticBack();
466
+ } else {
467
+ pushed = false;
385
468
  }
386
469
  };
387
470
  const onOpen = () => {
388
- if (deps.routedTo && !detachPopstate) {
389
- const target = deps.routedTo;
390
- try {
391
- history.pushState({ __bsRouted: target }, "", target);
392
- routePushed = true;
393
- } catch {
394
- }
395
- const onPop = (event) => {
396
- if (deps.isDestroyed()) return;
397
- const state = event.state;
398
- if (state && state.__bsRouted === target && deps.getSize() > 0) {
399
- routePushed = false;
400
- void deps.close();
401
- }
402
- };
403
- window.addEventListener("popstate", onPop);
404
- detachPopstate = () => window.removeEventListener("popstate", onPop);
405
- } else if (deps.closeOnBack && !detachPopstate) {
406
- try {
471
+ if (closer) return;
472
+ closer = {
473
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
474
+ isTop: () => deps.isTopSheet(),
475
+ close: () => void deps.close(),
476
+ consumed: false
477
+ };
478
+ ensureHandler();
479
+ closers.push(closer);
480
+ try {
481
+ if (deps.routedTo !== void 0) {
482
+ history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
483
+ } else {
407
484
  history.pushState({ __bsSheet: deps.sheetId }, "");
408
- closeOnBackPushed = true;
409
- } catch {
410
485
  }
411
- const onPop = () => {
412
- if (deps.isDestroyed()) return;
413
- if (deps.getSize() > 0 && deps.isTopSheet()) {
414
- const state = history.state;
415
- if (state?.__bsSheet !== deps.sheetId) closeOnBackPushed = false;
416
- void deps.close();
417
- }
418
- };
419
- window.addEventListener("popstate", onPop);
420
- detachPopstate = () => window.removeEventListener("popstate", onPop);
421
- }
422
- };
423
- const onClose = () => {
424
- detachPopstate?.();
425
- detachPopstate = null;
426
- if (routePushed) {
427
- routePushed = false;
428
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
429
- }
430
- if (closeOnBackPushed) {
431
- closeOnBackPushed = false;
432
- popOurMarker("__bsSheet", deps.sheetId);
486
+ pushed = true;
487
+ } catch {
488
+ pushed = false;
433
489
  }
434
490
  };
435
491
  const unsubscribeOpen = deps.on("open", onOpen);
436
- const unsubscribeClose = deps.on("close", onClose);
492
+ const unsubscribeClose = deps.on("close", teardownEntry);
437
493
  if (deps.getSize() > 0) onOpen();
438
494
  return () => {
439
495
  unsubscribeOpen();
440
496
  unsubscribeClose();
441
- detachPopstate?.();
442
- detachPopstate = null;
443
- if (routePushed) {
444
- routePushed = false;
445
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
446
- }
447
- if (closeOnBackPushed) {
448
- closeOnBackPushed = false;
449
- popOurMarker("__bsSheet", deps.sheetId);
450
- }
497
+ teardownEntry();
498
+ };
499
+ }
500
+ function installRouteChange(deps) {
501
+ if (typeof window === "undefined" || typeof history === "undefined") {
502
+ return () => {
503
+ };
504
+ }
505
+ const onChange = () => {
506
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
507
+ void deps.close();
508
+ };
509
+ const wrap = (key) => {
510
+ const original = history[key];
511
+ const patched = function(...args) {
512
+ original.apply(this, args);
513
+ onChange();
514
+ };
515
+ history[key] = patched;
516
+ return () => {
517
+ if (history[key] === patched) {
518
+ history[key] = original;
519
+ }
520
+ };
521
+ };
522
+ window.addEventListener("popstate", onChange);
523
+ const restorePush = wrap("pushState");
524
+ const restoreReplace = wrap("replaceState");
525
+ return () => {
526
+ window.removeEventListener("popstate", onChange);
527
+ restorePush();
528
+ restoreReplace();
451
529
  };
452
530
  }
453
531
 
@@ -549,13 +627,17 @@ function installResizeObserver(deps) {
549
627
  if (viewportSize2 > 0 && viewportSize2 < max) {
550
628
  deps.setMaxAxisSize(viewportSize2);
551
629
  }
630
+ const wasAnimating = deps.isAnimating?.() ?? false;
552
631
  deps.cancelInFlight();
553
632
  deps.newCycle();
554
633
  const current = deps.resolveActiveSnap();
555
634
  const newMax = deps.getMaxAxisSize();
556
635
  const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
557
636
  deps.setSize(targetSize);
558
- if (current && !deps.isDragging()) deps.applySize(targetSize);
637
+ if (current && !deps.isDragging()) {
638
+ deps.applySize(targetSize);
639
+ if (wasAnimating) deps.resyncAfterCancel?.();
640
+ }
559
641
  };
560
642
  let resizeObserver = null;
561
643
  if (typeof ResizeObserver !== "undefined") {
@@ -670,8 +752,88 @@ function devWarn(...args) {
670
752
  }
671
753
  }
672
754
 
755
+ // src/core/primitives/overlay-position.ts
756
+ function resolveSheetAnchoredStyle(mode, position, inset) {
757
+ const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
758
+ switch (mode) {
759
+ case "bottom":
760
+ if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
761
+ if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
762
+ return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
763
+ case "top":
764
+ if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
765
+ if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
766
+ return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
767
+ case "left":
768
+ if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
769
+ if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
770
+ return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
771
+ case "right":
772
+ if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
773
+ if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
774
+ return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
775
+ }
776
+ }
777
+ function applyOverlayPosition(ws, mode, position, inset) {
778
+ switch (position) {
779
+ case "top-left":
780
+ ws.top = inset;
781
+ ws.left = inset;
782
+ break;
783
+ case "top-center":
784
+ ws.top = inset;
785
+ ws.left = "50%";
786
+ ws.transform = "translateX(-50%)";
787
+ break;
788
+ case "top-right":
789
+ ws.top = inset;
790
+ ws.right = inset;
791
+ break;
792
+ case "center-left":
793
+ ws.top = "50%";
794
+ ws.left = inset;
795
+ ws.transform = "translateY(-50%)";
796
+ break;
797
+ case "center":
798
+ ws.top = "50%";
799
+ ws.left = "50%";
800
+ ws.transform = "translate(-50%, -50%)";
801
+ break;
802
+ case "center-right":
803
+ ws.top = "50%";
804
+ ws.right = inset;
805
+ ws.transform = "translateY(-50%)";
806
+ break;
807
+ case "bottom-left":
808
+ ws.bottom = inset;
809
+ ws.left = inset;
810
+ break;
811
+ case "bottom-center":
812
+ ws.bottom = inset;
813
+ ws.left = "50%";
814
+ ws.transform = "translateX(-50%)";
815
+ break;
816
+ case "bottom-right":
817
+ ws.bottom = inset;
818
+ ws.right = inset;
819
+ break;
820
+ case "sheet-top-left":
821
+ case "sheet-top-center":
822
+ case "sheet-top-right": {
823
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
824
+ if (style.top !== void 0) ws.top = style.top;
825
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
826
+ if (style.left !== void 0) ws.left = style.left;
827
+ if (style.right !== void 0) ws.right = style.right;
828
+ if (style.transform !== void 0) ws.transform = style.transform;
829
+ break;
830
+ }
831
+ }
832
+ }
833
+
673
834
  // src/core/controllers/scrim-controller.ts
674
835
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
836
+ var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
675
837
  var ScrimController = class {
676
838
  constructor(deps, opts) {
677
839
  __publicField(this, "screenComponent");
@@ -682,6 +844,8 @@ var ScrimController = class {
682
844
  __publicField(this, "getAllowedIds");
683
845
  __publicField(this, "getResolvedSnaps");
684
846
  __publicField(this, "snapToFn");
847
+ __publicField(this, "closeFn");
848
+ __publicField(this, "scrimInteractive", false);
685
849
  __publicField(this, "scrimMode");
686
850
  __publicField(this, "backdropRange");
687
851
  __publicField(this, "screenRange");
@@ -702,14 +866,17 @@ var ScrimController = class {
702
866
  this.getAllowedIds = deps.getAllowedIds;
703
867
  this.getResolvedSnaps = deps.getResolvedSnaps;
704
868
  this.snapToFn = deps.snapTo;
869
+ this.closeFn = deps.close;
705
870
  this.scrimMode = opts.scrimMode ?? "full";
706
871
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
707
872
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
708
873
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
709
874
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
875
+ const explicitColor = opts.scrimColor ?? preset?.color;
876
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
710
877
  if (this.screenComponent) {
711
878
  this.applyScrimStyles({
712
- color: opts.scrimColor ?? preset?.color,
879
+ color: effectiveColor,
713
880
  blur: opts.scrimBlur ?? preset?.blur,
714
881
  interactive: opts.scrimInteractive ?? preset?.interactive ?? false
715
882
  });
@@ -843,11 +1010,15 @@ var ScrimController = class {
843
1010
  }
844
1011
  const onClick = () => {
845
1012
  if (this.isDestroyed()) return;
1013
+ if (this.closeFn) {
1014
+ this.closeFn();
1015
+ return;
1016
+ }
846
1017
  const allowed = this.getAllowedIds();
847
1018
  const snaps = this.getResolvedSnaps();
848
1019
  const fallback = allowed.find((id) => {
849
1020
  const snap = findById(id, snaps);
850
- return snap !== null && snap.size > 0;
1021
+ return snap !== null && snap.size === 0;
851
1022
  });
852
1023
  if (fallback) this.snapToFn(fallback);
853
1024
  };
@@ -864,6 +1035,7 @@ var ScrimController = class {
864
1035
  if (this.isDestroyed()) return;
865
1036
  if (enabled === this.scrimEnabled) return;
866
1037
  this.scrimEnabled = enabled;
1038
+ this.invalidateOpacityCache();
867
1039
  if (!enabled) {
868
1040
  this.savedScrimRanges = {
869
1041
  screen: this.screenRange,
@@ -887,7 +1059,12 @@ var ScrimController = class {
887
1059
  blur: cfg.blur ?? null,
888
1060
  interactive: cfg.interactive
889
1061
  });
890
- this.screenRange = cfg.range;
1062
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1063
+ this.savedScrimRanges = { screen: cfg.range, backdrop: cfg.range };
1064
+ } else {
1065
+ this.screenRange = cfg.range;
1066
+ this.backdropRange = cfg.range;
1067
+ }
891
1068
  }
892
1069
  const noop = () => {
893
1070
  };
@@ -937,59 +1114,7 @@ var ScrimController = class {
937
1114
  wrapper.className = "bs-scrim-overlay";
938
1115
  const ws = wrapper.style;
939
1116
  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
- }
1117
+ applyOverlayPosition(ws, this.mode, position, inset);
993
1118
  if (interactive) {
994
1119
  ws.pointerEvents = "auto";
995
1120
  }
@@ -1048,6 +1173,7 @@ var ScrimController = class {
1048
1173
  }
1049
1174
  }
1050
1175
  if (opts.interactive !== void 0) {
1176
+ this.scrimInteractive = opts.interactive;
1051
1177
  s.pointerEvents = opts.interactive ? "auto" : "none";
1052
1178
  }
1053
1179
  }
@@ -1062,38 +1188,9 @@ var ScrimController = class {
1062
1188
  const s = this.screenComponent.style;
1063
1189
  s.position = "fixed";
1064
1190
  s.inset = insetByMode[this.mode];
1065
- s.pointerEvents = "none";
1191
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1066
1192
  }
1067
1193
  };
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
1194
 
1098
1195
  // src/core/animation/animation.ts
1099
1196
  var easeOutBack = (t) => {
@@ -1145,6 +1242,10 @@ var tween = ({
1145
1242
  promise
1146
1243
  };
1147
1244
  };
1245
+ var prefersReducedMotion = () => {
1246
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1247
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1248
+ };
1148
1249
 
1149
1250
  // src/core/animation/spring.ts
1150
1251
  var DEFAULT_SPRING = {
@@ -1175,14 +1276,20 @@ var runSpring = ({
1175
1276
  if (cancelled) return;
1176
1277
  const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
1177
1278
  lastTime = now;
1178
- const subDt = Math.min(dt, 1 / 240);
1179
- let stepsLeft = Math.max(1, Math.ceil(dt / subDt));
1279
+ const steps = Math.max(1, Math.ceil(dt * 240));
1280
+ const subDt = dt / steps;
1281
+ let stepsLeft = steps;
1180
1282
  while (stepsLeft-- > 0) {
1181
1283
  const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1182
1284
  const accel = force / cfg.mass;
1183
1285
  v += accel * subDt;
1184
1286
  x += v * subDt;
1185
1287
  }
1288
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1289
+ onUpdate(to, 0);
1290
+ resolve();
1291
+ return;
1292
+ }
1186
1293
  onUpdate(x, v);
1187
1294
  const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1188
1295
  if (isAtRest) {
@@ -1357,9 +1464,13 @@ var isVisible = (el) => {
1357
1464
  var focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
1358
1465
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1359
1466
  );
1467
+ var trapStack = [];
1360
1468
  var installFocusTrap = (container, options = {}) => {
1361
1469
  if (typeof document === "undefined") return () => {
1362
1470
  };
1471
+ const token = {};
1472
+ trapStack.push(token);
1473
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1363
1474
  const previouslyFocused = document.activeElement;
1364
1475
  const focusInitial = () => {
1365
1476
  const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
@@ -1367,6 +1478,7 @@ var installFocusTrap = (container, options = {}) => {
1367
1478
  };
1368
1479
  focusInitial();
1369
1480
  const handleKey = (e) => {
1481
+ if (!isActive()) return;
1370
1482
  if (e.key === "Escape" && options.onEscape) {
1371
1483
  options.onEscape();
1372
1484
  return;
@@ -1389,6 +1501,7 @@ var installFocusTrap = (container, options = {}) => {
1389
1501
  }
1390
1502
  };
1391
1503
  const handleFocusIn = (e) => {
1504
+ if (!isActive()) return;
1392
1505
  const target = e.target;
1393
1506
  if (target && !container.contains(target)) {
1394
1507
  const list = focusables(container);
@@ -1401,6 +1514,8 @@ var installFocusTrap = (container, options = {}) => {
1401
1514
  return () => {
1402
1515
  if (released) return;
1403
1516
  released = true;
1517
+ const idx = trapStack.indexOf(token);
1518
+ if (idx !== -1) trapStack.splice(idx, 1);
1404
1519
  document.removeEventListener("keydown", handleKey, true);
1405
1520
  document.removeEventListener("focusin", handleFocusIn, true);
1406
1521
  if (previouslyFocused && document.contains(previouslyFocused)) {
@@ -1456,11 +1571,17 @@ function createInertSiblings(rootProvider) {
1456
1571
  return {
1457
1572
  apply() {
1458
1573
  if (typeof document === "undefined") return;
1459
- const root = rootProvider();
1460
- const parent = root.parentElement ?? document.body;
1574
+ let top = rootProvider();
1575
+ while (top.parentElement && top.parentElement !== document.body) {
1576
+ top = top.parentElement;
1577
+ }
1578
+ const parent = top.parentElement ?? document.body;
1461
1579
  for (const child of Array.from(parent.children)) {
1462
- if (child === root) continue;
1580
+ if (child === top) continue;
1463
1581
  if (child.hasAttribute("inert")) continue;
1582
+ if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1583
+ continue;
1584
+ }
1464
1585
  child.setAttribute("inert", "");
1465
1586
  tracked.push(child);
1466
1587
  }
@@ -1476,7 +1597,6 @@ function createInertSiblings(rootProvider) {
1476
1597
  var LifecycleController = class {
1477
1598
  constructor(deps, opts) {
1478
1599
  __publicField(this, "element");
1479
- __publicField(this, "closeFn");
1480
1600
  __publicField(this, "focusTrapEnabled");
1481
1601
  __publicField(this, "initialFocus");
1482
1602
  __publicField(this, "closeOnEscape");
@@ -1490,7 +1610,6 @@ var LifecycleController = class {
1490
1610
  __publicField(this, "installed", false);
1491
1611
  __publicField(this, "destroyed", false);
1492
1612
  this.element = deps.element;
1493
- this.closeFn = deps.close;
1494
1613
  this.focusTrapEnabled = opts.focusTrap ?? false;
1495
1614
  this.initialFocus = opts.initialFocus;
1496
1615
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1500,6 +1619,9 @@ var LifecycleController = class {
1500
1619
  this.returnFocus = opts.returnFocus;
1501
1620
  this.inertSiblings = createInertSiblings(() => this.element);
1502
1621
  }
1622
+ get isInstalled() {
1623
+ return this.installed;
1624
+ }
1503
1625
  install() {
1504
1626
  if (this.destroyed) return;
1505
1627
  this.installed = true;
@@ -1508,8 +1630,7 @@ var LifecycleController = class {
1508
1630
  }
1509
1631
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1510
1632
  this.releaseFocusTrap = installFocusTrap(this.element, {
1511
- initialFocus: this.initialFocus,
1512
- onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
1633
+ initialFocus: this.initialFocus
1513
1634
  });
1514
1635
  }
1515
1636
  if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
@@ -1529,7 +1650,7 @@ var LifecycleController = class {
1529
1650
  this.releaseScrollLock = null;
1530
1651
  this.inertSiblings.remove();
1531
1652
  if (this.returnFocus) {
1532
- const target = typeof this.returnFocus === "function" ? this.returnFocus() : this.returnFocus;
1653
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1533
1654
  target?.focus?.();
1534
1655
  }
1535
1656
  }
@@ -1663,10 +1784,11 @@ var installGestures = (handle, mode, callbacks) => {
1663
1784
  };
1664
1785
 
1665
1786
  // src/core/primitives/rubber-band.ts
1666
- var RUBBER_BAND_FACTOR = 0.55;
1667
1787
  function rubberBand(overshoot, maxAxisSize) {
1668
1788
  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));
1789
+ if (cap <= 0) return 0;
1790
+ const abs = Math.abs(overshoot);
1791
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1670
1792
  }
1671
1793
 
1672
1794
  // src/core/features/soft-keyboard.ts
@@ -1695,6 +1817,7 @@ var GestureController = class {
1695
1817
  __publicField(this, "dragStartSize", 0);
1696
1818
  __publicField(this, "currentPointerKind", "touch");
1697
1819
  __publicField(this, "keyboardDismissed", false);
1820
+ __publicField(this, "dragSuppressed", false);
1698
1821
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
1699
1822
  this.deps = deps;
1700
1823
  }
@@ -1705,6 +1828,11 @@ var GestureController = class {
1705
1828
  if (this.detach) return this.detach;
1706
1829
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
1707
1830
  onStart: (_coord, kind) => {
1831
+ if (this.deps.getDisableDrag?.()) {
1832
+ this.dragSuppressed = true;
1833
+ return;
1834
+ }
1835
+ this.dragSuppressed = false;
1708
1836
  this.deps.cancelAnimation();
1709
1837
  this.isDragging_ = true;
1710
1838
  this.dragStartSize = this.deps.getDragContext().size;
@@ -1715,6 +1843,7 @@ var GestureController = class {
1715
1843
  this.deps.emit("dragstart", { size: this.dragStartSize });
1716
1844
  },
1717
1845
  onMove: (delta) => {
1846
+ if (this.dragSuppressed) return;
1718
1847
  const ctx = this.deps.getDragContext();
1719
1848
  const { min, max } = ctx.range;
1720
1849
  const maxAxis = ctx.maxAxisSize;
@@ -1738,6 +1867,10 @@ var GestureController = class {
1738
1867
  }
1739
1868
  },
1740
1869
  onEnd: (delta, velocity, kind) => {
1870
+ if (this.dragSuppressed) {
1871
+ this.dragSuppressed = false;
1872
+ return;
1873
+ }
1741
1874
  this.isDragging_ = false;
1742
1875
  this.keyboardDismissed = false;
1743
1876
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -1748,9 +1881,17 @@ var GestureController = class {
1748
1881
  this.deps.settleAfterDrag(delta, velocity, kind);
1749
1882
  },
1750
1883
  onCancel: () => {
1884
+ if (this.dragSuppressed) {
1885
+ this.dragSuppressed = false;
1886
+ return;
1887
+ }
1751
1888
  this.isDragging_ = false;
1752
1889
  this.keyboardDismissed = false;
1753
1890
  this.deps.getRoot()?.removeAttribute("data-dragging");
1891
+ this.deps.emit("dragend", {
1892
+ size: this.deps.getDragContext().size,
1893
+ velocity: 0
1894
+ });
1754
1895
  const restoreTo = this.dragStartSize;
1755
1896
  void this.deps.animateTo(restoreTo, 0);
1756
1897
  }
@@ -1824,6 +1965,287 @@ function createScrollCache(deps) {
1824
1965
  };
1825
1966
  }
1826
1967
 
1968
+ // src/core/primitives/anchor-animations.ts
1969
+ var DEFAULT_DURATION3 = 200;
1970
+ var DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
1971
+ var PRESET_FRAMES = {
1972
+ fade: {
1973
+ enter: [{ opacity: 0 }, { opacity: 1 }],
1974
+ exit: [{ opacity: 1 }, { opacity: 0 }]
1975
+ },
1976
+ scale: {
1977
+ enter: [
1978
+ { opacity: 0, transform: "scale(0.85)" },
1979
+ { opacity: 1, transform: "scale(1)" }
1980
+ ],
1981
+ exit: [
1982
+ { opacity: 1, transform: "scale(1)" },
1983
+ { opacity: 0, transform: "scale(0.85)" }
1984
+ ]
1985
+ },
1986
+ slide: {
1987
+ enter: [
1988
+ { opacity: 0, transform: "translateY(10px)" },
1989
+ { opacity: 1, transform: "translateY(0)" }
1990
+ ],
1991
+ exit: [
1992
+ { opacity: 1, transform: "translateY(0)" },
1993
+ { opacity: 0, transform: "translateY(10px)" }
1994
+ ]
1995
+ },
1996
+ pop: {
1997
+ enter: [
1998
+ { opacity: 0, transform: "scale(0.6)", offset: 0 },
1999
+ { opacity: 1, transform: "scale(1.06)", offset: 0.7 },
2000
+ { opacity: 1, transform: "scale(1)", offset: 1 }
2001
+ ],
2002
+ exit: [
2003
+ { opacity: 1, transform: "scale(1)" },
2004
+ { opacity: 0, transform: "scale(0.6)" }
2005
+ ]
2006
+ }
2007
+ };
2008
+ var INSTANT = {
2009
+ finished: Promise.resolve(),
2010
+ cancel: () => {
2011
+ }
2012
+ };
2013
+ function resolveTransitionFrames(spec, phase) {
2014
+ const fallback = { frames: null, duration: 0, easing: DEFAULT_EASING };
2015
+ if (spec === "none") return fallback;
2016
+ if (spec === void 0 || typeof spec === "string") {
2017
+ const preset2 = PRESET_FRAMES[spec ?? "fade"];
2018
+ return {
2019
+ frames: preset2[phase],
2020
+ duration: DEFAULT_DURATION3,
2021
+ easing: DEFAULT_EASING
2022
+ };
2023
+ }
2024
+ const custom = phase === "enter" ? spec.enter : spec.exit;
2025
+ if (custom) {
2026
+ return {
2027
+ frames: custom,
2028
+ duration: spec.duration ?? DEFAULT_DURATION3,
2029
+ easing: spec.easing ?? DEFAULT_EASING
2030
+ };
2031
+ }
2032
+ if (spec.preset === "none") return fallback;
2033
+ const preset = PRESET_FRAMES[spec.preset ?? "fade"];
2034
+ return {
2035
+ frames: preset[phase],
2036
+ duration: spec.duration ?? DEFAULT_DURATION3,
2037
+ easing: spec.easing ?? DEFAULT_EASING
2038
+ };
2039
+ }
2040
+ function runAnchorTransition(el, spec, phase) {
2041
+ const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
2042
+ if (respectReduced && prefersReducedMotion()) return INSTANT;
2043
+ const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
2044
+ if (!frames || duration <= 0) return INSTANT;
2045
+ if (typeof el.animate !== "function") return INSTANT;
2046
+ const animation = el.animate(frames, {
2047
+ duration,
2048
+ easing,
2049
+ fill: "both"
2050
+ });
2051
+ const finished = animation.finished.then(() => void 0).catch(() => void 0);
2052
+ return {
2053
+ finished,
2054
+ cancel: () => {
2055
+ try {
2056
+ animation.cancel();
2057
+ } catch {
2058
+ return;
2059
+ }
2060
+ }
2061
+ };
2062
+ }
2063
+
2064
+ // src/core/features/sheet-anchors.ts
2065
+ function attachAnchor(deps, opts) {
2066
+ const doc = deps.host.ownerDocument;
2067
+ const wrapper = doc.createElement("div");
2068
+ wrapper.className = "bs-anchor";
2069
+ const interactive = opts.interactive ?? true;
2070
+ const position = opts.position ?? "sheet-top-right";
2071
+ const isDock = position === "dock-bottom" || position === "dock-top";
2072
+ const ws = wrapper.style;
2073
+ ws.position = "fixed";
2074
+ if (isDock) {
2075
+ ws.left = "0";
2076
+ ws.right = "0";
2077
+ if (position === "dock-bottom") {
2078
+ ws.bottom = "0";
2079
+ } else {
2080
+ ws.top = "0";
2081
+ }
2082
+ } else {
2083
+ applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2084
+ }
2085
+ if (opts.fadeRange) {
2086
+ const [r0, r1] = opts.fadeRange;
2087
+ const span = Math.max(r1 - r0, 1e-4);
2088
+ ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
2089
+ }
2090
+ wrapper.appendChild(opts.element);
2091
+ deps.host.appendChild(wrapper);
2092
+ let visible = null;
2093
+ let inFlight = null;
2094
+ const shouldShow = (state) => {
2095
+ if (typeof opts.showOn === "function") return opts.showOn(state);
2096
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2097
+ return isDock || state.size > 0;
2098
+ };
2099
+ const applyVisibility = (next, animate) => {
2100
+ if (next === visible) return;
2101
+ visible = next;
2102
+ inFlight?.cancel();
2103
+ inFlight = null;
2104
+ wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
2105
+ if (next) {
2106
+ wrapper.style.visibility = "";
2107
+ if (animate) {
2108
+ inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
2109
+ }
2110
+ return;
2111
+ }
2112
+ if (!animate) {
2113
+ wrapper.style.visibility = "hidden";
2114
+ return;
2115
+ }
2116
+ const handle = runAnchorTransition(opts.element, opts.animation, "exit");
2117
+ inFlight = handle;
2118
+ void handle.finished.then(() => {
2119
+ if (inFlight === handle && visible === false) {
2120
+ wrapper.style.visibility = "hidden";
2121
+ }
2122
+ });
2123
+ };
2124
+ const evaluate = (animate) => {
2125
+ if (deps.isDestroyed()) return;
2126
+ applyVisibility(shouldShow(deps.getState()), animate);
2127
+ };
2128
+ const offs = [
2129
+ deps.on("snap", () => evaluate(true)),
2130
+ deps.on("open", () => evaluate(true)),
2131
+ deps.on("close", () => evaluate(true))
2132
+ ];
2133
+ evaluate(false);
2134
+ return {
2135
+ wrapper,
2136
+ detach: () => {
2137
+ offs.forEach((off) => off());
2138
+ inFlight?.cancel();
2139
+ inFlight = null;
2140
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
2141
+ },
2142
+ syncZ: (z) => {
2143
+ wrapper.style.zIndex = String(z);
2144
+ }
2145
+ };
2146
+ }
2147
+
2148
+ // src/core/features/scrim-stages.ts
2149
+ var matchesId = (def, activeId) => {
2150
+ if (def.for === void 0) return false;
2151
+ return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
2152
+ };
2153
+ var matchesRange = (def, progress) => {
2154
+ if (!def.forRange) return false;
2155
+ const [from, to] = def.forRange;
2156
+ return progress >= from && progress <= to;
2157
+ };
2158
+ function installScrimStages(deps, opts) {
2159
+ const doc = deps.host.ownerDocument;
2160
+ const entries = opts.stages.map((def) => {
2161
+ const wrapper = doc.createElement("div");
2162
+ wrapper.className = "bs-scrim-stage";
2163
+ const ws = wrapper.style;
2164
+ ws.position = "absolute";
2165
+ applyOverlayPosition(
2166
+ ws,
2167
+ deps.mode,
2168
+ def.position ?? opts.position ?? "center",
2169
+ def.inset ?? opts.inset ?? "16px"
2170
+ );
2171
+ ws.visibility = "hidden";
2172
+ ws.pointerEvents = "none";
2173
+ wrapper.appendChild(def.element);
2174
+ deps.host.appendChild(wrapper);
2175
+ return { def, wrapper, inFlight: null };
2176
+ });
2177
+ let active = null;
2178
+ const resolveActive = () => {
2179
+ const state = deps.getState();
2180
+ if (state.size === 0) {
2181
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2182
+ }
2183
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2184
+ };
2185
+ const hide = (entry, animate) => {
2186
+ entry.inFlight?.cancel();
2187
+ entry.wrapper.style.pointerEvents = "none";
2188
+ if (!animate) {
2189
+ entry.inFlight = null;
2190
+ entry.wrapper.style.visibility = "hidden";
2191
+ return;
2192
+ }
2193
+ const handle = runAnchorTransition(
2194
+ entry.def.element,
2195
+ entry.def.animation ?? opts.animation,
2196
+ "exit"
2197
+ );
2198
+ entry.inFlight = handle;
2199
+ void handle.finished.then(() => {
2200
+ if (entry.inFlight === handle && active !== entry) {
2201
+ entry.wrapper.style.visibility = "hidden";
2202
+ }
2203
+ });
2204
+ };
2205
+ const show = (entry, animate) => {
2206
+ entry.inFlight?.cancel();
2207
+ entry.inFlight = null;
2208
+ entry.wrapper.style.visibility = "";
2209
+ entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
2210
+ if (animate) {
2211
+ entry.inFlight = runAnchorTransition(
2212
+ entry.def.element,
2213
+ entry.def.animation ?? opts.animation,
2214
+ "enter"
2215
+ );
2216
+ }
2217
+ };
2218
+ const update = (animate) => {
2219
+ if (deps.isDestroyed()) return;
2220
+ const next = resolveActive();
2221
+ if (next === active) return;
2222
+ const prev = active;
2223
+ active = next;
2224
+ if (prev) hide(prev, animate);
2225
+ if (next) show(next, animate);
2226
+ };
2227
+ const offs = [
2228
+ deps.on("snap", () => update(true)),
2229
+ deps.on("open", () => update(true)),
2230
+ deps.on("close", () => update(true))
2231
+ ];
2232
+ if (entries.some((e) => e.def.forRange)) {
2233
+ offs.push(deps.on("progress", () => update(true)));
2234
+ }
2235
+ update(false);
2236
+ return () => {
2237
+ offs.forEach((off) => off());
2238
+ for (const entry of entries) {
2239
+ entry.inFlight?.cancel();
2240
+ entry.inFlight = null;
2241
+ if (entry.wrapper.parentNode) {
2242
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2243
+ }
2244
+ }
2245
+ active = null;
2246
+ };
2247
+ }
2248
+
1827
2249
  // src/core/primitives/engine-options.ts
1828
2250
  var DEFAULT_FLICK_VELOCITY = 0.65;
1829
2251
  var DEFAULT_DRAG_THRESHOLD = 18;
@@ -1842,6 +2264,12 @@ function resolveEngineOptions(opts, extras) {
1842
2264
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
1843
2265
  rubberBandEnabled: opts.rubberBand ?? true,
1844
2266
  closeOnBack: opts.closeOnBack ?? false,
2267
+ persistent: opts.persistent ?? false,
2268
+ disableClose: opts.disableClose ?? false,
2269
+ disableDrag: opts.disableDrag ?? false,
2270
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2271
+ radius: opts.radius,
2272
+ maxHeight: opts.maxHeight,
1845
2273
  initialAllowed,
1846
2274
  initialId,
1847
2275
  scrim: {
@@ -1868,7 +2296,8 @@ function resolveEngineOptions(opts, extras) {
1868
2296
  closeOnEscape: opts.closeOnEscape,
1869
2297
  lockBodyScroll: opts.lockBodyScroll,
1870
2298
  inertSiblings: opts.inertSiblings,
1871
- shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2299
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2300
+ returnFocus: opts.returnFocusTo
1872
2301
  }
1873
2302
  };
1874
2303
  }
@@ -1913,6 +2342,11 @@ var BottomSheetEngine = class {
1913
2342
  __publicField(this, "lifecycle");
1914
2343
  __publicField(this, "closeOnBack");
1915
2344
  __publicField(this, "routedTo");
2345
+ __publicField(this, "persistent");
2346
+ __publicField(this, "disableCloseFlag");
2347
+ __publicField(this, "disableDragFlag");
2348
+ __publicField(this, "closeOnRouteChange");
2349
+ __publicField(this, "maxHeightCap");
1916
2350
  __publicField(this, "snapPointsRaw");
1917
2351
  __publicField(this, "snaps");
1918
2352
  __publicField(this, "activeId");
@@ -1925,6 +2359,7 @@ var BottomSheetEngine = class {
1925
2359
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
1926
2360
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
1927
2361
  __publicField(this, "isTopSheet", true);
2362
+ __publicField(this, "opening", false);
1928
2363
  __publicField(this, "progressPayload", {
1929
2364
  value: 0,
1930
2365
  size: 0
@@ -1940,6 +2375,12 @@ var BottomSheetEngine = class {
1940
2375
  __publicField(this, "linkedSheets", []);
1941
2376
  __publicField(this, "scrollCache");
1942
2377
  __publicField(this, "teardowns", new TeardownStack());
2378
+ __publicField(this, "anchors", []);
2379
+ __publicField(this, "anchorHost", null);
2380
+ __publicField(this, "stackZ", 100);
2381
+ __publicField(this, "detachScrimStages", null);
2382
+ __publicField(this, "stackEffectEnabled", false);
2383
+ __publicField(this, "stackEffectPrimed", false);
1943
2384
  __publicField(this, "transformTemplate");
1944
2385
  const resolved = resolveEngineOptions(opts, {
1945
2386
  shouldApplyInertSiblings: () => {
@@ -1973,6 +2414,10 @@ var BottomSheetEngine = class {
1973
2414
  getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
1974
2415
  snapTo: (id) => {
1975
2416
  void this.snapTo(id);
2417
+ },
2418
+ close: () => {
2419
+ if (!this.canDismiss()) return;
2420
+ void this.close("backdrop");
1976
2421
  }
1977
2422
  },
1978
2423
  resolved.scrim
@@ -1996,6 +2441,11 @@ var BottomSheetEngine = class {
1996
2441
  );
1997
2442
  this.closeOnBack = resolved.closeOnBack;
1998
2443
  this.routedTo = opts.routedTo;
2444
+ this.persistent = resolved.persistent;
2445
+ this.disableCloseFlag = resolved.disableClose;
2446
+ this.disableDragFlag = resolved.disableDrag;
2447
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2448
+ this.stackEffectEnabled = opts.stackEffect ?? false;
1999
2449
  this.snapPointsRaw = opts.snapPoints;
2000
2450
  this.persistKey = opts.persistKey;
2001
2451
  if (opts.linkedSheets) {
@@ -2008,7 +2458,7 @@ var BottomSheetEngine = class {
2008
2458
  this.snapPointsRaw,
2009
2459
  resolved.initialAllowed,
2010
2460
  this.mode,
2011
- () => this.handle.offsetHeight,
2461
+ () => this.measureFitSize(),
2012
2462
  (maxAxisSize) => {
2013
2463
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2014
2464
  }
@@ -2042,6 +2492,19 @@ var BottomSheetEngine = class {
2042
2492
  io.observe(this.element);
2043
2493
  this.teardowns.add(() => io.disconnect());
2044
2494
  }
2495
+ this.installFitObserver();
2496
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2497
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2498
+ this.applyAutoAriaLabelledBy();
2499
+ if (this.closeOnRouteChange) {
2500
+ this.teardowns.add(
2501
+ installRouteChange({
2502
+ isDestroyed: () => this.destroyed,
2503
+ getSize: () => this.size,
2504
+ close: () => this.close()
2505
+ })
2506
+ );
2507
+ }
2045
2508
  if (this.persistKey) {
2046
2509
  this.teardowns.add(installPersist(this, this.persistKey));
2047
2510
  }
@@ -2066,7 +2529,7 @@ var BottomSheetEngine = class {
2066
2529
  isTopSheet: () => this.isTopSheet,
2067
2530
  getSize: () => this.size,
2068
2531
  isDestroyed: () => this.destroyed,
2069
- close: () => this.close(),
2532
+ close: () => this.close("back"),
2070
2533
  on: (event, fn) => this.on(event, fn),
2071
2534
  sheetId: this.id
2072
2535
  })
@@ -2160,6 +2623,10 @@ var BottomSheetEngine = class {
2160
2623
  const previousSize = this.size;
2161
2624
  this.scrollCache.cache(previousId, previousSize, target.size);
2162
2625
  this.activeId = id;
2626
+ if (wasClosed && target.size > 0) {
2627
+ this.opening = true;
2628
+ sheetStack.promote(this.id);
2629
+ }
2163
2630
  if (this.animation.viewTransitionsAvailable) {
2164
2631
  this.currentViewTransition?.skipTransition?.();
2165
2632
  const vt = document.startViewTransition(() => {
@@ -2174,19 +2641,26 @@ var BottomSheetEngine = class {
2174
2641
  } else {
2175
2642
  await this.animation.animateTo(target.size, velocityPxPerMs);
2176
2643
  }
2644
+ this.opening = false;
2177
2645
  externalSignal?.removeEventListener("abort", onExternalAbort);
2178
2646
  if (signal.aborted) return;
2179
2647
  this.scrollCache.restore(id, previousSize, target.size);
2180
2648
  this.updateAriaSlider();
2181
- this.emit("snap", { id, size: target.size });
2649
+ this.emit("snap", {
2650
+ id,
2651
+ size: target.size,
2652
+ progress: this.computeProgress(target.size)
2653
+ });
2182
2654
  if (wasClosed && target.size > 0) {
2183
2655
  this.emit("open", { id });
2184
2656
  this.handleOpen();
2185
2657
  notifyLinkedSheets(this.linkedSheets, this);
2658
+ this.emit("opened", { id });
2186
2659
  }
2187
2660
  if (target.size === 0) {
2188
2661
  this.emit("close", void 0);
2189
2662
  this.handleClose();
2663
+ this.emit("closed", void 0);
2190
2664
  }
2191
2665
  }
2192
2666
  async dragTo(targetSize, velocityOrOpts = 0) {
@@ -2211,18 +2685,76 @@ var BottomSheetEngine = class {
2211
2685
  const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
2212
2686
  return this.snapTo(target);
2213
2687
  }
2214
- close() {
2688
+ close(reason = "programmatic") {
2215
2689
  if (this.destroyed) return Promise.resolve();
2690
+ if (this.disableCloseFlag) return Promise.resolve();
2691
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2216
2692
  const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
2217
2693
  return this.snapTo(closedId ?? this.activeId);
2218
2694
  }
2219
- setAllowed(ids, snap) {
2695
+ canDismiss() {
2696
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2697
+ }
2698
+ isTop() {
2699
+ return this.isTopSheet;
2700
+ }
2701
+ depth() {
2702
+ return sheetStack.depthOf(this.id);
2703
+ }
2704
+ expand() {
2705
+ if (this.destroyed) return Promise.resolve();
2706
+ const ids = this.allowedIdsBySize();
2707
+ const target = ids[ids.length - 1];
2708
+ if (!target) return Promise.resolve();
2709
+ return this.snapTo(target);
2710
+ }
2711
+ collapse() {
2712
+ if (this.destroyed) return Promise.resolve();
2713
+ const ids = this.allowedIdsBySize();
2714
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
2715
+ const target = nonZero ?? ids[0];
2716
+ if (!target) return Promise.resolve();
2717
+ return this.snapTo(target);
2718
+ }
2719
+ setRadius(r) {
2220
2720
  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();
2721
+ this.element.style.setProperty(
2722
+ "--bs-radius",
2723
+ typeof r === "number" ? `${r}px` : r
2724
+ );
2725
+ }
2726
+ setMaxHeight(h) {
2727
+ if (this.destroyed) return;
2728
+ const value = typeof h === "number" ? `${h}px` : h;
2729
+ const axis = layoutAxis(this.mode);
2730
+ this.element.style.setProperty(
2731
+ axis === "height" ? "maxHeight" : "maxWidth",
2732
+ value
2733
+ );
2734
+ if (typeof h === "number") {
2735
+ this.maxHeightCap = h;
2736
+ this.clampToMaxHeight();
2737
+ } else if (typeof window !== "undefined") {
2738
+ const probe2 = document.createElement("div");
2739
+ probe2.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${axis === "height" ? "height" : "width"}:${value};`;
2740
+ document.body.appendChild(probe2);
2741
+ const measured = axis === "height" ? probe2.offsetHeight : probe2.offsetWidth;
2742
+ probe2.remove();
2743
+ this.maxHeightCap = measured > 0 ? measured : void 0;
2744
+ this.clampToMaxHeight();
2745
+ }
2746
+ }
2747
+ clampToMaxHeight() {
2748
+ if (this.maxHeightCap === void 0) return;
2749
+ if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2750
+ this.snaps.setMaxAxisSize(this.maxHeightCap);
2751
+ }
2752
+ if (this.size > this.maxHeightCap && !this.gesture?.isDragging) {
2753
+ this.applySize(this.maxHeightCap);
2225
2754
  }
2755
+ }
2756
+ setAllowed(ids, snap) {
2757
+ if (this.destroyed) return;
2226
2758
  this.snaps.setAllowedIds(ids);
2227
2759
  this.updateAriaSlider();
2228
2760
  if (snap && ids.includes(snap)) {
@@ -2262,6 +2794,79 @@ var BottomSheetEngine = class {
2262
2794
  setScrimOverlay(opts) {
2263
2795
  return this.scrim.setScrimOverlay(opts);
2264
2796
  }
2797
+ addAnchor(opts) {
2798
+ if (this.destroyed || typeof document === "undefined") return () => {
2799
+ };
2800
+ if (!this.anchorHost) {
2801
+ this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
2802
+ this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
2803
+ this.anchorHost.style.setProperty(
2804
+ "--bs-progress",
2805
+ String(this.computeProgress(this.size))
2806
+ );
2807
+ }
2808
+ const handle = attachAnchor(
2809
+ {
2810
+ mode: this.mode,
2811
+ host: this.anchorHost,
2812
+ getState: () => ({
2813
+ activeId: this.activeId,
2814
+ size: this.size,
2815
+ progress: this.computeProgress(this.size)
2816
+ }),
2817
+ on: (event, fn) => this.on(event, fn),
2818
+ isDestroyed: () => this.destroyed
2819
+ },
2820
+ opts
2821
+ );
2822
+ handle.syncZ(this.stackZ + 1);
2823
+ this.anchors.push(handle);
2824
+ const detach = () => {
2825
+ const idx = this.anchors.indexOf(handle);
2826
+ if (idx === -1) return;
2827
+ this.anchors.splice(idx, 1);
2828
+ handle.detach();
2829
+ };
2830
+ this.teardowns.add(detach);
2831
+ return detach;
2832
+ }
2833
+ setScrimStages(opts) {
2834
+ if (this.destroyed || typeof document === "undefined") return () => {
2835
+ };
2836
+ this.detachScrimStages?.();
2837
+ this.detachScrimStages = null;
2838
+ if (!opts) return () => {
2839
+ };
2840
+ const host = this.screenComponent?.parentElement;
2841
+ if (!host) {
2842
+ console.warn(
2843
+ "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
2844
+ );
2845
+ return () => {
2846
+ };
2847
+ }
2848
+ const detachInstalled = installScrimStages(
2849
+ {
2850
+ mode: this.mode,
2851
+ host,
2852
+ getState: () => ({
2853
+ activeId: this.activeId,
2854
+ size: this.size,
2855
+ progress: this.computeProgress(this.size)
2856
+ }),
2857
+ on: (event, fn) => this.on(event, fn),
2858
+ isDestroyed: () => this.destroyed
2859
+ },
2860
+ opts
2861
+ );
2862
+ const detach = () => {
2863
+ if (this.detachScrimStages === detach) this.detachScrimStages = null;
2864
+ detachInstalled();
2865
+ };
2866
+ this.detachScrimStages = detach;
2867
+ this.teardowns.add(() => this.detachScrimStages?.());
2868
+ return detach;
2869
+ }
2265
2870
  getScrimState() {
2266
2871
  return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
2267
2872
  }
@@ -2281,8 +2886,53 @@ var BottomSheetEngine = class {
2281
2886
  }
2282
2887
  this.updateAriaSlider();
2283
2888
  }
2889
+ recompute() {
2890
+ if (this.destroyed) return;
2891
+ this.snaps.recompute();
2892
+ this.scrim.invalidateOpacityCache();
2893
+ if (this.gesture?.isDragging) return;
2894
+ const current = this.snaps.findById(this.activeId);
2895
+ if (current) {
2896
+ this.size = current.size;
2897
+ this.applySize(this.size);
2898
+ this.updateAriaSlider();
2899
+ }
2900
+ }
2901
+ measureFitSize() {
2902
+ const vertical = layoutAxis(this.mode) === "height";
2903
+ const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
2904
+ const content = this.scrollContainer;
2905
+ const natural = content ? handleSize + (vertical ? content.scrollHeight : content.scrollWidth) : handleSize;
2906
+ if (typeof window === "undefined") return natural;
2907
+ const viewport = vertical ? window.innerHeight : window.innerWidth;
2908
+ return viewport > 0 ? Math.min(natural, viewport) : natural;
2909
+ }
2910
+ installFitObserver() {
2911
+ if (typeof ResizeObserver === "undefined") return;
2912
+ if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
2913
+ return;
2914
+ const targets = [this.handle];
2915
+ if (this.scrollContainer && this.scrollContainer !== this.handle) {
2916
+ targets.push(this.scrollContainer);
2917
+ }
2918
+ let raf = 0;
2919
+ const ro = new ResizeObserver(() => {
2920
+ if (raf) return;
2921
+ raf = requestAnimationFrame(() => {
2922
+ raf = 0;
2923
+ if (this.destroyed || this.gesture?.isDragging) return;
2924
+ this.recompute();
2925
+ });
2926
+ });
2927
+ for (const t of targets) ro.observe(t);
2928
+ this.teardowns.add(() => {
2929
+ ro.disconnect();
2930
+ if (raf) cancelAnimationFrame(raf);
2931
+ });
2932
+ }
2284
2933
  destroy() {
2285
2934
  this.destroyed = true;
2935
+ this.opening = false;
2286
2936
  this.currentAbort.abort();
2287
2937
  this.currentViewTransition?.skipTransition?.();
2288
2938
  this.currentViewTransition = null;
@@ -2332,6 +2982,36 @@ var BottomSheetEngine = class {
2332
2982
  frozen = true;
2333
2983
  return cancelled;
2334
2984
  }
2985
+ emitBeforeClose(reason) {
2986
+ let cancelled = false;
2987
+ let frozen = false;
2988
+ this.emit("before-close", {
2989
+ reason,
2990
+ cancel: () => {
2991
+ if (frozen) {
2992
+ console.warn(
2993
+ "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
2994
+ );
2995
+ return;
2996
+ }
2997
+ cancelled = true;
2998
+ }
2999
+ });
3000
+ frozen = true;
3001
+ return cancelled;
3002
+ }
3003
+ applyAutoAriaLabelledBy() {
3004
+ if (typeof document === "undefined") return;
3005
+ if (!this.lifecycle.focusTrapEnabled) return;
3006
+ if (this.element.getAttribute("aria-labelledby")) return;
3007
+ const scope = this.handle ?? this.element;
3008
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3009
+ if (!titled) return;
3010
+ if (!titled.id) {
3011
+ titled.id = nextInstanceId("bs-title");
3012
+ }
3013
+ this.element.setAttribute("aria-labelledby", titled.id);
3014
+ }
2335
3015
  recomputeSnaps() {
2336
3016
  const seen = /* @__PURE__ */ new Set();
2337
3017
  for (const p of this.snapPointsRaw) {
@@ -2348,24 +3028,42 @@ var BottomSheetEngine = class {
2348
3028
  getAllowedRange() {
2349
3029
  return this.snaps.getAllowedRange();
2350
3030
  }
3031
+ allowedIdsBySize() {
3032
+ return this.snaps.getAllowedIds().slice().sort(
3033
+ (a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0)
3034
+ );
3035
+ }
2351
3036
  updateAriaSlider() {
2352
- this.aria.setValue(this.snaps.getAllowedIds(), this.activeId);
3037
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
2353
3038
  }
2354
3039
  registerInStack() {
2355
3040
  this.teardowns.add(sheetStack.push({
2356
3041
  id: this.id,
2357
3042
  setZIndex: (z) => {
3043
+ this.stackZ = z;
2358
3044
  this.element.style.zIndex = String(z);
2359
3045
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3046
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
2360
3047
  },
2361
3048
  setIsTop: (isTop) => {
2362
3049
  this.isTopSheet = isTop;
2363
- if (this.backdrop) {
2364
- this.backdrop.style.display = isTop ? "" : "none";
2365
- }
2366
- }
3050
+ },
3051
+ isOpen: () => this.size > 0 || this.opening,
3052
+ setDepth: (depth) => this.applyStackDepth(depth)
2367
3053
  }));
2368
3054
  }
3055
+ applyStackDepth(depth) {
3056
+ if (!this.stackEffectEnabled) return;
3057
+ this.element.setAttribute("data-stack-depth", String(depth));
3058
+ if (!this.stackEffectPrimed) {
3059
+ this.stackEffectPrimed = true;
3060
+ const isVertical = this.mode === "bottom" || this.mode === "top";
3061
+ this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
3062
+ this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
3063
+ }
3064
+ const scale = Math.max(1 - depth * 0.04, 0.86);
3065
+ this.element.style.scale = scale === 1 ? "" : String(scale);
3066
+ }
2369
3067
  attach() {
2370
3068
  this.gesture = new GestureController({
2371
3069
  handle: this.handle,
@@ -2382,6 +3080,7 @@ var BottomSheetEngine = class {
2382
3080
  buf.rubberBandEnabled = this.rubberBandEnabled;
2383
3081
  return buf;
2384
3082
  },
3083
+ getDisableDrag: () => this.disableDragFlag,
2385
3084
  cancelAnimation: () => this.animation.cancel(),
2386
3085
  applySize: (size) => this.applySize(size),
2387
3086
  animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
@@ -2425,7 +3124,7 @@ var BottomSheetEngine = class {
2425
3124
  container: this.scrollContainer,
2426
3125
  isDragging: () => this.gesture?.isDragging ?? false,
2427
3126
  isAnimating: () => this.animation.isAnimating,
2428
- getAllowedIds: () => this.snaps.getAllowedIds(),
3127
+ getAllowedIds: () => this.allowedIdsBySize(),
2429
3128
  getActiveId: () => this.activeId,
2430
3129
  snapTo: (id) => {
2431
3130
  void this.snapTo(id);
@@ -2454,13 +3153,16 @@ var BottomSheetEngine = class {
2454
3153
  },
2455
3154
  newCycle: () => {
2456
3155
  this.newCycle();
2457
- }
3156
+ },
3157
+ isAnimating: () => this.animation.isAnimating,
3158
+ resyncAfterCancel: () => this.resyncAfterResize()
2458
3159
  }));
2459
3160
  if (this.lifecycle.closeOnEscape) {
2460
3161
  const onKey = (e) => {
2461
- if (this.destroyed) return;
3162
+ if (this.destroyed || e.defaultPrevented) return;
3163
+ if (!this.canDismiss()) return;
2462
3164
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2463
- void this.close();
3165
+ void this.close("escape");
2464
3166
  }
2465
3167
  };
2466
3168
  document.addEventListener("keydown", onKey);
@@ -2490,7 +3192,7 @@ var BottomSheetEngine = class {
2490
3192
  handle: this.handle,
2491
3193
  mode: this.mode,
2492
3194
  isDestroyed: () => this.destroyed,
2493
- getAllowedIds: () => this.snaps.getAllowedIds(),
3195
+ getAllowedIds: () => this.allowedIdsBySize(),
2494
3196
  getActiveId: () => this.activeId,
2495
3197
  snapTo: (id) => {
2496
3198
  void this.snapTo(id);
@@ -2511,6 +3213,7 @@ var BottomSheetEngine = class {
2511
3213
  });
2512
3214
  if (!target) return;
2513
3215
  const previousId = this.activeId;
3216
+ if (target.id === previousId && target.size === this.size) return;
2514
3217
  if (this.emitBeforeSnap(target, previousId)) {
2515
3218
  const restore = this.snaps.findById(previousId);
2516
3219
  if (restore) {
@@ -2519,21 +3222,34 @@ var BottomSheetEngine = class {
2519
3222
  return;
2520
3223
  }
2521
3224
  const previousSize = this.size;
3225
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
2522
3226
  this.scrollCache.cache(previousId, previousSize, target.size);
2523
3227
  this.activeId = target.id;
3228
+ if (previousSnapSize === 0 && target.size > 0) {
3229
+ this.opening = true;
3230
+ sheetStack.promote(this.id);
3231
+ }
2524
3232
  void this.animation.animateTo(target.size, velocity).then(() => {
3233
+ this.opening = false;
2525
3234
  if (signal.aborted) return;
2526
3235
  this.scrollCache.restore(target.id, previousSize, target.size);
2527
3236
  this.updateAriaSlider();
2528
- this.emit("snap", { id: target.id, size: target.size });
3237
+ this.emit("snap", {
3238
+ id: target.id,
3239
+ size: target.size,
3240
+ progress: this.computeProgress(target.size)
3241
+ });
2529
3242
  this.haptic();
2530
- if (previousId === "closed" && target.size > 0) {
3243
+ if (previousSnapSize === 0 && target.size > 0) {
2531
3244
  this.emit("open", { id: target.id });
2532
3245
  this.handleOpen();
3246
+ notifyLinkedSheets(this.linkedSheets, this);
3247
+ this.emit("opened", { id: target.id });
2533
3248
  }
2534
3249
  if (target.size === 0) {
2535
3250
  this.emit("close", void 0);
2536
3251
  this.handleClose();
3252
+ this.emit("closed", void 0);
2537
3253
  }
2538
3254
  });
2539
3255
  }
@@ -2555,6 +3271,9 @@ var BottomSheetEngine = class {
2555
3271
  if (this.scrimParent) {
2556
3272
  this.scrimParent.style.setProperty("--bs-size", `${size}px`);
2557
3273
  }
3274
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3275
+ this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3276
+ }
2558
3277
  }
2559
3278
  const progress = this.computeProgress(size);
2560
3279
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -2563,6 +3282,9 @@ var BottomSheetEngine = class {
2563
3282
  );
2564
3283
  if (progressChanged) {
2565
3284
  style.setProperty("--bs-progress", String(progress));
3285
+ if (this.anchorHost) {
3286
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3287
+ }
2566
3288
  }
2567
3289
  this.scrim.applyOpacity(progress, progressChanged);
2568
3290
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -2577,6 +3299,7 @@ var BottomSheetEngine = class {
2577
3299
  return Math.min(Math.max((size - min) / (max - min), 0), 1);
2578
3300
  }
2579
3301
  handleOpen() {
3302
+ sheetStack.promote(this.id);
2580
3303
  try {
2581
3304
  this.lifecycle.install();
2582
3305
  } catch (err) {
@@ -2590,8 +3313,31 @@ var BottomSheetEngine = class {
2590
3313
  });
2591
3314
  }
2592
3315
  }
3316
+ resyncAfterResize() {
3317
+ if (this.destroyed) return;
3318
+ const target = this.snaps.findById(this.activeId);
3319
+ if (!target) return;
3320
+ this.updateAriaSlider();
3321
+ this.emit("snap", {
3322
+ id: target.id,
3323
+ size: target.size,
3324
+ progress: this.computeProgress(target.size)
3325
+ });
3326
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3327
+ this.emit("open", { id: target.id });
3328
+ this.handleOpen();
3329
+ notifyLinkedSheets(this.linkedSheets, this);
3330
+ this.emit("opened", { id: target.id });
3331
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3332
+ this.emit("close", void 0);
3333
+ this.handleClose();
3334
+ this.emit("closed", void 0);
3335
+ }
3336
+ }
2593
3337
  handleClose() {
3338
+ this.opening = false;
2594
3339
  this.lifecycle.release();
3340
+ sheetStack.update();
2595
3341
  }
2596
3342
  };
2597
3343
  var BottomSheet = qwik.component$((props) => {
@@ -2608,6 +3354,7 @@ var BottomSheet = qwik.component$((props) => {
2608
3354
  isAnimating: false,
2609
3355
  progress: 0
2610
3356
  });
3357
+ const engineStore = qwik.useStore({ engine: void 0 });
2611
3358
  qwik.useVisibleTask$(({ cleanup }) => {
2612
3359
  if (!sheetRef.value) return;
2613
3360
  if (!handleRef.value || !contentRef.value) {
@@ -2632,9 +3379,12 @@ var BottomSheet = qwik.component$((props) => {
2632
3379
  closeOnBack: props.closeOnBack,
2633
3380
  lockBodyScroll: props.lockBodyScroll,
2634
3381
  rubberBand: props.rubberBand,
2635
- backdropRange: props.backdropRange
3382
+ backdropRange: props.backdropRange,
3383
+ radius: props.radius,
3384
+ maxHeight: props.maxHeight
2636
3385
  };
2637
3386
  const engine = new BottomSheetEngine(engineOpts);
3387
+ engineStore.engine = qwik.noSerialize(engine);
2638
3388
  const sync = () => {
2639
3389
  const s = engine.state;
2640
3390
  state.size = s.size;
@@ -2651,7 +3401,28 @@ var BottomSheet = qwik.component$((props) => {
2651
3401
  });
2652
3402
  engine.on("dragstart", sync);
2653
3403
  engine.on("dragend", sync);
2654
- cleanup(() => engine.destroy());
3404
+ cleanup(() => {
3405
+ engineStore.engine = void 0;
3406
+ engine.destroy();
3407
+ });
3408
+ });
3409
+ qwik.useTask$(({ track }) => {
3410
+ const points = track(() => props.snapPoints);
3411
+ const allowed = track(() => props.allowed);
3412
+ const engine = engineStore.engine;
3413
+ if (!engine) return;
3414
+ engine.setSnapPoints(
3415
+ points,
3416
+ allowed
3417
+ );
3418
+ });
3419
+ qwik.useTask$(({ track }) => {
3420
+ const radius = track(() => props.radius);
3421
+ const maxHeight = track(() => props.maxHeight);
3422
+ const engine = engineStore.engine;
3423
+ if (!engine) return;
3424
+ if (radius !== void 0) engine.setRadius(radius);
3425
+ if (maxHeight !== void 0) engine.setMaxHeight(maxHeight);
2655
3426
  });
2656
3427
  const showBackdrop = props.backdrop !== false;
2657
3428
  const mode = props.mode ?? "bottom";