'use client'; import { useRef } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Plus, Trash2 } from 'lucide-react'; import { cn } from '@/lib/utils/cn'; import type { Keyframe } from '@/types/animate'; interface Props { keyframes: Keyframe[]; selectedId: string | null; onSelect: (id: string) => void; onAdd: (offset: number) => void; onDelete: (id: string) => void; onMove: (id: string, newOffset: number) => void; } const TICKS = [0, 25, 50, 75, 100]; export function KeyframeTimeline({ keyframes, selectedId, onSelect, onAdd, onDelete, onMove }: Props) { const trackRef = useRef(null); const getOffsetFromEvent = (clientX: number): number => { if (!trackRef.current) return 0; const rect = trackRef.current.getBoundingClientRect(); const pct = ((clientX - rect.left) / rect.width) * 100; return Math.round(Math.min(100, Math.max(0, pct))); }; const handleTrackClick = (e: React.MouseEvent) => { // Ignore clicks that land directly on a keyframe marker if ((e.target as HTMLElement).closest('[data-keyframe-marker]')) return; onAdd(getOffsetFromEvent(e.clientX)); }; const handlePointerDown = (e: React.PointerEvent, id: string) => { e.preventDefault(); onSelect(id); const el = e.currentTarget as HTMLElement; el.setPointerCapture(e.pointerId); const handleMove = (me: PointerEvent) => { onMove(id, getOffsetFromEvent(me.clientX)); }; const handleUp = () => { el.removeEventListener('pointermove', handleMove); el.removeEventListener('pointerup', handleUp); }; el.addEventListener('pointermove', handleMove); el.addEventListener('pointerup', handleUp); }; const sorted = [...keyframes].sort((a, b) => a.offset - b.offset); const selectedKf = keyframes.find((k) => k.id === selectedId); return ( Keyframes
{keyframes.length} keyframe{keyframes.length !== 1 ? 's' : ''} {selectedKf ? ` · selected: ${selectedKf.offset}%` : ''}
{/* Track */}
{/* Center line */}
{/* Tick marks */} {TICKS.map((tick) => (
{tick}%
))} {/* Keyframe markers */} {sorted.map((kf) => (
{/* Offset labels below */}
{sorted.map((kf) => ( {kf.offset}% ))}
); }