'use client'; import { cn } from '@/lib/utils'; import type { IntervalListState } from '@/lib/visualizations/types'; import { IntervalBar } from '../primitives/interval-bar'; import { AnimatePresence } from 'framer-motion'; interface IntervalViewProps { intervalList: IntervalListState; className?: string; } const PIXELS_PER_UNIT = 20; const BAR_HEIGHT = 28; const ROW_GAP = 8; export function IntervalView({ intervalList, className, }: IntervalViewProps) { // Calculate timeline range const minValue = intervalList.minValue ?? 0; const maxValue = intervalList.maxValue ?? Math.max( ...intervalList.intervals.map((i) => i.end), 20 ); const timelineWidth = (maxValue - minValue) * PIXELS_PER_UNIT; // Generate tick marks for the number line const tickStep = maxValue <= 10 ? 1 : maxValue <= 20 ? 2 : 5; const ticks: number[] = []; for (let i = minValue; i <= maxValue; i += tickStep) { ticks.push(i); } // Calculate row assignments to prevent overlapping intervals const rows: IntervalListState['intervals'][] = []; for (const interval of intervalList.intervals) { let placed = false; for (const row of rows) { const overlaps = row.some( (existing) => !(interval.end <= existing.start || interval.start >= existing.end) ); if (!overlaps) { row.push(interval); placed = true; break; } } if (!placed) { rows.push([interval]); } } const intervalsHeight = rows.length * (BAR_HEIGHT + ROW_GAP) - ROW_GAP; return (
{intervalList.label && ( {intervalList.label} )}
{/* Intervals area */}
{rows.map((row, rowIndex) => row.map((interval) => (
)) )}
{/* Number line */}
{/* Horizontal line */}
{/* Tick marks and labels */} {ticks.map((tick) => { const x = (tick - minValue) * PIXELS_PER_UNIT; return (
{tick}
); })}
); }