summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks/timeline/element/use-element-interaction.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/hooks/timeline/element/use-element-interaction.ts')
-rw-r--r--apps/web/src/hooks/timeline/element/use-element-interaction.ts83
1 files changed, 49 insertions, 34 deletions
diff --git a/apps/web/src/hooks/timeline/element/use-element-interaction.ts b/apps/web/src/hooks/timeline/element/use-element-interaction.ts
index ea01c718..fae8c527 100644
--- a/apps/web/src/hooks/timeline/element/use-element-interaction.ts
+++ b/apps/web/src/hooks/timeline/element/use-element-interaction.ts
@@ -18,16 +18,14 @@ import { snapTimeToFrame } from "@/lib/time";
import { computeDropTarget } from "@/lib/timeline/drop-utils";
import { getMouseTimeFromClientX } from "@/lib/timeline/drag-utils";
import { generateUUID } from "@/utils/id";
-import {
- snapElementEdge,
- type SnapPoint,
-} from "@/lib/timeline/snap-utils";
+import { snapElementEdge, type SnapPoint } from "@/lib/timeline/snap-utils";
+import { registerCanceller } from "@/lib/cancel-interaction";
import type {
DropTarget,
ElementDragState,
TimelineElement,
TimelineTrack,
-} from "@/types/timeline";
+} from "@/lib/timeline";
interface UseElementInteractionProps {
zoomLevel: number;
@@ -213,6 +211,20 @@ export function useElementInteraction({
setDragDropTarget(null);
}, []);
+ const cancelCurrentDrag = useCallback(() => {
+ pendingDragRef.current = null;
+ mouseDownLocationRef.current = null;
+ setIsPendingDrag(false);
+ endDrag();
+ onSnapPointChange?.(null);
+ }, [endDrag, onSnapPointChange]);
+
+ useEffect(() => {
+ if (!dragState.isDragging && !isPendingDrag) return;
+
+ return registerCanceller({ fn: cancelCurrentDrag });
+ }, [dragState.isDragging, isPendingDrag, cancelCurrentDrag]);
+
const getDragSnapResult = useCallback(
({
frameSnappedTime,
@@ -445,33 +457,36 @@ export function useElementInteraction({
return;
}
- if (dropTarget.isNewTrack) {
- const newTrackId = generateUUID();
+ if (dropTarget.isNewTrack) {
+ const newTrackId = generateUUID();
- editor.timeline.moveElement({
- sourceTrackId: dragState.trackId,
- targetTrackId: newTrackId,
- elementId: dragState.elementId,
- newStartTime: snappedTime,
- createTrack: { type: sourceTrack.type, index: dropTarget.trackIndex },
- rippleEnabled: rippleEditingEnabled,
- });
- selectElement({ trackId: newTrackId, elementId: dragState.elementId });
- } else {
- const targetTrack = tracks[dropTarget.trackIndex];
- if (targetTrack) {
editor.timeline.moveElement({
sourceTrackId: dragState.trackId,
- targetTrackId: targetTrack.id,
+ targetTrackId: newTrackId,
elementId: dragState.elementId,
newStartTime: snappedTime,
+ createTrack: { type: sourceTrack.type, index: dropTarget.trackIndex },
rippleEnabled: rippleEditingEnabled,
});
- if (targetTrack.id !== dragState.trackId) {
- selectElement({ trackId: targetTrack.id, elementId: dragState.elementId });
+ selectElement({ trackId: newTrackId, elementId: dragState.elementId });
+ } else {
+ const targetTrack = tracks[dropTarget.trackIndex];
+ if (targetTrack) {
+ editor.timeline.moveElement({
+ sourceTrackId: dragState.trackId,
+ targetTrackId: targetTrack.id,
+ elementId: dragState.elementId,
+ newStartTime: snappedTime,
+ rippleEnabled: rippleEditingEnabled,
+ });
+ if (targetTrack.id !== dragState.trackId) {
+ selectElement({
+ trackId: targetTrack.id,
+ elementId: dragState.elementId,
+ });
+ }
}
}
- }
endDrag();
onSnapPointChange?.(null);
@@ -520,10 +535,10 @@ export function useElementInteraction({
element: TimelineElement;
track: TimelineTrack;
}) => {
- const isRightClick = event.button === MOUSE_BUTTON_RIGHT;
+ const isRightClick = event.button === MOUSE_BUTTON_RIGHT;
- // right-click: don't stop propagation so ContextMenu can open
- if (isRightClick) {
+ // right-click: don't stop propagation so ContextMenu can open
+ if (isRightClick) {
const alreadySelected = isElementSelected({
trackId: track.id,
elementId: element.id,
@@ -538,12 +553,12 @@ export function useElementInteraction({
return;
}
- event.stopPropagation();
- mouseDownLocationRef.current = { x: event.clientX, y: event.clientY };
+ event.stopPropagation();
+ mouseDownLocationRef.current = { x: event.clientX, y: event.clientY };
- const isMultiSelect = event.metaKey || event.ctrlKey || event.shiftKey;
+ const isMultiSelect = event.metaKey || event.ctrlKey || event.shiftKey;
- if (isMultiSelect) {
+ if (isMultiSelect) {
handleSelectionClick({
trackId: track.id,
elementId: element.id,
@@ -551,7 +566,7 @@ export function useElementInteraction({
});
}
- const clickOffsetTime = getClickOffsetTime({
+ const clickOffsetTime = getClickOffsetTime({
clientX: event.clientX,
elementRect: event.currentTarget.getBoundingClientRect(),
zoomLevel,
@@ -579,9 +594,9 @@ export function useElementInteraction({
element: TimelineElement;
track: TimelineTrack;
}) => {
- event.stopPropagation();
+ event.stopPropagation();
- if (mouseDownLocationRef.current) {
+ if (mouseDownLocationRef.current) {
const deltaX = Math.abs(event.clientX - mouseDownLocationRef.current.x);
const deltaY = Math.abs(event.clientY - mouseDownLocationRef.current.y);
if (deltaX > DRAG_THRESHOLD_PX || deltaY > DRAG_THRESHOLD_PX) {
@@ -593,7 +608,7 @@ export function useElementInteraction({
// modifier keys already handled in mousedown
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
- const alreadySelected = isElementSelected({
+ const alreadySelected = isElementSelected({
trackId: track.id,
elementId: element.id,
});