@surdeddd/bottom-sheet 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/qwik.js CHANGED
@@ -2358,6 +2358,7 @@ var BottomSheetEngine = class {
2358
2358
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2359
2359
  __publicField(this, "isTopSheet", true);
2360
2360
  __publicField(this, "opening", false);
2361
+ __publicField(this, "allowOvershoot", false);
2361
2362
  __publicField(this, "progressPayload", {
2362
2363
  value: 0,
2363
2364
  size: 0
@@ -2910,45 +2911,52 @@ var BottomSheetEngine = class {
2910
2911
  }
2911
2912
  return out;
2912
2913
  }
2913
- measureContentExtent(vertical) {
2914
+ measureSheetNatural(vertical) {
2915
+ const el = this.element;
2914
2916
  const content = this.scrollContainer;
2915
- if (!content) return 0;
2916
- const targets = this.contentTargets();
2917
- if (targets.length === 0) {
2918
- return vertical ? content.scrollHeight : content.scrollWidth;
2919
- }
2920
- let min = Infinity;
2921
- let max = -Infinity;
2922
- for (const el of targets) {
2923
- const r = el.getBoundingClientRect();
2924
- if (vertical) {
2925
- if (r.top < min) min = r.top;
2926
- if (r.bottom > max) max = r.bottom;
2927
- } else {
2928
- if (r.left < min) min = r.left;
2929
- if (r.right > max) max = r.right;
2930
- }
2931
- }
2932
- if (!isFinite(min) || !isFinite(max) || max <= min) {
2933
- return vertical ? content.scrollHeight : content.scrollWidth;
2934
- }
2935
- let pad = 0;
2936
- if (typeof getComputedStyle !== "undefined") {
2937
- const cs = getComputedStyle(content);
2938
- pad = vertical ? (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0) : (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0);
2939
- }
2940
- return max - min + pad;
2917
+ if (!content) {
2918
+ return vertical ? el.offsetHeight : el.offsetWidth;
2919
+ }
2920
+ const axis = vertical ? "height" : "width";
2921
+ const sheetStyle = el.style;
2922
+ const cs = content.style;
2923
+ const prevSheet = sheetStyle[axis];
2924
+ const prevFlex = cs.flex;
2925
+ const prevContent = vertical ? cs.height : cs.width;
2926
+ sheetStyle[axis] = "auto";
2927
+ cs.flex = "none";
2928
+ if (vertical) cs.height = "auto";
2929
+ else cs.width = "auto";
2930
+ const poked = vertical ? el.offsetHeight : el.offsetWidth;
2931
+ const contentBox = vertical ? content.clientHeight : content.clientWidth;
2932
+ const contentScroll = vertical ? content.scrollHeight : content.scrollWidth;
2933
+ const natural = Math.max(poked, poked - contentBox + contentScroll);
2934
+ sheetStyle[axis] = prevSheet;
2935
+ cs.flex = prevFlex;
2936
+ if (vertical) cs.height = prevContent;
2937
+ else cs.width = prevContent;
2938
+ return natural;
2941
2939
  }
2942
2940
  measureFitSize() {
2943
2941
  const vertical = layoutAxis(this.mode) === "height";
2944
- const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
2945
- const content = this.scrollContainer;
2946
- const natural = content ? handleSize + this.measureContentExtent(vertical) : handleSize;
2942
+ const natural = this.measureSheetNatural(vertical);
2947
2943
  const capped = this.maxHeightCap !== void 0 ? Math.min(natural, this.maxHeightCap) : natural;
2948
2944
  if (typeof window === "undefined") return capped;
2949
- const viewport = vertical ? window.innerHeight : window.innerWidth;
2945
+ const viewport = this.containingExtent(vertical);
2950
2946
  return viewport > 0 ? Math.min(capped, viewport) : capped;
2951
2947
  }
2948
+ // The fit cap is the sheet's containing block, not always the window: a
2949
+ // position:fixed sheet nested under a transform/filter/will-change ancestor
2950
+ // is clipped to that ancestor, so capping by window.innerHeight oversizes the
2951
+ // sheet and the content/footer get clipped. offsetParent resolves to that
2952
+ // ancestor when present, and is null (→ viewport) for a plain fixed sheet.
2953
+ containingExtent(vertical) {
2954
+ const op = this.element.offsetParent;
2955
+ if (op && op !== document.body && op !== document.documentElement) {
2956
+ return vertical ? op.clientHeight : op.clientWidth;
2957
+ }
2958
+ return vertical ? window.innerHeight : window.innerWidth;
2959
+ }
2952
2960
  installFitObserver() {
2953
2961
  if (typeof ResizeObserver === "undefined") return;
2954
2962
  if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
@@ -3029,6 +3037,10 @@ var BottomSheetEngine = class {
3029
3037
  this.backdrop.style.opacity = "";
3030
3038
  this.backdrop.style.pointerEvents = "";
3031
3039
  }
3040
+ if (this.anchorHost) {
3041
+ this.anchorHost.style.removeProperty("--bs-size");
3042
+ this.anchorHost.style.removeProperty("--bs-progress");
3043
+ }
3032
3044
  if (this.screenComponent) {
3033
3045
  this.screenComponent.style.opacity = "";
3034
3046
  this.screenComponent.style.display = "";
@@ -3040,6 +3052,7 @@ var BottomSheetEngine = class {
3040
3052
  this.bus.emit(event, payload);
3041
3053
  }
3042
3054
  newCycle() {
3055
+ this.allowOvershoot = false;
3043
3056
  this.currentAbort.abort();
3044
3057
  this.currentAbort = new AbortController();
3045
3058
  return this.currentAbort.signal;
@@ -3105,6 +3118,7 @@ var BottomSheetEngine = class {
3105
3118
  seen.add(p.id);
3106
3119
  }
3107
3120
  this.snaps.setRaw(this.snapPointsRaw);
3121
+ this.clampToMaxHeight();
3108
3122
  this.scrim.invalidateOpacityCache();
3109
3123
  }
3110
3124
  getAllowedRange() {
@@ -3311,9 +3325,13 @@ var BottomSheetEngine = class {
3311
3325
  this.opening = true;
3312
3326
  sheetStack.promote(this.id);
3313
3327
  }
3314
- void this.animation.animateTo(target.size, velocity).then(() => {
3315
- this.opening = false;
3328
+ const settleCap = this.snaps.getMaxAxisSize();
3329
+ const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3330
+ this.allowOvershoot = true;
3331
+ void this.animation.animateTo(settleSize, velocity).then(() => {
3316
3332
  if (signal.aborted) return;
3333
+ this.allowOvershoot = false;
3334
+ this.opening = false;
3317
3335
  this.scrollCache.restore(target.id, previousSize, target.size);
3318
3336
  this.updateAriaSlider();
3319
3337
  this.emit("snap", {
@@ -3345,7 +3363,7 @@ var BottomSheetEngine = class {
3345
3363
  }
3346
3364
  applySize(size) {
3347
3365
  const cap = this.snaps.getMaxAxisSize();
3348
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging ? cap : size;
3366
+ const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3349
3367
  this.size = clamped;
3350
3368
  const offset = cap - clamped;
3351
3369
  const style = this.element.style;
@@ -3437,6 +3455,8 @@ var BottomSheet = component$((props) => {
3437
3455
  const contentRef = useSignal();
3438
3456
  const backdropRef = useSignal();
3439
3457
  const scrimRef = useSignal();
3458
+ const headerRef = useSignal();
3459
+ const footerRef = useSignal();
3440
3460
  const initialActive = props.initial ?? props.snapPoints[0]?.id ?? "default";
3441
3461
  const state = useStore({
3442
3462
  size: 0,
@@ -3471,6 +3491,11 @@ var BottomSheet = component$((props) => {
3471
3491
  lockBodyScroll: props.lockBodyScroll,
3472
3492
  rubberBand: props.rubberBand,
3473
3493
  backdropRange: props.backdropRange,
3494
+ persistent: props.persistent,
3495
+ disableClose: props.disableClose,
3496
+ disableDrag: props.disableDrag,
3497
+ closeOnRouteChange: props.closeOnRouteChange,
3498
+ returnFocusTo: props.returnFocusTo,
3474
3499
  radius: props.radius,
3475
3500
  maxHeight: props.maxHeight
3476
3501
  };
@@ -3492,7 +3517,25 @@ var BottomSheet = component$((props) => {
3492
3517
  });
3493
3518
  engine.on("dragstart", sync);
3494
3519
  engine.on("dragend", sync);
3520
+ let onBackdropClick;
3521
+ if (props.backdrop !== false && props.closeOnBackdrop !== false && backdropRef.value) {
3522
+ onBackdropClick = () => {
3523
+ if (engine.canDismiss()) void engine.close("backdrop");
3524
+ };
3525
+ backdropRef.value.addEventListener("click", onBackdropClick);
3526
+ }
3527
+ const collapseEmpty = (el) => {
3528
+ if (!el) return;
3529
+ const empty = el.children.length === 0 && el.textContent?.trim() === "";
3530
+ if (empty) el.setAttribute("hidden", "");
3531
+ else el.removeAttribute("hidden");
3532
+ };
3533
+ collapseEmpty(headerRef.value);
3534
+ collapseEmpty(footerRef.value);
3495
3535
  cleanup(() => {
3536
+ if (onBackdropClick && backdropRef.value) {
3537
+ backdropRef.value.removeEventListener("click", onBackdropClick);
3538
+ }
3496
3539
  engineStore.engine = void 0;
3497
3540
  engine.destroy();
3498
3541
  });
@@ -3542,9 +3585,9 @@ var BottomSheet = component$((props) => {
3542
3585
  "aria-label": "Resize sheet"
3543
3586
  }
3544
3587
  ),
3545
- /* @__PURE__ */ jsx("div", { class: "bs-header", children: /* @__PURE__ */ jsx(Slot, { name: "header" }) }),
3588
+ /* @__PURE__ */ jsx("div", { class: "bs-header", ref: headerRef, children: /* @__PURE__ */ jsx(Slot, { name: "header" }) }),
3546
3589
  /* @__PURE__ */ jsx("div", { class: "bs-content", ref: contentRef, children: /* @__PURE__ */ jsx(Slot, {}) }),
3547
- /* @__PURE__ */ jsx("div", { class: "bs-footer", children: /* @__PURE__ */ jsx(Slot, { name: "footer" }) }),
3590
+ /* @__PURE__ */ jsx("div", { class: "bs-footer", ref: footerRef, children: /* @__PURE__ */ jsx(Slot, { name: "footer" }) }),
3548
3591
  /* @__PURE__ */ jsx("span", { class: "bs-sr-only", role: "status", "aria-live": "polite", children: state.activeId })
3549
3592
  ]
3550
3593
  }
package/dist/react.cjs CHANGED
@@ -2367,6 +2367,7 @@ var BottomSheetEngine = class {
2367
2367
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2368
2368
  __publicField(this, "isTopSheet", true);
2369
2369
  __publicField(this, "opening", false);
2370
+ __publicField(this, "allowOvershoot", false);
2370
2371
  __publicField(this, "progressPayload", {
2371
2372
  value: 0,
2372
2373
  size: 0
@@ -2919,45 +2920,52 @@ var BottomSheetEngine = class {
2919
2920
  }
2920
2921
  return out;
2921
2922
  }
2922
- measureContentExtent(vertical) {
2923
+ measureSheetNatural(vertical) {
2924
+ const el = this.element;
2923
2925
  const content = this.scrollContainer;
2924
- if (!content) return 0;
2925
- const targets = this.contentTargets();
2926
- if (targets.length === 0) {
2927
- return vertical ? content.scrollHeight : content.scrollWidth;
2928
- }
2929
- let min = Infinity;
2930
- let max = -Infinity;
2931
- for (const el of targets) {
2932
- const r = el.getBoundingClientRect();
2933
- if (vertical) {
2934
- if (r.top < min) min = r.top;
2935
- if (r.bottom > max) max = r.bottom;
2936
- } else {
2937
- if (r.left < min) min = r.left;
2938
- if (r.right > max) max = r.right;
2939
- }
2940
- }
2941
- if (!isFinite(min) || !isFinite(max) || max <= min) {
2942
- return vertical ? content.scrollHeight : content.scrollWidth;
2943
- }
2944
- let pad = 0;
2945
- if (typeof getComputedStyle !== "undefined") {
2946
- const cs = getComputedStyle(content);
2947
- pad = vertical ? (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0) : (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0);
2948
- }
2949
- return max - min + pad;
2926
+ if (!content) {
2927
+ return vertical ? el.offsetHeight : el.offsetWidth;
2928
+ }
2929
+ const axis = vertical ? "height" : "width";
2930
+ const sheetStyle = el.style;
2931
+ const cs = content.style;
2932
+ const prevSheet = sheetStyle[axis];
2933
+ const prevFlex = cs.flex;
2934
+ const prevContent = vertical ? cs.height : cs.width;
2935
+ sheetStyle[axis] = "auto";
2936
+ cs.flex = "none";
2937
+ if (vertical) cs.height = "auto";
2938
+ else cs.width = "auto";
2939
+ const poked = vertical ? el.offsetHeight : el.offsetWidth;
2940
+ const contentBox = vertical ? content.clientHeight : content.clientWidth;
2941
+ const contentScroll = vertical ? content.scrollHeight : content.scrollWidth;
2942
+ const natural = Math.max(poked, poked - contentBox + contentScroll);
2943
+ sheetStyle[axis] = prevSheet;
2944
+ cs.flex = prevFlex;
2945
+ if (vertical) cs.height = prevContent;
2946
+ else cs.width = prevContent;
2947
+ return natural;
2950
2948
  }
2951
2949
  measureFitSize() {
2952
2950
  const vertical = layoutAxis(this.mode) === "height";
2953
- const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
2954
- const content = this.scrollContainer;
2955
- const natural = content ? handleSize + this.measureContentExtent(vertical) : handleSize;
2951
+ const natural = this.measureSheetNatural(vertical);
2956
2952
  const capped = this.maxHeightCap !== void 0 ? Math.min(natural, this.maxHeightCap) : natural;
2957
2953
  if (typeof window === "undefined") return capped;
2958
- const viewport = vertical ? window.innerHeight : window.innerWidth;
2954
+ const viewport = this.containingExtent(vertical);
2959
2955
  return viewport > 0 ? Math.min(capped, viewport) : capped;
2960
2956
  }
2957
+ // The fit cap is the sheet's containing block, not always the window: a
2958
+ // position:fixed sheet nested under a transform/filter/will-change ancestor
2959
+ // is clipped to that ancestor, so capping by window.innerHeight oversizes the
2960
+ // sheet and the content/footer get clipped. offsetParent resolves to that
2961
+ // ancestor when present, and is null (→ viewport) for a plain fixed sheet.
2962
+ containingExtent(vertical) {
2963
+ const op = this.element.offsetParent;
2964
+ if (op && op !== document.body && op !== document.documentElement) {
2965
+ return vertical ? op.clientHeight : op.clientWidth;
2966
+ }
2967
+ return vertical ? window.innerHeight : window.innerWidth;
2968
+ }
2961
2969
  installFitObserver() {
2962
2970
  if (typeof ResizeObserver === "undefined") return;
2963
2971
  if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
@@ -3038,6 +3046,10 @@ var BottomSheetEngine = class {
3038
3046
  this.backdrop.style.opacity = "";
3039
3047
  this.backdrop.style.pointerEvents = "";
3040
3048
  }
3049
+ if (this.anchorHost) {
3050
+ this.anchorHost.style.removeProperty("--bs-size");
3051
+ this.anchorHost.style.removeProperty("--bs-progress");
3052
+ }
3041
3053
  if (this.screenComponent) {
3042
3054
  this.screenComponent.style.opacity = "";
3043
3055
  this.screenComponent.style.display = "";
@@ -3049,6 +3061,7 @@ var BottomSheetEngine = class {
3049
3061
  this.bus.emit(event, payload);
3050
3062
  }
3051
3063
  newCycle() {
3064
+ this.allowOvershoot = false;
3052
3065
  this.currentAbort.abort();
3053
3066
  this.currentAbort = new AbortController();
3054
3067
  return this.currentAbort.signal;
@@ -3114,6 +3127,7 @@ var BottomSheetEngine = class {
3114
3127
  seen.add(p.id);
3115
3128
  }
3116
3129
  this.snaps.setRaw(this.snapPointsRaw);
3130
+ this.clampToMaxHeight();
3117
3131
  this.scrim.invalidateOpacityCache();
3118
3132
  }
3119
3133
  getAllowedRange() {
@@ -3320,9 +3334,13 @@ var BottomSheetEngine = class {
3320
3334
  this.opening = true;
3321
3335
  sheetStack.promote(this.id);
3322
3336
  }
3323
- void this.animation.animateTo(target.size, velocity).then(() => {
3324
- this.opening = false;
3337
+ const settleCap = this.snaps.getMaxAxisSize();
3338
+ const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3339
+ this.allowOvershoot = true;
3340
+ void this.animation.animateTo(settleSize, velocity).then(() => {
3325
3341
  if (signal.aborted) return;
3342
+ this.allowOvershoot = false;
3343
+ this.opening = false;
3326
3344
  this.scrollCache.restore(target.id, previousSize, target.size);
3327
3345
  this.updateAriaSlider();
3328
3346
  this.emit("snap", {
@@ -3354,7 +3372,7 @@ var BottomSheetEngine = class {
3354
3372
  }
3355
3373
  applySize(size) {
3356
3374
  const cap = this.snaps.getMaxAxisSize();
3357
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging ? cap : size;
3375
+ const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3358
3376
  this.size = clamped;
3359
3377
  const offset = cap - clamped;
3360
3378
  const style = this.element.style;
@@ -3576,6 +3594,18 @@ function useBottomSheet(opts) {
3576
3594
  engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3577
3595
  }
3578
3596
  }, [opts.backdropOpacity]);
3597
+ react.useEffect(() => {
3598
+ if (!engineRef.current) return;
3599
+ if (opts.radius !== void 0) {
3600
+ engineRef.current.setRadius(opts.radius);
3601
+ }
3602
+ }, [opts.radius]);
3603
+ react.useEffect(() => {
3604
+ if (!engineRef.current) return;
3605
+ if (opts.maxHeight !== void 0) {
3606
+ engineRef.current.setMaxHeight(opts.maxHeight);
3607
+ }
3608
+ }, [opts.maxHeight]);
3579
3609
  const subscribe = react.useCallback((fn) => {
3580
3610
  subscribersRef.current.add(fn);
3581
3611
  return () => subscribersRef.current.delete(fn);
package/dist/react.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, p as ScrimStageDef, e as SheetEventMap, C as CloseReason, q as ScrimUpdate, m as ScrimOverlayOptions, B as BottomSheetEngine, a as SnapPointDef, b as SheetMode } from './BottomSheetEngine-CqiCz64W.cjs';
2
- export { d as SnapPoint } from './BottomSheetEngine-CqiCz64W.cjs';
1
+ import { c as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, p as ScrimStageDef, e as SheetEventMap, C as CloseReason, q as ScrimUpdate, m as ScrimOverlayOptions, B as BottomSheetEngine, a as SnapPointDef, b as SheetMode } from './BottomSheetEngine-7BDK2OWX.cjs';
2
+ export { d as SnapPoint } from './BottomSheetEngine-7BDK2OWX.cjs';
3
3
  import { OverlayOptions, OverlayState, Overlay as OverlayEngine } from './overlay.cjs';
4
4
  export { OverlayEdge } from './overlay.cjs';
5
5
  import * as react from 'react';
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, p as ScrimStageDef, e as SheetEventMap, C as CloseReason, q as ScrimUpdate, m as ScrimOverlayOptions, B as BottomSheetEngine, a as SnapPointDef, b as SheetMode } from './BottomSheetEngine-BHjKFtnM.js';
2
- export { d as SnapPoint } from './BottomSheetEngine-BHjKFtnM.js';
1
+ import { c as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, p as ScrimStageDef, e as SheetEventMap, C as CloseReason, q as ScrimUpdate, m as ScrimOverlayOptions, B as BottomSheetEngine, a as SnapPointDef, b as SheetMode } from './BottomSheetEngine-BFTb0-4Z.js';
2
+ export { d as SnapPoint } from './BottomSheetEngine-BFTb0-4Z.js';
3
3
  import { OverlayOptions, OverlayState, Overlay as OverlayEngine } from './overlay.js';
4
4
  export { OverlayEdge } from './overlay.js';
5
5
  import * as react from 'react';
package/dist/react.js CHANGED
@@ -2365,6 +2365,7 @@ var BottomSheetEngine = class {
2365
2365
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2366
2366
  __publicField(this, "isTopSheet", true);
2367
2367
  __publicField(this, "opening", false);
2368
+ __publicField(this, "allowOvershoot", false);
2368
2369
  __publicField(this, "progressPayload", {
2369
2370
  value: 0,
2370
2371
  size: 0
@@ -2917,45 +2918,52 @@ var BottomSheetEngine = class {
2917
2918
  }
2918
2919
  return out;
2919
2920
  }
2920
- measureContentExtent(vertical) {
2921
+ measureSheetNatural(vertical) {
2922
+ const el = this.element;
2921
2923
  const content = this.scrollContainer;
2922
- if (!content) return 0;
2923
- const targets = this.contentTargets();
2924
- if (targets.length === 0) {
2925
- return vertical ? content.scrollHeight : content.scrollWidth;
2926
- }
2927
- let min = Infinity;
2928
- let max = -Infinity;
2929
- for (const el of targets) {
2930
- const r = el.getBoundingClientRect();
2931
- if (vertical) {
2932
- if (r.top < min) min = r.top;
2933
- if (r.bottom > max) max = r.bottom;
2934
- } else {
2935
- if (r.left < min) min = r.left;
2936
- if (r.right > max) max = r.right;
2937
- }
2938
- }
2939
- if (!isFinite(min) || !isFinite(max) || max <= min) {
2940
- return vertical ? content.scrollHeight : content.scrollWidth;
2941
- }
2942
- let pad = 0;
2943
- if (typeof getComputedStyle !== "undefined") {
2944
- const cs = getComputedStyle(content);
2945
- pad = vertical ? (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0) : (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0);
2946
- }
2947
- return max - min + pad;
2924
+ if (!content) {
2925
+ return vertical ? el.offsetHeight : el.offsetWidth;
2926
+ }
2927
+ const axis = vertical ? "height" : "width";
2928
+ const sheetStyle = el.style;
2929
+ const cs = content.style;
2930
+ const prevSheet = sheetStyle[axis];
2931
+ const prevFlex = cs.flex;
2932
+ const prevContent = vertical ? cs.height : cs.width;
2933
+ sheetStyle[axis] = "auto";
2934
+ cs.flex = "none";
2935
+ if (vertical) cs.height = "auto";
2936
+ else cs.width = "auto";
2937
+ const poked = vertical ? el.offsetHeight : el.offsetWidth;
2938
+ const contentBox = vertical ? content.clientHeight : content.clientWidth;
2939
+ const contentScroll = vertical ? content.scrollHeight : content.scrollWidth;
2940
+ const natural = Math.max(poked, poked - contentBox + contentScroll);
2941
+ sheetStyle[axis] = prevSheet;
2942
+ cs.flex = prevFlex;
2943
+ if (vertical) cs.height = prevContent;
2944
+ else cs.width = prevContent;
2945
+ return natural;
2948
2946
  }
2949
2947
  measureFitSize() {
2950
2948
  const vertical = layoutAxis(this.mode) === "height";
2951
- const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
2952
- const content = this.scrollContainer;
2953
- const natural = content ? handleSize + this.measureContentExtent(vertical) : handleSize;
2949
+ const natural = this.measureSheetNatural(vertical);
2954
2950
  const capped = this.maxHeightCap !== void 0 ? Math.min(natural, this.maxHeightCap) : natural;
2955
2951
  if (typeof window === "undefined") return capped;
2956
- const viewport = vertical ? window.innerHeight : window.innerWidth;
2952
+ const viewport = this.containingExtent(vertical);
2957
2953
  return viewport > 0 ? Math.min(capped, viewport) : capped;
2958
2954
  }
2955
+ // The fit cap is the sheet's containing block, not always the window: a
2956
+ // position:fixed sheet nested under a transform/filter/will-change ancestor
2957
+ // is clipped to that ancestor, so capping by window.innerHeight oversizes the
2958
+ // sheet and the content/footer get clipped. offsetParent resolves to that
2959
+ // ancestor when present, and is null (→ viewport) for a plain fixed sheet.
2960
+ containingExtent(vertical) {
2961
+ const op = this.element.offsetParent;
2962
+ if (op && op !== document.body && op !== document.documentElement) {
2963
+ return vertical ? op.clientHeight : op.clientWidth;
2964
+ }
2965
+ return vertical ? window.innerHeight : window.innerWidth;
2966
+ }
2959
2967
  installFitObserver() {
2960
2968
  if (typeof ResizeObserver === "undefined") return;
2961
2969
  if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
@@ -3036,6 +3044,10 @@ var BottomSheetEngine = class {
3036
3044
  this.backdrop.style.opacity = "";
3037
3045
  this.backdrop.style.pointerEvents = "";
3038
3046
  }
3047
+ if (this.anchorHost) {
3048
+ this.anchorHost.style.removeProperty("--bs-size");
3049
+ this.anchorHost.style.removeProperty("--bs-progress");
3050
+ }
3039
3051
  if (this.screenComponent) {
3040
3052
  this.screenComponent.style.opacity = "";
3041
3053
  this.screenComponent.style.display = "";
@@ -3047,6 +3059,7 @@ var BottomSheetEngine = class {
3047
3059
  this.bus.emit(event, payload);
3048
3060
  }
3049
3061
  newCycle() {
3062
+ this.allowOvershoot = false;
3050
3063
  this.currentAbort.abort();
3051
3064
  this.currentAbort = new AbortController();
3052
3065
  return this.currentAbort.signal;
@@ -3112,6 +3125,7 @@ var BottomSheetEngine = class {
3112
3125
  seen.add(p.id);
3113
3126
  }
3114
3127
  this.snaps.setRaw(this.snapPointsRaw);
3128
+ this.clampToMaxHeight();
3115
3129
  this.scrim.invalidateOpacityCache();
3116
3130
  }
3117
3131
  getAllowedRange() {
@@ -3318,9 +3332,13 @@ var BottomSheetEngine = class {
3318
3332
  this.opening = true;
3319
3333
  sheetStack.promote(this.id);
3320
3334
  }
3321
- void this.animation.animateTo(target.size, velocity).then(() => {
3322
- this.opening = false;
3335
+ const settleCap = this.snaps.getMaxAxisSize();
3336
+ const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3337
+ this.allowOvershoot = true;
3338
+ void this.animation.animateTo(settleSize, velocity).then(() => {
3323
3339
  if (signal.aborted) return;
3340
+ this.allowOvershoot = false;
3341
+ this.opening = false;
3324
3342
  this.scrollCache.restore(target.id, previousSize, target.size);
3325
3343
  this.updateAriaSlider();
3326
3344
  this.emit("snap", {
@@ -3352,7 +3370,7 @@ var BottomSheetEngine = class {
3352
3370
  }
3353
3371
  applySize(size) {
3354
3372
  const cap = this.snaps.getMaxAxisSize();
3355
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging ? cap : size;
3373
+ const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3356
3374
  this.size = clamped;
3357
3375
  const offset = cap - clamped;
3358
3376
  const style = this.element.style;
@@ -3574,6 +3592,18 @@ function useBottomSheet(opts) {
3574
3592
  engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3575
3593
  }
3576
3594
  }, [opts.backdropOpacity]);
3595
+ useEffect(() => {
3596
+ if (!engineRef.current) return;
3597
+ if (opts.radius !== void 0) {
3598
+ engineRef.current.setRadius(opts.radius);
3599
+ }
3600
+ }, [opts.radius]);
3601
+ useEffect(() => {
3602
+ if (!engineRef.current) return;
3603
+ if (opts.maxHeight !== void 0) {
3604
+ engineRef.current.setMaxHeight(opts.maxHeight);
3605
+ }
3606
+ }, [opts.maxHeight]);
3577
3607
  const subscribe = useCallback((fn) => {
3578
3608
  subscribersRef.current.add(fn);
3579
3609
  return () => subscribersRef.current.delete(fn);