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