gantt-lib 0.6.0 → 0.6.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.
package/dist/index.d.mts CHANGED
@@ -924,4 +924,17 @@ declare const calculateOrthogonalPath: (from: {
924
924
  y: number;
925
925
  }) => string;
926
926
 
927
- export { Button, type ButtonProps, Calendar, type CalendarProps, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, type GanttDateRange, GridBackground, type GridConfig, type GridLine, Input, type InputProps, type MonthSpan, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskBarGeometry, type TaskDependency, TaskList, type TaskListProps, TaskRow, TimeScaleHeader, TodayIndicator, type WeekendBlock, buildAdjacencyList, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateOrthogonalPath, calculateSuccessorDate, calculateTaskBar, calculateWeekendBlocks, cascadeByLinks, computeLagFromDates, computeParentDates, computeParentProgress, detectCycles, detectEdgeZone, findParentId, formatDateLabel, getAllDependencyEdges, getChildren, getCursorForPosition, getDayOffset, getMonthDays, getMonthSpans, getMultiMonthDays, getSuccessorChain, getTransitiveCascadeChain, isTaskParent, isToday, isWeekend, parseUTCDate, pixelsToDate, recalculateIncomingLags, removeDependenciesBetweenTasks, useTaskDrag, validateDependencies };
927
+ /**
928
+ * Build a stable depth-first task order from parentId links.
929
+ * Sibling order follows the order in the input array.
930
+ * Tasks with missing parents are treated as root tasks.
931
+ */
932
+ declare function flattenHierarchy<T extends Task$1>(tasks: T[]): T[];
933
+ /**
934
+ * Normalize hierarchy-aware display fields.
935
+ * Parent task dates and progress are always recomputed from children,
936
+ * taking precedence over any hardcoded parent values from the input.
937
+ */
938
+ declare function normalizeHierarchyTasks<T extends Task$1>(tasks: T[]): T[];
939
+
940
+ export { Button, type ButtonProps, Calendar, type CalendarProps, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, type GanttDateRange, GridBackground, type GridConfig, type GridLine, Input, type InputProps, type MonthSpan, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskBarGeometry, type TaskDependency, TaskList, type TaskListProps, TaskRow, TimeScaleHeader, TodayIndicator, type WeekendBlock, buildAdjacencyList, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateOrthogonalPath, calculateSuccessorDate, calculateTaskBar, calculateWeekendBlocks, cascadeByLinks, computeLagFromDates, computeParentDates, computeParentProgress, detectCycles, detectEdgeZone, findParentId, flattenHierarchy, formatDateLabel, getAllDependencyEdges, getChildren, getCursorForPosition, getDayOffset, getMonthDays, getMonthSpans, getMultiMonthDays, getSuccessorChain, getTransitiveCascadeChain, isTaskParent, isToday, isWeekend, normalizeHierarchyTasks, parseUTCDate, pixelsToDate, recalculateIncomingLags, removeDependenciesBetweenTasks, useTaskDrag, validateDependencies };
package/dist/index.d.ts CHANGED
@@ -924,4 +924,17 @@ declare const calculateOrthogonalPath: (from: {
924
924
  y: number;
925
925
  }) => string;
926
926
 
