diff --git a/src/components/simulation-chart.tsx b/src/components/simulation-chart.tsx index b66f0c1..48cf9a0 100644 --- a/src/components/simulation-chart.tsx +++ b/src/components/simulation-chart.tsx @@ -53,16 +53,46 @@ const SimulationChart = ({ const totalHours = (parseInt(simulationDays, 10) || 3) * 24; const dispDays = parseInt(displayedDays, 10) || 2; - // Dynamically calculate tick interval based on displayed days - // Aim for ~40-50 pixels per tick for readability + // Calculate chart dimensions + const [containerWidth, setContainerWidth] = React.useState(1000); + const containerRef = React.useRef(null); + + React.useEffect(() => { + const updateWidth = () => { + if (containerRef.current) { + setContainerWidth(containerRef.current.clientWidth); + } + }; + + updateWidth(); + window.addEventListener('resize', updateWidth); + return () => window.removeEventListener('resize', updateWidth); + }, []); + + const simDays = parseInt(simulationDays, 10) || 3; + + // Y-axis takes ~80px, scrollable area gets the rest + const yAxisWidth = 80; + const scrollableWidth = containerWidth - yAxisWidth; + + // Dynamically calculate tick interval based on available pixel width + // Aim for ~46px per label to avoid overlaps on narrow screens const xTickInterval = React.useMemo(() => { - // Scale interval with displayed days: 1 day = 1h, 2 days = 2h, 3-4 days = 3h, 5+ days = 6h - if (dispDays <= 1) return 1; - if (dispDays <= 2) return 2; - if (dispDays <= 4) return 3; - if (dispDays <= 6) return 4; - return 6; - }, [dispDays]); + const MIN_PX_PER_TICK = 46; + const intervals = [1, 2, 3, 4, 6, 8, 12, 24]; + + const pxPerDay = scrollableWidth / Math.max(1, dispDays); + const ticksPerDay = Math.floor(pxPerDay / MIN_PX_PER_TICK); + + // Plenty of room: allow hourly ticks + if (ticksPerDay >= 16) return 1; + // Extremely tight: show one tick per day boundary + if (ticksPerDay <= 0) return 24; + + const idealInterval = 24 / ticksPerDay; + const selected = intervals.find((value) => value >= idealInterval); + return selected ?? 24; + }, [dispDays, scrollableWidth]); // Generate ticks for continuous time axis const chartTicks = React.useMemo(() => { @@ -207,28 +237,6 @@ const SimulationChart = ({ return Array.from(dataMap.values()).sort((a, b) => a.timeHours - b.timeHours); }, [combinedProfile, templateProfile, daysWithDeviations]); - // Calculate chart dimensions - const [containerWidth, setContainerWidth] = React.useState(1000); - const containerRef = React.useRef(null); - - React.useEffect(() => { - const updateWidth = () => { - if (containerRef.current) { - setContainerWidth(containerRef.current.clientWidth); - } - }; - - updateWidth(); - window.addEventListener('resize', updateWidth); - return () => window.removeEventListener('resize', updateWidth); - }, []); - - const simDays = parseInt(simulationDays, 10) || 3; - - // Y-axis takes ~80px, scrollable area gets the rest - const yAxisWidth = 80; - const scrollableWidth = containerWidth - yAxisWidth; - // Calculate chart width for scrollable area const chartWidth = simDays <= dispDays ? scrollableWidth