chore(repo): initialize planarchy workspace
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"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 (
|
||||
<div
|
||||
key={i}
|
||||
className={clsx(
|
||||
"absolute top-0 bottom-0 border-r",
|
||||
isToday ? "border-brand-300 border-r-2" :
|
||||
isSaturday ? "border-amber-200 bg-amber-50/40" :
|
||||
isSunday ? "border-gray-200 bg-gray-100/60" :
|
||||
"border-gray-100",
|
||||
)}
|
||||
style={{ left: i * CELL_WIDTH, width: CELL_WIDTH }}
|
||||
/>
|
||||
);
|
||||
}), [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 };
|
||||
}
|
||||
Reference in New Issue
Block a user