59 lines
1.7 KiB
TypeScript
59 lines
1.7 KiB
TypeScript
'use client';
|
|
|
|
import { motion } from 'framer-motion';
|
|
import { cn } from '@/lib/utils';
|
|
import type { IntervalState } from '@/lib/visualizations/types';
|
|
|
|
interface IntervalBarProps {
|
|
interval: IntervalState;
|
|
pixelsPerUnit: number;
|
|
minValue: number;
|
|
height?: number;
|
|
className?: string;
|
|
}
|
|
|
|
const STATE_CLASSES = {
|
|
normal: 'bg-[var(--muted)] border-[var(--border)] text-[var(--foreground)]',
|
|
highlighted: 'bg-[var(--primary)]/30 border-[var(--primary)] text-[var(--primary)]',
|
|
merging: 'bg-amber-500/30 border-amber-500 text-amber-500',
|
|
merged: 'bg-green-500/30 border-green-500 text-green-500',
|
|
dimmed: 'bg-[var(--muted)]/30 border-[var(--border)]/50 text-[var(--muted-foreground)] opacity-40',
|
|
} as const;
|
|
|
|
export function IntervalBar({
|
|
interval,
|
|
pixelsPerUnit,
|
|
minValue,
|
|
height = 32,
|
|
className,
|
|
}: IntervalBarProps) {
|
|
const width = (interval.end - interval.start) * pixelsPerUnit;
|
|
const left = (interval.start - minValue) * pixelsPerUnit;
|
|
const label = interval.label ?? `[${interval.start},${interval.end}]`;
|
|
|
|
return (
|
|
<motion.div
|
|
layout
|
|
initial={{ opacity: 0, scale: 0.9 }}
|
|
animate={{
|
|
opacity: 1,
|
|
scale: interval.state === 'highlighted' || interval.state === 'merging' ? 1.02 : 1,
|
|
}}
|
|
exit={{ opacity: 0, scale: 0.9 }}
|
|
transition={{ duration: 0.2 }}
|
|
className={cn(
|
|
'absolute flex items-center justify-center rounded border-2 font-mono text-xs font-medium transition-colors duration-200',
|
|
STATE_CLASSES[interval.state],
|
|
className
|
|
)}
|
|
style={{
|
|
left: `${left}px`,
|
|
width: `${Math.max(width, 40)}px`,
|
|
height: `${height}px`,
|
|
}}
|
|
>
|
|
{label}
|
|
</motion.div>
|
|
);
|
|
}
|