@rcarls/rc-bottom-sheet 0.4.2 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @rcarls/rc-bottom-sheet
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Patch Changes
6
+
7
+ - ba65db7: Fix an edge case left in the 0.4.2 resize-repin fix: the window-resize re-pin re-measured
8
+ via `getBoundingClientRect()` on the box that was still carrying its _previous_ inline pin,
9
+ so a fixed (non-`dvh`) snap point (for example a `132px` peek height, common for a
10
+ persistent summary sheet) computed the exact same, now-stale target, since neither the
11
+ requested height nor the stale box's edges had changed. Only `dvh`-based snap points were
12
+ actually re-deriving correctly. Now clears the previous `top`/`height` pin before
13
+ re-measuring on resize, so the dialog reverts to its CSS-driven (`inset-block-end`-anchored)
14
+ position first, the same as a sheet that has never been pinned. Added a regression test
15
+ covering this specific case (confirmed it fails without the fix).
16
+ - Updated dependencies [4ae2ef0]
17
+ - Updated dependencies [689340c]
18
+ - @rcarls/rc-common@0.5.0
19
+ - @rcarls/rc-dialog@0.5.0
20
+
3
21
  ## 0.4.2
4
22
 
5
23
  ### Patch Changes
@@ -1,7 +1,7 @@
1
1
  import { property as h } from "lit/decorators.js";
2
2
  import { RCDialog as b } from "@rcarls/rc-dialog";
3
3
  import { findExtremeSnapIndex as g, findNearestSnapIndex as y } from "@rcarls/rc-common";
