diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-13 04:41:33 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-13 05:08:29 +0200 |
| commit | 7d4e166678c35d48b22b8d1d05639f981a956a8b (patch) | |
| tree | eb596d4f4609053524224169c247db24052db353 /apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx | |
| parent | cad88ee4d1c9e06e7529b88ef5e5d3fdf3f89149 (diff) | |
| download | opencut-7d4e166678c35d48b22b8d1d05639f981a956a8b.tar.gz | |
fix: text measurement, rotation handle, video cache races, playback sync
Made-with: Cursor
Diffstat (limited to 'apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx')
| -rw-r--r-- | apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx | 220 |
1 files changed, 103 insertions, 117 deletions
diff --git a/apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx b/apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx index 515cb91a..6ee4b584 100644 --- a/apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx +++ b/apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx @@ -1,117 +1,103 @@ -import { useState } from "react";
-import { usePreviewViewport } from "@/components/editor/panels/preview/preview-viewport";
-import { usePreviewInteraction } from "@/hooks/use-preview-interaction";
-import type { SnapLine } from "@/lib/preview/preview-snap";
-import { TransformHandles } from "./transform-handles";
-import { MaskHandles } from "./mask-handles";
-import { SnapGuides } from "./snap-guides";
-import { TextEditOverlay } from "./text-edit-overlay";
-import { usePropertiesStore } from "../properties/stores/properties-store";
-import { useEditor } from "@/hooks/use-editor";
-
-export function PreviewInteractionOverlay() {
- const [snapLines, setSnapLines] = useState<SnapLine[]>([]);
- const editor = useEditor();
- const viewport = usePreviewViewport();
- const selectedElements = useEditor((e) => e.selection.getSelectedElements());
- const activeTabPerType = usePropertiesStore((s) => s.activeTabPerType);
-
- const selectedRef =
- selectedElements.length === 1 ? selectedElements[0] : null;
- const activeTrack = selectedRef
- ? editor.timeline.getTrackById({ trackId: selectedRef.trackId })
- : null;
- const activeElement =
- activeTrack?.elements.find(
- (element) => element.id === selectedRef?.elementId,
- ) ?? null;
- const isMaskMode = activeElement
- ? activeTabPerType[activeElement.type] === "masks"
- : false;
-
- const {
- onPointerDown,
- onPointerMove,
- onPointerUp,
- onDoubleClick,
- editingText,
- commitTextEdit,
- } = usePreviewInteraction({
- onSnapLinesChange: setSnapLines,
- isMaskMode,
- });
-
- const handlePointerDown = (event: React.PointerEvent) => {
- if (viewport.handlePanPointerDown({ event })) {
- return;
- }
-
- onPointerDown(event);
- };
-
- const handlePointerMove = (event: React.PointerEvent) => {
- if (viewport.handlePanPointerMove({ event })) {
- return;
- }
-
- onPointerMove(event);
- };
-
- const handlePointerUp = (event: React.PointerEvent) => {
- if (viewport.handlePanPointerUp({ event })) {
- return;
- }
-
- onPointerUp(event);
- };
-
- const handleContextMenu = (e: React.MouseEvent) => {
- const x = e.nativeEvent.offsetX;
- const y = e.nativeEvent.offsetY;
- const outsideCanvas =
- x < viewport.sceneLeft ||
- x > viewport.sceneLeft + viewport.sceneWidth ||
- y < viewport.sceneTop ||
- y > viewport.sceneTop + viewport.sceneHeight;
- if (outsideCanvas) {
- e.stopPropagation();
- }
- };
-
- return (
- <div className="absolute inset-0">
- <div
- className="absolute inset-0 pointer-events-auto"
- role="application"
- aria-label="Preview canvas"
- style={{
- cursor: viewport.isPanning
- ? "grabbing"
- : viewport.canPan
- ? "default"
- : undefined,
- }}
- onPointerDown={handlePointerDown}
- onPointerMove={handlePointerMove}
- onPointerUp={handlePointerUp}
- onPointerCancel={handlePointerUp}
- onDoubleClick={onDoubleClick}
- onDragStart={(e) => e.preventDefault()}
- onContextMenu={handleContextMenu}
- />
- {editingText ? (
- <TextEditOverlay
- trackId={editingText.trackId}
- elementId={editingText.elementId}
- element={editingText.element}
- onCommit={commitTextEdit}
- />
- ) : isMaskMode ? (
- <MaskHandles onSnapLinesChange={setSnapLines} />
- ) : (
- <TransformHandles onSnapLinesChange={setSnapLines} />
- )}
- <SnapGuides lines={snapLines} />
- </div>
- );
-}
+import { useState } from "react"; +import { usePreviewViewport } from "@/components/editor/panels/preview/preview-viewport"; +import { usePreviewInteraction } from "@/hooks/use-preview-interaction"; +import type { SnapLine } from "@/lib/preview/preview-snap"; +import { TransformHandles } from "./transform-handles"; +import { MaskHandles } from "./mask-handles"; +import { SnapGuides } from "./snap-guides"; +import { TextEditOverlay } from "./text-edit-overlay"; +import { usePropertiesStore } from "../properties/stores/properties-store"; +import { useEditor } from "@/hooks/use-editor"; + +export function PreviewInteractionOverlay() { + const [snapLines, setSnapLines] = useState<SnapLine[]>([]); + const editor = useEditor(); + const viewport = usePreviewViewport(); + const selectedElements = useEditor((e) => e.selection.getSelectedElements()); + const activeTabPerType = usePropertiesStore((s) => s.activeTabPerType); + + const selectedRef = + selectedElements.length === 1 ? selectedElements[0] : null; + const activeTrack = selectedRef + ? editor.timeline.getTrackById({ trackId: selectedRef.trackId }) + : null; + const activeElement = + activeTrack?.elements.find( + (element) => element.id === selectedRef?.elementId, + ) ?? null; + const isMaskMode = activeElement + ? activeTabPerType[activeElement.type] === "masks" + : false; + + const { + onPointerDown, + onPointerMove, + onPointerUp, + onDoubleClick, + editingText, + commitTextEdit, + } = usePreviewInteraction({ + onSnapLinesChange: setSnapLines, + isMaskMode, + }); + + const handlePointerDown = (event: React.PointerEvent) => { + if (viewport.handlePanPointerDown({ event })) { + return; + } + + onPointerDown(event); + }; + + const handlePointerMove = (event: React.PointerEvent) => { + if (viewport.handlePanPointerMove({ event })) { + return; + } + + onPointerMove(event); + }; + + const handlePointerUp = (event: React.PointerEvent) => { + if (viewport.handlePanPointerUp({ event })) { + return; + } + + onPointerUp(event); + }; + + return ( + <div className="absolute inset-0"> + <div + className="absolute inset-0 pointer-events-auto" + role="application" + aria-label="Preview canvas" + style={{ + cursor: viewport.isPanning + ? "grabbing" + : viewport.canPan + ? "default" + : undefined, + }} + onPointerDown={handlePointerDown} + onPointerMove={handlePointerMove} + onPointerUp={handlePointerUp} + onPointerCancel={handlePointerUp} + onDoubleClick={onDoubleClick} + onDragStart={(e) => e.preventDefault()} + /> + {editingText ? ( + <TextEditOverlay + trackId={editingText.trackId} + elementId={editingText.elementId} + element={editingText.element} + onCommit={commitTextEdit} + /> + ) : isMaskMode ? ( + <MaskHandles onSnapLinesChange={setSnapLines} /> + ) : ( + <TransformHandles onSnapLinesChange={setSnapLines} /> + )} + <SnapGuides lines={snapLines} /> + </div> + ); +} |
