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.mjs CHANGED
@@ -550,6 +550,159 @@ function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag
550
550
  return shiftBusinessDayOffset(anchorDate, offset, weekendPredicate);
551
551
  }
552
552
 
553
+ // src/core/scheduling/criticalPath.ts
554
+ function computeCriticalPath(tasks, options = {}) {
555
+ const { businessDays = false, weekendPredicate } = options;
556
+ const taskById = new Map(tasks.map((task) => [task.id, task]));
557
+ const childIds = /* @__PURE__ */ new Set();
558
+ for (const task of tasks) {
559
+ if (task.parentId) childIds.add(task.parentId);
560
+ }
561
+ const leafTasks = tasks.filter((task) => !childIds.has(task.id));
562
+ const leafIds = new Set(leafTasks.map((task) => task.id));
563
+ const leafIdsByAncestor = /* @__PURE__ */ new Map();
564
+ for (const leaf of leafTasks) {
565
+ let ancestor = leaf.parentId;
566
+ while (ancestor) {
567
+ const set = leafIdsByAncestor.get(ancestor) ?? /* @__PURE__ */ new Set();
568
+ set.add(leaf.id);
569
+ leafIdsByAncestor.set(ancestor, set);
570
+ ancestor = taskById.get(ancestor)?.parentId;
571
+ }
572
+ }
573
+ const durationOf = (task) => getTaskDuration(task.startDate, task.endDate, businessDays, weekendPredicate);
574
+ const projectStartDate = leafTasks.reduce((earliest, task) => {
575
+ const taskStart = parseDateOnly(task.startDate);
576
+ return taskStart.getTime() < earliest.getTime() ? taskStart : earliest;
577
+ }, parseDateOnly(leafTasks[0]?.startDate ?? /* @__PURE__ */ new Date()));
578
+ const offsetFromProjectStart = (date) => {
579
+ const targetDate = parseDateOnly(date);
580
+ if (businessDays && weekendPredicate) {
581
+ return getBusinessDayOffset(projectStartDate, targetDate, weekendPredicate);
582
+ }
583
+ return Math.round((targetDate.getTime() - projectStartDate.getTime()) / DAY_MS);
584
+ };
585
+ const scheduledStart = /* @__PURE__ */ new Map();
586
+ for (const leaf of leafTasks) {
587
+ scheduledStart.set(leaf.id, offsetFromProjectStart(leaf.startDate));
588
+ }
589
+ const succByPred = /* @__PURE__ */ new Map();
590
+ const predBySucc = /* @__PURE__ */ new Map();
591
+ const seenEdges = /* @__PURE__ */ new Set();
592
+ const addEdge = (predecessorId, succId, lag, type) => {
593
+ const edgeKey = `${predecessorId}|${succId}|${type}`;
594
+ if (seenEdges.has(edgeKey)) return;
595
+ seenEdges.add(edgeKey);
596
+ const outEdges = succByPred.get(predecessorId) ?? [];
597
+ outEdges.push({ succId, lag, type });
598
+ succByPred.set(predecessorId, outEdges);
599
+ const inEdges = predBySucc.get(succId) ?? [];
600
+ inEdges.push({ predId: predecessorId, lag, type });
601
+ predBySucc.set(succId, inEdges);
602
+ };
603
+ for (const succ of leafTasks) {
604
+ for (const dep of succ.dependencies ?? []) {
605
+ const lag = getDependencyLag(dep);
606
+ if (leafIds.has(dep.taskId)) {
607
+ addEdge(dep.taskId, succ.id, lag, dep.type);
608
+ continue;
609
+ }
610
+ const descendants = leafIdsByAncestor.get(dep.taskId);
611
+ if (!descendants || descendants.size === 0) continue;
612
+ for (const leafId of descendants) {
613
+ if (leafId === succ.id) continue;
614
+ addEdge(leafId, succ.id, lag, dep.type);
615
+ }
616
+ }
617
+ }
618
+ const indegree = /* @__PURE__ */ new Map();
619
+ for (const leaf of leafTasks) indegree.set(leaf.id, 0);
620
+ for (const edges of succByPred.values()) {
621
+ for (const { succId } of edges) {
622
+ indegree.set(succId, (indegree.get(succId) ?? 0) + 1);
623
+ }
624
+ }
625
+ const order = [];
626
+ const enqueued = /* @__PURE__ */ new Set();
627
+ const queue = leafTasks.filter((leaf) => (indegree.get(leaf.id) ?? 0) === 0).map((leaf) => leaf.id);
628
+ for (const id of queue) enqueued.add(id);
629
+ while (queue.length > 0) {
630
+ const current = queue.shift();
631
+ order.push(current);
632
+ for (const { succId } of succByPred.get(current) ?? []) {
633
+ const next = (indegree.get(succId) ?? 0) - 1;
634
+ indegree.set(succId, next);
635
+ if (next === 0 && !enqueued.has(succId)) {
636
+ enqueued.add(succId);
637
+ queue.push(succId);
638
+ }
639
+ }
640
+ }
641
+ const projectFinish = Math.max(
642
+ 0,
643
+ ...leafTasks.map((leaf) => (scheduledStart.get(leaf.id) ?? 0) + durationOf(leaf))
644
+ );
645
+ const LF = /* @__PURE__ */ new Map();
646
+ const LS = /* @__PURE__ */ new Map();
647
+ for (const leaf of leafTasks) LF.set(leaf.id, projectFinish);
648
+ for (let i = order.length - 1; i >= 0; i--) {
649
+ const id = order[i];
650
+ const task = taskById.get(id);
651
+ const durPred = durationOf(task);
652
+ const outEdges = succByPred.get(id) ?? [];
653
+ let lf = projectFinish;
654
+ if (outEdges.length > 0) {
655
+ const candidates = outEdges.map(({ succId, lag, type }) => {
656
+ const succLs = LS.get(succId) ?? projectFinish;
657
+ const durSucc = durationOf(taskById.get(succId));
658
+ switch (type) {
659
+ case "FS":
660
+ return succLs - lag;
661
+ case "SS":
662
+ return succLs - lag + durPred;
663
+ case "FF":
664
+ return succLs + durSucc - lag;
665
+ case "SF":
666
+ return succLs + durSucc - lag + durPred;
667
+ default:
668
+ return projectFinish;
669
+ }
670
+ });
671
+ lf = Math.min(projectFinish, ...candidates);
672
+ }
673
+ LF.set(id, lf);
674
+ LS.set(id, lf - durPred);
675
+ }
676
+ const critical = /* @__PURE__ */ new Set();
677
+ const ordered = new Set(order);
678
+ for (const leaf of leafTasks) {
679
+ if (!ordered.has(leaf.id)) continue;
680
+ const hasGraphEdge = (succByPred.get(leaf.id)?.length ?? 0) > 0 || (predBySucc.get(leaf.id)?.length ?? 0) > 0;
681
+ if (!hasGraphEdge && (leaf.dependencies?.length ?? 0) > 0) continue;
682
+ const es = scheduledStart.get(leaf.id) ?? 0;
683
+ const ls = LS.get(leaf.id) ?? 0;
684
+ if (ls - es <= 0) critical.add(leaf.id);
685
+ }
686
+ return critical;
687
+ }
688
+ function extendCriticalIdsToParents(criticalLeafIds, tasks) {
689
+ const result = new Set(criticalLeafIds);
690
+ const parentByChild = /* @__PURE__ */ new Map();
691
+ for (const task of tasks) {
692
+ if (task.parentId) parentByChild.set(task.id, task.parentId);
693
+ }
694
+ const stack = Array.from(criticalLeafIds);
695
+ while (stack.length > 0) {
696
+ const id = stack.pop();
697
+ const parentId = parentByChild.get(id);
698
+ if (parentId && !result.has(parentId)) {
699
+ result.add(parentId);
700
+ stack.push(parentId);
701
+ }
702
+ }
703
+ return result;
704
+ }
705
+
553
706
  // src/core/scheduling/commands.ts
