@surdeddd/bottom-sheet 0.9.0 → 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.cjs CHANGED
@@ -193,11 +193,18 @@ var resolveSnap = (point, mode, measureFit) => {
193
193
  return Math.max(0, el.getBoundingClientRect().width);
194
194
  };
195
195
 
196
+ // src/core/primitives/devWarn.ts
197
+ function devWarn(...args) {
198
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
199
+ console.warn(...args);
200
+ }
201
+ }
202
+
196
203
  // src/core/primitives/snap-points.ts
197
204
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
198
205
  const raw = resolveSnap(p.size, mode, measureFit);
199
206
  if (!Number.isFinite(raw) || raw < 0) {
200
- console.warn(
207
+ devWarn(
201
208
  `[BottomSheet] snap "${p.id}" resolved to invalid size (${String(p.size)} \u2192 ${raw}); clamped to 0.`
202
209
  );
203
210
  return { id: p.id, size: 0 };
@@ -209,7 +216,7 @@ var auditVhUsage = (points) => {
209
216
  for (const p of points) {
210
217
  if (typeof p.size !== "string") continue;
211
218
  if (!VH_NOT_DVH.test(p.size)) continue;
212
- console.warn(
219
+ devWarn(
213
220
  `[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`
214
221
  );
215
222
  }
@@ -263,6 +270,26 @@ var findDragSettleTarget = (input) => {
263
270
  );
264
271
  };
265
272
 
273
+ // src/core/primitives/cancelable-emit.ts
274
+ function emitCancelable(emit, base, warnLabel) {
275
+ let cancelled = false;
276
+ let frozen = false;
277
+ emit({
278
+ ...base,
279
+ cancel: () => {
280
+ if (frozen) {
281
+ devWarn(
282
+ `[BottomSheet] ${warnLabel}.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener.`
283
+ );
284
+ return;
285
+ }
286
+ cancelled = true;
287
+ }
288
+ });
289
+ frozen = true;
290
+ return cancelled;
291
+ }
292
+
266
293
  // src/core/primitives/snap-resolver.ts
267
294
  var SnapResolver = class {
268
295
  constructor(raw, allowed, mode, measureFit, onMaxAxisSizeChange) {
@@ -407,7 +434,9 @@ function notifyLinkedSheets(sheets, self) {
407
434
  try {
408
435
  if (linked.state.size === 0) continue;
409
436
  const ids = linked.getAllowedIds();
410
- const target = ids.find((id) => id !== "closed");
437
+ const resolved = linked.getResolvedSnaps();
438
+ const sizeOf = (id) => resolved.find((s) => s.id === id)?.size ?? 0;
439
+ const target = ids.find((id) => sizeOf(id) > 0);
411
440
  if (target && target !== linked.state.activeId) {
412
441
  void linked.snapTo(target);
413
442
  }
@@ -624,11 +653,15 @@ function installVisualViewport(deps) {
624
653
  if (clamp < deps.getMaxAxisSize()) {
625
654
  deps.setMaxAxisSize(clamp);
626
655
  }
656
+ const wasAnimating = deps.isAnimating?.() ?? false;
657
+ deps.cancelInFlight();
658
+ deps.newCycle();
627
659
  const max = deps.getMaxAxisSize();
628
660
  deps.setSize(Math.min(deps.getSize(), max));
629
661
  const current = deps.resolveActiveSnap();
630
662
  if (current && !deps.isDragging()) {
631
663
  deps.applySize(Math.min(current.size, max));
664
+ if (wasAnimating) deps.resyncAfterCancel?.();
632
665
  } else {
633
666
  deps.applySize(deps.getSize());
634
667
  }
@@ -769,6 +802,54 @@ function installFitObserver(deps) {
769
802
  };
770
803
  }
771
804
 
805
+ // src/core/features/max-height-controller.ts
806
+ function createMaxHeightController(deps) {
807
+ let maxHeightCap;
808
+ let maxHeightRaw;
809
+ const resolveCap = () => {
810
+ const raw = maxHeightRaw;
811
+ if (raw === void 0) {
812
+ maxHeightCap = void 0;
813
+ return;
814
+ }
815
+ if (typeof raw === "number") {
816
+ maxHeightCap = raw;
817
+ return;
818
+ }
819
+ if (typeof window === "undefined") return;
820
+ const measured = resolveSnap(raw, deps.mode);
821
+ maxHeightCap = measured > 0 ? measured : void 0;
822
+ };
823
+ const clampTo = () => {
824
+ if (maxHeightCap === void 0) return;
825
+ if (deps.getMaxAxisSize() > maxHeightCap) {
826
+ deps.setMaxAxisSize(maxHeightCap);
827
+ }
828
+ };
829
+ return {
830
+ setRadius(r) {
831
+ deps.element.style.setProperty(
832
+ "--bs-radius",
833
+ typeof r === "number" ? `${r}px` : r
834
+ );
835
+ },
836
+ setMaxHeight(h) {
837
+ const value = typeof h === "number" ? `${h}px` : h;
838
+ const axis = layoutAxis(deps.mode);
839
+ deps.element.style.setProperty(
840
+ axis === "height" ? "max-height" : "max-width",
841
+ value
842
+ );
843
+ maxHeightRaw = h;
844
+ resolveCap();
845
+ deps.recompute();
846
+ },
847
+ resolveCap,
848
+ clampTo,
849
+ getCap: () => maxHeightCap
850
+ };
851
+ }
852
+
772
853
  // src/core/features/fit-measurement.ts
773
854
  var measureSheetNatural = (element, content, vertical) => {
774
855
  if (!content) {
@@ -900,13 +981,6 @@ var WriteSentinel = class {
900
981
  }
901
982
  };
902
983
 
903
- // src/core/primitives/devWarn.ts
904
- function devWarn(...args) {
905
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
906
- console.warn(...args);
907
- }
908
- }
909
-
910
984
  // src/core/primitives/overlay-position.ts
911
985
  function resolveSheetAnchoredStyle(mode, position, inset) {
912
986
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
@@ -2552,8 +2626,7 @@ var BottomSheetEngine = class {
2552
2626
  __publicField(this, "disableCloseFlag");
2553
2627
  __publicField(this, "disableDragFlag");
2554
2628
  __publicField(this, "closeOnRouteChange");
2555
- __publicField(this, "maxHeightCap");
2556
- __publicField(this, "maxHeightRaw");
2629
+ __publicField(this, "maxHeight");
2557
2630
  __publicField(this, "snapPointsRaw");
2558
2631
  __publicField(this, "snaps");
2559
2632
  __publicField(this, "activeId");
@@ -2664,11 +2737,20 @@ var BottomSheetEngine = class {
2664
2737
  auditVhUsage(this.snapPointsRaw);
2665
2738
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2666
2739
  this.rootEl = this.element.closest(".bs-root");
2740
+ this.maxHeight = createMaxHeightController({
2741
+ element: this.element,
2742
+ mode: this.mode,
2743
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2744
+ setMaxAxisSize: (size) => {
2745
+ this.snaps.setMaxAxisSize(size);
2746
+ },
2747
+ recompute: () => this.recompute()
2748
+ });
2667
2749
  this.fitDeps = {
2668
2750
  element: this.element,
2669
2751
  scrollContainer: this.scrollContainer,
2670
2752
  mode: this.mode,
2671
- getMaxHeightCap: () => this.maxHeightCap
2753
+ getMaxHeightCap: () => this.maxHeight.getCap()
2672
2754
  };
2673
2755
  this.snaps = new SnapResolver(
2674
2756
  this.snapPointsRaw,
@@ -2779,7 +2861,7 @@ var BottomSheetEngine = class {
2779
2861
  }
2780
2862
  use(plugin) {
2781
2863
  if (this.destroyed) {
2782
- console.warn(
2864
+ devWarn(
2783
2865
  `[BottomSheet] use("${plugin.name}") called on destroyed engine \u2014 ignored.`
2784
2866
  );
2785
2867
  return this;
@@ -2821,6 +2903,9 @@ var BottomSheetEngine = class {
2821
2903
  getAllowedIds() {
2822
2904
  return this.snaps.getAllowedIds().slice();
2823
2905
  }
2906
+ getResolvedSnaps() {
2907
+ return this.snaps.getResolvedSnaps();
2908
+ }
2824
2909
  async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2825
2910
  if (this.destroyed) return;
2826
2911
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -2829,11 +2914,11 @@ var BottomSheetEngine = class {
2829
2914
  if (externalSignal?.aborted) return;
2830
2915
  const target = this.snaps.findById(id);
2831
2916
  if (!target) {
2832
- console.warn(`[BottomSheet] unknown snap id: ${id}`);
2917
+ devWarn(`[BottomSheet] unknown snap id: ${id}`);
2833
2918
  return;
2834
2919
  }
2835
2920
  if (!this.snaps.getAllowedIds().includes(id)) {
2836
- console.warn(`[BottomSheet] snap "${id}" is not in allowed list`);
2921
+ devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2837
2922
  return;
2838
2923
  }
2839
2924
  if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
@@ -2965,42 +3050,11 @@ var BottomSheetEngine = class {
2965
3050
  }
2966
3051
  setRadius(r) {
2967
3052
  if (this.destroyed) return;
2968
- this.element.style.setProperty(
2969
- "--bs-radius",
2970
- typeof r === "number" ? `${r}px` : r
2971
- );
3053
+ this.maxHeight.setRadius(r);
2972
3054
  }
2973
3055
  setMaxHeight(h) {
2974
3056
  if (this.destroyed) return;
2975
- const value = typeof h === "number" ? `${h}px` : h;
2976
- const axis = layoutAxis(this.mode);
2977
- this.element.style.setProperty(
2978
- axis === "height" ? "max-height" : "max-width",
2979
- value
2980
- );
2981
- this.maxHeightRaw = h;
2982
- this.resolveMaxHeightCap();
2983
- this.recompute();
2984
- }
2985
- resolveMaxHeightCap() {
2986
- const raw = this.maxHeightRaw;
2987
- if (raw === void 0) {
2988
- this.maxHeightCap = void 0;
2989
- return;
2990
- }
2991
- if (typeof raw === "number") {
2992
- this.maxHeightCap = raw;
2993
- return;
2994
- }
2995
- if (typeof window === "undefined") return;
2996
- const measured = resolveSnap(raw, this.mode);
2997
- this.maxHeightCap = measured > 0 ? measured : void 0;
2998
- }
2999
- clampToMaxHeight() {
3000
- if (this.maxHeightCap === void 0) return;
3001
- if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
3002
- this.snaps.setMaxAxisSize(this.maxHeightCap);
3003
- }
3057
+ this.maxHeight.setMaxHeight(h);
3004
3058
  }
3005
3059
  setAllowed(ids, snap) {
3006
3060
  if (this.destroyed) return;
@@ -3088,7 +3142,7 @@ var BottomSheetEngine = class {
3088
3142
  };
3089
3143
  const host = this.screenComponent?.parentElement;
3090
3144
  if (!host) {
3091
- console.warn(
3145
+ devWarn(
3092
3146
  "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
3093
3147
  );
3094
3148
  return () => {
@@ -3138,9 +3192,9 @@ var BottomSheetEngine = class {
3138
3192
  }
3139
3193
  recompute() {
3140
3194
  if (this.destroyed) return;
3141
- this.resolveMaxHeightCap();
3195
+ this.maxHeight.resolveCap();
3142
3196
  this.snaps.recompute();
3143
- this.clampToMaxHeight();
3197
+ this.maxHeight.clampTo();
3144
3198
  this.scrim.invalidateOpacityCache();
3145
3199
  if (this.gesture?.isDragging) return;
3146
3200
  const current = this.snaps.findById(this.activeId);
@@ -3190,42 +3244,18 @@ var BottomSheetEngine = class {
3190
3244
  return this.currentAbort.signal;
3191
3245
  }
3192
3246
  emitBeforeSnap(target, previousId) {
3193
- let cancelled = false;
3194
- let frozen = false;
3195
- this.emit("before-snap", {
3196
- id: target.id,
3197
- size: target.size,
3198
- previousId,
3199
- cancel: () => {
3200
- if (frozen) {
3201
- console.warn(
3202
- "[BottomSheet] before-snap.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3203
- );
3204
- return;
3205
- }
3206
- cancelled = true;
3207
- }
3208
- });
3209
- frozen = true;
3210
- return cancelled;
3247
+ return emitCancelable(
3248
+ (payload) => this.emit("before-snap", payload),
3249
+ { id: target.id, size: target.size, previousId },
3250
+ "before-snap"
3251
+ );
3211
3252
  }
3212
3253
  emitBeforeClose(reason) {
3213
- let cancelled = false;
3214
- let frozen = false;
3215
- this.emit("before-close", {
3216
- reason,
3217
- cancel: () => {
3218
- if (frozen) {
3219
- console.warn(
3220
- "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3221
- );
3222
- return;
3223
- }
3224
- cancelled = true;
3225
- }
3226
- });
3227
- frozen = true;
3228
- return cancelled;
3254
+ return emitCancelable(
3255
+ (payload) => this.emit("before-close", payload),
3256
+ { reason },
3257
+ "before-close"
3258
+ );
3229
3259
  }
3230
3260
  applyAutoAriaLabelledBy() {
3231
3261
  if (typeof document === "undefined") return;
@@ -3240,16 +3270,16 @@ var BottomSheetEngine = class {
3240
3270
  this.element.setAttribute("aria-labelledby", titled.id);
3241
3271
  }
3242
3272
  recomputeSnaps() {
3243
- this.resolveMaxHeightCap();
3273
+ this.maxHeight.resolveCap();
3244
3274
  this.snaps.setRaw(this.snapPointsRaw);
3245
- this.clampToMaxHeight();
3275
+ this.maxHeight.clampTo();
3246
3276
  this.scrim.invalidateOpacityCache();
3247
3277
  }
3248
3278
  auditDuplicateSnapIds(points) {
3249
3279
  const seen = /* @__PURE__ */ new Set();
3250
3280
  for (const p of points) {
3251
3281
  if (seen.has(p.id)) {
3252
- console.warn(
3282
+ devWarn(
3253
3283
  `[BottomSheet] duplicate snap id "${p.id}" \u2014 only the first occurrence is reachable.`
3254
3284
  );
3255
3285
  }
@@ -3416,7 +3446,15 @@ var BottomSheetEngine = class {
3416
3446
  setSize: (size) => {
3417
3447
  this.size = size;
3418
3448
  },
3419
- applySize: (size) => this.applySize(size)
3449
+ applySize: (size) => this.applySize(size),
3450
+ cancelInFlight: () => {
3451
+ this.animation.cancel();
3452
+ },
3453
+ newCycle: () => {
3454
+ this.newCycle();
3455
+ },
3456
+ isAnimating: () => this.animation.isAnimating,
3457
+ resyncAfterCancel: () => this.resyncAfterResize()
3420
3458
  }));
3421
3459
  }
3422
3460
  this.teardowns.add(installSliderKeyboard({
@@ -3577,6 +3615,72 @@ var BottomSheetEngine = class {
3577
3615
  sheetStack.update();
3578
3616
  }
3579
3617
  };
3618
+ function useEnginePropSync(engineRef, opts) {
3619
+ const optsRef = react.useRef(opts);
3620
+ optsRef.current = opts;
3621
+ const allowedKey = (opts.allowed ?? opts.snapPoints.map((p) => p.id)).join(
3622
+ "\0"
3623
+ );
3624
+ const defKey = opts.snapPoints.map((p) => `${p.id}${String(p.size)}`).join("\0");
3625
+ const defApplied = react.useRef(false);
3626
+ react.useEffect(() => {
3627
+ if (!defApplied.current) {
3628
+ defApplied.current = true;
3629
+ return;
3630
+ }
3631
+ const engine = engineRef.current;
3632
+ if (!engine) return;
3633
+ const current = optsRef.current;
3634
+ engine.setSnapPoints(
3635
+ current.snapPoints,
3636
+ current.allowed ? Array.from(current.allowed) : void 0
3637
+ );
3638
+ }, [defKey]);
3639
+ react.useEffect(() => {
3640
+ if (!engineRef.current) return;
3641
+ const ids = opts.allowed ? Array.from(opts.allowed) : opts.snapPoints.map((p) => p.id);
3642
+ engineRef.current.setAllowed(ids);
3643
+ }, [allowedKey]);
3644
+ react.useEffect(() => {
3645
+ if (!engineRef.current) return;
3646
+ if (opts.backdropColor !== void 0) {
3647
+ engineRef.current.setScrimColor(opts.backdropColor);
3648
+ }
3649
+ }, [opts.backdropColor]);
3650
+ react.useEffect(() => {
3651
+ if (!engineRef.current) return;
3652
+ if (opts.backdropOpacity !== void 0) {
3653
+ engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3654
+ }
3655
+ }, [opts.backdropOpacity]);
3656
+ react.useEffect(() => {
3657
+ if (!engineRef.current) return;
3658
+ if (opts.radius !== void 0) {
3659
+ engineRef.current.setRadius(opts.radius);
3660
+ }
3661
+ }, [opts.radius]);
3662
+ react.useEffect(() => {
3663
+ if (!engineRef.current) return;
3664
+ if (opts.maxHeight !== void 0) {
3665
+ engineRef.current.setMaxHeight(opts.maxHeight);
3666
+ }
3667
+ }, [opts.maxHeight]);
3668
+ react.useEffect(() => {
3669
+ if (opts.persistent !== void 0) {
3670
+ engineRef.current?.setPersistent(opts.persistent);
3671
+ }
3672
+ }, [opts.persistent]);
3673
+ react.useEffect(() => {
3674
+ if (opts.disableClose !== void 0) {
3675
+ engineRef.current?.setDisableClose(opts.disableClose);
3676
+ }
3677
+ }, [opts.disableClose]);
3678
+ react.useEffect(() => {
3679
+ if (opts.disableDrag !== void 0) {
3680
+ engineRef.current?.setDisableDrag(opts.disableDrag);
3681
+ }
3682
+ }, [opts.disableDrag]);
3683
+ }
3580
3684
 
3581
3685
  // src/core/features/teleport.ts
3582
3686
  var resolveTeleportTarget = (target) => {
@@ -3671,6 +3775,12 @@ function useBottomSheet(opts) {
3671
3775
  engine.on("before-close", (payload) => {
3672
3776
  optsRef.current.onBeforeClose?.(payload);
3673
3777
  }),
3778
+ engine.on("open", (payload) => {
3779
+ optsRef.current.onOpen?.(payload.id);
3780
+ }),
3781
+ engine.on("close", () => {
3782
+ optsRef.current.onClose?.();
3783
+ }),
3674
3784
  engine.on("opened", (payload) => {
3675
3785
  optsRef.current.onOpened?.(payload.id);
3676
3786
  }),
@@ -3686,6 +3796,20 @@ function useBottomSheet(opts) {
3686
3796
  optsRef.current.onDragEnd?.(payload);
3687
3797
  })
3688
3798
  ];
3799
+ if (optsRef.current.onDrag) {
3800
+ offs.push(
3801
+ engine.on("drag", (payload) => {
3802
+ optsRef.current.onDrag?.(payload);
3803
+ })
3804
+ );
3805
+ }
3806
+ if (optsRef.current.onProgress) {
3807
+ offs.push(
3808
+ engine.on("progress", (payload) => {
3809
+ optsRef.current.onProgress?.(payload);
3810
+ })
3811
+ );
3812
+ }
3689
3813
  return () => {
3690
3814
  offs.forEach((off) => off());
3691
3815
  restoreTeleport?.();
@@ -3695,68 +3819,17 @@ function useBottomSheet(opts) {
3695
3819
  setMounted((n) => n + 1);
3696
3820
  };
3697
3821
  }, []);
3698
- const allowedKey = (opts.allowed ?? opts.snapPoints.map((p) => p.id)).join(
3699
- "\0"
3700
- );
3701
- const defKey = opts.snapPoints.map((p) => `${p.id}${String(p.size)}`).join("\0");
3702
- const defApplied = react.useRef(false);
3703
- react.useEffect(() => {
3704
- if (!defApplied.current) {
3705
- defApplied.current = true;
3706
- return;
3707
- }
3708
- const engine = engineRef.current;
3709
- if (!engine) return;
3710
- const current = optsRef.current;
3711
- engine.setSnapPoints(
3712
- current.snapPoints,
3713
- current.allowed ? Array.from(current.allowed) : void 0
3714
- );
3715
- }, [defKey]);
3716
- react.useEffect(() => {
3717
- if (!engineRef.current) return;
3718
- const ids = opts.allowed ? Array.from(opts.allowed) : opts.snapPoints.map((p) => p.id);
3719
- engineRef.current.setAllowed(ids);
3720
- }, [allowedKey]);
3721
- react.useEffect(() => {
3722
- if (!engineRef.current) return;
3723
- if (opts.backdropColor !== void 0) {
3724
- engineRef.current.setScrimColor(opts.backdropColor);
3725
- }
3726
- }, [opts.backdropColor]);
3727
- react.useEffect(() => {
3728
- if (!engineRef.current) return;
3729
- if (opts.backdropOpacity !== void 0) {
3730
- engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3731
- }
3732
- }, [opts.backdropOpacity]);
3733
- react.useEffect(() => {
3734
- if (!engineRef.current) return;
3735
- if (opts.radius !== void 0) {
3736
- engineRef.current.setRadius(opts.radius);
3737
- }
3738
- }, [opts.radius]);
3739
- react.useEffect(() => {
3740
- if (!engineRef.current) return;
3741
- if (opts.maxHeight !== void 0) {
3742
- engineRef.current.setMaxHeight(opts.maxHeight);
3743
- }
3744
- }, [opts.maxHeight]);
3745
- react.useEffect(() => {
3746
- if (opts.persistent !== void 0) {
3747
- engineRef.current?.setPersistent(opts.persistent);
3748
- }
3749
- }, [opts.persistent]);
3750
- react.useEffect(() => {
3751
- if (opts.disableClose !== void 0) {
3752
- engineRef.current?.setDisableClose(opts.disableClose);
3753
- }
3754
- }, [opts.disableClose]);
3755
- react.useEffect(() => {
3756
- if (opts.disableDrag !== void 0) {
3757
- engineRef.current?.setDisableDrag(opts.disableDrag);
3758
- }
3759
- }, [opts.disableDrag]);
3822
+ useEnginePropSync(engineRef, {
3823
+ snapPoints: opts.snapPoints,
3824
+ allowed: opts.allowed,
3825
+ backdropColor: opts.backdropColor,
3826
+ backdropOpacity: opts.backdropOpacity,
3827
+ radius: opts.radius,
3828
+ maxHeight: opts.maxHeight,
3829
+ persistent: opts.persistent,
3830
+ disableClose: opts.disableClose,
3831
+ disableDrag: opts.disableDrag
3832
+ });
3760
3833
  const subscribe = react.useCallback((fn) => {
3761
3834
  subscribersRef.current.add(fn);
3762
3835
  return () => subscribersRef.current.delete(fn);
@@ -3865,10 +3938,14 @@ var BottomSheet = react.forwardRef(
3865
3938
  scrimStages,
3866
3939
  onChange,
3867
3940
  onBeforeClose,
3941
+ onOpen,
3942
+ onClose,
3868
3943
  onOpened,
3869
3944
  onClosed,
3870
3945
  onDragStart,
3871
3946
  onDragEnd,
3947
+ onDrag,
3948
+ onProgress,
3872
3949
  ariaLabel = "Bottom sheet",
3873
3950
  ariaLabelledBy,
3874
3951
  noSSR = false,
@@ -3894,10 +3971,14 @@ var BottomSheet = react.forwardRef(
3894
3971
  } = useBottomSheet({
3895
3972
  ...engineOpts,
3896
3973
  onBeforeClose,
3974
+ onOpen,
3975
+ onClose,
3897
3976
  onOpened,
3898
3977
  onClosed,
3899
3978
  onDragStart,
3900
- onDragEnd
3979
+ onDragEnd,
3980
+ onDrag,
3981
+ onProgress
3901
3982
  });
3902
3983
  const ariaModal = engineOpts.focusTrap === true;
3903
3984
  const lastAnnouncedRef = react.useRef("");
@@ -4181,7 +4262,7 @@ function installOverlaySwipe(deps) {
4181
4262
  };
4182
4263
  }
4183
4264
 
4184
- // src/core/overlay.ts
4265
+ // src/core/overlay-options.ts
4185
4266
  var OVERLAY_PRESETS = Object.freeze({
4186
4267
  sheet: Object.freeze({
4187
4268
  enterAnimation: "slide",
@@ -4212,6 +4293,8 @@ var OVERLAY_PRESETS = Object.freeze({
4212
4293
  swipeToClose: false
4213
4294
  })
4214
4295
  });
4296
+
4297
+ // src/core/overlay.ts
4215
4298
  var OverlayEngine = class {
4216
4299
  constructor(opts) {
4217
4300
  __publicField(this, "id", nextInstanceId("ovl"));
@@ -4288,7 +4371,7 @@ var OverlayEngine = class {
4288
4371
  this.onOpenCb = opts.onOpen;
4289
4372
  this.onCloseCb = opts.onClose;
4290
4373
  if (this.peekRaw !== void 0 && this.edge !== "bottom") {
4291
- console.warn(
4374
+ devWarn(
4292
4375
  "[OverlayEngine] `peek` is only supported on edge: 'bottom' \u2014 ignoring."
4293
4376
  );
4294
4377
  this.peekRaw = void 0;
@@ -4355,10 +4438,9 @@ var OverlayEngine = class {
4355
4438
  this.element.style.opacity = "1";
4356
4439
  if (this.backdrop) {
4357
4440
  this.backdrop.style.opacity = String(this.backdropOpacity);
4358
- this.backdrop.style.pointerEvents = this.backdropOpacity > 0 ? "auto" : "none";
4441
+ this.syncBackdropPointerEvents();
4359
4442
  if (this.backdropFilter) {
4360
- this.backdrop.style.backdropFilter = this.backdropFilter;
4361
- this.backdrop.style.webkitBackdropFilter = this.backdropFilter;
4443
+ this.applyBackdropFilter(this.backdropFilter);
4362
4444
  }
4363
4445
  }
4364
4446
  try {
@@ -4378,8 +4460,7 @@ var OverlayEngine = class {
4378
4460
  this.backdrop.style.opacity = "0";
4379
4461
  this.backdrop.style.pointerEvents = "none";
4380
4462
  if (this.backdropFilter) {
4381
- this.backdrop.style.backdropFilter = "";
4382
- this.backdrop.style.webkitBackdropFilter = "";
4463
+ this.applyBackdropFilter(void 0);
4383
4464
  }
4384
4465
  }
4385
4466
  this.element.setAttribute("data-state", "closed");
@@ -4437,8 +4518,7 @@ var OverlayEngine = class {
4437
4518
  this.backdrop.style.opacity = "0";
4438
4519
  this.backdrop.style.pointerEvents = "none";
4439
4520
  if (this.backdropFilter) {
4440
- this.backdrop.style.backdropFilter = "";
4441
- this.backdrop.style.webkitBackdropFilter = "";
4521
+ this.applyBackdropFilter(void 0);
4442
4522
  }
4443
4523
  }
4444
4524
  this.releaseInteractiveListeners();
@@ -4486,20 +4566,14 @@ var OverlayEngine = class {
4486
4566
  this.backdropOpacity = opacity;
4487
4567
  if (this.isOpen_ && this.backdrop) {
4488
4568
  this.backdrop.style.opacity = String(opacity);
4569
+ this.syncBackdropPointerEvents();
4489
4570
  }
4490
4571
  }
4491
4572
  setBackdropFilter(filter) {
4492
4573
  if (this.destroyed) return;
4493
4574
  this.backdropFilter = filter;
4494
4575
  if (!this.isOpen_ || !this.backdrop) return;
4495
- const style = this.backdrop.style;
4496
- if (filter) {
4497
- this.backdrop.style.backdropFilter = filter;
4498
- style.webkitBackdropFilter = filter;
4499
- } else {
4500
- this.backdrop.style.backdropFilter = "";
4501
- style.webkitBackdropFilter = "";
4502
- }
4576
+ this.applyBackdropFilter(filter);
4503
4577
  }
4504
4578
  setSwipeToClose(config) {
4505
4579
  if (this.destroyed) return;
@@ -4568,16 +4642,10 @@ var OverlayEngine = class {
4568
4642
  if (this.isOpen_ && this.backdrop) {
4569
4643
  if (merged.backdropOpacity !== void 0) {
4570
4644
  this.backdrop.style.opacity = String(this.backdropOpacity);
4645
+ this.syncBackdropPointerEvents();
4571
4646
  }
4572
4647
  if (merged.backdropFilter !== void 0) {
4573
- const style = this.backdrop.style;
4574
- if (this.backdropFilter) {
4575
- this.backdrop.style.backdropFilter = this.backdropFilter;
4576
- style.webkitBackdropFilter = this.backdropFilter;
4577
- } else {
4578
- this.backdrop.style.backdropFilter = "";
4579
- style.webkitBackdropFilter = "";
4580
- }
4648
+ this.applyBackdropFilter(this.backdropFilter);
4581
4649
  }
4582
4650
  }
4583
4651
  if (opts.duration !== void 0) this.duration = opts.duration;
@@ -4712,6 +4780,21 @@ var OverlayEngine = class {
4712
4780
  this.backdrop.style.pointerEvents = "none";
4713
4781
  }
4714
4782
  }
4783
+ syncBackdropPointerEvents() {
4784
+ if (!this.backdrop) return;
4785
+ this.backdrop.style.pointerEvents = this.backdropOpacity > 0 ? "auto" : "none";
4786
+ }
4787
+ applyBackdropFilter(filter) {
4788
+ if (!this.backdrop) return;
4789
+ const style = this.backdrop.style;
4790
+ if (filter) {
4791
+ this.backdrop.style.backdropFilter = filter;
4792
+ style.webkitBackdropFilter = filter;
4793
+ } else {
4794
+ this.backdrop.style.backdropFilter = "";
4795
+ style.webkitBackdropFilter = "";
4796
+ }
4797
+ }
4715
4798
  setWillChange(on) {
4716
4799
  this.element.style.willChange = on ? "transform, opacity" : "";
4717
4800
  if (this.backdrop) {
@@ -4944,6 +5027,8 @@ var Overlay = react.forwardRef(function Overlay2(props, ref) {
4944
5027
  )
4945
5028
  ] });
4946
5029
  });
5030
+
5031
+ // src/react/config-validation.ts
4947
5032
  var SUPPORTED_VERSION = 1;
4948
5033
  var TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
4949
5034
  "version",
@@ -4968,7 +5053,7 @@ var warnUnknownKeys = (obj, allowed, context) => {
4968
5053
  if (!obj || typeof obj !== "object") return;
4969
5054
  const unknown = Object.keys(obj).filter((k) => !allowed.has(k));
4970
5055
  if (unknown.length === 0) return;
4971
- console.warn(
5056
+ devWarn(
4972
5057
  `[BottomSheetFromConfig] Unknown ${unknown.length === 1 ? "key" : "keys"} in ${context}: ${unknown.map((k) => `"${k}"`).join(", ")}. Allowed: ${[...allowed].join(", ")}. Likely a typo \u2014 values ignored.`
4973
5058
  );
4974
5059
  };
@@ -4978,7 +5063,7 @@ function validateConfig(config) {
4978
5063
  validatedConfigs.add(config);
4979
5064
  let ok = true;
4980
5065
  if (config.version !== SUPPORTED_VERSION) {
4981
- console.warn(
5066
+ devWarn(
4982
5067
  `[BottomSheetFromConfig] Unsupported config version ${String(
4983
5068
  config.version
4984
5069
  )}; expected ${SUPPORTED_VERSION}. Falling back to defaults.`
@@ -4986,7 +5071,7 @@ function validateConfig(config) {
4986
5071
  return false;
4987
5072
  }
4988
5073
  if (!Array.isArray(config.snapPoints) || config.snapPoints.length === 0) {
4989
- console.warn(
5074
+ devWarn(
4990
5075
  "[BottomSheetFromConfig] config.snapPoints is missing or empty; using a single full-height fallback snap."
4991
5076
  );
4992
5077
  ok = false;