refactor(web): extract timeline touch helpers

This commit is contained in:
2026-04-01 09:48:04 +02:00
parent 167eec31de
commit 3abb3bc865
4 changed files with 151 additions and 30 deletions
+17 -25
View File
@@ -12,6 +12,7 @@ import {
scheduleLivePreview,
type LivePreviewSession,
} from "./timelineLivePreview.js";
import { getTouchPoint, resolveTouchDragDecision } from "./timelineTouch.js";
const DRAG_CLICK_THRESHOLD_PX = 5;
@@ -1033,11 +1034,6 @@ export function useTimelineDrag({
// ── Touch support ───────────────────────────────────────────────────────────
// Helper: extract clientX from a touch event (first active touch, then changedTouches as fallback)
function toClientX(e: React.TouchEvent): number {
return e.touches[0]?.clientX ?? e.changedTouches[0]?.clientX ?? 0;
}
const onProjectBarTouchStart = useCallback(
(
e: React.TouchEvent,
@@ -1049,10 +1045,11 @@ export function useTimelineDrag({
},
) => {
e.preventDefault();
touchStartRef.current = { x: toClientX(e), y: e.touches[0]?.clientY ?? 0, decided: true };
const point = getTouchPoint(e);
touchStartRef.current = { x: point.clientX, y: point.clientY, decided: true };
onProjectBarMouseDown(
{
clientX: toClientX(e),
clientX: point.clientX,
preventDefault: () => {},
stopPropagation: () => {},
} as unknown as React.MouseEvent,
@@ -1080,10 +1077,11 @@ export function useTimelineDrag({
},
) => {
e.preventDefault();
touchStartRef.current = { x: toClientX(e), y: e.touches[0]?.clientY ?? 0, decided: true };
const point = getTouchPoint(e);
touchStartRef.current = { x: point.clientX, y: point.clientY, decided: true };
onAllocMouseDown(
{
clientX: toClientX(e),
clientX: point.clientX,
preventDefault: () => {},
stopPropagation: () => {},
} as unknown as React.MouseEvent,
@@ -1103,10 +1101,11 @@ export function useTimelineDrag({
},
) => {
e.preventDefault();
touchStartRef.current = { x: toClientX(e), y: e.touches[0]?.clientY ?? 0, decided: false };
const point = getTouchPoint(e);
touchStartRef.current = { x: point.clientX, y: point.clientY, decided: false };
onRowMouseDown(
{
clientX: toClientX(e),
clientX: point.clientX,
preventDefault: () => {},
stopPropagation: () => {},
} as unknown as React.MouseEvent,
@@ -1118,30 +1117,23 @@ export function useTimelineDrag({
const onCanvasTouchMove = useCallback(
(e: React.TouchEvent) => {
const touch = e.touches[0];
if (!touch) return;
const point = getTouchPoint(e);
// Scroll vs drag disambiguation: once decided, stick with the decision
if (!touchStartRef.current.decided) {
const dx = Math.abs(touch.clientX - touchStartRef.current.x);
const dy = Math.abs(touch.clientY - touchStartRef.current.y);
if (dx > 8 || dy > 8) {
touchStartRef.current.decided = true;
if (dy > dx) return; // vertical scroll wins — don't intercept
} else {
return; // haven't moved enough to decide yet
}
const decision = resolveTouchDragDecision(touchStartRef.current, point);
touchStartRef.current = decision.nextState;
if (!decision.shouldHandleDrag) {
return;
}
onCanvasMouseMove({ clientX: touch.clientX } as React.MouseEvent);
onCanvasMouseMove({ clientX: point.clientX } as React.MouseEvent);
},
[onCanvasMouseMove],
);
const onCanvasTouchEnd = useCallback(
async (e: React.TouchEvent) => {
const clientX = e.changedTouches[0]?.clientX ?? 0;
const clientY = e.changedTouches[0]?.clientY ?? 0;
const { clientX, clientY } = getTouchPoint(e);
await onCanvasMouseUp({ clientX, clientY } as React.MouseEvent);
},
[onCanvasMouseUp],