refactor: move element resize logic to react hook
This commit is contained in:
@ -17,7 +17,8 @@ import { useTimelineStore } from "@/stores/timeline-store";
|
|||||||
import { usePlaybackStore } from "@/stores/playback-store";
|
import { usePlaybackStore } from "@/stores/playback-store";
|
||||||
import AudioWaveform from "./audio-waveform";
|
import AudioWaveform from "./audio-waveform";
|
||||||
import { toast } from "sonner";
|
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 {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@ -49,9 +50,21 @@ export function TimelineElement({
|
|||||||
} = useTimelineStore();
|
} = useTimelineStore();
|
||||||
const { currentTime } = usePlaybackStore();
|
const { currentTime } = usePlaybackStore();
|
||||||
|
|
||||||
const [resizing, setResizing] = useState<ResizeState | null>(null);
|
|
||||||
const [elementMenuOpen, setElementMenuOpen] = useState(false);
|
const [elementMenuOpen, setElementMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
resizing,
|
||||||
|
isResizing,
|
||||||
|
handleResizeStart,
|
||||||
|
handleResizeMove,
|
||||||
|
handleResizeEnd,
|
||||||
|
} = useTimelineElementResize({
|
||||||
|
element,
|
||||||
|
track,
|
||||||
|
zoomLevel,
|
||||||
|
onUpdateTrim: updateElementTrim,
|
||||||
|
});
|
||||||
|
|
||||||
const effectiveDuration =
|
const effectiveDuration =
|
||||||
element.duration - element.trimStart - element.trimEnd;
|
element.duration - element.trimStart - element.trimEnd;
|
||||||
const elementWidth = Math.max(80, effectiveDuration * 50 * zoomLevel);
|
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 = () => {
|
const handleDeleteElement = () => {
|
||||||
removeElementFromTrack(track.id, element.id);
|
removeElementFromTrack(track.id, element.id);
|
||||||
setElementMenuOpen(false);
|
setElementMenuOpen(false);
|
||||||
|
83
apps/web/src/hooks/use-timeline-element-resize.ts
Normal file
83
apps/web/src/hooks/use-timeline-element-resize.ts
Normal file
@ -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<ResizeState | null>(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,
|
||||||
|
};
|
||||||
|
}
|
Reference in New Issue
Block a user