gantt-lib 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +38 -10
- package/dist/index.d.ts +38 -10
- package/dist/index.js +636 -199
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +655 -219
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +273 -1
- 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) {
|
|
@@ -1353,15 +1353,11 @@ var TodayIndicator = ({ monthStart, dayWidth }) => {
|
|
|
1353
1353
|
today.getMonth(),
|
|
1354
1354
|
today.getDate()
|
|
1355
1355
|
));
|
|
1356
|
-
const isInMonth = (0, import_react4.useMemo)(() => {
|
|
1357
|
-
return todayLocal.getUTCFullYear() === monthStart.getUTCFullYear() && todayLocal.getUTCMonth() === monthStart.getUTCMonth();
|
|
1358
|
-
}, [monthStart, todayLocal]);
|
|
1359
1356
|
const position = (0, import_react4.useMemo)(() => {
|
|
1360
|
-
if (!isInMonth) return null;
|
|
1361
1357
|
const offset = getDayOffset(todayLocal, monthStart);
|
|
1362
1358
|
return Math.round(offset * dayWidth);
|
|
1363
|
-
}, [
|
|
1364
|
-
if (
|
|
1359
|
+
}, [monthStart, dayWidth, todayLocal]);
|
|
1360
|
+
if (isNaN(position)) {
|
|
1365
1361
|
return null;
|
|
1366
1362
|
}
|
|
1367
1363
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -1481,60 +1477,11 @@ var DragGuideLines_default = DragGuideLines;
|
|
|
1481
1477
|
var import_react6 = __toESM(require("react"));
|
|
1482
1478
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1483
1479
|
function calculateEffectiveLag(edge, predPosition, succPosition, monthStart, dayWidth) {
|
|
1484
|
-
const
|
|
1485
|
-
const
|
|
1486
|
-
const
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
switch (edge.type) {
|
|
1490
|
-
case "FS":
|
|
1491
|
-
lagMs = Date.UTC(
|
|
1492
|
-
succStartDate.getUTCFullYear(),
|
|
1493
|
-
succStartDate.getUTCMonth(),
|
|
1494
|
-
succStartDate.getUTCDate()
|
|
1495
|
-
) - Date.UTC(
|
|
1496
|
-
predEndDate.getUTCFullYear(),
|
|
1497
|
-
predEndDate.getUTCMonth(),
|
|
1498
|
-
predEndDate.getUTCDate()
|
|
1499
|
-
) - 24 * 60 * 60 * 1e3;
|
|
1500
|
-
break;
|
|
1501
|
-
case "SS":
|
|
1502
|
-
lagMs = Date.UTC(
|
|
1503
|
-
succStartDate.getUTCFullYear(),
|
|
1504
|
-
succStartDate.getUTCMonth(),
|
|
1505
|
-
succStartDate.getUTCDate()
|
|
1506
|
-
) - Date.UTC(
|
|
1507
|
-
predStartDate.getUTCFullYear(),
|
|
1508
|
-
predStartDate.getUTCMonth(),
|
|
1509
|
-
predStartDate.getUTCDate()
|
|
1510
|
-
);
|
|
1511
|
-
break;
|
|
1512
|
-
case "FF":
|
|
1513
|
-
lagMs = Date.UTC(
|
|
1514
|
-
succEndDate.getUTCFullYear(),
|
|
1515
|
-
succEndDate.getUTCMonth(),
|
|
1516
|
-
succEndDate.getUTCDate()
|
|
1517
|
-
) - Date.UTC(
|
|
1518
|
-
predEndDate.getUTCFullYear(),
|
|
1519
|
-
predEndDate.getUTCMonth(),
|
|
1520
|
-
predEndDate.getUTCDate()
|
|
1521
|
-
);
|
|
1522
|
-
break;
|
|
1523
|
-
case "SF":
|
|
1524
|
-
lagMs = Date.UTC(
|
|
1525
|
-
succEndDate.getUTCFullYear(),
|
|
1526
|
-
succEndDate.getUTCMonth(),
|
|
1527
|
-
succEndDate.getUTCDate()
|
|
1528
|
-
) - Date.UTC(
|
|
1529
|
-
predStartDate.getUTCFullYear(),
|
|
1530
|
-
predStartDate.getUTCMonth(),
|
|
1531
|
-
predStartDate.getUTCDate()
|
|
1532
|
-
) + 24 * 60 * 60 * 1e3;
|
|
1533
|
-
break;
|
|
1534
|
-
default:
|
|
1535
|
-
return 0;
|
|
1536
|
-
}
|
|
1537
|
-
return Math.round(lagMs / (24 * 60 * 60 * 1e3));
|
|
1480
|
+
const predStart = pixelsToDate(predPosition.left, monthStart, dayWidth);
|
|
1481
|
+
const predEnd = pixelsToDate(predPosition.right - dayWidth, monthStart, dayWidth);
|
|
1482
|
+
const succStart = pixelsToDate(succPosition.left, monthStart, dayWidth);
|
|
1483
|
+
const succEnd = pixelsToDate(succPosition.right - dayWidth, monthStart, dayWidth);
|
|
1484
|
+
return computeLagFromDates(edge.type, predStart, predEnd, succStart, succEnd);
|
|
1538
1485
|
}
|
|
1539
1486
|
var DependencyLines = import_react6.default.memo(({
|
|
1540
1487
|
tasks,
|
|
@@ -1542,7 +1489,8 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
1542
1489
|
dayWidth,
|
|
1543
1490
|
rowHeight,
|
|
1544
1491
|
gridWidth,
|
|
1545
|
-
dragOverrides
|
|
1492
|
+
dragOverrides,
|
|
1493
|
+
selectedDep
|
|
1546
1494
|
}) => {
|
|
1547
1495
|
const { taskPositions, taskIndices } = (0, import_react6.useMemo)(() => {
|
|
1548
1496
|
const positions = /* @__PURE__ */ new Map();
|
|
@@ -1656,30 +1604,60 @@ var DependencyLines = import_react6.default.memo(({
|
|
|
1656
1604
|
}
|
|
1657
1605
|
)
|
|
1658
1606
|
}
|
|
1659
|
-
)
|
|
1660
|
-
] }),
|
|
1661
|
-
lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react6.default.Fragment, { children: [
|
|
1662
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1663
|
-
"path",
|
|
1664
|
-
{
|
|
1665
|
-
d: path,
|
|
1666
|
-
className: hasCycle ? "gantt-dependency-path gantt-dependency-cycle" : "gantt-dependency-path",
|
|
1667
|
-
markerEnd: hasCycle ? "url(#arrowhead-cycle)" : "url(#arrowhead)"
|
|
1668
|
-
}
|
|
1669
1607
|
),
|
|
1670
|
-
|
|
1671
|
-
"
|
|
1608
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1609
|
+
"marker",
|
|
1672
1610
|
{
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1611
|
+
id: "arrowhead-selected",
|
|
1612
|
+
markerWidth: "8",
|
|
1613
|
+
markerHeight: "6",
|
|
1614
|
+
markerUnits: "userSpaceOnUse",
|
|
1615
|
+
refX: "7",
|
|
1616
|
+
refY: "3",
|
|
1617
|
+
orient: "auto",
|
|
1618
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1619
|
+
"polygon",
|
|
1620
|
+
{
|
|
1621
|
+
points: "0 0, 8 3, 0 6",
|
|
1622
|
+
fill: "#ef4444"
|
|
1623
|
+
}
|
|
1624
|
+
)
|
|
1680
1625
|
}
|
|
1681
1626
|
)
|
|
1682
|
-
] },
|
|
1627
|
+
] }),
|
|
1628
|
+
lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder }) => {
|
|
1629
|
+
const isSelected = selectedDep != null && id === `${selectedDep.predecessorId}-${selectedDep.successorId}-${selectedDep.linkType}`;
|
|
1630
|
+
let pathClassName = "gantt-dependency-path";
|
|
1631
|
+
if (isSelected) pathClassName += " gantt-dependency-selected";
|
|
1632
|
+
else if (hasCycle) pathClassName += " gantt-dependency-cycle";
|
|
1633
|
+
let markerEnd;
|
|
1634
|
+
if (isSelected) markerEnd = "url(#arrowhead-selected)";
|
|
1635
|
+
else if (hasCycle) markerEnd = "url(#arrowhead-cycle)";
|
|
1636
|
+
else markerEnd = "url(#arrowhead)";
|
|
1637
|
+
const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : "var(--gantt-dependency-line-color, #666666)";
|
|
1638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react6.default.Fragment, { children: [
|
|
1639
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1640
|
+
"path",
|
|
1641
|
+
{
|
|
1642
|
+
d: path,
|
|
1643
|
+
className: pathClassName,
|
|
1644
|
+
markerEnd
|
|
1645
|
+
}
|
|
1646
|
+
),
|
|
1647
|
+
lag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1648
|
+
"text",
|
|
1649
|
+
{
|
|
1650
|
+
className: "gantt-dependency-lag-label",
|
|
1651
|
+
x: lag < 0 ? toX + 14 : toX - 14,
|
|
1652
|
+
y: reverseOrder ? fromY - 4 : fromY + 12,
|
|
1653
|
+
textAnchor: "middle",
|
|
1654
|
+
fontSize: "10",
|
|
1655
|
+
fill: lagColor,
|
|
1656
|
+
children: lag > 0 ? `+${lag}` : `${lag}`
|
|
1657
|
+
}
|
|
1658
|
+
)
|
|
1659
|
+
] }, id);
|
|
1660
|
+
})
|
|
1683
1661
|
]
|
|
1684
1662
|
}
|
|
1685
1663
|
);
|
|
@@ -1688,17 +1666,49 @@ DependencyLines.displayName = "DependencyLines";
|
|
|
1688
1666
|
var DependencyLines_default = DependencyLines;
|
|
1689
1667
|
|
|
1690
1668
|
// src/components/TaskList/TaskList.tsx
|
|
1691
|
-
var import_react11 = require("react");
|
|
1669
|
+
var import_react11 = __toESM(require("react"));
|
|
1670
|
+
|
|
1671
|
+
// src/components/ui/Popover.tsx
|
|
1672
|
+
var RadixPopover = __toESM(require("@radix-ui/react-popover"));
|
|
1673
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1674
|
+
var Popover = ({ open, onOpenChange, children }) => {
|
|
1675
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RadixPopover.Root, { open, onOpenChange, children });
|
|
1676
|
+
};
|
|
1677
|
+
var PopoverTrigger = RadixPopover.Trigger;
|
|
1678
|
+
var PopoverContent = ({
|
|
1679
|
+
children,
|
|
1680
|
+
className,
|
|
1681
|
+
align = "start",
|
|
1682
|
+
side = "bottom",
|
|
1683
|
+
portal = true,
|
|
1684
|
+
collisionPadding = 8
|
|
1685
|
+
}) => {
|
|
1686
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1687
|
+
RadixPopover.Content,
|
|
1688
|
+
{
|
|
1689
|
+
className: `gantt-popover${className ? ` ${className}` : ""}`,
|
|
1690
|
+
align,
|
|
1691
|
+
side,
|
|
1692
|
+
collisionPadding,
|
|
1693
|
+
sideOffset: 4,
|
|
1694
|
+
children
|
|
1695
|
+
}
|
|
1696
|
+
);
|
|
1697
|
+
if (portal) {
|
|
1698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RadixPopover.Portal, { children: content });
|
|
1699
|
+
}
|
|
1700
|
+
return content;
|
|
1701
|
+
};
|
|
1692
1702
|
|
|
1693
1703
|
// src/components/TaskList/TaskListRow.tsx
|
|
1694
1704
|
var import_react10 = __toESM(require("react"));
|
|
1695
1705
|
|
|
1696
1706
|
// src/components/ui/Input.tsx
|
|
1697
1707
|
var import_react7 = __toESM(require("react"));
|
|
1698
|
-
var
|
|
1708
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1699
1709
|
var Input = import_react7.default.forwardRef(
|
|
1700
1710
|
({ className, ...props }, ref) => {
|
|
1701
|
-
return /* @__PURE__ */ (0,
|
|
1711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1702
1712
|
"input",
|
|
1703
1713
|
{
|
|
1704
1714
|
ref,
|
|
@@ -1718,7 +1728,7 @@ var import_date_fns3 = require("date-fns");
|
|
|
1718
1728
|
var import_react8 = require("react");
|
|
1719
1729
|
var import_date_fns2 = require("date-fns");
|
|
1720
1730
|
var import_locale2 = require("date-fns/locale");
|
|
1721
|
-
var
|
|
1731
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1722
1732
|
function getDayClassName(day, selected) {
|
|
1723
1733
|
const classes = ["gantt-day-btn"];
|
|
1724
1734
|
if (selected && (0, import_date_fns2.isSameDay)(day, selected)) classes.push("selected");
|
|
@@ -1788,12 +1798,12 @@ var Calendar = ({
|
|
|
1788
1798
|
const emptyDays = ((0, import_date_fns2.getDay)(firstDay) + 6) % 7;
|
|
1789
1799
|
const monthKey = (0, import_date_fns2.format)(month, "yyyy-MM");
|
|
1790
1800
|
const monthLabel = (0, import_date_fns2.format)(month, "LLLL yyyy", { locale: import_locale2.ru });
|
|
1791
|
-
const emptyCells = Array.from({ length: emptyDays }, (_, i) => /* @__PURE__ */ (0,
|
|
1801
|
+
const emptyCells = Array.from({ length: emptyDays }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "gantt-cal-empty-day" }, `e-${i}`));
|
|
1792
1802
|
const dayCells = Array.from({ length: totalDays }, (_, i) => {
|
|
1793
1803
|
const dayNum = i + 1;
|
|
1794
1804
|
const day = new Date(month.getFullYear(), month.getMonth(), dayNum);
|
|
1795
1805
|
const className = getDayClassName(day, selected);
|
|
1796
|
-
return /* @__PURE__ */ (0,
|
|
1806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1797
1807
|
"button",
|
|
1798
1808
|
{
|
|
1799
1809
|
type: "button",
|
|
@@ -1809,9 +1819,9 @@ var Calendar = ({
|
|
|
1809
1819
|
dayNum
|
|
1810
1820
|
);
|
|
1811
1821
|
});
|
|
1812
|
-
return /* @__PURE__ */ (0,
|
|
1813
|
-
/* @__PURE__ */ (0,
|
|
1814
|
-
/* @__PURE__ */ (0,
|
|
1822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "gantt-cal-month", "data-month": monthKey, children: [
|
|
1823
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "gantt-cal-month-header", children: monthLabel }),
|
|
1824
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "gantt-cal-month-days", children: [
|
|
1815
1825
|
emptyCells,
|
|
1816
1826
|
dayCells
|
|
1817
1827
|
] })
|
|
@@ -1823,42 +1833,10 @@ var Calendar = ({
|
|
|
1823
1833
|
() => months.map(renderMonth),
|
|
1824
1834
|
[months, renderMonth]
|
|
1825
1835
|
);
|
|
1826
|
-
return /* @__PURE__ */ (0,
|
|
1836
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: scrollRef, className: "gantt-cal-container", children: renderedMonths });
|
|
1827
1837
|
};
|
|
1828
1838
|
Calendar.displayName = "Calendar";
|
|
1829
1839
|
|
|
1830
|
-
// src/components/ui/Popover.tsx
|
|
1831
|
-
var RadixPopover = __toESM(require("@radix-ui/react-popover"));
|
|
1832
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1833
|
-
var Popover = ({ open, onOpenChange, children }) => {
|
|
1834
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RadixPopover.Root, { open, onOpenChange, children });
|
|
1835
|
-
};
|
|
1836
|
-
var PopoverTrigger = RadixPopover.Trigger;
|
|
1837
|
-
var PopoverContent = ({
|
|
1838
|
-
children,
|
|
1839
|
-
className,
|
|
1840
|
-
align = "start",
|
|
1841
|
-
side = "bottom",
|
|
1842
|
-
portal = true,
|
|
1843
|
-
collisionPadding = 8
|
|
1844
|
-
}) => {
|
|
1845
|
-
const content = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1846
|
-
RadixPopover.Content,
|
|
1847
|
-
{
|
|
1848
|
-
className: `gantt-popover${className ? ` ${className}` : ""}`,
|
|
1849
|
-
align,
|
|
1850
|
-
side,
|
|
1851
|
-
collisionPadding,
|
|
1852
|
-
sideOffset: 4,
|
|
1853
|
-
children
|
|
1854
|
-
}
|
|
1855
|
-
);
|
|
1856
|
-
if (portal) {
|
|
1857
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RadixPopover.Portal, { children: content });
|
|
1858
|
-
}
|
|
1859
|
-
return content;
|
|
1860
|
-
};
|
|
1861
|
-
|
|
1862
1840
|
// src/components/ui/DatePicker.tsx
|
|
1863
1841
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1864
1842
|
var DatePicker = ({
|
|
@@ -1923,19 +1901,182 @@ var DatePicker = ({
|
|
|
1923
1901
|
};
|
|
1924
1902
|
DatePicker.displayName = "DatePicker";
|
|
1925
1903
|
|
|
1926
|
-
// src/components/TaskList/
|
|
1904
|
+
// src/components/TaskList/DepIcons.tsx
|
|
1927
1905
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1906
|
+
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: [
|
|
1907
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m10 15 5 5 5-5" }),
|
|
1908
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 4h7a4 4 0 0 1 4 4v12" })
|
|
1909
|
+
] });
|
|
1910
|
+
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: [
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M3 5v14" }),
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M21 12H7" }),
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m15 18 6-6-6-6" })
|
|
1914
|
+
] });
|
|
1915
|
+
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: [
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M17 12H3" }),
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m11 18 6-6-6-6" }),
|
|
1918
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M21 5v14" })
|
|
1919
|
+
] });
|
|
1920
|
+
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: [
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "m14 15-5 5-5-5" }),
|
|
1922
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M20 4h-7a4 4 0 0 0-4 4v12" })
|
|
1923
|
+
] });
|
|
1924
|
+
var LINK_TYPE_ICONS = {
|
|
1925
|
+
FS: DepIconFS,
|
|
1926
|
+
SS: DepIconSS,
|
|
1927
|
+
FF: DepIconFF,
|
|
1928
|
+
SF: DepIconSF
|
|
1929
|
+
};
|
|
1930
|
+
var LINK_TYPE_LABELS = {
|
|
1931
|
+
FS: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435-\u043D\u0430\u0447\u0430\u043B\u043E",
|
|
1932
|
+
SS: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043D\u0430\u0447\u0430\u043B\u043E",
|
|
1933
|
+
FF: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435",
|
|
1934
|
+
SF: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435"
|
|
1935
|
+
};
|
|
1936
|
+
|
|
1937
|
+
// src/components/TaskList/TaskListRow.tsx
|
|
1938
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1939
|
+
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: [
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3 6h18" }),
|
|
1942
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
1943
|
+
] });
|
|
1944
|
+
function formatDepDescription(type, lag) {
|
|
1945
|
+
const effectiveLag = lag ?? 0;
|
|
1946
|
+
if (type === "FS") {
|
|
1947
|
+
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`;
|
|
1948
|
+
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`;
|
|
1949
|
+
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`;
|
|
1950
|
+
}
|
|
1951
|
+
if (type === "FF") {
|
|
1952
|
+
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`;
|
|
1953
|
+
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`;
|
|
1954
|
+
return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1955
|
+
}
|
|
1956
|
+
if (type === "SS") {
|
|
1957
|
+
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`;
|
|
1958
|
+
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`;
|
|
1959
|
+
return `\u041D\u0430\u0447\u0430\u0442\u044C \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u043D\u0430\u0447\u0430\u043B\u043E\u043C`;
|
|
1960
|
+
}
|
|
1961
|
+
if (type === "SF") {
|
|
1962
|
+
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`;
|
|
1963
|
+
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`;
|
|
1964
|
+
return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430`;
|
|
1965
|
+
}
|
|
1966
|
+
return "";
|
|
1967
|
+
}
|
|
1968
|
+
var DepChip = ({
|
|
1969
|
+
lag,
|
|
1970
|
+
dep,
|
|
1971
|
+
taskId,
|
|
1972
|
+
predecessorName,
|
|
1973
|
+
selectedChip,
|
|
1974
|
+
disableDependencyEditing,
|
|
1975
|
+
onChipSelect,
|
|
1976
|
+
onRowClick,
|
|
1977
|
+
onScrollToTask,
|
|
1978
|
+
onRemoveDependency,
|
|
1979
|
+
onChipSelectClear
|
|
1980
|
+
}) => {
|
|
1981
|
+
const isSelected = selectedChip?.successorId === taskId && selectedChip?.predecessorId === dep.taskId && selectedChip?.linkType === dep.type;
|
|
1982
|
+
const handleClick = (e) => {
|
|
1983
|
+
e.stopPropagation();
|
|
1984
|
+
if (disableDependencyEditing) return;
|
|
1985
|
+
onChipSelect?.(isSelected ? null : { successorId: taskId, predecessorId: dep.taskId, linkType: dep.type });
|
|
1986
|
+
if (!isSelected) {
|
|
1987
|
+
onRowClick?.(taskId);
|
|
1988
|
+
onScrollToTask?.(taskId);
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
const handleTrashClick = (e) => {
|
|
1992
|
+
e.stopPropagation();
|
|
1993
|
+
onRemoveDependency?.(taskId, dep.taskId, dep.type);
|
|
1994
|
+
onChipSelectClear();
|
|
1995
|
+
};
|
|
1996
|
+
const Icon = LINK_TYPE_ICONS[dep.type];
|
|
1997
|
+
const depPrefix = formatDepDescription(dep.type, lag);
|
|
1998
|
+
const depName = predecessorName ?? dep.taskId;
|
|
1999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: isSelected, onOpenChange: (open) => {
|
|
2000
|
+
if (!open) onChipSelectClear();
|
|
2001
|
+
}, children: [
|
|
2002
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "gantt-tl-dep-chip-wrapper", children: [
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2004
|
+
"span",
|
|
2005
|
+
{
|
|
2006
|
+
className: `gantt-tl-dep-chip${isSelected ? " gantt-tl-dep-chip-selected" : ""}`,
|
|
2007
|
+
onClick: handleClick,
|
|
2008
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, {}),
|
|
2010
|
+
lag != null && lag !== 0 ? lag > 0 ? `+${lag}` : `${lag}` : ""
|
|
2011
|
+
] })
|
|
2012
|
+
}
|
|
2013
|
+
) }),
|
|
2014
|
+
!disableDependencyEditing && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2015
|
+
"button",
|
|
2016
|
+
{
|
|
2017
|
+
type: "button",
|
|
2018
|
+
className: "gantt-tl-dep-chip-trash",
|
|
2019
|
+
"aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2020
|
+
onClick: handleTrashClick,
|
|
2021
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TrashIcon, {})
|
|
2022
|
+
}
|
|
2023
|
+
)
|
|
2024
|
+
] }),
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(PopoverContent, { portal: true, side: "bottom", align: "start", className: "gantt-tl-dep-info-popover", children: [
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-info-prefix", children: depPrefix }),
|
|
2027
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-info-name", children: depName })
|
|
2028
|
+
] })
|
|
2029
|
+
] });
|
|
2030
|
+
};
|
|
1928
2031
|
var toISODate = (value) => {
|
|
1929
2032
|
if (value instanceof Date) return value.toISOString().split("T")[0];
|
|
1930
2033
|
if (typeof value === "string" && value.includes("T")) return value.split("T")[0];
|
|
1931
2034
|
return value;
|
|
1932
2035
|
};
|
|
1933
2036
|
var TaskListRow = import_react10.default.memo(
|
|
1934
|
-
({
|
|
2037
|
+
({
|
|
2038
|
+
task,
|
|
2039
|
+
rowIndex,
|
|
2040
|
+
rowHeight,
|
|
2041
|
+
onTaskChange,
|
|
2042
|
+
selectedTaskId,
|
|
2043
|
+
onRowClick,
|
|
2044
|
+
disableTaskNameEditing = false,
|
|
2045
|
+
disableDependencyEditing = false,
|
|
2046
|
+
allTasks = [],
|
|
2047
|
+
activeLinkType,
|
|
2048
|
+
selectingPredecessorFor,
|
|
2049
|
+
onSetSelectingPredecessorFor,
|
|
2050
|
+
onAddDependency,
|
|
2051
|
+
onRemoveDependency,
|
|
2052
|
+
selectedChip,
|
|
2053
|
+
onChipSelect,
|
|
2054
|
+
onScrollToTask
|
|
2055
|
+
}) => {
|
|
1935
2056
|
const [editingName, setEditingName] = (0, import_react10.useState)(false);
|
|
1936
2057
|
const [nameValue, setNameValue] = (0, import_react10.useState)("");
|
|
1937
2058
|
const nameInputRef = (0, import_react10.useRef)(null);
|
|
2059
|
+
const [overflowOpen, setOverflowOpen] = (0, import_react10.useState)(false);
|
|
1938
2060
|
const isSelected = selectedTaskId === task.id;
|
|
2061
|
+
const isPicking = selectingPredecessorFor != null;
|
|
2062
|
+
const isSourceRow = isPicking && selectingPredecessorFor === task.id;
|
|
2063
|
+
const chips = (0, import_react10.useMemo)(() => {
|
|
2064
|
+
const succStart = new Date(task.startDate);
|
|
2065
|
+
const succEnd = new Date(task.endDate);
|
|
2066
|
+
const taskById = new Map((allTasks ?? []).map((t) => [t.id, t]));
|
|
2067
|
+
return (task.dependencies ?? []).map((dep) => {
|
|
2068
|
+
const pred = taskById.get(dep.taskId);
|
|
2069
|
+
const lag = pred ? computeLagFromDates(
|
|
2070
|
+
dep.type,
|
|
2071
|
+
new Date(pred.startDate),
|
|
2072
|
+
new Date(pred.endDate),
|
|
2073
|
+
succStart,
|
|
2074
|
+
succEnd
|
|
2075
|
+
) : dep.lag ?? 0;
|
|
2076
|
+
return { dep, lag, predecessorName: pred?.name ?? dep.taskId };
|
|
2077
|
+
});
|
|
2078
|
+
}, [task.dependencies, task.startDate, task.endDate, allTasks]);
|
|
2079
|
+
const linkWord = chips.length <= 4 ? "\u0441\u0432\u044F\u0437\u0438" : "\u0441\u0432\u044F\u0437\u0435\u0439";
|
|
1939
2080
|
(0, import_react10.useEffect)(() => {
|
|
1940
2081
|
if (editingName && nameInputRef.current) {
|
|
1941
2082
|
nameInputRef.current.focus();
|
|
@@ -1981,18 +2122,60 @@ var TaskListRow = import_react10.default.memo(
|
|
|
1981
2122
|
const handleRowClickInternal = (0, import_react10.useCallback)(() => {
|
|
1982
2123
|
onRowClick?.(task.id);
|
|
1983
2124
|
}, [task.id, onRowClick]);
|
|
2125
|
+
const handleNumberClick = (0, import_react10.useCallback)((e) => {
|
|
2126
|
+
e.stopPropagation();
|
|
2127
|
+
onRowClick?.(task.id);
|
|
2128
|
+
onScrollToTask?.(task.id);
|
|
2129
|
+
}, [task.id, onRowClick, onScrollToTask]);
|
|
2130
|
+
const handleAddClick = (0, import_react10.useCallback)((e) => {
|
|
2131
|
+
e.stopPropagation();
|
|
2132
|
+
onSetSelectingPredecessorFor?.(task.id);
|
|
2133
|
+
}, [task.id, onSetSelectingPredecessorFor]);
|
|
2134
|
+
const handlePredecessorPick = (0, import_react10.useCallback)((e) => {
|
|
2135
|
+
e.stopPropagation();
|
|
2136
|
+
if (!isPicking || isSourceRow) return;
|
|
2137
|
+
if (!selectingPredecessorFor || !activeLinkType) return;
|
|
2138
|
+
onAddDependency?.(task.id, selectingPredecessorFor, activeLinkType);
|
|
2139
|
+
}, [isPicking, isSourceRow, selectingPredecessorFor, task.id, activeLinkType, onAddDependency]);
|
|
2140
|
+
const isSelectedPredecessor = selectedChip != null && selectedChip.predecessorId === task.id;
|
|
2141
|
+
const handleDeleteSelected = (0, import_react10.useCallback)((e) => {
|
|
2142
|
+
e.stopPropagation();
|
|
2143
|
+
if (!selectedChip) return;
|
|
2144
|
+
onRemoveDependency?.(selectedChip.successorId, selectedChip.predecessorId, selectedChip.linkType);
|
|
2145
|
+
onChipSelect?.(null);
|
|
2146
|
+
}, [selectedChip, onRemoveDependency, onChipSelect]);
|
|
1984
2147
|
const startDateISO = toISODate(task.startDate);
|
|
1985
2148
|
const endDateISO = toISODate(task.endDate);
|
|
1986
|
-
return /* @__PURE__ */ (0,
|
|
2149
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1987
2150
|
"div",
|
|
1988
2151
|
{
|
|
1989
|
-
className:
|
|
2152
|
+
className: [
|
|
2153
|
+
"gantt-tl-row",
|
|
2154
|
+
isSelected ? "gantt-tl-row-selected" : "",
|
|
2155
|
+
isPicking && !isSourceRow ? "gantt-tl-row-picking" : "",
|
|
2156
|
+
isSourceRow ? "gantt-tl-row-picking-self" : ""
|
|
2157
|
+
].filter(Boolean).join(" "),
|
|
1990
2158
|
style: { minHeight: `${rowHeight}px` },
|
|
1991
2159
|
onClick: handleRowClickInternal,
|
|
1992
2160
|
children: [
|
|
1993
|
-
/* @__PURE__ */ (0,
|
|
1994
|
-
|
|
1995
|
-
|
|
2161
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2162
|
+
"div",
|
|
2163
|
+
{
|
|
2164
|
+
className: "gantt-tl-cell gantt-tl-cell-number",
|
|
2165
|
+
onClick: handleNumberClick,
|
|
2166
|
+
title: "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043A \u0440\u0430\u0431\u043E\u0442\u0435",
|
|
2167
|
+
children: [
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-num-label", children: rowIndex + 1 }),
|
|
2169
|
+
/* @__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: [
|
|
2170
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M17 12H3" }),
|
|
2171
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m11 18 6-6-6-6" }),
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M21 5v14" })
|
|
2173
|
+
] })
|
|
2174
|
+
]
|
|
2175
|
+
}
|
|
2176
|
+
),
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-cell gantt-tl-cell-name", children: [
|
|
2178
|
+
editingName && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1996
2179
|
Input,
|
|
1997
2180
|
{
|
|
1998
2181
|
ref: nameInputRef,
|
|
@@ -2005,7 +2188,7 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2005
2188
|
onClick: (e) => e.stopPropagation()
|
|
2006
2189
|
}
|
|
2007
2190
|
),
|
|
2008
|
-
/* @__PURE__ */ (0,
|
|
2191
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2009
2192
|
"button",
|
|
2010
2193
|
{
|
|
2011
2194
|
type: "button",
|
|
@@ -2016,7 +2199,7 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2016
2199
|
}
|
|
2017
2200
|
)
|
|
2018
2201
|
] }),
|
|
2019
|
-
/* @__PURE__ */ (0,
|
|
2202
|
+
/* @__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)(
|
|
2020
2203
|
DatePicker,
|
|
2021
2204
|
{
|
|
2022
2205
|
value: startDateISO,
|
|
@@ -2026,7 +2209,7 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2026
2209
|
disabled: task.locked
|
|
2027
2210
|
}
|
|
2028
2211
|
) }),
|
|
2029
|
-
/* @__PURE__ */ (0,
|
|
2212
|
+
/* @__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)(
|
|
2030
2213
|
DatePicker,
|
|
2031
2214
|
{
|
|
2032
2215
|
value: endDateISO,
|
|
@@ -2035,7 +2218,97 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2035
2218
|
portal: true,
|
|
2036
2219
|
disabled: task.locked
|
|
2037
2220
|
}
|
|
2038
|
-
) })
|
|
2221
|
+
) }),
|
|
2222
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2223
|
+
"div",
|
|
2224
|
+
{
|
|
2225
|
+
className: "gantt-tl-cell gantt-tl-cell-deps",
|
|
2226
|
+
onClick: isPicking && !isSourceRow ? handlePredecessorPick : void 0,
|
|
2227
|
+
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 ? (
|
|
2228
|
+
/* Full-replacement: "Зависит от [name]" → hover → "Удалить" */
|
|
2229
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2230
|
+
"button",
|
|
2231
|
+
{
|
|
2232
|
+
type: "button",
|
|
2233
|
+
className: "gantt-tl-dep-delete-label",
|
|
2234
|
+
onClick: handleDeleteSelected,
|
|
2235
|
+
"aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2236
|
+
children: [
|
|
2237
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-delete-label-default", children: "\u0421\u0432\u044F\u0437\u0430\u043D\u043E \u0441" }),
|
|
2238
|
+
/* @__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" })
|
|
2239
|
+
]
|
|
2240
|
+
}
|
|
2241
|
+
)
|
|
2242
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
2243
|
+
chips.length >= 2 ? (
|
|
2244
|
+
/* 2+ deps — show only "N связей" summary chip that opens a popover */
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: overflowOpen, onOpenChange: setOverflowOpen, children: [
|
|
2246
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2247
|
+
"button",
|
|
2248
|
+
{
|
|
2249
|
+
type: "button",
|
|
2250
|
+
className: "gantt-tl-dep-summary-chip",
|
|
2251
|
+
onClick: (e) => {
|
|
2252
|
+
e.stopPropagation();
|
|
2253
|
+
setOverflowOpen((v) => !v);
|
|
2254
|
+
},
|
|
2255
|
+
children: [
|
|
2256
|
+
chips.length,
|
|
2257
|
+
" ",
|
|
2258
|
+
linkWord
|
|
2259
|
+
]
|
|
2260
|
+
}
|
|
2261
|
+
) }),
|
|
2262
|
+
/* @__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)(
|
|
2263
|
+
DepChip,
|
|
2264
|
+
{
|
|
2265
|
+
lag,
|
|
2266
|
+
dep,
|
|
2267
|
+
taskId: task.id,
|
|
2268
|
+
predecessorName,
|
|
2269
|
+
selectedChip,
|
|
2270
|
+
disableDependencyEditing,
|
|
2271
|
+
onChipSelect,
|
|
2272
|
+
onRowClick,
|
|
2273
|
+
onScrollToTask,
|
|
2274
|
+
onRemoveDependency,
|
|
2275
|
+
onChipSelectClear: () => onChipSelect?.(null)
|
|
2276
|
+
},
|
|
2277
|
+
`${dep.taskId}-${dep.type}`
|
|
2278
|
+
)) }) })
|
|
2279
|
+
] })
|
|
2280
|
+
) : chips.length === 1 ? (
|
|
2281
|
+
/* Single chip — unified DepChip */
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2283
|
+
DepChip,
|
|
2284
|
+
{
|
|
2285
|
+
lag: chips[0].lag,
|
|
2286
|
+
dep: chips[0].dep,
|
|
2287
|
+
taskId: task.id,
|
|
2288
|
+
predecessorName: chips[0].predecessorName,
|
|
2289
|
+
selectedChip,
|
|
2290
|
+
disableDependencyEditing,
|
|
2291
|
+
onChipSelect,
|
|
2292
|
+
onRowClick,
|
|
2293
|
+
onScrollToTask,
|
|
2294
|
+
onRemoveDependency,
|
|
2295
|
+
onChipSelectClear: () => onChipSelect?.(null)
|
|
2296
|
+
}
|
|
2297
|
+
)
|
|
2298
|
+
) : null,
|
|
2299
|
+
!disableDependencyEditing && !isPicking && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2300
|
+
"button",
|
|
2301
|
+
{
|
|
2302
|
+
type: "button",
|
|
2303
|
+
className: "gantt-tl-dep-add",
|
|
2304
|
+
onClick: handleAddClick,
|
|
2305
|
+
"aria-label": "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2306
|
+
children: "+"
|
|
2307
|
+
}
|
|
2308
|
+
)
|
|
2309
|
+
] })
|
|
2310
|
+
}
|
|
2311
|
+
)
|
|
2039
2312
|
]
|
|
2040
2313
|
}
|
|
2041
2314
|
);
|
|
@@ -2044,7 +2317,8 @@ var TaskListRow = import_react10.default.memo(
|
|
|
2044
2317
|
TaskListRow.displayName = "TaskListRow";
|
|
2045
2318
|
|
|
2046
2319
|
// src/components/TaskList/TaskList.tsx
|
|
2047
|
-
var
|
|
2320
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2321
|
+
var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
|
|
2048
2322
|
var TaskList = ({
|
|
2049
2323
|
tasks,
|
|
2050
2324
|
rowHeight,
|
|
@@ -2054,7 +2328,10 @@ var TaskList = ({
|
|
|
2054
2328
|
selectedTaskId,
|
|
2055
2329
|
onTaskSelect,
|
|
2056
2330
|
show = true,
|
|
2057
|
-
disableTaskNameEditing = false
|
|
2331
|
+
disableTaskNameEditing = false,
|
|
2332
|
+
disableDependencyEditing = false,
|
|
2333
|
+
onScrollToTask,
|
|
2334
|
+
onSelectedChipChange
|
|
2058
2335
|
}) => {
|
|
2059
2336
|
const totalHeight = (0, import_react11.useMemo)(
|
|
2060
2337
|
() => tasks.length * rowHeight,
|
|
@@ -2063,19 +2340,144 @@ var TaskList = ({
|
|
|
2063
2340
|
const handleRowClick = (0, import_react11.useCallback)((taskId) => {
|
|
2064
2341
|
onTaskSelect?.(taskId);
|
|
2065
2342
|
}, [onTaskSelect]);
|
|
2066
|
-
|
|
2343
|
+
const [activeLinkType, setActiveLinkType] = (0, import_react11.useState)("FS");
|
|
2344
|
+
const [selectingPredecessorFor, setSelectingPredecessorFor] = (0, import_react11.useState)(null);
|
|
2345
|
+
const [typeMenuOpen, setTypeMenuOpen] = (0, import_react11.useState)(false);
|
|
2346
|
+
const [cycleError, setCycleError] = (0, import_react11.useState)(false);
|
|
2347
|
+
const overlayRef = (0, import_react11.useRef)(null);
|
|
2348
|
+
const [selectedChip, setSelectedChip] = (0, import_react11.useState)(null);
|
|
2349
|
+
const handleChipSelect = (0, import_react11.useCallback)((chip) => {
|
|
2350
|
+
setSelectedChip(chip);
|
|
2351
|
+
onSelectedChipChange?.(chip);
|
|
2352
|
+
}, [onSelectedChipChange]);
|
|
2353
|
+
(0, import_react11.useEffect)(() => {
|
|
2354
|
+
if (!selectingPredecessorFor && !selectedChip) return;
|
|
2355
|
+
const handleKeyDown = (e) => {
|
|
2356
|
+
if (e.key === "Escape") {
|
|
2357
|
+
setSelectingPredecessorFor(null);
|
|
2358
|
+
setSelectedChip(null);
|
|
2359
|
+
onSelectedChipChange?.(null);
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2362
|
+
const handleMouseDown = (e) => {
|
|
2363
|
+
const target = e.target;
|
|
2364
|
+
if (overlayRef.current?.contains(target)) return;
|
|
2365
|
+
if (target.closest?.(".gantt-popover")) return;
|
|
2366
|
+
setSelectingPredecessorFor(null);
|
|
2367
|
+
setSelectedChip(null);
|
|
2368
|
+
onSelectedChipChange?.(null);
|
|
2369
|
+
};
|
|
2370
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2371
|
+
document.addEventListener("mousedown", handleMouseDown, true);
|
|
2372
|
+
return () => {
|
|
2373
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
2374
|
+
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
2375
|
+
};
|
|
2376
|
+
}, [selectingPredecessorFor, selectedChip, onSelectedChipChange]);
|
|
2377
|
+
const handleAddDependency = (0, import_react11.useCallback)((successorTaskId, predecessorTaskId, linkType) => {
|
|
2378
|
+
if (successorTaskId === predecessorTaskId) return;
|
|
2379
|
+
const successor = tasks.find((t) => t.id === successorTaskId);
|
|
2380
|
+
if (!successor) return;
|
|
2381
|
+
const alreadyExists = (successor.dependencies ?? []).some(
|
|
2382
|
+
(d) => d.taskId === predecessorTaskId && d.type === linkType
|
|
2383
|
+
);
|
|
2384
|
+
if (alreadyExists) {
|
|
2385
|
+
setSelectingPredecessorFor(null);
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
const newDep = { taskId: predecessorTaskId, type: linkType, lag: 0 };
|
|
2389
|
+
const hypothetical = tasks.map(
|
|
2390
|
+
(t) => t.id === successorTaskId ? { ...t, dependencies: [...t.dependencies ?? [], newDep] } : t
|
|
2391
|
+
);
|
|
2392
|
+
const validation = validateDependencies(hypothetical);
|
|
2393
|
+
if (!validation.isValid) {
|
|
2394
|
+
setCycleError(true);
|
|
2395
|
+
setTimeout(() => setCycleError(false), 3e3);
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
const updatedTask = hypothetical.find((t) => t.id === successorTaskId);
|
|
2399
|
+
const predecessor = tasks.find((t) => t.id === predecessorTaskId);
|
|
2400
|
+
if (predecessor) {
|
|
2401
|
+
const predStart = new Date(predecessor.startDate);
|
|
2402
|
+
const predEnd = new Date(predecessor.endDate);
|
|
2403
|
+
const constraintDate = calculateSuccessorDate(predStart, predEnd, linkType, 0);
|
|
2404
|
+
const origSuccessor = tasks.find((t) => t.id === successorTaskId);
|
|
2405
|
+
const durationMs = new Date(origSuccessor.endDate).getTime() - new Date(origSuccessor.startDate).getTime();
|
|
2406
|
+
let newStart;
|
|
2407
|
+
let newEnd;
|
|
2408
|
+
if (linkType === "FS" || linkType === "SS") {
|
|
2409
|
+
newStart = constraintDate;
|
|
2410
|
+
newEnd = new Date(constraintDate.getTime() + durationMs);
|
|
2411
|
+
} else {
|
|
2412
|
+
newEnd = constraintDate;
|
|
2413
|
+
newStart = new Date(constraintDate.getTime() - durationMs);
|
|
2414
|
+
}
|
|
2415
|
+
const snappedTask = {
|
|
2416
|
+
...updatedTask,
|
|
2417
|
+
startDate: newStart.toISOString().split("T")[0],
|
|
2418
|
+
endDate: newEnd.toISOString().split("T")[0]
|
|
2419
|
+
};
|
|
2420
|
+
onTaskChange?.(snappedTask);
|
|
2421
|
+
} else {
|
|
2422
|
+
onTaskChange?.(updatedTask);
|
|
2423
|
+
}
|
|
2424
|
+
setSelectingPredecessorFor(null);
|
|
2425
|
+
}, [tasks, onTaskChange]);
|
|
2426
|
+
const handleRemoveDependency = (0, import_react11.useCallback)((taskId, predecessorTaskId, linkType) => {
|
|
2427
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
2428
|
+
if (!task) return;
|
|
2429
|
+
const updatedDeps = (task.dependencies ?? []).filter(
|
|
2430
|
+
(d) => !(d.taskId === predecessorTaskId && d.type === linkType)
|
|
2431
|
+
);
|
|
2432
|
+
onTaskChange?.({ ...task, dependencies: updatedDeps });
|
|
2433
|
+
}, [tasks, onTaskChange]);
|
|
2434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2067
2435
|
"div",
|
|
2068
2436
|
{
|
|
2437
|
+
ref: overlayRef,
|
|
2069
2438
|
className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}`,
|
|
2070
2439
|
style: { width: `${taskListWidth}px` },
|
|
2071
|
-
children: /* @__PURE__ */ (0,
|
|
2072
|
-
/* @__PURE__ */ (0,
|
|
2073
|
-
/* @__PURE__ */ (0,
|
|
2074
|
-
/* @__PURE__ */ (0,
|
|
2075
|
-
/* @__PURE__ */ (0,
|
|
2076
|
-
/* @__PURE__ */ (0,
|
|
2440
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-table", children: [
|
|
2441
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-header", style: { height: `${headerHeight + 0.5}px` }, children: [
|
|
2442
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-number", children: "\u2116" }),
|
|
2443
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-name", children: "\u0418\u043C\u044F" }),
|
|
2444
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
|
|
2445
|
+
/* @__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" }),
|
|
2446
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-headerCell gantt-tl-cell-deps", style: { position: "relative" }, children: [
|
|
2447
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2449
|
+
"button",
|
|
2450
|
+
{
|
|
2451
|
+
className: "gantt-tl-dep-type-trigger",
|
|
2452
|
+
disabled: disableDependencyEditing,
|
|
2453
|
+
onClick: (e) => e.stopPropagation(),
|
|
2454
|
+
children: [
|
|
2455
|
+
"\u0421\u0432\u044F\u0437\u0438 ",
|
|
2456
|
+
import_react11.default.createElement(LINK_TYPE_ICONS[activeLinkType]),
|
|
2457
|
+
" \u25BE"
|
|
2458
|
+
]
|
|
2459
|
+
}
|
|
2460
|
+
) }),
|
|
2461
|
+
/* @__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)(
|
|
2462
|
+
"button",
|
|
2463
|
+
{
|
|
2464
|
+
className: `gantt-tl-dep-type-option${activeLinkType === lt ? " active" : ""}`,
|
|
2465
|
+
onClick: () => {
|
|
2466
|
+
setActiveLinkType(lt);
|
|
2467
|
+
setTypeMenuOpen(false);
|
|
2468
|
+
},
|
|
2469
|
+
children: [
|
|
2470
|
+
import_react11.default.createElement(LINK_TYPE_ICONS[lt]),
|
|
2471
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: LINK_TYPE_LABELS[lt] })
|
|
2472
|
+
]
|
|
2473
|
+
},
|
|
2474
|
+
lt
|
|
2475
|
+
)) }) })
|
|
2476
|
+
] }),
|
|
2477
|
+
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!" })
|
|
2478
|
+
] })
|
|
2077
2479
|
] }),
|
|
2078
|
-
/* @__PURE__ */ (0,
|
|
2480
|
+
/* @__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)(
|
|
2079
2481
|
TaskListRow,
|
|
2080
2482
|
{
|
|
2081
2483
|
task,
|
|
@@ -2084,7 +2486,17 @@ var TaskList = ({
|
|
|
2084
2486
|
onTaskChange,
|
|
2085
2487
|
selectedTaskId,
|
|
2086
2488
|
onRowClick: handleRowClick,
|
|
2087
|
-
disableTaskNameEditing
|
|
2489
|
+
disableTaskNameEditing,
|
|
2490
|
+
disableDependencyEditing,
|
|
2491
|
+
allTasks: tasks,
|
|
2492
|
+
activeLinkType,
|
|
2493
|
+
selectingPredecessorFor,
|
|
2494
|
+
onSetSelectingPredecessorFor: setSelectingPredecessorFor,
|
|
2495
|
+
onAddDependency: handleAddDependency,
|
|
2496
|
+
onRemoveDependency: handleRemoveDependency,
|
|
2497
|
+
selectedChip,
|
|
2498
|
+
onChipSelect: handleChipSelect,
|
|
2499
|
+
onScrollToTask
|
|
2088
2500
|
},
|
|
2089
2501
|
task.id
|
|
2090
2502
|
)) })
|
|
@@ -2094,13 +2506,13 @@ var TaskList = ({
|
|
|
2094
2506
|
};
|
|
2095
2507
|
|
|
2096
2508
|
// src/components/GanttChart/GanttChart.tsx
|
|
2097
|
-
var
|
|
2509
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2098
2510
|
var GanttChart = (0, import_react12.forwardRef)(({
|
|
2099
2511
|
tasks,
|
|
2100
2512
|
dayWidth = 40,
|
|
2101
2513
|
rowHeight = 40,
|
|
2102
2514
|
headerHeight = 40,
|
|
2103
|
-
containerHeight
|
|
2515
|
+
containerHeight,
|
|
2104
2516
|
onChange,
|
|
2105
2517
|
onValidateDependencies,
|
|
2106
2518
|
enableAutoSchedule,
|
|
@@ -2108,10 +2520,12 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2108
2520
|
onCascade,
|
|
2109
2521
|
showTaskList = false,
|
|
2110
2522
|
taskListWidth = 520,
|
|
2111
|
-
disableTaskNameEditing = false
|
|
2523
|
+
disableTaskNameEditing = false,
|
|
2524
|
+
disableDependencyEditing = false
|
|
2112
2525
|
}, ref) => {
|
|
2113
2526
|
const scrollContainerRef = (0, import_react12.useRef)(null);
|
|
2114
2527
|
const [selectedTaskId, setSelectedTaskId] = (0, import_react12.useState)(null);
|
|
2528
|
+
const [selectedChip, setSelectedChip] = (0, import_react12.useState)(null);
|
|
2115
2529
|
const dateRange = (0, import_react12.useMemo)(() => getMultiMonthDays(tasks), [tasks]);
|
|
2116
2530
|
const [validationResult, setValidationResult] = (0, import_react12.useState)(null);
|
|
2117
2531
|
const [cascadeOverrides, setCascadeOverrides] = (0, import_react12.useState)(/* @__PURE__ */ new Map());
|
|
@@ -2159,12 +2573,30 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2159
2573
|
const scrollLeft = Math.round(todayOffset - containerWidth / 2 + dayWidth / 2);
|
|
2160
2574
|
container.scrollLeft = Math.max(0, scrollLeft);
|
|
2161
2575
|
}, [dateRange, dayWidth]);
|
|
2576
|
+
const scrollToTask = (0, import_react12.useCallback)((taskId) => {
|
|
2577
|
+
const container = scrollContainerRef.current;
|
|
2578
|
+
if (!container || dateRange.length === 0) return;
|
|
2579
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
2580
|
+
if (!task) return;
|
|
2581
|
+
const taskStart = new Date(task.startDate);
|
|
2582
|
+
const taskStartUTC = new Date(Date.UTC(
|
|
2583
|
+
taskStart.getUTCFullYear(),
|
|
2584
|
+
taskStart.getUTCMonth(),
|
|
2585
|
+
taskStart.getUTCDate()
|
|
2586
|
+
));
|
|
2587
|
+
const taskIndex = dateRange.findIndex((day) => day.getTime() === taskStartUTC.getTime());
|
|
2588
|
+
if (taskIndex === -1) return;
|
|
2589
|
+
const taskOffset = taskIndex * dayWidth;
|
|
2590
|
+
const scrollLeft = Math.round(taskOffset - dayWidth * 2);
|
|
2591
|
+
container.scrollLeft = Math.max(0, scrollLeft);
|
|
2592
|
+
}, [tasks, dateRange, dayWidth]);
|
|
2162
2593
|
(0, import_react12.useImperativeHandle)(
|
|
2163
2594
|
ref,
|
|
2164
2595
|
() => ({
|
|
2165
|
-
scrollToToday
|
|
2596
|
+
scrollToToday,
|
|
2597
|
+
scrollToTask
|
|
2166
2598
|
}),
|
|
2167
|
-
[scrollToToday]
|
|
2599
|
+
[scrollToToday, scrollToTask]
|
|
2168
2600
|
);
|
|
2169
2601
|
const [dragGuideLines, setDragGuideLines] = (0, import_react12.useState)(null);
|
|
2170
2602
|
const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react12.useState)(null);
|
|
@@ -2264,15 +2696,15 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2264
2696
|
window.removeEventListener("mouseup", handlePanEnd);
|
|
2265
2697
|
};
|
|
2266
2698
|
}, []);
|
|
2267
|
-
return /* @__PURE__ */ (0,
|
|
2699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-container", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2268
2700
|
"div",
|
|
2269
2701
|
{
|
|
2270
2702
|
ref: scrollContainerRef,
|
|
2271
2703
|
className: "gantt-scrollContainer",
|
|
2272
|
-
style: { height:
|
|
2704
|
+
style: { height: containerHeight ?? "auto", cursor: "grab" },
|
|
2273
2705
|
onMouseDown: handlePanStart,
|
|
2274
|
-
children: /* @__PURE__ */ (0,
|
|
2275
|
-
/* @__PURE__ */ (0,
|
|
2706
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gantt-scrollContent", children: [
|
|
2707
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2276
2708
|
TaskList,
|
|
2277
2709
|
{
|
|
2278
2710
|
tasks,
|
|
@@ -2283,11 +2715,14 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2283
2715
|
selectedTaskId: selectedTaskId ?? void 0,
|
|
2284
2716
|
onTaskSelect: handleTaskSelect,
|
|
2285
2717
|
show: showTaskList,
|
|
2286
|
-
disableTaskNameEditing
|
|
2718
|
+
disableTaskNameEditing,
|
|
2719
|
+
disableDependencyEditing,
|
|
2720
|
+
onScrollToTask: scrollToTask,
|
|
2721
|
+
onSelectedChipChange: setSelectedChip
|
|
2287
2722
|
}
|
|
2288
2723
|
),
|
|
2289
|
-
/* @__PURE__ */ (0,
|
|
2290
|
-
/* @__PURE__ */ (0,
|
|
2724
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { minWidth: `${gridWidth}px`, flex: 1 }, children: [
|
|
2725
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2291
2726
|
TimeScaleHeader_default,
|
|
2292
2727
|
{
|
|
2293
2728
|
days: dateRange,
|
|
@@ -2295,7 +2730,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2295
2730
|
headerHeight
|
|
2296
2731
|
}
|
|
2297
2732
|
) }),
|
|
2298
|
-
/* @__PURE__ */ (0,
|
|
2733
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2299
2734
|
"div",
|
|
2300
2735
|
{
|
|
2301
2736
|
className: "gantt-taskArea",
|
|
@@ -2304,7 +2739,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2304
2739
|
width: `${gridWidth}px`
|
|
2305
2740
|
},
|
|
2306
2741
|
children: [
|
|
2307
|
-
/* @__PURE__ */ (0,
|
|
2742
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2308
2743
|
GridBackground_default,
|
|
2309
2744
|
{
|
|
2310
2745
|
dateRange,
|
|
@@ -2312,8 +2747,8 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2312
2747
|
totalHeight: totalGridHeight
|
|
2313
2748
|
}
|
|
2314
2749
|
),
|
|
2315
|
-
todayInRange && /* @__PURE__ */ (0,
|
|
2316
|
-
/* @__PURE__ */ (0,
|
|
2750
|
+
todayInRange && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
|
|
2751
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2317
2752
|
DependencyLines_default,
|
|
2318
2753
|
{
|
|
2319
2754
|
tasks,
|
|
@@ -2321,10 +2756,11 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2321
2756
|
dayWidth,
|
|
2322
2757
|
rowHeight,
|
|
2323
2758
|
gridWidth,
|
|
2324
|
-
dragOverrides: dependencyOverrides
|
|
2759
|
+
dragOverrides: dependencyOverrides,
|
|
2760
|
+
selectedDep: selectedChip
|
|
2325
2761
|
}
|
|
2326
2762
|
),
|
|
2327
|
-
dragGuideLines && /* @__PURE__ */ (0,
|
|
2763
|
+
dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2328
2764
|
DragGuideLines_default,
|
|
2329
2765
|
{
|
|
2330
2766
|
isDragging: dragGuideLines.isDragging,
|
|
@@ -2334,7 +2770,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
|
|
|
2334
2770
|
totalHeight: totalGridHeight
|
|
2335
2771
|
}
|
|
2336
2772
|
),
|
|
2337
|
-
tasks.map((task, index) => /* @__PURE__ */ (0,
|
|
2773
|
+
tasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2338
2774
|
TaskRow_default,
|
|
2339
2775
|
{
|
|
2340
2776
|
task,
|
|
@@ -2373,7 +2809,7 @@ GanttChart.displayName = "GanttChart";
|
|
|
2373
2809
|
|
|
2374
2810
|
// src/components/ui/Button.tsx
|
|
2375
2811
|
var import_react13 = __toESM(require("react"));
|
|
2376
|
-
var
|
|
2812
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2377
2813
|
var Button = import_react13.default.forwardRef(
|
|
2378
2814
|
({ className, variant = "default", size = "default", children, ...props }, ref) => {
|
|
2379
2815
|
const classes = [
|
|
@@ -2382,7 +2818,7 @@ var Button = import_react13.default.forwardRef(
|
|
|
2382
2818
|
size !== "default" ? `gantt-btn-${size}` : "",
|
|
2383
2819
|
className || ""
|
|
2384
2820
|
].filter(Boolean).join(" ");
|
|
2385
|
-
return /* @__PURE__ */ (0,
|
|
2821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { ref, className: classes, ...props, children });
|
|
2386
2822
|
}
|
|
2387
2823
|
);
|
|
2388
2824
|
Button.displayName = "Button";
|
|
@@ -2412,6 +2848,7 @@ Button.displayName = "Button";
|
|
|
2412
2848
|
calculateTaskBar,
|
|
2413
2849
|
calculateWeekendBlocks,
|
|
2414
2850
|
cascadeByLinks,
|
|
2851
|
+
computeLagFromDates,
|
|
2415
2852
|
detectCycles,
|
|
2416
2853
|
detectEdgeZone,
|
|
2417
2854
|
formatDateLabel,
|