diff options
Diffstat (limited to 'apps/web/src/hooks/use-timeline-element-resize.ts')
| -rw-r--r-- | apps/web/src/hooks/use-timeline-element-resize.ts | 330 |
1 files changed, 165 insertions, 165 deletions
diff --git a/apps/web/src/hooks/use-timeline-element-resize.ts b/apps/web/src/hooks/use-timeline-element-resize.ts index 9b664af4..f16ca8e2 100644 --- a/apps/web/src/hooks/use-timeline-element-resize.ts +++ b/apps/web/src/hooks/use-timeline-element-resize.ts @@ -4,197 +4,197 @@ import { useMediaStore } from "@/stores/media-store"; import { useTimelineStore } from "@/stores/timeline-store"; interface UseTimelineElementResizeProps { - element: TimelineElement; - track: TimelineTrack; - zoomLevel: number; - onUpdateTrim: ( - trackId: string, - elementId: string, - trimStart: number, - trimEnd: number, - ) => void; - onUpdateDuration: ( - trackId: string, - elementId: string, - duration: number, - ) => void; + element: TimelineElement; + track: TimelineTrack; + zoomLevel: number; + onUpdateTrim: ( + trackId: string, + elementId: string, + trimStart: number, + trimEnd: number + ) => void; + onUpdateDuration: ( + trackId: string, + elementId: string, + duration: number + ) => void; } export function useTimelineElementResize({ - element, - track, - zoomLevel, - onUpdateTrim, - onUpdateDuration, + element, + track, + zoomLevel, + onUpdateTrim, + onUpdateDuration, }: UseTimelineElementResizeProps) { - const [resizing, setResizing] = useState<ResizeState | null>(null); - const { mediaItems } = useMediaStore(); - const { updateElementStartTime } = useTimelineStore(); + const [resizing, setResizing] = useState<ResizeState | null>(null); + const { mediaItems } = useMediaStore(); + const { updateElementStartTime } = useTimelineStore(); - // Set up document-level mouse listeners during resize (like proper drag behavior) - useEffect(() => { - if (!resizing) return; + // Set up document-level mouse listeners during resize (like proper drag behavior) + useEffect(() => { + if (!resizing) return; - const handleDocumentMouseMove = (e: MouseEvent) => { - updateTrimFromMouseMove({ clientX: e.clientX }); - }; + const handleDocumentMouseMove = (e: MouseEvent) => { + updateTrimFromMouseMove({ clientX: e.clientX }); + }; - const handleDocumentMouseUp = () => { - handleResizeEnd(); - }; + const handleDocumentMouseUp = () => { + handleResizeEnd(); + }; - // Add document-level listeners for proper drag behavior - document.addEventListener("mousemove", handleDocumentMouseMove); - document.addEventListener("mouseup", handleDocumentMouseUp); + // Add document-level listeners for proper drag behavior + document.addEventListener("mousemove", handleDocumentMouseMove); + document.addEventListener("mouseup", handleDocumentMouseUp); - return () => { - document.removeEventListener("mousemove", handleDocumentMouseMove); - document.removeEventListener("mouseup", handleDocumentMouseUp); - }; - }, [resizing]); // Re-run when resizing state changes + return () => { + document.removeEventListener("mousemove", handleDocumentMouseMove); + document.removeEventListener("mouseup", handleDocumentMouseUp); + }; + }, [resizing]); // Re-run when resizing state changes - const handleResizeStart = ( - e: React.MouseEvent, - elementId: string, - side: "left" | "right", - ) => { - e.stopPropagation(); - e.preventDefault(); + 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, - }); - }; + setResizing({ + elementId, + side, + startX: e.clientX, + initialTrimStart: element.trimStart, + initialTrimEnd: element.trimEnd, + }); + }; - const canExtendElementDuration = () => { - // Text elements can always be extended - if (element.type === "text") { - return true; - } + const canExtendElementDuration = () => { + // Text elements can always be extended + if (element.type === "text") { + return true; + } - // Media elements - check the media type - if (element.type === "media") { - const mediaItem = mediaItems.find((item) => item.id === element.mediaId); - if (!mediaItem) return false; + // Media elements - check the media type + if (element.type === "media") { + const mediaItem = mediaItems.find((item) => item.id === element.mediaId); + if (!mediaItem) return false; - // Images can be extended (static content) - if (mediaItem.type === "image") { - return true; - } + // Images can be extended (static content) + if (mediaItem.type === "image") { + return true; + } - // Videos and audio cannot be extended beyond their natural duration - // (no additional content exists) - return false; - } + // Videos and audio cannot be extended beyond their natural duration + // (no additional content exists) + return false; + } - return false; - }; + return false; + }; - const updateTrimFromMouseMove = (e: { clientX: number }) => { - if (!resizing) return; + const updateTrimFromMouseMove = (e: { clientX: number }) => { + if (!resizing) return; - const deltaX = e.clientX - resizing.startX; - // Reasonable sensitivity for resize operations - similar to timeline scale - const deltaTime = deltaX / (50 * zoomLevel); + const deltaX = e.clientX - resizing.startX; + // Reasonable sensitivity for resize operations - similar to timeline scale + const deltaTime = deltaX / (50 * zoomLevel); - if (resizing.side === "left") { - // Left resize - different behavior for media vs text/image elements - const maxAllowed = element.duration - resizing.initialTrimEnd - 0.1; - const calculated = resizing.initialTrimStart + deltaTime; + if (resizing.side === "left") { + // Left resize - different behavior for media vs text/image elements + const maxAllowed = element.duration - resizing.initialTrimEnd - 0.1; + const calculated = resizing.initialTrimStart + deltaTime; - if (calculated >= 0) { - // Normal trimming within available content - const newTrimStart = Math.min(maxAllowed, calculated); - const trimDelta = newTrimStart - resizing.initialTrimStart; - const newStartTime = element.startTime + trimDelta; + if (calculated >= 0) { + // Normal trimming within available content + const newTrimStart = Math.min(maxAllowed, calculated); + const trimDelta = newTrimStart - resizing.initialTrimStart; + const newStartTime = element.startTime + trimDelta; - onUpdateTrim( - track.id, - element.id, - newTrimStart, - resizing.initialTrimEnd, - ); - updateElementStartTime(track.id, element.id, newStartTime); - } else { - // Trying to extend beyond trimStart = 0 - if (canExtendElementDuration()) { - // Text/Image: extend element to the left by moving startTime and increasing duration - const extensionAmount = Math.abs(calculated); - const newStartTime = element.startTime - extensionAmount; - const newDuration = element.duration + extensionAmount; + onUpdateTrim( + track.id, + element.id, + newTrimStart, + resizing.initialTrimEnd + ); + updateElementStartTime(track.id, element.id, newStartTime); + } else { + // Trying to extend beyond trimStart = 0 + if (canExtendElementDuration()) { + // Text/Image: extend element to the left by moving startTime and increasing duration + const extensionAmount = Math.abs(calculated); + const newStartTime = element.startTime - extensionAmount; + const newDuration = element.duration + extensionAmount; - // Keep trimStart at 0 and extend the element - onUpdateTrim(track.id, element.id, 0, resizing.initialTrimEnd); - onUpdateDuration(track.id, element.id, newDuration); - updateElementStartTime(track.id, element.id, newStartTime); - } else { - // Video/Audio: can't extend beyond original content - limit to trimStart = 0 - const newTrimStart = 0; - const trimDelta = newTrimStart - resizing.initialTrimStart; - const newStartTime = element.startTime + trimDelta; + // Keep trimStart at 0 and extend the element + onUpdateTrim(track.id, element.id, 0, resizing.initialTrimEnd); + onUpdateDuration(track.id, element.id, newDuration); + updateElementStartTime(track.id, element.id, newStartTime); + } else { + // Video/Audio: can't extend beyond original content - limit to trimStart = 0 + const newTrimStart = 0; + const trimDelta = newTrimStart - resizing.initialTrimStart; + const newStartTime = element.startTime + trimDelta; - onUpdateTrim( - track.id, - element.id, - newTrimStart, - resizing.initialTrimEnd, - ); - updateElementStartTime(track.id, element.id, newStartTime); - } - } - } else { - // Right resize - can extend duration for supported element types - const calculated = resizing.initialTrimEnd - deltaTime; + onUpdateTrim( + track.id, + element.id, + newTrimStart, + resizing.initialTrimEnd + ); + updateElementStartTime(track.id, element.id, newStartTime); + } + } + } else { + // Right resize - can extend duration for supported element types + const calculated = resizing.initialTrimEnd - deltaTime; - if (calculated < 0) { - // We're trying to extend beyond original duration - if (canExtendElementDuration()) { - // Extend the duration instead of reducing trimEnd further - const extensionNeeded = Math.abs(calculated); - const newDuration = element.duration + extensionNeeded; - const newTrimEnd = 0; // Reset trimEnd to 0 since we're extending + if (calculated < 0) { + // We're trying to extend beyond original duration + if (canExtendElementDuration()) { + // Extend the duration instead of reducing trimEnd further + const extensionNeeded = Math.abs(calculated); + const newDuration = element.duration + extensionNeeded; + const newTrimEnd = 0; // Reset trimEnd to 0 since we're extending - // Update duration first, then trim - onUpdateDuration(track.id, element.id, newDuration); - onUpdateTrim( - track.id, - element.id, - resizing.initialTrimStart, - newTrimEnd, - ); - } else { - // Can't extend - just set trimEnd to 0 (maximum possible extension) - onUpdateTrim(track.id, element.id, resizing.initialTrimStart, 0); - } - } else { - // Normal trimming within original duration - const maxTrimEnd = element.duration - resizing.initialTrimStart - 0.1; // Leave at least 0.1s visible - const newTrimEnd = Math.max(0, Math.min(maxTrimEnd, calculated)); + // Update duration first, then trim + onUpdateDuration(track.id, element.id, newDuration); + onUpdateTrim( + track.id, + element.id, + resizing.initialTrimStart, + newTrimEnd + ); + } else { + // Can't extend - just set trimEnd to 0 (maximum possible extension) + onUpdateTrim(track.id, element.id, resizing.initialTrimStart, 0); + } + } else { + // Normal trimming within original duration + const maxTrimEnd = element.duration - resizing.initialTrimStart - 0.1; // Leave at least 0.1s visible + const newTrimEnd = Math.max(0, Math.min(maxTrimEnd, calculated)); - onUpdateTrim( - track.id, - element.id, - resizing.initialTrimStart, - newTrimEnd, - ); - } - } - }; + onUpdateTrim( + track.id, + element.id, + resizing.initialTrimStart, + newTrimEnd + ); + } + } + }; - const handleResizeEnd = () => { - setResizing(null); - }; + const handleResizeEnd = () => { + setResizing(null); + }; - return { - resizing, - isResizing: resizing !== null, - handleResizeStart, - // Return empty handlers since we use document listeners now - handleResizeMove: () => {}, // Not used anymore - handleResizeEnd: () => {}, // Not used anymore - }; + return { + resizing, + isResizing: resizing !== null, + handleResizeStart, + // Return empty handlers since we use document listeners now + handleResizeMove: () => {}, // Not used anymore + handleResizeEnd: () => {}, // Not used anymore + }; } |
