refactor(web): extract project drag helpers
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildProjectShiftMutationInput, createProjectDragState } from "./timelineProjectDrag.js";
|
||||
|
||||
type TestProjectDragState = {
|
||||
isDragging: boolean;
|
||||
projectId: string | null;
|
||||
projectName: string | null;
|
||||
allocationId: string | null;
|
||||
originalStartDate: Date | null;
|
||||
originalEndDate: Date | null;
|
||||
currentStartDate: Date | null;
|
||||
currentEndDate: Date | null;
|
||||
startMouseX: number;
|
||||
pointerDeltaX: number;
|
||||
originalLeft: number;
|
||||
blockWidth: number;
|
||||
daysDelta: number;
|
||||
};
|
||||
|
||||
describe("timelineProjectDrag", () => {
|
||||
it("creates project-bar drag state with default legacy metrics cleared", () => {
|
||||
expect(
|
||||
createProjectDragState<TestProjectDragState>({
|
||||
projectId: "project-1",
|
||||
projectName: "Alpha",
|
||||
startDate: new Date("2025-01-10T00:00:00.000Z"),
|
||||
endDate: new Date("2025-01-20T00:00:00.000Z"),
|
||||
startMouseX: 240,
|
||||
}),
|
||||
).toEqual({
|
||||
isDragging: true,
|
||||
projectId: "project-1",
|
||||
projectName: "Alpha",
|
||||
allocationId: null,
|
||||
originalStartDate: new Date("2025-01-10T00:00:00.000Z"),
|
||||
originalEndDate: new Date("2025-01-20T00:00:00.000Z"),
|
||||
currentStartDate: new Date("2025-01-10T00:00:00.000Z"),
|
||||
currentEndDate: new Date("2025-01-20T00:00:00.000Z"),
|
||||
startMouseX: 240,
|
||||
pointerDeltaX: 0,
|
||||
originalLeft: 0,
|
||||
blockWidth: 0,
|
||||
daysDelta: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves allocation metadata and block geometry for legacy allocation-block drags", () => {
|
||||
expect(
|
||||
createProjectDragState<TestProjectDragState>({
|
||||
projectId: "project-1",
|
||||
projectName: "Alpha",
|
||||
allocationId: "alloc-7",
|
||||
startDate: new Date("2025-01-10T00:00:00.000Z"),
|
||||
endDate: new Date("2025-01-20T00:00:00.000Z"),
|
||||
startMouseX: 320,
|
||||
originalLeft: 144,
|
||||
blockWidth: 96,
|
||||
}),
|
||||
).toMatchObject({
|
||||
allocationId: "alloc-7",
|
||||
originalLeft: 144,
|
||||
blockWidth: 96,
|
||||
});
|
||||
});
|
||||
|
||||
it("refuses to build mutation input for no-op drags or incomplete state", () => {
|
||||
expect(
|
||||
buildProjectShiftMutationInput({
|
||||
projectId: "project-1",
|
||||
currentStartDate: new Date("2025-01-10T00:00:00.000Z"),
|
||||
currentEndDate: new Date("2025-01-20T00:00:00.000Z"),
|
||||
daysDelta: 0,
|
||||
}),
|
||||
).toBeNull();
|
||||
|
||||
expect(
|
||||
buildProjectShiftMutationInput({
|
||||
projectId: null,
|
||||
currentStartDate: new Date("2025-01-10T00:00:00.000Z"),
|
||||
currentEndDate: new Date("2025-01-20T00:00:00.000Z"),
|
||||
daysDelta: 2,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("builds mutation input only when a real drag produced complete dates", () => {
|
||||
expect(
|
||||
buildProjectShiftMutationInput({
|
||||
projectId: "project-1",
|
||||
currentStartDate: new Date("2025-01-12T00:00:00.000Z"),
|
||||
currentEndDate: new Date("2025-01-22T00:00:00.000Z"),
|
||||
daysDelta: 2,
|
||||
}),
|
||||
).toEqual({
|
||||
projectId: "project-1",
|
||||
newStartDate: new Date("2025-01-12T00:00:00.000Z"),
|
||||
newEndDate: new Date("2025-01-22T00:00:00.000Z"),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
type ProjectDragStateLike = {
|
||||
isDragging: boolean;
|
||||
projectId: string | null;
|
||||
projectName: string | null;
|
||||
allocationId: string | null;
|
||||
originalStartDate: Date | null;
|
||||
originalEndDate: Date | null;
|
||||
currentStartDate: Date | null;
|
||||
currentEndDate: Date | null;
|
||||
startMouseX: number;
|
||||
pointerDeltaX: number;
|
||||
originalLeft: number;
|
||||
blockWidth: number;
|
||||
daysDelta: number;
|
||||
};
|
||||
|
||||
type CreateProjectDragStateInput = {
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
allocationId?: string | null;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
startMouseX: number;
|
||||
originalLeft?: number;
|
||||
blockWidth?: number;
|
||||
};
|
||||
|
||||
export function createProjectDragState<TState extends ProjectDragStateLike>(
|
||||
input: CreateProjectDragStateInput,
|
||||
): TState {
|
||||
return {
|
||||
isDragging: true,
|
||||
projectId: input.projectId,
|
||||
projectName: input.projectName,
|
||||
allocationId: input.allocationId ?? null,
|
||||
originalStartDate: input.startDate,
|
||||
originalEndDate: input.endDate,
|
||||
currentStartDate: input.startDate,
|
||||
currentEndDate: input.endDate,
|
||||
startMouseX: input.startMouseX,
|
||||
pointerDeltaX: 0,
|
||||
originalLeft: input.originalLeft ?? 0,
|
||||
blockWidth: input.blockWidth ?? 0,
|
||||
daysDelta: 0,
|
||||
} as TState;
|
||||
}
|
||||
|
||||
export function buildProjectShiftMutationInput(
|
||||
drag: Pick<ProjectDragStateLike, "daysDelta" | "projectId" | "currentStartDate" | "currentEndDate">,
|
||||
): { projectId: string; newStartDate: Date; newEndDate: Date } | null {
|
||||
if (drag.daysDelta === 0 || !drag.projectId || !drag.currentStartDate || !drag.currentEndDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
projectId: drag.projectId,
|
||||
newStartDate: drag.currentStartDate,
|
||||
newEndDate: drag.currentEndDate,
|
||||
};
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
startAllocationMultiDrag,
|
||||
updateAllocationMultiDrag,
|
||||
} from "./timelineAllocationMultiDrag.js";
|
||||
import { buildProjectShiftMutationInput, createProjectDragState } from "./timelineProjectDrag.js";
|
||||
import {
|
||||
createMultiSelectState,
|
||||
finalizeMultiSelectDraft,
|
||||
@@ -448,17 +449,7 @@ export function useTimelineDrag({
|
||||
const finalDrag = dragStateRef.current;
|
||||
if (!finalDrag.isDragging) return null;
|
||||
|
||||
const mutationInput =
|
||||
finalDrag.daysDelta !== 0 &&
|
||||
finalDrag.projectId &&
|
||||
finalDrag.currentStartDate &&
|
||||
finalDrag.currentEndDate
|
||||
? {
|
||||
projectId: finalDrag.projectId,
|
||||
newStartDate: finalDrag.currentStartDate,
|
||||
newEndDate: finalDrag.currentEndDate,
|
||||
}
|
||||
: null;
|
||||
const mutationInput = buildProjectShiftMutationInput(finalDrag);
|
||||
|
||||
if (finalDrag.daysDelta !== 0) {
|
||||
preserveLivePreview(projectPreviewRef.current);
|
||||
@@ -543,21 +534,13 @@ export function useTimelineDrag({
|
||||
if (e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const state: DragState = {
|
||||
isDragging: true,
|
||||
const state = createProjectDragState<DragState>({
|
||||
projectId: opts.projectId,
|
||||
projectName: opts.projectName,
|
||||
allocationId: null,
|
||||
originalStartDate: opts.startDate,
|
||||
originalEndDate: opts.endDate,
|
||||
currentStartDate: opts.startDate,
|
||||
currentEndDate: opts.endDate,
|
||||
startDate: opts.startDate,
|
||||
endDate: opts.endDate,
|
||||
startMouseX: e.clientX,
|
||||
pointerDeltaX: 0,
|
||||
originalLeft: 0,
|
||||
blockWidth: 0,
|
||||
daysDelta: 0,
|
||||
};
|
||||
});
|
||||
dragStateRef.current = state;
|
||||
setDragState(state);
|
||||
|
||||
@@ -602,21 +585,16 @@ export function useTimelineDrag({
|
||||
if (e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const state: DragState = {
|
||||
isDragging: true,
|
||||
const state = createProjectDragState<DragState>({
|
||||
projectId: opts.projectId,
|
||||
projectName: opts.projectName,
|
||||
allocationId: opts.allocationId ?? null,
|
||||
originalStartDate: opts.startDate,
|
||||
originalEndDate: opts.endDate,
|
||||
currentStartDate: opts.startDate,
|
||||
currentEndDate: opts.endDate,
|
||||
startDate: opts.startDate,
|
||||
endDate: opts.endDate,
|
||||
startMouseX: e.clientX,
|
||||
pointerDeltaX: 0,
|
||||
originalLeft: opts.blockLeft,
|
||||
blockWidth: opts.blockWidth,
|
||||
daysDelta: 0,
|
||||
};
|
||||
});
|
||||
dragStateRef.current = state;
|
||||
setDragState(state);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user