Files
Nexus/apps/web/src/components/timeline/dragMath.ts
T
Hartmut e7b74f13bd refactor: consolidate duplicated code across web and API packages
- Extract shared render helpers (vacation blocks, range overlay, overbooking blink) into renderHelpers.tsx
- Centralize status badge styles and vacation color maps into status-styles.ts
- Extract dragMath.ts utility from useTimelineDrag for reuse
- Split useInvalidatePlanningViews into useInvalidateTimeline (4 queries) + useInvalidatePlanningViews (8 queries)
- Adopt findUniqueOrThrow() and Prisma select constants across API routers
- Add shared fmtEur() helper for API-side money formatting
- Wrap TimelineResourcePanel and TimelineProjectPanel with React.memo
- Fix pre-existing TS2589 deep type errors in TeamCalendar and VacationModal
- 38 files changed, reducing ~400 lines of duplicated code

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-19 00:10:08 +01:00

52 lines
1.6 KiB
TypeScript

/**
* Pure math utilities for timeline drag operations.
* Extracted from useTimelineDrag to make the conversion logic testable
* and reusable across different drag modes.
*/
/** Convert a pixel delta to a number of whole days based on cell width. */
export function pixelsToDays(deltaX: number, cellWidth: number): number {
return Math.round(deltaX / cellWidth);
}
/**
* Shift a date by a given number of days, returning a new Date.
* Does not mutate the input.
*/
export function shiftDate(date: Date, daysDelta: number): Date {
const result = new Date(date);
result.setDate(result.getDate() + daysDelta);
return result;
}
/**
* Compute new start/end dates for a drag operation.
* Handles move, resize-start, and resize-end modes with clamping
* to prevent start from crossing past end (or vice versa).
*/
export function computeDragDates(
mode: "move" | "resize-start" | "resize-end",
originalStart: Date,
originalEnd: Date,
daysDelta: number,
): { start: Date; end: Date } {
const newStart = new Date(originalStart);
const newEnd = new Date(originalEnd);
if (mode === "move") {
newStart.setDate(newStart.getDate() + daysDelta);
newEnd.setDate(newEnd.getDate() + daysDelta);
} else if (mode === "resize-start") {
newStart.setDate(newStart.getDate() + daysDelta);
// Clamp: allow same-day but prevent crossing
if (newStart > newEnd) newStart.setTime(newEnd.getTime());
} else {
// resize-end
newEnd.setDate(newEnd.getDate() + daysDelta);
// Clamp: allow same-day but prevent crossing
if (newEnd < newStart) newEnd.setTime(newStart.getTime());
}
return { start: newStart, end: newEnd };
}