@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/element.js CHANGED
@@ -31,16 +31,52 @@ var init_sheetStack = __esm({
31
31
  this.entries.splice(idx, 1);
32
32
  this.recompute();
33
33
  }
34
+ promote(id) {
35
+ const idx = this.entries.findIndex((e) => e.id === id);
36
+ if (idx === -1) return;
37
+ if (idx !== this.entries.length - 1) {
38
+ const [entry] = this.entries.splice(idx, 1);
39
+ this.entries.push(entry);
40
+ }
41
+ this.recompute();
42
+ }
43
+ update() {
44
+ this.recompute();
45
+ }
34
46
  size() {
35
47
  return this.entries.length;
36
48
  }
49
+ depthOf(id) {
50
+ const open = [];
51
+ for (const entry of this.entries) {
52
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
53
+ }
54
+ const idx = open.findIndex((e) => e.id === id);
55
+ if (idx === -1) return 0;
56
+ return open.length - 1 - idx;
57
+ }
37
58
  clear() {
38
59
  this.entries = [];
39
60
  }
40
61
  recompute() {
62
+ let top = null;
63
+ const open = [];
64
+ for (const entry of this.entries) {
65
+ if (!entry.isOpen || entry.isOpen()) {
66
+ top = entry;
67
+ open.push(entry);
68
+ }
69
+ }
70
+ if (!top && this.entries.length > 0) {
71
+ top = this.entries[this.entries.length - 1];
72
+ }
41
73
  this.entries.forEach((entry, i) => {
42
74
  entry.setZIndex(BASE_Z + i * STEP);
43
- entry.setIsTop(i === this.entries.length - 1);
75
+ entry.setIsTop(entry === top);
76
+ if (entry.setDepth) {
77
+ const openIdx = open.indexOf(entry);
78
+ entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
79
+ }
44
80
  });
45
81
  }
46
82
  };
