diff options
Diffstat (limited to 'apps/web/src/masks/use-mask-handles.ts')
| -rw-r--r-- | apps/web/src/masks/use-mask-handles.ts | 86 |
1 files changed, 45 insertions, 41 deletions
diff --git a/apps/web/src/masks/use-mask-handles.ts b/apps/web/src/masks/use-mask-handles.ts index b81de4f4..00ab0de8 100644 --- a/apps/web/src/masks/use-mask-handles.ts +++ b/apps/web/src/masks/use-mask-handles.ts @@ -1,12 +1,8 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { usePreviewViewport } from "@/preview/components/preview-viewport"; import { useEditor } from "@/editor/use-editor"; import { useShiftKey } from "@/hooks/use-shift-key"; import { masksRegistry } from "@/masks"; -import { - parseCustomMaskHandleId, - parseCustomMaskSegmentHandleId, -} from "@/masks/custom-path"; import { appendPointToCustomMask } from "@/masks/definitions/custom"; import { getVisibleElementsWithBounds, @@ -17,14 +13,15 @@ import { type SnapLine, } from "@/preview/preview-snap"; import type { SelectedMaskPointSelection } from "@/selection/editor-selection"; -import type { Mask, MaskInteractionResult } from "@/masks/types"; +import type { Mask, MaskHandleId, MaskInteractionResult } from "@/masks/types"; import type { MaskableElement } from "@/timeline"; +import { isMaskableElement } from "@/timeline/element-utils"; import { registerCanceller } from "@/editor/cancel-interaction"; interface DragState { trackId: string; elementId: string; - handleId: string; + handleId: MaskHandleId; startCanvasX: number; startCanvasY: number; startParams: Mask["params"]; @@ -99,12 +96,12 @@ export function useMaskHandles({ const editor = useEditor(); const isShiftHeldRef = useShiftKey(); const viewport = usePreviewViewport(); - const [activeHandleId, setActiveHandleId] = useState<string | null>(null); + const [activeHandleId, setActiveHandleId] = useState<MaskHandleId | null>(null); const dragStateRef = useRef<DragState | null>(null); const pendingSegmentInsertRef = useRef<PendingSegmentInsertState | null>( null, ); - const captureRef = useRef<{ element: HTMLElement; pointerId: number } | null>( + const captureRef = useRef<{ element: Element; pointerId: number } | null>( null, ); @@ -137,7 +134,8 @@ export function useMaskHandles({ item.trackId === sel.trackId && item.elementId === sel.elementId, ); if (!entry) return null; - const element = entry.element as MaskableElement; + if (!isMaskableElement(entry.element)) return null; + const element = entry.element; const masks = element.masks ?? []; if (masks.length === 0) return null; const activeMaskId = masks.some((mask) => @@ -186,28 +184,27 @@ export function useMaskHandles({ ); const handlePositions = baseHandlePositions.map((handle) => { - const parsedHandle = parseCustomMaskHandleId({ handleId: handle.id }); - if (!parsedHandle || parsedHandle.part !== "anchor") { + if (handle.id.kind !== "anchor") { return handle; } return { ...handle, - isSelected: selectedPointIds.has(parsedHandle.pointId), + isSelected: selectedPointIds.has(handle.id.pointId), }; }); - const customMaskPointIds = - selectedWithMask?.mask.type === "custom" - ? handlePositions - .filter((h) => h.kind === "point") - .map((h) => { - const parsedHandle = parseCustomMaskHandleId({ handleId: h.id }); - return parsedHandle?.part === "anchor" - ? parsedHandle.pointId - : null; - }) - .filter((id): id is string => id !== null) - : []; + const customMaskPointIds = useMemo( + () => + selectedWithMask?.mask.type === "custom" + ? handlePositions + .filter((h) => h.kind === "point") + .map((h) => { + return h.id.kind === "anchor" ? h.id.pointId : null; + }) + .filter((id): id is string => id !== null) + : [], + [handlePositions, selectedWithMask?.mask.type], + ); const isCreatingCustomMask = selectedWithMask?.mask.type === "custom" && (!selectedWithMask.mask.params.closed || customMaskPointIds.length === 0); @@ -339,23 +336,30 @@ export function useMaskHandles({ ]); const handlePointerDown = useCallback( - ({ event, handleId }: { event: React.PointerEvent; handleId: string }) => { + ({ + event, + handleId, + }: { + event: React.PointerEvent; + handleId: MaskHandleId; + }) => { if (!selectedWithMask) return; if (event.button !== 0) return; event.stopPropagation(); - const parsedHandle = - selectedWithMask.mask.type === "custom" - ? parseCustomMaskHandleId({ handleId }) + const anchorHandle = + selectedWithMask.mask.type === "custom" && handleId.kind === "anchor" + ? handleId : null; - const parsedSegmentHandle = - selectedWithMask.mask.type === "custom" - ? parseCustomMaskSegmentHandleId({ handleId }) + const segmentHandle = + selectedWithMask.mask.type === "custom" && handleId.kind === "segment" + ? handleId : null; if (isCreatingCustomMask) { const firstPointId = customMaskPointIds[0]; if ( firstPointId && - handleId === `point:${firstPointId}:anchor` && + handleId.kind === "anchor" && + handleId.pointId === firstPointId && customMaskPointIds.length >= 3 && selectedWithMask.mask.type === "custom" ) { @@ -390,13 +394,13 @@ export function useMaskHandles({ }); if (!pos) return; - if (parsedSegmentHandle && selectedWithMask.mask.type === "custom") { + if (segmentHandle && selectedWithMask.mask.type === "custom") { setActiveHandleId(handleId); pendingSegmentInsertRef.current = { trackId: selectedWithMask.trackId, elementId: selectedWithMask.elementId, maskId: selectedWithMask.mask.id, - segmentIndex: parsedSegmentHandle.segmentIndex, + segmentIndex: segmentHandle.index, startClientX: event.clientX, startClientY: event.clientY, startCanvasX: pos.x, @@ -404,7 +408,7 @@ export function useMaskHandles({ startParams: { ...selectedWithMask.mask.params }, bounds: selectedWithMask.bounds, }; - const captureTarget = event.currentTarget as HTMLElement; + const captureTarget = event.currentTarget; captureTarget.setPointerCapture(event.pointerId); captureRef.current = { element: captureTarget, @@ -413,9 +417,9 @@ export function useMaskHandles({ return; } - if (parsedHandle?.part === "anchor") { + if (anchorHandle) { updateCustomMaskPointSelection({ - pointId: parsedHandle.pointId, + pointId: anchorHandle.pointId, toggleSelection: event.shiftKey, }); if (event.shiftKey) { @@ -434,7 +438,7 @@ export function useMaskHandles({ startParams: { ...selectedWithMask.mask.params }, }; setActiveHandleId(handleId); - const captureTarget = event.currentTarget as HTMLElement; + const captureTarget = event.currentTarget; captureTarget.setPointerCapture(event.pointerId); captureRef.current = { element: captureTarget, @@ -513,13 +517,13 @@ export function useMaskHandles({ dragStateRef.current = { trackId: pendingSegmentInsert.trackId, elementId: pendingSegmentInsert.elementId, - handleId: "position", + handleId: { kind: "position" }, startCanvasX: pendingSegmentInsert.startCanvasX, startCanvasY: pendingSegmentInsert.startCanvasY, startParams: pendingSegmentInsert.startParams, }; pendingSegmentInsertRef.current = null; - setActiveHandleId("position"); + setActiveHandleId({ kind: "position" }); } } |