554
707
  function buildTaskRangeFromStart(startDate, duration, businessDays = false, weekendPredicate, snapDirection = 1) {
555
708
  const normalizedStart = businessDays && weekendPredicate ? alignToWorkingDay(startDate, snapDirection, weekendPredicate) : normalizeUTCDate(startDate);
@@ -2412,8 +2565,7 @@ function handleGlobalMouseMove(e) {
2412
2565
  activeDrag.weekendPredicate,
2413
2566
  activeDrag.cascadeContext
2414
2567
  );
2415
- const hasTasksWithIncomingDependencies = cascadeResult.some((task) => task.dependencies?.length);
2416
- const previewTasks = hasTasksWithIncomingDependencies ? (() => {
2568
+ const previewTasks = (() => {
2417
2569
  const mergedPreviewTaskById = new Map(activeDrag.cascadeContext?.taskById);
2418
2570
  for (const task of cascadeResult) {
2419
2571
  mergedPreviewTaskById.set(task.id, task);
@@ -2434,7 +2586,7 @@ function handleGlobalMouseMove(e) {
2434
2586
  ) : task.dependencies
2435
2587
  };
2436
2588
  });
2437
- })() : [];
2589
+ })();
2438
2590
  const overrides = /* @__PURE__ */ new Map();
2439
2591
  overrides.set(dragId, { left: newLeft, width: newWidth });
