import { Textarea } from "@/components/ui/textarea"; import { FontPicker } from "@/components/ui/font-picker"; import type { FontFamily } from "@/constants/font-constants"; import type { TextElement } from "@/types/timeline"; import { Slider } from "@/components/ui/slider"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { useState, useRef } from "react"; import { PanelBaseView } from "@/components/editor/panels/panel-base-view"; import { TEXT_PROPERTIES_TABS, isTextPropertiesTab, useTextPropertiesStore, } from "@/stores/text-properties-store"; import { PropertyItem, PropertyItemLabel, PropertyItemValue, } from "./property-item"; import { ColorPicker } from "@/components/ui/color-picker"; import { cn } from "@/utils/ui"; import { capitalizeFirstLetter, uppercase } from "@/utils/string"; import { clamp } from "@/utils/math"; import { HugeiconsIcon } from "@hugeicons/react"; import { LayoutGridIcon } from "@hugeicons/core-free-icons"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { useEditor } from "@/hooks/use-editor"; import { DEFAULT_COLOR } from "@/constants/project-constants"; import { MIN_FONT_SIZE, MAX_FONT_SIZE } from "@/constants/text-constants"; export function TextProperties({ element, trackId, }: { element: TextElement; trackId: string; }) { const editor = useEditor(); const { activeTab, setActiveTab } = useTextPropertiesStore(); const containerRef = useRef(null); const [fontSizeInput, setFontSizeInput] = useState( element.fontSize.toString(), ); const [opacityInput, setOpacityInput] = useState( Math.round(element.opacity * 100).toString(), ); const lastSelectedColor = useRef(DEFAULT_COLOR); const handleFontSizeChange = ({ value }: { value: string }) => { setFontSizeInput(value); if (value.trim() !== "") { const parsed = parseInt(value, 10); const fontSize = Number.isNaN(parsed) ? element.fontSize : clamp({ value: parsed, min: MIN_FONT_SIZE, max: MAX_FONT_SIZE }); editor.timeline.updateElement({ trackId, elementId: element.id, updates: { fontSize }, }); } }; const handleFontSizeBlur = () => { const parsed = parseInt(fontSizeInput, 10); const fontSize = Number.isNaN(parsed) ? element.fontSize : clamp({ value: parsed, min: MIN_FONT_SIZE, max: MAX_FONT_SIZE }); setFontSizeInput(fontSize.toString()); editor.timeline.updateElement({ trackId, elementId: element.id, updates: { fontSize }, }); }; const handleOpacityChange = ({ value }: { value: string }) => { setOpacityInput(value); if (value.trim() !== "") { const parsed = parseInt(value, 10); const opacityPercent = Number.isNaN(parsed) ? Math.round(element.opacity * 100) : clamp({ value: parsed, min: 0, max: 100 }); editor.timeline.updateElement({ trackId, elementId: element.id, updates: { opacity: opacityPercent / 100 }, }); } }; const handleOpacityBlur = () => { const parsed = parseInt(opacityInput, 10); const opacityPercent = Number.isNaN(parsed) ? Math.round(element.opacity * 100) : clamp({ value: parsed, min: 0, max: 100 }); setOpacityInput(opacityPercent.toString()); editor.timeline.updateElement({ trackId, elementId: element.id, updates: { opacity: opacityPercent / 100 }, }); }; const handleColorChange = ({ color }: { color: string }) => { if (color !== "transparent") { lastSelectedColor.current = color; } editor.timeline.updateElement({ trackId, elementId: element.id, updates: { backgroundColor: color }, }); }; const handleTransparentToggle = ({ isTransparent, }: { isTransparent: boolean; }) => { const newColor = isTransparent ? "transparent" : lastSelectedColor.current; editor.timeline.updateElement({ trackId, elementId: element.id, updates: { backgroundColor: newColor }, }); }; return ( { if (isTextPropertiesTab(v)) setActiveTab(v); }} ref={containerRef} tabs={TEXT_PROPERTIES_TABS.map((t) => ({ value: t.value, label: t.label, content: t.value === "transform" ? (
) : (