From 91c95b12d1d6b967511c0dc7dfa3fed00886b6e0 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 30 Jul 2025 11:47:43 +0200 Subject: [PATCH] Merge pull request [#467](https://github.com/mazeincoding/AppCut/issues/467) --- .../properties-panel/text-properties.tsx | 205 +++++++++++++++++- 1 file changed, 195 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/editor/properties-panel/text-properties.tsx b/apps/web/src/components/editor/properties-panel/text-properties.tsx index e147ea65..f8380ff4 100644 --- a/apps/web/src/components/editor/properties-panel/text-properties.tsx +++ b/apps/web/src/components/editor/properties-panel/text-properties.tsx @@ -5,6 +5,8 @@ import { TextElement } from "@/types/timeline"; import { useTimelineStore } from "@/stores/timeline-store"; import { Slider } from "@/components/ui/slider"; import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { useState } from "react"; import { PropertyItem, PropertyItemLabel, @@ -20,6 +22,70 @@ export function TextProperties({ }) { const { updateTextElement } = useTimelineStore(); + // Local state for input values to allow temporary empty/invalid states + const [fontSizeInput, setFontSizeInput] = useState( + element.fontSize.toString() + ); + const [opacityInput, setOpacityInput] = useState( + Math.round(element.opacity * 100).toString() + ); + + const parseAndValidateNumber = ( + value: string, + min: number, + max: number, + fallback: number + ): number => { + const parsed = parseInt(value, 10); + if (isNaN(parsed)) return fallback; + return Math.max(min, Math.min(max, parsed)); + }; + + const handleFontSizeChange = (value: string) => { + setFontSizeInput(value); + + if (value.trim() !== "") { + const fontSize = parseAndValidateNumber(value, 8, 300, element.fontSize); + updateTextElement(trackId, element.id, { fontSize }); + } + }; + + const handleFontSizeBlur = () => { + const fontSize = parseAndValidateNumber( + fontSizeInput, + 8, + 300, + element.fontSize + ); + setFontSizeInput(fontSize.toString()); + updateTextElement(trackId, element.id, { fontSize }); + }; + + const handleOpacityChange = (value: string) => { + setOpacityInput(value); + + if (value.trim() !== "") { + const opacityPercent = parseAndValidateNumber( + value, + 0, + 100, + Math.round(element.opacity * 100) + ); + updateTextElement(trackId, element.id, { opacity: opacityPercent / 100 }); + } + }; + + const handleOpacityBlur = () => { + const opacityPercent = parseAndValidateNumber( + opacityInput, + 0, + 100, + Math.round(element.opacity * 100) + ); + setOpacityInput(opacityPercent.toString()); + updateTextElement(trackId, element.id, { opacity: opacityPercent / 100 }); + }; + return (