@rcarls/rc-bottom-sheet 0.4.2 → 0.6.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,40 @@
1
1
  # @rcarls/rc-bottom-sheet
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Patch Changes
6
+
7
+ - ee7ba6c: Keep resize pinning, drag origins, and snapped bottom sheets correctly
8
+ positioned when a layout-containing ancestor establishes a non-viewport
9
+ containing block. Report animated snap changes only after the sheet settles so
10
+ consumers can defer discrete content and accessibility state changes until the
11
+ surface motion completes. Expose resize-start timing so content can be prepared
12
+ before an interactive expansion reveals it.
13
+ - Updated dependencies [6683eb9]
14
+ - Updated dependencies [ee7ba6c]
15
+ - Updated dependencies [30eb232]
16
+ - Updated dependencies [57370e4]
17
+ - @rcarls/rc-common@0.6.0
18
+ - @rcarls/rc-dialog@0.6.0
19
+
20
+ ## 0.5.0
21
+
22
+ ### Patch Changes
23
+
24
+ - ba65db7: Fix an edge case left in the 0.4.2 resize-repin fix: the window-resize re-pin re-measured
25
+ via `getBoundingClientRect()` on the box that was still carrying its _previous_ inline pin,
26
+ so a fixed (non-`dvh`) snap point (for example a `132px` peek height, common for a
27
+ persistent summary sheet) computed the exact same, now-stale target, since neither the
28
+ requested height nor the stale box's edges had changed. Only `dvh`-based snap points were
29
+ actually re-deriving correctly. Now clears the previous `top`/`height` pin before
30
+ re-measuring on resize, so the dialog reverts to its CSS-driven (`inset-block-end`-anchored)
31
+ position first, the same as a sheet that has never been pinned. Added a regression test
32
+ covering this specific case (confirmed it fails without the fix).
33
+ - Updated dependencies [4ae2ef0]
34
+ - Updated dependencies [689340c]
35
+ - @rcarls/rc-common@0.5.0
36
+ - @rcarls/rc-dialog@0.5.0
37
+
3
38
  ## 0.4.2
4
39
 
5
40
  ### Patch Changes
package/README.md CHANGED
@@ -61,12 +61,15 @@ then docks to that container's block-end edge instead of the viewport.
61
61
  List `snap-points` as CSS heights in ascending order. Slow drag releases
62
62
  settle at the nearest point; a swipe at `swipe-velocity` or faster settles at
63
63
  the first or last point. Settling animates unless the user prefers reduced
64
- motion.
64
+ motion. `rc-bottom-sheet-resize-start` fires as an interactive resize begins,
65
+ before the sheet geometry changes, so consumers can prepare content that the
66
+ drag will reveal.
65
67
 
66
68
  Call `snapTo(index)` to move programmatically. The
67
- `rc-bottom-sheet-snap` event reports the selected `index`, effective target
68
- `height` after CSS size constraints, and whether the trigger was a drag or API
69
- call. Constrained targets remain docked to the same block-end edge.
69
+ `rc-bottom-sheet-snap` event fires after the sheet has settled and reports the
70
+ selected `index`, effective target `height` after CSS size constraints, and
71
+ whether the trigger was a drag or API call. Interrupted animations do not emit
72
+ a stale event. Constrained targets remain docked to the same block-end edge.
70
73
 
71
74
  Set `--rc-bottom-sheet-snap-duration` and
72
75
  `--rc-bottom-sheet-snap-easing` on the dialog to customize its settle motion.
@@ -323,6 +323,24 @@
323
323
  ],
324
324
  "description": "Snaps to a declared target by zero-based index.\n\nFinite fractional indices are truncated and out-of-range indices clamp to\nthe nearest endpoint. Non-finite indices and calls without valid snap\npoints have no effect."
325
325
  },
326
+ {
327
+ "kind": "method",
328
+ "name": "_onResizeStart",
329
+ "privacy": "protected",
330
+ "return": {
331
+ "type": {
332
+ "text": "void"
333
+ }
334
+ },
335
+ "parameters": [
336
+ {
337
+ "name": "detail",
338
+ "type": {
339
+ "text": "ResizeLifecycleDetail"
340
+ }
341
+ }
342
+ ]
343
+ },
326
344
  {
327
345
  "kind": "method",
328
346
  "name": "_onResizeEnd",
@@ -441,6 +459,12 @@
441
459
  "type": {
442
460
  "text": "'animated' | 'instant'"
443
461
  }
462
+ },
463
+ {
464
+ "name": "onSettled",
465
+ "type": {
466
+ "text": "() => void"
467
+ }
444
468
  }
