@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
@@ -183,11 +183,18 @@ var resolveSnap = (point, mode, measureFit) => {
183
183
  return Math.max(0, el.getBoundingClientRect().width);
184
184
  };
185
185
 
186
+ // src/core/primitives/devWarn.ts
187
+ function devWarn(...args) {
188
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
189
+ console.warn(...args);
190
+ }
191
+ }
192
+
186
193
  // src/core/primitives/snap-points.ts
187
194
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
188
195
  const raw = resolveSnap(p.size, mode, measureFit);
189
196
  if (!Number.isFinite(raw) || raw < 0) {
190
- console.warn(
197
+ devWarn(
191
198
  `[BottomSheet] snap "${p.id}" resolved to invalid size (${String(p.size)} \u2192 ${raw}); clamped to 0.`
192
199
  );
193
200
  return { id: p.id, size: 0 };
@@ -199,7 +206,7 @@ var auditVhUsage = (points) => {
199
206
  for (const p of points) {
200
207
  if (typeof p.size !== "string") continue;
201
208
  if (!VH_NOT_DVH.test(p.size)) continue;
202
- console.warn(
209
+ devWarn(
203
210
  `[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`
204
211
  );
205
212
  }
@@ -253,6 +260,26 @@ var findDragSettleTarget = (input) => {
253
260
  );
254
261
  };
255
262
 
263
+ // src/core/primitives/cancelable-emit.ts
264
+ function emitCancelable(emit, base, warnLabel) {
265
+ let cancelled = false;
266
+ let frozen = false;
267
+ emit({
268
+ ...base,
269
+ cancel: () => {
270
+ if (frozen) {
271
+ devWarn(
272
+ `[BottomSheet] ${warnLabel}.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener.`
273
+ );
274
+ return;
275
+ }
276
+ cancelled = true;
277
+ }
278
+ });
279
+ frozen = true;
280
+ return cancelled;
281
+ }
282
+
256
283
  // src/core/primitives/snap-resolver.ts
257
284
  var SnapResolver = class {
258
285
  constructor(raw, allowed, mode, measureFit, onMaxAxisSizeChange) {
@@ -397,7 +424,9 @@ function notifyLinkedSheets(sheets, self) {
397
424
  try {
398
425
  if (linked.state.size === 0) continue;
399
426
  const ids = linked.getAllowedIds();
400
- const target = ids.find((id) => id !== "closed");
427
+ const resolved = linked.getResolvedSnaps();
428
+ const sizeOf = (id) => resolved.find((s) => s.id === id)?.size ?? 0;
429
+ const target = ids.find((id) => sizeOf(id) > 0);
401
430
  if (target && target !== linked.state.activeId) {
402
431
  void linked.snapTo(target);
403
432
  }
@@ -614,11 +643,15 @@ function installVisualViewport(deps) {
614
643
  if (clamp < deps.getMaxAxisSize()) {
615
644
  deps.setMaxAxisSize(clamp);
616
645
  }
646
+ const wasAnimating = deps.isAnimating?.() ?? false;
647
+ deps.cancelInFlight();
648
+ deps.newCycle();
617
649
  const max = deps.getMaxAxisSize();
618
650
  deps.setSize(Math.min(deps.getSize(), max));
619
651
  const current = deps.resolveActiveSnap();
620
652
  if (current && !deps.isDragging()) {
621
653
  deps.applySize(Math.min(current.size, max));
654
+ if (wasAnimating) deps.resyncAfterCancel?.();
622
655
  } else {
623
656
  deps.applySize(deps.getSize());
624
657
  }
@@ -759,6 +792,54 @@ function installFitObserver(deps) {
759
792
  };
760
793
  }
761
794
 
795
+ // src/core/features/max-height-controller.ts
796
+ function createMaxHeightController(deps) {
797
+ let maxHeightCap;
798
+ let maxHeightRaw;
799
+ const resolveCap = () => {
800
+ const raw = maxHeightRaw;
801
+ if (raw === void 0) {
802
+ maxHeightCap = void 0;
803
+ return;
804
+ }
805
+ if (typeof raw === "number") {
806
+ maxHeightCap = raw;
807
+ return;
808
+ }
809
+ if (typeof window === "undefined") return;
810
+ const measured = resolveSnap(raw, deps.mode);
811
+ maxHeightCap = measured > 0 ? measured : void 0;
812
+ };
813
+ const clampTo = () => {
814
+ if (maxHeightCap === void 0) return;
815
+ if (deps.getMaxAxisSize() > maxHeightCap) {
816
+ deps.setMaxAxisSize(maxHeightCap);
817
+ }
818
+ };
819
+ return {
820
+ setRadius(r) {
821
+ deps.element.style.setProperty(
822
+ "--bs-radius",
823
+ typeof r === "number" ? `${r}px` : r
824
+ );
825
+ },
826
+ setMaxHeight(h) {
827
+ const value = typeof h === "number" ? `${h}px` : h;
828
+ const axis = layoutAxis(deps.mode);
829
+ deps.element.style.setProperty(
830
+ axis === "height" ? "max-height" : "max-width",
831
+ value
832
+ );
833
+ maxHeightRaw = h;
834
+ resolveCap();
835
+ deps.recompute();
836
+ },
837
+ resolveCap,
838
+ clampTo,
839
+ getCap: () => maxHeightCap
840
+ };
841
+ }
842
+
762
843
  // src/core/features/fit-measurement.ts
763
844
  var measureSheetNatural = (element, content, vertical) => {
764
845
  if (!content) {
@@ -890,13 +971,6 @@ var WriteSentinel = class {
890
971
  }
891
972
  };
892
973
 
893
- // src/core/primitives/devWarn.ts
894
- function devWarn(...args) {
895
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
896
- console.warn(...args);
897
- }
898
- }
899
-
900
974
  // src/core/primitives/overlay-position.ts
901
975
  function resolveSheetAnchoredStyle(mode, position, inset) {
902
976
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
@@ -2542,8 +2616,7 @@ var BottomSheetEngine = class {
2542
2616
  __publicField(this, "disableCloseFlag");
2543
2617
  __publicField(this, "disableDragFlag");
2544
2618
  __publicField(this, "closeOnRouteChange");
2545
- __publicField(this, "maxHeightCap");
2546
- __publicField(this, "maxHeightRaw");
2619
+ __publicField(this, "maxHeight");
2547
2620
  __publicField(this, "snapPointsRaw");
2548
2621
  __publicField(this, "snaps");
2549
2622
  __publicField(this, "activeId");
@@ -2654,11 +2727,20 @@ var BottomSheetEngine = class {
2654
2727
  auditVhUsage(this.snapPointsRaw);
2655
2728
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2656
2729
  this.rootEl = this.element.closest(".bs-root");
2730
+ this.maxHeight = createMaxHeightController({
2731
+ element: this.element,
2732
+ mode: this.mode,
2733
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2734
+ setMaxAxisSize: (size) => {
2735
+ this.snaps.setMaxAxisSize(size);
2736
+ },
2737
+ recompute: () => this.recompute()
2738
+ });
2657
2739
  this.fitDeps = {
2658
2740
  element: this.element,
2659
2741
  scrollContainer: this.scrollContainer,
2660
2742
  mode: this.mode,
2661
- getMaxHeightCap: () => this.maxHeightCap
2743
+ getMaxHeightCap: () => this.maxHeight.getCap()
2662
2744
  };
2663
2745
  this.snaps = new SnapResolver(
2664
2746
  this.snapPointsRaw,
@@ -2769,7 +2851,7 @@ var BottomSheetEngine = class {
2769
2851
  }
2770
2852
  use(plugin) {
2771
2853
  if (this.destroyed) {
2772
- console.warn(
2854
+ devWarn(
2773
2855
  `[BottomSheet] use("${plugin.name}") called on destroyed engine \u2014 ignored.`
2774
2856
  );
2775
2857
  return this;
@@ -2811,6 +2893,9 @@ var BottomSheetEngine = class {
2811
2893
  getAllowedIds() {
2812
2894
  return this.snaps.getAllowedIds().slice();
2813
2895
  }
2896
+ getResolvedSnaps() {
2897
+ return this.snaps.getResolvedSnaps();
2898
+ }
2814
2899
  async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2815
2900
  if (this.destroyed) return;
2816
2901
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -2819,11 +2904,11 @@ var BottomSheetEngine = class {
2819
2904
  if (externalSignal?.aborted) return;
2820
2905
  const target = this.snaps.findById(id);
2821
2906
  if (!target) {
2822
- console.warn(`[BottomSheet] unknown snap id: ${id}`);
2907
+ devWarn(`[BottomSheet] unknown snap id: ${id}`);
2823
2908
  return;
2824
2909
  }
2825
2910
  if (!this.snaps.getAllowedIds().includes(id)) {
2826
- console.warn(`[BottomSheet] snap "${id}" is not in allowed list`);
2911
+ devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2827
2912
  return;
2828
2913
  }
2829
2914
  if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
@@ -2955,42 +3040,11 @@ var BottomSheetEngine = class {
2955
3040
  }
2956
3041
  setRadius(r) {
2957
3042
  if (this.destroyed) return;
2958
- this.element.style.setProperty(
2959
- "--bs-radius",
2960
- typeof r === "number" ? `${r}px` : r
2961
- );
3043
+ this.maxHeight.setRadius(r);
2962
3044
  }
2963
3045
  setMaxHeight(h) {
2964
3046
  if (this.destroyed) return;
2965
- const value = typeof h === "number" ? `${h}px` : h;
2966
- const axis = layoutAxis(this.mode);
2967
- this.element.style.setProperty(
2968
- axis === "height" ? "max-height" : "max-width",
2969
- value
2970
- );
2971
- this.maxHeightRaw = h;
2972
- this.resolveMaxHeightCap();
2973
- this.recompute();
2974
- }
2975
- resolveMaxHeightCap() {
2976
- const raw = this.maxHeightRaw;
2977
- if (raw === void 0) {
2978
- this.maxHeightCap = void 0;
2979
- return;
2980
- }
2981
- if (typeof raw === "number") {
2982
- this.maxHeightCap = raw;
2983
- return;
2984
- }
2985
- if (typeof window === "undefined") return;
2986
- const measured = resolveSnap(raw, this.mode);
2987
- this.maxHeightCap = measured > 0 ? measured : void 0;
2988
- }
2989
- clampToMaxHeight() {
2990
- if (this.maxHeightCap === void 0) return;
2991
- if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2992
- this.snaps.setMaxAxisSize(this.maxHeightCap);
2993
- }
3047
+ this.maxHeight.setMaxHeight(h);
2994
3048
  }
2995
3049
  setAllowed(ids, snap) {
2996
3050
  if (this.destroyed) return;
@@ -3078,7 +3132,7 @@ var BottomSheetEngine = class {
3078
3132
  };
3079
3133
  const host = this.screenComponent?.parentElement;
3080
3134
  if (!host) {
3081
- console.warn(
3135
+ devWarn(
3082
3136
  "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
3083
3137
  );
3084
3138
  return () => {
@@ -3128,9 +3182,9 @@ var BottomSheetEngine = class {
3128
3182
  }
3129
3183
  recompute() {
3130
3184
  if (this.destroyed) return;
3131
- this.resolveMaxHeightCap();
3185
+ this.maxHeight.resolveCap();
3132
3186
  this.snaps.recompute();
3133
- this.clampToMaxHeight();
3187
+ this.maxHeight.clampTo();
3134
3188
  this.scrim.invalidateOpacityCache();
3135
3189
  if (this.gesture?.isDragging) return;
3136
3190
  const current = this.snaps.findById(this.activeId);
@@ -3180,42 +3234,18 @@ var BottomSheetEngine = class {
3180
3234
  return this.currentAbort.signal;
3181
3235
  }
3182
3236
  emitBeforeSnap(target, previousId) {
3183
- let cancelled = false;
3184
- let frozen = false;
3185
- this.emit("before-snap", {
3186
- id: target.id,
3187
- size: target.size,
3188
- previousId,
3189
- cancel: () => {
3190
- if (frozen) {
3191
- console.warn(
3192
- "[BottomSheet] before-snap.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3193
- );
3194
- return;
3195
- }
3196
- cancelled = true;
3197
- }
3198
- });
3199
- frozen = true;
3200
- return cancelled;
3237
+ return emitCancelable(
3238
+ (payload) => this.emit("before-snap", payload),
3239
+ { id: target.id, size: target.size, previousId },
3240
+ "before-snap"
3241
+ );
3201
3242
  }
3202
3243
  emitBeforeClose(reason) {
3203
- let cancelled = false;
3204
- let frozen = false;
3205
- this.emit("before-close", {
3206
- reason,
3207
- cancel: () => {
3208
- if (frozen) {
3209
- console.warn(
3210
- "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3211
- );
3212
- return;
3213
- }
3214
- cancelled = true;
3215
- }
3216
- });
3217
- frozen = true;
3218
- return cancelled;
3244
+ return emitCancelable(
3245
+ (payload) => this.emit("before-close", payload),
3246
+ { reason },
3247
+ "before-close"
3248
+ );
3219
3249
  }
3220
3250
  applyAutoAriaLabelledBy() {
3221
3251
  if (typeof document === "undefined") return;
@@ -3230,16 +3260,16 @@ var BottomSheetEngine = class {
3230
3260
  this.element.setAttribute("aria-labelledby", titled.id);
3231
3261
  }
3232
3262
  recomputeSnaps() {
3233
- this.resolveMaxHeightCap();
3263
+ this.maxHeight.resolveCap();
3234
3264
  this.snaps.setRaw(this.snapPointsRaw);
3235
- this.clampToMaxHeight();
3265
+ this.maxHeight.clampTo();
3236
3266
  this.scrim.invalidateOpacityCache();
3237
3267
  }
3238
3268
  auditDuplicateSnapIds(points) {
3239
3269
  const seen = /* @__PURE__ */ new Set();
3240
3270
  for (const p of points) {
3241
3271
  if (seen.has(p.id)) {
3242
- console.warn(
3272
+ devWarn(
3243
3273
  `[BottomSheet] duplicate snap id "${p.id}" \u2014 only the first occurrence is reachable.`
3244
3274
  );
3245
3275
  }
@@ -3406,7 +3436,15 @@ var BottomSheetEngine = class {
3406
3436
  setSize: (size) => {
3407
3437
  this.size = size;
3408
3438
  },
3409
- applySize: (size) => this.applySize(size)
3439
+ applySize: (size) => this.applySize(size),
3440
+ cancelInFlight: () => {
3441
+ this.animation.cancel();
3442
+ },
3443
+ newCycle: () => {
3444
+ this.newCycle();
3445
+ },
3446
+ isAnimating: () => this.animation.isAnimating,
3447
+ resyncAfterCancel: () => this.resyncAfterResize()
3410
3448
  }));
3411
3449
  }
3412
3450
  this.teardowns.add(installSliderKeyboard({
@@ -61,6 +61,8 @@
61
61
  onclosed?: () => void;
62
62
  ondragstart?: () => void;
63
63
  ondragend?: () => void;
64
+ ondrag?: (payload: SheetEventMap["drag"]) => void;
65
+ onprogress?: (payload: SheetEventMap["progress"]) => void;
64
66
  onchange?: (state: EngineState & { activeId: TId }) => void;
65
67
  };
66
68
 
@@ -109,6 +111,8 @@
109
111
  onclosed,
110
112
  ondragstart,
111
113
  ondragend,
114
+ ondrag,
115
+ onprogress,
112
116
  onchange,
113
117
  }: Props = $props();
114
118
 
@@ -202,6 +206,8 @@
202
206
  sync();
203
207
  ondragend?.();
204
208
  });
209
+ if (ondrag) engine.on("drag", payload => ondrag(payload));
210
+ if (onprogress) engine.on("progress", payload => onprogress(payload));
205
211
  });
206
212
 
207
213
  onDestroy(() => {