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/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-OZVb2o_D.mjs';
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 computeLagFromDates, w as computeParentDates, x as computeParentProgress, y as detectCycles, z as findParentId, A as getAllDependencyEdges, C as getAllDescendants, E as getBusinessDayOffset, F as getChildren, H as getDependencyLag, I as getSuccessorChain, J as getTaskDuration, K as getTransitiveCascadeChain, N as isAncestorTask, O as isTaskParent, P as moveTaskRange, Q as moveTaskWithCascade, S as normalizeDependencyLag, U as normalizePredecessorDates, X as normalizeTaskDependencyLags, Y as normalizeUTCDate, Z as parseDateOnly, _ as recalculateIncomingLags, $ as recalculateProjectSchedule, a0 as recalculateTaskFromDependencies, a1 as reflowTasksOnModeSwitch, a2 as removeDependenciesBetweenTasks, a3 as resizeTaskWithCascade, a4 as shiftBusinessDayOffset, a5 as universalCascade, a6 as validateDependencies } from './index-OZVb2o_D.mjs';
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-OZVb2o_D.js';
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 computeLagFromDates, w as computeParentDates, x as computeParentProgress, y as detectCycles, z as findParentId, A as getAllDependencyEdges, C as getAllDescendants, E as getBusinessDayOffset, F as getChildren, H as getDependencyLag, I as getSuccessorChain, J as getTaskDuration, K as getTransitiveCascadeChain, N as isAncestorTask, O as isTaskParent, P as moveTaskRange, Q as moveTaskWithCascade, S as normalizeDependencyLag, U as normalizePredecessorDates, X as normalizeTaskDependencyLags, Y as normalizeUTCDate, Z as parseDateOnly, _ as recalculateIncomingLags, $ as recalculateProjectSchedule, a0 as recalculateTaskFromDependencies, a1 as reflowTasksOnModeSwitch, a2 as removeDependenciesBetweenTasks, a3 as resizeTaskWithCascade, a4 as shiftBusinessDayOffset, a5 as universalCascade, a6 as validateDependencies } from './index-OZVb2o_D.js';
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 hasTasksWithIncomingDependencies = cascadeResult.some((task) => task.dependencies?.length);
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.filter((task) => renderedTaskIds.has(task.id)).map((task) => [task.id, task])) : /* @__PURE__ */ new Map();
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,