gantt-lib 0.123.0 → 0.124.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/core/scheduling/index.d.mts +1 -1
- package/dist/core/scheduling/index.d.ts +1 -1
- package/dist/core/scheduling/index.js +157 -0
- package/dist/core/scheduling/index.js.map +1 -1
- package/dist/core/scheduling/index.mjs +155 -0
- package/dist/core/scheduling/index.mjs.map +1 -1
- package/dist/{index-OZVb2o_D.d.mts → index-DD3YvhvA.d.mts} +38 -1
- package/dist/{index-OZVb2o_D.d.ts → index-DD3YvhvA.d.ts} +38 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +217 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +215 -18
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +32 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React$1, { ReactNode } from 'react';
|
|
2
|
-
import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as TimelineMarker, b as TaskDateChangeMode, c as ResourcePlannerChartProps, d as ResourceTimelineResource } from './index-
|
|
3
|
-
export { B as BuiltInResourceTableColumnId, D as DAY_MS, e as DependencyError, G as GanttChartMode, f as GanttDateRange, g as GridConfig, h as GridLine, L as LinkType, M as MonthSpan, i as ResourceTableColumnId, j as ResourceTableColumnWidthMap, k as ResourceTimelineMove, l as ResourceTimelineResourceMenuCommand, m as TaskBarGeometry, W as WeekendBlock, n as alignToWorkingDay, o as areTasksHierarchicallyRelated, p as buildAdjacencyList, q as buildTaskRangeFromEnd, r as buildTaskRangeFromStart, s as calculateSuccessorDate, t as cascadeByLinks, u as clampTaskRangeForIncomingFS, v as
|
|
2
|
+
import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as TimelineMarker, b as TaskDateChangeMode, c as ResourcePlannerChartProps, d as ResourceTimelineResource } from './index-DD3YvhvA.mjs';
|
|
3
|
+
export { B as BuiltInResourceTableColumnId, C as CriticalPathOptions, D as DAY_MS, e as DependencyError, G as GanttChartMode, f as GanttDateRange, g as GridConfig, h as GridLine, L as LinkType, M as MonthSpan, i as ResourceTableColumnId, j as ResourceTableColumnWidthMap, k as ResourceTimelineMove, l as ResourceTimelineResourceMenuCommand, m as TaskBarGeometry, W as WeekendBlock, n as alignToWorkingDay, o as areTasksHierarchicallyRelated, p as buildAdjacencyList, q as buildTaskRangeFromEnd, r as buildTaskRangeFromStart, s as calculateSuccessorDate, t as cascadeByLinks, u as clampTaskRangeForIncomingFS, v as computeCriticalPath, w as computeLagFromDates, x as computeParentDates, y as computeParentProgress, z as detectCycles, A as extendCriticalIdsToParents, E as findParentId, F as getAllDependencyEdges, H as getAllDescendants, I as getBusinessDayOffset, J as getChildren, K as getDependencyLag, N as getSuccessorChain, O as getTaskDuration, P as getTransitiveCascadeChain, Q as isAncestorTask, S as isTaskParent, U as moveTaskRange, X as moveTaskWithCascade, Y as normalizeDependencyLag, Z as normalizePredecessorDates, _ as normalizeTaskDependencyLags, $ as normalizeUTCDate, a0 as parseDateOnly, a1 as recalculateIncomingLags, a2 as recalculateProjectSchedule, a3 as recalculateTaskFromDependencies, a4 as reflowTasksOnModeSwitch, a5 as removeDependenciesBetweenTasks, a6 as resizeTaskWithCascade, a7 as shiftBusinessDayOffset, a8 as universalCascade, a9 as validateDependencies } from './index-DD3YvhvA.mjs';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import * as RadixPopover from '@radix-ui/react-popover';
|
|
6
6
|
|
|
@@ -648,6 +648,8 @@ interface GanttModeProps<TTask extends Task = Task> extends TaskChartSharedProps
|
|
|
648
648
|
isWeekend?: (date: Date) => boolean;
|
|
649
649
|
/** Считать duration в рабочих днях, исключая выходные (default: true) */
|
|
650
650
|
businessDays?: boolean;
|
|
651
|
+
/** Highlight critical path tasks (bars and dependency lines) or hide non-critical tasks (default: off) */
|
|
652
|
+
criticalPathMode?: 'highlight' | 'hide';
|
|
651
653
|
}
|
|
652
654
|
interface TableMatrixModeProps<TTask extends Task = Task> extends TaskChartSharedProps<TTask> {
|
|
653
655
|
mode: 'table-matrix';
|
|
@@ -782,6 +784,8 @@ interface TaskRowProps {
|
|
|
782
784
|
showBaseline?: boolean;
|
|
783
785
|
/** Whether this row matches the active filter highlight */
|
|
784
786
|
isFilterMatch?: boolean;
|
|
787
|
+
/** Whether this task lies on the critical path */
|
|
788
|
+
isCritical?: boolean;
|
|
785
789
|
/** Calculate duration in business days (excluding weekends) */
|
|
786
790
|
businessDays?: boolean;
|
|
787
791
|
/** Custom weekend configuration */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React$1, { ReactNode } from 'react';
|
|
2
|
-
import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as TimelineMarker, b as TaskDateChangeMode, c as ResourcePlannerChartProps, d as ResourceTimelineResource } from './index-
|
|
3
|
-
export { B as BuiltInResourceTableColumnId, D as DAY_MS, e as DependencyError, G as GanttChartMode, f as GanttDateRange, g as GridConfig, h as GridLine, L as LinkType, M as MonthSpan, i as ResourceTableColumnId, j as ResourceTableColumnWidthMap, k as ResourceTimelineMove, l as ResourceTimelineResourceMenuCommand, m as TaskBarGeometry, W as WeekendBlock, n as alignToWorkingDay, o as areTasksHierarchicallyRelated, p as buildAdjacencyList, q as buildTaskRangeFromEnd, r as buildTaskRangeFromStart, s as calculateSuccessorDate, t as cascadeByLinks, u as clampTaskRangeForIncomingFS, v as
|
|
2
|
+
import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as TimelineMarker, b as TaskDateChangeMode, c as ResourcePlannerChartProps, d as ResourceTimelineResource } from './index-DD3YvhvA.js';
|
|
3
|
+
export { B as BuiltInResourceTableColumnId, C as CriticalPathOptions, D as DAY_MS, e as DependencyError, G as GanttChartMode, f as GanttDateRange, g as GridConfig, h as GridLine, L as LinkType, M as MonthSpan, i as ResourceTableColumnId, j as ResourceTableColumnWidthMap, k as ResourceTimelineMove, l as ResourceTimelineResourceMenuCommand, m as TaskBarGeometry, W as WeekendBlock, n as alignToWorkingDay, o as areTasksHierarchicallyRelated, p as buildAdjacencyList, q as buildTaskRangeFromEnd, r as buildTaskRangeFromStart, s as calculateSuccessorDate, t as cascadeByLinks, u as clampTaskRangeForIncomingFS, v as computeCriticalPath, w as computeLagFromDates, x as computeParentDates, y as computeParentProgress, z as detectCycles, A as extendCriticalIdsToParents, E as findParentId, F as getAllDependencyEdges, H as getAllDescendants, I as getBusinessDayOffset, J as getChildren, K as getDependencyLag, N as getSuccessorChain, O as getTaskDuration, P as getTransitiveCascadeChain, Q as isAncestorTask, S as isTaskParent, U as moveTaskRange, X as moveTaskWithCascade, Y as normalizeDependencyLag, Z as normalizePredecessorDates, _ as normalizeTaskDependencyLags, $ as normalizeUTCDate, a0 as parseDateOnly, a1 as recalculateIncomingLags, a2 as recalculateProjectSchedule, a3 as recalculateTaskFromDependencies, a4 as reflowTasksOnModeSwitch, a5 as removeDependenciesBetweenTasks, a6 as resizeTaskWithCascade, a7 as shiftBusinessDayOffset, a8 as universalCascade, a9 as validateDependencies } from './index-DD3YvhvA.js';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import * as RadixPopover from '@radix-ui/react-popover';
|
|
6
6
|
|
|
@@ -648,6 +648,8 @@ interface GanttModeProps<TTask extends Task = Task> extends TaskChartSharedProps
|
|
|
648
648
|
isWeekend?: (date: Date) => boolean;
|
|
649
649
|
/** Считать duration в рабочих днях, исключая выходные (default: true) */
|
|
650
650
|
businessDays?: boolean;
|
|
651
|
+
/** Highlight critical path tasks (bars and dependency lines) or hide non-critical tasks (default: off) */
|
|
652
|
+
criticalPathMode?: 'highlight' | 'hide';
|
|
651
653
|
}
|
|
652
654
|
interface TableMatrixModeProps<TTask extends Task = Task> extends TaskChartSharedProps<TTask> {
|
|
653
655
|
mode: 'table-matrix';
|
|
@@ -782,6 +784,8 @@ interface TaskRowProps {
|
|
|
782
784
|
showBaseline?: boolean;
|
|
783
785
|
/** Whether this row matches the active filter highlight */
|
|
784
786
|
isFilterMatch?: boolean;
|
|
787
|
+
/** Whether this task lies on the critical path */
|
|
788
|
+
isCritical?: boolean;
|
|
785
789
|
/** Calculate duration in business days (excluding weekends) */
|
|
786
790
|
businessDays?: boolean;
|
|
787
791
|
/** Custom weekend configuration */
|
package/dist/index.js
CHANGED
|
@@ -73,6 +73,7 @@ __export(index_exports, {
|
|
|
73
73
|
cascadeByLinks: () => cascadeByLinks,
|
|
74
74
|
clampDateRangeForIncomingFS: () => clampDateRangeForIncomingFS,
|
|
75
75
|
clampTaskRangeForIncomingFS: () => clampTaskRangeForIncomingFS,
|
|
76
|
+
computeCriticalPath: () => computeCriticalPath,
|
|
76
77
|
computeLagFromDates: () => computeLagFromDates,
|
|
77
78
|
computeParentDates: () => computeParentDates,
|
|
78
79
|
computeParentProgress: () => computeParentProgress,
|
|
@@ -81,6 +82,7 @@ __export(index_exports, {
|
|
|
81
82
|
detectCycles: () => detectCycles,
|
|
82
83
|
detectEdgeZone: () => detectEdgeZone,
|
|
83
84
|
expired: () => expired,
|
|
85
|
+
extendCriticalIdsToParents: () => extendCriticalIdsToParents,
|
|
84
86
|
findParentId: () => findParentId,
|
|
85
87
|
flattenHierarchy: () => flattenHierarchy,
|
|
86
88
|
formatDateLabel: () => formatDateLabel,
|
|
@@ -693,6 +695,159 @@ function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag
|
|
|
693
695
|
return shiftBusinessDayOffset(anchorDate, offset, weekendPredicate);
|
|
694
696
|
}
|
|
695
697
|
|
|
698
|
+
// src/core/scheduling/criticalPath.ts
|
|
699
|
+
function computeCriticalPath(tasks, options = {}) {
|
|
700
|
+
const { businessDays = false, weekendPredicate } = options;
|
|
701
|
+
const taskById = new Map(tasks.map((task) => [task.id, task]));
|
|
702
|
+
const childIds = /* @__PURE__ */ new Set();
|
|
703
|
+
for (const task of tasks) {
|
|
704
|
+
if (task.parentId) childIds.add(task.parentId);
|
|
705
|
+
}
|
|
706
|
+
const leafTasks = tasks.filter((task) => !childIds.has(task.id));
|
|
707
|
+
const leafIds = new Set(leafTasks.map((task) => task.id));
|
|
708
|
+
const leafIdsByAncestor = /* @__PURE__ */ new Map();
|
|
709
|
+
for (const leaf of leafTasks) {
|
|
710
|
+
let ancestor = leaf.parentId;
|
|
711
|
+
while (ancestor) {
|
|
712
|
+
const set = leafIdsByAncestor.get(ancestor) ?? /* @__PURE__ */ new Set();
|
|
713
|
+
set.add(leaf.id);
|
|
714
|
+
leafIdsByAncestor.set(ancestor, set);
|
|
715
|
+
ancestor = taskById.get(ancestor)?.parentId;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const durationOf = (task) => getTaskDuration(task.startDate, task.endDate, businessDays, weekendPredicate);
|
|
719
|
+
const projectStartDate = leafTasks.reduce((earliest, task) => {
|
|
720
|
+
const taskStart = parseDateOnly(task.startDate);
|
|
721
|
+
return taskStart.getTime() < earliest.getTime() ? taskStart : earliest;
|
|
722
|
+
}, parseDateOnly(leafTasks[0]?.startDate ?? /* @__PURE__ */ new Date()));
|
|
723
|
+
const offsetFromProjectStart = (date) => {
|
|
724
|
+
const targetDate = parseDateOnly(date);
|
|
725
|
+
if (businessDays && weekendPredicate) {
|
|
726
|
+
return getBusinessDayOffset(projectStartDate, targetDate, weekendPredicate);
|
|
727
|
+
}
|
|
728
|
+
return Math.round((targetDate.getTime() - projectStartDate.getTime()) / DAY_MS);
|
|
729
|
+
};
|
|
730
|
+
const scheduledStart = /* @__PURE__ */ new Map();
|
|
731
|
+
for (const leaf of leafTasks) {
|
|
732
|
+
scheduledStart.set(leaf.id, offsetFromProjectStart(leaf.startDate));
|
|
733
|
+
}
|
|
734
|
+
const succByPred = /* @__PURE__ */ new Map();
|
|
735
|
+
const predBySucc = /* @__PURE__ */ new Map();
|
|
736
|
+
const seenEdges = /* @__PURE__ */ new Set();
|
|
737
|
+
const addEdge = (predecessorId, succId, lag, type) => {
|
|
738
|
+
const edgeKey = `${predecessorId}|${succId}|${type}`;
|
|
739
|
+
if (seenEdges.has(edgeKey)) return;
|
|
740
|
+
seenEdges.add(edgeKey);
|
|
741
|
+
const outEdges = succByPred.get(predecessorId) ?? [];
|
|
742
|
+
outEdges.push({ succId, lag, type });
|
|
743
|
+
succByPred.set(predecessorId, outEdges);
|
|
744
|
+
const inEdges = predBySucc.get(succId) ?? [];
|
|
745
|
+
inEdges.push({ predId: predecessorId, lag, type });
|
|
746
|
+
predBySucc.set(succId, inEdges);
|
|
747
|
+
};
|
|
748
|
+
for (const succ of leafTasks) {
|
|
749
|
+
for (const dep of succ.dependencies ?? []) {
|
|
750
|
+
const lag = getDependencyLag(dep);
|
|
751
|
+
if (leafIds.has(dep.taskId)) {
|
|
752
|
+
addEdge(dep.taskId, succ.id, lag, dep.type);
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
const descendants = leafIdsByAncestor.get(dep.taskId);
|
|
756
|
+
if (!descendants || descendants.size === 0) continue;
|
|
757
|
+
for (const leafId of descendants) {
|
|
758
|
+
if (leafId === succ.id) continue;
|
|
759
|
+
addEdge(leafId, succ.id, lag, dep.type);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
const indegree = /* @__PURE__ */ new Map();
|
|
764
|
+
for (const leaf of leafTasks) indegree.set(leaf.id, 0);
|
|
765
|
+
for (const edges of succByPred.values()) {
|
|
766
|
+
for (const { succId } of edges) {
|
|
767
|
+
indegree.set(succId, (indegree.get(succId) ?? 0) + 1);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const order = [];
|
|
771
|
+
const enqueued = /* @__PURE__ */ new Set();
|
|
772
|
+
const queue = leafTasks.filter((leaf) => (indegree.get(leaf.id) ?? 0) === 0).map((leaf) => leaf.id);
|
|
773
|
+
for (const id of queue) enqueued.add(id);
|
|
774
|
+
while (queue.length > 0) {
|
|
775
|
+
const current = queue.shift();
|
|
776
|
+
order.push(current);
|
|
777
|
+
for (const { succId } of succByPred.get(current) ?? []) {
|
|
778
|
+
const next = (indegree.get(succId) ?? 0) - 1;
|
|
779
|
+
indegree.set(succId, next);
|
|
780
|
+
if (next === 0 && !enqueued.has(succId)) {
|
|
781
|
+
enqueued.add(succId);
|
|
782
|
+
queue.push(succId);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
const projectFinish = Math.max(
|
|
787
|
+
0,
|
|
788
|
+
...leafTasks.map((leaf) => (scheduledStart.get(leaf.id) ?? 0) + durationOf(leaf))
|
|
789
|
+
);
|
|
790
|
+
const LF = /* @__PURE__ */ new Map();
|
|
791
|
+
const LS = /* @__PURE__ */ new Map();
|
|
792
|
+
for (const leaf of leafTasks) LF.set(leaf.id, projectFinish);
|
|
793
|
+
for (let i = order.length - 1; i >= 0; i--) {
|
|
794
|
+
const id = order[i];
|
|
795
|
+
const task = taskById.get(id);
|
|
796
|
+
const durPred = durationOf(task);
|
|
797
|
+
const outEdges = succByPred.get(id) ?? [];
|
|
798
|
+
let lf = projectFinish;
|
|
799
|
+
if (outEdges.length > 0) {
|
|
800
|
+
const candidates = outEdges.map(({ succId, lag, type }) => {
|
|
801
|
+
const succLs = LS.get(succId) ?? projectFinish;
|
|
802
|
+
const durSucc = durationOf(taskById.get(succId));
|
|
803
|
+
switch (type) {
|
|
804
|
+
case "FS":
|
|
805
|
+
return succLs - lag;
|
|
806
|
+
case "SS":
|
|
807
|
+
return succLs - lag + durPred;
|
|
808
|
+
case "FF":
|
|
809
|
+
return succLs + durSucc - lag;
|
|
810
|
+
case "SF":
|
|
811
|
+
return succLs + durSucc - lag + durPred;
|
|
812
|
+
default:
|
|
813
|
+
return projectFinish;
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
lf = Math.min(projectFinish, ...candidates);
|
|
817
|
+
}
|
|
818
|
+
LF.set(id, lf);
|
|
819
|
+
LS.set(id, lf - durPred);
|
|
820
|
+
}
|
|
821
|
+
const critical = /* @__PURE__ */ new Set();
|
|
822
|
+
const ordered = new Set(order);
|
|
823
|
+
for (const leaf of leafTasks) {
|
|
824
|
+
if (!ordered.has(leaf.id)) continue;
|
|
825
|
+
const hasGraphEdge = (succByPred.get(leaf.id)?.length ?? 0) > 0 || (predBySucc.get(leaf.id)?.length ?? 0) > 0;
|
|
826
|
+
if (!hasGraphEdge && (leaf.dependencies?.length ?? 0) > 0) continue;
|
|
827
|
+
const es = scheduledStart.get(leaf.id) ?? 0;
|
|
828
|
+
const ls = LS.get(leaf.id) ?? 0;
|
|
829
|
+
if (ls - es <= 0) critical.add(leaf.id);
|
|
830
|
+
}
|
|
831
|
+
return critical;
|
|
832
|
+
}
|
|
833
|
+
function extendCriticalIdsToParents(criticalLeafIds, tasks) {
|
|
834
|
+
const result = new Set(criticalLeafIds);
|
|
835
|
+
const parentByChild = /* @__PURE__ */ new Map();
|
|
836
|
+
for (const task of tasks) {
|
|
837
|
+
if (task.parentId) parentByChild.set(task.id, task.parentId);
|
|
838
|
+
}
|
|
839
|
+
const stack = Array.from(criticalLeafIds);
|
|
840
|
+
while (stack.length > 0) {
|
|
841
|
+
const id = stack.pop();
|
|
842
|
+
const parentId = parentByChild.get(id);
|
|
843
|
+
if (parentId && !result.has(parentId)) {
|
|
844
|
+
result.add(parentId);
|
|
845
|
+
stack.push(parentId);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return result;
|
|
849
|
+
}
|
|
850
|
+
|
|
696
851
|
// src/core/scheduling/commands.ts
|
|
697
852
|
function buildTaskRangeFromStart(startDate, duration, businessDays = false, weekendPredicate, snapDirection = 1) {
|
|
698
853
|
const normalizedStart = businessDays && weekendPredicate ? alignToWorkingDay(startDate, snapDirection, weekendPredicate) : normalizeUTCDate(startDate);
|
|
@@ -2555,8 +2710,7 @@ function handleGlobalMouseMove(e) {
|
|
|
2555
2710
|
activeDrag.weekendPredicate,
|
|
2556
2711
|
activeDrag.cascadeContext
|
|
2557
2712
|
);
|
|
2558
|
-
const
|
|
2559
|
-
const previewTasks = hasTasksWithIncomingDependencies ? (() => {
|
|
2713
|
+
const previewTasks = (() => {
|
|
2560
2714
|
const mergedPreviewTaskById = new Map(activeDrag.cascadeContext?.taskById);
|
|
2561
2715
|
for (const task of cascadeResult) {
|
|
2562
2716
|
mergedPreviewTaskById.set(task.id, task);
|
|
@@ -2577,7 +2731,7 @@ function handleGlobalMouseMove(e) {
|
|
|
2577
2731
|
) : task.dependencies
|
|
2578
2732
|
};
|
|
2579
2733
|
});
|
|
2580
|
-
})()
|
|
2734
|
+
})();
|
|
2581
2735
|
const overrides = /* @__PURE__ */ new Map();
|
|
2582
2736
|
overrides.set(dragId, { left: newLeft, width: newWidth });
|
|
2583
2737
|
for (const task of cascadeResult) {
|
|
@@ -2980,10 +3134,10 @@ var useTaskDrag = (options) => {
|
|
|
2980
3134
|
// src/components/TaskRow/TaskRow.tsx
|
|
2981
3135
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2982
3136
|
var arePropsEqual = (prevProps, nextProps) => {
|
|
2983
|
-
return prevProps.task.id === nextProps.task.id && prevProps.task.name === nextProps.task.name && prevProps.task.startDate === nextProps.task.startDate && prevProps.task.endDate === nextProps.task.endDate && prevProps.task.baselineStartDate === nextProps.task.baselineStartDate && prevProps.task.baselineEndDate === nextProps.task.baselineEndDate && prevProps.task.type === nextProps.task.type && prevProps.task.color === nextProps.task.color && prevProps.task.progress === nextProps.task.progress && prevProps.task.accepted === nextProps.task.accepted && prevProps.monthStart.getTime() === nextProps.monthStart.getTime() && prevProps.dayWidth === nextProps.dayWidth && prevProps.rowHeight === nextProps.rowHeight && prevProps.overridePosition?.left === nextProps.overridePosition?.left && prevProps.overridePosition?.width === nextProps.overridePosition?.width && prevProps.previewPositionStore === nextProps.previewPositionStore && prevProps.allTasks === nextProps.allTasks && prevProps.disableConstraints === nextProps.disableConstraints && prevProps.task.locked === nextProps.task.locked && prevProps.task.synced === nextProps.task.synced && prevProps.task.divider === nextProps.task.divider && prevProps.highlightExpiredTasks === nextProps.highlightExpiredTasks && prevProps.showBaseline === nextProps.showBaseline && prevProps.isFilterMatch === nextProps.isFilterMatch && prevProps.businessDays === nextProps.businessDays && prevProps.customDays === nextProps.customDays && prevProps.isWeekend === nextProps.isWeekend && prevProps.disableTaskDrag === nextProps.disableTaskDrag && prevProps.disableDependencyEditing === nextProps.disableDependencyEditing && prevProps.isDependencyDragActive === nextProps.isDependencyDragActive && prevProps.viewMode === nextProps.viewMode;
|
|
3137
|
+
return prevProps.task.id === nextProps.task.id && prevProps.task.name === nextProps.task.name && prevProps.task.startDate === nextProps.task.startDate && prevProps.task.endDate === nextProps.task.endDate && prevProps.task.baselineStartDate === nextProps.task.baselineStartDate && prevProps.task.baselineEndDate === nextProps.task.baselineEndDate && prevProps.task.type === nextProps.task.type && prevProps.task.color === nextProps.task.color && prevProps.task.progress === nextProps.task.progress && prevProps.task.accepted === nextProps.task.accepted && prevProps.monthStart.getTime() === nextProps.monthStart.getTime() && prevProps.dayWidth === nextProps.dayWidth && prevProps.rowHeight === nextProps.rowHeight && prevProps.overridePosition?.left === nextProps.overridePosition?.left && prevProps.overridePosition?.width === nextProps.overridePosition?.width && prevProps.previewPositionStore === nextProps.previewPositionStore && prevProps.allTasks === nextProps.allTasks && prevProps.disableConstraints === nextProps.disableConstraints && prevProps.task.locked === nextProps.task.locked && prevProps.task.synced === nextProps.task.synced && prevProps.task.divider === nextProps.task.divider && prevProps.highlightExpiredTasks === nextProps.highlightExpiredTasks && prevProps.isCritical === nextProps.isCritical && prevProps.showBaseline === nextProps.showBaseline && prevProps.isFilterMatch === nextProps.isFilterMatch && prevProps.businessDays === nextProps.businessDays && prevProps.customDays === nextProps.customDays && prevProps.isWeekend === nextProps.isWeekend && prevProps.disableTaskDrag === nextProps.disableTaskDrag && prevProps.disableDependencyEditing === nextProps.disableDependencyEditing && prevProps.isDependencyDragActive === nextProps.isDependencyDragActive && prevProps.viewMode === nextProps.viewMode;
|
|
2984
3138
|
};
|
|
2985
3139
|
var TaskRow = import_react3.default.memo(
|
|
2986
|
-
({ task, monthStart, dayWidth, rowHeight, onTasksChange, onDragStateChange, rowIndex, allTasks, enableAutoSchedule, disableConstraints, overridePosition, previewPositionStore, onCascadeProgress, onCascade, divider, highlightExpiredTasks, showBaseline = false, isFilterMatch = false, businessDays, customDays, isWeekend: isWeekend3, disableTaskDrag = false, disableDependencyEditing = false, onDependencyPortPointerDown, isDependencyDragActive = false, viewMode = "day" }) => {
|
|
3140
|
+
({ task, monthStart, dayWidth, rowHeight, onTasksChange, onDragStateChange, rowIndex, allTasks, enableAutoSchedule, disableConstraints, overridePosition, previewPositionStore, onCascadeProgress, onCascade, divider, highlightExpiredTasks, isCritical = false, showBaseline = false, isFilterMatch = false, businessDays, customDays, isWeekend: isWeekend3, disableTaskDrag = false, disableDependencyEditing = false, onDependencyPortPointerDown, isDependencyDragActive = false, viewMode = "day" }) => {
|
|
2987
3141
|
const defaultParentBarColor = "#782FC4";
|
|
2988
3142
|
const { divider: taskDivider } = task;
|
|
2989
3143
|
const normalizedTask = (0, import_react3.useMemo)(() => normalizeTaskDatesForType(task), [task]);
|
|
@@ -3173,7 +3327,7 @@ var TaskRow = import_react3.default.memo(
|
|
|
3173
3327
|
"div",
|
|
3174
3328
|
{
|
|
3175
3329
|
"data-taskbar": true,
|
|
3176
|
-
className: `gantt-tr-taskBar ${isDragging ? "gantt-tr-dragging" : ""} ${task.locked ? "gantt-tr-locked" : ""} ${task.synced === false ? "gantt-tr-unsynced" : ""} ${isParent ? "gantt-tr-parentBar" : ""} ${milestone ? "gantt-tr-milestone" : ""}`,
|
|
3330
|
+
className: `gantt-tr-taskBar ${isDragging ? "gantt-tr-dragging" : ""} ${task.locked ? "gantt-tr-locked" : ""} ${task.synced === false ? "gantt-tr-unsynced" : ""} ${isParent ? "gantt-tr-parentBar" : ""} ${milestone ? "gantt-tr-milestone" : ""} ${isCritical ? "gantt-tr-critical" : ""}`,
|
|
3177
3331
|
style: {
|
|
3178
3332
|
left: `${visualLeft}px`,
|
|
3179
3333
|
...barStyle,
|
|
@@ -3588,6 +3742,7 @@ var DependencyLines = import_react7.default.memo(({
|
|
|
3588
3742
|
rowIndexByTaskId,
|
|
3589
3743
|
dragOverrides,
|
|
3590
3744
|
selectedDep,
|
|
3745
|
+
criticalTaskIds,
|
|
3591
3746
|
businessDays = true,
|
|
3592
3747
|
weekendPredicate,
|
|
3593
3748
|
onDependencyClick
|
|
@@ -3726,6 +3881,9 @@ var DependencyLines = import_react7.default.memo(({
|
|
|
3726
3881
|
const to = { x: finalToX, y: toY };
|
|
3727
3882
|
const path = calculateDependencyPath(from, to, arrivesFromRight);
|
|
3728
3883
|
const hasCycle = cycleInfo.has(edge.predecessorId) || cycleInfo.has(edge.successorId);
|
|
3884
|
+
const isCritical = Boolean(
|
|
3885
|
+
criticalTaskIds?.has(edge.predecessorId) && criticalTaskIds.has(edge.successorId)
|
|
3886
|
+
);
|
|
3729
3887
|
lines2.push({
|
|
3730
3888
|
id: `${edge.predecessorId}-${edge.successorId}-${edge.type}`,
|
|
3731
3889
|
predecessorId: edge.predecessorId,
|
|
@@ -3738,12 +3896,13 @@ var DependencyLines = import_react7.default.memo(({
|
|
|
3738
3896
|
toX: finalToX,
|
|
3739
3897
|
fromY,
|
|
3740
3898
|
reverseOrder,
|
|
3741
|
-
isVirtual
|
|
3899
|
+
isVirtual,
|
|
3900
|
+
isCritical
|
|
3742
3901
|
});
|
|
3743
3902
|
}
|
|
3744
3903
|
}
|
|
3745
3904
|
return lines2;
|
|
3746
|
-
}, [tasks, allTasks, taskPositions, taskIndices, cycleInfo, collapsedParentIds]);
|
|
3905
|
+
}, [tasks, allTasks, taskPositions, taskIndices, cycleInfo, collapsedParentIds, criticalTaskIds]);
|
|
3747
3906
|
const svgHeight = totalHeight ?? tasks.length * rowHeight;
|
|
3748
3907
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3749
3908
|
"svg",
|
|
@@ -3830,20 +3989,41 @@ var DependencyLines = import_react7.default.memo(({
|
|
|
3830
3989
|
}
|
|
3831
3990
|
)
|
|
3832
3991
|
}
|
|
3992
|
+
),
|
|
3993
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3994
|
+
"marker",
|
|
3995
|
+
{
|
|
3996
|
+
id: "arrowhead-critical",
|
|
3997
|
+
markerWidth: "8",
|
|
3998
|
+
markerHeight: "6",
|
|
3999
|
+
markerUnits: "userSpaceOnUse",
|
|
4000
|
+
refX: "7",
|
|
4001
|
+
refY: "3",
|
|
4002
|
+
orient: "auto",
|
|
4003
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4004
|
+
"polygon",
|
|
4005
|
+
{
|
|
4006
|
+
points: "0 0, 8 3, 0 6",
|
|
4007
|
+
fill: "var(--gantt-critical-path-color, #dc2626)"
|
|
4008
|
+
}
|
|
4009
|
+
)
|
|
4010
|
+
}
|
|
3833
4011
|
)
|
|
3834
4012
|
] }),
|
|
3835
|
-
lines.map(({ id, predecessorId, successorId, linkType, path, hasCycle, lag, fromX, toX, fromY, reverseOrder, isVirtual }) => {
|
|
4013
|
+
lines.map(({ id, predecessorId, successorId, linkType, path, hasCycle, lag, fromX, toX, fromY, reverseOrder, isVirtual, isCritical }) => {
|
|
3836
4014
|
const isHovered = hoveredLineId === id;
|
|
3837
4015
|
const isSelected = selectedDep != null && id === `${selectedDep.predecessorId}-${selectedDep.successorId}-${selectedDep.linkType}`;
|
|
3838
4016
|
let pathClassName = "gantt-dependency-path";
|
|
3839
4017
|
if (isSelected) pathClassName += " gantt-dependency-selected";
|
|
3840
4018
|
else if (hasCycle) pathClassName += " gantt-dependency-cycle";
|
|
4019
|
+
else if (isCritical) pathClassName += " gantt-dependency-critical";
|
|
3841
4020
|
if (isVirtual && !isSelected) pathClassName += " gantt-dependency-virtual";
|
|
3842
4021
|
let markerEnd;
|
|
3843
4022
|
if (isSelected) markerEnd = "url(#arrowhead-selected)";
|
|
3844
4023
|
else if (hasCycle) markerEnd = "url(#arrowhead-cycle)";
|
|
4024
|
+
else if (isCritical) markerEnd = "url(#arrowhead-critical)";
|
|
3845
4025
|
else markerEnd = "url(#arrowhead)";
|
|
3846
|
-
const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : "var(--gantt-dependency-line-color, #666666)";
|
|
4026
|
+
const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : isCritical ? "var(--gantt-critical-path-color, #dc2626)" : "var(--gantt-dependency-line-color, #666666)";
|
|
3847
4027
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
|
|
3848
4028
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3849
4029
|
"g",
|
|
@@ -12551,6 +12731,7 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12551
12731
|
onTaskDateChangeModeChange: externalOnTaskDateChangeModeChange
|
|
12552
12732
|
} = props;
|
|
12553
12733
|
const dayWidth = !isTableMatrixMode ? props.dayWidth ?? 40 : 40;
|
|
12734
|
+
const criticalPathMode = !isTableMatrixMode && !isPlanFactMode ? props.criticalPathMode : void 0;
|
|
12554
12735
|
const viewMode = !isTableMatrixMode && !isPlanFactMode ? props.viewMode ?? "day" : "day";
|
|
12555
12736
|
const customDays = !isTableMatrixMode ? props.customDays : void 0;
|
|
12556
12737
|
const isWeekend3 = !isTableMatrixMode ? props.isWeekend : void 0;
|
|
@@ -12750,10 +12931,23 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12750
12931
|
[resolvedRowContentLines, rowHeight]
|
|
12751
12932
|
);
|
|
12752
12933
|
const normalizedTasks = (0, import_react18.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
|
|
12934
|
+
const [previewTasksById, setPreviewTasksById] = (0, import_react18.useState)(/* @__PURE__ */ new Map());
|
|
12935
|
+
const previewNormalizedTasks = (0, import_react18.useMemo)(() => {
|
|
12936
|
+
if (previewTasksById.size === 0) return normalizedTasks;
|
|
12937
|
+
return normalizedTasks.map((task) => previewTasksById.get(task.id) ?? task);
|
|
12938
|
+
}, [normalizedTasks, previewTasksById]);
|
|
12753
12939
|
const isCustomWeekend = (0, import_react18.useMemo)(
|
|
12754
12940
|
() => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
|
|
12755
12941
|
[customDays, isWeekend3]
|
|
12756
12942
|
);
|
|
12943
|
+
const criticalTaskIds = (0, import_react18.useMemo)(() => {
|
|
12944
|
+
if (!criticalPathMode) return /* @__PURE__ */ new Set();
|
|
12945
|
+
const leafCritical = computeCriticalPath(previewNormalizedTasks, {
|
|
12946
|
+
businessDays,
|
|
12947
|
+
weekendPredicate: isCustomWeekend
|
|
12948
|
+
});
|
|
12949
|
+
return extendCriticalIdsToParents(leafCritical, previewNormalizedTasks);
|
|
12950
|
+
}, [criticalPathMode, previewNormalizedTasks, businessDays, isCustomWeekend]);
|
|
12757
12951
|
const dateRangeTasks = (0, import_react18.useMemo)(() => {
|
|
12758
12952
|
if (isPlanFactMode) {
|
|
12759
12953
|
return getPlanFactRangeTasks(normalizedTasks);
|
|
@@ -12798,8 +12992,11 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12798
12992
|
if (filterMode === "hide" && taskFilter) {
|
|
12799
12993
|
tasks2 = tasks2.filter(taskFilter);
|
|
12800
12994
|
}
|
|
12995
|
+
if (criticalPathMode === "hide") {
|
|
12996
|
+
tasks2 = tasks2.filter((task) => criticalTaskIds.has(task.id));
|
|
12997
|
+
}
|
|
12801
12998
|
return tasks2;
|
|
12802
|
-
}, [normalizedTasks, collapsedParentIds, filterMode, taskFilter]);
|
|
12999
|
+
}, [normalizedTasks, collapsedParentIds, filterMode, taskFilter, criticalPathMode, criticalTaskIds]);
|
|
12803
13000
|
const matchedTaskIds = (0, import_react18.useMemo)(() => {
|
|
12804
13001
|
if (!taskFilter) return /* @__PURE__ */ new Set();
|
|
12805
13002
|
return new Set(visibleTasks.filter(taskFilter).map((task) => task.id));
|
|
@@ -12992,7 +13189,6 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12992
13189
|
}, [effectiveRowHeight, tasks, visibleTasks]);
|
|
12993
13190
|
const [dragGuideLines, setDragGuideLines] = (0, import_react18.useState)(null);
|
|
12994
13191
|
const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react18.useState)(null);
|
|
12995
|
-
const [previewTasksById, setPreviewTasksById] = (0, import_react18.useState)(/* @__PURE__ */ new Map());
|
|
12996
13192
|
(0, import_react18.useEffect)(() => {
|
|
12997
13193
|
const result = validateDependencies(tasks);
|
|
12998
13194
|
setValidationResult(result);
|
|
@@ -13120,14 +13316,10 @@ function TaskGanttChartInner(props, ref) {
|
|
|
13120
13316
|
}
|
|
13121
13317
|
setCascadeOverrides((current) => arePositionMapsEqual(current, renderedOverrides) ? current : renderedOverrides);
|
|
13122
13318
|
setPreviewTasksById((current) => {
|
|
13123
|
-
const next = previewTasks.length > 0 ? new Map(previewTasks.
|
|
13319
|
+
const next = previewTasks.length > 0 ? new Map(previewTasks.map((task) => [task.id, task])) : /* @__PURE__ */ new Map();
|
|
13124
13320
|
return arePreviewTaskMapsEqual(current, next) ? current : next;
|
|
13125
13321
|
});
|
|
13126
13322
|
}, [previewPositionStore]);
|
|
13127
|
-
const previewNormalizedTasks = (0, import_react18.useMemo)(() => {
|
|
13128
|
-
if (previewTasksById.size === 0) return normalizedTasks;
|
|
13129
|
-
return normalizedTasks.map((task) => previewTasksById.get(task.id) ?? task);
|
|
13130
|
-
}, [normalizedTasks, previewTasksById]);
|
|
13131
13323
|
const previewVisibleTasks = (0, import_react18.useMemo)(() => {
|
|
13132
13324
|
if (previewTasksById.size === 0) return visibleTasks;
|
|
13133
13325
|
return visibleTasks.map((task) => previewTasksById.get(task.id) ?? task);
|
|
@@ -13727,7 +13919,8 @@ function TaskGanttChartInner(props, ref) {
|
|
|
13727
13919
|
selectedDep: selectedChip,
|
|
13728
13920
|
businessDays,
|
|
13729
13921
|
weekendPredicate: isCustomWeekend,
|
|
13730
|
-
onDependencyClick: handleDependencyLineClick
|
|
13922
|
+
onDependencyClick: handleDependencyLineClick,
|
|
13923
|
+
criticalTaskIds: criticalPathMode ? criticalTaskIds : void 0
|
|
13731
13924
|
}
|
|
13732
13925
|
),
|
|
13733
13926
|
dependencyLineMenu && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
@@ -13797,6 +13990,9 @@ function TaskGanttChartInner(props, ref) {
|
|
|
13797
13990
|
} else {
|
|
13798
13991
|
setDragGuideLines((current) => current === null ? current : null);
|
|
13799
13992
|
setDraggedTaskOverride((current) => current === null ? current : null);
|
|
13993
|
+
previewPositionStore.clear();
|
|
13994
|
+
setCascadeOverrides((current) => current.size === 0 ? current : /* @__PURE__ */ new Map());
|
|
13995
|
+
setPreviewTasksById((current) => current.size === 0 ? current : /* @__PURE__ */ new Map());
|
|
13800
13996
|
}
|
|
13801
13997
|
},
|
|
13802
13998
|
rowIndex: index,
|
|
@@ -13809,6 +14005,7 @@ function TaskGanttChartInner(props, ref) {
|
|
|
13809
14005
|
highlightExpiredTasks,
|
|
13810
14006
|
showBaseline,
|
|
13811
14007
|
isFilterMatch: filterMode === "highlight" ? matchedTaskIds.has(task.id) : false,
|
|
14008
|
+
isCritical: criticalTaskIds.has(task.id),
|
|
13812
14009
|
businessDays,
|
|
13813
14010
|
customDays,
|
|
13814
14011
|
isWeekend: isWeekend3,
|
|
@@ -13931,6 +14128,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
|
|
|
13931
14128
|
cascadeByLinks,
|
|
13932
14129
|
clampDateRangeForIncomingFS,
|
|
13933
14130
|
clampTaskRangeForIncomingFS,
|
|
14131
|
+
computeCriticalPath,
|
|
13934
14132
|
computeLagFromDates,
|
|
13935
14133
|
computeParentDates,
|
|
13936
14134
|
computeParentProgress,
|
|
@@ -13939,6 +14137,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
|
|
|
13939
14137
|
detectCycles,
|
|
13940
14138
|
detectEdgeZone,
|
|
13941
14139
|
expired,
|
|
14140
|
+
extendCriticalIdsToParents,
|
|
13942
14141
|
findParentId,
|
|
13943
14142
|
flattenHierarchy,
|
|
13944
14143
|
formatDateLabel,
|