summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks
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/hooks
parent33a8705f0d967891402c61dd3192e6bad24921ef (diff)
downloadopencut-fca99d6126c31fbb18ed9f1034cee6f940b040e8.tar.gz
refactor
Diffstat (limited to 'apps/web/src/hooks')
-rw-r--r--apps/web/src/hooks/timeline/use-edge-auto-scroll.ts5
-rw-r--r--apps/web/src/hooks/timeline/use-timeline-zoom.ts22
-rw-r--r--apps/web/src/hooks/use-container-size.ts29
-rw-r--r--apps/web/src/hooks/use-fullscreen.ts30
-rw-r--r--apps/web/src/hooks/use-preview-interaction.ts10
5 files changed, 87 insertions, 9 deletions
diff --git a/apps/web/src/hooks/timeline/use-edge-auto-scroll.ts b/apps/web/src/hooks/timeline/use-edge-auto-scroll.ts
index 095c0599..fc5df4d9 100644
--- a/apps/web/src/hooks/timeline/use-edge-auto-scroll.ts
+++ b/apps/web/src/hooks/timeline/use-edge-auto-scroll.ts
@@ -60,10 +60,7 @@ export function useEdgeAutoScroll({
mouseXRelative > viewportWidth - edgeThreshold &&
rulerViewport.scrollLeft < scrollMax
) {
- const edgeDistance = Math.max(
- 0,
- viewportWidth - edgeThreshold - mouseXRelative,
- );
+ const edgeDistance = Math.max(0, viewportWidth - mouseXRelative);
const intensity = 1 - edgeDistance / edgeThreshold;
scrollSpeed = maxScrollSpeed * intensity;
}
diff --git a/apps/web/src/hooks/timeline/use-timeline-zoom.ts b/apps/web/src/hooks/timeline/use-timeline-zoom.ts
index 13024103..10563a05 100644
--- a/apps/web/src/hooks/timeline/use-timeline-zoom.ts
+++ b/apps/web/src/hooks/timeline/use-timeline-zoom.ts
@@ -44,7 +44,7 @@ export function useTimelineZoom({
null,
);
- const [zoomLevel, setZoomLevel] = useState(() => {
+ const [zoomLevel, setZoomLevelRaw] = useState(() => {
if (initialZoom !== undefined) {
hasInitializedRef.current = true;
return Math.max(
@@ -56,6 +56,18 @@ export function useTimelineZoom({
});
const previousZoomRef = useRef(zoomLevel);
const hasRestoredScrollRef = useRef(false);
+ const preZoomScrollLeftRef = useRef(0);
+
+ const setZoomLevel = useCallback(
+ (updater: number | ((prev: number) => number)) => {
+ const scrollElement = tracksScrollRef.current;
+ if (scrollElement) {
+ preZoomScrollLeftRef.current = scrollElement.scrollLeft;
+ }
+ setZoomLevelRaw(updater);
+ },
+ [tracksScrollRef],
+ );
const handleWheel = useCallback(
(event: ReactWheelEvent) => {
@@ -82,7 +94,7 @@ export function useTimelineZoom({
return;
}
},
- [minZoom],
+ [minZoom, setZoomLevel],
);
useEffect(() => {
@@ -99,7 +111,7 @@ export function useTimelineZoom({
}
return prev;
});
- }, [minZoom, initialZoom]);
+ }, [minZoom, initialZoom, setZoomLevel]);
const wrappedSetZoomLevel = useCallback(
(zoomLevelOrUpdater: number | ((prev: number) => number)) => {
@@ -115,7 +127,7 @@ export function useTimelineZoom({
return clampedZoom;
});
},
- [minZoom],
+ [minZoom, setZoomLevel],
);
useLayoutEffect(() => {
@@ -128,7 +140,7 @@ export function useTimelineZoom({
return;
}
- const currentScrollLeft = scrollElement.scrollLeft;
+ const currentScrollLeft = preZoomScrollLeftRef.current;
const playheadTime = editor.playback.getCurrentTime();
const sliderPercent = zoomToSlider({ zoomLevel, minZoom });
diff --git a/apps/web/src/hooks/use-container-size.ts b/apps/web/src/hooks/use-container-size.ts
new file mode 100644
index 00000000..8b46f20e
--- /dev/null
+++ b/apps/web/src/hooks/use-container-size.ts
@@ -0,0 +1,29 @@
+import { useEffect, useState } from "react";
+
+export function useContainerSize({
+ containerRef,
+}: {
+ containerRef: React.RefObject<HTMLElement | null>;
+}) {
+ const [size, setSize] = useState({ width: 0, height: 0 });
+
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
+
+ const observer = new ResizeObserver((entries) => {
+ for (const entry of entries) {
+ const { width, height } = entry.contentRect;
+ setSize({ width, height });
+ }
+ });
+
+ observer.observe(container);
+
+ return () => {
+ observer.disconnect();
+ };
+ }, [containerRef]);
+
+ return size;
+}
diff --git a/apps/web/src/hooks/use-fullscreen.ts b/apps/web/src/hooks/use-fullscreen.ts
new file mode 100644
index 00000000..193441c9
--- /dev/null
+++ b/apps/web/src/hooks/use-fullscreen.ts
@@ -0,0 +1,30 @@
+import { useCallback, useEffect, useState } from "react";
+
+export function useFullscreen({
+ containerRef,
+}: {
+ containerRef: React.RefObject<HTMLElement | null>;
+}) {
+ const [isFullscreen, setIsFullscreen] = useState(false);
+
+ useEffect(() => {
+ const handleChange = () => {
+ setIsFullscreen(document.fullscreenElement !== null);
+ };
+ document.addEventListener("fullscreenchange", handleChange);
+ return () => {
+ document.removeEventListener("fullscreenchange", handleChange);
+ };
+ }, []);
+
+ const toggleFullscreen = useCallback(() => {
+ if (!containerRef.current) return;
+ if (document.fullscreenElement) {
+ document.exitFullscreen();
+ } else {
+ containerRef.current.requestFullscreen();
+ }
+ }, [containerRef]);
+
+ return { isFullscreen, toggleFullscreen };
+}
diff --git a/apps/web/src/hooks/use-preview-interaction.ts b/apps/web/src/hooks/use-preview-interaction.ts
index 6aeb0c9b..31b33504 100644
--- a/apps/web/src/hooks/use-preview-interaction.ts
+++ b/apps/web/src/hooks/use-preview-interaction.ts
@@ -137,6 +137,16 @@ export function usePreviewInteraction({
const deltaX = currentPos.x - dragStateRef.current.startX;
const deltaY = currentPos.y - dragStateRef.current.startY;
+ const hasMovement = Math.abs(deltaX) > 0.5 || Math.abs(deltaY) > 0.5;
+
+
+ if (!hasMovement) {
+ dragStateRef.current = null;
+ setIsDragging(false);
+ event.currentTarget.releasePointerCapture(event.pointerId);
+ return;
+ }
+
// revert to pre-drag state so the command captures the correct undo snapshot
editor.timeline.updateTracks(dragStateRef.current.tracksSnapshot);