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.js
CHANGED
|
@@ -530,6 +530,8 @@ __export(index_exports, {
|
|
|
530
530
|
calculateDependencyPath: () => calculateDependencyPath,
|
|
531
531
|
calculateGridLines: () => calculateGridLines,
|
|
532
532
|
calculateGridWidth: () => calculateGridWidth,
|
|
533
|
+
calculateMilestoneConnectionBounds: () => calculateMilestoneConnectionBounds,
|
|
534
|
+
calculateMilestoneGeometry: () => calculateMilestoneGeometry,
|
|
533
535
|
calculateMonthGridLines: () => calculateMonthGridLines,
|
|
534
536
|
calculateOrthogonalPath: () => calculateOrthogonalPath,
|
|
535
537
|
calculateSuccessorDate: () => calculateSuccessorDate,
|
|
@@ -581,6 +583,7 @@ __export(index_exports, {
|
|
|
581
583
|
nameContains: () => nameContains,
|
|
582
584
|
normalizeDependencyLag: () => normalizeDependencyLag,
|
|
583
585
|
normalizeHierarchyTasks: () => normalizeHierarchyTasks,
|
|
586
|
+
normalizePredecessorDates: () => normalizePredecessorDates,
|
|
584
587
|
normalizeTaskDates: () => normalizeTaskDates,
|
|
585
588
|
normalizeUTCDate: () => normalizeUTCDate,
|
|
586
589
|
not: () => not,
|
|
@@ -596,6 +599,7 @@ __export(index_exports, {
|
|
|
596
599
|
removeDependenciesBetweenTasks: () => removeDependenciesBetweenTasks,
|
|
597
600
|
resizeTaskWithCascade: () => resizeTaskWithCascade,
|
|
598
601
|
resolveDateRangeFromPixels: () => resolveDateRangeFromPixels,
|
|
602
|
+
resolveTaskHorizontalGeometry: () => resolveTaskHorizontalGeometry,
|
|
599
603
|
shiftBusinessDayOffset: () => shiftBusinessDayOffset,
|
|
600
604
|
subtractBusinessDays: () => subtractBusinessDays2,
|
|
601
605
|
universalCascade: () => universalCascade,
|
|
@@ -614,6 +618,12 @@ init_dateMath();
|
|
|
614
618
|
|
|
615
619
|
// src/core/scheduling/dependencies.ts
|
|
616
620
|
init_dateMath();
|
|
621
|
+
function normalizePredecessorDates(predecessor, parseDateFn) {
|
|
622
|
+
const predStart = parseDateFn(predecessor.startDate);
|
|
623
|
+
const isMilestone = predecessor.type === "milestone";
|
|
624
|
+
const predEnd = isMilestone ? new Date(predStart.getTime() - DAY_MS) : parseDateFn(predecessor.endDate);
|
|
625
|
+
return { predStart, predEnd };
|
|
626
|
+
}
|
|
617
627
|
function getDependencyLag(dep) {
|
|
618
628
|
return Number.isFinite(dep.lag) ? dep.lag : 0;
|
|
619
629
|
}
|
|
@@ -889,8 +899,7 @@ function clampTaskRangeForIncomingFS(task, proposedStart, proposedEnd, allTasks,
|
|
|
889
899
|
if (!predecessor) {
|
|
890
900
|
continue;
|
|
891
901
|
}
|
|
892
|
-
const predecessorStart =
|
|
893
|
-
const predecessorEnd = parseDateOnly(predecessor.endDate);
|
|
902
|
+
const { predStart: predecessorStart, predEnd: predecessorEnd } = normalizePredecessorDates(predecessor, parseDateOnly);
|
|
894
903
|
const predecessorDuration = getTaskDuration(
|
|
895
904
|
predecessorStart,
|
|
896
905
|
predecessorEnd,
|
|
@@ -926,8 +935,10 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks, busin
|
|
|
926
935
|
if (!predecessor) {
|
|
927
936
|
return { ...dep, lag: getDependencyLag(dep) };
|
|
928
937
|
}
|
|
929
|
-
const predecessorStart =
|
|
930
|
-
|
|
938
|
+
const { predStart: predecessorStart, predEnd: predecessorEnd } = normalizePredecessorDates(
|
|
939
|
+
predecessor,
|
|
940
|
+
(d) => new Date(d instanceof Date ? d.getTime() : `${String(d).split("T")[0]}T00:00:00.000Z`)
|
|
941
|
+
);
|
|
931
942
|
const nextLag = computeLagFromDates(
|
|
932
943
|
dep.type,
|
|
933
944
|
predecessorStart,
|
|
@@ -942,6 +953,12 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks, busin
|
|
|
942
953
|
}
|
|
943
954
|
|
|
944
955
|
// src/core/scheduling/cascade.ts
|
|
956
|
+
function parseCascadeDateInput(date) {
|
|
957
|
+
if (date instanceof Date) {
|
|
958
|
+
return normalizeUTCDate(date);
|
|
959
|
+
}
|
|
960
|
+
return normalizeUTCDate(/* @__PURE__ */ new Date(`${date.split("T")[0]}T00:00:00.000Z`));
|
|
961
|
+
}
|
|
945
962
|
function getSuccessorChain(draggedTaskId, allTasks, linkTypes = ["FS"]) {
|
|
946
963
|
const successorMap = /* @__PURE__ */ new Map();
|
|
947
964
|
for (const task of allTasks) {
|
|
@@ -1020,7 +1037,21 @@ function cascadeByLinks(movedTaskId, newStart, newEnd, allTasks, skipChildCascad
|
|
|
1020
1037
|
const origStart = new Date(orig.startDate);
|
|
1021
1038
|
const origEnd = new Date(orig.endDate);
|
|
1022
1039
|
const duration = getTaskDuration(origStart, origEnd);
|
|
1023
|
-
const
|
|
1040
|
+
const currentTask = taskById.get(currentId);
|
|
1041
|
+
const { predStart: normalizedPredStart, predEnd: normalizedPredEnd } = normalizePredecessorDates(
|
|
1042
|
+
{
|
|
1043
|
+
startDate: predStart,
|
|
1044
|
+
endDate: predEnd,
|
|
1045
|
+
type: currentTask.type
|
|
1046
|
+
},
|
|
1047
|
+
parseCascadeDateInput
|
|
1048
|
+
);
|
|
1049
|
+
const constraintDate = calculateSuccessorDate(
|
|
1050
|
+
normalizedPredStart,
|
|
1051
|
+
normalizedPredEnd,
|
|
1052
|
+
dep.type,
|
|
1053
|
+
getDependencyLag(dep)
|
|
1054
|
+
);
|
|
1024
1055
|
let newSuccStart;
|
|
1025
1056
|
let newSuccEnd;
|
|
1026
1057
|
if (dep.type === "FS" || dep.type === "SS") {
|
|
@@ -1175,9 +1206,17 @@ function universalCascade(movedTask, newStart, newEnd, allTasks, businessDays =
|
|
|
1175
1206
|
if (!dep) continue;
|
|
1176
1207
|
const origStart = new Date(task.startDate);
|
|
1177
1208
|
const origEnd = new Date(task.endDate);
|
|
1209
|
+
const { predStart: normalizedPredStart, predEnd: normalizedPredEnd } = normalizePredecessorDates(
|
|
1210
|
+
{
|
|
1211
|
+
startDate: currStart,
|
|
1212
|
+
endDate: currEnd,
|
|
1213
|
+
type: currentOriginal.type
|
|
1214
|
+
},
|
|
1215
|
+
parseCascadeDateInput
|
|
1216
|
+
);
|
|
1178
1217
|
const constraintDate = calculateSuccessorDate(
|
|
1179
|
-
|
|
1180
|
-
|
|
1218
|
+
normalizedPredStart,
|
|
1219
|
+
normalizedPredEnd,
|
|
1181
1220
|
dep.type,
|
|
1182
1221
|
getDependencyLag(dep),
|
|
1183
1222
|
businessDays,
|
|
@@ -1394,8 +1433,7 @@ function recalculateTaskFromDependencies(taskId, snapshot, options) {
|
|
|
1394
1433
|
for (const dep of task.dependencies) {
|
|
1395
1434
|
const predecessor = snapshot.find((t) => t.id === dep.taskId);
|
|
1396
1435
|
if (!predecessor) continue;
|
|
1397
|
-
const predStart =
|
|
1398
|
-
const predEnd = parseDateOnly(predecessor.endDate);
|
|
1436
|
+
const { predStart, predEnd } = normalizePredecessorDates(predecessor, parseDateOnly);
|
|
1399
1437
|
const constraintDate = calculateSuccessorDate(
|
|
1400
1438
|
predStart,
|
|
1401
1439
|
predEnd,
|
|
@@ -1511,8 +1549,7 @@ function recalculateProjectSchedule(snapshot, options) {
|
|
|
1511
1549
|
if (!predecessor) {
|
|
1512
1550
|
continue;
|
|
1513
1551
|
}
|
|
1514
|
-
const predecessorStart =
|
|
1515
|
-
const predecessorEnd = parseDateOnly(predecessor.endDate);
|
|
1552
|
+
const { predStart: predecessorStart, predEnd: predecessorEnd } = normalizePredecessorDates(predecessor, parseDateOnly);
|
|
1516
1553
|
const constraintDate = calculateSuccessorDate(
|
|
1517
1554
|
predecessorStart,
|
|
1518
1555
|
predecessorEnd,
|
|
@@ -1931,6 +1968,45 @@ var calculateTaskBar = (taskStartDate, taskEndDate, monthStart, dayWidth) => {
|
|
|
1931
1968
|
const width = Math.round((duration + 1) * dayWidth);
|
|
1932
1969
|
return { left, width };
|
|
1933
1970
|
};
|
|
1971
|
+
var calculateMilestoneGeometry = (taskDate, monthStart, dayWidth, size = 14) => {
|
|
1972
|
+
const { left, width } = calculateTaskBar(taskDate, taskDate, monthStart, dayWidth);
|
|
1973
|
+
const centerX = Math.round(left + width / 2);
|
|
1974
|
+
const halfSize = Math.round(size / 2);
|
|
1975
|
+
return {
|
|
1976
|
+
centerX,
|
|
1977
|
+
left: centerX - halfSize,
|
|
1978
|
+
right: centerX + halfSize,
|
|
1979
|
+
size
|
|
1980
|
+
};
|
|
1981
|
+
};
|
|
1982
|
+
var calculateMilestoneConnectionBounds = (dayLeft, dayWidth, size = 14) => {
|
|
1983
|
+
const halfDiagonal = Math.round(size / Math.SQRT2);
|
|
1984
|
+
const visualNudge = 2;
|
|
1985
|
+
return {
|
|
1986
|
+
left: dayLeft + halfDiagonal + visualNudge,
|
|
1987
|
+
right: dayLeft + dayWidth - halfDiagonal - visualNudge
|
|
1988
|
+
};
|
|
1989
|
+
};
|
|
1990
|
+
var resolveTaskHorizontalGeometry = (task, monthStart, dayWidth, override) => {
|
|
1991
|
+
const startDate = new Date(task.startDate);
|
|
1992
|
+
const endDate = new Date(task.endDate);
|
|
1993
|
+
if (task.type === "milestone") {
|
|
1994
|
+
const size = 14;
|
|
1995
|
+
if (override) {
|
|
1996
|
+
return calculateMilestoneConnectionBounds(override.left, dayWidth, size);
|
|
1997
|
+
}
|
|
1998
|
+
const bar2 = calculateTaskBar(startDate, startDate, monthStart, dayWidth);
|
|
1999
|
+
return calculateMilestoneConnectionBounds(bar2.left, dayWidth, size);
|
|
2000
|
+
}
|
|
2001
|
+
if (override) {
|
|
2002
|
+
return {
|
|
2003
|
+
left: override.left,
|
|
2004
|
+
right: override.left + override.width
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
const bar = calculateTaskBar(startDate, endDate, monthStart, dayWidth);
|
|
2008
|
+
return { left: bar.left, right: bar.left + bar.width };
|
|
2009
|
+
};
|
|
1934
2010
|
var pixelsToDate = (pixels, monthStart, dayWidth) => {
|
|
1935
2011
|
const days = Math.round(pixels / dayWidth);
|
|
1936
2012
|
return new Date(Date.UTC(
|
|
@@ -2034,6 +2110,9 @@ var calculateDependencyPath = (from, to, arrivesFromRight) => {
|
|
|
2034
2110
|
if (fy === ty) {
|
|
2035
2111
|
return `M ${fx} ${fy} H ${tx}`;
|
|
2036
2112
|
}
|
|
2113
|
+
if (fx === tx) {
|
|
2114
|
+
return `M ${fx} ${fy} V ${ty}`;
|
|
2115
|
+
}
|
|
2037
2116
|
const C = 2;
|
|
2038
2117
|
const goingDown = ty > fy;
|
|
2039
2118
|
const dirY = goingDown ? 1 : -1;
|
|
@@ -2141,6 +2220,37 @@ var isTaskExpired = (task, referenceDate = /* @__PURE__ */ new Date()) => {
|
|
|
2141
2220
|
return actualProgress < expectedProgress;
|
|
2142
2221
|
};
|
|
2143
2222
|
|
|
2223
|
+
// src/utils/taskType.ts
|
|
2224
|
+
init_dateUtils();
|
|
2225
|
+
var TASK_TYPE_DEFAULT = "task";
|
|
2226
|
+
function getTaskType(task) {
|
|
2227
|
+
return task.type ?? TASK_TYPE_DEFAULT;
|
|
2228
|
+
}
|
|
2229
|
+
function isMilestoneTask(task) {
|
|
2230
|
+
return getTaskType(task) === "milestone";
|
|
2231
|
+
}
|
|
2232
|
+
function normalizeMilestoneStartDate(startDateInput) {
|
|
2233
|
+
const parsedStartDate = parseUTCDate(startDateInput);
|
|
2234
|
+
if (startDateInput instanceof Date) {
|
|
2235
|
+
return new Date(Date.UTC(
|
|
2236
|
+
parsedStartDate.getUTCFullYear(),
|
|
2237
|
+
parsedStartDate.getUTCMonth(),
|
|
2238
|
+
parsedStartDate.getUTCDate()
|
|
2239
|
+
));
|
|
2240
|
+
}
|
|
2241
|
+
return parsedStartDate.toISOString().split("T")[0];
|
|
2242
|
+
}
|
|
2243
|
+
function normalizeTaskDatesForType(task) {
|
|
2244
|
+
if (!isMilestoneTask(task)) {
|
|
2245
|
+
return task;
|
|
2246
|
+
}
|
|
2247
|
+
const startDate = normalizeMilestoneStartDate(task.startDate);
|
|
2248
|
+
return {
|
|
2249
|
+
...task,
|
|
2250
|
+
endDate: startDate
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2144
2254
|
// src/hooks/useTaskDrag.ts
|
|
2145
2255
|
var import_react2 = require("react");
|
|
2146
2256
|
|
|
@@ -2159,6 +2269,17 @@ function resolveDateRangeFromPixels(mode, left, width, monthStart, dayWidth, tas
|
|
|
2159
2269
|
monthStart.getUTCMonth(),
|
|
2160
2270
|
monthStart.getUTCDate() + rawEndOffset
|
|
2161
2271
|
));
|
|
2272
|
+
const isMilestone = task.type === "milestone";
|
|
2273
|
+
if (isMilestone) {
|
|
2274
|
+
const anchorDate = mode === "resize-right" ? rawEndDate : rawStartDate;
|
|
2275
|
+
if (businessDays && weekendPredicate) {
|
|
2276
|
+
const originalAnchor = mode === "resize-right" ? new Date(task.endDate) : new Date(task.startDate);
|
|
2277
|
+
const snapDirection2 = anchorDate.getTime() >= originalAnchor.getTime() ? 1 : -1;
|
|
2278
|
+
const alignedDate = alignToWorkingDay(anchorDate, snapDirection2, weekendPredicate);
|
|
2279
|
+
return { start: alignedDate, end: alignedDate };
|
|
2280
|
+
}
|
|
2281
|
+
return { start: anchorDate, end: anchorDate };
|
|
2282
|
+
}
|
|
2162
2283
|
if (!(businessDays && weekendPredicate)) {
|
|
2163
2284
|
return { start: rawStartDate, end: rawEndDate };
|
|
2164
2285
|
}
|
|
@@ -2249,8 +2370,10 @@ function handleGlobalMouseMove(e) {
|
|
|
2249
2370
|
const activeDrag = globalActiveDrag;
|
|
2250
2371
|
const { startX, initialLeft, initialWidth, mode, dayWidth, onProgress, allTasks } = activeDrag;
|
|
2251
2372
|
const deltaX = e.clientX - startX;
|
|
2373
|
+
const draggedTask = allTasks.find((t) => t.id === activeDrag.taskId);
|
|
2374
|
+
const effectiveWidth = draggedTask && isMilestoneTask(draggedTask) ? dayWidth : initialWidth;
|
|
2252
2375
|
let newLeft = initialLeft;
|
|
2253
|
-
let newWidth =
|
|
2376
|
+
let newWidth = effectiveWidth;
|
|
2254
2377
|
switch (mode) {
|
|
2255
2378
|
case "move":
|
|
2256
2379
|
newLeft = snapToGrid(initialLeft + deltaX, dayWidth);
|
|
@@ -2266,7 +2389,6 @@ function handleGlobalMouseMove(e) {
|
|
|
2266
2389
|
newWidth = Math.max(dayWidth, snappedWidth);
|
|
2267
2390
|
break;
|
|
2268
2391
|
}
|
|
2269
|
-
const draggedTask = allTasks.find((t) => t.id === activeDrag.taskId);
|
|
2270
2392
|
if (activeDrag.businessDays && activeDrag.weekendPredicate && draggedTask) {
|
|
2271
2393
|
const previewRange = clampDateRangeForIncomingFS(
|
|
2272
2394
|
draggedTask,
|
|
@@ -2308,6 +2430,9 @@ function handleGlobalMouseMove(e) {
|
|
|
2308
2430
|
newLeft = Math.round(alignedStartDay * dayWidth);
|
|
2309
2431
|
newWidth = Math.round((alignedEndDay - alignedStartDay + 1) * dayWidth);
|
|
2310
2432
|
}
|
|
2433
|
+
if (draggedTask && isMilestoneTask(draggedTask)) {
|
|
2434
|
+
newWidth = dayWidth;
|
|
2435
|
+
}
|
|
2311
2436
|
if (!activeDrag.disableConstraints && activeDrag.onCascadeProgress) {
|
|
2312
2437
|
const { dayWidth: dayWidth2, monthStart: mStart, taskId: dragId } = activeDrag;
|
|
2313
2438
|
const originalDraggedTask = draggedTask ?? allTasks.find((t) => t.id === dragId);
|
|
@@ -2344,7 +2469,8 @@ function handleGlobalMouseMove(e) {
|
|
|
2344
2469
|
};
|
|
2345
2470
|
})();
|
|
2346
2471
|
const previewStartDate = previewRange.start;
|
|
2347
|
-
const
|
|
2472
|
+
const isMilestone = originalDraggedTask ? isMilestoneTask(originalDraggedTask) : false;
|
|
2473
|
+
const previewEndDate = isMilestone ? previewRange.start : previewRange.end;
|
|
2348
2474
|
const movedTaskData = originalDraggedTask ?? { id: dragId, name: "", startDate: "", endDate: "" };
|
|
2349
2475
|
const cascadeResult = universalCascade(
|
|
2350
2476
|
{ ...movedTaskData, startDate: previewStartDate.toISOString(), endDate: previewEndDate.toISOString() },
|
|
@@ -2434,6 +2560,9 @@ var useTaskDrag = (options) => {
|
|
|
2434
2560
|
businessDays = true,
|
|
2435
2561
|
weekendPredicate
|
|
2436
2562
|
} = options;
|
|
2563
|
+
const rawHookTask = allTasks.find((t) => t.id === taskId);
|
|
2564
|
+
const hookTask = rawHookTask ? normalizeTaskDatesForType(rawHookTask) : void 0;
|
|
2565
|
+
const hookTaskIsMilestone = hookTask ? isMilestoneTask(hookTask) : false;
|
|
2437
2566
|
const isOwnerRef = (0, import_react2.useRef)(false);
|
|
2438
2567
|
const effectiveLocked = locked || disableTaskDrag;
|
|
2439
2568
|
const [isDragging, setIsDragging] = (0, import_react2.useState)(false);
|
|
@@ -2455,11 +2584,11 @@ var useTaskDrag = (options) => {
|
|
|
2455
2584
|
return Math.round((ms1 - ms2) / (1e3 * 60 * 60 * 24));
|
|
2456
2585
|
};
|
|
2457
2586
|
const startOffset = getUTCDayDifference2(initialStartDate, monthStart);
|
|
2458
|
-
const duration = getUTCDayDifference2(initialEndDate, initialStartDate);
|
|
2587
|
+
const duration = hookTaskIsMilestone ? 0 : getUTCDayDifference2(initialEndDate, initialStartDate);
|
|
2459
2588
|
const left = Math.round(startOffset * dayWidth);
|
|
2460
2589
|
const width = Math.round((duration + 1) * dayWidth);
|
|
2461
2590
|
return { left, width };
|
|
2462
|
-
}, [initialStartDate, initialEndDate, monthStart, dayWidth]);
|
|
2591
|
+
}, [initialStartDate, initialEndDate, monthStart, dayWidth, hookTaskIsMilestone]);
|
|
2463
2592
|
(0, import_react2.useEffect)(() => {
|
|
2464
2593
|
if (isOwnerRef.current && globalActiveDrag) return;
|
|
2465
2594
|
const { left, width } = getInitialPosition();
|
|
@@ -2494,7 +2623,8 @@ var useTaskDrag = (options) => {
|
|
|
2494
2623
|
const handleComplete = (0, import_react2.useCallback)((finalLeft, finalWidth, finalMode) => {
|
|
2495
2624
|
const wasOwner = isOwnerRef.current;
|
|
2496
2625
|
isOwnerRef.current = false;
|
|
2497
|
-
const
|
|
2626
|
+
const currentTaskRaw = allTasks.find((t) => t.id === taskId);
|
|
2627
|
+
const currentTask = currentTaskRaw ? normalizeTaskDatesForType(currentTaskRaw) : void 0;
|
|
2498
2628
|
const finalRange = currentTask ? clampDateRangeForIncomingFS(
|
|
2499
2629
|
currentTask,
|
|
2500
2630
|
resolveDateRangeFromPixels(
|
|
@@ -2528,7 +2658,7 @@ var useTaskDrag = (options) => {
|
|
|
2528
2658
|
};
|
|
2529
2659
|
})();
|
|
2530
2660
|
const newStartDate = finalRange.start;
|
|
2531
|
-
const newEndDate = finalRange.end;
|
|
2661
|
+
const newEndDate = currentTask && isMilestoneTask(currentTask) ? finalRange.start : finalRange.end;
|
|
2532
2662
|
setIsDragging(false);
|
|
2533
2663
|
setDragMode(null);
|
|
2534
2664
|
if (onDragStateChange) {
|
|
@@ -2540,6 +2670,23 @@ var useTaskDrag = (options) => {
|
|
|
2540
2670
|
});
|
|
2541
2671
|
}
|
|
2542
2672
|
if (wasOwner) {
|
|
2673
|
+
const startUnchanged = newStartDate.getTime() === Date.UTC(
|
|
2674
|
+
initialStartDate.getUTCFullYear(),
|
|
2675
|
+
initialStartDate.getUTCMonth(),
|
|
2676
|
+
initialStartDate.getUTCDate()
|
|
2677
|
+
);
|
|
2678
|
+
const baselineEndDate = hookTaskIsMilestone ? initialStartDate : initialEndDate;
|
|
2679
|
+
const endUnchanged = newEndDate.getTime() === Date.UTC(
|
|
2680
|
+
baselineEndDate.getUTCFullYear(),
|
|
2681
|
+
baselineEndDate.getUTCMonth(),
|
|
2682
|
+
baselineEndDate.getUTCDate()
|
|
2683
|
+
);
|
|
2684
|
+
if (startUnchanged && endUnchanged) {
|
|
2685
|
+
const { left, width } = getInitialPosition();
|
|
2686
|
+
setCurrentLeft(left);
|
|
2687
|
+
setCurrentWidth(width);
|
|
2688
|
+
return;
|
|
2689
|
+
}
|
|
2543
2690
|
if (!disableConstraints && onCascade && allTasks.length > 0) {
|
|
2544
2691
|
const draggedTaskData = currentTask;
|
|
2545
2692
|
const movedTask = {
|
|
@@ -2577,7 +2724,8 @@ var useTaskDrag = (options) => {
|
|
|
2577
2724
|
businessDays,
|
|
2578
2725
|
weekendPredicate,
|
|
2579
2726
|
initialStartDate,
|
|
2580
|
-
initialEndDate
|
|
2727
|
+
initialEndDate,
|
|
2728
|
+
hookTaskIsMilestone
|
|
2581
2729
|
]);
|
|
2582
2730
|
const handleCancel = (0, import_react2.useCallback)(() => {
|
|
2583
2731
|
isOwnerRef.current = false;
|
|
@@ -2620,6 +2768,9 @@ var useTaskDrag = (options) => {
|
|
|
2620
2768
|
if (currentTask2 && isTaskParent(taskId, allTasks)) {
|
|
2621
2769
|
mode = "move";
|
|
2622
2770
|
}
|
|
2771
|
+
if (currentTask2 && isMilestoneTask(currentTask2)) {
|
|
2772
|
+
mode = "move";
|
|
2773
|
+
}
|
|
2623
2774
|
}
|
|
2624
2775
|
if (!mode) {
|
|
2625
2776
|
return;
|
|
@@ -2705,14 +2856,16 @@ var useTaskDrag = (options) => {
|
|
|
2705
2856
|
// src/components/TaskRow/TaskRow.tsx
|
|
2706
2857
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2707
2858
|
var arePropsEqual = (prevProps, nextProps) => {
|
|
2708
|
-
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;
|
|
2859
|
+
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;
|
|
2709
2860
|
};
|
|
2710
2861
|
var TaskRow = import_react3.default.memo(
|
|
2711
2862
|
({ task, monthStart, dayWidth, rowHeight, onTasksChange, onDragStateChange, rowIndex, allTasks, enableAutoSchedule, disableConstraints, overridePosition, onCascadeProgress, onCascade, divider, highlightExpiredTasks, isFilterMatch = false, businessDays, customDays, isWeekend: isWeekend3, disableTaskDrag = false }) => {
|
|
2712
2863
|
const defaultParentBarColor = "#782FC4";
|
|
2713
2864
|
const { divider: taskDivider } = task;
|
|
2714
|
-
const
|
|
2715
|
-
const
|
|
2865
|
+
const normalizedTask = (0, import_react3.useMemo)(() => normalizeTaskDatesForType(task), [task]);
|
|
2866
|
+
const milestone = (0, import_react3.useMemo)(() => isMilestoneTask(normalizedTask), [normalizedTask]);
|
|
2867
|
+
const taskStartDate = (0, import_react3.useMemo)(() => parseUTCDate(normalizedTask.startDate), [normalizedTask.startDate]);
|
|
2868
|
+
const taskEndDate = (0, import_react3.useMemo)(() => parseUTCDate(normalizedTask.endDate), [normalizedTask.endDate]);
|
|
2716
2869
|
const isParent = (0, import_react3.useMemo)(() => {
|
|
2717
2870
|
return allTasks ? isTaskParent(task.id, allTasks) : false;
|
|
2718
2871
|
}, [allTasks, task.id]);
|
|
@@ -2721,12 +2874,16 @@ var TaskRow = import_react3.default.memo(
|
|
|
2721
2874
|
}, [allTasks, task.id]);
|
|
2722
2875
|
const isExpired = (0, import_react3.useMemo)(() => {
|
|
2723
2876
|
if (!highlightExpiredTasks) return false;
|
|
2724
|
-
return isTaskExpired(
|
|
2725
|
-
}, [
|
|
2877
|
+
return isTaskExpired(normalizedTask);
|
|
2878
|
+
}, [normalizedTask.startDate, normalizedTask.endDate, normalizedTask.progress, highlightExpiredTasks]);
|
|
2726
2879
|
const { left, width } = (0, import_react3.useMemo)(
|
|
2727
2880
|
() => calculateTaskBar(taskStartDate, taskEndDate, monthStart, dayWidth),
|
|
2728
2881
|
[taskStartDate, taskEndDate, monthStart, dayWidth]
|
|
2729
2882
|
);
|
|
2883
|
+
const milestoneGeometry = (0, import_react3.useMemo)(
|
|
2884
|
+
() => calculateMilestoneGeometry(taskStartDate, monthStart, dayWidth),
|
|
2885
|
+
[taskStartDate, monthStart, dayWidth]
|
|
2886
|
+
);
|
|
2730
2887
|
const barColor = isExpired ? "var(--gantt-expired-color)" : task.color || "var(--gantt-task-bar-default-color)";
|
|
2731
2888
|
const progressWidth = (0, import_react3.useMemo)(() => {
|
|
2732
2889
|
if (task.progress === void 0 || task.progress <= 0) return 0;
|
|
@@ -2765,7 +2922,7 @@ var TaskRow = import_react3.default.memo(
|
|
|
2765
2922
|
}, [defaultParentBarColor, isExpired, isParent, progressWidth, barColor, progressColor, task.color]);
|
|
2766
2923
|
const handleDragEnd = (result) => {
|
|
2767
2924
|
const updatedTask = {
|
|
2768
|
-
...
|
|
2925
|
+
...normalizedTask,
|
|
2769
2926
|
startDate: result.startDate.toISOString(),
|
|
2770
2927
|
endDate: result.endDate.toISOString(),
|
|
2771
2928
|
...result.updatedDependencies !== void 0 && { dependencies: result.updatedDependencies }
|
|
@@ -2804,8 +2961,20 @@ var TaskRow = import_react3.default.memo(
|
|
|
2804
2961
|
});
|
|
2805
2962
|
const displayLeft = overridePosition?.left ?? (isDragging ? currentLeft : left);
|
|
2806
2963
|
const displayWidth = overridePosition?.width ?? (isDragging ? currentWidth : width);
|
|
2964
|
+
const displayMilestoneGeometry = (0, import_react3.useMemo)(() => {
|
|
2965
|
+
const centerX = Math.round(displayLeft + dayWidth / 2);
|
|
2966
|
+
const halfSize = Math.round(milestoneGeometry.size / 2);
|
|
2967
|
+
return {
|
|
2968
|
+
centerX,
|
|
2969
|
+
left: centerX - halfSize,
|
|
2970
|
+
right: centerX + halfSize,
|
|
2971
|
+
size: milestoneGeometry.size
|
|
2972
|
+
};
|
|
2973
|
+
}, [displayLeft, dayWidth, milestoneGeometry.size]);
|
|
2974
|
+
const visualLeft = milestone ? displayMilestoneGeometry.left : displayLeft;
|
|
2975
|
+
const visualWidth = milestone ? displayMilestoneGeometry.size : displayWidth;
|
|
2807
2976
|
const currentStartDate = isDragging ? pixelsToDate(displayLeft, monthStart, dayWidth) : taskStartDate;
|
|
2808
|
-
const currentEndDate = isDragging ? pixelsToDate(displayLeft + displayWidth - dayWidth, monthStart, dayWidth) : taskEndDate;
|
|
2977
|
+
const currentEndDate = isDragging ? milestone ? pixelsToDate(displayLeft, monthStart, dayWidth) : pixelsToDate(displayLeft + displayWidth - dayWidth, monthStart, dayWidth) : taskEndDate;
|
|
2809
2978
|
const dateRangeLabel = formatDateRangeLabel(currentStartDate, currentEndDate);
|
|
2810
2979
|
const durationDays = businessDays ? getBusinessDaysCount(currentStartDate, currentEndDate, weekendPredicate) : Math.round(
|
|
2811
2980
|
(currentEndDate.getTime() - currentStartDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
@@ -2820,9 +2989,9 @@ var TaskRow = import_react3.default.memo(
|
|
|
2820
2989
|
return `${count} \u0437\u0430\u0434\u0430\u0447`;
|
|
2821
2990
|
};
|
|
2822
2991
|
const estimatedTextWidth = isParent ? 120 : durationDays >= 10 ? 76 : 62;
|
|
2823
|
-
const showProgressInside = progressWidth > 0 && displayWidth > estimatedTextWidth;
|
|
2992
|
+
const showProgressInside = !milestone && progressWidth > 0 && displayWidth > estimatedTextWidth;
|
|
2824
2993
|
const MIN_DURATION_WIDTH = isParent ? 80 : 50;
|
|
2825
|
-
const showDurationInside = durationDays >= 2 && displayWidth > MIN_DURATION_WIDTH;
|
|
2994
|
+
const showDurationInside = !milestone && durationDays >= 2 && displayWidth > MIN_DURATION_WIDTH;
|
|
2826
2995
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
2827
2996
|
"div",
|
|
2828
2997
|
{
|
|
@@ -2836,18 +3005,24 @@ var TaskRow = import_react3.default.memo(
|
|
|
2836
3005
|
"div",
|
|
2837
3006
|
{
|
|
2838
3007
|
"data-taskbar": true,
|
|
2839
|
-
className: `gantt-tr-taskBar ${isDragging ? "gantt-tr-dragging" : ""} ${task.locked ? "gantt-tr-locked" : ""} ${isParent ? "gantt-tr-parentBar" : ""}`,
|
|
3008
|
+
className: `gantt-tr-taskBar ${isDragging ? "gantt-tr-dragging" : ""} ${task.locked ? "gantt-tr-locked" : ""} ${isParent ? "gantt-tr-parentBar" : ""} ${milestone ? "gantt-tr-milestone" : ""}`,
|
|
2840
3009
|
style: {
|
|
2841
|
-
left: `${
|
|
2842
|
-
width: `${displayWidth}px`,
|
|
3010
|
+
left: `${visualLeft}px`,
|
|
2843
3011
|
...barStyle,
|
|
2844
|
-
|
|
3012
|
+
...milestone ? {
|
|
3013
|
+
height: `${displayMilestoneGeometry.size}px`,
|
|
3014
|
+
width: `${displayMilestoneGeometry.size}px`,
|
|
3015
|
+
padding: 0
|
|
3016
|
+
} : {
|
|
3017
|
+
width: `${visualWidth}px`,
|
|
3018
|
+
height: isParent ? "var(--gantt-parent-bar-height, 14px)" : "var(--gantt-task-bar-height)"
|
|
3019
|
+
},
|
|
2845
3020
|
cursor: dragHandleProps.style.cursor,
|
|
2846
3021
|
userSelect: dragHandleProps.style.userSelect
|
|
2847
3022
|
},
|
|
2848
3023
|
onMouseDown: dragHandleProps.onMouseDown,
|
|
2849
3024
|
children: [
|
|
2850
|
-
progressWidth > 0 && progressWidth < 100 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3025
|
+
!milestone && progressWidth > 0 && progressWidth < 100 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2851
3026
|
"div",
|
|
2852
3027
|
{
|
|
2853
3028
|
className: "gantt-tr-progressBar",
|
|
@@ -2860,13 +3035,13 @@ var TaskRow = import_react3.default.memo(
|
|
|
2860
3035
|
}
|
|
2861
3036
|
}
|
|
2862
3037
|
),
|
|
2863
|
-
!isParent && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleLeft" }),
|
|
3038
|
+
!isParent && !milestone && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleLeft" }),
|
|
2864
3039
|
showDurationInside && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gantt-tr-taskDuration", children: isParent ? getChildCountLabel(childCount) : `${durationDays} \u0434` }),
|
|
2865
3040
|
progressWidth > 0 && showProgressInside && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gantt-tr-progressText", children: [
|
|
2866
3041
|
progressWidth,
|
|
2867
3042
|
"%"
|
|
2868
3043
|
] }),
|
|
2869
|
-
!isParent && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleRight" })
|
|
3044
|
+
!isParent && !milestone && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleRight" })
|
|
2870
3045
|
]
|
|
2871
3046
|
}
|
|
2872
3047
|
),
|
|
@@ -2875,7 +3050,7 @@ var TaskRow = import_react3.default.memo(
|
|
|
2875
3050
|
{
|
|
2876
3051
|
className: `gantt-tr-leftLabels ${task.locked ? "gantt-tr-leftLabels-locked" : ""}`,
|
|
2877
3052
|
style: {
|
|
2878
|
-
left: `${
|
|
3053
|
+
left: `${visualLeft}px`
|
|
2879
3054
|
},
|
|
2880
3055
|
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gantt-tr-dateLabel gantt-tr-dateLabelLeft", children: dateRangeLabel })
|
|
2881
3056
|
}
|
|
@@ -2886,7 +3061,7 @@ var TaskRow = import_react3.default.memo(
|
|
|
2886
3061
|
className: "gantt-tr-lockIcon",
|
|
2887
3062
|
style: {
|
|
2888
3063
|
position: "absolute",
|
|
2889
|
-
left: `${
|
|
3064
|
+
left: `${visualLeft - 16}px`,
|
|
2890
3065
|
top: "50%",
|
|
2891
3066
|
transform: "translateY(-50%)",
|
|
2892
3067
|
width: "12px",
|
|
@@ -2906,11 +3081,11 @@ var TaskRow = import_react3.default.memo(
|
|
|
2906
3081
|
{
|
|
2907
3082
|
className: "gantt-tr-rightLabels",
|
|
2908
3083
|
style: {
|
|
2909
|
-
left: `${
|
|
3084
|
+
left: `${visualLeft + Math.max(visualWidth, 20) - Math.min(6, Math.max(visualWidth, 20) / 2) + 8}px`,
|
|
2910
3085
|
color: isParent ? task.color || defaultParentBarColor : barColor
|
|
2911
3086
|
},
|
|
2912
3087
|
children: [
|
|
2913
|
-
!showDurationInside && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gantt-tr-externalDuration", children: isParent ? getChildCountLabel(childCount) : `${durationDays} \u0434` }),
|
|
3088
|
+
!showDurationInside && !milestone && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gantt-tr-externalDuration", children: isParent ? getChildCountLabel(childCount) : `${durationDays} \u0434` }),
|
|
2914
3089
|
progressWidth > 0 && !showProgressInside && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gantt-tr-externalProgress", children: [
|
|
2915
3090
|
progressWidth,
|
|
2916
3091
|
"%"
|
|
@@ -3159,16 +3334,12 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
3159
3334
|
const taskMap = new Map(tasksForPositions.map((t) => [t.id, t]));
|
|
3160
3335
|
const visibleTaskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
3161
3336
|
tasks.forEach((task, index) => {
|
|
3162
|
-
const startDate = new Date(task.startDate);
|
|
3163
|
-
const endDate = new Date(task.endDate);
|
|
3164
|
-
const computed = calculateTaskBar(startDate, endDate, monthStart, dayWidth);
|
|
3165
3337
|
const override = dragOverrides?.get(task.id);
|
|
3166
|
-
const
|
|
3167
|
-
const resolvedWidth = override?.width ?? computed.width;
|
|
3338
|
+
const computed = resolveTaskHorizontalGeometry(task, monthStart, dayWidth, override);
|
|
3168
3339
|
indices.set(task.id, index);
|
|
3169
3340
|
positions.set(task.id, {
|
|
3170
|
-
left:
|
|
3171
|
-
right:
|
|
3341
|
+
left: computed.left,
|
|
3342
|
+
right: computed.right,
|
|
3172
3343
|
rowTop: index * rowHeight,
|
|
3173
3344
|
isVirtual: false
|
|
3174
3345
|
});
|
|
@@ -3182,15 +3353,11 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
3182
3353
|
if (!visibleAncestor) continue;
|
|
3183
3354
|
const ancestorPosition = positions.get(visibleAncestor.id);
|
|
3184
3355
|
if (!ancestorPosition) continue;
|
|
3185
|
-
const startDate = new Date(task.startDate);
|
|
3186
|
-
const endDate = new Date(task.endDate);
|
|
3187
|
-
const computed = calculateTaskBar(startDate, endDate, monthStart, dayWidth);
|
|
3188
3356
|
const override = dragOverrides?.get(task.id);
|
|
3189
|
-
const
|
|
3190
|
-
const resolvedWidth = override?.width ?? computed.width;
|
|
3357
|
+
const computed = resolveTaskHorizontalGeometry(task, monthStart, dayWidth, override);
|
|
3191
3358
|
positions.set(task.id, {
|
|
3192
|
-
left:
|
|
3193
|
-
right:
|
|
3359
|
+
left: computed.left,
|
|
3360
|
+
right: computed.right,
|
|
3194
3361
|
rowTop: ancestorPosition.rowTop,
|
|
3195
3362
|
isVirtual: true
|
|
3196
3363
|
});
|
|
@@ -3206,6 +3373,7 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
3206
3373
|
}, [tasks, allTasks]);
|
|
3207
3374
|
const lines = (0, import_react6.useMemo)(() => {
|
|
3208
3375
|
const tasksForEdges = allTasks ?? tasks;
|
|
3376
|
+
const taskMap = new Map(tasksForEdges.map((task) => [task.id, task]));
|
|
3209
3377
|
const edges = getAllDependencyEdges(tasksForEdges);
|
|
3210
3378
|
const lines2 = [];
|
|
3211
3379
|
for (const edge of edges) {
|
|
@@ -3216,9 +3384,11 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
3216
3384
|
if (!predecessor || !successor) {
|
|
3217
3385
|
continue;
|
|
3218
3386
|
}
|
|
3387
|
+
const predecessorTask = taskMap.get(edge.predecessorId);
|
|
3388
|
+
const successorTask = taskMap.get(edge.successorId);
|
|
3219
3389
|
if (allTasks && collapsedParentIds.size > 0) {
|
|
3220
|
-
const
|
|
3221
|
-
if (areBothHiddenInSameParent(edge.predecessorId, edge.successorId, collapsedParentIds,
|
|
3390
|
+
const taskMap2 = new Map(allTasks.map((t) => [t.id, t]));
|
|
3391
|
+
if (areBothHiddenInSameParent(edge.predecessorId, edge.successorId, collapsedParentIds, taskMap2)) {
|
|
3222
3392
|
continue;
|
|
3223
3393
|
}
|
|
3224
3394
|
}
|
|
@@ -3238,11 +3408,18 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
3238
3408
|
fromY = predecessor.rowTop + rowHeight - 10;
|
|
3239
3409
|
toY = successor.rowTop + 6;
|
|
3240
3410
|
}
|
|
3241
|
-
|
|
3411
|
+
let fromX = edge.type === "SS" || edge.type === "SF" ? predecessor.left : predecessor.right;
|
|
3242
3412
|
const toX = edge.type === "FF" || edge.type === "SF" ? successor.right : successor.left;
|
|
3413
|
+
const stackedMilestonesSameDay = Boolean(
|
|
3414
|
+
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"
|
|
3415
|
+
);
|
|
3416
|
+
const finalToX = stackedMilestonesSameDay ? Math.round(((predecessor.left + predecessor.right) / 2 + (successor.left + successor.right) / 2) / 2) : toX;
|
|
3417
|
+
if (stackedMilestonesSameDay) {
|
|
3418
|
+
fromX = finalToX;
|
|
3419
|
+
}
|
|
3243
3420
|
const arrivesFromRight = edge.type === "FF" || edge.type === "SF";
|
|
3244
3421
|
const from = { x: fromX, y: fromY };
|
|
3245
|
-
const to = { x:
|
|
3422
|
+
const to = { x: finalToX, y: toY };
|
|
3246
3423
|
const path = calculateDependencyPath(from, to, arrivesFromRight);
|
|
3247
3424
|
const hasCycle = cycleInfo.has(edge.predecessorId) || cycleInfo.has(edge.successorId);
|
|
3248
3425
|
lines2.push({
|
|
@@ -3251,7 +3428,7 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
3251
3428
|
hasCycle,
|
|
3252
3429
|
lag: edge.lag,
|
|
3253
3430
|
fromX,
|
|
3254
|
-
toX,
|
|
3431
|
+
toX: finalToX,
|
|
3255
3432
|
fromY,
|
|
3256
3433
|
reverseOrder,
|
|
3257
3434
|
isVirtual
|
|
@@ -3264,6 +3441,7 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
3264
3441
|
"svg",
|
|
3265
3442
|
{
|
|
3266
3443
|
className: "gantt-dependencies-svg",
|
|
3444
|
+
"data-testid": "dependency-lines-svg",
|
|
3267
3445
|
width: gridWidth,
|
|
3268
3446
|
height: svgHeight,
|
|
3269
3447
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -4238,7 +4416,7 @@ var DepChip = ({
|
|
|
4238
4416
|
const predecessor = taskById.get(dep.taskId);
|
|
4239
4417
|
if (!predecessor) return;
|
|
4240
4418
|
const predStart = parseUTCDate(predecessor.startDate);
|
|
4241
|
-
const predEnd = parseUTCDate(predecessor.endDate);
|
|
4419
|
+
const predEnd = predecessor.type === "milestone" ? predStart : parseUTCDate(predecessor.endDate);
|
|
4242
4420
|
const origStart = parseUTCDate(task.startDate);
|
|
4243
4421
|
const origEnd = parseUTCDate(task.endDate);
|
|
4244
4422
|
const durationMs = origEnd.getTime() - origStart.getTime();
|
|
@@ -4501,10 +4679,12 @@ var TaskListRow = import_react10.default.memo(
|
|
|
4501
4679
|
const editingName = editingColumnId === "name";
|
|
4502
4680
|
const editingDuration = editingColumnId === "duration";
|
|
4503
4681
|
const editingProgress = editingColumnId === "progress";
|
|
4682
|
+
const normalizedTask = (0, import_react10.useMemo)(() => normalizeTaskDatesForType(task), [task]);
|
|
4683
|
+
const isMilestone = (0, import_react10.useMemo)(() => isMilestoneTask(normalizedTask), [normalizedTask]);
|
|
4504
4684
|
const [nameValue, setNameValue] = (0, import_react10.useState)("");
|
|
4505
4685
|
const nameInputRef = (0, import_react10.useRef)(null);
|
|
4506
4686
|
const [durationValue, setDurationValue] = (0, import_react10.useState)(
|
|
4507
|
-
() => getInclusiveDurationDays(
|
|
4687
|
+
() => getInclusiveDurationDays(normalizedTask.startDate, normalizedTask.endDate)
|
|
4508
4688
|
);
|
|
4509
4689
|
const durationInputRef = (0, import_react10.useRef)(null);
|
|
4510
4690
|
const dependencySearchInputRef = (0, import_react10.useRef)(null);
|
|
@@ -4705,14 +4885,14 @@ var TaskListRow = import_react10.default.memo(
|
|
|
4705
4885
|
e.stopPropagation();
|
|
4706
4886
|
durationConfirmedRef.current = false;
|
|
4707
4887
|
setDurationValue(
|
|
4708
|
-
getDuration(
|
|
4888
|
+
isMilestone ? 0 : getDuration(normalizedTask.startDate, normalizedTask.endDate)
|
|
4709
4889
|
);
|
|
4710
4890
|
setEditingColumnId("duration");
|
|
4711
4891
|
},
|
|
4712
|
-
[task.locked,
|
|
4892
|
+
[task.locked, normalizedTask.startDate, normalizedTask.endDate, getDuration, isMilestone]
|
|
4713
4893
|
);
|
|
4714
4894
|
const applyDurationChange = (0, import_react10.useCallback)((nextDuration) => {
|
|
4715
|
-
const normalizedDuration = Math.max(
|
|
4895
|
+
const normalizedDuration = Math.max(0, Math.round(nextDuration) || 0);
|
|
4716
4896
|
setDurationValue(normalizedDuration);
|
|
4717
4897
|
}, []);
|
|
4718
4898
|
const handleDurationSave = (0, import_react10.useCallback)(() => {
|
|
@@ -4720,19 +4900,26 @@ var TaskListRow = import_react10.default.memo(
|
|
|
4720
4900
|
durationConfirmedRef.current = false;
|
|
4721
4901
|
return;
|
|
4722
4902
|
}
|
|
4723
|
-
const
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
...task,
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4903
|
+
const rounded = Math.round(durationValue) || 0;
|
|
4904
|
+
if (isMilestone && rounded > 0) {
|
|
4905
|
+
onTasksChange?.([
|
|
4906
|
+
{ ...task, type: "task", endDate: getEndDate(task.startDate, rounded) }
|
|
4907
|
+
]);
|
|
4908
|
+
} else if (!isMilestone && rounded === 0) {
|
|
4909
|
+
onTasksChange?.([
|
|
4910
|
+
{ ...task, type: "milestone", endDate: task.startDate }
|
|
4911
|
+
]);
|
|
4912
|
+
} else if (!isMilestone && rounded > 0) {
|
|
4913
|
+
onTasksChange?.([
|
|
4914
|
+
{ ...task, endDate: getEndDate(task.startDate, rounded) }
|
|
4915
|
+
]);
|
|
4916
|
+
}
|
|
4730
4917
|
setEditingColumnId(null);
|
|
4731
|
-
}, [durationValue, task, onTasksChange, getEndDate]);
|
|
4918
|
+
}, [durationValue, task, onTasksChange, getEndDate, isMilestone]);
|
|
4732
4919
|
const handleDurationCancel = (0, import_react10.useCallback)(() => {
|
|
4733
|
-
setDurationValue(getDuration(
|
|
4920
|
+
setDurationValue(isMilestone ? 0 : getDuration(normalizedTask.startDate, normalizedTask.endDate));
|
|
4734
4921
|
setEditingColumnId(null);
|
|
4735
|
-
}, [
|
|
4922
|
+
}, [normalizedTask.startDate, normalizedTask.endDate, getDuration, isMilestone]);
|
|
4736
4923
|
const handleDurationAdjust = (0, import_react10.useCallback)(
|
|
4737
4924
|
(delta) => {
|
|
4738
4925
|
applyDurationChange(durationValue + delta);
|
|
@@ -4744,25 +4931,26 @@ var TaskListRow = import_react10.default.memo(
|
|
|
4744
4931
|
e.stopPropagation();
|
|
4745
4932
|
if (e.key === "Enter") {
|
|
4746
4933
|
durationConfirmedRef.current = true;
|
|
4747
|
-
const
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
endDate:
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4934
|
+
const rounded = Math.round(durationValue) || 0;
|
|
4935
|
+
if (isMilestone && rounded > 0) {
|
|
4936
|
+
onTasksChange?.([
|
|
4937
|
+
{ ...task, type: "task", endDate: getEndDate(task.startDate, rounded) }
|
|
4938
|
+
]);
|
|
4939
|
+
} else if (!isMilestone && rounded === 0) {
|
|
4940
|
+
onTasksChange?.([
|
|
4941
|
+
{ ...task, type: "milestone", endDate: task.startDate }
|
|
4942
|
+
]);
|
|
4943
|
+
} else if (!isMilestone && rounded > 0) {
|
|
4944
|
+
onTasksChange?.([
|
|
4945
|
+
{ ...task, endDate: getEndDate(task.startDate, rounded) }
|
|
4946
|
+
]);
|
|
4947
|
+
}
|
|
4760
4948
|
setEditingColumnId(null);
|
|
4761
4949
|
} else if (e.key === "Escape") {
|
|
4762
4950
|
handleDurationCancel();
|
|
4763
4951
|
}
|
|
4764
4952
|
},
|
|
4765
|
-
[durationValue, task, onTasksChange, handleDurationCancel, getEndDate]
|
|
4953
|
+
[durationValue, task, onTasksChange, handleDurationCancel, getEndDate, isMilestone]
|
|
4766
4954
|
);
|
|
4767
4955
|
const handleProgressClick = (0, import_react10.useCallback)(
|
|
4768
4956
|
(e) => {
|
|
@@ -4833,17 +5021,54 @@ var TaskListRow = import_react10.default.memo(
|
|
|
4833
5021
|
}
|
|
4834
5022
|
}, [editingProgress]);
|
|
4835
5023
|
(0, import_react10.useEffect)(() => {
|
|
4836
|
-
setDurationValue(getDuration(
|
|
4837
|
-
}, [
|
|
5024
|
+
setDurationValue(getDuration(normalizedTask.startDate, normalizedTask.endDate));
|
|
5025
|
+
}, [normalizedTask.startDate, normalizedTask.endDate, getDuration]);
|
|
4838
5026
|
(0, import_react10.useEffect)(() => {
|
|
4839
5027
|
if (editingDuration && durationInputRef.current) {
|
|
4840
5028
|
durationInputRef.current.focus();
|
|
4841
5029
|
durationInputRef.current.select();
|
|
4842
5030
|
}
|
|
4843
5031
|
}, [editingDuration]);
|
|
5032
|
+
const emitMilestoneDateChange = (0, import_react10.useCallback)((nextDateISO) => {
|
|
5033
|
+
const alignedDate = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${nextDateISO}T00:00:00.000Z`), 1, weekendPredicate) : /* @__PURE__ */ new Date(`${nextDateISO}T00:00:00.000Z`);
|
|
5034
|
+
const clampedRange = clampTaskRangeForIncomingFS(
|
|
5035
|
+
task,
|
|
5036
|
+
alignedDate,
|
|
5037
|
+
alignedDate,
|
|
5038
|
+
allTasks,
|
|
5039
|
+
businessDays,
|
|
5040
|
+
weekendPredicate
|
|
5041
|
+
);
|
|
5042
|
+
const normalized = normalizeTaskDatesForType({
|
|
5043
|
+
...task,
|
|
5044
|
+
startDate: clampedRange.start.toISOString().split("T")[0],
|
|
5045
|
+
endDate: clampedRange.end.toISOString().split("T")[0]
|
|
5046
|
+
});
|
|
5047
|
+
const startDate = parseUTCDate(normalized.startDate);
|
|
5048
|
+
const endDate = parseUTCDate(normalized.endDate);
|
|
5049
|
+
onTasksChange?.([
|
|
5050
|
+
{
|
|
5051
|
+
...normalized,
|
|
5052
|
+
...task.dependencies && {
|
|
5053
|
+
dependencies: recalculateIncomingLags(
|
|
5054
|
+
task,
|
|
5055
|
+
startDate,
|
|
5056
|
+
endDate,
|
|
5057
|
+
allTasks,
|
|
5058
|
+
businessDays,
|
|
5059
|
+
weekendPredicate
|
|
5060
|
+
)
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
]);
|
|
5064
|
+
}, [task, onTasksChange, allTasks, businessDays, weekendPredicate]);
|
|
4844
5065
|
const handleStartDateChange = (0, import_react10.useCallback)(
|
|
4845
5066
|
(newDateISO) => {
|
|
4846
5067
|
if (!newDateISO) return;
|
|
5068
|
+
if (isMilestone) {
|
|
5069
|
+
emitMilestoneDateChange(newDateISO);
|
|
5070
|
+
return;
|
|
5071
|
+
}
|
|
4847
5072
|
let nextEndISO;
|
|
4848
5073
|
const normalizedInputStart = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`), 1, weekendPredicate) : /* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`);
|
|
4849
5074
|
if (businessDays) {
|
|
@@ -4890,11 +5115,15 @@ var TaskListRow = import_react10.default.memo(
|
|
|
4890
5115
|
}
|
|
4891
5116
|
]);
|
|
4892
5117
|
},
|
|
4893
|
-
[task, onTasksChange, businessDays, getDuration, getEndDate, allTasks, weekendPredicate]
|
|
5118
|
+
[task, onTasksChange, businessDays, getDuration, getEndDate, allTasks, weekendPredicate, isMilestone, emitMilestoneDateChange]
|
|
4894
5119
|
);
|
|
4895
5120
|
const handleEndDateChange = (0, import_react10.useCallback)(
|
|
4896
5121
|
(newDateISO) => {
|
|
4897
5122
|
if (!newDateISO) return;
|
|
5123
|
+
if (isMilestone) {
|
|
5124
|
+
emitMilestoneDateChange(newDateISO);
|
|
5125
|
+
return;
|
|
5126
|
+
}
|
|
4898
5127
|
let nextStartISO;
|
|
4899
5128
|
const normalizedInputEnd = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`), -1, weekendPredicate) : /* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`);
|
|
4900
5129
|
if (businessDays) {
|
|
@@ -4941,7 +5170,7 @@ var TaskListRow = import_react10.default.memo(
|
|
|
4941
5170
|
}
|
|
4942
5171
|
]);
|
|
4943
5172
|
},
|
|
4944
|
-
[task, onTasksChange, businessDays, getDuration, weekendPredicate, allTasks]
|
|
5173
|
+
[task, onTasksChange, businessDays, getDuration, weekendPredicate, allTasks, isMilestone, emitMilestoneDateChange]
|
|
4945
5174
|
);
|
|
4946
5175
|
const handleRowClickInternal = (0, import_react10.useCallback)(() => {
|
|
4947
5176
|
onRowClick?.(task.id);
|
|
@@ -5212,8 +5441,8 @@ var TaskListRow = import_react10.default.memo(
|
|
|
5212
5441
|
},
|
|
5213
5442
|
[selectedChip?.successorId, selectedChip?.predecessorId, selectedChip?.linkType, onRemoveDependency, onChipSelect]
|
|
5214
5443
|
);
|
|
5215
|
-
const startDateISO = toISODate(
|
|
5216
|
-
const endDateISO = editingDuration ? getEndDate(
|
|
5444
|
+
const startDateISO = toISODate(normalizedTask.startDate);
|
|
5445
|
+
const endDateISO = editingDuration ? getEndDate(normalizedTask.startDate, durationValue) : toISODate(normalizedTask.endDate);
|
|
5217
5446
|
const numberCell = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
5218
5447
|
"div",
|
|
5219
5448
|
{
|
|
@@ -5619,10 +5848,10 @@ var TaskListRow = import_react10.default.memo(
|
|
|
5619
5848
|
{
|
|
5620
5849
|
ref: durationInputRef,
|
|
5621
5850
|
type: "number",
|
|
5622
|
-
min:
|
|
5851
|
+
min: 0,
|
|
5623
5852
|
step: 1,
|
|
5624
5853
|
value: durationValue,
|
|
5625
|
-
onChange: (e) => applyDurationChange(parseInt(e.target.value, 10) ||
|
|
5854
|
+
onChange: (e) => applyDurationChange(parseInt(e.target.value, 10) || 0),
|
|
5626
5855
|
onBlur: handleDurationSave,
|
|
5627
5856
|
onKeyDown: handleDurationKeyDown,
|
|
5628
5857
|
className: "gantt-tl-number-input"
|
|
@@ -5683,14 +5912,11 @@ var TaskListRow = import_react10.default.memo(
|
|
|
5683
5912
|
]
|
|
5684
5913
|
}
|
|
5685
5914
|
),
|
|
5686
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.
|
|
5915
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5687
5916
|
"span",
|
|
5688
5917
|
{
|
|
5689
5918
|
style: editingDuration ? { visibility: "hidden", pointerEvents: "none" } : void 0,
|
|
5690
|
-
children:
|
|
5691
|
-
getDuration(task.startDate, task.endDate),
|
|
5692
|
-
"\u0434"
|
|
5693
|
-
]
|
|
5919
|
+
children: isMilestone ? "0" : `${getDuration(normalizedTask.startDate, normalizedTask.endDate)}\u0434`
|
|
5694
5920
|
}
|
|
5695
5921
|
)
|
|
5696
5922
|
]
|
|
@@ -7180,7 +7406,8 @@ function GanttChartInner(props, ref) {
|
|
|
7180
7406
|
}
|
|
7181
7407
|
return;
|
|
7182
7408
|
}
|
|
7183
|
-
const
|
|
7409
|
+
const sourceTasks = tasks.map((task) => task.id === updatedTask.id ? updatedTask : task);
|
|
7410
|
+
const cascadedTasks = disableConstraints ? [updatedTask] : universalCascade(updatedTask, newStart, newEnd, sourceTasks, businessDays, isCustomWeekend);
|
|
7184
7411
|
onTasksChange?.(cascadedTasks);
|
|
7185
7412
|
}, [tasks, onTasksChange, disableConstraints, editingTaskId, businessDays, isCustomWeekend]);
|
|
7186
7413
|
const handleDelete = (0, import_react13.useCallback)((taskId) => {
|
|
@@ -7634,6 +7861,8 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
|
|
|
7634
7861
|
calculateDependencyPath,
|
|
7635
7862
|
calculateGridLines,
|
|
7636
7863
|
calculateGridWidth,
|
|
7864
|
+
calculateMilestoneConnectionBounds,
|
|
7865
|
+
calculateMilestoneGeometry,
|
|
7637
7866
|
calculateMonthGridLines,
|
|
7638
7867
|
calculateOrthogonalPath,
|
|
7639
7868
|
calculateSuccessorDate,
|
|
@@ -7685,6 +7914,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
|
|
|
7685
7914
|
nameContains,
|
|
7686
7915
|
normalizeDependencyLag,
|
|
7687
7916
|
normalizeHierarchyTasks,
|
|
7917
|
+
normalizePredecessorDates,
|
|
7688
7918
|
normalizeTaskDates,
|
|
7689
7919
|
normalizeUTCDate,
|
|
7690
7920
|
not,
|
|
@@ -7700,6 +7930,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
|
|
|
7700
7930
|
removeDependenciesBetweenTasks,
|
|
7701
7931
|
resizeTaskWithCascade,
|
|
7702
7932
|
resolveDateRangeFromPixels,
|
|
7933
|
+
resolveTaskHorizontalGeometry,
|
|
7703
7934
|
shiftBusinessDayOffset,
|
|
7704
7935
|
subtractBusinessDays,
|
|
7705
7936
|
universalCascade,
|