gantt-lib 0.145.1 → 0.146.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
@@ -2070,8 +2070,8 @@ function recalculateProjectSchedule(snapshot, options) {
2070
2070
  }
2071
2071
  }
2072
2072
  const queue = snapshot.filter((task) => (indegree.get(task.id) ?? 0) === 0).map((task) => task.id);
2073
- while (queue.length > 0) {
2074
- const currentId = queue.shift();
2073
+ for (let queueIndex = 0; queueIndex < queue.length; queueIndex++) {
2074
+ const currentId = queue[queueIndex];
2075
2075
  for (const successorId of successorIdsByTask.get(currentId) ?? []) {
2076
2076
  const nextIndegree = (indegree.get(successorId) ?? 0) - 1;
2077
2077
  indegree.set(successorId, nextIndegree);
@@ -2122,7 +2122,13 @@ function recalculateProjectSchedule(snapshot, options) {
2122
2122
  queue.push(successorId);
2123
2123
  }
2124
2124
  }
2125
- const parentsByDepth = snapshot.filter((task) => isTaskParent(task.id, snapshot)).map((task) => {
2125
+ const childrenByParent = /* @__PURE__ */ new Map();
2126
+ for (const task of snapshot) if (task.parentId) {
2127
+ const children = childrenByParent.get(task.parentId) ?? [];
2128
+ children.push(task.id);
2129
+ childrenByParent.set(task.parentId, children);
2130
+ }
2131
+ const parentsByDepth = snapshot.filter((task) => childrenByParent.has(task.id)).map((task) => {
2126
2132
  let depth = 0;
2127
2133
  let current = task.parentId ? workingMap.get(task.parentId) : void 0;
2128
2134
  while (current) {
@@ -2131,17 +2137,21 @@ function recalculateProjectSchedule(snapshot, options) {
2131
2137
  }
2132
2138
  return { taskId: task.id, depth };
2133
2139
  }).sort((left, right) => right.depth - left.depth);
2134
- const workingTasks = () => Array.from(workingMap.values());
2135
2140
  for (const { taskId } of parentsByDepth) {
2136
2141
  const parent = workingMap.get(taskId);
2137
2142
  if (!parent || parent.locked) {
2138
2143
  continue;
2139
2144
  }
2140
- const { startDate, endDate } = computeParentDates(taskId, workingTasks());
2145
+ let startTime = Infinity, endTime = -Infinity;
2146
+ for (const childId of childrenByParent.get(taskId)) {
2147
+ const child = workingMap.get(childId);
2148
+ startTime = Math.min(startTime, new Date(child.startDate).getTime());
2149
+ endTime = Math.max(endTime, new Date(child.endDate).getTime());
2150
+ }
2141
2151
  workingMap.set(taskId, {
2142
2152
  ...parent,
2143
- startDate: toIsoDate(startDate),
2144
- endDate: toIsoDate(endDate)
2153
+ startDate: toIsoDate(new Date(startTime)),
2154
+ endDate: toIsoDate(new Date(endTime))
2145
2155
  });
2146
2156
  }
2147
2157
  return createChangedResult(snapshot, Array.from(workingMap.values()));
@@ -7567,6 +7577,7 @@ var DepChip = ({
7567
7577
  weekendPredicate
7568
7578
  }) => {
7569
7579
  const [popoverOpen, setPopoverOpen] = useState6(false);
7580
+ const [typeMenuOpen, setTypeMenuOpen] = useState6(false);
7570
7581
  const lagAbs = Math.abs(lag ?? 0);
7571
7582
  const [inputAbs, setInputAbs] = useState6(lagAbs === 0 ? "" : String(lagAbs));
7572
7583
  useEffect6(() => {
@@ -7688,6 +7699,69 @@ var DepChip = ({
7688
7699
  const Icon = LINK_TYPE_ICONS[dep.type];
7689
7700
  const depName = predecessorName ?? dep.taskId;
7690
7701
  const effectiveLag = lag ?? 0;
7702
+ const handleTypeChange = useCallback6(
7703
+ (newType) => {
7704
+ if (newType === dep.type) return;
7705
+ if (!onTasksChange || !allTasks) return;
7706
+ if ((task.dependencies ?? []).some((d) => d.taskId === dep.taskId && d.type === newType)) return;
7707
+ const taskById = new Map(allTasks.map((t) => [t.id, t]));
7708
+ const predecessor = taskById.get(dep.taskId);
7709
+ if (!predecessor) return;
7710
+ const predStart = parseUTCDate(predecessor.startDate);
7711
+ const predEnd = predecessor.type === "milestone" ? predStart : parseUTCDate(predecessor.endDate);
7712
+ const origStart = parseUTCDate(task.startDate);
7713
+ const origEnd = parseUTCDate(task.endDate);
7714
+ const durationMs = origEnd.getTime() - origStart.getTime();
7715
+ const normalizedLag = normalizeDependencyLag(
7716
+ newType,
7717
+ effectiveLag,
7718
+ predStart,
7719
+ predEnd,
7720
+ businessDays,
7721
+ weekendPredicate,
7722
+ task.type
7723
+ );
7724
+ const constraintDate = calculateSuccessorDate(
7725
+ predStart,
7726
+ predEnd,
7727
+ newType,
7728
+ normalizedLag,
7729
+ businessDays,
7730
+ weekendPredicate,
7731
+ task.type
7732
+ );
7733
+ let newStart, newEnd;
7734
+ if (newType === "FS" || newType === "SS") {
7735
+ newStart = constraintDate;
7736
+ if (businessDays) {
7737
+ const businessDuration = getBusinessDaysCount(origStart, origEnd, weekendPredicate);
7738
+ newEnd = addBusinessDays(constraintDate, businessDuration, weekendPredicate);
7739
+ } else {
7740
+ newEnd = new Date(constraintDate.getTime() + durationMs);
7741
+ }
7742
+ } else {
7743
+ newEnd = constraintDate;
7744
+ if (businessDays) {
7745
+ const businessDuration = getBusinessDaysCount(origStart, origEnd, weekendPredicate);
7746
+ newStart = subtractBusinessDays(constraintDate, businessDuration, weekendPredicate);
7747
+ } else {
7748
+ newStart = new Date(constraintDate.getTime() - durationMs);
7749
+ }
7750
+ }
7751
+ onTasksChange([
7752
+ {
7753
+ ...task,
7754
+ startDate: newStart.toISOString().split("T")[0],
7755
+ endDate: newEnd.toISOString().split("T")[0],
7756
+ dependencies: (task.dependencies ?? []).map(
7757
+ (existingDep) => existingDep.taskId === dep.taskId && existingDep.type === dep.type ? { ...existingDep, type: newType, lag: normalizedLag } : existingDep
7758
+ )
7759
+ }
7760
+ ]);
7761
+ onChipSelect?.({ successorId: taskId, predecessorId: dep.taskId, linkType: newType });
7762
+ },
7763
+ [dep.type, dep.taskId, task, allTasks, onTasksChange, businessDays, weekendPredicate, effectiveLag, onChipSelect, taskId]
7764
+ );
7691
7765
  const actionVerb = dep.type === "FS" || dep.type === "SS" ? "\u043D\u0430\u0447\u0430\u0442\u044C" : "\u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C";
7692
7766
  const zeroPlaceholder = dep.type === "SF" ? "\u0447\u0451\u0442\u043A\u043E" : dep.type === "FF" ? "\u0432\u043C\u0435\u0441\u0442\u0435" : dep.type === "SS" ? "\u0432\u043C\u0435\u0441\u0442\u0435" : "\u0441\u0440\u0430\u0437\u0443";
7693
7767
  let afterWhat;
@@ -7819,27 +7893,71 @@ var DepChip = ({
7819
7893
  !disableDependencyEditing && /* @__PURE__ */ jsxs10(Fragment4, { children: [
7820
7894
  /* @__PURE__ */ jsx15("hr", { className: "gantt-tl-dep-edit-divider" }),
7821
7895
  /* @__PURE__ */ jsxs10("div", { className: "gantt-tl-dep-edit-actions", children: [
7822
- /* @__PURE__ */ jsx15(
7823
- "button",
7824
- {
7825
- type: "button",
7826
- className: "gantt-tl-dep-edit-close",
7827
- onClick: () => {
7828
- setPopoverOpen(false);
7829
- onChipSelectClear();
7830
- },
7831
- children: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"
7832
- }
7833
- ),
7834
- /* @__PURE__ */ jsx15(
7835
- "button",
7836
- {
7837
- type: "button",
7838
- className: "gantt-tl-dep-edit-delete",
7839
- onClick: handleTrashClick,
7840
- children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C"
7841
- }
7842
- )
7896
+ /* @__PURE__ */ jsxs10(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
7897
+ /* @__PURE__ */ jsx15(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs10(
7898
+ "button",
7899
+ {
7900
+ type: "button",
7901
+ className: "gantt-tl-dep-edit-type-trigger",
7902
+ onClick: (e) => e.stopPropagation(),
7903
+ "aria-label": "\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u0442\u0438\u043F \u0441\u0432\u044F\u0437\u0438",
7904
+ title: "\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u0442\u0438\u043F \u0441\u0432\u044F\u0437\u0438",
7905
+ children: [
7906
+ /* @__PURE__ */ jsx15(Icon, {}),
7907
+ /* @__PURE__ */ jsx15("span", { children: LINK_TYPE_LABELS_RU2[dep.type] }),
7908
+ /* @__PURE__ */ jsx15("span", { className: "gantt-tl-dep-edit-type-caret", "aria-hidden": "true", children: "\u25BE" })
7909
+ ]
7910
+ }
7911
+ ) }),
7912
+ /* @__PURE__ */ jsx15(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ jsx15("div", { className: "gantt-tl-dep-type-menu", children: LINK_TYPE_ORDER.map((linkType) => {
7913
+ const TypeIcon = LINK_TYPE_ICONS[linkType];
7914
+ const isCurrent = linkType === dep.type;
7915
+ const isDuplicate = !isCurrent && (task.dependencies ?? []).some(
7916
+ (d) => d.taskId === dep.taskId && d.type === linkType
7917
+ );
7918
+ return /* @__PURE__ */ jsxs10(
7919
+ "button",
7920
+ {
7921
+ type: "button",
7922
+ className: `gantt-tl-dep-type-option${isCurrent ? " active" : ""}${isDuplicate ? " gantt-tl-dep-type-option-duplicate" : ""}`,
7923
+ disabled: isDuplicate,
7924
+ onClick: () => {
7925
+ handleTypeChange(linkType);
7926
+ setTypeMenuOpen(false);
7927
+ },
7928
+ title: isDuplicate ? "\u0421\u0432\u044F\u0437\u044C \u0442\u0430\u043A\u043E\u0433\u043E \u0442\u0438\u043F\u0430 \u0443\u0436\u0435 \u0435\u0441\u0442\u044C" : `\u0422\u0438\u043F \u0441\u0432\u044F\u0437\u0438 ${LINK_TYPE_LABELS_RU2[linkType]}`,
7929
+ children: [
7930
+ /* @__PURE__ */ jsx15(TypeIcon, {}),
7931
+ /* @__PURE__ */ jsx15("span", { children: LINK_TYPE_LABELS_RU2[linkType] })
7932
+ ]
7933
+ },
7934
+ linkType
7935
+ );
7936
+ }) }) })
7937
+ ] }),
7938
+ /* @__PURE__ */ jsxs10("div", { className: "gantt-tl-dep-edit-actions-right", children: [
7939
+ /* @__PURE__ */ jsx15(
7940
+ "button",
7941
+ {
7942
+ type: "button",
7943
+ className: "gantt-tl-dep-edit-close",
7944
+ onClick: () => {
7945
+ setPopoverOpen(false);
7946
+ onChipSelectClear();
7947
+ },
7948
+ children: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"
7949
+ }
7950
+ ),
7951
+ /* @__PURE__ */ jsx15(
7952
+ "button",
7953
+ {
7954
+ type: "button",
7955
+ className: "gantt-tl-dep-edit-delete",
7956
+ onClick: handleTrashClick,
7957
+ children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C"
7958
+ }
7959
+ )
7960
+ ] })
7843
7961
  ] })
7844
7962
  ] })
7845
7963
  ] })
@@ -9345,7 +9463,7 @@ var TaskListRow = React11.memo(
9345
9463
  {
9346
9464
  className: "gantt-tl-dep-overflow-list",
9347
9465
  onClick: (e) => e.stopPropagation(),
9348
- children: chips.map(({ dep, lag, predecessorName }) => /* @__PURE__ */ jsx15(
9466
+ children: chips.map(({ dep, lag, predecessorName }, chipIndex) => /* @__PURE__ */ jsx15(
9349
9467
  DepChip,
9350
9468
  {
9351
9469
  lag,
@@ -9367,7 +9485,7 @@ var TaskListRow = React11.memo(
9367
9485
  businessDays,
9368
9486
  weekendPredicate
9369
9487
  },
9370
- `${dep.taskId}-${dep.type}`
9488
+ `${dep.taskId}-${chipIndex}`
9371
9489
  ))
9372
9490
  }
9373
9491
  ) })
@@ -9395,7 +9513,8 @@ var TaskListRow = React11.memo(
9395
9513
  onTasksChange,
9396
9514
  businessDays,
9397
9515
  weekendPredicate
9398
- }
9516
+ },
9517
+ chips[0].dep.taskId
9399
9518
  )
9400
9519
  ) : null,
9401
9520
  !printDependencyLabels && !disableDependencyEditing && !isPicking && /* @__PURE__ */ jsx15(