@gunkin/uiify 0.0.2-alpha → 0.0.4-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/behavior.css +16 -1
- package/dist/components/behavior.css.map +1 -1
- package/dist/components/candle-chart/CandleChart.d.ts +10 -1
- package/dist/components/candle-chart/CandleChart.d.ts.map +1 -1
- package/dist/components/candle-chart/CandleChart.js +82 -42
- package/dist/components/candle-chart/CandleChart.js.map +1 -1
- package/dist/components/candle-chart/client/CandleChartClient.d.ts +2 -2
- package/dist/components/candle-chart/client/CandleChartClient.d.ts.map +1 -1
- package/dist/components/candle-chart/client/CandleChartClient.js +103 -14
- package/dist/components/candle-chart/client/CandleChartClient.js.map +1 -1
- package/dist/components/candle-chart/client/index.d.ts +1 -0
- package/dist/components/candle-chart/client/index.d.ts.map +1 -1
- package/dist/components/candle-chart/client/index.js.map +1 -1
- package/dist/components/candle-chart/types.d.ts +39 -0
- package/dist/components/candle-chart/types.d.ts.map +1 -1
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.d.ts +11 -0
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.d.ts.map +1 -0
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.js +21 -0
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.js.map +1 -0
- package/dist/components/internal/live-chart/types.d.ts +36 -0
- package/dist/components/internal/live-chart/types.d.ts.map +1 -0
- package/dist/components/internal/live-chart/types.js +1 -0
- package/dist/components/internal/live-chart/types.js.map +1 -0
- package/dist/components/internal/live-chart/use-live-chart-window.d.ts +18 -0
- package/dist/components/internal/live-chart/use-live-chart-window.d.ts.map +1 -0
- package/dist/components/internal/live-chart/use-live-chart-window.js +75 -0
- package/dist/components/internal/live-chart/use-live-chart-window.js.map +1 -0
- package/dist/components/line-chart/LineChart.d.ts +6 -1
- package/dist/components/line-chart/LineChart.d.ts.map +1 -1
- package/dist/components/line-chart/LineChart.js +82 -48
- package/dist/components/line-chart/LineChart.js.map +1 -1
- package/dist/components/line-chart/client/LineChartClient.d.ts +2 -2
- package/dist/components/line-chart/client/LineChartClient.d.ts.map +1 -1
- package/dist/components/line-chart/client/LineChartClient.js +106 -14
- package/dist/components/line-chart/client/LineChartClient.js.map +1 -1
- package/dist/components/line-chart/client/index.d.ts +1 -1
- package/dist/components/line-chart/client/index.d.ts.map +1 -1
- package/dist/components/line-chart/client/index.js.map +1 -1
- package/dist/components/line-chart/line-chart-geometry.d.ts +7 -0
- package/dist/components/line-chart/line-chart-geometry.d.ts.map +1 -1
- package/dist/components/line-chart/line-chart-geometry.js +14 -2
- package/dist/components/line-chart/line-chart-geometry.js.map +1 -1
- package/dist/components/line-chart/types.d.ts +42 -0
- package/dist/components/line-chart/types.d.ts.map +1 -1
- package/dist/components/virtual-scroll/VirtualScroll.d.ts.map +1 -1
- package/dist/components/virtual-scroll/VirtualScroll.js +40 -44
- package/dist/components/virtual-scroll/VirtualScroll.js.map +1 -1
- package/dist/components/virtual-scroll/index.d.ts +1 -1
- package/dist/components/virtual-scroll/index.d.ts.map +1 -1
- package/dist/components/virtual-scroll/index.js.map +1 -1
- package/dist/components/virtual-scroll/types.d.ts +5 -0
- package/dist/components/virtual-scroll/types.d.ts.map +1 -1
- package/dist/components/virtual-scroll/types.js.map +1 -1
- package/dist/components/virtual-scroll/virtual-scroll-axis.d.ts +9 -0
- package/dist/components/virtual-scroll/virtual-scroll-axis.d.ts.map +1 -0
- package/dist/components/virtual-scroll/virtual-scroll-axis.js +18 -0
- package/dist/components/virtual-scroll/virtual-scroll-axis.js.map +1 -0
- package/dist/styles/index.css +1 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { flushSync } from "react-dom";
|
|
11
11
|
import { useResizeObserver } from "@gunkin/uiify/hooks";
|
|
12
12
|
import { resolveVirtualScrollAnchorAdjustment } from "./virtual-scroll-anchor.js";
|
|
13
|
+
import { readScrollOffset, writeScrollOffset } from "./virtual-scroll-axis.js";
|
|
13
14
|
import {
|
|
14
15
|
createVirtualScrollIosScroll,
|
|
15
16
|
detectTouchScrollEnvironment
|
|
@@ -64,6 +65,7 @@ function VirtualScroll(props) {
|
|
|
64
65
|
items,
|
|
65
66
|
height = 600,
|
|
66
67
|
rowHeight = VIRTUAL_SCROLL_ROW_HEIGHT,
|
|
68
|
+
orientation = "vertical",
|
|
67
69
|
overscan = VIRTUAL_SCROLL_OVERSCAN,
|
|
68
70
|
className,
|
|
69
71
|
testId,
|
|
@@ -78,8 +80,9 @@ function VirtualScroll(props) {
|
|
|
78
80
|
isScrollingResetDelay = VIRTUAL_SCROLL_SCROLL_RESET_DELAY
|
|
79
81
|
} = props;
|
|
80
82
|
const dynamicLayout = estimateRowHeight !== void 0;
|
|
83
|
+
const horizontal = orientation === "horizontal";
|
|
81
84
|
const ref = useRef(null);
|
|
82
|
-
const
|
|
85
|
+
const scrollOffsetRef = useRef(0);
|
|
83
86
|
const itemsRef = useRef(items);
|
|
84
87
|
itemsRef.current = items;
|
|
85
88
|
const smoothRafRef = useRef(0);
|
|
@@ -94,7 +97,7 @@ function VirtualScroll(props) {
|
|
|
94
97
|
const [rangeVersion, setRangeVersion] = useState(0);
|
|
95
98
|
const [measurementVersion, setMeasurementVersion] = useState(0);
|
|
96
99
|
const measuredSize = useResizeObserver(ref);
|
|
97
|
-
const
|
|
100
|
+
const viewportSize = (horizontal ? measuredSize?.width : measuredSize?.height) ?? height;
|
|
98
101
|
const measurementStoreRef = useRef(
|
|
99
102
|
createVirtualScrollMeasurementStore({
|
|
100
103
|
itemCount: items.length,
|
|
@@ -108,29 +111,31 @@ function VirtualScroll(props) {
|
|
|
108
111
|
const scrollOptionsRef = useRef({
|
|
109
112
|
dynamicLayout,
|
|
110
113
|
itemCount: items.length,
|
|
114
|
+
orientation,
|
|
111
115
|
overscan,
|
|
112
116
|
rowHeight,
|
|
113
|
-
|
|
117
|
+
viewportSize
|
|
114
118
|
});
|
|
115
119
|
scrollOptionsRef.current = {
|
|
116
120
|
dynamicLayout,
|
|
117
121
|
itemCount: items.length,
|
|
122
|
+
orientation,
|
|
118
123
|
overscan,
|
|
119
124
|
rowHeight,
|
|
120
|
-
|
|
125
|
+
viewportSize
|
|
121
126
|
};
|
|
122
127
|
const onRangeChangeRef = useRef(onRangeChange);
|
|
123
128
|
onRangeChangeRef.current = onRangeChange;
|
|
124
129
|
const onScrollStateChangeRef = useRef(onScrollStateChange);
|
|
125
130
|
onScrollStateChangeRef.current = onScrollStateChange;
|
|
126
131
|
const computeRange = () => {
|
|
127
|
-
const { dynamicLayout: dynamicLayout2, itemCount, overscan: overscan2, rowHeight: rowHeight2,
|
|
128
|
-
const scrollTop =
|
|
132
|
+
const { dynamicLayout: dynamicLayout2, itemCount, overscan: overscan2, rowHeight: rowHeight2, viewportSize: viewportSize2 } = scrollOptionsRef.current;
|
|
133
|
+
const scrollTop = scrollOffsetRef.current;
|
|
129
134
|
if (dynamicLayout2) {
|
|
130
135
|
const store = measurementStoreRef.current;
|
|
131
136
|
return computeDynamicVirtualScrollRange({
|
|
132
137
|
scrollTop,
|
|
133
|
-
viewportHeight:
|
|
138
|
+
viewportHeight: viewportSize2,
|
|
134
139
|
itemCount,
|
|
135
140
|
overscan: overscan2,
|
|
136
141
|
findStartIndex: store.findStartIndex,
|
|
@@ -140,7 +145,7 @@ function VirtualScroll(props) {
|
|
|
140
145
|
}
|
|
141
146
|
return computeVirtualScrollRange({
|
|
142
147
|
scrollTop,
|
|
143
|
-
viewportHeight:
|
|
148
|
+
viewportHeight: viewportSize2,
|
|
144
149
|
itemCount,
|
|
145
150
|
rowHeight: rowHeight2,
|
|
146
151
|
overscan: overscan2
|
|
@@ -160,19 +165,19 @@ function VirtualScroll(props) {
|
|
|
160
165
|
syncRangeFromScrollRef.current = () => {
|
|
161
166
|
const element = ref.current;
|
|
162
167
|
if (!element) return;
|
|
163
|
-
const
|
|
168
|
+
const scrollOffset = readScrollOffset(element, scrollOptionsRef.current.orientation);
|
|
164
169
|
const previousRange = computeRange();
|
|
165
|
-
|
|
170
|
+
scrollOffsetRef.current = scrollOffset;
|
|
166
171
|
const nextRange = computeRange();
|
|
167
172
|
if (rangesEqual(previousRange, nextRange)) return;
|
|
168
173
|
emitRangeChange(nextRange);
|
|
169
|
-
const { dynamicLayout: dynamicLayout2, itemCount,
|
|
174
|
+
const { dynamicLayout: dynamicLayout2, itemCount, viewportSize: viewportSize2, rowHeight: rowHeight2, overscan: overscan2 } = scrollOptionsRef.current;
|
|
170
175
|
const slotCount2 = computeActiveVirtualScrollSlotCount(
|
|
171
176
|
itemCount,
|
|
172
177
|
nextRange.start,
|
|
173
178
|
Math.max(
|
|
174
179
|
nextRange.end - nextRange.start,
|
|
175
|
-
computeVirtualScrollSlotCount(
|
|
180
|
+
computeVirtualScrollSlotCount(viewportSize2, rowHeight2, overscan2)
|
|
176
181
|
)
|
|
177
182
|
);
|
|
178
183
|
const store = measurementStoreRef.current;
|
|
@@ -196,7 +201,8 @@ function VirtualScroll(props) {
|
|
|
196
201
|
applyScrollDeltaRef.current = (delta) => {
|
|
197
202
|
const element = ref.current;
|
|
198
203
|
if (!element || delta === 0) return;
|
|
199
|
-
|
|
204
|
+
const { orientation: orientation2 } = scrollOptionsRef.current;
|
|
205
|
+
writeScrollOffset(element, orientation2, readScrollOffset(element, orientation2) + delta, "auto");
|
|
200
206
|
syncRangeFromScrollRef.current();
|
|
201
207
|
};
|
|
202
208
|
const applyScrollOffset = (offset, behavior) => {
|
|
@@ -204,15 +210,10 @@ function VirtualScroll(props) {
|
|
|
204
210
|
if (!element) return;
|
|
205
211
|
const store = measurementStoreRef.current;
|
|
206
212
|
const totalSize2 = dynamicLayout ? store.getTotalSize() : items.length * rowHeight;
|
|
207
|
-
const clamped = clampScrollOffset(offset, totalSize2,
|
|
208
|
-
|
|
209
|
-
if (typeof element.scrollTo === "function") {
|
|
210
|
-
element.scrollTo({ top: clamped, behavior: resolved });
|
|
211
|
-
} else {
|
|
212
|
-
element.scrollTop = clamped;
|
|
213
|
-
}
|
|
213
|
+
const clamped = clampScrollOffset(offset, totalSize2, viewportSize);
|
|
214
|
+
writeScrollOffset(element, orientation, clamped, resolveScrollBehavior(behavior));
|
|
214
215
|
syncRangeFromScrollRef.current();
|
|
215
|
-
if (isSmoothScrollActive(behavior ?? "auto", element
|
|
216
|
+
if (isSmoothScrollActive(behavior ?? "auto", readScrollOffset(element, orientation), clamped)) {
|
|
216
217
|
if (smoothRafRef.current !== 0) cancelAnimationFrame(smoothRafRef.current);
|
|
217
218
|
const tick = () => {
|
|
218
219
|
syncRangeFromScrollRef.current();
|
|
@@ -221,7 +222,7 @@ function VirtualScroll(props) {
|
|
|
221
222
|
smoothRafRef.current = 0;
|
|
222
223
|
return;
|
|
223
224
|
}
|
|
224
|
-
if (isSmoothScrollActive("smooth", node
|
|
225
|
+
if (isSmoothScrollActive("smooth", readScrollOffset(node, orientation), clamped)) {
|
|
225
226
|
smoothRafRef.current = requestAnimationFrame(tick);
|
|
226
227
|
return;
|
|
227
228
|
}
|
|
@@ -253,8 +254,8 @@ function VirtualScroll(props) {
|
|
|
253
254
|
index,
|
|
254
255
|
normalized.align,
|
|
255
256
|
metrics,
|
|
256
|
-
scrollOptionsRef.current.
|
|
257
|
-
|
|
257
|
+
scrollOptionsRef.current.viewportSize,
|
|
258
|
+
scrollOffsetRef.current
|
|
258
259
|
);
|
|
259
260
|
applyScrollOffsetRef.current(offset, normalized.behavior);
|
|
260
261
|
},
|
|
@@ -264,7 +265,7 @@ function VirtualScroll(props) {
|
|
|
264
265
|
scrollToEnd(options) {
|
|
265
266
|
const store = measurementStoreRef.current;
|
|
266
267
|
const totalSize2 = dynamicLayout ? store.getTotalSize() : scrollOptionsRef.current.itemCount * scrollOptionsRef.current.rowHeight;
|
|
267
|
-
const offset = Math.max(0, totalSize2 - scrollOptionsRef.current.
|
|
268
|
+
const offset = Math.max(0, totalSize2 - scrollOptionsRef.current.viewportSize);
|
|
268
269
|
applyScrollOffsetRef.current(offset, normalizeScrollBehaviorOptions(options));
|
|
269
270
|
}
|
|
270
271
|
}),
|
|
@@ -284,9 +285,9 @@ function VirtualScroll(props) {
|
|
|
284
285
|
if (items.length === 0) return 0;
|
|
285
286
|
return Math.max(
|
|
286
287
|
range.end - range.start,
|
|
287
|
-
computeVirtualScrollSlotCount(
|
|
288
|
+
computeVirtualScrollSlotCount(viewportSize, rowHeight, overscan)
|
|
288
289
|
);
|
|
289
|
-
}, [items.length, overscan, range.end, range.start, rowHeight,
|
|
290
|
+
}, [items.length, overscan, range.end, range.start, rowHeight, viewportSize]);
|
|
290
291
|
const rowSlots = useMemo(
|
|
291
292
|
() => buildRowSlots(items, range.start, slotCount),
|
|
292
293
|
[items, range.start, slotCount]
|
|
@@ -297,7 +298,7 @@ function VirtualScroll(props) {
|
|
|
297
298
|
const rowOffset = store.getRowOffset(index);
|
|
298
299
|
if (!store.setMeasuredHeight(itemId, index, height2)) return;
|
|
299
300
|
const adjustment = computeScrollAdjustmentForRowResize({
|
|
300
|
-
scrollTop:
|
|
301
|
+
scrollTop: scrollOffsetRef.current,
|
|
301
302
|
rowOffset,
|
|
302
303
|
previousHeight,
|
|
303
304
|
nextHeight: height2
|
|
@@ -327,12 +328,13 @@ function VirtualScroll(props) {
|
|
|
327
328
|
if (!Number.isInteger(index)) continue;
|
|
328
329
|
const itemId = itemsRef.current[index]?.id;
|
|
329
330
|
if (itemId === void 0) continue;
|
|
330
|
-
|
|
331
|
+
const size = horizontal ? entry.contentRect.width : entry.contentRect.height;
|
|
332
|
+
handleRowMeasureRef.current(index, itemId, size);
|
|
331
333
|
}
|
|
332
334
|
});
|
|
333
335
|
fragment.observeUsing(observer);
|
|
334
336
|
return () => fragment.unobserveUsing(observer);
|
|
335
|
-
}, [dynamicLayout]);
|
|
337
|
+
}, [dynamicLayout, horizontal]);
|
|
336
338
|
useEffect(() => {
|
|
337
339
|
const element = ref.current;
|
|
338
340
|
if (!element) return;
|
|
@@ -379,40 +381,33 @@ function VirtualScroll(props) {
|
|
|
379
381
|
const store = measurementStoreRef.current;
|
|
380
382
|
const nextTotalSize = dynamicLayout ? store.getTotalSize() : items.length * rowHeight;
|
|
381
383
|
const previous = anchorSnapshotRef.current;
|
|
384
|
+
const scrollOffset = element ? readScrollOffset(element, orientation) : scrollOffsetRef.current;
|
|
382
385
|
const nextSnapshot = {
|
|
383
386
|
firstItemId: items[0]?.id,
|
|
384
387
|
itemCount: items.length,
|
|
385
388
|
totalSize: nextTotalSize,
|
|
386
|
-
scrollTop:
|
|
387
|
-
// banned-read-ok: anchor snapshot reads native position.
|
|
389
|
+
scrollTop: scrollOffset
|
|
388
390
|
};
|
|
389
391
|
const adjustment = resolveVirtualScrollAnchorAdjustment({
|
|
390
392
|
anchor,
|
|
391
393
|
followAppend,
|
|
392
|
-
viewportHeight,
|
|
394
|
+
viewportHeight: viewportSize,
|
|
393
395
|
previous,
|
|
394
396
|
next: nextSnapshot
|
|
395
397
|
});
|
|
396
398
|
if (adjustment && element) {
|
|
397
|
-
|
|
398
|
-
element.scrollTo({
|
|
399
|
-
top: element.scrollTop + adjustment.delta,
|
|
400
|
-
// banned-read-ok: retain the anchored viewport.
|
|
401
|
-
behavior: "smooth"
|
|
402
|
-
});
|
|
403
|
-
} else {
|
|
404
|
-
element.scrollTop += adjustment.delta;
|
|
405
|
-
}
|
|
399
|
+
writeScrollOffset(element, orientation, scrollOffset + adjustment.delta, adjustment.behavior);
|
|
406
400
|
syncRangeFromScrollRef.current();
|
|
407
401
|
}
|
|
408
402
|
anchorSnapshotRef.current = nextSnapshot;
|
|
409
|
-
}, [anchor, followAppend, items, dynamicLayout, rowHeight,
|
|
403
|
+
}, [anchor, followAppend, items, dynamicLayout, orientation, rowHeight, viewportSize]);
|
|
410
404
|
useEffect(() => {
|
|
411
405
|
emitRangeChange(range);
|
|
412
406
|
}, [range]);
|
|
413
407
|
const scrollportStyle = {
|
|
414
408
|
height,
|
|
415
|
-
|
|
409
|
+
overflowX: horizontal ? "auto" : void 0,
|
|
410
|
+
overflowY: horizontal ? "hidden" : "auto",
|
|
416
411
|
"--uiify-virtual-scroll-count": items.length,
|
|
417
412
|
"--uiify-virtual-scroll-row-height": `${rowHeight}px`,
|
|
418
413
|
...dynamicLayout ? { "--uiify-virtual-scroll-total-size": `${totalSize}px` } : {}
|
|
@@ -427,6 +422,7 @@ function VirtualScroll(props) {
|
|
|
427
422
|
"data-testid": testId,
|
|
428
423
|
"data-uiify-virtual-scroll": "",
|
|
429
424
|
"data-dynamic-layout": dynamicLayout ? "" : void 0,
|
|
425
|
+
"data-orientation": orientation,
|
|
430
426
|
role,
|
|
431
427
|
className,
|
|
432
428
|
style: scrollportStyle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/virtual-scroll/VirtualScroll.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FragmentInstance,\n type ReactElement,\n} from \"react\";\nimport { flushSync } from \"react-dom\";\nimport { useResizeObserver } from \"@gunkin/uiify/hooks\";\nimport { resolveVirtualScrollAnchorAdjustment } from \"./virtual-scroll-anchor.js\";\nimport {\n createVirtualScrollIosScroll,\n detectTouchScrollEnvironment,\n} from \"./virtual-scroll-ios.js\";\nimport {\n computeScrollAdjustmentForRowResize,\n createVirtualScrollMeasurementStore,\n} from \"./virtual-scroll-measurements.js\";\nimport {\n computeDynamicVirtualScrollRange,\n computeVirtualScrollRange,\n rangesEqual,\n toVirtualScrollRangeChange,\n} from \"./virtual-scroll-range.js\";\nimport {\n computeActiveVirtualScrollSlotCount,\n computeVirtualScrollSlotCount,\n VirtualScrollRowShell,\n} from \"./VirtualScrollRowShell.js\";\nimport {\n clampScrollOffset,\n computeScrollOffsetForIndex,\n isSmoothScrollActive,\n normalizeScrollBehaviorOptions,\n normalizeScrollToIndexOptions,\n resolveScrollBehavior,\n} from \"./virtual-scroll-scroll.js\";\nimport {\n createVirtualScrollScrollState,\n supportsScrollEndEvent,\n} from \"./virtual-scroll-scroll-state.js\";\nimport { applyVirtualScrollRangeToDom } from \"./virtual-scroll-scroll-sync.js\";\nimport type {\n VirtualScrollHandle,\n VirtualScrollItem,\n VirtualScrollOwnProps,\n VirtualScrollRowSlot,\n VirtualScrollScrollBehaviorOptions,\n VirtualScrollScrollToIndexOptions,\n} from \"./types.js\";\nimport { VirtualScrollRowItem } from \"./VirtualScrollRowItem.js\";\nimport {\n VIRTUAL_SCROLL_OVERSCAN,\n VIRTUAL_SCROLL_ROW_HEIGHT,\n VIRTUAL_SCROLL_SCROLL_RESET_DELAY,\n} from \"./types.js\";\n\nfunction buildRowSlots<T extends VirtualScrollItem>(\n items: readonly T[],\n rangeStart: number,\n slotCount: number,\n): readonly VirtualScrollRowSlot<T>[] {\n const activeSlotCount = computeActiveVirtualScrollSlotCount(items.length, rangeStart, slotCount);\n return Array.from({ length: activeSlotCount }, (_, slotIndex) => {\n const index = rangeStart + slotIndex;\n return {\n index,\n item: items[index] ?? null,\n slotIndex,\n };\n });\n}\n\nexport function VirtualScroll<T extends VirtualScrollItem>(\n props: VirtualScrollOwnProps<T>,\n): ReactElement {\n const {\n items,\n height = 600,\n rowHeight = VIRTUAL_SCROLL_ROW_HEIGHT,\n overscan = VIRTUAL_SCROLL_OVERSCAN,\n className,\n testId,\n renderRow,\n scrollRef,\n estimateRowHeight,\n onRangeChange,\n role = \"list\",\n anchor = \"start\",\n followAppend = false,\n onScrollStateChange,\n isScrollingResetDelay = VIRTUAL_SCROLL_SCROLL_RESET_DELAY,\n } = props;\n\n const dynamicLayout = estimateRowHeight !== undefined;\n const ref = useRef<HTMLDivElement>(null);\n const scrollTopRef = useRef(0);\n const itemsRef = useRef(items);\n itemsRef.current = items;\n const smoothRafRef = useRef(0);\n const lastRangeChangeRef = useRef<string>(\"\");\n const measuredHeightsRef = useRef(new Map<string, number>());\n const anchorSnapshotRef = useRef({\n firstItemId: items[0]?.id,\n itemCount: items.length,\n totalSize: 0,\n scrollTop: 0,\n });\n\n const [rangeVersion, setRangeVersion] = useState(0);\n const [measurementVersion, setMeasurementVersion] = useState(0);\n\n const measuredSize = useResizeObserver(ref);\n const viewportHeight = measuredSize?.height ?? height;\n\n const measurementStoreRef = useRef(\n createVirtualScrollMeasurementStore({\n itemCount: items.length,\n defaultRowHeight: rowHeight,\n ...(estimateRowHeight ? { estimateRowHeight } : {}),\n measuredHeights: measuredHeightsRef.current,\n itemIdAtIndex: (index) => itemsRef.current[index]?.id,\n }),\n );\n\n measurementStoreRef.current.syncItemCount(items.length);\n\n const scrollOptionsRef = useRef({\n dynamicLayout,\n itemCount: items.length,\n overscan,\n rowHeight,\n viewportHeight,\n });\n scrollOptionsRef.current = {\n dynamicLayout,\n itemCount: items.length,\n overscan,\n rowHeight,\n viewportHeight,\n };\n\n const onRangeChangeRef = useRef(onRangeChange);\n onRangeChangeRef.current = onRangeChange;\n\n const onScrollStateChangeRef = useRef(onScrollStateChange);\n onScrollStateChangeRef.current = onScrollStateChange;\n\n const computeRange = (): ReturnType<typeof computeVirtualScrollRange> => {\n const { dynamicLayout, itemCount, overscan, rowHeight, viewportHeight } =\n scrollOptionsRef.current;\n const scrollTop = scrollTopRef.current;\n\n if (dynamicLayout) {\n const store = measurementStoreRef.current;\n return computeDynamicVirtualScrollRange({\n scrollTop,\n viewportHeight,\n itemCount,\n overscan,\n findStartIndex: store.findStartIndex,\n findEndIndex: store.findEndIndex,\n getRowOffset: store.getRowOffset,\n });\n }\n\n return computeVirtualScrollRange({\n scrollTop,\n viewportHeight,\n itemCount,\n rowHeight,\n overscan,\n });\n };\n\n const emitRangeChange = (range: ReturnType<typeof computeVirtualScrollRange>): void => {\n const callback = onRangeChangeRef.current;\n if (!callback) return;\n const payload = toVirtualScrollRangeChange(range);\n const signature = `${payload.overscanStart}:${payload.overscanEnd}`;\n if (signature === lastRangeChangeRef.current) return;\n lastRangeChangeRef.current = signature;\n callback(payload);\n };\n\n const syncRangeFromScrollRef = useRef(() => {});\n\n syncRangeFromScrollRef.current = () => {\n const element = ref.current;\n if (!element) return;\n\n const scrollTop = element.scrollTop; // banned-read-ok: scroll position drives the virtual range.\n const previousRange = computeRange();\n scrollTopRef.current = scrollTop;\n\n const nextRange = computeRange();\n if (rangesEqual(previousRange, nextRange)) return;\n\n emitRangeChange(nextRange);\n\n const { dynamicLayout, itemCount, viewportHeight, rowHeight, overscan } =\n scrollOptionsRef.current;\n const slotCount = computeActiveVirtualScrollSlotCount(\n itemCount,\n nextRange.start,\n Math.max(\n nextRange.end - nextRange.start,\n computeVirtualScrollSlotCount(viewportHeight, rowHeight, overscan),\n ),\n );\n const store = measurementStoreRef.current;\n\n // Write range CSS vars on the scroll handler so rows move in the same frame as scrollTop.\n applyVirtualScrollRangeToDom(element, nextRange, {\n dynamicLayout,\n slotCount,\n getRowOffset: store.getRowOffset.bind(store),\n });\n\n // Commit the visible slice synchronously so content matches scroll position on large jumps.\n flushSync(() => {\n setRangeVersion((version) => version + 1);\n });\n };\n\n const iosControllerRef = useRef(\n createVirtualScrollIosScroll({\n enabled: detectTouchScrollEnvironment(),\n resetDelay: isScrollingResetDelay,\n }),\n );\n\n const applyScrollDeltaRef = useRef((_delta: number) => {});\n\n applyScrollDeltaRef.current = (delta: number) => {\n const element = ref.current;\n if (!element || delta === 0) return;\n element.scrollTop += delta; // banned-read-ok: preserve the viewport while rows resize.\n syncRangeFromScrollRef.current();\n };\n\n const applyScrollOffset = (\n offset: number,\n behavior: VirtualScrollScrollBehaviorOptions[\"behavior\"],\n ): void => {\n const element = ref.current;\n if (!element) return;\n\n const store = measurementStoreRef.current;\n const totalSize = dynamicLayout ? store.getTotalSize() : items.length * rowHeight;\n const clamped = clampScrollOffset(offset, totalSize, viewportHeight);\n const resolved = resolveScrollBehavior(behavior);\n\n if (typeof element.scrollTo === \"function\") {\n element.scrollTo({ top: clamped, behavior: resolved });\n } else {\n element.scrollTop = clamped; // banned-read-ok: native scroll fallback for this viewport.\n }\n syncRangeFromScrollRef.current();\n\n if (isSmoothScrollActive(behavior ?? \"auto\", element.scrollTop, clamped)) {\n // banned-read-ok: poll native smooth-scroll completion.\n if (smoothRafRef.current !== 0) cancelAnimationFrame(smoothRafRef.current);\n const tick = (): void => {\n syncRangeFromScrollRef.current();\n const node = ref.current;\n if (!node) {\n smoothRafRef.current = 0;\n return;\n }\n if (isSmoothScrollActive(\"smooth\", node.scrollTop, clamped)) {\n // banned-read-ok: poll native smooth-scroll completion.\n smoothRafRef.current = requestAnimationFrame(tick);\n return;\n }\n smoothRafRef.current = 0;\n };\n smoothRafRef.current = requestAnimationFrame(tick);\n }\n };\n\n const applyScrollOffsetRef = useRef(\n (_offset: number, _behavior: VirtualScrollScrollBehaviorOptions[\"behavior\"]) => {},\n );\n\n applyScrollOffsetRef.current = applyScrollOffset;\n\n useImperativeHandle(\n scrollRef,\n (): VirtualScrollHandle => ({\n get element() {\n return ref.current;\n },\n scrollToIndex(index: number, options?: VirtualScrollScrollToIndexOptions): void {\n const normalized = normalizeScrollToIndexOptions(options);\n const store = measurementStoreRef.current;\n const metrics = {\n getRowOffset: store.getRowOffset.bind(store),\n getRowHeight: store.getRowHeight.bind(store),\n getTotalSize: store.getTotalSize.bind(store),\n };\n const offset = computeScrollOffsetForIndex(\n index,\n normalized.align,\n metrics,\n scrollOptionsRef.current.viewportHeight,\n scrollTopRef.current,\n );\n applyScrollOffsetRef.current(offset, normalized.behavior);\n },\n scrollToOffset(offset: number, options?: VirtualScrollScrollBehaviorOptions): void {\n applyScrollOffsetRef.current(offset, normalizeScrollBehaviorOptions(options));\n },\n scrollToEnd(options?: VirtualScrollScrollBehaviorOptions): void {\n const store = measurementStoreRef.current;\n const totalSize = dynamicLayout\n ? store.getTotalSize()\n : scrollOptionsRef.current.itemCount * scrollOptionsRef.current.rowHeight;\n const offset = Math.max(0, totalSize - scrollOptionsRef.current.viewportHeight);\n applyScrollOffsetRef.current(offset, normalizeScrollBehaviorOptions(options));\n },\n }),\n [dynamicLayout],\n );\n\n const range = useMemo(() => {\n void rangeVersion;\n void measurementVersion;\n return computeRange();\n }, [rangeVersion, measurementVersion]);\n\n const totalSize = useMemo(() => {\n void measurementVersion;\n if (dynamicLayout) return measurementStoreRef.current.getTotalSize();\n return items.length * rowHeight;\n }, [dynamicLayout, items.length, measurementVersion, rowHeight]);\n\n const slotCount = useMemo(() => {\n if (items.length === 0) return 0;\n return Math.max(\n range.end - range.start,\n computeVirtualScrollSlotCount(viewportHeight, rowHeight, overscan),\n );\n }, [items.length, overscan, range.end, range.start, rowHeight, viewportHeight]);\n\n const rowSlots = useMemo(\n () => buildRowSlots(items, range.start, slotCount),\n [items, range.start, slotCount],\n );\n\n const handleRowMeasureRef = useRef((index: number, itemId: string, height: number) => {\n const store = measurementStoreRef.current;\n const previousHeight = store.getRowHeight(index);\n const rowOffset = store.getRowOffset(index);\n if (!store.setMeasuredHeight(itemId, index, height)) return;\n\n const adjustment = computeScrollAdjustmentForRowResize({\n scrollTop: scrollTopRef.current,\n rowOffset,\n previousHeight,\n nextHeight: height,\n });\n\n if (adjustment > 0) {\n const ios = iosControllerRef.current;\n if (ios.isTouchScrolling()) {\n ios.queueAdjustment(adjustment);\n } else {\n applyScrollDeltaRef.current(adjustment);\n }\n }\n\n setMeasurementVersion((version) => version + 1);\n });\n\n const rowsFragmentRef = useRef<FragmentInstance>(null);\n\n // One ResizeObserver for the whole pooled row set. React keeps the\n // subscription in sync as slots mount and unmount — pinned by\n // core/fragment-instance.test.tsx. Fixed layout never measures rows, so no\n // observer is created there at all.\n useEffect(() => {\n if (!dynamicLayout) return;\n const fragment = rowsFragmentRef.current;\n if (!fragment || typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const target = entry.target;\n if (!(target instanceof HTMLElement)) continue;\n const raw = target.dataset[\"uiifyRowIndex\"];\n if (raw === undefined) continue;\n const index = Number(raw);\n if (!Number.isInteger(index)) continue;\n const itemId = itemsRef.current[index]?.id;\n if (itemId === undefined) continue;\n handleRowMeasureRef.current(index, itemId, entry.contentRect.height);\n }\n });\n\n fragment.observeUsing(observer);\n return () => fragment.unobserveUsing(observer);\n }, [dynamicLayout]);\n\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const scrollState = createVirtualScrollScrollState({\n resetDelay: isScrollingResetDelay,\n supportsScrollEnd: supportsScrollEndEvent(),\n onChange: (isScrolling) => {\n onScrollStateChangeRef.current?.({ isScrolling });\n if (!isScrolling) {\n iosControllerRef.current.flushAdjustments(applyScrollDeltaRef.current);\n }\n },\n });\n\n let onScroll: (() => void) | null = null;\n let onScrollEnd: (() => void) | null = null;\n const ios = iosControllerRef.current;\n const onTouchStart = (): void => ios.onTouchStart();\n const onTouchEnd = (): void => ios.onTouchEnd();\n\n onScroll = () => {\n scrollState.notifyScroll();\n syncRangeFromScrollRef.current();\n };\n element.addEventListener(\"scroll\", onScroll, { passive: true });\n if (supportsScrollEndEvent()) {\n onScrollEnd = () => {\n scrollState.notifyScrollEnd();\n syncRangeFromScrollRef.current();\n };\n element.addEventListener(\"scrollend\", onScrollEnd, { passive: true });\n }\n element.addEventListener(\"touchstart\", onTouchStart, { passive: true });\n element.addEventListener(\"touchend\", onTouchEnd, { passive: true });\n\n return () => {\n if (smoothRafRef.current !== 0) cancelAnimationFrame(smoothRafRef.current);\n if (onScroll) element.removeEventListener(\"scroll\", onScroll);\n if (onScrollEnd) element.removeEventListener(\"scrollend\", onScrollEnd);\n element.removeEventListener(\"touchstart\", onTouchStart);\n element.removeEventListener(\"touchend\", onTouchEnd);\n scrollState.dispose();\n };\n }, [isScrollingResetDelay]);\n\n useEffect(() => {\n const element = ref.current;\n const store = measurementStoreRef.current;\n const nextTotalSize = dynamicLayout ? store.getTotalSize() : items.length * rowHeight;\n const previous = anchorSnapshotRef.current;\n const nextSnapshot = {\n firstItemId: items[0]?.id,\n itemCount: items.length,\n totalSize: nextTotalSize,\n scrollTop: element?.scrollTop ?? scrollTopRef.current, // banned-read-ok: anchor snapshot reads native position.\n };\n\n const adjustment = resolveVirtualScrollAnchorAdjustment({\n anchor,\n followAppend,\n viewportHeight,\n previous,\n next: nextSnapshot,\n });\n\n if (adjustment && element) {\n if (adjustment.behavior === \"smooth\" && typeof element.scrollTo === \"function\") {\n element.scrollTo({\n top: element.scrollTop + adjustment.delta, // banned-read-ok: retain the anchored viewport.\n behavior: \"smooth\",\n });\n } else {\n element.scrollTop += adjustment.delta; // banned-read-ok: retain the anchored viewport.\n }\n syncRangeFromScrollRef.current();\n }\n\n anchorSnapshotRef.current = nextSnapshot;\n }, [anchor, followAppend, items, dynamicLayout, rowHeight, viewportHeight]);\n\n useEffect(() => {\n emitRangeChange(range);\n }, [range]);\n\n const scrollportStyle = {\n height,\n overflowY: \"auto\",\n \"--uiify-virtual-scroll-count\": items.length,\n \"--uiify-virtual-scroll-row-height\": `${rowHeight}px`,\n ...(dynamicLayout ? { \"--uiify-virtual-scroll-total-size\": `${totalSize}px` } : {}),\n } as CSSProperties;\n\n const spacerStyle = dynamicLayout\n ? undefined\n : ({\n \"--uiify-range-start\": range.start,\n } as CSSProperties);\n\n return (\n <div\n ref={ref}\n data-testid={testId}\n data-uiify-virtual-scroll=\"\"\n data-dynamic-layout={dynamicLayout ? \"\" : undefined}\n role={role}\n className={className}\n style={scrollportStyle}\n >\n <div data-part=\"spacer\" style={spacerStyle}>\n {rowSlots.map((slot) => {\n const store = measurementStoreRef.current;\n const rowOffset = dynamicLayout ? store.getRowOffset(slot.index) : undefined;\n const measuredRowHeight = dynamicLayout ? store.getRowHeight(slot.index) : undefined;\n\n return (\n <VirtualScrollRowShell\n key={`slot-${slot.slotIndex}`}\n slotIndex={slot.slotIndex}\n index={slot.index}\n itemCount={items.length}\n listRole={role}\n dynamicLayout={dynamicLayout}\n {...(rowOffset !== undefined ? { rowOffset } : {})}\n {...(measuredRowHeight !== undefined ? { rowHeight: measuredRowHeight } : {})}\n >\n {slot.item ? (\n <VirtualScrollRowItem item={slot.item} index={slot.index} renderRow={renderRow} />\n ) : null}\n </VirtualScrollRowShell>\n );\n })}\n </div>\n </div>\n );\n}\nVirtualScroll.displayName = \"VirtualScroll\";\n"],"mappings":"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","names":["dynamicLayout","overscan","rowHeight","viewportHeight","range","slotCount","totalSize","height"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/virtual-scroll/VirtualScroll.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FragmentInstance,\n type ReactElement,\n} from \"react\";\nimport { flushSync } from \"react-dom\";\nimport { useResizeObserver } from \"@gunkin/uiify/hooks\";\nimport { resolveVirtualScrollAnchorAdjustment } from \"./virtual-scroll-anchor.js\";\nimport { readScrollOffset, writeScrollOffset } from \"./virtual-scroll-axis.js\";\nimport {\n createVirtualScrollIosScroll,\n detectTouchScrollEnvironment,\n} from \"./virtual-scroll-ios.js\";\nimport {\n computeScrollAdjustmentForRowResize,\n createVirtualScrollMeasurementStore,\n} from \"./virtual-scroll-measurements.js\";\nimport {\n computeDynamicVirtualScrollRange,\n computeVirtualScrollRange,\n rangesEqual,\n toVirtualScrollRangeChange,\n} from \"./virtual-scroll-range.js\";\nimport {\n computeActiveVirtualScrollSlotCount,\n computeVirtualScrollSlotCount,\n VirtualScrollRowShell,\n} from \"./VirtualScrollRowShell.js\";\nimport {\n clampScrollOffset,\n computeScrollOffsetForIndex,\n isSmoothScrollActive,\n normalizeScrollBehaviorOptions,\n normalizeScrollToIndexOptions,\n resolveScrollBehavior,\n} from \"./virtual-scroll-scroll.js\";\nimport {\n createVirtualScrollScrollState,\n supportsScrollEndEvent,\n} from \"./virtual-scroll-scroll-state.js\";\nimport { applyVirtualScrollRangeToDom } from \"./virtual-scroll-scroll-sync.js\";\nimport type {\n VirtualScrollHandle,\n VirtualScrollItem,\n VirtualScrollOwnProps,\n VirtualScrollRowSlot,\n VirtualScrollScrollBehaviorOptions,\n VirtualScrollScrollToIndexOptions,\n} from \"./types.js\";\nimport { VirtualScrollRowItem } from \"./VirtualScrollRowItem.js\";\nimport {\n VIRTUAL_SCROLL_OVERSCAN,\n VIRTUAL_SCROLL_ROW_HEIGHT,\n VIRTUAL_SCROLL_SCROLL_RESET_DELAY,\n} from \"./types.js\";\n\nfunction buildRowSlots<T extends VirtualScrollItem>(\n items: readonly T[],\n rangeStart: number,\n slotCount: number,\n): readonly VirtualScrollRowSlot<T>[] {\n const activeSlotCount = computeActiveVirtualScrollSlotCount(items.length, rangeStart, slotCount);\n return Array.from({ length: activeSlotCount }, (_, slotIndex) => {\n const index = rangeStart + slotIndex;\n return {\n index,\n item: items[index] ?? null,\n slotIndex,\n };\n });\n}\n\nexport function VirtualScroll<T extends VirtualScrollItem>(\n props: VirtualScrollOwnProps<T>,\n): ReactElement {\n const {\n items,\n height = 600,\n rowHeight = VIRTUAL_SCROLL_ROW_HEIGHT,\n orientation = \"vertical\",\n overscan = VIRTUAL_SCROLL_OVERSCAN,\n className,\n testId,\n renderRow,\n scrollRef,\n estimateRowHeight,\n onRangeChange,\n role = \"list\",\n anchor = \"start\",\n followAppend = false,\n onScrollStateChange,\n isScrollingResetDelay = VIRTUAL_SCROLL_SCROLL_RESET_DELAY,\n } = props;\n\n const dynamicLayout = estimateRowHeight !== undefined;\n const horizontal = orientation === \"horizontal\";\n const ref = useRef<HTMLDivElement>(null);\n const scrollOffsetRef = useRef(0);\n const itemsRef = useRef(items);\n itemsRef.current = items;\n const smoothRafRef = useRef(0);\n const lastRangeChangeRef = useRef<string>(\"\");\n const measuredHeightsRef = useRef(new Map<string, number>());\n const anchorSnapshotRef = useRef({\n firstItemId: items[0]?.id,\n itemCount: items.length,\n totalSize: 0,\n scrollTop: 0,\n });\n\n const [rangeVersion, setRangeVersion] = useState(0);\n const [measurementVersion, setMeasurementVersion] = useState(0);\n\n const measuredSize = useResizeObserver(ref);\n const viewportSize = (horizontal ? measuredSize?.width : measuredSize?.height) ?? height;\n\n const measurementStoreRef = useRef(\n createVirtualScrollMeasurementStore({\n itemCount: items.length,\n defaultRowHeight: rowHeight,\n ...(estimateRowHeight ? { estimateRowHeight } : {}),\n measuredHeights: measuredHeightsRef.current,\n itemIdAtIndex: (index) => itemsRef.current[index]?.id,\n }),\n );\n\n measurementStoreRef.current.syncItemCount(items.length);\n\n const scrollOptionsRef = useRef({\n dynamicLayout,\n itemCount: items.length,\n orientation,\n overscan,\n rowHeight,\n viewportSize,\n });\n scrollOptionsRef.current = {\n dynamicLayout,\n itemCount: items.length,\n orientation,\n overscan,\n rowHeight,\n viewportSize,\n };\n\n const onRangeChangeRef = useRef(onRangeChange);\n onRangeChangeRef.current = onRangeChange;\n\n const onScrollStateChangeRef = useRef(onScrollStateChange);\n onScrollStateChangeRef.current = onScrollStateChange;\n\n const computeRange = (): ReturnType<typeof computeVirtualScrollRange> => {\n const { dynamicLayout, itemCount, overscan, rowHeight, viewportSize } =\n scrollOptionsRef.current;\n const scrollTop = scrollOffsetRef.current;\n\n if (dynamicLayout) {\n const store = measurementStoreRef.current;\n return computeDynamicVirtualScrollRange({\n scrollTop,\n viewportHeight: viewportSize,\n itemCount,\n overscan,\n findStartIndex: store.findStartIndex,\n findEndIndex: store.findEndIndex,\n getRowOffset: store.getRowOffset,\n });\n }\n\n return computeVirtualScrollRange({\n scrollTop,\n viewportHeight: viewportSize,\n itemCount,\n rowHeight,\n overscan,\n });\n };\n\n const emitRangeChange = (range: ReturnType<typeof computeVirtualScrollRange>): void => {\n const callback = onRangeChangeRef.current;\n if (!callback) return;\n const payload = toVirtualScrollRangeChange(range);\n const signature = `${payload.overscanStart}:${payload.overscanEnd}`;\n if (signature === lastRangeChangeRef.current) return;\n lastRangeChangeRef.current = signature;\n callback(payload);\n };\n\n const syncRangeFromScrollRef = useRef(() => {});\n\n syncRangeFromScrollRef.current = () => {\n const element = ref.current;\n if (!element) return;\n\n const scrollOffset = readScrollOffset(element, scrollOptionsRef.current.orientation);\n const previousRange = computeRange();\n scrollOffsetRef.current = scrollOffset;\n\n const nextRange = computeRange();\n if (rangesEqual(previousRange, nextRange)) return;\n\n emitRangeChange(nextRange);\n\n const { dynamicLayout, itemCount, viewportSize, rowHeight, overscan } =\n scrollOptionsRef.current;\n const slotCount = computeActiveVirtualScrollSlotCount(\n itemCount,\n nextRange.start,\n Math.max(\n nextRange.end - nextRange.start,\n computeVirtualScrollSlotCount(viewportSize, rowHeight, overscan),\n ),\n );\n const store = measurementStoreRef.current;\n\n // Write range CSS vars on the scroll handler so rows move in the same frame as the scroll offset.\n applyVirtualScrollRangeToDom(element, nextRange, {\n dynamicLayout,\n slotCount,\n getRowOffset: store.getRowOffset.bind(store),\n });\n\n // Commit the visible slice synchronously so content matches scroll position on large jumps.\n flushSync(() => {\n setRangeVersion((version) => version + 1);\n });\n };\n\n const iosControllerRef = useRef(\n createVirtualScrollIosScroll({\n enabled: detectTouchScrollEnvironment(),\n resetDelay: isScrollingResetDelay,\n }),\n );\n\n const applyScrollDeltaRef = useRef((_delta: number) => {});\n\n applyScrollDeltaRef.current = (delta: number) => {\n const element = ref.current;\n if (!element || delta === 0) return;\n // Preserve the viewport while rows resize.\n const { orientation } = scrollOptionsRef.current;\n writeScrollOffset(element, orientation, readScrollOffset(element, orientation) + delta, \"auto\");\n syncRangeFromScrollRef.current();\n };\n\n const applyScrollOffset = (\n offset: number,\n behavior: VirtualScrollScrollBehaviorOptions[\"behavior\"],\n ): void => {\n const element = ref.current;\n if (!element) return;\n\n const store = measurementStoreRef.current;\n const totalSize = dynamicLayout ? store.getTotalSize() : items.length * rowHeight;\n const clamped = clampScrollOffset(offset, totalSize, viewportSize);\n writeScrollOffset(element, orientation, clamped, resolveScrollBehavior(behavior));\n syncRangeFromScrollRef.current();\n\n if (isSmoothScrollActive(behavior ?? \"auto\", readScrollOffset(element, orientation), clamped)) {\n if (smoothRafRef.current !== 0) cancelAnimationFrame(smoothRafRef.current);\n const tick = (): void => {\n syncRangeFromScrollRef.current();\n const node = ref.current;\n if (!node) {\n smoothRafRef.current = 0;\n return;\n }\n if (isSmoothScrollActive(\"smooth\", readScrollOffset(node, orientation), clamped)) {\n smoothRafRef.current = requestAnimationFrame(tick);\n return;\n }\n smoothRafRef.current = 0;\n };\n smoothRafRef.current = requestAnimationFrame(tick);\n }\n };\n\n const applyScrollOffsetRef = useRef(\n (_offset: number, _behavior: VirtualScrollScrollBehaviorOptions[\"behavior\"]) => {},\n );\n\n applyScrollOffsetRef.current = applyScrollOffset;\n\n useImperativeHandle(\n scrollRef,\n (): VirtualScrollHandle => ({\n get element() {\n return ref.current;\n },\n scrollToIndex(index: number, options?: VirtualScrollScrollToIndexOptions): void {\n const normalized = normalizeScrollToIndexOptions(options);\n const store = measurementStoreRef.current;\n const metrics = {\n getRowOffset: store.getRowOffset.bind(store),\n getRowHeight: store.getRowHeight.bind(store),\n getTotalSize: store.getTotalSize.bind(store),\n };\n const offset = computeScrollOffsetForIndex(\n index,\n normalized.align,\n metrics,\n scrollOptionsRef.current.viewportSize,\n scrollOffsetRef.current,\n );\n applyScrollOffsetRef.current(offset, normalized.behavior);\n },\n scrollToOffset(offset: number, options?: VirtualScrollScrollBehaviorOptions): void {\n applyScrollOffsetRef.current(offset, normalizeScrollBehaviorOptions(options));\n },\n scrollToEnd(options?: VirtualScrollScrollBehaviorOptions): void {\n const store = measurementStoreRef.current;\n const totalSize = dynamicLayout\n ? store.getTotalSize()\n : scrollOptionsRef.current.itemCount * scrollOptionsRef.current.rowHeight;\n const offset = Math.max(0, totalSize - scrollOptionsRef.current.viewportSize);\n applyScrollOffsetRef.current(offset, normalizeScrollBehaviorOptions(options));\n },\n }),\n [dynamicLayout],\n );\n\n const range = useMemo(() => {\n void rangeVersion;\n void measurementVersion;\n return computeRange();\n }, [rangeVersion, measurementVersion]);\n\n const totalSize = useMemo(() => {\n void measurementVersion;\n if (dynamicLayout) return measurementStoreRef.current.getTotalSize();\n return items.length * rowHeight;\n }, [dynamicLayout, items.length, measurementVersion, rowHeight]);\n\n const slotCount = useMemo(() => {\n if (items.length === 0) return 0;\n return Math.max(\n range.end - range.start,\n computeVirtualScrollSlotCount(viewportSize, rowHeight, overscan),\n );\n }, [items.length, overscan, range.end, range.start, rowHeight, viewportSize]);\n\n const rowSlots = useMemo(\n () => buildRowSlots(items, range.start, slotCount),\n [items, range.start, slotCount],\n );\n\n const handleRowMeasureRef = useRef((index: number, itemId: string, height: number) => {\n const store = measurementStoreRef.current;\n const previousHeight = store.getRowHeight(index);\n const rowOffset = store.getRowOffset(index);\n if (!store.setMeasuredHeight(itemId, index, height)) return;\n\n const adjustment = computeScrollAdjustmentForRowResize({\n scrollTop: scrollOffsetRef.current,\n rowOffset,\n previousHeight,\n nextHeight: height,\n });\n\n if (adjustment > 0) {\n const ios = iosControllerRef.current;\n if (ios.isTouchScrolling()) {\n ios.queueAdjustment(adjustment);\n } else {\n applyScrollDeltaRef.current(adjustment);\n }\n }\n\n setMeasurementVersion((version) => version + 1);\n });\n\n const rowsFragmentRef = useRef<FragmentInstance>(null);\n\n // One ResizeObserver for the whole pooled row set. React keeps the\n // subscription in sync as slots mount and unmount — pinned by\n // core/fragment-instance.test.tsx. Fixed layout never measures rows, so no\n // observer is created there at all.\n useEffect(() => {\n if (!dynamicLayout) return;\n const fragment = rowsFragmentRef.current;\n if (!fragment || typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const target = entry.target;\n if (!(target instanceof HTMLElement)) continue;\n const raw = target.dataset[\"uiifyRowIndex\"];\n if (raw === undefined) continue;\n const index = Number(raw);\n if (!Number.isInteger(index)) continue;\n const itemId = itemsRef.current[index]?.id;\n if (itemId === undefined) continue;\n const size = horizontal ? entry.contentRect.width : entry.contentRect.height;\n handleRowMeasureRef.current(index, itemId, size);\n }\n });\n\n fragment.observeUsing(observer);\n return () => fragment.unobserveUsing(observer);\n }, [dynamicLayout, horizontal]);\n\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const scrollState = createVirtualScrollScrollState({\n resetDelay: isScrollingResetDelay,\n supportsScrollEnd: supportsScrollEndEvent(),\n onChange: (isScrolling) => {\n onScrollStateChangeRef.current?.({ isScrolling });\n if (!isScrolling) {\n iosControllerRef.current.flushAdjustments(applyScrollDeltaRef.current);\n }\n },\n });\n\n let onScroll: (() => void) | null = null;\n let onScrollEnd: (() => void) | null = null;\n const ios = iosControllerRef.current;\n const onTouchStart = (): void => ios.onTouchStart();\n const onTouchEnd = (): void => ios.onTouchEnd();\n\n onScroll = () => {\n scrollState.notifyScroll();\n syncRangeFromScrollRef.current();\n };\n element.addEventListener(\"scroll\", onScroll, { passive: true });\n if (supportsScrollEndEvent()) {\n onScrollEnd = () => {\n scrollState.notifyScrollEnd();\n syncRangeFromScrollRef.current();\n };\n element.addEventListener(\"scrollend\", onScrollEnd, { passive: true });\n }\n element.addEventListener(\"touchstart\", onTouchStart, { passive: true });\n element.addEventListener(\"touchend\", onTouchEnd, { passive: true });\n\n return () => {\n if (smoothRafRef.current !== 0) cancelAnimationFrame(smoothRafRef.current);\n if (onScroll) element.removeEventListener(\"scroll\", onScroll);\n if (onScrollEnd) element.removeEventListener(\"scrollend\", onScrollEnd);\n element.removeEventListener(\"touchstart\", onTouchStart);\n element.removeEventListener(\"touchend\", onTouchEnd);\n scrollState.dispose();\n };\n }, [isScrollingResetDelay]);\n\n useEffect(() => {\n const element = ref.current;\n const store = measurementStoreRef.current;\n const nextTotalSize = dynamicLayout ? store.getTotalSize() : items.length * rowHeight;\n const previous = anchorSnapshotRef.current;\n const scrollOffset = element ? readScrollOffset(element, orientation) : scrollOffsetRef.current;\n const nextSnapshot = {\n firstItemId: items[0]?.id,\n itemCount: items.length,\n totalSize: nextTotalSize,\n scrollTop: scrollOffset,\n };\n\n const adjustment = resolveVirtualScrollAnchorAdjustment({\n anchor,\n followAppend,\n viewportHeight: viewportSize,\n previous,\n next: nextSnapshot,\n });\n\n if (adjustment && element) {\n // Retain the anchored viewport.\n writeScrollOffset(element, orientation, scrollOffset + adjustment.delta, adjustment.behavior);\n syncRangeFromScrollRef.current();\n }\n\n anchorSnapshotRef.current = nextSnapshot;\n }, [anchor, followAppend, items, dynamicLayout, orientation, rowHeight, viewportSize]);\n\n useEffect(() => {\n emitRangeChange(range);\n }, [range]);\n\n const scrollportStyle = {\n height,\n overflowX: horizontal ? \"auto\" : undefined,\n overflowY: horizontal ? \"hidden\" : \"auto\",\n \"--uiify-virtual-scroll-count\": items.length,\n \"--uiify-virtual-scroll-row-height\": `${rowHeight}px`,\n ...(dynamicLayout ? { \"--uiify-virtual-scroll-total-size\": `${totalSize}px` } : {}),\n } as CSSProperties;\n\n const spacerStyle = dynamicLayout\n ? undefined\n : ({\n \"--uiify-range-start\": range.start,\n } as CSSProperties);\n\n return (\n <div\n ref={ref}\n data-testid={testId}\n data-uiify-virtual-scroll=\"\"\n data-dynamic-layout={dynamicLayout ? \"\" : undefined}\n data-orientation={orientation}\n role={role}\n className={className}\n style={scrollportStyle}\n >\n <div data-part=\"spacer\" style={spacerStyle}>\n {rowSlots.map((slot) => {\n const store = measurementStoreRef.current;\n const rowOffset = dynamicLayout ? store.getRowOffset(slot.index) : undefined;\n const measuredRowHeight = dynamicLayout ? store.getRowHeight(slot.index) : undefined;\n\n return (\n <VirtualScrollRowShell\n key={`slot-${slot.slotIndex}`}\n slotIndex={slot.slotIndex}\n index={slot.index}\n itemCount={items.length}\n listRole={role}\n dynamicLayout={dynamicLayout}\n {...(rowOffset !== undefined ? { rowOffset } : {})}\n {...(measuredRowHeight !== undefined ? { rowHeight: measuredRowHeight } : {})}\n >\n {slot.item ? (\n <VirtualScrollRowItem item={slot.item} index={slot.index} renderRow={renderRow} />\n ) : null}\n </VirtualScrollRowShell>\n );\n })}\n </div>\n </div>\n );\n}\nVirtualScroll.displayName = \"VirtualScroll\";\n"],"mappings":"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","names":["dynamicLayout","overscan","rowHeight","viewportSize","range","slotCount","orientation","totalSize","height"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { VirtualScroll } from "./VirtualScroll.js";
|
|
2
|
-
export type { VirtualScrollAnchor, VirtualScrollHandle, VirtualScrollItem, VirtualScrollListRole, VirtualScrollOwnProps, VirtualScrollRangeChange, VirtualScrollRowSlot, VirtualScrollScrollAlign, VirtualScrollScrollBehavior, VirtualScrollScrollBehaviorOptions, VirtualScrollScrollState, VirtualScrollScrollToIndexOptions, } from "./types.js";
|
|
2
|
+
export type { VirtualScrollAnchor, VirtualScrollHandle, VirtualScrollItem, VirtualScrollListRole, VirtualScrollOrientation, VirtualScrollOwnProps, VirtualScrollRangeChange, VirtualScrollRowSlot, VirtualScrollScrollAlign, VirtualScrollScrollBehavior, VirtualScrollScrollBehaviorOptions, VirtualScrollScrollState, VirtualScrollScrollToIndexOptions, } from "./types.js";
|
|
3
3
|
export { VIRTUAL_SCROLL_ANCHOR_THRESHOLD, VIRTUAL_SCROLL_OVERSCAN, VIRTUAL_SCROLL_ROW_HEIGHT, VIRTUAL_SCROLL_SCROLL_RESET_DELAY, } from "./types.js";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/virtual-scroll/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,2BAA2B,EAC3B,kCAAkC,EAClC,wBAAwB,EACxB,iCAAiC,GAClC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,+BAA+B,EAC/B,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/virtual-scroll/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,2BAA2B,EAC3B,kCAAkC,EAClC,wBAAwB,EACxB,iCAAiC,GAClC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,+BAA+B,EAC/B,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/virtual-scroll/index.ts"],"sourcesContent":["export { VirtualScroll } from \"./VirtualScroll.js\";\nexport type {\n VirtualScrollAnchor,\n VirtualScrollHandle,\n VirtualScrollItem,\n VirtualScrollListRole,\n VirtualScrollOwnProps,\n VirtualScrollRangeChange,\n VirtualScrollRowSlot,\n VirtualScrollScrollAlign,\n VirtualScrollScrollBehavior,\n VirtualScrollScrollBehaviorOptions,\n VirtualScrollScrollState,\n VirtualScrollScrollToIndexOptions,\n} from \"./types.js\";\nexport {\n VIRTUAL_SCROLL_ANCHOR_THRESHOLD,\n VIRTUAL_SCROLL_OVERSCAN,\n VIRTUAL_SCROLL_ROW_HEIGHT,\n VIRTUAL_SCROLL_SCROLL_RESET_DELAY,\n} from \"./types.js\";\n"],"mappings":"AAAA,SAAS,qBAAqB;
|
|
1
|
+
{"version":3,"sources":["../../../src/components/virtual-scroll/index.ts"],"sourcesContent":["export { VirtualScroll } from \"./VirtualScroll.js\";\nexport type {\n VirtualScrollAnchor,\n VirtualScrollHandle,\n VirtualScrollItem,\n VirtualScrollListRole,\n VirtualScrollOrientation,\n VirtualScrollOwnProps,\n VirtualScrollRangeChange,\n VirtualScrollRowSlot,\n VirtualScrollScrollAlign,\n VirtualScrollScrollBehavior,\n VirtualScrollScrollBehaviorOptions,\n VirtualScrollScrollState,\n VirtualScrollScrollToIndexOptions,\n} from \"./types.js\";\nexport {\n VIRTUAL_SCROLL_ANCHOR_THRESHOLD,\n VIRTUAL_SCROLL_OVERSCAN,\n VIRTUAL_SCROLL_ROW_HEIGHT,\n VIRTUAL_SCROLL_SCROLL_RESET_DELAY,\n} from \"./types.js\";\n"],"mappings":"AAAA,SAAS,qBAAqB;AAgB9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
|
|
@@ -12,6 +12,8 @@ export interface VirtualScrollItem {
|
|
|
12
12
|
}
|
|
13
13
|
export type VirtualScrollListRole = "list" | "grid";
|
|
14
14
|
export type VirtualScrollAnchor = "start" | "end";
|
|
15
|
+
/** Scroll axis. Row size props (`rowHeight`, `estimateRowHeight`) measure along this axis. */
|
|
16
|
+
export type VirtualScrollOrientation = "vertical" | "horizontal";
|
|
15
17
|
export type VirtualScrollScrollAlign = "start" | "center" | "end" | "auto";
|
|
16
18
|
export type VirtualScrollScrollBehavior = "auto" | "instant" | "smooth";
|
|
17
19
|
export interface VirtualScrollScrollBehaviorOptions {
|
|
@@ -41,8 +43,11 @@ export interface VirtualScrollHandle {
|
|
|
41
43
|
}
|
|
42
44
|
export interface VirtualScrollOwnProps<T extends VirtualScrollItem> {
|
|
43
45
|
readonly items: readonly T[];
|
|
46
|
+
/** Scrollport height; also the viewport extent used before the first resize measurement. */
|
|
44
47
|
readonly height?: number;
|
|
48
|
+
/** Fixed item size along the scroll axis — row height when vertical, item width when horizontal. */
|
|
45
49
|
readonly rowHeight?: number;
|
|
50
|
+
readonly orientation?: VirtualScrollOrientation;
|
|
46
51
|
readonly overscan?: number;
|
|
47
52
|
readonly className?: string;
|
|
48
53
|
readonly testId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/virtual-scroll/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/virtual-scroll/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5C,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,uFAAuF;AACvF,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD,8EAA8E;AAC9E,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpD,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,KAAK,CAAC;AAElD,8FAA8F;AAC9F,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG,YAAY,CAAC;AAEjE,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAE3E,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,2BAA2B,CAAC;CACjD;AAED,MAAM,WAAW,iCAAkC,SAAQ,kCAAkC;IAC3F,QAAQ,CAAC,KAAK,CAAC,EAAE,wBAAwB,CAAC;CAC3C;AAED,MAAM,WAAW,wBAAwB;IACvC,6CAA6C;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iCAAiC,GAAG,IAAI,CAAC;IAChF,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kCAAkC,GAAG,IAAI,CAAC;IACnF,WAAW,CAAC,OAAO,CAAC,EAAE,kCAAkC,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,iBAAiB;IAChE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,oGAAoG;IACpG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,wBAAwB,CAAC;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC9C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACvD,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC3C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACzE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,iBAAiB;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;CACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/virtual-scroll/types.ts"],"sourcesContent":["import type { ReactNode, Ref } from \"react\";\n\n/** Fixed row height — default for scroll math when `estimateRowHeight` is omitted. */\nexport const VIRTUAL_SCROLL_ROW_HEIGHT = 64;\n\n/** Overscan rows above and below the viewport to avoid flicker during fast scroll. */\nexport const VIRTUAL_SCROLL_OVERSCAN = 5;\n\n/** Default delay before treating scroll as settled when `scrollend` is unavailable. */\nexport const VIRTUAL_SCROLL_SCROLL_RESET_DELAY = 150;\n\n/** Distance from list bottom treated as \"pinned to end\" for anchor/follow. */\nexport const VIRTUAL_SCROLL_ANCHOR_THRESHOLD = 1;\n\nexport interface VirtualScrollItem {\n readonly id: string;\n}\n\nexport type VirtualScrollListRole = \"list\" | \"grid\";\n\nexport type VirtualScrollAnchor = \"start\" | \"end\";\n\nexport type VirtualScrollScrollAlign = \"start\" | \"center\" | \"end\" | \"auto\";\n\nexport type VirtualScrollScrollBehavior = \"auto\" | \"instant\" | \"smooth\";\n\nexport interface VirtualScrollScrollBehaviorOptions {\n readonly behavior?: VirtualScrollScrollBehavior;\n}\n\nexport interface VirtualScrollScrollToIndexOptions extends VirtualScrollScrollBehaviorOptions {\n readonly align?: VirtualScrollScrollAlign;\n}\n\nexport interface VirtualScrollRangeChange {\n /** First visible row index (no overscan). */\n readonly startIndex: number;\n /** Last visible row index, inclusive (no overscan). */\n readonly endIndex: number;\n /** First rendered row index including overscan. */\n readonly overscanStart: number;\n /** Last rendered row index, inclusive, including overscan. */\n readonly overscanEnd: number;\n}\n\nexport interface VirtualScrollScrollState {\n readonly isScrolling: boolean;\n}\n\nexport interface VirtualScrollHandle {\n readonly element: HTMLDivElement | null;\n scrollToIndex(index: number, options?: VirtualScrollScrollToIndexOptions): void;\n scrollToOffset(offset: number, options?: VirtualScrollScrollBehaviorOptions): void;\n scrollToEnd(options?: VirtualScrollScrollBehaviorOptions): void;\n}\n\nexport interface VirtualScrollOwnProps<T extends VirtualScrollItem> {\n readonly items: readonly T[];\n readonly height?: number;\n readonly rowHeight?: number;\n readonly overscan?: number;\n readonly className?: string;\n readonly testId?: string;\n readonly renderRow: (item: T, index: number) => ReactNode;\n readonly scrollRef?: Ref<VirtualScrollHandle>;\n readonly estimateRowHeight?: (index: number) => number;\n readonly onRangeChange?: (range: VirtualScrollRangeChange) => void;\n readonly role?: VirtualScrollListRole;\n readonly anchor?: VirtualScrollAnchor;\n readonly followAppend?: boolean | \"smooth\";\n readonly onScrollStateChange?: (state: VirtualScrollScrollState) => void;\n readonly isScrollingResetDelay?: number;\n}\n\nexport interface VirtualScrollRowSlot<T extends VirtualScrollItem> {\n readonly slotIndex: number;\n readonly index: number;\n readonly item: T | null;\n}\n"],"mappings":"AAGO,MAAM,4BAA4B;AAGlC,MAAM,0BAA0B;AAGhC,MAAM,oCAAoC;AAG1C,MAAM,kCAAkC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/virtual-scroll/types.ts"],"sourcesContent":["import type { ReactNode, Ref } from \"react\";\n\n/** Fixed row height — default for scroll math when `estimateRowHeight` is omitted. */\nexport const VIRTUAL_SCROLL_ROW_HEIGHT = 64;\n\n/** Overscan rows above and below the viewport to avoid flicker during fast scroll. */\nexport const VIRTUAL_SCROLL_OVERSCAN = 5;\n\n/** Default delay before treating scroll as settled when `scrollend` is unavailable. */\nexport const VIRTUAL_SCROLL_SCROLL_RESET_DELAY = 150;\n\n/** Distance from list bottom treated as \"pinned to end\" for anchor/follow. */\nexport const VIRTUAL_SCROLL_ANCHOR_THRESHOLD = 1;\n\nexport interface VirtualScrollItem {\n readonly id: string;\n}\n\nexport type VirtualScrollListRole = \"list\" | \"grid\";\n\nexport type VirtualScrollAnchor = \"start\" | \"end\";\n\n/** Scroll axis. Row size props (`rowHeight`, `estimateRowHeight`) measure along this axis. */\nexport type VirtualScrollOrientation = \"vertical\" | \"horizontal\";\n\nexport type VirtualScrollScrollAlign = \"start\" | \"center\" | \"end\" | \"auto\";\n\nexport type VirtualScrollScrollBehavior = \"auto\" | \"instant\" | \"smooth\";\n\nexport interface VirtualScrollScrollBehaviorOptions {\n readonly behavior?: VirtualScrollScrollBehavior;\n}\n\nexport interface VirtualScrollScrollToIndexOptions extends VirtualScrollScrollBehaviorOptions {\n readonly align?: VirtualScrollScrollAlign;\n}\n\nexport interface VirtualScrollRangeChange {\n /** First visible row index (no overscan). */\n readonly startIndex: number;\n /** Last visible row index, inclusive (no overscan). */\n readonly endIndex: number;\n /** First rendered row index including overscan. */\n readonly overscanStart: number;\n /** Last rendered row index, inclusive, including overscan. */\n readonly overscanEnd: number;\n}\n\nexport interface VirtualScrollScrollState {\n readonly isScrolling: boolean;\n}\n\nexport interface VirtualScrollHandle {\n readonly element: HTMLDivElement | null;\n scrollToIndex(index: number, options?: VirtualScrollScrollToIndexOptions): void;\n scrollToOffset(offset: number, options?: VirtualScrollScrollBehaviorOptions): void;\n scrollToEnd(options?: VirtualScrollScrollBehaviorOptions): void;\n}\n\nexport interface VirtualScrollOwnProps<T extends VirtualScrollItem> {\n readonly items: readonly T[];\n /** Scrollport height; also the viewport extent used before the first resize measurement. */\n readonly height?: number;\n /** Fixed item size along the scroll axis — row height when vertical, item width when horizontal. */\n readonly rowHeight?: number;\n readonly orientation?: VirtualScrollOrientation;\n readonly overscan?: number;\n readonly className?: string;\n readonly testId?: string;\n readonly renderRow: (item: T, index: number) => ReactNode;\n readonly scrollRef?: Ref<VirtualScrollHandle>;\n readonly estimateRowHeight?: (index: number) => number;\n readonly onRangeChange?: (range: VirtualScrollRangeChange) => void;\n readonly role?: VirtualScrollListRole;\n readonly anchor?: VirtualScrollAnchor;\n readonly followAppend?: boolean | \"smooth\";\n readonly onScrollStateChange?: (state: VirtualScrollScrollState) => void;\n readonly isScrollingResetDelay?: number;\n}\n\nexport interface VirtualScrollRowSlot<T extends VirtualScrollItem> {\n readonly slotIndex: number;\n readonly index: number;\n readonly item: T | null;\n}\n"],"mappings":"AAGO,MAAM,4BAA4B;AAGlC,MAAM,0BAA0B;AAGhC,MAAM,oCAAoC;AAG1C,MAAM,kCAAkC;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VirtualScrollOrientation } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Logical scroll offset along the list axis. RTL `scrollLeft` runs from 0 to
|
|
4
|
+
* `-max`, so its magnitude is the distance from the inline-start edge.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readScrollOffset(element: HTMLElement, orientation: VirtualScrollOrientation): number;
|
|
7
|
+
/** Move the scrollport to a logical offset along the list axis. */
|
|
8
|
+
export declare function writeScrollOffset(element: HTMLElement, orientation: VirtualScrollOrientation, offset: number, behavior: ScrollBehavior): void;
|
|
9
|
+
//# sourceMappingURL=virtual-scroll-axis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-scroll-axis.d.ts","sourceRoot":"","sources":["../../../src/components/virtual-scroll/virtual-scroll-axis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,wBAAwB,GACpC,MAAM,CAIR;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,wBAAwB,EACrC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,GACvB,IAAI,CAWN"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function readScrollOffset(element, orientation) {
|
|
2
|
+
return orientation === "horizontal" ? Math.abs(element.scrollLeft) : element.scrollTop;
|
|
3
|
+
}
|
|
4
|
+
function writeScrollOffset(element, orientation, offset, behavior) {
|
|
5
|
+
if (orientation === "horizontal") {
|
|
6
|
+
const left = element.matches(":dir(rtl)") ? -offset : offset;
|
|
7
|
+
if (typeof element.scrollTo === "function") element.scrollTo({ left, behavior });
|
|
8
|
+
else element.scrollLeft = left;
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (typeof element.scrollTo === "function") element.scrollTo({ top: offset, behavior });
|
|
12
|
+
else element.scrollTop = offset;
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
readScrollOffset,
|
|
16
|
+
writeScrollOffset
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=virtual-scroll-axis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/virtual-scroll/virtual-scroll-axis.ts"],"sourcesContent":["import type { VirtualScrollOrientation } from \"./types.js\";\n\n/**\n * Logical scroll offset along the list axis. RTL `scrollLeft` runs from 0 to\n * `-max`, so its magnitude is the distance from the inline-start edge.\n */\nexport function readScrollOffset(\n element: HTMLElement,\n orientation: VirtualScrollOrientation,\n): number {\n return orientation === \"horizontal\"\n ? Math.abs(element.scrollLeft) // banned-read-ok: scroll position drives the virtual range.\n : element.scrollTop; // banned-read-ok: scroll position drives the virtual range.\n}\n\n/** Move the scrollport to a logical offset along the list axis. */\nexport function writeScrollOffset(\n element: HTMLElement,\n orientation: VirtualScrollOrientation,\n offset: number,\n behavior: ScrollBehavior,\n): void {\n if (orientation === \"horizontal\") {\n // `:dir()` resolves from the DOM `dir` attribute — no style or layout read.\n const left = element.matches(\":dir(rtl)\") ? -offset : offset;\n if (typeof element.scrollTo === \"function\") element.scrollTo({ left, behavior });\n else element.scrollLeft = left; // banned-read-ok: native scroll fallback for this viewport.\n return;\n }\n\n if (typeof element.scrollTo === \"function\") element.scrollTo({ top: offset, behavior });\n else element.scrollTop = offset; // banned-read-ok: native scroll fallback for this viewport.\n}\n"],"mappings":"AAMO,SAAS,iBACd,SACA,aACQ;AACR,SAAO,gBAAgB,eACnB,KAAK,IAAI,QAAQ,UAAU,IAC3B,QAAQ;AACd;AAGO,SAAS,kBACd,SACA,aACA,QACA,UACM;AACN,MAAI,gBAAgB,cAAc;AAEhC,UAAM,OAAO,QAAQ,QAAQ,WAAW,IAAI,CAAC,SAAS;AACtD,QAAI,OAAO,QAAQ,aAAa,WAAY,SAAQ,SAAS,EAAE,MAAM,SAAS,CAAC;AAAA,QAC1E,SAAQ,aAAa;AAC1B;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,aAAa,WAAY,SAAQ,SAAS,EAAE,KAAK,QAAQ,SAAS,CAAC;AAAA,MACjF,SAAQ,YAAY;AAC3B;","names":[]}
|