@surdeddd/bottom-sheet 0.3.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 (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 +480 -89
  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 +480 -89
  8. package/dist/index.cjs +341 -80
  9. package/dist/index.d.cts +3 -2
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.js +341 -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 +352 -81
  21. package/dist/qwik.d.cts +4 -2
  22. package/dist/qwik.d.ts +4 -2
  23. package/dist/qwik.js +352 -81
  24. package/dist/react.cjs +427 -87
  25. package/dist/react.d.cts +29 -4
  26. package/dist/react.d.ts +29 -4
  27. package/dist/react.js +427 -87
  28. package/dist/solid.cjs +341 -80
  29. package/dist/solid.d.cts +38 -2
  30. package/dist/solid.d.ts +38 -2
  31. package/dist/solid.js +341 -80
  32. package/dist/solid.source.jsx +341 -80
  33. package/dist/styles.css +57 -56
  34. package/dist/svelte-core.cjs +346 -80
  35. package/dist/svelte-core.js +346 -80
  36. package/dist/svelte-src/BottomSheet.svelte +103 -5
  37. package/dist/svelte.cjs +421 -88
  38. package/dist/svelte.d.cts +21 -0
  39. package/dist/svelte.d.ts +21 -0
  40. package/dist/svelte.js +421 -88
  41. package/dist/vue.cjs +584 -91
  42. package/dist/vue.d.cts +51 -2
  43. package/dist/vue.d.ts +51 -2
  44. package/dist/vue.js +585 -92
  45. package/package.json +6 -6
package/dist/element.cjs CHANGED
@@ -48,6 +48,15 @@ var init_sheetStack = __esm({
48
48
  size() {
49
49
  return this.entries.length;
50
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
+ }
51
60
  clear() {
52
61
  this.entries = [];
53
62
  }
@@ -185,7 +194,9 @@ var init_cssLength = __esm({
185
194
  resolveSnap = (point, mode, measureFit) => {
186
195
  if (typeof point === "number") return Math.max(0, point);
187
196
  if (point === "full") return viewportSize(mode);
188
- if (point === "fit") return Math.max(0, measureFit?.() ?? 0);
197
+ if (point === "fit" || point === "content") {
198
+ return Math.max(0, measureFit?.() ?? 0);
199
+ }
189
200
  const pctMatch = point.match(PERCENT);
190
201
  if (pctMatch) {
191
202
  const pct = parseFloat(pctMatch[1]) / 100;
@@ -457,86 +468,122 @@ function installRoute(deps) {
457
468
  return () => {
458
469
  };
459
470
  }
460
- let routePushed = false;
461
- let closeOnBackPushed = false;
462
- let detachPopstate = null;
463
- const popOurMarker = (key, expected) => {
464
- const state = history.state;
465
- if (state && state[key] === expected) {
466
- try {
467
- history.back();
468
- } catch {
469
- }
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;
470
485
  }
471
486
  };
472
487
  const onOpen = () => {
473
- if (deps.routedTo && !detachPopstate) {
474
- const target = deps.routedTo;
475
- try {
476
- history.pushState({ __bsRouted: target }, "", target);
477
- routePushed = true;
478
- } catch {
479
- }
480
- const onPop = (event) => {
481
- if (deps.isDestroyed()) return;
482
- const state = event.state;
483
- if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
484
- routePushed = false;
485
- void deps.close();
486
- }
487
- };
488
- window.addEventListener("popstate", onPop);
489
- detachPopstate = () => window.removeEventListener("popstate", onPop);
490
- } else if (deps.closeOnBack && !detachPopstate) {
491
- 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 {
492
501
  history.pushState({ __bsSheet: deps.sheetId }, "");
493
- closeOnBackPushed = true;
494
- } catch {
495
502
  }
496
- const onPop = () => {
497
- if (deps.isDestroyed()) return;
498
- if (deps.getSize() > 0 && deps.isTopSheet()) {
499
- const state = history.state;
500
- if (state?.__bsSheet !== deps.sheetId) closeOnBackPushed = false;
501
- void deps.close();
502
- }
503
- };
504
- window.addEventListener("popstate", onPop);
505
- detachPopstate = () => window.removeEventListener("popstate", onPop);
506
- }
507
- };
508
- const onClose = () => {
509
- detachPopstate?.();
510
- detachPopstate = null;
511
- if (routePushed) {
512
- routePushed = false;
513
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
514
- }
515
- if (closeOnBackPushed) {
516
- closeOnBackPushed = false;
517
- popOurMarker("__bsSheet", deps.sheetId);
503
+ pushed = true;
504
+ } catch {
505
+ pushed = false;
518
506
  }
519
507
  };
520
508
  const unsubscribeOpen = deps.on("open", onOpen);
521
- const unsubscribeClose = deps.on("close", onClose);
509
+ const unsubscribeClose = deps.on("close", teardownEntry);
522
510
  if (deps.getSize() > 0) onOpen();
523
511
  return () => {
524
512
  unsubscribeOpen();
525
513
  unsubscribeClose();
526
- detachPopstate?.();
527
- detachPopstate = null;
528
- if (routePushed) {
529
- routePushed = false;
530
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
531
- }
532
- if (closeOnBackPushed) {
533
- closeOnBackPushed = false;
534
- popOurMarker("__bsSheet", deps.sheetId);
535
- }
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();
536
546
  };
537
547
  }
548
+ var closers, handlerInstalled, suppress, ensureHandler, programmaticBack;
538
549
  var init_route = __esm({
539
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
+ };
540
587
  }
541
588
  });
542
589
 
@@ -884,7 +931,7 @@ var init_overlay_position = __esm({
884
931
  });
885
932
 
886
933
  // src/core/controllers/scrim-controller.ts
887
- var VALID_CSS_LENGTH, ScrimController;
934
+ var VALID_CSS_LENGTH, DEFAULT_SCRIM_DIM, ScrimController;
888
935
  var init_scrim_controller = __esm({
889
936
  "src/core/controllers/scrim-controller.ts"() {
890
937
  init_types();
@@ -894,6 +941,7 @@ var init_scrim_controller = __esm({
894
941
  init_devWarn();
895
942
  init_overlay_position();
896
943
  VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
944
+ DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
897
945
  ScrimController = class {
898
946
  constructor(deps, opts) {
899
947
  __publicField(this, "screenComponent");
@@ -932,9 +980,11 @@ var init_scrim_controller = __esm({
932
980
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
933
981
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
934
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);
935
985
  if (this.screenComponent) {
936
986
  this.applyScrimStyles({
937
- color: opts.scrimColor ?? preset?.color,
987
+ color: effectiveColor,
938
988
  blur: opts.scrimBlur ?? preset?.blur,
939
989
  interactive: opts.scrimInteractive ?? preset?.interactive ?? false
940
990
  });
@@ -1754,7 +1804,7 @@ var init_lifecycle_controller = __esm({
1754
1804
  this.releaseScrollLock = null;
1755
1805
  this.inertSiblings.remove();
1756
1806
  if (this.returnFocus) {
1757
- 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;
1758
1808
  target?.focus?.();
1759
1809
  }
1760
1810
  }
@@ -1942,6 +1992,7 @@ var init_gesture_controller = __esm({
1942
1992
  __publicField(this, "dragStartSize", 0);
1943
1993
  __publicField(this, "currentPointerKind", "touch");
1944
1994
  __publicField(this, "keyboardDismissed", false);
1995
+ __publicField(this, "dragSuppressed", false);
1945
1996
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
1946
1997
  this.deps = deps;
1947
1998
  }
@@ -1952,6 +2003,11 @@ var init_gesture_controller = __esm({
1952
2003
  if (this.detach) return this.detach;
1953
2004
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
1954
2005
  onStart: (_coord, kind) => {
2006
+ if (this.deps.getDisableDrag?.()) {
2007
+ this.dragSuppressed = true;
2008
+ return;
2009
+ }
2010
+ this.dragSuppressed = false;
1955
2011
  this.deps.cancelAnimation();
1956
2012
  this.isDragging_ = true;
1957
2013
  this.dragStartSize = this.deps.getDragContext().size;
@@ -1962,6 +2018,7 @@ var init_gesture_controller = __esm({
1962
2018
  this.deps.emit("dragstart", { size: this.dragStartSize });
1963
2019
  },
1964
2020
  onMove: (delta) => {
2021
+ if (this.dragSuppressed) return;
1965
2022
  const ctx = this.deps.getDragContext();
1966
2023
  const { min, max } = ctx.range;
1967
2024
  const maxAxis = ctx.maxAxisSize;
@@ -1985,6 +2042,10 @@ var init_gesture_controller = __esm({
1985
2042
  }
1986
2043
  },
1987
2044
  onEnd: (delta, velocity, kind) => {
2045
+ if (this.dragSuppressed) {
2046
+ this.dragSuppressed = false;
2047
+ return;
2048
+ }
1988
2049
  this.isDragging_ = false;
1989
2050
  this.keyboardDismissed = false;
1990
2051
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -1995,6 +2056,10 @@ var init_gesture_controller = __esm({
1995
2056
  this.deps.settleAfterDrag(delta, velocity, kind);
1996
2057
  },
1997
2058
  onCancel: () => {
2059
+ if (this.dragSuppressed) {
2060
+ this.dragSuppressed = false;
2061
+ return;
2062
+ }
1998
2063
  this.isDragging_ = false;
1999
2064
  this.keyboardDismissed = false;
2000
2065
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -2402,6 +2467,12 @@ function resolveEngineOptions(opts, extras) {
2402
2467
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2403
2468
  rubberBandEnabled: opts.rubberBand ?? true,
2404
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,
2405
2476
  initialAllowed,
2406
2477
  initialId,
2407
2478
  scrim: {
@@ -2428,7 +2499,8 @@ function resolveEngineOptions(opts, extras) {
2428
2499
  closeOnEscape: opts.closeOnEscape,
2429
2500
  lockBodyScroll: opts.lockBodyScroll,
2430
2501
  inertSiblings: opts.inertSiblings,
2431
- shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2502
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2503
+ returnFocus: opts.returnFocusTo
2432
2504
  }
2433
2505
  };
2434
2506
  }
@@ -2509,6 +2581,11 @@ var init_BottomSheetEngine = __esm({
2509
2581
  __publicField(this, "lifecycle");
2510
2582
  __publicField(this, "closeOnBack");
2511
2583
  __publicField(this, "routedTo");
2584
+ __publicField(this, "persistent");
2585
+ __publicField(this, "disableCloseFlag");
2586
+ __publicField(this, "disableDragFlag");
2587
+ __publicField(this, "closeOnRouteChange");
2588
+ __publicField(this, "maxHeightCap");
2512
2589
  __publicField(this, "snapPointsRaw");
2513
2590
  __publicField(this, "snaps");
2514
2591
  __publicField(this, "activeId");
@@ -2521,6 +2598,7 @@ var init_BottomSheetEngine = __esm({
2521
2598
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2522
2599
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2523
2600
  __publicField(this, "isTopSheet", true);
2601
+ __publicField(this, "opening", false);
2524
2602
  __publicField(this, "progressPayload", {
2525
2603
  value: 0,
2526
2604
  size: 0
@@ -2577,7 +2655,8 @@ var init_BottomSheetEngine = __esm({
2577
2655
  void this.snapTo(id);
2578
2656
  },
2579
2657
  close: () => {
2580
- void this.close();
2658
+ if (!this.canDismiss()) return;
2659
+ void this.close("backdrop");
2581
2660
  }
2582
2661
  },
2583
2662
  resolved.scrim
@@ -2601,6 +2680,10 @@ var init_BottomSheetEngine = __esm({
2601
2680
  );
2602
2681
  this.closeOnBack = resolved.closeOnBack;
2603
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;
2604
2687
  this.stackEffectEnabled = opts.stackEffect ?? false;
2605
2688
  this.snapPointsRaw = opts.snapPoints;
2606
2689
  this.persistKey = opts.persistKey;
@@ -2614,7 +2697,7 @@ var init_BottomSheetEngine = __esm({
2614
2697
  this.snapPointsRaw,
2615
2698
  resolved.initialAllowed,
2616
2699
  this.mode,
2617
- () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2700
+ () => this.measureFitSize(),
2618
2701
  (maxAxisSize) => {
2619
2702
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2620
2703
  }
@@ -2648,6 +2731,19 @@ var init_BottomSheetEngine = __esm({
2648
2731
  io.observe(this.element);
2649
2732
  this.teardowns.add(() => io.disconnect());
2650
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
+ }
2651
2747
  if (this.persistKey) {
2652
2748
  this.teardowns.add(installPersist(this, this.persistKey));
2653
2749
  }
@@ -2672,7 +2768,7 @@ var init_BottomSheetEngine = __esm({
2672
2768
  isTopSheet: () => this.isTopSheet,
2673
2769
  getSize: () => this.size,
2674
2770
  isDestroyed: () => this.destroyed,
2675
- close: () => this.close(),
2771
+ close: () => this.close("back"),
2676
2772
  on: (event, fn) => this.on(event, fn),
2677
2773
  sheetId: this.id
2678
2774
  })
@@ -2766,6 +2862,10 @@ var init_BottomSheetEngine = __esm({
2766
2862
  const previousSize = this.size;
2767
2863
  this.scrollCache.cache(previousId, previousSize, target.size);
2768
2864
  this.activeId = id;
2865
+ if (wasClosed && target.size > 0) {
2866
+ this.opening = true;
2867
+ sheetStack.promote(this.id);
2868
+ }
2769
2869
  if (this.animation.viewTransitionsAvailable) {
2770
2870
  this.currentViewTransition?.skipTransition?.();
2771
2871
  const vt = document.startViewTransition(() => {
@@ -2780,19 +2880,26 @@ var init_BottomSheetEngine = __esm({
2780
2880
  } else {
2781
2881
  await this.animation.animateTo(target.size, velocityPxPerMs);
2782
2882
  }
2883
+ this.opening = false;
2783
2884
  externalSignal?.removeEventListener("abort", onExternalAbort);
2784
2885
  if (signal.aborted) return;
2785
2886
  this.scrollCache.restore(id, previousSize, target.size);
2786
2887
  this.updateAriaSlider();
2787
- this.emit("snap", { id, size: target.size });
2888
+ this.emit("snap", {
2889
+ id,
2890
+ size: target.size,
2891
+ progress: this.computeProgress(target.size)
2892
+ });
2788
2893
  if (wasClosed && target.size > 0) {
2789
2894
  this.emit("open", { id });
2790
2895
  this.handleOpen();
2791
2896
  notifyLinkedSheets(this.linkedSheets, this);
2897
+ this.emit("opened", { id });
2792
2898
  }
2793
2899
  if (target.size === 0) {
2794
2900
  this.emit("close", void 0);
2795
2901
  this.handleClose();
2902
+ this.emit("closed", void 0);
2796
2903
  }
2797
2904
  }
2798
2905
  async dragTo(targetSize, velocityOrOpts = 0) {
@@ -2817,11 +2924,74 @@ var init_BottomSheetEngine = __esm({
2817
2924
  const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
2818
2925
  return this.snapTo(target);
2819
2926
  }
2820
- close() {
2927
+ close(reason = "programmatic") {
2821
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();
2822
2931
  const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
2823
2932
  return this.snapTo(closedId ?? this.activeId);
2824
2933
  }
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) {
2959
+ if (this.destroyed) return;
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);
2993
+ }
2994
+ }
2825
2995
  setAllowed(ids, snap) {
2826
2996
  if (this.destroyed) return;
2827
2997
  this.snaps.setAllowedIds(ids);
@@ -2955,8 +3125,53 @@ var init_BottomSheetEngine = __esm({
2955
3125
  }
2956
3126
  this.updateAriaSlider();
2957
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
+ }
2958
3172
  destroy() {
2959
3173
  this.destroyed = true;
3174
+ this.opening = false;
2960
3175
  this.currentAbort.abort();
2961
3176
  this.currentViewTransition?.skipTransition?.();
2962
3177
  this.currentViewTransition = null;
@@ -3006,6 +3221,36 @@ var init_BottomSheetEngine = __esm({
3006
3221
  frozen = true;
3007
3222
  return cancelled;
3008
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
+ }
3009
3254
  recomputeSnaps() {
3010
3255
  const seen = /* @__PURE__ */ new Set();
3011
3256
  for (const p of this.snapPointsRaw) {
@@ -3041,11 +3286,8 @@ var init_BottomSheetEngine = __esm({
3041
3286
  },
3042
3287
  setIsTop: (isTop) => {
3043
3288
  this.isTopSheet = isTop;
3044
- if (this.backdrop) {
3045
- this.backdrop.style.display = isTop ? "" : "none";
3046
- }
3047
3289
  },
3048
- isOpen: () => this.size > 0,
3290
+ isOpen: () => this.size > 0 || this.opening,
3049
3291
  setDepth: (depth) => this.applyStackDepth(depth)
3050
3292
  }));
3051
3293
  }
@@ -3077,6 +3319,7 @@ var init_BottomSheetEngine = __esm({
3077
3319
  buf.rubberBandEnabled = this.rubberBandEnabled;
3078
3320
  return buf;
3079
3321
  },
3322
+ getDisableDrag: () => this.disableDragFlag,
3080
3323
  cancelAnimation: () => this.animation.cancel(),
3081
3324
  applySize: (size) => this.applySize(size),
3082
3325
  animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
@@ -3156,8 +3399,9 @@ var init_BottomSheetEngine = __esm({
3156
3399
  if (this.lifecycle.closeOnEscape) {
3157
3400
  const onKey = (e) => {
3158
3401
  if (this.destroyed || e.defaultPrevented) return;
3402
+ if (!this.canDismiss()) return;
3159
3403
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
3160
- void this.close();
3404
+ void this.close("escape");
3161
3405
  }
3162
3406
  };
3163
3407
  document.addEventListener("keydown", onKey);
@@ -3220,20 +3464,31 @@ var init_BottomSheetEngine = __esm({
3220
3464
  const previousSnapSize = this.snaps.findById(previousId)?.size;
3221
3465
  this.scrollCache.cache(previousId, previousSize, target.size);
3222
3466
  this.activeId = target.id;
3467
+ if (previousSnapSize === 0 && target.size > 0) {
3468
+ this.opening = true;
3469
+ sheetStack.promote(this.id);
3470
+ }
3223
3471
  void this.animation.animateTo(target.size, velocity).then(() => {
3472
+ this.opening = false;
3224
3473
  if (signal.aborted) return;
3225
3474
  this.scrollCache.restore(target.id, previousSize, target.size);
3226
3475
  this.updateAriaSlider();
3227
- 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
+ });
3228
3481
  this.haptic();
3229
3482
  if (previousSnapSize === 0 && target.size > 0) {
3230
3483
  this.emit("open", { id: target.id });
3231
3484
  this.handleOpen();
3232
3485
  notifyLinkedSheets(this.linkedSheets, this);
3486
+ this.emit("opened", { id: target.id });
3233
3487
  }
3234
3488
  if (target.size === 0) {
3235
3489
  this.emit("close", void 0);
3236
3490
  this.handleClose();
3491
+ this.emit("closed", void 0);
3237
3492
  }
3238
3493
  });
3239
3494
  }
@@ -3302,17 +3557,24 @@ var init_BottomSheetEngine = __esm({
3302
3557
  const target = this.snaps.findById(this.activeId);
3303
3558
  if (!target) return;
3304
3559
  this.updateAriaSlider();
3305
- this.emit("snap", { id: target.id, size: target.size });
3560
+ this.emit("snap", {
3561
+ id: target.id,
3562
+ size: target.size,
3563
+ progress: this.computeProgress(target.size)
3564
+ });
3306
3565
  if (target.size > 0 && !this.lifecycle.isInstalled) {
3307
3566
  this.emit("open", { id: target.id });
3308
3567
  this.handleOpen();
3309
3568
  notifyLinkedSheets(this.linkedSheets, this);
3569
+ this.emit("opened", { id: target.id });
3310
3570
  } else if (target.size === 0 && this.lifecycle.isInstalled) {
3311
3571
  this.emit("close", void 0);
3312
3572
  this.handleClose();
3573
+ this.emit("closed", void 0);
3313
3574
  }
3314
3575
  }
3315
3576
  handleClose() {
3577
+ this.opening = false;
3316
3578
  this.lifecycle.release();
3317
3579
  sheetStack.update();
3318
3580
  }
@@ -3324,7 +3586,7 @@ var init_BottomSheetEngine = __esm({
3324
3586
  var baseStyles;
3325
3587
  var init_baseStyles = __esm({
3326
3588
  "src/web-component/baseStyles.ts"() {
3327
- 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}}`;
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}}`;
3328
3590
  }
3329
3591
  });
3330
3592
 
@@ -3334,7 +3596,7 @@ __export(BottomSheetElement_exports, {
3334
3596
  BottomSheetElement: () => exports.BottomSheetElement,
3335
3597
  defineBottomSheet: () => exports.defineBottomSheet
3336
3598
  });
3337
- 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; exports.BottomSheetElement = void 0; 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;
3338
3600
  var init_BottomSheetElement = __esm({
3339
3601
  "src/web-component/BottomSheetElement.ts"() {
3340
3602
  init_BottomSheetEngine();
@@ -3351,6 +3613,23 @@ var init_BottomSheetElement = __esm({
3351
3613
  ATTR_LOCK_BODY_SCROLL = "lock-body-scroll";
3352
3614
  ATTR_SHEET_LABEL = "sheet-label";
3353
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
+ ]);
3354
3633
  isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
3355
3634
  DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
3356
3635
  parseSnapPoints = (raw) => {
@@ -3380,6 +3659,11 @@ var init_BottomSheetElement = __esm({
3380
3659
  parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
3381
3660
  parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
3382
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
+ };
3383
3667
  ANCHOR_POSITIONS = [
3384
3668
  "top-left",
3385
3669
  "top-center",
@@ -3532,7 +3816,17 @@ var init_BottomSheetElement = __esm({
3532
3816
  ATTR_CLOSE_ON_ESCAPE,
3533
3817
  ATTR_LOCK_BODY_SCROLL,
3534
3818
  ATTR_SHEET_LABEL,
3535
- ATTR_STACK_EFFECT
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
3536
3830
  ];
3537
3831
  }
3538
3832
  connectedCallback() {
@@ -3551,11 +3845,39 @@ var init_BottomSheetElement = __esm({
3551
3845
  disconnectedCallback() {
3552
3846
  this.teardownEngine();
3553
3847
  }
3554
- attributeChangedCallback() {
3848
+ attributeChangedCallback(name, _old, value) {
3555
3849
  if (!this.engine) return;
3850
+ if (LIVE_ATTRS.has(name)) {
3851
+ this.applyLiveAttribute(name, value);
3852
+ return;
3853
+ }
3556
3854
  this.teardownEngine();
3557
3855
  this.initEngine();
3558
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
+ }
3559
3881
  getEngine() {
3560
3882
  return this.engine;
3561
3883
  }
@@ -3590,7 +3912,9 @@ var init_BottomSheetElement = __esm({
3590
3912
  const showBackdrop = this.getAttribute(ATTR_BACKDROP) !== "false";
3591
3913
  backdrop.style.display = showBackdrop ? "" : "none";
3592
3914
  if (showBackdrop) {
3593
- this.backdropClickHandler = () => void this.engine?.close();
3915
+ this.backdropClickHandler = () => {
3916
+ if (this.engine?.canDismiss()) void this.engine.close("backdrop");
3917
+ };
3594
3918
  backdrop.addEventListener("click", this.backdropClickHandler);
3595
3919
  }
3596
3920
  const opts = {
@@ -3607,10 +3931,19 @@ var init_BottomSheetElement = __esm({
3607
3931
  focusTrap,
3608
3932
  closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
3609
3933
  lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false",
3610
- stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true"
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
3611
3942
  };
3612
3943
  this.engine = new BottomSheetEngine(opts);
3613
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);
3614
3947
  for (const el of Array.from(
3615
3948
  this.querySelectorAll('[slot="anchor"]')
3616
3949
  )) {
@@ -3634,6 +3967,26 @@ var init_BottomSheetElement = __esm({
3634
3967
  this.engine.on(
3635
3968
  "progress",
3636
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 }))
3637
3990
  )
3638
3991
  ];
3639
3992
  }
@@ -3643,8 +3996,29 @@ var init_BottomSheetElement = __esm({
3643
3996
  open(id) {
3644
3997
  return this.engine?.open(id) ?? Promise.resolve();
3645
3998
  }
3646
- close() {
3647
- 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);
3648
4022
  }
3649
4023
  setAllowed(ids, snap) {
3650
4024
  this.engine?.setAllowed(ids, snap);
@@ -3657,9 +4031,26 @@ var init_BottomSheetElement = __esm({
3657
4031
  return this.engine?.setScrimStages(opts) ?? (() => {
3658
4032
  });
3659
4033
  }
4034
+ recompute() {
4035
+ this.engine?.recompute();
4036
+ }
3660
4037
  get sheetState() {
3661
4038
  return this.engine?.state ?? null;
3662
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
+ }
3663
4054
  };
3664
4055
  exports.defineBottomSheet = (tag = "bottom-sheet") => {
3665
4056
  if (typeof customElements === "undefined" || customElements.get(tag)) return;