2440
2592
  for (const task of cascadeResult) {
@@ -2837,10 +2989,10 @@ var useTaskDrag = (options) => {
2837
2989
  // src/components/TaskRow/TaskRow.tsx
2838
2990
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2839
2991
  var arePropsEqual = (prevProps, nextProps) => {
2840
- 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;
2992
+ 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;
2841
2993
  };
2842
2994
  var TaskRow = React2.memo(
2843
- ({ 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" }) => {
2995
+ ({ 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" }) => {
2844
2996
  const defaultParentBarColor = "#782FC4";
2845
2997
  const { divider: taskDivider } = task;
2846
2998
  const normalizedTask = useMemo2(() => normalizeTaskDatesForType(task), [task]);
@@ -3030,7 +3182,7 @@ var TaskRow = React2.memo(
3030
3182
  "div",
3031
3183
  {
3032
3184
  "data-taskbar": true,
3033
- 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" : ""}`,
3185
+ 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" : ""}`,
3034
3186
  style: {
3035
3187
  left: `${visualLeft}px`,
3036
3188
  ...barStyle,
@@ -3445,6 +3597,7 @@ var DependencyLines = React6.memo(({
3445
3597
  rowIndexByTaskId,
3446
3598
  dragOverrides,
3447
3599
  selectedDep,
3600
+ criticalTaskIds,
3448
3601
  businessDays = true,
3449
3602
  weekendPredicate,
3450
3603
  onDependencyClick
@@ -3583,6 +3736,9 @@ var DependencyLines = React6.memo(({
3583
3736
  const to = { x: finalToX, y: toY };
3584
3737
  const path = calculateDependencyPath(from, to, arrivesFromRight);
3585
3738
  const hasCycle = cycleInfo.has(edge.predecessorId) || cycleInfo.has(edge.successorId);
3739
+ const isCritical = Boolean(
3740
+ criticalTaskIds?.has(edge.predecessorId) && criticalTaskIds.has(edge.successorId)
3741
+ );
3586
3742
  lines2.push({
3587
3743
  id: `${edge.predecessorId}-${edge.successorId}-${edge.type}`,
3588
3744
  predecessorId: edge.predecessorId,
@@ -3595,12 +3751,13 @@ var DependencyLines = React6.memo(({
3595
3751
  toX: finalToX,
3596
3752
  fromY,
3597
3753
  reverseOrder,
3598
- isVirtual
3754
+ isVirtual,
3755
+ isCritical
3599
3756
  });
3600
3757
  }
3601
3758
  }
3602
3759
  return lines2;
3603
- }, [tasks, allTasks, taskPositions, taskIndices, cycleInfo, collapsedParentIds]);
3760
+ }, [tasks, allTasks, taskPositions, taskIndices, cycleInfo, collapsedParentIds, criticalTaskIds]);
3604
3761
  const svgHeight = totalHeight ?? tasks.length * rowHeight;
3605
3762
  return /* @__PURE__ */ jsxs5(
3606
3763
  "svg",
@@ -3687,20 +3844,41 @@ var DependencyLines = React6.memo(({
3687
3844
  }
3688
3845
  )
3689
3846
  }
3847
+ ),
3848
+ /* @__PURE__ */ jsx7(
3849
+ "marker",
3850
+ {
3851
+ id: "arrowhead-critical",
3852
+ markerWidth: "8",
3853
+ markerHeight: "6",
3854
+ markerUnits: "userSpaceOnUse",
3855
+ refX: "7",
3856
+ refY: "3",
3857
+ orient: "auto",
3858
+ children: /* @__PURE__ */ jsx7(
3859
+ "polygon",
3860
+ {
3861
+ points: "0 0, 8 3, 0 6",
3862
+ fill: "var(--gantt-critical-path-color, #dc2626)"
3863
+ }
3864
+ )
3865
+ }
3690
3866
  )
3691
3867
  ] }),
3692
- lines.map(({ id, predecessorId, successorId, linkType, path, hasCycle, lag, fromX, toX, fromY, reverseOrder, isVirtual }) => {
3868
+ lines.map(({ id, predecessorId, successorId, linkType, path, hasCycle, lag, fromX, toX, fromY, reverseOrder, isVirtual, isCritical }) => {
3693
3869
  const isHovered = hoveredLineId === id;
3694
3870
  const isSelected = selectedDep != null && id === `${selectedDep.predecessorId}-${selectedDep.successorId}-${selectedDep.linkType}`;
3695
3871
  let pathClassName = "gantt-dependency-path";
3696
3872
  if (isSelected) pathClassName += " gantt-dependency-selected";
3697
3873
  else if (hasCycle) pathClassName += " gantt-dependency-cycle";
3874
+ else if (isCritical) pathClassName += " gantt-dependency-critical";
3698
3875
  if (isVirtual && !isSelected) pathClassName += " gantt-dependency-virtual";
3699
3876
  let markerEnd;
3700
3877
  if (isSelected) markerEnd = "url(#arrowhead-selected)";
3701
3878
  else if (hasCycle) markerEnd = "url(#arrowhead-cycle)";
3879
+ else if (isCritical) markerEnd = "url(#arrowhead-critical)";
3702
3880
  else markerEnd = "url(#arrowhead)";
3703
- const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : "var(--gantt-dependency-line-color, #666666)";
3881
+ const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : isCritical ? "var(--gantt-critical-path-color, #dc2626)" : "var(--gantt-dependency-line-color, #666666)";
3704
3882
  return /* @__PURE__ */ jsxs5(React6.Fragment, { children: [
3705
3883
  /* @__PURE__ */ jsxs5(
3706
3884
  "g",
@@ -12432,6 +12610,7 @@ function TaskGanttChartInner(props, ref) {
12432
12610
  onTaskDateChangeModeChange: externalOnTaskDateChangeModeChange
12433
12611
  } = props;
12434
12612
  const dayWidth = !isTableMatrixMode ? props.dayWidth ?? 40 : 40;
12613
+ const criticalPathMode = !isTableMatrixMode && !isPlanFactMode ? props.criticalPathMode : void 0;
12435
12614
  const viewMode = !isTableMatrixMode && !isPlanFactMode ? props.viewMode ?? "day" : "day";
12436
12615
  const customDays = !isTableMatrixMode ? props.customDays : void 0;
12437
12616
  const isWeekend3 = !isTableMatrixMode ? props.isWeekend : void 0;
@@ -12631,10 +12810,23 @@ function TaskGanttChartInner(props, ref) {
12631
12810
  [resolvedRowContentLines, rowHeight]
12632
12811
  );
12633
12812
  const normalizedTasks = useMemo13(() => normalizeHierarchyTasks(tasks), [tasks]);
12813
+ const [previewTasksById, setPreviewTasksById] = useState12(/* @__PURE__ */ new Map());
12814
+ const previewNormalizedTasks = useMemo13(() => {
12815
+ if (previewTasksById.size === 0) return normalizedTasks;
12816
+ return normalizedTasks.map((task) => previewTasksById.get(task.id) ?? task);
12817
+ }, [normalizedTasks, previewTasksById]);
12634
12818
  const isCustomWeekend = useMemo13(
12635
12819
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
12636
12820
  [customDays, isWeekend3]
12637
12821
  );
12822
+ const criticalTaskIds = useMemo13(() => {
12823
+ if (!criticalPathMode) return /* @__PURE__ */ new Set();
12824
+ const leafCritical = computeCriticalPath(previewNormalizedTasks, {
12825
+ businessDays,
12826
+ weekendPredicate: isCustomWeekend
12827
+ });
12828
+ return extendCriticalIdsToParents(leafCritical, previewNormalizedTasks);
12829
+ }, [criticalPathMode, previewNormalizedTasks, businessDays, isCustomWeekend]);
12638
12830
  const dateRangeTasks = useMemo13(() => {
12639
12831
  if (isPlanFactMode) {
12640
12832
  return getPlanFactRangeTasks(normalizedTasks);
@@ -12679,8 +12871,11 @@ function TaskGanttChartInner(props, ref) {
12679
12871
  if (filterMode === "hide" && taskFilter) {
12680
12872
  tasks2 = tasks2.filter(taskFilter);
12681
12873
  }
12874
+ if (criticalPathMode === "hide") {
12875
+ tasks2 = tasks2.filter((task) => criticalTaskIds.has(task.id));
12876
+ }
12682
12877
  return tasks2;
12683
- }, [normalizedTasks, collapsedParentIds, filterMode, taskFilter]);
12878
+ }, [normalizedTasks, collapsedParentIds, filterMode, taskFilter, criticalPathMode, criticalTaskIds]);
12684
12879
  const matchedTaskIds = useMemo13(() => {
12685
12880
  if (!taskFilter) return /* @__PURE__ */ new Set();
12686
12881
  return new Set(visibleTasks.filter(taskFilter).map((task) => task.id));
@@ -12873,7 +13068,6 @@ function TaskGanttChartInner(props, ref) {
12873
13068
  }, [effectiveRowHeight, tasks, visibleTasks]);
12874
13069
  const [dragGuideLines, setDragGuideLines] = useState12(null);
12875
13070
  const [draggedTaskOverride, setDraggedTaskOverride] = useState12(null);
12876
- const [previewTasksById, setPreviewTasksById] = useState12(/* @__PURE__ */ new Map());
12877
13071
  useEffect10(() => {
12878
13072
  const result = validateDependencies(tasks);
12879
13073
  setValidationResult(result);
@@ -13001,14 +13195,10 @@ function TaskGanttChartInner(props, ref) {
13001
13195
  }
13002
13196
  setCascadeOverrides((current) => arePositionMapsEqual(current, renderedOverrides) ? current : renderedOverrides);
13003
13197
  setPreviewTasksById((current) => {
13004
- const next = previewTasks.length > 0 ? new Map(previewTasks.filter((task) => renderedTaskIds.has(task.id)).map((task) => [task.id, task])) : /* @__PURE__ */ new Map();
13198
+ const next = previewTasks.length > 0 ? new Map(previewTasks.map((task) => [task.id, task])) : /* @__PURE__ */ new Map();
13005
13199
  return arePreviewTaskMapsEqual(current, next) ? current : next;
13006
13200
  });
13007
13201
  }, [previewPositionStore]);
13008
- const previewNormalizedTasks = useMemo13(() => {
13009
- if (previewTasksById.size === 0) return normalizedTasks;
13010
- return normalizedTasks.map((task) => previewTasksById.get(task.id) ?? task);
13011
- }, [normalizedTasks, previewTasksById]);
13012
13202
  const previewVisibleTasks = useMemo13(() => {
13013
13203
  if (previewTasksById.size === 0) return visibleTasks;
13014
13204
  return visibleTasks.map((task) => previewTasksById.get(task.id) ?? task);
@@ -13608,7 +13798,8 @@ function TaskGanttChartInner(props, ref) {
13608
13798
  selectedDep: selectedChip,
13609
13799
  businessDays,
13610
13800
  weekendPredicate: isCustomWeekend,
13611
- onDependencyClick: handleDependencyLineClick
13801
+ onDependencyClick: handleDependencyLineClick,
13802
+ criticalTaskIds: criticalPathMode ? criticalTaskIds : void 0
13612
13803
  }
13613
13804
  ),
13614
13805
  dependencyLineMenu && /* @__PURE__ */ jsxs16(
@@ -13678,6 +13869,9 @@ function TaskGanttChartInner(props, ref) {
13678
13869
  } else {
13679
13870
  setDragGuideLines((current) => current === null ? current : null);
13680
13871
  setDraggedTaskOverride((current) => current === null ? current : null);
13872
+ previewPositionStore.clear();
13873
+ setCascadeOverrides((current) => current.size === 0 ? current : /* @__PURE__ */ new Map());
13874
+ setPreviewTasksById((current) => current.size === 0 ? current : /* @__PURE__ */ new Map());
13681
13875
  }
13682
13876
  },
13683
13877
  rowIndex: index,
@@ -13690,6 +13884,7 @@ function TaskGanttChartInner(props, ref) {
13690
13884
  highlightExpiredTasks,
13691
13885
  showBaseline,
13692
13886
  isFilterMatch: filterMode === "highlight" ? matchedTaskIds.has(task.id) : false,
13887
+ isCritical: criticalTaskIds.has(task.id),
13693
13888
  businessDays,
13694
13889
  customDays,
13695
13890
  isWeekend: isWeekend3,
@@ -13811,6 +14006,7 @@ export {
13811
14006
  cascadeByLinks,
13812
14007
  clampDateRangeForIncomingFS,
13813
14008
  clampTaskRangeForIncomingFS,
14009
+ computeCriticalPath,
13814
14010
  computeLagFromDates,
13815
14011
  computeParentDates,
13816
14012
  computeParentProgress,
@@ -13819,6 +14015,7 @@ export {
13819
14015
  detectCycles,
13820
14016
  detectEdgeZone,
13821
14017
  expired,
14018
+ extendCriticalIdsToParents,
13822
14019
  findParentId,
13823
14020
  flattenHierarchy,
13824
14021
  formatDateLabel,