gantt-lib 0.122.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
 
@@ -572,6 +572,8 @@ interface TaskChartSharedProps<TTask extends Task = Task> {
572
572
  onUngroupTask?: (taskId: string) => void;
573
573
  /** Enable add task button at bottom of task list (default: true) */
574
574
  enableAddTask?: boolean;
575
+ /** Label text for the add task button (default: 'Добавить работу') */
576
+ addTaskLabel?: string;
575
577
  /** Default duration for newly created tasks, interpreted in the active day mode (default: 5). */
576
578
  defaultTaskDurationDays?: number;
577
579
  /** View mode: 'day' renders one column per day, 'week' renders one column per 7 days, 'month' renders one column per month (default: 'day') */
@@ -646,6 +648,8 @@ interface GanttModeProps<TTask extends Task = Task> extends TaskChartSharedProps
646
648
  isWeekend?: (date: Date) => boolean;
647
649
  /** Считать duration в рабочих днях, исключая выходные (default: true) */
648
650
  businessDays?: boolean;
651
+ /** Highlight critical path tasks (bars and dependency lines) or hide non-critical tasks (default: off) */
652
+ criticalPathMode?: 'highlight' | 'hide';
649
653
  }
650
654
  interface TableMatrixModeProps<TTask extends Task = Task> extends TaskChartSharedProps<TTask> {
651
655
  mode: 'table-matrix';
@@ -780,6 +784,8 @@ interface TaskRowProps {
780
784
  showBaseline?: boolean;
781
785
  /** Whether this row matches the active filter highlight */
782
786
  isFilterMatch?: boolean;
787
+ /** Whether this task lies on the critical path */
788
+ isCritical?: boolean;
783
789
  /** Calculate duration in business days (excluding weekends) */
784
790
  businessDays?: boolean;
785
791
  /** Custom weekend configuration */
@@ -962,6 +968,8 @@ interface TaskListProps {
962
968
  editingTaskId?: string | null;
963
969
  /** Enable add task button at bottom of task list (default: true) */
964
970
  enableAddTask?: boolean;
971
+ /** Label text for the add task button (default: 'Добавить работу') */
972
+ addTaskLabel?: string;
965
973
  /** Default duration for newly created tasks, interpreted in the active day mode (default: 5). */
966
974
  defaultTaskDurationDays?: number;
967
975
  /** Set of collapsed parent task IDs */
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
 
@@ -572,6 +572,8 @@ interface TaskChartSharedProps<TTask extends Task = Task> {
572
572
  onUngroupTask?: (taskId: string) => void;
573
573
  /** Enable add task button at bottom of task list (default: true) */
574
574
  enableAddTask?: boolean;
575
+ /** Label text for the add task button (default: 'Добавить работу') */
576
+ addTaskLabel?: string;
575
577
  /** Default duration for newly created tasks, interpreted in the active day mode (default: 5). */
576
578
  defaultTaskDurationDays?: number;
577
579
  /** View mode: 'day' renders one column per day, 'week' renders one column per 7 days, 'month' renders one column per month (default: 'day') */
@@ -646,6 +648,8 @@ interface GanttModeProps<TTask extends Task = Task> extends TaskChartSharedProps
646
648
  isWeekend?: (date: Date) => boolean;
647
649
  /** Считать duration в рабочих днях, исключая выходные (default: true) */
648
650
  businessDays?: boolean;
651
+ /** Highlight critical path tasks (bars and dependency lines) or hide non-critical tasks (default: off) */
652
+ criticalPathMode?: 'highlight' | 'hide';
649
653
  }
650
654
  interface TableMatrixModeProps<TTask extends Task = Task> extends TaskChartSharedProps<TTask> {
651
655
  mode: 'table-matrix';
@@ -780,6 +784,8 @@ interface TaskRowProps {
780
784
  showBaseline?: boolean;
781
785
  /** Whether this row matches the active filter highlight */
782
786
  isFilterMatch?: boolean;
787
+ /** Whether this task lies on the critical path */
788
+ isCritical?: boolean;
783
789
  /** Calculate duration in business days (excluding weekends) */
784
790
  businessDays?: boolean;
785
791
  /** Custom weekend configuration */
@@ -962,6 +968,8 @@ interface TaskListProps {
962
968
  editingTaskId?: string | null;
963
969
  /** Enable add task button at bottom of task list (default: true) */
964
970
  enableAddTask?: boolean;
971
+ /** Label text for the add task button (default: 'Добавить работу') */
972
+ addTaskLabel?: string;
965
973
  /** Default duration for newly created tasks, interpreted in the active day mode (default: 5). */
966
974
  defaultTaskDurationDays?: number;
967
975
  /** Set of collapsed parent task IDs */
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",
@@ -7505,6 +7685,7 @@ var TaskList = ({
7505
7685
  disableTaskListReorder = false,
7506
7686
  editingTaskId: propEditingTaskId,
7507
7687
  enableAddTask = true,
7688
+ addTaskLabel = "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0430\u0431\u043E\u0442\u0443",
7508
7689
  defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
7509
7690
  collapsedParentIds: externalCollapsedParentIds,
7510
7691
  onToggleCollapse: externalOnToggleCollapse,
@@ -8598,7 +8779,7 @@ var TaskList = ({
8598
8779
  nestingDepth: 0
8599
8780
  }
8600
8781
  ),
8601
- enableAddTask && onAdd && !isCreating && !pendingInsert && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8782
+ enableAddTask && onAdd && !isCreating && !pendingInsert && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8602
8783
  "button",
8603
8784
  {
8604
8785
  className: `gantt-tl-add-btn${!reorderDisabled && dragOverTarget?.index === visibleTasks.length ? " gantt-tl-add-btn-drag-over" : ""}`,
@@ -8624,7 +8805,10 @@ var TaskList = ({
8624
8805
  handleDrop(visibleTasks.length, e);
8625
8806
  },
8626
8807
  type: "button",
8627
- children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0430\u0431\u043E\u0442\u0443"
8808
+ children: [
8809
+ "+ ",
8810
+ addTaskLabel
8811
+ ]
8628
8812
  }
8629
8813
  )
8630
8814
  ] })
@@ -12520,6 +12704,7 @@ function TaskGanttChartInner(props, ref) {
12520
12704
  onDemoteTask,
12521
12705
  onUngroupTask,
12522
12706
  enableAddTask = true,
12707
+ addTaskLabel = "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0430\u0431\u043E\u0442\u0443",
12523
12708
  defaultTaskDurationDays,
12524
12709
  taskFilter,
12525
12710
  filterMode = "highlight",
@@ -12546,6 +12731,7 @@ function TaskGanttChartInner(props, ref) {
12546
12731
  onTaskDateChangeModeChange: externalOnTaskDateChangeModeChange
12547
12732
  } = props;
12548
12733
  const dayWidth = !isTableMatrixMode ? props.dayWidth ?? 40 : 40;
12734
+ const criticalPathMode = !isTableMatrixMode && !isPlanFactMode ? props.criticalPathMode : void 0;
12549
12735
  const viewMode = !isTableMatrixMode && !isPlanFactMode ? props.viewMode ?? "day" : "day";
12550
12736
  const customDays = !isTableMatrixMode ? props.customDays : void 0;
12551
12737
  const isWeekend3 = !isTableMatrixMode ? props.isWeekend : void 0;
@@ -12745,10 +12931,23 @@ function TaskGanttChartInner(props, ref) {
12745
12931
  [resolvedRowContentLines, rowHeight]
12746
12932
  );
12747
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]);
12748
12939
  const isCustomWeekend = (0, import_react18.useMemo)(
12749
12940
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
12750
12941
  [customDays, isWeekend3]
12751
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]);
12752
12951
  const dateRangeTasks = (0, import_react18.useMemo)(() => {
12753
12952
  if (isPlanFactMode) {
12754
12953
  return getPlanFactRangeTasks(normalizedTasks);
@@ -12793,8 +12992,11 @@ function TaskGanttChartInner(props, ref) {
12793
12992
  if (filterMode === "hide" && taskFilter) {
12794
12993
  tasks2 = tasks2.filter(taskFilter);
12795
12994
  }
12995
+ if (criticalPathMode === "hide") {
12996
+ tasks2 = tasks2.filter((task) => criticalTaskIds.has(task.id));
12997
+ }
12796
12998
  return tasks2;
12797
- }, [normalizedTasks, collapsedParentIds, filterMode, taskFilter]);
12999
+ }, [normalizedTasks, collapsedParentIds, filterMode, taskFilter, criticalPathMode, criticalTaskIds]);
12798
13000
  const matchedTaskIds = (0, import_react18.useMemo)(() => {
12799
13001
  if (!taskFilter) return /* @__PURE__ */ new Set();
12800
13002
  return new Set(visibleTasks.filter(taskFilter).map((task) => task.id));
@@ -12987,7 +13189,6 @@ function TaskGanttChartInner(props, ref) {
12987
13189
  }, [effectiveRowHeight, tasks, visibleTasks]);
12988
13190
  const [dragGuideLines, setDragGuideLines] = (0, import_react18.useState)(null);
12989
13191
  const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react18.useState)(null);
12990
- const [previewTasksById, setPreviewTasksById] = (0, import_react18.useState)(/* @__PURE__ */ new Map());
12991
13192
  (0, import_react18.useEffect)(() => {
12992
13193
  const result = validateDependencies(tasks);
12993
13194
  setValidationResult(result);
@@ -13115,14 +13316,10 @@ function TaskGanttChartInner(props, ref) {
13115
13316
  }
13116
13317
  setCascadeOverrides((current) => arePositionMapsEqual(current, renderedOverrides) ? current : renderedOverrides);
13117
13318
  setPreviewTasksById((current) => {
13118
- 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();
13119
13320
  return arePreviewTaskMapsEqual(current, next) ? current : next;
13120
13321
  });
13121
13322
  }, [previewPositionStore]);
13122
- const previewNormalizedTasks = (0, import_react18.useMemo)(() => {
13123
- if (previewTasksById.size === 0) return normalizedTasks;
13124
- return normalizedTasks.map((task) => previewTasksById.get(task.id) ?? task);
13125
- }, [normalizedTasks, previewTasksById]);
13126
13323
  const previewVisibleTasks = (0, import_react18.useMemo)(() => {
13127
13324
  if (previewTasksById.size === 0) return visibleTasks;
13128
13325
  return visibleTasks.map((task) => previewTasksById.get(task.id) ?? task);
@@ -13550,6 +13747,7 @@ function TaskGanttChartInner(props, ref) {
13550
13747
  disableTaskDrag: disableTaskDrag || disableTaskListReorder,
13551
13748
  editingTaskId,
13552
13749
  enableAddTask,
13750
+ addTaskLabel,
13553
13751
  defaultTaskDurationDays,
13554
13752
  collapsedParentIds,
13555
13753
  onToggleCollapse: handleToggleCollapse,
@@ -13721,7 +13919,8 @@ function TaskGanttChartInner(props, ref) {
13721
13919
  selectedDep: selectedChip,
13722
13920
  businessDays,
13723
13921
  weekendPredicate: isCustomWeekend,
13724
- onDependencyClick: handleDependencyLineClick
13922
+ onDependencyClick: handleDependencyLineClick,
13923
+ criticalTaskIds: criticalPathMode ? criticalTaskIds : void 0
13725
13924
  }
13726
13925
  ),
13727
13926
  dependencyLineMenu && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
@@ -13791,6 +13990,9 @@ function TaskGanttChartInner(props, ref) {
13791
13990
  } else {
13792
13991
  setDragGuideLines((current) => current === null ? current : null);
13793
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());
13794
13996
  }
13795
13997
  },
13796
13998
  rowIndex: index,
@@ -13803,6 +14005,7 @@ function TaskGanttChartInner(props, ref) {
13803
14005
  highlightExpiredTasks,
13804
14006
  showBaseline,
13805
14007
  isFilterMatch: filterMode === "highlight" ? matchedTaskIds.has(task.id) : false,
14008
+ isCritical: criticalTaskIds.has(task.id),
13806
14009
  businessDays,
13807
14010
  customDays,
13808
14011
  isWeekend: isWeekend3,
@@ -13925,6 +14128,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
13925
14128
  cascadeByLinks,
13926
14129
  clampDateRangeForIncomingFS,
13927
14130
  clampTaskRangeForIncomingFS,
14131
+ computeCriticalPath,
13928
14132
  computeLagFromDates,
13929
14133
  computeParentDates,
13930
14134
  computeParentProgress,
@@ -13933,6 +14137,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
13933
14137
  detectCycles,
13934
14138
  detectEdgeZone,
13935
14139
  expired,
14140
+ extendCriticalIdsToParents,
13936
14141
  findParentId,
13937
14142
  flattenHierarchy,
13938
14143
  formatDateLabel,