gantt-lib 0.60.2 → 0.62.0

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.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { T as Task$1, V as ValidationResult } from './index-CliEEiHA.mjs';
3
- export { D as DAY_MS, a as DependencyError, G as GanttDateRange, b as GridConfig, c as GridLine, L as LinkType, M as MonthSpan, d as TaskBarGeometry, W as WeekendBlock, e as alignToWorkingDay, f as areTasksHierarchicallyRelated, g as buildAdjacencyList, h as buildTaskRangeFromEnd, i as buildTaskRangeFromStart, j as calculateSuccessorDate, k as cascadeByLinks, l as clampTaskRangeForIncomingFS, m as computeLagFromDates, n as computeParentDates, o as computeParentProgress, p as detectCycles, q as findParentId, r as getAllDependencyEdges, s as getAllDescendants, t as getBusinessDayOffset, u as getChildren, v as getDependencyLag, w as getSuccessorChain, x as getTaskDuration, y as getTransitiveCascadeChain, z as isAncestorTask, A as isTaskParent, B as moveTaskRange, C as normalizeDependencyLag, E as normalizeUTCDate, F as parseDateOnly, H as recalculateIncomingLags, I as reflowTasksOnModeSwitch, J as removeDependenciesBetweenTasks, K as shiftBusinessDayOffset, N as universalCascade, O as validateDependencies } from './index-CliEEiHA.mjs';
2
+ import { T as Task$1, V as ValidationResult } from './index-DMA7NbWe.mjs';
3
+ export { D as DAY_MS, a as DependencyError, G as GanttDateRange, b as GridConfig, c as GridLine, L as LinkType, M as MonthSpan, d as TaskBarGeometry, W as WeekendBlock, e as alignToWorkingDay, f as areTasksHierarchicallyRelated, g as buildAdjacencyList, h as buildTaskRangeFromEnd, i as buildTaskRangeFromStart, j as calculateSuccessorDate, k as cascadeByLinks, l as clampTaskRangeForIncomingFS, m as computeLagFromDates, n as computeParentDates, o as computeParentProgress, p as detectCycles, q as findParentId, r as getAllDependencyEdges, s as getAllDescendants, t as getBusinessDayOffset, u as getChildren, v as getDependencyLag, w as getSuccessorChain, x as getTaskDuration, y as getTransitiveCascadeChain, z as isAncestorTask, A as isTaskParent, B as moveTaskRange, C as moveTaskWithCascade, E as normalizeDependencyLag, F as normalizeUTCDate, H as parseDateOnly, I as recalculateIncomingLags, J as recalculateProjectSchedule, K as recalculateTaskFromDependencies, N as reflowTasksOnModeSwitch, O as removeDependenciesBetweenTasks, P as resizeTaskWithCascade, Q as shiftBusinessDayOffset, R as universalCascade, S as validateDependencies } from './index-DMA7NbWe.mjs';
4
4
  import * as RadixPopover from '@radix-ui/react-popover';
5
5
 
6
6
  /**
@@ -863,6 +863,40 @@ interface UseTaskDragReturn {
863
863
  */
864
864
  declare const useTaskDrag: (options: UseTaskDragOptions) => UseTaskDragReturn;
865
865
 
866
+ /**
867
+ * UI adapter: converts pixel coordinates to date ranges for drag interactions.
868
+ * @module adapters/scheduling
869
+ *
870
+ * These functions bridge the chart's pixel-space (left, width, dayWidth)
871
+ * with the scheduling domain's date-space. They depend on core scheduling
872
+ * primitives but are NOT part of the domain core themselves.
873
+ */
874
+
875
+ /**
876
+ * Convert pixel coordinates to a date range, applying business-day alignment
877
+ * when businessDays mode is active. This is the pure scheduling core of
878
+ * drag-to-date conversion.
879
+ *
880
+ * Extracted from useTaskDrag.ts resolveDraggedRange.
881
+ */
882
+ declare function resolveDateRangeFromPixels(mode: 'move' | 'resize-left' | 'resize-right', left: number, width: number, monthStart: Date, dayWidth: number, task: Task$1, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): {
883
+ start: Date;
884
+ end: Date;
885
+ };
886
+ /**
887
+ * Clamp a proposed date range based on incoming FS dependencies.
888
+ * For resize-right mode, returns range unchanged (only start is clamped).
889
+ *
890
+ * Extracted from useTaskDrag.ts clampDraggedRangeForIncomingFS.
891
+ */
892
+ declare function clampDateRangeForIncomingFS(task: Task$1, range: {
893
+ start: Date;
894
+ end: Date;
895
+ }, allTasks: Task$1[], mode: 'move' | 'resize-left' | 'resize-right', businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): {
896
+ start: Date;
897
+ end: Date;
898
+ };
899
+
866
900
  /**
867
901
  * Checks whether a task is behind the expected progress for the current date.
868
902
  * Uses the same progress-based rule as expired task highlighting in TaskRow.
@@ -1051,4 +1085,4 @@ interface VisibleReorderPosition {
1051
1085
  */
