refactor(web): extract timeline touch helpers
This commit is contained in:
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user