@surdeddd/bottom-sheet 0.3.0 → 0.4.1

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 (45) hide show
  1. package/dist/{BottomSheetEngine-CAymrH-P.d.cts → BottomSheetEngine-BJrH7qY3.d.cts} +39 -3
  2. package/dist/{BottomSheetEngine-DcqcuudN.d.ts → BottomSheetEngine-L05jGZvU.d.ts} +39 -3
  3. package/dist/element.cjs +490 -91
  4. package/dist/element.d.cts +23 -4
  5. package/dist/element.d.ts +23 -4
  6. package/dist/element.iife.global.js +2 -2
  7. package/dist/element.js +490 -91
  8. package/dist/index.cjs +347 -80
  9. package/dist/index.d.cts +3 -2
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.js +347 -80
  12. package/dist/integrations/formik.d.cts +1 -1
  13. package/dist/integrations/formik.d.ts +1 -1
  14. package/dist/integrations/react-hook-form.d.cts +1 -1
  15. package/dist/integrations/react-hook-form.d.ts +1 -1
  16. package/dist/overlay.cjs +13 -2
  17. package/dist/overlay.js +13 -2
  18. package/dist/preact.d.cts +1 -1
  19. package/dist/preact.d.ts +1 -1
  20. package/dist/qwik.cjs +358 -81
  21. package/dist/qwik.d.cts +4 -2
  22. package/dist/qwik.d.ts +4 -2
  23. package/dist/qwik.js +358 -81
  24. package/dist/react.cjs +451 -89
  25. package/dist/react.d.cts +29 -4
  26. package/dist/react.d.ts +29 -4
  27. package/dist/react.js +451 -89
  28. package/dist/solid.cjs +347 -80
  29. package/dist/solid.d.cts +38 -2
  30. package/dist/solid.d.ts +38 -2
  31. package/dist/solid.js +347 -80
  32. package/dist/solid.source.jsx +347 -80
  33. package/dist/styles.css +68 -55
  34. package/dist/svelte-core.cjs +352 -80
  35. package/dist/svelte-core.js +352 -80
  36. package/dist/svelte-src/BottomSheet.svelte +113 -15
  37. package/dist/svelte.cjs +474 -133
  38. package/dist/svelte.d.cts +21 -0
  39. package/dist/svelte.d.ts +21 -0
  40. package/dist/svelte.js +474 -133
  41. package/dist/vue.cjs +610 -109
  42. package/dist/vue.d.cts +51 -2
  43. package/dist/vue.d.ts +51 -2
  44. package/dist/vue.js +611 -110
  45. package/package.json +8 -8
