"use client"; import { useCallback, useEffect, useRef } from "react"; import { usePreviewViewport } from "@/components/editor/panels/preview/preview-viewport"; import { useEditor } from "@/hooks/use-editor"; import type { TextElement } from "@/lib/timeline"; import { FONT_SIZE_SCALE_REFERENCE, } from "@/constants/text-constants"; import { DEFAULTS } from "@/lib/timeline/defaults"; import { getElementLocalTime, resolveTransformAtTime, } from "@/lib/animation"; export function TextEditOverlay({ trackId, elementId, element, onCommit, }: { trackId: string; elementId: string; element: TextElement; onCommit: () => void; }) { const editor = useEditor(); const viewport = usePreviewViewport(); const divRef = useRef(null); useEffect(() => { const div = divRef.current; if (!div) return; div.focus(); const range = document.createRange(); range.selectNodeContents(div); const selection = window.getSelection(); selection?.removeAllRanges(); selection?.addRange(range); }, []); const handleInput = useCallback(() => { const div = divRef.current; if (!div) return; const text = div.innerText; editor.timeline.previewElements({ updates: [{ trackId, elementId, updates: { content: text } }], }); }, [editor.timeline, trackId, elementId]); const handleKeyDown = useCallback( ({ event }: { event: React.KeyboardEvent }) => { const { key } = event; if (key === "Escape") { event.preventDefault(); onCommit(); return; } }, [onCommit], ); const canvasSize = editor.project.getActive().settings.canvasSize; if (!canvasSize) return null; const currentTime = editor.playback.getCurrentTime(); const localTime = getElementLocalTime({ timelineTime: currentTime, elementStartTime: element.startTime, elementDuration: element.duration, }); const transform = resolveTransformAtTime({ baseTransform: element.transform, animations: element.animations, localTime, }); const { x: posX, y: posY } = viewport.positionToOverlay({ positionX: transform.position.x, positionY: transform.position.y, }); const { x: displayScaleX } = viewport.getDisplayScale(); const scaledFontSize = element.fontSize * (canvasSize.height / FONT_SIZE_SCALE_REFERENCE); const lineHeight = element.lineHeight ?? DEFAULTS.text.lineHeight; const fontWeight = element.fontWeight === "bold" ? "bold" : "normal"; const fontStyle = element.fontStyle === "italic" ? "italic" : "normal"; const canvasLetterSpacing = element.letterSpacing ?? 0; const lineHeightPx = scaledFontSize * lineHeight; const bg = element.background; const shouldShowBackground = bg.enabled && bg.color && bg.color !== "transparent"; const fontSizeRatio = element.fontSize / DEFAULTS.text.element.fontSize; const canvasPaddingX = shouldShowBackground ? (bg.paddingX ?? DEFAULTS.text.background.paddingX) * fontSizeRatio : 0; const canvasPaddingY = shouldShowBackground ? (bg.paddingY ?? DEFAULTS.text.background.paddingY) * fontSizeRatio : 0; return (
{/* biome-ignore lint/a11y/useSemanticElements: contenteditable required for multiline, IME, paste */}
handleKeyDown({ event })} > {element.content || ""}
); }