@surdeddd/bottom-sheet 0.1.0 → 0.2.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 (50) hide show
  1. package/README.md +6 -0
  2. package/dist/BottomSheetEngine-CIT9Htti.d.cts +219 -0
  3. package/dist/BottomSheetEngine-Df9MwBbm.d.ts +219 -0
  4. package/dist/element.cjs +95 -440
  5. package/dist/element.d.cts +3 -56
  6. package/dist/element.d.ts +3 -56
  7. package/dist/element.iife.global.js +2 -2
  8. package/dist/element.js +95 -440
  9. package/dist/index.cjs +84 -494
  10. package/dist/index.d.cts +3 -84
  11. package/dist/index.d.ts +3 -84
  12. package/dist/index.js +84 -494
  13. package/dist/integrations/formik.d.cts +2 -8
  14. package/dist/integrations/formik.d.ts +2 -8
  15. package/dist/integrations/react-hook-form.d.cts +2 -11
  16. package/dist/integrations/react-hook-form.d.ts +2 -11
  17. package/dist/overlay.cjs +0 -87
  18. package/dist/overlay.d.cts +1 -137
  19. package/dist/overlay.d.ts +1 -137
  20. package/dist/overlay.js +0 -87
  21. package/dist/preact.d.cts +2 -2
  22. package/dist/preact.d.ts +2 -2
  23. package/dist/preact.js +0 -1
  24. package/dist/qwik.cjs +87 -439
  25. package/dist/qwik.d.cts +3 -24
  26. package/dist/qwik.d.ts +3 -24
  27. package/dist/qwik.js +87 -439
  28. package/dist/react.cjs +91 -496
  29. package/dist/react.d.cts +16 -158
  30. package/dist/react.d.ts +16 -158
  31. package/dist/react.js +91 -496
  32. package/dist/solid.cjs +87 -439
  33. package/dist/solid.d.cts +4 -31
  34. package/dist/solid.d.ts +4 -31
  35. package/dist/solid.js +87 -439
  36. package/dist/styles.css +27 -102
  37. package/dist/svelte.cjs +139 -462
  38. package/dist/svelte.d.ts +9 -2
  39. package/dist/svelte.js +139 -462
  40. package/dist/themes/ios.css +0 -3
  41. package/dist/themes/material.css +0 -3
  42. package/dist/themes/vercel.css +0 -3
  43. package/dist/{transform-Brkf-wzo.d.cts → transform-jVfAikE9.d.cts} +0 -1
  44. package/dist/{transform-Brkf-wzo.d.ts → transform-jVfAikE9.d.ts} +0 -1
  45. package/dist/vue.cjs +104 -446
  46. package/dist/vue.d.ts +7 -1
  47. package/dist/vue.js +105 -447
  48. package/package.json +3 -3
  49. package/dist/BottomSheetEngine-CvSVbo6c.d.cts +0 -704
  50. package/dist/BottomSheetEngine-DbO8rW_3.d.ts +0 -704
