"use client"; import { clsx } from "clsx"; import { useMemo } from "react"; import { addDays, createDatePositionCache } from "~/components/timeline/utils.js"; import { formatMonthYear } from "~/lib/format.js"; export function useTimelineLayout( viewStart: Date, viewDays: number, zoom: "day" | "week" | "month", showWeekends: boolean, today: Date, ) { const CELL_WIDTH = zoom === "day" ? 40 : zoom === "week" ? 14 : 4; // Visible dates, filtered by showWeekends const dates = useMemo(() => { const result: Date[] = []; for (let i = 0; i < viewDays; i++) { const d = addDays(viewStart, i); if (!showWeekends && (d.getDay() === 0 || d.getDay() === 6)) continue; result.push(d); } return result; }, [viewStart, viewDays, showWeekends]); const visibleDays = dates.length; const totalCanvasWidth = visibleDays * CELL_WIDTH; // O(1) position helpers via pre-computed cache const { toLeft, toWidth } = useMemo( () => createDatePositionCache(viewStart, viewDays, CELL_WIDTH, showWeekends), [viewStart, viewDays, CELL_WIDTH, showWeekends], ); // Grid lines — memoized; identical for every row const gridLines = useMemo(() => dates.map((date, i) => { const isToday = date.toDateString() === today.toDateString(); const isSaturday = date.getDay() === 6; const isSunday = date.getDay() === 0; return (
); }), [dates, CELL_WIDTH, today]); // eslint-disable-line react-hooks/exhaustive-deps // Month groups for the month header const monthGroups = useMemo(() => { const groups: { label: string; colCount: number }[] = []; for (const d of dates) { const label = formatMonthYear(d); const last = groups[groups.length - 1]; if (last && last.label === label) last.colCount++; else groups.push({ label, colCount: 1 }); } return groups; }, [dates]); // Convert clientX to a Date on the visible timeline function xToDate(clientX: number, rowCanvasRect: DOMRect): Date { const x = clientX - rowCanvasRect.left; const colIndex = Math.max(0, Math.min(dates.length - 1, Math.floor(x / CELL_WIDTH))); return dates[colIndex] ?? today; } return { CELL_WIDTH, dates, visibleDays, totalCanvasWidth, toLeft, toWidth, gridLines, monthGroups, xToDate }; }