From fca99d6126c31fbb18ed9f1034cee6f940b040e8 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Tue, 10 Feb 2026 16:28:21 +0100 Subject: refactor --- .../editor/panels/timeline/timeline-toolbar.tsx | 285 +++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx (limited to 'apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx') diff --git a/apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx b/apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx new file mode 100644 index 00000000..0b02c63f --- /dev/null +++ b/apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx @@ -0,0 +1,285 @@ +import { useEditor } from "@/hooks/use-editor"; +import { + TooltipProvider, + Tooltip, + TooltipTrigger, + TooltipContent, +} from "@/components/ui/tooltip"; +import { Button } from "@/components/ui/button"; +import { SplitSquareHorizontal } from "lucide-react"; +import { + SplitButton, + SplitButtonLeft, + SplitButtonRight, + SplitButtonSeparator, +} from "@/components/ui/split-button"; +import { Slider } from "@/components/ui/slider"; +import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; +import { sliderToZoom, zoomToSlider } from "@/lib/timeline/zoom-utils"; +import { ScenesView } from "../../scenes-view"; +import { type TAction, invokeAction } from "@/lib/actions"; +import { cn } from "@/utils/ui"; +import { useTimelineStore } from "@/stores/timeline-store"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Bookmark02Icon, + Delete02Icon, + SnowIcon, + ScissorIcon, + MagnetIcon, + Link04Icon, + SearchAddIcon, + SearchMinusIcon, + Copy01Icon, + AlignLeftIcon, + AlignRightIcon, + Layers01Icon, +} from "@hugeicons/core-free-icons"; +import { HugeiconsIcon } from "@hugeicons/react"; + +export function TimelineToolbar({ + zoomLevel, + minZoom, + setZoomLevel, +}: { + zoomLevel: number; + minZoom: number; + setZoomLevel: ({ zoom }: { zoom: number }) => void; +}) { + const handleZoom = ({ direction }: { direction: "in" | "out" }) => { + const newZoomLevel = + direction === "in" + ? Math.min( + TIMELINE_CONSTANTS.ZOOM_MAX, + zoomLevel * TIMELINE_CONSTANTS.ZOOM_BUTTON_FACTOR, + ) + : Math.max(minZoom, zoomLevel / TIMELINE_CONSTANTS.ZOOM_BUTTON_FACTOR); + setZoomLevel({ zoom: newZoomLevel }); + }; + + return ( + +
+ + + + + setZoomLevel({ zoom })} + onZoom={handleZoom} + /> +
+
+ ); +} + +function ToolbarLeftSection() { + const editor = useEditor(); + const currentTime = editor.playback.getCurrentTime(); + const currentBookmarked = editor.scenes.isBookmarked({ time: currentTime }); + + const handleAction = ({ + action, + event, + }: { + action: TAction; + event: React.MouseEvent; + }) => { + event.stopPropagation(); + invokeAction(action); + }; + + return ( +
+ + } + tooltip="Split element" + onClick={({ event }) => handleAction({ action: "split", event })} + /> + + } + tooltip="Split left" + onClick={({ event }) => handleAction({ action: "split-left", event })} + /> + + } + tooltip="Split right" + onClick={({ event }) => + handleAction({ action: "split-right", event }) + } + /> + + } + tooltip="Coming soon" /* separate audio */ + disabled={true} + onClick={({ event: _event }) => {}} + /> + + } + tooltip="Duplicate element" + onClick={({ event }) => + handleAction({ action: "duplicate-selected", event }) + } + /> + + } + tooltip="Coming soon" /* freeze frame */ + disabled={true} + onClick={({ event: _event }) => {}} + /> + + } + tooltip="Delete element" + onClick={({ event }) => + handleAction({ action: "delete-selected", event }) + } + /> + +
+ + + } + isActive={currentBookmarked} + tooltip={currentBookmarked ? "Remove bookmark" : "Add bookmark"} + onClick={({ event }) => + handleAction({ action: "toggle-bookmark", event }) + } + /> + + +
+ ); +} + +function SceneSelector() { + const editor = useEditor(); + const currentScene = editor.scenes.getActiveScene(); + + return ( +
+ + {currentScene?.name || "No Scene"} + + + {}} type="button"> + + + + +
+ ); +} + +function ToolbarRightSection({ + zoomLevel, + minZoom, + onZoomChange, + onZoom, +}: { + zoomLevel: number; + minZoom: number; + onZoomChange: (zoom: number) => void; + onZoom: (options: { direction: "in" | "out" }) => void; +}) { + const { + snappingEnabled, + rippleEditingEnabled, + toggleSnapping, + toggleRippleEditing, + } = useTimelineStore(); + + return ( +
+ + } + isActive={snappingEnabled} + tooltip="Auto snapping" + onClick={() => toggleSnapping()} + /> + + } + isActive={rippleEditingEnabled} + tooltip="Ripple editing" + onClick={() => toggleRippleEditing()} + /> + + +
+ +
+ + + onZoomChange(sliderToZoom({ sliderPosition: values[0], minZoom })) + } + min={0} + max={1} + step={0.005} + /> + +
+
+ ); +} + +function ToolbarButton({ + icon, + tooltip, + onClick, + disabled, + isActive, +}: { + icon: React.ReactNode; + tooltip: string; + onClick: ({ event }: { event: React.MouseEvent }) => void; + disabled?: boolean; + isActive?: boolean; +}) { + return ( + + + + + {tooltip} + + ); +} -- cgit v1.3.1