gantt-lib 0.64.0 → 0.70.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/core/scheduling/index.d.mts +1 -1
- package/dist/core/scheduling/index.d.ts +1 -1
- package/dist/core/scheduling/index.js +46 -11
- package/dist/core/scheduling/index.js.map +1 -1
- package/dist/core/scheduling/index.mjs +45 -11
- package/dist/core/scheduling/index.mjs.map +1 -1
- package/dist/{index-DMA7NbWe.d.mts → index-DlJm2l_7.d.mts} +16 -1
- package/dist/{index-DMA7NbWe.d.ts → index-DlJm2l_7.d.ts} +16 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +41 -3
- package/dist/index.d.ts +41 -3
- package/dist/index.js +332 -101
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +328 -101
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +10 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -499,6 +499,12 @@ init_dateMath();
|
|
|
499
499
|
|
|
500
500
|
// src/core/scheduling/dependencies.ts
|
|
501
501
|
init_dateMath();
|
|
502
|
+
function normalizePredecessorDates(predecessor, parseDateFn) {
|
|
503
|
+
const predStart = parseDateFn(predecessor.startDate);
|
|
504
|
+
const isMilestone = predecessor.type === "milestone";
|
|
505
|
+
const predEnd = isMilestone ? new Date(predStart.getTime() - DAY_MS) : parseDateFn(predecessor.endDate);
|
|
506
|
+
return { predStart, predEnd };
|
|
507
|
+
}
|
|
502
508
|
function getDependencyLag(dep) {
|
|
503
509
|
return Number.isFinite(dep.lag) ? dep.lag : 0;
|
|
504
510
|
}
|
|
@@ -774,8 +780,7 @@ function clampTaskRangeForIncomingFS(task, proposedStart, proposedEnd, allTasks,
|
|
|
774
780
|
if (!predecessor) {
|
|
775
781
|
continue;
|
|
776
782
|
}
|
|
777
|
-
const predecessorStart =
|
|
778
|
-
const predecessorEnd = parseDateOnly(predecessor.endDate);
|
|
783
|
+
const { predStart: predecessorStart, predEnd: predecessorEnd } = normalizePredecessorDates(predecessor, parseDateOnly);
|
|
779
784
|
const predecessorDuration = getTaskDuration(
|
|
780
785
|
predecessorStart,
|
|
781
786
|
predecessorEnd,
|
|
@@ -811,8 +816,10 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks, busin
|
|
|
811
816
|
if (!predecessor) {
|
|
812
817
|
return { ...dep, lag: getDependencyLag(dep) };
|
|
813
818
|
}
|
|
814
|
-
const predecessorStart =
|
|
815
|
-
|
|
819
|
+
const { predStart: predecessorStart, predEnd: predecessorEnd } = normalizePredecessorDates(
|
|
820
|
+
predecessor,
|
|
821
|
+
(d) => new Date(d instanceof Date ? d.getTime() : `${String(d).split("T")[0]}T00:00:00.000Z`)
|
|
822
|
+
);
|
|
816
823
|
const nextLag = computeLagFromDates(
|
|
817
824
|
dep.type,
|
|
818
825
|
predecessorStart,
|
|
@@ -827,6 +834,12 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks, busin
|
|
|
827
834
|
}
|
|
828
835
|
|
|
829
836
|
// src/core/scheduling/cascade.ts
|
|
837
|
+
function parseCascadeDateInput(date) {
|
|
838
|
+
if (date instanceof Date) {
|
|
839
|
+
return normalizeUTCDate(date);
|
|
840
|
+
}
|
|
841
|
+
return normalizeUTCDate(/* @__PURE__ */ new Date(`${date.split("T")[0]}T00:00:00.000Z`));
|
|
842
|
+
}
|
|
830
843
|
function getSuccessorChain(draggedTaskId, allTasks, linkTypes = ["FS"]) {
|
|
831
844
|
const successorMap = /* @__PURE__ */ new Map();
|
|
832
845
|
for (const task of allTasks) {
|
|
@@ -905,7 +918,21 @@ function cascadeByLinks(movedTaskId, newStart, newEnd, allTasks, skipChildCascad
|
|
|
905
918
|
const origStart = new Date(orig.startDate);
|
|
906
919
|
const origEnd = new Date(orig.endDate);
|
|
907
920
|
const duration = getTaskDuration(origStart, origEnd);
|
|
908
|
-
const
|
|
921
|
+
const currentTask = taskById.get(currentId);
|
|
922
|
+
const { predStart: normalizedPredStart, predEnd: normalizedPredEnd } = normalizePredecessorDates(
|
|
923
|
+
{
|
|
924
|
+
startDate: predStart,
|
|
925
|
+
endDate: predEnd,
|
|
926
|
+
type: currentTask.type
|
|
927
|
+
},
|
|
928
|
+
parseCascadeDateInput
|
|
929
|
+
);
|
|
930
|
+
const constraintDate = calculateSuccessorDate(
|
|
931
|
+
normalizedPredStart,
|
|
932
|
+
normalizedPredEnd,
|
|
933
|
+
dep.type,
|
|
934
|
+
getDependencyLag(dep)
|
|
935
|
+
);
|
|
909
936
|
let newSuccStart;
|
|
910
937
|
let newSuccEnd;
|
|
911
938
|
if (dep.type === "FS" || dep.type === "SS") {
|
|
@@ -1060,9 +1087,17 @@ function universalCascade(movedTask, newStart, newEnd, allTasks, businessDays =
|
|
|
1060
1087
|
if (!dep) continue;
|
|
1061
1088
|
const origStart = new Date(task.startDate);
|
|
1062
1089
|
const origEnd = new Date(task.endDate);
|
|
1090
|
+
const { predStart: normalizedPredStart, predEnd: normalizedPredEnd } = normalizePredecessorDates(
|
|
1091
|
+
{
|
|
1092
|
+
startDate: currStart,
|
|
1093
|
+
endDate: currEnd,
|
|
1094
|
+
type: currentOriginal.type
|
|
1095
|
+
},
|
|
1096
|
+
parseCascadeDateInput
|
|
1097
|
+
);
|
|
1063
1098
|
const constraintDate = calculateSuccessorDate(
|
|
1064
|
-
|
|
1065
|
-
|
|
1099
|
+
normalizedPredStart,
|
|
1100
|
+
normalizedPredEnd,
|
|
1066
1101
|
dep.type,
|
|
1067
1102
|
getDependencyLag(dep),
|
|
1068
1103
|
businessDays,
|
|
@@ -1279,8 +1314,7 @@ function recalculateTaskFromDependencies(taskId, snapshot, options) {
|
|
|
1279
1314
|
for (const dep of task.dependencies) {
|
|
1280
1315
|
const predecessor = snapshot.find((t) => t.id === dep.taskId);
|
|
1281
1316
|
if (!predecessor) continue;
|
|
1282
|
-
const predStart =
|
|
1283
|
-
const predEnd = parseDateOnly(predecessor.endDate);
|
|
1317
|
+
const { predStart, predEnd } = normalizePredecessorDates(predecessor, parseDateOnly);
|
|
1284
1318
|
const constraintDate = calculateSuccessorDate(
|
|
1285
1319
|
predStart,
|
|
1286
1320
|
predEnd,
|
|
@@ -1396,8 +1430,7 @@ function recalculateProjectSchedule(snapshot, options) {
|
|
|
1396
1430
|
if (!predecessor) {
|
|
1397
1431
|
continue;
|
|
1398
1432
|
}
|
|
1399
|
-
const predecessorStart =
|
|
1400
|
-
const predecessorEnd = parseDateOnly(predecessor.endDate);
|
|
1433
|
+
const { predStart: predecessorStart, predEnd: predecessorEnd } = normalizePredecessorDates(predecessor, parseDateOnly);
|
|
1401
1434
|
const constraintDate = calculateSuccessorDate(
|
|
1402
1435
|
predecessorStart,
|
|
1403
1436
|
predecessorEnd,
|
|
@@ -1816,6 +1849,45 @@ var calculateTaskBar = (taskStartDate, taskEndDate, monthStart, dayWidth) => {
|
|
|
1816
1849
|
const width = Math.round((duration + 1) * dayWidth);
|
|
1817
1850
|
return { left, width };
|
|
1818
1851
|
};
|
|
1852
|
+
var calculateMilestoneGeometry = (taskDate, monthStart, dayWidth, size = 14) => {
|
|
1853
|
+
const { left, width } = calculateTaskBar(taskDate, taskDate, monthStart, dayWidth);
|
|
1854
|
+
const centerX = Math.round(left + width / 2);
|
|
1855
|
+
const halfSize = Math.round(size / 2);
|
|
1856
|
+
return {
|
|
1857
|
+
centerX,
|
|
1858
|
+
left: centerX - halfSize,
|
|
1859
|
+
right: centerX + halfSize,
|
|
1860
|
+
size
|
|
1861
|
+
};
|
|
1862
|
+
};
|
|
1863
|
+
var calculateMilestoneConnectionBounds = (dayLeft, dayWidth, size = 14) => {
|
|
1864
|
+
const halfDiagonal = Math.round(size / Math.SQRT2);
|
|
1865
|
+
const visualNudge = 2;
|
|
1866
|
+
return {
|
|
1867
|
+
left: dayLeft + halfDiagonal + visualNudge,
|
|
1868
|
+
right: dayLeft + dayWidth - halfDiagonal - visualNudge
|
|
1869
|
+
};
|
|
1870
|
+
};
|
|
1871
|
+
var resolveTaskHorizontalGeometry = (task, monthStart, dayWidth, override) => {
|
|
1872
|
+
const startDate = new Date(task.startDate);
|
|
1873
|
+
const endDate = new Date(task.endDate);
|
|
1874
|
+
if (task.type === "milestone") {
|
|
1875
|
+
const size = 14;
|
|
1876
|
+
if (override) {
|
|
1877
|
+
return calculateMilestoneConnectionBounds(override.left, dayWidth, size);
|
|
1878
|
+
}
|
|
1879
|
+
const bar2 = calculateTaskBar(startDate, startDate, monthStart, dayWidth);
|
|
1880
|
+
return calculateMilestoneConnectionBounds(bar2.left, dayWidth, size);
|
|
1881
|
+
}
|
|
1882
|
+
if (override) {
|
|
1883
|
+
return {
|
|
1884
|
+
left: override.left,
|
|
1885
|
+
right: override.left + override.width
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
const bar = calculateTaskBar(startDate, endDate, monthStart, dayWidth);
|
|
1889
|
+
return { left: bar.left, right: bar.left + bar.width };
|
|
1890
|
+
};
|
|
1819
1891
|
var pixelsToDate = (pixels, monthStart, dayWidth) => {
|
|
1820
1892
|
const days = Math.round(pixels / dayWidth);
|
|
1821
1893
|
return new Date(Date.UTC(
|
|
@@ -1919,6 +1991,9 @@ var calculateDependencyPath = (from, to, arrivesFromRight) => {
|
|
|
1919
1991
|
if (fy === ty) {
|
|
1920
1992
|
return `M ${fx} ${fy} H ${tx}`;
|
|
1921
1993
|
}
|
|
1994
|
+
if (fx === tx) {
|
|
1995
|
+
return `M ${fx} ${fy} V ${ty}`;
|
|
1996
|
+
}
|
|
1922
1997
|
const C = 2;
|
|
1923
1998
|
const goingDown = ty > fy;
|
|
1924
1999
|
const dirY = goingDown ? 1 : -1;
|
|
@@ -2026,6 +2101,37 @@ var isTaskExpired = (task, referenceDate = /* @__PURE__ */ new Date()) => {
|
|
|
2026
2101
|
return actualProgress < expectedProgress;
|
|
2027
2102
|
};
|
|
2028
2103
|
|
|
2104
|
+
// src/utils/taskType.ts
|
|
2105
|
+
init_dateUtils();
|
|
2106
|
+
var TASK_TYPE_DEFAULT = "task";
|
|
2107
|
+
function getTaskType(task) {
|
|
2108
|
+
return task.type ?? TASK_TYPE_DEFAULT;
|
|
2109
|
+
}
|
|
2110
|
+
function isMilestoneTask(task) {
|
|
2111
|
+
return getTaskType(task) === "milestone";
|
|
2112
|
+
}
|
|
2113
|
+
function normalizeMilestoneStartDate(startDateInput) {
|
|
2114
|
+
const parsedStartDate = parseUTCDate(startDateInput);
|
|
2115
|
+
if (startDateInput instanceof Date) {
|
|
2116
|
+
return new Date(Date.UTC(
|
|
2117
|
+
parsedStartDate.getUTCFullYear(),
|
|
2118
|
+
parsedStartDate.getUTCMonth(),
|
|
2119
|
+
parsedStartDate.getUTCDate()
|
|
2120
|
+
));
|
|
2121
|
+
}
|
|
2122
|
+
return parsedStartDate.toISOString().split("T")[0];
|
|
2123
|
+
}
|
|
2124
|
+
function normalizeTaskDatesForType(task) {
|
|
2125
|
+
if (!isMilestoneTask(task)) {
|
|
2126
|
+
return task;
|
|
2127
|
+
}
|
|
2128
|
+
const startDate = normalizeMilestoneStartDate(task.startDate);
|
|
2129
|
+
return {
|
|
2130
|
+
...task,
|
|
2131
|
+
endDate: startDate
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2029
2135
|
// src/hooks/useTaskDrag.ts
|
|
2030
2136
|
import { useEffect, useRef, useState, useCallback } from "react";
|
|
2031
2137
|
|
|
@@ -2044,6 +2150,17 @@ function resolveDateRangeFromPixels(mode, left, width, monthStart, dayWidth, tas
|
|
|
2044
2150
|
monthStart.getUTCMonth(),
|
|
2045
2151
|
monthStart.getUTCDate() + rawEndOffset
|
|
2046
2152
|
));
|
|
2153
|
+
const isMilestone = task.type === "milestone";
|
|
2154
|
+
if (isMilestone) {
|
|
2155
|
+
const anchorDate = mode === "resize-right" ? rawEndDate : rawStartDate;
|
|
2156
|
+
if (businessDays && weekendPredicate) {
|
|
2157
|
+
const originalAnchor = mode === "resize-right" ? new Date(task.endDate) : new Date(task.startDate);
|
|
2158
|
+
const snapDirection2 = anchorDate.getTime() >= originalAnchor.getTime() ? 1 : -1;
|
|
2159
|
+
const alignedDate = alignToWorkingDay(anchorDate, snapDirection2, weekendPredicate);
|
|
2160
|
+
return { start: alignedDate, end: alignedDate };
|
|
2161
|
+
}
|
|
2162
|
+
return { start: anchorDate, end: anchorDate };
|
|
2163
|
+
}
|
|
2047
2164
|
if (!(businessDays && weekendPredicate)) {
|
|
2048
2165
|
return { start: rawStartDate, end: rawEndDate };
|
|
2049
2166
|
}
|
|
@@ -2134,8 +2251,10 @@ function handleGlobalMouseMove(e) {
|
|
|
2134
2251
|
const activeDrag = globalActiveDrag;
|
|
2135
2252
|
const { startX, initialLeft, initialWidth, mode, dayWidth, onProgress, allTasks } = activeDrag;
|
|
2136
2253
|
const deltaX = e.clientX - startX;
|
|
2254
|
+
const draggedTask = allTasks.find((t) => t.id === activeDrag.taskId);
|
|
2255
|
+
const effectiveWidth = draggedTask && isMilestoneTask(draggedTask) ? dayWidth : initialWidth;
|
|
2137
2256
|
let newLeft = initialLeft;
|
|
2138
|
-
let newWidth =
|
|
2257
|
+
let newWidth = effectiveWidth;
|
|
2139
2258
|
switch (mode) {
|
|
2140
2259
|
case "move":
|
|
2141
2260
|
newLeft = snapToGrid(initialLeft + deltaX, dayWidth);
|
|
@@ -2151,7 +2270,6 @@ function handleGlobalMouseMove(e) {
|
|
|
2151
2270
|
newWidth = Math.max(dayWidth, snappedWidth);
|
|
2152
2271
|
break;
|
|
2153
2272
|
}
|
|
2154
|
-
const draggedTask = allTasks.find((t) => t.id === activeDrag.taskId);
|
|
2155
2273
|
if (activeDrag.businessDays && activeDrag.weekendPredicate && draggedTask) {
|
|
2156
2274
|
const previewRange = clampDateRangeForIncomingFS(
|
|
2157
2275
|
draggedTask,
|
|
@@ -2193,6 +2311,9 @@ function handleGlobalMouseMove(e) {
|
|
|
2193
2311
|
newLeft = Math.round(alignedStartDay * dayWidth);
|
|
2194
2312
|
newWidth = Math.round((alignedEndDay - alignedStartDay + 1) * dayWidth);
|
|
2195
2313
|
}
|
|
2314
|
+
if (draggedTask && isMilestoneTask(draggedTask)) {
|
|
2315
|
+
newWidth = dayWidth;
|
|
2316
|
+
}
|
|
2196
2317
|
if (!activeDrag.disableConstraints && activeDrag.onCascadeProgress) {
|
|
2197
2318
|
const { dayWidth: dayWidth2, monthStart: mStart, taskId: dragId } = activeDrag;
|
|
2198
2319
|
const originalDraggedTask = draggedTask ?? allTasks.find((t) => t.id === dragId);
|
|
@@ -2229,7 +2350,8 @@ function handleGlobalMouseMove(e) {
|
|
|
2229
2350
|
};
|
|
2230
2351
|
})();
|
|
2231
2352
|
const previewStartDate = previewRange.start;
|
|
2232
|
-
const
|
|
2353
|
+
const isMilestone = originalDraggedTask ? isMilestoneTask(originalDraggedTask) : false;
|
|
2354
|
+
const previewEndDate = isMilestone ? previewRange.start : previewRange.end;
|
|
2233
2355
|
const movedTaskData = originalDraggedTask ?? { id: dragId, name: "", startDate: "", endDate: "" };
|
|
2234
2356
|
const cascadeResult = universalCascade(
|
|
2235
2357
|
{ ...movedTaskData, startDate: previewStartDate.toISOString(), endDate: previewEndDate.toISOString() },
|
|
@@ -2319,6 +2441,9 @@ var useTaskDrag = (options) => {
|
|
|
2319
2441
|
businessDays = true,
|
|
2320
2442
|
weekendPredicate
|
|
2321
2443
|
} = options;
|
|
2444
|
+
const rawHookTask = allTasks.find((t) => t.id === taskId);
|
|
2445
|
+
const hookTask = rawHookTask ? normalizeTaskDatesForType(rawHookTask) : void 0;
|
|
2446
|
+
const hookTaskIsMilestone = hookTask ? isMilestoneTask(hookTask) : false;
|
|
2322
2447
|
const isOwnerRef = useRef(false);
|
|
2323
2448
|
const effectiveLocked = locked || disableTaskDrag;
|
|
2324
2449
|
const [isDragging, setIsDragging] = useState(false);
|
|
@@ -2340,11 +2465,11 @@ var useTaskDrag = (options) => {
|
|
|
2340
2465
|
return Math.round((ms1 - ms2) / (1e3 * 60 * 60 * 24));
|
|
2341
2466
|
};
|
|
2342
2467
|
const startOffset = getUTCDayDifference2(initialStartDate, monthStart);
|
|
2343
|
-
const duration = getUTCDayDifference2(initialEndDate, initialStartDate);
|
|
2468
|
+
const duration = hookTaskIsMilestone ? 0 : getUTCDayDifference2(initialEndDate, initialStartDate);
|
|
2344
2469
|
const left = Math.round(startOffset * dayWidth);
|
|
2345
2470
|
const width = Math.round((duration + 1) * dayWidth);
|
|
2346
2471
|
return { left, width };
|
|
2347
|
-
}, [initialStartDate, initialEndDate, monthStart, dayWidth]);
|
|
2472
|
+
}, [initialStartDate, initialEndDate, monthStart, dayWidth, hookTaskIsMilestone]);
|
|
2348
2473
|
useEffect(() => {
|
|
2349
2474
|
if (isOwnerRef.current && globalActiveDrag) return;
|
|
2350
2475
|
const { left, width } = getInitialPosition();
|
|
@@ -2379,7 +2504,8 @@ var useTaskDrag = (options) => {
|
|
|
2379
2504
|
const handleComplete = useCallback((finalLeft, finalWidth, finalMode) => {
|
|
2380
2505
|
const wasOwner = isOwnerRef.current;
|
|
2381
2506
|
isOwnerRef.current = false;
|
|
2382
|
-
const
|
|
2507
|
+
const currentTaskRaw = allTasks.find((t) => t.id === taskId);
|
|
2508
|
+
const currentTask = currentTaskRaw ? normalizeTaskDatesForType(currentTaskRaw) : void 0;
|
|
2383
2509
|
const finalRange = currentTask ? clampDateRangeForIncomingFS(
|
|
2384
2510
|
currentTask,
|
|
2385
2511
|
resolveDateRangeFromPixels(
|
|
@@ -2413,7 +2539,7 @@ var useTaskDrag = (options) => {
|
|
|
2413
2539
|
};
|
|
2414
2540
|
})();
|
|
2415
2541
|
const newStartDate = finalRange.start;
|
|
2416
|
-
const newEndDate = finalRange.end;
|
|
2542
|
+
const newEndDate = currentTask && isMilestoneTask(currentTask) ? finalRange.start : finalRange.end;
|
|
2417
2543
|
setIsDragging(false);
|
|
2418
2544
|
setDragMode(null);
|
|
2419
2545
|
if (onDragStateChange) {
|
|
@@ -2425,6 +2551,23 @@ var useTaskDrag = (options) => {
|
|
|
2425
2551
|
});
|
|
2426
2552
|
}
|
|
2427
2553
|
if (wasOwner) {
|
|
2554
|
+
const startUnchanged = newStartDate.getTime() === Date.UTC(
|
|
2555
|
+
initialStartDate.getUTCFullYear(),
|
|
2556
|
+
initialStartDate.getUTCMonth(),
|
|
2557
|
+
initialStartDate.getUTCDate()
|
|
2558
|
+
);
|
|
2559
|
+
const baselineEndDate = hookTaskIsMilestone ? initialStartDate : initialEndDate;
|
|
2560
|
+
const endUnchanged = newEndDate.getTime() === Date.UTC(
|
|
2561
|
+
baselineEndDate.getUTCFullYear(),
|
|
2562
|
+
baselineEndDate.getUTCMonth(),
|
|
2563
|
+
baselineEndDate.getUTCDate()
|
|
2564
|
+
);
|
|
2565
|
+
if (startUnchanged && endUnchanged) {
|
|
2566
|
+
const { left, width } = getInitialPosition();
|
|
2567
|
+
setCurrentLeft(left);
|
|
2568
|
+
setCurrentWidth(width);
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2428
2571
|
if (!disableConstraints && onCascade && allTasks.length > 0) {
|
|
2429
2572
|
const draggedTaskData = currentTask;
|
|
2430
2573
|
const movedTask = {
|
|
@@ -2462,7 +2605,8 @@ var useTaskDrag = (options) => {
|
|
|
2462
2605
|
businessDays,
|
|
2463
2606
|
weekendPredicate,
|
|
2464
2607
|
initialStartDate,
|
|
2465
|
-
initialEndDate
|
|
2608
|
+
initialEndDate,
|
|
2609
|
+
hookTaskIsMilestone
|
|
2466
2610
|
]);
|
|
2467
2611
|
const handleCancel = useCallback(() => {
|
|
2468
2612
|
isOwnerRef.current = false;
|
|
@@ -2505,6 +2649,9 @@ var useTaskDrag = (options) => {
|
|
|
2505
2649
|
if (currentTask2 && isTaskParent(taskId, allTasks)) {
|
|
2506
2650
|
mode = "move";
|
|
2507
2651
|
}
|
|
2652
|
+
if (currentTask2 && isMilestoneTask(currentTask2)) {
|
|
2653
|
+
mode = "move";
|
|
2654
|
+
}
|
|
2508
2655
|
}
|
|
2509
2656
|
if (!mode) {
|
|
2510
2657
|
return;
|
|
@@ -2590,14 +2737,16 @@ var useTaskDrag = (options) => {
|
|
|
2590
2737
|
// src/components/TaskRow/TaskRow.tsx
|
|
2591
2738
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2592
2739
|
var arePropsEqual = (prevProps, nextProps) => {
|
|
2593
|
-
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.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.allTasks === nextProps.allTasks && prevProps.disableConstraints === nextProps.disableConstraints && prevProps.task.locked === nextProps.task.locked && prevProps.task.divider === nextProps.task.divider && prevProps.highlightExpiredTasks === nextProps.highlightExpiredTasks && prevProps.isFilterMatch === nextProps.isFilterMatch && prevProps.businessDays === nextProps.businessDays && prevProps.customDays === nextProps.customDays && prevProps.isWeekend === nextProps.isWeekend && prevProps.disableTaskDrag === nextProps.disableTaskDrag;
|
|
2740
|
+
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.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.allTasks === nextProps.allTasks && prevProps.disableConstraints === nextProps.disableConstraints && prevProps.task.locked === nextProps.task.locked && prevProps.task.divider === nextProps.task.divider && prevProps.highlightExpiredTasks === nextProps.highlightExpiredTasks && prevProps.isFilterMatch === nextProps.isFilterMatch && prevProps.businessDays === nextProps.businessDays && prevProps.customDays === nextProps.customDays && prevProps.isWeekend === nextProps.isWeekend && prevProps.disableTaskDrag === nextProps.disableTaskDrag;
|
|
2594
2741
|
};
|
|
2595
2742
|
var TaskRow = React2.memo(
|
|
2596
2743
|
({ task, monthStart, dayWidth, rowHeight, onTasksChange, onDragStateChange, rowIndex, allTasks, enableAutoSchedule, disableConstraints, overridePosition, onCascadeProgress, onCascade, divider, highlightExpiredTasks, isFilterMatch = false, businessDays, customDays, isWeekend: isWeekend3, disableTaskDrag = false }) => {
|
|
2597
2744
|
const defaultParentBarColor = "#782FC4";
|
|
2598
2745
|
const { divider: taskDivider } = task;
|
|
2599
|
-
const
|
|
2600
|
-
const
|
|
2746
|
+
const normalizedTask = useMemo2(() => normalizeTaskDatesForType(task), [task]);
|
|
2747
|
+
const milestone = useMemo2(() => isMilestoneTask(normalizedTask), [normalizedTask]);
|
|
2748
|
+
const taskStartDate = useMemo2(() => parseUTCDate(normalizedTask.startDate), [normalizedTask.startDate]);
|
|
2749
|
+
const taskEndDate = useMemo2(() => parseUTCDate(normalizedTask.endDate), [normalizedTask.endDate]);
|
|
2601
2750
|
const isParent = useMemo2(() => {
|
|
2602
2751
|
return allTasks ? isTaskParent(task.id, allTasks) : false;
|
|
2603
2752
|
}, [allTasks, task.id]);
|
|
@@ -2606,12 +2755,16 @@ var TaskRow = React2.memo(
|
|
|
2606
2755
|
}, [allTasks, task.id]);
|
|
2607
2756
|
const isExpired = useMemo2(() => {
|
|
2608
2757
|
if (!highlightExpiredTasks) return false;
|
|
2609
|
-
return isTaskExpired(
|
|
2610
|
-
}, [
|
|
2758
|
+
return isTaskExpired(normalizedTask);
|
|
2759
|
+
}, [normalizedTask.startDate, normalizedTask.endDate, normalizedTask.progress, highlightExpiredTasks]);
|
|
2611
2760
|
const { left, width } = useMemo2(
|
|
2612
2761
|
() => calculateTaskBar(taskStartDate, taskEndDate, monthStart, dayWidth),
|
|
2613
2762
|
[taskStartDate, taskEndDate, monthStart, dayWidth]
|
|
2614
2763
|
);
|
|
2764
|
+
const milestoneGeometry = useMemo2(
|
|
2765
|
+
() => calculateMilestoneGeometry(taskStartDate, monthStart, dayWidth),
|
|
2766
|
+
[taskStartDate, monthStart, dayWidth]
|
|
2767
|
+
);
|
|
2615
2768
|
const barColor = isExpired ? "var(--gantt-expired-color)" : task.color || "var(--gantt-task-bar-default-color)";
|
|
2616
2769
|
const progressWidth = useMemo2(() => {
|
|
2617
2770
|
if (task.progress === void 0 || task.progress <= 0) return 0;
|
|
@@ -2650,7 +2803,7 @@ var TaskRow = React2.memo(
|
|
|
2650
2803
|
}, [defaultParentBarColor, isExpired, isParent, progressWidth, barColor, progressColor, task.color]);
|
|
2651
2804
|
const handleDragEnd = (result) => {
|
|
2652
2805
|
const updatedTask = {
|
|
2653
|
-
...
|
|
2806
|
+
...normalizedTask,
|
|
2654
2807
|
startDate: result.startDate.toISOString(),
|
|
2655
2808
|
endDate: result.endDate.toISOString(),
|
|
2656
2809
|
...result.updatedDependencies !== void 0 && { dependencies: result.updatedDependencies }
|
|
@@ -2689,8 +2842,20 @@ var TaskRow = React2.memo(
|
|
|
2689
2842
|
});
|
|
2690
2843
|
const displayLeft = overridePosition?.left ?? (isDragging ? currentLeft : left);
|
|
2691
2844
|
const displayWidth = overridePosition?.width ?? (isDragging ? currentWidth : width);
|
|
2845
|
+
const displayMilestoneGeometry = useMemo2(() => {
|
|
2846
|
+
const centerX = Math.round(displayLeft + dayWidth / 2);
|
|
2847
|
+
const halfSize = Math.round(milestoneGeometry.size / 2);
|
|
2848
|
+
return {
|
|
2849
|
+
centerX,
|
|
2850
|
+
left: centerX - halfSize,
|
|
2851
|
+
right: centerX + halfSize,
|
|
2852
|
+
size: milestoneGeometry.size
|
|
2853
|
+
};
|
|
2854
|
+
}, [displayLeft, dayWidth, milestoneGeometry.size]);
|
|
2855
|
+
const visualLeft = milestone ? displayMilestoneGeometry.left : displayLeft;
|
|
2856
|
+
const visualWidth = milestone ? displayMilestoneGeometry.size : displayWidth;
|
|
2692
2857
|
const currentStartDate = isDragging ? pixelsToDate(displayLeft, monthStart, dayWidth) : taskStartDate;
|
|
2693
|
-
const currentEndDate = isDragging ? pixelsToDate(displayLeft + displayWidth - dayWidth, monthStart, dayWidth) : taskEndDate;
|
|
2858
|
+
const currentEndDate = isDragging ? milestone ? pixelsToDate(displayLeft, monthStart, dayWidth) : pixelsToDate(displayLeft + displayWidth - dayWidth, monthStart, dayWidth) : taskEndDate;
|
|
2694
2859
|
const dateRangeLabel = formatDateRangeLabel(currentStartDate, currentEndDate);
|
|
2695
2860
|
const durationDays = businessDays ? getBusinessDaysCount(currentStartDate, currentEndDate, weekendPredicate) : Math.round(
|
|
2696
2861
|
(currentEndDate.getTime() - currentStartDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
@@ -2705,9 +2870,9 @@ var TaskRow = React2.memo(
|
|
|
2705
2870
|
return `${count} \u0437\u0430\u0434\u0430\u0447`;
|
|
2706
2871
|
};
|
|
2707
2872
|
const estimatedTextWidth = isParent ? 120 : durationDays >= 10 ? 76 : 62;
|
|
2708
|
-
const showProgressInside = progressWidth > 0 && displayWidth > estimatedTextWidth;
|
|
2873
|
+
const showProgressInside = !milestone && progressWidth > 0 && displayWidth > estimatedTextWidth;
|
|
2709
2874
|
const MIN_DURATION_WIDTH = isParent ? 80 : 50;
|
|
2710
|
-
const showDurationInside = durationDays >= 2 && displayWidth > MIN_DURATION_WIDTH;
|
|
2875
|
+
const showDurationInside = !milestone && durationDays >= 2 && displayWidth > MIN_DURATION_WIDTH;
|
|
2711
2876
|
return /* @__PURE__ */ jsxs2(
|
|
2712
2877
|
"div",
|
|
2713
2878
|
{
|
|
@@ -2721,18 +2886,24 @@ var TaskRow = React2.memo(
|
|
|
2721
2886
|
"div",
|
|
2722
2887
|
{
|
|
2723
2888
|
"data-taskbar": true,
|
|
2724
|
-
className: `gantt-tr-taskBar ${isDragging ? "gantt-tr-dragging" : ""} ${task.locked ? "gantt-tr-locked" : ""} ${isParent ? "gantt-tr-parentBar" : ""}`,
|
|
2889
|
+
className: `gantt-tr-taskBar ${isDragging ? "gantt-tr-dragging" : ""} ${task.locked ? "gantt-tr-locked" : ""} ${isParent ? "gantt-tr-parentBar" : ""} ${milestone ? "gantt-tr-milestone" : ""}`,
|
|
2725
2890
|
style: {
|
|
2726
|
-
left: `${
|
|
2727
|
-
width: `${displayWidth}px`,
|
|
2891
|
+
left: `${visualLeft}px`,
|
|
2728
2892
|
...barStyle,
|
|
2729
|
-
|
|
2893
|
+
...milestone ? {
|
|
2894
|
+
height: `${displayMilestoneGeometry.size}px`,
|
|
2895
|
+
width: `${displayMilestoneGeometry.size}px`,
|
|
2896
|
+
padding: 0
|
|
2897
|
+
} : {
|
|
2898
|
+
width: `${visualWidth}px`,
|
|
2899
|
+
height: isParent ? "var(--gantt-parent-bar-height, 14px)" : "var(--gantt-task-bar-height)"
|
|
2900
|
+
},
|
|
2730
2901
|
cursor: dragHandleProps.style.cursor,
|
|
2731
2902
|
userSelect: dragHandleProps.style.userSelect
|
|
2732
2903
|
},
|
|
2733
2904
|
onMouseDown: dragHandleProps.onMouseDown,
|
|
2734
2905
|
children: [
|
|
2735
|
-
progressWidth > 0 && progressWidth < 100 && /* @__PURE__ */ jsx2(
|
|
2906
|
+
!milestone && progressWidth > 0 && progressWidth < 100 && /* @__PURE__ */ jsx2(
|
|
2736
2907
|
"div",
|
|
2737
2908
|
{
|
|
2738
2909
|
className: "gantt-tr-progressBar",
|
|
@@ -2745,13 +2916,13 @@ var TaskRow = React2.memo(
|
|
|
2745
2916
|
}
|
|
2746
2917
|
}
|
|
2747
2918
|
),
|
|
2748
|
-
!isParent && /* @__PURE__ */ jsx2("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleLeft" }),
|
|
2919
|
+
!isParent && !milestone && /* @__PURE__ */ jsx2("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleLeft" }),
|
|
2749
2920
|
showDurationInside && /* @__PURE__ */ jsx2("span", { className: "gantt-tr-taskDuration", children: isParent ? getChildCountLabel(childCount) : `${durationDays} \u0434` }),
|
|
2750
2921
|
progressWidth > 0 && showProgressInside && /* @__PURE__ */ jsxs2("span", { className: "gantt-tr-progressText", children: [
|
|
2751
2922
|
progressWidth,
|
|
2752
2923
|
"%"
|
|
2753
2924
|
] }),
|
|
2754
|
-
!isParent && /* @__PURE__ */ jsx2("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleRight" })
|
|
2925
|
+
!isParent && !milestone && /* @__PURE__ */ jsx2("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleRight" })
|
|
2755
2926
|
]
|
|
2756
2927
|
}
|
|
2757
2928
|
),
|
|
@@ -2760,7 +2931,7 @@ var TaskRow = React2.memo(
|
|
|
2760
2931
|
{
|
|
2761
2932
|
className: `gantt-tr-leftLabels ${task.locked ? "gantt-tr-leftLabels-locked" : ""}`,
|
|
2762
2933
|
style: {
|
|
2763
|
-
left: `${
|
|
2934
|
+
left: `${visualLeft}px`
|
|
2764
2935
|
},
|
|
2765
2936
|
children: /* @__PURE__ */ jsx2("span", { className: "gantt-tr-dateLabel gantt-tr-dateLabelLeft", children: dateRangeLabel })
|
|
2766
2937
|
}
|
|
@@ -2771,7 +2942,7 @@ var TaskRow = React2.memo(
|
|
|
2771
2942
|
className: "gantt-tr-lockIcon",
|
|
2772
2943
|
style: {
|
|
2773
2944
|
position: "absolute",
|
|
2774
|
-
left: `${
|
|
2945
|
+
left: `${visualLeft - 16}px`,
|
|
2775
2946
|
top: "50%",
|
|
2776
2947
|
transform: "translateY(-50%)",
|
|
2777
2948
|
width: "12px",
|
|
@@ -2791,11 +2962,11 @@ var TaskRow = React2.memo(
|
|
|
2791
2962
|
{
|
|
2792
2963
|
className: "gantt-tr-rightLabels",
|
|
2793
2964
|
style: {
|
|
2794
|
-
left: `${
|
|
2965
|
+
left: `${visualLeft + Math.max(visualWidth, 20) - Math.min(6, Math.max(visualWidth, 20) / 2) + 8}px`,
|
|
2795
2966
|
color: isParent ? task.color || defaultParentBarColor : barColor
|
|
2796
2967
|
},
|
|
2797
2968
|
children: [
|
|
2798
|
-
!showDurationInside && /* @__PURE__ */ jsx2("span", { className: "gantt-tr-externalDuration", children: isParent ? getChildCountLabel(childCount) : `${durationDays} \u0434` }),
|
|
2969
|
+
!showDurationInside && !milestone && /* @__PURE__ */ jsx2("span", { className: "gantt-tr-externalDuration", children: isParent ? getChildCountLabel(childCount) : `${durationDays} \u0434` }),
|
|
2799
2970
|
progressWidth > 0 && !showProgressInside && /* @__PURE__ */ jsxs2("span", { className: "gantt-tr-externalProgress", children: [
|
|
2800
2971
|
progressWidth,
|
|
2801
2972
|
"%"
|
|
@@ -3044,16 +3215,12 @@ var DependencyLines = React5.memo(({
|
|
|
3044
3215
|
const taskMap = new Map(tasksForPositions.map((t) => [t.id, t]));
|
|
3045
3216
|
const visibleTaskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
3046
3217
|
tasks.forEach((task, index) => {
|
|
3047
|
-
const startDate = new Date(task.startDate);
|
|
3048
|
-
const endDate = new Date(task.endDate);
|
|
3049
|
-
const computed = calculateTaskBar(startDate, endDate, monthStart, dayWidth);
|
|
3050
3218
|
const override = dragOverrides?.get(task.id);
|
|
3051
|
-
const
|
|
3052
|
-
const resolvedWidth = override?.width ?? computed.width;
|
|
3219
|
+
const computed = resolveTaskHorizontalGeometry(task, monthStart, dayWidth, override);
|
|
3053
3220
|
indices.set(task.id, index);
|
|
3054
3221
|
positions.set(task.id, {
|
|
3055
|
-
left:
|
|
3056
|
-
right:
|
|
3222
|
+
left: computed.left,
|
|
3223
|
+
right: computed.right,
|
|
3057
3224
|
rowTop: index * rowHeight,
|
|
3058
3225
|
isVirtual: false
|
|
3059
3226
|
});
|
|
@@ -3067,15 +3234,11 @@ var DependencyLines = React5.memo(({
|
|
|
3067
3234
|
if (!visibleAncestor) continue;
|
|
3068
3235
|
const ancestorPosition = positions.get(visibleAncestor.id);
|
|
3069
3236
|
if (!ancestorPosition) continue;
|
|
3070
|
-
const startDate = new Date(task.startDate);
|
|
3071
|
-
const endDate = new Date(task.endDate);
|
|
3072
|
-
const computed = calculateTaskBar(startDate, endDate, monthStart, dayWidth);
|
|
3073
3237
|
const override = dragOverrides?.get(task.id);
|
|
3074
|
-
const
|
|
3075
|
-
const resolvedWidth = override?.width ?? computed.width;
|
|
3238
|
+
const computed = resolveTaskHorizontalGeometry(task, monthStart, dayWidth, override);
|
|
3076
3239
|
positions.set(task.id, {
|
|
3077
|
-
left:
|
|
3078
|
-
right:
|
|
3240
|
+
left: computed.left,
|
|
3241
|
+
right: computed.right,
|
|
3079
3242
|
rowTop: ancestorPosition.rowTop,
|
|
3080
3243
|
isVirtual: true
|
|
3081
3244
|
});
|
|
@@ -3091,6 +3254,7 @@ var DependencyLines = React5.memo(({
|
|
|
3091
3254
|
}, [tasks, allTasks]);
|
|
3092
3255
|
const lines = useMemo5(() => {
|
|
3093
3256
|
const tasksForEdges = allTasks ?? tasks;
|
|
3257
|
+
const taskMap = new Map(tasksForEdges.map((task) => [task.id, task]));
|
|
3094
3258
|
const edges = getAllDependencyEdges(tasksForEdges);
|
|
3095
3259
|
const lines2 = [];
|
|
3096
3260
|
for (const edge of edges) {
|
|
@@ -3101,9 +3265,11 @@ var DependencyLines = React5.memo(({
|
|
|
3101
3265
|
if (!predecessor || !successor) {
|
|
3102
3266
|
continue;
|
|
3103
3267
|
}
|
|
3268
|
+
const predecessorTask = taskMap.get(edge.predecessorId);
|
|
3269
|
+
const successorTask = taskMap.get(edge.successorId);
|
|
3104
3270
|
if (allTasks && collapsedParentIds.size > 0) {
|
|
3105
|
-
const
|
|
3106
|
-
if (areBothHiddenInSameParent(edge.predecessorId, edge.successorId, collapsedParentIds,
|
|
3271
|
+
const taskMap2 = new Map(allTasks.map((t) => [t.id, t]));
|
|
3272
|
+
if (areBothHiddenInSameParent(edge.predecessorId, edge.successorId, collapsedParentIds, taskMap2)) {
|
|
3107
3273
|
continue;
|
|
3108
3274
|
}
|
|
3109
3275
|
}
|
|
@@ -3123,11 +3289,18 @@ var DependencyLines = React5.memo(({
|
|
|
3123
3289
|
fromY = predecessor.rowTop + rowHeight - 10;
|
|
3124
3290
|
toY = successor.rowTop + 6;
|
|
3125
3291
|
}
|
|
3126
|
-
|
|
3292
|
+
let fromX = edge.type === "SS" || edge.type === "SF" ? predecessor.left : predecessor.right;
|
|
3127
3293
|
const toX = edge.type === "FF" || edge.type === "SF" ? successor.right : successor.left;
|
|
3294
|
+
const stackedMilestonesSameDay = Boolean(
|
|
3295
|
+
predecessorTask && successorTask && isMilestoneTask(predecessorTask) && isMilestoneTask(successorTask) && edge.lag === 0 && new Date(predecessorTask.startDate).toISOString().split("T")[0] === new Date(successorTask.startDate).toISOString().split("T")[0] && predecessor.rowTop !== successor.rowTop && edge.type === "FS"
|
|
3296
|
+
);
|
|
3297
|
+
const finalToX = stackedMilestonesSameDay ? Math.round(((predecessor.left + predecessor.right) / 2 + (successor.left + successor.right) / 2) / 2) : toX;
|
|
3298
|
+
if (stackedMilestonesSameDay) {
|
|
3299
|
+
fromX = finalToX;
|
|
3300
|
+
}
|
|
3128
3301
|
const arrivesFromRight = edge.type === "FF" || edge.type === "SF";
|
|
3129
3302
|
const from = { x: fromX, y: fromY };
|
|
3130
|
-
const to = { x:
|
|
3303
|
+
const to = { x: finalToX, y: toY };
|
|
3131
3304
|
const path = calculateDependencyPath(from, to, arrivesFromRight);
|
|
3132
3305
|
const hasCycle = cycleInfo.has(edge.predecessorId) || cycleInfo.has(edge.successorId);
|
|
3133
3306
|
lines2.push({
|
|
@@ -3136,7 +3309,7 @@ var DependencyLines = React5.memo(({
|
|
|
3136
3309
|
hasCycle,
|
|
3137
3310
|
lag: edge.lag,
|
|
3138
3311
|
fromX,
|
|
3139
|
-
toX,
|
|
3312
|
+
toX: finalToX,
|
|
3140
3313
|
fromY,
|
|
3141
3314
|
reverseOrder,
|
|
3142
3315
|
isVirtual
|
|
@@ -3149,6 +3322,7 @@ var DependencyLines = React5.memo(({
|
|
|
3149
3322
|
"svg",
|
|
3150
3323
|
{
|
|
3151
3324
|
className: "gantt-dependencies-svg",
|
|
3325
|
+
"data-testid": "dependency-lines-svg",
|
|
3152
3326
|
width: gridWidth,
|
|
3153
3327
|
height: svgHeight,
|
|
3154
3328
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -4147,7 +4321,7 @@ var DepChip = ({
|
|
|
4147
4321
|
const predecessor = taskById.get(dep.taskId);
|
|
4148
4322
|
if (!predecessor) return;
|
|
4149
4323
|
const predStart = parseUTCDate(predecessor.startDate);
|
|
4150
|
-
const predEnd = parseUTCDate(predecessor.endDate);
|
|
4324
|
+
const predEnd = predecessor.type === "milestone" ? predStart : parseUTCDate(predecessor.endDate);
|
|
4151
4325
|
const origStart = parseUTCDate(task.startDate);
|
|
4152
4326
|
const origEnd = parseUTCDate(task.endDate);
|
|
4153
4327
|
const durationMs = origEnd.getTime() - origStart.getTime();
|
|
@@ -4410,10 +4584,12 @@ var TaskListRow = React9.memo(
|
|
|
4410
4584
|
const editingName = editingColumnId === "name";
|
|
4411
4585
|
const editingDuration = editingColumnId === "duration";
|
|
4412
4586
|
const editingProgress = editingColumnId === "progress";
|
|
4587
|
+
const normalizedTask = useMemo7(() => normalizeTaskDatesForType(task), [task]);
|
|
4588
|
+
const isMilestone = useMemo7(() => isMilestoneTask(normalizedTask), [normalizedTask]);
|
|
4413
4589
|
const [nameValue, setNameValue] = useState4("");
|
|
4414
4590
|
const nameInputRef = useRef4(null);
|
|
4415
4591
|
const [durationValue, setDurationValue] = useState4(
|
|
4416
|
-
() => getInclusiveDurationDays(
|
|
4592
|
+
() => getInclusiveDurationDays(normalizedTask.startDate, normalizedTask.endDate)
|
|
4417
4593
|
);
|
|
4418
4594
|
const durationInputRef = useRef4(null);
|
|
4419
4595
|
const dependencySearchInputRef = useRef4(null);
|
|
@@ -4614,14 +4790,14 @@ var TaskListRow = React9.memo(
|
|
|
4614
4790
|
e.stopPropagation();
|
|
4615
4791
|
durationConfirmedRef.current = false;
|
|
4616
4792
|
setDurationValue(
|
|
4617
|
-
getDuration(
|
|
4793
|
+
isMilestone ? 0 : getDuration(normalizedTask.startDate, normalizedTask.endDate)
|
|
4618
4794
|
);
|
|
4619
4795
|
setEditingColumnId("duration");
|
|
4620
4796
|
},
|
|
4621
|
-
[task.locked,
|
|
4797
|
+
[task.locked, normalizedTask.startDate, normalizedTask.endDate, getDuration, isMilestone]
|
|
4622
4798
|
);
|
|
4623
4799
|
const applyDurationChange = useCallback4((nextDuration) => {
|
|
4624
|
-
const normalizedDuration = Math.max(
|
|
4800
|
+
const normalizedDuration = Math.max(0, Math.round(nextDuration) || 0);
|
|
4625
4801
|
setDurationValue(normalizedDuration);
|
|
4626
4802
|
}, []);
|
|
4627
4803
|
const handleDurationSave = useCallback4(() => {
|
|
@@ -4629,19 +4805,26 @@ var TaskListRow = React9.memo(
|
|
|
4629
4805
|
durationConfirmedRef.current = false;
|
|
4630
4806
|
return;
|
|
4631
4807
|
}
|
|
4632
|
-
const
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
...task,
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4808
|
+
const rounded = Math.round(durationValue) || 0;
|
|
4809
|
+
if (isMilestone && rounded > 0) {
|
|
4810
|
+
onTasksChange?.([
|
|
4811
|
+
{ ...task, type: "task", endDate: getEndDate(task.startDate, rounded) }
|
|
4812
|
+
]);
|
|
4813
|
+
} else if (!isMilestone && rounded === 0) {
|
|
4814
|
+
onTasksChange?.([
|
|
4815
|
+
{ ...task, type: "milestone", endDate: task.startDate }
|
|
4816
|
+
]);
|
|
4817
|
+
} else if (!isMilestone && rounded > 0) {
|
|
4818
|
+
onTasksChange?.([
|
|
4819
|
+
{ ...task, endDate: getEndDate(task.startDate, rounded) }
|
|
4820
|
+
]);
|
|
4821
|
+
}
|
|
4639
4822
|
setEditingColumnId(null);
|
|
4640
|
-
}, [durationValue, task, onTasksChange, getEndDate]);
|
|
4823
|
+
}, [durationValue, task, onTasksChange, getEndDate, isMilestone]);
|
|
4641
4824
|
const handleDurationCancel = useCallback4(() => {
|
|
4642
|
-
setDurationValue(getDuration(
|
|
4825
|
+
setDurationValue(isMilestone ? 0 : getDuration(normalizedTask.startDate, normalizedTask.endDate));
|
|
4643
4826
|
setEditingColumnId(null);
|
|
4644
|
-
}, [
|
|
4827
|
+
}, [normalizedTask.startDate, normalizedTask.endDate, getDuration, isMilestone]);
|
|
4645
4828
|
const handleDurationAdjust = useCallback4(
|
|
4646
4829
|
(delta) => {
|
|
4647
4830
|
applyDurationChange(durationValue + delta);
|
|
@@ -4653,25 +4836,26 @@ var TaskListRow = React9.memo(
|
|
|
4653
4836
|
e.stopPropagation();
|
|
4654
4837
|
if (e.key === "Enter") {
|
|
4655
4838
|
durationConfirmedRef.current = true;
|
|
4656
|
-
const
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
endDate:
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4839
|
+
const rounded = Math.round(durationValue) || 0;
|
|
4840
|
+
if (isMilestone && rounded > 0) {
|
|
4841
|
+
onTasksChange?.([
|
|
4842
|
+
{ ...task, type: "task", endDate: getEndDate(task.startDate, rounded) }
|
|
4843
|
+
]);
|
|
4844
|
+
} else if (!isMilestone && rounded === 0) {
|
|
4845
|
+
onTasksChange?.([
|
|
4846
|
+
{ ...task, type: "milestone", endDate: task.startDate }
|
|
4847
|
+
]);
|
|
4848
|
+
} else if (!isMilestone && rounded > 0) {
|
|
4849
|
+
onTasksChange?.([
|
|
4850
|
+
{ ...task, endDate: getEndDate(task.startDate, rounded) }
|
|
4851
|
+
]);
|
|
4852
|
+
}
|
|
4669
4853
|
setEditingColumnId(null);
|
|
4670
4854
|
} else if (e.key === "Escape") {
|
|
4671
4855
|
handleDurationCancel();
|
|
4672
4856
|
}
|
|
4673
4857
|
},
|
|
4674
|
-
[durationValue, task, onTasksChange, handleDurationCancel, getEndDate]
|
|
4858
|
+
[durationValue, task, onTasksChange, handleDurationCancel, getEndDate, isMilestone]
|
|
4675
4859
|
);
|
|
4676
4860
|
const handleProgressClick = useCallback4(
|
|
4677
4861
|
(e) => {
|
|
@@ -4742,17 +4926,54 @@ var TaskListRow = React9.memo(
|
|
|
4742
4926
|
}
|
|
4743
4927
|
}, [editingProgress]);
|
|
4744
4928
|
useEffect4(() => {
|
|
4745
|
-
setDurationValue(getDuration(
|
|
4746
|
-
}, [
|
|
4929
|
+
setDurationValue(getDuration(normalizedTask.startDate, normalizedTask.endDate));
|
|
4930
|
+
}, [normalizedTask.startDate, normalizedTask.endDate, getDuration]);
|
|
4747
4931
|
useEffect4(() => {
|
|
4748
4932
|
if (editingDuration && durationInputRef.current) {
|
|
4749
4933
|
durationInputRef.current.focus();
|
|
4750
4934
|
durationInputRef.current.select();
|
|
4751
4935
|
}
|
|
4752
4936
|
}, [editingDuration]);
|
|
4937
|
+
const emitMilestoneDateChange = useCallback4((nextDateISO) => {
|
|
4938
|
+
const alignedDate = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${nextDateISO}T00:00:00.000Z`), 1, weekendPredicate) : /* @__PURE__ */ new Date(`${nextDateISO}T00:00:00.000Z`);
|
|
4939
|
+
const clampedRange = clampTaskRangeForIncomingFS(
|
|
4940
|
+
task,
|
|
4941
|
+
alignedDate,
|
|
4942
|
+
alignedDate,
|
|
4943
|
+
allTasks,
|
|
4944
|
+
businessDays,
|
|
4945
|
+
weekendPredicate
|
|
4946
|
+
);
|
|
4947
|
+
const normalized = normalizeTaskDatesForType({
|
|
4948
|
+
...task,
|
|
4949
|
+
startDate: clampedRange.start.toISOString().split("T")[0],
|
|
4950
|
+
endDate: clampedRange.end.toISOString().split("T")[0]
|
|
4951
|
+
});
|
|
4952
|
+
const startDate = parseUTCDate(normalized.startDate);
|
|
4953
|
+
const endDate = parseUTCDate(normalized.endDate);
|
|
4954
|
+
onTasksChange?.([
|
|
4955
|
+
{
|
|
4956
|
+
...normalized,
|
|
4957
|
+
...task.dependencies && {
|
|
4958
|
+
dependencies: recalculateIncomingLags(
|
|
4959
|
+
task,
|
|
4960
|
+
startDate,
|
|
4961
|
+
endDate,
|
|
4962
|
+
allTasks,
|
|
4963
|
+
businessDays,
|
|
4964
|
+
weekendPredicate
|
|
4965
|
+
)
|
|
4966
|
+
}
|
|
4967
|
+
}
|
|
4968
|
+
]);
|
|
4969
|
+
}, [task, onTasksChange, allTasks, businessDays, weekendPredicate]);
|
|
4753
4970
|
const handleStartDateChange = useCallback4(
|
|
4754
4971
|
(newDateISO) => {
|
|
4755
4972
|
if (!newDateISO) return;
|
|
4973
|
+
if (isMilestone) {
|
|
4974
|
+
emitMilestoneDateChange(newDateISO);
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4756
4977
|
let nextEndISO;
|
|
4757
4978
|
const normalizedInputStart = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`), 1, weekendPredicate) : /* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`);
|
|
4758
4979
|
if (businessDays) {
|
|
@@ -4799,11 +5020,15 @@ var TaskListRow = React9.memo(
|
|
|
4799
5020
|
}
|
|
4800
5021
|
]);
|
|
4801
5022
|
},
|
|
4802
|
-
[task, onTasksChange, businessDays, getDuration, getEndDate, allTasks, weekendPredicate]
|
|
5023
|
+
[task, onTasksChange, businessDays, getDuration, getEndDate, allTasks, weekendPredicate, isMilestone, emitMilestoneDateChange]
|
|
4803
5024
|
);
|
|
4804
5025
|
const handleEndDateChange = useCallback4(
|
|
4805
5026
|
(newDateISO) => {
|
|
4806
5027
|
if (!newDateISO) return;
|
|
5028
|
+
if (isMilestone) {
|
|
5029
|
+
emitMilestoneDateChange(newDateISO);
|
|
5030
|
+
return;
|
|
5031
|
+
}
|
|
4807
5032
|
let nextStartISO;
|
|
4808
5033
|
const normalizedInputEnd = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`), -1, weekendPredicate) : /* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`);
|
|
4809
5034
|
if (businessDays) {
|
|
@@ -4850,7 +5075,7 @@ var TaskListRow = React9.memo(
|
|
|
4850
5075
|
}
|
|
4851
5076
|
]);
|
|
4852
5077
|
},
|
|
4853
|
-
[task, onTasksChange, businessDays, getDuration, weekendPredicate, allTasks]
|
|
5078
|
+
[task, onTasksChange, businessDays, getDuration, weekendPredicate, allTasks, isMilestone, emitMilestoneDateChange]
|
|
4854
5079
|
);
|
|
4855
5080
|
const handleRowClickInternal = useCallback4(() => {
|
|
4856
5081
|
onRowClick?.(task.id);
|
|
@@ -5121,8 +5346,8 @@ var TaskListRow = React9.memo(
|
|
|
5121
5346
|
},
|
|
5122
5347
|
[selectedChip?.successorId, selectedChip?.predecessorId, selectedChip?.linkType, onRemoveDependency, onChipSelect]
|
|
5123
5348
|
);
|
|
5124
|
-
const startDateISO = toISODate(
|
|
5125
|
-
const endDateISO = editingDuration ? getEndDate(
|
|
5349
|
+
const startDateISO = toISODate(normalizedTask.startDate);
|
|
5350
|
+
const endDateISO = editingDuration ? getEndDate(normalizedTask.startDate, durationValue) : toISODate(normalizedTask.endDate);
|
|
5126
5351
|
const numberCell = /* @__PURE__ */ jsxs9(
|
|
5127
5352
|
"div",
|
|
5128
5353
|
{
|
|
@@ -5528,10 +5753,10 @@ var TaskListRow = React9.memo(
|
|
|
5528
5753
|
{
|
|
5529
5754
|
ref: durationInputRef,
|
|
5530
5755
|
type: "number",
|
|
5531
|
-
min:
|
|
5756
|
+
min: 0,
|
|
5532
5757
|
step: 1,
|
|
5533
5758
|
value: durationValue,
|
|
5534
|
-
onChange: (e) => applyDurationChange(parseInt(e.target.value, 10) ||
|
|
5759
|
+
onChange: (e) => applyDurationChange(parseInt(e.target.value, 10) || 0),
|
|
5535
5760
|
onBlur: handleDurationSave,
|
|
5536
5761
|
onKeyDown: handleDurationKeyDown,
|
|
5537
5762
|
className: "gantt-tl-number-input"
|
|
@@ -5592,14 +5817,11 @@ var TaskListRow = React9.memo(
|
|
|
5592
5817
|
]
|
|
5593
5818
|
}
|
|
5594
5819
|
),
|
|
5595
|
-
/* @__PURE__ */
|
|
5820
|
+
/* @__PURE__ */ jsx12(
|
|
5596
5821
|
"span",
|
|
5597
5822
|
{
|
|
5598
5823
|
style: editingDuration ? { visibility: "hidden", pointerEvents: "none" } : void 0,
|
|
5599
|
-
children:
|
|
5600
|
-
getDuration(task.startDate, task.endDate),
|
|
5601
|
-
"\u0434"
|
|
5602
|
-
]
|
|
5824
|
+
children: isMilestone ? "0" : `${getDuration(normalizedTask.startDate, normalizedTask.endDate)}\u0434`
|
|
5603
5825
|
}
|
|
5604
5826
|
)
|
|
5605
5827
|
]
|
|
@@ -7089,7 +7311,8 @@ function GanttChartInner(props, ref) {
|
|
|
7089
7311
|
}
|
|
7090
7312
|
return;
|
|
7091
7313
|
}
|
|
7092
|
-
const
|
|
7314
|
+
const sourceTasks = tasks.map((task) => task.id === updatedTask.id ? updatedTask : task);
|
|
7315
|
+
const cascadedTasks = disableConstraints ? [updatedTask] : universalCascade(updatedTask, newStart, newEnd, sourceTasks, businessDays, isCustomWeekend);
|
|
7093
7316
|
onTasksChange?.(cascadedTasks);
|
|
7094
7317
|
}, [tasks, onTasksChange, disableConstraints, editingTaskId, businessDays, isCustomWeekend]);
|
|
7095
7318
|
const handleDelete = useCallback6((taskId) => {
|
|
@@ -7542,6 +7765,8 @@ export {
|
|
|
7542
7765
|
calculateDependencyPath,
|
|
7543
7766
|
calculateGridLines,
|
|
7544
7767
|
calculateGridWidth,
|
|
7768
|
+
calculateMilestoneConnectionBounds,
|
|
7769
|
+
calculateMilestoneGeometry,
|
|
7545
7770
|
calculateMonthGridLines,
|
|
7546
7771
|
calculateOrthogonalPath,
|
|
7547
7772
|
calculateSuccessorDate,
|
|
@@ -7593,6 +7818,7 @@ export {
|
|
|
7593
7818
|
nameContains,
|
|
7594
7819
|
normalizeDependencyLag,
|
|
7595
7820
|
normalizeHierarchyTasks,
|
|
7821
|
+
normalizePredecessorDates,
|
|
7596
7822
|
normalizeTaskDates,
|
|
7597
7823
|
normalizeUTCDate,
|
|
7598
7824
|
not,
|
|
@@ -7608,6 +7834,7 @@ export {
|
|
|
7608
7834
|
removeDependenciesBetweenTasks,
|
|
7609
7835
|
resizeTaskWithCascade,
|
|
7610
7836
|
resolveDateRangeFromPixels,
|
|
7837
|
+
resolveTaskHorizontalGeometry,
|
|
7611
7838
|
shiftBusinessDayOffset,
|
|
7612
7839
|
subtractBusinessDays2 as subtractBusinessDays,
|
|
7613
7840
|
universalCascade,
|