445
469
  ]
446
470
  },
@@ -510,12 +534,19 @@
510
534
  }
511
535
  ],
512
536
  "events": [
537
+ {
538
+ "name": "rc-bottom-sheet-resize-start",
539
+ "type": {
540
+ "text": "CustomEvent"
541
+ },
542
+ "description": "Fires when an interactive resize begins. `detail: { height, inputType }`"
543
+ },
513
544
  {
514
545
  "name": "rc-bottom-sheet-snap",
515
546
  "type": {
516
547
  "text": "CustomEvent"
517
548
  },
518
- "description": "Fires when a drag release or `snapTo()` call selects a snap target. `detail: { index, height, trigger }`"
549
+ "description": "Fires after a drag release or `snapTo()` call settles at its snap target. `detail: { index, height, trigger }`"
519
550
  },
520
551
  {
521
552
  "description": "Inherited from `rc-dialog`; fired when the sheet opens.",
@@ -1,12 +1,12 @@
1
- import { property as h } from "lit/decorators.js";
2
- import { RCDialog as b } from "@rcarls/rc-dialog";
3
- import { findExtremeSnapIndex as g, findNearestSnapIndex as y } from "@rcarls/rc-common";
4
- var _ = Object.defineProperty, c = (o, t, e, n) => {
5
- for (var i = void 0, s = o.length - 1, r; s >= 0; s--)
6
- (r = o[s]) && (i = r(t, e, i) || i);
7
- return i && _(t, e, i), i;
1
+ import { property as l } from "lit/decorators.js";
2
+ import { RCDialog as g } from "@rcarls/rc-dialog";
3
+ import { findExtremeSnapIndex as y, findNearestSnapIndex as _ } from "@rcarls/rc-common";
4
+ var v = Object.defineProperty, h = (o, t, e, i) => {
5
+ for (var n = void 0, s = o.length - 1, p; s >= 0; s--)
6
+ (p = o[s]) && (n = p(t, e, n) || n);
7
+ return n && v(t, e, n), n;
8
8
  };
9
- const v = 300, x = "cubic-bezier(0.4, 0, 0.2, 1)", S = `
9
+ const f = 300, x = "cubic-bezier(0.4, 0, 0.2, 1)", S = `
10
10
  @layer rc-base {
11
11
  rc-bottom-sheet > dialog {
12
12
  position: var(--rc-bottom-sheet-position, fixed);
@@ -59,16 +59,19 @@ const v = 300, x = "cubic-bezier(0.4, 0, 0.2, 1)", S = `
59
59
  outline-offset: -0.25rem;
60
60
  }
61
61
  }
62
- `, f = 24;
63
- function z(o) {
64
- const t = o.getBoundingClientRect();
65
- return getComputedStyle(o).position === "static" && (o.style.position = "fixed"), o.style.translate = "none", o.style.insetBlockStart = "auto", o.style.marginBlock = "0", o.style.boxSizing = "border-box", o.style.top = `${t.top}px`, o.style.height = `${t.height}px`, t;
62
+ `, z = 24;
63
+ function w(o) {
64
+ const t = o.getBoundingClientRect(), e = getComputedStyle(o), i = o.offsetParent, n = Number.parseFloat(e.top), s = i ? t.top - i.getBoundingClientRect().top - i.clientTop + i.scrollTop : Number.isFinite(n) ? n : t.top;
65
+ return e.position === "static" && (o.style.position = "fixed"), o.style.translate = "none", o.style.insetBlockStart = "auto", o.style.marginBlock = "0", o.style.boxSizing = "border-box", o.style.top = `${s}px`, o.style.height = `${t.height}px`, t;
66
66
  }
67
67
  var a;
68
- const l = (a = class extends b {
68
+ const c = (a = class extends g {
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) {
@@ -101,14 +104,23 @@ const l = (a = class extends b {
101
104
  snapTo(t, e = "animated") {
102
105
  Number.isFinite(t) && this._snapToIndex(Math.trunc(t), e, "api");
103
106
  }
107
+ _onResizeStart(t) {
108
+ this.dispatchEvent(
109
+ new CustomEvent("rc-bottom-sheet-resize-start", {
110
+ bubbles: !0,
111
+ composed: !0,
112
+ detail: { height: t.height, inputType: t.inputType }
113
+ })
114
+ );
115
+ }
104
116
  _onResizeEnd(t) {
105
117
  if (t.inputType === "pointer" && this.swipeDismiss && this._shouldSwipeDismiss(t)) {
106
118
  this.requestClose();
107
119
  return;
108
120
  }
109
- const e = this._resolvedSnapPoints(), n = t.edge.includes("n") ? -t.velocityY : t.velocityY, i = Math.abs(t.height - t.startHeight);
110
- if (t.inputType === "pointer" && e.length > 0 && i >= f && Math.abs(n) >= this.swipeVelocity) {
111
- const s = g(e, n > 0 ? 1 : -1);
121
+ const e = this._resolvedSnapPoints(), i = t.edge.includes("n") ? -t.velocityY : t.velocityY, n = Math.abs(t.height - t.startHeight);
122
+ if (t.inputType === "pointer" && e.length > 0 && n >= z && Math.abs(i) >= this.swipeVelocity) {
123
+ const s = y(e, i > 0 ? 1 : -1);
112
124
  this._snapToIndex(s, "animated", "drag");
113
125
  return;
114
126
  }
@@ -118,41 +130,45 @@ const l = (a = class extends b {
118
130
  return t.edge.includes("n") && t.startHeight - t.height >= a.swipeDismissThreshold;
119
131
  }
120
132
  _snapToNearestPoint(t = "api") {
121
- const e = this._$dialog, n = this._resolvedSnapPoints();
122
- if (!e || n.length === 0)
133
+ const e = this._$dialog, i = this._resolvedSnapPoints();
134
+ if (!e || i.length === 0)
123
135
  return;
124
- const i = e.getBoundingClientRect().height, s = y(n, i);
136
+ const n = e.getBoundingClientRect().height, s = _(i, n);
125
137
  this._snapToIndex(s, "animated", t);
126
138
  }
127
- _snapToIndex(t, e, n) {
128
- const i = this._$dialog, s = this._resolvedSnapPoints();
129
- if (!i || s.length === 0)
139
+ _snapToIndex(t, e, i) {
140
+ const n = this._$dialog, s = this._resolvedSnapPoints();
141
+ if (!n || s.length === 0)
130
142
  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`;
133
- const d = i.getBoundingClientRect().height;
134
- i.style.top = `${r.top}px`, i.style.height = `${r.height}px`;
135
- const u = r.bottom - d;
136
- this._applySnap(i, u, d, e), this.dispatchEvent(
137
- new CustomEvent("rc-bottom-sheet-snap", {
138
- bubbles: !0,
139
- composed: !0,
140
- detail: { index: p, height: d, trigger: n }
141
- })
142
- );
143
+ const p = w(n), d = n.style.top, r = Math.max(0, Math.min(t, s.length - 1)), m = s[r];
144
+ this._lastSnapIndex = r, this._activeSnapAnimation?.cancel(), this._activeSnapAnimation = null, n.style.height = `${m}px`;
145
+ const u = n.getBoundingClientRect().height;
146
+ n.style.top = d, n.style.height = `${p.height}px`;
147
+ const b = Number.parseFloat(d) + p.height - u;
148
+ this._applySnap(n, b, u, e, () => {
149
+ this.dispatchEvent(
150
+ new CustomEvent("rc-bottom-sheet-snap", {
151
+ bubbles: !0,
152
+ composed: !0,
153
+ detail: { index: r, height: u, trigger: i }
154
+ })
155
+ );
156
+ });
143
157
  }
144
- _applySnap(t, e, n, i) {
145
- const s = t.getBoundingClientRect();
146
- if (this._activeSnapAnimation?.cancel(), this._activeSnapAnimation = null, i === "instant" || this._reducedMotion?.matches) {
147
- t.style.top = `${e}px`, t.style.height = `${n}px`;
158
+ _applySnap(t, e, i, n, s) {
159
+ const p = t.getBoundingClientRect(), d = Number.parseFloat(t.style.top);
160
+ if (this._activeSnapAnimation?.cancel(), this._activeSnapAnimation = null, n === "instant" || this._reducedMotion?.matches) {
161
+ t.style.top = `${e}px`, t.style.height = `${i}px`, s();
148
162
  return;
149
163
  }
150
- if (s.top === e && s.height === n)
164
+ if (d === e && p.height === i) {
165
+ s();
151
166
  return;
167
+ }
152
168
  const r = t.animate(
153
169
  [
154
- { top: `${s.top}px`, height: `${s.height}px` },
155
- { top: `${e}px`, height: `${n}px` }
170
+ { top: `${d}px`, height: `${p.height}px` },
171
+ { top: `${e}px`, height: `${i}px` }
156
172
  ],
157
173
  {
158
174
  duration: this._snapDuration(t),
@@ -160,14 +176,22 @@ const l = (a = class extends b {
160
176
  fill: "forwards"
161
177
  }
162
178
  );
163
- this._activeSnapAnimation = r, r.finished.then(() => r.commitStyles()).catch(() => {
164
- }).finally(() => {
165
- r.cancel(), this._activeSnapAnimation === r && (this._activeSnapAnimation = null);
166
- });
179
+ this._activeSnapAnimation = r, r.finished.then(
180
+ () => {
181
+ if (this._activeSnapAnimation !== r) {
182
+ r.cancel();
183
+ return;
184
+ }
185
+ t.style.top = `${e}px`, t.style.height = `${i}px`, r.cancel(), this._activeSnapAnimation = null, s();
186
+ },
187
+ () => {
188
+ r.cancel(), this._activeSnapAnimation === r && (this._activeSnapAnimation = null);
189
+ }
190
+ );
167
191
  }
168
192
  _snapDuration(t) {
169
- const e = getComputedStyle(t).getPropertyValue("--rc-bottom-sheet-snap-duration").trim(), n = Number.parseFloat(e);
170
- return Number.isFinite(n) ? n : v;
193
+ const e = getComputedStyle(t).getPropertyValue("--rc-bottom-sheet-snap-duration").trim(), i = Number.parseFloat(e);
194
+ return Number.isFinite(i) ? i : f;
171
195
  }
172
196
  _snapEasing(t) {
173
197
  return getComputedStyle(t).getPropertyValue("--rc-bottom-sheet-snap-easing").trim() || x;
@@ -183,42 +207,42 @@ const l = (a = class extends b {
183
207
  return window.innerHeight * (e / 100);
184
208
  if (t.endsWith("px") || String(e) === t)
185
209
  return e;
186
- const n = document.createElement("div");
187
- Object.assign(n.style, {
210
+ const i = document.createElement("div");
211
+ Object.assign(i.style, {
188
212
  position: "fixed",
189
213
  visibility: "hidden",
190
214
  pointerEvents: "none",
191
215
  blockSize: t,
192
216
  inlineSize: "0",
193
217
  inset: "0 auto auto 0"
194
- }), document.body.append(n);
195
- const i = n.getBoundingClientRect().height;
196
- return n.remove(), i;
218
+ }), document.body.append(i);
219
+ const n = i.getBoundingClientRect().height;
220
+ return i.remove(), n;
197
221
  }
198
222
  }, a.swipeDismissThreshold = 96, a._styledRoots = /* @__PURE__ */ new Set(), a);
199
- c([
200
- h({ type: Boolean, attribute: "light-dismiss" })
201
- ], l.prototype, "lightDismiss");
202
- c([
203
- h({ type: String, reflect: !0 })
204
- ], l.prototype, "resize");
205
- c([
206
- h({ type: String, attribute: "resize-origin" })
207
- ], l.prototype, "resizeOrigin");
208
- c([
209
- h({ type: String, attribute: "resize-handle" })
210
- ], l.prototype, "resizeHandle");
211
- c([
212
- h({ type: String, attribute: "snap-points" })
213
- ], l.prototype, "snapPoints");
214
- c([
215
- h({ type: Boolean, attribute: "swipe-dismiss" })
216
- ], l.prototype, "swipeDismiss");
217
- c([
218
- h({ type: Number, attribute: "swipe-velocity" })
219
- ], l.prototype, "swipeVelocity");
220
- let I = l;
223
+ h([
224
+ l({ type: Boolean, attribute: "light-dismiss" })
225
+ ], c.prototype, "lightDismiss");
226
+ h([
227
+ l({ type: String, reflect: !0 })
228
+ ], c.prototype, "resize");
229
+ h([
230
+ l({ type: String, attribute: "resize-origin" })
231
+ ], c.prototype, "resizeOrigin");
232
+ h([
233
+ l({ type: String, attribute: "resize-handle" })
234
+ ], c.prototype, "resizeHandle");
235
+ h([
236
+ l({ type: String, attribute: "snap-points" })
237
+ ], c.prototype, "snapPoints");
238
+ h([
239
+ l({ type: Boolean, attribute: "swipe-dismiss" })
240
+ ], c.prototype, "swipeDismiss");
241
+ h([
242
+ l({ type: Number, attribute: "swipe-velocity" })
243
+ ], c.prototype, "swipeVelocity");
244
+ let C = c;
221
245
  export {
222
- I as R
246
+ C as R
223
247
  };
224
- //# sourceMappingURL=rc-bottom-sheet-CR2usG4R.js.map
248
+ //# sourceMappingURL=rc-bottom-sheet-BZg5AVUG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-bottom-sheet-BZg5AVUG.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-resize-start': CustomEvent<RCBottomSheetResizeStartDetail>;\n 'rc-bottom-sheet-snap': CustomEvent<RCBottomSheetSnapDetail>;\n }\n}\n\n/** Detail shape for `rc-bottom-sheet-resize-start`. */\nexport interface RCBottomSheetResizeStartDetail {\n /** Rendered sheet height when the resize gesture begins. */\n height: number;\n\n /** Input mechanism initiating the resize. */\n inputType: 'pointer' | 'keyboard';\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 const styles = getComputedStyle(target);\n // `getBoundingClientRect().top` is viewport-relative, while an authored\n // `top` value is relative to the element's fixed/absolute containing block.\n // They are only interchangeable when the viewport is that containing block.\n // Derive the current *visual* top from the offset parent's box when one is\n // available. `offsetTop` itself is insufficient here because it ignores an\n // in-flight Web Animation; `getComputedStyle().top` can have the opposite\n // problem for fixed positioning, reporting a viewport-relative used value.\n const offsetParent = target.offsetParent as HTMLElement | null;\n const computedTop = Number.parseFloat(styles.top);\n const localTop = offsetParent\n ? rect.top -\n offsetParent.getBoundingClientRect().top -\n offsetParent.clientTop +\n offsetParent.scrollTop\n : Number.isFinite(computedTop)\n ? computedTop\n : rect.top;\n\n if (styles.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 = `${localTop}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-resize-start - Fires when an interactive resize begins.\n * `detail: { height, inputType }`\n * @fires rc-bottom-sheet-snap - Fires after a drag release or `snapTo()` call\n * settles at its 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 _onResizeStart(detail: ResizeLifecycleDetail): void {\n this.dispatchEvent(\n new CustomEvent<RCBottomSheetResizeStartDetail>('rc-bottom-sheet-resize-start', {\n bubbles: true,\n composed: true,\n detail: { height: detail.height, inputType: detail.inputType },\n }),\n );\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 pinnedTop = $dialog.style.top;\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 = pinnedTop;\n $dialog.style.height = `${rect.height}px`;\n\n const currentTop = Number.parseFloat(pinnedTop);\n const targetTop = currentTop + rect.height - targetHeight;\n\n this._applySnap($dialog, targetTop, targetHeight, behavior, () => {\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\n private _applySnap(\n $dialog: HTMLDialogElement,\n top: number,\n height: number,\n behavior: 'animated' | 'instant',\n onSettled: () => void,\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 const fromTop = Number.parseFloat($dialog.style.top);\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 onSettled();\n\n return;\n }\n\n if (fromTop === top && fromRect.height === height) {\n onSettled();\n\n return;\n }\n\n const animation = $dialog.animate(\n [\n { top: `${fromTop}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.then(\n () => {\n // A newer gesture or snapTo() call owns the final geometry and event.\n if (this._activeSnapAnimation !== animation) {\n animation.cancel();\n\n return;\n }\n\n // Persist the known target explicitly before removing the animation's\n // forwards fill. Unlike commitStyles(), this remains safe if a test or\n // consumer detaches the dialog as the finished promise resolves.\n $dialog.style.top = `${top}px`;\n $dialog.style.height = `${height}px`;\n animation.cancel();\n this._activeSnapAnimation = null;\n\n onSettled();\n },\n () => {\n // Interrupted by a newer gesture or snapTo() call, which owns applying\n // its own final styles and reporting its settled snap point.\n animation.cancel();\n\n if (this._activeSnapAnimation === animation) {\n this._activeSnapAnimation = null;\n }\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","styles","offsetParent","computedTop","localTop","_RCBottomSheet","_a","RCDialog","$dialog","root","$style","index","behavior","detail","points","velocity","distance","targetIndex","findExtremeSnapIndex","trigger","height","nearestIndex","findNearestSnapIndex","pinnedTop","clampedIndex","requestedHeight","targetHeight","targetTop","top","onSettled","fromRect","fromTop","animation","raw","parsed","point","numeric","probe","__decorateClass","property","RCBottomSheet"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { R as t } from "./rc-bottom-sheet-CR2usG4R.js";
1
+ import { R as t } from "./rc-bottom-sheet-BZg5AVUG.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-BZg5AVUG.js";
2
2
  export {
3
3
  e as RCBottomSheet
4
4
  };
@@ -5,9 +5,17 @@ declare global {
5
5
  'rc-bottom-sheet': RCBottomSheet;
6
6
  }
7
7
  interface HTMLElementEventMap {
8
+ 'rc-bottom-sheet-resize-start': CustomEvent<RCBottomSheetResizeStartDetail>;
8
9
  'rc-bottom-sheet-snap': CustomEvent<RCBottomSheetSnapDetail>;
9
10
  }
10
11
  }
12
+ /** Detail shape for `rc-bottom-sheet-resize-start`. */
13
+ export interface RCBottomSheetResizeStartDetail {
14
+ /** Rendered sheet height when the resize gesture begins. */
15
+ height: number;
16
+ /** Input mechanism initiating the resize. */
17
+ inputType: 'pointer' | 'keyboard';
18
+ }
11
19
  /** Detail shape for `rc-bottom-sheet-snap`. */
12
20
  export interface RCBottomSheetSnapDetail {
13
21
  /** Index into the resolved `snap-points` list selected as the target. */
@@ -48,8 +56,10 @@ export interface RCBottomSheetSnapDetail {
48
56
  * @fires rc-dialog-request-close - Inherited from `rc-dialog`; cancelable close request.
49
57
  * @fires rc-dialog-cancel - Inherited from `rc-dialog`; backward-compatible cancel alias.
50
58
  * @fires rc-dialog-close - Inherited from `rc-dialog`; fired after the sheet closes.
51
- * @fires rc-bottom-sheet-snap - Fires when a drag release or `snapTo()` call
52
- * selects a snap target. `detail: { index, height, trigger }`
59
+ * @fires rc-bottom-sheet-resize-start - Fires when an interactive resize begins.
60
+ * `detail: { height, inputType }`
61
+ * @fires rc-bottom-sheet-snap - Fires after a drag release or `snapTo()` call
62
+ * settles at its snap target. `detail: { index, height, trigger }`
53
63
  *
54
64
  * @attr light-dismiss - When present, a click on the backdrop area calls `requestClose()`.
55
65
  * Present by default; inherited from `rc-dialog`.
@@ -146,6 +156,7 @@ export declare class RCBottomSheet extends RCDialog {
146
156
  * sheet.snapTo(1);
147
157
  */
148
158
  snapTo(index: number, behavior?: 'animated' | 'instant'): void;
159
+ protected _onResizeStart(detail: ResizeLifecycleDetail): void;
149
160
  protected _onResizeEnd(detail: ResizeLifecycleDetail): void;
150
161
  private _shouldSwipeDismiss;
151
162
  private _snapToNearestPoint;
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.6.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.6.0",
49
+ "@rcarls/rc-dialog": "0.6.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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"}