summaryrefslogtreecommitdiff
path: root/apps/web/src/masks/use-mask-handles.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/masks/use-mask-handles.ts')
-rw-r--r--apps/web/src/masks/use-mask-handles.ts86
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" });
}
}