927
- export { Button, type ButtonProps, Calendar, type CalendarProps, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, type GanttDateRange, GridBackground, type GridConfig, type GridLine, Input, type InputProps, type MonthSpan, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskBarGeometry, type TaskDependency, TaskList, type TaskListProps, TaskRow, TimeScaleHeader, TodayIndicator, type WeekendBlock, buildAdjacencyList, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateOrthogonalPath, calculateSuccessorDate, calculateTaskBar, calculateWeekendBlocks, cascadeByLinks, computeLagFromDates, computeParentDates, computeParentProgress, detectCycles, detectEdgeZone, findParentId, formatDateLabel, getAllDependencyEdges, getChildren, getCursorForPosition, getDayOffset, getMonthDays, getMonthSpans, getMultiMonthDays, getSuccessorChain, getTransitiveCascadeChain, isTaskParent, isToday, isWeekend, parseUTCDate, pixelsToDate, recalculateIncomingLags, removeDependenciesBetweenTasks, useTaskDrag, validateDependencies };
927
+ /**
928
+ * Build a stable depth-first task order from parentId links.
929
+ * Sibling order follows the order in the input array.
930
+ * Tasks with missing parents are treated as root tasks.
931
+ */
932
+ declare function flattenHierarchy<T extends Task$1>(tasks: T[]): T[];
933
+ /**
934
+ * Normalize hierarchy-aware display fields.
935
+ * Parent task dates and progress are always recomputed from children,
936
+ * taking precedence over any hardcoded parent values from the input.
937
+ */
938
+ declare function normalizeHierarchyTasks<T extends Task$1>(tasks: T[]): T[];
939
+
940
+ export { Button, type ButtonProps, Calendar, type CalendarProps, DatePicker, type DatePickerProps, DragGuideLines, GanttChart, type GanttChartHandle, type GanttChartProps, type GanttDateRange, GridBackground, type GridConfig, type GridLine, Input, type InputProps, type MonthSpan, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskBarGeometry, type TaskDependency, TaskList, type TaskListProps, TaskRow, TimeScaleHeader, TodayIndicator, type WeekendBlock, buildAdjacencyList, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateOrthogonalPath, calculateSuccessorDate, calculateTaskBar, calculateWeekendBlocks, cascadeByLinks, computeLagFromDates, computeParentDates, computeParentProgress, detectCycles, detectEdgeZone, findParentId, flattenHierarchy, formatDateLabel, getAllDependencyEdges, getChildren, getCursorForPosition, getDayOffset, getMonthDays, getMonthSpans, getMultiMonthDays, getSuccessorChain, getTransitiveCascadeChain, isTaskParent, isToday, isWeekend, normalizeHierarchyTasks, parseUTCDate, pixelsToDate, recalculateIncomingLags, removeDependenciesBetweenTasks, useTaskDrag, validateDependencies };
package/dist/index.js CHANGED
@@ -61,6 +61,7 @@ __export(index_exports, {
61
61
  detectCycles: () => detectCycles,
62
62
  detectEdgeZone: () => detectEdgeZone,
63
63
  findParentId: () => findParentId,
64
+ flattenHierarchy: () => flattenHierarchy,
64
65
  formatDateLabel: () => formatDateLabel,
65
66
  getAllDependencyEdges: () => getAllDependencyEdges,
66
67
  getChildren: () => getChildren,
@@ -74,6 +75,7 @@ __export(index_exports, {
74
75
  isTaskParent: () => isTaskParent,
75
76
  isToday: () => isToday,
76
77
  isWeekend: () => isWeekend,
78
+ normalizeHierarchyTasks: () => normalizeHierarchyTasks,
77
79
  parseUTCDate: () => parseUTCDate,
78
80
  pixelsToDate: () => pixelsToDate,
79
81
  recalculateIncomingLags: () => recalculateIncomingLags,
@@ -575,6 +577,55 @@ function findParentId(taskId, tasks) {
575
577
  return task?.parentId;
576
578
  }
577
579
 
580
+ // src/utils/hierarchyOrder.ts
581
+ function flattenHierarchy(tasks) {
582
+ const byId = new Map(tasks.map((task) => [task.id, task]));
583
+ const byParent = /* @__PURE__ */ new Map();
584
+ for (const task of tasks) {
585
+ const normalizedParentId = task.parentId && byId.has(task.parentId) ? task.parentId : void 0;
586
+ const siblings = byParent.get(normalizedParentId) ?? [];
587
+ siblings.push(task);
588
+ byParent.set(normalizedParentId, siblings);
589
+ }
590
+ const result = [];
591
+ const visited = /* @__PURE__ */ new Set();
592
+ const walk = (parentId) => {
593
+ const children = byParent.get(parentId) ?? [];
594
+ for (const task of children) {
595
+ if (visited.has(task.id)) continue;
596
+ visited.add(task.id);
597
+ result.push(task);
598
+ walk(task.id);
599
+ }
600
+ };
601
+ walk(void 0);
602
+ for (const task of tasks) {
603
+ if (!visited.has(task.id)) {
604
+ result.push(task);
605
+ }
606
+ }
607
+ return result;
608
+ }
609
+ function normalizeHierarchyTasks(tasks) {
610
+ const orderedTasks = flattenHierarchy(tasks).map((task) => ({ ...task }));
611
+ for (const task of [...orderedTasks].reverse()) {
612
+ if (!isTaskParent(task.id, orderedTasks)) continue;
613
+ const { startDate, endDate } = computeParentDates(task.id, orderedTasks);
614
+ const progress = computeParentProgress(task.id, orderedTasks);
615
+ const normalizedStartDate = startDate.toISOString().split("T")[0];
616
+ const normalizedEndDate = endDate.toISOString().split("T")[0];
617
+ const parentIndex = orderedTasks.findIndex((candidate) => candidate.id === task.id);
618
+ if (parentIndex === -1) continue;
619
+ orderedTasks[parentIndex] = {
620
+ ...orderedTasks[parentIndex],
621
+ startDate: normalizedStartDate,
622
+ endDate: normalizedEndDate,
623
+ progress
624
+ };
625
+ }
626
+ return orderedTasks;
627
+ }
628
+
578
629
  // src/components/TimeScaleHeader/TimeScaleHeader.tsx
579
630
  var import_react = require("react");
580
631
  var import_date_fns = require("date-fns");
@@ -3144,13 +3195,14 @@ var TaskList = ({
3144
3195
  return next;
3145
3196
  });
3146
3197
  }, []);
3198
+ const orderedTasks = (0, import_react12.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
3147
3199
  const visibleTasks = (0, import_react12.useMemo)(() => {
3148
- return tasks.filter((task) => {
3200
+ return orderedTasks.filter((task) => {
3149
3201
  if (!task.parentId) return true;
3150
3202
  const parentCollapsed = collapsedParentIds.has(task.parentId);
3151
3203
  return !parentCollapsed;
3152
3204
  });
3153
- }, [tasks, collapsedParentIds]);
3205
+ }, [orderedTasks, collapsedParentIds]);
3154
3206
  const totalHeight = (0, import_react12.useMemo)(
3155
3207
  () => visibleTasks.length * rowHeight,
3156
3208
  [visibleTasks.length, rowHeight]
@@ -3272,9 +3324,9 @@ var TaskList = ({
3272
3324
  dragOriginIndexRef.current = null;
3273
3325
  return;
3274
3326
  }
3275
- const reordered = [...tasks];
3327
+ const reordered = [...orderedTasks];
3276
3328
  const [moved] = reordered.splice(originIndex, 1);
3277
- const insertIndex = dropIndex === tasks.length ? tasks.length - 1 : originIndex < dropIndex ? dropIndex - 1 : dropIndex;
3329
+ const insertIndex = dropIndex === visibleTasks.length ? visibleTasks.length - 1 : originIndex < dropIndex ? dropIndex - 1 : dropIndex;
3278
3330
  const isChild = !!moved.parentId;
3279
3331
  const isParent = tasks.some((t) => t.parentId === moved.id);
3280
3332
  const taskType = isParent ? "PARENT" : isChild ? "CHILD" : "ROOT";
@@ -3289,7 +3341,7 @@ var TaskList = ({
3289
3341
  insertIndex,
3290
3342
  direction: originIndex < dropIndex ? "DOWN" : "UP"
3291
3343
  });
3292
- console.log("[TASKS ARRAY LENGTH]", tasks.length);
3344
+ console.log("[TASKS ARRAY LENGTH]", orderedTasks.length);
3293
3345
  let inferredParentId;
3294
3346
  if (moved.parentId) {
3295
3347
  const parentIndex = reordered.findIndex((t) => t.id === moved.parentId);
@@ -3374,7 +3426,7 @@ var TaskList = ({
3374
3426
  setDraggingIndex(null);
3375
3427
  setDragOverIndex(null);
3376
3428
  dragOriginIndexRef.current = null;
3377
- }, [tasks, onReorder, onTaskSelect]);
3429
+ }, [orderedTasks, visibleTasks.length, onReorder, onTaskSelect]);
3378
3430
  const handleDragEnd = (0, import_react12.useCallback)(() => {
3379
3431
  setDraggingIndex(null);
3380
3432
  setDragOverIndex(null);
@@ -3497,16 +3549,16 @@ var TaskList = ({
3497
3549
  enableAddTask && onAdd && !isCreating && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3498
3550
  "button",
3499
3551
  {
3500
- className: `gantt-tl-add-btn${dragOverIndex === tasks.length ? " gantt-tl-add-btn-drag-over" : ""}`,
3552
+ className: `gantt-tl-add-btn${dragOverIndex === visibleTasks.length ? " gantt-tl-add-btn-drag-over" : ""}`,
3501
3553
  onClick: () => setIsCreating(true),
3502
3554
  onDragEnter: (e) => {
3503
3555
  e.preventDefault();
3504
- setDragOverIndex(tasks.length);
3556
+ setDragOverIndex(visibleTasks.length);
3505
3557
  },
3506
3558
  onDragOver: (e) => {
3507
3559
  e.preventDefault();
3508
3560
  e.dataTransfer.dropEffect = "move";
3509
- setDragOverIndex(tasks.length);
3561
+ setDragOverIndex(visibleTasks.length);
3510
3562
  },
3511
3563
  onDragLeave: (e) => {
3512
3564
  e.preventDefault();
@@ -3514,7 +3566,7 @@ var TaskList = ({
3514
3566
  },
3515
3567
  onDrop: (e) => {
3516
3568
  e.preventDefault();
3517
- handleDrop(tasks.length, e);
3569
+ handleDrop(visibleTasks.length, e);
3518
3570
  },
3519
3571
  type: "button",
3520
3572
  children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443"
@@ -3554,7 +3606,8 @@ var GanttChart = (0, import_react13.forwardRef)(({
3554
3606
  const [selectedChip, setSelectedChip] = (0, import_react13.useState)(null);
3555
3607
  const [collapsedParentIds, setCollapsedParentIds] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
3556
3608
  const [editingTaskId, setEditingTaskId] = (0, import_react13.useState)(null);
3557
- const dateRange = (0, import_react13.useMemo)(() => getMultiMonthDays(tasks), [tasks]);
3609
+ const normalizedTasks = (0, import_react13.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
3610
+ const dateRange = (0, import_react13.useMemo)(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
3558
3611
  const [validationResult, setValidationResult] = (0, import_react13.useState)(null);
3559
3612
  const [cascadeOverrides, setCascadeOverrides] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
3560
3613
  const gridWidth = (0, import_react13.useMemo)(
@@ -3562,12 +3615,12 @@ var GanttChart = (0, import_react13.forwardRef)(({
3562
3615
  [dateRange.length, dayWidth]
3563
3616
  );
3564
3617
  const filteredTasks = (0, import_react13.useMemo)(() => {
3565
- return tasks.filter((task) => {
3618
+ return normalizedTasks.filter((task) => {
3566
3619
  if (!task.parentId) return true;
3567
3620
  const parentCollapsed = collapsedParentIds.has(task.parentId);
3568
3621
  return !parentCollapsed;
3569
3622
  });
3570
- }, [tasks, collapsedParentIds]);
3623
+ }, [normalizedTasks, collapsedParentIds]);
3571
3624
  const totalGridHeight = (0, import_react13.useMemo)(
3572
3625
  () => filteredTasks.length * rowHeight,
3573
3626
  [filteredTasks.length, rowHeight]
@@ -3813,9 +3866,9 @@ var GanttChart = (0, import_react13.forwardRef)(({
3813
3866
  updatedCount: updated.length
3814
3867
  });
3815
3868
  console.log("=== GANTT CHART handleReorder END ===\n");
3816
- return updated;
3869
+ return normalizeHierarchyTasks(updated);
3817
3870
  });
3818
- onReorder?.(reorderedTasks, movedTaskId, inferredParentId);
3871
+ onReorder?.(normalizeHierarchyTasks(reorderedTasks), movedTaskId, inferredParentId);
3819
3872
  }, [onChange, onReorder]);
3820
3873
  const dependencyOverrides = (0, import_react13.useMemo)(() => {
3821
3874
  const map = new Map(cascadeOverrides);
@@ -3882,15 +3935,15 @@ var GanttChart = (0, import_react13.forwardRef)(({
3882
3935
  const parentId = taskToPromote.parentId;
3883
3936
  const siblings = currentTasks.filter((t) => t.parentId === parentId);
3884
3937
  if (siblings.length <= 1) {
3885
- return currentTasks.map(
3938
+ return normalizeHierarchyTasks(currentTasks.map(
3886
3939
  (t) => t.id === taskId ? { ...t, parentId: void 0 } : t
3887
- );
3940
+ ));
3888
3941
  }
3889
3942
  const lastSiblingIndex = currentTasks.map((t, i) => ({ task: t, index: i })).filter(({ task }) => task.parentId === parentId).sort((a, b) => b.index - a.index)[0];
3890
3943
  if (!lastSiblingIndex) {
3891
- return currentTasks.map(
3944
+ return normalizeHierarchyTasks(currentTasks.map(
3892
3945
  (t) => t.id === taskId ? { ...t, parentId: void 0 } : t
3893
- );
3946
+ ));
3894
3947
  }
3895
3948
  const withoutPromotedTask = currentTasks.filter((t) => t.id !== taskId);
3896
3949
  const insertIndex = lastSiblingIndex.index + 1;
@@ -3900,7 +3953,7 @@ var GanttChart = (0, import_react13.forwardRef)(({
3900
3953
  promotedTask,
3901
3954
  ...withoutPromotedTask.slice(insertIndex)
3902
3955
  ];
3903
- return newTasks;
3956
+ return normalizeHierarchyTasks(newTasks);
3904
3957
  });
3905
3958
  }, [onChange]);
3906
3959
  const handleDemoteTask = (0, import_react13.useCallback)((taskId, newParentId) => {
@@ -3933,7 +3986,7 @@ var GanttChart = (0, import_react13.forwardRef)(({
3933
3986
  updatedTasks = updatedTasks.map(
3934
3987
  (t) => t.id === newParentId ? { ...t, startDate: parentDates.startDate.toISOString().split("T")[0], endDate: parentDates.endDate.toISOString().split("T")[0], progress: parentProgress } : t
3935
3988
  );
3936
- return updatedTasks;
3989
+ return normalizeHierarchyTasks(updatedTasks);
3937
3990
  });
3938
3991
  }, [onChange]);
3939
3992
  const panStateRef = (0, import_react13.useRef)(null);
@@ -3991,7 +4044,7 @@ var GanttChart = (0, import_react13.forwardRef)(({
3991
4044
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3992
4045
  TaskList,
3993
4046
  {
3994
- tasks,
4047
+ tasks: normalizedTasks,
3995
4048
  rowHeight,
3996
4049
  headerHeight,
3997
4050
  taskListWidth,
@@ -4046,7 +4099,7 @@ var GanttChart = (0, import_react13.forwardRef)(({
4046
4099
  DependencyLines_default,
4047
4100
  {
4048
4101
  tasks: filteredTasks,
4049
- allTasks: tasks,
4102
+ allTasks: normalizedTasks,
4050
4103
  collapsedParentIds,
4051
4104
  monthStart,
4052
4105
  dayWidth,
@@ -4084,7 +4137,7 @@ var GanttChart = (0, import_react13.forwardRef)(({
4084
4137
  }
4085
4138
  },
4086
4139
  rowIndex: index,
4087
- allTasks: tasks,
4140
+ allTasks: normalizedTasks,
4088
4141
  enableAutoSchedule: enableAutoSchedule ?? false,
4089
4142
  disableConstraints: disableConstraints ?? false,
4090
4143
  overridePosition: cascadeOverrides.get(task.id),
@@ -4151,6 +4204,7 @@ Button.displayName = "Button";
4151
4204
  detectCycles,
4152
4205
  detectEdgeZone,
4153
4206
  findParentId,
4207
+ flattenHierarchy,
4154
4208
  formatDateLabel,
4155
4209
  getAllDependencyEdges,
4156
4210
  getChildren,
@@ -4164,6 +4218,7 @@ Button.displayName = "Button";
4164
4218
  isTaskParent,
4165
4219
  isToday,
4166
4220
  isWeekend,
4221
+ normalizeHierarchyTasks,
4167
4222
  parseUTCDate,
4168
4223
  pixelsToDate,
4169
4224
  recalculateIncomingLags,