1052
1086
  declare function getVisibleReorderPosition(orderedTasks: TaskLike[], visibleTasks: TaskLike[], movedTaskId: string, originVisibleIndex: number, dropVisibleIndex: number): VisibleReorderPosition | null;
1053
1087
 
1054
- export { type BuiltInTaskListColumnId, Button, type ButtonProps, Calendar, type CalendarProps, type CustomDayConfig, type CustomDayPredicateConfig, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskDependency, TaskList, type TaskListColumn, type TaskListColumnContext, type TaskListProps, type TaskPredicate, TaskRow, TimeScaleHeader, TodayIndicator, ValidationResult, type VisibleReorderPosition, type WeekBlock, type WeekSpan, type YearSpan, addBusinessDays, and, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateMonthGridLines, calculateOrthogonalPath, calculateTaskBar, calculateWeekGridLines, calculateWeekendBlocks, createCustomDayPredicate, createDateKey, detectEdgeZone, expired, flattenHierarchy, formatDateLabel, formatDateRangeLabel, getBusinessDaysCount, getCursorForPosition, getDayOffset, getMonthBlocks, getMonthDays, getMonthSpans, getMultiMonthDays, getVisibleReorderPosition, getWeekBlocks, getWeekSpans, getYearSpans, inDateRange, isTaskExpired, isToday, isWeekend, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, subtractBusinessDays, useTaskDrag, withoutDeps };
1088
+ export { type BuiltInTaskListColumnId, Button, type ButtonProps, Calendar, type CalendarProps, type CustomDayConfig, type CustomDayPredicateConfig, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskDependency, TaskList, type TaskListColumn, type TaskListColumnContext, type TaskListProps, type TaskPredicate, TaskRow, TimeScaleHeader, TodayIndicator, ValidationResult, type VisibleReorderPosition, type WeekBlock, type WeekSpan, type YearSpan, addBusinessDays, and, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateMonthGridLines, calculateOrthogonalPath, calculateTaskBar, calculateWeekGridLines, calculateWeekendBlocks, clampDateRangeForIncomingFS, createCustomDayPredicate, createDateKey, detectEdgeZone, expired, flattenHierarchy, formatDateLabel, formatDateRangeLabel, getBusinessDaysCount, getCursorForPosition, getDayOffset, getMonthBlocks, getMonthDays, getMonthSpans, getMultiMonthDays, getVisibleReorderPosition, getWeekBlocks, getWeekSpans, getYearSpans, inDateRange, isTaskExpired, isToday, isWeekend, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, resolveDateRangeFromPixels, subtractBusinessDays, useTaskDrag, withoutDeps };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { T as Task$1, V as ValidationResult } from './index-CliEEiHA.js';
3
- export { D as DAY_MS, a as DependencyError, G as GanttDateRange, b as GridConfig, c as GridLine, L as LinkType, M as MonthSpan, d as TaskBarGeometry, W as WeekendBlock, e as alignToWorkingDay, f as areTasksHierarchicallyRelated, g as buildAdjacencyList, h as buildTaskRangeFromEnd, i as buildTaskRangeFromStart, j as calculateSuccessorDate, k as cascadeByLinks, l as clampTaskRangeForIncomingFS, m as computeLagFromDates, n as computeParentDates, o as computeParentProgress, p as detectCycles, q as findParentId, r as getAllDependencyEdges, s as getAllDescendants, t as getBusinessDayOffset, u as getChildren, v as getDependencyLag, w as getSuccessorChain, x as getTaskDuration, y as getTransitiveCascadeChain, z as isAncestorTask, A as isTaskParent, B as moveTaskRange, C as normalizeDependencyLag, E as normalizeUTCDate, F as parseDateOnly, H as recalculateIncomingLags, I as reflowTasksOnModeSwitch, J as removeDependenciesBetweenTasks, K as shiftBusinessDayOffset, N as universalCascade, O as validateDependencies } from './index-CliEEiHA.js';
2
+ import { T as Task$1, V as ValidationResult } from './index-DMA7NbWe.js';
3
+ export { D as DAY_MS, a as DependencyError, G as GanttDateRange, b as GridConfig, c as GridLine, L as LinkType, M as MonthSpan, d as TaskBarGeometry, W as WeekendBlock, e as alignToWorkingDay, f as areTasksHierarchicallyRelated, g as buildAdjacencyList, h as buildTaskRangeFromEnd, i as buildTaskRangeFromStart, j as calculateSuccessorDate, k as cascadeByLinks, l as clampTaskRangeForIncomingFS, m as computeLagFromDates, n as computeParentDates, o as computeParentProgress, p as detectCycles, q as findParentId, r as getAllDependencyEdges, s as getAllDescendants, t as getBusinessDayOffset, u as getChildren, v as getDependencyLag, w as getSuccessorChain, x as getTaskDuration, y as getTransitiveCascadeChain, z as isAncestorTask, A as isTaskParent, B as moveTaskRange, C as moveTaskWithCascade, E as normalizeDependencyLag, F as normalizeUTCDate, H as parseDateOnly, I as recalculateIncomingLags, J as recalculateProjectSchedule, K as recalculateTaskFromDependencies, N as reflowTasksOnModeSwitch, O as removeDependenciesBetweenTasks, P as resizeTaskWithCascade, Q as shiftBusinessDayOffset, R as universalCascade, S as validateDependencies } from './index-DMA7NbWe.js';
4
4
  import * as RadixPopover from '@radix-ui/react-popover';
