gantt-lib 0.1.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +42 -8
- package/dist/index.d.ts +42 -8
- package/dist/index.js +679 -196
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +695 -213
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +273 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -55,6 +55,7 @@ __export(index_exports, {
|
|
|
55
55
|
calculateTaskBar: () => calculateTaskBar,
|
|
56
56
|
calculateWeekendBlocks: () => calculateWeekendBlocks,
|
|
57
57
|
cascadeByLinks: () => cascadeByLinks,
|
|
58
|
+
computeLagFromDates: () => computeLagFromDates,
|
|
58
59
|
detectCycles: () => detectCycles,
|
|
59
60
|
detectEdgeZone: () => detectEdgeZone,
|
|
60
61
|
formatDateLabel: () => formatDateLabel,
|
|
@@ -271,11 +272,35 @@ function detectCycles(tasks) {
|
|
|
271
272
|
}
|
|
272
273
|
return { hasCycle: false };
|
|
273
274
|
}
|
|
275
|
+
function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd) {
|
|
276
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
277
|
+
const pS = Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate());
|
|
278
|
+
const pE = Date.UTC(predEnd.getUTCFullYear(), predEnd.getUTCMonth(), predEnd.getUTCDate());
|
|
279
|
+
const sS = Date.UTC(succStart.getUTCFullYear(), succStart.getUTCMonth(), succStart.getUTCDate());
|
|
280
|
+
const sE = Date.UTC(succEnd.getUTCFullYear(), succEnd.getUTCMonth(), succEnd.getUTCDate());
|
|
281
|
+
switch (linkType) {
|
|
282
|
+
case "FS":
|
|
283
|
+
return Math.round((sS - pE) / DAY_MS) - 1;
|
|
284
|
+
case "SS":
|
|
285
|
+
return Math.round((sS - pS) / DAY_MS);
|
|
286
|
+
case "FF":
|
|
287
|
+
return Math.round((sE - pE) / DAY_MS);
|
|
288
|
+
case "SF":
|
|
289
|
+
return Math.round((sE - pS) / DAY_MS) + 1;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
274
292
|
function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag = 0) {
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
293
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
294
|
+
switch (linkType) {
|
|
295
|
+
case "FS":
|
|
296
|
+
return new Date(predecessorEnd.getTime() + (lag + 1) * DAY_MS);
|
|
297
|
+
case "SS":
|
|
298
|
+
return new Date(predecessorStart.getTime() + lag * DAY_MS);
|
|
299
|
+
case "FF":
|
|
300
|
+
return new Date(predecessorEnd.getTime() + lag * DAY_MS);
|
|
301
|
+
case "SF":
|
|
302
|
+
return new Date(predecessorStart.getTime() + (lag - 1) * DAY_MS);
|
|
303
|
+
}
|
|
279
304
|
}
|
|
280
305
|
function validateDependencies(tasks) {
|
|
281
306
|
const errors = [];
|
|
@@ -422,35 +447,10 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks) {
|
|
|
422
447
|
return task.dependencies.map((dep) => {
|
|
423
448
|
const predecessor = taskById.get(dep.taskId);
|
|
424
449
|
if (!predecessor) return dep;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
);
|
|
430
|
-
return { ...dep, lag: lagDays };
|
|
431
|
-
}
|
|
432
|
-
if (dep.type === "SS") {
|
|
433
|
-
const predStart = new Date(predecessor.startDate);
|
|
434
|
-
const lagDays = Math.max(0, Math.round(
|
|
435
|
-
(Date.UTC(newStartDate.getUTCFullYear(), newStartDate.getUTCMonth(), newStartDate.getUTCDate()) - Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate())) / (24 * 60 * 60 * 1e3)
|
|
436
|
-
));
|
|
437
|
-
return { ...dep, lag: lagDays };
|
|
438
|
-
}
|
|
439
|
-
if (dep.type === "FF") {
|
|
440
|
-
const predEnd = new Date(predecessor.endDate);
|
|
441
|
-
const lagDays = Math.round(
|
|
442
|
-
(Date.UTC(newEndDate.getUTCFullYear(), newEndDate.getUTCMonth(), newEndDate.getUTCDate()) - Date.UTC(predEnd.getUTCFullYear(), predEnd.getUTCMonth(), predEnd.getUTCDate())) / (24 * 60 * 60 * 1e3)
|
|
443
|
-
);
|
|
444
|
-
return { ...dep, lag: lagDays };
|
|
445
|
-
}
|
|
446
|
-
if (dep.type === "SF") {
|
|
447
|
-
const predStart = new Date(predecessor.startDate);
|
|
448
|
-
const lagDays = Math.min(0, Math.round(
|
|
449
|
-
(Date.UTC(newEndDate.getUTCFullYear(), newEndDate.getUTCMonth(), newEndDate.getUTCDate()) - Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate()) + 24 * 60 * 60 * 1e3) / (24 * 60 * 60 * 1e3)
|
|
450
|
-
));
|
|
451
|
-
return { ...dep, lag: lagDays };
|
|
452
|
-
}
|
|
453
|
-
return dep;
|
|
450
|
+
const predStart = new Date(predecessor.startDate);
|
|
451
|
+
const predEnd = new Date(predecessor.endDate);
|
|
452
|
+
const lagDays = computeLagFromDates(dep.type, predStart, predEnd, newStartDate, newEndDate);
|
|
453
|
+
return { ...dep, lag: lagDays };
|
|
454
454
|
});
|
|
455
455
|
}
|
|
456
456
|
function getAllDependencyEdges(tasks) {
|
|
@@ -1169,29 +1169,51 @@ var useTaskDrag = (options) => {
|
|
|
1169
1169
|
// src/components/TaskRow/TaskRow.tsx
|
|
1170
1170
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1171
1171
|
var arePropsEqual = (prevProps, nextProps) => {
|
|
1172
|
-
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;
|
|
1172
|
+
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;
|
|
1173
1173
|
};
|
|
1174
1174
|
var TaskRow = import_react3.default.memo(
|
|
1175
|
-
({ task, monthStart, dayWidth, rowHeight, onChange, onDragStateChange, rowIndex, allTasks, enableAutoSchedule, disableConstraints, overridePosition, onCascadeProgress, onCascade, divider }) => {
|
|
1175
|
+
({ task, monthStart, dayWidth, rowHeight, onChange, onDragStateChange, rowIndex, allTasks, enableAutoSchedule, disableConstraints, overridePosition, onCascadeProgress, onCascade, divider, highlightExpiredTasks }) => {
|
|
1176
1176
|
const { divider: taskDivider } = task;
|
|
1177
1177
|
const taskStartDate = (0, import_react3.useMemo)(() => parseUTCDate(task.startDate), [task.startDate]);
|
|
1178
1178
|
const taskEndDate = (0, import_react3.useMemo)(() => parseUTCDate(task.endDate), [task.endDate]);
|
|
1179
|
+
const isExpired = (0, import_react3.useMemo)(() => {
|
|
1180
|
+
if (!highlightExpiredTasks) return false;
|
|
1181
|
+
const now = /* @__PURE__ */ new Date();
|
|
1182
|
+
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
1183
|
+
const tomorrow = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate() + 1));
|
|
1184
|
+
const taskStart = parseUTCDate(task.startDate);
|
|
1185
|
+
const taskEnd = parseUTCDate(task.endDate);
|
|
1186
|
+
const actualProgress = task.progress ?? 0;
|
|
1187
|
+
if (actualProgress >= 100) {
|
|
1188
|
+
return false;
|
|
1189
|
+
}
|
|
1190
|
+
const msPerDay = 1e3 * 60 * 60 * 24;
|
|
1191
|
+
const taskDuration = taskEnd.getTime() - taskStart.getTime() + msPerDay;
|
|
1192
|
+
const isTaskEndingTodayOrTomorrow = today.getUTCFullYear() === taskEnd.getUTCFullYear() && today.getUTCMonth() === taskEnd.getUTCMonth() && today.getUTCDate() === taskEnd.getUTCDate() || tomorrow.getUTCFullYear() === taskEnd.getUTCFullYear() && tomorrow.getUTCMonth() === taskEnd.getUTCMonth() && tomorrow.getUTCDate() === taskEnd.getUTCDate();
|
|
1193
|
+
const elapsedCutoff = isTaskEndingTodayOrTomorrow ? new Date(today.getTime() - msPerDay) : today;
|
|
1194
|
+
const daysFromStart = elapsedCutoff.getTime() - taskStart.getTime();
|
|
1195
|
+
const todayPosition = Math.min(100, Math.max(0, daysFromStart / taskDuration * 100));
|
|
1196
|
+
return actualProgress < todayPosition;
|
|
1197
|
+
}, [task.startDate, task.endDate, task.progress, highlightExpiredTasks]);
|
|
1179
1198
|
const { left, width } = (0, import_react3.useMemo)(
|
|
1180
1199
|
() => calculateTaskBar(taskStartDate, taskEndDate, monthStart, dayWidth),
|
|
1181
1200
|
[taskStartDate, taskEndDate, monthStart, dayWidth]
|
|
1182
1201
|
);
|
|
1183
|
-
const barColor = task.color || "var(--gantt-task-bar-default-color)";
|
|
1202
|
+
const barColor = isExpired ? "var(--gantt-expired-color)" : task.color || "var(--gantt-task-bar-default-color)";
|
|
1184
1203
|
const progressWidth = (0, import_react3.useMemo)(() => {
|
|
1185
1204
|
if (task.progress === void 0 || task.progress <= 0) return 0;
|
|
1186
1205
|
return Math.min(100, Math.max(0, Math.round(task.progress)));
|
|
1187
1206
|
}, [task.progress]);
|
|
1188
1207
|
const progressColor = (0, import_react3.useMemo)(() => {
|
|
1208
|
+
if (isExpired) {
|
|
1209
|
+
return "color-mix(in srgb, var(--gantt-expired-color) 40%, black)";
|
|
1210
|
+
}
|
|
1189
1211
|
if (progressWidth === 100) {
|
|
1190
1212
|
return task.accepted ? "var(--gantt-progress-accepted, #22c55e)" : "var(--gantt-progress-completed, #fbbf24)";
|
|
1191
1213
|
}
|
|
1192
1214
|
const baseColor = task.color || "var(--gantt-task-bar-default-color)";
|
|
1193
1215
|
return `color-mix(in srgb, ${baseColor} 40%, black)`;
|
|
1194
|
-
}, [progressWidth, task.accepted, task.color]);
|
|
1216
|
+
}, [isExpired, progressWidth, task.accepted, task.color]);
|
|
1195
1217
|
const handleDragEnd = (result) => {
|
|
1196
1218
|
const updatedTask = {
|
|
1197
1219
|
...task,
|
|
@@ -1477,60 +1499,11 @@ var DragGuideLines_default = DragGuideLines;
|
|
|
1477
1499
|
var import_react6 = __toESM(require("react"));
|
|
1478
1500
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1479
1501
|
function calculateEffectiveLag(edge, predPosition, succPosition, monthStart, dayWidth) {
|
|
1480
|
-
const
|
|
1481
|
-
const
|
|
1482
|
-
const
|
|
1483
|
-
const
|
|
1484
|
-
|
|
1485
|
-
switch (edge.type) {
|
|
1486
|
-
case "FS":
|
|
1487
|
-
lagMs = Date.UTC(
|
|
1488
|
-
succStartDate.getUTCFullYear(),
|
|
1489
|
-
succStartDate.getUTCMonth(),
|
|
1490
|
-
succStartDate.getUTCDate()
|
|
1491
|
-
) - Date.UTC(
|
|
1492
|
-
predEndDate.getUTCFullYear(),
|
|
1493
|
-
predEndDate.getUTCMonth(),
|
|
1494
|
-
predEndDate.getUTCDate()
|
|
1495
|
-
) - 24 * 60 * 60 * 1e3;
|
|
1496
|
-
break;
|
|
1497
|
-
case "SS":
|
|
1498
|
-
lagMs = Date.UTC(
|
|
1499
|
-
succStartDate.getUTCFullYear(),
|
|
1500
|
-
succStartDate.getUTCMonth(),
|
|
1501
|
-
succStartDate.getUTCDate()
|
|
1502
|
-
) - Date.UTC(
|
|
1503
|
-
predStartDate.getUTCFullYear(),
|
|
1504
|
-
predStartDate.getUTCMonth(),
|
|
1505
|
-
predStartDate.getUTCDate()
|
|
1506
|
-
);
|
|
1507
|
-
break;
|
|
1508
|
-
case "FF":
|
|
1509
|
-
lagMs = Date.UTC(
|
|
1510
|
-
succEndDate.getUTCFullYear(),
|
|
1511
|
-
succEndDate.getUTCMonth(),
|
|
1512
|
-
succEndDate.getUTCDate()
|
|
1513
|
-
) - Date.UTC(
|
|
1514
|
-
predEndDate.getUTCFullYear(),
|
|
1515
|
-
predEndDate.getUTCMonth(),
|
|
1516
|
-
predEndDate.getUTCDate()
|
|
1517
|
-
);
|
|
1518
|
-
break;
|
|
1519
|
-
case "SF":
|
|
1520
|
-
lagMs = Date.UTC(
|
|
1521
|
-
succEndDate.getUTCFullYear(),
|
|
1522
|
-
succEndDate.getUTCMonth(),
|
|
1523
|
-
succEndDate.getUTCDate()
|
|
1524
|
-
) - Date.UTC(
|
|
1525
|
-
predStartDate.getUTCFullYear(),
|
|
1526
|
-
predStartDate.getUTCMonth(),
|
|
1527
|
-
predStartDate.getUTCDate()
|
|
1528
|
-
) + 24 * 60 * 60 * 1e3;
|
|
1529
|
-
break;
|
|
1530
|
-
default:
|
|
1531
|
-
return 0;
|
|
1532
|
-
}
|
|
1533
|
-
return Math.round(lagMs / (24 * 60 * 60 * 1e3));
|
|
1502
|
+
const predStart = pixelsToDate(predPosition.left, monthStart, dayWidth);
|
|
1503
|
+
const predEnd = pixelsToDate(predPosition.right - dayWidth, monthStart, dayWidth);
|
|
1504
|
+
const succStart = pixelsToDate(succPosition.left, monthStart, dayWidth);
|
|
1505
|
+
const succEnd = pixelsToDate(succPosition.right - dayWidth, monthStart, dayWidth);
|
|
1506
|
+
return computeLagFromDates(edge.type, predStart, predEnd, succStart, succEnd);
|
|
1534
1507
|
}
|
|
1535
1508
|
var DependencyLines = import_react6.default.memo(({
|
|
1536
1509
|
tasks,
|
|
@@ -1538,7 +1511,8 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
1538
1511
|
dayWidth,
|
|
1539
1512
|
rowHeight,
|
|
1540
1513
|
gridWidth,
|
|
1541
|
-
dragOverrides
|
|
1514
|
+
dragOverrides,
|
|
1515
|
+
selectedDep
|
|
1542
1516
|
}) => {
|
|
1543
1517
|
const { taskPositions, taskIndices } = (0, import_react6.useMemo)(() => {
|
|
1544
1518
|
const positions = /* @__PURE__ */ new Map();
|
|
@@ -1652,30 +1626,60 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
1652
1626
|
}
|
|
1653
1627
|
)
|
|
1654
1628
|
}
|
|
1655
|
-
)
|
|
1656
|
-
] }),
|
|
1657
|
-
lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react6.default.Fragment, { children: [
|
|
1658
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1659
|
-
"path",
|
|
1660
|
-
{
|
|
1661
|
-
d: path,
|
|
1662
|
-
className: hasCycle ? "gantt-dependency-path gantt-dependency-cycle" : "gantt-dependency-path",
|
|
1663
|
-
markerEnd: hasCycle ? "url(#arrowhead-cycle)" : "url(#arrowhead)"
|
|
1664
|
-
}
|
|
1665
1629
|
),
|
|
1666
|
-
|
|
1667
|
-
"
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1631
|
+
"marker",
|
|
1668
1632
|
{
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1633
|
+
id: "arrowhead-selected",
|
|
1634
|
+
markerWidth: "8",
|
|
1635
|
+
markerHeight: "6",
|
|
1636
|
+
markerUnits: "userSpaceOnUse",
|
|
1637
|
+
refX: "7",
|
|
1638
|
+
refY: "3",
|
|
1639
|
+
orient: "auto",
|
|
1640
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1641
|
+
"polygon",
|
|
1642
|
+
{
|
|
1643
|
+
points: "0 0, 8 3, 0 6",
|
|
1644
|
+
fill: "#ef4444"
|
|
1645
|
+
}
|
|
1646
|
+
)
|
|
1676
1647
|
}
|
|
1677
1648
|
)
|
|
1678
|
-
] },
|
|
1649
|
+
] }),
|
|
1650
|
+
lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder }) => {
|
|
1651
|
+
const isSelected = selectedDep != null && id === `${selectedDep.predecessorId}-${selectedDep.successorId}-${selectedDep.linkType}`;
|
|
1652
|
+
let pathClassName = "gantt-dependency-path";
|
|
1653
|
+
if (isSelected) pathClassName += " gantt-dependency-selected";
|
|
1654
|
+
else if (hasCycle) pathClassName += " gantt-dependency-cycle";
|
|
1655
|
+
let markerEnd;
|
|
1656
|
+
if (isSelected) markerEnd = "url(#arrowhead-selected)";
|
|
1657
|
+
else if (hasCycle) markerEnd = "url(#arrowhead-cycle)";
|
|
1658
|
+
else markerEnd = "url(#arrowhead)";
|
|
1659
|
+
const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : "var(--gantt-dependency-line-color, #666666)";
|
|
1660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react6.default.Fragment, { children: [
|
|
1661
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1662
|
+
"path",
|
|
1663
|
+
{
|
|
1664
|
+
d: path,
|
|
1665
|
+
className: pathClassName,
|
|
1666
|
+
markerEnd
|
|
1667
|
+
}
|
|
1668
|
+
),
|
|
1669
|
+
lag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1670
|
+
"text",
|
|
1671
|
+
{
|
|
1672
|
+
className: "gantt-dependency-lag-label",
|
|
1673
|
+
x: lag < 0 ? toX + 14 : toX - 14,
|
|
1674
|
+
y: reverseOrder ? fromY - 4 : fromY + 12,
|
|
1675
|
+
textAnchor: "middle",
|
|
1676
|
+
fontSize: "10",
|
|
1677
|
+
fill: lagColor,
|
|
1678
|
+
children: lag > 0 ? `+${lag}` : `${lag}`
|
|
1679
|
+
}
|
|
1680
|
+
)
|
|
1681
|
+
] }, id);
|
|
1682
|
+
})
|
|
1679
1683
|
]
|
|
1680
1684
|
}
|
|
1681
1685
|
);
|
|
@@ -1684,17 +1688,51 @@ DependencyLines.displayName = "DependencyLines";
|
|
|
1684
1688
|
var DependencyLines_default = DependencyLines;
|
|
1685
1689
|
|
|
1686
1690
|
// src/components/TaskList/TaskList.tsx
|
|
1687
|
-
var import_react11 = require("react");
|
|
1691
|
+
var import_react11 = __toESM(require("react"));
|
|
1692
|
+
|
|
1693
|
+
// src/components/ui/Popover.tsx
|
|
1694
|
+
var RadixPopover = __toESM(require("@radix-ui/react-popover"));
|
|
1695
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1696
|
+
var Popover = ({ open, onOpenChange, children }) => {
|
|
1697
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RadixPopover.Root, { open, onOpenChange, children });
|
|
1698
|
+
};
|
|
1699
|
+
var PopoverTrigger = RadixPopover.Trigger;
|
|
1700
|
+
var PopoverContent = ({
|
|
1701
|
+
children,
|
|
1702
|
+
className,
|
|
1703
|
+
align = "start",
|
|
1704
|
+
side = "bottom",
|
|
1705
|
+
portal = true,
|
|
1706
|
+
collisionPadding = 8,
|
|
1707
|
+
onInteractOutside
|
|
1708
|
+
}) => {
|
|
1709
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1710
|
+
RadixPopover.Content,
|
|
1711
|
+
{
|
|
1712
|
+
className: `gantt-popover${className ? ` ${className}` : ""}`,
|
|
1713
|
+
align,
|
|
1714
|
+
side,
|
|
1715
|
+
collisionPadding,
|
|
1716
|
+
sideOffset: 4,
|
|
1717
|
+
onInteractOutside,
|
|
1718
|
+
children
|
|
1719
|
+
}
|
|
1720
|
+
);
|
|
1721
|
+
if (portal) {
|
|
1722
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RadixPopover.Portal, { children: content });
|
|
1723
|
+
}
|
|
1724
|
+
return content;
|
|
1725
|
+
};
|
|
1688
1726
|
|
|
1689
1727
|
// src/components/TaskList/TaskListRow.tsx
|
|
1690
1728
|
var import_react10 = __toESM(require("react"));
|
|
1691
1729
|
|
|
1692
1730
|
// src/components/ui/Input.tsx
|
|
1693
1731
|
var import_react7 = __toESM(require("react"));
|
|
1694
|
-
var
|
|
1732
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1695
1733
|
var Input = import_react7.default.forwardRef(
|
|
1696
1734
|
({ className, ...props }, ref) => {
|
|
1697
|
-
return /* @__PURE__ */ (0,
|
|
1735
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1698
1736
|
"input",
|
|
1699
1737
|
{
|
|
1700
1738
|
ref,
|
|
@@ -1714,7 +1752,7 @@ var import_date_fns3 = require("date-fns");
|
|
|
1714
1752
|
var import_react8 = require("react");
|
|
1715
1753
|
var import_date_fns2 = require("date-fns");
|
|
1716
1754
|
var import_locale2 = require("date-fns/locale");
|
|
1717
|
-
var
|
|
1755
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1718
1756
|
function getDayClassName(day, selected) {
|
|
1719
1757
|
const classes = ["gantt-day-btn"];
|
|
1720
1758
|
if (selected && (0, import_date_fns2.isSameDay)(day, selected)) classes.push("selected");
|
|
@@ -1784,12 +1822,12 @@ var Calendar = ({
|
|
|
1784
1822
|
const emptyDays = ((0, import_date_fns2.getDay)(firstDay) + 6) % 7;
|
|
1785
1823
|
const monthKey = (0, import_date_fns2.format)(month, "yyyy-MM");
|
|
1786
1824
|
const monthLabel = (0, import_date_fns2.format)(month, "LLLL yyyy", { locale: import_locale2.ru });
|
|
1787
|
-
const emptyCells = Array.from({ length: emptyDays }, (_, i) => /* @__PURE__ */ (0,
|
|
1825
|
+
const emptyCells = Array.from({ length: emptyDays }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "gantt-cal-empty-day" }, `e-${i}`));
|
|
1788
1826
|
const dayCells = Array.from({ length: totalDays }, (_, i) => {
|
|
1789
1827
|
const dayNum = i + 1;
|
|
1790
1828
|
const day = new Date(month.getFullYear(), month.getMonth(), dayNum);
|
|
1791
1829
|
const className = getDayClassName(day, selected);
|
|
1792
|
-
return /* @__PURE__ */ (0,
|
|
1830
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1793
1831
|
"button",
|
|
1794
1832
|
{
|
|
1795
1833
|
type: "button",
|
|
@@ -1805,9 +1843,9 @@ var Calendar = ({
|
|
|
1805
1843
|
dayNum
|
|
1806
1844
|
);
|
|
1807
1845
|
});
|
|
1808
|
-
return /* @__PURE__ */ (0,
|
|
1809
|
-
/* @__PURE__ */ (0,
|
|
1810
|
-
/* @__PURE__ */ (0,
|
|
1846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "gantt-cal-month", "data-month": monthKey, children: [
|
|
1847
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "gantt-cal-month-header", children: monthLabel }),
|
|
1848
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "gantt-cal-month-days", children: [
|
|
1811
1849
|
emptyCells,
|
|
1812
1850
|
dayCells
|
|
1813
1851
|
] })
|
|
@@ -1819,42 +1857,10 @@ var Calendar = ({
|
|
|
1819
1857
|
() => months.map(renderMonth),
|
|
1820
1858
|
[months, renderMonth]
|
|
1821
1859
|
);
|
|
1822
|
-
return /* @__PURE__ */ (0,
|
|
1860
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: scrollRef, className: "gantt-cal-container", children: renderedMonths });
|
|
1823
1861
|
};
|
|
1824
1862
|
Calendar.displayName = "Calendar";
|
|
1825
1863
|
|
|
1826
|
-
// src/components/ui/Popover.tsx
|
|
1827
|
-
var RadixPopover = __toESM(require("@radix-ui/react-popover"));
|
|
1828
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1829
|
-
var Popover = ({ open, onOpenChange, children }) => {
|
|
1830
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RadixPopover.Root, { open, onOpenChange, children });
|
|
1831
|
-
};
|
|
1832
|
-
var PopoverTrigger = RadixPopover.Trigger;
|
|
1833
|
-
var PopoverContent = ({
|
|
1834
|
-
children,
|
|
1835
|
-
className,
|
|
1836
|
-
align = "start",
|
|
1837
|
-
side = "bottom",
|
|
1838
|
-
portal = true,
|
|
1839
|
-
collisionPadding = 8
|
|
1840
|
-
}) => {
|
|
1841
|
-
const content = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1842
|
-
RadixPopover.Content,
|
|
1843
|
-
{
|
|
1844
|
-
className: `gantt-popover${className ? ` ${className}` : ""}`,
|
|
1845
|
-
align,
|
|
1846
|
-
side,
|
|
1847
|
-
collisionPadding,
|
|
1848
|
-
sideOffset: 4,
|
|
1849
|
-
children
|
|
1850
|
-
}
|
|
1851
|
-
);
|
|
1852
|
-
if (portal) {
|
|
1853
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RadixPopover.Portal, { children: content });
|
|
1854
|
-
}
|
|
1855
|
-
return content;
|
|
1856
|
-
};
|
|
1857
|
-
|
|
1858
1864
|
// src/components/ui/DatePicker.tsx
|
|
1859
1865
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1860
1866
|
var DatePicker = ({
|
|
@@ -1919,19 +1925,198 @@ var DatePicker = ({
|
|
|
1919
1925
|
};
|
|
1920
1926
|
DatePicker.displayName = "DatePicker";
|
|
1921
1927
|
|
|
1922
|
-
// src/components/TaskList/
|
|
1928
|
+
// src/components/TaskList/DepIcons.tsx
|
|
1923
1929
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1930
|
+
var DepIconFS = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1931
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m10 15 5 5 5-5" }),
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 4h7a4 4 0 0 1 4 4v12" })
|
|
1933
|
+
] });
|
|
1934
|
+
var DepIconSS = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1935
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M3 5v14" }),
|
|
1936
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M21 12H7" }),
|
|
1937
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m15 18 6-6-6-6" })
|
|
1938
|
+
] });
|
|
1939
|
+
var DepIconFF = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M17 12H3" }),
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m11 18 6-6-6-6" }),
|
|
1942
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M21 5v14" })
|
|
1943
|
+
] });
|
|
1944
|
+
var DepIconSF = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1945
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m14 15-5 5-5-5" }),
|
|
1946
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M20 4h-7a4 4 0 0 0-4 4v12" })
|
|
1947
|
+
] });
|
|
1948
|
+
var LINK_TYPE_ICONS = {
|
|
1949
|
+
FS: DepIconFS,
|
|
1950
|
+
SS: DepIconSS,
|
|
1951
|
+
FF: DepIconFF,
|
|
1952
|
+
SF: DepIconSF
|
|
1953
|
+
};
|
|
1954
|
+
var LINK_TYPE_LABELS = {
|
|
1955
|
+
FS: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435-\u043D\u0430\u0447\u0430\u043B\u043E",
|
|
1956
|
+
SS: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043D\u0430\u0447\u0430\u043B\u043E",
|
|
1957
|
+
FF: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435",
|
|
1958
|
+
SF: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435"
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
// src/components/TaskList/TaskListRow.tsx
|
|
1962
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1963
|
+
var TrashIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
|
|
1965
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3 6h18" }),
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
1967
|
+
] });
|
|
1968
|
+
function formatDepDescription(type, lag) {
|
|
1969
|
+
const effectiveLag = lag ?? 0;
|
|
1970
|
+
if (type === "FS") {
|
|
1971
|
+
if (effectiveLag > 0) return `\u041D\u0430\u0447\u0430\u0442\u044C \u0447\u0435\u0440\u0435\u0437 ${effectiveLag} \u0434\u043D. \u043F\u043E\u0441\u043B\u0435 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1972
|
+
if (effectiveLag < 0) return `\u041D\u0430\u0447\u0430\u0442\u044C \u0437\u0430 ${Math.abs(effectiveLag)} \u0434\u043D. \u0434\u043E \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1973
|
+
return `\u041D\u0430\u0447\u0430\u0442\u044C \u0441\u0440\u0430\u0437\u0443 \u043F\u043E\u0441\u043B\u0435 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1974
|
+
}
|
|
1975
|
+
if (type === "FF") {
|
|
1976
|
+
if (effectiveLag > 0) return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0447\u0435\u0440\u0435\u0437 ${effectiveLag} \u0434\u043D. \u043F\u043E\u0441\u043B\u0435 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1977
|
+
if (effectiveLag < 0) return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0437\u0430 ${Math.abs(effectiveLag)} \u0434\u043D. \u0434\u043E \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1978
|
+
return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1979
|
+
}
|
|
1980
|
+
if (type === "SS") {
|
|
1981
|
+
if (effectiveLag > 0) return `\u041D\u0430\u0447\u0430\u0442\u044C \u0447\u0435\u0440\u0435\u0437 ${effectiveLag} \u0434\u043D. \u043F\u043E\u0441\u043B\u0435 \u043D\u0430\u0447\u0430\u043B\u0430`;
|
|
1982
|
+
if (effectiveLag < 0) return `\u041D\u0430\u0447\u0430\u0442\u044C \u0437\u0430 ${Math.abs(effectiveLag)} \u0434\u043D. \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430`;
|
|
1983
|
+
return `\u041D\u0430\u0447\u0430\u0442\u044C \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u043D\u0430\u0447\u0430\u043B\u043E\u043C`;
|
|
1984
|
+
}
|
|
1985
|
+
if (type === "SF") {
|
|
1986
|
+
if (effectiveLag > 0) return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0447\u0435\u0440\u0435\u0437 ${effectiveLag} \u0434\u043D. \u043F\u043E\u0441\u043B\u0435 \u043D\u0430\u0447\u0430\u043B\u0430`;
|
|
1987
|
+
if (effectiveLag < 0) return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0437\u0430 ${Math.abs(effectiveLag)} \u0434\u043D. \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430`;
|
|
1988
|
+
return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430`;
|
|
1989
|
+
}
|
|
1990
|
+
return "";
|
|
1991
|
+
}
|
|
1992
|
+
var DepChip = ({
|
|
1993
|
+
lag,
|
|
1994
|
+
dep,
|
|
1995
|
+
taskId,
|
|
1996
|
+
predecessorName,
|
|
1997
|
+
selectedChip,
|
|
1998
|
+
disableDependencyEditing,
|
|
1999
|
+
onChipSelect,
|
|
2000
|
+
onRowClick,
|
|
2001
|
+
onScrollToTask,
|
|
2002
|
+
onRemoveDependency,
|
|
2003
|
+
onChipSelectClear
|
|
2004
|
+
}) => {
|
|
2005
|
+
const isSelected = selectedChip?.successorId === taskId && selectedChip?.predecessorId === dep.taskId && selectedChip?.linkType === dep.type;
|
|
2006
|
+
const handleClick = (e) => {
|
|
2007
|
+
e.stopPropagation();
|
|
2008
|
+
if (disableDependencyEditing) return;
|
|
2009
|
+
onChipSelect?.(isSelected ? null : { successorId: taskId, predecessorId: dep.taskId, linkType: dep.type });
|
|
2010
|
+
if (!isSelected) {
|
|
2011
|
+
onRowClick?.(taskId);
|
|
2012
|
+
onScrollToTask?.(taskId);
|
|
2013
|
+
}
|
|
2014
|
+
};
|
|
2015
|
+
const handleTrashClick = (e) => {
|
|
2016
|
+
e.stopPropagation();
|
|
2017
|
+
onRemoveDependency?.(taskId, dep.taskId, dep.type);
|
|
2018
|
+
onChipSelectClear();
|
|
2019
|
+
};
|
|
2020
|
+
const Icon = LINK_TYPE_ICONS[dep.type];
|
|
2021
|
+
const depPrefix = formatDepDescription(dep.type, lag);
|
|
2022
|
+
const depName = predecessorName ?? dep.taskId;
|
|
2023
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: isSelected, onOpenChange: (open) => {
|
|
2024
|
+
}, children: [
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "gantt-tl-dep-chip-wrapper", children: [
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2027
|
+
"span",
|
|
2028
|
+
{
|
|
2029
|
+
className: `gantt-tl-dep-chip${isSelected ? " gantt-tl-dep-chip-selected" : ""}`,
|
|
2030
|
+
onClick: handleClick,
|
|
2031
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
2032
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, {}),
|
|
2033
|
+
lag != null && lag !== 0 ? lag > 0 ? `+${lag}` : `${lag}` : ""
|
|
2034
|
+
] })
|
|
2035
|
+
}
|
|
2036
|
+
),
|
|
2037
|
+
!disableDependencyEditing && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2038
|
+
"button",
|
|
2039
|
+
{
|
|
2040
|
+
type: "button",
|
|
2041
|
+
className: "gantt-tl-dep-chip-trash",
|
|
2042
|
+
"aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2043
|
+
onClick: handleTrashClick,
|
|
2044
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TrashIcon, {})
|
|
2045
|
+
}
|
|
2046
|
+
)
|
|
2047
|
+
] }) }),
|
|
2048
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2049
|
+
PopoverContent,
|
|
2050
|
+
{
|
|
2051
|
+
portal: true,
|
|
2052
|
+
side: "bottom",
|
|
2053
|
+
align: "start",
|
|
2054
|
+
className: "gantt-tl-dep-info-popover",
|
|
2055
|
+
onInteractOutside: (event) => {
|
|
2056
|
+
const target = event.target;
|
|
2057
|
+
if (target?.closest?.(".gantt-tl-dep-chip") || target?.closest?.(".gantt-tl-dep-delete-label") || target?.closest?.(".gantt-tl-dep-chip-trash")) {
|
|
2058
|
+
event.preventDefault();
|
|
2059
|
+
} else {
|
|
2060
|
+
onChipSelectClear();
|
|
2061
|
+
}
|
|
2062
|
+
},
|
|
2063
|
+
children: [
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-info-prefix", children: depPrefix }),
|
|
2065
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-info-name", children: depName })
|
|
2066
|
+
]
|
|
2067
|
+
}
|
|
2068
|
+
)
|
|
2069
|
+
] });
|
|
2070
|
+
};
|
|
1924
2071
|
var toISODate = (value) => {
|
|
1925
2072
|
if (value instanceof Date) return value.toISOString().split("T")[0];
|
|
1926
2073
|
if (typeof value === "string" && value.includes("T")) return value.split("T")[0];
|
|
1927
2074
|
return value;
|
|
1928
2075
|
};
|
|
1929
2076
|
var TaskListRow = import_react10.default.memo(
|
|
1930
|
-
({
|
|
2077
|
+
({
|
|
2078
|
+
task,
|
|
2079
|
+
rowIndex,
|
|
2080
|
+
rowHeight,
|
|
2081
|
+
onTaskChange,
|
|
2082
|
+
selectedTaskId,
|
|
2083
|
+
onRowClick,
|
|
2084
|
+
disableTaskNameEditing = false,
|
|
2085
|
+
disableDependencyEditing = false,
|
|
2086
|
+
allTasks = [],
|
|
2087
|
+
activeLinkType,
|
|
2088
|
+
selectingPredecessorFor,
|
|
2089
|
+
onSetSelectingPredecessorFor,
|
|
2090
|
+
onAddDependency,
|
|
2091
|
+
onRemoveDependency,
|
|
2092
|
+
selectedChip,
|
|
2093
|
+
onChipSelect,
|
|
2094
|
+
onScrollToTask
|
|
2095
|
+
}) => {
|
|
1931
2096
|
const [editingName, setEditingName] = (0, import_react10.useState)(false);
|
|
1932
2097
|
const [nameValue, setNameValue] = (0, import_react10.useState)("");
|
|
1933
2098
|
const nameInputRef = (0, import_react10.useRef)(null);
|
|
2099
|
+
const [overflowOpen, setOverflowOpen] = (0, import_react10.useState)(false);
|
|
1934
2100
|
const isSelected = selectedTaskId === task.id;
|
|
2101
|
+
const isPicking = selectingPredecessorFor != null;
|
|
2102
|
+
const isSourceRow = isPicking && selectingPredecessorFor === task.id;
|
|
2103
|
+
const chips = (0, import_react10.useMemo)(() => {
|
|
2104
|
+
const succStart = new Date(task.startDate);
|
|
2105
|
+
const succEnd = new Date(task.endDate);
|
|
2106
|
+
const taskById = new Map((allTasks ?? []).map((t) => [t.id, t]));
|
|
2107
|
+
return (task.dependencies ?? []).map((dep) => {
|
|
2108
|
+
const pred = taskById.get(dep.taskId);
|
|
2109
|
+
const lag = pred ? computeLagFromDates(
|
|
2110
|
+
dep.type,
|
|
2111
|
+
new Date(pred.startDate),
|
|
2112
|
+
new Date(pred.endDate),
|
|
2113
|
+
succStart,
|
|
2114
|
+
succEnd
|
|
2115
|
+
) : dep.lag ?? 0;
|
|
2116
|
+
return { dep, lag, predecessorName: pred?.name ?? dep.taskId };
|
|
2117
|
+
});
|
|
2118
|
+
}, [task.dependencies, task.startDate, task.endDate, allTasks]);
|
|
2119
|
+
const linkWord = chips.length <= 4 ? "\u0441\u0432\u044F\u0437\u0438" : "\u0441\u0432\u044F\u0437\u0435\u0439";
|
|
1935
2120
|
(0, import_react10.useEffect)(() => {
|
|
1936
2121
|
if (editingName && nameInputRef.current) {
|
|
1937
2122
|
nameInputRef.current.focus();
|
|
@@ -1977,18 +2162,60 @@ var TaskListRow = import_react10.default.memo(
|
|
|
1977
2162
|
const handleRowClickInternal = (0, import_react10.useCallback)(() => {
|
|
1978
2163
|
onRowClick?.(task.id);
|
|
1979
2164
|
}, [task.id, onRowClick]);
|
|
2165
|
+
const handleNumberClick = (0, import_react10.useCallback)((e) => {
|
|
2166
|
+
e.stopPropagation();
|
|
2167
|
+
onRowClick?.(task.id);
|
|
2168
|
+
onScrollToTask?.(task.id);
|
|
2169
|
+
}, [task.id, onRowClick, onScrollToTask]);
|
|
2170
|
+
const handleAddClick = (0, import_react10.useCallback)((e) => {
|
|
2171
|
+
e.stopPropagation();
|
|
2172
|
+
onSetSelectingPredecessorFor?.(task.id);
|
|
2173
|
+
}, [task.id, onSetSelectingPredecessorFor]);
|
|
2174
|
+
const handlePredecessorPick = (0, import_react10.useCallback)((e) => {
|
|
2175
|
+
e.stopPropagation();
|
|
2176
|
+
if (!isPicking || isSourceRow) return;
|
|
2177
|
+
if (!selectingPredecessorFor || !activeLinkType) return;
|
|
2178
|
+
onAddDependency?.(task.id, selectingPredecessorFor, activeLinkType);
|
|
2179
|
+
}, [isPicking, isSourceRow, selectingPredecessorFor, task.id, activeLinkType, onAddDependency]);
|
|
2180
|
+
const isSelectedPredecessor = selectedChip != null && selectedChip.predecessorId === task.id;
|
|
2181
|
+
const handleDeleteSelected = (0, import_react10.useCallback)((e) => {
|
|
2182
|
+
e.stopPropagation();
|
|
2183
|
+
if (!selectedChip) return;
|
|
2184
|
+
onRemoveDependency?.(selectedChip.successorId, selectedChip.predecessorId, selectedChip.linkType);
|
|
2185
|
+
onChipSelect?.(null);
|
|
2186
|
+
}, [selectedChip, onRemoveDependency, onChipSelect]);
|
|
1980
2187
|
const startDateISO = toISODate(task.startDate);
|
|
1981
2188
|
const endDateISO = toISODate(task.endDate);
|
|
1982
|
-
return /* @__PURE__ */ (0,
|
|
2189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1983
2190
|
"div",
|
|
1984
2191
|
{
|
|
1985
|
-
className:
|
|
2192
|
+
className: [
|
|
2193
|
+
"gantt-tl-row",
|
|
2194
|
+
isSelected ? "gantt-tl-row-selected" : "",
|
|
2195
|
+
isPicking && !isSourceRow ? "gantt-tl-row-picking" : "",
|
|
2196
|
+
isSourceRow ? "gantt-tl-row-picking-self" : ""
|
|
2197
|
+
].filter(Boolean).join(" "),
|
|
1986
2198
|
style: { minHeight: `${rowHeight}px` },
|
|
1987
2199
|
onClick: handleRowClickInternal,
|
|
1988
2200
|
children: [
|
|
1989
|
-
/* @__PURE__ */ (0,
|
|
1990
|
-
|
|
1991
|
-
|
|
2201
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2202
|
+
"div",
|
|
2203
|
+
{
|
|
2204
|
+
className: "gantt-tl-cell gantt-tl-cell-number",
|
|
2205
|
+
onClick: handleNumberClick,
|
|
2206
|
+
title: "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043A \u0440\u0430\u0431\u043E\u0442\u0435",
|
|
2207
|
+
children: [
|
|
2208
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-num-label", children: rowIndex + 1 }),
|
|
2209
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { className: "gantt-tl-num-icon", xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2210
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M17 12H3" }),
|
|
2211
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m11 18 6-6-6-6" }),
|
|
2212
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M21 5v14" })
|
|
2213
|
+
] })
|
|
2214
|
+
]
|
|
2215
|
+
}
|
|
2216
|
+
),
|
|
2217
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-cell gantt-tl-cell-name", children: [
|
|
2218
|
+
editingName && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1992
2219
|
Input,
|
|
1993
2220
|
{
|
|
1994
2221
|
ref: nameInputRef,
|
|
@@ -2001,7 +2228,7 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2001
2228
|
onClick: (e) => e.stopPropagation()
|
|
2002
2229
|
}
|
|
2003
2230
|
),
|
|
2004
|
-
/* @__PURE__ */ (0,
|
|
2231
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2005
2232
|
"button",
|
|
2006
2233
|
{
|
|
2007
2234
|
type: "button",
|
|
@@ -2012,7 +2239,7 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2012
2239
|
}
|
|
2013
2240
|
)
|
|
2014
2241
|
] }),
|
|
2015
|
-
/* @__PURE__ */ (0,
|
|
2242
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-cell gantt-tl-cell-date", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2016
2243
|
DatePicker,
|
|
2017
2244
|
{
|
|
2018
2245
|
value: startDateISO,
|
|
@@ -2022,7 +2249,7 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2022
2249
|
disabled: task.locked
|
|
2023
2250
|
}
|
|
2024
2251
|
) }),
|
|
2025
|
-
/* @__PURE__ */ (0,
|
|
2252
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-cell gantt-tl-cell-date", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2026
2253
|
DatePicker,
|
|
2027
2254
|
{
|
|
2028
2255
|
value: endDateISO,
|
|
@@ -2031,7 +2258,97 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2031
2258
|
portal: true,
|
|
2032
2259
|
disabled: task.locked
|
|
2033
2260
|
}
|
|
2034
|
-
) })
|
|
2261
|
+
) }),
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2263
|
+
"div",
|
|
2264
|
+
{
|
|
2265
|
+
className: "gantt-tl-cell gantt-tl-cell-deps",
|
|
2266
|
+
onClick: isPicking && !isSourceRow ? handlePredecessorPick : void 0,
|
|
2267
|
+
children: isSourceRow ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-source-hint", children: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0443" }) : isSelectedPredecessor && !disableDependencyEditing ? (
|
|
2268
|
+
/* Full-replacement: "Зависит от [name]" → hover → "Удалить" */
|
|
2269
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2270
|
+
"button",
|
|
2271
|
+
{
|
|
2272
|
+
type: "button",
|
|
2273
|
+
className: "gantt-tl-dep-delete-label",
|
|
2274
|
+
onClick: handleDeleteSelected,
|
|
2275
|
+
"aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2276
|
+
children: [
|
|
2277
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-delete-label-default", children: "\u0421\u0432\u044F\u0437\u0430\u043D\u043E \u0441" }),
|
|
2278
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-delete-label-hover", children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C" })
|
|
2279
|
+
]
|
|
2280
|
+
}
|
|
2281
|
+
)
|
|
2282
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
2283
|
+
chips.length >= 2 ? (
|
|
2284
|
+
/* 2+ deps — show only "N связей" summary chip that opens a popover */
|
|
2285
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: overflowOpen, onOpenChange: setOverflowOpen, children: [
|
|
2286
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2287
|
+
"button",
|
|
2288
|
+
{
|
|
2289
|
+
type: "button",
|
|
2290
|
+
className: "gantt-tl-dep-summary-chip",
|
|
2291
|
+
onClick: (e) => {
|
|
2292
|
+
e.stopPropagation();
|
|
2293
|
+
setOverflowOpen((v) => !v);
|
|
2294
|
+
},
|
|
2295
|
+
children: [
|
|
2296
|
+
chips.length,
|
|
2297
|
+
" ",
|
|
2298
|
+
linkWord
|
|
2299
|
+
]
|
|
2300
|
+
}
|
|
2301
|
+
) }),
|
|
2302
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-dep-overflow-list", onClick: (e) => e.stopPropagation(), children: chips.map(({ dep, lag, predecessorName }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2303
|
+
DepChip,
|
|
2304
|
+
{
|
|
2305
|
+
lag,
|
|
2306
|
+
dep,
|
|
2307
|
+
taskId: task.id,
|
|
2308
|
+
predecessorName,
|
|
2309
|
+
selectedChip,
|
|
2310
|
+
disableDependencyEditing,
|
|
2311
|
+
onChipSelect,
|
|
2312
|
+
onRowClick,
|
|
2313
|
+
onScrollToTask,
|
|
2314
|
+
onRemoveDependency,
|
|
2315
|
+
onChipSelectClear: () => onChipSelect?.(null)
|
|
2316
|
+
},
|
|
2317
|
+
`${dep.taskId}-${dep.type}`
|
|
2318
|
+
)) }) })
|
|
2319
|
+
] })
|
|
2320
|
+
) : chips.length === 1 ? (
|
|
2321
|
+
/* Single chip — unified DepChip */
|
|
2322
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2323
|
+
DepChip,
|
|
2324
|
+
{
|
|
2325
|
+
lag: chips[0].lag,
|
|
2326
|
+
dep: chips[0].dep,
|
|
2327
|
+
taskId: task.id,
|
|
2328
|
+
predecessorName: chips[0].predecessorName,
|
|
2329
|
+
selectedChip,
|
|
2330
|
+
disableDependencyEditing,
|
|
2331
|
+
onChipSelect,
|
|
2332
|
+
onRowClick,
|
|
2333
|
+
onScrollToTask,
|
|
2334
|
+
onRemoveDependency,
|
|
2335
|
+
onChipSelectClear: () => onChipSelect?.(null)
|
|
2336
|
+
}
|
|
2337
|
+
)
|
|
2338
|
+
) : null,
|
|
2339
|
+
!disableDependencyEditing && !isPicking && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2340
|
+
"button",
|
|
2341
|
+
{
|
|
2342
|
+
type: "button",
|
|
2343
|
+
className: "gantt-tl-dep-add",
|
|
2344
|
+
onClick: handleAddClick,
|
|
2345
|
+
"aria-label": "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2346
|
+
children: "+"
|
|
2347
|
+
}
|
|
2348
|
+
)
|
|
2349
|
+
] })
|
|
2350
|
+
}
|
|
2351
|
+
)
|
|
2035
2352
|
]
|
|
2036
2353
|
}
|
|
2037
2354
|
);
|
|
@@ -2040,7 +2357,8 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2040
2357
|
TaskListRow.displayName = "TaskListRow";
|
|
2041
2358
|
|
|
2042
2359
|
// src/components/TaskList/TaskList.tsx
|
|
2043
|
-
var
|
|
2360
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2361
|
+
var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
|
|
2044
2362
|
var TaskList = ({
|
|
2045
2363
|
tasks,
|
|
2046
2364
|
rowHeight,
|
|
@@ -2050,7 +2368,10 @@ var TaskList = ({
|
|
|
2050
2368
|
selectedTaskId,
|
|
2051
2369
|
onTaskSelect,
|
|
2052
2370
|
show = true,
|
|
2053
|
-
disableTaskNameEditing = false
|
|
2371
|
+
disableTaskNameEditing = false,
|
|
2372
|
+
disableDependencyEditing = false,
|
|
2373
|
+
onScrollToTask,
|
|
2374
|
+
onSelectedChipChange
|
|
2054
2375
|
}) => {
|
|
2055
2376
|
const totalHeight = (0, import_react11.useMemo)(
|
|
2056
2377
|
() => tasks.length * rowHeight,
|
|
@@ -2059,19 +2380,144 @@ var TaskList = ({
|
|
|
2059
2380
|
const handleRowClick = (0, import_react11.useCallback)((taskId) => {
|
|
2060
2381
|
onTaskSelect?.(taskId);
|
|
2061
2382
|
}, [onTaskSelect]);
|
|
2062
|
-
|
|
2383
|
+
const [activeLinkType, setActiveLinkType] = (0, import_react11.useState)("FS");
|
|
2384
|
+
const [selectingPredecessorFor, setSelectingPredecessorFor] = (0, import_react11.useState)(null);
|
|
2385
|
+
const [typeMenuOpen, setTypeMenuOpen] = (0, import_react11.useState)(false);
|
|
2386
|
+
const [cycleError, setCycleError] = (0, import_react11.useState)(false);
|
|
2387
|
+
const overlayRef = (0, import_react11.useRef)(null);
|
|
2388
|
+
const [selectedChip, setSelectedChip] = (0, import_react11.useState)(null);
|
|
2389
|
+
const handleChipSelect = (0, import_react11.useCallback)((chip) => {
|
|
2390
|
+
setSelectedChip(chip);
|
|
2391
|
+
onSelectedChipChange?.(chip);
|
|
2392
|
+
}, [onSelectedChipChange]);
|
|
2393
|
+
(0, import_react11.useEffect)(() => {
|
|
2394
|
+
if (!selectingPredecessorFor && !selectedChip) return;
|
|
2395
|
+
const handleKeyDown = (e) => {
|
|
2396
|
+
if (e.key === "Escape") {
|
|
2397
|
+
setSelectingPredecessorFor(null);
|
|
2398
|
+
setSelectedChip(null);
|
|
2399
|
+
onSelectedChipChange?.(null);
|
|
2400
|
+
}
|
|
2401
|
+
};
|
|
2402
|
+
const handleMouseDown = (e) => {
|
|
2403
|
+
const target = e.target;
|
|
2404
|
+
if (overlayRef.current?.contains(target)) return;
|
|
2405
|
+
if (target.closest?.(".gantt-popover")) return;
|
|
2406
|
+
setSelectingPredecessorFor(null);
|
|
2407
|
+
setSelectedChip(null);
|
|
2408
|
+
onSelectedChipChange?.(null);
|
|
2409
|
+
};
|
|
2410
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2411
|
+
document.addEventListener("mousedown", handleMouseDown, true);
|
|
2412
|
+
return () => {
|
|
2413
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
2414
|
+
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
2415
|
+
};
|
|
2416
|
+
}, [selectingPredecessorFor, selectedChip, onSelectedChipChange]);
|
|
2417
|
+
const handleAddDependency = (0, import_react11.useCallback)((successorTaskId, predecessorTaskId, linkType) => {
|
|
2418
|
+
if (successorTaskId === predecessorTaskId) return;
|
|
2419
|
+
const successor = tasks.find((t) => t.id === successorTaskId);
|
|
2420
|
+
if (!successor) return;
|
|
2421
|
+
const alreadyExists = (successor.dependencies ?? []).some(
|
|
2422
|
+
(d) => d.taskId === predecessorTaskId && d.type === linkType
|
|
2423
|
+
);
|
|
2424
|
+
if (alreadyExists) {
|
|
2425
|
+
setSelectingPredecessorFor(null);
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
const newDep = { taskId: predecessorTaskId, type: linkType, lag: 0 };
|
|
2429
|
+
const hypothetical = tasks.map(
|
|
2430
|
+
(t) => t.id === successorTaskId ? { ...t, dependencies: [...t.dependencies ?? [], newDep] } : t
|
|
2431
|
+
);
|
|
2432
|
+
const validation = validateDependencies(hypothetical);
|
|
2433
|
+
if (!validation.isValid) {
|
|
2434
|
+
setCycleError(true);
|
|
2435
|
+
setTimeout(() => setCycleError(false), 3e3);
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
const updatedTask = hypothetical.find((t) => t.id === successorTaskId);
|
|
2439
|
+
const predecessor = tasks.find((t) => t.id === predecessorTaskId);
|
|
2440
|
+
if (predecessor) {
|
|
2441
|
+
const predStart = new Date(predecessor.startDate);
|
|
2442
|
+
const predEnd = new Date(predecessor.endDate);
|
|
2443
|
+
const constraintDate = calculateSuccessorDate(predStart, predEnd, linkType, 0);
|
|
2444
|
+
const origSuccessor = tasks.find((t) => t.id === successorTaskId);
|
|
2445
|
+
const durationMs = new Date(origSuccessor.endDate).getTime() - new Date(origSuccessor.startDate).getTime();
|
|
2446
|
+
let newStart;
|
|
2447
|
+
let newEnd;
|
|
2448
|
+
if (linkType === "FS" || linkType === "SS") {
|
|
2449
|
+
newStart = constraintDate;
|
|
2450
|
+
newEnd = new Date(constraintDate.getTime() + durationMs);
|
|
2451
|
+
} else {
|
|
2452
|
+
newEnd = constraintDate;
|
|
2453
|
+
newStart = new Date(constraintDate.getTime() - durationMs);
|
|
2454
|
+
}
|
|
2455
|
+
const snappedTask = {
|
|
2456
|
+
...updatedTask,
|
|
2457
|
+
startDate: newStart.toISOString().split("T")[0],
|
|
2458
|
+
endDate: newEnd.toISOString().split("T")[0]
|
|
2459
|
+
};
|
|
2460
|
+
onTaskChange?.(snappedTask);
|
|
2461
|
+
} else {
|
|
2462
|
+
onTaskChange?.(updatedTask);
|
|
2463
|
+
}
|
|
2464
|
+
setSelectingPredecessorFor(null);
|
|
2465
|
+
}, [tasks, onTaskChange]);
|
|
2466
|
+
const handleRemoveDependency = (0, import_react11.useCallback)((taskId, predecessorTaskId, linkType) => {
|
|
2467
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
2468
|
+
if (!task) return;
|
|
2469
|
+
const updatedDeps = (task.dependencies ?? []).filter(
|
|
2470
|
+
(d) => !(d.taskId === predecessorTaskId && d.type === linkType)
|
|
2471
|
+
);
|
|
2472
|
+
onTaskChange?.({ ...task, dependencies: updatedDeps });
|
|
2473
|
+
}, [tasks, onTaskChange]);
|
|
2474
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2063
2475
|
"div",
|
|
2064
2476
|
{
|
|
2477
|
+
ref: overlayRef,
|
|
2065
2478
|
className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}`,
|
|
2066
2479
|
style: { width: `${taskListWidth}px` },
|
|
2067
|
-
children: /* @__PURE__ */ (0,
|
|
2068
|
-
/* @__PURE__ */ (0,
|
|
2069
|
-
/* @__PURE__ */ (0,
|
|
2070
|
-
/* @__PURE__ */ (0,
|
|
2071
|
-
/* @__PURE__ */ (0,
|
|
2072
|
-
/* @__PURE__ */ (0,
|
|
2480
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-table", children: [
|
|
2481
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-header", style: { height: `${headerHeight + 0.5}px` }, children: [
|
|
2482
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-number", children: "\u2116" }),
|
|
2483
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-name", children: "\u0418\u043C\u044F" }),
|
|
2484
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
|
|
2485
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435" }),
|
|
2486
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-headerCell gantt-tl-cell-deps", style: { position: "relative" }, children: [
|
|
2487
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2489
|
+
"button",
|
|
2490
|
+
{
|
|
2491
|
+
className: "gantt-tl-dep-type-trigger",
|
|
2492
|
+
disabled: disableDependencyEditing,
|
|
2493
|
+
onClick: (e) => e.stopPropagation(),
|
|
2494
|
+
children: [
|
|
2495
|
+
"\u0421\u0432\u044F\u0437\u0438 ",
|
|
2496
|
+
import_react11.default.createElement(LINK_TYPE_ICONS[activeLinkType]),
|
|
2497
|
+
" \u25BE"
|
|
2498
|
+
]
|
|
2499
|
+
}
|
|
2500
|
+
) }),
|
|
2501
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-dep-type-menu", children: LINK_TYPE_ORDER.map((lt) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2502
|
+
"button",
|
|
2503
|
+
{
|
|
2504
|
+
className: `gantt-tl-dep-type-option${activeLinkType === lt ? " active" : ""}`,
|
|
2505
|
+
onClick: () => {
|
|
2506
|
+
setActiveLinkType(lt);
|
|
2507
|
+
setTypeMenuOpen(false);
|
|
2508
|
+
},
|
|
2509
|
+
children: [
|
|
2510
|
+
import_react11.default.createElement(LINK_TYPE_ICONS[lt]),
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: LINK_TYPE_LABELS[lt] })
|
|
2512
|
+
]
|
|
2513
|
+
},
|
|
2514
|
+
lt
|
|
2515
|
+
)) }) })
|
|
2516
|
+
] }),
|
|
2517
|
+
cycleError && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-dep-error", children: "\u0426\u0438\u043A\u043B \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439!" })
|
|
2518
|
+
] })
|
|
2073
2519
|
] }),
|
|
2074
|
-
/* @__PURE__ */ (0,
|
|
2520
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-body", style: { height: `${totalHeight}px` }, children: tasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2075
2521
|
TaskListRow,
|
|
2076
2522
|
{
|
|
2077
2523
|
task,
|
|
@@ -2080,7 +2526,17 @@ var TaskList = ({
|
|
|
2080
2526
|
onTaskChange,
|
|
2081
2527
|
selectedTaskId,
|
|
2082
2528
|
onRowClick: handleRowClick,
|
|
2083
|
-
disableTaskNameEditing
|
|
2529
|
+
disableTaskNameEditing,
|
|
2530
|
+
disableDependencyEditing,
|
|
2531
|
+
allTasks: tasks,
|
|
2532
|
+
activeLinkType,
|
|
2533
|
+
selectingPredecessorFor,
|
|
2534
|
+
onSetSelectingPredecessorFor: setSelectingPredecessorFor,
|
|
2535
|
+
onAddDependency: handleAddDependency,
|
|
2536
|
+
onRemoveDependency: handleRemoveDependency,
|
|
2537
|
+
selectedChip,
|
|
2538
|
+
onChipSelect: handleChipSelect,
|
|
2539
|
+
onScrollToTask
|
|
2084
2540
|
},
|
|
2085
2541
|
task.id
|
|
2086
2542
|
)) })
|
|
@@ -2090,7 +2546,7 @@ var TaskList = ({
|
|
|
2090
2546
|
};
|
|
2091
2547
|
|
|
2092
2548
|
// src/components/GanttChart/GanttChart.tsx
|
|
2093
|
-
var
|
|
2549
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2094
2550
|
var GanttChart = (0, import_react12.forwardRef)(({
|
|
2095
2551
|
tasks,
|
|
2096
2552
|
dayWidth = 40,
|
|
@@ -2104,10 +2560,13 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2104
2560
|
onCascade,
|
|
2105
2561
|
showTaskList = false,
|
|
2106
2562
|
taskListWidth = 520,
|
|
2107
|
-
disableTaskNameEditing = false
|
|
2563
|
+
disableTaskNameEditing = false,
|
|
2564
|
+
disableDependencyEditing = false,
|
|
2565
|
+
highlightExpiredTasks = false
|
|
2108
2566
|
}, ref) => {
|
|
2109
2567
|
const scrollContainerRef = (0, import_react12.useRef)(null);
|
|
2110
2568
|
const [selectedTaskId, setSelectedTaskId] = (0, import_react12.useState)(null);
|
|
2569
|
+
const [selectedChip, setSelectedChip] = (0, import_react12.useState)(null);
|
|
2111
2570
|
const dateRange = (0, import_react12.useMemo)(() => getMultiMonthDays(tasks), [tasks]);
|
|
2112
2571
|
const [validationResult, setValidationResult] = (0, import_react12.useState)(null);
|
|
2113
2572
|
const [cascadeOverrides, setCascadeOverrides] = (0, import_react12.useState)(/* @__PURE__ */ new Map());
|
|
@@ -2155,12 +2614,30 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2155
2614
|
const scrollLeft = Math.round(todayOffset - containerWidth / 2 + dayWidth / 2);
|
|
2156
2615
|
container.scrollLeft = Math.max(0, scrollLeft);
|
|
2157
2616
|
}, [dateRange, dayWidth]);
|
|
2617
|
+
const scrollToTask = (0, import_react12.useCallback)((taskId) => {
|
|
2618
|
+
const container = scrollContainerRef.current;
|
|
2619
|
+
if (!container || dateRange.length === 0) return;
|
|
2620
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
2621
|
+
if (!task) return;
|
|
2622
|
+
const taskStart = new Date(task.startDate);
|
|
2623
|
+
const taskStartUTC = new Date(Date.UTC(
|
|
2624
|
+
taskStart.getUTCFullYear(),
|
|
2625
|
+
taskStart.getUTCMonth(),
|
|
2626
|
+
taskStart.getUTCDate()
|
|
2627
|
+
));
|
|
2628
|
+
const taskIndex = dateRange.findIndex((day) => day.getTime() === taskStartUTC.getTime());
|
|
2629
|
+
if (taskIndex === -1) return;
|
|
2630
|
+
const taskOffset = taskIndex * dayWidth;
|
|
2631
|
+
const scrollLeft = Math.round(taskOffset - dayWidth * 2);
|
|
2632
|
+
container.scrollLeft = Math.max(0, scrollLeft);
|
|
2633
|
+
}, [tasks, dateRange, dayWidth]);
|
|
2158
2634
|
(0, import_react12.useImperativeHandle)(
|
|
2159
2635
|
ref,
|
|
2160
2636
|
() => ({
|
|
2161
|
-
scrollToToday
|
|
2637
|
+
scrollToToday,
|
|
2638
|
+
scrollToTask
|
|
2162
2639
|
}),
|
|
2163
|
-
[scrollToToday]
|
|
2640
|
+
[scrollToToday, scrollToTask]
|
|
2164
2641
|
);
|
|
2165
2642
|
const [dragGuideLines, setDragGuideLines] = (0, import_react12.useState)(null);
|
|
2166
2643
|
const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react12.useState)(null);
|
|
@@ -2260,15 +2737,15 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2260
2737
|
window.removeEventListener("mouseup", handlePanEnd);
|
|
2261
2738
|
};
|
|
2262
2739
|
}, []);
|
|
2263
|
-
return /* @__PURE__ */ (0,
|
|
2740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-container", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2264
2741
|
"div",
|
|
2265
2742
|
{
|
|
2266
2743
|
ref: scrollContainerRef,
|
|
2267
2744
|
className: "gantt-scrollContainer",
|
|
2268
2745
|
style: { height: containerHeight ?? "auto", cursor: "grab" },
|
|
2269
2746
|
onMouseDown: handlePanStart,
|
|
2270
|
-
children: /* @__PURE__ */ (0,
|
|
2271
|
-
/* @__PURE__ */ (0,
|
|
2747
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gantt-scrollContent", children: [
|
|
2748
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2272
2749
|
TaskList,
|
|
2273
2750
|
{
|
|
2274
2751
|
tasks,
|
|
@@ -2279,11 +2756,14 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2279
2756
|
selectedTaskId: selectedTaskId ?? void 0,
|
|
2280
2757
|
onTaskSelect: handleTaskSelect,
|
|
2281
2758
|
show: showTaskList,
|
|
2282
|
-
disableTaskNameEditing
|
|
2759
|
+
disableTaskNameEditing,
|
|
2760
|
+
disableDependencyEditing,
|
|
2761
|
+
onScrollToTask: scrollToTask,
|
|
2762
|
+
onSelectedChipChange: setSelectedChip
|
|
2283
2763
|
}
|
|
2284
2764
|
),
|
|
2285
|
-
/* @__PURE__ */ (0,
|
|
2286
|
-
/* @__PURE__ */ (0,
|
|
2765
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { minWidth: `${gridWidth}px`, flex: 1 }, children: [
|
|
2766
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2287
2767
|
TimeScaleHeader_default,
|
|
2288
2768
|
{
|
|
2289
2769
|
days: dateRange,
|
|
@@ -2291,7 +2771,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2291
2771
|
headerHeight
|
|
2292
2772
|
}
|
|
2293
2773
|
) }),
|
|
2294
|
-
/* @__PURE__ */ (0,
|
|
2774
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2295
2775
|
"div",
|
|
2296
2776
|
{
|
|
2297
2777
|
className: "gantt-taskArea",
|
|
@@ -2300,7 +2780,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2300
2780
|
width: `${gridWidth}px`
|
|
2301
2781
|
},
|
|
2302
2782
|
children: [
|
|
2303
|
-
/* @__PURE__ */ (0,
|
|
2783
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2304
2784
|
GridBackground_default,
|
|
2305
2785
|
{
|
|
2306
2786
|
dateRange,
|
|
@@ -2308,8 +2788,8 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2308
2788
|
totalHeight: totalGridHeight
|
|
2309
2789
|
}
|
|
2310
2790
|
),
|
|
2311
|
-
todayInRange && /* @__PURE__ */ (0,
|
|
2312
|
-
/* @__PURE__ */ (0,
|
|
2791
|
+
todayInRange && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
|
|
2792
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2313
2793
|
DependencyLines_default,
|
|
2314
2794
|
{
|
|
2315
2795
|
tasks,
|
|
@@ -2317,10 +2797,11 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2317
2797
|
dayWidth,
|
|
2318
2798
|
rowHeight,
|
|
2319
2799
|
gridWidth,
|
|
2320
|
-
dragOverrides: dependencyOverrides
|
|
2800
|
+
dragOverrides: dependencyOverrides,
|
|
2801
|
+
selectedDep: selectedChip
|
|
2321
2802
|
}
|
|
2322
2803
|
),
|
|
2323
|
-
dragGuideLines && /* @__PURE__ */ (0,
|
|
2804
|
+
dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2324
2805
|
DragGuideLines_default,
|
|
2325
2806
|
{
|
|
2326
2807
|
isDragging: dragGuideLines.isDragging,
|
|
@@ -2330,7 +2811,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2330
2811
|
totalHeight: totalGridHeight
|
|
2331
2812
|
}
|
|
2332
2813
|
),
|
|
2333
|
-
tasks.map((task, index) => /* @__PURE__ */ (0,
|
|
2814
|
+
tasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2334
2815
|
TaskRow_default,
|
|
2335
2816
|
{
|
|
2336
2817
|
task,
|
|
@@ -2353,7 +2834,8 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2353
2834
|
disableConstraints: disableConstraints ?? false,
|
|
2354
2835
|
overridePosition: cascadeOverrides.get(task.id),
|
|
2355
2836
|
onCascadeProgress: handleCascadeProgress,
|
|
2356
|
-
onCascade: handleCascade
|
|
2837
|
+
onCascade: handleCascade,
|
|
2838
|
+
highlightExpiredTasks
|
|
2357
2839
|
},
|
|
2358
2840
|
task.id
|
|
2359
2841
|
))
|
|
@@ -2369,7 +2851,7 @@ GanttChart.displayName = "GanttChart";
|
|
|
2369
2851
|
|
|
2370
2852
|
// src/components/ui/Button.tsx
|
|
2371
2853
|
var import_react13 = __toESM(require("react"));
|
|
2372
|
-
var
|
|
2854
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2373
2855
|
var Button = import_react13.default.forwardRef(
|
|
2374
2856
|
({ className, variant = "default", size = "default", children, ...props }, ref) => {
|
|
2375
2857
|
const classes = [
|
|
@@ -2378,7 +2860,7 @@ var Button = import_react13.default.forwardRef(
|
|
|
2378
2860
|
size !== "default" ? `gantt-btn-${size}` : "",
|
|
2379
2861
|
className || ""
|
|
2380
2862
|
].filter(Boolean).join(" ");
|
|
2381
|
-
return /* @__PURE__ */ (0,
|
|
2863
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { ref, className: classes, ...props, children });
|
|
2382
2864
|
}
|
|
2383
2865
|
);
|
|
2384
2866
|
Button.displayName = "Button";
|
|
@@ -2408,6 +2890,7 @@ Button.displayName = "Button";
|
|
|
2408
2890
|
calculateTaskBar,
|
|
2409
2891
|
calculateWeekendBlocks,
|
|
2410
2892
|
cascadeByLinks,
|
|
2893
|
+
computeLagFromDates,
|
|
2411
2894
|
detectCycles,
|
|
2412
2895
|
detectEdgeZone,
|
|
2413
2896
|
formatDateLabel,
|