refactor(web): extract drag position helpers
This commit is contained in:
@@ -0,0 +1,131 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { resolveAllocationDragPosition, resolveProjectDragPosition } from "./timelineDragPosition.js";
|
||||||
|
|
||||||
|
describe("timelineDragPosition", () => {
|
||||||
|
it("ignores project drags that are inactive or missing their original dates", () => {
|
||||||
|
expect(
|
||||||
|
resolveProjectDragPosition(
|
||||||
|
{
|
||||||
|
isDragging: false,
|
||||||
|
originalStartDate: new Date("2026-01-10"),
|
||||||
|
originalEndDate: new Date("2026-01-12"),
|
||||||
|
currentStartDate: null,
|
||||||
|
currentEndDate: null,
|
||||||
|
startMouseX: 100,
|
||||||
|
pointerDeltaX: 0,
|
||||||
|
daysDelta: 0,
|
||||||
|
},
|
||||||
|
180,
|
||||||
|
40,
|
||||||
|
),
|
||||||
|
).toEqual({ handled: false });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolveProjectDragPosition(
|
||||||
|
{
|
||||||
|
isDragging: true,
|
||||||
|
originalStartDate: null,
|
||||||
|
originalEndDate: new Date("2026-01-12"),
|
||||||
|
currentStartDate: null,
|
||||||
|
currentEndDate: null,
|
||||||
|
startMouseX: 100,
|
||||||
|
pointerDeltaX: 0,
|
||||||
|
daysDelta: 0,
|
||||||
|
},
|
||||||
|
180,
|
||||||
|
40,
|
||||||
|
),
|
||||||
|
).toEqual({ handled: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates only the pointer delta when the rounded day delta does not change", () => {
|
||||||
|
const result = resolveProjectDragPosition(
|
||||||
|
{
|
||||||
|
isDragging: true,
|
||||||
|
originalStartDate: new Date("2026-01-10"),
|
||||||
|
originalEndDate: new Date("2026-01-12"),
|
||||||
|
currentStartDate: null,
|
||||||
|
currentEndDate: null,
|
||||||
|
startMouseX: 100,
|
||||||
|
pointerDeltaX: 0,
|
||||||
|
daysDelta: 0,
|
||||||
|
},
|
||||||
|
118,
|
||||||
|
40,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
handled: true,
|
||||||
|
pointerDeltaX: 18,
|
||||||
|
daysDelta: 0,
|
||||||
|
shouldSyncState: false,
|
||||||
|
nextState: {
|
||||||
|
pointerDeltaX: 18,
|
||||||
|
daysDelta: 0,
|
||||||
|
currentStartDate: null,
|
||||||
|
currentEndDate: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("recomputes project dates once the drag crosses into a different day bucket", () => {
|
||||||
|
const result = resolveProjectDragPosition(
|
||||||
|
{
|
||||||
|
isDragging: true,
|
||||||
|
originalStartDate: new Date("2026-01-10"),
|
||||||
|
originalEndDate: new Date("2026-01-12"),
|
||||||
|
currentStartDate: null,
|
||||||
|
currentEndDate: null,
|
||||||
|
startMouseX: 100,
|
||||||
|
pointerDeltaX: 0,
|
||||||
|
daysDelta: 0,
|
||||||
|
},
|
||||||
|
195,
|
||||||
|
40,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
handled: true,
|
||||||
|
pointerDeltaX: 95,
|
||||||
|
daysDelta: 2,
|
||||||
|
shouldSyncState: true,
|
||||||
|
nextState: {
|
||||||
|
currentStartDate: new Date("2026-01-12"),
|
||||||
|
currentEndDate: new Date("2026-01-14"),
|
||||||
|
pointerDeltaX: 95,
|
||||||
|
daysDelta: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clamps allocation resize-end drags so the end date does not cross the start date", () => {
|
||||||
|
const result = resolveAllocationDragPosition(
|
||||||
|
{
|
||||||
|
isActive: true,
|
||||||
|
mode: "resize-end",
|
||||||
|
originalStartDate: new Date("2026-01-10"),
|
||||||
|
originalEndDate: new Date("2026-01-12"),
|
||||||
|
currentStartDate: null,
|
||||||
|
currentEndDate: null,
|
||||||
|
startMouseX: 100,
|
||||||
|
pointerDeltaX: 0,
|
||||||
|
daysDelta: 0,
|
||||||
|
},
|
||||||
|
-10,
|
||||||
|
40,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
handled: true,
|
||||||
|
pointerDeltaX: -110,
|
||||||
|
daysDelta: -3,
|
||||||
|
shouldSyncState: true,
|
||||||
|
nextState: {
|
||||||
|
currentStartDate: new Date("2026-01-10"),
|
||||||
|
currentEndDate: new Date("2026-01-10"),
|
||||||
|
pointerDeltaX: -110,
|
||||||
|
daysDelta: -3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { computeDragDates, pixelsToDays } from "~/components/timeline/dragMath.js";
|
||||||
|
|
||||||
|
type DragPositionLike = {
|
||||||
|
originalStartDate: Date | null;
|
||||||
|
originalEndDate: Date | null;
|
||||||
|
currentStartDate: Date | null;
|
||||||
|
currentEndDate: Date | null;
|
||||||
|
startMouseX: number;
|
||||||
|
pointerDeltaX: number;
|
||||||
|
daysDelta: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DragMode = "move" | "resize-start" | "resize-end";
|
||||||
|
|
||||||
|
export type TimelineDragPositionResult<State> =
|
||||||
|
| { handled: false }
|
||||||
|
| { handled: true; nextState: State; pointerDeltaX: number; daysDelta: number; shouldSyncState: boolean };
|
||||||
|
|
||||||
|
function resolveDragPosition<State extends DragPositionLike>(
|
||||||
|
state: State,
|
||||||
|
clientX: number,
|
||||||
|
cellWidth: number,
|
||||||
|
mode: DragMode,
|
||||||
|
): TimelineDragPositionResult<State> {
|
||||||
|
const pointerDeltaX = clientX - state.startMouseX;
|
||||||
|
const daysDelta = pixelsToDays(pointerDeltaX, cellWidth);
|
||||||
|
|
||||||
|
if (daysDelta === state.daysDelta) {
|
||||||
|
return {
|
||||||
|
handled: true,
|
||||||
|
nextState: pointerDeltaX === state.pointerDeltaX ? state : { ...state, pointerDeltaX },
|
||||||
|
pointerDeltaX,
|
||||||
|
daysDelta,
|
||||||
|
shouldSyncState: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { start, end } = computeDragDates(mode, state.originalStartDate!, state.originalEndDate!, daysDelta);
|
||||||
|
return {
|
||||||
|
handled: true,
|
||||||
|
nextState: {
|
||||||
|
...state,
|
||||||
|
currentStartDate: start,
|
||||||
|
currentEndDate: end,
|
||||||
|
pointerDeltaX,
|
||||||
|
daysDelta,
|
||||||
|
},
|
||||||
|
pointerDeltaX,
|
||||||
|
daysDelta,
|
||||||
|
shouldSyncState: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveProjectDragPosition<State extends DragPositionLike & { isDragging: boolean }>(
|
||||||
|
drag: State,
|
||||||
|
clientX: number,
|
||||||
|
cellWidth: number,
|
||||||
|
): TimelineDragPositionResult<State> {
|
||||||
|
if (!drag.isDragging || !drag.originalStartDate || !drag.originalEndDate) {
|
||||||
|
return { handled: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveDragPosition(drag, clientX, cellWidth, "move");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveAllocationDragPosition<
|
||||||
|
State extends DragPositionLike & { isActive: boolean; mode: DragMode },
|
||||||
|
>(alloc: State, clientX: number, cellWidth: number): TimelineDragPositionResult<State> {
|
||||||
|
if (!alloc.isActive || !alloc.originalStartDate || !alloc.originalEndDate) {
|
||||||
|
return { handled: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveDragPosition(alloc, clientX, cellWidth, alloc.mode);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useCallback, useEffect, useRef, useState, type MutableRefObject } from "react";
|
import { useCallback, useEffect, useRef, useState, type MutableRefObject } from "react";
|
||||||
import { trpc } from "~/lib/trpc/client.js";
|
import { trpc } from "~/lib/trpc/client.js";
|
||||||
import { useInvalidateTimeline } from "./useInvalidatePlanningViews.js";
|
import { useInvalidateTimeline } from "./useInvalidatePlanningViews.js";
|
||||||
import { pixelsToDays, computeDragDates } from "~/components/timeline/dragMath.js";
|
import { pixelsToDays } from "~/components/timeline/dragMath.js";
|
||||||
import {
|
import {
|
||||||
captureLivePreviewTargets,
|
captureLivePreviewTargets,
|
||||||
clearLivePreview,
|
clearLivePreview,
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
import { resolveAllocationRelease } from "./timelineAllocationRelease.js";
|
import { resolveAllocationRelease } from "./timelineAllocationRelease.js";
|
||||||
import { createAllocationDragState } from "./timelineAllocationDragState.js";
|
import { createAllocationDragState } from "./timelineAllocationDragState.js";
|
||||||
import { cleanupTimelineDragState } from "./timelineDragCleanup.js";
|
import { cleanupTimelineDragState } from "./timelineDragCleanup.js";
|
||||||
|
import { resolveAllocationDragPosition, resolveProjectDragPosition } from "./timelineDragPosition.js";
|
||||||
import { attachDocumentMouseDrag } from "./timelineDocumentDrag.js";
|
import { attachDocumentMouseDrag } from "./timelineDocumentDrag.js";
|
||||||
import { buildProjectShiftMutationInput, createProjectDragState } from "./timelineProjectDrag.js";
|
import { buildProjectShiftMutationInput, createProjectDragState } from "./timelineProjectDrag.js";
|
||||||
import { beginProjectDragSession } from "./timelineProjectDragSession.js";
|
import { beginProjectDragSession } from "./timelineProjectDragSession.js";
|
||||||
@@ -343,35 +344,14 @@ export function useTimelineDrag({
|
|||||||
|
|
||||||
const updateProjectDragPosition = useCallback(
|
const updateProjectDragPosition = useCallback(
|
||||||
(clientX: number) => {
|
(clientX: number) => {
|
||||||
const drag = dragStateRef.current;
|
const result = resolveProjectDragPosition(dragStateRef.current, clientX, cellWidthRef.current);
|
||||||
if (!drag.isDragging || !drag.originalStartDate || !drag.originalEndDate) return false;
|
if (!result.handled) return false;
|
||||||
|
|
||||||
const deltaX = clientX - drag.startMouseX;
|
updateLivePreview(projectPreviewRef, result.pointerDeltaX, result.daysDelta);
|
||||||
const daysDelta = pixelsToDays(deltaX, cellWidthRef.current);
|
dragStateRef.current = result.nextState;
|
||||||
updateLivePreview(projectPreviewRef, deltaX, daysDelta);
|
if (result.shouldSyncState) {
|
||||||
|
setDragState(result.nextState);
|
||||||
if (daysDelta === drag.daysDelta) {
|
|
||||||
if (deltaX !== drag.pointerDeltaX) {
|
|
||||||
dragStateRef.current = { ...drag, pointerDeltaX: deltaX };
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { start: newStart, end: newEnd } = computeDragDates(
|
|
||||||
"move",
|
|
||||||
drag.originalStartDate,
|
|
||||||
drag.originalEndDate,
|
|
||||||
daysDelta,
|
|
||||||
);
|
|
||||||
const updated: DragState = {
|
|
||||||
...drag,
|
|
||||||
currentStartDate: newStart,
|
|
||||||
currentEndDate: newEnd,
|
|
||||||
pointerDeltaX: deltaX,
|
|
||||||
daysDelta,
|
|
||||||
};
|
|
||||||
dragStateRef.current = updated;
|
|
||||||
setDragState(updated);
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[updateLivePreview],
|
[updateLivePreview],
|
||||||
@@ -379,36 +359,14 @@ export function useTimelineDrag({
|
|||||||
|
|
||||||
const updateAllocationDragPosition = useCallback(
|
const updateAllocationDragPosition = useCallback(
|
||||||
(clientX: number) => {
|
(clientX: number) => {
|
||||||
const alloc = allocDragRef.current;
|
const result = resolveAllocationDragPosition(allocDragRef.current, clientX, cellWidthRef.current);
|
||||||
if (!alloc.isActive || !alloc.originalStartDate || !alloc.originalEndDate) return false;
|
if (!result.handled) return false;
|
||||||
|
|
||||||
const pointerDeltaX = clientX - alloc.startMouseX;
|
updateLivePreview(allocPreviewRef, result.pointerDeltaX, result.daysDelta);
|
||||||
const daysDelta = pixelsToDays(pointerDeltaX, cellWidthRef.current);
|
allocDragRef.current = result.nextState;
|
||||||
updateLivePreview(allocPreviewRef, pointerDeltaX, daysDelta);
|
if (result.shouldSyncState) {
|
||||||
|
setAllocDragState(result.nextState);
|
||||||
if (daysDelta === alloc.daysDelta) {
|
|
||||||
if (pointerDeltaX !== alloc.pointerDeltaX) {
|
|
||||||
allocDragRef.current = { ...alloc, pointerDeltaX };
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { start: newStart, end: newEnd } = computeDragDates(
|
|
||||||
alloc.mode,
|
|
||||||
alloc.originalStartDate,
|
|
||||||
alloc.originalEndDate,
|
|
||||||
daysDelta,
|
|
||||||
);
|
|
||||||
|
|
||||||
const updated: AllocDragState = {
|
|
||||||
...alloc,
|
|
||||||
currentStartDate: newStart,
|
|
||||||
currentEndDate: newEnd,
|
|
||||||
pointerDeltaX,
|
|
||||||
daysDelta,
|
|
||||||
};
|
|
||||||
allocDragRef.current = updated;
|
|
||||||
setAllocDragState(updated);
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[updateLivePreview],
|
[updateLivePreview],
|
||||||
|
|||||||
@@ -292,6 +292,21 @@ export const rules = [
|
|||||||
],
|
],
|
||||||
forbidden: [],
|
forbidden: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
file: "apps/web/src/hooks/timelineDragPosition.ts",
|
||||||
|
maxLines: 80,
|
||||||
|
required: [
|
||||||
|
{
|
||||||
|
pattern: /\bexport function resolveProjectDragPosition\b/,
|
||||||
|
message: "timeline drag position helpers must keep project drag date derivation centralized",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /\bexport function resolveAllocationDragPosition\b/,
|
||||||
|
message: "timeline drag position helpers must keep allocation drag date derivation centralized",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
forbidden: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
file: "apps/web/src/hooks/timelineDocumentDrag.ts",
|
file: "apps/web/src/hooks/timelineDocumentDrag.ts",
|
||||||
maxLines: 50,
|
maxLines: 50,
|
||||||
@@ -383,6 +398,10 @@ export const rules = [
|
|||||||
pattern: /from "\.\/timelineDragCleanup\.js"/,
|
pattern: /from "\.\/timelineDragCleanup\.js"/,
|
||||||
message: "timeline drag must keep unmount teardown delegated to the extracted helper module",
|
message: "timeline drag must keep unmount teardown delegated to the extracted helper module",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: /from "\.\/timelineDragPosition\.js"/,
|
||||||
|
message: "timeline drag must keep project and allocation drag position derivation delegated to the extracted helper module",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pattern: /from "\.\/timelineDocumentDrag\.js"/,
|
pattern: /from "\.\/timelineDocumentDrag\.js"/,
|
||||||
message: "timeline drag must keep document mouse listener lifecycle delegated to the extracted helper module",
|
message: "timeline drag must keep document mouse listener lifecycle delegated to the extracted helper module",
|
||||||
@@ -433,6 +452,10 @@ export const rules = [
|
|||||||
pattern: /\bfunction attachDocumentMouseDrag\b/,
|
pattern: /\bfunction attachDocumentMouseDrag\b/,
|
||||||
message: "timeline drag must not re-inline extracted document listener helper implementations",
|
message: "timeline drag must not re-inline extracted document listener helper implementations",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: /\bconst (?:deltaX|pointerDeltaX) = clientX - (?:drag|alloc)\.startMouseX;[\s\S]*computeDragDates\(/,
|
||||||
|
message: "timeline drag must not re-inline extracted project or allocation drag position helpers",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pattern: /\bfunction (?:isAllocationMultiSelected|startAllocationMultiDrag|updateAllocationMultiDrag|finalizeAllocationMultiDrag)\b/,
|
pattern: /\bfunction (?:isAllocationMultiSelected|startAllocationMultiDrag|updateAllocationMultiDrag|finalizeAllocationMultiDrag)\b/,
|
||||||
message: "timeline drag must not re-inline extracted allocation multi-drag helper implementations",
|
message: "timeline drag must not re-inline extracted allocation multi-drag helper implementations",
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ describe("architecture guardrails", () => {
|
|||||||
const allocationActionsRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationActions.ts");
|
const allocationActionsRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationActions.ts");
|
||||||
const allocationReleaseRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationRelease.ts");
|
const allocationReleaseRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationRelease.ts");
|
||||||
const cleanupRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDragCleanup.ts");
|
const cleanupRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDragCleanup.ts");
|
||||||
|
const positionRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDragPosition.ts");
|
||||||
const documentDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDocumentDrag.ts");
|
const documentDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDocumentDrag.ts");
|
||||||
const allocationDragStateRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationDragState.ts");
|
const allocationDragStateRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationDragState.ts");
|
||||||
const projectDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineProjectDrag.ts");
|
const projectDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineProjectDrag.ts");
|
||||||
@@ -100,6 +101,7 @@ describe("architecture guardrails", () => {
|
|||||||
assert.ok(allocationActionsRule);
|
assert.ok(allocationActionsRule);
|
||||||
assert.ok(allocationReleaseRule);
|
assert.ok(allocationReleaseRule);
|
||||||
assert.ok(cleanupRule);
|
assert.ok(cleanupRule);
|
||||||
|
assert.ok(positionRule);
|
||||||
assert.ok(documentDragRule);
|
assert.ok(documentDragRule);
|
||||||
assert.ok(allocationDragStateRule);
|
assert.ok(allocationDragStateRule);
|
||||||
assert.ok(projectDragRule);
|
assert.ok(projectDragRule);
|
||||||
@@ -116,6 +118,7 @@ describe("architecture guardrails", () => {
|
|||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag completion rules delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag completion rules delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation release classification delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation release classification delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep unmount teardown delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep unmount teardown delegated to the extracted helper module",
|
||||||
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project and allocation drag position derivation delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep document mouse listener lifecycle delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep document mouse listener lifecycle delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
||||||
@@ -180,6 +183,10 @@ describe("architecture guardrails", () => {
|
|||||||
"apps/web/src/hooks/timelineDragCleanup.ts: missing guardrail anchor: timeline drag cleanup helpers must keep unmount teardown centralized",
|
"apps/web/src/hooks/timelineDragCleanup.ts: missing guardrail anchor: timeline drag cleanup helpers must keep unmount teardown centralized",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
assert.deepEqual(evaluateRule(positionRule, "export function resolveProjectDragPosition() {}\n"), [
|
||||||
|
"apps/web/src/hooks/timelineDragPosition.ts: missing guardrail anchor: timeline drag position helpers must keep allocation drag date derivation centralized",
|
||||||
|
]);
|
||||||
|
|
||||||
assert.deepEqual(evaluateRule(documentDragRule, ""), [
|
assert.deepEqual(evaluateRule(documentDragRule, ""), [
|
||||||
"apps/web/src/hooks/timelineDocumentDrag.ts: missing guardrail anchor: timeline document drag helpers must keep document mouse listener wiring centralized",
|
"apps/web/src/hooks/timelineDocumentDrag.ts: missing guardrail anchor: timeline document drag helpers must keep document mouse listener wiring centralized",
|
||||||
]);
|
]);
|
||||||
@@ -211,6 +218,7 @@ describe("architecture guardrails", () => {
|
|||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag completion rules delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag completion rules delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation release classification delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation release classification delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep unmount teardown delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep unmount teardown delegated to the extracted helper module",
|
||||||
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project and allocation drag position derivation delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep document mouse listener lifecycle delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep document mouse listener lifecycle delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
||||||
|
|||||||
Reference in New Issue
Block a user