4
- var _ = Object.defineProperty, c = (o, t, e, n) => {
4
+ var _ = Object.defineProperty, p = (o, t, e, n) => {
5
5
  for (var i = void 0, s = o.length - 1, r; s >= 0; s--)
6
6
  (r = o[s]) && (i = r(t, e, i) || i);
7
7
  return i && _(t, e, i), i;
@@ -68,7 +68,10 @@ var a;
68
68
  const l = (a = class extends b {
69
69
  constructor() {
70
70
  super(...arguments), this.lightDismiss = !0, this.resize = "vertical", this.resizeOrigin = "top", this.resizeHandle = "[data-rc-bottom-sheet-handle]", this.snapPoints = "", this.swipeDismiss = !0, this.swipeVelocity = 500, this._reducedMotion = typeof window < "u" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null, this._activeSnapAnimation = null, this._lastSnapIndex = null, this._onWindowResize = () => {
71
- !this.open || this._lastSnapIndex === null || this._snapToIndex(this._lastSnapIndex, "instant", "api");
71
+ if (!this.open || this._lastSnapIndex === null)
72
+ return;
73
+ const t = this._$dialog;
74
+ t?.style.removeProperty("top"), t?.style.removeProperty("height"), t?.style.removeProperty("inset-block-start"), this._snapToIndex(this._lastSnapIndex, "instant", "api");
72
75
  };
73
76
  }
74
77
  static _ensureBaseStyles(t) {
@@ -128,8 +131,8 @@ const l = (a = class extends b {
128
131
  const i = this._$dialog, s = this._resolvedSnapPoints();
129
132
  if (!i || s.length === 0)
130
133
  return;
131
- const r = z(i), p = Math.max(0, Math.min(t, s.length - 1)), m = s[p];
132
- this._lastSnapIndex = p, this._activeSnapAnimation?.cancel(), this._activeSnapAnimation = null, i.style.height = `${m}px`;
134
+ const r = z(i), c = Math.max(0, Math.min(t, s.length - 1)), m = s[c];
135
+ this._lastSnapIndex = c, this._activeSnapAnimation?.cancel(), this._activeSnapAnimation = null, i.style.height = `${m}px`;
133
136
  const d = i.getBoundingClientRect().height;
134
137
  i.style.top = `${r.top}px`, i.style.height = `${r.height}px`;
135
138
  const u = r.bottom - d;
@@ -137,7 +140,7 @@ const l = (a = class extends b {
137
140
  new CustomEvent("rc-bottom-sheet-snap", {
138
141
  bubbles: !0,
139
142
  composed: !0,
140
- detail: { index: p, height: d, trigger: n }
143
+ detail: { index: c, height: d, trigger: n }
141
144
  })
142
145
  );
143
146
  }
@@ -196,29 +199,29 @@ const l = (a = class extends b {
196
199
  return n.remove(), i;
197
200
  }
198
201
  }, a.swipeDismissThreshold = 96, a._styledRoots = /* @__PURE__ */ new Set(), a);
199
- c([
202
+ p([
200
203
  h({ type: Boolean, attribute: "light-dismiss" })
201
204
  ], l.prototype, "lightDismiss");
202
- c([
205
+ p([
203
206
  h({ type: String, reflect: !0 })
204
207
  ], l.prototype, "resize");
205
- c([
208
+ p([
206
209
  h({ type: String, attribute: "resize-origin" })
207
210
  ], l.prototype, "resizeOrigin");
208
- c([
211
+ p([
209
212
  h({ type: String, attribute: "resize-handle" })
210
213
  ], l.prototype, "resizeHandle");
211
- c([
214
+ p([
212
215
  h({ type: String, attribute: "snap-points" })
213
216
  ], l.prototype, "snapPoints");
214
- c([
217
+ p([
215
218
  h({ type: Boolean, attribute: "swipe-dismiss" })
216
219
  ], l.prototype, "swipeDismiss");
217
- c([
220
+ p([
218
221
  h({ type: Number, attribute: "swipe-velocity" })
219
222
  ], l.prototype, "swipeVelocity");
220
- let I = l;
223
+ let C = l;
221
224
  export {
222
- I as R
225
+ C as R
223
226
  };
224
- //# sourceMappingURL=rc-bottom-sheet-CR2usG4R.js.map
227
+ //# sourceMappingURL=rc-bottom-sheet-C_FNJ-1w.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-bottom-sheet-C_FNJ-1w.js","sources":["../src/rc-bottom-sheet.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\n\nimport { RCDialog } from '@rcarls/rc-dialog';\nimport {\n findExtremeSnapIndex,\n findNearestSnapIndex,\n type ResizeDirection,\n type ResizeLifecycleDetail,\n type ResizeOrigin,\n} from '@rcarls/rc-common';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-bottom-sheet': RCBottomSheet;\n }\n\n interface HTMLElementEventMap {\n 'rc-bottom-sheet-snap': CustomEvent<RCBottomSheetSnapDetail>;\n }\n}\n\n/** Detail shape for `rc-bottom-sheet-snap`. */\nexport interface RCBottomSheetSnapDetail {\n /** Index into the resolved `snap-points` list selected as the target. */\n index: number;\n\n /** Effective target height in pixels, after CSS size constraints. */\n height: number;\n\n /** Whether the snap came from a drag release or a `snapTo()` call. */\n trigger: 'drag' | 'api';\n}\n\nconst DEFAULT_SNAP_DURATION_MS = 300;\nconst DEFAULT_SNAP_EASING = 'cubic-bezier(0.4, 0, 0.2, 1)';\n\nconst LIGHT_DOM_CSS = `\n@layer rc-base {\n rc-bottom-sheet > dialog {\n position: var(--rc-bottom-sheet-position, fixed);\n inset-block-start: auto;\n inset-block-end: var(--rc-bottom-sheet-inset-block-end, 0px);\n inset-inline: var(--rc-bottom-sheet-inset-inline, 0px);\n box-sizing: border-box;\n inline-size: 100%;\n max-inline-size: var(--rc-bottom-sheet-max-inline-size, 40rem);\n max-block-size: var(--rc-bottom-sheet-max-block-size, 70dvh);\n margin-block: 0;\n margin-inline: auto;\n overflow: auto;\n }\n\n rc-bottom-sheet > dialog [data-rc-bottom-sheet-handle] {\n box-sizing: border-box;\n cursor: ns-resize;\n touch-action: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n rc-bottom-sheet > dialog > [data-rc-bottom-sheet-handle] {\n position: relative;\n display: block;\n inline-size: var(--rc-bottom-sheet-handle-target-inline-size, 100%);\n min-block-size: var(--rc-bottom-sheet-handle-target-block-size, 3rem);\n margin: var(--rc-bottom-sheet-handle-target-margin, 0);\n padding: 0;\n border: 0;\n border-radius: inherit;\n background: transparent;\n appearance: none;\n }\n\n rc-bottom-sheet > dialog > [data-rc-bottom-sheet-handle]::before {\n content: '';\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n inline-size: var(--rc-bottom-sheet-handle-inline-size, 2rem);\n block-size: var(--rc-bottom-sheet-handle-block-size, 0.25rem);\n border-radius: var(--rc-bottom-sheet-handle-radius, 999px);\n background: var(--rc-bottom-sheet-handle-color, GrayText);\n translate: -50% -50%;\n }\n\n rc-bottom-sheet > dialog [data-rc-bottom-sheet-handle]:focus-visible {\n outline: auto;\n outline-offset: -0.25rem;\n }\n}\n`;\n\n// Floor so a noisy near-zero-distance release can't misread as a decisive\n// fling; a real swipe travels at least this far even if it's very quick.\nconst MIN_SWIPE_DISTANCE = 24;\n\n/**\n * Pins only the block-axis (top/height) geometry as inline styles, leaving\n * the inline axis (left/width) to CSS. `@rcarls/rc-common`'s\n * `pinElementBox` pins all four edges — appropriate for its own drag-resize\n * gesture math, where the inline axis can genuinely change — but a bottom\n * sheet only ever resizes/repositions along the block axis; its own\n * `LIGHT_DOM_CSS` deliberately leaves the inline axis owned by\n * `inset-inline`/`inline-size` custom properties (e.g. so a host page can\n * confine a sheet to a layout pane narrower than the viewport). Pinning\n * `left`/`width` here would freeze whatever the *unconfined* box happened\n * to measure at pin time as an inline style, which — having higher\n * cascade priority than any stylesheet rule — permanently overrides that\n * confinement regardless of specificity or layers.\n */\nfunction pinBlockBox(target: HTMLElement): DOMRect {\n const rect = target.getBoundingClientRect();\n\n if (getComputedStyle(target).position === 'static') {\n target.style.position = 'fixed';\n }\n\n target.style.translate = 'none';\n target.style.insetBlockStart = 'auto';\n target.style.marginBlock = '0';\n target.style.boxSizing = 'border-box';\n target.style.top = `${rect.top}px`;\n target.style.height = `${rect.height}px`;\n\n return rect;\n}\n\n/**\n * Modal bottom-sheet wrapper for a native `<dialog>`, built on `rc-dialog`.\n *\n * The direct child `<dialog>` remains the semantic dialog surface and owns\n * content, labels, and form behavior. `rc-bottom-sheet` supplies the same\n * open/close lifecycle, focus restoration, cancel/request-close events, and\n * light-dismiss affordance as `rc-dialog`. The component docks the sheet to\n * the viewport block-end by default, while themes provide its visual surface.\n *\n * Dragging the resize handle past a decisive velocity in either direction\n * (a \"swipe\" or \"fling,\" distinct from a slow deliberate drag) snaps\n * straight to the extreme `snap-points` entry in that direction — swipe up\n * to fully expand, swipe down to fully collapse — regardless of how close\n * the release position was to some other point. Below that velocity, the\n * sheet settles to whichever declared snap point is nearest the release\n * height. All settling, whether from a drag or from `snapTo()`, animates\n * over `--rc-bottom-sheet-snap-duration`, follows\n * `--rc-bottom-sheet-snap-easing`, and is skipped for\n * `prefers-reduced-motion: reduce`.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-bottom-sheet rc-bottom-sheet docs}\n * @see {@link https://m3.material.io/components/bottom-sheets/overview Material Design bottom sheets}\n *\n * @slot - Place a `<dialog>` element with the sheet content and optional\n * `[data-rc-bottom-sheet-handle]` resize handle here.\n *\n * @fires rc-dialog-open - Inherited from `rc-dialog`; fired when the sheet opens.\n * @fires rc-dialog-toggle - Inherited from `rc-dialog`; fired when user/native interaction changes open state.\n * @fires rc-dialog-request-close - Inherited from `rc-dialog`; cancelable close request.\n * @fires rc-dialog-cancel - Inherited from `rc-dialog`; backward-compatible cancel alias.\n * @fires rc-dialog-close - Inherited from `rc-dialog`; fired after the sheet closes.\n * @fires rc-bottom-sheet-snap - Fires when a drag release or `snapTo()` call\n * selects a snap target. `detail: { index, height, trigger }`\n *\n * @attr light-dismiss - When present, a click on the backdrop area calls `requestClose()`.\n * Present by default; inherited from `rc-dialog`.\n * @attr resize - Enables edge resizing, mirroring the CSS `resize` property values.\n * Defaults to `vertical`; inherited from `rc-dialog`.\n * @attr resize-origin - Origin for resize edge hit-testing or explicit handles. Defaults to\n * `top`; inherited from `rc-dialog`.\n * @attr resize-handle - CSS selector, scoped to the inner `<dialog>`, for explicit resize\n * handles. Defaults to `[data-rc-bottom-sheet-handle]`; inherited from `rc-dialog`.\n * @attr snap-points - Whitespace-separated CSS heights, in ascending order, addressable by\n * `snapTo()` and settled to on drag release.\n * @attr swipe-dismiss - Whether a decisive downward swipe of at least 96 pixels requests close.\n * @attr swipe-velocity - Minimum release velocity, in pixels per second, that counts as a\n * decisive swipe rather than a slow deliberate drag.\n *\n * @cssprop [--rc-bottom-sheet-bg=Canvas] - Sheet surface background.\n * @cssprop [--rc-bottom-sheet-color=CanvasText] - Sheet text color.\n * @cssprop [--rc-bottom-sheet-radius=1rem 1rem 0 0] - Sheet corner radius.\n * @cssprop [--rc-bottom-sheet-shadow=none] - Sheet elevation shadow.\n * @cssprop [--rc-bottom-sheet-position=fixed] - Positioning scheme. Use `absolute` with `show()` to dock a non-modal sheet to a positioned ancestor.\n * @cssprop [--rc-bottom-sheet-inset-block-end=0px] - Distance from the positioning container's block-end edge.\n * @cssprop [--rc-bottom-sheet-inset-inline=0px] - Inline-axis inset used with automatic margins.\n * @cssprop [--rc-bottom-sheet-max-inline-size=40rem] - Maximum sheet width.\n * @cssprop [--rc-bottom-sheet-max-block-size=70dvh] - Maximum sheet height.\n * @cssprop [--rc-bottom-sheet-padding=0 1rem 1rem] - Sheet padding.\n * @cssprop [--rc-bottom-sheet-scrim=var(--rc-dialog-scrim)] - Modal backdrop color.\n * @cssprop [--rc-bottom-sheet-handle-color=GrayText] - Resize handle color.\n * @cssprop [--rc-bottom-sheet-handle-inline-size=2rem] - Resize handle width.\n * @cssprop [--rc-bottom-sheet-handle-block-size=0.25rem] - Resize handle height.\n * @cssprop [--rc-bottom-sheet-handle-radius=999px] - Resize handle corner radius.\n * @cssprop [--rc-bottom-sheet-handle-target-inline-size=100%] - Pointer target width for a direct-child handle.\n * @cssprop [--rc-bottom-sheet-handle-target-block-size=3rem] - Pointer target height for a direct-child handle.\n * @cssprop [--rc-bottom-sheet-handle-target-margin=0] - Margin around a direct-child handle target.\n * @cssprop [--rc-bottom-sheet-snap-duration=300ms] - Duration of the settle animation\n * after a drag release or `snapTo()` call.\n * @cssprop [--rc-bottom-sheet-snap-easing=cubic-bezier(0.4, 0, 0.2, 1)] - Easing\n * for the settle animation after a drag release or `snapTo()` call.\n */\nexport class RCBottomSheet extends RCDialog {\n private static readonly swipeDismissThreshold = 96;\n private static readonly _styledRoots = new Set<Document | ShadowRoot>();\n\n private static _ensureBaseStyles(root: Document | ShadowRoot): void {\n if (RCBottomSheet._styledRoots.has(root)) {\n return;\n }\n\n RCBottomSheet._styledRoots.add(root);\n\n const $style = document.createElement('style');\n\n $style.setAttribute('data-rc-light-dom-base', 'rc-bottom-sheet');\n $style.textContent = LIGHT_DOM_CSS;\n\n if (root instanceof Document) {\n root.head.append($style);\n } else {\n root.append($style);\n }\n }\n\n /**\n * Bottom sheets light-dismiss by default.\n *\n * Set `light-dismiss=\"false\"` as a property from JavaScript when a sheet must\n * be closed only by explicit actions.\n */\n @property({ type: Boolean, attribute: 'light-dismiss' })\n override lightDismiss = true;\n\n /** Bottom sheets resize vertically from the top edge by default. */\n @property({ type: String, reflect: true })\n override resize: ResizeDirection = 'vertical';\n\n /** Top-origin resizing keeps the sheet docked to the viewport bottom. */\n @property({ type: String, attribute: 'resize-origin' })\n override resizeOrigin: ResizeOrigin = 'top';\n\n /** Default optional handle selector. Falls back to top-edge resize when absent. */\n @property({ type: String, attribute: 'resize-handle' })\n override resizeHandle = '[data-rc-bottom-sheet-handle]';\n\n /**\n * Whitespace-separated CSS heights in ascending order.\n *\n * Each height becomes an addressable snap target. Slow releases choose the\n * nearest target, while decisive swipes choose the first or last target.\n */\n @property({ type: String, attribute: 'snap-points' })\n snapPoints = '';\n\n /**\n * Whether a downward pointer resize of at least 96 pixels requests close.\n *\n * When false, a decisive downward swipe settles at the first snap point.\n */\n @property({ type: Boolean, attribute: 'swipe-dismiss' })\n swipeDismiss = true;\n\n /**\n * Minimum release velocity, in pixels per second, that counts as a decisive\n * swipe rather than a slow deliberate drag. A swipe jumps straight to the\n * extreme `snap-points` entry in its direction; below this, the release\n * settles to the nearest point instead.\n */\n @property({ type: Number, attribute: 'swipe-velocity' })\n swipeVelocity = 500;\n\n private readonly _reducedMotion =\n typeof window !== 'undefined' ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;\n\n private _activeSnapAnimation: Animation | null = null;\n\n // Snap-point heights are resolved from the *current* viewport at the\n // moment a snap is applied (dvh units, or a probe-element measurement for\n // other CSS units) and then frozen as inline styles by pinBlockBox — nothing\n // re-derives them afterward. A window resize/maximize/restore while a\n // sheet is open (without the user dragging it) otherwise leaves it pinned\n // to stale pre-resize geometry. Track the last-applied index so a resize\n // can re-run the same snap against fresh geometry.\n private _lastSnapIndex: number | null = null;\n private readonly _onWindowResize = (): void => {\n if (!this.open || this._lastSnapIndex === null) {\n return;\n }\n\n // _snapToIndex re-measures via pinBlockBox's getBoundingClientRect(),\n // but that reads back whatever is *currently rendered* — the stale\n // pin from before this resize, still anchored to the old viewport.\n // For a fixed-height snap point (no dvh in play) that stale box's\n // bottom edge is unchanged, so re-deriving from it would compute the\n // exact same (now wrong) target. Clear the previous pin first so the\n // dialog reverts to its CSS-driven position (inset-block-end-anchored)\n // before re-measuring, the same as an as-yet-never-snapped sheet.\n const $dialog = this._$dialog;\n\n $dialog?.style.removeProperty('top');\n $dialog?.style.removeProperty('height');\n $dialog?.style.removeProperty('inset-block-start');\n\n this._snapToIndex(this._lastSnapIndex, 'instant', 'api');\n };\n\n override connectedCallback(): void {\n super.connectedCallback();\n RCBottomSheet._ensureBaseStyles(this.getRootNode() as Document | ShadowRoot);\n window.addEventListener('resize', this._onWindowResize);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('resize', this._onWindowResize);\n }\n\n /**\n * Snaps to a declared target by zero-based index.\n *\n * Finite fractional indices are truncated and out-of-range indices clamp to\n * the nearest endpoint. Non-finite indices and calls without valid snap\n * points have no effect.\n *\n * @param index - zero-based index into `snapPoints`\n * @param behavior - whether to animate the movement\n *\n * @example\n * const sheet = document.querySelector('rc-bottom-sheet');\n * sheet.snapTo(1);\n */\n snapTo(index: number, behavior: 'animated' | 'instant' = 'animated'): void {\n if (!Number.isFinite(index)) {\n return;\n }\n\n this._snapToIndex(Math.trunc(index), behavior, 'api');\n }\n\n protected override _onResizeEnd(detail: ResizeLifecycleDetail): void {\n if (detail.inputType === 'pointer' && this.swipeDismiss && this._shouldSwipeDismiss(detail)) {\n this.requestClose();\n\n return;\n }\n\n const points = this._resolvedSnapPoints();\n const velocity = detail.edge.includes('n') ? -detail.velocityY : detail.velocityY;\n const distance = Math.abs(detail.height - detail.startHeight);\n\n if (\n detail.inputType === 'pointer' &&\n points.length > 0 &&\n distance >= MIN_SWIPE_DISTANCE &&\n Math.abs(velocity) >= this.swipeVelocity\n ) {\n // Decisive fling: jump to the extreme point in the swipe direction,\n // regardless of proximity to some other point. Height grows as the\n // sheet is dragged upward, so a positive velocity is a swipe up.\n const targetIndex = findExtremeSnapIndex(points, velocity > 0 ? 1 : -1);\n\n this._snapToIndex(targetIndex, 'animated', 'drag');\n\n return;\n }\n\n this._snapToNearestPoint('drag');\n }\n\n private _shouldSwipeDismiss(detail: ResizeLifecycleDetail): boolean {\n return (\n detail.edge.includes('n') &&\n detail.startHeight - detail.height >= RCBottomSheet.swipeDismissThreshold\n );\n }\n\n private _snapToNearestPoint(trigger: 'drag' | 'api' = 'api'): void {\n const $dialog = this._$dialog;\n const points = this._resolvedSnapPoints();\n\n if (!$dialog || points.length === 0) {\n return;\n }\n\n const height = $dialog.getBoundingClientRect().height;\n const nearestIndex = findNearestSnapIndex(points, height);\n\n this._snapToIndex(nearestIndex, 'animated', trigger);\n }\n\n private _snapToIndex(\n index: number,\n behavior: 'animated' | 'instant',\n trigger: 'drag' | 'api',\n ): void {\n const $dialog = this._$dialog;\n const points = this._resolvedSnapPoints();\n\n if (!$dialog || points.length === 0) {\n return;\n }\n\n // Pinning (border-box, explicit top/height) is normally established by\n // the first drag gesture; snapTo() must do the same so a sheet that has\n // never been dragged still measures and sets height consistently.\n const rect = pinBlockBox($dialog);\n const clampedIndex = Math.max(0, Math.min(index, points.length - 1));\n const requestedHeight = points[clampedIndex];\n\n this._lastSnapIndex = clampedIndex;\n\n // An in-flight animation wins over inline geometry in the cascade. Pin\n // its current visual box before cancelling so the constrained target can\n // be measured without jumping back to the prior snap point.\n this._activeSnapAnimation?.cancel();\n this._activeSnapAnimation = null;\n\n $dialog.style.height = `${requestedHeight}px`;\n\n const targetHeight = $dialog.getBoundingClientRect().height;\n\n // Restore the current visual box before applying or animating the target.\n // CSS min/max block-size can make targetHeight differ from the authored\n // snap point; anchoring from the effective height keeps the block-end edge\n // fixed in either case.\n $dialog.style.top = `${rect.top}px`;\n $dialog.style.height = `${rect.height}px`;\n\n const targetTop = rect.bottom - targetHeight;\n\n this._applySnap($dialog, targetTop, targetHeight, behavior);\n\n this.dispatchEvent(\n new CustomEvent<RCBottomSheetSnapDetail>('rc-bottom-sheet-snap', {\n bubbles: true,\n composed: true,\n detail: { index: clampedIndex, height: targetHeight, trigger },\n }),\n );\n }\n\n private _applySnap(\n $dialog: HTMLDialogElement,\n top: number,\n height: number,\n behavior: 'animated' | 'instant',\n ): void {\n // Measure before cancelling any in-flight animation. `_snapToIndex()`\n // normally pins and cancels first, while this ordering also keeps direct\n // calls robust if applying a snap is refactored later.\n const fromRect = $dialog.getBoundingClientRect();\n\n this._activeSnapAnimation?.cancel();\n this._activeSnapAnimation = null;\n\n if (behavior === 'instant' || this._reducedMotion?.matches) {\n $dialog.style.top = `${top}px`;\n $dialog.style.height = `${height}px`;\n\n return;\n }\n\n if (fromRect.top === top && fromRect.height === height) {\n return;\n }\n\n const animation = $dialog.animate(\n [\n { top: `${fromRect.top}px`, height: `${fromRect.height}px` },\n { top: `${top}px`, height: `${height}px` },\n ],\n {\n duration: this._snapDuration($dialog),\n easing: this._snapEasing($dialog),\n fill: 'forwards',\n },\n );\n\n this._activeSnapAnimation = animation;\n\n animation.finished\n .then(() => animation.commitStyles())\n .catch(() => {\n // Interrupted by a newer gesture or snapTo() call, which owns applying\n // its own final styles.\n })\n .finally(() => {\n animation.cancel();\n\n if (this._activeSnapAnimation === animation) {\n this._activeSnapAnimation = null;\n }\n });\n }\n\n private _snapDuration($dialog: HTMLDialogElement): number {\n const raw = getComputedStyle($dialog)\n .getPropertyValue('--rc-bottom-sheet-snap-duration')\n .trim();\n const parsed = Number.parseFloat(raw);\n\n return Number.isFinite(parsed) ? parsed : DEFAULT_SNAP_DURATION_MS;\n }\n\n private _snapEasing($dialog: HTMLDialogElement): string {\n return (\n getComputedStyle($dialog).getPropertyValue('--rc-bottom-sheet-snap-easing').trim() ||\n DEFAULT_SNAP_EASING\n );\n }\n\n private _resolvedSnapPoints(): number[] {\n if (!this.snapPoints.trim()) {\n return [];\n }\n\n return this.snapPoints\n .trim()\n .split(/\\s+/)\n .map((point) => this._resolveSnapPoint(point))\n .filter((point): point is number => Number.isFinite(point) && point > 0);\n }\n\n private _resolveSnapPoint(point: string): number {\n const numeric = Number.parseFloat(point);\n\n if (!Number.isFinite(numeric)) {\n return Number.NaN;\n }\n\n if (point.endsWith('%')) {\n return window.innerHeight * (numeric / 100);\n }\n\n if (point.endsWith('px') || String(numeric) === point) {\n return numeric;\n }\n\n const probe = document.createElement('div');\n\n Object.assign(probe.style, {\n position: 'fixed',\n visibility: 'hidden',\n pointerEvents: 'none',\n blockSize: point,\n inlineSize: '0',\n inset: '0 auto auto 0',\n });\n\n document.body.append(probe);\n\n const height = probe.getBoundingClientRect().height;\n\n probe.remove();\n\n return height;\n }\n}\n\nexport default RCBottomSheet;\n"],"names":["DEFAULT_SNAP_DURATION_MS","DEFAULT_SNAP_EASING","LIGHT_DOM_CSS","MIN_SWIPE_DISTANCE","pinBlockBox","target","rect","_RCBottomSheet","_a","RCDialog","$dialog","root","$style","index","behavior","detail","points","velocity","distance","targetIndex","findExtremeSnapIndex","trigger","height","nearestIndex","findNearestSnapIndex","clampedIndex","requestedHeight","targetHeight","targetTop","top","fromRect","animation","raw","parsed","point","numeric","probe","__decorateClass","property","RCBottomSheet"],"mappings":";;;;;;;;AAiCA,MAAMA,IAA2B,KAC3BC,IAAsB,gCAEtBC,IAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAyDhBC,IAAqB;AAgB3B,SAASC,EAAYC,GAA8B;AACjD,QAAMC,IAAOD,EAAO,sBAAA;AAEpB,SAAI,iBAAiBA,CAAM,EAAE,aAAa,aACxCA,EAAO,MAAM,WAAW,UAG1BA,EAAO,MAAM,YAAY,QACzBA,EAAO,MAAM,kBAAkB,QAC/BA,EAAO,MAAM,cAAc,KAC3BA,EAAO,MAAM,YAAY,cACzBA,EAAO,MAAM,MAAM,GAAGC,EAAK,GAAG,MAC9BD,EAAO,MAAM,SAAS,GAAGC,EAAK,MAAM,MAE7BA;AACT;;AAyEO,MAAMC,KAANC,IAAA,cAA4BC,EAAS;AAAA,EAArC,cAAA;AAAA,UAAA,GAAA,SAAA,GA8BL,KAAS,eAAe,IAIxB,KAAS,SAA0B,YAInC,KAAS,eAA6B,OAItC,KAAS,eAAe,iCASxB,KAAA,aAAa,IAQb,KAAA,eAAe,IASf,KAAA,gBAAgB,KAEhB,KAAiB,iBACf,OAAO,SAAW,MAAc,OAAO,WAAW,kCAAkC,IAAI,MAE1F,KAAQ,uBAAyC,MASjD,KAAQ,iBAAgC,MACxC,KAAiB,kBAAkB,MAAY;AAC7C,UAAI,CAAC,KAAK,QAAQ,KAAK,mBAAmB;AACxC;AAWF,YAAMC,IAAU,KAAK;AAErB,MAAAA,GAAS,MAAM,eAAe,KAAK,GACnCA,GAAS,MAAM,eAAe,QAAQ,GACtCA,GAAS,MAAM,eAAe,mBAAmB,GAEjD,KAAK,aAAa,KAAK,gBAAgB,WAAW,KAAK;AAAA,IACzD;AAAA,EAAA;AAAA,EAnGA,OAAe,kBAAkBC,GAAmC;AAClE,QAAIH,EAAc,aAAa,IAAIG,CAAI;AACrC;AAGF,IAAAH,EAAc,aAAa,IAAIG,CAAI;AAEnC,UAAMC,IAAS,SAAS,cAAc,OAAO;AAE7C,IAAAA,EAAO,aAAa,0BAA0B,iBAAiB,GAC/DA,EAAO,cAAcV,GAEjBS,aAAgB,WAClBA,EAAK,KAAK,OAAOC,CAAM,IAEvBD,EAAK,OAAOC,CAAM;AAAA,EAEtB;AAAA,EAoFS,oBAA0B;AACjC,UAAM,kBAAA,GACNJ,EAAc,kBAAkB,KAAK,aAAsC,GAC3E,OAAO,iBAAiB,UAAU,KAAK,eAAe;AAAA,EACxD;AAAA,EAES,uBAA6B;AACpC,UAAM,qBAAA,GACN,OAAO,oBAAoB,UAAU,KAAK,eAAe;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,OAAOK,GAAeC,IAAmC,YAAkB;AACzE,IAAK,OAAO,SAASD,CAAK,KAI1B,KAAK,aAAa,KAAK,MAAMA,CAAK,GAAGC,GAAU,KAAK;AAAA,EACtD;AAAA,EAEmB,aAAaC,GAAqC;AACnE,QAAIA,EAAO,cAAc,aAAa,KAAK,gBAAgB,KAAK,oBAAoBA,CAAM,GAAG;AAC3F,WAAK,aAAA;AAEL;AAAA,IACF;AAEA,UAAMC,IAAS,KAAK,oBAAA,GACdC,IAAWF,EAAO,KAAK,SAAS,GAAG,IAAI,CAACA,EAAO,YAAYA,EAAO,WAClEG,IAAW,KAAK,IAAIH,EAAO,SAASA,EAAO,WAAW;AAE5D,QACEA,EAAO,cAAc,aACrBC,EAAO,SAAS,KAChBE,KAAYf,KACZ,KAAK,IAAIc,CAAQ,KAAK,KAAK,eAC3B;AAIA,YAAME,IAAcC,EAAqBJ,GAAQC,IAAW,IAAI,IAAI,EAAE;AAEtE,WAAK,aAAaE,GAAa,YAAY,MAAM;AAEjD;AAAA,IACF;AAEA,SAAK,oBAAoB,MAAM;AAAA,EACjC;AAAA,EAEQ,oBAAoBJ,GAAwC;AAClE,WACEA,EAAO,KAAK,SAAS,GAAG,KACxBA,EAAO,cAAcA,EAAO,UAAUP,EAAc;AAAA,EAExD;AAAA,EAEQ,oBAAoBa,IAA0B,OAAa;AACjE,UAAMX,IAAU,KAAK,UACfM,IAAS,KAAK,oBAAA;AAEpB,QAAI,CAACN,KAAWM,EAAO,WAAW;AAChC;AAGF,UAAMM,IAASZ,EAAQ,sBAAA,EAAwB,QACzCa,IAAeC,EAAqBR,GAAQM,CAAM;AAExD,SAAK,aAAaC,GAAc,YAAYF,CAAO;AAAA,EACrD;AAAA,EAEQ,aACNR,GACAC,GACAO,GACM;AACN,UAAMX,IAAU,KAAK,UACfM,IAAS,KAAK,oBAAA;AAEpB,QAAI,CAACN,KAAWM,EAAO,WAAW;AAChC;AAMF,UAAMV,IAAOF,EAAYM,CAAO,GAC1Be,IAAe,KAAK,IAAI,GAAG,KAAK,IAAIZ,GAAOG,EAAO,SAAS,CAAC,CAAC,GAC7DU,IAAkBV,EAAOS,CAAY;AAE3C,SAAK,iBAAiBA,GAKtB,KAAK,sBAAsB,OAAA,GAC3B,KAAK,uBAAuB,MAE5Bf,EAAQ,MAAM,SAAS,GAAGgB,CAAe;AAEzC,UAAMC,IAAejB,EAAQ,sBAAA,EAAwB;AAMrD,IAAAA,EAAQ,MAAM,MAAM,GAAGJ,EAAK,GAAG,MAC/BI,EAAQ,MAAM,SAAS,GAAGJ,EAAK,MAAM;AAErC,UAAMsB,IAAYtB,EAAK,SAASqB;AAEhC,SAAK,WAAWjB,GAASkB,GAAWD,GAAcb,CAAQ,GAE1D,KAAK;AAAA,MACH,IAAI,YAAqC,wBAAwB;AAAA,QAC/D,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ,EAAE,OAAOW,GAAc,QAAQE,GAAc,SAAAN,EAAA;AAAA,MAAQ,CAC9D;AAAA,IAAA;AAAA,EAEL;AAAA,EAEQ,WACNX,GACAmB,GACAP,GACAR,GACM;AAIN,UAAMgB,IAAWpB,EAAQ,sBAAA;AAKzB,QAHA,KAAK,sBAAsB,OAAA,GAC3B,KAAK,uBAAuB,MAExBI,MAAa,aAAa,KAAK,gBAAgB,SAAS;AAC1D,MAAAJ,EAAQ,MAAM,MAAM,GAAGmB,CAAG,MAC1BnB,EAAQ,MAAM,SAAS,GAAGY,CAAM;AAEhC;AAAA,IACF;AAEA,QAAIQ,EAAS,QAAQD,KAAOC,EAAS,WAAWR;AAC9C;AAGF,UAAMS,IAAYrB,EAAQ;AAAA,MACxB;AAAA,QACE,EAAE,KAAK,GAAGoB,EAAS,GAAG,MAAM,QAAQ,GAAGA,EAAS,MAAM,KAAA;AAAA,QACtD,EAAE,KAAK,GAAGD,CAAG,MAAM,QAAQ,GAAGP,CAAM,KAAA;AAAA,MAAK;AAAA,MAE3C;AAAA,QACE,UAAU,KAAK,cAAcZ,CAAO;AAAA,QACpC,QAAQ,KAAK,YAAYA,CAAO;AAAA,QAChC,MAAM;AAAA,MAAA;AAAA,IACR;AAGF,SAAK,uBAAuBqB,GAE5BA,EAAU,SACP,KAAK,MAAMA,EAAU,cAAc,EACnC,MAAM,MAAM;AAAA,IAGb,CAAC,EACA,QAAQ,MAAM;AACb,MAAAA,EAAU,OAAA,GAEN,KAAK,yBAAyBA,MAChC,KAAK,uBAAuB;AAAA,IAEhC,CAAC;AAAA,EACL;AAAA,EAEQ,cAAcrB,GAAoC;AACxD,UAAMsB,IAAM,iBAAiBtB,CAAO,EACjC,iBAAiB,iCAAiC,EAClD,KAAA,GACGuB,IAAS,OAAO,WAAWD,CAAG;AAEpC,WAAO,OAAO,SAASC,CAAM,IAAIA,IAASjC;AAAA,EAC5C;AAAA,EAEQ,YAAYU,GAAoC;AACtD,WACE,iBAAiBA,CAAO,EAAE,iBAAiB,+BAA+B,EAAE,UAC5ET;AAAA,EAEJ;AAAA,EAEQ,sBAAgC;AACtC,WAAK,KAAK,WAAW,SAId,KAAK,WACT,OACA,MAAM,KAAK,EACX,IAAI,CAACiC,MAAU,KAAK,kBAAkBA,CAAK,CAAC,EAC5C,OAAO,CAACA,MAA2B,OAAO,SAASA,CAAK,KAAKA,IAAQ,CAAC,IAPhE,CAAA;AAAA,EAQX;AAAA,EAEQ,kBAAkBA,GAAuB;AAC/C,UAAMC,IAAU,OAAO,WAAWD,CAAK;AAEvC,QAAI,CAAC,OAAO,SAASC,CAAO;AAC1B,aAAO,OAAO;AAGhB,QAAID,EAAM,SAAS,GAAG;AACpB,aAAO,OAAO,eAAeC,IAAU;AAGzC,QAAID,EAAM,SAAS,IAAI,KAAK,OAAOC,CAAO,MAAMD;AAC9C,aAAOC;AAGT,UAAMC,IAAQ,SAAS,cAAc,KAAK;AAE1C,WAAO,OAAOA,EAAM,OAAO;AAAA,MACzB,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAWF;AAAA,MACX,YAAY;AAAA,MACZ,OAAO;AAAA,IAAA,CACR,GAED,SAAS,KAAK,OAAOE,CAAK;AAE1B,UAAMd,IAASc,EAAM,sBAAA,EAAwB;AAE7C,WAAAA,EAAM,OAAA,GAECd;AAAA,EACT;AACF,GAnWEd,EAAwB,wBAAwB,IAChDA,EAAwB,mCAAmB,IAAA,GAFtCA;AA8BI6B,EAAA;AAAA,EADRC,EAAS,EAAE,MAAM,SAAS,WAAW,iBAAiB;AAAA,GA7B5C/B,EA8BF,WAAA,cAAA;AAIA8B,EAAA;AAAA,EADRC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAjC9B/B,EAkCF,WAAA,QAAA;AAIA8B,EAAA;AAAA,EADRC,EAAS,EAAE,MAAM,QAAQ,WAAW,iBAAiB;AAAA,GArC3C/B,EAsCF,WAAA,cAAA;AAIA8B,EAAA;AAAA,EADRC,EAAS,EAAE,MAAM,QAAQ,WAAW,iBAAiB;AAAA,GAzC3C/B,EA0CF,WAAA,cAAA;AAST8B,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,WAAW,eAAe;AAAA,GAlDzC/B,EAmDX,WAAA,YAAA;AAQA8B,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,WAAW,iBAAiB;AAAA,GA1D5C/B,EA2DX,WAAA,cAAA;AASA8B,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,WAAW,kBAAkB;AAAA,GAnE5C/B,EAoEX,WAAA,eAAA;AApEK,IAAMgC,IAANhC;"}
@@ -1,4 +1,4 @@
1
- import { R as t } from "./rc-bottom-sheet-CR2usG4R.js";
1
+ import { R as t } from "./rc-bottom-sheet-C_FNJ-1w.js";
2
2
  customElements.get("rc-bottom-sheet") || customElements.define("rc-bottom-sheet", t);
3
3
  export {
4
4
  t as RCBottomSheet
@@ -1,4 +1,4 @@
1
- import { R as e } from "./rc-bottom-sheet-CR2usG4R.js";
1
+ import { R as e } from "./rc-bottom-sheet-C_FNJ-1w.js";
2
2
  export {
3
3
  e as RCBottomSheet
4
4
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.2",
6
+ "version": "0.5.0",
7
7
  "description": "Modal bottom-sheet wrapper for a native <dialog>.",
8
8
  "repository": {
9
9
  "type": "git",
@@ -45,8 +45,8 @@
45
45
  "test:browser:firefox": "vitest --run --project=firefox"
46
46
  },
47
47
  "dependencies": {
48
- "@rcarls/rc-common": "0.4.2",
49
- "@rcarls/rc-dialog": "0.4.2"
48
+ "@rcarls/rc-common": "0.5.0",
49
+ "@rcarls/rc-dialog": "0.5.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@custom-elements-manifest/analyzer": "0.11.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"rc-bottom-sheet-CR2usG4R.js","sources":["../src/rc-bottom-sheet.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\n\nimport { RCDialog } from '@rcarls/rc-dialog';\nimport {\n findExtremeSnapIndex,\n findNearestSnapIndex,\n type ResizeDirection,\n type ResizeLifecycleDetail,\n type ResizeOrigin,\n} from '@rcarls/rc-common';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-bottom-sheet': RCBottomSheet;\n }\n\n interface HTMLElementEventMap {\n 'rc-bottom-sheet-snap': CustomEvent<RCBottomSheetSnapDetail>;\n }\n}\n\n/** Detail shape for `rc-bottom-sheet-snap`. */\nexport interface RCBottomSheetSnapDetail {\n /** Index into the resolved `snap-points` list selected as the target. */\n index: number;\n\n /** Effective target height in pixels, after CSS size constraints. */\n height: number;\n\n /** Whether the snap came from a drag release or a `snapTo()` call. */\n trigger: 'drag' | 'api';\n}\n\nconst DEFAULT_SNAP_DURATION_MS = 300;\nconst DEFAULT_SNAP_EASING = 'cubic-bezier(0.4, 0, 0.2, 1)';\n\nconst LIGHT_DOM_CSS = `\n@layer rc-base {\n rc-bottom-sheet > dialog {\n position: var(--rc-bottom-sheet-position, fixed);\n inset-block-start: auto;\n inset-block-end: var(--rc-bottom-sheet-inset-block-end, 0px);\n inset-inline: var(--rc-bottom-sheet-inset-inline, 0px);\n box-sizing: border-box;\n inline-size: 100%;\n max-inline-size: var(--rc-bottom-sheet-max-inline-size, 40rem);\n max-block-size: var(--rc-bottom-sheet-max-block-size, 70dvh);\n margin-block: 0;\n margin-inline: auto;\n overflow: auto;\n }\n\n rc-bottom-sheet > dialog [data-rc-bottom-sheet-handle] {\n box-sizing: border-box;\n cursor: ns-resize;\n touch-action: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n rc-bottom-sheet > dialog > [data-rc-bottom-sheet-handle] {\n position: relative;\n display: block;\n inline-size: var(--rc-bottom-sheet-handle-target-inline-size, 100%);\n min-block-size: var(--rc-bottom-sheet-handle-target-block-size, 3rem);\n margin: var(--rc-bottom-sheet-handle-target-margin, 0);\n padding: 0;\n border: 0;\n border-radius: inherit;\n background: transparent;\n appearance: none;\n }\n\n rc-bottom-sheet > dialog > [data-rc-bottom-sheet-handle]::before {\n content: '';\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n inline-size: var(--rc-bottom-sheet-handle-inline-size, 2rem);\n block-size: var(--rc-bottom-sheet-handle-block-size, 0.25rem);\n border-radius: var(--rc-bottom-sheet-handle-radius, 999px);\n background: var(--rc-bottom-sheet-handle-color, GrayText);\n translate: -50% -50%;\n }\n\n rc-bottom-sheet > dialog [data-rc-bottom-sheet-handle]:focus-visible {\n outline: auto;\n outline-offset: -0.25rem;\n }\n}\n`;\n\n// Floor so a noisy near-zero-distance release can't misread as a decisive\n// fling; a real swipe travels at least this far even if it's very quick.\nconst MIN_SWIPE_DISTANCE = 24;\n\n/**\n * Pins only the block-axis (top/height) geometry as inline styles, leaving\n * the inline axis (left/width) to CSS. `@rcarls/rc-common`'s\n * `pinElementBox` pins all four edges — appropriate for its own drag-resize\n * gesture math, where the inline axis can genuinely change — but a bottom\n * sheet only ever resizes/repositions along the block axis; its own\n * `LIGHT_DOM_CSS` deliberately leaves the inline axis owned by\n * `inset-inline`/`inline-size` custom properties (e.g. so a host page can\n * confine a sheet to a layout pane narrower than the viewport). Pinning\n * `left`/`width` here would freeze whatever the *unconfined* box happened\n * to measure at pin time as an inline style, which — having higher\n * cascade priority than any stylesheet rule — permanently overrides that\n * confinement regardless of specificity or layers.\n */\nfunction pinBlockBox(target: HTMLElement): DOMRect {\n const rect = target.getBoundingClientRect();\n\n if (getComputedStyle(target).position === 'static') {\n target.style.position = 'fixed';\n }\n\n target.style.translate = 'none';\n target.style.insetBlockStart = 'auto';\n target.style.marginBlock = '0';\n target.style.boxSizing = 'border-box';\n target.style.top = `${rect.top}px`;\n target.style.height = `${rect.height}px`;\n\n return rect;\n}\n\n/**\n * Modal bottom-sheet wrapper for a native `<dialog>`, built on `rc-dialog`.\n *\n * The direct child `<dialog>` remains the semantic dialog surface and owns\n * content, labels, and form behavior. `rc-bottom-sheet` supplies the same\n * open/close lifecycle, focus restoration, cancel/request-close events, and\n * light-dismiss affordance as `rc-dialog`. The component docks the sheet to\n * the viewport block-end by default, while themes provide its visual surface.\n *\n * Dragging the resize handle past a decisive velocity in either direction\n * (a \"swipe\" or \"fling,\" distinct from a slow deliberate drag) snaps\n * straight to the extreme `snap-points` entry in that direction — swipe up\n * to fully expand, swipe down to fully collapse — regardless of how close\n * the release position was to some other point. Below that velocity, the\n * sheet settles to whichever declared snap point is nearest the release\n * height. All settling, whether from a drag or from `snapTo()`, animates\n * over `--rc-bottom-sheet-snap-duration`, follows\n * `--rc-bottom-sheet-snap-easing`, and is skipped for\n * `prefers-reduced-motion: reduce`.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-bottom-sheet rc-bottom-sheet docs}\n * @see {@link https://m3.material.io/components/bottom-sheets/overview Material Design bottom sheets}\n *\n * @slot - Place a `<dialog>` element with the sheet content and optional\n * `[data-rc-bottom-sheet-handle]` resize handle here.\n *\n * @fires rc-dialog-open - Inherited from `rc-dialog`; fired when the sheet opens.\n * @fires rc-dialog-toggle - Inherited from `rc-dialog`; fired when user/native interaction changes open state.\n * @fires rc-dialog-request-close - Inherited from `rc-dialog`; cancelable close request.\n * @fires rc-dialog-cancel - Inherited from `rc-dialog`; backward-compatible cancel alias.\n * @fires rc-dialog-close - Inherited from `rc-dialog`; fired after the sheet closes.\n * @fires rc-bottom-sheet-snap - Fires when a drag release or `snapTo()` call\n * selects a snap target. `detail: { index, height, trigger }`\n *\n * @attr light-dismiss - When present, a click on the backdrop area calls `requestClose()`.\n * Present by default; inherited from `rc-dialog`.\n * @attr resize - Enables edge resizing, mirroring the CSS `resize` property values.\n * Defaults to `vertical`; inherited from `rc-dialog`.\n * @attr resize-origin - Origin for resize edge hit-testing or explicit handles. Defaults to\n * `top`; inherited from `rc-dialog`.\n * @attr resize-handle - CSS selector, scoped to the inner `<dialog>`, for explicit resize\n * handles. Defaults to `[data-rc-bottom-sheet-handle]`; inherited from `rc-dialog`.\n * @attr snap-points - Whitespace-separated CSS heights, in ascending order, addressable by\n * `snapTo()` and settled to on drag release.\n * @attr swipe-dismiss - Whether a decisive downward swipe of at least 96 pixels requests close.\n * @attr swipe-velocity - Minimum release velocity, in pixels per second, that counts as a\n * decisive swipe rather than a slow deliberate drag.\n *\n * @cssprop [--rc-bottom-sheet-bg=Canvas] - Sheet surface background.\n * @cssprop [--rc-bottom-sheet-color=CanvasText] - Sheet text color.\n * @cssprop [--rc-bottom-sheet-radius=1rem 1rem 0 0] - Sheet corner radius.\n * @cssprop [--rc-bottom-sheet-shadow=none] - Sheet elevation shadow.\n * @cssprop [--rc-bottom-sheet-position=fixed] - Positioning scheme. Use `absolute` with `show()` to dock a non-modal sheet to a positioned ancestor.\n * @cssprop [--rc-bottom-sheet-inset-block-end=0px] - Distance from the positioning container's block-end edge.\n * @cssprop [--rc-bottom-sheet-inset-inline=0px] - Inline-axis inset used with automatic margins.\n * @cssprop [--rc-bottom-sheet-max-inline-size=40rem] - Maximum sheet width.\n * @cssprop [--rc-bottom-sheet-max-block-size=70dvh] - Maximum sheet height.\n * @cssprop [--rc-bottom-sheet-padding=0 1rem 1rem] - Sheet padding.\n * @cssprop [--rc-bottom-sheet-scrim=var(--rc-dialog-scrim)] - Modal backdrop color.\n * @cssprop [--rc-bottom-sheet-handle-color=GrayText] - Resize handle color.\n * @cssprop [--rc-bottom-sheet-handle-inline-size=2rem] - Resize handle width.\n * @cssprop [--rc-bottom-sheet-handle-block-size=0.25rem] - Resize handle height.\n * @cssprop [--rc-bottom-sheet-handle-radius=999px] - Resize handle corner radius.\n * @cssprop [--rc-bottom-sheet-handle-target-inline-size=100%] - Pointer target width for a direct-child handle.\n * @cssprop [--rc-bottom-sheet-handle-target-block-size=3rem] - Pointer target height for a direct-child handle.\n * @cssprop [--rc-bottom-sheet-handle-target-margin=0] - Margin around a direct-child handle target.\n * @cssprop [--rc-bottom-sheet-snap-duration=300ms] - Duration of the settle animation\n * after a drag release or `snapTo()` call.\n * @cssprop [--rc-bottom-sheet-snap-easing=cubic-bezier(0.4, 0, 0.2, 1)] - Easing\n * for the settle animation after a drag release or `snapTo()` call.\n */\nexport class RCBottomSheet extends RCDialog {\n private static readonly swipeDismissThreshold = 96;\n private static readonly _styledRoots = new Set<Document | ShadowRoot>();\n\n private static _ensureBaseStyles(root: Document | ShadowRoot): void {\n if (RCBottomSheet._styledRoots.has(root)) {\n return;\n }\n\n RCBottomSheet._styledRoots.add(root);\n\n const $style = document.createElement('style');\n\n $style.setAttribute('data-rc-light-dom-base', 'rc-bottom-sheet');\n $style.textContent = LIGHT_DOM_CSS;\n\n if (root instanceof Document) {\n root.head.append($style);\n } else {\n root.append($style);\n }\n }\n\n /**\n * Bottom sheets light-dismiss by default.\n *\n * Set `light-dismiss=\"false\"` as a property from JavaScript when a sheet must\n * be closed only by explicit actions.\n */\n @property({ type: Boolean, attribute: 'light-dismiss' })\n override lightDismiss = true;\n\n /** Bottom sheets resize vertically from the top edge by default. */\n @property({ type: String, reflect: true })\n override resize: ResizeDirection = 'vertical';\n\n /** Top-origin resizing keeps the sheet docked to the viewport bottom. */\n @property({ type: String, attribute: 'resize-origin' })\n override resizeOrigin: ResizeOrigin = 'top';\n\n /** Default optional handle selector. Falls back to top-edge resize when absent. */\n @property({ type: String, attribute: 'resize-handle' })\n override resizeHandle = '[data-rc-bottom-sheet-handle]';\n\n /**\n * Whitespace-separated CSS heights in ascending order.\n *\n * Each height becomes an addressable snap target. Slow releases choose the\n * nearest target, while decisive swipes choose the first or last target.\n */\n @property({ type: String, attribute: 'snap-points' })\n snapPoints = '';\n\n /**\n * Whether a downward pointer resize of at least 96 pixels requests close.\n *\n * When false, a decisive downward swipe settles at the first snap point.\n */\n @property({ type: Boolean, attribute: 'swipe-dismiss' })\n swipeDismiss = true;\n\n /**\n * Minimum release velocity, in pixels per second, that counts as a decisive\n * swipe rather than a slow deliberate drag. A swipe jumps straight to the\n * extreme `snap-points` entry in its direction; below this, the release\n * settles to the nearest point instead.\n */\n @property({ type: Number, attribute: 'swipe-velocity' })\n swipeVelocity = 500;\n\n private readonly _reducedMotion =\n typeof window !== 'undefined' ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;\n\n private _activeSnapAnimation: Animation | null = null;\n\n // Snap-point heights are resolved from the *current* viewport at the\n // moment a snap is applied (dvh units, or a probe-element measurement for\n // other CSS units) and then frozen as inline styles by pinBlockBox — nothing\n // re-derives them afterward. A window resize/maximize/restore while a\n // sheet is open (without the user dragging it) otherwise leaves it pinned\n // to stale pre-resize geometry. Track the last-applied index so a resize\n // can re-run the same snap against fresh geometry.\n private _lastSnapIndex: number | null = null;\n private readonly _onWindowResize = (): void => {\n if (!this.open || this._lastSnapIndex === null) {\n return;\n }\n\n this._snapToIndex(this._lastSnapIndex, 'instant', 'api');\n };\n\n override connectedCallback(): void {\n super.connectedCallback();\n RCBottomSheet._ensureBaseStyles(this.getRootNode() as Document | ShadowRoot);\n window.addEventListener('resize', this._onWindowResize);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('resize', this._onWindowResize);\n }\n\n /**\n * Snaps to a declared target by zero-based index.\n *\n * Finite fractional indices are truncated and out-of-range indices clamp to\n * the nearest endpoint. Non-finite indices and calls without valid snap\n * points have no effect.\n *\n * @param index - zero-based index into `snapPoints`\n * @param behavior - whether to animate the movement\n *\n * @example\n * const sheet = document.querySelector('rc-bottom-sheet');\n * sheet.snapTo(1);\n */\n snapTo(index: number, behavior: 'animated' | 'instant' = 'animated'): void {\n if (!Number.isFinite(index)) {\n return;\n }\n\n this._snapToIndex(Math.trunc(index), behavior, 'api');\n }\n\n protected override _onResizeEnd(detail: ResizeLifecycleDetail): void {\n if (detail.inputType === 'pointer' && this.swipeDismiss && this._shouldSwipeDismiss(detail)) {\n this.requestClose();\n\n return;\n }\n\n const points = this._resolvedSnapPoints();\n const velocity = detail.edge.includes('n') ? -detail.velocityY : detail.velocityY;\n const distance = Math.abs(detail.height - detail.startHeight);\n\n if (\n detail.inputType === 'pointer' &&\n points.length > 0 &&\n distance >= MIN_SWIPE_DISTANCE &&\n Math.abs(velocity) >= this.swipeVelocity\n ) {\n // Decisive fling: jump to the extreme point in the swipe direction,\n // regardless of proximity to some other point. Height grows as the\n // sheet is dragged upward, so a positive velocity is a swipe up.\n const targetIndex = findExtremeSnapIndex(points, velocity > 0 ? 1 : -1);\n\n this._snapToIndex(targetIndex, 'animated', 'drag');\n\n return;\n }\n\n this._snapToNearestPoint('drag');\n }\n\n private _shouldSwipeDismiss(detail: ResizeLifecycleDetail): boolean {\n return (\n detail.edge.includes('n') &&\n detail.startHeight - detail.height >= RCBottomSheet.swipeDismissThreshold\n );\n }\n\n private _snapToNearestPoint(trigger: 'drag' | 'api' = 'api'): void {\n const $dialog = this._$dialog;\n const points = this._resolvedSnapPoints();\n\n if (!$dialog || points.length === 0) {\n return;\n }\n\n const height = $dialog.getBoundingClientRect().height;\n const nearestIndex = findNearestSnapIndex(points, height);\n\n this._snapToIndex(nearestIndex, 'animated', trigger);\n }\n\n private _snapToIndex(\n index: number,\n behavior: 'animated' | 'instant',\n trigger: 'drag' | 'api',\n ): void {\n const $dialog = this._$dialog;\n const points = this._resolvedSnapPoints();\n\n if (!$dialog || points.length === 0) {\n return;\n }\n\n // Pinning (border-box, explicit top/height) is normally established by\n // the first drag gesture; snapTo() must do the same so a sheet that has\n // never been dragged still measures and sets height consistently.\n const rect = pinBlockBox($dialog);\n const clampedIndex = Math.max(0, Math.min(index, points.length - 1));\n const requestedHeight = points[clampedIndex];\n\n this._lastSnapIndex = clampedIndex;\n\n // An in-flight animation wins over inline geometry in the cascade. Pin\n // its current visual box before cancelling so the constrained target can\n // be measured without jumping back to the prior snap point.\n this._activeSnapAnimation?.cancel();\n this._activeSnapAnimation = null;\n\n $dialog.style.height = `${requestedHeight}px`;\n\n const targetHeight = $dialog.getBoundingClientRect().height;\n\n // Restore the current visual box before applying or animating the target.\n // CSS min/max block-size can make targetHeight differ from the authored\n // snap point; anchoring from the effective height keeps the block-end edge\n // fixed in either case.\n $dialog.style.top = `${rect.top}px`;\n $dialog.style.height = `${rect.height}px`;\n\n const targetTop = rect.bottom - targetHeight;\n\n this._applySnap($dialog, targetTop, targetHeight, behavior);\n\n this.dispatchEvent(\n new CustomEvent<RCBottomSheetSnapDetail>('rc-bottom-sheet-snap', {\n bubbles: true,\n composed: true,\n detail: { index: clampedIndex, height: targetHeight, trigger },\n }),\n );\n }\n\n private _applySnap(\n $dialog: HTMLDialogElement,\n top: number,\n height: number,\n behavior: 'animated' | 'instant',\n ): void {\n // Measure before cancelling any in-flight animation. `_snapToIndex()`\n // normally pins and cancels first, while this ordering also keeps direct\n // calls robust if applying a snap is refactored later.\n const fromRect = $dialog.getBoundingClientRect();\n\n this._activeSnapAnimation?.cancel();\n this._activeSnapAnimation = null;\n\n if (behavior === 'instant' || this._reducedMotion?.matches) {\n $dialog.style.top = `${top}px`;\n $dialog.style.height = `${height}px`;\n\n return;\n }\n\n if (fromRect.top === top && fromRect.height === height) {\n return;\n }\n\n const animation = $dialog.animate(\n [\n { top: `${fromRect.top}px`, height: `${fromRect.height}px` },\n { top: `${top}px`, height: `${height}px` },\n ],\n {\n duration: this._snapDuration($dialog),\n easing: this._snapEasing($dialog),\n fill: 'forwards',\n },\n );\n\n this._activeSnapAnimation = animation;\n\n animation.finished\n .then(() => animation.commitStyles())\n .catch(() => {\n // Interrupted by a newer gesture or snapTo() call, which owns applying\n // its own final styles.\n })\n .finally(() => {\n animation.cancel();\n\n if (this._activeSnapAnimation === animation) {\n this._activeSnapAnimation = null;\n }\n });\n }\n\n private _snapDuration($dialog: HTMLDialogElement): number {\n const raw = getComputedStyle($dialog)\n .getPropertyValue('--rc-bottom-sheet-snap-duration')\n .trim();\n const parsed = Number.parseFloat(raw);\n\n return Number.isFinite(parsed) ? parsed : DEFAULT_SNAP_DURATION_MS;\n }\n\n private _snapEasing($dialog: HTMLDialogElement): string {\n return (\n getComputedStyle($dialog).getPropertyValue('--rc-bottom-sheet-snap-easing').trim() ||\n DEFAULT_SNAP_EASING\n );\n }\n\n private _resolvedSnapPoints(): number[] {\n if (!this.snapPoints.trim()) {\n return [];\n }\n\n return this.snapPoints\n .trim()\n .split(/\\s+/)\n .map((point) => this._resolveSnapPoint(point))\n .filter((point): point is number => Number.isFinite(point) && point > 0);\n }\n\n private _resolveSnapPoint(point: string): number {\n const numeric = Number.parseFloat(point);\n\n if (!Number.isFinite(numeric)) {\n return Number.NaN;\n }\n\n if (point.endsWith('%')) {\n return window.innerHeight * (numeric / 100);\n }\n\n if (point.endsWith('px') || String(numeric) === point) {\n return numeric;\n }\n\n const probe = document.createElement('div');\n\n Object.assign(probe.style, {\n position: 'fixed',\n visibility: 'hidden',\n pointerEvents: 'none',\n blockSize: point,\n inlineSize: '0',\n inset: '0 auto auto 0',\n });\n\n document.body.append(probe);\n\n const height = probe.getBoundingClientRect().height;\n\n probe.remove();\n\n return height;\n }\n}\n\nexport default RCBottomSheet;\n"],"names":["DEFAULT_SNAP_DURATION_MS","DEFAULT_SNAP_EASING","LIGHT_DOM_CSS","MIN_SWIPE_DISTANCE","pinBlockBox","target","rect","_RCBottomSheet","_a","RCDialog","root","$style","index","behavior","detail","points","velocity","distance","targetIndex","findExtremeSnapIndex","trigger","$dialog","height","nearestIndex","findNearestSnapIndex","clampedIndex","requestedHeight","targetHeight","targetTop","top","fromRect","animation","raw","parsed","point","numeric","probe","__decorateClass","property","RCBottomSheet"],"mappings":"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"}