summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline/timeline-toolbar.tsx
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-02-10 16:28:21 +0100
committerMaze Winther <mazewinther@gmail.com>2026-02-10 16:28:21 +0100
commitfca99d6126c31fbb18ed9f1034cee6f940b040e8 (patch)
tree11500ff0fcc4a933650e3e91f1ea00fd315c4d0c /apps/web/src/components/editor/timeline/timeline-toolbar.tsx
parent33a8705f0d967891402c61dd3192e6bad24921ef (diff)
downloadopencut-fca99d6126c31fbb18ed9f1034cee6f940b040e8.tar.gz
refactor
Diffstat (limited to 'apps/web/src/components/editor/timeline/timeline-toolbar.tsx')
-rw-r--r--apps/web/src/components/editor/timeline/timeline-toolbar.tsx354
1 files changed, 0 insertions, 354 deletions
diff --git a/apps/web/src/components/editor/timeline/timeline-toolbar.tsx b/apps/web/src/components/editor/timeline/timeline-toolbar.tsx
deleted file mode 100644
index 6ac6fba5..00000000
--- a/apps/web/src/components/editor/timeline/timeline-toolbar.tsx
+++ /dev/null
@@ -1,354 +0,0 @@
-import { useEditor } from "@/hooks/use-editor";
-import {
- TooltipProvider,
- Tooltip,
- TooltipTrigger,
- TooltipContent,
-} from "@/components/ui/tooltip";
-import { Button } from "@/components/ui/button";
-import { SkipBack, SplitSquareHorizontal } from "lucide-react";
-import {
- SplitButton,
- SplitButtonLeft,
- SplitButtonRight,
- SplitButtonSeparator,
-} from "@/components/ui/split-button";
-import { Slider } from "@/components/ui/slider";
-import { formatTimeCode } from "@/lib/time";
-import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
-import { sliderToZoom, zoomToSlider } from "@/lib/timeline/zoom-utils";
-import { EditableTimecode } from "@/components/editable-timecode";
-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,
- PauseIcon,
- PlayIcon,
- 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 (
- <ScrollArea className="scrollbar-hidden">
- <div className="flex h-10 items-center justify-between border-b px-2 py-1">
- <ToolbarLeftSection />
-
- <SceneSelector />
-
- <ToolbarRightSection
- zoomLevel={zoomLevel}
- minZoom={minZoom}
- onZoomChange={(zoom) => setZoomLevel({ zoom })}
- onZoom={handleZoom}
- />
- </div>
- </ScrollArea>
- );
-}
-
-function ToolbarLeftSection() {
- const editor = useEditor();
- const currentTime = editor.playback.getCurrentTime();
- const isPlaying = editor.playback.getIsPlaying();
- const currentBookmarked = editor.scenes.isBookmarked({ time: currentTime });
-
- const handleAction = ({
- action,
- event,
- }: {
- action: TAction;
- event: React.MouseEvent;
- }) => {
- event.stopPropagation();
- invokeAction(action);
- };
-
- return (
- <div className="flex items-center gap-1">
- <TooltipProvider delayDuration={500}>
- <ToolbarButton
- icon={
- isPlaying ? (
- <HugeiconsIcon icon={PauseIcon} />
- ) : (
- <HugeiconsIcon icon={PlayIcon} />
- )
- }
- tooltip={isPlaying ? "Pause" : "Play"}
- onClick={({ event }) =>
- handleAction({ action: "toggle-play", event })
- }
- />
-
- <ToolbarButton
- icon={<SkipBack />}
- tooltip="Go to start"
- onClick={({ event }) => handleAction({ action: "goto-start", event })}
- />
-
- <div className="bg-border mx-1 h-6 w-px" />
-
- <TimeDisplay />
-
- <div className="bg-border mx-1 h-6 w-px" />
-
- <ToolbarButton
- icon={<HugeiconsIcon icon={ScissorIcon} />}
- tooltip="Split element"
- onClick={({ event }) => handleAction({ action: "split", event })}
- />
-
- <ToolbarButton
- icon={<HugeiconsIcon icon={AlignLeftIcon} />}
- tooltip="Split left"
- onClick={({ event }) => handleAction({ action: "split-left", event })}
- />
-
- <ToolbarButton
- icon={<HugeiconsIcon icon={AlignRightIcon} />}
- tooltip="Split right"
- onClick={({ event }) =>
- handleAction({ action: "split-right", event })
- }
- />
-
- <ToolbarButton
- icon={<SplitSquareHorizontal />}
- tooltip="Coming soon" /* separate audio */
- disabled={true}
- onClick={({ event: _event }) => {}}
- />
-
- <ToolbarButton
- icon={<HugeiconsIcon icon={Copy01Icon} />}
- tooltip="Duplicate element"
- onClick={({ event }) =>
- handleAction({ action: "duplicate-selected", event })
- }
- />
-
- <ToolbarButton
- icon={<HugeiconsIcon icon={SnowIcon} />}
- tooltip="Coming soon" /* freeze frame */
- disabled={true}
- onClick={({ event: _event }) => {}}
- />
-
- <ToolbarButton
- icon={<HugeiconsIcon icon={Delete02Icon} />}
- tooltip="Delete element"
- onClick={({ event }) =>
- handleAction({ action: "delete-selected", event })
- }
- />
-
- <div className="bg-border mx-1 h-6 w-px" />
-
- <Tooltip>
- <ToolbarButton
- icon={
- <HugeiconsIcon
- icon={Bookmark02Icon}
- className={currentBookmarked ? "fill-primary text-primary" : ""}
- />
- }
- tooltip={currentBookmarked ? "Remove bookmark" : "Add bookmark"}
- onClick={({ event }) =>
- handleAction({ action: "toggle-bookmark", event })
- }
- />
- </Tooltip>
- </TooltipProvider>
- </div>
- );
-}
-
-function TimeDisplay() {
- const editor = useEditor();
- const currentTime = editor.playback.getCurrentTime();
- const totalDuration = editor.timeline.getTotalDuration();
- const fps = editor.project.getActive().settings.fps;
-
- return (
- <div className="flex flex-row items-center justify-center px-2">
- <EditableTimecode
- time={currentTime}
- duration={totalDuration}
- format="HH:MM:SS:FF"
- fps={fps}
- onTimeChange={({ time }) => editor.playback.seek({ time })}
- className="text-center"
- />
- <div className="text-muted-foreground px-2 font-mono text-xs">/</div>
- <div className="text-muted-foreground text-center font-mono text-xs">
- {formatTimeCode({
- timeInSeconds: totalDuration,
- format: "HH:MM:SS:FF",
- fps,
- })}
- </div>
- </div>
- );
-}
-
-function SceneSelector() {
- const editor = useEditor();
- const currentScene = editor.scenes.getActiveScene();
-
- return (
- <div>
- <SplitButton className="border-foreground/10 border">
- <SplitButtonLeft>{currentScene?.name || "No Scene"}</SplitButtonLeft>
- <SplitButtonSeparator />
- <ScenesView>
- <SplitButtonRight onClick={() => {}} type="button">
- <HugeiconsIcon icon={Layers01Icon} className="size-4" />
- </SplitButtonRight>
- </ScenesView>
- </SplitButton>
- </div>
- );
-}
-
-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 (
- <div className="flex items-center gap-1">
- <TooltipProvider delayDuration={500}>
- <ToolbarButton
- icon={
- <HugeiconsIcon
- icon={MagnetIcon}
- className={cn(snappingEnabled ? "text-primary" : "")}
- />
- }
- tooltip="Auto snapping"
- onClick={() => toggleSnapping()}
- />
-
- <ToolbarButton
- icon={
- <HugeiconsIcon
- icon={Link04Icon}
- className={cn(
- rippleEditingEnabled ? "text-primary" : "",
- "scale-110",
- )}
- />
- }
- tooltip="Ripple editing"
- onClick={() => toggleRippleEditing()}
- />
- </TooltipProvider>
-
- <div className="bg-border mx-1 h-6 w-px" />
-
- <div className="flex items-center gap-1">
- <Button
- variant="text"
- size="icon"
- type="button"
- onClick={() => onZoom({ direction: "out" })}
- >
- <HugeiconsIcon icon={SearchMinusIcon} />
- </Button>
- <Slider
- className="w-28"
- value={[zoomToSlider({ zoomLevel, minZoom })]}
- onValueChange={(values) =>
- onZoomChange(sliderToZoom({ sliderPosition: values[0], minZoom }))
- }
- min={0}
- max={1}
- step={0.005}
- />
- <Button
- variant="text"
- size="icon"
- type="button"
- onClick={() => onZoom({ direction: "in" })}
- >
- <HugeiconsIcon icon={SearchAddIcon} />
- </Button>
- </div>
- </div>
- );
-}
-
-function ToolbarButton({
- icon,
- tooltip,
- onClick,
- disabled,
-}: {
- icon: React.ReactNode;
- tooltip: string;
- onClick: ({ event }: { event: React.MouseEvent }) => void;
- disabled?: boolean;
-}) {
- return (
- <Tooltip delayDuration={200}>
- <TooltipTrigger asChild>
- <Button
- variant="text"
- size="icon"
- type="button"
- onClick={(event) => onClick({ event })}
- className={disabled ? "cursor-not-allowed opacity-50" : ""}
- >
- {icon}
- </Button>
- </TooltipTrigger>
- <TooltipContent>{tooltip}</TooltipContent>
- </Tooltip>
- );
-}