@rcarls/rc-virtual-scroller 0.0.0 → 0.7.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.
@@ -0,0 +1,403 @@
1
+ import { css as B, LitElement as C, html as W } from "lit";
2
+ import { property as y, state as N } from "lit/decorators.js";
3
+ import { styleMap as D } from "lit/directives/style-map.js";
4
+ import { cssZoom as S, logicalRect as q, RafScheduler as Z, HORIZONTAL_LTR_FLOW as U, observeLocale as H, resolveFlow as $, isReversed as j, physicalAxis as V, findNearestScrollAncestor as J } from "@rcarls/rc-common";
5
+ function x(o, e = 0) {
6
+ return Number.isFinite(o) && o >= 0 ? Math.floor(o) : e;
7
+ }
8
+ function K(o) {
9
+ return Number.isFinite(o) && o > 0 ? o : 0;
10
+ }
11
+ function Q(o) {
12
+ const { count: e, overscan: i, itemsPerLine: t, lineSize: s, viewStart: r, viewSize: n, focused: l } = o, h = Math.ceil(e / t), m = Math.min(
13
+ h,
14
+ Math.max(0, Math.floor((Math.max(0, r) + 0.5) / s) - i)
15
+ ), a = Math.min(
16
+ h,
17
+ Math.max(m, Math.ceil((r + n - 0.5) / s) + i)
18
+ );
19
+ let g = Math.min(e, m * t), d = Math.min(e, a * t);
20
+ if (l >= 0 && l < e) {
21
+ const v = Math.floor(l / t);
22
+ g = Math.min(g, v * t), d = Math.max(d, Math.min(e, (v + 1) * t));
23
+ }
24
+ return {
25
+ start: g,
26
+ end: d,
27
+ spacerStart: Math.floor(g / t) * s,
28
+ spacerEnd: Math.max(0, h - Math.ceil(d / t)) * s
29
+ };
30
+ }
31
+ function k(o) {
32
+ const e = o.replace(/\[[^\]]*\]/g, "").trim().split(/\s+/);
33
+ return e.every((i) => /^\d+(?:\.\d+)?px$/.test(i)) ? e.map(Number.parseFloat) : null;
34
+ }
35
+ function O(o, e, i) {
36
+ if (o === "auto" && e === "auto")
37
+ return 1;
38
+ const t = /^span (\d+)$/.exec(o === "auto" ? e : o);
39
+ return t && (o === "auto" || e === "auto") ? Math.max(1, Number(t[1])) : o === "1" && (e === "-1" || e === String(i + 1)) ? i : null;
40
+ }
41
+ function X(o, e, i, t, s, r = 1) {
42
+ const n = getComputedStyle(o), l = o.children, h = l.item(0), m = Number.parseFloat(e === "inline" ? n.columnGap : n.rowGap) || 0, a = (c) => ({ kind: "unsupported", reason: c });
43
+ if (n.display === "grid" || n.display === "inline-grid") {
44
+ if (n.gridAutoFlow.includes("dense"))
45
+ return a("Dense grid placement is not a sequential collection.");
46
+ if (n.gridAutoFlow !== (e === "inline" ? "column" : "row"))
47
+ return a("Grid auto-flow must advance along the windowed axis.");
48
+ const c = k(
49
+ e === "inline" ? n.gridTemplateRows : n.gridTemplateColumns
50
+ ), b = k(
51
+ e === "inline" ? n.gridTemplateColumns : n.gridTemplateRows
52
+ );
53
+ if (!h)
54
+ return {
55
+ kind: "incomplete",
56
+ geometry: { ...s, itemsPerLine: t || s.itemsPerLine }
57
+ };
58
+ const p = getComputedStyle(h), w = c ? O(
59
+ e === "inline" ? p.gridRowStart : p.gridColumnStart,
60
+ e === "inline" ? p.gridRowEnd : p.gridColumnEnd,
61
+ c.length
62
+ ) : null, z = t || (c && w ? Math.floor(c.length / w) : 0);
63
+ if (!z)
64
+ return a(
65
+ "Cannot infer grid capacity; set items-per-line for a uniform sequential layout."
66
+ );
67
+ for (let E = 0; E < Math.min(l.length, z * 3); E++) {
68
+ const P = l.item(E);
69
+ if (!P)
70
+ continue;
71
+ const _ = getComputedStyle(P), F = e === "inline" ? _.gridColumnStart : _.gridRowStart, I = e === "inline" ? _.gridColumnEnd : _.gridRowEnd, A = c ? O(
72
+ e === "inline" ? _.gridRowStart : _.gridColumnStart,
73
+ e === "inline" ? _.gridRowEnd : _.gridColumnEnd,
74
+ c.length
75
+ ) : null;
76
+ if (_.order !== "0" || !t && A !== w || O(F, I, b?.length ?? 1) !== 1)
77
+ return a("Items must use uniform spans and sequential DOM-order placement.");
78
+ }
79
+ const M = b?.[0];
80
+ if (M === void 0 || M === 0)
81
+ return { kind: "incomplete", geometry: { ...s, itemsPerLine: z } };
82
+ if (b?.some((E) => Math.abs(E - M) > 0.5))
83
+ return a("Grid lines must have a uniform pitch.");
84
+ const G = S(o) / r;
85
+ return {
86
+ kind: "ready",
87
+ geometry: {
88
+ itemsPerLine: z,
89
+ lineSize: (M + m) * G,
90
+ measured: !0
91
+ }
92
+ };
93
+ }
94
+ if (t > 1 || n.display.includes("flex") && (n.flexWrap !== "nowrap" || n.flexDirection !== (e === "inline" ? "row" : "column")))
95
+ return a("Non-grid collections must form one sequential, nonwrapping line of items.");
96
+ if (!h)
97
+ return { kind: "incomplete", geometry: { ...s, itemsPerLine: 1 } };
98
+ const d = h.getBoundingClientRect(), v = q(d, d, i), L = e === "inline" ? v.inlineSize : v.blockSize;
99
+ let R = L + m * S(o);
100
+ for (let c = 1; c < Math.min(l.length, 3); c++) {
101
+ const b = l.item(c);
102
+ if (!b)
103
+ continue;
104
+ const p = q(b.getBoundingClientRect(), d, i), w = e === "inline" ? p.inlineStart : p.blockStart, z = e === "inline" ? p.inlineSize : p.blockSize;
105
+ if (c === 1 && (R = w), Math.abs(z - L) > 0.5 || Math.abs(w - c * R) > 0.5 || R <= 0)
106
+ return a("Items must have a uniform pitch and follow DOM order.");
107
+ }
108
+ return R > 0 ? { kind: "ready", geometry: { itemsPerLine: 1, lineSize: R / r, measured: !0 } } : { kind: "incomplete", geometry: s };
109
+ }
110
+ const Y = B`:host{box-sizing:border-box;overflow-anchor:none;min-inline-size:0;display:block}:host([hidden]){display:none}:host([axis=inline]){align-items:stretch;inline-size:max-content;min-inline-size:100%;display:flex}:host([axis=inline]) ::slotted(*){flex:none}[part=spacer-start],[part=spacer-end]{overflow-anchor:none;pointer-events:none;flex:none;display:block}:host(:not([axis=inline])) [part=spacer-start],:host(:not([axis=inline])) [part=spacer-end]{inline-size:100%}`;
111
+ var ee = Object.defineProperty, te = Object.getOwnPropertyDescriptor, f = (o, e, i, t) => {
112
+ for (var s = t > 1 ? void 0 : t ? te(e, i) : e, r = o.length - 1, n; r >= 0; r--)
113
+ (n = o[r]) && (s = (t ? n(e, i, s) : n(s)) || s);
114
+ return t && s && ee(e, i, s), s;
115
+ };
116
+ const ie = 0.5, T = class T extends C {
117
+ constructor() {
118
+ super(...arguments), this.axis = "block", this._count = 0, this._itemSize = 0, this._overscan = 2, this._capacity = 0, this.disabled = !1, this.scrollTarget = null, this._spacerStart = 0, this._spacerEnd = 0, this._frame = new Z(this), this._geometry = { itemsPerLine: 1, lineSize: 0, measured: !1 }, this._needsSample = !0, this._resetGeometry = !1, this._unsupported = !1, this._warnedLayouts = /* @__PURE__ */ new Set(), this._$container = null, this._$scrollSource = null, this._contentObserver = null, this._flow = U, this._resolvedTarget = null, this._resizeObserver = null, this._unobserveLocale = null, this._lastDetail = null, this._needsResolve = !0, this._requestEvaluate = () => {
119
+ this.isConnected && this._frame.schedule(() => this._evaluate());
120
+ }, this._requestSample = () => {
121
+ this._needsSample = !0, this._requestEvaluate();
122
+ }, this._requestResolve = () => {
123
+ this._needsResolve = !0, this._requestSample();
124
+ }, this._observingLayout = !1;
125
+ }
126
+ get count() {
127
+ return this._count;
128
+ }
129
+ set count(e) {
130
+ const i = this._count;
131
+ this._count = x(e), this.requestUpdate("count", i);
132
+ }
133
+ get itemSize() {
134
+ return this._itemSize;
135
+ }
136
+ set itemSize(e) {
137
+ const i = this._itemSize;
138
+ this._itemSize = K(e), this.requestUpdate("itemSize", i);
139
+ }
140
+ get itemsPerLine() {
141
+ return this._capacity;
142
+ }
143
+ set itemsPerLine(e) {
144
+ const i = this._capacity;
145
+ this._capacity = x(e), this.requestUpdate("itemsPerLine", i);
146
+ }
147
+ get overscan() {
148
+ return this._overscan;
149
+ }
150
+ set overscan(e) {
151
+ const i = this._overscan;
152
+ this._overscan = x(e, 2), this.requestUpdate("overscan", i);
153
+ }
154
+ /** Read-only first reported item index, also exposed as the computed `first` attribute. */
155
+ get first() {
156
+ return this._lastDetail?.start ?? 0;
157
+ }
158
+ /** Read-only last reported item index, or -1 for an empty range; also exposed as `last`. */
159
+ get last() {
160
+ return (this._lastDetail?.end ?? 0) - 1;
161
+ }
162
+ /**
163
+ * The most recently reported range, or `null` before the first measurement.
164
+ * The element measures on its own schedule, which can be before a consumer
165
+ * has attached its listener; render from this once when subscribing, then
166
+ * follow `rc-virtual-scroller-range`. Identical ranges are never re-sent, so
167
+ * a subscriber that skips this can wait for an event that will not come.
168
+ */
169
+ get range() {
170
+ return this._lastDetail;
171
+ }
172
+ connectedCallback() {
173
+ super.connectedCallback(), this.addEventListener("focusin", this._requestResolve), this.addEventListener("focusout", this._requestEvaluate), typeof ResizeObserver == "function" && (this._resizeObserver = new ResizeObserver(this._requestResolve)), typeof MutationObserver == "function" && (this._contentObserver = new MutationObserver(this._requestSample)), this._unobserveLocale = H(this._requestResolve, this), this.ownerDocument.defaultView?.addEventListener("resize", this._requestResolve), this._requestResolve();
174
+ }
175
+ disconnectedCallback() {
176
+ this.removeEventListener("focusin", this._requestResolve), this.removeEventListener("focusout", this._requestEvaluate), this._detachScrollTarget(), this._resizeObserver?.disconnect(), this._resizeObserver = null, this._observingLayout = !1, this._contentObserver?.disconnect(), this._contentObserver = null, this._$container = null, this.ownerDocument.defaultView?.removeEventListener("resize", this._requestResolve), this._unobserveLocale?.(), this._unobserveLocale = null, super.disconnectedCallback();
177
+ }
178
+ firstUpdated() {
179
+ this._requestResolve();
180
+ }
181
+ updated(e) {
182
+ (e.has("axis") || e.has("itemsPerLine")) && (this._resetGeometry = !0), (e.has("axis") || e.has("itemsPerLine") || e.has("count") || e.has("itemSize") || e.has("overscan") || e.has("disabled") || e.has("scrollTarget")) && this._requestResolve();
183
+ }
184
+ render() {
185
+ const e = this.axis === "inline" ? "inlineSize" : "blockSize";
186
+ return W`
187
+ <div part="spacer-start" style=${D({ [e]: `${this._spacerStart}px` })}></div>
188
+ <slot @slotchange=${this._requestResolve}></slot>
189
+ <div part="spacer-end" style=${D({ [e]: `${this._spacerEnd}px` })}></div>
190
+ `;
191
+ }
192
+ /**
193
+ * Scrolls the item at `index` into view along `axis`. Accurate to the current
194
+ * line pitch, which means accurate to `item-size` until at least one line has
195
+ * rendered.
196
+ */
197
+ scrollToIndex(e, i = {}) {
198
+ if (!this.isConnected || !Number.isFinite(e) || this.count === 0 || this._unsupported)
199
+ return;
200
+ const t = this._resolvedTarget ?? this._attachScrollTarget(), s = this._effectiveLineSize();
201
+ if (!t || !s)
202
+ return;
203
+ this._flow = $(this);
204
+ const r = Math.min(Math.max(Math.floor(e), 0), Math.max(this.count - 1, 0)), n = Math.floor(r / this._geometry.itemsPerLine) * s, { viewStart: l, viewSize: h } = this._view(t), m = i.align ?? "start";
205
+ let a = n;
206
+ if (m === "center")
207
+ a = n - (h - s) / 2;
208
+ else if (m === "end")
209
+ a = n - (h - s);
210
+ else if (m === "nearest") {
211
+ if (n >= l && n + s <= l + h)
212
+ return;
213
+ a = n < l ? n : n - (h - s);
214
+ }
215
+ const g = S(this), d = t === this._documentScroller() ? 1 : S(t), v = (a - l) * g * (j(this.axis, this._flow) ? -1 : 1) / d, L = i.behavior ?? "auto";
216
+ V(this.axis, this._flow) === "x" ? t.scrollBy({ left: v, behavior: L }) : t.scrollBy({ top: v, behavior: L });
217
+ }
218
+ /**
219
+ * Re-measures the slotted container and re-evaluates the range. Call this
220
+ * when something outside the element's own observers changed the item size,
221
+ * such as a font load or a theme swap.
222
+ */
223
+ measure() {
224
+ this._resetGeometry = !0, this._requestResolve();
225
+ }
226
+ _attachScrollTarget() {
227
+ const e = this.scrollTarget ?? J(this);
228
+ return e === this._resolvedTarget || (this._detachScrollTarget(), this._resolvedTarget = e, this._$scrollSource = e === this._documentScroller() ? this.ownerDocument : e, this._$scrollSource?.addEventListener("scroll", this._requestEvaluate, { passive: !0 })), e;
229
+ }
230
+ _detachScrollTarget() {
231
+ this._$scrollSource?.removeEventListener("scroll", this._requestEvaluate), this._$scrollSource = null, this._resolvedTarget = null;
232
+ }
233
+ /** The scroll target's visible client box in viewport coordinates. */
234
+ _portViewportRect(e) {
235
+ if (e === this._documentScroller()) {
236
+ const s = this.ownerDocument.documentElement;
237
+ return new DOMRect(0, 0, s.clientWidth, s.clientHeight);
238
+ }
239
+ const i = e.getBoundingClientRect(), t = S(e);
240
+ return new DOMRect(
241
+ i.left + e.clientLeft * t,
242
+ i.top + e.clientTop * t,
243
+ e.clientWidth * t,
244
+ e.clientHeight * t
245
+ );
246
+ }
247
+ _documentScroller() {
248
+ return this.ownerDocument.scrollingElement ?? this.ownerDocument.documentElement;
249
+ }
250
+ /**
251
+ * Where the visible part of the scroll target sits relative to the host,
252
+ * measured from the host's start edge along `axis` in the host's own flow.
253
+ * Working from the two rects rather than from `scrollTop`/`scrollLeft`
254
+ * keeps the arithmetic identical in every writing mode and direction.
255
+ * Reported in the host's own CSS pixels, the unit its spacers are written in.
256
+ */
257
+ _view(e) {
258
+ const i = q(
259
+ this._portViewportRect(e),
260
+ this.getBoundingClientRect(),
261
+ this._flow
262
+ ), t = S(this);
263
+ return this.axis === "inline" ? {
264
+ viewStart: i.inlineStart / t,
265
+ viewSize: i.inlineSize / t
266
+ } : {
267
+ viewStart: i.blockStart / t,
268
+ viewSize: i.blockSize / t
269
+ };
270
+ }
271
+ _effectiveLineSize() {
272
+ return this._geometry.measured ? this._geometry.lineSize : this.itemSize;
273
+ }
274
+ /** Rebind observers only when their actual targets changed. */
275
+ _observeLayout() {
276
+ const e = this.firstElementChild, i = this._resolvedTarget;
277
+ if (this._attachScrollTarget(), e !== this._$container)
278
+ this._$container = e, this._resetGeometry = !0, this._contentObserver?.disconnect(), e && this._contentObserver?.observe(e, { childList: !0 });
279
+ else if (i === this._resolvedTarget && this._observingLayout)
280
+ return;
281
+ this._observingLayout = !0, this._resizeObserver?.disconnect(), this._resizeObserver?.observe(this), e && this._resizeObserver?.observe(e), this._resolvedTarget && this._resolvedTarget !== this._documentScroller() && this._resizeObserver?.observe(this._resolvedTarget);
282
+ }
283
+ /**
284
+ * Focus retargets to a closed shadow host. Stop at the item that owns it,
285
+ * rather than asking ordinary contains() to cross a shadow boundary.
286
+ */
287
+ _focusedIndex(e) {
288
+ const i = this.getRootNode();
289
+ let t = i instanceof ShadowRoot ? i.activeElement : this.ownerDocument.activeElement;
290
+ for (; t; ) {
291
+ let s = t;
292
+ for (; s && s.parentElement !== e; ) {
293
+ const r = s.getRootNode();
294
+ s = s.assignedSlot ?? s.parentElement ?? (r instanceof ShadowRoot ? r.host : null);
295
+ }
296
+ if (s)
297
+ return this.first + Array.prototype.indexOf.call(e.children, s);
298
+ t = t.shadowRoot?.activeElement ?? null;
299
+ }
300
+ return -1;
301
+ }
302
+ _evaluate() {
303
+ if (!this.isConnected)
304
+ return;
305
+ (this._needsResolve || !this._resolvedTarget) && (this._needsResolve = !1, this._observeLayout(), this._flow = $(this));
306
+ const e = this._$container, i = this._resolvedTarget;
307
+ if (this.disabled || !e || this.count === 0) {
308
+ this._applyRange(0, this.count, 0, 0);
309
+ return;
310
+ }
311
+ if (!i || !this.getClientRects().length || !i.getClientRects().length) {
312
+ this._lastDetail && this._lastDetail.end > this.count && this._applyRange(
313
+ Math.min(this.first, this.count),
314
+ this.count,
315
+ this._spacerStart,
316
+ this._spacerEnd
317
+ );
318
+ return;
319
+ }
320
+ if (this._resetGeometry && (this._resetGeometry = !1, this._geometry = {
321
+ itemsPerLine: this.itemsPerLine || 1,
322
+ lineSize: this.itemSize,
323
+ measured: !1
324
+ }, this._needsSample = !0), this._needsSample) {
325
+ this._needsSample = !1;
326
+ const r = X(
327
+ e,
328
+ this.axis,
329
+ this._flow,
330
+ this.itemsPerLine,
331
+ this._geometry,
332
+ S(this)
333
+ );
334
+ this._unsupported = r.kind === "unsupported", r.kind === "unsupported" || (this._geometry = r.geometry);
335
+ }
336
+ const t = this._effectiveLineSize();
337
+ if (this._unsupported || !t) {
338
+ this._applyRange(0, this.count, 0, 0);
339
+ return;
340
+ }
341
+ const s = Q({
342
+ ...this._geometry,
343
+ lineSize: t,
344
+ count: this.count,
345
+ overscan: this.overscan,
346
+ ...this._view(i),
347
+ focused: this._focusedIndex(e)
348
+ });
349
+ this._applyRange(s.start, s.end, s.spacerStart, s.spacerEnd);
350
+ }
351
+ _applyRange(e, i, t, s) {
352
+ this._spacerStart = t, this._spacerEnd = s, this.setAttribute("first", String(e)), this.setAttribute("last", String(i - 1));
353
+ const r = {
354
+ start: e,
355
+ end: i,
356
+ itemsPerLine: this._geometry.itemsPerLine,
357
+ lineSize: this._effectiveLineSize(),
358
+ measured: this._geometry.measured
359
+ }, n = this._lastDetail;
360
+ n && n.start === r.start && n.end === r.end && n.itemsPerLine === r.itemsPerLine && n.measured === r.measured && Math.abs(n.lineSize - r.lineSize) < ie || (this._lastDetail = Object.freeze(r), this.dispatchEvent(
361
+ new CustomEvent("rc-virtual-scroller-range", {
362
+ detail: r,
363
+ bubbles: !0,
364
+ composed: !0
365
+ })
366
+ ));
367
+ }
368
+ };
369
+ T.styles = Y, T.enabledWarnings = (C.enabledWarnings ?? []).filter(
370
+ (e) => e !== "change-in-update"
371
+ );
372
+ let u = T;
373
+ f([
374
+ y({ reflect: !0 })
375
+ ], u.prototype, "axis", 2);
376
+ f([
377
+ y({ type: Number })
378
+ ], u.prototype, "count", 1);
379
+ f([
380
+ y({ type: Number, attribute: "item-size" })
381
+ ], u.prototype, "itemSize", 1);
382
+ f([
383
+ y({ type: Number, attribute: "items-per-line" })
384
+ ], u.prototype, "itemsPerLine", 1);
385
+ f([
386
+ y({ type: Number })
387
+ ], u.prototype, "overscan", 1);
388
+ f([
389
+ y({ type: Boolean, reflect: !0 })
390
+ ], u.prototype, "disabled", 2);
391
+ f([
392
+ y({ attribute: !1 })
393
+ ], u.prototype, "scrollTarget", 2);
394
+ f([
395
+ N()
396
+ ], u.prototype, "_spacerStart", 2);
397
+ f([
398
+ N()
399
+ ], u.prototype, "_spacerEnd", 2);
400
+ export {
401
+ u as R
402
+ };
403
+ //# sourceMappingURL=rc-virtual-scroller-BA_t6Xkx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-virtual-scroller-BA_t6Xkx.js","sources":["../src/range.ts","../src/sample.ts","../src/rc-virtual-scroller.styles.ts","../src/rc-virtual-scroller.ts"],"sourcesContent":["/** A finite, nonnegative integer configuration value. */\nexport function whole(value: number, fallback = 0): number {\n return Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;\n}\n\n/** A finite positive pixel estimate, or zero when unavailable. */\nexport function positive(value: number): number {\n return Number.isFinite(value) && value > 0 ? value : 0;\n}\n\nexport interface Geometry {\n /** Effective number of sequential items in a line. */\n itemsPerLine: number;\n\n /** Line pitch including the inter-line gap. */\n lineSize: number;\n\n /** Whether the pitch came from layout rather than the author estimate. */\n measured: boolean;\n}\n\nexport interface WindowInput extends Geometry {\n count: number;\n overscan: number;\n viewStart: number;\n viewSize: number;\n focused: number;\n}\n\nexport interface WindowRange {\n start: number;\n end: number;\n spacerStart: number;\n spacerEnd: number;\n}\n\n/** Calculate a contiguous window from validated, logical layout facts. */\nexport function calculateWindow(input: WindowInput): WindowRange {\n const { count, overscan, itemsPerLine, lineSize, viewStart, viewSize, focused } = input;\n const lines = Math.ceil(count / itemsPerLine);\n // Half a pixel absorbs rect snapping at exact line boundaries in Firefox.\n const firstLine = Math.min(\n lines,\n Math.max(0, Math.floor((Math.max(0, viewStart) + 0.5) / lineSize) - overscan),\n );\n const lastLine = Math.min(\n lines,\n Math.max(firstLine, Math.ceil((viewStart + viewSize - 0.5) / lineSize) + overscan),\n );\n\n let start = Math.min(count, firstLine * itemsPerLine);\n let end = Math.min(count, lastLine * itemsPerLine);\n\n if (focused >= 0 && focused < count) {\n const focusedLine = Math.floor(focused / itemsPerLine);\n\n start = Math.min(start, focusedLine * itemsPerLine);\n end = Math.max(end, Math.min(count, (focusedLine + 1) * itemsPerLine));\n }\n\n return {\n start,\n end,\n spacerStart: Math.floor(start / itemsPerLine) * lineSize,\n spacerEnd: Math.max(0, lines - Math.ceil(end / itemsPerLine)) * lineSize,\n };\n}\n\n/** Read resolved pixel tracks, ignoring CSS grid line-name annotations. */\nexport function resolvedTracks(value: string): number[] | null {\n const tokens = value\n .replace(/\\[[^\\]]*\\]/g, '')\n .trim()\n .split(/\\s+/);\n\n if (!tokens.every((token) => /^\\d+(?:\\.\\d+)?px$/.test(token))) {\n return null;\n }\n\n return tokens.map(Number.parseFloat);\n}\n\n/** Resolve the deliberately small, numeric subset of grid span syntax we support. */\nexport function trackSpan(start: string, end: string, count: number): number | null {\n if (start === 'auto' && end === 'auto') {\n return 1;\n }\n\n const span = /^span (\\d+)$/.exec(start === 'auto' ? end : start);\n\n if (span && (start === 'auto' || end === 'auto')) {\n return Math.max(1, Number(span[1]));\n }\n\n if (start === '1' && (end === '-1' || end === String(count + 1))) {\n return count;\n }\n\n return null;\n}\n","import { cssZoom, logicalRect, type Flow, type LogicalAxis } from '@rcarls/rc-common';\nimport { resolvedTracks, trackSpan, type Geometry } from './range.js';\n\nexport type LayoutSample =\n | { kind: 'ready' | 'incomplete'; geometry: Geometry }\n | { kind: 'unsupported'; reason: string };\n\n/**\n * Sample regular line geometry without changing the consumer's layout.\n * Geometry is reported in the host's CSS pixels, whatever units each\n * measurement is read in.\n */\nexport function sampleLayout(\n $container: Element,\n axis: LogicalAxis,\n flow: Flow,\n override: number,\n previous: Geometry,\n hostZoom = 1,\n): LayoutSample {\n const styles = getComputedStyle($container);\n const $items = $container.children;\n const $first = $items.item(0);\n const gap = Number.parseFloat(axis === 'inline' ? styles.columnGap : styles.rowGap) || 0;\n const unsupported = (reason: string): LayoutSample => ({ kind: 'unsupported', reason });\n const grid = styles.display === 'grid' || styles.display === 'inline-grid';\n\n if (grid) {\n if (styles.gridAutoFlow.includes('dense')) {\n return unsupported('Dense grid placement is not a sequential collection.');\n }\n\n if (styles.gridAutoFlow !== (axis === 'inline' ? 'column' : 'row')) {\n return unsupported('Grid auto-flow must advance along the windowed axis.');\n }\n\n const cross = resolvedTracks(\n axis === 'inline' ? styles.gridTemplateRows : styles.gridTemplateColumns,\n );\n const along = resolvedTracks(\n axis === 'inline' ? styles.gridTemplateColumns : styles.gridTemplateRows,\n );\n\n if (!$first) {\n return {\n kind: 'incomplete',\n geometry: { ...previous, itemsPerLine: override || previous.itemsPerLine },\n };\n }\n\n const firstStyle = getComputedStyle($first);\n const span = cross\n ? trackSpan(\n axis === 'inline' ? firstStyle.gridRowStart : firstStyle.gridColumnStart,\n axis === 'inline' ? firstStyle.gridRowEnd : firstStyle.gridColumnEnd,\n cross.length,\n )\n : null;\n const capacity = override || (cross && span ? Math.floor(cross.length / span) : 0);\n\n if (!capacity) {\n return unsupported(\n 'Cannot infer grid capacity; set items-per-line for a uniform sequential layout.',\n );\n }\n\n // Inspect a bounded sample even when focus pins thousands of rendered items.\n for (let i = 0; i < Math.min($items.length, capacity * 3); i++) {\n const $item = $items.item(i);\n\n if (!$item) {\n continue;\n }\n\n const itemStyle = getComputedStyle($item);\n const alongStart = axis === 'inline' ? itemStyle.gridColumnStart : itemStyle.gridRowStart;\n const alongEnd = axis === 'inline' ? itemStyle.gridColumnEnd : itemStyle.gridRowEnd;\n const itemSpan = cross\n ? trackSpan(\n axis === 'inline' ? itemStyle.gridRowStart : itemStyle.gridColumnStart,\n axis === 'inline' ? itemStyle.gridRowEnd : itemStyle.gridColumnEnd,\n cross.length,\n )\n : null;\n\n if (\n itemStyle.order !== '0' ||\n (!override && itemSpan !== span) ||\n trackSpan(alongStart, alongEnd, along?.length ?? 1) !== 1\n ) {\n return unsupported('Items must use uniform spans and sequential DOM-order placement.');\n }\n }\n\n const trackSize = along?.[0];\n\n if (trackSize === undefined || trackSize === 0) {\n return { kind: 'incomplete', geometry: { ...previous, itemsPerLine: capacity } };\n }\n\n if (along?.some((size) => Math.abs(size - trackSize) > 0.5)) {\n return unsupported('Grid lines must have a uniform pitch.');\n }\n\n // Computed tracks and gaps are in the container's own CSS pixels.\n const containerToHostCssScale = cssZoom($container) / hostZoom;\n\n return {\n kind: 'ready',\n geometry: {\n itemsPerLine: capacity,\n lineSize: (trackSize + gap) * containerToHostCssScale,\n measured: true,\n },\n };\n }\n\n if (\n override > 1 ||\n (styles.display.includes('flex') &&\n (styles.flexWrap !== 'nowrap' ||\n styles.flexDirection !== (axis === 'inline' ? 'row' : 'column')))\n ) {\n return unsupported('Non-grid collections must form one sequential, nonwrapping line of items.');\n }\n\n if (!$first) {\n return { kind: 'incomplete', geometry: { ...previous, itemsPerLine: 1 } };\n }\n\n const firstRect = $first.getBoundingClientRect();\n const own = logicalRect(firstRect, firstRect, flow);\n const extent = axis === 'inline' ? own.inlineSize : own.blockSize;\n // Rects are in viewport pixels; the computed gap is in the container's own.\n let pitch = extent + gap * cssZoom($container);\n\n for (let i = 1; i < Math.min($items.length, 3); i++) {\n const $item = $items.item(i);\n\n if (!$item) {\n continue;\n }\n\n const rect = logicalRect($item.getBoundingClientRect(), firstRect, flow);\n const start = axis === 'inline' ? rect.inlineStart : rect.blockStart;\n const size = axis === 'inline' ? rect.inlineSize : rect.blockSize;\n\n if (i === 1) {\n pitch = start;\n }\n\n if (Math.abs(size - extent) > 0.5 || Math.abs(start - i * pitch) > 0.5 || pitch <= 0) {\n return unsupported('Items must have a uniform pitch and follow DOM order.');\n }\n }\n\n return pitch > 0\n ? { kind: 'ready', geometry: { itemsPerLine: 1, lineSize: pitch / hostZoom, measured: true } }\n : { kind: 'incomplete', geometry: previous };\n}\n","import { css } from 'lit';\n\nexport const virtualScrollerStyles = css`\n :host {\n display: block;\n min-inline-size: 0;\n box-sizing: border-box;\n /*\n * Scroll anchoring exists to keep the reading position stable when content\n * before the viewport changes size. That is exactly what this element does\n * on every frame, so leaving anchoring on makes the browser fight the\n * spacer writes and the scroll position drifts. This is a correctness\n * requirement, not a tuning knob, so it is not a custom property.\n */\n overflow-anchor: none;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n /*\n * Windowing the inline axis lays the spacers and the container out along\n * it. Flex rows follow the writing mode and direction, so the start spacer\n * lands on the logical start in RTL and in vertical text alike.\n */\n :host([axis='inline']) {\n display: flex;\n align-items: stretch;\n inline-size: max-content;\n min-inline-size: 100%;\n }\n\n :host([axis='inline']) ::slotted(*) {\n flex: none;\n }\n\n [part='spacer-start'],\n [part='spacer-end'] {\n /*\n * The spacers stand in for the lines that are not rendered. They must not\n * collapse margins with the slotted container or contribute a cross-axis\n * size, and they carry no paint of their own.\n */\n display: block;\n flex: none;\n overflow-anchor: none;\n pointer-events: none;\n }\n\n :host(:not([axis='inline'])) [part='spacer-start'],\n :host(:not([axis='inline'])) [part='spacer-end'] {\n inline-size: 100%;\n }\n`;\n\nexport default virtualScrollerStyles;\n","import { LitElement, html } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { styleMap } from 'lit/directives/style-map.js';\n\nimport {\n HORIZONTAL_LTR_FLOW,\n RafScheduler,\n cssZoom,\n findNearestScrollAncestor,\n isReversed,\n logicalRect,\n observeLocale,\n physicalAxis,\n resolveFlow,\n type Flow,\n type LogicalAxis,\n} from '@rcarls/rc-common';\nimport { calculateWindow, positive, whole, type Geometry } from './range.js';\nimport { sampleLayout } from './sample.js';\n\nimport virtualScrollerStyles from './rc-virtual-scroller.styles.js';\n\nexport type RCVirtualScrollerAxis = LogicalAxis;\nexport type RCVirtualScrollerAlign = 'start' | 'center' | 'end' | 'nearest';\n\nexport interface RCVirtualScrollerRangeDetail {\n /** First item index the consumer should render, inclusive. */\n readonly start: number;\n /** Item index one past the last the consumer should render. */\n readonly end: number;\n /**\n * Effective items in one line: the explicit override, inferred uniform grid\n * capacity accounting for spans, or one for a non-grid collection.\n */\n readonly itemsPerLine: number;\n /** Line pitch in pixels along the scrolling axis, including the gap. */\n readonly lineSize: number;\n /** `false` while `lineSize` is still the `item-size` estimate, `true` once a real line was measured. */\n readonly measured: boolean;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-virtual-scroller': RCVirtualScroller;\n }\n\n interface HTMLElementEventMap {\n 'rc-virtual-scroller-range': CustomEvent<RCVirtualScrollerRangeDetail>;\n }\n}\n\n/** Line pitch differences below this are measurement noise, not a real change. */\nconst LINE_SIZE_EPSILON = 0.5;\n\n/**\n * Reports which slice of a long collection is currently on screen and reserves\n * the scroll space for the rest, so the consumer can render a few dozen items\n * instead of a few thousand.\n *\n * The element is deliberately headless: it never creates, recycles, or\n * positions item DOM. It measures geometry, dispatches\n * `rc-virtual-scroller-range`, and sizes two spacers around the default slot.\n * The consumer slots its own single container element (a `<ul>`, an\n * `<rc-list>`, a `<table>` body) and renders `detail.start`..`detail.end` into\n * it with whatever framework it already uses. That keeps the markup, the CSS\n * layout, and the accessibility semantics with the code that owns them, and it\n * means a native `grid-template-columns: repeat(auto-fill, ...)` keeps working\n * untouched.\n *\n * `axis` is logical. `block` (the default) windows lines stacked the way\n * paragraphs stack, which is vertical in horizontal writing modes and\n * horizontal in vertical ones. `inline` windows a row of items running the way\n * text runs, such as a horizontal shelf; the slotted container then lays its\n * items along the inline axis (`display: flex`, or a grid with\n * `grid-auto-flow: column`). Direction and writing mode are read from the\n * element's own computed style, so RTL and vertical text need no configuration.\n *\n * Virtualization requires sequential DOM-order items with uniform line pitch\n * and capacity (the final line may be partial). Regular grids, uniform numeric\n * spans, lists, and nonwrapping shelves are measured from CSS. Use\n * `items-per-line` for ambiguous grid capacity; it does not enable variable\n * pitch, masonry, dense placement, or reordered items. Detectably unsupported\n * layouts render the full collection and warn in development. `item-size` is\n * only a bootstrap estimate. Hidden layouts retain their last valid window.\n *\n * Accessibility is the consumer's, because the consumer owns the roles. A\n * virtualized set must still tell assistive technology its real size: put\n * `aria-setsize` (the true total) and `aria-posinset` (the item's real index,\n * not its DOM position) on each item, or `aria-rowcount` / `aria-rowindex` for\n * a grid. See the package README. The one part this element does handle is\n * focus: the reported range is always widened to include the focused item, so\n * scrolling never silently drops focus to `<body>`.\n *\n * @slot - The consumer's single container element, holding only the items in the current range.\n *\n * @csspart spacer-start - Space along the scrolling axis standing in for the lines before the range.\n * @csspart spacer-end - Space along the scrolling axis standing in for the lines after the range.\n *\n * @attr axis - Logical axis to window along: `block` (the default) or `inline`.\n * @attr count - Total number of items in the collection, including the ones not rendered.\n * @attr item-size - Estimated line pitch in pixels, used until a real line can be measured.\n * @attr items-per-line - Explicit grid capacity; zero (the default) restores automatic inference.\n * @attr overscan - Extra lines rendered beyond each edge of the viewport. Defaults to `2`.\n * @attr disabled - Reports the whole collection as the range and stops measuring.\n * @attr [first] - Reflected, computed. First item index in the current range.\n * @attr [last] - Reflected, computed. Last item index in the current range, inclusive. `-1` when the range is empty.\n *\n * @fires rc-virtual-scroller-range - The visible range changed, or its geometry was re-measured.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-virtual-scroller}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/feed/ | WAI-ARIA APG: Feed pattern}\n */\nexport class RCVirtualScroller extends LitElement {\n static override styles = virtualScrollerStyles;\n\n // Geometry can only be measured after render, and the reflected range\n // attributes are written from that measurement, so an update legitimately\n // schedules one follow-up update.\n static override enabledWarnings = (LitElement.enabledWarnings ?? []).filter(\n (warning) => warning !== 'change-in-update',\n );\n\n /** Logical axis to window along. */\n @property({ reflect: true })\n axis: RCVirtualScrollerAxis = 'block';\n\n private _count = 0;\n private _itemSize = 0;\n private _overscan = 2;\n private _capacity = 0;\n\n /** Total collection size, normalized to a nonnegative integer. Invalid values become zero. Host writes are silent. */\n @property({ type: Number })\n get count(): number {\n return this._count;\n }\n\n set count(value: number) {\n const old = this._count;\n\n this._count = whole(value);\n this.requestUpdate('count', old);\n }\n\n /**\n * Estimated line pitch in pixels, used for the first frame and whenever no\n * line is available to measure. Get this roughly right: it is what the very\n * first `rc-virtual-scroller-range` is computed from, and what a restored\n * scroll position lands against. Invalid or nonpositive values become zero.\n * Host writes are silent.\n */\n @property({ type: Number, attribute: 'item-size' })\n get itemSize(): number {\n return this._itemSize;\n }\n\n set itemSize(value: number) {\n const old = this._itemSize;\n\n this._itemSize = positive(value);\n this.requestUpdate('itemSize', old);\n }\n\n /** Grid capacity override, normalized to a nonnegative integer; zero means automatic. Host writes are silent. */\n @property({ type: Number, attribute: 'items-per-line' })\n get itemsPerLine(): number {\n return this._capacity;\n }\n\n set itemsPerLine(value: number) {\n const old = this._capacity;\n\n this._capacity = whole(value);\n this.requestUpdate('itemsPerLine', old);\n }\n\n /** Extra lines beyond each viewport edge, floored to an integer. Invalid/negative values become two. Host writes are silent. */\n @property({ type: Number })\n get overscan(): number {\n return this._overscan;\n }\n\n set overscan(value: number) {\n const old = this._overscan;\n\n this._overscan = whole(value, 2);\n this.requestUpdate('overscan', old);\n }\n\n /**\n * Reports the whole collection as the range and stops measuring. An escape\n * hatch for short collections, printing, and A/B measurement; the consumer's\n * render path stays the same either way.\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * The scroll container to measure against. Defaults to the nearest scrolling\n * ancestor. Set this when the ancestor only becomes scrollable after its own\n * upgrade, or when the intended scrollport is further up than the first\n * `overflow: auto` element.\n */\n @property({ attribute: false })\n scrollTarget: Element | null = null;\n\n /** Read-only first reported item index, also exposed as the computed `first` attribute. */\n get first(): number {\n return this._lastDetail?.start ?? 0;\n }\n\n /** Read-only last reported item index, or -1 for an empty range; also exposed as `last`. */\n get last(): number {\n return (this._lastDetail?.end ?? 0) - 1;\n }\n\n /**\n * The most recently reported range, or `null` before the first measurement.\n * The element measures on its own schedule, which can be before a consumer\n * has attached its listener; render from this once when subscribing, then\n * follow `rc-virtual-scroller-range`. Identical ranges are never re-sent, so\n * a subscriber that skips this can wait for an event that will not come.\n */\n get range(): RCVirtualScrollerRangeDetail | null {\n return this._lastDetail;\n }\n\n @state()\n private _spacerStart = 0;\n\n @state()\n private _spacerEnd = 0;\n\n private readonly _frame = new RafScheduler(this);\n\n private _geometry: Geometry = { itemsPerLine: 1, lineSize: 0, measured: false };\n private _needsSample = true;\n private _resetGeometry = false;\n private _unsupported = false;\n private readonly _warnedLayouts = new Set<string>();\n private _$container: Element | null = null;\n private _$scrollSource: EventTarget | null = null;\n private _contentObserver: MutationObserver | null = null;\n\n /** The host's own flow, resolved with the scroll target rather than per frame. */\n private _flow: Flow = HORIZONTAL_LTR_FLOW;\n\n private _resolvedTarget: Element | null = null;\n\n private _resizeObserver: ResizeObserver | null = null;\n\n private _unobserveLocale: (() => void) | null = null;\n\n private _lastDetail: RCVirtualScrollerRangeDetail | null = null;\n\n /** Set when the surrounding layout changed enough to re-find the scrollport. */\n private _needsResolve = true;\n\n override connectedCallback(): void {\n super.connectedCallback();\n\n // Focus moving in or out changes which items must stay rendered, and\n // nothing else would tell us: a blur fires no scroll and no resize.\n this.addEventListener('focusin', this._requestResolve);\n this.addEventListener('focusout', this._requestEvaluate);\n\n if (typeof ResizeObserver === 'function') {\n this._resizeObserver = new ResizeObserver(this._requestResolve);\n }\n\n if (typeof MutationObserver === 'function') {\n this._contentObserver = new MutationObserver(this._requestSample);\n }\n\n // A direction flip reverses the inline axis without resizing anything.\n this._unobserveLocale = observeLocale(this._requestResolve, this);\n this.ownerDocument.defaultView?.addEventListener('resize', this._requestResolve);\n this._requestResolve();\n }\n\n override disconnectedCallback(): void {\n this.removeEventListener('focusin', this._requestResolve);\n this.removeEventListener('focusout', this._requestEvaluate);\n this._detachScrollTarget();\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n this._observingLayout = false;\n this._contentObserver?.disconnect();\n this._contentObserver = null;\n this._$container = null;\n this.ownerDocument.defaultView?.removeEventListener('resize', this._requestResolve);\n this._unobserveLocale?.();\n this._unobserveLocale = null;\n\n super.disconnectedCallback();\n }\n\n protected override firstUpdated(): void {\n if (import.meta.env.DEV && !this.firstElementChild) {\n console.warn(\n '[rc-virtual-scroller] No child element found. Slot a single container element (a <ul>, an <rc-list>) holding the items in the current range.',\n this,\n );\n }\n\n this._requestResolve();\n }\n\n protected override updated(changed: Map<PropertyKey, unknown>): void {\n if (changed.has('axis') || changed.has('itemsPerLine')) {\n this._resetGeometry = true;\n }\n\n if (\n changed.has('axis') ||\n changed.has('itemsPerLine') ||\n changed.has('count') ||\n changed.has('itemSize') ||\n changed.has('overscan') ||\n changed.has('disabled') ||\n changed.has('scrollTarget')\n ) {\n this._requestResolve();\n }\n }\n\n protected override render() {\n const size = this.axis === 'inline' ? 'inlineSize' : 'blockSize';\n\n return html`\n <div part=\"spacer-start\" style=${styleMap({ [size]: `${this._spacerStart}px` })}></div>\n <slot @slotchange=${this._requestResolve}></slot>\n <div part=\"spacer-end\" style=${styleMap({ [size]: `${this._spacerEnd}px` })}></div>\n `;\n }\n\n /**\n * Scrolls the item at `index` into view along `axis`. Accurate to the current\n * line pitch, which means accurate to `item-size` until at least one line has\n * rendered.\n */\n scrollToIndex(\n index: number,\n options: { align?: RCVirtualScrollerAlign; behavior?: ScrollBehavior } = {},\n ): void {\n if (!this.isConnected || !Number.isFinite(index) || this.count === 0 || this._unsupported) {\n return;\n }\n\n const target = this._resolvedTarget ?? this._attachScrollTarget();\n const lineSize = this._effectiveLineSize();\n\n if (!target || !lineSize) {\n return;\n }\n\n // Read at call time, like any other interaction: the direction may have\n // flipped in this same task, before the observer's notice arrives.\n this._flow = resolveFlow(this);\n\n const clamped = Math.min(Math.max(Math.floor(index), 0), Math.max(this.count - 1, 0));\n const lineStart = Math.floor(clamped / this._geometry.itemsPerLine) * lineSize;\n const { viewStart, viewSize } = this._view(target);\n const align = options.align ?? 'start';\n\n let desired = lineStart;\n\n if (align === 'center') {\n desired = lineStart - (viewSize - lineSize) / 2;\n } else if (align === 'end') {\n desired = lineStart - (viewSize - lineSize);\n } else if (align === 'nearest') {\n if (lineStart >= viewStart && lineStart + lineSize <= viewStart + viewSize) {\n return;\n }\n\n desired = lineStart < viewStart ? lineStart : lineStart - (viewSize - lineSize);\n }\n\n // A relative scroll, expressed in the host's own flow, means the scroll\n // container's origin (negative scrollLeft in RTL and vertical-rl) never\n // has to be interpreted here.\n // The scroll target scrolls in its own CSS pixels, which differ from the\n // host's when only one of them sits under a `zoom`.\n const hostZoom = cssZoom(this);\n const targetZoom = target === this._documentScroller() ? 1 : cssZoom(target);\n const scrollDeltaTargetCssPx =\n ((desired - viewStart) * hostZoom * (isReversed(this.axis, this._flow) ? -1 : 1)) /\n targetZoom;\n const behavior = options.behavior ?? 'auto';\n\n if (physicalAxis(this.axis, this._flow) === 'x') {\n target.scrollBy({ left: scrollDeltaTargetCssPx, behavior });\n } else {\n target.scrollBy({ top: scrollDeltaTargetCssPx, behavior });\n }\n }\n\n /**\n * Re-measures the slotted container and re-evaluates the range. Call this\n * when something outside the element's own observers changed the item size,\n * such as a font load or a theme swap.\n */\n measure(): void {\n this._resetGeometry = true;\n this._requestResolve();\n }\n\n /**\n * Schedules an evaluation. `RafScheduler` keeps only the most recently\n * scheduled callback, so every path has to funnel through this single one:\n * a scroll arriving between a resize and its frame must not be able to drop\n * the resize's work.\n */\n private readonly _requestEvaluate = (): void => {\n if (this.isConnected) {\n this._frame.schedule(() => this._evaluate());\n }\n };\n\n private readonly _requestSample = (): void => {\n this._needsSample = true;\n this._requestEvaluate();\n };\n\n /** Requests an evaluation that re-finds the scrollport and flow first. */\n private readonly _requestResolve = (): void => {\n this._needsResolve = true;\n this._requestSample();\n };\n\n private _attachScrollTarget(): Element | null {\n const next = this.scrollTarget ?? findNearestScrollAncestor(this);\n\n if (next === this._resolvedTarget) {\n return next;\n }\n\n this._detachScrollTarget();\n this._resolvedTarget = next;\n this._$scrollSource = next === this._documentScroller() ? this.ownerDocument : next;\n this._$scrollSource?.addEventListener('scroll', this._requestEvaluate, { passive: true });\n\n return next;\n }\n\n private _detachScrollTarget(): void {\n this._$scrollSource?.removeEventListener('scroll', this._requestEvaluate);\n this._$scrollSource = null;\n this._resolvedTarget = null;\n }\n\n /** The scroll target's visible client box in viewport coordinates. */\n private _portViewportRect(target: Element): DOMRect {\n if (target === this._documentScroller()) {\n const root = this.ownerDocument.documentElement;\n\n return new DOMRect(0, 0, root.clientWidth, root.clientHeight);\n }\n\n // The client box is in the target's own CSS pixels; the rect is in\n // viewport pixels. Under CSS `zoom` they differ by the target's zoom.\n const viewportRect = target.getBoundingClientRect();\n const targetZoom = cssZoom(target);\n\n return new DOMRect(\n viewportRect.left + target.clientLeft * targetZoom,\n viewportRect.top + target.clientTop * targetZoom,\n target.clientWidth * targetZoom,\n target.clientHeight * targetZoom,\n );\n }\n\n private _documentScroller(): Element {\n return this.ownerDocument.scrollingElement ?? this.ownerDocument.documentElement;\n }\n\n /**\n * Where the visible part of the scroll target sits relative to the host,\n * measured from the host's start edge along `axis` in the host's own flow.\n * Working from the two rects rather than from `scrollTop`/`scrollLeft`\n * keeps the arithmetic identical in every writing mode and direction.\n * Reported in the host's own CSS pixels, the unit its spacers are written in.\n */\n private _view(target: Element): { viewStart: number; viewSize: number } {\n const viewViewportPx = logicalRect(\n this._portViewportRect(target),\n this.getBoundingClientRect(),\n this._flow,\n );\n const hostZoom = cssZoom(this);\n\n return this.axis === 'inline'\n ? {\n viewStart: viewViewportPx.inlineStart / hostZoom,\n viewSize: viewViewportPx.inlineSize / hostZoom,\n }\n : {\n viewStart: viewViewportPx.blockStart / hostZoom,\n viewSize: viewViewportPx.blockSize / hostZoom,\n };\n }\n\n private _effectiveLineSize(): number {\n return this._geometry.measured ? this._geometry.lineSize : this.itemSize;\n }\n\n /** Rebind observers only when their actual targets changed. */\n private _observeLayout(): void {\n const $container = this.firstElementChild;\n const $oldTarget = this._resolvedTarget;\n\n this._attachScrollTarget();\n\n if ($container !== this._$container) {\n this._$container = $container;\n this._resetGeometry = true;\n this._contentObserver?.disconnect();\n\n if ($container) {\n this._contentObserver?.observe($container, { childList: true });\n }\n } else if ($oldTarget === this._resolvedTarget && this._observingLayout) {\n return;\n }\n\n this._observingLayout = true;\n this._resizeObserver?.disconnect();\n this._resizeObserver?.observe(this);\n\n if ($container) {\n this._resizeObserver?.observe($container);\n }\n\n if (this._resolvedTarget && this._resolvedTarget !== this._documentScroller()) {\n this._resizeObserver?.observe(this._resolvedTarget);\n }\n }\n\n private _observingLayout = false;\n\n /**\n * Focus retargets to a closed shadow host. Stop at the item that owns it,\n * rather than asking ordinary contains() to cross a shadow boundary.\n */\n private _focusedIndex($container: Element): number {\n const root = this.getRootNode();\n let $active =\n root instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;\n\n while ($active) {\n let $ancestor: Element | null = $active;\n\n while ($ancestor && $ancestor.parentElement !== $container) {\n const ancestorRoot: Node = $ancestor.getRootNode();\n\n $ancestor =\n $ancestor.assignedSlot ??\n $ancestor.parentElement ??\n (ancestorRoot instanceof ShadowRoot ? ancestorRoot.host : null);\n }\n\n if ($ancestor) {\n return this.first + Array.prototype.indexOf.call($container.children, $ancestor);\n }\n\n $active = $active.shadowRoot?.activeElement ?? null;\n }\n\n return -1;\n }\n\n private _evaluate(): void {\n if (!this.isConnected) {\n return;\n }\n\n if (this._needsResolve || !this._resolvedTarget) {\n this._needsResolve = false;\n this._observeLayout();\n this._flow = resolveFlow(this);\n }\n\n const $container = this._$container;\n const $target = this._resolvedTarget;\n\n if (this.disabled || !$container || this.count === 0) {\n this._applyRange(0, this.count, 0, 0);\n\n return;\n }\n\n if (!$target || !this.getClientRects().length || !$target.getClientRects().length) {\n // An unavailable layout is not a new measurement. Only clamp a changed\n // collection; preserve the mounted slice and spacer geometry while hidden.\n if (this._lastDetail && this._lastDetail.end > this.count) {\n this._applyRange(\n Math.min(this.first, this.count),\n this.count,\n this._spacerStart,\n this._spacerEnd,\n );\n }\n\n return;\n }\n\n if (this._resetGeometry) {\n this._resetGeometry = false;\n\n this._geometry = {\n itemsPerLine: this.itemsPerLine || 1,\n lineSize: this.itemSize,\n measured: false,\n };\n\n this._needsSample = true;\n }\n\n if (this._needsSample) {\n this._needsSample = false;\n\n const sample = sampleLayout(\n $container,\n this.axis,\n this._flow,\n this.itemsPerLine,\n this._geometry,\n cssZoom(this),\n );\n\n this._unsupported = sample.kind === 'unsupported';\n\n if (sample.kind === 'unsupported') {\n if (import.meta.env.DEV && !this._warnedLayouts.has(sample.reason)) {\n this._warnedLayouts.add(sample.reason);\n\n console.warn(\n `[rc-virtual-scroller] ${sample.reason} Rendering the full collection.`,\n this,\n );\n }\n } else {\n this._geometry = sample.geometry;\n }\n }\n\n const lineSize = this._effectiveLineSize();\n\n if (this._unsupported || !lineSize) {\n this._applyRange(0, this.count, 0, 0);\n\n return;\n }\n\n const range = calculateWindow({\n ...this._geometry,\n lineSize,\n count: this.count,\n overscan: this.overscan,\n ...this._view($target),\n focused: this._focusedIndex($container),\n });\n\n this._applyRange(range.start, range.end, range.spacerStart, range.spacerEnd);\n }\n\n private _applyRange(start: number, end: number, spacerStart: number, spacerEnd: number): void {\n this._spacerStart = spacerStart;\n this._spacerEnd = spacerEnd;\n this.setAttribute('first', String(start));\n this.setAttribute('last', String(end - 1));\n\n const detail: RCVirtualScrollerRangeDetail = {\n start,\n end,\n itemsPerLine: this._geometry.itemsPerLine,\n lineSize: this._effectiveLineSize(),\n measured: this._geometry.measured,\n };\n const previous = this._lastDetail;\n\n if (\n previous &&\n previous.start === detail.start &&\n previous.end === detail.end &&\n previous.itemsPerLine === detail.itemsPerLine &&\n previous.measured === detail.measured &&\n Math.abs(previous.lineSize - detail.lineSize) < LINE_SIZE_EPSILON\n ) {\n // Re-dispatching an identical range would make the consumer re-render,\n // which lands back here through slotchange, forever.\n\n return;\n }\n\n this._lastDetail = Object.freeze(detail);\n\n this.dispatchEvent(\n new CustomEvent<RCVirtualScrollerRangeDetail>('rc-virtual-scroller-range', {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n }\n}\n"],"names":["whole","value","fallback","positive","calculateWindow","input","count","overscan","itemsPerLine","lineSize","viewStart","viewSize","focused","lines","firstLine","lastLine","start","end","focusedLine","resolvedTracks","tokens","token","trackSpan","span","sampleLayout","$container","axis","flow","override","previous","hostZoom","styles","$items","$first","gap","unsupported","reason","cross","along","firstStyle","capacity","i","$item","itemStyle","alongStart","alongEnd","itemSpan","trackSize","size","containerToHostCssScale","cssZoom","firstRect","own","logicalRect","extent","pitch","rect","virtualScrollerStyles","css","LINE_SIZE_EPSILON","_RCVirtualScroller","LitElement","RafScheduler","HORIZONTAL_LTR_FLOW","old","observeLocale","changed","html","styleMap","index","options","target","resolveFlow","clamped","lineStart","align","desired","targetZoom","scrollDeltaTargetCssPx","isReversed","behavior","physicalAxis","next","findNearestScrollAncestor","root","viewportRect","viewViewportPx","$oldTarget","$active","$ancestor","ancestorRoot","$target","sample","range","spacerStart","spacerEnd","detail","warning","RCVirtualScroller","__decorateClass","property","state"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { R as r } from "./rc-virtual-scroller-BA_t6Xkx.js";
2
+ customElements.get("rc-virtual-scroller") || customElements.define("rc-virtual-scroller", r);
3
+ export {
4
+ r as RCVirtualScroller
5
+ };
6
+ //# sourceMappingURL=rc-virtual-scroller-define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-virtual-scroller-define.js","sources":["../src/define.ts"],"sourcesContent":["import { RCVirtualScroller } from './index.js';\n\ncustomElements.get('rc-virtual-scroller') ||\n customElements.define('rc-virtual-scroller', RCVirtualScroller);\n\nexport * from './index.js';\n"],"names":["RCVirtualScroller"],"mappings":";AAEA,eAAe,IAAI,qBAAqB,KACtC,eAAe,OAAO,uBAAuBA,CAAiB;"}
@@ -0,0 +1,5 @@
1
+ import { R as o } from "./rc-virtual-scroller-BA_t6Xkx.js";
2
+ export {
3
+ o as RCVirtualScroller
4
+ };
5
+ //# sourceMappingURL=rc-virtual-scroller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-virtual-scroller.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1 @@
1
+ export * from './index.js';
@@ -0,0 +1 @@
1
+ export * from './rc-virtual-scroller.js';
@@ -0,0 +1,31 @@
1
+ /** A finite, nonnegative integer configuration value. */
2
+ export declare function whole(value: number, fallback?: number): number;
3
+ /** A finite positive pixel estimate, or zero when unavailable. */
4
+ export declare function positive(value: number): number;
5
+ export interface Geometry {
6
+ /** Effective number of sequential items in a line. */
7
+ itemsPerLine: number;
8
+ /** Line pitch including the inter-line gap. */
9
+ lineSize: number;
10
+ /** Whether the pitch came from layout rather than the author estimate. */
11
+ measured: boolean;
12
+ }
13
+ export interface WindowInput extends Geometry {
14
+ count: number;
15
+ overscan: number;
16
+ viewStart: number;
17
+ viewSize: number;
18
+ focused: number;
19
+ }
20
+ export interface WindowRange {
21
+ start: number;
22
+ end: number;
23
+ spacerStart: number;
24
+ spacerEnd: number;
25
+ }
26
+ /** Calculate a contiguous window from validated, logical layout facts. */
27
+ export declare function calculateWindow(input: WindowInput): WindowRange;
28
+ /** Read resolved pixel tracks, ignoring CSS grid line-name annotations. */
29
+ export declare function resolvedTracks(value: string): number[] | null;
30
+ /** Resolve the deliberately small, numeric subset of grid span syntax we support. */
31
+ export declare function trackSpan(start: string, end: string, count: number): number | null;