5
5
 
6
6
  /**
@@ -863,6 +863,40 @@ interface UseTaskDragReturn {
863
863
  */
864
864
  declare const useTaskDrag: (options: UseTaskDragOptions) => UseTaskDragReturn;
865
865
 
866
+ /**
867
+ * UI adapter: converts pixel coordinates to date ranges for drag interactions.
868
+ * @module adapters/scheduling
869
+ *
870
+ * These functions bridge the chart's pixel-space (left, width, dayWidth)
871
+ * with the scheduling domain's date-space. They depend on core scheduling
872
+ * primitives but are NOT part of the domain core themselves.
873
+ */
874
+
875
+ /**
876
+ * Convert pixel coordinates to a date range, applying business-day alignment
877
+ * when businessDays mode is active. This is the pure scheduling core of
878
+ * drag-to-date conversion.
879
+ *
880
+ * Extracted from useTaskDrag.ts resolveDraggedRange.
881
+ */
882
+ declare function resolveDateRangeFromPixels(mode: 'move' | 'resize-left' | 'resize-right', left: number, width: number, monthStart: Date, dayWidth: number, task: Task$1, businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): {
883
+ start: Date;
884
+ end: Date;
885
+ };
886
+ /**
887
+ * Clamp a proposed date range based on incoming FS dependencies.
888
+ * For resize-right mode, returns range unchanged (only start is clamped).
889
+ *
890
+ * Extracted from useTaskDrag.ts clampDraggedRangeForIncomingFS.
891
+ */
892
+ declare function clampDateRangeForIncomingFS(task: Task$1, range: {
893
+ start: Date;
894
+ end: Date;
895
+ }, allTasks: Task$1[], mode: 'move' | 'resize-left' | 'resize-right', businessDays?: boolean, weekendPredicate?: (date: Date) => boolean): {
896
+ start: Date;
897
+ end: Date;
898
+ };
899
+
866
900
  /**
867
901
  * Checks whether a task is behind the expected progress for the current date.
868
902
  * Uses the same progress-based rule as expired task highlighting in TaskRow.
@@ -1051,4 +1085,4 @@ interface VisibleReorderPosition {
1051
1085
  */
1052
1086
  declare function getVisibleReorderPosition(orderedTasks: TaskLike[], visibleTasks: TaskLike[], movedTaskId: string, originVisibleIndex: number, dropVisibleIndex: number): VisibleReorderPosition | null;
1053
1087
 
