perf(timeline): add horizontal virtualization for allocation bars
Tracks scroll position via requestAnimationFrame to avoid re-renders on every pixel. Allocation bars outside the visible horizontal window (+ 10-column overscan) are skipped during render, reducing DOM nodes significantly at day zoom (365 days × 40px = 14,600px canvas). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -678,6 +678,13 @@ function TimelineViewContent({
|
||||
};
|
||||
}, [resourceHover?.resourceId, isInitialLoading, hasActivePointerOverlay]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// ─── Scroll-left tracking for horizontal virtualization ────────────────────
|
||||
// Updated via RAF so React state only updates after a frame, not on every
|
||||
// pixel of scroll. The ref gives instant reads inside event handlers.
|
||||
const scrollLeftRef = useRef(0);
|
||||
const scrollRafRef = useRef<number | null>(null);
|
||||
const [scrollLeft, setScrollLeft] = useState(0);
|
||||
|
||||
// ─── Lazy-extend date range on scroll ─────────────────────────────────────
|
||||
function handleContainerScroll() {
|
||||
const el = scrollContainerRef.current;
|
||||
@@ -686,6 +693,13 @@ function TimelineViewContent({
|
||||
if (distanceFromRight < CELL_WIDTH * 40) {
|
||||
setViewDays((d) => d + 120);
|
||||
}
|
||||
scrollLeftRef.current = el.scrollLeft;
|
||||
if (scrollRafRef.current === null) {
|
||||
scrollRafRef.current = requestAnimationFrame(() => {
|
||||
scrollRafRef.current = null;
|
||||
setScrollLeft(scrollLeftRef.current);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent) => {
|
||||
@@ -809,6 +823,8 @@ function TimelineViewContent({
|
||||
optimisticAllocations={optimisticAllocations}
|
||||
suppressHoverInteractions={hasActivePointerOverlay}
|
||||
{...(!isSelfServiceTimeline ? { onInlineEdit: (id: string, vals: { startDate: Date; endDate: Date; hoursPerDay: number }, rect: DOMRect) => setInlineEditTarget({ allocationId: id, ...vals, barRect: rect }) } : {})}
|
||||
scrollLeft={scrollLeft}
|
||||
containerWidth={scrollContainerRef.current?.clientWidth ?? 1200}
|
||||
CELL_WIDTH={CELL_WIDTH}
|
||||
dates={dates}
|
||||
totalCanvasWidth={totalCanvasWidth}
|
||||
|
||||
Reference in New Issue
Block a user