gantt-lib 0.60.0 → 0.60.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,410 @@
1
+ /**
2
+ * Dependency link types following PM standard
3
+ * - FS (Finish-to-Start): Predecessor must finish before successor starts
4
+ * - SS (Start-to-Start): Predecessor must start before successor starts
5
+ * - FF (Finish-to-Finish): Predecessor must finish before successor finishes
6
+ * - SF (Start-to-Finish): Predecessor must start before successor finishes
7
+ */
8
+ type LinkType = 'FS' | 'SS' | 'FF' | 'SF';
9
+ /**
10
+ * Single dependency relationship (predecessor link)
11
+ */
12
+ interface TaskDependency {
13
+ /** ID of the predecessor task */
14
+ taskId: string;
15
+ /** Type of link: FS (finish-to-start), SS, FF, SF */
16
+ type: LinkType;
17
+ /** Lag in days (positive or negative integer) */
18
+ lag: number;
19
+ }
20
+ /**
21
+ * Error or warning from dependency validation
22
+ */
23
+ interface DependencyError {
24
+ /** Type of error */
25
+ type: 'cycle' | 'constraint' | 'missing-task';
26
+ /** ID of the task with the error */
27
+ taskId: string;
28
+ /** Human-readable error message */
29
+ message: string;
30
+ /** Related task IDs (e.g., cycle path, referenced tasks) */
31
+ relatedTaskIds?: string[];
32
+ }
33
+ /**
34
+ * Result of dependency validation
35
+ */
36
+ interface ValidationResult {
37
+ /** True if no errors found */
38
+ isValid: boolean;
39
+ /** Array of errors/warnings (empty if valid) */
40
+ errors: DependencyError[];
41
+ }
42
+ /**
43
+ * Task data structure for Gantt chart
44
+ */
45
+ interface Task {
46
+ /** Unique identifier for the task */
47
+ id: string;
48
+ /** Display name of the task */
49
+ name: string;
50
+ /** Task start date (ISO string or Date object) */
51
+ startDate: string | Date;
52
+ /** Task end date (ISO string or Date object) */
53
+ endDate: string | Date;
54
+ /** Optional color for task bar visualization */
55
+ color?: string;
56
+ /** Optional parent task ID for hierarchy relationship */
57
+ parentId?: string;
58
+ /**
59
+ * Optional progress value from 0-100
60
+ * - Decimal values are allowed and rounded to nearest integer for display
61
+ * - Values are clamped to 0-100 range
62
+ * - Undefined or 0 means no progress is displayed
63
+ * - Progress is visual-only, no user interaction
64
+ */
65
+ progress?: number;
66
+ /**
67
+ * Optional flag indicating if task is accepted
68
+ * - Only meaningful when progress is 100%
69
+ * - Affects the color of the progress bar (green for accepted, yellow for completed)
70
+ */
71
+ accepted?: boolean;
72
+ /**
73
+ * Optional array of predecessor dependencies
74
+ * Each dependency specifies a predecessor task, link type, and optional lag
75
+ */
76
+ dependencies?: TaskDependency[];
77
+ /**
78
+ * Optional flag to prevent drag and resize interactions.
79
+ * When true, the task bar cannot be moved or resized.
80
+ * Independent of accepted/progress — consumer controls both separately.
81
+ */
82
+ locked?: boolean;
83
+ }
84
+ /**
85
+ * Date range for Gantt chart display
86
+ */
87
+ interface GanttDateRange {
88
+ /** Start date of the visible range */
89
+ start: Date;
90
+ /** End date of the visible range */
91
+ end: Date;
92
+ }
93
+ /**
94
+ * Task bar positioning and dimensions
95
+ */
96
+ interface TaskBarGeometry {
97
+ /** Left position in pixels */
98
+ left: number;
99
+ /** Width in pixels */
100
+ width: number;
101
+ }
102
+ /**
103
+ * Grid configuration for layout calculations
104
+ */
105
+ interface GridConfig {
106
+ /** Width of each day column in pixels */
107
+ dayWidth: number;
108
+ /** Height of each task row in pixels */
109
+ rowHeight: number;
110
+ }
111
+ /**
112
+ * Represents a month span in the calendar header
113
+ */
114
+ interface MonthSpan {
115
+ /** First day of the month (UTC) */
116
+ month: Date;
117
+ /** Number of days this month spans in the visible range */
118
+ days: number;
119
+ /** Start index in the date range array */
120
+ startIndex: number;
121
+ }
122
+ /**
123
+ * Represents a vertical grid line
124
+ */
125
+ interface GridLine {
126
+ /** X position in pixels */
127
+ x: number;
128
+ /** True if this line is at the start of a month */
129
+ isMonthStart: boolean;
130
+ /** True if this line is at the start of a week (Monday) */
131
+ isWeekStart: boolean;
132
+ }
133
+ /**
134
+ * Represents a weekend background block
135
+ */
136
+ interface WeekendBlock {
137
+ /** Left position in pixels */
138
+ left: number;
139
+ /** Width in pixels */
140
+ width: number;
141
+ }
142
+
143
+ /**
144
+ * Pure date math utilities for the core scheduling module.
145
+ * Zero React/DOM/date-fns dependencies.
146
+ *
147
+ * Functions moved from:
148
+ * - dependencyUtils.ts: normalizeUTCDate, parseDateOnly, getBusinessDayOffset, shiftBusinessDayOffset, DAY_MS
149
+ * - dateUtils.ts: getBusinessDaysCount, addBusinessDays, subtractBusinessDays
150
+ */
151
+ declare const DAY_MS: number;
152
+ /**
153
+ * Normalize a Date to UTC midnight (hours/minutes/seconds zeroed).
154
+ */
155
+ declare function normalizeUTCDate(date: Date): Date;
156
+ /**
157
+ * Parse a date string or Date object to a UTC midnight Date.
158
+ * Handles ISO strings like "2025-01-15" by appending T00:00:00.000Z.
159
+ */
160
+ declare function parseDateOnly(date: string | Date): Date;
161
+ /**
162
+ * Compute the business-day offset between two dates.
163
+ * Steps through each calendar day, counting only non-weekend days.
164
+ * Returns a positive number if toDate > fromDate, negative if toDate < fromDate.
165
+ */
166
+ declare function getBusinessDayOffset(fromDate: Date, toDate: Date, weekendPredicate: (date: Date) => boolean): number;
167
+ /**
168
+ * Shift a date by a business-day offset, skipping weekends.
169
+ */
170
+ declare function shiftBusinessDayOffset(date: Date, offset: number, weekendPredicate: (date: Date) => boolean): Date;
171
+ /**
172
+ * Count business days between two dates (inclusive), excluding weekends.
173
+ * Returns minimum 1.
174
+ */
175
+ declare function getBusinessDaysCount(startDate: string | Date, endDate: string | Date, weekendPredicate: (date: Date) => boolean): number;
176
+ /**
177
+ * Calculate end date by adding N business days to start date.
178
+ * Returns a Date object.
179
+ */
180
+ declare function addBusinessDays(startDate: string | Date, businessDays: number, weekendPredicate: (date: Date) => boolean): Date;
181
+ /**
182
+ * Calculate start date by subtracting N business days from end date.
183
+ * Returns a Date object.
184
+ */
185
+ declare function subtractBusinessDays(endDate: string | Date, businessDays: number, weekendPredicate: (date: Date) => boolean): Date;
186
+ /**
187
+ * Snap a date to the nearest working day in the given direction.
188
+ */
189
+ declare function alignToWorkingDay(date: Date, direction: 1 | -1, weekendPredicate: (date: Date) => boolean): Date;
190
+ /**
191
+ * Get task duration in days (inclusive).
192
+ * If businessDays mode, counts business days using weekendPredicate.
193
+ * Otherwise, counts calendar days.
194
+ */
195
+ declare function getTaskDuration(startDate: string | Date, endDate: string | Date, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): number;
196
+
197
+ /**
198
+ * Dependency calculation functions.
199
+ * Moved from dependencyUtils.ts — verbatim implementations.
200
+ * Zero React/DOM/date-fns imports.
201
+ */
202
+
203
+ /**
204
+ * Get lag value from dependency, defaulting to 0.
205
+ */
206
+ declare function getDependencyLag(dep: Pick<TaskDependency, 'lag'>): number;
207
+ /**
208
+ * Normalize lag for FS links — clamp to >= -predecessorDuration.
209
+ */
210
+ declare function normalizeDependencyLag(linkType: LinkType, lag: number, predecessorStart: Date, predecessorEnd: Date, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): number;
211
+ /**
212
+ * Compute lag (in days) from actual predecessor/successor dates.
213
+ * This is the single source of truth for lag semantics.
214
+ *
215
+ * Semantics (lag=0 = natural, gap-free connection):
216
+ * - FS: lag = succStart - predEnd - 1 (adjacent days = 0)
217
+ * - SS: lag = succStart - predStart
218
+ * - FF: lag = succEnd - predEnd
219
+ * - SF: lag = succEnd - predStart + 1 (symmetric to FS)
220
+ */
221
+ declare function computeLagFromDates(linkType: LinkType, predStart: Date, predEnd: Date, succStart: Date, succEnd: Date, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): number;
222
+ /**
223
+ * Calculate successor date based on predecessor dates, link type, and lag.
224
+ *
225
+ * Link type semantics:
226
+ * - FS: Successor start = Predecessor end + lag + 1 day (lag=0 -> next day)
227
+ * - SS: Successor start = Predecessor start + lag
228
+ * - FF: Successor end = Predecessor end + lag
229
+ * - SF: Successor end = Predecessor start + lag - 1 day (lag=0 -> day before)
230
+ */
231
+ declare function calculateSuccessorDate(predecessorStart: Date, predecessorEnd: Date, linkType: LinkType, lag?: number, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): Date;
232
+
233
+ /**
234
+ * Cascade engine functions.
235
+ * Moved from dependencyUtils.ts — verbatim implementations.
236
+ * Zero React/DOM/date-fns imports.
237
+ */
238
+
239
+ /**
240
+ * Get successor tasks of a dragged task using BFS, filtered by link type(s).
241
+ */
242
+ declare function getSuccessorChain(draggedTaskId: string, allTasks: Task[], linkTypes?: LinkType[]): Task[];
243
+ /**
244
+ * Cascade successors by actual link constraints (BFS, constraint-based).
245
+ */
246
+ declare function cascadeByLinks(movedTaskId: string, newStart: Date, newEnd: Date, allTasks: Task[], skipChildCascade?: boolean): Task[];
247
+ /**
248
+ * Get transitive closure of successors for cascading.
249
+ */
250
+ declare function getTransitiveCascadeChain(changedTaskId: string, allTasks: Task[], firstLevelLinkTypes: LinkType[]): Task[];
251
+ /**
252
+ * Universal cascade engine that propagates a moved task's new position through
253
+ * the entire dependency+hierarchy graph using BFS with change detection.
254
+ */
255
+ declare function universalCascade(movedTask: Task, newStart: Date, newEnd: Date, allTasks: Task[], businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): Task[];
256
+ /**
257
+ * Recalculate all task dates when switching between business/calendar day modes.
258
+ */
259
+ declare function reflowTasksOnModeSwitch(sourceTasks: Task[], toBusinessDays: boolean, weekendPredicate: (date: Date) => boolean): Task[];
260
+
261
+ /**
262
+ * High-level schedule command functions.
263
+ * Moved from dependencyUtils.ts — verbatim implementations.
264
+ * Zero React/DOM/date-fns imports.
265
+ */
266
+
267
+ /**
268
+ * Build a task range (start/end dates) from a start date and duration.
269
+ */
270
+ declare function buildTaskRangeFromStart(startDate: Date, duration: number, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean, snapDirection?: 1 | -1): {
271
+ start: Date;
272
+ end: Date;
273
+ };
274
+ /**
275
+ * Build a task range (start/end dates) from an end date and duration.
276
+ */
277
+ declare function buildTaskRangeFromEnd(endDate: Date, duration: number, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean, snapDirection?: 1 | -1): {
278
+ start: Date;
279
+ end: Date;
280
+ };
281
+ /**
282
+ * Move a task range to a new start date, preserving duration.
283
+ */
284
+ declare function moveTaskRange(originalStart: string | Date, originalEnd: string | Date, proposedStart: Date, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean, snapDirection?: 1 | -1): {
285
+ start: Date;
286
+ end: Date;
287
+ };
288
+ /**
289
+ * Clamp task range start date based on incoming FS dependencies.
290
+ */
291
+ declare function clampTaskRangeForIncomingFS(task: Pick<Task, 'dependencies'>, proposedStart: Date, proposedEnd: Date, allTasks: Task[], businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): {
292
+ start: Date;
293
+ end: Date;
294
+ };
295
+ /**
296
+ * Recalculate incoming dependency lags after a task's dates change.
297
+ */
298
+ declare function recalculateIncomingLags(task: Task, newStartDate: Date, newEndDate: Date, allTasks: Task[], businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): NonNullable<Task['dependencies']>;
299
+ /**
300
+ * Convert pixel coordinates to a date range, applying business-day alignment
301
+ * when businessDays mode is active. This is the pure scheduling core of
302
+ * drag-to-date conversion.
303
+ *
304
+ * Extracted from useTaskDrag.ts resolveDraggedRange.
305
+ */
306
+ declare function resolveDateRangeFromPixels(mode: 'move' | 'resize-left' | 'resize-right', left: number, width: number, monthStart: Date, dayWidth: number, task: Task, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): {
307
+ start: Date;
308
+ end: Date;
309
+ };
310
+ /**
311
+ * Clamp a proposed date range based on incoming FS dependencies.
312
+ * For resize-right mode, returns range unchanged (only start is clamped).
313
+ *
314
+ * Extracted from useTaskDrag.ts clampDraggedRangeForIncomingFS.
315
+ */
316
+ declare function clampDateRangeForIncomingFS(task: Task, range: {
317
+ start: Date;
318
+ end: Date;
319
+ }, allTasks: Task[], mode: 'move' | 'resize-left' | 'resize-right', businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): {
320
+ start: Date;
321
+ end: Date;
322
+ };
323
+
324
+ /**
325
+ * Dependency validation and cycle detection.
326
+ * Moved from dependencyUtils.ts — verbatim implementations.
327
+ * Zero React/DOM/date-fns imports.
328
+ */
329
+
330
+ /**
331
+ * Build adjacency list for dependency graph (task -> successors)
332
+ */
333
+ declare function buildAdjacencyList(tasks: Task[]): Map<string, string[]>;
334
+ /**
335
+ * Detect circular dependencies using depth-first search
336
+ */
337
+ declare function detectCycles(tasks: Task[]): {
338
+ hasCycle: boolean;
339
+ cyclePath?: string[];
340
+ };
341
+ /**
342
+ * Validate all dependencies in the task list
343
+ */
344
+ declare function validateDependencies(tasks: Task[]): ValidationResult;
345
+
346
+ /**
347
+ * Hierarchy scheduling functions.
348
+ * Moved from dependencyUtils.ts — verbatim implementations.
349
+ * Zero React/DOM/date-fns imports.
350
+ */
351
+
352
+ /**
353
+ * Get all child tasks of a parent task.
354
+ * Returns tasks where task.parentId === parentId.
355
+ */
356
+ declare function getChildren(parentId: string, tasks: Task[]): Task[];
357
+ /**
358
+ * Check if a task is a parent (has children).
359
+ * Returns true if any task has this task as parentId.
360
+ */
361
+ declare function isTaskParent(taskId: string, tasks: Task[]): boolean;
362
+ /**
363
+ * Compute parent task dates from children.
364
+ * Returns { startDate, endDate } where:
365
+ * - startDate = min(children.startDate) or own startDate if no children
366
+ * - endDate = max(children.endDate) or own endDate if no children
367
+ */
368
+ declare function computeParentDates(parentId: string, tasks: Task[]): {
369
+ startDate: Date;
370
+ endDate: Date;
371
+ };
372
+ /**
373
+ * Compute parent task progress from children (weighted average by duration).
374
+ * Returns 0 if no children.
375
+ * Progress is rounded to 1 decimal place.
376
+ */
377
+ declare function computeParentProgress(parentId: string, tasks: Task[]): number;
378
+ /**
379
+ * Get all descendant tasks of a parent task (transitive closure of children).
380
+ * Returns all tasks where task.parentId is in the hierarchy of the parent.
381
+ */
382
+ declare function getAllDescendants(parentId: string, tasks: Task[]): Task[];
383
+ /**
384
+ * Get all dependency edges for rendering.
385
+ * Returns array of { predecessorId, successorId, type, lag }
386
+ */
387
+ declare function getAllDependencyEdges(tasks: Task[]): Array<{
388
+ predecessorId: string;
389
+ successorId: string;
390
+ type: 'FS' | 'SS' | 'FF' | 'SF';
391
+ lag: number;
392
+ }>;
393
+ /**
394
+ * Remove dependencies between two tasks in both directions.
395
+ */
396
+ declare function removeDependenciesBetweenTasks(taskId1: string, taskId2: string, tasks: Task[]): Task[];
397
+ /**
398
+ * Find the parent ID of a task.
399
+ */
400
+ declare function findParentId(taskId: string, tasks: Task[]): string | undefined;
401
+ /**
402
+ * Returns true when ancestorId is an ancestor of taskId in the current hierarchy.
403
+ */
404
+ declare function isAncestorTask(ancestorId: string, taskId: string, tasks: Task[]): boolean;
405
+ /**
406
+ * Returns true when tasks are in the same ancestry chain.
407
+ */
408
+ declare function areTasksHierarchicallyRelated(taskId1: string, taskId2: string, tasks: Task[]): boolean;
409
+
410
+ export { isTaskParent as A, moveTaskRange as B, normalizeDependencyLag as C, DAY_MS as D, normalizeUTCDate as E, parseDateOnly as F, type GanttDateRange as G, recalculateIncomingLags as H, reflowTasksOnModeSwitch as I, removeDependenciesBetweenTasks as J, shiftBusinessDayOffset as K, type LinkType as L, type MonthSpan as M, universalCascade as N, validateDependencies as O, type TaskDependency as P, addBusinessDays as Q, clampDateRangeForIncomingFS as R, getBusinessDaysCount as S, type Task as T, resolveDateRangeFromPixels as U, type ValidationResult as V, type WeekendBlock as W, subtractBusinessDays as X, type DependencyError as a, type GridConfig as b, type GridLine as c, type TaskBarGeometry as d, alignToWorkingDay as e, areTasksHierarchicallyRelated as f, buildAdjacencyList as g, buildTaskRangeFromEnd as h, buildTaskRangeFromStart as i, calculateSuccessorDate as j, cascadeByLinks as k, clampTaskRangeForIncomingFS as l, computeLagFromDates as m, computeParentDates as n, computeParentProgress as o, detectCycles as p, findParentId as q, getAllDependencyEdges as r, getAllDescendants as s, getBusinessDayOffset as t, getChildren as u, getDependencyLag as v, getSuccessorChain as w, getTaskDuration as x, getTransitiveCascadeChain as y, isAncestorTask as z };