diff options
Diffstat (limited to 'apps/web/src/components/editor/panels/properties/index.tsx')
| -rw-r--r-- | apps/web/src/components/editor/panels/properties/index.tsx | 141 |
1 files changed, 75 insertions, 66 deletions
diff --git a/apps/web/src/components/editor/panels/properties/index.tsx b/apps/web/src/components/editor/panels/properties/index.tsx index cb45996d..3f3ff1f3 100644 --- a/apps/web/src/components/editor/panels/properties/index.tsx +++ b/apps/web/src/components/editor/panels/properties/index.tsx @@ -1,86 +1,95 @@ "use client"; import { ScrollArea } from "@/components/ui/scroll-area"; -import { AudioProperties } from "./audio-properties"; -import { VideoProperties } from "./video-properties"; -import { TextProperties } from "./text-properties"; -import { EffectProperties } from "./effect-properties"; -import { ClipEffectsProperties } from "./clip-effects-properties"; -import { EmptyView } from "./empty-view"; +import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { useEditor } from "@/hooks/use-editor"; import { useElementSelection } from "@/hooks/timeline/element/use-element-selection"; -import { usePropertiesStore } from "@/stores/properties-store"; -import { isVisualElement } from "@/lib/timeline"; -import type { TimelineElement, TimelineTrack } from "@/types/timeline"; - -function ElementProperties({ - track, - element, -}: { - track: TimelineTrack; - element: TimelineElement; -}) { - if (element.type === "text") { - return <TextProperties element={element} trackId={track.id} />; - } - if (element.type === "audio") { - return <AudioProperties />; - } - if ( - element.type === "video" || - element.type === "image" || - element.type === "sticker" - ) { - return <VideoProperties element={element} trackId={track.id} />; - } - if (element.type === "effect") { - return <EffectProperties element={element} trackId={track.id} />; - } - return null; -} +import { usePropertiesStore } from "./stores/properties-store"; +import { getPropertiesConfig } from "./registry"; +import { cn } from "@/utils/ui"; +import { EmptyView } from "./empty-view"; export function PropertiesPanel() { const editor = useEditor(); + useEditor((e) => e.timeline.getTracks()); + useEditor((e) => e.media.getAssets()); const { selectedElements } = useElementSelection(); - const clipEffectsTarget = usePropertiesStore( - (state) => state.clipEffectsTarget, - ); + const { activeTabPerType, setActiveTab } = usePropertiesStore(); - const clipEffectsTrack = clipEffectsTarget - ? editor.timeline.getTrackById({ trackId: clipEffectsTarget.trackId }) - : null; - const clipEffectsElement = clipEffectsTrack?.elements.find( - (element) => element.id === clipEffectsTarget?.elementId, - ); - const isShowingClipEffects = - clipEffectsTrack && - clipEffectsElement && - isVisualElement(clipEffectsElement); + if (selectedElements.length === 0) { + return ( + <div className="panel bg-background flex h-full flex-col items-center justify-center overflow-hidden rounded-sm border"> + <EmptyView /> + </div> + ); + } + + if (selectedElements.length > 1) { + return ( + <div className="panel bg-background flex h-full flex-col items-center justify-center overflow-hidden rounded-sm border"> + <p className="text-muted-foreground text-sm"> + {selectedElements.length} elements selected.0 + </p> + </div> + ); + } + + const mediaAssets = editor.media.getAssets(); const elementsWithTracks = editor.timeline.getElementsWithTracks({ elements: selectedElements, }); + const elementWithTrack = elementsWithTracks[0]; + + if (!elementWithTrack) return null; + + const { element, track } = elementWithTrack; + const config = getPropertiesConfig({ element, mediaAssets }); + const visibleTabs = config.tabs; + + const storedTabId = activeTabPerType[element.type]; + const isStoredTabVisible = visibleTabs.some((t) => t.id === storedTabId); + const activeTabId = isStoredTabVisible ? storedTabId : config.defaultTab; + const activeTab = + visibleTabs.find((t) => t.id === activeTabId) ?? visibleTabs[0]; + + if (!activeTab) return null; return ( - <div className="panel bg-background h-full rounded-sm border overflow-hidden"> - {isShowingClipEffects ? ( - <ClipEffectsProperties - element={clipEffectsElement} - trackId={clipEffectsTrack.id} - /> - ) : selectedElements.length > 0 ? ( - <ScrollArea className="h-full scrollbar-hidden"> - {elementsWithTracks.map(({ track, element }) => ( - <ElementProperties - key={element.id} - track={track} - element={element} - /> + <div className="panel bg-background flex h-full overflow-hidden rounded-sm border"> + <TooltipProvider delayDuration={0}> + <div className="flex shrink-0 flex-col gap-0.5 border-r p-1 scrollbar-hidden overflow-y-auto"> + {visibleTabs.map((tab) => ( + <Tooltip key={tab.id}> + <TooltipTrigger asChild> + <Button + variant={tab.id === activeTab.id ? "secondary" : "ghost"} + size="icon" + onClick={() => setActiveTab(element.type, tab.id)} + aria-label={tab.label} + className={cn( + "shrink-0", + "h-8 w-8", + tab.id !== activeTab.id && "text-muted-foreground", + )} + > + {tab.icon} + </Button> + </TooltipTrigger> + <TooltipContent side="right">{tab.label}</TooltipContent> + </Tooltip> ))} - </ScrollArea> - ) : ( - <EmptyView /> - )} + </div> + </TooltipProvider> + <ScrollArea className="flex-1 scrollbar-hidden"> + {activeTab.content({ trackId: track.id })} + </ScrollArea> </div> ); } |
