gantt-lib 0.124.2 → 0.126.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
@@ -443,8 +443,7 @@ function subtractBusinessDays2(endDate, businessDays, weekendPredicate) {
443
443
  // src/core/scheduling/dependencies.ts
444
444
  function normalizePredecessorDates(predecessor, parseDateFn) {
445
445
  const predStart = parseDateFn(predecessor.startDate);
446
- const isMilestone = predecessor.type === "milestone";
447
- const predEnd = isMilestone ? new Date(predStart.getTime() - DAY_MS) : parseDateFn(predecessor.endDate);
446
+ const predEnd = predecessor.type === "milestone" ? predStart : parseDateFn(predecessor.endDate);
448
447
  return { predStart, predEnd };
449
448
  }
450
449
  function getDependencyLag(dep) {
@@ -453,7 +452,7 @@ function getDependencyLag(dep) {
453
452
  function normalizeTaskDependencyLags(task) {
454
453
  return task;
455
454
  }
456
- function normalizeDependencyLag(linkType, lag, predecessorStart, predecessorEnd, businessDays = false, weekendPredicate) {
455
+ function normalizeDependencyLag(linkType, lag, predecessorStart, predecessorEnd, businessDays = false, weekendPredicate, successorType) {
457
456
  if (linkType !== "FS") {
458
457
  return lag;
459
458
  }
@@ -463,9 +462,10 @@ function normalizeDependencyLag(linkType, lag, predecessorStart, predecessorEnd,
463
462
  businessDays,
464
463
  weekendPredicate
465
464
  );
466
- return Math.max(-predecessorDuration, lag);
465
+ const minimumLag = successorType === "milestone" ? -Math.max(0, predecessorDuration - 1) : -predecessorDuration;
466
+ return Math.max(minimumLag, lag);
467
467
  }
468
- function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd, businessDays = false, weekendPredicate) {
468
+ function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd, businessDays = false, weekendPredicate, successorType) {
469
469
  const pS = Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate());
470
470
  const pE = Date.UTC(predEnd.getUTCFullYear(), predEnd.getUTCMonth(), predEnd.getUTCDate());
471
471
  const sS = Date.UTC(succStart.getUTCFullYear(), succStart.getUTCMonth(), succStart.getUTCDate());
@@ -475,11 +475,12 @@ function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd, b
475
475
  case "FS":
476
476
  return normalizeDependencyLag(
477
477
  linkType,
478
- Math.round((sS - pE) / DAY_MS) - 1,
478
+ Math.round((sS - pE) / DAY_MS) - (successorType === "milestone" ? 0 : 1),
479
479
  predStart,
480
480
  predEnd,
481
481
  businessDays,
482
- weekendPredicate
482
+ weekendPredicate,
483
+ successorType
483
484
  );
484
485
  case "SS":
485
486
  return Math.round((sS - pS) / DAY_MS);
@@ -496,11 +497,12 @@ function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd, b
496
497
  case "FS":
497
498
  return normalizeDependencyLag(
498
499
  linkType,
499
- businessOffset - 1,
500
+ businessOffset - (successorType === "milestone" ? 0 : 1),
500
501
  predStart,
501
502
  predEnd,
502
503
  businessDays,
503
- weekendPredicate
504
+ weekendPredicate,
505
+ successorType
504
506
  );
505
507
  case "SS":
506
508
  return businessOffset;
@@ -510,19 +512,20 @@ function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd, b
510
512
  return businessOffset + 1;
511
513
  }
512
514
  }
513
- function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag = 0, businessDays = false, weekendPredicate) {
515
+ function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag = 0, businessDays = false, weekendPredicate, successorType) {
514
516
  const normalizedLag = normalizeDependencyLag(
515
517
  linkType,
516
518
  lag,
517
519
  predecessorStart,
518
520
  predecessorEnd,
519
521
  businessDays,
520
- weekendPredicate
522
+ weekendPredicate,
523
+ successorType
521
524
  );
522
525
  if (!businessDays || !weekendPredicate) {
523
526
  switch (linkType) {
524
527
  case "FS":
525
- return new Date(predecessorEnd.getTime() + (normalizedLag + 1) * DAY_MS);
528
+ return new Date(predecessorEnd.getTime() + (normalizedLag + (successorType === "milestone" ? 0 : 1)) * DAY_MS);
526
529
  case "SS":
527
530
  return new Date(predecessorStart.getTime() + normalizedLag * DAY_MS);
528
531
  case "FF":
@@ -535,7 +538,7 @@ function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag
535
538
  let offset;
536
539
  switch (linkType) {
537
540
  case "FS":
538
- offset = normalizedLag + 1;
541
+ offset = normalizedLag + (successorType === "milestone" ? 0 : 1);
539
542
  break;
540
543
  case "SS":
541
544
  offset = normalizedLag;
@@ -767,7 +770,8 @@ function clampTaskRangeForIncomingFS(task, proposedStart, proposedEnd, allTasks,
767
770
  "FS",
768
771
  -predecessorDuration,
769
772
  businessDays,
770
- weekendPredicate
773
+ weekendPredicate,
774
+ task.type
771
775
  );
772
776
  if (!minAllowedStart || candidateMinStart.getTime() > minAllowedStart.getTime()) {
773
777
  minAllowedStart = candidateMinStart;
@@ -801,7 +805,8 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks, busin
801
805
  newStartDate,
802
806
  newEndDate,
803
807
  businessDays,
804
- weekendPredicate
808
+ weekendPredicate,
809
+ task.type
805
810
  );
806
811
  return { ...dep, lag: nextLag };
807
812
  });
@@ -916,7 +921,10 @@ function cascadeByLinks(movedTaskId, newStart, newEnd, allTasks, skipChildCascad
916
921
  normalizedPredStart,
917
922
  normalizedPredEnd,
918
923
  dep.type,
919
- getDependencyLag(dep)
924
+ getDependencyLag(dep),
925
+ false,
926
+ void 0,
927
+ task.type
920
928
  );
921
929
  let newSuccStart;
922
930
  let newSuccEnd;
@@ -1078,7 +1086,8 @@ function universalCascade(movedTask, newStart, newEnd, allTasks, businessDays =
1078
1086
  dep.type,
1079
1087
  getDependencyLag(dep),
1080
1088
  businessDays,
1081
- weekendPredicate
1089
+ weekendPredicate,
1090
+ task.type
1082
1091
  );
1083
1092
  let succNewStart;
1084
1093
  let succNewEnd;
@@ -1378,7 +1387,8 @@ function recalculateTaskFromDependencies(taskId, snapshot, options) {
1378
1387
  dep.type,
1379
1388
  getDependencyLag(dep),
1380
1389
  businessDays,
1381
- weekendPredicate
1390
+ weekendPredicate,
1391
+ task.type
1382
1392
  );
1383
1393
  const duration = getTaskDuration(
1384
1394
  parseDateOnly(task.startDate),
@@ -1494,7 +1504,8 @@ function recalculateProjectSchedule(snapshot, options) {
1494
1504
  dep.type,
1495
1505
  getDependencyLag(dep),
1496
1506
  businessDays,
1497
- weekendPredicate
1507
+ weekendPredicate,
1508
+ currentTask.type
1498
1509
  );
1499
1510
  const candidateRange = dep.type === "FS" || dep.type === "SS" ? buildTaskRangeFromStart(constraintDate, duration, businessDays, weekendPredicate) : buildTaskRangeFromEnd(constraintDate, duration, businessDays, weekendPredicate);
1500
1511
  if (!constrainedRange || candidateRange.start.getTime() > constrainedRange.start.getTime() || candidateRange.start.getTime() === constrainedRange.start.getTime() && candidateRange.end.getTime() > constrainedRange.end.getTime()) {
@@ -3032,7 +3043,8 @@ var TaskRow = React2.memo(
3032
3043
  }
3033
3044
  return calculateTaskBar(baselineStartDate, baselineEndDate, monthStart, dayWidth);
3034
3045
  }, [baselineStartDate, baselineEndDate, milestone, monthStart, dayWidth]);
3035
- const barColor = isExpired ? "var(--gantt-expired-color)" : task.color || "var(--gantt-task-bar-default-color)";
3046
+ const criticalBarColor = "var(--gantt-critical-path-color, #dc2626)";
3047
+ const barColor = isExpired ? "var(--gantt-expired-color)" : isCritical ? criticalBarColor : task.color || "var(--gantt-task-bar-default-color)";
3036
3048
  const progressWidth = useMemo2(() => {
3037
3049
  if (task.progress === void 0 || task.progress <= 0) return 0;
3038
3050
  return Math.min(100, Math.max(0, Math.round(task.progress)));
@@ -3041,21 +3053,31 @@ var TaskRow = React2.memo(
3041
3053
  if (isExpired) {
3042
3054
  return "color-mix(in srgb, var(--gantt-expired-color) 40%, black)";
3043
3055
  }
3056
+ if (isCritical) {
3057
+ return `color-mix(in srgb, ${criticalBarColor} 40%, black)`;
3058
+ }
3044
3059
  if (progressWidth === 100) {
3045
3060
  return task.accepted ? "var(--gantt-progress-accepted, #22c55e)" : "var(--gantt-progress-completed, #fbbf24)";
3046
3061
  }
3047
3062
  const baseColor = task.color || "var(--gantt-task-bar-default-color)";
3048
3063
  return `color-mix(in srgb, ${baseColor} 40%, black)`;
3049
- }, [isExpired, progressWidth, task.accepted, task.color]);
3064
+ }, [isCritical, isExpired, progressWidth, task.accepted, task.color]);
3050
3065
  const externalTaskNameColor = useMemo2(() => {
3051
3066
  if (isExpired) {
3052
3067
  return "color-mix(in srgb, var(--gantt-expired-color) 40%, black)";
3053
3068
  }
3054
3069
  const baseColor = isParent ? task.color || defaultParentBarColor : task.color || "var(--gantt-task-bar-default-color)";
3055
3070
  return `color-mix(in srgb, ${baseColor} 40%, black)`;
3056
- }, [defaultParentBarColor, isExpired, isParent, task.color]);
3071
+ }, [defaultParentBarColor, isCritical, isExpired, isParent, task.color]);
3057
3072
  const barStyle = useMemo2(() => {
3058
3073
  const parentBarColor = task.color || defaultParentBarColor;
3074
+ if (isCritical && !isExpired) {
3075
+ const c = criticalBarColor;
3076
+ if (isParent) {
3077
+ return { backgroundColor: c, "--gantt-parent-bar-color": c };
3078
+ }
3079
+ return { backgroundColor: c };
3080
+ }
3059
3081
  if (isParent) {
3060
3082
  if (progressWidth >= 100) {
3061
3083
  const c = isExpired ? "color-mix(in srgb, var(--gantt-expired-color) 40%, black)" : `color-mix(in srgb, ${parentBarColor} 40%, black)`;
@@ -3067,7 +3089,7 @@ var TaskRow = React2.memo(
3067
3089
  return { backgroundColor: progressColor };
3068
3090
  }
3069
3091
  return { backgroundColor: barColor };
3070
- }, [defaultParentBarColor, isExpired, isParent, progressWidth, barColor, progressColor, task.color]);
3092
+ }, [isCritical, defaultParentBarColor, isExpired, isParent, progressWidth, barColor, progressColor, task.color]);
3071
3093
  const handleDragEnd = (result) => {
3072
3094
  const updatedTask = {
3073
3095
  ...normalizedTask,
@@ -3646,6 +3668,7 @@ var DependencyLines = React6.memo(({
3646
3668
  for (const task of allTasks) {
3647
3669
  if (positions.has(task.id)) continue;
3648
3670
  if (collapsedParentIds.size > 0 && isTaskHidden(task.id, collapsedParentIds, taskMap)) continue;
3671
+ if (rowIndexByTaskId && !rowIndexByTaskId.has(task.id)) continue;
3649
3672
  hidden.add(task.id);
3650
3673
  const override = dragOverrides?.get(task.id);
3651
3674
  const computed = resolveTaskHorizontalGeometry(task, monthStart, dayWidth, override);
@@ -5085,7 +5108,8 @@ var DepChip = ({
5085
5108
  predStart,
5086
5109
  predEnd,
5087
5110
  businessDays,
5088
- weekendPredicate
5111
+ weekendPredicate,
5112
+ task.type
5089
5113
  );
5090
5114
  const constraintDate = calculateSuccessorDate(
5091
5115
  predStart,
@@ -5093,7 +5117,8 @@ var DepChip = ({
5093
5117
  dep.type,
5094
5118
  normalizedLag,
5095
5119
  businessDays,
5096
- weekendPredicate
5120
+ weekendPredicate,
5121
+ task.type
5097
5122
  );
5098
5123
  let newStart, newEnd;
5099
5124
  if (dep.type === "FS" || dep.type === "SS") {
@@ -7921,14 +7946,15 @@ var TaskList = ({
7921
7946
  const predecessor = tasks.find((t) => t.id === predecessorTaskId);
7922
7947
  if (predecessor) {
7923
7948
  const predStart = new Date(predecessor.startDate);
7924
- const predEnd = new Date(predecessor.endDate);
7949
+ const predEnd = predecessor.type === "milestone" ? predStart : new Date(predecessor.endDate);
7925
7950
  const constraintDate = calculateSuccessorDate(
7926
7951
  predStart,
7927
7952
  predEnd,
7928
7953
  linkType,
7929
7954
  0,
7930
7955
  businessDays ?? true,
7931
- weekendPredicate
7956
+ weekendPredicate,
7957
+ updatedTask.type
7932
7958
  );
7933
7959
  const origSuccessor = tasks.find((t) => t.id === successorTaskId);
7934
7960
  const duration = getTaskDuration(
@@ -12728,11 +12754,12 @@ function TaskGanttChartInner(props, ref) {
12728
12754
  }
12729
12755
  const constraintDate = calculateSuccessorDate(
12730
12756
  parseUTCDate(predecessor.startDate),
12731
- parseUTCDate(predecessor.endDate),
12757
+ predecessor.type === "milestone" ? parseUTCDate(predecessor.startDate) : parseUTCDate(predecessor.endDate),
12732
12758
  nextDependency.type,
12733
12759
  0,
12734
12760
  businessDays,
12735
- isCustomWeekend
12761
+ isCustomWeekend,
12762
+ successor.type
12736
12763
  );
12737
12764
  const duration = getTaskDuration(
12738
12765
  updatedSuccessor.startDate,
@@ -12889,13 +12916,18 @@ function TaskGanttChartInner(props, ref) {
12889
12916
  if (filterMode === "hide") {
12890
12917
  return /* @__PURE__ */ new Set();
12891
12918
  }
12892
- if ((!highlightedTaskIds || highlightedTaskIds.size === 0) && matchedTaskIds.size === 0) {
12919
+ const criticalHighlightMode = criticalPathMode === "highlight";
12920
+ const hasExternalHighlights = !!highlightedTaskIds && highlightedTaskIds.size > 0;
12921
+ if (!hasExternalHighlights && matchedTaskIds.size === 0 && !criticalHighlightMode) {
12893
12922
  return /* @__PURE__ */ new Set();
12894
12923
  }
12895
12924
  const mergedHighlightedTaskIds = new Set(highlightedTaskIds ?? []);
12896
12925
  matchedTaskIds.forEach((taskId) => mergedHighlightedTaskIds.add(taskId));
12926
+ if (criticalHighlightMode) {
12927
+ criticalTaskIds.forEach((taskId) => mergedHighlightedTaskIds.add(taskId));
12928
+ }
12897
12929
  return mergedHighlightedTaskIds;
12898
- }, [filterMode, highlightedTaskIds, matchedTaskIds]);
12930
+ }, [filterMode, highlightedTaskIds, matchedTaskIds, criticalPathMode, criticalTaskIds]);
12899
12931
  const totalGridHeight = useMemo13(
12900
12932
  () => visibleTasks.length * effectiveRowHeight,
12901
12933
  [effectiveRowHeight, visibleTasks.length]
@@ -13890,7 +13922,7 @@ function TaskGanttChartInner(props, ref) {
13890
13922
  onCascade: handleCascade,
13891
13923
  highlightExpiredTasks,
13892
13924
  showBaseline,
13893
- isFilterMatch: filterMode === "highlight" ? matchedTaskIds.has(task.id) : false,
13925
+ isFilterMatch: (filterMode === "highlight" ? matchedTaskIds.has(task.id) : false) || (criticalPathMode === "highlight" ? criticalTaskIds.has(task.id) : false),
13894
13926
  isCritical: criticalTaskIds.has(task.id),
13895
13927
  businessDays,
13896
13928
  customDays,