gantt-lib 0.112.0 → 0.113.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 +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +629 -337
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +629 -337
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +71 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -699,10 +699,10 @@ function buildTaskRangeFromStart(startDate, duration, businessDays = false, week
|
|
|
699
699
|
end: parseDateOnly(addBusinessDays(normalizedStart, duration, weekendPredicate))
|
|
700
700
|
};
|
|
701
701
|
}
|
|
702
|
-
const
|
|
702
|
+
const DAY_MS6 = 24 * 60 * 60 * 1e3;
|
|
703
703
|
return {
|
|
704
704
|
start: normalizedStart,
|
|
705
|
-
end: new Date(normalizedStart.getTime() + (Math.max(1, duration) - 1) *
|
|
705
|
+
end: new Date(normalizedStart.getTime() + (Math.max(1, duration) - 1) * DAY_MS6)
|
|
706
706
|
};
|
|
707
707
|
}
|
|
708
708
|
function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendPredicate, snapDirection = -1) {
|
|
@@ -713,9 +713,9 @@ function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendP
|
|
|
713
713
|
end: normalizedEnd
|
|
714
714
|
};
|
|
715
715
|
}
|
|
716
|
-
const
|
|
716
|
+
const DAY_MS6 = 24 * 60 * 60 * 1e3;
|
|
717
717
|
return {
|
|
718
|
-
start: new Date(normalizedEnd.getTime() - (Math.max(1, duration) - 1) *
|
|
718
|
+
start: new Date(normalizedEnd.getTime() - (Math.max(1, duration) - 1) * DAY_MS6),
|
|
719
719
|
end: normalizedEnd
|
|
720
720
|
};
|
|
721
721
|
}
|
|
@@ -1130,13 +1130,13 @@ function computeParentProgress(parentId, tasks) {
|
|
|
1130
1130
|
if (children.length === 0) {
|
|
1131
1131
|
return 0;
|
|
1132
1132
|
}
|
|
1133
|
-
const
|
|
1133
|
+
const DAY_MS6 = 24 * 60 * 60 * 1e3;
|
|
1134
1134
|
let totalWeight = 0;
|
|
1135
1135
|
let weightedSum = 0;
|
|
1136
1136
|
for (const child of children) {
|
|
1137
1137
|
const start = new Date(child.startDate).getTime();
|
|
1138
1138
|
const end = new Date(child.endDate).getTime();
|
|
1139
|
-
const duration = (end - start +
|
|
1139
|
+
const duration = (end - start + DAY_MS6) / DAY_MS6;
|
|
1140
1140
|
const progress = child.progress ?? 0;
|
|
1141
1141
|
totalWeight += duration;
|
|
1142
1142
|
weightedSum += duration * progress;
|
|
@@ -10566,8 +10566,9 @@ function TableMatrix({
|
|
|
10566
10566
|
}
|
|
10567
10567
|
|
|
10568
10568
|
// src/components/PlanFactMatrix/PlanFactMatrix.tsx
|
|
10569
|
-
var import_react17 = require("react");
|
|
10569
|
+
var import_react17 = __toESM(require("react"));
|
|
10570
10570
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
10571
|
+
var DAY_MS5 = 24 * 60 * 60 * 1e3;
|
|
10571
10572
|
function joinClasses2(...values) {
|
|
10572
10573
|
return values.filter(Boolean).join(" ");
|
|
10573
10574
|
}
|
|
@@ -10590,13 +10591,25 @@ function parseNumberInput(value) {
|
|
|
10590
10591
|
}
|
|
10591
10592
|
return parsed;
|
|
10592
10593
|
}
|
|
10593
|
-
function
|
|
10594
|
+
function getDateOnlyMs(date) {
|
|
10595
|
+
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
|
|
10596
|
+
}
|
|
10597
|
+
function getPlannedIndexRange(task, rangeStartMs, rangeLength) {
|
|
10598
|
+
if (rangeLength <= 0) return null;
|
|
10594
10599
|
const start = parseUTCDate(task.startDate);
|
|
10595
10600
|
const end = parseUTCDate(task.endDate);
|
|
10596
|
-
const
|
|
10597
|
-
const
|
|
10598
|
-
const
|
|
10599
|
-
|
|
10601
|
+
const startIndex = Math.ceil((getDateOnlyMs(start) - rangeStartMs) / DAY_MS5);
|
|
10602
|
+
const endIndex = Math.floor((getDateOnlyMs(end) - rangeStartMs) / DAY_MS5);
|
|
10603
|
+
const clampedStartIndex = Math.max(0, startIndex);
|
|
10604
|
+
const clampedEndIndex = Math.min(rangeLength - 1, endIndex);
|
|
10605
|
+
if (clampedStartIndex > clampedEndIndex) return null;
|
|
10606
|
+
return {
|
|
10607
|
+
startIndex: clampedStartIndex,
|
|
10608
|
+
endIndex: clampedEndIndex
|
|
10609
|
+
};
|
|
10610
|
+
}
|
|
10611
|
+
function isDateIndexWithinPlannedRange(dateIndex, range) {
|
|
10612
|
+
return !!range && range.startIndex <= dateIndex && dateIndex <= range.endIndex;
|
|
10600
10613
|
}
|
|
10601
10614
|
function formatValue(value) {
|
|
10602
10615
|
if (value === void 0) return "";
|
|
@@ -10695,6 +10708,261 @@ function PlanFactCellEditor({
|
|
|
10695
10708
|
}
|
|
10696
10709
|
);
|
|
10697
10710
|
}
|
|
10711
|
+
function getCellSignatureForTask(cell, taskId) {
|
|
10712
|
+
return cell?.taskId === taskId ? `${cell.dateIndex}:${cell.kind}` : "";
|
|
10713
|
+
}
|
|
10714
|
+
function getEditingCellSignatureForTask(cell, taskId) {
|
|
10715
|
+
return cell?.taskId === taskId ? `${cell.dateIndex}:${cell.kind}:${cell.startValue ?? ""}` : "";
|
|
10716
|
+
}
|
|
10717
|
+
function getRangeAnchorSignatureForTask(range, taskId) {
|
|
10718
|
+
return range?.anchor.taskId === taskId ? `${range.anchor.dateIndex}:${range.anchor.kind}` : "";
|
|
10719
|
+
}
|
|
10720
|
+
function doesRangeTouchRow(bounds, rowIndex) {
|
|
10721
|
+
if (!bounds) return false;
|
|
10722
|
+
const firstSubrowIndex = rowIndex * 2;
|
|
10723
|
+
const lastSubrowIndex = firstSubrowIndex + 1;
|
|
10724
|
+
return bounds.fromSubrowIndex <= lastSubrowIndex && bounds.toSubrowIndex >= firstSubrowIndex;
|
|
10725
|
+
}
|
|
10726
|
+
function areRangeBoundsEqual(left, right) {
|
|
10727
|
+
if (left === right) return true;
|
|
10728
|
+
if (!left || !right) return false;
|
|
10729
|
+
return left.fromDateIndex === right.fromDateIndex && left.toDateIndex === right.toDateIndex && left.fromSubrowIndex === right.fromSubrowIndex && left.toSubrowIndex === right.toSubrowIndex;
|
|
10730
|
+
}
|
|
10731
|
+
function PlanFactRowInner({
|
|
10732
|
+
task,
|
|
10733
|
+
rowIndex,
|
|
10734
|
+
dateRange,
|
|
10735
|
+
dateKeys,
|
|
10736
|
+
renderedDateIndices,
|
|
10737
|
+
rowHeight,
|
|
10738
|
+
subrowHeight,
|
|
10739
|
+
dayWidth,
|
|
10740
|
+
plannedRange,
|
|
10741
|
+
todayDateIndex,
|
|
10742
|
+
isParent,
|
|
10743
|
+
isHighlighted,
|
|
10744
|
+
selectedTaskId,
|
|
10745
|
+
activeCell,
|
|
10746
|
+
editingCell,
|
|
10747
|
+
selectedRange,
|
|
10748
|
+
renderedRangeBounds,
|
|
10749
|
+
didDragSelectRef,
|
|
10750
|
+
isSelectingRef,
|
|
10751
|
+
isFillDraggingRef,
|
|
10752
|
+
onTaskSelect,
|
|
10753
|
+
selectSingleCell,
|
|
10754
|
+
queueHoverCellUpdate,
|
|
10755
|
+
setActiveCell,
|
|
10756
|
+
setEditingCell,
|
|
10757
|
+
setFillRange,
|
|
10758
|
+
clearSelectedCells,
|
|
10759
|
+
commitCell,
|
|
10760
|
+
commitSelectedCells,
|
|
10761
|
+
moveActiveCell,
|
|
10762
|
+
extendSelectedRange,
|
|
10763
|
+
focusCell,
|
|
10764
|
+
showOverflowTooltip,
|
|
10765
|
+
hideOverflowTooltip
|
|
10766
|
+
}) {
|
|
10767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
10768
|
+
"div",
|
|
10769
|
+
{
|
|
10770
|
+
"data-gantt-task-row-id": task.id,
|
|
10771
|
+
className: joinClasses2(
|
|
10772
|
+
"gantt-pf-row",
|
|
10773
|
+
isParent && "gantt-pf-row-parent",
|
|
10774
|
+
selectedTaskId === task.id && "gantt-pf-row-selected",
|
|
10775
|
+
isHighlighted && "gantt-pf-row-highlighted"
|
|
10776
|
+
),
|
|
10777
|
+
style: {
|
|
10778
|
+
top: `${rowIndex * rowHeight}px`,
|
|
10779
|
+
height: `${rowHeight}px`,
|
|
10780
|
+
gridTemplateColumns: `repeat(${dateRange.length}, ${dayWidth}px)`,
|
|
10781
|
+
["--gantt-pf-today-left"]: todayDateIndex !== void 0 && todayDateIndex >= 0 ? `${todayDateIndex * dayWidth}px` : void 0
|
|
10782
|
+
},
|
|
10783
|
+
onClick: () => onTaskSelect?.(task.id),
|
|
10784
|
+
children: renderedDateIndices.map((dateIndex) => {
|
|
10785
|
+
const dateKey = dateKeys[dateIndex];
|
|
10786
|
+
if (dateKey === void 0) return null;
|
|
10787
|
+
const planned = isDateIndexWithinPlannedRange(dateIndex, plannedRange);
|
|
10788
|
+
return ["plan", "fact"].map((kind) => {
|
|
10789
|
+
const subrowIndex = getSubrowIndex(rowIndex, kind);
|
|
10790
|
+
const isInRenderedRange = !!renderedRangeBounds && dateIndex >= renderedRangeBounds.fromDateIndex && dateIndex <= renderedRangeBounds.toDateIndex && subrowIndex >= renderedRangeBounds.fromSubrowIndex && subrowIndex <= renderedRangeBounds.toSubrowIndex;
|
|
10791
|
+
const planValue = task.planByDate?.[dateKey];
|
|
10792
|
+
const factValue = task.factByDate?.[dateKey];
|
|
10793
|
+
const value = kind === "plan" ? planValue : factValue;
|
|
10794
|
+
const factStatus = factValue === void 0 || planValue === void 0 ? null : factValue >= planValue ? "success" : "warning";
|
|
10795
|
+
const isActive = activeCell?.taskId === task.id && activeCell.dateIndex === dateIndex && activeCell.kind === kind;
|
|
10796
|
+
const isEditing = editingCell?.taskId === task.id && editingCell.dateIndex === dateIndex && editingCell.kind === kind;
|
|
10797
|
+
const currentCell = { taskId: task.id, dateIndex, kind };
|
|
10798
|
+
const isSelected = !isParent && isInRenderedRange;
|
|
10799
|
+
const showFillHandle = !isParent && !isEditing && isInRenderedRange && renderedRangeBounds !== null && dateIndex === renderedRangeBounds.toDateIndex && subrowIndex === renderedRangeBounds.toSubrowIndex;
|
|
10800
|
+
const isRangeAnchor = !showFillHandle && !isParent && selectedRange?.anchor.taskId === task.id && selectedRange.anchor.dateIndex === dateIndex && selectedRange.anchor.kind === kind;
|
|
10801
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
10802
|
+
"div",
|
|
10803
|
+
{
|
|
10804
|
+
"data-plan-fact-task-id": task.id,
|
|
10805
|
+
"data-plan-fact-date-index": dateIndex,
|
|
10806
|
+
"data-plan-fact-kind": kind,
|
|
10807
|
+
className: joinClasses2(
|
|
10808
|
+
"gantt-pf-cell",
|
|
10809
|
+
`gantt-pf-cell-${kind}`,
|
|
10810
|
+
planned && kind === "plan" && "gantt-pf-cell-planned",
|
|
10811
|
+
value !== void 0 && "gantt-pf-cell-hasValue",
|
|
10812
|
+
kind === "fact" && factStatus === "success" && "gantt-pf-cell-factSuccess",
|
|
10813
|
+
kind === "fact" && factStatus === "warning" && "gantt-pf-cell-factWarning",
|
|
10814
|
+
isSelected && "gantt-pf-cell-selected",
|
|
10815
|
+
isInRenderedRange && renderedRangeBounds !== null && dateIndex === renderedRangeBounds.fromDateIndex && "gantt-pf-cell-rangeLeft",
|
|
10816
|
+
isInRenderedRange && renderedRangeBounds !== null && dateIndex === renderedRangeBounds.toDateIndex && "gantt-pf-cell-rangeRight",
|
|
10817
|
+
isInRenderedRange && renderedRangeBounds !== null && subrowIndex === renderedRangeBounds.fromSubrowIndex && "gantt-pf-cell-rangeTop",
|
|
10818
|
+
isInRenderedRange && renderedRangeBounds !== null && subrowIndex === renderedRangeBounds.toSubrowIndex && "gantt-pf-cell-rangeBottom",
|
|
10819
|
+
isRangeAnchor && "gantt-pf-cell-rangeAnchor",
|
|
10820
|
+
isActive && "gantt-pf-cell-active",
|
|
10821
|
+
isEditing && "gantt-pf-cell-editing",
|
|
10822
|
+
isParent && "gantt-pf-cell-readonly"
|
|
10823
|
+
),
|
|
10824
|
+
style: {
|
|
10825
|
+
gridColumn: dateIndex + 1,
|
|
10826
|
+
gridRow: kind === "plan" ? 1 : 2,
|
|
10827
|
+
height: `${subrowHeight}px`
|
|
10828
|
+
},
|
|
10829
|
+
tabIndex: isParent ? -1 : 0,
|
|
10830
|
+
onMouseDown: (event) => {
|
|
10831
|
+
if (isParent) return;
|
|
10832
|
+
event.preventDefault();
|
|
10833
|
+
event.stopPropagation();
|
|
10834
|
+
didDragSelectRef.current = false;
|
|
10835
|
+
isSelectingRef.current = true;
|
|
10836
|
+
selectSingleCell(currentCell);
|
|
10837
|
+
onTaskSelect?.(task.id);
|
|
10838
|
+
event.currentTarget.focus();
|
|
10839
|
+
},
|
|
10840
|
+
onMouseEnter: () => {
|
|
10841
|
+
if (isParent) return;
|
|
10842
|
+
if (!isFillDraggingRef.current && !isSelectingRef.current) return;
|
|
10843
|
+
queueHoverCellUpdate(currentCell);
|
|
10844
|
+
},
|
|
10845
|
+
onFocus: () => {
|
|
10846
|
+
if (isParent) return;
|
|
10847
|
+
setActiveCell({ taskId: task.id, dateIndex, kind });
|
|
10848
|
+
},
|
|
10849
|
+
onClick: (event) => {
|
|
10850
|
+
event.stopPropagation();
|
|
10851
|
+
if (didDragSelectRef.current) {
|
|
10852
|
+
didDragSelectRef.current = false;
|
|
10853
|
+
return;
|
|
10854
|
+
}
|
|
10855
|
+
onTaskSelect?.(task.id);
|
|
10856
|
+
if (isParent) return;
|
|
10857
|
+
selectSingleCell(currentCell);
|
|
10858
|
+
event.currentTarget.focus();
|
|
10859
|
+
},
|
|
10860
|
+
onDoubleClick: (event) => {
|
|
10861
|
+
event.stopPropagation();
|
|
10862
|
+
if (isParent) return;
|
|
10863
|
+
setEditingCell({ taskId: task.id, dateIndex, kind });
|
|
10864
|
+
},
|
|
10865
|
+
onKeyDown: (event) => {
|
|
10866
|
+
if (isParent || isEditing) return;
|
|
10867
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
10868
|
+
event.preventDefault();
|
|
10869
|
+
event.stopPropagation();
|
|
10870
|
+
const direction = event.key.replace("Arrow", "").toLowerCase();
|
|
10871
|
+
if (event.shiftKey) {
|
|
10872
|
+
extendSelectedRange(selectedRange?.focus ?? currentCell, direction);
|
|
10873
|
+
} else {
|
|
10874
|
+
moveActiveCell(currentCell, direction);
|
|
10875
|
+
}
|
|
10876
|
+
return;
|
|
10877
|
+
}
|
|
10878
|
+
if (event.key === "Enter" || event.key === "F2") {
|
|
10879
|
+
event.preventDefault();
|
|
10880
|
+
event.stopPropagation();
|
|
10881
|
+
setEditingCell(selectedRange?.anchor ?? currentCell);
|
|
10882
|
+
return;
|
|
10883
|
+
}
|
|
10884
|
+
if (event.key === "Backspace" || event.key === "Delete") {
|
|
10885
|
+
event.preventDefault();
|
|
10886
|
+
event.stopPropagation();
|
|
10887
|
+
clearSelectedCells();
|
|
10888
|
+
return;
|
|
10889
|
+
}
|
|
10890
|
+
if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
10891
|
+
event.preventDefault();
|
|
10892
|
+
event.stopPropagation();
|
|
10893
|
+
setEditingCell({ ...currentCell, startValue: event.key });
|
|
10894
|
+
}
|
|
10895
|
+
},
|
|
10896
|
+
children: [
|
|
10897
|
+
isEditing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
10898
|
+
PlanFactCellEditor,
|
|
10899
|
+
{
|
|
10900
|
+
value,
|
|
10901
|
+
startValue: editingCell.startValue,
|
|
10902
|
+
onCommit: (nextValue) => {
|
|
10903
|
+
commitCell(task, dateIndex, kind, nextValue);
|
|
10904
|
+
setEditingCell(null);
|
|
10905
|
+
const nextActiveCell = { taskId: task.id, dateIndex, kind };
|
|
10906
|
+
selectSingleCell(nextActiveCell);
|
|
10907
|
+
focusCell(nextActiveCell);
|
|
10908
|
+
},
|
|
10909
|
+
onCommitRange: (nextValue) => {
|
|
10910
|
+
commitSelectedCells(nextValue);
|
|
10911
|
+
setEditingCell(null);
|
|
10912
|
+
const nextActiveCell = { taskId: task.id, dateIndex, kind };
|
|
10913
|
+
selectSingleCell(nextActiveCell);
|
|
10914
|
+
focusCell(nextActiveCell);
|
|
10915
|
+
},
|
|
10916
|
+
onCancel: () => {
|
|
10917
|
+
setEditingCell(null);
|
|
10918
|
+
const nextActiveCell = { taskId: task.id, dateIndex, kind };
|
|
10919
|
+
selectSingleCell(nextActiveCell);
|
|
10920
|
+
focusCell(nextActiveCell);
|
|
10921
|
+
}
|
|
10922
|
+
}
|
|
10923
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
10924
|
+
"span",
|
|
10925
|
+
{
|
|
10926
|
+
className: "gantt-pf-cellValue",
|
|
10927
|
+
onMouseEnter: (event) => {
|
|
10928
|
+
if (isParent || value === void 0) return;
|
|
10929
|
+
const compactValue = formatValue(value);
|
|
10930
|
+
const fullValue = formatTooltipValue(value);
|
|
10931
|
+
showOverflowTooltip(event.currentTarget, fullValue, compactValue !== fullValue);
|
|
10932
|
+
},
|
|
10933
|
+
onMouseLeave: hideOverflowTooltip,
|
|
10934
|
+
children: isParent ? "" : formatValue(value)
|
|
10935
|
+
}
|
|
10936
|
+
),
|
|
10937
|
+
showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
10938
|
+
"span",
|
|
10939
|
+
{
|
|
10940
|
+
className: "gantt-pf-fillHandle",
|
|
10941
|
+
"aria-hidden": "true",
|
|
10942
|
+
onMouseDown: (event) => {
|
|
10943
|
+
event.preventDefault();
|
|
10944
|
+
event.stopPropagation();
|
|
10945
|
+
isSelectingRef.current = false;
|
|
10946
|
+
isFillDraggingRef.current = true;
|
|
10947
|
+
setFillRange(selectedRange);
|
|
10948
|
+
}
|
|
10949
|
+
}
|
|
10950
|
+
)
|
|
10951
|
+
]
|
|
10952
|
+
},
|
|
10953
|
+
`${task.id}:${dateKey}:${kind}`
|
|
10954
|
+
);
|
|
10955
|
+
});
|
|
10956
|
+
})
|
|
10957
|
+
}
|
|
10958
|
+
);
|
|
10959
|
+
}
|
|
10960
|
+
function arePlanFactRowsEqual(previous, next) {
|
|
10961
|
+
const previousRangeTouchesRow = doesRangeTouchRow(previous.renderedRangeBounds, previous.rowIndex);
|
|
10962
|
+
const nextRangeTouchesRow = doesRangeTouchRow(next.renderedRangeBounds, next.rowIndex);
|
|
10963
|
+
return previous.task === next.task && previous.rowIndex === next.rowIndex && previous.dateRange === next.dateRange && previous.dateKeys === next.dateKeys && previous.renderedDateIndices === next.renderedDateIndices && previous.rowHeight === next.rowHeight && previous.subrowHeight === next.subrowHeight && previous.dayWidth === next.dayWidth && previous.plannedRange === next.plannedRange && previous.todayDateIndex === next.todayDateIndex && previous.isParent === next.isParent && previous.isHighlighted === next.isHighlighted && previous.selectedTaskId === previous.task.id === (next.selectedTaskId === next.task.id) && getCellSignatureForTask(previous.activeCell, previous.task.id) === getCellSignatureForTask(next.activeCell, next.task.id) && getEditingCellSignatureForTask(previous.editingCell, previous.task.id) === getEditingCellSignatureForTask(next.editingCell, next.task.id) && getRangeAnchorSignatureForTask(previous.selectedRange, previous.task.id) === getRangeAnchorSignatureForTask(next.selectedRange, next.task.id) && previousRangeTouchesRow === nextRangeTouchesRow && (!nextRangeTouchesRow || areRangeBoundsEqual(previous.renderedRangeBounds, next.renderedRangeBounds));
|
|
10964
|
+
}
|
|
10965
|
+
var PlanFactRow = import_react17.default.memo(PlanFactRowInner, arePlanFactRowsEqual);
|
|
10698
10966
|
function PlanFactMatrix({
|
|
10699
10967
|
tasks,
|
|
10700
10968
|
allTasks = tasks,
|
|
@@ -10708,7 +10976,10 @@ function PlanFactMatrix({
|
|
|
10708
10976
|
onTasksChange,
|
|
10709
10977
|
onCellCommit,
|
|
10710
10978
|
highlightedTaskIds,
|
|
10711
|
-
filterMode = "highlight"
|
|
10979
|
+
filterMode = "highlight",
|
|
10980
|
+
visibleRowIndices,
|
|
10981
|
+
visibleDateIndices,
|
|
10982
|
+
todayDateIndex
|
|
10712
10983
|
}) {
|
|
10713
10984
|
const [activeCell, setActiveCell] = (0, import_react17.useState)(null);
|
|
10714
10985
|
const [editingCell, setEditingCell] = (0, import_react17.useState)(null);
|
|
@@ -10718,10 +10989,20 @@ function PlanFactMatrix({
|
|
|
10718
10989
|
const isSelectingRef = (0, import_react17.useRef)(false);
|
|
10719
10990
|
const isFillDraggingRef = (0, import_react17.useRef)(false);
|
|
10720
10991
|
const didDragSelectRef = (0, import_react17.useRef)(false);
|
|
10992
|
+
const pendingHoverCellRef = (0, import_react17.useRef)(null);
|
|
10993
|
+
const hoverFrameRef = (0, import_react17.useRef)(null);
|
|
10721
10994
|
const rootRef = (0, import_react17.useRef)(null);
|
|
10722
10995
|
const bodyRef = (0, import_react17.useRef)(null);
|
|
10723
10996
|
const totalWidth = dateRange.length * dayWidth;
|
|
10724
10997
|
const subrowHeight = rowHeight / 2;
|
|
10998
|
+
const renderedRowIndices = (0, import_react17.useMemo)(
|
|
10999
|
+
() => visibleRowIndices ?? tasks.map((_, index) => index),
|
|
11000
|
+
[tasks, visibleRowIndices]
|
|
11001
|
+
);
|
|
11002
|
+
const renderedDateIndices = (0, import_react17.useMemo)(
|
|
11003
|
+
() => visibleDateIndices ?? dateRange.map((_, index) => index),
|
|
11004
|
+
[dateRange, visibleDateIndices]
|
|
11005
|
+
);
|
|
10725
11006
|
const parentTaskIds = (0, import_react17.useMemo)(() => {
|
|
10726
11007
|
const ids = /* @__PURE__ */ new Set();
|
|
10727
11008
|
for (const task of allTasks) {
|
|
@@ -10730,11 +11011,32 @@ function PlanFactMatrix({
|
|
|
10730
11011
|
return ids;
|
|
10731
11012
|
}, [allTasks]);
|
|
10732
11013
|
const dateKeys = (0, import_react17.useMemo)(() => dateRange.map(formatDateKey), [dateRange]);
|
|
11014
|
+
const dateRangeStartMs = dateRange[0] ? getDateOnlyMs(dateRange[0]) : 0;
|
|
10733
11015
|
const taskIndexById = (0, import_react17.useMemo)(() => {
|
|
10734
11016
|
const indexById = /* @__PURE__ */ new Map();
|
|
10735
11017
|
tasks.forEach((task, index) => indexById.set(task.id, index));
|
|
10736
11018
|
return indexById;
|
|
10737
11019
|
}, [tasks]);
|
|
11020
|
+
const taskById = (0, import_react17.useMemo)(
|
|
11021
|
+
() => new Map(tasks.map((task) => [task.id, task])),
|
|
11022
|
+
[tasks]
|
|
11023
|
+
);
|
|
11024
|
+
const monthSeparatorIndices = (0, import_react17.useMemo)(() => {
|
|
11025
|
+
const indices = [];
|
|
11026
|
+
for (let index = 1; index < dateRange.length; index += 1) {
|
|
11027
|
+
if (dateRange[index].getUTCDate() === 1) {
|
|
11028
|
+
indices.push(index);
|
|
11029
|
+
}
|
|
11030
|
+
}
|
|
11031
|
+
return indices;
|
|
11032
|
+
}, [dateRange]);
|
|
11033
|
+
const plannedRangeByTaskId = (0, import_react17.useMemo)(() => {
|
|
11034
|
+
const rangeByTaskId = /* @__PURE__ */ new Map();
|
|
11035
|
+
for (const task of tasks) {
|
|
11036
|
+
rangeByTaskId.set(task.id, getPlannedIndexRange(task, dateRangeStartMs, dateRange.length));
|
|
11037
|
+
}
|
|
11038
|
+
return rangeByTaskId;
|
|
11039
|
+
}, [dateRange.length, dateRangeStartMs, tasks]);
|
|
10738
11040
|
const focusCell = (0, import_react17.useCallback)((cell) => {
|
|
10739
11041
|
window.requestAnimationFrame(() => {
|
|
10740
11042
|
const selector = [
|
|
@@ -10746,6 +11048,11 @@ function PlanFactMatrix({
|
|
|
10746
11048
|
});
|
|
10747
11049
|
}, []);
|
|
10748
11050
|
const clearSelection = (0, import_react17.useCallback)(() => {
|
|
11051
|
+
if (hoverFrameRef.current !== null) {
|
|
11052
|
+
window.cancelAnimationFrame(hoverFrameRef.current);
|
|
11053
|
+
hoverFrameRef.current = null;
|
|
11054
|
+
}
|
|
11055
|
+
pendingHoverCellRef.current = null;
|
|
10749
11056
|
isSelectingRef.current = false;
|
|
10750
11057
|
isFillDraggingRef.current = false;
|
|
10751
11058
|
didDragSelectRef.current = false;
|
|
@@ -10758,6 +11065,29 @@ function PlanFactMatrix({
|
|
|
10758
11065
|
const hideOverflowTooltip = (0, import_react17.useCallback)(() => {
|
|
10759
11066
|
setOverflowTooltip(null);
|
|
10760
11067
|
}, []);
|
|
11068
|
+
const flushPendingHoverCell = (0, import_react17.useCallback)(() => {
|
|
11069
|
+
hoverFrameRef.current = null;
|
|
11070
|
+
const currentCell = pendingHoverCellRef.current;
|
|
11071
|
+
if (!currentCell) return;
|
|
11072
|
+
if (isFillDraggingRef.current && selectedRange) {
|
|
11073
|
+
setFillRange({ anchor: selectedRange.anchor, focus: currentCell });
|
|
11074
|
+
setActiveCell(currentCell);
|
|
11075
|
+
onTaskSelect?.(currentCell.taskId);
|
|
11076
|
+
return;
|
|
11077
|
+
}
|
|
11078
|
+
if (!isSelectingRef.current) return;
|
|
11079
|
+
didDragSelectRef.current = true;
|
|
11080
|
+
setSelectedRange((currentRange) => ({
|
|
11081
|
+
anchor: currentRange?.anchor ?? currentCell,
|
|
11082
|
+
focus: currentCell
|
|
11083
|
+
}));
|
|
11084
|
+
onTaskSelect?.(currentCell.taskId);
|
|
11085
|
+
}, [onTaskSelect, selectedRange]);
|
|
11086
|
+
const queueHoverCellUpdate = (0, import_react17.useCallback)((cell) => {
|
|
11087
|
+
pendingHoverCellRef.current = cell;
|
|
11088
|
+
if (hoverFrameRef.current !== null) return;
|
|
11089
|
+
hoverFrameRef.current = window.requestAnimationFrame(flushPendingHoverCell);
|
|
11090
|
+
}, [flushPendingHoverCell]);
|
|
10761
11091
|
const showOverflowTooltip = (0, import_react17.useCallback)((target, label, force = false) => {
|
|
10762
11092
|
if (!rootRef.current || !label) return;
|
|
10763
11093
|
if (!force && target.scrollWidth <= target.clientWidth) {
|
|
@@ -10796,6 +11126,11 @@ function PlanFactMatrix({
|
|
|
10796
11126
|
)
|
|
10797
11127
|
};
|
|
10798
11128
|
}, [taskIndexById]);
|
|
11129
|
+
const renderedRange = fillRange ?? selectedRange;
|
|
11130
|
+
const renderedRangeBounds = (0, import_react17.useMemo)(
|
|
11131
|
+
() => renderedRange ? getRangeBounds(renderedRange) : null,
|
|
11132
|
+
[getRangeBounds, renderedRange]
|
|
11133
|
+
);
|
|
10799
11134
|
const getCellFromPosition = (0, import_react17.useCallback)((subrowIndex, dateIndex) => {
|
|
10800
11135
|
const task = tasks[Math.floor(subrowIndex / 2)];
|
|
10801
11136
|
if (!task) return null;
|
|
@@ -10815,33 +11150,6 @@ function PlanFactMatrix({
|
|
|
10815
11150
|
const cellSubrowIndex = getSubrowIndex(cellTaskIndex, cell.kind);
|
|
10816
11151
|
return cell.dateIndex >= bounds.fromDateIndex && cell.dateIndex <= bounds.toDateIndex && cellSubrowIndex >= bounds.fromSubrowIndex && cellSubrowIndex <= bounds.toSubrowIndex;
|
|
10817
11152
|
}, [getRangeBounds, taskIndexById]);
|
|
10818
|
-
const isCellInSelectedRange = (0, import_react17.useCallback)((cell) => {
|
|
10819
|
-
if (!selectedRange) return false;
|
|
10820
|
-
return isCellInRange(cell, fillRange ?? selectedRange);
|
|
10821
|
-
}, [fillRange, isCellInRange, selectedRange]);
|
|
10822
|
-
const getSelectedRangeEdgeClasses = (0, import_react17.useCallback)((cell) => {
|
|
10823
|
-
if (!selectedRange) return [];
|
|
10824
|
-
const range = fillRange ?? selectedRange;
|
|
10825
|
-
const bounds = getRangeBounds(range);
|
|
10826
|
-
const taskIndex = taskIndexById.get(cell.taskId);
|
|
10827
|
-
if (!bounds || taskIndex === void 0 || !isCellInRange(cell, range)) return [];
|
|
10828
|
-
const subrowIndex = getSubrowIndex(taskIndex, cell.kind);
|
|
10829
|
-
return [
|
|
10830
|
-
cell.dateIndex === bounds.fromDateIndex && "gantt-pf-cell-rangeLeft",
|
|
10831
|
-
cell.dateIndex === bounds.toDateIndex && "gantt-pf-cell-rangeRight",
|
|
10832
|
-
subrowIndex === bounds.fromSubrowIndex && "gantt-pf-cell-rangeTop",
|
|
10833
|
-
subrowIndex === bounds.toSubrowIndex && "gantt-pf-cell-rangeBottom"
|
|
10834
|
-
];
|
|
10835
|
-
}, [fillRange, getRangeBounds, isCellInRange, selectedRange, taskIndexById]);
|
|
10836
|
-
const isFillHandleCell = (0, import_react17.useCallback)((cell) => {
|
|
10837
|
-
if (!selectedRange) return false;
|
|
10838
|
-
const range = fillRange ?? selectedRange;
|
|
10839
|
-
const bounds = getRangeBounds(range);
|
|
10840
|
-
const taskIndex = taskIndexById.get(cell.taskId);
|
|
10841
|
-
if (!bounds || taskIndex === void 0 || !isCellInRange(cell, range)) return false;
|
|
10842
|
-
const subrowIndex = getSubrowIndex(taskIndex, cell.kind);
|
|
10843
|
-
return cell.dateIndex === bounds.toDateIndex && subrowIndex === bounds.toSubrowIndex;
|
|
10844
|
-
}, [fillRange, getRangeBounds, isCellInRange, selectedRange, taskIndexById]);
|
|
10845
11153
|
const commitCell = (0, import_react17.useCallback)((task, dateIndex, kind, value) => {
|
|
10846
11154
|
const dateKey = dateKeys[dateIndex];
|
|
10847
11155
|
const source = kind === "plan" ? task.planByDate : task.factByDate;
|
|
@@ -10864,40 +11172,45 @@ function PlanFactMatrix({
|
|
|
10864
11172
|
value
|
|
10865
11173
|
});
|
|
10866
11174
|
}, [dateKeys, dateRange, onCellCommit, onTasksChange]);
|
|
10867
|
-
const
|
|
10868
|
-
if (!selectedRange) {
|
|
10869
|
-
if (!activeCell) return;
|
|
10870
|
-
const task = tasks.find((candidate) => candidate.id === activeCell.taskId);
|
|
10871
|
-
if (!task || parentTaskIds.has(task.id)) return;
|
|
10872
|
-
commitCell(task, activeCell.dateIndex, activeCell.kind, void 0);
|
|
10873
|
-
return;
|
|
10874
|
-
}
|
|
11175
|
+
const commitRangeCells = (0, import_react17.useCallback)((bounds, value, mode) => {
|
|
10875
11176
|
const changedTasksById = /* @__PURE__ */ new Map();
|
|
10876
|
-
for (
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
11177
|
+
for (let subrowIndex = bounds.fromSubrowIndex; subrowIndex <= bounds.toSubrowIndex; subrowIndex += 1) {
|
|
11178
|
+
const task = tasks[Math.floor(subrowIndex / 2)];
|
|
11179
|
+
if (!task || parentTaskIds.has(task.id)) continue;
|
|
11180
|
+
const kind = subrowIndex % 2 === 0 ? "plan" : "fact";
|
|
11181
|
+
const currentChangedTask = changedTasksById.get(task.id) ?? task;
|
|
11182
|
+
let nextPlanByDate = currentChangedTask.planByDate;
|
|
11183
|
+
let nextFactByDate = currentChangedTask.factByDate;
|
|
10880
11184
|
let didChange = false;
|
|
10881
|
-
for (let dateIndex =
|
|
11185
|
+
for (let dateIndex = bounds.fromDateIndex; dateIndex <= bounds.toDateIndex; dateIndex += 1) {
|
|
10882
11186
|
const dateKey = dateKeys[dateIndex];
|
|
10883
|
-
|
|
10884
|
-
|
|
11187
|
+
if (dateKey === void 0) continue;
|
|
11188
|
+
const currentValues = kind === "plan" ? nextPlanByDate : nextFactByDate;
|
|
11189
|
+
const currentValue = currentValues?.[dateKey];
|
|
11190
|
+
const nextValue = mode === "clear" ? void 0 : value;
|
|
11191
|
+
if (currentValue === nextValue) continue;
|
|
11192
|
+
if (kind === "plan") {
|
|
10885
11193
|
nextPlanByDate = { ...nextPlanByDate ?? {} };
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
11194
|
+
if (nextValue === void 0) {
|
|
11195
|
+
delete nextPlanByDate[dateKey];
|
|
11196
|
+
} else {
|
|
11197
|
+
nextPlanByDate[dateKey] = nextValue;
|
|
11198
|
+
}
|
|
11199
|
+
} else {
|
|
10891
11200
|
nextFactByDate = { ...nextFactByDate ?? {} };
|
|
10892
|
-
|
|
10893
|
-
|
|
11201
|
+
if (nextValue === void 0) {
|
|
11202
|
+
delete nextFactByDate[dateKey];
|
|
11203
|
+
} else {
|
|
11204
|
+
nextFactByDate[dateKey] = nextValue;
|
|
11205
|
+
}
|
|
10894
11206
|
}
|
|
11207
|
+
didChange = true;
|
|
10895
11208
|
}
|
|
10896
11209
|
if (didChange) {
|
|
10897
11210
|
changedTasksById.set(task.id, {
|
|
10898
|
-
...
|
|
10899
|
-
...nextPlanByDate !==
|
|
10900
|
-
...nextFactByDate !==
|
|
11211
|
+
...currentChangedTask,
|
|
11212
|
+
...nextPlanByDate !== currentChangedTask.planByDate ? { planByDate: nextPlanByDate ?? {} } : {},
|
|
11213
|
+
...nextFactByDate !== currentChangedTask.factByDate ? { factByDate: nextFactByDate ?? {} } : {}
|
|
10901
11214
|
});
|
|
10902
11215
|
}
|
|
10903
11216
|
}
|
|
@@ -10905,67 +11218,46 @@ function PlanFactMatrix({
|
|
|
10905
11218
|
if (changedTasks.length > 0) {
|
|
10906
11219
|
onTasksChange?.(changedTasks);
|
|
10907
11220
|
}
|
|
10908
|
-
}, [
|
|
11221
|
+
}, [dateKeys, onTasksChange, parentTaskIds, tasks]);
|
|
11222
|
+
const clearSelectedCells = (0, import_react17.useCallback)(() => {
|
|
11223
|
+
const activeRange = fillRange ?? selectedRange;
|
|
11224
|
+
if (!activeRange) {
|
|
11225
|
+
if (!activeCell) return;
|
|
11226
|
+
const task = taskById.get(activeCell.taskId);
|
|
11227
|
+
if (!task || parentTaskIds.has(task.id)) return;
|
|
11228
|
+
commitCell(task, activeCell.dateIndex, activeCell.kind, void 0);
|
|
11229
|
+
return;
|
|
11230
|
+
}
|
|
11231
|
+
const bounds = getRangeBounds(activeRange);
|
|
11232
|
+
if (bounds) {
|
|
11233
|
+
commitRangeCells(bounds, void 0, "clear");
|
|
11234
|
+
}
|
|
11235
|
+
}, [activeCell, commitCell, commitRangeCells, fillRange, getRangeBounds, parentTaskIds, selectedRange, taskById]);
|
|
10909
11236
|
const commitSelectedCells = (0, import_react17.useCallback)((value) => {
|
|
10910
|
-
|
|
11237
|
+
const activeRange = fillRange ?? selectedRange;
|
|
11238
|
+
if (!activeRange) {
|
|
10911
11239
|
if (!activeCell) return;
|
|
10912
|
-
const task =
|
|
11240
|
+
const task = taskById.get(activeCell.taskId);
|
|
10913
11241
|
if (!task || parentTaskIds.has(task.id)) return;
|
|
10914
11242
|
commitCell(task, activeCell.dateIndex, activeCell.kind, value);
|
|
10915
11243
|
return;
|
|
10916
11244
|
}
|
|
10917
|
-
const
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
let nextPlanByDate = task.planByDate;
|
|
10921
|
-
let nextFactByDate = task.factByDate;
|
|
10922
|
-
let didChange = false;
|
|
10923
|
-
for (let dateIndex = 0; dateIndex < dateKeys.length; dateIndex += 1) {
|
|
10924
|
-
const dateKey = dateKeys[dateIndex];
|
|
10925
|
-
const planCell = { taskId: task.id, dateIndex, kind: "plan" };
|
|
10926
|
-
if (isCellInSelectedRange(planCell) && nextPlanByDate?.[dateKey] !== value) {
|
|
10927
|
-
nextPlanByDate = { ...nextPlanByDate ?? {} };
|
|
10928
|
-
if (value === void 0) {
|
|
10929
|
-
delete nextPlanByDate[dateKey];
|
|
10930
|
-
} else {
|
|
10931
|
-
nextPlanByDate[dateKey] = value;
|
|
10932
|
-
}
|
|
10933
|
-
didChange = true;
|
|
10934
|
-
}
|
|
10935
|
-
const factCell = { taskId: task.id, dateIndex, kind: "fact" };
|
|
10936
|
-
if (isCellInSelectedRange(factCell) && nextFactByDate?.[dateKey] !== value) {
|
|
10937
|
-
nextFactByDate = { ...nextFactByDate ?? {} };
|
|
10938
|
-
if (value === void 0) {
|
|
10939
|
-
delete nextFactByDate[dateKey];
|
|
10940
|
-
} else {
|
|
10941
|
-
nextFactByDate[dateKey] = value;
|
|
10942
|
-
}
|
|
10943
|
-
didChange = true;
|
|
10944
|
-
}
|
|
10945
|
-
}
|
|
10946
|
-
if (didChange) {
|
|
10947
|
-
changedTasksById.set(task.id, {
|
|
10948
|
-
...task,
|
|
10949
|
-
...nextPlanByDate !== task.planByDate ? { planByDate: nextPlanByDate ?? {} } : {},
|
|
10950
|
-
...nextFactByDate !== task.factByDate ? { factByDate: nextFactByDate ?? {} } : {}
|
|
10951
|
-
});
|
|
10952
|
-
}
|
|
10953
|
-
}
|
|
10954
|
-
const changedTasks = Array.from(changedTasksById.values());
|
|
10955
|
-
if (changedTasks.length > 0) {
|
|
10956
|
-
onTasksChange?.(changedTasks);
|
|
11245
|
+
const bounds = getRangeBounds(activeRange);
|
|
11246
|
+
if (bounds) {
|
|
11247
|
+
commitRangeCells(bounds, value, "set");
|
|
10957
11248
|
}
|
|
10958
|
-
}, [activeCell, commitCell,
|
|
11249
|
+
}, [activeCell, commitCell, commitRangeCells, fillRange, getRangeBounds, parentTaskIds, selectedRange, taskById]);
|
|
10959
11250
|
const getCellValue = (0, import_react17.useCallback)((cell) => {
|
|
10960
|
-
const task =
|
|
11251
|
+
const task = taskById.get(cell.taskId);
|
|
10961
11252
|
if (!task) return void 0;
|
|
10962
11253
|
const dateKey = dateKeys[cell.dateIndex];
|
|
10963
11254
|
return cell.kind === "plan" ? task.planByDate?.[dateKey] : task.factByDate?.[dateKey];
|
|
10964
|
-
}, [dateKeys,
|
|
10965
|
-
const applyFillRange = (0, import_react17.useCallback)(() => {
|
|
10966
|
-
|
|
11255
|
+
}, [dateKeys, taskById]);
|
|
11256
|
+
const applyFillRange = (0, import_react17.useCallback)((nextFillRange) => {
|
|
11257
|
+
const targetRange = nextFillRange ?? fillRange;
|
|
11258
|
+
if (!selectedRange || !targetRange) return;
|
|
10967
11259
|
const sourceBounds = getRangeBounds(selectedRange);
|
|
10968
|
-
const targetBounds = getRangeBounds(
|
|
11260
|
+
const targetBounds = getRangeBounds(targetRange);
|
|
10969
11261
|
if (!sourceBounds || !targetBounds) return;
|
|
10970
11262
|
const sourceDateSpan = sourceBounds.toDateIndex - sourceBounds.fromDateIndex + 1;
|
|
10971
11263
|
const sourceSubrowSpan = sourceBounds.toSubrowIndex - sourceBounds.fromSubrowIndex + 1;
|
|
@@ -10973,7 +11265,7 @@ function PlanFactMatrix({
|
|
|
10973
11265
|
for (let subrowIndex = targetBounds.fromSubrowIndex; subrowIndex <= targetBounds.toSubrowIndex; subrowIndex += 1) {
|
|
10974
11266
|
const targetCellForRow = getCellFromPosition(subrowIndex, targetBounds.fromDateIndex);
|
|
10975
11267
|
if (!targetCellForRow || parentTaskIds.has(targetCellForRow.taskId)) continue;
|
|
10976
|
-
const originalTask =
|
|
11268
|
+
const originalTask = taskById.get(targetCellForRow.taskId);
|
|
10977
11269
|
if (!originalTask) continue;
|
|
10978
11270
|
let changedTask = changedTasksById.get(originalTask.id) ?? originalTask;
|
|
10979
11271
|
let nextPlanByDate = changedTask.planByDate;
|
|
@@ -11019,8 +11311,8 @@ function PlanFactMatrix({
|
|
|
11019
11311
|
if (changedTasks.length > 0) {
|
|
11020
11312
|
onTasksChange?.(changedTasks);
|
|
11021
11313
|
}
|
|
11022
|
-
setSelectedRange(
|
|
11023
|
-
setActiveCell(
|
|
11314
|
+
setSelectedRange(targetRange);
|
|
11315
|
+
setActiveCell(targetRange.anchor);
|
|
11024
11316
|
setFillRange(null);
|
|
11025
11317
|
}, [
|
|
11026
11318
|
dateKeys,
|
|
@@ -11032,7 +11324,7 @@ function PlanFactMatrix({
|
|
|
11032
11324
|
onTasksChange,
|
|
11033
11325
|
parentTaskIds,
|
|
11034
11326
|
selectedRange,
|
|
11035
|
-
|
|
11327
|
+
taskById
|
|
11036
11328
|
]);
|
|
11037
11329
|
const moveActiveCell = (0, import_react17.useCallback)((cell, direction) => {
|
|
11038
11330
|
const taskIndex = tasks.findIndex((task) => task.id === cell.taskId);
|
|
@@ -11104,9 +11396,19 @@ function PlanFactMatrix({
|
|
|
11104
11396
|
}, [dateRange.length, focusCell, onTaskSelect, parentTaskIds, selectedRange, tasks]);
|
|
11105
11397
|
(0, import_react17.useEffect)(() => {
|
|
11106
11398
|
const endSelection = () => {
|
|
11399
|
+
let pendingFillRange = null;
|
|
11400
|
+
if (hoverFrameRef.current !== null) {
|
|
11401
|
+
window.cancelAnimationFrame(hoverFrameRef.current);
|
|
11402
|
+
hoverFrameRef.current = null;
|
|
11403
|
+
const pendingCell = pendingHoverCellRef.current;
|
|
11404
|
+
if (pendingCell && isFillDraggingRef.current && selectedRange) {
|
|
11405
|
+
pendingFillRange = { anchor: selectedRange.anchor, focus: pendingCell };
|
|
11406
|
+
}
|
|
11407
|
+
flushPendingHoverCell();
|
|
11408
|
+
}
|
|
11107
11409
|
if (isFillDraggingRef.current) {
|
|
11108
11410
|
isFillDraggingRef.current = false;
|
|
11109
|
-
applyFillRange();
|
|
11411
|
+
applyFillRange(pendingFillRange);
|
|
11110
11412
|
}
|
|
11111
11413
|
isSelectingRef.current = false;
|
|
11112
11414
|
};
|
|
@@ -11114,7 +11416,12 @@ function PlanFactMatrix({
|
|
|
11114
11416
|
return () => {
|
|
11115
11417
|
window.removeEventListener("mouseup", endSelection);
|
|
11116
11418
|
};
|
|
11117
|
-
}, [applyFillRange]);
|
|
11419
|
+
}, [applyFillRange, flushPendingHoverCell]);
|
|
11420
|
+
(0, import_react17.useEffect)(() => () => {
|
|
11421
|
+
if (hoverFrameRef.current !== null) {
|
|
11422
|
+
window.cancelAnimationFrame(hoverFrameRef.current);
|
|
11423
|
+
}
|
|
11424
|
+
}, []);
|
|
11118
11425
|
(0, import_react17.useEffect)(() => {
|
|
11119
11426
|
const handleKeyDown = (event) => {
|
|
11120
11427
|
if (event.key !== "Escape") return;
|
|
@@ -11144,15 +11451,39 @@ function PlanFactMatrix({
|
|
|
11144
11451
|
["--gantt-pf-day-width"]: `${dayWidth}px`
|
|
11145
11452
|
},
|
|
11146
11453
|
children: [
|
|
11147
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.
|
|
11148
|
-
|
|
11454
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "gantt-pf-header", style: { width: `${totalWidth}px`, height: `${headerHeight}px` }, children: [
|
|
11455
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11456
|
+
TimeScaleHeader_default,
|
|
11457
|
+
{
|
|
11458
|
+
days: dateRange,
|
|
11459
|
+
dayWidth,
|
|
11460
|
+
headerHeight: headerHeight - 1,
|
|
11461
|
+
viewMode: "day"
|
|
11462
|
+
}
|
|
11463
|
+
),
|
|
11464
|
+
todayDateIndex !== void 0 && todayDateIndex >= 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11465
|
+
"span",
|
|
11466
|
+
{
|
|
11467
|
+
className: "gantt-pf-headerTodayLine",
|
|
11468
|
+
"aria-hidden": "true",
|
|
11469
|
+
style: {
|
|
11470
|
+
left: `${todayDateIndex * dayWidth}px`,
|
|
11471
|
+
top: `${Math.max(0, headerHeight / 2)}px`
|
|
11472
|
+
}
|
|
11473
|
+
}
|
|
11474
|
+
)
|
|
11475
|
+
] }),
|
|
11476
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "gantt-pf-monthSeparatorLayer", "aria-hidden": "true", children: monthSeparatorIndices.map((dateIndex) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11477
|
+
"span",
|
|
11149
11478
|
{
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11479
|
+
className: "gantt-pf-monthSeparator",
|
|
11480
|
+
style: {
|
|
11481
|
+
left: `${Math.round(dateIndex * dayWidth)}px`,
|
|
11482
|
+
top: `${Math.max(0, headerHeight / 2)}px`
|
|
11483
|
+
}
|
|
11484
|
+
},
|
|
11485
|
+
`month-separator-${dateIndex}`
|
|
11486
|
+
)) }),
|
|
11156
11487
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11157
11488
|
"div",
|
|
11158
11489
|
{
|
|
@@ -11163,202 +11494,48 @@ function PlanFactMatrix({
|
|
|
11163
11494
|
minHeight: bodyMinHeight,
|
|
11164
11495
|
width: `${totalWidth}px`
|
|
11165
11496
|
},
|
|
11166
|
-
children:
|
|
11497
|
+
children: renderedRowIndices.map((rowIndex) => {
|
|
11498
|
+
const task = tasks[rowIndex];
|
|
11499
|
+
if (!task) return null;
|
|
11167
11500
|
const isParent = parentTaskIds.has(task.id);
|
|
11168
11501
|
const isHighlighted = filterMode === "highlight" && !!highlightedTaskIds?.has(task.id);
|
|
11169
11502
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11170
|
-
|
|
11503
|
+
PlanFactRow,
|
|
11171
11504
|
{
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
"gantt-pf-cell",
|
|
11207
|
-
`gantt-pf-cell-${kind}`,
|
|
11208
|
-
planned && kind === "plan" && !isParent && "gantt-pf-cell-planned",
|
|
11209
|
-
value !== void 0 && "gantt-pf-cell-hasValue",
|
|
11210
|
-
kind === "fact" && factStatus === "success" && "gantt-pf-cell-factSuccess",
|
|
11211
|
-
kind === "fact" && factStatus === "warning" && "gantt-pf-cell-factWarning",
|
|
11212
|
-
isSelected && "gantt-pf-cell-selected",
|
|
11213
|
-
...getSelectedRangeEdgeClasses(currentCell),
|
|
11214
|
-
isRangeAnchor && "gantt-pf-cell-rangeAnchor",
|
|
11215
|
-
isActive && "gantt-pf-cell-active",
|
|
11216
|
-
isEditing && "gantt-pf-cell-editing",
|
|
11217
|
-
isParent && "gantt-pf-cell-readonly"
|
|
11218
|
-
),
|
|
11219
|
-
style: {
|
|
11220
|
-
gridColumn: dateIndex + 1,
|
|
11221
|
-
gridRow: kind === "plan" ? 1 : 2,
|
|
11222
|
-
height: `${subrowHeight}px`
|
|
11223
|
-
},
|
|
11224
|
-
tabIndex: isParent ? -1 : 0,
|
|
11225
|
-
onMouseDown: (event) => {
|
|
11226
|
-
if (isParent) return;
|
|
11227
|
-
event.preventDefault();
|
|
11228
|
-
event.stopPropagation();
|
|
11229
|
-
didDragSelectRef.current = false;
|
|
11230
|
-
isSelectingRef.current = true;
|
|
11231
|
-
selectSingleCell(currentCell);
|
|
11232
|
-
onTaskSelect?.(task.id);
|
|
11233
|
-
event.currentTarget.focus();
|
|
11234
|
-
},
|
|
11235
|
-
onMouseEnter: () => {
|
|
11236
|
-
if (!isParent && isFillDraggingRef.current && selectedRange) {
|
|
11237
|
-
setFillRange({ anchor: selectedRange.anchor, focus: currentCell });
|
|
11238
|
-
setActiveCell(currentCell);
|
|
11239
|
-
onTaskSelect?.(task.id);
|
|
11240
|
-
return;
|
|
11241
|
-
}
|
|
11242
|
-
if (isParent || !isSelectingRef.current) return;
|
|
11243
|
-
didDragSelectRef.current = true;
|
|
11244
|
-
setSelectedRange((currentRange) => ({
|
|
11245
|
-
anchor: currentRange?.anchor ?? currentCell,
|
|
11246
|
-
focus: currentCell
|
|
11247
|
-
}));
|
|
11248
|
-
onTaskSelect?.(task.id);
|
|
11249
|
-
},
|
|
11250
|
-
onFocus: () => {
|
|
11251
|
-
if (isParent) return;
|
|
11252
|
-
setActiveCell({ taskId: task.id, dateIndex, kind });
|
|
11253
|
-
},
|
|
11254
|
-
onClick: (event) => {
|
|
11255
|
-
event.stopPropagation();
|
|
11256
|
-
if (didDragSelectRef.current) {
|
|
11257
|
-
didDragSelectRef.current = false;
|
|
11258
|
-
return;
|
|
11259
|
-
}
|
|
11260
|
-
onTaskSelect?.(task.id);
|
|
11261
|
-
if (isParent) return;
|
|
11262
|
-
selectSingleCell(currentCell);
|
|
11263
|
-
event.currentTarget.focus();
|
|
11264
|
-
},
|
|
11265
|
-
onDoubleClick: (event) => {
|
|
11266
|
-
event.stopPropagation();
|
|
11267
|
-
if (isParent) return;
|
|
11268
|
-
setEditingCell({ taskId: task.id, dateIndex, kind });
|
|
11269
|
-
},
|
|
11270
|
-
onKeyDown: (event) => {
|
|
11271
|
-
if (isParent || isEditing) return;
|
|
11272
|
-
if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
11273
|
-
event.preventDefault();
|
|
11274
|
-
event.stopPropagation();
|
|
11275
|
-
const direction = event.key.replace("Arrow", "").toLowerCase();
|
|
11276
|
-
if (event.shiftKey) {
|
|
11277
|
-
extendSelectedRange(selectedRange?.focus ?? currentCell, direction);
|
|
11278
|
-
} else {
|
|
11279
|
-
moveActiveCell(currentCell, direction);
|
|
11280
|
-
}
|
|
11281
|
-
return;
|
|
11282
|
-
}
|
|
11283
|
-
if (event.key === "Enter" || event.key === "F2") {
|
|
11284
|
-
event.preventDefault();
|
|
11285
|
-
event.stopPropagation();
|
|
11286
|
-
setEditingCell(selectedRange?.anchor ?? currentCell);
|
|
11287
|
-
return;
|
|
11288
|
-
}
|
|
11289
|
-
if (event.key === "Backspace" || event.key === "Delete") {
|
|
11290
|
-
event.preventDefault();
|
|
11291
|
-
event.stopPropagation();
|
|
11292
|
-
clearSelectedCells();
|
|
11293
|
-
return;
|
|
11294
|
-
}
|
|
11295
|
-
if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
11296
|
-
event.preventDefault();
|
|
11297
|
-
event.stopPropagation();
|
|
11298
|
-
setEditingCell({ ...currentCell, startValue: event.key });
|
|
11299
|
-
}
|
|
11300
|
-
},
|
|
11301
|
-
children: [
|
|
11302
|
-
isEditing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11303
|
-
PlanFactCellEditor,
|
|
11304
|
-
{
|
|
11305
|
-
value,
|
|
11306
|
-
startValue: editingCell.startValue,
|
|
11307
|
-
onCommit: (nextValue) => {
|
|
11308
|
-
commitCell(task, dateIndex, kind, nextValue);
|
|
11309
|
-
setEditingCell(null);
|
|
11310
|
-
const nextActiveCell = { taskId: task.id, dateIndex, kind };
|
|
11311
|
-
selectSingleCell(nextActiveCell);
|
|
11312
|
-
focusCell(nextActiveCell);
|
|
11313
|
-
},
|
|
11314
|
-
onCommitRange: (nextValue) => {
|
|
11315
|
-
commitSelectedCells(nextValue);
|
|
11316
|
-
setEditingCell(null);
|
|
11317
|
-
const nextActiveCell = { taskId: task.id, dateIndex, kind };
|
|
11318
|
-
setActiveCell(nextActiveCell);
|
|
11319
|
-
focusCell(nextActiveCell);
|
|
11320
|
-
},
|
|
11321
|
-
onCancel: () => {
|
|
11322
|
-
setEditingCell(null);
|
|
11323
|
-
const nextActiveCell = { taskId: task.id, dateIndex, kind };
|
|
11324
|
-
setActiveCell(nextActiveCell);
|
|
11325
|
-
focusCell(nextActiveCell);
|
|
11326
|
-
}
|
|
11327
|
-
}
|
|
11328
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11329
|
-
"span",
|
|
11330
|
-
{
|
|
11331
|
-
className: "gantt-pf-cellValue",
|
|
11332
|
-
onMouseEnter: (event) => {
|
|
11333
|
-
if (isParent || value === void 0) return;
|
|
11334
|
-
const compactValue = formatValue(value);
|
|
11335
|
-
const fullValue = formatTooltipValue(value);
|
|
11336
|
-
showOverflowTooltip(event.currentTarget, fullValue, compactValue !== fullValue);
|
|
11337
|
-
},
|
|
11338
|
-
onMouseLeave: hideOverflowTooltip,
|
|
11339
|
-
children: isParent ? "" : formatValue(value)
|
|
11340
|
-
}
|
|
11341
|
-
),
|
|
11342
|
-
showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
11343
|
-
"span",
|
|
11344
|
-
{
|
|
11345
|
-
className: "gantt-pf-fillHandle",
|
|
11346
|
-
"aria-hidden": "true",
|
|
11347
|
-
onMouseDown: (event) => {
|
|
11348
|
-
event.preventDefault();
|
|
11349
|
-
event.stopPropagation();
|
|
11350
|
-
isSelectingRef.current = false;
|
|
11351
|
-
isFillDraggingRef.current = true;
|
|
11352
|
-
setFillRange(selectedRange);
|
|
11353
|
-
}
|
|
11354
|
-
}
|
|
11355
|
-
)
|
|
11356
|
-
]
|
|
11357
|
-
},
|
|
11358
|
-
`${task.id}:${dateKey}:${kind}`
|
|
11359
|
-
);
|
|
11360
|
-
});
|
|
11361
|
-
})
|
|
11505
|
+
task,
|
|
11506
|
+
rowIndex,
|
|
11507
|
+
dateRange,
|
|
11508
|
+
dateKeys,
|
|
11509
|
+
renderedDateIndices,
|
|
11510
|
+
rowHeight,
|
|
11511
|
+
subrowHeight,
|
|
11512
|
+
dayWidth,
|
|
11513
|
+
plannedRange: plannedRangeByTaskId.get(task.id) ?? null,
|
|
11514
|
+
todayDateIndex,
|
|
11515
|
+
isParent,
|
|
11516
|
+
isHighlighted,
|
|
11517
|
+
selectedTaskId,
|
|
11518
|
+
activeCell,
|
|
11519
|
+
editingCell,
|
|
11520
|
+
selectedRange,
|
|
11521
|
+
renderedRangeBounds,
|
|
11522
|
+
didDragSelectRef,
|
|
11523
|
+
isSelectingRef,
|
|
11524
|
+
isFillDraggingRef,
|
|
11525
|
+
onTaskSelect,
|
|
11526
|
+
selectSingleCell,
|
|
11527
|
+
queueHoverCellUpdate,
|
|
11528
|
+
setActiveCell,
|
|
11529
|
+
setEditingCell,
|
|
11530
|
+
setFillRange,
|
|
11531
|
+
clearSelectedCells,
|
|
11532
|
+
commitCell,
|
|
11533
|
+
commitSelectedCells,
|
|
11534
|
+
moveActiveCell,
|
|
11535
|
+
extendSelectedRange,
|
|
11536
|
+
focusCell,
|
|
11537
|
+
showOverflowTooltip,
|
|
11538
|
+
hideOverflowTooltip
|
|
11362
11539
|
},
|
|
11363
11540
|
task.id
|
|
11364
11541
|
);
|
|
@@ -11794,6 +11971,57 @@ function createTaskPreviewPositionStore() {
|
|
|
11794
11971
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
11795
11972
|
var SCROLL_TO_ROW_CONTEXT_ROWS = 2;
|
|
11796
11973
|
var TASK_ROW_OVERSCAN = 8;
|
|
11974
|
+
var PLAN_FACT_COLUMN_OVERSCAN = 24;
|
|
11975
|
+
var PLAN_FACT_COLUMN_WINDOW_STEP = 14;
|
|
11976
|
+
function getFullMonthDays(tasks) {
|
|
11977
|
+
if (!tasks || tasks.length === 0) {
|
|
11978
|
+
return getMultiMonthDays(tasks);
|
|
11979
|
+
}
|
|
11980
|
+
let minDate = null;
|
|
11981
|
+
let maxDate = null;
|
|
11982
|
+
for (const task of tasks) {
|
|
11983
|
+
const start = parseUTCDate(task.startDate);
|
|
11984
|
+
const end = parseUTCDate(task.endDate);
|
|
11985
|
+
if (!minDate || start.getTime() < minDate.getTime()) {
|
|
11986
|
+
minDate = start;
|
|
11987
|
+
}
|
|
11988
|
+
if (!maxDate || end.getTime() > maxDate.getTime()) {
|
|
11989
|
+
maxDate = end;
|
|
11990
|
+
}
|
|
11991
|
+
}
|
|
11992
|
+
if (!minDate || !maxDate) {
|
|
11993
|
+
return getMultiMonthDays(tasks);
|
|
11994
|
+
}
|
|
11995
|
+
const startOfMonth2 = new Date(Date.UTC(minDate.getUTCFullYear(), minDate.getUTCMonth(), 1));
|
|
11996
|
+
const endOfMonth = new Date(Date.UTC(maxDate.getUTCFullYear(), maxDate.getUTCMonth() + 1, 0));
|
|
11997
|
+
const days = [];
|
|
11998
|
+
const current = new Date(startOfMonth2);
|
|
11999
|
+
while (current.getTime() <= endOfMonth.getTime()) {
|
|
12000
|
+
days.push(new Date(Date.UTC(
|
|
12001
|
+
current.getUTCFullYear(),
|
|
12002
|
+
current.getUTCMonth(),
|
|
12003
|
+
current.getUTCDate()
|
|
12004
|
+
)));
|
|
12005
|
+
current.setUTCDate(current.getUTCDate() + 1);
|
|
12006
|
+
}
|
|
12007
|
+
return days;
|
|
12008
|
+
}
|
|
12009
|
+
function getPlanFactRangeTasks(tasks) {
|
|
12010
|
+
const rangeTasks = [];
|
|
12011
|
+
for (const task of tasks) {
|
|
12012
|
+
rangeTasks.push({ startDate: task.startDate, endDate: task.endDate });
|
|
12013
|
+
for (const dateKey of Object.keys(task.planByDate ?? {})) {
|
|
12014
|
+
rangeTasks.push({ startDate: dateKey, endDate: dateKey });
|
|
12015
|
+
}
|
|
12016
|
+
for (const dateKey of Object.keys(task.factByDate ?? {})) {
|
|
12017
|
+
rangeTasks.push({ startDate: dateKey, endDate: dateKey });
|
|
12018
|
+
}
|
|
12019
|
+
}
|
|
12020
|
+
return rangeTasks;
|
|
12021
|
+
}
|
|
12022
|
+
function clampScrollValue(value, max) {
|
|
12023
|
+
return Math.min(max, Math.max(0, value));
|
|
12024
|
+
}
|
|
11797
12025
|
function arePositionMapsEqual(left, right) {
|
|
11798
12026
|
if (left.size !== right.size) return false;
|
|
11799
12027
|
for (const [taskId, leftPosition] of left) {
|
|
@@ -11916,6 +12144,7 @@ function TaskGanttChartInner(props, ref) {
|
|
|
11916
12144
|
const [taskListHasRightShadow, setTaskListHasRightShadow] = (0, import_react18.useState)(false);
|
|
11917
12145
|
const [internalTaskDateChangeMode, setInternalTaskDateChangeMode] = (0, import_react18.useState)("preserve-duration");
|
|
11918
12146
|
const [scrollViewport, setScrollViewport] = (0, import_react18.useState)({ scrollTop: 0, viewportHeight: 0 });
|
|
12147
|
+
const [planFactDateWindow, setPlanFactDateWindow] = (0, import_react18.useState)(null);
|
|
11919
12148
|
const [selectedChip, setSelectedChip] = (0, import_react18.useState)(null);
|
|
11920
12149
|
const [activeTimelineTooltip, setActiveTimelineTooltip] = (0, import_react18.useState)(null);
|
|
11921
12150
|
const [internalCollapsedParentIds, setInternalCollapsedParentIds] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
|
|
@@ -11934,6 +12163,9 @@ function TaskGanttChartInner(props, ref) {
|
|
|
11934
12163
|
[customDays, isWeekend3]
|
|
11935
12164
|
);
|
|
11936
12165
|
const dateRangeTasks = (0, import_react18.useMemo)(() => {
|
|
12166
|
+
if (isPlanFactMode) {
|
|
12167
|
+
return getPlanFactRangeTasks(normalizedTasks);
|
|
12168
|
+
}
|
|
11937
12169
|
if (!showBaseline) {
|
|
11938
12170
|
return normalizedTasks;
|
|
11939
12171
|
}
|
|
@@ -11942,8 +12174,11 @@ function TaskGanttChartInner(props, ref) {
|
|
|
11942
12174
|
startDate: task.baselineStartDate && parseUTCDate(task.baselineStartDate).getTime() < parseUTCDate(task.startDate).getTime() ? task.baselineStartDate : task.startDate,
|
|
11943
12175
|
endDate: task.baselineEndDate && parseUTCDate(task.baselineEndDate).getTime() > parseUTCDate(task.endDate).getTime() ? task.baselineEndDate : task.endDate
|
|
11944
12176
|
}));
|
|
11945
|
-
}, [normalizedTasks, showBaseline]);
|
|
11946
|
-
const dateRange = (0, import_react18.useMemo)(
|
|
12177
|
+
}, [isPlanFactMode, normalizedTasks, showBaseline]);
|
|
12178
|
+
const dateRange = (0, import_react18.useMemo)(
|
|
12179
|
+
() => isPlanFactMode ? getFullMonthDays(dateRangeTasks) : getMultiMonthDays(dateRangeTasks),
|
|
12180
|
+
[dateRangeTasks, isPlanFactMode]
|
|
12181
|
+
);
|
|
11947
12182
|
const [validationResult, setValidationResult] = (0, import_react18.useState)(null);
|
|
11948
12183
|
const [cascadeOverrides, setCascadeOverrides] = (0, import_react18.useState)(/* @__PURE__ */ new Map());
|
|
11949
12184
|
const gridWidth = (0, import_react18.useMemo)(
|
|
@@ -12009,11 +12244,12 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12009
12244
|
const firstDay = dateRange[0];
|
|
12010
12245
|
return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
|
|
12011
12246
|
}, [dateRange]);
|
|
12012
|
-
const
|
|
12247
|
+
const todayIndex = (0, import_react18.useMemo)(() => {
|
|
12013
12248
|
const now = /* @__PURE__ */ new Date();
|
|
12014
12249
|
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
12015
|
-
return dateRange.
|
|
12250
|
+
return dateRange.findIndex((day) => day.getTime() === today.getTime());
|
|
12016
12251
|
}, [dateRange]);
|
|
12252
|
+
const todayInRange = todayIndex !== -1;
|
|
12017
12253
|
const visibleTimelineMarkers = (0, import_react18.useMemo)(() => {
|
|
12018
12254
|
if (isTableMatrixMode || !timelineMarkers || timelineMarkers.length === 0 || dateRange.length === 0) {
|
|
12019
12255
|
return [];
|
|
@@ -12032,9 +12268,9 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12032
12268
|
if (!container || dateRange.length === 0) return;
|
|
12033
12269
|
const now = /* @__PURE__ */ new Date();
|
|
12034
12270
|
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
12035
|
-
const
|
|
12036
|
-
if (
|
|
12037
|
-
const todayOffset =
|
|
12271
|
+
const todayIndex2 = dateRange.findIndex((day) => day.getTime() === today.getTime());
|
|
12272
|
+
if (todayIndex2 === -1) return;
|
|
12273
|
+
const todayOffset = todayIndex2 * dayWidth;
|
|
12038
12274
|
const containerWidth = container.clientWidth;
|
|
12039
12275
|
const scrollLeft = Math.round(todayOffset + dayWidth / 2 - containerWidth * 0.3);
|
|
12040
12276
|
container.scrollLeft = Math.max(0, scrollLeft);
|
|
@@ -12048,13 +12284,33 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12048
12284
|
frameId = null;
|
|
12049
12285
|
const nextHasRightShadow = container.scrollLeft > 0;
|
|
12050
12286
|
const nextViewportHeight = Math.max(0, container.clientHeight - timelineHeaderHeight);
|
|
12287
|
+
const nextViewportWidth = Math.max(0, container.clientWidth - (showTaskList ? taskListWidth : 0));
|
|
12051
12288
|
const nextScrollTop = container.scrollTop;
|
|
12289
|
+
const nextScrollLeft = container.scrollLeft;
|
|
12290
|
+
const nextChartScrollLeft = Math.max(0, nextScrollLeft);
|
|
12052
12291
|
setTaskListHasRightShadow(
|
|
12053
12292
|
(previous) => previous === nextHasRightShadow ? previous : nextHasRightShadow
|
|
12054
12293
|
);
|
|
12055
12294
|
setScrollViewport(
|
|
12056
12295
|
(previous) => previous.scrollTop === nextScrollTop && previous.viewportHeight === nextViewportHeight ? previous : { scrollTop: nextScrollTop, viewportHeight: nextViewportHeight }
|
|
12057
12296
|
);
|
|
12297
|
+
setPlanFactDateWindow((previous) => {
|
|
12298
|
+
if (!isPlanFactMode || dateRange.length === 0 || nextViewportWidth <= 0) {
|
|
12299
|
+
return previous === null ? previous : null;
|
|
12300
|
+
}
|
|
12301
|
+
const firstVisibleColumn = Math.max(0, Math.floor(nextChartScrollLeft / dayWidth));
|
|
12302
|
+
const visibleColumnCount = Math.max(1, Math.ceil(nextViewportWidth / dayWidth));
|
|
12303
|
+
const lastVisibleColumn = Math.min(dateRange.length - 1, firstVisibleColumn + visibleColumnCount - 1);
|
|
12304
|
+
const rangeStart = Math.max(
|
|
12305
|
+
0,
|
|
12306
|
+
Math.floor(Math.max(0, firstVisibleColumn - PLAN_FACT_COLUMN_OVERSCAN) / PLAN_FACT_COLUMN_WINDOW_STEP) * PLAN_FACT_COLUMN_WINDOW_STEP
|
|
12307
|
+
);
|
|
12308
|
+
const rangeEnd = Math.min(
|
|
12309
|
+
dateRange.length - 1,
|
|
12310
|
+
Math.ceil((lastVisibleColumn + PLAN_FACT_COLUMN_OVERSCAN + 1) / PLAN_FACT_COLUMN_WINDOW_STEP) * PLAN_FACT_COLUMN_WINDOW_STEP - 1
|
|
12311
|
+
);
|
|
12312
|
+
return previous?.start === rangeStart && previous.end === rangeEnd ? previous : { start: rangeStart, end: rangeEnd };
|
|
12313
|
+
});
|
|
12058
12314
|
};
|
|
12059
12315
|
const scheduleUpdate = () => {
|
|
12060
12316
|
if (frameId !== null) return;
|
|
@@ -12075,16 +12331,16 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12075
12331
|
container.removeEventListener("scroll", scheduleUpdate);
|
|
12076
12332
|
window.removeEventListener("resize", scheduleUpdate);
|
|
12077
12333
|
};
|
|
12078
|
-
}, [timelineHeaderHeight]);
|
|
12334
|
+
}, [dateRange.length, dayWidth, isPlanFactMode, showTaskList, taskListWidth, timelineHeaderHeight]);
|
|
12079
12335
|
const scrollToToday = (0, import_react18.useCallback)(() => {
|
|
12080
12336
|
if (isTableMatrixMode) return;
|
|
12081
12337
|
const container = scrollContainerRef.current;
|
|
12082
12338
|
if (!container || dateRange.length === 0) return;
|
|
12083
12339
|
const now = /* @__PURE__ */ new Date();
|
|
12084
12340
|
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
12085
|
-
const
|
|
12086
|
-
if (
|
|
12087
|
-
const todayOffset =
|
|
12341
|
+
const todayIndex2 = dateRange.findIndex((day) => day.getTime() === today.getTime());
|
|
12342
|
+
if (todayIndex2 === -1) return;
|
|
12343
|
+
const todayOffset = todayIndex2 * dayWidth;
|
|
12088
12344
|
const containerWidth = container.clientWidth;
|
|
12089
12345
|
const scrollLeft = Math.round(todayOffset + dayWidth / 2 - containerWidth * 0.3);
|
|
12090
12346
|
container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
|
|
@@ -12320,6 +12576,15 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12320
12576
|
}
|
|
12321
12577
|
return Array.from(indices).sort((left, right) => left - right);
|
|
12322
12578
|
}, [effectiveRowHeight, forcedRenderedTaskIds, scrollViewport, visibleTaskIndexMap, visibleTasks.length]);
|
|
12579
|
+
const visiblePlanFactDateIndices = (0, import_react18.useMemo)(() => {
|
|
12580
|
+
if (!isPlanFactMode || dateRange.length === 0 || !planFactDateWindow) {
|
|
12581
|
+
return void 0;
|
|
12582
|
+
}
|
|
12583
|
+
return Array.from(
|
|
12584
|
+
{ length: planFactDateWindow.end - planFactDateWindow.start + 1 },
|
|
12585
|
+
(_, index) => planFactDateWindow.start + index
|
|
12586
|
+
);
|
|
12587
|
+
}, [dateRange.length, isPlanFactMode, planFactDateWindow]);
|
|
12323
12588
|
const renderedChartTasks = (0, import_react18.useMemo)(
|
|
12324
12589
|
() => visibleTaskWindowIndices.map((index) => {
|
|
12325
12590
|
const task = previewVisibleTasks[index];
|
|
@@ -12576,7 +12841,10 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12576
12841
|
startX: e.clientX,
|
|
12577
12842
|
startY: e.clientY,
|
|
12578
12843
|
scrollX: container.scrollLeft,
|
|
12579
|
-
scrollY: container.scrollTop
|
|
12844
|
+
scrollY: container.scrollTop,
|
|
12845
|
+
currentX: e.clientX,
|
|
12846
|
+
currentY: e.clientY,
|
|
12847
|
+
frameId: null
|
|
12580
12848
|
};
|
|
12581
12849
|
if (document.activeElement instanceof HTMLElement) {
|
|
12582
12850
|
document.activeElement.blur();
|
|
@@ -12585,16 +12853,37 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12585
12853
|
e.preventDefault();
|
|
12586
12854
|
}, []);
|
|
12587
12855
|
(0, import_react18.useEffect)(() => {
|
|
12588
|
-
const
|
|
12856
|
+
const flushPanMove = () => {
|
|
12589
12857
|
const pan = panStateRef.current;
|
|
12590
12858
|
if (!pan?.active) return;
|
|
12859
|
+
pan.frameId = null;
|
|
12591
12860
|
const container = scrollContainerRef.current;
|
|
12592
12861
|
if (!container) return;
|
|
12593
|
-
|
|
12594
|
-
|
|
12862
|
+
const maxScrollLeft = Math.max(0, container.scrollWidth - container.clientWidth);
|
|
12863
|
+
const maxScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
|
|
12864
|
+
const nextScrollLeft = clampScrollValue(pan.scrollX - (pan.currentX - pan.startX), maxScrollLeft);
|
|
12865
|
+
const nextScrollTop = clampScrollValue(pan.scrollY - (pan.currentY - pan.startY), maxScrollTop);
|
|
12866
|
+
if (Math.abs(container.scrollLeft - nextScrollLeft) > 0.5) {
|
|
12867
|
+
container.scrollLeft = nextScrollLeft;
|
|
12868
|
+
}
|
|
12869
|
+
if (Math.abs(container.scrollTop - nextScrollTop) > 0.5) {
|
|
12870
|
+
container.scrollTop = nextScrollTop;
|
|
12871
|
+
}
|
|
12872
|
+
};
|
|
12873
|
+
const handlePanMove = (e) => {
|
|
12874
|
+
const pan = panStateRef.current;
|
|
12875
|
+
if (!pan?.active) return;
|
|
12876
|
+
pan.currentX = e.clientX;
|
|
12877
|
+
pan.currentY = e.clientY;
|
|
12878
|
+
if (pan.frameId !== null) return;
|
|
12879
|
+
pan.frameId = window.requestAnimationFrame(flushPanMove);
|
|
12595
12880
|
};
|
|
12596
12881
|
const handlePanEnd = () => {
|
|
12597
|
-
|
|
12882
|
+
const pan = panStateRef.current;
|
|
12883
|
+
if (!pan?.active) return;
|
|
12884
|
+
if (pan.frameId !== null) {
|
|
12885
|
+
window.cancelAnimationFrame(pan.frameId);
|
|
12886
|
+
}
|
|
12598
12887
|
panStateRef.current = null;
|
|
12599
12888
|
const container = scrollContainerRef.current;
|
|
12600
12889
|
if (container) container.style.cursor = "";
|
|
@@ -12721,7 +13010,10 @@ function TaskGanttChartInner(props, ref) {
|
|
|
12721
13010
|
onTasksChange: handleTaskChange,
|
|
12722
13011
|
onCellCommit: onPlanFactCellCommit,
|
|
12723
13012
|
highlightedTaskIds: taskListHighlightedTaskIds,
|
|
12724
|
-
filterMode
|
|
13013
|
+
filterMode,
|
|
13014
|
+
visibleRowIndices: visibleTaskWindowIndices,
|
|
13015
|
+
visibleDateIndices: visiblePlanFactDateIndices,
|
|
13016
|
+
todayDateIndex: todayInRange ? todayIndex : void 0
|
|
12725
13017
|
}
|
|
12726
13018
|
) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
12727
13019
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|