chore(repo): initialize planarchy workspace
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
"use client";
|
||||
|
||||
import { clsx } from "clsx";
|
||||
import { MONTHS_SHORT } from "./timelineConstants.js";
|
||||
|
||||
interface TimelineHeaderProps {
|
||||
monthGroups: { label: string; colCount: number }[];
|
||||
dates: Date[];
|
||||
CELL_WIDTH: number;
|
||||
LABEL_WIDTH: number;
|
||||
HEADER_MONTH_HEIGHT: number;
|
||||
HEADER_DAY_HEIGHT: number;
|
||||
zoom: "day" | "week" | "month";
|
||||
viewMode: "resource" | "project";
|
||||
today: Date;
|
||||
}
|
||||
|
||||
export function TimelineHeader({
|
||||
monthGroups,
|
||||
dates,
|
||||
CELL_WIDTH,
|
||||
LABEL_WIDTH,
|
||||
HEADER_MONTH_HEIGHT,
|
||||
HEADER_DAY_HEIGHT,
|
||||
zoom,
|
||||
viewMode,
|
||||
today,
|
||||
}: TimelineHeaderProps) {
|
||||
return (
|
||||
<>
|
||||
{/* Month header */}
|
||||
<div
|
||||
className="sticky top-0 z-40 flex bg-white border-b border-gray-100"
|
||||
style={{ height: HEADER_MONTH_HEIGHT }}
|
||||
>
|
||||
<div className="flex-shrink-0 border-r border-gray-200" style={{ width: LABEL_WIDTH }} />
|
||||
<div className="flex">
|
||||
{monthGroups.map((m, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="text-xs font-semibold text-gray-500 border-r border-gray-200 px-2 flex items-center bg-gray-50"
|
||||
style={{ width: m.colCount * CELL_WIDTH }}
|
||||
>
|
||||
{m.label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Day header — hidden at month zoom (cells too narrow for labels) */}
|
||||
{zoom !== "month" && (
|
||||
<div
|
||||
className="sticky z-40 flex bg-gray-50 border-b border-gray-200 select-none"
|
||||
style={{ top: HEADER_MONTH_HEIGHT, height: HEADER_DAY_HEIGHT }}
|
||||
>
|
||||
<div
|
||||
className="flex-shrink-0 border-r border-gray-200 flex items-center px-4 text-xs font-medium text-gray-400 uppercase tracking-wider"
|
||||
style={{ width: LABEL_WIDTH }}
|
||||
>
|
||||
{viewMode === "resource" ? "Resource" : "Project / Resource"}
|
||||
</div>
|
||||
<div className="flex">
|
||||
{dates.map((date, i) => {
|
||||
const isToday = date.toDateString() === today.toDateString();
|
||||
const isMonday = date.getDay() === 1;
|
||||
const isSaturday = date.getDay() === 6;
|
||||
const isSunday = date.getDay() === 0;
|
||||
// Week zoom: show label only on Mondays to avoid overcrowding
|
||||
const showLabel = zoom === "day" || isMonday;
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={clsx(
|
||||
"flex-shrink-0 border-r flex flex-col items-center justify-center text-xs overflow-hidden",
|
||||
isToday ? "bg-brand-50 border-brand-200" :
|
||||
isSaturday ? "bg-amber-50/60 border-amber-200" :
|
||||
isSunday ? "bg-gray-100/80 border-gray-200" :
|
||||
isMonday ? "border-gray-200" : "border-gray-100",
|
||||
)}
|
||||
style={{ width: CELL_WIDTH, height: HEADER_DAY_HEIGHT }}
|
||||
>
|
||||
{showLabel && (
|
||||
<>
|
||||
<span className={clsx(
|
||||
"font-medium leading-none",
|
||||
isToday ? "text-brand-600" : isSaturday ? "text-amber-600" : "text-gray-600",
|
||||
)}>
|
||||
{zoom === "week"
|
||||
? `${date.getDate()} ${MONTHS_SHORT[date.getMonth()]}`
|
||||
: date.getDate()}
|
||||
</span>
|
||||
{zoom === "day" && (
|
||||
<span className={clsx(
|
||||
"text-[9px] leading-none mt-0.5",
|
||||
isSaturday ? "text-amber-400" : "text-gray-300",
|
||||
)}>
|
||||
{["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"][date.getDay()]}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user