diff --git a/src/components/roadmap/gantt-bar.tsx b/src/components/roadmap/gantt-bar.tsx new file mode 100644 index 0000000..68f18b9 --- /dev/null +++ b/src/components/roadmap/gantt-bar.tsx @@ -0,0 +1,75 @@ +"use client"; + +import { useMemo } from "react"; + +import { datePosition } from "@/lib/roadmap/timeline"; +import type { TimelineRange } from "@/lib/roadmap/timeline"; + +interface GanttBarProps { + href: string; + startDate: string; + targetDate: string; + range: TimelineRange; + /** Hex/rgb colour from the client. */ + color: string; + /** Indent from the left of the track — subtasks use a larger pad. */ + insetLeft: number; + /** Bar height in px. Cards taller, subtasks shorter. */ + height: number; + /** Tooltip text shown on hover (native title attribute). */ + title: string; + /** Optional strikethrough for subtasks whose `done` is true. */ + strike?: boolean; +} + +/** + * A single horizontal bar in the Gantt track. Width is derived from + * the date range against the same `TimelineRange` the header uses, so + * a card and the month ticks above it can never disagree. Falls back to + * a small minimum width so a card with `start_date == target_date` + * still gets a clickable target. + */ +export function GanttBar({ + href, + startDate, + targetDate, + range, + color, + insetLeft, + height, + title, + strike = false, +}: GanttBarProps) { + const MIN_WIDTH = 0.012; + + const { left, width } = useMemo(() => { + const anchor = startDate || targetDate; + const startFraction = datePosition(anchor, range); + const endFraction = Math.max( + startFraction + MIN_WIDTH, + datePosition(targetDate || anchor, range) + ); + return { left: startFraction, width: endFraction - startFraction }; + }, [startDate, targetDate, range]); + + return ( + + {title} + + ); +}