@@ -156,7 +192,9 @@ var init_cssLength = __esm({
156
192
  resolveSnap = (point, mode, measureFit) => {
157
193
  if (typeof point === "number") return Math.max(0, point);
158
194
  if (point === "full") return viewportSize(mode);
159
- if (point === "fit") return Math.max(0, measureFit?.() ?? 0);
195
+ if (point === "fit" || point === "content") {
196
+ return Math.max(0, measureFit?.() ?? 0);
197
+ }
160
198
  const pctMatch = point.match(PERCENT);
161
199
  if (pctMatch) {
162
200
  const pct = parseFloat(pctMatch[1]) / 100;
@@ -362,14 +400,17 @@ function installAutoCollapse(deps) {
362
400
  const fire = () => {
363
401
  if (deps.isDestroyed()) return;
364
402
  if (deps.isDragging()) return;
365
- const allowed = deps.getAllowedIds();
366
- const target = allowed.find((id) => {
403
+ const active = deps.resolveSnap(deps.getActiveId());
404
+ if (!active || active.size === 0) return;
405
+ let target = null;
406
+ for (const id of deps.getAllowedIds()) {
367
407
  const snap = deps.resolveSnap(id);
368
- return snap !== null && snap.size > 0;
369
- });
408
+ if (!snap || snap.size <= 0) continue;
409
+ if (!target || snap.size < target.size) target = snap;
410
+ }
370
411
  if (!target) return;
371
- if (target === deps.getActiveId()) return;
372
- deps.snapTo(target);
412
+ if (target.id === active.id || target.size >= active.size) return;
413
+ deps.snapTo(target.id);
373
414
  };
374
415
  const reset = () => {
375
416
  clearTimer();
@@ -380,6 +421,7 @@ function installAutoCollapse(deps) {
380
421
  }, ms);
381
422
  };
382
423
  const unsubscribe = deps.on("snap", () => reset());
424
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
383
425
  return () => {
384
426
  clearTimer();
385
427
  unsubscribe();
@@ -396,6 +438,7 @@ function notifyLinkedSheets(sheets, self) {
396
438
  for (const linked of sheets) {
397
439
  if (linked === self) continue;
398
440
  try {
441
+ if (linked.state.size === 0) continue;
399
442
  const ids = linked.getAllowedIds();
400
443
  const target = ids.find((id) => id !== "closed");
401
444
  if (target && target !== linked.state.activeId) {
@@ -423,86 +466,122 @@ function installRoute(deps) {
423
466
  return () => {
424
467
  };
425
468
  }
426
- let routePushed = false;
427
- let closeOnBackPushed = false;
428
- let detachPopstate = null;
429
- const popOurMarker = (key, expected) => {
430
- const state = history.state;
431
- if (state && state[key] === expected) {
432
- try {
433
- history.back();
434
- } catch {
435
- }
469
+ let pushed = false;
470
+ let closer = null;
471
+ const teardownEntry = () => {
472
+ if (closer) {
473
+ const idx = closers.indexOf(closer);
474
+ if (idx !== -1) closers.splice(idx, 1);
475
+ }
476
+ const wasConsumed = closer?.consumed ?? false;
477
+ closer = null;
478
+ if (pushed && !wasConsumed) {
479
+ pushed = false;
480
+ programmaticBack();
481
+ } else {
482
+ pushed = false;
436
483
  }
437
484
  };
438
485
  const onOpen = () => {
439
- if (deps.routedTo && !detachPopstate) {
440
- const target = deps.routedTo;
441
- try {
442
- history.pushState({ __bsRouted: target }, "", target);
443
- routePushed = true;
444
- } catch {
445
- }
446
- const onPop = (event) => {
447
- if (deps.isDestroyed()) return;
448
- const state = event.state;
449
- if (state && state.__bsRouted === target && deps.getSize() > 0) {
450
- routePushed = false;
451
- void deps.close();
452
- }
453
- };
454
- window.addEventListener("popstate", onPop);
455
- detachPopstate = () => window.removeEventListener("popstate", onPop);
456
- } else if (deps.closeOnBack && !detachPopstate) {
457
- try {
486
+ if (closer) return;
487
+ closer = {
488
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
489
+ isTop: () => deps.isTopSheet(),
490
+ close: () => void deps.close(),
491
+ consumed: false
492
+ };
493
+ ensureHandler();
494
+ closers.push(closer);
495
+ try {
496
+ if (deps.routedTo !== void 0) {
497
+ history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
498
+ } else {
458
499
  history.pushState({ __bsSheet: deps.sheetId }, "");
459
- closeOnBackPushed = true;
460
- } catch {
461
500
  }
462
- const onPop = () => {
463
- if (deps.isDestroyed()) return;
464
- if (deps.getSize() > 0 && deps.isTopSheet()) {
465
- const state = history.state;
466
- if (state?.__bsSheet !== deps.sheetId) closeOnBackPushed = false;
467
- void deps.close();
468
- }
469
- };
470
- window.addEventListener("popstate", onPop);
471
- detachPopstate = () => window.removeEventListener("popstate", onPop);
472
- }
473
- };
474
- const onClose = () => {
475
- detachPopstate?.();
476
- detachPopstate = null;
477
- if (routePushed) {
478
- routePushed = false;
479
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
480
- }
481
- if (closeOnBackPushed) {
482
- closeOnBackPushed = false;
483
- popOurMarker("__bsSheet", deps.sheetId);
501
+ pushed = true;
502
+ } catch {
503
+ pushed = false;
484
504
  }
485
505
  };
486
506
  const unsubscribeOpen = deps.on("open", onOpen);
487
- const unsubscribeClose = deps.on("close", onClose);
507
+ const unsubscribeClose = deps.on("close", teardownEntry);
488
508
  if (deps.getSize() > 0) onOpen();
489
509
  return () => {
490
510
  unsubscribeOpen();
491
511
  unsubscribeClose();
492
- detachPopstate?.();
493
- detachPopstate = null;
494
- if (routePushed) {
495
- routePushed = false;
496
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
497
- }
498
- if (closeOnBackPushed) {
499
- closeOnBackPushed = false;
500
- popOurMarker("__bsSheet", deps.sheetId);
501
- }
512
+ teardownEntry();
513
+ };
514
+ }
515
+ function installRouteChange(deps) {
516
+ if (typeof window === "undefined" || typeof history === "undefined") {
517
+ return () => {
518
+ };
519
+ }
520
+ const onChange = () => {
521
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
522
+ void deps.close();
523
+ };
524
+ const wrap = (key) => {
525
+ const original = history[key];
526
+ const patched = function(...args) {
527
+ original.apply(this, args);
528
+ onChange();
529
+ };
530
+ history[key] = patched;
531
+ return () => {
532
+ if (history[key] === patched) {
533
+ history[key] = original;
534
+ }
535
+ };
536
+ };
537
+ window.addEventListener("popstate", onChange);
538
+ const restorePush = wrap("pushState");
539
+ const restoreReplace = wrap("replaceState");
540
+ return () => {
541
+ window.removeEventListener("popstate", onChange);
542
+ restorePush();
543
+ restoreReplace();
502
544
  };
503
545
  }
546
+ var closers, handlerInstalled, suppress, ensureHandler, programmaticBack;
504
547
  var init_route = __esm({
505
548
  "src/core/features/route.ts"() {
549
+ closers = [];
550
+ handlerInstalled = false;
551
+ suppress = 0;
552
+ ensureHandler = () => {
553
+ if (handlerInstalled || typeof window === "undefined") return;
554
+ handlerInstalled = true;
555
+ window.addEventListener("popstate", () => {
556
+ if (suppress > 0) {
557
+ suppress -= 1;
558
+ return;
559
+ }
560
+ let target = null;
561
+ for (let i = closers.length - 1; i >= 0; i -= 1) {
562
+ const c = closers[i];
563
+ if (!c.isOpen()) continue;
564
+ if (!target) target = c;
565
+ if (c.isTop()) {
566
+ target = c;
567
+ break;
568
+ }
569
+ }
570
+ if (!target) return;
571
+ target.consumed = true;
572
+ const idx = closers.indexOf(target);
573
+ if (idx !== -1) closers.splice(idx, 1);
574
+ target.close();
575
+ });
576
+ };
577
+ programmaticBack = () => {
578
+ suppress += 1;
579
+ try {
580
+ history.back();
581
+ } catch {
582
+ suppress = Math.max(0, suppress - 1);
583
+ }
584
+ };
506
585
  }
507
586
  });
508
587
 
@@ -614,13 +693,17 @@ function installResizeObserver(deps) {
614
693
  if (viewportSize2 > 0 && viewportSize2 < max) {
615
694
  deps.setMaxAxisSize(viewportSize2);
616
695
  }
696
+ const wasAnimating = deps.isAnimating?.() ?? false;
617
697
  deps.cancelInFlight();
618
698
  deps.newCycle();
619
699
  const current = deps.resolveActiveSnap();
620
700
  const newMax = deps.getMaxAxisSize();
621
701
  const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
622
702
  deps.setSize(targetSize);
623
- if (current && !deps.isDragging()) deps.applySize(targetSize);
703
+ if (current && !deps.isDragging()) {
704
+ deps.applySize(targetSize);
705
+ if (wasAnimating) deps.resyncAfterCancel?.();
706
+ }
624
707
  };
625
708
  let resizeObserver = null;
626
709
  if (typeof ResizeObserver !== "undefined") {
@@ -762,7 +845,7 @@ var init_devWarn = __esm({
762
845
  }
763
846
  });
764
847
 
765
- // src/core/controllers/scrim-controller.ts
848
+ // src/core/primitives/overlay-position.ts
766
849
  function resolveSheetAnchoredStyle(mode, position, inset) {
767
850
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
768
851
  switch (mode) {
@@ -784,15 +867,69 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
784
867
  return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
785
868
  }
786
869
  }
787
- function applySheetAnchoredPosition(ws, mode, position, inset) {
788
- const style = resolveSheetAnchoredStyle(mode, position, inset);
789
- if (style.top !== void 0) ws.top = style.top;
790
- if (style.bottom !== void 0) ws.bottom = style.bottom;
791
- if (style.left !== void 0) ws.left = style.left;
792
- if (style.right !== void 0) ws.right = style.right;
793
- if (style.transform !== void 0) ws.transform = style.transform;
870
+ function applyOverlayPosition(ws, mode, position, inset) {
871
+ switch (position) {
872
+ case "top-left":
873
+ ws.top = inset;
874
+ ws.left = inset;
875
+ break;
876
+ case "top-center":
877
+ ws.top = inset;
878
+ ws.left = "50%";
879
+ ws.transform = "translateX(-50%)";
880
+ break;
881
+ case "top-right":
882
+ ws.top = inset;
883
+ ws.right = inset;
884
+ break;
885
+ case "center-left":
886
+ ws.top = "50%";
887
+ ws.left = inset;
888
+ ws.transform = "translateY(-50%)";
889
+ break;
890
+ case "center":
891
+ ws.top = "50%";
892
+ ws.left = "50%";
893
+ ws.transform = "translate(-50%, -50%)";
894
+ break;
895
+ case "center-right":
896
+ ws.top = "50%";
897
+ ws.right = inset;
898
+ ws.transform = "translateY(-50%)";
899
+ break;
900
+ case "bottom-left":
901
+ ws.bottom = inset;
902
+ ws.left = inset;
903
+ break;
904
+ case "bottom-center":
905
+ ws.bottom = inset;
906
+ ws.left = "50%";
907
+ ws.transform = "translateX(-50%)";
908
+ break;
909
+ case "bottom-right":
910
+ ws.bottom = inset;
911
+ ws.right = inset;
912
+ break;
913
+ case "sheet-top-left":
914
+ case "sheet-top-center":
915
+ case "sheet-top-right": {
916
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
917
+ if (style.top !== void 0) ws.top = style.top;
918
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
919
+ if (style.left !== void 0) ws.left = style.left;
920
+ if (style.right !== void 0) ws.right = style.right;
921
+ if (style.transform !== void 0) ws.transform = style.transform;
922
+ break;
923
+ }
924
+ }
794
925
  }
795
- var VALID_CSS_LENGTH, ScrimController;
926
+ var init_overlay_position = __esm({
927
+ "src/core/primitives/overlay-position.ts"() {
928
+ }
929
+ });
930
+
931
+ // src/core/controllers/scrim-controller.ts
932
+ var VALID_CSS_LENGTH, DEFAULT_SCRIM_DIM, ScrimController;
796
933
  var init_scrim_controller = __esm({
797
934
  "src/core/controllers/scrim-controller.ts"() {
798
935
  init_types();
@@ -800,7 +937,9 @@ var init_scrim_controller = __esm({
800
937
  init_hot_path_thresholds();
801
938
  init_opacity_dedup();
802
939
  init_devWarn();
940
+ init_overlay_position();
803
941
  VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
942
+ DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
804
943
  ScrimController = class {
805
944
  constructor(deps, opts) {
806
945
  __publicField(this, "screenComponent");
@@ -811,6 +950,8 @@ var init_scrim_controller = __esm({
811
950
  __publicField(this, "getAllowedIds");
812
951
  __publicField(this, "getResolvedSnaps");
813
952
  __publicField(this, "snapToFn");
953
+ __publicField(this, "closeFn");
954
+ __publicField(this, "scrimInteractive", false);
814
955
  __publicField(this, "scrimMode");
815
956
  __publicField(this, "backdropRange");
816
957
  __publicField(this, "screenRange");
@@ -831,14 +972,17 @@ var init_scrim_controller = __esm({
831
972
  this.getAllowedIds = deps.getAllowedIds;
832
973
  this.getResolvedSnaps = deps.getResolvedSnaps;
833
974
  this.snapToFn = deps.snapTo;
975
+ this.closeFn = deps.close;
834
976
  this.scrimMode = opts.scrimMode ?? "full";
835
977
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
836
978
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
837
979
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
838
980
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
981
+ const explicitColor = opts.scrimColor ?? preset?.color;
982
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
839
983
  if (this.screenComponent) {
840
984
  this.applyScrimStyles({
841
- color: opts.scrimColor ?? preset?.color,
985
+ color: effectiveColor,
842
986
  blur: opts.scrimBlur ?? preset?.blur,
843
987
  interactive: opts.scrimInteractive ?? preset?.interactive ?? false
844
988
  });
@@ -972,11 +1116,15 @@ var init_scrim_controller = __esm({
972
1116
  }
973
1117
  const onClick = () => {
974
1118
  if (this.isDestroyed()) return;
1119
+ if (this.closeFn) {
1120
+ this.closeFn();
1121
+ return;
1122
+ }
975
1123
  const allowed = this.getAllowedIds();
976
1124
  const snaps = this.getResolvedSnaps();
977
1125
  const fallback = allowed.find((id) => {
978
1126
  const snap = findById(id, snaps);
979
- return snap !== null && snap.size > 0;
1127
+ return snap !== null && snap.size === 0;
980
1128
  });
981
1129
  if (fallback) this.snapToFn(fallback);
982
1130
  };
@@ -993,6 +1141,7 @@ var init_scrim_controller = __esm({
993
1141
  if (this.isDestroyed()) return;
994
1142
  if (enabled === this.scrimEnabled) return;
995
1143
  this.scrimEnabled = enabled;
1144
+ this.invalidateOpacityCache();
996
1145
  if (!enabled) {
997
1146
  this.savedScrimRanges = {
998
1147
  screen: this.screenRange,
@@ -1016,7 +1165,12 @@ var init_scrim_controller = __esm({
1016
1165
  blur: cfg.blur ?? null,
1017
1166
  interactive: cfg.interactive
1018
1167
  });
1019
- this.screenRange = cfg.range;
1168
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1169
+ this.savedScrimRanges = { screen: cfg.range, backdrop: cfg.range };
1170
+ } else {
1171
+ this.screenRange = cfg.range;
1172
+ this.backdropRange = cfg.range;
1173
+ }
1020
1174
  }
1021
1175
  const noop = () => {
1022
1176
  };
@@ -1066,59 +1220,7 @@ var init_scrim_controller = __esm({
1066
1220
  wrapper.className = "bs-scrim-overlay";
1067
1221
  const ws = wrapper.style;
1068
1222
  ws.position = "absolute";
1069
- switch (position) {
1070
- case "top-left":
1071
- ws.top = inset;
1072
- ws.left = inset;
1073
- break;
1074
- case "top-center":
1075
- ws.top = inset;
1076
- ws.left = "50%";
1077
- ws.transform = "translateX(-50%)";
1078
- break;
1079
- case "top-right":
1080
- ws.top = inset;
1081
- ws.right = inset;
1082
- break;
1083
- case "center-left":
1084
- ws.top = "50%";
1085
- ws.left = inset;
1086
- ws.transform = "translateY(-50%)";
1087
- break;
1088
- case "center":
1089
- ws.top = "50%";
1090
- ws.left = "50%";
1091
- ws.transform = "translate(-50%, -50%)";
1092
- break;
1093
- case "center-right":
1094
- ws.top = "50%";
1095
- ws.right = inset;
1096
- ws.transform = "translateY(-50%)";
1097
- break;
1098
- case "bottom-left":
1099
- ws.bottom = inset;
1100
- ws.left = inset;
1101
- break;
1102
- case "bottom-center":
1103
- ws.bottom = inset;
1104
- ws.left = "50%";
1105
- ws.transform = "translateX(-50%)";
1106
- break;
1107
- case "bottom-right":
1108
- ws.bottom = inset;
1109
- ws.right = inset;
1110
- break;
1111
- case "sheet-top-left":
1112
- case "sheet-top-center":
1113
- case "sheet-top-right":
1114
- applySheetAnchoredPosition(
1115
- ws,
1116
- this.mode,
1117
- position,
1118
- inset
1119
- );
1120
- break;
1121
- }
1223
+ applyOverlayPosition(ws, this.mode, position, inset);
1122
1224
  if (interactive) {
1123
1225
  ws.pointerEvents = "auto";
1124
1226
  }
@@ -1177,6 +1279,7 @@ var init_scrim_controller = __esm({
1177
1279
  }
1178
1280
  }
1179
1281
  if (opts.interactive !== void 0) {
1282
+ this.scrimInteractive = opts.interactive;
1180
1283
  s.pointerEvents = opts.interactive ? "auto" : "none";
1181
1284
  }
1182
1285
  }
@@ -1191,14 +1294,14 @@ var init_scrim_controller = __esm({
1191
1294
  const s = this.screenComponent.style;
1192
1295
  s.position = "fixed";
1193
1296
  s.inset = insetByMode[this.mode];
1194
- s.pointerEvents = "none";
1297
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1195
1298
  }
1196
1299
  };
1197
1300
  }
1198
1301
  });
1199
1302
 
1200
1303
  // src/core/animation/animation.ts
1201
- var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween;
1304
+ var easeOutBack, easeOutCubic, easeLinear, easeOutQuint, tween, prefersReducedMotion;
1202
1305
  var init_animation = __esm({
1203
1306
  "src/core/animation/animation.ts"() {
1204
1307
  easeOutBack = (t) => {
@@ -1250,6 +1353,10 @@ var init_animation = __esm({
1250
1353
  promise
1251
1354
  };
1252
1355
  };
1356
+ prefersReducedMotion = () => {
1357
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1358
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1359
+ };
1253
1360
  }
1254
1361
  });
1255
1362
 
@@ -1285,14 +1392,20 @@ var init_spring = __esm({
1285
1392
  if (cancelled) return;
1286
1393
  const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
1287
1394
  lastTime = now;
1288
- const subDt = Math.min(dt, 1 / 240);
1289
- let stepsLeft = Math.max(1, Math.ceil(dt / subDt));
1395
+ const steps = Math.max(1, Math.ceil(dt * 240));
1396
+ const subDt = dt / steps;
1397
+ let stepsLeft = steps;
1290
1398
  while (stepsLeft-- > 0) {
1291
1399
  const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1292
1400
  const accel = force / cfg.mass;
1293
1401
  v += accel * subDt;
1294
1402
  x += v * subDt;
1295
1403
  }
1404
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1405
+ onUpdate(to, 0);
1406
+ resolve();
1407
+ return;
1408
+ }
1296
1409
  onUpdate(x, v);
1297
1410
  const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1298
1411
  if (isAtRest) {
@@ -1461,7 +1574,7 @@ var init_animation_runner = __esm({
1461
1574
  });
1462
1575
 
1463
1576
  // src/core/lifecycle/focusTrap.ts
1464
- var FOCUSABLE, isVisible, focusables, installFocusTrap;
1577
+ var FOCUSABLE, isVisible, focusables, trapStack, installFocusTrap;
1465
1578
  var init_focusTrap = __esm({
1466
1579
  "src/core/lifecycle/focusTrap.ts"() {
1467
1580
  FOCUSABLE = [
@@ -1486,9 +1599,13 @@ var init_focusTrap = __esm({
1486
1599
  focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
1487
1600
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1488
1601
  );
1602
+ trapStack = [];
1489
1603
  installFocusTrap = (container, options = {}) => {
1490
1604
  if (typeof document === "undefined") return () => {
1491
1605
  };
1606
+ const token = {};
1607
+ trapStack.push(token);
1608
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1492
1609
  const previouslyFocused = document.activeElement;
1493
1610
  const focusInitial = () => {
1494
1611
  const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
@@ -1496,6 +1613,7 @@ var init_focusTrap = __esm({
1496
1613
  };
1497
1614
  focusInitial();
1498
1615
  const handleKey = (e) => {
1616
+ if (!isActive()) return;
1499
1617
  if (e.key === "Escape" && options.onEscape) {
1500
1618
  options.onEscape();
1501
1619
  return;
@@ -1518,6 +1636,7 @@ var init_focusTrap = __esm({
1518
1636
  }
1519
1637
  };
1520
1638
  const handleFocusIn = (e) => {
1639
+ if (!isActive()) return;
1521
1640
  const target = e.target;
1522
1641
  if (target && !container.contains(target)) {
1523
1642
  const list = focusables(container);
@@ -1530,6 +1649,8 @@ var init_focusTrap = __esm({
1530
1649
  return () => {
1531
1650
  if (released) return;
1532
1651
  released = true;
1652
+ const idx = trapStack.indexOf(token);
1653
+ if (idx !== -1) trapStack.splice(idx, 1);
1533
1654
  document.removeEventListener("keydown", handleKey, true);
1534
1655
  document.removeEventListener("focusin", handleFocusIn, true);
1535
1656
  if (previouslyFocused && document.contains(previouslyFocused)) {
@@ -1592,11 +1713,17 @@ function createInertSiblings(rootProvider) {
1592
1713
  return {
1593
1714
  apply() {
1594
1715
  if (typeof document === "undefined") return;
1595
- const root = rootProvider();
1596
- const parent = root.parentElement ?? document.body;
1716
+ let top = rootProvider();
1717
+ while (top.parentElement && top.parentElement !== document.body) {
1718
+ top = top.parentElement;
1719
+ }
1720
+ const parent = top.parentElement ?? document.body;
1597
1721
  for (const child of Array.from(parent.children)) {
1598
- if (child === root) continue;
1722
+ if (child === top) continue;
1599
1723
  if (child.hasAttribute("inert")) continue;
1724
+ if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1725
+ continue;
1726
+ }
1600
1727
  child.setAttribute("inert", "");
1601
1728
  tracked.push(child);
1602
1729
  }
@@ -1622,7 +1749,6 @@ var init_lifecycle_controller = __esm({
1622
1749
  LifecycleController = class {
1623
1750
  constructor(deps, opts) {
1624
1751
  __publicField(this, "element");
1625
- __publicField(this, "closeFn");
1626
1752
  __publicField(this, "focusTrapEnabled");
1627
1753
  __publicField(this, "initialFocus");
1628
1754
  __publicField(this, "closeOnEscape");
@@ -1636,7 +1762,6 @@ var init_lifecycle_controller = __esm({
1636
1762
  __publicField(this, "installed", false);
1637
1763
  __publicField(this, "destroyed", false);
1638
1764
  this.element = deps.element;
1639
- this.closeFn = deps.close;
1640
1765
  this.focusTrapEnabled = opts.focusTrap ?? false;
1641
1766
  this.initialFocus = opts.initialFocus;
1642
1767
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1646,6 +1771,9 @@ var init_lifecycle_controller = __esm({
1646
1771
  this.returnFocus = opts.returnFocus;
1647
1772
  this.inertSiblings = createInertSiblings(() => this.element);
1648
1773
  }
1774
+ get isInstalled() {
1775
+ return this.installed;
1776
+ }
1649
1777
  install() {
1650
1778
  if (this.destroyed) return;
1651
1779
  this.installed = true;
@@ -1654,8 +1782,7 @@ var init_lifecycle_controller = __esm({
1654
1782
  }
1655
1783
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1656
1784
  this.releaseFocusTrap = installFocusTrap(this.element, {
1657
- initialFocus: this.initialFocus,
1658
- onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
1785
+ initialFocus: this.initialFocus
1659
1786
  });
1660
1787
  }
1661
1788
  if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
@@ -1675,7 +1802,7 @@ var init_lifecycle_controller = __esm({
1675
1802
  this.releaseScrollLock = null;
1676
1803
  this.inertSiblings.remove();
1677
1804
  if (this.returnFocus) {
1678
- const target = typeof this.returnFocus === "function" ? this.returnFocus() : this.returnFocus;
1805
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1679
1806
  target?.focus?.();
1680
1807
  }
1681
1808
  }
@@ -1818,12 +1945,12 @@ var init_gestures = __esm({
1818
1945
  // src/core/primitives/rubber-band.ts
1819
1946
  function rubberBand(overshoot, maxAxisSize) {
1820
1947
  const cap = Math.min(maxAxisSize * 0.15, 80);
1821
- return Math.sign(overshoot) * Math.pow(Math.abs(overshoot), RUBBER_BAND_FACTOR) * Math.min(6, cap / Math.max(Math.abs(overshoot), 1));
1948
+ if (cap <= 0) return 0;
1949
+ const abs = Math.abs(overshoot);
1950
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1822
1951
  }
1823
- var RUBBER_BAND_FACTOR;
1824
1952
  var init_rubber_band = __esm({
1825
1953
  "src/core/primitives/rubber-band.ts"() {
1826
- RUBBER_BAND_FACTOR = 0.55;
1827
1954
  }
1828
1955
  });
1829
1956
 
@@ -1863,6 +1990,7 @@ var init_gesture_controller = __esm({
1863
1990
  __publicField(this, "dragStartSize", 0);
1864
1991
  __publicField(this, "currentPointerKind", "touch");
1865
1992
  __publicField(this, "keyboardDismissed", false);
1993
+ __publicField(this, "dragSuppressed", false);
1866
1994
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
1867
1995
  this.deps = deps;
1868
1996
  }
@@ -1873,6 +2001,11 @@ var init_gesture_controller = __esm({
1873
2001
  if (this.detach) return this.detach;
1874
2002
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
1875
2003
  onStart: (_coord, kind) => {
2004
+ if (this.deps.getDisableDrag?.()) {
2005
+ this.dragSuppressed = true;
2006
+ return;
2007
+ }
2008
+ this.dragSuppressed = false;
1876
2009
  this.deps.cancelAnimation();
1877
2010
  this.isDragging_ = true;
1878
2011
  this.dragStartSize = this.deps.getDragContext().size;
@@ -1883,6 +2016,7 @@ var init_gesture_controller = __esm({
1883
2016
  this.deps.emit("dragstart", { size: this.dragStartSize });
1884
2017
  },
1885
2018
  onMove: (delta) => {
2019
+ if (this.dragSuppressed) return;
1886
2020
  const ctx = this.deps.getDragContext();
1887
2021
  const { min, max } = ctx.range;
1888
2022
  const maxAxis = ctx.maxAxisSize;
@@ -1906,6 +2040,10 @@ var init_gesture_controller = __esm({
1906
2040
  }
1907
2041
  },
1908
2042
  onEnd: (delta, velocity, kind) => {
2043
+ if (this.dragSuppressed) {
2044
+ this.dragSuppressed = false;
2045
+ return;
2046
+ }
1909
2047
  this.isDragging_ = false;
1910
2048
  this.keyboardDismissed = false;
1911
2049
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -1916,9 +2054,17 @@ var init_gesture_controller = __esm({
1916
2054
  this.deps.settleAfterDrag(delta, velocity, kind);
1917
2055
  },
1918
2056
  onCancel: () => {
2057
+ if (this.dragSuppressed) {
2058
+ this.dragSuppressed = false;
2059
+ return;
2060
+ }
1919
2061
  this.isDragging_ = false;
1920
2062
  this.keyboardDismissed = false;
1921
2063
  this.deps.getRoot()?.removeAttribute("data-dragging");
2064
+ this.deps.emit("dragend", {
2065
+ size: this.deps.getDragContext().size,
2066
+ velocity: 0
2067
+ });
1922
2068
  const restoreTo = this.dragStartSize;
1923
2069
  void this.deps.animateTo(restoreTo, 0);
1924
2070
  }
@@ -2003,6 +2149,306 @@ var init_scroll_cache = __esm({
2003
2149
  }
2004
2150
  });
2005
2151
 
2152
+ // src/core/primitives/anchor-animations.ts
2153
+ function resolveTransitionFrames(spec, phase) {
2154
+ const fallback = { frames: null, duration: 0, easing: DEFAULT_EASING };
2155
+ if (spec === "none") return fallback;
2156
+ if (spec === void 0 || typeof spec === "string") {
2157
+ const preset2 = PRESET_FRAMES[spec ?? "fade"];
2158
+ return {
2159
+ frames: preset2[phase],
2160
+ duration: DEFAULT_DURATION3,
2161
+ easing: DEFAULT_EASING
2162
+ };
2163
+ }
2164
+ const custom = phase === "enter" ? spec.enter : spec.exit;
2165
+ if (custom) {
2166
+ return {
2167
+ frames: custom,
2168
+ duration: spec.duration ?? DEFAULT_DURATION3,
2169
+ easing: spec.easing ?? DEFAULT_EASING
2170
+ };
2171
+ }
2172
+ if (spec.preset === "none") return fallback;
2173
+ const preset = PRESET_FRAMES[spec.preset ?? "fade"];
2174
+ return {
2175
+ frames: preset[phase],
2176
+ duration: spec.duration ?? DEFAULT_DURATION3,
2177
+ easing: spec.easing ?? DEFAULT_EASING
2178
+ };
2179
+ }
2180
+ function runAnchorTransition(el, spec, phase) {
2181
+ const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
2182
+ if (respectReduced && prefersReducedMotion()) return INSTANT;
2183
+ const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
2184
+ if (!frames || duration <= 0) return INSTANT;
2185
+ if (typeof el.animate !== "function") return INSTANT;
2186
+ const animation = el.animate(frames, {
2187
+ duration,
2188
+ easing,
2189
+ fill: "both"
2190
+ });
2191
+ const finished = animation.finished.then(() => void 0).catch(() => void 0);
2192
+ return {
2193
+ finished,
2194
+ cancel: () => {
2195
+ try {
2196
+ animation.cancel();
2197
+ } catch {
2198
+ return;
2199
+ }
2200
+ }
2201
+ };
2202
+ }
2203
+ var DEFAULT_DURATION3, DEFAULT_EASING, PRESET_FRAMES, INSTANT;
2204
+ var init_anchor_animations = __esm({
2205
+ "src/core/primitives/anchor-animations.ts"() {
2206
+ init_animation();
2207
+ DEFAULT_DURATION3 = 200;
2208
+ DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
2209
+ PRESET_FRAMES = {
2210
+ fade: {
2211
+ enter: [{ opacity: 0 }, { opacity: 1 }],
2212
+ exit: [{ opacity: 1 }, { opacity: 0 }]
2213
+ },
2214
+ scale: {
2215
+ enter: [
2216
+ { opacity: 0, transform: "scale(0.85)" },
2217
+ { opacity: 1, transform: "scale(1)" }
2218
+ ],
2219
+ exit: [
2220
+ { opacity: 1, transform: "scale(1)" },
2221
+ { opacity: 0, transform: "scale(0.85)" }
2222
+ ]
2223
+ },
2224
+ slide: {
2225
+ enter: [
2226
+ { opacity: 0, transform: "translateY(10px)" },
2227
+ { opacity: 1, transform: "translateY(0)" }
2228
+ ],
2229
+ exit: [
2230
+ { opacity: 1, transform: "translateY(0)" },
2231
+ { opacity: 0, transform: "translateY(10px)" }
2232
+ ]
2233
+ },
2234
+ pop: {
2235
+ enter: [
2236
+ { opacity: 0, transform: "scale(0.6)", offset: 0 },
2237
+ { opacity: 1, transform: "scale(1.06)", offset: 0.7 },
2238
+ { opacity: 1, transform: "scale(1)", offset: 1 }
2239
+ ],
2240
+ exit: [
2241
+ { opacity: 1, transform: "scale(1)" },
2242
+ { opacity: 0, transform: "scale(0.6)" }
2243
+ ]
2244
+ }
2245
+ };
2246
+ INSTANT = {
2247
+ finished: Promise.resolve(),
2248
+ cancel: () => {
2249
+ }
2250
+ };
2251
+ }
2252
+ });
2253
+
2254
+ // src/core/features/sheet-anchors.ts
2255
+ function attachAnchor(deps, opts) {
2256
+ const doc = deps.host.ownerDocument;
2257
+ const wrapper = doc.createElement("div");
2258
+ wrapper.className = "bs-anchor";
2259
+ const interactive = opts.interactive ?? true;
2260
+ const position = opts.position ?? "sheet-top-right";
2261
+ const isDock = position === "dock-bottom" || position === "dock-top";
2262
+ const ws = wrapper.style;
2263
+ ws.position = "fixed";
2264
+ if (isDock) {
2265
+ ws.left = "0";
2266
+ ws.right = "0";
2267
+ if (position === "dock-bottom") {
2268
+ ws.bottom = "0";
2269
+ } else {
2270
+ ws.top = "0";
2271
+ }
2272
+ } else {
2273
+ applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2274
+ }
2275
+ if (opts.fadeRange) {
2276
+ const [r0, r1] = opts.fadeRange;
2277
+ const span = Math.max(r1 - r0, 1e-4);
2278
+ ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
2279
+ }
2280
+ wrapper.appendChild(opts.element);
2281
+ deps.host.appendChild(wrapper);
2282
+ let visible = null;
2283
+ let inFlight = null;
2284
+ const shouldShow = (state) => {
2285
+ if (typeof opts.showOn === "function") return opts.showOn(state);
2286
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2287
+ return isDock || state.size > 0;
2288
+ };
2289
+ const applyVisibility = (next, animate) => {
2290
+ if (next === visible) return;
2291
+ visible = next;
2292
+ inFlight?.cancel();
2293
+ inFlight = null;
2294
+ wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
2295
+ if (next) {
2296
+ wrapper.style.visibility = "";
2297
+ if (animate) {
2298
+ inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
2299
+ }
2300
+ return;
2301
+ }
2302
+ if (!animate) {
2303
+ wrapper.style.visibility = "hidden";
2304
+ return;
2305
+ }
2306
+ const handle = runAnchorTransition(opts.element, opts.animation, "exit");
2307
+ inFlight = handle;
2308
+ void handle.finished.then(() => {
2309
+ if (inFlight === handle && visible === false) {
2310
+ wrapper.style.visibility = "hidden";
2311
+ }
2312
+ });
2313
+ };
2314
+ const evaluate = (animate) => {
2315
+ if (deps.isDestroyed()) return;
2316
+ applyVisibility(shouldShow(deps.getState()), animate);
2317
+ };
2318
+ const offs = [
2319
+ deps.on("snap", () => evaluate(true)),
2320
+ deps.on("open", () => evaluate(true)),
2321
+ deps.on("close", () => evaluate(true))
2322
+ ];
2323
+ evaluate(false);
2324
+ return {
2325
+ wrapper,
2326
+ detach: () => {
2327
+ offs.forEach((off) => off());
2328
+ inFlight?.cancel();
2329
+ inFlight = null;
2330
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
2331
+ },
2332
+ syncZ: (z) => {
2333
+ wrapper.style.zIndex = String(z);
2334
+ }
2335
+ };
2336
+ }
2337
+ var init_sheet_anchors = __esm({
2338
+ "src/core/features/sheet-anchors.ts"() {
2339
+ init_overlay_position();
2340
+ init_anchor_animations();
2341
+ }
2342
+ });
2343
+
2344
+ // src/core/features/scrim-stages.ts
2345
+ function installScrimStages(deps, opts) {
2346
+ const doc = deps.host.ownerDocument;
2347
+ const entries = opts.stages.map((def) => {
2348
+ const wrapper = doc.createElement("div");
2349
+ wrapper.className = "bs-scrim-stage";
2350
+ const ws = wrapper.style;
2351
+ ws.position = "absolute";
2352
+ applyOverlayPosition(
2353
+ ws,
2354
+ deps.mode,
2355
+ def.position ?? opts.position ?? "center",
2356
+ def.inset ?? opts.inset ?? "16px"
2357
+ );
2358
+ ws.visibility = "hidden";
2359
+ ws.pointerEvents = "none";
2360
+ wrapper.appendChild(def.element);
2361
+ deps.host.appendChild(wrapper);
2362
+ return { def, wrapper, inFlight: null };
2363
+ });
2364
+ let active = null;
2365
+ const resolveActive = () => {
2366
+ const state = deps.getState();
2367
+ if (state.size === 0) {
2368
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2369
+ }
2370
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2371
+ };
2372
+ const hide = (entry, animate) => {
2373
+ entry.inFlight?.cancel();
2374
+ entry.wrapper.style.pointerEvents = "none";
2375
+ if (!animate) {
2376
+ entry.inFlight = null;
2377
+ entry.wrapper.style.visibility = "hidden";
2378
+ return;
2379
+ }
2380
+ const handle = runAnchorTransition(
2381
+ entry.def.element,
2382
+ entry.def.animation ?? opts.animation,
2383
+ "exit"
2384
+ );
2385
+ entry.inFlight = handle;
2386
+ void handle.finished.then(() => {
2387
+ if (entry.inFlight === handle && active !== entry) {
2388
+ entry.wrapper.style.visibility = "hidden";
2389
+ }
2390
+ });
2391
+ };
2392
+ const show = (entry, animate) => {
2393
+ entry.inFlight?.cancel();
2394
+ entry.inFlight = null;
2395
+ entry.wrapper.style.visibility = "";
2396
+ entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
2397
+ if (animate) {
2398
+ entry.inFlight = runAnchorTransition(
2399
+ entry.def.element,
2400
+ entry.def.animation ?? opts.animation,
2401
+ "enter"
2402
+ );
2403
+ }
2404
+ };
2405
+ const update = (animate) => {
2406
+ if (deps.isDestroyed()) return;
2407
+ const next = resolveActive();
2408
+ if (next === active) return;
2409
+ const prev = active;
2410
+ active = next;
2411
+ if (prev) hide(prev, animate);
2412
+ if (next) show(next, animate);
2413
+ };
2414
+ const offs = [
2415
+ deps.on("snap", () => update(true)),
2416
+ deps.on("open", () => update(true)),
2417
+ deps.on("close", () => update(true))
2418
+ ];
2419
+ if (entries.some((e) => e.def.forRange)) {
2420
+ offs.push(deps.on("progress", () => update(true)));
2421
+ }
2422
+ update(false);
2423
+ return () => {
2424
+ offs.forEach((off) => off());
2425
+ for (const entry of entries) {
2426
+ entry.inFlight?.cancel();
2427
+ entry.inFlight = null;
2428
+ if (entry.wrapper.parentNode) {
2429
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2430
+ }
2431
+ }
2432
+ active = null;
2433
+ };
2434
+ }
2435
+ var matchesId, matchesRange;
2436
+ var init_scrim_stages = __esm({
2437
+ "src/core/features/scrim-stages.ts"() {
2438
+ init_overlay_position();
2439
+ init_anchor_animations();
2440
+ matchesId = (def, activeId) => {
2441
+ if (def.for === void 0) return false;
2442
+ return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
2443
+ };
2444
+ matchesRange = (def, progress) => {
2445
+ if (!def.forRange) return false;
2446
+ const [from, to] = def.forRange;
2447
+ return progress >= from && progress <= to;
2448
+ };
2449
+ }
2450
+ });
2451
+
2006
2452
  // src/core/primitives/engine-options.ts
2007
2453
  function resolveEngineOptions(opts, extras) {
2008
2454
  const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
@@ -2019,6 +2465,12 @@ function resolveEngineOptions(opts, extras) {
2019
2465
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2020
2466
  rubberBandEnabled: opts.rubberBand ?? true,
2021
2467
  closeOnBack: opts.closeOnBack ?? false,
2468
+ persistent: opts.persistent ?? false,
2469
+ disableClose: opts.disableClose ?? false,
2470
+ disableDrag: opts.disableDrag ?? false,
2471
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2472
+ radius: opts.radius,
2473
+ maxHeight: opts.maxHeight,
2022
2474
  initialAllowed,
2023
2475
  initialId,
2024
2476
  scrim: {
@@ -2045,7 +2497,8 @@ function resolveEngineOptions(opts, extras) {
2045
2497
  closeOnEscape: opts.closeOnEscape,
2046
2498
  lockBodyScroll: opts.lockBodyScroll,
2047
2499
  inertSiblings: opts.inertSiblings,
2048
- shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2500
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2501
+ returnFocus: opts.returnFocusTo
2049
2502
  }
2050
2503
  };
2051
2504
  }
@@ -2083,6 +2536,8 @@ var init_BottomSheetEngine = __esm({
2083
2536
  init_aria_slider_writer();
2084
2537
  init_hot_path_thresholds();
2085
2538
  init_scroll_cache();
2539
+ init_sheet_anchors();
2540
+ init_scrim_stages();
2086
2541
  init_engine_options();
2087
2542
  init_opacity_dedup();
2088
2543
  HAPTIC_DURATION_MS = 8;
@@ -2124,6 +2579,11 @@ var init_BottomSheetEngine = __esm({
2124
2579
  __publicField(this, "lifecycle");
2125
2580
  __publicField(this, "closeOnBack");
2126
2581
  __publicField(this, "routedTo");
2582
+ __publicField(this, "persistent");
2583
+ __publicField(this, "disableCloseFlag");
2584
+ __publicField(this, "disableDragFlag");
2585
+ __publicField(this, "closeOnRouteChange");
2586
+ __publicField(this, "maxHeightCap");
2127
2587
  __publicField(this, "snapPointsRaw");
2128
2588
  __publicField(this, "snaps");
2129
2589
  __publicField(this, "activeId");
@@ -2136,6 +2596,7 @@ var init_BottomSheetEngine = __esm({
2136
2596
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2137
2597
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2138
2598
  __publicField(this, "isTopSheet", true);
2599
+ __publicField(this, "opening", false);
2139
2600
  __publicField(this, "progressPayload", {
2140
2601
  value: 0,
2141
2602
  size: 0
@@ -2151,6 +2612,12 @@ var init_BottomSheetEngine = __esm({
2151
2612
  __publicField(this, "linkedSheets", []);
2152
2613
  __publicField(this, "scrollCache");
2153
2614
  __publicField(this, "teardowns", new TeardownStack());
2615
+ __publicField(this, "anchors", []);
2616
+ __publicField(this, "anchorHost", null);
2617
+ __publicField(this, "stackZ", 100);
2618
+ __publicField(this, "detachScrimStages", null);
2619
+ __publicField(this, "stackEffectEnabled", false);
2620
+ __publicField(this, "stackEffectPrimed", false);
2154
2621
  __publicField(this, "transformTemplate");
2155
2622
  const resolved = resolveEngineOptions(opts, {
2156
2623
  shouldApplyInertSiblings: () => {
@@ -2184,6 +2651,10 @@ var init_BottomSheetEngine = __esm({
2184
2651
  getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
2185
2652
  snapTo: (id) => {
2186
2653
  void this.snapTo(id);
2654
+ },
2655
+ close: () => {
2656
+ if (!this.canDismiss()) return;
2657
+ void this.close("backdrop");
2187
2658
  }
2188
2659
  },
2189
2660
  resolved.scrim
@@ -2207,6 +2678,11 @@ var init_BottomSheetEngine = __esm({
2207
2678
  );
2208
2679
  this.closeOnBack = resolved.closeOnBack;
2209
2680
  this.routedTo = opts.routedTo;
2681
+ this.persistent = resolved.persistent;
2682
+ this.disableCloseFlag = resolved.disableClose;
2683
+ this.disableDragFlag = resolved.disableDrag;
2684
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2685
+ this.stackEffectEnabled = opts.stackEffect ?? false;
2210
2686
  this.snapPointsRaw = opts.snapPoints;
2211
2687
  this.persistKey = opts.persistKey;
2212
2688
  if (opts.linkedSheets) {
@@ -2219,7 +2695,7 @@ var init_BottomSheetEngine = __esm({
2219
2695
  this.snapPointsRaw,
2220
2696
  resolved.initialAllowed,
2221
2697
  this.mode,
2222
- () => this.handle.offsetHeight,
2698
+ () => this.measureFitSize(),
2223
2699
  (maxAxisSize) => {
2224
2700
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2225
2701
  }
@@ -2253,6 +2729,19 @@ var init_BottomSheetEngine = __esm({
2253
2729
  io.observe(this.element);
2254
2730
  this.teardowns.add(() => io.disconnect());
2255
2731
  }
2732
+ this.installFitObserver();
2733
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2734
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2735
+ this.applyAutoAriaLabelledBy();
2736
+ if (this.closeOnRouteChange) {
2737
+ this.teardowns.add(
2738
+ installRouteChange({
2739
+ isDestroyed: () => this.destroyed,
2740
+ getSize: () => this.size,
2741
+ close: () => this.close()
2742
+ })
2743
+ );
2744
+ }
2256
2745
  if (this.persistKey) {
2257
2746
  this.teardowns.add(installPersist(this, this.persistKey));
2258
2747
  }
@@ -2277,7 +2766,7 @@ var init_BottomSheetEngine = __esm({
2277
2766
  isTopSheet: () => this.isTopSheet,
2278
2767
  getSize: () => this.size,
2279
2768
  isDestroyed: () => this.destroyed,
2280
- close: () => this.close(),
2769
+ close: () => this.close("back"),
2281
2770
  on: (event, fn) => this.on(event, fn),
2282
2771
  sheetId: this.id
2283
2772
  })
@@ -2371,6 +2860,10 @@ var init_BottomSheetEngine = __esm({
2371
2860
  const previousSize = this.size;
2372
2861
  this.scrollCache.cache(previousId, previousSize, target.size);
2373
2862
  this.activeId = id;
2863
+ if (wasClosed && target.size > 0) {
2864
+ this.opening = true;
2865
+ sheetStack.promote(this.id);
2866
+ }
2374
2867
  if (this.animation.viewTransitionsAvailable) {
2375
2868
  this.currentViewTransition?.skipTransition?.();
2376
2869
  const vt = document.startViewTransition(() => {
@@ -2385,19 +2878,26 @@ var init_BottomSheetEngine = __esm({
2385
2878
  } else {
2386
2879
  await this.animation.animateTo(target.size, velocityPxPerMs);
2387
2880
  }
2881
+ this.opening = false;
2388
2882
  externalSignal?.removeEventListener("abort", onExternalAbort);
2389
2883
  if (signal.aborted) return;
2390
2884
  this.scrollCache.restore(id, previousSize, target.size);
2391
2885
  this.updateAriaSlider();
2392
- this.emit("snap", { id, size: target.size });
2886
+ this.emit("snap", {
2887
+ id,
2888
+ size: target.size,
2889
+ progress: this.computeProgress(target.size)
2890
+ });
2393
2891
  if (wasClosed && target.size > 0) {
2394
2892
  this.emit("open", { id });
2395
2893
  this.handleOpen();
2396
2894
  notifyLinkedSheets(this.linkedSheets, this);
2895
+ this.emit("opened", { id });
2397
2896
  }
2398
2897
  if (target.size === 0) {
2399
2898
  this.emit("close", void 0);
2400
2899
  this.handleClose();
2900
+ this.emit("closed", void 0);
2401
2901
  }
2402
2902
  }
2403
2903
  async dragTo(targetSize, velocityOrOpts = 0) {
@@ -2422,18 +2922,76 @@ var init_BottomSheetEngine = __esm({
2422
2922
  const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
2423
2923
  return this.snapTo(target);
2424
2924
  }
2425
- close() {
2925
+ close(reason = "programmatic") {
2426
2926
  if (this.destroyed) return Promise.resolve();
2927
+ if (this.disableCloseFlag) return Promise.resolve();
2928
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2427
2929
  const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
2428
2930
  return this.snapTo(closedId ?? this.activeId);
2429
2931
  }
2430
- setAllowed(ids, snap) {
2932
+ canDismiss() {
2933
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2934
+ }
2935
+ isTop() {
2936
+ return this.isTopSheet;
2937
+ }
2938
+ depth() {
2939
+ return sheetStack.depthOf(this.id);
2940
+ }
2941
+ expand() {
2942
+ if (this.destroyed) return Promise.resolve();
2943
+ const ids = this.allowedIdsBySize();
2944
+ const target = ids[ids.length - 1];
2945
+ if (!target) return Promise.resolve();
2946
+ return this.snapTo(target);
2947
+ }
2948
+ collapse() {
2949
+ if (this.destroyed) return Promise.resolve();
2950
+ const ids = this.allowedIdsBySize();
2951
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
2952
+ const target = nonZero ?? ids[0];
2953
+ if (!target) return Promise.resolve();
2954
+ return this.snapTo(target);
2955
+ }
2956
+ setRadius(r) {
2431
2957
  if (this.destroyed) return;
2432
- const willSnap = snap !== void 0 && ids.includes(snap) || !ids.includes(this.activeId) && ids.length > 0;
2433
- if (!willSnap) {
2434
- this.animation.cancel();
2435
- this.newCycle();
2958
+ this.element.style.setProperty(
2959
+ "--bs-radius",
2960
+ typeof r === "number" ? `${r}px` : r
2961
+ );
2962
+ }
2963
+ setMaxHeight(h) {
2964
+ if (this.destroyed) return;
2965
+ const value = typeof h === "number" ? `${h}px` : h;
2966
+ const axis = layoutAxis(this.mode);
2967
+ this.element.style.setProperty(
2968
+ axis === "height" ? "maxHeight" : "maxWidth",
2969
+ value
2970
+ );
2971
+ if (typeof h === "number") {
2972
+ this.maxHeightCap = h;
2973
+ this.clampToMaxHeight();
2974
+ } else if (typeof window !== "undefined") {
2975
+ const probe2 = document.createElement("div");
2976
+ probe2.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${axis === "height" ? "height" : "width"}:${value};`;
2977
+ document.body.appendChild(probe2);
2978
+ const measured = axis === "height" ? probe2.offsetHeight : probe2.offsetWidth;
2979
+ probe2.remove();
2980
+ this.maxHeightCap = measured > 0 ? measured : void 0;
2981
+ this.clampToMaxHeight();
2982
+ }
2983
+ }
2984
+ clampToMaxHeight() {
2985
+ if (this.maxHeightCap === void 0) return;
2986
+ if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2987
+ this.snaps.setMaxAxisSize(this.maxHeightCap);
2988
+ }
2989
+ if (this.size > this.maxHeightCap && !this.gesture?.isDragging) {
2990
+ this.applySize(this.maxHeightCap);
2436
2991
  }
2992
+ }
2993
+ setAllowed(ids, snap) {
2994
+ if (this.destroyed) return;
2437
2995
  this.snaps.setAllowedIds(ids);
2438
2996
  this.updateAriaSlider();
2439
2997
  if (snap && ids.includes(snap)) {
@@ -2473,6 +3031,79 @@ var init_BottomSheetEngine = __esm({
2473
3031
  setScrimOverlay(opts) {
2474
3032
  return this.scrim.setScrimOverlay(opts);
2475
3033
  }
3034
+ addAnchor(opts) {
3035
+ if (this.destroyed || typeof document === "undefined") return () => {
3036
+ };
3037
+ if (!this.anchorHost) {
3038
+ this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
3039
+ this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
3040
+ this.anchorHost.style.setProperty(
3041
+ "--bs-progress",
3042
+ String(this.computeProgress(this.size))
3043
+ );
3044
+ }
3045
+ const handle = attachAnchor(
3046
+ {
3047
+ mode: this.mode,
3048
+ host: this.anchorHost,
3049
+ getState: () => ({
3050
+ activeId: this.activeId,
3051
+ size: this.size,
3052
+ progress: this.computeProgress(this.size)
3053
+ }),
3054
+ on: (event, fn) => this.on(event, fn),
3055
+ isDestroyed: () => this.destroyed
3056
+ },
3057
+ opts
3058
+ );
3059
+ handle.syncZ(this.stackZ + 1);
3060
+ this.anchors.push(handle);
3061
+ const detach = () => {
3062
+ const idx = this.anchors.indexOf(handle);
3063
+ if (idx === -1) return;
3064
+ this.anchors.splice(idx, 1);
3065
+ handle.detach();
3066
+ };
3067
+ this.teardowns.add(detach);
3068
+ return detach;
3069
+ }
3070
+ setScrimStages(opts) {
3071
+ if (this.destroyed || typeof document === "undefined") return () => {
3072
+ };
3073
+ this.detachScrimStages?.();
3074
+ this.detachScrimStages = null;
3075
+ if (!opts) return () => {
3076
+ };
3077
+ const host = this.screenComponent?.parentElement;
3078
+ if (!host) {
3079
+ console.warn(
3080
+ "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
3081
+ );
3082
+ return () => {
3083
+ };
3084
+ }
3085
+ const detachInstalled = installScrimStages(
3086
+ {
3087
+ mode: this.mode,
3088
+ host,
3089
+ getState: () => ({
3090
+ activeId: this.activeId,
3091
+ size: this.size,
3092
+ progress: this.computeProgress(this.size)
3093
+ }),
3094
+ on: (event, fn) => this.on(event, fn),
3095
+ isDestroyed: () => this.destroyed
3096
+ },
3097
+ opts
3098
+ );
3099
+ const detach = () => {
3100
+ if (this.detachScrimStages === detach) this.detachScrimStages = null;
3101
+ detachInstalled();
3102
+ };
3103
+ this.detachScrimStages = detach;
3104
+ this.teardowns.add(() => this.detachScrimStages?.());
3105
+ return detach;
3106
+ }
2476
3107
  getScrimState() {
2477
3108
  return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
2478
3109
  }
@@ -2492,8 +3123,53 @@ var init_BottomSheetEngine = __esm({
2492
3123
  }
2493
3124
  this.updateAriaSlider();
2494
3125
  }
3126
+ recompute() {
3127
+ if (this.destroyed) return;
3128
+ this.snaps.recompute();
3129
+ this.scrim.invalidateOpacityCache();
3130
+ if (this.gesture?.isDragging) return;
3131
+ const current = this.snaps.findById(this.activeId);
3132
+ if (current) {
3133
+ this.size = current.size;
3134
+ this.applySize(this.size);
3135
+ this.updateAriaSlider();
3136
+ }
3137
+ }
3138
+ measureFitSize() {
3139
+ const vertical = layoutAxis(this.mode) === "height";
3140
+ const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
3141
+ const content = this.scrollContainer;
3142
+ const natural = content ? handleSize + (vertical ? content.scrollHeight : content.scrollWidth) : handleSize;
3143
+ if (typeof window === "undefined") return natural;
3144
+ const viewport = vertical ? window.innerHeight : window.innerWidth;
3145
+ return viewport > 0 ? Math.min(natural, viewport) : natural;
3146
+ }
3147
+ installFitObserver() {
3148
+ if (typeof ResizeObserver === "undefined") return;
3149
+ if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
3150
+ return;
3151
+ const targets = [this.handle];
3152
+ if (this.scrollContainer && this.scrollContainer !== this.handle) {
3153
+ targets.push(this.scrollContainer);
3154
+ }
3155
+ let raf = 0;
3156
+ const ro = new ResizeObserver(() => {
3157
+ if (raf) return;
3158
+ raf = requestAnimationFrame(() => {
3159
+ raf = 0;
3160
+ if (this.destroyed || this.gesture?.isDragging) return;
3161
+ this.recompute();
3162
+ });
3163
+ });
3164
+ for (const t of targets) ro.observe(t);
3165
+ this.teardowns.add(() => {
3166
+ ro.disconnect();
3167
+ if (raf) cancelAnimationFrame(raf);
3168
+ });
3169
+ }
2495
3170
  destroy() {
2496
3171
  this.destroyed = true;
3172
+ this.opening = false;
2497
3173
  this.currentAbort.abort();
2498
3174
  this.currentViewTransition?.skipTransition?.();
2499
3175
  this.currentViewTransition = null;
@@ -2543,6 +3219,36 @@ var init_BottomSheetEngine = __esm({
2543
3219
  frozen = true;
2544
3220
  return cancelled;
2545
3221
  }
3222
+ emitBeforeClose(reason) {
3223
+ let cancelled = false;
3224
+ let frozen = false;
3225
+ this.emit("before-close", {
3226
+ reason,
3227
+ cancel: () => {
3228
+ if (frozen) {
3229
+ console.warn(
3230
+ "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3231
+ );
3232
+ return;
3233
+ }
3234
+ cancelled = true;
3235
+ }
3236
+ });
3237
+ frozen = true;
3238
+ return cancelled;
3239
+ }
3240
+ applyAutoAriaLabelledBy() {
3241
+ if (typeof document === "undefined") return;
3242
+ if (!this.lifecycle.focusTrapEnabled) return;
3243
+ if (this.element.getAttribute("aria-labelledby")) return;
3244
+ const scope = this.handle ?? this.element;
3245
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3246
+ if (!titled) return;
3247
+ if (!titled.id) {
3248
+ titled.id = nextInstanceId("bs-title");
3249
+ }
3250
+ this.element.setAttribute("aria-labelledby", titled.id);
3251
+ }
2546
3252
  recomputeSnaps() {
2547
3253
  const seen = /* @__PURE__ */ new Set();
2548
3254
  for (const p of this.snapPointsRaw) {
@@ -2559,24 +3265,42 @@ var init_BottomSheetEngine = __esm({
2559
3265
  getAllowedRange() {
2560
3266
  return this.snaps.getAllowedRange();
2561
3267
  }
3268
+ allowedIdsBySize() {
3269
+ return this.snaps.getAllowedIds().slice().sort(
3270
+ (a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0)
3271
+ );
3272
+ }
2562
3273
  updateAriaSlider() {
2563
- this.aria.setValue(this.snaps.getAllowedIds(), this.activeId);
3274
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
2564
3275
  }
2565
3276
  registerInStack() {
2566
3277
  this.teardowns.add(sheetStack.push({
2567
3278
  id: this.id,
2568
3279
  setZIndex: (z) => {
3280
+ this.stackZ = z;
2569
3281
  this.element.style.zIndex = String(z);
2570
3282
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3283
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
2571
3284
  },
2572
3285
  setIsTop: (isTop) => {
2573
3286
  this.isTopSheet = isTop;
2574
- if (this.backdrop) {
2575
- this.backdrop.style.display = isTop ? "" : "none";
2576
- }
2577
- }
3287
+ },
3288
+ isOpen: () => this.size > 0 || this.opening,
3289
+ setDepth: (depth) => this.applyStackDepth(depth)
2578
3290
  }));
2579
3291
  }
3292
+ applyStackDepth(depth) {
3293
+ if (!this.stackEffectEnabled) return;
3294
+ this.element.setAttribute("data-stack-depth", String(depth));
3295
+ if (!this.stackEffectPrimed) {
3296
+ this.stackEffectPrimed = true;
3297
+ const isVertical = this.mode === "bottom" || this.mode === "top";
3298
+ this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
3299
+ this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
3300
+ }
3301
+ const scale = Math.max(1 - depth * 0.04, 0.86);
3302
+ this.element.style.scale = scale === 1 ? "" : String(scale);
3303
+ }
2580
3304
  attach() {
2581
3305
  this.gesture = new GestureController({
2582
3306
  handle: this.handle,
@@ -2593,6 +3317,7 @@ var init_BottomSheetEngine = __esm({
2593
3317
  buf.rubberBandEnabled = this.rubberBandEnabled;
2594
3318
  return buf;
2595
3319
  },
3320
+ getDisableDrag: () => this.disableDragFlag,
2596
3321
  cancelAnimation: () => this.animation.cancel(),
2597
3322
  applySize: (size) => this.applySize(size),
2598
3323
  animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
@@ -2636,7 +3361,7 @@ var init_BottomSheetEngine = __esm({
2636
3361
  container: this.scrollContainer,
2637
3362
  isDragging: () => this.gesture?.isDragging ?? false,
2638
3363
  isAnimating: () => this.animation.isAnimating,
2639
- getAllowedIds: () => this.snaps.getAllowedIds(),
3364
+ getAllowedIds: () => this.allowedIdsBySize(),
2640
3365
  getActiveId: () => this.activeId,
2641
3366
  snapTo: (id) => {
2642
3367
  void this.snapTo(id);
@@ -2665,13 +3390,16 @@ var init_BottomSheetEngine = __esm({
2665
3390
  },
2666
3391
  newCycle: () => {
2667
3392
  this.newCycle();
2668
- }
3393
+ },
3394
+ isAnimating: () => this.animation.isAnimating,
3395
+ resyncAfterCancel: () => this.resyncAfterResize()
2669
3396
  }));
2670
3397
  if (this.lifecycle.closeOnEscape) {
2671
3398
  const onKey = (e) => {
2672
- if (this.destroyed) return;
3399
+ if (this.destroyed || e.defaultPrevented) return;
3400
+ if (!this.canDismiss()) return;
2673
3401
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2674
- void this.close();
3402
+ void this.close("escape");
2675
3403
  }
2676
3404
  };
2677
3405
  document.addEventListener("keydown", onKey);
@@ -2701,7 +3429,7 @@ var init_BottomSheetEngine = __esm({
2701
3429
  handle: this.handle,
2702
3430
  mode: this.mode,
2703
3431
  isDestroyed: () => this.destroyed,
2704
- getAllowedIds: () => this.snaps.getAllowedIds(),
3432
+ getAllowedIds: () => this.allowedIdsBySize(),
2705
3433
  getActiveId: () => this.activeId,
2706
3434
  snapTo: (id) => {
2707
3435
  void this.snapTo(id);
@@ -2722,6 +3450,7 @@ var init_BottomSheetEngine = __esm({
2722
3450
  });
2723
3451
  if (!target) return;
2724
3452
  const previousId = this.activeId;
3453
+ if (target.id === previousId && target.size === this.size) return;
2725
3454
  if (this.emitBeforeSnap(target, previousId)) {
2726
3455
  const restore = this.snaps.findById(previousId);
2727
3456
  if (restore) {
@@ -2730,21 +3459,34 @@ var init_BottomSheetEngine = __esm({
2730
3459
  return;
2731
3460
  }
2732
3461
  const previousSize = this.size;
3462
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
2733
3463
  this.scrollCache.cache(previousId, previousSize, target.size);
2734
3464
  this.activeId = target.id;
3465
+ if (previousSnapSize === 0 && target.size > 0) {
3466
+ this.opening = true;
3467
+ sheetStack.promote(this.id);
3468
+ }
2735
3469
  void this.animation.animateTo(target.size, velocity).then(() => {
3470
+ this.opening = false;
2736
3471
  if (signal.aborted) return;
2737
3472
  this.scrollCache.restore(target.id, previousSize, target.size);
2738
3473
  this.updateAriaSlider();
2739
- this.emit("snap", { id: target.id, size: target.size });
3474
+ this.emit("snap", {
3475
+ id: target.id,
3476
+ size: target.size,
3477
+ progress: this.computeProgress(target.size)
3478
+ });
2740
3479
  this.haptic();
2741
- if (previousId === "closed" && target.size > 0) {
3480
+ if (previousSnapSize === 0 && target.size > 0) {
2742
3481
  this.emit("open", { id: target.id });
2743
3482
  this.handleOpen();
3483
+ notifyLinkedSheets(this.linkedSheets, this);
3484
+ this.emit("opened", { id: target.id });
2744
3485
  }
2745
3486
  if (target.size === 0) {
2746
3487
  this.emit("close", void 0);
2747
3488
  this.handleClose();
3489
+ this.emit("closed", void 0);
2748
3490
  }
2749
3491
  });
2750
3492
  }
@@ -2766,6 +3508,9 @@ var init_BottomSheetEngine = __esm({
2766
3508
  if (this.scrimParent) {
2767
3509
  this.scrimParent.style.setProperty("--bs-size", `${size}px`);
2768
3510
  }
3511
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3512
+ this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3513
+ }
2769
3514
  }
2770
3515
  const progress = this.computeProgress(size);
2771
3516
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -2774,6 +3519,9 @@ var init_BottomSheetEngine = __esm({
2774
3519
  );
2775
3520
  if (progressChanged) {
2776
3521
  style.setProperty("--bs-progress", String(progress));
3522
+ if (this.anchorHost) {
3523
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3524
+ }
2777
3525
  }
2778
3526
  this.scrim.applyOpacity(progress, progressChanged);
2779
3527
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -2788,6 +3536,7 @@ var init_BottomSheetEngine = __esm({
2788
3536
  return Math.min(Math.max((size - min) / (max - min), 0), 1);
2789
3537
  }
2790
3538
  handleOpen() {
3539
+ sheetStack.promote(this.id);
2791
3540
  try {
2792
3541
  this.lifecycle.install();
2793
3542
  } catch (err) {
@@ -2801,8 +3550,31 @@ var init_BottomSheetEngine = __esm({
2801
3550
  });
2802
3551
  }
2803
3552
  }
3553
+ resyncAfterResize() {
3554
+ if (this.destroyed) return;
3555
+ const target = this.snaps.findById(this.activeId);
3556
+ if (!target) return;
3557
+ this.updateAriaSlider();
3558
+ this.emit("snap", {
3559
+ id: target.id,
3560
+ size: target.size,
3561
+ progress: this.computeProgress(target.size)
3562
+ });
3563
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3564
+ this.emit("open", { id: target.id });
3565
+ this.handleOpen();
3566
+ notifyLinkedSheets(this.linkedSheets, this);
3567
+ this.emit("opened", { id: target.id });
3568
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3569
+ this.emit("close", void 0);
3570
+ this.handleClose();
3571
+ this.emit("closed", void 0);
3572
+ }
3573
+ }
2804
3574
  handleClose() {
3575
+ this.opening = false;
2805
3576
  this.lifecycle.release();
3577
+ sheetStack.update();
2806
3578
  }
2807
3579
  };
2808
3580
  }
@@ -2812,7 +3584,7 @@ var init_BottomSheetEngine = __esm({
2812
3584
  var baseStyles;
2813
3585
  var init_baseStyles = __esm({
2814
3586
  "src/web-component/baseStyles.ts"() {
2815
- baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--bs-shadow:0 -3px 12px rgba(0,0,0,0.12);--bs-radius:20px;--bs-handle-color:#b5b3a8;--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-header-min-height:80px;--bs-backdrop-color:rgba(0,0,0,0.5);--bs-scrim-color:rgba(0,0,0,0.4);--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color);opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface);box-shadow:var(--bs-shadow);z-index:var(--bs-z-sheet);overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius);border-top-right-radius:var(--bs-radius);border-top:1px solid var(--bs-surface-border);padding-bottom:env(safe-area-inset-bottom);box-shadow:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba(0,0,0,calc(var(--bs-progress,0) * 0.15 + 0.12))}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-bottom:1px solid var(--bs-surface-border);flex-direction:column-reverse;box-shadow:0 3px 12px rgba(0,0,0,0.12);padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-right:1px solid var(--bs-surface-border);box-shadow:3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius);border-bottom-left-radius:var(--bs-radius);border-left:1px solid var(--bs-surface-border);box-shadow:-3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:8px 16px 4px;cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface);-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius)}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color);border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface);z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:0 16px 16px}.bs-screen{position:absolute;inset:0;pointer-events:none;z-index:var(--bs-z-scrim)}.bs-scrim-overlay{z-index:calc(var(--bs-z-sheet) + 1);pointer-events:none}.bs-scrim{background:var(--bs-scrim-color);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-scrim,.bs-root[data-animating="true"] .bs-scrim{transition:none}.bs-scrim{backdrop-filter:blur(var(--bs-scrim-blur));-webkit-backdrop-filter:blur(var(--bs-scrim-blur))}.bs-button-slot{position:absolute;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface:#1a1a1d;--bs-surface-border:rgba(255,255,255,0.08);--bs-handle-color:rgba(255,255,255,0.45);--bs-shadow:0 -3px 16px rgba(0,0,0,0.5)}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,#ffffff);--ovl-surface-border:var(--bs-surface-border,rgba(0,0,0,0.08));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,20px);--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
3587
+ baseStyles = `:where(.bs-root){--bs-surface-d:#ffffff;--bs-surface-border-d:rgba(225,227,237,1);--bs-radius-d:20px;--bs-handle-color-d:rgba(26,22,20,0.3);--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-header-min-height:80px;--bs-backdrop-color-d:rgba(0,0,0,0.5);--bs-scrim-color-d:transparent;--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color,var(--bs-backdrop-color-d));opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.24s cubic-bezier(0.22,1,0.36,1);will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface,var(--bs-surface-d));box-shadow:var(--bs-shadow,var(--bs-shadow-auto,none));z-index:var(--bs-z-sheet);pointer-events:auto;overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius,var(--bs-radius-d));border-top-right-radius:var(--bs-radius,var(--bs-radius-d));border-top:1px solid var(--bs-surface-border,var(--bs-surface-border-d));padding-bottom:env(safe-area-inset-bottom);--bs-shadow-auto:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba(0,0,0,calc(var(--bs-progress,0) * 0.15 + 0.12))}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-right-radius:var(--bs-radius,var(--bs-radius-d));border-bottom:1px solid var(--bs-surface-border,var(--bs-surface-border-d));flex-direction:column-reverse;--bs-shadow-auto:0 3px 12px rgba(0,0,0,0.12);padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-right-radius:var(--bs-radius,var(--bs-radius-d));border-right:1px solid var(--bs-surface-border,var(--bs-surface-border-d));--bs-shadow-auto:3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-left-radius:var(--bs-radius,var(--bs-radius-d));border-left:1px solid var(--bs-surface-border,var(--bs-surface-border-d));--bs-shadow-auto:-3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:8px 16px 4px;cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface,var(--bs-surface-d));-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius,var(--bs-radius-d))}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color,var(--bs-handle-color-d));border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface,var(--bs-surface-d));z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border,var(--bs-surface-border-d))}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border,var(--bs-surface-border-d))}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color,var(--bs-handle-color-d)),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color,var(--bs-handle-color-d)),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color,var(--bs-handle-color-d)) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;user-select:text;padding:0 16px 16px}.bs-footer{flex:0 0 auto;padding:12px 16px calc(12px + env(safe-area-inset-bottom));border-top:1px solid var(--bs-surface-border,var(--bs-surface-border-d));background:var(--bs-surface,var(--bs-surface-d))}.bs-screen{position:absolute;inset:0;pointer-events:none;z-index:var(--bs-z-scrim);background:var(--bs-scrim-color,var(--bs-scrim-color-d));transition:opacity 0.24s cubic-bezier(0.22,1,0.36,1);will-change:opacity}.bs-root[data-dragging="true"] .bs-screen,.bs-root[data-animating="true"] .bs-screen{transition:none}.bs-scrim-overlay{z-index:calc(var(--bs-z-sheet) + 1);pointer-events:none}.bs-button-slot{position:absolute;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(100% + 12px)}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(100% + 12px)}.bs-sheet[data-mode="top"] .bs-button-slot{bottom:auto;top:calc(100% + 12px)}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface-d:#1a1a1d;--bs-surface-border-d:rgba(255,255,255,0.08);--bs-handle-color-d:rgba(255,255,255,0.45);--bs-shadow-auto:0 -3px 16px rgba(0,0,0,0.5)}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,var(--bs-surface-d,#ffffff));--ovl-surface-border:var(--bs-surface-border,var(--bs-surface-border-d,rgba(0,0,0,0.08)));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,var(--bs-radius-d,20px));--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
2816
3588
  }
2817
3589
  });
2818
3590
 
@@ -2822,7 +3594,7 @@ __export(BottomSheetElement_exports, {
2822
3594
  BottomSheetElement: () => BottomSheetElement,
2823
3595
  defineBottomSheet: () => defineBottomSheet
2824
3596
  });
2825
- var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, buildSlot, buildShadowTree, BottomSheetElement, registered, defineBottomSheet;
3597
+ var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, ATTR_PERSISTENT, ATTR_DISABLE_CLOSE, ATTR_DISABLE_DRAG, ATTR_CLOSE_ON_ROUTE_CHANGE, ATTR_RADIUS, ATTR_MAX_HEIGHT, ATTR_RETURN_FOCUS_TO, ATTR_BACKDROP_COLOR, ATTR_SCRIM_COLOR, ATTR_SNAP, LIVE_ATTRS, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, parseDimension, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions, buildSlot, buildShadowTree, BaseHTMLElement, BottomSheetElement, defineBottomSheet;
2826
3598
  var init_BottomSheetElement = __esm({
2827
3599
  "src/web-component/BottomSheetElement.ts"() {
2828
3600
  init_BottomSheetEngine();
@@ -2837,6 +3609,25 @@ var init_BottomSheetElement = __esm({
2837
3609
  ATTR_FOCUS_TRAP = "focus-trap";
2838
3610
  ATTR_CLOSE_ON_ESCAPE = "close-on-escape";
2839
3611
  ATTR_LOCK_BODY_SCROLL = "lock-body-scroll";
3612
+ ATTR_SHEET_LABEL = "sheet-label";
3613
+ ATTR_STACK_EFFECT = "stack-effect";
3614
+ ATTR_PERSISTENT = "persistent";
3615
+ ATTR_DISABLE_CLOSE = "disable-close";
3616
+ ATTR_DISABLE_DRAG = "disable-drag";
3617
+ ATTR_CLOSE_ON_ROUTE_CHANGE = "close-on-route-change";
3618
+ ATTR_RADIUS = "radius";
3619
+ ATTR_MAX_HEIGHT = "max-height";
3620
+ ATTR_RETURN_FOCUS_TO = "return-focus-to";
3621
+ ATTR_BACKDROP_COLOR = "backdrop-color";
3622
+ ATTR_SCRIM_COLOR = "scrim-color";
3623
+ ATTR_SNAP = "snap";
3624
+ LIVE_ATTRS = /* @__PURE__ */ new Set([
3625
+ ATTR_RADIUS,
3626
+ ATTR_MAX_HEIGHT,
3627
+ ATTR_BACKDROP_COLOR,
3628
+ ATTR_SCRIM_COLOR,
3629
+ ATTR_SNAP
3630
+ ]);
2840
3631
  isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
2841
3632
  DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
2842
3633
  parseSnapPoints = (raw) => {
@@ -2866,6 +3657,49 @@ var init_BottomSheetElement = __esm({
2866
3657
  parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
2867
3658
  parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
2868
3659
  parseList = (raw) => raw ? raw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
3660
+ parseDimension = (raw) => {
3661
+ if (raw === null) return void 0;
3662
+ const numeric = Number(raw);
3663
+ return Number.isFinite(numeric) && raw.trim() !== "" ? numeric : raw;
3664
+ };
3665
+ ANCHOR_POSITIONS = [
3666
+ "top-left",
3667
+ "top-center",
3668
+ "top-right",
3669
+ "center-left",
3670
+ "center",
3671
+ "center-right",
3672
+ "bottom-left",
3673
+ "bottom-center",
3674
+ "bottom-right",
3675
+ "sheet-top-left",
3676
+ "sheet-top-center",
3677
+ "sheet-top-right",
3678
+ "dock-bottom",
3679
+ "dock-top"
3680
+ ];
3681
+ ANCHOR_ANIMATIONS = [
3682
+ "fade",
3683
+ "scale",
3684
+ "slide",
3685
+ "pop",
3686
+ "none"
3687
+ ];
3688
+ parseAnchorOptions = (el) => {
3689
+ const position = el.getAttribute("data-position");
3690
+ const animation = el.getAttribute("data-animation");
3691
+ const showOn = parseList(el.getAttribute("data-show-on"));
3692
+ const fadeRangeRaw = parseList(el.getAttribute("data-fade-range"));
3693
+ const fadeRange = fadeRangeRaw?.length === 2 && fadeRangeRaw.every((v) => Number.isFinite(Number(v))) ? [Number(fadeRangeRaw[0]), Number(fadeRangeRaw[1])] : void 0;
3694
+ return {
3695
+ position: position && ANCHOR_POSITIONS.includes(position) ? position : void 0,
3696
+ inset: el.getAttribute("data-inset") ?? void 0,
3697
+ showOn,
3698
+ fadeRange,
3699
+ interactive: el.getAttribute("data-interactive") !== "false",
3700
+ animation: animation && ANCHOR_ANIMATIONS.includes(animation) ? animation : void 0
3701
+ };
3702
+ };
2869
3703
  buildSlot = (name) => {
2870
3704
  const slot = document.createElement("slot");
2871
3705
  slot.name = name;
@@ -2935,7 +3769,9 @@ var init_BottomSheetElement = __esm({
2935
3769
  }
2936
3770
  };
2937
3771
  };
2938
- BottomSheetElement = class extends HTMLElement {
3772
+ BaseHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
3773
+ };
3774
+ BottomSheetElement = class extends BaseHTMLElement {
2939
3775
  constructor() {
2940
3776
  super();
2941
3777
  __publicField(this, "engine", null);
@@ -2943,17 +3779,12 @@ var init_BottomSheetElement = __esm({
2943
3779
  __publicField(this, "offHandlers", []);
2944
3780
  __publicField(this, "refs");
2945
3781
  __publicField(this, "backdropClickHandler", null);
3782
+ __publicField(this, "stylesheetLinked", false);
3783
+ __publicField(this, "declarativeAnchors", []);
2946
3784
  this.root = this.attachShadow({ mode: "open" });
2947
3785
  const baseStyle = document.createElement("style");
2948
3786
  baseStyle.textContent = baseStyles;
2949
3787
  this.root.appendChild(baseStyle);
2950
- const stylesheet = this.getAttribute(ATTR_STYLESHEET);
2951
- if (stylesheet) {
2952
- const link = document.createElement("link");
2953
- link.rel = "stylesheet";
2954
- link.href = stylesheet;
2955
- this.root.appendChild(link);
2956
- }
2957
3788
  const inlineStyle = document.createElement("style");
2958
3789
  inlineStyle.textContent = [
2959
3790
  ":host { display: contents; }",
@@ -2981,20 +3812,70 @@ var init_BottomSheetElement = __esm({
2981
3812
  ATTR_ANIMATION,
2982
3813
  ATTR_FOCUS_TRAP,
2983
3814
  ATTR_CLOSE_ON_ESCAPE,
2984
- ATTR_LOCK_BODY_SCROLL
3815
+ ATTR_LOCK_BODY_SCROLL,
3816
+ ATTR_SHEET_LABEL,
3817
+ ATTR_STACK_EFFECT,
3818
+ ATTR_PERSISTENT,
3819
+ ATTR_DISABLE_CLOSE,
3820
+ ATTR_DISABLE_DRAG,
3821
+ ATTR_CLOSE_ON_ROUTE_CHANGE,
3822
+ ATTR_RADIUS,
3823
+ ATTR_MAX_HEIGHT,
3824
+ ATTR_RETURN_FOCUS_TO,
3825
+ ATTR_BACKDROP_COLOR,
3826
+ ATTR_SCRIM_COLOR,
3827
+ ATTR_SNAP
2985
3828
  ];
2986
3829
  }
2987
3830
  connectedCallback() {
3831
+ if (!this.stylesheetLinked) {
3832
+ this.stylesheetLinked = true;
3833
+ const stylesheet = this.getAttribute(ATTR_STYLESHEET);
3834
+ if (stylesheet) {
3835
+ const link = document.createElement("link");
3836
+ link.rel = "stylesheet";
3837
+ link.href = stylesheet;
3838
+ this.root.appendChild(link);
3839
+ }
3840
+ }
2988
3841
  this.initEngine();
2989
3842
  }
2990
3843
  disconnectedCallback() {
2991
3844
  this.teardownEngine();
2992
3845
  }
2993
- attributeChangedCallback() {
3846
+ attributeChangedCallback(name, _old, value) {
2994
3847
  if (!this.engine) return;
3848
+ if (LIVE_ATTRS.has(name)) {
3849
+ this.applyLiveAttribute(name, value);
3850
+ return;
3851
+ }
2995
3852
  this.teardownEngine();
2996
3853
  this.initEngine();
2997
3854
  }
3855
+ applyLiveAttribute(name, value) {
3856
+ if (!this.engine) return;
3857
+ switch (name) {
3858
+ case ATTR_RADIUS: {
3859
+ const r = parseDimension(value);
3860
+ if (r !== void 0) this.engine.setRadius(r);
3861
+ break;
3862
+ }
3863
+ case ATTR_MAX_HEIGHT: {
3864
+ const h = parseDimension(value);
3865
+ if (h !== void 0) this.engine.setMaxHeight(h);
3866
+ break;
3867
+ }
3868
+ case ATTR_BACKDROP_COLOR:
3869
+ case ATTR_SCRIM_COLOR:
3870
+ this.engine.setScrimColor(value);
3871
+ break;
3872
+ case ATTR_SNAP:
3873
+ if (value && value !== this.engine.state.activeId) {
3874
+ void this.engine.snapTo(value);
3875
+ }
3876
+ break;
3877
+ }
3878
+ }
2998
3879
  getEngine() {
2999
3880
  return this.engine;
3000
3881
  }
@@ -3007,6 +3888,10 @@ var init_BottomSheetElement = __esm({
3007
3888
  }
3008
3889
  this.engine?.destroy();
3009
3890
  this.engine = null;
3891
+ for (const el of this.declarativeAnchors) {
3892
+ this.appendChild(el);
3893
+ }
3894
+ this.declarativeAnchors = [];
3010
3895
  }
3011
3896
  initEngine() {
3012
3897
  const { sheet, handle, content, backdrop, announcer } = this.refs;
@@ -3018,10 +3903,16 @@ var init_BottomSheetElement = __esm({
3018
3903
  } else {
3019
3904
  sheet.removeAttribute("aria-modal");
3020
3905
  }
3906
+ sheet.setAttribute(
3907
+ "aria-label",
3908
+ this.getAttribute(ATTR_SHEET_LABEL) ?? "Bottom sheet"
3909
+ );
3021
3910
  const showBackdrop = this.getAttribute(ATTR_BACKDROP) !== "false";
3022
3911
  backdrop.style.display = showBackdrop ? "" : "none";
3023
3912
  if (showBackdrop) {
3024
- this.backdropClickHandler = () => void this.engine?.close();
3913
+ this.backdropClickHandler = () => {
3914
+ if (this.engine?.canDismiss()) void this.engine.close("backdrop");
3915
+ };
3025
3916
  backdrop.addEventListener("click", this.backdropClickHandler);
3026
3917
  }
3027
3918
  const opts = {
@@ -3037,11 +3928,29 @@ var init_BottomSheetElement = __esm({
3037
3928
  animation: parseAnimation(this.getAttribute(ATTR_ANIMATION)),
3038
3929
  focusTrap,
3039
3930
  closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
3040
- lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false"
3931
+ lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false",
3932
+ stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true",
3933
+ persistent: this.getAttribute(ATTR_PERSISTENT) === "true",
3934
+ disableClose: this.getAttribute(ATTR_DISABLE_CLOSE) === "true",
3935
+ disableDrag: this.getAttribute(ATTR_DISABLE_DRAG) === "true",
3936
+ closeOnRouteChange: this.getAttribute(ATTR_CLOSE_ON_ROUTE_CHANGE) === "true",
3937
+ radius: parseDimension(this.getAttribute(ATTR_RADIUS)),
3938
+ maxHeight: parseDimension(this.getAttribute(ATTR_MAX_HEIGHT)),
3939
+ returnFocusTo: this.getAttribute(ATTR_RETURN_FOCUS_TO) ?? void 0
3041
3940
  };
3042
3941
  this.engine = new BottomSheetEngine(opts);
3942
+ sheet.setAttribute("data-active", this.engine.state.activeId);
3943
+ const scrimColor = this.getAttribute(ATTR_SCRIM_COLOR) ?? this.getAttribute(ATTR_BACKDROP_COLOR);
3944
+ if (scrimColor !== null) this.engine.setScrimColor(scrimColor);
3945
+ for (const el of Array.from(
3946
+ this.querySelectorAll('[slot="anchor"]')
3947
+ )) {
3948
+ this.declarativeAnchors.push(el);
3949
+ this.engine.addAnchor({ ...parseAnchorOptions(el), element: el });
3950
+ }
3043
3951
  this.offHandlers = [
3044
3952
  this.engine.on("snap", (payload) => {
3953
+ sheet.setAttribute("data-active", payload.id);
3045
3954
  announcer.textContent = payload.id;
3046
3955
  this.dispatchEvent(new CustomEvent("snap", { detail: payload }));
3047
3956
  }),
@@ -3056,6 +3965,26 @@ var init_BottomSheetElement = __esm({
3056
3965
  this.engine.on(
3057
3966
  "progress",
3058
3967
  (payload) => this.dispatchEvent(new CustomEvent("progress", { detail: payload }))
3968
+ ),
3969
+ this.engine.on(
3970
+ "before-close",
3971
+ (payload) => this.dispatchEvent(new CustomEvent("before-close", { detail: payload }))
3972
+ ),
3973
+ this.engine.on(
3974
+ "opened",
3975
+ (payload) => this.dispatchEvent(new CustomEvent("opened", { detail: payload }))
3976
+ ),
3977
+ this.engine.on(
3978
+ "closed",
3979
+ () => this.dispatchEvent(new CustomEvent("closed"))
3980
+ ),
3981
+ this.engine.on(
3982
+ "dragstart",
3983
+ (payload) => this.dispatchEvent(new CustomEvent("drag-start", { detail: payload }))
3984
+ ),
3985
+ this.engine.on(
3986
+ "dragend",
3987
+ (payload) => this.dispatchEvent(new CustomEvent("drag-end", { detail: payload }))
3059
3988
  )
3060
3989
  ];
3061
3990
  }
@@ -3065,21 +3994,66 @@ var init_BottomSheetElement = __esm({
3065
3994
  open(id) {
3066
3995
  return this.engine?.open(id) ?? Promise.resolve();
3067
3996
  }
3068
- close() {
3069
- return this.engine?.close() ?? Promise.resolve();
3997
+ close(reason) {
3998
+ return this.engine?.close(reason) ?? Promise.resolve();
3999
+ }
4000
+ expand() {
4001
+ return this.engine?.expand() ?? Promise.resolve();
4002
+ }
4003
+ collapse() {
4004
+ return this.engine?.collapse() ?? Promise.resolve();
4005
+ }
4006
+ isTop() {
4007
+ return this.engine?.isTop() ?? false;
4008
+ }
4009
+ depth() {
4010
+ return this.engine?.depth() ?? 0;
4011
+ }
4012
+ canDismiss() {
4013
+ return this.engine?.canDismiss() ?? false;
4014
+ }
4015
+ setRadius(r) {
4016
+ this.engine?.setRadius(r);
4017
+ }
4018
+ setMaxHeight(h) {
4019
+ this.engine?.setMaxHeight(h);
3070
4020
  }
3071
4021
  setAllowed(ids, snap) {
3072
4022
  this.engine?.setAllowed(ids, snap);
3073
4023
  }
4024
+ addAnchor(opts) {
4025
+ return this.engine?.addAnchor(opts) ?? (() => {
4026
+ });
4027
+ }
4028
+ setScrimStages(opts) {
4029
+ return this.engine?.setScrimStages(opts) ?? (() => {
4030
+ });
4031
+ }
4032
+ recompute() {
4033
+ this.engine?.recompute();
4034
+ }
3074
4035
  get sheetState() {
3075
4036
  return this.engine?.state ?? null;
3076
4037
  }
4038
+ get snap() {
4039
+ return this.engine?.state.activeId ?? this.getAttribute(ATTR_SNAP);
4040
+ }
4041
+ set snap(id) {
4042
+ if (id === null) {
4043
+ this.removeAttribute(ATTR_SNAP);
4044
+ return;
4045
+ }
4046
+ if (this.engine) {
4047
+ if (id !== this.engine.state.activeId) void this.engine.snapTo(id);
4048
+ } else {
4049
+ this.setAttribute(ATTR_SNAP, id);
4050
+ }
4051
+ }
3077
4052
  };
3078
- registered = false;
3079
4053
  defineBottomSheet = (tag = "bottom-sheet") => {
3080
- if (registered || customElements.get(tag)) return;
3081
- customElements.define(tag, BottomSheetElement);
3082
- registered = true;
4054
+ if (typeof customElements === "undefined" || customElements.get(tag)) return;
4055
+ customElements.define(tag, class extends BottomSheetElement {
4056
+ });
3083
4057
  };
3084
4058
  }
3085
4059
  });