gantt-lib 0.52.0 → 0.53.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
@@ -721,6 +721,22 @@ function validateDependencies(tasks) {
721
721
  }
722
722
  }
723
723
  }
724
+ for (const task of tasks) {
725
+ if (!task.dependencies) continue;
726
+ for (const dep of task.dependencies) {
727
+ if (!taskIds.has(dep.taskId)) {
728
+ continue;
729
+ }
730
+ if (areTasksHierarchicallyRelated(task.id, dep.taskId, tasks)) {
731
+ errors.push({
732
+ type: "constraint",
733
+ taskId: task.id,
734
+ message: `Dependencies between parent and child tasks are not allowed: ${dep.taskId} -> ${task.id}`,
735
+ relatedTaskIds: [dep.taskId, task.id]
736
+ });
737
+ }
738
+ }
739
+ }
724
740
  const cycleResult = detectCycles(tasks);
725
741
  if (cycleResult.hasCycle && cycleResult.cyclePath) {
726
742
  errors.push({
@@ -979,6 +995,28 @@ function findParentId(taskId, tasks) {
979
995
  const task = tasks.find((t) => t.id === taskId);
980
996
  return task?.parentId;
981
997
  }
998
+ function isAncestorTask(ancestorId, taskId, tasks) {
999
+ const taskById = new Map(tasks.map((task) => [task.id, task]));
1000
+ const visited = /* @__PURE__ */ new Set();
1001
+ let current = taskById.get(taskId);
1002
+ while (current?.parentId) {
1003
+ if (current.parentId === ancestorId) {
1004
+ return true;
1005
+ }
1006
+ if (visited.has(current.parentId)) {
1007
+ return false;
1008
+ }
1009
+ visited.add(current.parentId);
1010
+ current = taskById.get(current.parentId);
1011
+ }
1012
+ return false;
1013
+ }
1014
+ function areTasksHierarchicallyRelated(taskId1, taskId2, tasks) {
1015
+ if (taskId1 === taskId2) {
1016
+ return true;
1017
+ }
1018
+ return isAncestorTask(taskId1, taskId2, tasks) || isAncestorTask(taskId2, taskId1, tasks);
1019
+ }
982
1020
  function getAllDescendants(parentId, tasks) {
983
1021
  const descendants = [];
984
1022
  const visited = /* @__PURE__ */ new Set();
@@ -5834,7 +5872,7 @@ var TaskList = ({
5834
5872
  const [selectingPredecessorFor, setSelectingPredecessorFor] = useState6(null);
5835
5873
  const [dependencyPickMode, setDependencyPickMode] = useState6("successor");
5836
5874
  const [typeMenuOpen, setTypeMenuOpen] = useState6(false);
5837
- const [cycleError, setCycleError] = useState6(false);
5875
+ const [dependencyError, setDependencyError] = useState6(null);
5838
5876
  const overlayRef = useRef6(null);
5839
5877
  const [selectedChip, setSelectedChip] = useState6(null);
5840
5878
  const handleChipSelect = useCallback5((chip) => {
@@ -5869,6 +5907,12 @@ var TaskList = ({
5869
5907
  }, [selectingPredecessorFor, selectedChip, selectedTaskId, onTaskSelect, onSelectedChipChange]);
5870
5908
  const handleAddDependency = useCallback5((successorTaskId, predecessorTaskId, linkType) => {
5871
5909
  if (successorTaskId === predecessorTaskId) return;
5910
+ if (areTasksHierarchicallyRelated(successorTaskId, predecessorTaskId, tasks)) {
5911
+ setDependencyError("\u0421\u0432\u044F\u0437\u0438 \u043C\u0435\u0436\u0434\u0443 \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u0435\u043C \u0438 \u043F\u043E\u0442\u043E\u043C\u043A\u043E\u043C \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u044B");
5912
+ setTimeout(() => setDependencyError(null), 3e3);
5913
+ setSelectingPredecessorFor(null);
5914
+ return;
5915
+ }
5872
5916
  const successor = tasks.find((t) => t.id === successorTaskId);
5873
5917
  if (!successor) return;
5874
5918
  const alreadyExists = (successor.dependencies ?? []).some(
@@ -5884,8 +5928,11 @@ var TaskList = ({
5884
5928
  );
5885
5929
  const validation = validateDependencies(hypothetical);
5886
5930
  if (!validation.isValid) {
5887
- setCycleError(true);
5888
- setTimeout(() => setCycleError(false), 3e3);
5931
+ const hasHierarchyConstraint = validation.errors.some((error) => error.type === "constraint");
5932
+ setDependencyError(
5933
+ hasHierarchyConstraint ? "\u0421\u0432\u044F\u0437\u0438 \u043C\u0435\u0436\u0434\u0443 \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u0435\u043C \u0438 \u043F\u043E\u0442\u043E\u043C\u043A\u043E\u043C \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u044B" : "\u0426\u0438\u043A\u043B \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439!"
5934
+ );
5935
+ setTimeout(() => setDependencyError(null), 3e3);
5889
5936
  return;
5890
5937
  }
5891
5938
  const updatedTask = hypothetical.find((t) => t.id === successorTaskId);
@@ -6209,7 +6256,7 @@ var TaskList = ({
6209
6256
  lt
6210
6257
  )) }) })
6211
6258
  ] }),
6212
- cycleError && /* @__PURE__ */ jsx14("div", { className: "gantt-tl-dep-error", children: "\u0426\u0438\u043A\u043B \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439!" })
6259
+ dependencyError && /* @__PURE__ */ jsx14("div", { className: "gantt-tl-dep-error", children: dependencyError })
6213
6260
  ]
6214
6261
  },
6215
6262
  col.id
@@ -7010,6 +7057,7 @@ export {
7010
7057
  addBusinessDays,
7011
7058
  alignToWorkingDay,
7012
7059
  and,
7060
+ areTasksHierarchicallyRelated,
7013
7061
  buildAdjacencyList,
7014
7062
  buildTaskRangeFromEnd,
7015
7063
  buildTaskRangeFromStart,
@@ -7056,6 +7104,7 @@ export {
7056
7104
  getWeekSpans,
7057
7105
  getYearSpans,
7058
7106
  inDateRange,
7107
+ isAncestorTask,
7059
7108
  isTaskExpired,
7060
7109
  isTaskParent,
7061
7110
  isToday,