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