"use client"; import { useCallback, useEffect, useRef } from "react"; import { usePreviewViewport } from "@/preview/components/preview-viewport"; import { useEditor } from "@/editor/use-editor"; import type { TextElement } from "@/timeline"; import { DEFAULTS } from "@/timeline/defaults"; import { getElementLocalTime, } from "@/animation"; import { resolveTransformAtTime } from "@/rendering/animation-values"; import { buildTransformFromParams } from "@/rendering"; import { resolveTextLayout } from "@/text/primitives"; import { buildTextBackgroundFromElement, buildTextLayoutParamsFromElement, } from "@/text/measure-element"; 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: { params: { 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: buildTransformFromParams({ params: element.params }), 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 textParams = buildTextLayoutParamsFromElement({ element }); const resolvedTextLayout = resolveTextLayout({ text: textParams, canvasHeight: canvasSize.height, }); const lineHeight = textParams.lineHeight ?? DEFAULTS.text.lineHeight; const canvasLetterSpacing = textParams.letterSpacing ?? 0; const lineHeightPx = resolvedTextLayout.lineHeightPx; const bg = buildTextBackgroundFromElement({ element }); const shouldShowBackground = bg.enabled && bg.color && bg.color !== "transparent"; const fontSizeRatio = resolvedTextLayout.fontSizeRatio; const canvasPaddingX = shouldShowBackground ? (bg.paddingX ?? DEFAULTS.text.background.paddingX) * fontSizeRatio : 0; const canvasPaddingY = shouldShowBackground ? (bg.paddingY ?? DEFAULTS.text.background.paddingY) * fontSizeRatio : 0; return (
handleKeyDown({ event })} > {textParams.content}
); }