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.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/components/GanttChart/GanttChart.tsx
|
|
4
|
-
import { useMemo as
|
|
4
|
+
import { useMemo as useMemo9, useCallback as useCallback6, useRef as useRef5, useState as useState6, useEffect as useEffect5, useImperativeHandle, forwardRef } from "react";
|
|
5
5
|
|
|
6
6
|
// src/utils/dateUtils.ts
|
|
7
7
|
var parseUTCDate = (date) => {
|
|
@@ -195,11 +195,35 @@ function detectCycles(tasks) {
|
|
|
195
195
|
}
|
|
196
196
|
return { hasCycle: false };
|
|
197
197
|
}
|
|
198
|
+
function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd) {
|
|
199
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
200
|
+
const pS = Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate());
|
|
201
|
+
const pE = Date.UTC(predEnd.getUTCFullYear(), predEnd.getUTCMonth(), predEnd.getUTCDate());
|
|
202
|
+
const sS = Date.UTC(succStart.getUTCFullYear(), succStart.getUTCMonth(), succStart.getUTCDate());
|
|
203
|
+
const sE = Date.UTC(succEnd.getUTCFullYear(), succEnd.getUTCMonth(), succEnd.getUTCDate());
|
|
204
|
+
switch (linkType) {
|
|
205
|
+
case "FS":
|
|
206
|
+
return Math.round((sS - pE) / DAY_MS) - 1;
|
|
207
|
+
case "SS":
|
|
208
|
+
return Math.round((sS - pS) / DAY_MS);
|
|
209
|
+
case "FF":
|
|
210
|
+
return Math.round((sE - pE) / DAY_MS);
|
|
211
|
+
case "SF":
|
|
212
|
+
return Math.round((sE - pS) / DAY_MS) + 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
198
215
|
function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag = 0) {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
216
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
217
|
+
switch (linkType) {
|
|
218
|
+
case "FS":
|
|
219
|
+
return new Date(predecessorEnd.getTime() + (lag + 1) * DAY_MS);
|
|
220
|
+
case "SS":
|
|
221
|
+
return new Date(predecessorStart.getTime() + lag * DAY_MS);
|
|
222
|
+
case "FF":
|
|
223
|
+
return new Date(predecessorEnd.getTime() + lag * DAY_MS);
|
|
224
|
+
case "SF":
|
|
225
|
+
return new Date(predecessorStart.getTime() + (lag - 1) * DAY_MS);
|
|
226
|
+
}
|
|
203
227
|
}
|
|
204
228
|
function validateDependencies(tasks) {
|
|
205
229
|
const errors = [];
|
|
@@ -346,35 +370,10 @@ function recalculateIncomingLags(task, newStartDate, newEndDate, allTasks) {
|
|
|
346
370
|
return task.dependencies.map((dep) => {
|
|
347
371
|
const predecessor = taskById.get(dep.taskId);
|
|
348
372
|
if (!predecessor) return dep;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
);
|
|
354
|
-
return { ...dep, lag: lagDays };
|
|
355
|
-
}
|
|
356
|
-
if (dep.type === "SS") {
|
|
357
|
-
const predStart = new Date(predecessor.startDate);
|
|
358
|
-
const lagDays = Math.max(0, Math.round(
|
|
359
|
-
(Date.UTC(newStartDate.getUTCFullYear(), newStartDate.getUTCMonth(), newStartDate.getUTCDate()) - Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate())) / (24 * 60 * 60 * 1e3)
|
|
360
|
-
));
|
|
361
|
-
return { ...dep, lag: lagDays };
|
|
362
|
-
}
|
|
363
|
-
if (dep.type === "FF") {
|
|
364
|
-
const predEnd = new Date(predecessor.endDate);
|
|
365
|
-
const lagDays = Math.round(
|
|
366
|
-
(Date.UTC(newEndDate.getUTCFullYear(), newEndDate.getUTCMonth(), newEndDate.getUTCDate()) - Date.UTC(predEnd.getUTCFullYear(), predEnd.getUTCMonth(), predEnd.getUTCDate())) / (24 * 60 * 60 * 1e3)
|
|
367
|
-
);
|
|
368
|
-
return { ...dep, lag: lagDays };
|
|
369
|
-
}
|
|
370
|
-
if (dep.type === "SF") {
|
|
371
|
-
const predStart = new Date(predecessor.startDate);
|
|
372
|
-
const lagDays = Math.min(0, Math.round(
|
|
373
|
-
(Date.UTC(newEndDate.getUTCFullYear(), newEndDate.getUTCMonth(), newEndDate.getUTCDate()) - Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate()) + 24 * 60 * 60 * 1e3) / (24 * 60 * 60 * 1e3)
|
|
374
|
-
));
|
|
375
|
-
return { ...dep, lag: lagDays };
|
|
376
|
-
}
|
|
377
|
-
return dep;
|
|
373
|
+
const predStart = new Date(predecessor.startDate);
|
|
374
|
+
const predEnd = new Date(predecessor.endDate);
|
|
375
|
+
const lagDays = computeLagFromDates(dep.type, predStart, predEnd, newStartDate, newEndDate);
|
|
376
|
+
return { ...dep, lag: lagDays };
|
|
378
377
|
});
|
|
379
378
|
}
|
|
380
379
|
function getAllDependencyEdges(tasks) {
|
|
@@ -1277,15 +1276,11 @@ var TodayIndicator = ({ monthStart, dayWidth }) => {
|
|
|
1277
1276
|
today.getMonth(),
|
|
1278
1277
|
today.getDate()
|
|
1279
1278
|
));
|
|
1280
|
-
const isInMonth = useMemo3(() => {
|
|
1281
|
-
return todayLocal.getUTCFullYear() === monthStart.getUTCFullYear() && todayLocal.getUTCMonth() === monthStart.getUTCMonth();
|
|
1282
|
-
}, [monthStart, todayLocal]);
|
|
1283
1279
|
const position = useMemo3(() => {
|
|
1284
|
-
if (!isInMonth) return null;
|
|
1285
1280
|
const offset = getDayOffset(todayLocal, monthStart);
|
|
1286
1281
|
return Math.round(offset * dayWidth);
|
|
1287
|
-
}, [
|
|
1288
|
-
if (
|
|
1282
|
+
}, [monthStart, dayWidth, todayLocal]);
|
|
1283
|
+
if (isNaN(position)) {
|
|
1289
1284
|
return null;
|
|
1290
1285
|
}
|
|
1291
1286
|
return /* @__PURE__ */ jsx3(
|
|
@@ -1405,60 +1400,11 @@ var DragGuideLines_default = DragGuideLines;
|
|
|
1405
1400
|
import React5, { useMemo as useMemo5 } from "react";
|
|
1406
1401
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1407
1402
|
function calculateEffectiveLag(edge, predPosition, succPosition, monthStart, dayWidth) {
|
|
1408
|
-
const
|
|
1409
|
-
const
|
|
1410
|
-
const
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
switch (edge.type) {
|
|
1414
|
-
case "FS":
|
|
1415
|
-
lagMs = Date.UTC(
|
|
1416
|
-
succStartDate.getUTCFullYear(),
|
|
1417
|
-
succStartDate.getUTCMonth(),
|
|
1418
|
-
succStartDate.getUTCDate()
|
|
1419
|
-
) - Date.UTC(
|
|
1420
|
-
predEndDate.getUTCFullYear(),
|
|
1421
|
-
predEndDate.getUTCMonth(),
|
|
1422
|
-
predEndDate.getUTCDate()
|
|
1423
|
-
) - 24 * 60 * 60 * 1e3;
|
|
1424
|
-
break;
|
|
1425
|
-
case "SS":
|
|
1426
|
-
lagMs = Date.UTC(
|
|
1427
|
-
succStartDate.getUTCFullYear(),
|
|
1428
|
-
succStartDate.getUTCMonth(),
|
|
1429
|
-
succStartDate.getUTCDate()
|
|
1430
|
-
) - Date.UTC(
|
|
1431
|
-
predStartDate.getUTCFullYear(),
|
|
1432
|
-
predStartDate.getUTCMonth(),
|
|
1433
|
-
predStartDate.getUTCDate()
|
|
1434
|
-
);
|
|
1435
|
-
break;
|
|
1436
|
-
case "FF":
|
|
1437
|
-
lagMs = Date.UTC(
|
|
1438
|
-
succEndDate.getUTCFullYear(),
|
|
1439
|
-
succEndDate.getUTCMonth(),
|
|
1440
|
-
succEndDate.getUTCDate()
|
|
1441
|
-
) - Date.UTC(
|
|
1442
|
-
predEndDate.getUTCFullYear(),
|
|
1443
|
-
predEndDate.getUTCMonth(),
|
|
1444
|
-
predEndDate.getUTCDate()
|
|
1445
|
-
);
|
|
1446
|
-
break;
|
|
1447
|
-
case "SF":
|
|
1448
|
-
lagMs = Date.UTC(
|
|
1449
|
-
succEndDate.getUTCFullYear(),
|
|
1450
|
-
succEndDate.getUTCMonth(),
|
|
1451
|
-
succEndDate.getUTCDate()
|
|
1452
|
-
) - Date.UTC(
|
|
1453
|
-
predStartDate.getUTCFullYear(),
|
|
1454
|
-
predStartDate.getUTCMonth(),
|
|
1455
|
-
predStartDate.getUTCDate()
|
|
1456
|
-
) + 24 * 60 * 60 * 1e3;
|
|
1457
|
-
break;
|
|
1458
|
-
default:
|
|
1459
|
-
return 0;
|
|
1460
|
-
}
|
|
1461
|
-
return Math.round(lagMs / (24 * 60 * 60 * 1e3));
|
|
1403
|
+
const predStart = pixelsToDate(predPosition.left, monthStart, dayWidth);
|
|
1404
|
+
const predEnd = pixelsToDate(predPosition.right - dayWidth, monthStart, dayWidth);
|
|
1405
|
+
const succStart = pixelsToDate(succPosition.left, monthStart, dayWidth);
|
|
1406
|
+
const succEnd = pixelsToDate(succPosition.right - dayWidth, monthStart, dayWidth);
|
|
1407
|
+
return computeLagFromDates(edge.type, predStart, predEnd, succStart, succEnd);
|
|
1462
1408
|
}
|
|
1463
1409
|
var DependencyLines = React5.memo(({
|
|
1464
1410
|
tasks,
|
|
@@ -1466,7 +1412,8 @@ var DependencyLines = React5.memo(({
|
|
|
1466
1412
|
dayWidth,
|
|
1467
1413
|
rowHeight,
|
|
1468
1414
|
gridWidth,
|
|
1469
|
-
dragOverrides
|
|
1415
|
+
dragOverrides,
|
|
1416
|
+
selectedDep
|
|
1470
1417
|
}) => {
|
|
1471
1418
|
const { taskPositions, taskIndices } = useMemo5(() => {
|
|
1472
1419
|
const positions = /* @__PURE__ */ new Map();
|
|
@@ -1580,30 +1527,60 @@ var DependencyLines = React5.memo(({
|
|
|
1580
1527
|
}
|
|
1581
1528
|
)
|
|
1582
1529
|
}
|
|
1583
|
-
)
|
|
1584
|
-
] }),
|
|
1585
|
-
lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder }) => /* @__PURE__ */ jsxs5(React5.Fragment, { children: [
|
|
1586
|
-
/* @__PURE__ */ jsx6(
|
|
1587
|
-
"path",
|
|
1588
|
-
{
|
|
1589
|
-
d: path,
|
|
1590
|
-
className: hasCycle ? "gantt-dependency-path gantt-dependency-cycle" : "gantt-dependency-path",
|
|
1591
|
-
markerEnd: hasCycle ? "url(#arrowhead-cycle)" : "url(#arrowhead)"
|
|
1592
|
-
}
|
|
1593
1530
|
),
|
|
1594
|
-
|
|
1595
|
-
"
|
|
1531
|
+
/* @__PURE__ */ jsx6(
|
|
1532
|
+
"marker",
|
|
1596
1533
|
{
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1534
|
+
id: "arrowhead-selected",
|
|
1535
|
+
markerWidth: "8",
|
|
1536
|
+
markerHeight: "6",
|
|
1537
|
+
markerUnits: "userSpaceOnUse",
|
|
1538
|
+
refX: "7",
|
|
1539
|
+
refY: "3",
|
|
1540
|
+
orient: "auto",
|
|
1541
|
+
children: /* @__PURE__ */ jsx6(
|
|
1542
|
+
"polygon",
|
|
1543
|
+
{
|
|
1544
|
+
points: "0 0, 8 3, 0 6",
|
|
1545
|
+
fill: "#ef4444"
|
|
1546
|
+
}
|
|
1547
|
+
)
|
|
1604
1548
|
}
|
|
1605
1549
|
)
|
|
1606
|
-
] },
|
|
1550
|
+
] }),
|
|
1551
|
+
lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder }) => {
|
|
1552
|
+
const isSelected = selectedDep != null && id === `${selectedDep.predecessorId}-${selectedDep.successorId}-${selectedDep.linkType}`;
|
|
1553
|
+
let pathClassName = "gantt-dependency-path";
|
|
1554
|
+
if (isSelected) pathClassName += " gantt-dependency-selected";
|
|
1555
|
+
else if (hasCycle) pathClassName += " gantt-dependency-cycle";
|
|
1556
|
+
let markerEnd;
|
|
1557
|
+
if (isSelected) markerEnd = "url(#arrowhead-selected)";
|
|
1558
|
+
else if (hasCycle) markerEnd = "url(#arrowhead-cycle)";
|
|
1559
|
+
else markerEnd = "url(#arrowhead)";
|
|
1560
|
+
const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : "var(--gantt-dependency-line-color, #666666)";
|
|
1561
|
+
return /* @__PURE__ */ jsxs5(React5.Fragment, { children: [
|
|
1562
|
+
/* @__PURE__ */ jsx6(
|
|
1563
|
+
"path",
|
|
1564
|
+
{
|
|
1565
|
+
d: path,
|
|
1566
|
+
className: pathClassName,
|
|
1567
|
+
markerEnd
|
|
1568
|
+
}
|
|
1569
|
+
),
|
|
1570
|
+
lag !== 0 && /* @__PURE__ */ jsx6(
|
|
1571
|
+
"text",
|
|
1572
|
+
{
|
|
1573
|
+
className: "gantt-dependency-lag-label",
|
|
1574
|
+
x: lag < 0 ? toX + 14 : toX - 14,
|
|
1575
|
+
y: reverseOrder ? fromY - 4 : fromY + 12,
|
|
1576
|
+
textAnchor: "middle",
|
|
1577
|
+
fontSize: "10",
|
|
1578
|
+
fill: lagColor,
|
|
1579
|
+
children: lag > 0 ? `+${lag}` : `${lag}`
|
|
1580
|
+
}
|
|
1581
|
+
)
|
|
1582
|
+
] }, id);
|
|
1583
|
+
})
|
|
1607
1584
|
]
|
|
1608
1585
|
}
|
|
1609
1586
|
);
|
|
@@ -1612,17 +1589,49 @@ DependencyLines.displayName = "DependencyLines";
|
|
|
1612
1589
|
var DependencyLines_default = DependencyLines;
|
|
1613
1590
|
|
|
1614
1591
|
// src/components/TaskList/TaskList.tsx
|
|
1615
|
-
import { useMemo as
|
|
1592
|
+
import React10, { useMemo as useMemo8, useCallback as useCallback5, useState as useState5, useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
1593
|
+
|
|
1594
|
+
// src/components/ui/Popover.tsx
|
|
1595
|
+
import * as RadixPopover from "@radix-ui/react-popover";
|
|
1596
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1597
|
+
var Popover = ({ open, onOpenChange, children }) => {
|
|
1598
|
+
return /* @__PURE__ */ jsx7(RadixPopover.Root, { open, onOpenChange, children });
|
|
1599
|
+
};
|
|
1600
|
+
var PopoverTrigger = RadixPopover.Trigger;
|
|
1601
|
+
var PopoverContent = ({
|
|
1602
|
+
children,
|
|
1603
|
+
className,
|
|
1604
|
+
align = "start",
|
|
1605
|
+
side = "bottom",
|
|
1606
|
+
portal = true,
|
|
1607
|
+
collisionPadding = 8
|
|
1608
|
+
}) => {
|
|
1609
|
+
const content = /* @__PURE__ */ jsx7(
|
|
1610
|
+
RadixPopover.Content,
|
|
1611
|
+
{
|
|
1612
|
+
className: `gantt-popover${className ? ` ${className}` : ""}`,
|
|
1613
|
+
align,
|
|
1614
|
+
side,
|
|
1615
|
+
collisionPadding,
|
|
1616
|
+
sideOffset: 4,
|
|
1617
|
+
children
|
|
1618
|
+
}
|
|
1619
|
+
);
|
|
1620
|
+
if (portal) {
|
|
1621
|
+
return /* @__PURE__ */ jsx7(RadixPopover.Portal, { children: content });
|
|
1622
|
+
}
|
|
1623
|
+
return content;
|
|
1624
|
+
};
|
|
1616
1625
|
|
|
1617
1626
|
// src/components/TaskList/TaskListRow.tsx
|
|
1618
|
-
import React9, { useState as useState4, useRef as useRef3, useEffect as useEffect3, useCallback as useCallback4 } from "react";
|
|
1627
|
+
import React9, { useState as useState4, useRef as useRef3, useEffect as useEffect3, useCallback as useCallback4, useMemo as useMemo7 } from "react";
|
|
1619
1628
|
|
|
1620
1629
|
// src/components/ui/Input.tsx
|
|
1621
1630
|
import React6 from "react";
|
|
1622
|
-
import { jsx as
|
|
1631
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1623
1632
|
var Input = React6.forwardRef(
|
|
1624
1633
|
({ className, ...props }, ref) => {
|
|
1625
|
-
return /* @__PURE__ */
|
|
1634
|
+
return /* @__PURE__ */ jsx8(
|
|
1626
1635
|
"input",
|
|
1627
1636
|
{
|
|
1628
1637
|
ref,
|
|
@@ -1654,19 +1663,19 @@ import {
|
|
|
1654
1663
|
subMonths,
|
|
1655
1664
|
isSameDay,
|
|
1656
1665
|
getDay,
|
|
1657
|
-
isToday as
|
|
1666
|
+
isToday as isToday2,
|
|
1658
1667
|
isWeekend as isWeekend2,
|
|
1659
1668
|
isBefore,
|
|
1660
1669
|
startOfDay
|
|
1661
1670
|
} from "date-fns";
|
|
1662
1671
|
import { ru as ru2 } from "date-fns/locale";
|
|
1663
|
-
import { jsx as
|
|
1672
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1664
1673
|
function getDayClassName(day, selected) {
|
|
1665
1674
|
const classes = ["gantt-day-btn"];
|
|
1666
1675
|
if (selected && isSameDay(day, selected)) classes.push("selected");
|
|
1667
|
-
if (
|
|
1676
|
+
if (isToday2(day)) classes.push("today");
|
|
1668
1677
|
if (isWeekend2(day)) classes.push("weekend");
|
|
1669
|
-
if (isBefore(day, startOfDay(/* @__PURE__ */ new Date())) && !
|
|
1678
|
+
if (isBefore(day, startOfDay(/* @__PURE__ */ new Date())) && !isToday2(day)) classes.push("past");
|
|
1670
1679
|
return classes.join(" ");
|
|
1671
1680
|
}
|
|
1672
1681
|
var Calendar = ({
|
|
@@ -1730,12 +1739,12 @@ var Calendar = ({
|
|
|
1730
1739
|
const emptyDays = (getDay(firstDay) + 6) % 7;
|
|
1731
1740
|
const monthKey = format2(month, "yyyy-MM");
|
|
1732
1741
|
const monthLabel = format2(month, "LLLL yyyy", { locale: ru2 });
|
|
1733
|
-
const emptyCells = Array.from({ length: emptyDays }, (_, i) => /* @__PURE__ */
|
|
1742
|
+
const emptyCells = Array.from({ length: emptyDays }, (_, i) => /* @__PURE__ */ jsx9("div", { className: "gantt-cal-empty-day" }, `e-${i}`));
|
|
1734
1743
|
const dayCells = Array.from({ length: totalDays }, (_, i) => {
|
|
1735
1744
|
const dayNum = i + 1;
|
|
1736
1745
|
const day = new Date(month.getFullYear(), month.getMonth(), dayNum);
|
|
1737
1746
|
const className = getDayClassName(day, selected);
|
|
1738
|
-
return /* @__PURE__ */
|
|
1747
|
+
return /* @__PURE__ */ jsx9(
|
|
1739
1748
|
"button",
|
|
1740
1749
|
{
|
|
1741
1750
|
type: "button",
|
|
@@ -1752,7 +1761,7 @@ var Calendar = ({
|
|
|
1752
1761
|
);
|
|
1753
1762
|
});
|
|
1754
1763
|
return /* @__PURE__ */ jsxs6("div", { className: "gantt-cal-month", "data-month": monthKey, children: [
|
|
1755
|
-
/* @__PURE__ */
|
|
1764
|
+
/* @__PURE__ */ jsx9("div", { className: "gantt-cal-month-header", children: monthLabel }),
|
|
1756
1765
|
/* @__PURE__ */ jsxs6("div", { className: "gantt-cal-month-days", children: [
|
|
1757
1766
|
emptyCells,
|
|
1758
1767
|
dayCells
|
|
@@ -1765,42 +1774,10 @@ var Calendar = ({
|
|
|
1765
1774
|
() => months.map(renderMonth),
|
|
1766
1775
|
[months, renderMonth]
|
|
1767
1776
|
);
|
|
1768
|
-
return /* @__PURE__ */
|
|
1777
|
+
return /* @__PURE__ */ jsx9("div", { ref: scrollRef, className: "gantt-cal-container", children: renderedMonths });
|
|
1769
1778
|
};
|
|
1770
1779
|
Calendar.displayName = "Calendar";
|
|
1771
1780
|
|
|
1772
|
-
// src/components/ui/Popover.tsx
|
|
1773
|
-
import * as RadixPopover from "@radix-ui/react-popover";
|
|
1774
|
-
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1775
|
-
var Popover = ({ open, onOpenChange, children }) => {
|
|
1776
|
-
return /* @__PURE__ */ jsx9(RadixPopover.Root, { open, onOpenChange, children });
|
|
1777
|
-
};
|
|
1778
|
-
var PopoverTrigger = RadixPopover.Trigger;
|
|
1779
|
-
var PopoverContent = ({
|
|
1780
|
-
children,
|
|
1781
|
-
className,
|
|
1782
|
-
align = "start",
|
|
1783
|
-
side = "bottom",
|
|
1784
|
-
portal = true,
|
|
1785
|
-
collisionPadding = 8
|
|
1786
|
-
}) => {
|
|
1787
|
-
const content = /* @__PURE__ */ jsx9(
|
|
1788
|
-
RadixPopover.Content,
|
|
1789
|
-
{
|
|
1790
|
-
className: `gantt-popover${className ? ` ${className}` : ""}`,
|
|
1791
|
-
align,
|
|
1792
|
-
side,
|
|
1793
|
-
collisionPadding,
|
|
1794
|
-
sideOffset: 4,
|
|
1795
|
-
children
|
|
1796
|
-
}
|
|
1797
|
-
);
|
|
1798
|
-
if (portal) {
|
|
1799
|
-
return /* @__PURE__ */ jsx9(RadixPopover.Portal, { children: content });
|
|
1800
|
-
}
|
|
1801
|
-
return content;
|
|
1802
|
-
};
|
|
1803
|
-
|
|
1804
1781
|
// src/components/ui/DatePicker.tsx
|
|
1805
1782
|
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1806
1783
|
var DatePicker = ({
|
|
@@ -1865,19 +1842,182 @@ var DatePicker = ({
|
|
|
1865
1842
|
};
|
|
1866
1843
|
DatePicker.displayName = "DatePicker";
|
|
1867
1844
|
|
|
1868
|
-
// src/components/TaskList/
|
|
1845
|
+
// src/components/TaskList/DepIcons.tsx
|
|
1869
1846
|
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1847
|
+
var DepIconFS = () => /* @__PURE__ */ jsxs8("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: [
|
|
1848
|
+
/* @__PURE__ */ jsx11("path", { d: "m10 15 5 5 5-5" }),
|
|
1849
|
+
/* @__PURE__ */ jsx11("path", { d: "M4 4h7a4 4 0 0 1 4 4v12" })
|
|
1850
|
+
] });
|
|
1851
|
+
var DepIconSS = () => /* @__PURE__ */ jsxs8("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: [
|
|
1852
|
+
/* @__PURE__ */ jsx11("path", { d: "M3 5v14" }),
|
|
1853
|
+
/* @__PURE__ */ jsx11("path", { d: "M21 12H7" }),
|
|
1854
|
+
/* @__PURE__ */ jsx11("path", { d: "m15 18 6-6-6-6" })
|
|
1855
|
+
] });
|
|
1856
|
+
var DepIconFF = () => /* @__PURE__ */ jsxs8("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: [
|
|
1857
|
+
/* @__PURE__ */ jsx11("path", { d: "M17 12H3" }),
|
|
1858
|
+
/* @__PURE__ */ jsx11("path", { d: "m11 18 6-6-6-6" }),
|
|
1859
|
+
/* @__PURE__ */ jsx11("path", { d: "M21 5v14" })
|
|
1860
|
+
] });
|
|
1861
|
+
var DepIconSF = () => /* @__PURE__ */ jsxs8("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: [
|
|
1862
|
+
/* @__PURE__ */ jsx11("path", { d: "m14 15-5 5-5-5" }),
|
|
1863
|
+
/* @__PURE__ */ jsx11("path", { d: "M20 4h-7a4 4 0 0 0-4 4v12" })
|
|
1864
|
+
] });
|
|
1865
|
+
var LINK_TYPE_ICONS = {
|
|
1866
|
+
FS: DepIconFS,
|
|
1867
|
+
SS: DepIconSS,
|
|
1868
|
+
FF: DepIconFF,
|
|
1869
|
+
SF: DepIconSF
|
|
1870
|
+
};
|
|
1871
|
+
var LINK_TYPE_LABELS = {
|
|
1872
|
+
FS: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435-\u043D\u0430\u0447\u0430\u043B\u043E",
|
|
1873
|
+
SS: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043D\u0430\u0447\u0430\u043B\u043E",
|
|
1874
|
+
FF: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435",
|
|
1875
|
+
SF: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435"
|
|
1876
|
+
};
|
|
1877
|
+
|
|
1878
|
+
// src/components/TaskList/TaskListRow.tsx
|
|
1879
|
+
import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1880
|
+
var TrashIcon = () => /* @__PURE__ */ jsxs9("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: [
|
|
1881
|
+
/* @__PURE__ */ jsx12("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
|
|
1882
|
+
/* @__PURE__ */ jsx12("path", { d: "M3 6h18" }),
|
|
1883
|
+
/* @__PURE__ */ jsx12("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
1884
|
+
] });
|
|
1885
|
+
function formatDepDescription(type, lag) {
|
|
1886
|
+
const effectiveLag = lag ?? 0;
|
|
1887
|
+
if (type === "FS") {
|
|
1888
|
+
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`;
|
|
1889
|
+
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`;
|
|
1890
|
+
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`;
|
|
1891
|
+
}
|
|
1892
|
+
if (type === "FF") {
|
|
1893
|
+
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`;
|
|
1894
|
+
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`;
|
|
1895
|
+
return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F`;
|
|
1896
|
+
}
|
|
1897
|
+
if (type === "SS") {
|
|
1898
|
+
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`;
|
|
1899
|
+
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`;
|
|
1900
|
+
return `\u041D\u0430\u0447\u0430\u0442\u044C \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u043D\u0430\u0447\u0430\u043B\u043E\u043C`;
|
|
1901
|
+
}
|
|
1902
|
+
if (type === "SF") {
|
|
1903
|
+
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`;
|
|
1904
|
+
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`;
|
|
1905
|
+
return `\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430`;
|
|
1906
|
+
}
|
|
1907
|
+
return "";
|
|
1908
|
+
}
|
|
1909
|
+
var DepChip = ({
|
|
1910
|
+
lag,
|
|
1911
|
+
dep,
|
|
1912
|
+
taskId,
|
|
1913
|
+
predecessorName,
|
|
1914
|
+
selectedChip,
|
|
1915
|
+
disableDependencyEditing,
|
|
1916
|
+
onChipSelect,
|
|
1917
|
+
onRowClick,
|
|
1918
|
+
onScrollToTask,
|
|
1919
|
+
onRemoveDependency,
|
|
1920
|
+
onChipSelectClear
|
|
1921
|
+
}) => {
|
|
1922
|
+
const isSelected = selectedChip?.successorId === taskId && selectedChip?.predecessorId === dep.taskId && selectedChip?.linkType === dep.type;
|
|
1923
|
+
const handleClick = (e) => {
|
|
1924
|
+
e.stopPropagation();
|
|
1925
|
+
if (disableDependencyEditing) return;
|
|
1926
|
+
onChipSelect?.(isSelected ? null : { successorId: taskId, predecessorId: dep.taskId, linkType: dep.type });
|
|
1927
|
+
if (!isSelected) {
|
|
1928
|
+
onRowClick?.(taskId);
|
|
1929
|
+
onScrollToTask?.(taskId);
|
|
1930
|
+
}
|
|
1931
|
+
};
|
|
1932
|
+
const handleTrashClick = (e) => {
|
|
1933
|
+
e.stopPropagation();
|
|
1934
|
+
onRemoveDependency?.(taskId, dep.taskId, dep.type);
|
|
1935
|
+
onChipSelectClear();
|
|
1936
|
+
};
|
|
1937
|
+
const Icon = LINK_TYPE_ICONS[dep.type];
|
|
1938
|
+
const depPrefix = formatDepDescription(dep.type, lag);
|
|
1939
|
+
const depName = predecessorName ?? dep.taskId;
|
|
1940
|
+
return /* @__PURE__ */ jsxs9(Popover, { open: isSelected, onOpenChange: (open) => {
|
|
1941
|
+
if (!open) onChipSelectClear();
|
|
1942
|
+
}, children: [
|
|
1943
|
+
/* @__PURE__ */ jsxs9("span", { className: "gantt-tl-dep-chip-wrapper", children: [
|
|
1944
|
+
/* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx12(
|
|
1945
|
+
"span",
|
|
1946
|
+
{
|
|
1947
|
+
className: `gantt-tl-dep-chip${isSelected ? " gantt-tl-dep-chip-selected" : ""}`,
|
|
1948
|
+
onClick: handleClick,
|
|
1949
|
+
children: /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
1950
|
+
/* @__PURE__ */ jsx12(Icon, {}),
|
|
1951
|
+
lag != null && lag !== 0 ? lag > 0 ? `+${lag}` : `${lag}` : ""
|
|
1952
|
+
] })
|
|
1953
|
+
}
|
|
1954
|
+
) }),
|
|
1955
|
+
!disableDependencyEditing && /* @__PURE__ */ jsx12(
|
|
1956
|
+
"button",
|
|
1957
|
+
{
|
|
1958
|
+
type: "button",
|
|
1959
|
+
className: "gantt-tl-dep-chip-trash",
|
|
1960
|
+
"aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
1961
|
+
onClick: handleTrashClick,
|
|
1962
|
+
children: /* @__PURE__ */ jsx12(TrashIcon, {})
|
|
1963
|
+
}
|
|
1964
|
+
)
|
|
1965
|
+
] }),
|
|
1966
|
+
/* @__PURE__ */ jsxs9(PopoverContent, { portal: true, side: "bottom", align: "start", className: "gantt-tl-dep-info-popover", children: [
|
|
1967
|
+
/* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-info-prefix", children: depPrefix }),
|
|
1968
|
+
/* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-info-name", children: depName })
|
|
1969
|
+
] })
|
|
1970
|
+
] });
|
|
1971
|
+
};
|
|
1870
1972
|
var toISODate = (value) => {
|
|
1871
1973
|
if (value instanceof Date) return value.toISOString().split("T")[0];
|
|
1872
1974
|
if (typeof value === "string" && value.includes("T")) return value.split("T")[0];
|
|
1873
1975
|
return value;
|
|
1874
1976
|
};
|
|
1875
1977
|
var TaskListRow = React9.memo(
|
|
1876
|
-
({
|
|
1978
|
+
({
|
|
1979
|
+
task,
|
|
1980
|
+
rowIndex,
|
|
1981
|
+
rowHeight,
|
|
1982
|
+
onTaskChange,
|
|
1983
|
+
selectedTaskId,
|
|
1984
|
+
onRowClick,
|
|
1985
|
+
disableTaskNameEditing = false,
|
|
1986
|
+
disableDependencyEditing = false,
|
|
1987
|
+
allTasks = [],
|
|
1988
|
+
activeLinkType,
|
|
1989
|
+
selectingPredecessorFor,
|
|
1990
|
+
onSetSelectingPredecessorFor,
|
|
1991
|
+
onAddDependency,
|
|
1992
|
+
onRemoveDependency,
|
|
1993
|
+
selectedChip,
|
|
1994
|
+
onChipSelect,
|
|
1995
|
+
onScrollToTask
|
|
1996
|
+
}) => {
|
|
1877
1997
|
const [editingName, setEditingName] = useState4(false);
|
|
1878
1998
|
const [nameValue, setNameValue] = useState4("");
|
|
1879
1999
|
const nameInputRef = useRef3(null);
|
|
2000
|
+
const [overflowOpen, setOverflowOpen] = useState4(false);
|
|
1880
2001
|
const isSelected = selectedTaskId === task.id;
|
|
2002
|
+
const isPicking = selectingPredecessorFor != null;
|
|
2003
|
+
const isSourceRow = isPicking && selectingPredecessorFor === task.id;
|
|
2004
|
+
const chips = useMemo7(() => {
|
|
2005
|
+
const succStart = new Date(task.startDate);
|
|
2006
|
+
const succEnd = new Date(task.endDate);
|
|
2007
|
+
const taskById = new Map((allTasks ?? []).map((t) => [t.id, t]));
|
|
2008
|
+
return (task.dependencies ?? []).map((dep) => {
|
|
2009
|
+
const pred = taskById.get(dep.taskId);
|
|
2010
|
+
const lag = pred ? computeLagFromDates(
|
|
2011
|
+
dep.type,
|
|
2012
|
+
new Date(pred.startDate),
|
|
2013
|
+
new Date(pred.endDate),
|
|
2014
|
+
succStart,
|
|
2015
|
+
succEnd
|
|
2016
|
+
) : dep.lag ?? 0;
|
|
2017
|
+
return { dep, lag, predecessorName: pred?.name ?? dep.taskId };
|
|
2018
|
+
});
|
|
2019
|
+
}, [task.dependencies, task.startDate, task.endDate, allTasks]);
|
|
2020
|
+
const linkWord = chips.length <= 4 ? "\u0441\u0432\u044F\u0437\u0438" : "\u0441\u0432\u044F\u0437\u0435\u0439";
|
|
1881
2021
|
useEffect3(() => {
|
|
1882
2022
|
if (editingName && nameInputRef.current) {
|
|
1883
2023
|
nameInputRef.current.focus();
|
|
@@ -1923,18 +2063,60 @@ var TaskListRow = React9.memo(
|
|
|
1923
2063
|
const handleRowClickInternal = useCallback4(() => {
|
|
1924
2064
|
onRowClick?.(task.id);
|
|
1925
2065
|
}, [task.id, onRowClick]);
|
|
2066
|
+
const handleNumberClick = useCallback4((e) => {
|
|
2067
|
+
e.stopPropagation();
|
|
2068
|
+
onRowClick?.(task.id);
|
|
2069
|
+
onScrollToTask?.(task.id);
|
|
2070
|
+
}, [task.id, onRowClick, onScrollToTask]);
|
|
2071
|
+
const handleAddClick = useCallback4((e) => {
|
|
2072
|
+
e.stopPropagation();
|
|
2073
|
+
onSetSelectingPredecessorFor?.(task.id);
|
|
2074
|
+
}, [task.id, onSetSelectingPredecessorFor]);
|
|
2075
|
+
const handlePredecessorPick = useCallback4((e) => {
|
|
2076
|
+
e.stopPropagation();
|
|
2077
|
+
if (!isPicking || isSourceRow) return;
|
|
2078
|
+
if (!selectingPredecessorFor || !activeLinkType) return;
|
|
2079
|
+
onAddDependency?.(task.id, selectingPredecessorFor, activeLinkType);
|
|
2080
|
+
}, [isPicking, isSourceRow, selectingPredecessorFor, task.id, activeLinkType, onAddDependency]);
|
|
2081
|
+
const isSelectedPredecessor = selectedChip != null && selectedChip.predecessorId === task.id;
|
|
2082
|
+
const handleDeleteSelected = useCallback4((e) => {
|
|
2083
|
+
e.stopPropagation();
|
|
2084
|
+
if (!selectedChip) return;
|
|
2085
|
+
onRemoveDependency?.(selectedChip.successorId, selectedChip.predecessorId, selectedChip.linkType);
|
|
2086
|
+
onChipSelect?.(null);
|
|
2087
|
+
}, [selectedChip, onRemoveDependency, onChipSelect]);
|
|
1926
2088
|
const startDateISO = toISODate(task.startDate);
|
|
1927
2089
|
const endDateISO = toISODate(task.endDate);
|
|
1928
|
-
return /* @__PURE__ */
|
|
2090
|
+
return /* @__PURE__ */ jsxs9(
|
|
1929
2091
|
"div",
|
|
1930
2092
|
{
|
|
1931
|
-
className:
|
|
2093
|
+
className: [
|
|
2094
|
+
"gantt-tl-row",
|
|
2095
|
+
isSelected ? "gantt-tl-row-selected" : "",
|
|
2096
|
+
isPicking && !isSourceRow ? "gantt-tl-row-picking" : "",
|
|
2097
|
+
isSourceRow ? "gantt-tl-row-picking-self" : ""
|
|
2098
|
+
].filter(Boolean).join(" "),
|
|
1932
2099
|
style: { minHeight: `${rowHeight}px` },
|
|
1933
2100
|
onClick: handleRowClickInternal,
|
|
1934
2101
|
children: [
|
|
1935
|
-
/* @__PURE__ */
|
|
1936
|
-
|
|
1937
|
-
|
|
2102
|
+
/* @__PURE__ */ jsxs9(
|
|
2103
|
+
"div",
|
|
2104
|
+
{
|
|
2105
|
+
className: "gantt-tl-cell gantt-tl-cell-number",
|
|
2106
|
+
onClick: handleNumberClick,
|
|
2107
|
+
title: "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043A \u0440\u0430\u0431\u043E\u0442\u0435",
|
|
2108
|
+
children: [
|
|
2109
|
+
/* @__PURE__ */ jsx12("span", { className: "gantt-tl-num-label", children: rowIndex + 1 }),
|
|
2110
|
+
/* @__PURE__ */ jsxs9("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: [
|
|
2111
|
+
/* @__PURE__ */ jsx12("path", { d: "M17 12H3" }),
|
|
2112
|
+
/* @__PURE__ */ jsx12("path", { d: "m11 18 6-6-6-6" }),
|
|
2113
|
+
/* @__PURE__ */ jsx12("path", { d: "M21 5v14" })
|
|
2114
|
+
] })
|
|
2115
|
+
]
|
|
2116
|
+
}
|
|
2117
|
+
),
|
|
2118
|
+
/* @__PURE__ */ jsxs9("div", { className: "gantt-tl-cell gantt-tl-cell-name", children: [
|
|
2119
|
+
editingName && /* @__PURE__ */ jsx12(
|
|
1938
2120
|
Input,
|
|
1939
2121
|
{
|
|
1940
2122
|
ref: nameInputRef,
|
|
@@ -1947,7 +2129,7 @@ var TaskListRow = React9.memo(
|
|
|
1947
2129
|
onClick: (e) => e.stopPropagation()
|
|
1948
2130
|
}
|
|
1949
2131
|
),
|
|
1950
|
-
/* @__PURE__ */
|
|
2132
|
+
/* @__PURE__ */ jsx12(
|
|
1951
2133
|
"button",
|
|
1952
2134
|
{
|
|
1953
2135
|
type: "button",
|
|
@@ -1958,7 +2140,7 @@ var TaskListRow = React9.memo(
|
|
|
1958
2140
|
}
|
|
1959
2141
|
)
|
|
1960
2142
|
] }),
|
|
1961
|
-
/* @__PURE__ */
|
|
2143
|
+
/* @__PURE__ */ jsx12("div", { className: "gantt-tl-cell gantt-tl-cell-date", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx12(
|
|
1962
2144
|
DatePicker,
|
|
1963
2145
|
{
|
|
1964
2146
|
value: startDateISO,
|
|
@@ -1968,7 +2150,7 @@ var TaskListRow = React9.memo(
|
|
|
1968
2150
|
disabled: task.locked
|
|
1969
2151
|
}
|
|
1970
2152
|
) }),
|
|
1971
|
-
/* @__PURE__ */
|
|
2153
|
+
/* @__PURE__ */ jsx12("div", { className: "gantt-tl-cell gantt-tl-cell-date", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx12(
|
|
1972
2154
|
DatePicker,
|
|
1973
2155
|
{
|
|
1974
2156
|
value: endDateISO,
|
|
@@ -1977,7 +2159,97 @@ var TaskListRow = React9.memo(
|
|
|
1977
2159
|
portal: true,
|
|
1978
2160
|
disabled: task.locked
|
|
1979
2161
|
}
|
|
1980
|
-
) })
|
|
2162
|
+
) }),
|
|
2163
|
+
/* @__PURE__ */ jsx12(
|
|
2164
|
+
"div",
|
|
2165
|
+
{
|
|
2166
|
+
className: "gantt-tl-cell gantt-tl-cell-deps",
|
|
2167
|
+
onClick: isPicking && !isSourceRow ? handlePredecessorPick : void 0,
|
|
2168
|
+
children: isSourceRow ? /* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-source-hint", children: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0443" }) : isSelectedPredecessor && !disableDependencyEditing ? (
|
|
2169
|
+
/* Full-replacement: "Зависит от [name]" → hover → "Удалить" */
|
|
2170
|
+
/* @__PURE__ */ jsxs9(
|
|
2171
|
+
"button",
|
|
2172
|
+
{
|
|
2173
|
+
type: "button",
|
|
2174
|
+
className: "gantt-tl-dep-delete-label",
|
|
2175
|
+
onClick: handleDeleteSelected,
|
|
2176
|
+
"aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2177
|
+
children: [
|
|
2178
|
+
/* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-delete-label-default", children: "\u0421\u0432\u044F\u0437\u0430\u043D\u043E \u0441" }),
|
|
2179
|
+
/* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-delete-label-hover", children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C" })
|
|
2180
|
+
]
|
|
2181
|
+
}
|
|
2182
|
+
)
|
|
2183
|
+
) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
2184
|
+
chips.length >= 2 ? (
|
|
2185
|
+
/* 2+ deps — show only "N связей" summary chip that opens a popover */
|
|
2186
|
+
/* @__PURE__ */ jsxs9(Popover, { open: overflowOpen, onOpenChange: setOverflowOpen, children: [
|
|
2187
|
+
/* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
|
|
2188
|
+
"button",
|
|
2189
|
+
{
|
|
2190
|
+
type: "button",
|
|
2191
|
+
className: "gantt-tl-dep-summary-chip",
|
|
2192
|
+
onClick: (e) => {
|
|
2193
|
+
e.stopPropagation();
|
|
2194
|
+
setOverflowOpen((v) => !v);
|
|
2195
|
+
},
|
|
2196
|
+
children: [
|
|
2197
|
+
chips.length,
|
|
2198
|
+
" ",
|
|
2199
|
+
linkWord
|
|
2200
|
+
]
|
|
2201
|
+
}
|
|
2202
|
+
) }),
|
|
2203
|
+
/* @__PURE__ */ jsx12(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ jsx12("div", { className: "gantt-tl-dep-overflow-list", onClick: (e) => e.stopPropagation(), children: chips.map(({ dep, lag, predecessorName }) => /* @__PURE__ */ jsx12(
|
|
2204
|
+
DepChip,
|
|
2205
|
+
{
|
|
2206
|
+
lag,
|
|
2207
|
+
dep,
|
|
2208
|
+
taskId: task.id,
|
|
2209
|
+
predecessorName,
|
|
2210
|
+
selectedChip,
|
|
2211
|
+
disableDependencyEditing,
|
|
2212
|
+
onChipSelect,
|
|
2213
|
+
onRowClick,
|
|
2214
|
+
onScrollToTask,
|
|
2215
|
+
onRemoveDependency,
|
|
2216
|
+
onChipSelectClear: () => onChipSelect?.(null)
|
|
2217
|
+
},
|
|
2218
|
+
`${dep.taskId}-${dep.type}`
|
|
2219
|
+
)) }) })
|
|
2220
|
+
] })
|
|
2221
|
+
) : chips.length === 1 ? (
|
|
2222
|
+
/* Single chip — unified DepChip */
|
|
2223
|
+
/* @__PURE__ */ jsx12(
|
|
2224
|
+
DepChip,
|
|
2225
|
+
{
|
|
2226
|
+
lag: chips[0].lag,
|
|
2227
|
+
dep: chips[0].dep,
|
|
2228
|
+
taskId: task.id,
|
|
2229
|
+
predecessorName: chips[0].predecessorName,
|
|
2230
|
+
selectedChip,
|
|
2231
|
+
disableDependencyEditing,
|
|
2232
|
+
onChipSelect,
|
|
2233
|
+
onRowClick,
|
|
2234
|
+
onScrollToTask,
|
|
2235
|
+
onRemoveDependency,
|
|
2236
|
+
onChipSelectClear: () => onChipSelect?.(null)
|
|
2237
|
+
}
|
|
2238
|
+
)
|
|
2239
|
+
) : null,
|
|
2240
|
+
!disableDependencyEditing && !isPicking && /* @__PURE__ */ jsx12(
|
|
2241
|
+
"button",
|
|
2242
|
+
{
|
|
2243
|
+
type: "button",
|
|
2244
|
+
className: "gantt-tl-dep-add",
|
|
2245
|
+
onClick: handleAddClick,
|
|
2246
|
+
"aria-label": "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
|
|
2247
|
+
children: "+"
|
|
2248
|
+
}
|
|
2249
|
+
)
|
|
2250
|
+
] })
|
|
2251
|
+
}
|
|
2252
|
+
)
|
|
1981
2253
|
]
|
|
1982
2254
|
}
|
|
1983
2255
|
);
|
|
@@ -1986,7 +2258,8 @@ var TaskListRow = React9.memo(
|
|
|
1986
2258
|
TaskListRow.displayName = "TaskListRow";
|
|
1987
2259
|
|
|
1988
2260
|
// src/components/TaskList/TaskList.tsx
|
|
1989
|
-
import { jsx as
|
|
2261
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2262
|
+
var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
|
|
1990
2263
|
var TaskList = ({
|
|
1991
2264
|
tasks,
|
|
1992
2265
|
rowHeight,
|
|
@@ -1996,28 +2269,156 @@ var TaskList = ({
|
|
|
1996
2269
|
selectedTaskId,
|
|
1997
2270
|
onTaskSelect,
|
|
1998
2271
|
show = true,
|
|
1999
|
-
disableTaskNameEditing = false
|
|
2272
|
+
disableTaskNameEditing = false,
|
|
2273
|
+
disableDependencyEditing = false,
|
|
2274
|
+
onScrollToTask,
|
|
2275
|
+
onSelectedChipChange
|
|
2000
2276
|
}) => {
|
|
2001
|
-
const totalHeight =
|
|
2277
|
+
const totalHeight = useMemo8(
|
|
2002
2278
|
() => tasks.length * rowHeight,
|
|
2003
2279
|
[tasks.length, rowHeight]
|
|
2004
2280
|
);
|
|
2005
2281
|
const handleRowClick = useCallback5((taskId) => {
|
|
2006
2282
|
onTaskSelect?.(taskId);
|
|
2007
2283
|
}, [onTaskSelect]);
|
|
2008
|
-
|
|
2284
|
+
const [activeLinkType, setActiveLinkType] = useState5("FS");
|
|
2285
|
+
const [selectingPredecessorFor, setSelectingPredecessorFor] = useState5(null);
|
|
2286
|
+
const [typeMenuOpen, setTypeMenuOpen] = useState5(false);
|
|
2287
|
+
const [cycleError, setCycleError] = useState5(false);
|
|
2288
|
+
const overlayRef = useRef4(null);
|
|
2289
|
+
const [selectedChip, setSelectedChip] = useState5(null);
|
|
2290
|
+
const handleChipSelect = useCallback5((chip) => {
|
|
2291
|
+
setSelectedChip(chip);
|
|
2292
|
+
onSelectedChipChange?.(chip);
|
|
2293
|
+
}, [onSelectedChipChange]);
|
|
2294
|
+
useEffect4(() => {
|
|
2295
|
+
if (!selectingPredecessorFor && !selectedChip) return;
|
|
2296
|
+
const handleKeyDown = (e) => {
|
|
2297
|
+
if (e.key === "Escape") {
|
|
2298
|
+
setSelectingPredecessorFor(null);
|
|
2299
|
+
setSelectedChip(null);
|
|
2300
|
+
onSelectedChipChange?.(null);
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
const handleMouseDown = (e) => {
|
|
2304
|
+
const target = e.target;
|
|
2305
|
+
if (overlayRef.current?.contains(target)) return;
|
|
2306
|
+
if (target.closest?.(".gantt-popover")) return;
|
|
2307
|
+
setSelectingPredecessorFor(null);
|
|
2308
|
+
setSelectedChip(null);
|
|
2309
|
+
onSelectedChipChange?.(null);
|
|
2310
|
+
};
|
|
2311
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2312
|
+
document.addEventListener("mousedown", handleMouseDown, true);
|
|
2313
|
+
return () => {
|
|
2314
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
2315
|
+
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
2316
|
+
};
|
|
2317
|
+
}, [selectingPredecessorFor, selectedChip, onSelectedChipChange]);
|
|
2318
|
+
const handleAddDependency = useCallback5((successorTaskId, predecessorTaskId, linkType) => {
|
|
2319
|
+
if (successorTaskId === predecessorTaskId) return;
|
|
2320
|
+
const successor = tasks.find((t) => t.id === successorTaskId);
|
|
2321
|
+
if (!successor) return;
|
|
2322
|
+
const alreadyExists = (successor.dependencies ?? []).some(
|
|
2323
|
+
(d) => d.taskId === predecessorTaskId && d.type === linkType
|
|
2324
|
+
);
|
|
2325
|
+
if (alreadyExists) {
|
|
2326
|
+
setSelectingPredecessorFor(null);
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2329
|
+
const newDep = { taskId: predecessorTaskId, type: linkType, lag: 0 };
|
|
2330
|
+
const hypothetical = tasks.map(
|
|
2331
|
+
(t) => t.id === successorTaskId ? { ...t, dependencies: [...t.dependencies ?? [], newDep] } : t
|
|
2332
|
+
);
|
|
2333
|
+
const validation = validateDependencies(hypothetical);
|
|
2334
|
+
if (!validation.isValid) {
|
|
2335
|
+
setCycleError(true);
|
|
2336
|
+
setTimeout(() => setCycleError(false), 3e3);
|
|
2337
|
+
return;
|
|
2338
|
+
}
|
|
2339
|
+
const updatedTask = hypothetical.find((t) => t.id === successorTaskId);
|
|
2340
|
+
const predecessor = tasks.find((t) => t.id === predecessorTaskId);
|
|
2341
|
+
if (predecessor) {
|
|
2342
|
+
const predStart = new Date(predecessor.startDate);
|
|
2343
|
+
const predEnd = new Date(predecessor.endDate);
|
|
2344
|
+
const constraintDate = calculateSuccessorDate(predStart, predEnd, linkType, 0);
|
|
2345
|
+
const origSuccessor = tasks.find((t) => t.id === successorTaskId);
|
|
2346
|
+
const durationMs = new Date(origSuccessor.endDate).getTime() - new Date(origSuccessor.startDate).getTime();
|
|
2347
|
+
let newStart;
|
|
2348
|
+
let newEnd;
|
|
2349
|
+
if (linkType === "FS" || linkType === "SS") {
|
|
2350
|
+
newStart = constraintDate;
|
|
2351
|
+
newEnd = new Date(constraintDate.getTime() + durationMs);
|
|
2352
|
+
} else {
|
|
2353
|
+
newEnd = constraintDate;
|
|
2354
|
+
newStart = new Date(constraintDate.getTime() - durationMs);
|
|
2355
|
+
}
|
|
2356
|
+
const snappedTask = {
|
|
2357
|
+
...updatedTask,
|
|
2358
|
+
startDate: newStart.toISOString().split("T")[0],
|
|
2359
|
+
endDate: newEnd.toISOString().split("T")[0]
|
|
2360
|
+
};
|
|
2361
|
+
onTaskChange?.(snappedTask);
|
|
2362
|
+
} else {
|
|
2363
|
+
onTaskChange?.(updatedTask);
|
|
2364
|
+
}
|
|
2365
|
+
setSelectingPredecessorFor(null);
|
|
2366
|
+
}, [tasks, onTaskChange]);
|
|
2367
|
+
const handleRemoveDependency = useCallback5((taskId, predecessorTaskId, linkType) => {
|
|
2368
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
2369
|
+
if (!task) return;
|
|
2370
|
+
const updatedDeps = (task.dependencies ?? []).filter(
|
|
2371
|
+
(d) => !(d.taskId === predecessorTaskId && d.type === linkType)
|
|
2372
|
+
);
|
|
2373
|
+
onTaskChange?.({ ...task, dependencies: updatedDeps });
|
|
2374
|
+
}, [tasks, onTaskChange]);
|
|
2375
|
+
return /* @__PURE__ */ jsx13(
|
|
2009
2376
|
"div",
|
|
2010
2377
|
{
|
|
2378
|
+
ref: overlayRef,
|
|
2011
2379
|
className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}`,
|
|
2012
2380
|
style: { width: `${taskListWidth}px` },
|
|
2013
|
-
children: /* @__PURE__ */
|
|
2014
|
-
/* @__PURE__ */
|
|
2015
|
-
/* @__PURE__ */
|
|
2016
|
-
/* @__PURE__ */
|
|
2017
|
-
/* @__PURE__ */
|
|
2018
|
-
/* @__PURE__ */
|
|
2381
|
+
children: /* @__PURE__ */ jsxs10("div", { className: "gantt-tl-table", children: [
|
|
2382
|
+
/* @__PURE__ */ jsxs10("div", { className: "gantt-tl-header", style: { height: `${headerHeight + 0.5}px` }, children: [
|
|
2383
|
+
/* @__PURE__ */ jsx13("div", { className: "gantt-tl-headerCell gantt-tl-cell-number", children: "\u2116" }),
|
|
2384
|
+
/* @__PURE__ */ jsx13("div", { className: "gantt-tl-headerCell gantt-tl-cell-name", children: "\u0418\u043C\u044F" }),
|
|
2385
|
+
/* @__PURE__ */ jsx13("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
|
|
2386
|
+
/* @__PURE__ */ jsx13("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435" }),
|
|
2387
|
+
/* @__PURE__ */ jsxs10("div", { className: "gantt-tl-headerCell gantt-tl-cell-deps", style: { position: "relative" }, children: [
|
|
2388
|
+
/* @__PURE__ */ jsxs10(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
|
|
2389
|
+
/* @__PURE__ */ jsx13(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs10(
|
|
2390
|
+
"button",
|
|
2391
|
+
{
|
|
2392
|
+
className: "gantt-tl-dep-type-trigger",
|
|
2393
|
+
disabled: disableDependencyEditing,
|
|
2394
|
+
onClick: (e) => e.stopPropagation(),
|
|
2395
|
+
children: [
|
|
2396
|
+
"\u0421\u0432\u044F\u0437\u0438 ",
|
|
2397
|
+
React10.createElement(LINK_TYPE_ICONS[activeLinkType]),
|
|
2398
|
+
" \u25BE"
|
|
2399
|
+
]
|
|
2400
|
+
}
|
|
2401
|
+
) }),
|
|
2402
|
+
/* @__PURE__ */ jsx13(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ jsx13("div", { className: "gantt-tl-dep-type-menu", children: LINK_TYPE_ORDER.map((lt) => /* @__PURE__ */ jsxs10(
|
|
2403
|
+
"button",
|
|
2404
|
+
{
|
|
2405
|
+
className: `gantt-tl-dep-type-option${activeLinkType === lt ? " active" : ""}`,
|
|
2406
|
+
onClick: () => {
|
|
2407
|
+
setActiveLinkType(lt);
|
|
2408
|
+
setTypeMenuOpen(false);
|
|
2409
|
+
},
|
|
2410
|
+
children: [
|
|
2411
|
+
React10.createElement(LINK_TYPE_ICONS[lt]),
|
|
2412
|
+
/* @__PURE__ */ jsx13("span", { children: LINK_TYPE_LABELS[lt] })
|
|
2413
|
+
]
|
|
2414
|
+
},
|
|
2415
|
+
lt
|
|
2416
|
+
)) }) })
|
|
2417
|
+
] }),
|
|
2418
|
+
cycleError && /* @__PURE__ */ jsx13("div", { className: "gantt-tl-dep-error", children: "\u0426\u0438\u043A\u043B \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439!" })
|
|
2419
|
+
] })
|
|
2019
2420
|
] }),
|
|
2020
|
-
/* @__PURE__ */
|
|
2421
|
+
/* @__PURE__ */ jsx13("div", { className: "gantt-tl-body", style: { height: `${totalHeight}px` }, children: tasks.map((task, index) => /* @__PURE__ */ jsx13(
|
|
2021
2422
|
TaskListRow,
|
|
2022
2423
|
{
|
|
2023
2424
|
task,
|
|
@@ -2026,7 +2427,17 @@ var TaskList = ({
|
|
|
2026
2427
|
onTaskChange,
|
|
2027
2428
|
selectedTaskId,
|
|
2028
2429
|
onRowClick: handleRowClick,
|
|
2029
|
-
disableTaskNameEditing
|
|
2430
|
+
disableTaskNameEditing,
|
|
2431
|
+
disableDependencyEditing,
|
|
2432
|
+
allTasks: tasks,
|
|
2433
|
+
activeLinkType,
|
|
2434
|
+
selectingPredecessorFor,
|
|
2435
|
+
onSetSelectingPredecessorFor: setSelectingPredecessorFor,
|
|
2436
|
+
onAddDependency: handleAddDependency,
|
|
2437
|
+
onRemoveDependency: handleRemoveDependency,
|
|
2438
|
+
selectedChip,
|
|
2439
|
+
onChipSelect: handleChipSelect,
|
|
2440
|
+
onScrollToTask
|
|
2030
2441
|
},
|
|
2031
2442
|
task.id
|
|
2032
2443
|
)) })
|
|
@@ -2036,13 +2447,13 @@ var TaskList = ({
|
|
|
2036
2447
|
};
|
|
2037
2448
|
|
|
2038
2449
|
// src/components/GanttChart/GanttChart.tsx
|
|
2039
|
-
import { jsx as
|
|
2450
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2040
2451
|
var GanttChart = forwardRef(({
|
|
2041
2452
|
tasks,
|
|
2042
2453
|
dayWidth = 40,
|
|
2043
2454
|
rowHeight = 40,
|
|
2044
2455
|
headerHeight = 40,
|
|
2045
|
-
containerHeight
|
|
2456
|
+
containerHeight,
|
|
2046
2457
|
onChange,
|
|
2047
2458
|
onValidateDependencies,
|
|
2048
2459
|
enableAutoSchedule,
|
|
@@ -2050,34 +2461,36 @@ var GanttChart = forwardRef(({
|
|
|
2050
2461
|
onCascade,
|
|
2051
2462
|
showTaskList = false,
|
|
2052
2463
|
taskListWidth = 520,
|
|
2053
|
-
disableTaskNameEditing = false
|
|
2464
|
+
disableTaskNameEditing = false,
|
|
2465
|
+
disableDependencyEditing = false
|
|
2054
2466
|
}, ref) => {
|
|
2055
|
-
const scrollContainerRef =
|
|
2056
|
-
const [selectedTaskId, setSelectedTaskId] =
|
|
2057
|
-
const
|
|
2058
|
-
const
|
|
2059
|
-
const [
|
|
2060
|
-
const
|
|
2467
|
+
const scrollContainerRef = useRef5(null);
|
|
2468
|
+
const [selectedTaskId, setSelectedTaskId] = useState6(null);
|
|
2469
|
+
const [selectedChip, setSelectedChip] = useState6(null);
|
|
2470
|
+
const dateRange = useMemo9(() => getMultiMonthDays(tasks), [tasks]);
|
|
2471
|
+
const [validationResult, setValidationResult] = useState6(null);
|
|
2472
|
+
const [cascadeOverrides, setCascadeOverrides] = useState6(/* @__PURE__ */ new Map());
|
|
2473
|
+
const gridWidth = useMemo9(
|
|
2061
2474
|
() => Math.round(dateRange.length * dayWidth),
|
|
2062
2475
|
[dateRange.length, dayWidth]
|
|
2063
2476
|
);
|
|
2064
|
-
const totalGridHeight =
|
|
2477
|
+
const totalGridHeight = useMemo9(
|
|
2065
2478
|
() => tasks.length * rowHeight,
|
|
2066
2479
|
[tasks.length, rowHeight]
|
|
2067
2480
|
);
|
|
2068
|
-
const monthStart =
|
|
2481
|
+
const monthStart = useMemo9(() => {
|
|
2069
2482
|
if (dateRange.length === 0) {
|
|
2070
2483
|
return new Date(Date.UTC((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth(), 1));
|
|
2071
2484
|
}
|
|
2072
2485
|
const firstDay = dateRange[0];
|
|
2073
2486
|
return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
|
|
2074
2487
|
}, [dateRange]);
|
|
2075
|
-
const todayInRange =
|
|
2488
|
+
const todayInRange = useMemo9(() => {
|
|
2076
2489
|
const now = /* @__PURE__ */ new Date();
|
|
2077
2490
|
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
2078
2491
|
return dateRange.some((day) => day.getTime() === today.getTime());
|
|
2079
2492
|
}, [dateRange]);
|
|
2080
|
-
|
|
2493
|
+
useEffect5(() => {
|
|
2081
2494
|
const container = scrollContainerRef.current;
|
|
2082
2495
|
if (!container || dateRange.length === 0) return;
|
|
2083
2496
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -2101,16 +2514,34 @@ var GanttChart = forwardRef(({
|
|
|
2101
2514
|
const scrollLeft = Math.round(todayOffset - containerWidth / 2 + dayWidth / 2);
|
|
2102
2515
|
container.scrollLeft = Math.max(0, scrollLeft);
|
|
2103
2516
|
}, [dateRange, dayWidth]);
|
|
2517
|
+
const scrollToTask = useCallback6((taskId) => {
|
|
2518
|
+
const container = scrollContainerRef.current;
|
|
2519
|
+
if (!container || dateRange.length === 0) return;
|
|
2520
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
2521
|
+
if (!task) return;
|
|
2522
|
+
const taskStart = new Date(task.startDate);
|
|
2523
|
+
const taskStartUTC = new Date(Date.UTC(
|
|
2524
|
+
taskStart.getUTCFullYear(),
|
|
2525
|
+
taskStart.getUTCMonth(),
|
|
2526
|
+
taskStart.getUTCDate()
|
|
2527
|
+
));
|
|
2528
|
+
const taskIndex = dateRange.findIndex((day) => day.getTime() === taskStartUTC.getTime());
|
|
2529
|
+
if (taskIndex === -1) return;
|
|
2530
|
+
const taskOffset = taskIndex * dayWidth;
|
|
2531
|
+
const scrollLeft = Math.round(taskOffset - dayWidth * 2);
|
|
2532
|
+
container.scrollLeft = Math.max(0, scrollLeft);
|
|
2533
|
+
}, [tasks, dateRange, dayWidth]);
|
|
2104
2534
|
useImperativeHandle(
|
|
2105
2535
|
ref,
|
|
2106
2536
|
() => ({
|
|
2107
|
-
scrollToToday
|
|
2537
|
+
scrollToToday,
|
|
2538
|
+
scrollToTask
|
|
2108
2539
|
}),
|
|
2109
|
-
[scrollToToday]
|
|
2540
|
+
[scrollToToday, scrollToTask]
|
|
2110
2541
|
);
|
|
2111
|
-
const [dragGuideLines, setDragGuideLines] =
|
|
2112
|
-
const [draggedTaskOverride, setDraggedTaskOverride] =
|
|
2113
|
-
|
|
2542
|
+
const [dragGuideLines, setDragGuideLines] = useState6(null);
|
|
2543
|
+
const [draggedTaskOverride, setDraggedTaskOverride] = useState6(null);
|
|
2544
|
+
useEffect5(() => {
|
|
2114
2545
|
const result = validateDependencies(tasks);
|
|
2115
2546
|
setValidationResult(result);
|
|
2116
2547
|
onValidateDependencies?.(result);
|
|
@@ -2139,7 +2570,7 @@ var GanttChart = forwardRef(({
|
|
|
2139
2570
|
});
|
|
2140
2571
|
onCascade?.(allCascaded);
|
|
2141
2572
|
}, [tasks, onChange, disableConstraints, onCascade]);
|
|
2142
|
-
const dependencyOverrides =
|
|
2573
|
+
const dependencyOverrides = useMemo9(() => {
|
|
2143
2574
|
const map = new Map(cascadeOverrides);
|
|
2144
2575
|
if (draggedTaskOverride) {
|
|
2145
2576
|
map.set(draggedTaskOverride.taskId, {
|
|
@@ -2162,7 +2593,7 @@ var GanttChart = forwardRef(({
|
|
|
2162
2593
|
const handleTaskSelect = useCallback6((taskId) => {
|
|
2163
2594
|
setSelectedTaskId(taskId);
|
|
2164
2595
|
}, []);
|
|
2165
|
-
const panStateRef =
|
|
2596
|
+
const panStateRef = useRef5(null);
|
|
2166
2597
|
const handlePanStart = useCallback6((e) => {
|
|
2167
2598
|
if (e.button !== 0) return;
|
|
2168
2599
|
const target = e.target;
|
|
@@ -2184,7 +2615,7 @@ var GanttChart = forwardRef(({
|
|
|
2184
2615
|
container.style.cursor = "grabbing";
|
|
2185
2616
|
e.preventDefault();
|
|
2186
2617
|
}, []);
|
|
2187
|
-
|
|
2618
|
+
useEffect5(() => {
|
|
2188
2619
|
const handlePanMove = (e) => {
|
|
2189
2620
|
const pan = panStateRef.current;
|
|
2190
2621
|
if (!pan?.active) return;
|
|
@@ -2206,15 +2637,15 @@ var GanttChart = forwardRef(({
|
|
|
2206
2637
|
window.removeEventListener("mouseup", handlePanEnd);
|
|
2207
2638
|
};
|
|
2208
2639
|
}, []);
|
|
2209
|
-
return /* @__PURE__ */
|
|
2640
|
+
return /* @__PURE__ */ jsx14("div", { className: "gantt-container", children: /* @__PURE__ */ jsx14(
|
|
2210
2641
|
"div",
|
|
2211
2642
|
{
|
|
2212
2643
|
ref: scrollContainerRef,
|
|
2213
2644
|
className: "gantt-scrollContainer",
|
|
2214
|
-
style: { height:
|
|
2645
|
+
style: { height: containerHeight ?? "auto", cursor: "grab" },
|
|
2215
2646
|
onMouseDown: handlePanStart,
|
|
2216
|
-
children: /* @__PURE__ */
|
|
2217
|
-
/* @__PURE__ */
|
|
2647
|
+
children: /* @__PURE__ */ jsxs11("div", { className: "gantt-scrollContent", children: [
|
|
2648
|
+
/* @__PURE__ */ jsx14(
|
|
2218
2649
|
TaskList,
|
|
2219
2650
|
{
|
|
2220
2651
|
tasks,
|
|
@@ -2225,11 +2656,14 @@ var GanttChart = forwardRef(({
|
|
|
2225
2656
|
selectedTaskId: selectedTaskId ?? void 0,
|
|
2226
2657
|
onTaskSelect: handleTaskSelect,
|
|
2227
2658
|
show: showTaskList,
|
|
2228
|
-
disableTaskNameEditing
|
|
2659
|
+
disableTaskNameEditing,
|
|
2660
|
+
disableDependencyEditing,
|
|
2661
|
+
onScrollToTask: scrollToTask,
|
|
2662
|
+
onSelectedChipChange: setSelectedChip
|
|
2229
2663
|
}
|
|
2230
2664
|
),
|
|
2231
|
-
/* @__PURE__ */
|
|
2232
|
-
/* @__PURE__ */
|
|
2665
|
+
/* @__PURE__ */ jsxs11("div", { style: { minWidth: `${gridWidth}px`, flex: 1 }, children: [
|
|
2666
|
+
/* @__PURE__ */ jsx14("div", { className: "gantt-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ jsx14(
|
|
2233
2667
|
TimeScaleHeader_default,
|
|
2234
2668
|
{
|
|
2235
2669
|
days: dateRange,
|
|
@@ -2237,7 +2671,7 @@ var GanttChart = forwardRef(({
|
|
|
2237
2671
|
headerHeight
|
|
2238
2672
|
}
|
|
2239
2673
|
) }),
|
|
2240
|
-
/* @__PURE__ */
|
|
2674
|
+
/* @__PURE__ */ jsxs11(
|
|
2241
2675
|
"div",
|
|
2242
2676
|
{
|
|
2243
2677
|
className: "gantt-taskArea",
|
|
@@ -2246,7 +2680,7 @@ var GanttChart = forwardRef(({
|
|
|
2246
2680
|
width: `${gridWidth}px`
|
|
2247
2681
|
},
|
|
2248
2682
|
children: [
|
|
2249
|
-
/* @__PURE__ */
|
|
2683
|
+
/* @__PURE__ */ jsx14(
|
|
2250
2684
|
GridBackground_default,
|
|
2251
2685
|
{
|
|
2252
2686
|
dateRange,
|
|
@@ -2254,8 +2688,8 @@ var GanttChart = forwardRef(({
|
|
|
2254
2688
|
totalHeight: totalGridHeight
|
|
2255
2689
|
}
|
|
2256
2690
|
),
|
|
2257
|
-
todayInRange && /* @__PURE__ */
|
|
2258
|
-
/* @__PURE__ */
|
|
2691
|
+
todayInRange && /* @__PURE__ */ jsx14(TodayIndicator_default, { monthStart, dayWidth }),
|
|
2692
|
+
/* @__PURE__ */ jsx14(
|
|
2259
2693
|
DependencyLines_default,
|
|
2260
2694
|
{
|
|
2261
2695
|
tasks,
|
|
@@ -2263,10 +2697,11 @@ var GanttChart = forwardRef(({
|
|
|
2263
2697
|
dayWidth,
|
|
2264
2698
|
rowHeight,
|
|
2265
2699
|
gridWidth,
|
|
2266
|
-
dragOverrides: dependencyOverrides
|
|
2700
|
+
dragOverrides: dependencyOverrides,
|
|
2701
|
+
selectedDep: selectedChip
|
|
2267
2702
|
}
|
|
2268
2703
|
),
|
|
2269
|
-
dragGuideLines && /* @__PURE__ */
|
|
2704
|
+
dragGuideLines && /* @__PURE__ */ jsx14(
|
|
2270
2705
|
DragGuideLines_default,
|
|
2271
2706
|
{
|
|
2272
2707
|
isDragging: dragGuideLines.isDragging,
|
|
@@ -2276,7 +2711,7 @@ var GanttChart = forwardRef(({
|
|
|
2276
2711
|
totalHeight: totalGridHeight
|
|
2277
2712
|
}
|
|
2278
2713
|
),
|
|
2279
|
-
tasks.map((task, index) => /* @__PURE__ */
|
|
2714
|
+
tasks.map((task, index) => /* @__PURE__ */ jsx14(
|
|
2280
2715
|
TaskRow_default,
|
|
2281
2716
|
{
|
|
2282
2717
|
task,
|
|
@@ -2315,7 +2750,7 @@ GanttChart.displayName = "GanttChart";
|
|
|
2315
2750
|
|
|
2316
2751
|
// src/components/ui/Button.tsx
|
|
2317
2752
|
import React12 from "react";
|
|
2318
|
-
import { jsx as
|
|
2753
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2319
2754
|
var Button = React12.forwardRef(
|
|
2320
2755
|
({ className, variant = "default", size = "default", children, ...props }, ref) => {
|
|
2321
2756
|
const classes = [
|
|
@@ -2324,7 +2759,7 @@ var Button = React12.forwardRef(
|
|
|
2324
2759
|
size !== "default" ? `gantt-btn-${size}` : "",
|
|
2325
2760
|
className || ""
|
|
2326
2761
|
].filter(Boolean).join(" ");
|
|
2327
|
-
return /* @__PURE__ */
|
|
2762
|
+
return /* @__PURE__ */ jsx15("button", { ref, className: classes, ...props, children });
|
|
2328
2763
|
}
|
|
2329
2764
|
);
|
|
2330
2765
|
Button.displayName = "Button";
|
|
@@ -2353,6 +2788,7 @@ export {
|
|
|
2353
2788
|
calculateTaskBar,
|
|
2354
2789
|
calculateWeekendBlocks,
|
|
2355
2790
|
cascadeByLinks,
|
|
2791
|
+
computeLagFromDates,
|
|
2356
2792
|
detectCycles,
|
|
2357
2793
|
detectEdgeZone,
|
|
2358
2794
|
formatDateLabel,
|