package/dist/element.js CHANGED
@@ -46,6 +46,15 @@ var init_sheetStack = __esm({
46
46
  size() {
47
47
  return this.entries.length;
48
48
  }
49
+ depthOf(id) {
50
+ const open = [];
51
+ for (const entry of this.entries) {
52
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
53
+ }
54
+ const idx = open.findIndex((e) => e.id === id);
55
+ if (idx === -1) return 0;
56
+ return open.length - 1 - idx;
57
+ }
49
58
  clear() {
50
59
  this.entries = [];
51
60
  }
@@ -183,7 +192,9 @@ var init_cssLength = __esm({
183
192
  resolveSnap = (point, mode, measureFit) => {
184
193
  if (typeof point === "number") return Math.max(0, point);
185
194
  if (point === "full") return viewportSize(mode);
186
- if (point === "fit") return Math.max(0, measureFit?.() ?? 0);
195
+ if (point === "fit" || point === "content") {
196
+ return Math.max(0, measureFit?.() ?? 0);
197
+ }
187
198
  const pctMatch = point.match(PERCENT);
188
199
  if (pctMatch) {
189
200
  const pct = parseFloat(pctMatch[1]) / 100;
@@ -455,86 +466,122 @@ function installRoute(deps) {
455
466
  return () => {
456
467
  };
457
468
  }
458
- let routePushed = false;
459
- let closeOnBackPushed = false;
460
- let detachPopstate = null;
461
- const popOurMarker = (key, expected) => {
462
- const state = history.state;
463
- if (state && state[key] === expected) {
464
- try {
465
- history.back();
466
- } catch {
467
- }
469
+ let pushed = false;
470
+ let closer = null;
471
+ const teardownEntry = () => {
472
+ if (closer) {
473
+ const idx = closers.indexOf(closer);
474
+ if (idx !== -1) closers.splice(idx, 1);
475
+ }
476
+ const wasConsumed = closer?.consumed ?? false;
477
+ closer = null;
478
+ if (pushed && !wasConsumed) {
479
+ pushed = false;
480
+ programmaticBack();
481
+ } else {
482
+ pushed = false;
468
483
  }
469
484
  };
470
485
  const onOpen = () => {
471
- if (deps.routedTo && !detachPopstate) {
472
- const target = deps.routedTo;
473
- try {
474
- history.pushState({ __bsRouted: target }, "", target);
475
- routePushed = true;
476
- } catch {
477
- }
478
- const onPop = (event) => {
479
- if (deps.isDestroyed()) return;
480
- const state = event.state;
481
- if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
482
- routePushed = false;
483
- void deps.close();
484
- }
485
- };
486
- window.addEventListener("popstate", onPop);
487
- detachPopstate = () => window.removeEventListener("popstate", onPop);
488
- } else if (deps.closeOnBack && !detachPopstate) {
489
- try {
486
+ if (closer) return;
487
+ closer = {
488
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
489
+ isTop: () => deps.isTopSheet(),
490
+ close: () => void deps.close(),
491
+ consumed: false
492
+ };
493
+ ensureHandler();
494
+ closers.push(closer);
495
+ try {
496
+ if (deps.routedTo !== void 0) {
497
+ history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
498
+ } else {
490
499
  history.pushState({ __bsSheet: deps.sheetId }, "");
491
- closeOnBackPushed = true;
492
- } catch {
493
500
  }
494
- const onPop = () => {
495
- if (deps.isDestroyed()) return;
496
- if (deps.getSize() > 0 && deps.isTopSheet()) {
497
- const state = history.state;
498
- if (state?.__bsSheet !== deps.sheetId) closeOnBackPushed = false;
499
- void deps.close();
500
- }
501
- };
502
- window.addEventListener("popstate", onPop);
503
- detachPopstate = () => window.removeEventListener("popstate", onPop);
504
- }
505
- };
506
- const onClose = () => {
507
- detachPopstate?.();
508
- detachPopstate = null;
509
- if (routePushed) {
510
- routePushed = false;
511
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
512
- }
513
- if (closeOnBackPushed) {
514
- closeOnBackPushed = false;
515
- popOurMarker("__bsSheet", deps.sheetId);
501
+ pushed = true;
502
+ } catch {
503
+ pushed = false;
516
504
  }
517
505
  };
518
506
  const unsubscribeOpen = deps.on("open", onOpen);
519
- const unsubscribeClose = deps.on("close", onClose);
507
+ const unsubscribeClose = deps.on("close", teardownEntry);
520
508
  if (deps.getSize() > 0) onOpen();
521
509
  return () => {
522
510
  unsubscribeOpen();
523
511
  unsubscribeClose();
524
- detachPopstate?.();
525
- detachPopstate = null;
526
- if (routePushed) {
527
- routePushed = false;
528
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
529
- }
530
- if (closeOnBackPushed) {
531
- closeOnBackPushed = false;
532
- popOurMarker("__bsSheet", deps.sheetId);
533
- }
512
+ teardownEntry();
534
513
  };
535
514
  }
515
+ function installRouteChange(deps) {
516
+ if (typeof window === "undefined" || typeof history === "undefined") {
517
+ return () => {
518
+ };
519
+ }
520
+ const onChange = () => {
521
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
522
+ void deps.close();
523
+ };
524
+ const wrap = (key) => {
525
+ const original = history[key];
526
+ const patched = function(...args) {
527
+ original.apply(this, args);
528
+ onChange();
529
+ };
530
+ history[key] = patched;
531
+ return () => {
532
+ if (history[key] === patched) {
533
+ history[key] = original;
534
+ }
535
+ };
536
+ };
537
+ window.addEventListener("popstate", onChange);
538
+ const restorePush = wrap("pushState");
539
+ const restoreReplace = wrap("replaceState");
540
+ return () => {
541
+ window.removeEventListener("popstate", onChange);
542
+ restorePush();
543
+ restoreReplace();
544
+ };
545
+ }
546
+ var closers, handlerInstalled, suppress, ensureHandler, programmaticBack;
536
547
  var init_route = __esm({
537
548
  "src/core/features/route.ts"() {
549
+ closers = [];
550
+ handlerInstalled = false;
551
+ suppress = 0;
552
+ ensureHandler = () => {
553
+ if (handlerInstalled || typeof window === "undefined") return;
554
+ handlerInstalled = true;
555
+ window.addEventListener("popstate", () => {
556
+ if (suppress > 0) {
557
+ suppress -= 1;
558
+ return;
559
+ }
560
+ let target = null;
561
+ for (let i = closers.length - 1; i >= 0; i -= 1) {
562
+ const c = closers[i];
563
+ if (!c.isOpen()) continue;
564
+ if (!target) target = c;
565
+ if (c.isTop()) {
566
+ target = c;
567
+ break;
568
+ }
569
+ }
570
+ if (!target) return;
571
+ target.consumed = true;
572
+ const idx = closers.indexOf(target);
573
+ if (idx !== -1) closers.splice(idx, 1);
574
+ target.close();
575
+ });
576
+ };
577
+ programmaticBack = () => {
578
+ suppress += 1;
579
+ try {
580
+ history.back();
581
+ } catch {
582
+ suppress = Math.max(0, suppress - 1);
583
+ }
584
+ };
538
585
  }
539
586
  });
540
587
 
@@ -882,7 +929,7 @@ var init_overlay_position = __esm({
882
929
  });
883
930
 
884
931
  // src/core/controllers/scrim-controller.ts
885
- var VALID_CSS_LENGTH, ScrimController;
932
+ var VALID_CSS_LENGTH, DEFAULT_SCRIM_DIM, ScrimController;
886
933
  var init_scrim_controller = __esm({
887
934
  "src/core/controllers/scrim-controller.ts"() {
888
935
  init_types();
@@ -892,6 +939,7 @@ var init_scrim_controller = __esm({
892
939
  init_devWarn();
893
940
  init_overlay_position();
894
941
  VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
942
+ DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
895
943
  ScrimController = class {
896
944
  constructor(deps, opts) {
897
945
  __publicField(this, "screenComponent");
@@ -930,9 +978,11 @@ var init_scrim_controller = __esm({
930
978
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
931
979
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
932
980
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
981
+ const explicitColor = opts.scrimColor ?? preset?.color;
982
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
933
983
  if (this.screenComponent) {
934
984
  this.applyScrimStyles({
935
- color: opts.scrimColor ?? preset?.color,
985
+ color: effectiveColor,
936
986
  blur: opts.scrimBlur ?? preset?.blur,
937
987
  interactive: opts.scrimInteractive ?? preset?.interactive ?? false
938
988
  });
@@ -1752,7 +1802,7 @@ var init_lifecycle_controller = __esm({
1752
1802
  this.releaseScrollLock = null;
1753
1803
  this.inertSiblings.remove();
1754
1804
  if (this.returnFocus) {
1755
- const target = typeof this.returnFocus === "function" ? this.returnFocus() : this.returnFocus;
1805
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1756
1806
  target?.focus?.();
1757
1807
  }
1758
1808
  }
@@ -1940,6 +1990,7 @@ var init_gesture_controller = __esm({
1940
1990
  __publicField(this, "dragStartSize", 0);
1941
1991
  __publicField(this, "currentPointerKind", "touch");
1942
1992
  __publicField(this, "keyboardDismissed", false);
1993
+ __publicField(this, "dragSuppressed", false);
1943
1994
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
1944
1995
  this.deps = deps;
1945
1996
  }
@@ -1950,6 +2001,11 @@ var init_gesture_controller = __esm({
1950
2001
  if (this.detach) return this.detach;
1951
2002
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
1952
2003
  onStart: (_coord, kind) => {
2004
+ if (this.deps.getDisableDrag?.()) {
2005
+ this.dragSuppressed = true;
2006
+ return;
2007
+ }
2008
+ this.dragSuppressed = false;
1953
2009
  this.deps.cancelAnimation();
1954
2010
  this.isDragging_ = true;
1955
2011
  this.dragStartSize = this.deps.getDragContext().size;
@@ -1960,6 +2016,7 @@ var init_gesture_controller = __esm({
1960
2016
  this.deps.emit("dragstart", { size: this.dragStartSize });
1961
2017
  },
1962
2018
  onMove: (delta) => {
2019
+ if (this.dragSuppressed) return;
1963
2020
  const ctx = this.deps.getDragContext();
1964
2021
  const { min, max } = ctx.range;
1965
2022
  const maxAxis = ctx.maxAxisSize;
@@ -1983,6 +2040,10 @@ var init_gesture_controller = __esm({
1983
2040
  }
1984
2041
  },
1985
2042
  onEnd: (delta, velocity, kind) => {
2043
+ if (this.dragSuppressed) {
2044
+ this.dragSuppressed = false;
2045
+ return;
2046
+ }
1986
2047
  this.isDragging_ = false;
1987
2048
  this.keyboardDismissed = false;
1988
2049
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -1993,6 +2054,10 @@ var init_gesture_controller = __esm({
1993
2054
  this.deps.settleAfterDrag(delta, velocity, kind);
1994
2055
  },
1995
2056
  onCancel: () => {
2057
+ if (this.dragSuppressed) {
2058
+ this.dragSuppressed = false;
2059
+ return;
2060
+ }
1996
2061
  this.isDragging_ = false;
1997
2062
  this.keyboardDismissed = false;
1998
2063
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -2400,6 +2465,12 @@ function resolveEngineOptions(opts, extras) {
2400
2465
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2401
2466
  rubberBandEnabled: opts.rubberBand ?? true,
2402
2467
  closeOnBack: opts.closeOnBack ?? false,
2468
+ persistent: opts.persistent ?? false,
2469
+ disableClose: opts.disableClose ?? false,
2470
+ disableDrag: opts.disableDrag ?? false,
2471
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2472
+ radius: opts.radius,
2473
+ maxHeight: opts.maxHeight,
2403
2474
  initialAllowed,
2404
2475
  initialId,
2405
2476
  scrim: {
@@ -2426,7 +2497,8 @@ function resolveEngineOptions(opts, extras) {
2426
2497
  closeOnEscape: opts.closeOnEscape,
2427
2498
  lockBodyScroll: opts.lockBodyScroll,
2428
2499
  inertSiblings: opts.inertSiblings,
2429
- shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2500
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2501
+ returnFocus: opts.returnFocusTo
2430
2502
  }
2431
2503
  };
2432
2504
  }
@@ -2507,6 +2579,11 @@ var init_BottomSheetEngine = __esm({
2507
2579
  __publicField(this, "lifecycle");
2508
2580
  __publicField(this, "closeOnBack");
2509
2581
  __publicField(this, "routedTo");
2582
+ __publicField(this, "persistent");
2583
+ __publicField(this, "disableCloseFlag");
2584
+ __publicField(this, "disableDragFlag");
2585
+ __publicField(this, "closeOnRouteChange");
2586
+ __publicField(this, "maxHeightCap");
2510
2587
  __publicField(this, "snapPointsRaw");
2511
2588
  __publicField(this, "snaps");
2512
2589
  __publicField(this, "activeId");
@@ -2519,6 +2596,7 @@ var init_BottomSheetEngine = __esm({
2519
2596
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2520
2597
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2521
2598
  __publicField(this, "isTopSheet", true);
2599
+ __publicField(this, "opening", false);
2522
2600
  __publicField(this, "progressPayload", {
2523
2601
  value: 0,
2524
2602
  size: 0
@@ -2575,7 +2653,8 @@ var init_BottomSheetEngine = __esm({
2575
2653
  void this.snapTo(id);
2576
2654
  },
2577
2655
  close: () => {
2578
- void this.close();
2656
+ if (!this.canDismiss()) return;
2657
+ void this.close("backdrop");
2579
2658
  }
2580
2659
  },
2581
2660
  resolved.scrim
@@ -2599,6 +2678,10 @@ var init_BottomSheetEngine = __esm({
2599
2678
  );
2600
2679
  this.closeOnBack = resolved.closeOnBack;
2601
2680
  this.routedTo = opts.routedTo;
2681
+ this.persistent = resolved.persistent;
2682
+ this.disableCloseFlag = resolved.disableClose;
2683
+ this.disableDragFlag = resolved.disableDrag;
2684
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2602
2685
  this.stackEffectEnabled = opts.stackEffect ?? false;
2603
2686
  this.snapPointsRaw = opts.snapPoints;
2604
2687
  this.persistKey = opts.persistKey;
@@ -2612,7 +2695,7 @@ var init_BottomSheetEngine = __esm({
2612
2695
  this.snapPointsRaw,
2613
2696
  resolved.initialAllowed,
2614
2697
  this.mode,
2615
- () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2698
+ () => this.measureFitSize(),
2616
2699
  (maxAxisSize) => {
2617
2700
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2618
2701
  }
@@ -2646,6 +2729,19 @@ var init_BottomSheetEngine = __esm({
2646
2729
  io.observe(this.element);
2647
2730
  this.teardowns.add(() => io.disconnect());
2648
2731
  }
2732
+ this.installFitObserver();
2733
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2734
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2735
+ this.applyAutoAriaLabelledBy();
2736
+ if (this.closeOnRouteChange) {
2737
+ this.teardowns.add(
2738
+ installRouteChange({
2739
+ isDestroyed: () => this.destroyed,
2740
+ getSize: () => this.size,
2741
+ close: () => this.close()
2742
+ })
2743
+ );
2744
+ }
2649
2745
  if (this.persistKey) {
2650
2746
  this.teardowns.add(installPersist(this, this.persistKey));
2651
2747
  }
@@ -2670,7 +2766,7 @@ var init_BottomSheetEngine = __esm({
2670
2766
  isTopSheet: () => this.isTopSheet,
2671
2767
  getSize: () => this.size,
2672
2768
  isDestroyed: () => this.destroyed,
2673
- close: () => this.close(),
2769
+ close: () => this.close("back"),
2674
2770
  on: (event, fn) => this.on(event, fn),
2675
2771
  sheetId: this.id
2676
2772
  })
@@ -2764,6 +2860,10 @@ var init_BottomSheetEngine = __esm({
2764
2860
  const previousSize = this.size;
2765
2861
  this.scrollCache.cache(previousId, previousSize, target.size);
2766
2862
  this.activeId = id;
2863
+ if (wasClosed && target.size > 0) {
2864
+ this.opening = true;
2865
+ sheetStack.promote(this.id);
2866
+ }
2767
2867
  if (this.animation.viewTransitionsAvailable) {
2768
2868
  this.currentViewTransition?.skipTransition?.();
2769
2869
  const vt = document.startViewTransition(() => {
@@ -2778,19 +2878,26 @@ var init_BottomSheetEngine = __esm({
2778
2878
  } else {
2779
2879
  await this.animation.animateTo(target.size, velocityPxPerMs);
2780
2880
  }
2881
+ this.opening = false;
2781
2882
  externalSignal?.removeEventListener("abort", onExternalAbort);
2782
2883
  if (signal.aborted) return;
2783
2884
  this.scrollCache.restore(id, previousSize, target.size);
2784
2885
  this.updateAriaSlider();
2785
- this.emit("snap", { id, size: target.size });
2886
+ this.emit("snap", {
2887
+ id,
2888
+ size: target.size,
2889
+ progress: this.computeProgress(target.size)
2890
+ });
2786
2891
  if (wasClosed && target.size > 0) {
2787
2892
  this.emit("open", { id });
2788
2893
  this.handleOpen();
2789
2894
  notifyLinkedSheets(this.linkedSheets, this);
2895
+ this.emit("opened", { id });
2790
2896
  }
2791
2897
  if (target.size === 0) {
2792
2898
  this.emit("close", void 0);
2793
2899
  this.handleClose();
2900
+ this.emit("closed", void 0);
2794
2901
  }
2795
2902
  }
2796
2903
  async dragTo(targetSize, velocityOrOpts = 0) {
@@ -2815,11 +2922,74 @@ var init_BottomSheetEngine = __esm({
2815
2922
  const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
2816
2923
  return this.snapTo(target);
2817
2924
  }
2818
- close() {
2925
+ close(reason = "programmatic") {
2819
2926
  if (this.destroyed) return Promise.resolve();
2927
+ if (this.disableCloseFlag) return Promise.resolve();
2928
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2820
2929
  const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
2821
2930
  return this.snapTo(closedId ?? this.activeId);
2822
2931
  }
2932
+ canDismiss() {
2933
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2934
+ }
2935
+ isTop() {
2936
+ return this.isTopSheet;
2937
+ }
2938
+ depth() {
2939
+ return sheetStack.depthOf(this.id);
2940
+ }
2941
+ expand() {
2942
+ if (this.destroyed) return Promise.resolve();
2943
+ const ids = this.allowedIdsBySize();
2944
+ const target = ids[ids.length - 1];
2945
+ if (!target) return Promise.resolve();
2946
+ return this.snapTo(target);
2947
+ }
2948
+ collapse() {
2949
+ if (this.destroyed) return Promise.resolve();
2950
+ const ids = this.allowedIdsBySize();
2951
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
2952
+ const target = nonZero ?? ids[0];
2953
+ if (!target) return Promise.resolve();
2954
+ return this.snapTo(target);
2955
+ }
2956
+ setRadius(r) {
2957
+ if (this.destroyed) return;
2958
+ this.element.style.setProperty(
2959
+ "--bs-radius",
2960
+ typeof r === "number" ? `${r}px` : r
2961
+ );
2962
+ }
2963
+ setMaxHeight(h) {
2964
+ if (this.destroyed) return;
2965
+ const value = typeof h === "number" ? `${h}px` : h;
2966
+ const axis = layoutAxis(this.mode);
2967
+ this.element.style.setProperty(
2968
+ axis === "height" ? "maxHeight" : "maxWidth",
2969
+ value
2970
+ );
2971
+ if (typeof h === "number") {
2972
+ this.maxHeightCap = h;
2973
+ this.clampToMaxHeight();
2974
+ } else if (typeof window !== "undefined") {
2975
+ const probe2 = document.createElement("div");
2976
+ probe2.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${axis === "height" ? "height" : "width"}:${value};`;
2977
+ document.body.appendChild(probe2);
2978
+ const measured = axis === "height" ? probe2.offsetHeight : probe2.offsetWidth;
2979
+ probe2.remove();
2980
+ this.maxHeightCap = measured > 0 ? measured : void 0;
2981
+ this.clampToMaxHeight();
2982
+ }
2983
+ }
2984
+ clampToMaxHeight() {
2985
+ if (this.maxHeightCap === void 0) return;
2986
+ if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2987
+ this.snaps.setMaxAxisSize(this.maxHeightCap);
2988
+ }
2989
+ if (this.size > this.maxHeightCap && !this.gesture?.isDragging) {
2990
+ this.applySize(this.maxHeightCap);
2991
+ }
2992
+ }
2823
2993
  setAllowed(ids, snap) {
2824
2994
  if (this.destroyed) return;
2825
2995
  this.snaps.setAllowedIds(ids);
@@ -2953,8 +3123,53 @@ var init_BottomSheetEngine = __esm({
2953
3123
  }
2954
3124
  this.updateAriaSlider();
2955
3125
  }
3126
+ recompute() {
3127
+ if (this.destroyed) return;
3128
+ this.snaps.recompute();
3129
+ this.scrim.invalidateOpacityCache();
3130
+ if (this.gesture?.isDragging) return;
3131
+ const current = this.snaps.findById(this.activeId);
3132
+ if (current) {
3133
+ this.size = current.size;
3134
+ this.applySize(this.size);
3135
+ this.updateAriaSlider();
3136
+ }
3137
+ }
3138
+ measureFitSize() {
3139
+ const vertical = layoutAxis(this.mode) === "height";
3140
+ const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
3141
+ const content = this.scrollContainer;
3142
+ const natural = content ? handleSize + (vertical ? content.scrollHeight : content.scrollWidth) : handleSize;
3143
+ if (typeof window === "undefined") return natural;
3144
+ const viewport = vertical ? window.innerHeight : window.innerWidth;
3145
+ return viewport > 0 ? Math.min(natural, viewport) : natural;
3146
+ }
3147
+ installFitObserver() {
3148
+ if (typeof ResizeObserver === "undefined") return;
3149
+ if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
3150
+ return;
3151
+ const targets = [this.handle];
3152
+ if (this.scrollContainer && this.scrollContainer !== this.handle) {
3153
+ targets.push(this.scrollContainer);
3154
+ }
3155
+ let raf = 0;
3156
+ const ro = new ResizeObserver(() => {
3157
+ if (raf) return;
3158
+ raf = requestAnimationFrame(() => {
3159
+ raf = 0;
3160
+ if (this.destroyed || this.gesture?.isDragging) return;
3161
+ this.recompute();
3162
+ });
3163
+ });
3164
+ for (const t of targets) ro.observe(t);
3165
+ this.teardowns.add(() => {
3166
+ ro.disconnect();
3167
+ if (raf) cancelAnimationFrame(raf);
3168
+ });
3169
+ }
2956
3170
  destroy() {
2957
3171
  this.destroyed = true;
3172
+ this.opening = false;
2958
3173
  this.currentAbort.abort();
2959
3174
  this.currentViewTransition?.skipTransition?.();
2960
3175
  this.currentViewTransition = null;
@@ -3004,6 +3219,36 @@ var init_BottomSheetEngine = __esm({
3004
3219
  frozen = true;
3005
3220
  return cancelled;
3006
3221
  }
3222
+ emitBeforeClose(reason) {
3223
+ let cancelled = false;
3224
+ let frozen = false;
3225
+ this.emit("before-close", {
3226
+ reason,
3227
+ cancel: () => {
3228
+ if (frozen) {
3229
+ console.warn(
3230
+ "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3231
+ );
3232
+ return;
3233
+ }
3234
+ cancelled = true;
3235
+ }
3236
+ });
3237
+ frozen = true;
3238
+ return cancelled;
3239
+ }
3240
+ applyAutoAriaLabelledBy() {
3241
+ if (typeof document === "undefined") return;
3242
+ if (!this.lifecycle.focusTrapEnabled) return;
3243
+ if (this.element.getAttribute("aria-labelledby")) return;
3244
+ const scope = this.handle ?? this.element;
3245
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3246
+ if (!titled) return;
3247
+ if (!titled.id) {
3248
+ titled.id = nextInstanceId("bs-title");
3249
+ }
3250
+ this.element.setAttribute("aria-labelledby", titled.id);
3251
+ }
3007
3252
  recomputeSnaps() {
3008
3253
  const seen = /* @__PURE__ */ new Set();
3009
3254
  for (const p of this.snapPointsRaw) {
@@ -3039,11 +3284,8 @@ var init_BottomSheetEngine = __esm({
3039
3284
  },
3040
3285
  setIsTop: (isTop) => {
3041
3286
  this.isTopSheet = isTop;
3042
- if (this.backdrop) {
3043
- this.backdrop.style.display = isTop ? "" : "none";
3044
- }
3045
3287
  },
3046
- isOpen: () => this.size > 0,
3288
+ isOpen: () => this.size > 0 || this.opening,
3047
3289
  setDepth: (depth) => this.applyStackDepth(depth)
3048
3290
  }));
3049
3291
  }
@@ -3075,6 +3317,7 @@ var init_BottomSheetEngine = __esm({
3075
3317
  buf.rubberBandEnabled = this.rubberBandEnabled;
3076
3318
  return buf;
3077
3319
  },
3320
+ getDisableDrag: () => this.disableDragFlag,
3078
3321
  cancelAnimation: () => this.animation.cancel(),
3079
3322
  applySize: (size) => this.applySize(size),
3080
3323
  animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
@@ -3154,8 +3397,9 @@ var init_BottomSheetEngine = __esm({
3154
3397
  if (this.lifecycle.closeOnEscape) {
3155
3398
  const onKey = (e) => {
3156
3399
  if (this.destroyed || e.defaultPrevented) return;
3400
+ if (!this.canDismiss()) return;
3157
3401
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
3158
- void this.close();
3402
+ void this.close("escape");
3159
3403
  }
3160
3404
  };
3161
3405
  document.addEventListener("keydown", onKey);
@@ -3218,20 +3462,31 @@ var init_BottomSheetEngine = __esm({
3218
3462
  const previousSnapSize = this.snaps.findById(previousId)?.size;
3219
3463
  this.scrollCache.cache(previousId, previousSize, target.size);
3220
3464
  this.activeId = target.id;
3465
+ if (previousSnapSize === 0 && target.size > 0) {
3466
+ this.opening = true;
3467
+ sheetStack.promote(this.id);
3468
+ }
3221
3469
  void this.animation.animateTo(target.size, velocity).then(() => {
3470
+ this.opening = false;
3222
3471
  if (signal.aborted) return;
3223
3472
  this.scrollCache.restore(target.id, previousSize, target.size);
3224
3473
  this.updateAriaSlider();
3225
- this.emit("snap", { id: target.id, size: target.size });
3474
+ this.emit("snap", {
3475
+ id: target.id,
3476
+ size: target.size,
3477
+ progress: this.computeProgress(target.size)
3478
+ });
3226
3479
  this.haptic();
3227
3480
  if (previousSnapSize === 0 && target.size > 0) {
3228
3481
  this.emit("open", { id: target.id });
3229
3482
  this.handleOpen();
3230
3483
  notifyLinkedSheets(this.linkedSheets, this);
3484
+ this.emit("opened", { id: target.id });
3231
3485
  }
3232
3486
  if (target.size === 0) {
3233
3487
  this.emit("close", void 0);
3234
3488
  this.handleClose();
3489
+ this.emit("closed", void 0);
3235
3490
  }
3236
3491
  });
3237
3492
  }
@@ -3256,6 +3511,9 @@ var init_BottomSheetEngine = __esm({
3256
3511
  if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3257
3512
  this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3258
3513
  }
3514
+ if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3515
+ this.rootEl.style.setProperty("--bs-size", `${size}px`);
3516
+ }
3259
3517
  }
3260
3518
  const progress = this.computeProgress(size);
3261
3519
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -3267,6 +3525,9 @@ var init_BottomSheetEngine = __esm({
3267
3525
  if (this.anchorHost) {
3268
3526
  this.anchorHost.style.setProperty("--bs-progress", String(progress));
3269
3527
  }
3528
+ if (this.rootEl && this.rootEl !== this.anchorHost) {
3529
+ this.rootEl.style.setProperty("--bs-progress", String(progress));
3530
+ }
3270
3531
  }
3271
3532
  this.scrim.applyOpacity(progress, progressChanged);
3272
3533
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -3300,17 +3561,24 @@ var init_BottomSheetEngine = __esm({
3300
3561
  const target = this.snaps.findById(this.activeId);
3301
3562
  if (!target) return;
3302
3563
  this.updateAriaSlider();
3303
- this.emit("snap", { id: target.id, size: target.size });
3564
+ this.emit("snap", {
3565
+ id: target.id,
3566
+ size: target.size,
3567
+ progress: this.computeProgress(target.size)
3568
+ });
3304
3569
  if (target.size > 0 && !this.lifecycle.isInstalled) {
3305
3570
  this.emit("open", { id: target.id });
3306
3571
  this.handleOpen();
3307
3572
  notifyLinkedSheets(this.linkedSheets, this);
3573
+ this.emit("opened", { id: target.id });
3308
3574
  } else if (target.size === 0 && this.lifecycle.isInstalled) {
3309
3575
  this.emit("close", void 0);
3310
3576
  this.handleClose();
3577
+ this.emit("closed", void 0);
3311
3578
  }
3312
3579
  }
3313
3580
  handleClose() {
3581
+ this.opening = false;
3314
3582
  this.lifecycle.release();
3315
3583
  sheetStack.update();
3316
3584
  }
@@ -3322,7 +3590,7 @@ var init_BottomSheetEngine = __esm({
3322
3590
  var baseStyles;
3323
3591
  var init_baseStyles = __esm({
3324
3592
  "src/web-component/baseStyles.ts"() {
3325
- baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--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,var(--bs-shadow-auto,none));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);--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);border-bottom-right-radius:var(--bs-radius);border-bottom:1px solid var(--bs-surface-border);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);border-bottom-right-radius:var(--bs-radius);border-right:1px solid var(--bs-surface-border);--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);border-bottom-left-radius:var(--bs-radius);border-left:1px solid var(--bs-surface-border);--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);-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;user-select:text;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-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,#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}}`;
3593
+ 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:fixed;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)}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-mode="top"]{bottom:auto;top:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-mode="left"]{left:calc(var(--bs-size,0px) + 12px);right:auto;bottom:auto;top:16px}.bs-button-slot[data-mode="right"]{right:calc(var(--bs-size,0px) + 12px);left:auto;bottom:auto;top:16px}@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}}`;
3326
3594
  }
3327
3595
  });
3328
3596
 
@@ -3332,7 +3600,7 @@ __export(BottomSheetElement_exports, {
3332
3600
  BottomSheetElement: () => BottomSheetElement,
3333
3601
  defineBottomSheet: () => defineBottomSheet
3334
3602
  });
3335
- 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, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions, buildSlot, buildShadowTree, BaseHTMLElement, BottomSheetElement, defineBottomSheet;
3603
+ var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, ATTR_PERSISTENT, ATTR_DISABLE_CLOSE, ATTR_DISABLE_DRAG, ATTR_CLOSE_ON_ROUTE_CHANGE, ATTR_RADIUS, ATTR_MAX_HEIGHT, ATTR_RETURN_FOCUS_TO, ATTR_BACKDROP_COLOR, ATTR_SCRIM_COLOR, ATTR_SNAP, LIVE_ATTRS, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, parseDimension, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions, buildSlot, buildShadowTree, BaseHTMLElement, BottomSheetElement, defineBottomSheet;
3336
3604
  var init_BottomSheetElement = __esm({
3337
3605
  "src/web-component/BottomSheetElement.ts"() {
3338
3606
  init_BottomSheetEngine();
@@ -3349,6 +3617,23 @@ var init_BottomSheetElement = __esm({
3349
3617
  ATTR_LOCK_BODY_SCROLL = "lock-body-scroll";
3350
3618
  ATTR_SHEET_LABEL = "sheet-label";
3351
3619
  ATTR_STACK_EFFECT = "stack-effect";
3620
+ ATTR_PERSISTENT = "persistent";
3621
+ ATTR_DISABLE_CLOSE = "disable-close";
3622
+ ATTR_DISABLE_DRAG = "disable-drag";
3623
+ ATTR_CLOSE_ON_ROUTE_CHANGE = "close-on-route-change";
3624
+ ATTR_RADIUS = "radius";
3625
+ ATTR_MAX_HEIGHT = "max-height";
3626
+ ATTR_RETURN_FOCUS_TO = "return-focus-to";
3627
+ ATTR_BACKDROP_COLOR = "backdrop-color";
3628
+ ATTR_SCRIM_COLOR = "scrim-color";
3629
+ ATTR_SNAP = "snap";
3630
+ LIVE_ATTRS = /* @__PURE__ */ new Set([
3631
+ ATTR_RADIUS,
3632
+ ATTR_MAX_HEIGHT,
3633
+ ATTR_BACKDROP_COLOR,
3634
+ ATTR_SCRIM_COLOR,
3635
+ ATTR_SNAP
3636
+ ]);
3352
3637
  isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
3353
3638
  DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
3354
3639
  parseSnapPoints = (raw) => {
@@ -3378,6 +3663,11 @@ var init_BottomSheetElement = __esm({
3378
3663
  parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
3379
3664
  parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
3380
3665
  parseList = (raw) => raw ? raw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
3666
+ parseDimension = (raw) => {
3667
+ if (raw === null) return void 0;
3668
+ const numeric = Number(raw);
3669
+ return Number.isFinite(numeric) && raw.trim() !== "" ? numeric : raw;
3670
+ };
3381
3671
  ANCHOR_POSITIONS = [
3382
3672
  "top-left",
3383
3673
  "top-center",
@@ -3467,8 +3757,8 @@ var init_BottomSheetElement = __esm({
3467
3757
  announcer.setAttribute("aria-live", "polite");
3468
3758
  announcer.setAttribute("aria-atomic", "true");
3469
3759
  announcer.style.cssText = "position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);";
3470
- sheet.append(leftButton, rightButton, handle, content);
3471
- root.append(backdrop, screen, sheet, announcer);
3760
+ sheet.append(handle, content);
3761
+ root.append(backdrop, screen, sheet, leftButton, rightButton, announcer);
3472
3762
  const fragment = document.createDocumentFragment();
3473
3763
  fragment.appendChild(root);
3474
3764
  return {
@@ -3530,7 +3820,17 @@ var init_BottomSheetElement = __esm({
3530
3820
  ATTR_CLOSE_ON_ESCAPE,
3531
3821
  ATTR_LOCK_BODY_SCROLL,
3532
3822
  ATTR_SHEET_LABEL,
3533
- ATTR_STACK_EFFECT
3823
+ ATTR_STACK_EFFECT,
3824
+ ATTR_PERSISTENT,
3825
+ ATTR_DISABLE_CLOSE,
3826
+ ATTR_DISABLE_DRAG,
3827
+ ATTR_CLOSE_ON_ROUTE_CHANGE,
3828
+ ATTR_RADIUS,
3829
+ ATTR_MAX_HEIGHT,
3830
+ ATTR_RETURN_FOCUS_TO,
3831
+ ATTR_BACKDROP_COLOR,
3832
+ ATTR_SCRIM_COLOR,
3833
+ ATTR_SNAP
3534
3834
  ];
3535
3835
  }
3536
3836
  connectedCallback() {
@@ -3549,11 +3849,39 @@ var init_BottomSheetElement = __esm({
3549
3849
  disconnectedCallback() {
3550
3850
  this.teardownEngine();
3551
3851
  }
3552
- attributeChangedCallback() {
3852
+ attributeChangedCallback(name, _old, value) {
3553
3853
  if (!this.engine) return;
3854
+ if (LIVE_ATTRS.has(name)) {
3855
+ this.applyLiveAttribute(name, value);
3856
+ return;
3857
+ }
3554
3858
  this.teardownEngine();
3555
3859
  this.initEngine();
3556
3860
  }
3861
+ applyLiveAttribute(name, value) {
3862
+ if (!this.engine) return;
3863
+ switch (name) {
3864
+ case ATTR_RADIUS: {
3865
+ const r = parseDimension(value);
3866
+ if (r !== void 0) this.engine.setRadius(r);
3867
+ break;
3868
+ }
3869
+ case ATTR_MAX_HEIGHT: {
3870
+ const h = parseDimension(value);
3871
+ if (h !== void 0) this.engine.setMaxHeight(h);
3872
+ break;
3873
+ }
3874
+ case ATTR_BACKDROP_COLOR:
3875
+ case ATTR_SCRIM_COLOR:
3876
+ this.engine.setScrimColor(value);
3877
+ break;
3878
+ case ATTR_SNAP:
3879
+ if (value && value !== this.engine.state.activeId) {
3880
+ void this.engine.snapTo(value);
3881
+ }
3882
+ break;
3883
+ }
3884
+ }
3557
3885
  getEngine() {
3558
3886
  return this.engine;
3559
3887
  }
@@ -3575,6 +3903,8 @@ var init_BottomSheetElement = __esm({
3575
3903
  const { sheet, handle, content, backdrop, announcer } = this.refs;
3576
3904
  const mode = parseMode(this.getAttribute(ATTR_MODE));
3577
3905
  sheet.setAttribute("data-mode", mode);
3906
+ this.refs.leftButton.setAttribute("data-mode", mode);
3907
+ this.refs.rightButton.setAttribute("data-mode", mode);
3578
3908
  const focusTrap = this.getAttribute(ATTR_FOCUS_TRAP) === "true";
3579
3909
  if (focusTrap) {
3580
3910
  sheet.setAttribute("aria-modal", "true");
@@ -3588,7 +3918,9 @@ var init_BottomSheetElement = __esm({
3588
3918
  const showBackdrop = this.getAttribute(ATTR_BACKDROP) !== "false";
3589
3919
  backdrop.style.display = showBackdrop ? "" : "none";
3590
3920
  if (showBackdrop) {
3591
- this.backdropClickHandler = () => void this.engine?.close();
3921
+ this.backdropClickHandler = () => {
3922
+ if (this.engine?.canDismiss()) void this.engine.close("backdrop");
3923
+ };
3592
3924
  backdrop.addEventListener("click", this.backdropClickHandler);
3593
3925
  }
3594
3926
  const opts = {
@@ -3605,10 +3937,19 @@ var init_BottomSheetElement = __esm({
3605
3937
  focusTrap,
3606
3938
  closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
3607
3939
  lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false",
3608
- stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true"
3940
+ stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true",
3941
+ persistent: this.getAttribute(ATTR_PERSISTENT) === "true",
3942
+ disableClose: this.getAttribute(ATTR_DISABLE_CLOSE) === "true",
3943
+ disableDrag: this.getAttribute(ATTR_DISABLE_DRAG) === "true",
3944
+ closeOnRouteChange: this.getAttribute(ATTR_CLOSE_ON_ROUTE_CHANGE) === "true",
3945
+ radius: parseDimension(this.getAttribute(ATTR_RADIUS)),
3946
+ maxHeight: parseDimension(this.getAttribute(ATTR_MAX_HEIGHT)),
3947
+ returnFocusTo: this.getAttribute(ATTR_RETURN_FOCUS_TO) ?? void 0
3609
3948
  };
3610
3949
  this.engine = new BottomSheetEngine(opts);
3611
3950
  sheet.setAttribute("data-active", this.engine.state.activeId);
3951
+ const scrimColor = this.getAttribute(ATTR_SCRIM_COLOR) ?? this.getAttribute(ATTR_BACKDROP_COLOR);
3952
+ if (scrimColor !== null) this.engine.setScrimColor(scrimColor);
3612
3953
  for (const el of Array.from(
3613
3954
  this.querySelectorAll('[slot="anchor"]')
3614
3955
  )) {
@@ -3632,6 +3973,26 @@ var init_BottomSheetElement = __esm({
3632
3973
  this.engine.on(
3633
3974
  "progress",
3634
3975
  (payload) => this.dispatchEvent(new CustomEvent("progress", { detail: payload }))
3976
+ ),
3977
+ this.engine.on(
3978
+ "before-close",
3979
+ (payload) => this.dispatchEvent(new CustomEvent("before-close", { detail: payload }))
3980
+ ),
3981
+ this.engine.on(
3982
+ "opened",
3983
+ (payload) => this.dispatchEvent(new CustomEvent("opened", { detail: payload }))
3984
+ ),
3985
+ this.engine.on(
3986
+ "closed",
3987
+ () => this.dispatchEvent(new CustomEvent("closed"))
3988
+ ),
3989
+ this.engine.on(
3990
+ "dragstart",
3991
+ (payload) => this.dispatchEvent(new CustomEvent("drag-start", { detail: payload }))
3992
+ ),
3993
+ this.engine.on(
3994
+ "dragend",
3995
+ (payload) => this.dispatchEvent(new CustomEvent("drag-end", { detail: payload }))
3635
3996
  )
3636
3997
  ];
3637
3998
  }
@@ -3641,8 +4002,29 @@ var init_BottomSheetElement = __esm({
3641
4002
  open(id) {
3642
4003
  return this.engine?.open(id) ?? Promise.resolve();
3643
4004
  }
3644
- close() {
3645
- return this.engine?.close() ?? Promise.resolve();
4005
+ close(reason) {
4006
+ return this.engine?.close(reason) ?? Promise.resolve();
4007
+ }
4008
+ expand() {
4009
+ return this.engine?.expand() ?? Promise.resolve();
4010
+ }
4011
+ collapse() {
4012
+ return this.engine?.collapse() ?? Promise.resolve();
4013
+ }
4014
+ isTop() {
4015
+ return this.engine?.isTop() ?? false;
4016
+ }
4017
+ depth() {
4018
+ return this.engine?.depth() ?? 0;
4019
+ }
4020
+ canDismiss() {
4021
+ return this.engine?.canDismiss() ?? false;
4022
+ }
4023
+ setRadius(r) {
4024
+ this.engine?.setRadius(r);
4025
+ }
4026
+ setMaxHeight(h) {
4027
+ this.engine?.setMaxHeight(h);
3646
4028
  }
3647
4029
  setAllowed(ids, snap) {
3648
4030
  this.engine?.setAllowed(ids, snap);
@@ -3655,9 +4037,26 @@ var init_BottomSheetElement = __esm({
3655
4037
  return this.engine?.setScrimStages(opts) ?? (() => {
3656
4038
  });
3657
4039
  }
4040
+ recompute() {
4041
+ this.engine?.recompute();
4042
+ }
3658
4043
  get sheetState() {
3659
4044
  return this.engine?.state ?? null;
3660
4045
  }
4046
+ get snap() {
4047
+ return this.engine?.state.activeId ?? this.getAttribute(ATTR_SNAP);
4048
+ }
4049
+ set snap(id) {
4050
+ if (id === null) {
4051
+ this.removeAttribute(ATTR_SNAP);
4052
+ return;
4053
+ }
4054
+ if (this.engine) {
4055
+ if (id !== this.engine.state.activeId) void this.engine.snapTo(id);
4056
+ } else {
4057
+ this.setAttribute(ATTR_SNAP, id);
4058
+ }
4059
+ }
3661
4060
  };
3662
4061
  defineBottomSheet = (tag = "bottom-sheet") => {
3663
4062
  if (typeof customElements === "undefined" || customElements.get(tag)) return;