import { useLayoutEffect, useRef, useState } from 'react' import { ImagePlus, Plus } from 'lucide-react' import { imagePathsFromDataTransfer, imagePathsFromFiles } from '../lib/keyframe-drop' import { lastFrameFromDuration, type DraggedFrame } from '../lib/keyframe-timeline' import { applyKeyframeImagePaths, type KeyframeItem } from '../lib/multi-keyframe' import { KeyframeTimeline } from './KeyframeTimeline' interface MultiKeyframePanelProps { keyframes: readonly KeyframeItem[] duration: number | null fps: number maxCount: number playheadFrame: number onPlayheadChange: (frameIndex: number) => void onChange: (keyframes: KeyframeItem[]) => void onDragFrameChange?: (drag: DraggedFrame | null) => void } export function MultiKeyframePanel({ keyframes, duration, fps, maxCount, playheadFrame, onPlayheadChange, onChange, onDragFrameChange, }: MultiKeyframePanelProps) { const fileInputRef = useRef(null) const replaceIdRef = useRef(null) const [isDragOver, setIsDragOver] = useState(false) const lastFrame = duration == null ? null : lastFrameFromDuration(duration, fps) const addEnabled = lastFrame !== null && keyframes.length < maxCount useLayoutEffect(() => { if (lastFrame === null) onDragFrameChange?.(null) }, [lastFrame, onDragFrameChange]) const applyPaths = (paths: string[], replaceId: string | null = null) => { onChange(applyKeyframeImagePaths({ keyframes, paths, replaceId, lastFrame, preferredFrame: playheadFrame, maxCount, })) } const openFilePicker = (replaceId: string | null) => { replaceIdRef.current = replaceId fileInputRef.current?.click() } return (
Keyframes {keyframes.length}/{maxCount}
{lastFrame === null ? (
Choose a duration to place keyframes
) : ( { onChange(keyframes.map((keyframe) => ( keyframe.id === id ? { ...keyframe, frameIndex } : keyframe ))) onPlayheadChange(frameIndex) }} onStrengthChange={(id, strength) => { onChange(keyframes.map((keyframe) => ( keyframe.id === id ? { ...keyframe, strength } : keyframe ))) }} onReplaceRequest={(id) => openFilePicker(id)} onDelete={(id) => onChange(keyframes.filter((keyframe) => keyframe.id !== id))} onImagesDrop={(dataTransfer, replaceId) => { applyPaths(imagePathsFromDataTransfer(dataTransfer), replaceId) }} /> )} { applyPaths(imagePathsFromFiles(event.target.files ?? []), replaceIdRef.current) replaceIdRef.current = null event.target.value = '' }} />
) }