package/dist/element.js CHANGED
@@ -270,11 +270,6 @@ var init_snap_resolver = __esm({
270
270
  this.allowedIds = allowed ?? raw.map((p) => p.id);
271
271
  this.recompute();
272
272
  }
273
- /**
274
- * Re-resolve raw → resolved against the current viewport / fit-measure,
275
- * recompute maxAxisSize, invalidate range cache, notify engine. Called on
276
- * resize, orientationchange, visualViewport changes, and snap-list swaps.
277
- */
278
273
  recompute() {
279
274
  this.resolved = resolveSnapList(this.raw, this.mode, this.measureFit);
280
275
  this.maxAxisSize = this.resolved.reduce(
@@ -284,15 +279,10 @@ var init_snap_resolver = __esm({
284
279
  this.rangeCache = null;
285
280
  this.onMaxAxisSizeChange?.(this.maxAxisSize);
286
281
  }
287
- /** Replace the raw snap list and recompute. Engine calls on `setSnapPoints`. */
288
282
  setRaw(raw) {
289
283
  this.raw = raw;
290
284
  this.recompute();
291
285
  }
292
- /**
293
- * Replace the allow-list. Range cache depends on `(resolvedSnaps ×
294
- * allowedIds)` so flipping ids alone invalidates without a full recompute.
295
- */
296
286
  setAllowedIds(ids) {
297
287
  this.allowedIds = ids;
298
288
  this.rangeCache = null;
@@ -300,11 +290,6 @@ var init_snap_resolver = __esm({
300
290
  findById(id) {
301
291
  return findById(id, this.resolved);
302
292
  }
303
- /**
304
- * Pure drag-settle target resolution. Engine passes the gesture-specific
305
- * fields (delta, velocity, pointerKind, flickVelocity, dragThreshold) plus
306
- * `size`/`activeId` since those are still engine-owned.
307
- */
308
293
  findDragSettleTarget(args) {
309
294
  return findDragSettleTarget({
310
295
  ...args,
@@ -313,18 +298,9 @@ var init_snap_resolver = __esm({
313
298
  maxAxisSize: this.maxAxisSize
314
299
  });
315
300
  }
316
- /**
317
- * Memoised allowed-range query. Twice-per-drag-frame (gesture onMove +
318
- * applySize→computeProgress); cache collapses 2× iteration of resolvedSnaps
319
- * to one recompute per geometry/allow-list change.
320
- */
321
301
  getAllowedRange() {
322
302
  return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
323
303
  }
324
- /**
325
- * Read-only view of resolved snaps. Returned as `readonly` to discourage
326
- * accidental mutation by feature install sites that close over this list.
327
- */
328
304
  getResolvedSnaps() {
329
305
  return this.resolved;
330
306
  }
@@ -334,18 +310,6 @@ var init_snap_resolver = __esm({
334
310
  getMaxAxisSize() {
335
311
  return this.maxAxisSize;
336
312
  }
337
- /**
338
- * External clamp override. The resize-observer feature compares
339
- * `getMaxAxisSize()` against the live viewport and shrinks if the largest
340
- * resolved snap exceeds it (mobile address-bar collapse / soft-keyboard
341
- * window resize). The next `recompute()` overwrites this clamp from the
342
- * resolved-snap reduction.
343
- *
344
- * Does NOT invalidate the range cache — viewport clamping doesn't change
345
- * `(resolvedSnaps × allowedIds) → range`. DOES fire `onMaxAxisSizeChange`
346
- * so the resolver owns the layout-axis style write end-to-end (symmetric
347
- * with `recompute()` — feature call sites no longer duplicate the write).
348
- */
349
313
  setMaxAxisSize(n) {
350
314
  this.maxAxisSize = n;
351
315
  this.onMaxAxisSizeChange?.(n);
@@ -769,36 +733,17 @@ var init_opacity_dedup = __esm({
769
733
  constructor() {
770
734
  __publicField(this, "last", -1);
771
735
  }
772
- /**
773
- * Returns `true` (and updates the cached value) if `next` differs from the
774
- * cached `last` by more than `epsilon`. Returns `false` (and leaves the
775
- * cache untouched) when the delta is within epsilon — caller skips the
776
- * CSSOM write.
777
- */
778
736
  shouldWrite(next, epsilon) {
779
737
  if (Math.abs(next - this.last) <= epsilon) return false;
780
738
  this.last = next;
781
739
  return true;
782
740
  }
783
- /**
784
- * Force the next `shouldWrite` to pass. Resets the cache to `-1` so any
785
- * non-negative `next` exceeds the epsilon comparison. Called from setters
786
- * that mutate state under the cache (range/mode/enabled flips that change
787
- * the progress→opacity mapping but don't move `progress` itself).
788
- */
789
741
  invalidate() {
790
742
  this.last = -1;
791
743
  }
792
- /**
793
- * Record `value` as the last-written without performing a write. Used
794
- * when a setter writes a literal value through a different path (e.g.
795
- * `setScrimMode("off")` writes `opacity: "0"` inline) and wants future
796
- * `shouldWrite` calls to short-circuit against that known cached state.
797
- */
798
744
  setLastWritten(value) {
799
745
  this.last = value;
800
746
  }
801
- /** Read-only accessor for diagnostic / dedup-aware peek. */
802
747
  get value() {
803
748
  return this.last;
804
749
  }
@@ -806,45 +751,74 @@ var init_opacity_dedup = __esm({
806
751
  }
807
752
  });
808
753
 
754
+ // src/core/primitives/devWarn.ts
755
+ function devWarn(...args) {
756
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
757
+ console.warn(...args);
758
+ }
759
+ }
760
+ var init_devWarn = __esm({
761
+ "src/core/primitives/devWarn.ts"() {
762
+ }
763
+ });
764
+
809
765
  // src/core/controllers/scrim-controller.ts
810
- var ScrimController;
766
+ function resolveSheetAnchoredStyle(mode, position, inset) {
767
+ const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
768
+ switch (mode) {
769
+ case "bottom":
770
+ if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
771
+ if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
772
+ return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
773
+ case "top":
774
+ if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
775
+ if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
776
+ return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
777
+ case "left":
778
+ if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
779
+ if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
780
+ return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
781
+ case "right":
782
+ if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
783
+ if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
784
+ return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
785
+ }
786
+ }
787
+ function applySheetAnchoredPosition(ws, mode, position, inset) {
788
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
789
+ if (style.top !== void 0) ws.top = style.top;
790
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
791
+ if (style.left !== void 0) ws.left = style.left;
792
+ if (style.right !== void 0) ws.right = style.right;
793
+ if (style.transform !== void 0) ws.transform = style.transform;
794
+ }
795
+ var VALID_CSS_LENGTH, ScrimController;
811
796
  var init_scrim_controller = __esm({
812
797
  "src/core/controllers/scrim-controller.ts"() {
813
798
  init_types();
814
799
  init_snap_points();
815
800
  init_hot_path_thresholds();
816
801
  init_opacity_dedup();
802
+ init_devWarn();
803
+ VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
817
804
  ScrimController = class {
818
805
  constructor(deps, opts) {
819
- // ── DOM refs (mirror engine's; cached for hot path) ─────────────────
820
806
  __publicField(this, "screenComponent");
821
807
  __publicField(this, "backdrop");
822
808
  __publicField(this, "mode");
823
- // ── Engine callbacks ────────────────────────────────────────────────
824
809
  __publicField(this, "isDestroyed");
825
810
  __publicField(this, "isTopSheet");
826
811
  __publicField(this, "getAllowedIds");
827
812
  __publicField(this, "getResolvedSnaps");
828
813
  __publicField(this, "snapToFn");
829
- // ── Mode + state ────────────────────────────────────────────────────
830
814
  __publicField(this, "scrimMode");
831
815
  __publicField(this, "backdropRange");
832
816
  __publicField(this, "screenRange");
833
- // setScrimEnabled toggle — gated through `scrimEnabled` rather than nuking
834
- // ranges so re-enable can restore prior values without a remount. Public
835
- // so tests / introspection can read it without an `as any` cast.
836
817
  __publicField(this, "scrimEnabled", true);
837
818
  __publicField(this, "savedScrimRanges", null);
838
- // ── Tap-to-close ────────────────────────────────────────────────────
839
819
  __publicField(this, "scrimTapToCloseEnabled", false);
840
820
  __publicField(this, "detachScrimTap", null);
841
- // ── Scrim overlay slot (positioned floating element) ────────────────
842
821
  __publicField(this, "scrimOverlayTeardown", null);
843
- // ── Hot-path opacity dedup caches (per-frame writes) ────────────────
844
- // Numeric sentinels migrated to WriteSentinel primitive — single
845
- // invalidation surface, shared with the engine's size/progress sentinels.
846
- // The two string-typed sentinels (`Pointer`, `Display`) keep their inline
847
- // pattern: equality on string union, no epsilon, different reset semantics.
848
822
  __publicField(this, "backdropOpacitySentinel", new WriteSentinel());
849
823
  __publicField(this, "screenOpacitySentinel", new WriteSentinel());
850
824
  __publicField(this, "lastBackdropPointer", "");
@@ -879,41 +853,15 @@ var init_scrim_controller = __esm({
879
853
  }
880
854
  }
881
855
  }
882
- /**
883
- * Invalidate the opacity dedup caches. Called from any setter that mutates
884
- * range/mode/enabled — opacity is a pure function of (progress, range,
885
- * enabled), so a config change makes the cached values stale even if
886
- * `progress` itself didn't move. Next `applyOpacity(progress, false)` call
887
- * sees the -1 sentinel and re-runs the math instead of short-circuiting.
888
- *
889
- * Public for the engine: `setSnapPoints` mutates resolved geometry which
890
- * shifts `progress→opacity` mapping under the cache; engine calls this to
891
- * keep the next render frame correct on a static sheet.
892
- * @internal
893
- */
894
856
  invalidateOpacityCache() {
895
857
  this.backdropOpacitySentinel.invalidate();
896
858
  this.screenOpacitySentinel.invalidate();
897
859
  }
898
- /** Engine calls this when its `attach()` runs — installs scrim-tap-click if requested. */
899
860
  attach() {
900
861
  if (this.scrimTapToCloseEnabled) {
901
862
  this.setScrimTapToClose(true);
902
863
  }
903
864
  }
904
- // ── Hot-path: opacity computation called per-frame from engine.applySize ──
905
- /**
906
- * Per-frame opacity writer. Computes backdrop + screen opacity from the
907
- * sheet's progress (0..1 within allowed range), gated by sub-pixel dedup
908
- * caches so steady-state frames don't trigger style recalc.
909
- *
910
- * `progressChanged` is the engine's already-computed dedup gate — when
911
- * `false`, opacity is by construction a pure function of `progress` so
912
- * cached values are still valid; we short-circuit the Math.min/max +
913
- * division work entirely. Setters that mutate ranges/mode/enabled
914
- * invalidate the cache by writing -1 sentinels so the next call re-runs
915
- * the math.
916
- */
917
865
  applyOpacity(progress, progressChanged) {
918
866
  if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
919
867
  return;
@@ -959,80 +907,30 @@ var init_scrim_controller = __esm({
959
907
  }
960
908
  }
961
909
  }
962
- // ── Public setters (engine forwards these 1:1) ─────────────────────
963
- /**
964
- * Replace the [start, end] progress window in which the backdrop fades 0→1.
965
- * @param range - Tuple of two progress values in 0..1 (start <= end).
966
- * @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
967
- * @example
968
- * // Backdrop only starts darkening after the sheet crosses 30% open.
969
- * engine.setBackdropRange([0.3, 1]);
970
- */
971
910
  setBackdropRange(range, applySize) {
972
911
  if (this.isDestroyed()) return;
973
912
  this.backdropRange = range;
974
913
  this.invalidateOpacityCache();
975
914
  applySize();
976
915
  }
977
- /**
978
- * Replace the [start, end] progress window in which the screen scrim fades 0→1.
979
- * @param range - Tuple of two progress values in 0..1 (start <= end).
980
- * @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
981
- * @example
982
- * // Scrim is fully opaque only when the sheet is past 50% — softer feel.
983
- * engine.setScreenRange([0, 0.5]);
984
- */
985
916
  setScreenRange(range, applySize) {
986
917
  if (this.isDestroyed()) return;
987
918
  this.screenRange = range;
988
919
  this.invalidateOpacityCache();
989
920
  applySize();
990
921
  }
991
- /**
992
- * Set the scrim background color. Pass `null`/`undefined` to clear the
993
- * inline write and let consumer CSS take over.
994
- * @param color - Any valid CSS color string, or `null`/`undefined` to clear.
995
- * @example
996
- * engine.setScrimColor("rgba(0, 0, 0, 0.6)");
997
- * engine.setScrimColor(null); // hand back to consumer CSS
998
- */
999
922
  setScrimColor(color) {
1000
923
  if (this.isDestroyed()) return;
1001
924
  this.applyScrimStyles({ color: color === void 0 ? null : color });
1002
925
  }
1003
- /**
1004
- * Set the scrim backdrop-filter blur radius. Pass `null`/`undefined` to clear.
1005
- * @param blur - CSS length (e.g. `"8px"`) consumed as `blur(<value>)`, or
1006
- * `null`/`undefined` to remove the filter.
1007
- * @example
1008
- * engine.setScrimBlur("12px"); // frosted-glass effect
1009
- * engine.setScrimBlur(null); // disable blur
1010
- */
1011
926
  setScrimBlur(blur) {
1012
927
  if (this.isDestroyed()) return;
1013
928
  this.applyScrimStyles({ blur: blur === void 0 ? null : blur });
1014
929
  }
1015
- /**
1016
- * Toggle whether the scrim layer captures pointer events.
1017
- * @param interactive - `true` → `pointer-events: auto`; `false` → `none`.
1018
- * @example
1019
- * engine.setScrimInteractive(true); // scrim swallows clicks
1020
- * engine.setScrimInteractive(false); // clicks pass through to content below
1021
- */
1022
930
  setScrimInteractive(interactive) {
1023
931
  if (this.isDestroyed()) return;
1024
932
  this.applyScrimStyles({ interactive });
1025
933
  }
1026
- /**
1027
- * Switch scrim positioning at runtime.
1028
- * @param mode - `"full"` (consumer CSS), `"above-sheet"` (engine-pinned inset
1029
- * so only the area NOT covered by the sheet is dimmed), or
1030
- * `"off"` (display:none + opacity:0 — no remount needed).
1031
- * @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
1032
- * @example
1033
- * engine.setScrimMode("above-sheet"); // dim only the area above the sheet
1034
- * engine.setScrimMode("off"); // disable scrim layer
1035
- */
1036
934
  setScrimMode(mode, applySize) {
1037
935
  if (this.isDestroyed()) return;
1038
936
  if (mode === this.scrimMode) return;
@@ -1062,15 +960,6 @@ var init_scrim_controller = __esm({
1062
960
  }
1063
961
  applySize();
1064
962
  }
1065
- /**
1066
- * Enable or disable click-on-scrim → snap to first non-zero allowed snap.
1067
- * Auto-promotes `pointer-events: auto` when scrim is non-interactive so the
1068
- * click listener actually fires.
1069
- * @param enabled - `true` installs the click listener; `false` removes it.
1070
- * @example
1071
- * engine.setScrimTapToClose(true); // tap outside sheet collapses it
1072
- * engine.setScrimTapToClose(false); // require explicit close button
1073
- */
1074
963
  setScrimTapToClose(enabled) {
1075
964
  if (this.isDestroyed()) return;
1076
965
  if (!this.screenComponent) return;
@@ -1100,16 +989,6 @@ var init_scrim_controller = __esm({
1100
989
  this.scrimTapToCloseEnabled = false;
1101
990
  }
1102
991
  }
1103
- /**
1104
- * Toggle the scrim layer without remounting. Disabling stashes current
1105
- * `screenRange`/`backdropRange` and degenerates them to `[1, 1]` (opacity
1106
- * formula → 0 across the whole range); re-enabling restores the saved values.
1107
- * @param enabled - `false` hides scrim, `true` restores prior ranges.
1108
- * @param applySize - Engine-supplied recompute hook; runs one applyOpacity pass.
1109
- * @example
1110
- * engine.setScrimEnabled(false); // hide scrim, ranges remembered
1111
- * engine.setScrimEnabled(true); // restore prior ranges (no remount)
1112
- */
1113
992
  setScrimEnabled(enabled, applySize) {
1114
993
  if (this.isDestroyed()) return;
1115
994
  if (enabled === this.scrimEnabled) return;
@@ -1128,20 +1007,6 @@ var init_scrim_controller = __esm({
1128
1007
  }
1129
1008
  applySize();
1130
1009
  }
1131
- /**
1132
- * Batch-update multiple scrim fields with a single applySize() at the end.
1133
- * If `preset` is set, it seeds color/blur/interactive/range first; explicit
1134
- * fields override.
1135
- * @param opts - Partial scrim config: preset, mode, enabled, tapToClose,
1136
- * color, blur, interactive, range.
1137
- * @param applySize - Engine-supplied recompute hook; runs ONCE per call.
1138
- * @example
1139
- * engine.setScrim({
1140
- * preset: "frosted",
1141
- * mode: "above-sheet",
1142
- * tapToClose: true,
1143
- * });
1144
- */
1145
1010
  setScrim(opts, applySize) {
1146
1011
  if (this.isDestroyed()) return;
1147
1012
  if (opts.preset) {
@@ -1164,27 +1029,18 @@ var init_scrim_controller = __esm({
1164
1029
  interactive: opts.interactive
1165
1030
  });
1166
1031
  if (opts.range !== void 0) {
1167
- this.screenRange = opts.range;
1032
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1033
+ this.savedScrimRanges = {
1034
+ screen: opts.range,
1035
+ backdrop: this.savedScrimRanges.backdrop
1036
+ };
1037
+ } else {
1038
+ this.screenRange = opts.range;
1039
+ }
1168
1040
  }
1169
1041
  this.invalidateOpacityCache();
1170
1042
  applySize();
1171
1043
  }
1172
- /**
1173
- * Inject a positioned floating element into the scrim layer (e.g. a close
1174
- * button anchored top-right). Replaces any prior overlay so repeated calls
1175
- * (reactive effects re-running) don't accumulate wrappers.
1176
- * @param opts - Children element, optional `inset` (default `"16px"`),
1177
- * `position` (9-way grid, default `"top-right"`), and
1178
- * `interactive` flag (default `true`).
1179
- * @returns Teardown function that removes the wrapper from the DOM.
1180
- * @example
1181
- * const teardown = engine.setScrimOverlay({
1182
- * children: closeBtn,
1183
- * position: "top-right",
1184
- * inset: "20px",
1185
- * });
1186
- * // later: teardown(); // remove the floating button
1187
- */
1188
1044
  setScrimOverlay(opts) {
1189
1045
  if (this.isDestroyed()) return () => {
1190
1046
  };
@@ -1192,7 +1048,18 @@ var init_scrim_controller = __esm({
1192
1048
  };
1193
1049
  this.scrimOverlayTeardown?.();
1194
1050
  const screen = this.screenComponent;
1195
- const inset = opts.inset ?? "16px";
1051
+ const insetRaw = opts.inset ?? "16px";
1052
+ const inset = VALID_CSS_LENGTH.test(insetRaw) ? insetRaw : "16px";
1053
+ if (inset !== insetRaw) {
1054
+ devWarn(
1055
+ `[bottom-sheet] setScrimOverlay: invalid inset ${JSON.stringify(insetRaw)}; expected a CSS length like "16px" / "1rem" / "5%". Falling back to "16px".`
1056
+ );
1057
+ }
1058
+ if (opts.children.ownerDocument && opts.children.ownerDocument !== screen.ownerDocument) {
1059
+ devWarn(
1060
+ "[bottom-sheet] setScrimOverlay: children belong to a different document than the scrim. Cross-document adoption can drop event listeners; construct children via the same document as the sheet root."
1061
+ );
1062
+ }
1196
1063
  const position = opts.position ?? "top-right";
1197
1064
  const interactive = opts.interactive ?? true;
1198
1065
  const wrapper = screen.ownerDocument.createElement("div");
@@ -1241,12 +1108,30 @@ var init_scrim_controller = __esm({
1241
1108
  ws.bottom = inset;
1242
1109
  ws.right = inset;
1243
1110
  break;
1111
+ case "sheet-top-left":
1112
+ case "sheet-top-center":
1113
+ case "sheet-top-right":
1114
+ applySheetAnchoredPosition(
1115
+ ws,
1116
+ this.mode,
1117
+ position,
1118
+ inset
1119
+ );
1120
+ break;
1244
1121
  }
1245
1122
  if (interactive) {
1246
1123
  ws.pointerEvents = "auto";
1247
1124
  }
1248
1125
  wrapper.appendChild(opts.children);
1249
- screen.appendChild(wrapper);
1126
+ const host = screen.parentElement;
1127
+ if (!host) {
1128
+ devWarn(
1129
+ "[bottom-sheet] setScrimOverlay: scrim element has no parent, skipping overlay injection. Mount the sheet root before calling setScrimOverlay."
1130
+ );
1131
+ return () => {
1132
+ };
1133
+ }
1134
+ host.appendChild(wrapper);
1250
1135
  const teardown = () => {
1251
1136
  if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
1252
1137
  if (this.scrimOverlayTeardown === teardown) {
@@ -1256,7 +1141,6 @@ var init_scrim_controller = __esm({
1256
1141
  this.scrimOverlayTeardown = teardown;
1257
1142
  return teardown;
1258
1143
  }
1259
- /** Tear down toggle-state listeners + remove any injected overlay wrapper. */
1260
1144
  destroy() {
1261
1145
  this.detachScrimTap?.();
1262
1146
  this.detachScrimTap = null;
@@ -1275,8 +1159,6 @@ var init_scrim_controller = __esm({
1275
1159
  this.lastBackdropPointer = "";
1276
1160
  this.lastScreenDisplay = "";
1277
1161
  }
1278
- // ── Private helpers ────────────────────────────────────────────────
1279
- /** Centralized scrim style writer — atomic 3-property write across all setters. */
1280
1162
  applyScrimStyles(opts) {
1281
1163
  if (!this.screenComponent) return;
1282
1164
  const s = this.screenComponent.style;
@@ -1298,7 +1180,6 @@ var init_scrim_controller = __esm({
1298
1180
  s.pointerEvents = opts.interactive ? "auto" : "none";
1299
1181
  }
1300
1182
  }
1301
- /** Pin the screen overlay to the area NOT occupied by the sheet. */
1302
1183
  applyAboveSheetInset() {
1303
1184
  if (!this.screenComponent) return;
1304
1185
  const insetByMode = {
@@ -1489,13 +1370,9 @@ var init_animation_runner = __esm({
1489
1370
  __publicField(this, "duration");
1490
1371
  __publicField(this, "easing");
1491
1372
  __publicField(this, "respectReducedMotion");
1492
- /** Live-tracked via mq listener so animateTo skips matchMedia per snap. */
1493
1373
  __publicField(this, "reducedMotion", false);
1494
- /** Disposes the matchMedia change listener — drained from destroy(). */
1495
1374
  __publicField(this, "detachReducedMotion", null);
1496
- /** Computed once — both inputs (option + API presence) stable for life. */
1497
1375
  __publicField(this, "viewTransitionsAvailable");
1498
- /** In-flight handles. Public for tests / debugging only — engine drives via cancel(). */
1499
1376
  __publicField(this, "currentTween", null);
1500
1377
  __publicField(this, "currentSpring", null);
1501
1378
  this.element = deps.element;
@@ -1526,26 +1403,13 @@ var init_animation_runner = __esm({
1526
1403
  }
1527
1404
  }
1528
1405
  }
1529
- /**
1530
- * Live derived flag — `true` whenever a spring/tween handle is in flight.
1531
- * Reading the canonical handles (instead of mirroring into a separate
1532
- * boolean) eliminates the reduced-motion early-return latent bug where
1533
- * the flag stayed true forever, and removes the cross-controller
1534
- * setIsAnimating callback.
1535
- */
1536
1406
  get isAnimating() {
1537
1407
  return this.currentTween !== null || this.currentSpring !== null;
1538
1408
  }
1539
- /** Cancel any in-flight tween/spring without touching engine state. */
1540
1409
  cancel() {
1541
1410
  this.currentTween?.cancel();
1542
1411
  this.currentSpring?.cancel();
1543
1412
  }
1544
- /**
1545
- * Animate from current size to `target`. Resolves when the animation
1546
- * completes; resolves early if cancelled mid-flight (caller checks an
1547
- * abort signal post-await for cycle-invariant correctness).
1548
- */
1549
1413
  async animateTo(target, velocityPxPerMs) {
1550
1414
  if (target === this.getSize()) return;
1551
1415
  this.currentTween?.cancel();
@@ -1586,7 +1450,6 @@ var init_animation_runner = __esm({
1586
1450
  if (!this.isDragging()) this.element.style.willChange = "auto";
1587
1451
  }
1588
1452
  }
1589
- /** Tear down the matchMedia listener. Engine calls from destroy(). */
1590
1453
  destroy() {
1591
1454
  this.detachReducedMotion?.();
1592
1455
  this.detachReducedMotion = null;
@@ -1768,12 +1631,8 @@ var init_lifecycle_controller = __esm({
1768
1631
  __publicField(this, "inertSiblings");
1769
1632
  __publicField(this, "shouldApplyInertSiblings");
1770
1633
  __publicField(this, "returnFocus");
1771
- /** Toggle-state teardowns — fire on close, NOT in destroy-only stack. */
1772
1634
  __publicField(this, "releaseFocusTrap", null);
1773
1635
  __publicField(this, "releaseScrollLock", null);
1774
- /** True between install() and release(). Guards release()/destroy() from
1775
- * re-running side effects (notably `returnFocus.focus()`) on already-
1776
- * released or never-installed instances. */
1777
1636
  __publicField(this, "installed", false);
1778
1637
  __publicField(this, "destroyed", false);
1779
1638
  this.element = deps.element;
@@ -1787,14 +1646,6 @@ var init_lifecycle_controller = __esm({
1787
1646
  this.returnFocus = opts.returnFocus;
1788
1647
  this.inertSiblings = createInertSiblings(() => this.element);
1789
1648
  }
1790
- /**
1791
- * Run the open-time install steps. Idempotent: each step checks its
1792
- * existing teardown ref so a re-open during a half-cancelled close
1793
- * doesn't double-install. Sets `installed=true` BEFORE the first side
1794
- * effect so a partial throw (detached / shadow-DOM target) leaves the
1795
- * caller's `release()` rollback path armed to undo whatever did succeed.
1796
- * No-op when destroyed.
1797
- */
1798
1649
  install() {
1799
1650
  if (this.destroyed) return;
1800
1651
  this.installed = true;
@@ -1811,22 +1662,10 @@ var init_lifecycle_controller = __esm({
1811
1662
  this.inertSiblings.apply();
1812
1663
  }
1813
1664
  }
1814
- /**
1815
- * Update the post-release focus target. Overlay re-binds this when the
1816
- * consumer changes `returnFocus` after construction; the new value applies
1817
- * on the next `release()`. No-op when destroyed.
1818
- */
1819
1665
  setReturnFocus(target) {
1820
1666
  if (this.destroyed) return;
1821
1667
  this.returnFocus = target;
1822
1668
  }
1823
- /**
1824
- * Release all install-time teardowns. Short-circuits when not installed,
1825
- * so `close() → destroy()` (each routing through here) only restores
1826
- * focus once. The `installed` guard lets `inertSiblings.remove()` and
1827
- * the focus-trap/scroll-lock teardown nullings stay simple — they were
1828
- * already idempotent, but `returnFocus.focus()` was firing on every call.
1829
- */
1830
1669
  release() {
1831
1670
  if (!this.installed) return;
1832
1671
  this.installed = false;
@@ -1840,12 +1679,6 @@ var init_lifecycle_controller = __esm({
1840
1679
  target?.focus?.();
1841
1680
  }
1842
1681
  }
1843
- /**
1844
- * Final teardown — releases any still-installed listeners and seals the
1845
- * controller. After destroy(), install() is a no-op and setReturnFocus()
1846
- * is rejected, so post-destroy mutations can't queue up a stale focus
1847
- * restore.
1848
- */
1849
1682
  destroy() {
1850
1683
  this.release();
1851
1684
  this.destroyed = true;
@@ -1860,12 +1693,10 @@ var init_gestures = __esm({
1860
1693
  "src/core/gestures.ts"() {
1861
1694
  isAxisVertical = (mode) => mode === "bottom" || mode === "top";
1862
1695
  SampleRing = class {
1863
- // current count, ≤ capacity
1864
1696
  constructor(capacity) {
1865
1697
  __publicField(this, "slots");
1866
1698
  __publicField(this, "capacity");
1867
1699
  __publicField(this, "head", 0);
1868
- // next write index
1869
1700
  __publicField(this, "size", 0);
1870
1701
  this.capacity = capacity;
1871
1702
  this.slots = new Array(capacity);
@@ -1878,12 +1709,6 @@ var init_gestures = __esm({
1878
1709
  this.head = (this.head + 1) % this.capacity;
1879
1710
  if (this.size < this.capacity) this.size++;
1880
1711
  }
1881
- /**
1882
- * Drop oldest entries whose timestamp is below `cutoff`, but never shrink
1883
- * below `minKeep`. Mirrors the original
1884
- * `while (samples.length > minKeep && samples[0].t < cutoff) samples.shift()`
1885
- * but without copying — just advances the logical tail.
1886
- */
1887
1712
  pruneBefore(cutoff, minKeep) {
1888
1713
  while (this.size > minKeep) {
1889
1714
  const oldestIdx = (this.head - this.size + this.capacity) % this.capacity;
@@ -2036,28 +1861,14 @@ var init_gesture_controller = __esm({
2036
1861
  __publicField(this, "detach", null);
2037
1862
  __publicField(this, "isDragging_", false);
2038
1863
  __publicField(this, "dragStartSize", 0);
2039
- // Only "touch" gestures are eligible for soft-keyboard dismissal —
2040
- // mouse/trackpad users don't have a keyboard to dismiss.
2041
1864
  __publicField(this, "currentPointerKind", "touch");
2042
- // One blur per drag — flipping focus every move event races the keyboard's
2043
- // own animations on iOS and makes the page jitter.
2044
1865
  __publicField(this, "keyboardDismissed", false);
2045
- // Reused payload object for the "drag" event — mutated each onMove instead
2046
- // of allocating a fresh `{size, delta}` per frame. At 60-120Hz a long drag
2047
- // would otherwise churn tens of KB through the GC. Mirrors browser
2048
- // PointerEvent semantics: consumers must NOT retain the reference across
2049
- // frames (clone if needed). See SheetEventMap.drag JSDoc.
2050
1866
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
2051
1867
  this.deps = deps;
2052
1868
  }
2053
- /** True between `dragstart` and `dragend`/`onCancel`. */
2054
1869
  get isDragging() {
2055
1870
  return this.isDragging_;
2056
1871
  }
2057
- /**
2058
- * Bind pointer listeners on the handle and return a teardown. Engine pushes
2059
- * the teardown into its TeardownStack so destroy() drains it once.
2060
- */
2061
1872
  install() {
2062
1873
  if (this.detach) return this.detach;
2063
1874
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
@@ -2114,11 +1925,6 @@ var init_gesture_controller = __esm({
2114
1925
  });
2115
1926
  return this.detach;
2116
1927
  }
2117
- /**
2118
- * Force-clear drag visual state. Engine calls from destroy() when destroyed
2119
- * mid-drag — gesture detach won't synthesize an onCancel, so we have to
2120
- * roll back the data-attribute + will-change ourselves.
2121
- */
2122
1928
  forceClearDragState() {
2123
1929
  if (!this.isDragging_) return;
2124
1930
  this.isDragging_ = false;
@@ -2142,11 +1948,6 @@ var init_aria_slider_writer = __esm({
2142
1948
  mode === "bottom" || mode === "top" ? "vertical" : "horizontal"
2143
1949
  );
2144
1950
  }
2145
- /**
2146
- * Update slider position. `allowedIds` length determines `aria-valuemax`;
2147
- * `activeId` indexes into it for `aria-valuenow` + `aria-valuetext`.
2148
- * Empty list → strip every dynamic attr so AT skips the handle entirely.
2149
- */
2150
1951
  setValue(allowedIds, activeId) {
2151
1952
  if (allowedIds.length === 0) {
2152
1953
  this.handle.removeAttribute("aria-valuemin");
@@ -2224,9 +2025,6 @@ function resolveEngineOptions(opts, extras) {
2224
2025
  scrimMode: opts.scrimMode,
2225
2026
  scrimColor: opts.scrimColor,
2226
2027
  scrimBlur: opts.scrimBlur,
2227
- // Auto-promote pointer-events when tap-to-close is on so the click
2228
- // listener actually receives events. Caller can still force `false`
2229
- // explicitly to opt out — `??` only kicks in for `undefined`.
2230
2028
  scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
2231
2029
  scrimTapToClose: opts.scrimTapToClose,
2232
2030
  scrimPreset: opts.scrimPreset,
@@ -2247,10 +2045,6 @@ function resolveEngineOptions(opts, extras) {
2247
2045
  closeOnEscape: opts.closeOnEscape,
2248
2046
  lockBodyScroll: opts.lockBodyScroll,
2249
2047
  inertSiblings: opts.inertSiblings,
2250
- // Folded in from `extras` so the lifecycle bag is fully assembled at
2251
- // the call site — engine no longer needs a spread+override at
2252
- // controller construction. When `extras` is omitted, the field stays
2253
- // undefined and the controller uses its own default predicate.
2254
2048
  shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2255
2049
  }
2256
2050
  };
@@ -2319,6 +2113,7 @@ var init_BottomSheetEngine = __esm({
2319
2113
  __publicField(this, "scrollContainer");
2320
2114
  __publicField(this, "backdrop");
2321
2115
  __publicField(this, "screenComponent");
2116
+ __publicField(this, "scrimParent", null);
2322
2117
  __publicField(this, "mode");
2323
2118
  __publicField(this, "flickVelocity");
2324
2119
  __publicField(this, "dragThreshold");
@@ -2329,40 +2124,22 @@ var init_BottomSheetEngine = __esm({
2329
2124
  __publicField(this, "lifecycle");
2330
2125
  __publicField(this, "closeOnBack");
2331
2126
  __publicField(this, "routedTo");
2332
- // Engine retains `snapPointsRaw` for the "find a closed-id" query in
2333
- // close() (raw input, not resolved geometry). Resolver owns geometry.
2334
2127
  __publicField(this, "snapPointsRaw");
2335
2128
  __publicField(this, "snaps");
2336
2129
  __publicField(this, "activeId");
2337
2130
  __publicField(this, "size", 0);
2338
- // Constructed in attach(); a plugin's install() could read `state` before
2339
- // attach completes if the constructor throws partway through. State getter
2340
- // null-guards via `this.gesture?.isDragging ?? false`.
2341
2131
  __publicField(this, "gesture");
2342
2132
  __publicField(this, "rootEl", null);
2343
2133
  __publicField(this, "destroyed", false);
2344
- // In-flight ViewTransition handle. A second snapTo while a VT is in flight
2345
- // would otherwise let Chromium reject the second's promise and flicker —
2346
- // newCycle() calls skipTransition() to abort cleanly.
2347
2134
  __publicField(this, "currentViewTransition", null);
2348
- // Each cycle captures the signal at entry; when a newer cycle aborts the
2349
- // controller, the captured signal flips to `aborted=true` and post-await
2350
- // guards short-circuit (prevents stale snap/open/close emits).
2351
2135
  __publicField(this, "currentAbort", new AbortController());
2352
- // Sub-pixel `--bs-size` deltas and <1% `--bs-progress` deltas are not
2353
- // visually distinguishable; gating CSSOM writes drops 30-50% of writes
2354
- // during the spring's settle tail.
2355
2136
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2356
2137
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2357
2138
  __publicField(this, "isTopSheet", true);
2358
- // Pooled payload — identity documented in SheetEventMap.progress JSDoc;
2359
- // consumers must not retain across frames.
2360
2139
  __publicField(this, "progressPayload", {
2361
2140
  value: 0,
2362
2141
  size: 0
2363
2142
  });
2364
- // Pooled buffer for `getDragContext()` — onMove calls this 60-120×/s.
2365
- // Inner `range` object identity is also stable (mutated in place).
2366
2143
  __publicField(this, "dragContextBuf", {
2367
2144
  size: 0,
2368
2145
  maxAxisSize: 0,
@@ -2373,13 +2150,7 @@ var init_BottomSheetEngine = __esm({
2373
2150
  __publicField(this, "persistKey");
2374
2151
  __publicField(this, "linkedSheets", []);
2375
2152
  __publicField(this, "scrollCache");
2376
- // Single LIFO stack for ALL destroy-only teardowns. Plugins (added last)
2377
- // tear down first before the infrastructure they depend on. Toggle-semantic
2378
- // listeners (focus-trap / scroll-lock / scrim tap-to-close) stay as named
2379
- // fields on their controllers.
2380
2153
  __publicField(this, "teardowns", new TeardownStack());
2381
- // Pre-computed transform template — built once per engine since `mode` is
2382
- // fixed for its lifetime. Avoids the 4-arm switch at 60-120 Hz.
2383
2154
  __publicField(this, "transformTemplate");
2384
2155
  const resolved = resolveEngineOptions(opts, {
2385
2156
  shouldApplyInertSiblings: () => {
@@ -2394,7 +2165,8 @@ var init_BottomSheetEngine = __esm({
2394
2165
  this.handle = opts.handle ?? opts.element;
2395
2166
  this.scrollContainer = opts.scrollContainer;
2396
2167
  this.backdrop = opts.backdrop;
2397
- this.screenComponent = opts.screenComponent;
2168
+ this.screenComponent = opts.scrim ?? opts.screenComponent;
2169
+ this.scrimParent = this.screenComponent?.parentElement ?? null;
2398
2170
  this.mode = resolved.mode;
2399
2171
  this.transformTemplate = buildTransformTemplate(this.mode);
2400
2172
  this.aria = new AriaSliderWriter(this.handle, this.mode);
@@ -2422,8 +2194,6 @@ var init_BottomSheetEngine = __esm({
2422
2194
  getRootEl: () => this.rootEl,
2423
2195
  applySize: (size) => this.applySize(size),
2424
2196
  getSize: () => this.size,
2425
- // Optional chain protects synthetic test paths that drive animateTo
2426
- // before attach() (gesture is constructed there).
2427
2197
  isDragging: () => this.gesture?.isDragging ?? false
2428
2198
  },
2429
2199
  resolved.animation
@@ -2528,21 +2298,6 @@ var init_BottomSheetEngine = __esm({
2528
2298
  on(event, fn) {
2529
2299
  return this.bus.on(event, fn);
2530
2300
  }
2531
- /**
2532
- * Register a plugin. `install` runs synchronously; if it returns a function,
2533
- * it's captured for teardown during `destroy()`. No-op if already destroyed
2534
- * — installing into a torn-down engine would leak listeners.
2535
- *
2536
- * Throws from `plugin.install` are caught and rethrown via `queueMicrotask`
2537
- * so a buggy plugin can't take the engine down with it: the engine stays
2538
- * usable, sibling plugins still install, and the error surfaces in dev tools
2539
- * on the next tick instead of being swallowed. Mirrors the per-fn isolation
2540
- * model used for teardowns.
2541
- *
2542
- * Plugins that accept the second `scope` argument get transactional cleanup:
2543
- * push partial-install cleanups via `scope.add(fn)` and the engine drains
2544
- * them on throw, OR merges them into destroy-time teardowns on success.
2545
- */
2546
2301
  use(plugin) {
2547
2302
  if (this.destroyed) {
2548
2303
  console.warn(
@@ -2552,9 +2307,6 @@ var init_BottomSheetEngine = __esm({
2552
2307
  }
2553
2308
  const scoped = [];
2554
2309
  const scope = {
2555
- // typeof check — not just truthiness — so a JS-only plugin pushing a
2556
- // bogus value is silently rejected instead of queueing a confusing
2557
- // TypeError on destroy() drain.
2558
2310
  add: (fn) => {
2559
2311
  if (typeof fn === "function") scoped.push(fn);
2560
2312
  }
@@ -2583,36 +2335,13 @@ var init_BottomSheetEngine = __esm({
2583
2335
  }
2584
2336
  return this;
2585
2337
  }
2586
- /**
2587
- * Replace the linked-sheets list at runtime. Use when peer engines aren't
2588
- * available at construction time (mounted later in the tree). The wiring
2589
- * to the `open` event is independent of this list — it reads `linkedSheets`
2590
- * lazily on emit, so this setter is a simple field swap.
2591
- */
2592
2338
  setLinkedSheets(sheets) {
2593
2339
  if (this.destroyed) return;
2594
2340
  this.linkedSheets = sheets;
2595
2341
  }
2596
- /** Read-only view of the current allow-list, for cross-sheet target negotiation. */
2597
2342
  getAllowedIds() {
2598
2343
  return this.snaps.getAllowedIds().slice();
2599
2344
  }
2600
- /**
2601
- * Animate to a snap point by id. Resolves when the animation completes.
2602
- *
2603
- * Two call shapes:
2604
- * ```ts
2605
- * engine.snapTo("full"); // default velocity
2606
- * engine.snapTo("full", 0.5); // px/ms initial velocity
2607
- * engine.snapTo("full", { velocity: 0.5, signal }); // abortable
2608
- * ```
2609
- *
2610
- * When `opts.signal` is provided, calling `controller.abort()` cancels the
2611
- * in-flight tween/spring and the returned promise resolves without firing
2612
- * `snap`. The captured `AbortSignal` is checked after each await so external
2613
- * aborts ride the same cycle invariant as internal ones (resize, destroy,
2614
- * concurrent snapTo).
2615
- */
2616
2345
  async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2617
2346
  if (this.destroyed) return;
2618
2347
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -2671,16 +2400,6 @@ var init_BottomSheetEngine = __esm({
2671
2400
  this.handleClose();
2672
2401
  }
2673
2402
  }
2674
- /**
2675
- * Drive the sheet to an arbitrary px size, bypassing snap matching.
2676
- * Useful for preview scrubs, animation chaining, or programmatic gestures.
2677
- * Animates with the configured spring/tween. Resolves when the animation
2678
- * completes. Does NOT update `activeId` — call `snapTo(id)` after to commit.
2679
- *
2680
- * Unlike `snapTo`, this does not fire `before-snap` (it isn't a snap commit)
2681
- * nor `snap` (the sheet isn't settling to a known snap). `progress` events
2682
- * still fire via applySize. Target is clamped to `[0, maxAxisSize]`.
2683
- */
2684
2403
  async dragTo(targetSize, velocityOrOpts = 0) {
2685
2404
  if (this.destroyed) return;
2686
2405
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -2718,20 +2437,10 @@ var init_BottomSheetEngine = __esm({
2718
2437
  this.snaps.setAllowedIds(ids);
2719
2438
  this.updateAriaSlider();
2720
2439
  if (snap && ids.includes(snap)) {
2721
- void this.snapTo(
2722
- snap,
2723
- 0,
2724
- /* internal */
2725
- true
2726
- );
2440
+ void this.snapTo(snap, 0, true);
2727
2441
  } else if (!ids.includes(this.activeId)) {
2728
2442
  const fallback = ids[0];
2729
- if (fallback) void this.snapTo(
2730
- fallback,
2731
- 0,
2732
- /* internal */
2733
- true
2734
- );
2443
+ if (fallback) void this.snapTo(fallback, 0, true);
2735
2444
  }
2736
2445
  }
2737
2446
  setBackdropRange(range) {
@@ -2749,21 +2458,6 @@ var init_BottomSheetEngine = __esm({
2749
2458
  setScrimInteractive(interactive) {
2750
2459
  this.scrim.setScrimInteractive(interactive);
2751
2460
  }
2752
- /**
2753
- * Batch-update multiple scrim fields in one call. Apply order is:
2754
- * 1. `preset` — fills baseline color/blur/range/interactive.
2755
- * 2. `mode` — re-positions the scrim (fixed inset / clear / hide).
2756
- * 3. `enabled` — flips the opacity contribution on/off.
2757
- * 4. `tapToClose` — installs/tears the click listener.
2758
- * 5. `color` / `blur` / `interactive` / `range` — explicit overrides.
2759
- * 6. ONE `applySize` so the per-frame opacity stays consistent.
2760
- *
2761
- * Performance: collecting changes here (vs calling individual setters)
2762
- * avoids repeated `applySize` calls during a multi-field swap. Setters
2763
- * called here suppress their own internal `applySize` either by passing
2764
- * through fast-paths (mode/enabled no-op when value unchanged) or by
2765
- * being visual-only (color/blur/interactive don't influence opacity).
2766
- */
2767
2461
  setScrim(opts) {
2768
2462
  this.scrim.setScrim(opts, () => this.applySize(this.size));
2769
2463
  }
@@ -2779,11 +2473,6 @@ var init_BottomSheetEngine = __esm({
2779
2473
  setScrimOverlay(opts) {
2780
2474
  return this.scrim.setScrimOverlay(opts);
2781
2475
  }
2782
- /**
2783
- * Read-only snapshot of scrim state. Use the `setScrim*` setters to mutate.
2784
- * Stable public API — internals (e.g. cached opacity sentinels) are NOT
2785
- * exposed so consumers can't accidentally couple to ScrimController shape.
2786
- */
2787
2476
  getScrimState() {
2788
2477
  return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
2789
2478
  }
@@ -2829,24 +2518,11 @@ var init_BottomSheetEngine = __esm({
2829
2518
  emit(event, payload) {
2830
2519
  this.bus.emit(event, payload);
2831
2520
  }
2832
- /**
2833
- * Abort the current cycle's signal and replace the controller. Returns the
2834
- * fresh signal so the caller can capture it locally before any await — when
2835
- * a future newCycle() / destroy aborts our controller, the captured signal
2836
- * flips to aborted and post-await guards short-circuit.
2837
- */
2838
2521
  newCycle() {
2839
2522
  this.currentAbort.abort();
2840
2523
  this.currentAbort = new AbortController();
2841
2524
  return this.currentAbort.signal;
2842
2525
  }
2843
- /**
2844
- * Fire `before-snap` and report whether a listener cancelled it. The
2845
- * `cancel()` callback exposed to listeners is frozen after the synchronous
2846
- * emit phase so async callers can't silently miss — they get a console
2847
- * warning. Returns `true` when cancelled so callers can early-return /
2848
- * spring-back without re-implementing the cancel/frozen pattern.
2849
- */
2850
2526
  emitBeforeSnap(target, previousId) {
2851
2527
  let cancelled = false;
2852
2528
  let frozen = false;
@@ -2880,30 +2556,9 @@ var init_BottomSheetEngine = __esm({
2880
2556
  this.snaps.setRaw(this.snapPointsRaw);
2881
2557
  this.scrim.invalidateOpacityCache();
2882
2558
  }
2883
- /**
2884
- * Memoised `allowedRange()` — delegates to SnapResolver which owns the cache.
2885
- * Engine wrapper kept so existing call sites don't all need updating;
2886
- * resolver-level memoization survives across method invocations.
2887
- */
2888
2559
  getAllowedRange() {
2889
2560
  return this.snaps.getAllowedRange();
2890
2561
  }
2891
- /**
2892
- * Write ARIA slider value attributes onto the handle so the engine itself
2893
- * is the source of truth for screen-reader announcements. Called from the
2894
- * constructor and from every code path that mutates `activeId` /
2895
- * `allowedIds`. WAI-ARIA 1.2 requires `aria-valuenow` for `role="slider"` —
2896
- * without it screen readers can't announce drag/snap progress to keyboard
2897
- * + AT users (WCAG 4.1.2 Name, Role, Value).
2898
- *
2899
- * Engine does NOT set `role="slider"` itself — that's the adapter's or
2900
- * consumer markup's responsibility. We only decorate with dynamic values.
2901
- *
2902
- * Empty allowed-list is malformed input (settable via `setAllowed([])`);
2903
- * short-circuit to remove ARIA value attributes so screen readers see a
2904
- * no-value slider rather than a degenerate `min=max=now=0` (WAI-ARIA 1.2
2905
- * requires `valuemax > valuemin` for `role="slider"`).
2906
- */
2907
2562
  updateAriaSlider() {
2908
2563
  this.aria.setValue(this.snaps.getAllowedIds(), this.activeId);
2909
2564
  }
@@ -2928,11 +2583,6 @@ var init_BottomSheetEngine = __esm({
2928
2583
  element: this.element,
2929
2584
  mode: this.mode,
2930
2585
  getRoot: () => this.rootEl,
2931
- // Single coalesced reader — narrows the DI surface and removes 3 getter
2932
- // dispatches per onMove frame at 60-120Hz. Returns a SHARED buffer
2933
- // (this.dragContextBuf) — see field JSDoc for the retention contract.
2934
- // Inner `range` object is mutated in place too so callers caching its
2935
- // identity between frames stay valid.
2936
2586
  getDragContext: () => {
2937
2587
  const buf = this.dragContextBuf;
2938
2588
  const r = this.getAllowedRange();
@@ -3098,7 +2748,6 @@ var init_BottomSheetEngine = __esm({
3098
2748
  }
3099
2749
  });
3100
2750
  }
3101
- /** Trigger a brief haptic tick (iOS Safari + Android Chrome support). */
3102
2751
  haptic() {
3103
2752
  if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3104
2753
  try {
@@ -3114,6 +2763,9 @@ var init_BottomSheetEngine = __esm({
3114
2763
  style.transform = this.transformTemplate(offset);
3115
2764
  if (this.sizeWriteSentinel.shouldWrite(size, SIZE_WRITE_EPSILON)) {
3116
2765
  style.setProperty("--bs-size", `${size}px`);
2766
+ if (this.scrimParent) {
2767
+ this.scrimParent.style.setProperty("--bs-size", `${size}px`);
2768
+ }
3117
2769
  }
3118
2770
  const progress = this.computeProgress(size);
3119
2771
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -3160,7 +2812,7 @@ var init_BottomSheetEngine = __esm({
3160
2812
  var baseStyles;
3161
2813
  var init_baseStyles = __esm({
3162
2814
  "src/web-component/baseStyles.ts"() {
3163
- baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--bs-shadow:0 -3px 12px rgba(0,0,0,0.12);--bs-radius:20px;--bs-handle-color:#b5b3a8;--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-header-min-height:80px;--bs-backdrop-color:rgba(0,0,0,0.5);--bs-scrim-color:rgba(0,0,0,0.4);--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color);opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface);box-shadow:var(--bs-shadow);z-index:var(--bs-z-sheet);overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius);border-top-right-radius:var(--bs-radius);border-top:1px solid var(--bs-surface-border);padding-bottom:env(safe-area-inset-bottom);box-shadow:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba(0,0,0,calc(var(--bs-progress,0) * 0.15 + 0.12))}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-bottom:1px solid var(--bs-surface-border);flex-direction:column-reverse;box-shadow:0 3px 12px rgba(0,0,0,0.12);padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-right:1px solid var(--bs-surface-border);box-shadow:3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius);border-bottom-left-radius:var(--bs-radius);border-left:1px solid var(--bs-surface-border);box-shadow:-3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:8px 16px 4px;cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface);-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius)}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color);border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface);z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:0 16px 16px}.bs-screen{position:absolute;inset:0;pointer-events:none;display:none;z-index:var(--bs-z-scrim)}.bs-scrim{background:var(--bs-scrim-color);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-scrim,.bs-root[data-animating="true"] .bs-scrim{transition:none}.bs-scrim{backdrop-filter:blur(var(--bs-scrim-blur));-webkit-backdrop-filter:blur(var(--bs-scrim-blur))}.bs-button-slot{position:absolute;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface:#1a1a1d;--bs-surface-border:rgba(255,255,255,0.08);--bs-handle-color:rgba(255,255,255,0.45);--bs-shadow:0 -3px 16px rgba(0,0,0,0.5)}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,#ffffff);--ovl-surface-border:var(--bs-surface-border,rgba(0,0,0,0.08));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,20px);--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
2815
+ baseStyles = `:where(.bs-root){--bs-surface:#ffffff;--bs-surface-border:rgba(225,227,237,1);--bs-shadow:0 -3px 12px rgba(0,0,0,0.12);--bs-radius:20px;--bs-handle-color:#b5b3a8;--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-header-min-height:80px;--bs-backdrop-color:rgba(0,0,0,0.5);--bs-scrim-color:rgba(0,0,0,0.4);--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color);opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface);box-shadow:var(--bs-shadow);z-index:var(--bs-z-sheet);overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius);border-top-right-radius:var(--bs-radius);border-top:1px solid var(--bs-surface-border);padding-bottom:env(safe-area-inset-bottom);box-shadow:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba(0,0,0,calc(var(--bs-progress,0) * 0.15 + 0.12))}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-bottom:1px solid var(--bs-surface-border);flex-direction:column-reverse;box-shadow:0 3px 12px rgba(0,0,0,0.12);padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius);border-bottom-right-radius:var(--bs-radius);border-right:1px solid var(--bs-surface-border);box-shadow:3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius);border-bottom-left-radius:var(--bs-radius);border-left:1px solid var(--bs-surface-border);box-shadow:-3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:8px 16px 4px;cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface);-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius)}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color);border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface);z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border)}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:0 16px 16px}.bs-screen{position:absolute;inset:0;pointer-events:none;z-index:var(--bs-z-scrim)}.bs-scrim-overlay{z-index:calc(var(--bs-z-sheet) + 1);pointer-events:none}.bs-scrim{background:var(--bs-scrim-color);transition:opacity 0.15s linear;will-change:opacity}.bs-root[data-dragging="true"] .bs-scrim,.bs-root[data-animating="true"] .bs-scrim{transition:none}.bs-scrim{backdrop-filter:blur(var(--bs-scrim-blur));-webkit-backdrop-filter:blur(var(--bs-scrim-blur))}.bs-button-slot{position:absolute;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px);transition:bottom 0.15s ease}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface:#1a1a1d;--bs-surface-border:rgba(255,255,255,0.08);--bs-handle-color:rgba(255,255,255,0.45);--bs-shadow:0 -3px 16px rgba(0,0,0,0.5)}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,#ffffff);--ovl-surface-border:var(--bs-surface-border,rgba(0,0,0,0.08));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,20px);--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
3164
2816
  }
3165
2817
  });
3166
2818
 
@@ -3343,6 +2995,9 @@ var init_BottomSheetElement = __esm({
3343
2995
  this.teardownEngine();
3344
2996
  this.initEngine();
3345
2997
  }
2998
+ getEngine() {
2999
+ return this.engine;
3000
+ }
3346
3001
  teardownEngine() {
3347
3002
  this.offHandlers.forEach((off) => off());
3348
3003
  this.offHandlers = [];
@@ -3374,7 +3029,7 @@ var init_BottomSheetElement = __esm({
3374
3029
  handle,
3375
3030
  scrollContainer: content,
3376
3031
  backdrop: showBackdrop ? backdrop : void 0,
3377
- screenComponent: this.refs.screen,
3032
+ scrim: this.refs.screen,
3378
3033
  mode,
3379
3034
  snapPoints: parseSnapPoints(this.getAttribute(ATTR_SNAP_POINTS)),
3380
3035
  allowed: parseList(this.getAttribute(ATTR_ALLOWED)),