From f3763b846543debeb3ebfdbbbee16f614931cb06 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 7 Jul 2025 19:13:20 +0200 Subject: [PATCH] refactor: move element resize logic to react hook --- .../components/editor/timeline-element.tsx | 70 ++++------------ .../src/hooks/use-timeline-element-resize.ts | 83 +++++++++++++++++++ 2 files changed, 98 insertions(+), 55 deletions(-) create mode 100644 apps/web/src/hooks/use-timeline-element-resize.ts diff --git a/apps/web/src/components/editor/timeline-element.tsx b/apps/web/src/components/editor/timeline-element.tsx index fe2f6bc..b2c95bb 100644 --- a/apps/web/src/components/editor/timeline-element.tsx +++ b/apps/web/src/components/editor/timeline-element.tsx @@ -17,7 +17,8 @@ import { useTimelineStore } from "@/stores/timeline-store"; import { usePlaybackStore } from "@/stores/playback-store"; import AudioWaveform from "./audio-waveform"; import { toast } from "sonner"; -import { TimelineElementProps, ResizeState, TrackType } from "@/types/timeline"; +import { TimelineElementProps, TrackType } from "@/types/timeline"; +import { useTimelineElementResize } from "@/hooks/use-timeline-element-resize"; import { DropdownMenu, DropdownMenuContent, @@ -49,9 +50,21 @@ export function TimelineElement({ } = useTimelineStore(); const { currentTime } = usePlaybackStore(); - const [resizing, setResizing] = useState(null); const [elementMenuOpen, setElementMenuOpen] = useState(false); + const { + resizing, + isResizing, + handleResizeStart, + handleResizeMove, + handleResizeEnd, + } = useTimelineElementResize({ + element, + track, + zoomLevel, + onUpdateTrim: updateElementTrim, + }); + const effectiveDuration = element.duration - element.trimStart - element.trimEnd; const elementWidth = Math.max(80, effectiveDuration * 50 * zoomLevel); @@ -77,59 +90,6 @@ export function TimelineElement({ } }; - // Resize handles for trimming elements - const handleResizeStart = ( - e: React.MouseEvent, - elementId: string, - side: "left" | "right" - ) => { - e.stopPropagation(); - e.preventDefault(); - - setResizing({ - elementId, - side, - startX: e.clientX, - initialTrimStart: element.trimStart, - initialTrimEnd: element.trimEnd, - }); - }; - - const updateTrimFromMouseMove = (e: { clientX: number }) => { - if (!resizing) return; - - const deltaX = e.clientX - resizing.startX; - const deltaTime = deltaX / (50 * zoomLevel); - - if (resizing.side === "left") { - const newTrimStart = Math.max( - 0, - Math.min( - element.duration - element.trimEnd - 0.1, - resizing.initialTrimStart + deltaTime - ) - ); - updateElementTrim(track.id, element.id, newTrimStart, element.trimEnd); - } else { - const newTrimEnd = Math.max( - 0, - Math.min( - element.duration - element.trimStart - 0.1, - resizing.initialTrimEnd - deltaTime - ) - ); - updateElementTrim(track.id, element.id, element.trimStart, newTrimEnd); - } - }; - - const handleResizeMove = (e: React.MouseEvent) => { - updateTrimFromMouseMove(e); - }; - - const handleResizeEnd = () => { - setResizing(null); - }; - const handleDeleteElement = () => { removeElementFromTrack(track.id, element.id); setElementMenuOpen(false); diff --git a/apps/web/src/hooks/use-timeline-element-resize.ts b/apps/web/src/hooks/use-timeline-element-resize.ts new file mode 100644 index 0000000..b12087e --- /dev/null +++ b/apps/web/src/hooks/use-timeline-element-resize.ts @@ -0,0 +1,83 @@ +import { useState } from "react"; +import { ResizeState, TimelineElement, TimelineTrack } from "@/types/timeline"; + +interface UseTimelineElementResizeProps { + element: TimelineElement; + track: TimelineTrack; + zoomLevel: number; + onUpdateTrim: ( + trackId: string, + elementId: string, + trimStart: number, + trimEnd: number + ) => void; +} + +export function useTimelineElementResize({ + element, + track, + zoomLevel, + onUpdateTrim, +}: UseTimelineElementResizeProps) { + const [resizing, setResizing] = useState(null); + + const handleResizeStart = ( + e: React.MouseEvent, + elementId: string, + side: "left" | "right" + ) => { + e.stopPropagation(); + e.preventDefault(); + + setResizing({ + elementId, + side, + startX: e.clientX, + initialTrimStart: element.trimStart, + initialTrimEnd: element.trimEnd, + }); + }; + + const updateTrimFromMouseMove = (e: { clientX: number }) => { + if (!resizing) return; + + const deltaX = e.clientX - resizing.startX; + const deltaTime = deltaX / (50 * zoomLevel); + + if (resizing.side === "left") { + const newTrimStart = Math.max( + 0, + Math.min( + element.duration - element.trimEnd - 0.1, + resizing.initialTrimStart + deltaTime + ) + ); + onUpdateTrim(track.id, element.id, newTrimStart, element.trimEnd); + } else { + const newTrimEnd = Math.max( + 0, + Math.min( + element.duration - element.trimStart - 0.1, + resizing.initialTrimEnd - deltaTime + ) + ); + onUpdateTrim(track.id, element.id, element.trimStart, newTrimEnd); + } + }; + + const handleResizeMove = (e: React.MouseEvent) => { + updateTrimFromMouseMove(e); + }; + + const handleResizeEnd = () => { + setResizing(null); + }; + + return { + resizing, + isResizing: resizing !== null, + handleResizeStart, + handleResizeMove, + handleResizeEnd, + }; +}