@surdeddd/bottom-sheet 0.9.1 → 0.10.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 +108 -66
  2. package/dist/{BottomSheetEngine-CBRcBwV-.d.ts → BottomSheetEngine-DRjCU73e.d.ts} +6 -5
  3. package/dist/{BottomSheetEngine-BcyruNP1.d.cts → BottomSheetEngine-DzsIwD28.d.cts} +6 -5
  4. package/dist/element.cjs +161 -99
  5. package/dist/element.d.cts +1 -1
  6. package/dist/element.d.ts +1 -1
  7. package/dist/element.iife.global.js +2 -2
  8. package/dist/element.js +161 -99
  9. package/dist/index.cjs +155 -115
  10. package/dist/index.d.cts +2 -2
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +155 -115
  13. package/dist/integrations/formik.d.cts +1 -1
  14. package/dist/integrations/formik.d.ts +1 -1
  15. package/dist/integrations/react-hook-form.d.cts +1 -1
  16. package/dist/integrations/react-hook-form.d.ts +1 -1
  17. package/dist/overlay.cjs +34 -25
  18. package/dist/overlay.d.cts +3 -0
  19. package/dist/overlay.d.ts +3 -0
  20. package/dist/overlay.js +34 -25
  21. package/dist/preact.d.cts +2 -1
  22. package/dist/preact.d.ts +2 -1
  23. package/dist/qwik.cjs +179 -93
  24. package/dist/qwik.d.cts +11 -2
  25. package/dist/qwik.d.ts +11 -2
  26. package/dist/qwik.js +179 -93
  27. package/dist/react.cjs +266 -181
  28. package/dist/react.d.cts +26 -4
  29. package/dist/react.d.ts +26 -4
  30. package/dist/react.js +266 -181
  31. package/dist/solid.cjs +145 -92
  32. package/dist/solid.d.cts +14 -4
  33. package/dist/solid.d.ts +14 -4
  34. package/dist/solid.js +145 -92
  35. package/dist/solid.source.jsx +145 -92
  36. package/dist/styles.css +17 -1
  37. package/dist/svelte-core.cjs +128 -90
  38. package/dist/svelte-core.js +128 -90
  39. package/dist/svelte-src/BottomSheet.svelte +6 -0
  40. package/dist/svelte.cjs +130 -90
  41. package/dist/svelte.d.cts +2 -0
  42. package/dist/svelte.d.ts +2 -0
  43. package/dist/svelte.js +130 -90
  44. package/dist/teleport-7uYNiEAT.d.cts +3 -0
  45. package/dist/teleport-7uYNiEAT.d.ts +3 -0
  46. package/dist/vue.cjs +136 -91
  47. package/dist/vue.d.cts +3 -1
  48. package/dist/vue.d.ts +3 -1
  49. package/dist/vue.js +136 -91
  50. package/package.json +6 -6
package/dist/react.js CHANGED
@@ -191,11 +191,18 @@ var resolveSnap = (point, mode, measureFit) => {
191
191
  return Math.max(0, el.getBoundingClientRect().width);
192
192
  };
193
193
 
194
+ // src/core/primitives/devWarn.ts
195
+ function devWarn(...args) {
196
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
197
+ console.warn(...args);
198
+ }
199
+ }
200
+
194
201
  // src/core/primitives/snap-points.ts
195
202
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
196
203
  const raw = resolveSnap(p.size, mode, measureFit);
