import { useCallback, useRef, useState } from "react"; import { useSimulationStore } from "@store/simulationStore"; import { useProcessStore } from "@store/processStore"; import { useGanttScroll } from "@hooks/useGanttScroll"; import GanttRow from "./GanttRow"; import GanttLegend from "./GanttLegend"; import GanttTooltip, { type TooltipData } from "./GanttTooltip"; import { CELL_H, CELL_W } from "./constants"; // ── GanttChart ──────────────────────────────────────────────── const GanttChart = () => { const ticks = useSimulationStore((s) => s.ticks); const status = useSimulationStore((s) => s.status); const processes = useProcessStore((s) => s.processes); // Scrollable container ref for auto-scroll const scrollRef = useRef(null); const currentTickIndex = ticks.length - 1; useGanttScroll(scrollRef, currentTickIndex, status === "running"); // Tooltip state const [tooltip, setTooltip] = useState(null); const handleMouseLeave = useCallback(() => setTooltip(null), []); // Empty state if (ticks.length === 0) { return (
Start the simulation to see the Gantt chart
); } // ── ruler (tick numbers) ────────────────────────────────── const tickNumbers = ticks.map((t) => t.tick); const totalWidth = ticks.length * CELL_W; return (
{/* ── scrollable grid + cursor ── */}
{/* Tick ruler */}
{/* Spacer for sticky process labels column */}
{tickNumbers.map((t) => (
{t % 5 === 0 ? t : ""}
))}
{/* Process rows */}
{processes.map((process) => (
{/* Process label (sticky on the left) */}
{process.name}
))} {/* Current-tick cursor line */} {currentTickIndex >= 0 && (
)}
{/* ── process name labels (sticky left, overlay) ── */} {/* Rendered OUTSIDE the scrollable div so they stay fixed on the left */} {/* We achieve this with a separate absolute-positioned column */} {/* ── legend ── */} {/* ── tooltip ── */} {tooltip && }
); }; export default GanttChart;