1054
- export { type BuiltInTaskListColumnId, Button, type ButtonProps, Calendar, type CalendarProps, type CustomDayConfig, type CustomDayPredicateConfig, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskDependency, TaskList, type TaskListColumn, type TaskListColumnContext, type TaskListProps, type TaskPredicate, TaskRow, TimeScaleHeader, TodayIndicator, ValidationResult, type VisibleReorderPosition, type WeekBlock, type WeekSpan, type YearSpan, addBusinessDays, and, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateMonthGridLines, calculateOrthogonalPath, calculateTaskBar, calculateWeekGridLines, calculateWeekendBlocks, createCustomDayPredicate, createDateKey, detectEdgeZone, expired, flattenHierarchy, formatDateLabel, formatDateRangeLabel, getBusinessDaysCount, getCursorForPosition, getDayOffset, getMonthBlocks, getMonthDays, getMonthSpans, getMultiMonthDays, getVisibleReorderPosition, getWeekBlocks, getWeekSpans, getYearSpans, inDateRange, isTaskExpired, isToday, isWeekend, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, subtractBusinessDays, useTaskDrag, withoutDeps };
1088
+ export { type BuiltInTaskListColumnId, Button, type ButtonProps, Calendar, type CalendarProps, type CustomDayConfig, type CustomDayPredicateConfig, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskDependency, TaskList, type TaskListColumn, type TaskListColumnContext, type TaskListProps, type TaskPredicate, TaskRow, TimeScaleHeader, TodayIndicator, ValidationResult, type VisibleReorderPosition, type WeekBlock, type WeekSpan, type YearSpan, addBusinessDays, and, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateMonthGridLines, calculateOrthogonalPath, calculateTaskBar, calculateWeekGridLines, calculateWeekendBlocks, clampDateRangeForIncomingFS, createCustomDayPredicate, createDateKey, detectEdgeZone, expired, flattenHierarchy, formatDateLabel, formatDateRangeLabel, getBusinessDaysCount, getCursorForPosition, getDayOffset, getMonthBlocks, getMonthDays, getMonthSpans, getMultiMonthDays, getVisibleReorderPosition, getWeekBlocks, getWeekSpans, getYearSpans, inDateRange, isTaskExpired, isToday, isWeekend, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, resolveDateRangeFromPixels, subtractBusinessDays, useTaskDrag, withoutDeps };
package/dist/index.js CHANGED
@@ -537,6 +537,7 @@ __export(index_exports, {
537
537
  calculateWeekGridLines: () => calculateWeekGridLines,
538
538
  calculateWeekendBlocks: () => calculateWeekendBlocks,
539
539
  cascadeByLinks: () => cascadeByLinks,
540
+ clampDateRangeForIncomingFS: () => clampDateRangeForIncomingFS,
540
541
  clampTaskRangeForIncomingFS: () => clampTaskRangeForIncomingFS,
541
542
  computeLagFromDates: () => computeLagFromDates,
542
543
  computeParentDates: () => computeParentDates,
@@ -576,6 +577,7 @@ __export(index_exports, {
576
577
  isToday: () => isToday,
577
578
  isWeekend: () => isWeekend,
578
579
  moveTaskRange: () => moveTaskRange,
580
+ moveTaskWithCascade: () => moveTaskWithCascade,
579
581
  nameContains: () => nameContains,
580
582
  normalizeDependencyLag: () => normalizeDependencyLag,
581
583
  normalizeHierarchyTasks: () => normalizeHierarchyTasks,
@@ -588,8 +590,12 @@ __export(index_exports, {
588
590
  pixelsToDate: () => pixelsToDate,
589
591
  progressInRange: () => progressInRange,
590
592
  recalculateIncomingLags: () => recalculateIncomingLags,
593
+ recalculateProjectSchedule: () => recalculateProjectSchedule,
594
+ recalculateTaskFromDependencies: () => recalculateTaskFromDependencies,
591
595
  reflowTasksOnModeSwitch: () => reflowTasksOnModeSwitch,
592
596
  removeDependenciesBetweenTasks: () => removeDependenciesBetweenTasks,
597
+ resizeTaskWithCascade: () => resizeTaskWithCascade,
598
+ resolveDateRangeFromPixels: () => resolveDateRangeFromPixels,
593
599
  shiftBusinessDayOffset: () => shiftBusinessDayOffset,
594
600
  subtractBusinessDays: () => subtractBusinessDays2,
595
601
  universalCascade: () => universalCascade,
@@ -934,62 +940,6 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks, busin
934
940
  return { ...dep, lag: nextLag };
935
941
  });
936
942
  }
937
- function resolveDateRangeFromPixels(mode, left, width, monthStart, dayWidth, task, businessDays, weekendPredicate) {
938
- const dayOffset = Math.round(left / dayWidth);
939
- const rawStartDate = new Date(Date.UTC(
940
- monthStart.getUTCFullYear(),
941
- monthStart.getUTCMonth(),
942
- monthStart.getUTCDate() + dayOffset
943
- ));
944
- const rawEndOffset = dayOffset + Math.round(width / dayWidth) - 1;
945
- const rawEndDate = new Date(Date.UTC(
946
- monthStart.getUTCFullYear(),
947
- monthStart.getUTCMonth(),
948
- monthStart.getUTCDate() + rawEndOffset
949
- ));
950
- if (!(businessDays && weekendPredicate)) {
951
- return { start: rawStartDate, end: rawEndDate };
952
- }
953
- if (mode === "move") {
954
- const originalStart2 = new Date(task.startDate);
955
- const snapDirection2 = rawStartDate.getTime() >= originalStart2.getTime() ? 1 : -1;
956
- return moveTaskRange(
957
- task.startDate,
958
- task.endDate,
959
- rawStartDate,
960
- true,
961
- weekendPredicate,
962
- snapDirection2
963
- );
964
- }
965
- if (mode === "resize-right") {
966
- const fixedStart = new Date(task.startDate);
967
- const originalEnd = new Date(task.endDate);
968
- const snapDirection2 = rawEndDate.getTime() >= originalEnd.getTime() ? 1 : -1;
969
- const alignedEnd = alignToWorkingDay(rawEndDate, snapDirection2, weekendPredicate);
970
- const duration2 = Math.max(1, getBusinessDaysCount(fixedStart, alignedEnd, weekendPredicate));
971
- return buildTaskRangeFromStart(fixedStart, duration2, true, weekendPredicate);
972
- }
973
- const fixedEnd = new Date(task.endDate);
974
- const originalStart = new Date(task.startDate);
975
- const snapDirection = rawStartDate.getTime() >= originalStart.getTime() ? 1 : -1;
976
- const alignedStart = alignToWorkingDay(rawStartDate, snapDirection, weekendPredicate);
977
- const duration = Math.max(1, getBusinessDaysCount(alignedStart, fixedEnd, weekendPredicate));
978
- return buildTaskRangeFromEnd(fixedEnd, duration, true, weekendPredicate);
979
- }
980
- function clampDateRangeForIncomingFS(task, range, allTasks, mode, businessDays, weekendPredicate) {
981
- if (mode === "resize-right") {
982
- return range;
983
- }
984
- return clampTaskRangeForIncomingFS(
985
- task,
986
- range.start,
987
- range.end,
988
- allTasks,
989
- businessDays,
990
- weekendPredicate
991
- );
992
- }
993
943
 
994
944
  // src/core/scheduling/cascade.ts
995
945
  function getSuccessorChain(draggedTaskId, allTasks, linkTypes = ["FS"]) {
@@ -1306,6 +1256,313 @@ function reflowTasksOnModeSwitch(sourceTasks, toBusinessDays, weekendPredicate)
1306
1256
  return tasks;
1307
1257
  }
1308
1258
 
1259
+ // src/core/scheduling/execute.ts
1260
+ init_dateMath();
1261
+ function toIsoDate(date) {
1262
+ return date.toISOString().split("T")[0];
1263
+ }
1264
+ function createChangedResult(snapshot, nextTasks) {
1265
+ const originalById = new Map(snapshot.map((task) => [task.id, task]));
1266
+ const changedTasks = nextTasks.filter((task) => JSON.stringify(originalById.get(task.id)) !== JSON.stringify(task));
1267
+ return {
1268
+ changedTasks,
1269
+ changedIds: changedTasks.map((task) => task.id)
1270
+ };
1271
+ }
1272
+ function moveTaskWithCascade(taskId, newStart, snapshot, options) {
1273
+ const task = snapshot.find((t) => t.id === taskId);
1274
+ if (!task) {
1275
+ return { changedTasks: [], changedIds: [] };
1276
+ }
1277
+ const businessDays = options?.businessDays ?? false;
1278
+ const weekendPredicate = options?.weekendPredicate;
1279
+ const newRange = moveTaskRange(
1280
+ task.startDate,
1281
+ task.endDate,
1282
+ newStart,
1283
+ businessDays,
1284
+ weekendPredicate
1285
+ );
1286
+ const updatedDependencies = recalculateIncomingLags(
1287
+ task,
1288
+ newRange.start,
1289
+ newRange.end,
1290
+ snapshot,
1291
+ businessDays,
1292
+ weekendPredicate
1293
+ );
1294
+ const movedTask = {
1295
+ ...task,
1296
+ startDate: newRange.start.toISOString().split("T")[0],
1297
+ endDate: newRange.end.toISOString().split("T")[0],
1298
+ dependencies: updatedDependencies
1299
+ };
1300
+ if (options?.skipCascade) {
1301
+ return {
1302
+ changedTasks: [movedTask],
1303
+ changedIds: [movedTask.id]
1304
+ };
1305
+ }
1306
+ const cascadeResult = universalCascade(
1307
+ movedTask,
1308
+ newRange.start,
1309
+ newRange.end,
1310
+ snapshot,
1311
+ businessDays,
1312
+ weekendPredicate
1313
+ );
1314
+ const resultMap = /* @__PURE__ */ new Map();
1315
+ resultMap.set(movedTask.id, movedTask);
1316
+ for (const t of cascadeResult) {
1317
+ resultMap.set(t.id, t);
1318
+ }
1319
+ const changedTasks = Array.from(resultMap.values());
1320
+ return {
1321
+ changedTasks,
1322
+ changedIds: changedTasks.map((t) => t.id)
1323
+ };
1324
+ }
1325
+ function resizeTaskWithCascade(taskId, anchor, newDate, snapshot, options) {
1326
+ const task = snapshot.find((t) => t.id === taskId);
1327
+ if (!task) {
1328
+ return { changedTasks: [], changedIds: [] };
1329
+ }
1330
+ const businessDays = options?.businessDays ?? false;
1331
+ const weekendPredicate = options?.weekendPredicate;
1332
+ const originalStart = parseDateOnly(task.startDate);
1333
+ const originalEnd = parseDateOnly(task.endDate);
1334
+ let newRange;
1335
+ if (anchor === "end") {
1336
+ newRange = { start: originalStart, end: newDate };
1337
+ } else {
1338
+ newRange = { start: newDate, end: originalEnd };
1339
+ }
1340
+ const updatedDependencies = recalculateIncomingLags(
1341
+ task,
1342
+ newRange.start,
1343
+ newRange.end,
1344
+ snapshot,
1345
+ businessDays,
1346
+ weekendPredicate
1347
+ );
1348
+ const resizedTask = {
1349
+ ...task,
1350
+ startDate: newRange.start.toISOString().split("T")[0],
1351
+ endDate: newRange.end.toISOString().split("T")[0],
1352
+ dependencies: updatedDependencies
1353
+ };
1354
+ if (options?.skipCascade) {
1355
+ return {
1356
+ changedTasks: [resizedTask],
1357
+ changedIds: [resizedTask.id]
1358
+ };
1359
+ }
1360
+ const cascadeResult = universalCascade(
1361
+ resizedTask,
1362
+ newRange.start,
1363
+ newRange.end,
1364
+ snapshot,
1365
+ businessDays,
1366
+ weekendPredicate
1367
+ );
1368
+ const resultMap = /* @__PURE__ */ new Map();
1369
+ resultMap.set(resizedTask.id, resizedTask);
1370
+ for (const t of cascadeResult) {
1371
+ resultMap.set(t.id, t);
1372
+ }
1373
+ const changedTasks = Array.from(resultMap.values());
1374
+ return {
1375
+ changedTasks,
1376
+ changedIds: changedTasks.map((t) => t.id)
1377
+ };
1378
+ }
1379
+ function recalculateTaskFromDependencies(taskId, snapshot, options) {
1380
+ const task = snapshot.find((t) => t.id === taskId);
1381
+ if (!task) {
1382
+ return { changedTasks: [], changedIds: [] };
1383
+ }
1384
+ const businessDays = options?.businessDays ?? false;
1385
+ const weekendPredicate = options?.weekendPredicate;
1386
+ if (!task.dependencies || task.dependencies.length === 0) {
1387
+ return {
1388
+ changedTasks: [task],
1389
+ changedIds: [task.id]
1390
+ };
1391
+ }
1392
+ let constrainedStart = null;
1393
+ let constrainedEnd = null;
1394
+ for (const dep of task.dependencies) {
1395
+ const predecessor = snapshot.find((t) => t.id === dep.taskId);
1396
+ if (!predecessor) continue;
1397
+ const predStart = parseDateOnly(predecessor.startDate);
1398
+ const predEnd = parseDateOnly(predecessor.endDate);
1399
+ const constraintDate = calculateSuccessorDate(
1400
+ predStart,
1401
+ predEnd,
1402
+ dep.type,
1403
+ getDependencyLag(dep),
1404
+ businessDays,
1405
+ weekendPredicate
1406
+ );
1407
+ const duration = getTaskDuration(
1408
+ parseDateOnly(task.startDate),
1409
+ parseDateOnly(task.endDate),
1410
+ businessDays,
1411
+ weekendPredicate
1412
+ );
1413
+ let range;
1414
+ if (dep.type === "FS" || dep.type === "SS") {
1415
+ range = buildTaskRangeFromStart(constraintDate, duration, businessDays, weekendPredicate);
1416
+ } else {
1417
+ range = buildTaskRangeFromEnd(constraintDate, duration, businessDays, weekendPredicate);
1418
+ }
1419
+ if (!constrainedStart || range.start.getTime() > constrainedStart.getTime()) {
1420
+ constrainedStart = range.start;
1421
+ constrainedEnd = range.end;
1422
+ }
1423
+ }
1424
+ if (!constrainedStart || !constrainedEnd) {
1425
+ return {
1426
+ changedTasks: [task],
1427
+ changedIds: [task.id]
1428
+ };
1429
+ }
1430
+ const updatedDependencies = recalculateIncomingLags(
1431
+ task,
1432
+ constrainedStart,
1433
+ constrainedEnd,
1434
+ snapshot,
1435
+ businessDays,
1436
+ weekendPredicate
1437
+ );
1438
+ const recalculatedTask = {
1439
+ ...task,
1440
+ startDate: constrainedStart.toISOString().split("T")[0],
1441
+ endDate: constrainedEnd.toISOString().split("T")[0],
1442
+ dependencies: updatedDependencies
1443
+ };
1444
+ if (options?.skipCascade) {
1445
+ return {
1446
+ changedTasks: [recalculatedTask],
1447
+ changedIds: [recalculatedTask.id]
1448
+ };
1449
+ }
1450
+ const cascadeResult = universalCascade(
1451
+ recalculatedTask,
1452
+ constrainedStart,
1453
+ constrainedEnd,
1454
+ snapshot,
1455
+ businessDays,
1456
+ weekendPredicate
1457
+ );
1458
+ const resultMap = /* @__PURE__ */ new Map();
1459
+ resultMap.set(recalculatedTask.id, recalculatedTask);
1460
+ for (const t of cascadeResult) {
1461
+ resultMap.set(t.id, t);
1462
+ }
1463
+ const changedTasks = Array.from(resultMap.values());
1464
+ return {
1465
+ changedTasks,
1466
+ changedIds: changedTasks.map((t) => t.id)
1467
+ };
1468
+ }
1469
+ function recalculateProjectSchedule(snapshot, options) {
1470
+ const businessDays = options?.businessDays ?? false;
1471
+ const weekendPredicate = options?.weekendPredicate;
1472
+ const workingMap = new Map(snapshot.map((task) => [task.id, { ...task }]));
1473
+ const indegree = /* @__PURE__ */ new Map();
1474
+ const successorIdsByTask = /* @__PURE__ */ new Map();
1475
+ for (const task of snapshot) {
1476
+ indegree.set(task.id, 0);
1477
+ successorIdsByTask.set(task.id, []);
1478
+ }
1479
+ for (const task of snapshot) {
1480
+ for (const dep of task.dependencies ?? []) {
1481
+ if (!workingMap.has(dep.taskId)) {
1482
+ continue;
1483
+ }
1484
+ indegree.set(task.id, (indegree.get(task.id) ?? 0) + 1);
1485
+ successorIdsByTask.get(dep.taskId)?.push(task.id);
1486
+ }
1487
+ }
1488
+ const queue = snapshot.filter((task) => (indegree.get(task.id) ?? 0) === 0).map((task) => task.id);
1489
+ while (queue.length > 0) {
1490
+ const currentId = queue.shift();
1491
+ for (const successorId of successorIdsByTask.get(currentId) ?? []) {
1492
+ const nextIndegree = (indegree.get(successorId) ?? 0) - 1;
1493
+ indegree.set(successorId, nextIndegree);
1494
+ if (nextIndegree !== 0) {
1495
+ continue;
1496
+ }
1497
+ const currentTask = workingMap.get(successorId);
1498
+ if (!currentTask || currentTask.locked || !currentTask.dependencies?.length) {
1499
+ queue.push(successorId);
1500
+ continue;
1501
+ }
1502
+ const duration = getTaskDuration(
1503
+ parseDateOnly(currentTask.startDate),
1504
+ parseDateOnly(currentTask.endDate),
1505
+ businessDays,
1506
+ weekendPredicate
1507
+ );
1508
+ let constrainedRange = null;
1509
+ for (const dep of currentTask.dependencies) {
1510
+ const predecessor = workingMap.get(dep.taskId);
1511
+ if (!predecessor) {
1512
+ continue;
1513
+ }
1514
+ const predecessorStart = parseDateOnly(predecessor.startDate);
1515
+ const predecessorEnd = parseDateOnly(predecessor.endDate);
1516
+ const constraintDate = calculateSuccessorDate(
1517
+ predecessorStart,
1518
+ predecessorEnd,
1519
+ dep.type,
1520
+ getDependencyLag(dep),
1521
+ businessDays,
1522
+ weekendPredicate
1523
+ );
1524
+ const candidateRange = dep.type === "FS" || dep.type === "SS" ? buildTaskRangeFromStart(constraintDate, duration, businessDays, weekendPredicate) : buildTaskRangeFromEnd(constraintDate, duration, businessDays, weekendPredicate);
1525
+ if (!constrainedRange || candidateRange.start.getTime() > constrainedRange.start.getTime() || candidateRange.start.getTime() === constrainedRange.start.getTime() && candidateRange.end.getTime() > constrainedRange.end.getTime()) {
1526
+ constrainedRange = candidateRange;
1527
+ }
1528
+ }
1529
+ if (!constrainedRange) {
1530
+ queue.push(successorId);
1531
+ continue;
1532
+ }
1533
+ workingMap.set(successorId, {
1534
+ ...currentTask,
1535
+ startDate: toIsoDate(constrainedRange.start),
1536
+ endDate: toIsoDate(constrainedRange.end)
1537
+ });
1538
+ queue.push(successorId);
1539
+ }
1540
+ }
1541
+ const parentsByDepth = snapshot.filter((task) => isTaskParent(task.id, snapshot)).map((task) => {
1542
+ let depth = 0;
1543
+ let current = task.parentId ? workingMap.get(task.parentId) : void 0;
1544
+ while (current) {
1545
+ depth++;
1546
+ current = current.parentId ? workingMap.get(current.parentId) : void 0;
1547
+ }
1548
+ return { taskId: task.id, depth };
1549
+ }).sort((left, right) => right.depth - left.depth);
1550
+ const workingTasks = () => Array.from(workingMap.values());
1551
+ for (const { taskId } of parentsByDepth) {
1552
+ const parent = workingMap.get(taskId);
1553
+ if (!parent || parent.locked) {
1554
+ continue;
1555
+ }
1556
+ const { startDate, endDate } = computeParentDates(taskId, workingTasks());
1557
+ workingMap.set(taskId, {
1558
+ ...parent,
1559
+ startDate: toIsoDate(startDate),
1560
+ endDate: toIsoDate(endDate)
1561
+ });
1562
+ }
1563
+ return createChangedResult(snapshot, Array.from(workingMap.values()));
1564
+ }
1565
+
1309
1566
  // src/core/scheduling/validation.ts
1310
1567
  function buildAdjacencyList(tasks) {
1311
1568
  const graph = /* @__PURE__ */ new Map();
@@ -1886,6 +2143,67 @@ var isTaskExpired = (task, referenceDate = /* @__PURE__ */ new Date()) => {
1886
2143
 
1887
2144
  // src/hooks/useTaskDrag.ts
1888
2145
  var import_react2 = require("react");
2146
+
2147
+ // src/adapters/scheduling/drag.ts
2148
+ init_dateMath();
2149
+ function resolveDateRangeFromPixels(mode, left, width, monthStart, dayWidth, task, businessDays, weekendPredicate) {
2150
+ const dayOffset = Math.round(left / dayWidth);
2151
+ const rawStartDate = new Date(Date.UTC(
2152
+ monthStart.getUTCFullYear(),
2153
+ monthStart.getUTCMonth(),
2154
+ monthStart.getUTCDate() + dayOffset
2155
+ ));
2156
+ const rawEndOffset = dayOffset + Math.round(width / dayWidth) - 1;
2157
+ const rawEndDate = new Date(Date.UTC(
2158
+ monthStart.getUTCFullYear(),
2159
+ monthStart.getUTCMonth(),
2160
+ monthStart.getUTCDate() + rawEndOffset
2161
+ ));
2162
+ if (!(businessDays && weekendPredicate)) {
2163
+ return { start: rawStartDate, end: rawEndDate };
2164
+ }
2165
+ if (mode === "move") {
2166
+ const originalStart2 = new Date(task.startDate);
2167
+ const snapDirection2 = rawStartDate.getTime() >= originalStart2.getTime() ? 1 : -1;
2168
+ return moveTaskRange(
2169
+ task.startDate,
2170
+ task.endDate,
2171
+ rawStartDate,
2172
+ true,
2173
+ weekendPredicate,
2174
+ snapDirection2
2175
+ );
2176
+ }
2177
+ if (mode === "resize-right") {
2178
+ const fixedStart = new Date(task.startDate);
2179
+ const originalEnd = new Date(task.endDate);
2180
+ const snapDirection2 = rawEndDate.getTime() >= originalEnd.getTime() ? 1 : -1;
2181
+ const alignedEnd = alignToWorkingDay(rawEndDate, snapDirection2, weekendPredicate);
2182
+ const duration2 = Math.max(1, getBusinessDaysCount(fixedStart, alignedEnd, weekendPredicate));
2183
+ return buildTaskRangeFromStart(fixedStart, duration2, true, weekendPredicate);
2184
+ }
2185
+ const fixedEnd = new Date(task.endDate);
2186
+ const originalStart = new Date(task.startDate);
2187
+ const snapDirection = rawStartDate.getTime() >= originalStart.getTime() ? 1 : -1;
2188
+ const alignedStart = alignToWorkingDay(rawStartDate, snapDirection, weekendPredicate);
2189
+ const duration = Math.max(1, getBusinessDaysCount(alignedStart, fixedEnd, weekendPredicate));
2190
+ return buildTaskRangeFromEnd(fixedEnd, duration, true, weekendPredicate);
2191
+ }
2192
+ function clampDateRangeForIncomingFS(task, range, allTasks, mode, businessDays, weekendPredicate) {
2193
+ if (mode === "resize-right") {
2194
+ return range;
2195
+ }
2196
+ return clampTaskRangeForIncomingFS(
2197
+ task,
2198
+ range.start,
2199
+ range.end,
2200
+ allTasks,
2201
+ businessDays,
2202
+ weekendPredicate
2203
+ );
2204
+ }
2205
+
2206
+ // src/hooks/useTaskDrag.ts
1889
2207
  var globalActiveDrag = null;
1890
2208
  var globalRafId = null;
1891
2209
  function getDayOffsetFromMonthStart(date, monthStart) {
@@ -6803,8 +7121,11 @@ function GanttChartInner(props, ref) {
6803
7121
  });
6804
7122
  }
6805
7123
  const normalized = normalizeHierarchyTasks(updated);
7124
+ if (onReorder) {
7125
+ onReorder(normalized, movedTaskId, inferredParentId);
7126
+ return;
7127
+ }
6806
7128
  onTasksChange?.(normalized);
6807
- onReorder?.(normalized, movedTaskId, inferredParentId);
6808
7129
  }, [onTasksChange, onReorder]);
6809
7130
  const dependencyOverrides = (0, import_react13.useMemo)(() => {
6810
7131
  const map = new Map(cascadeOverrides);
@@ -7219,6 +7540,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
7219
7540
  calculateWeekGridLines,
7220
7541
  calculateWeekendBlocks,
7221
7542
  cascadeByLinks,
7543
+ clampDateRangeForIncomingFS,
7222
7544
  clampTaskRangeForIncomingFS,
7223
7545
  computeLagFromDates,
7224
7546
  computeParentDates,
@@ -7258,6 +7580,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
7258
7580
  isToday,
7259
7581
  isWeekend,
7260
7582
  moveTaskRange,
7583
+ moveTaskWithCascade,
7261
7584
  nameContains,
7262
7585
  normalizeDependencyLag,
7263
7586
  normalizeHierarchyTasks,
@@ -7270,8 +7593,12 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
7270
7593
  pixelsToDate,
7271
7594
  progressInRange,
7272
7595
  recalculateIncomingLags,
7596
+ recalculateProjectSchedule,
7597
+ recalculateTaskFromDependencies,
7273
7598
  reflowTasksOnModeSwitch,
7274
7599
  removeDependenciesBetweenTasks,
7600
+ resizeTaskWithCascade,
7601
+ resolveDateRangeFromPixels,
7275
7602
  shiftBusinessDayOffset,
7276
7603
  subtractBusinessDays,
7277
7604
  universalCascade,