diff options
Diffstat (limited to 'apps/web/src/components/editor/properties-panel')
3 files changed, 69 insertions, 85 deletions
diff --git a/apps/web/src/components/editor/properties-panel/index.tsx b/apps/web/src/components/editor/properties-panel/index.tsx index 720c4a26..6c79327e 100644 --- a/apps/web/src/components/editor/properties-panel/index.tsx +++ b/apps/web/src/components/editor/properties-panel/index.tsx @@ -1,95 +1,22 @@ "use client"; -import { FPS_PRESETS } from "@/constants/timeline-constants"; -import { useAspectRatio } from "@/hooks/use-aspect-ratio"; import { useMediaStore } from "@/stores/media-store"; -import { useProjectStore } from "@/stores/project-store"; import { useTimelineStore } from "@/stores/timeline-store"; -import { Label } from "../../ui/label"; import { ScrollArea } from "../../ui/scroll-area"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "../../ui/select"; import { AudioProperties } from "./audio-properties"; import { MediaProperties } from "./media-properties"; -import { - PropertyItem, - PropertyItemLabel, - PropertyItemValue, -} from "./property-item"; import { TextProperties } from "./text-properties"; +import { SquareSlashIcon } from "lucide-react"; export function PropertiesPanel() { - const { activeProject, updateProjectFps } = useProjectStore(); - const { getDisplayName, canvasSize } = useAspectRatio(); const { selectedElements, tracks } = useTimelineStore(); const { mediaItems } = useMediaStore(); - const handleFpsChange = (value: string) => { - const fps = parseFloat(value); - if (!isNaN(fps) && fps > 0) { - updateProjectFps(fps); - } - }; - - const emptyView = ( - <div className="space-y-4 p-5"> - {/* Media Properties */} - <div className="flex flex-col gap-3"> - <PropertyItem direction="column"> - <PropertyItemLabel className="text-xs text-muted-foreground"> - Name: - </PropertyItemLabel> - <PropertyItemValue className="text-xs truncate"> - {activeProject?.name || ""} - </PropertyItemValue> - </PropertyItem> - <PropertyItem direction="column"> - <PropertyItemLabel className="text-xs text-muted-foreground"> - Aspect ratio: - </PropertyItemLabel> - <PropertyItemValue className="text-xs truncate"> - {getDisplayName()} - </PropertyItemValue> - </PropertyItem> - <PropertyItem direction="column"> - <PropertyItemLabel className="text-xs text-muted-foreground"> - Resolution: - </PropertyItemLabel> - <PropertyItemValue className="text-xs truncate"> - {`${canvasSize.width} × ${canvasSize.height}`} - </PropertyItemValue> - </PropertyItem> - <div className="flex flex-col gap-1"> - <Label className="text-xs text-muted-foreground">Frame rate:</Label> - <Select - value={(activeProject?.fps || 30).toString()} - onValueChange={handleFpsChange} - > - <SelectTrigger className="w-32 h-6 text-xs"> - <SelectValue /> - </SelectTrigger> - <SelectContent> - {FPS_PRESETS.map(({ value, label }) => ( - <SelectItem key={value} value={value} className="text-xs"> - {label} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - </div> - </div> - ); - return ( - <ScrollArea className="h-full bg-panel rounded-sm"> - {selectedElements.length > 0 - ? selectedElements.map(({ trackId, elementId }) => { + <> + {selectedElements.length > 0 ? ( + <ScrollArea className="h-full bg-panel rounded-sm"> + {selectedElements.map(({ trackId, elementId }) => { const track = tracks.find((t) => t.id === trackId); const element = track?.elements.find((e) => e.id === elementId); @@ -116,8 +43,28 @@ export function PropertiesPanel() { ); } return null; - }) - : emptyView} - </ScrollArea> + })} + </ScrollArea> + ) : ( + <EmptyView /> + )} + </> + ); +} + +function EmptyView() { + return ( + <div className="bg-panel h-full p-4 flex flex-col items-center justify-center gap-3"> + <SquareSlashIcon + className="w-10 h-10 text-muted-foreground" + strokeWidth={1.5} + /> + <div className="flex flex-col gap-2 text-center"> + <p className="text-lg font-medium">It’s empty here</p> + <p className="text-sm text-muted-foreground text-balance"> + Click an element on the timeline to edit its properties + </p> + </div> + </div> ); } diff --git a/apps/web/src/components/editor/properties-panel/property-item.tsx b/apps/web/src/components/editor/properties-panel/property-item.tsx index 804ffb39..dc6b6d74 100644 --- a/apps/web/src/components/editor/properties-panel/property-item.tsx +++ b/apps/web/src/components/editor/properties-panel/property-item.tsx @@ -1,4 +1,6 @@ import { cn } from "@/lib/utils"; +import { ChevronDown } from "lucide-react"; +import { useState } from "react"; interface PropertyItemProps { direction?: "row" | "column"; @@ -17,7 +19,7 @@ export function PropertyItem({ "flex gap-2", direction === "row" ? "items-center justify-between gap-6" - : "flex-col gap-1", + : "flex-col gap-1.5", className )} > @@ -33,7 +35,11 @@ export function PropertyItemLabel({ children: React.ReactNode; className?: string; }) { - return <label className={cn("text-xs", className)}>{children}</label>; + return ( + <label className={cn("text-xs text-muted-foreground", className)}> + {children} + </label> + ); } export function PropertyItemValue({ @@ -43,5 +49,36 @@ export function PropertyItemValue({ children: React.ReactNode; className?: string; }) { - return <div className={cn("flex-1", className)}>{children}</div>; + return <div className={cn("flex-1 text-sm", className)}>{children}</div>; +} + +interface PropertyGroupProps { + title: string; + children: React.ReactNode; + defaultExpanded?: boolean; + className?: string; +} + +export function PropertyGroup({ + title, + children, + defaultExpanded = true, + className, +}: PropertyGroupProps) { + const [isExpanded, setIsExpanded] = useState(defaultExpanded); + + return ( + <PropertyItem direction="column" className={cn("gap-3", className)}> + <div + className="flex items-center gap-1.5 cursor-pointer" + onClick={() => setIsExpanded(!isExpanded)} + > + <PropertyItemLabel className="cursor-pointer"> + {title} + </PropertyItemLabel> + <ChevronDown className={cn("size-3", !isExpanded && "-rotate-90")} /> + </div> + {isExpanded && <PropertyItemValue>{children}</PropertyItemValue>} + </PropertyItem> + ); } 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 f8380ff4..a06624eb 100644 --- a/apps/web/src/components/editor/properties-panel/text-properties.tsx +++ b/apps/web/src/components/editor/properties-panel/text-properties.tsx @@ -91,7 +91,7 @@ export function TextProperties({ <Textarea placeholder="Name" defaultValue={element.content} - className="min-h-[4.5rem] resize-none bg-background/50" + className="min-h-18 resize-none bg-background/50" onChange={(e) => updateTextElement(trackId, element.id, { content: e.target.value }) } |