197
204
  if (!Number.isFinite(raw) || raw < 0) {
198
- console.warn(
205
+ devWarn(
199
206
  `[BottomSheet] snap "${p.id}" resolved to invalid size (${String(p.size)} \u2192 ${raw}); clamped to 0.`
200
207
  );
201
208
  return { id: p.id, size: 0 };
@@ -207,7 +214,7 @@ var auditVhUsage = (points) => {
207
214
  for (const p of points) {
208
215
  if (typeof p.size !== "string") continue;
209
216
  if (!VH_NOT_DVH.test(p.size)) continue;
210
- console.warn(
217
+ devWarn(
211
218
  `[BottomSheet] snap "${p.id}" uses "vh" \u2014 prefer "dvh" on mobile (iOS Safari URL bar makes vh unstable). See https://web.dev/blog/viewport-units`
212
219
  );
213
220
  }
@@ -261,6 +268,26 @@ var findDragSettleTarget = (input) => {
261
268
  );
262
269
  };
263
270
 
271
+ // src/core/primitives/cancelable-emit.ts
272
+ function emitCancelable(emit, base, warnLabel) {
273
+ let cancelled = false;
274
+ let frozen = false;
275
+ emit({
276
+ ...base,
277
+ cancel: () => {
278
+ if (frozen) {
279
+ devWarn(
280
+ `[BottomSheet] ${warnLabel}.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener.`
281
+ );
282
+ return;
283
+ }
284
+ cancelled = true;
285
+ }
286
+ });
287
+ frozen = true;
288
+ return cancelled;
289
+ }
290
+
264
291
  // src/core/primitives/snap-resolver.ts
265
292
  var SnapResolver = class {
266
293
  constructor(raw, allowed, mode, measureFit, onMaxAxisSizeChange) {
@@ -405,7 +432,9 @@ function notifyLinkedSheets(sheets, self) {
405
432
  try {
406
433
  if (linked.state.size === 0) continue;
407
434
  const ids = linked.getAllowedIds();
408
- const target = ids.find((id) => id !== "closed");
435
+ const resolved = linked.getResolvedSnaps();
436
+ const sizeOf = (id) => resolved.find((s) => s.id === id)?.size ?? 0;
437
+ const target = ids.find((id) => sizeOf(id) > 0);
409
438
  if (target && target !== linked.state.activeId) {
410
439
  void linked.snapTo(target);
411
440
  }
@@ -622,11 +651,15 @@ function installVisualViewport(deps) {
622
651
  if (clamp < deps.getMaxAxisSize()) {
623
652
  deps.setMaxAxisSize(clamp);
624
653
  }
654
+ const wasAnimating = deps.isAnimating?.() ?? false;
655
+ deps.cancelInFlight();
656
+ deps.newCycle();
625
657
  const max = deps.getMaxAxisSize();
626
658
  deps.setSize(Math.min(deps.getSize(), max));
627
659
  const current = deps.resolveActiveSnap();
628
660
  if (current && !deps.isDragging()) {
629
661
  deps.applySize(Math.min(current.size, max));
662
+ if (wasAnimating) deps.resyncAfterCancel?.();
630
663
  } else {
631
664
  deps.applySize(deps.getSize());
632
665
  }
@@ -767,6 +800,54 @@ function installFitObserver(deps) {
767
800
  };
768
801
  }
769
802
 
803
+ // src/core/features/max-height-controller.ts
804
+ function createMaxHeightController(deps) {
805
+ let maxHeightCap;
806
+ let maxHeightRaw;
807
+ const resolveCap = () => {
808
+ const raw = maxHeightRaw;
809
+ if (raw === void 0) {
810
+ maxHeightCap = void 0;
811
+ return;
812
+ }
813
+ if (typeof raw === "number") {
814
+ maxHeightCap = raw;
815
+ return;
816
+ }
817
+ if (typeof window === "undefined") return;
818
+ const measured = resolveSnap(raw, deps.mode);
819
+ maxHeightCap = measured > 0 ? measured : void 0;
820
+ };
821
+ const clampTo = () => {
822
+ if (maxHeightCap === void 0) return;
823
+ if (deps.getMaxAxisSize() > maxHeightCap) {
824
+ deps.setMaxAxisSize(maxHeightCap);
825
+ }
826
+ };
827
+ return {
828
+ setRadius(r) {
829
+ deps.element.style.setProperty(
830
+ "--bs-radius",
831
+ typeof r === "number" ? `${r}px` : r
832
+ );
833
+ },
834
+ setMaxHeight(h) {
835
+ const value = typeof h === "number" ? `${h}px` : h;
836
+ const axis = layoutAxis(deps.mode);
837
+ deps.element.style.setProperty(
838
+ axis === "height" ? "max-height" : "max-width",
839
+ value
840
+ );
841
+ maxHeightRaw = h;
842
+ resolveCap();
843
+ deps.recompute();
844
+ },
845
+ resolveCap,
846
+ clampTo,
847
+ getCap: () => maxHeightCap
848
+ };
849
+ }
850
+
770
851
  // src/core/features/fit-measurement.ts
771
852
  var measureSheetNatural = (element, content, vertical) => {
772
853
  if (!content) {
@@ -898,13 +979,6 @@ var WriteSentinel = class {
898
979
  }
899
980
  };
900
981
 
901
- // src/core/primitives/devWarn.ts
902
- function devWarn(...args) {
903
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
904
- console.warn(...args);
905
- }
906
- }
907
-
908
982
  // src/core/primitives/overlay-position.ts
909
983
  function resolveSheetAnchoredStyle(mode, position, inset) {
910
984
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
@@ -2550,8 +2624,7 @@ var BottomSheetEngine = class {
2550
2624
  __publicField(this, "disableCloseFlag");
2551
2625
  __publicField(this, "disableDragFlag");
2552
2626
  __publicField(this, "closeOnRouteChange");
2553
- __publicField(this, "maxHeightCap");
2554
- __publicField(this, "maxHeightRaw");
2627
+ __publicField(this, "maxHeight");
2555
2628
  __publicField(this, "snapPointsRaw");
2556
2629
  __publicField(this, "snaps");
2557
2630
  __publicField(this, "activeId");
@@ -2662,11 +2735,20 @@ var BottomSheetEngine = class {
2662
2735
  auditVhUsage(this.snapPointsRaw);
2663
2736
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2664
2737
  this.rootEl = this.element.closest(".bs-root");
2738
+ this.maxHeight = createMaxHeightController({
2739
+ element: this.element,
2740
+ mode: this.mode,
2741
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2742
+ setMaxAxisSize: (size) => {
2743
+ this.snaps.setMaxAxisSize(size);
2744
+ },
2745
+ recompute: () => this.recompute()
2746
+ });
2665
2747
  this.fitDeps = {
2666
2748
  element: this.element,
2667
2749
  scrollContainer: this.scrollContainer,
2668
2750
  mode: this.mode,
2669
- getMaxHeightCap: () => this.maxHeightCap
2751
+ getMaxHeightCap: () => this.maxHeight.getCap()
2670
2752
  };
2671
2753
  this.snaps = new SnapResolver(
2672
2754
  this.snapPointsRaw,
@@ -2777,7 +2859,7 @@ var BottomSheetEngine = class {
2777
2859
  }
2778
2860
  use(plugin) {
2779
2861
  if (this.destroyed) {
2780
- console.warn(
2862
+ devWarn(
2781
2863
  `[BottomSheet] use("${plugin.name}") called on destroyed engine \u2014 ignored.`
2782
2864
  );
2783
2865
  return this;
@@ -2819,6 +2901,9 @@ var BottomSheetEngine = class {
2819
2901
  getAllowedIds() {
2820
2902
  return this.snaps.getAllowedIds().slice();
2821
2903
  }
2904
+ getResolvedSnaps() {
2905
+ return this.snaps.getResolvedSnaps();
2906
+ }
2822
2907
  async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2823
2908
  if (this.destroyed) return;
2824
2909
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -2827,11 +2912,11 @@ var BottomSheetEngine = class {
2827
2912
  if (externalSignal?.aborted) return;
2828
2913
  const target = this.snaps.findById(id);
2829
2914
  if (!target) {
2830
- console.warn(`[BottomSheet] unknown snap id: ${id}`);
2915
+ devWarn(`[BottomSheet] unknown snap id: ${id}`);
2831
2916
  return;
2832
2917
  }
2833
2918
  if (!this.snaps.getAllowedIds().includes(id)) {
2834
- console.warn(`[BottomSheet] snap "${id}" is not in allowed list`);
2919
+ devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2835
2920
  return;
2836
2921
  }
2837
2922
  if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
@@ -2963,42 +3048,11 @@ var BottomSheetEngine = class {
2963
3048
  }
2964
3049
  setRadius(r) {
2965
3050
  if (this.destroyed) return;
2966
- this.element.style.setProperty(
2967
- "--bs-radius",
2968
- typeof r === "number" ? `${r}px` : r
2969
- );
3051
+ this.maxHeight.setRadius(r);
2970
3052
  }
2971
3053
  setMaxHeight(h) {
2972
3054
  if (this.destroyed) return;
2973
- const value = typeof h === "number" ? `${h}px` : h;
2974
- const axis = layoutAxis(this.mode);
2975
- this.element.style.setProperty(
2976
- axis === "height" ? "max-height" : "max-width",
2977
- value
2978
- );
2979
- this.maxHeightRaw = h;
2980
- this.resolveMaxHeightCap();
2981
- this.recompute();
2982
- }
2983
- resolveMaxHeightCap() {
2984
- const raw = this.maxHeightRaw;
2985
- if (raw === void 0) {
2986
- this.maxHeightCap = void 0;
2987
- return;
2988
- }
2989
- if (typeof raw === "number") {
2990
- this.maxHeightCap = raw;
2991
- return;
2992
- }
2993
- if (typeof window === "undefined") return;
2994
- const measured = resolveSnap(raw, this.mode);
2995
- this.maxHeightCap = measured > 0 ? measured : void 0;
2996
- }
2997
- clampToMaxHeight() {
2998
- if (this.maxHeightCap === void 0) return;
2999
- if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
3000
- this.snaps.setMaxAxisSize(this.maxHeightCap);
3001
- }
3055
+ this.maxHeight.setMaxHeight(h);
3002
3056
  }
3003
3057
  setAllowed(ids, snap) {
3004
3058
  if (this.destroyed) return;
@@ -3086,7 +3140,7 @@ var BottomSheetEngine = class {
3086
3140
  };
3087
3141
  const host = this.screenComponent?.parentElement;
3088
3142
  if (!host) {
3089
- console.warn(
3143
+ devWarn(
3090
3144
  "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
3091
3145
  );
3092
3146
  return () => {
@@ -3136,9 +3190,9 @@ var BottomSheetEngine = class {
3136
3190
  }
3137
3191
  recompute() {
3138
3192
  if (this.destroyed) return;
3139
- this.resolveMaxHeightCap();
3193
+ this.maxHeight.resolveCap();
3140
3194
  this.snaps.recompute();
3141
- this.clampToMaxHeight();
3195
+ this.maxHeight.clampTo();
3142
3196
  this.scrim.invalidateOpacityCache();
3143
3197
  if (this.gesture?.isDragging) return;
3144
3198
  const current = this.snaps.findById(this.activeId);
@@ -3188,42 +3242,18 @@ var BottomSheetEngine = class {
3188
3242
  return this.currentAbort.signal;
3189
3243
  }
3190
3244
  emitBeforeSnap(target, previousId) {
3191
- let cancelled = false;
3192
- let frozen = false;
3193
- this.emit("before-snap", {
3194
- id: target.id,
3195
- size: target.size,
3196
- previousId,
3197
- cancel: () => {
3198
- if (frozen) {
3199
- console.warn(
3200
- "[BottomSheet] before-snap.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3201
- );
3202
- return;
3203
- }
3204
- cancelled = true;
3205
- }
3206
- });
3207
- frozen = true;
3208
- return cancelled;
3245
+ return emitCancelable(
3246
+ (payload) => this.emit("before-snap", payload),
3247
+ { id: target.id, size: target.size, previousId },
3248
+ "before-snap"
3249
+ );
3209
3250
  }
3210
3251
  emitBeforeClose(reason) {
3211
- let cancelled = false;
3212
- let frozen = false;
3213
- this.emit("before-close", {
3214
- reason,
3215
- cancel: () => {
3216
- if (frozen) {
3217
- console.warn(
3218
- "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3219
- );
3220
- return;
3221
- }
3222
- cancelled = true;
3223
- }
3224
- });
3225
- frozen = true;
3226
- return cancelled;
3252
+ return emitCancelable(
3253
+ (payload) => this.emit("before-close", payload),
3254
+ { reason },
3255
+ "before-close"
3256
+ );
3227
3257
  }
3228
3258
  applyAutoAriaLabelledBy() {
3229
3259
  if (typeof document === "undefined") return;
@@ -3238,16 +3268,16 @@ var BottomSheetEngine = class {
3238
3268
  this.element.setAttribute("aria-labelledby", titled.id);
3239
3269
  }
3240
3270
  recomputeSnaps() {
3241
- this.resolveMaxHeightCap();
3271
+ this.maxHeight.resolveCap();
3242
3272
  this.snaps.setRaw(this.snapPointsRaw);
3243
- this.clampToMaxHeight();
3273
+ this.maxHeight.clampTo();
3244
3274
  this.scrim.invalidateOpacityCache();
3245
3275
  }
3246
3276
  auditDuplicateSnapIds(points) {
3247
3277
  const seen = /* @__PURE__ */ new Set();
3248
3278
  for (const p of points) {
3249
3279
  if (seen.has(p.id)) {
3250
- console.warn(
3280
+ devWarn(
3251
3281
  `[BottomSheet] duplicate snap id "${p.id}" \u2014 only the first occurrence is reachable.`
3252
3282
  );
3253
3283
  }
@@ -3414,7 +3444,15 @@ var BottomSheetEngine = class {
3414
3444
  setSize: (size) => {
3415
3445
  this.size = size;
3416
3446
  },
3417
- applySize: (size) => this.applySize(size)
3447
+ applySize: (size) => this.applySize(size),
3448
+ cancelInFlight: () => {
3449
+ this.animation.cancel();
3450
+ },
3451
+ newCycle: () => {
3452
+ this.newCycle();
3453
+ },
3454
+ isAnimating: () => this.animation.isAnimating,
3455
+ resyncAfterCancel: () => this.resyncAfterResize()
3418
3456
  }));
3419
3457
  }
3420
3458
  this.teardowns.add(installSliderKeyboard({
@@ -3575,6 +3613,72 @@ var BottomSheetEngine = class {
3575
3613
  sheetStack.update();
3576
3614
  }
3577
3615
  };
3616
+ function useEnginePropSync(engineRef, opts) {
3617
+ const optsRef = useRef(opts);
3618
+ optsRef.current = opts;
3619
+ const allowedKey = (opts.allowed ?? opts.snapPoints.map((p) => p.id)).join(
3620
+ "\0"
3621
+ );
3622
+ const defKey = opts.snapPoints.map((p) => `${p.id}${String(p.size)}`).join("\0");
3623
+ const defApplied = useRef(false);
3624
+ useEffect(() => {
3625
+ if (!defApplied.current) {
3626
+ defApplied.current = true;
3627
+ return;
3628
+ }
3629
+ const engine = engineRef.current;
3630
+ if (!engine) return;
3631
+ const current = optsRef.current;
3632
+ engine.setSnapPoints(
3633
+ current.snapPoints,
3634
+ current.allowed ? Array.from(current.allowed) : void 0
3635
+ );
3636
+ }, [defKey]);
3637
+ useEffect(() => {
3638
+ if (!engineRef.current) return;
3639
+ const ids = opts.allowed ? Array.from(opts.allowed) : opts.snapPoints.map((p) => p.id);
3640
+ engineRef.current.setAllowed(ids);
3641
+ }, [allowedKey]);
3642
+ useEffect(() => {
3643
+ if (!engineRef.current) return;
3644
+ if (opts.backdropColor !== void 0) {
3645
+ engineRef.current.setScrimColor(opts.backdropColor);
3646
+ }
3647
+ }, [opts.backdropColor]);
3648
+ useEffect(() => {
3649
+ if (!engineRef.current) return;
3650
+ if (opts.backdropOpacity !== void 0) {
3651
+ engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3652
+ }
3653
+ }, [opts.backdropOpacity]);
3654
+ useEffect(() => {
3655
+ if (!engineRef.current) return;
3656
+ if (opts.radius !== void 0) {
3657
+ engineRef.current.setRadius(opts.radius);
3658
+ }
3659
+ }, [opts.radius]);
3660
+ useEffect(() => {
3661
+ if (!engineRef.current) return;
3662
+ if (opts.maxHeight !== void 0) {
3663
+ engineRef.current.setMaxHeight(opts.maxHeight);
3664
+ }
3665
+ }, [opts.maxHeight]);
3666
+ useEffect(() => {
3667
+ if (opts.persistent !== void 0) {
3668
+ engineRef.current?.setPersistent(opts.persistent);
3669
+ }
3670
+ }, [opts.persistent]);
3671
+ useEffect(() => {
3672
+ if (opts.disableClose !== void 0) {
3673
+ engineRef.current?.setDisableClose(opts.disableClose);
3674
+ }
3675
+ }, [opts.disableClose]);
3676
+ useEffect(() => {
3677
+ if (opts.disableDrag !== void 0) {
3678
+ engineRef.current?.setDisableDrag(opts.disableDrag);
3679
+ }
3680
+ }, [opts.disableDrag]);
3681
+ }
3578
3682
 
3579
3683
  // src/core/features/teleport.ts
3580
3684
  var resolveTeleportTarget = (target) => {
@@ -3669,6 +3773,12 @@ function useBottomSheet(opts) {
3669
3773
  engine.on("before-close", (payload) => {
3670
3774
  optsRef.current.onBeforeClose?.(payload);
3671
3775
  }),
3776
+ engine.on("open", (payload) => {
3777
+ optsRef.current.onOpen?.(payload.id);
3778
+ }),
3779
+ engine.on("close", () => {
3780
+ optsRef.current.onClose?.();
3781
+ }),
3672
3782
  engine.on("opened", (payload) => {
3673
3783
  optsRef.current.onOpened?.(payload.id);
3674
3784
  }),
@@ -3684,6 +3794,20 @@ function useBottomSheet(opts) {
3684
3794
  optsRef.current.onDragEnd?.(payload);
3685
3795
  })
3686
3796
  ];
3797
+ if (optsRef.current.onDrag) {
3798
+ offs.push(
3799
+ engine.on("drag", (payload) => {
3800
+ optsRef.current.onDrag?.(payload);
3801
+ })
3802
+ );
3803
+ }
3804
+ if (optsRef.current.onProgress) {
3805
+ offs.push(
3806
+ engine.on("progress", (payload) => {
3807
+ optsRef.current.onProgress?.(payload);
3808
+ })
3809
+ );
3810
+ }
3687
3811
  return () => {
3688
3812
  offs.forEach((off) => off());
3689
3813
  restoreTeleport?.();
@@ -3693,68 +3817,17 @@ function useBottomSheet(opts) {
3693
3817
  setMounted((n) => n + 1);
3694
3818
  };
3695
3819
  }, []);
3696
- const allowedKey = (opts.allowed ?? opts.snapPoints.map((p) => p.id)).join(
3697
- "\0"
3698
- );
3699
- const defKey = opts.snapPoints.map((p) => `${p.id}${String(p.size)}`).join("\0");
3700
- const defApplied = useRef(false);
3701
- useEffect(() => {
3702
- if (!defApplied.current) {
3703
- defApplied.current = true;
3704
- return;
3705
- }
3706
- const engine = engineRef.current;
3707
- if (!engine) return;
3708
- const current = optsRef.current;
3709
- engine.setSnapPoints(
3710
- current.snapPoints,
3711
- current.allowed ? Array.from(current.allowed) : void 0
3712
- );
3713
- }, [defKey]);
3714
- useEffect(() => {
3715
- if (!engineRef.current) return;
3716
- const ids = opts.allowed ? Array.from(opts.allowed) : opts.snapPoints.map((p) => p.id);
3717
- engineRef.current.setAllowed(ids);
3718
- }, [allowedKey]);
3719
- useEffect(() => {
3720
- if (!engineRef.current) return;
3721
- if (opts.backdropColor !== void 0) {
3722
- engineRef.current.setScrimColor(opts.backdropColor);
3723
- }
3724
- }, [opts.backdropColor]);
3725
- useEffect(() => {
3726
- if (!engineRef.current) return;
3727
- if (opts.backdropOpacity !== void 0) {
3728
- engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3729
- }
3730
- }, [opts.backdropOpacity]);
3731
- useEffect(() => {
3732
- if (!engineRef.current) return;
3733
- if (opts.radius !== void 0) {
3734
- engineRef.current.setRadius(opts.radius);
3735
- }
3736
- }, [opts.radius]);
3737
- useEffect(() => {
3738
- if (!engineRef.current) return;
3739
- if (opts.maxHeight !== void 0) {
3740
- engineRef.current.setMaxHeight(opts.maxHeight);
3741
- }
3742
- }, [opts.maxHeight]);
3743
- useEffect(() => {
3744
- if (opts.persistent !== void 0) {
3745
- engineRef.current?.setPersistent(opts.persistent);
3746
- }
3747
- }, [opts.persistent]);
3748
- useEffect(() => {
3749
- if (opts.disableClose !== void 0) {
3750
- engineRef.current?.setDisableClose(opts.disableClose);
3751
- }
3752
- }, [opts.disableClose]);
3753
- useEffect(() => {
3754
- if (opts.disableDrag !== void 0) {
3755
- engineRef.current?.setDisableDrag(opts.disableDrag);
3756
- }
3757
- }, [opts.disableDrag]);
3820
+ useEnginePropSync(engineRef, {
3821
+ snapPoints: opts.snapPoints,
3822
+ allowed: opts.allowed,
3823
+ backdropColor: opts.backdropColor,
3824
+ backdropOpacity: opts.backdropOpacity,
3825
+ radius: opts.radius,
3826
+ maxHeight: opts.maxHeight,
3827
+ persistent: opts.persistent,
3828
+ disableClose: opts.disableClose,
3829
+ disableDrag: opts.disableDrag
3830
+ });
3758
3831
  const subscribe = useCallback((fn) => {
3759
3832
  subscribersRef.current.add(fn);
3760
3833
  return () => subscribersRef.current.delete(fn);
@@ -3863,10 +3936,14 @@ var BottomSheet = forwardRef(
3863
3936
  scrimStages,
3864
3937
  onChange,
3865
3938
  onBeforeClose,
3939
+ onOpen,
3940
+ onClose,
3866
3941
  onOpened,
3867
3942
  onClosed,
3868
3943
  onDragStart,
3869
3944
  onDragEnd,
3945
+ onDrag,
3946
+ onProgress,
3870
3947
  ariaLabel = "Bottom sheet",
3871
3948
  ariaLabelledBy,
3872
3949
  noSSR = false,
@@ -3892,10 +3969,14 @@ var BottomSheet = forwardRef(
3892
3969
  } = useBottomSheet({
3893
3970
  ...engineOpts,
3894
3971
  onBeforeClose,
3972
+ onOpen,
3973
+ onClose,
3895
3974
  onOpened,
3896
3975
  onClosed,
3897
3976
  onDragStart,
3898
- onDragEnd
3977
+ onDragEnd,
3978
+ onDrag,
3979
+ onProgress
3899
3980
  });
3900
3981
  const ariaModal = engineOpts.focusTrap === true;
3901
3982
  const lastAnnouncedRef = useRef("");
@@ -4179,7 +4260,7 @@ function installOverlaySwipe(deps) {
4179
4260
  };
4180
4261
  }
4181
4262
 
4182
- // src/core/overlay.ts
4263
+ // src/core/overlay-options.ts
4183
4264
  var OVERLAY_PRESETS = Object.freeze({
4184
4265
  sheet: Object.freeze({
4185
4266
  enterAnimation: "slide",
@@ -4210,6 +4291,8 @@ var OVERLAY_PRESETS = Object.freeze({
4210
4291
  swipeToClose: false
4211
4292
  })
4212
4293
  });
4294
+
4295
+ // src/core/overlay.ts
4213
4296
  var OverlayEngine = class {
4214
4297
  constructor(opts) {
4215
4298
  __publicField(this, "id", nextInstanceId("ovl"));
@@ -4286,7 +4369,7 @@ var OverlayEngine = class {
4286
4369
  this.onOpenCb = opts.onOpen;
4287
4370
  this.onCloseCb = opts.onClose;
4288
4371
  if (this.peekRaw !== void 0 && this.edge !== "bottom") {
4289
- console.warn(
4372
+ devWarn(
4290
4373
  "[OverlayEngine] `peek` is only supported on edge: 'bottom' \u2014 ignoring."
4291
4374
  );
4292
4375
  this.peekRaw = void 0;
@@ -4353,10 +4436,9 @@ var OverlayEngine = class {
4353
4436
  this.element.style.opacity = "1";
4354
4437
  if (this.backdrop) {
4355
4438
  this.backdrop.style.opacity = String(this.backdropOpacity);
4356
- this.backdrop.style.pointerEvents = this.backdropOpacity > 0 ? "auto" : "none";
4439
+ this.syncBackdropPointerEvents();
4357
4440
  if (this.backdropFilter) {
4358
- this.backdrop.style.backdropFilter = this.backdropFilter;
4359
- this.backdrop.style.webkitBackdropFilter = this.backdropFilter;
4441
+ this.applyBackdropFilter(this.backdropFilter);
4360
4442
  }
4361
4443
  }
4362
4444
  try {
@@ -4376,8 +4458,7 @@ var OverlayEngine = class {
4376
4458
  this.backdrop.style.opacity = "0";
4377
4459
  this.backdrop.style.pointerEvents = "none";
4378
4460
  if (this.backdropFilter) {
4379
- this.backdrop.style.backdropFilter = "";
4380
- this.backdrop.style.webkitBackdropFilter = "";
4461
+ this.applyBackdropFilter(void 0);
4381
4462
  }
4382
4463
  }
4383
4464
  this.element.setAttribute("data-state", "closed");
@@ -4435,8 +4516,7 @@ var OverlayEngine = class {
4435
4516
  this.backdrop.style.opacity = "0";
4436
4517
  this.backdrop.style.pointerEvents = "none";
4437
4518
  if (this.backdropFilter) {
4438
- this.backdrop.style.backdropFilter = "";
4439
- this.backdrop.style.webkitBackdropFilter = "";
4519
+ this.applyBackdropFilter(void 0);
4440
4520
  }
4441
4521
  }
4442
4522
  this.releaseInteractiveListeners();
@@ -4484,20 +4564,14 @@ var OverlayEngine = class {
4484
4564
  this.backdropOpacity = opacity;
4485
4565
  if (this.isOpen_ && this.backdrop) {
4486
4566
  this.backdrop.style.opacity = String(opacity);
4567
+ this.syncBackdropPointerEvents();
4487
4568
  }
4488
4569
  }
4489
4570
  setBackdropFilter(filter) {
4490
4571
  if (this.destroyed) return;
4491
4572
  this.backdropFilter = filter;
4492
4573
  if (!this.isOpen_ || !this.backdrop) return;
4493
- const style = this.backdrop.style;
4494
- if (filter) {
4495
- this.backdrop.style.backdropFilter = filter;
4496
- style.webkitBackdropFilter = filter;
4497
- } else {
4498
- this.backdrop.style.backdropFilter = "";
4499
- style.webkitBackdropFilter = "";
4500
- }
4574
+ this.applyBackdropFilter(filter);
4501
4575
  }
4502
4576
  setSwipeToClose(config) {
4503
4577
  if (this.destroyed) return;
@@ -4566,16 +4640,10 @@ var OverlayEngine = class {
4566
4640
  if (this.isOpen_ && this.backdrop) {
4567
4641
  if (merged.backdropOpacity !== void 0) {
4568
4642
  this.backdrop.style.opacity = String(this.backdropOpacity);
4643
+ this.syncBackdropPointerEvents();
4569
4644
  }
4570
4645
  if (merged.backdropFilter !== void 0) {
4571
- const style = this.backdrop.style;
4572
- if (this.backdropFilter) {
4573
- this.backdrop.style.backdropFilter = this.backdropFilter;
4574
- style.webkitBackdropFilter = this.backdropFilter;
4575
- } else {
4576
- this.backdrop.style.backdropFilter = "";
4577
- style.webkitBackdropFilter = "";
4578
- }
4646
+ this.applyBackdropFilter(this.backdropFilter);
4579
4647
  }
4580
4648
  }
4581
4649
  if (opts.duration !== void 0) this.duration = opts.duration;
@@ -4710,6 +4778,21 @@ var OverlayEngine = class {
4710
4778
  this.backdrop.style.pointerEvents = "none";
4711
4779
  }
4712
4780
  }
4781
+ syncBackdropPointerEvents() {
4782
+ if (!this.backdrop) return;
4783
+ this.backdrop.style.pointerEvents = this.backdropOpacity > 0 ? "auto" : "none";
4784
+ }
4785
+ applyBackdropFilter(filter) {
4786
+ if (!this.backdrop) return;
4787
+ const style = this.backdrop.style;
4788
+ if (filter) {
4789
+ this.backdrop.style.backdropFilter = filter;
4790
+ style.webkitBackdropFilter = filter;
4791
+ } else {
4792
+ this.backdrop.style.backdropFilter = "";
4793
+ style.webkitBackdropFilter = "";
4794
+ }
4795
+ }
4713
4796
  setWillChange(on) {
4714
4797
  this.element.style.willChange = on ? "transform, opacity" : "";
4715
4798
  if (this.backdrop) {
@@ -4942,6 +5025,8 @@ var Overlay = forwardRef(function Overlay2(props, ref) {
4942
5025
  )
4943
5026
  ] });
4944
5027
  });
5028
+
5029
+ // src/react/config-validation.ts
4945
5030
  var SUPPORTED_VERSION = 1;
4946
5031
  var TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
4947
5032
  "version",
@@ -4966,7 +5051,7 @@ var warnUnknownKeys = (obj, allowed, context) => {
4966
5051
  if (!obj || typeof obj !== "object") return;
4967
5052
  const unknown = Object.keys(obj).filter((k) => !allowed.has(k));
4968
5053
  if (unknown.length === 0) return;
4969
- console.warn(
5054
+ devWarn(
4970
5055
  `[BottomSheetFromConfig] Unknown ${unknown.length === 1 ? "key" : "keys"} in ${context}: ${unknown.map((k) => `"${k}"`).join(", ")}. Allowed: ${[...allowed].join(", ")}. Likely a typo \u2014 values ignored.`
4971
5056
  );
4972
5057
  };
@@ -4976,7 +5061,7 @@ function validateConfig(config) {
4976
5061
  validatedConfigs.add(config);
4977
5062
  let ok = true;
4978
5063
  if (config.version !== SUPPORTED_VERSION) {
4979
- console.warn(
5064
+ devWarn(
4980
5065
  `[BottomSheetFromConfig] Unsupported config version ${String(
4981
5066
  config.version
4982
5067
  )}; expected ${SUPPORTED_VERSION}. Falling back to defaults.`
@@ -4984,7 +5069,7 @@ function validateConfig(config) {
4984
5069
  return false;
4985
5070
  }
4986
5071
  if (!Array.isArray(config.snapPoints) || config.snapPoints.length === 0) {
4987
- console.warn(
5072
+ devWarn(
4988
5073
  "[BottomSheetFromConfig] config.snapPoints is missing or empty; using a single full-height fallback snap."
4989
5074
  );
4990
5075
  ok = false;