refactor(web): extract range selection bootstrap
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { finalizeRangeSelection, updateRangeSelectionDraft } from "./timelineRangeSelection.js";
|
||||
import { createRangeSelectionState, finalizeRangeSelection, updateRangeSelectionDraft } from "./timelineRangeSelection.js";
|
||||
|
||||
type TestRangeState = {
|
||||
isSelecting: boolean;
|
||||
@@ -11,6 +11,23 @@ type TestRangeState = {
|
||||
};
|
||||
|
||||
describe("timelineRangeSelection", () => {
|
||||
it("creates a selection draft with currentDate anchored to the start date", () => {
|
||||
expect(
|
||||
createRangeSelectionState<TestRangeState>(
|
||||
"res-1",
|
||||
new Date("2025-01-15T12:00:00.000Z"),
|
||||
100,
|
||||
),
|
||||
).toEqual({
|
||||
isSelecting: true,
|
||||
resourceId: "res-1",
|
||||
startDate: new Date("2025-01-15T12:00:00.000Z"),
|
||||
currentDate: new Date("2025-01-15T12:00:00.000Z"),
|
||||
suggestedProjectId: null,
|
||||
startClientX: 100,
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores updates when no full day boundary was crossed", () => {
|
||||
const state: TestRangeState = {
|
||||
isSelecting: true,
|
||||
|
||||
@@ -18,6 +18,22 @@ export type RangeSelectionResult = {
|
||||
anchorY: number;
|
||||
};
|
||||
|
||||
export function createRangeSelectionState<TState extends RangeStateLike>(
|
||||
resourceId: string,
|
||||
startDate: Date,
|
||||
startClientX: number,
|
||||
suggestedProjectId?: string | null,
|
||||
): TState {
|
||||
return {
|
||||
isSelecting: true,
|
||||
resourceId,
|
||||
startDate,
|
||||
currentDate: startDate,
|
||||
suggestedProjectId: suggestedProjectId ?? null,
|
||||
startClientX,
|
||||
} as TState;
|
||||
}
|
||||
|
||||
export function updateRangeSelectionDraft<TState extends RangeStateLike>(
|
||||
state: TState,
|
||||
currentClientX: number,
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
updateMultiSelectDraft,
|
||||
} from "./timelineMultiSelect.js";
|
||||
import { reconcileOptimisticEntries } from "./timelineOptimisticAllocations.js";
|
||||
import { finalizeRangeSelection, updateRangeSelectionDraft } from "./timelineRangeSelection.js";
|
||||
import { createRangeSelectionState, finalizeRangeSelection, updateRangeSelectionDraft } from "./timelineRangeSelection.js";
|
||||
import { getTouchPoint, resolveTouchDragDecision } from "./timelineTouch.js";
|
||||
|
||||
const DRAG_CLICK_THRESHOLD_PX = 5;
|
||||
@@ -826,14 +826,12 @@ export function useTimelineDrag({
|
||||
if (dragStateRef.current.isDragging || allocDragRef.current.isActive) return;
|
||||
if (e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
const state: RangeState = {
|
||||
isSelecting: true,
|
||||
resourceId: opts.resourceId,
|
||||
startDate: opts.startDate,
|
||||
currentDate: opts.startDate,
|
||||
suggestedProjectId: opts.suggestedProjectId ?? null,
|
||||
startClientX: e.clientX,
|
||||
};
|
||||
const state = createRangeSelectionState<RangeState>(
|
||||
opts.resourceId,
|
||||
opts.startDate,
|
||||
e.clientX,
|
||||
opts.suggestedProjectId,
|
||||
);
|
||||
rangeStateRef.current = state;
|
||||
setRangeState(state);
|
||||
},
|
||||
|
||||
@@ -153,6 +153,10 @@ export const rules = [
|
||||
file: "apps/web/src/hooks/timelineRangeSelection.ts",
|
||||
maxLines: 90,
|
||||
required: [
|
||||
{
|
||||
pattern: /\bexport function createRangeSelectionState\b/,
|
||||
message: "timeline range helpers must keep selection bootstrap centralized",
|
||||
},
|
||||
{
|
||||
pattern: /\bexport function updateRangeSelectionDraft\b/,
|
||||
message: "timeline range helpers must keep preview date derivation centralized",
|
||||
@@ -304,7 +308,7 @@ export const rules = [
|
||||
],
|
||||
forbidden: [
|
||||
{
|
||||
pattern: /\bfunction (?:toPxValue|joinTransforms|captureLivePreviewTargets|renderLivePreview|scheduleLivePreview|clearLivePreview|datesMatch|preserveLivePreview)\b/,
|
||||
pattern: /\bfunction (?:toPxValue|joinTransforms|captureLivePreviewTargets|renderLivePreview|scheduleLivePreview|clearLivePreview|datesMatch|preserveLivePreview|createRangeSelectionState)\b/,
|
||||
message: "timeline drag must not re-inline live preview helper implementations",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -121,6 +121,7 @@ describe("architecture guardrails", () => {
|
||||
]);
|
||||
|
||||
assert.deepEqual(evaluateRule(rangeRule, "export function updateRangeSelectionDraft() {}\n"), [
|
||||
"apps/web/src/hooks/timelineRangeSelection.ts: missing guardrail anchor: timeline range helpers must keep selection bootstrap centralized",
|
||||
"apps/web/src/hooks/timelineRangeSelection.ts: missing guardrail anchor: timeline range helpers must keep ordered range finalization centralized",
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user