@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.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();
536
515
  };
537
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();
546
+ };
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
  }
@@ -3258,6 +3513,9 @@ var init_BottomSheetEngine = __esm({
3258
3513
  if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3259
3514
  this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3260
3515
  }
3516
+ if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3517
+ this.rootEl.style.setProperty("--bs-size", `${size}px`);
3518
+ }
3261
3519
  }
3262
3520
  const progress = this.computeProgress(size);
3263
3521
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -3269,6 +3527,9 @@ var init_BottomSheetEngine = __esm({
3269
3527
  if (this.anchorHost) {
3270
3528
  this.anchorHost.style.setProperty("--bs-progress", String(progress));
3271
3529
  }
3530
+ if (this.rootEl && this.rootEl !== this.anchorHost) {
3531
+ this.rootEl.style.setProperty("--bs-progress", String(progress));
3532
+ }
3272
3533
  }
3273
3534
  this.scrim.applyOpacity(progress, progressChanged);
3274
3535
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -3302,17 +3563,24 @@ var init_BottomSheetEngine = __esm({
3302
3563
  const target = this.snaps.findById(this.activeId);
3303
3564
  if (!target) return;
3304
3565
  this.updateAriaSlider();
3305
- this.emit("snap", { id: target.id, size: target.size });
3566
+ this.emit("snap", {
3567
+ id: target.id,
3568
+ size: target.size,
3569
+ progress: this.computeProgress(target.size)
3570
+ });
3306
3571
  if (target.size > 0 && !this.lifecycle.isInstalled) {
3307
3572
  this.emit("open", { id: target.id });
3308
3573
  this.handleOpen();
3309
3574
  notifyLinkedSheets(this.linkedSheets, this);
3575
+ this.emit("opened", { id: target.id });
3310
3576
  } else if (target.size === 0 && this.lifecycle.isInstalled) {
3311
3577
  this.emit("close", void 0);
3312
3578
  this.handleClose();
3579
+ this.emit("closed", void 0);
3313
3580
  }
3314
3581
  }
3315
3582
  handleClose() {
3583
+ this.opening = false;
3316
3584
  this.lifecycle.release();
3317
3585
  sheetStack.update();
3318
3586
  }
@@ -3324,7 +3592,7 @@ var init_BottomSheetEngine = __esm({
3324
3592
  var baseStyles;
3325
3593
  var init_baseStyles = __esm({
3326
3594
  "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}}`;
3595
+ 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}}`;
3328
3596
  }
3329
3597
  });
3330
3598
 
@@ -3334,7 +3602,7 @@ __export(BottomSheetElement_exports, {
3334
3602
  BottomSheetElement: () => exports.BottomSheetElement,
3335
3603
  defineBottomSheet: () => exports.defineBottomSheet
3336
3604
  });
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;
3605
+ 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
3606
  var init_BottomSheetElement = __esm({
3339
3607
  "src/web-component/BottomSheetElement.ts"() {
3340
3608
  init_BottomSheetEngine();
@@ -3351,6 +3619,23 @@ var init_BottomSheetElement = __esm({
3351
3619
  ATTR_LOCK_BODY_SCROLL = "lock-body-scroll";
3352
3620
  ATTR_SHEET_LABEL = "sheet-label";
3353
3621
  ATTR_STACK_EFFECT = "stack-effect";
3622
+ ATTR_PERSISTENT = "persistent";
3623
+ ATTR_DISABLE_CLOSE = "disable-close";
3624
+ ATTR_DISABLE_DRAG = "disable-drag";
3625
+ ATTR_CLOSE_ON_ROUTE_CHANGE = "close-on-route-change";
3626
+ ATTR_RADIUS = "radius";
3627
+ ATTR_MAX_HEIGHT = "max-height";
3628
+ ATTR_RETURN_FOCUS_TO = "return-focus-to";
3629
+ ATTR_BACKDROP_COLOR = "backdrop-color";
3630
+ ATTR_SCRIM_COLOR = "scrim-color";
3631
+ ATTR_SNAP = "snap";
3632
+ LIVE_ATTRS = /* @__PURE__ */ new Set([
3633
+ ATTR_RADIUS,
3634
+ ATTR_MAX_HEIGHT,
3635
+ ATTR_BACKDROP_COLOR,
3636
+ ATTR_SCRIM_COLOR,
3637
+ ATTR_SNAP
3638
+ ]);
3354
3639
  isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
3355
3640
  DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
3356
3641
  parseSnapPoints = (raw) => {
@@ -3380,6 +3665,11 @@ var init_BottomSheetElement = __esm({
3380
3665
  parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
3381
3666
  parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
3382
3667
  parseList = (raw) => raw ? raw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
3668
+ parseDimension = (raw) => {
3669
+ if (raw === null) return void 0;
3670
+ const numeric = Number(raw);
3671
+ return Number.isFinite(numeric) && raw.trim() !== "" ? numeric : raw;
3672
+ };
3383
3673
  ANCHOR_POSITIONS = [
3384
3674
  "top-left",
3385
3675
  "top-center",
@@ -3469,8 +3759,8 @@ var init_BottomSheetElement = __esm({
3469
3759
  announcer.setAttribute("aria-live", "polite");
3470
3760
  announcer.setAttribute("aria-atomic", "true");
3471
3761
  announcer.style.cssText = "position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);";
3472
- sheet.append(leftButton, rightButton, handle, content);
3473
- root.append(backdrop, screen, sheet, announcer);
3762
+ sheet.append(handle, content);
3763
+ root.append(backdrop, screen, sheet, leftButton, rightButton, announcer);
3474
3764
  const fragment = document.createDocumentFragment();
3475
3765
  fragment.appendChild(root);
3476
3766
  return {
@@ -3532,7 +3822,17 @@ var init_BottomSheetElement = __esm({
3532
3822
  ATTR_CLOSE_ON_ESCAPE,
3533
3823
  ATTR_LOCK_BODY_SCROLL,
3534
3824
  ATTR_SHEET_LABEL,
3535
- ATTR_STACK_EFFECT
3825
+ ATTR_STACK_EFFECT,
3826
+ ATTR_PERSISTENT,
3827
+ ATTR_DISABLE_CLOSE,
3828
+ ATTR_DISABLE_DRAG,
3829
+ ATTR_CLOSE_ON_ROUTE_CHANGE,
3830
+ ATTR_RADIUS,
3831
+ ATTR_MAX_HEIGHT,
3832
+ ATTR_RETURN_FOCUS_TO,
3833
+ ATTR_BACKDROP_COLOR,
3834
+ ATTR_SCRIM_COLOR,
3835
+ ATTR_SNAP
3536
3836
  ];
3537
3837
  }
3538
3838
  connectedCallback() {
@@ -3551,11 +3851,39 @@ var init_BottomSheetElement = __esm({
3551
3851
  disconnectedCallback() {
3552
3852
  this.teardownEngine();
3553
3853
  }
3554
- attributeChangedCallback() {
3854
+ attributeChangedCallback(name, _old, value) {
3555
3855
  if (!this.engine) return;
3856
+ if (LIVE_ATTRS.has(name)) {
3857
+ this.applyLiveAttribute(name, value);
3858
+ return;
3859
+ }
3556
3860
  this.teardownEngine();
3557
3861
  this.initEngine();
3558
3862
  }
3863
+ applyLiveAttribute(name, value) {
3864
+ if (!this.engine) return;
3865
+ switch (name) {
3866
+ case ATTR_RADIUS: {
3867
+ const r = parseDimension(value);
3868
+ if (r !== void 0) this.engine.setRadius(r);
3869
+ break;
3870
+ }
3871
+ case ATTR_MAX_HEIGHT: {
3872
+ const h = parseDimension(value);
3873
+ if (h !== void 0) this.engine.setMaxHeight(h);
3874
+ break;
3875
+ }
3876
+ case ATTR_BACKDROP_COLOR:
3877
+ case ATTR_SCRIM_COLOR:
3878
+ this.engine.setScrimColor(value);
3879
+ break;
3880
+ case ATTR_SNAP:
3881
+ if (value && value !== this.engine.state.activeId) {
3882
+ void this.engine.snapTo(value);
3883
+ }
3884
+ break;
3885
+ }
3886
+ }
3559
3887
  getEngine() {
3560
3888
  return this.engine;
3561
3889
  }
@@ -3577,6 +3905,8 @@ var init_BottomSheetElement = __esm({
3577
3905
  const { sheet, handle, content, backdrop, announcer } = this.refs;
3578
3906
  const mode = parseMode(this.getAttribute(ATTR_MODE));
3579
3907
  sheet.setAttribute("data-mode", mode);
3908
+ this.refs.leftButton.setAttribute("data-mode", mode);
3909
+ this.refs.rightButton.setAttribute("data-mode", mode);
3580
3910
  const focusTrap = this.getAttribute(ATTR_FOCUS_TRAP) === "true";
3581
3911
  if (focusTrap) {
3582
3912
  sheet.setAttribute("aria-modal", "true");
@@ -3590,7 +3920,9 @@ var init_BottomSheetElement = __esm({
3590
3920
  const showBackdrop = this.getAttribute(ATTR_BACKDROP) !== "false";
3591
3921
  backdrop.style.display = showBackdrop ? "" : "none";
3592
3922
  if (showBackdrop) {
3593
- this.backdropClickHandler = () => void this.engine?.close();
3923
+ this.backdropClickHandler = () => {
3924
+ if (this.engine?.canDismiss()) void this.engine.close("backdrop");
3925
+ };
3594
3926
  backdrop.addEventListener("click", this.backdropClickHandler);
3595
3927
  }
3596
3928
  const opts = {
@@ -3607,10 +3939,19 @@ var init_BottomSheetElement = __esm({
3607
3939
  focusTrap,
3608
3940
  closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
3609
3941
  lockBodyScroll: this.getAttribute(ATTR_LOCK_BODY_SCROLL) !== "false",
3610
- stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true"
3942
+ stackEffect: this.getAttribute(ATTR_STACK_EFFECT) === "true",
3943
+ persistent: this.getAttribute(ATTR_PERSISTENT) === "true",
3944
+ disableClose: this.getAttribute(ATTR_DISABLE_CLOSE) === "true",
3945
+ disableDrag: this.getAttribute(ATTR_DISABLE_DRAG) === "true",
3946
+ closeOnRouteChange: this.getAttribute(ATTR_CLOSE_ON_ROUTE_CHANGE) === "true",
3947
+ radius: parseDimension(this.getAttribute(ATTR_RADIUS)),
3948
+ maxHeight: parseDimension(this.getAttribute(ATTR_MAX_HEIGHT)),
3949
+ returnFocusTo: this.getAttribute(ATTR_RETURN_FOCUS_TO) ?? void 0
3611
3950
  };
3612
3951
  this.engine = new BottomSheetEngine(opts);
3613
3952
  sheet.setAttribute("data-active", this.engine.state.activeId);
3953
+ const scrimColor = this.getAttribute(ATTR_SCRIM_COLOR) ?? this.getAttribute(ATTR_BACKDROP_COLOR);
3954
+ if (scrimColor !== null) this.engine.setScrimColor(scrimColor);
3614
3955
  for (const el of Array.from(
3615
3956
  this.querySelectorAll('[slot="anchor"]')
3616
3957
  )) {
@@ -3634,6 +3975,26 @@ var init_BottomSheetElement = __esm({
3634
3975
  this.engine.on(
3635
3976
  "progress",
3636
3977
  (payload) => this.dispatchEvent(new CustomEvent("progress", { detail: payload }))
3978
+ ),
3979
+ this.engine.on(
3980
+ "before-close",
3981
+ (payload) => this.dispatchEvent(new CustomEvent("before-close", { detail: payload }))
3982
+ ),
3983
+ this.engine.on(
3984
+ "opened",
3985
+ (payload) => this.dispatchEvent(new CustomEvent("opened", { detail: payload }))
3986
+ ),
3987
+ this.engine.on(
3988
+ "closed",
3989
+ () => this.dispatchEvent(new CustomEvent("closed"))
3990
+ ),
3991
+ this.engine.on(
3992
+ "dragstart",
3993
+ (payload) => this.dispatchEvent(new CustomEvent("drag-start", { detail: payload }))
3994
+ ),
3995
+ this.engine.on(
3996
+ "dragend",
3997
+ (payload) => this.dispatchEvent(new CustomEvent("drag-end", { detail: payload }))
3637
3998
  )
3638
3999
  ];
3639
4000
  }
@@ -3643,8 +4004,29 @@ var init_BottomSheetElement = __esm({
3643
4004
  open(id) {
3644
4005
  return this.engine?.open(id) ?? Promise.resolve();
3645
4006
  }
3646
- close() {
3647
- return this.engine?.close() ?? Promise.resolve();
4007
+ close(reason) {
4008
+ return this.engine?.close(reason) ?? Promise.resolve();
4009
+ }
4010
+ expand() {
4011
+ return this.engine?.expand() ?? Promise.resolve();
4012
+ }
4013
+ collapse() {
4014
+ return this.engine?.collapse() ?? Promise.resolve();
4015
+ }
4016
+ isTop() {
4017
+ return this.engine?.isTop() ?? false;
4018
+ }
4019
+ depth() {
4020
+ return this.engine?.depth() ?? 0;
4021
+ }
4022
+ canDismiss() {
4023
+ return this.engine?.canDismiss() ?? false;
4024
+ }
4025
+ setRadius(r) {
4026
+ this.engine?.setRadius(r);
4027
+ }
4028
+ setMaxHeight(h) {
4029
+ this.engine?.setMaxHeight(h);
3648
4030
  }
3649
4031
  setAllowed(ids, snap) {
3650
4032
  this.engine?.setAllowed(ids, snap);
@@ -3657,9 +4039,26 @@ var init_BottomSheetElement = __esm({
3657
4039
  return this.engine?.setScrimStages(opts) ?? (() => {
3658
4040
  });
3659
4041
  }
4042
+ recompute() {
4043
+ this.engine?.recompute();
4044
+ }
3660
4045
  get sheetState() {
3661
4046
  return this.engine?.state ?? null;
3662
4047
  }
4048
+ get snap() {
4049
+ return this.engine?.state.activeId ?? this.getAttribute(ATTR_SNAP);
4050
+ }
4051
+ set snap(id) {
4052
+ if (id === null) {
4053
+ this.removeAttribute(ATTR_SNAP);
4054
+ return;
4055
+ }
4056
+ if (this.engine) {
4057
+ if (id !== this.engine.state.activeId) void this.engine.snapTo(id);
4058
+ } else {
4059
+ this.setAttribute(ATTR_SNAP, id);
4060
+ }
4061
+ }
3663
4062
  };
3664
4063
  exports.defineBottomSheet = (tag = "bottom-sheet") => {
3665
4064
  if (typeof customElements === "undefined" || customElements.get(tag)) return;