gantt-lib 0.111.0 → 0.112.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.js CHANGED
@@ -41,6 +41,7 @@ __export(index_exports, {
41
41
  GanttChart: () => GanttChart,
42
42
  GridBackground: () => GridBackground_default,
43
43
  Input: () => Input,
44
+ PlanFactMatrix: () => PlanFactMatrix,
44
45
  Popover: () => Popover,
45
46
  PopoverContent: () => PopoverContent,
46
47
  PopoverTrigger: () => PopoverTrigger,
@@ -144,7 +145,7 @@ __export(index_exports, {
144
145
  module.exports = __toCommonJS(index_exports);
145
146
 
146
147
  // src/components/GanttChart/GanttChart.tsx
147
- var import_react17 = require("react");
148
+ var import_react18 = require("react");
148
149
 
149
150
  // src/core/scheduling/dateMath.ts
150
151
  var DAY_MS = 24 * 60 * 60 * 1e3;
@@ -10564,6 +10565,824 @@ function TableMatrix({
10564
10565
  ] });
10565
10566
  }
10566
10567
 
10568
+ // src/components/PlanFactMatrix/PlanFactMatrix.tsx
10569
+ var import_react17 = require("react");
10570
+ var import_jsx_runtime18 = require("react/jsx-runtime");
10571
+ function joinClasses2(...values) {
10572
+ return values.filter(Boolean).join(" ");
10573
+ }
10574
+ function formatDateKey(date) {
10575
+ const year = date.getUTCFullYear();
10576
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
10577
+ const day = String(date.getUTCDate()).padStart(2, "0");
10578
+ return `${year}-${month}-${day}`;
10579
+ }
10580
+ function escapeAttributeValue(value) {
10581
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
10582
+ }
10583
+ function parseNumberInput(value) {
10584
+ const trimmed = value.trim();
10585
+ if (trimmed === "") return void 0;
10586
+ const normalized = trimmed.replace(/\s+/g, "").replace(",", ".");
10587
+ const parsed = Number(normalized);
10588
+ if (!Number.isFinite(parsed) || parsed < 0) {
10589
+ return null;
10590
+ }
10591
+ return parsed;
10592
+ }
10593
+ function isDateWithinTask(task, date) {
10594
+ const start = parseUTCDate(task.startDate);
10595
+ const end = parseUTCDate(task.endDate);
10596
+ const dateMs = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
10597
+ const startMs = Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate());
10598
+ const endMs = Date.UTC(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate());
10599
+ return startMs <= dateMs && dateMs <= endMs;
10600
+ }
10601
+ function formatValue(value) {
10602
+ if (value === void 0) return "";
10603
+ const absoluteValue = Math.abs(value);
10604
+ if (absoluteValue >= 1e6) {
10605
+ const compactValue = value / 1e6;
10606
+ const fractionDigits = absoluteValue >= 1e7 ? 0 : 1;
10607
+ return `${compactValue.toLocaleString("ru-RU", {
10608
+ minimumFractionDigits: 0,
10609
+ maximumFractionDigits: fractionDigits,
10610
+ useGrouping: false
10611
+ })}m`;
10612
+ }
10613
+ if (absoluteValue >= 1e4) {
10614
+ const compactValue = value / 1e3;
10615
+ const fractionDigits = absoluteValue >= 1e5 ? 0 : 1;
10616
+ return `${compactValue.toLocaleString("ru-RU", {
10617
+ minimumFractionDigits: 0,
10618
+ maximumFractionDigits: fractionDigits,
10619
+ useGrouping: false
10620
+ })}k`;
10621
+ }
10622
+ return Number.isInteger(value) ? String(value) : String(value).replace(".", ",");
10623
+ }
10624
+ function formatTooltipValue(value) {
10625
+ if (value === void 0) return "";
10626
+ return value.toLocaleString("ru-RU", { maximumFractionDigits: 20 });
10627
+ }
10628
+ function getSubrowIndex(taskIndex, kind) {
10629
+ return taskIndex * 2 + (kind === "plan" ? 0 : 1);
10630
+ }
10631
+ function findEditableTaskIndex(tasks, parentTaskIds, startIndex, step) {
10632
+ let index = startIndex;
10633
+ while (index >= 0 && index < tasks.length) {
10634
+ if (!parentTaskIds.has(tasks[index].id)) {
10635
+ return index;
10636
+ }
10637
+ index += step;
10638
+ }
10639
+ return null;
10640
+ }
10641
+ function PlanFactCellEditor({
10642
+ value,
10643
+ startValue,
10644
+ onCommit,
10645
+ onCommitRange,
10646
+ onCancel
10647
+ }) {
10648
+ const [draft, setDraft] = (0, import_react17.useState)(startValue ?? (value === void 0 ? "" : String(value)));
10649
+ const inputRef = (0, import_react17.useRef)(null);
10650
+ (0, import_react17.useEffect)(() => {
10651
+ inputRef.current?.focus();
10652
+ if (startValue === void 0) {
10653
+ inputRef.current?.select();
10654
+ } else {
10655
+ inputRef.current?.setSelectionRange(startValue.length, startValue.length);
10656
+ }
10657
+ }, [startValue]);
10658
+ const commit = (0, import_react17.useCallback)(() => {
10659
+ const parsed = parseNumberInput(draft);
10660
+ if (parsed === null) {
10661
+ onCancel();
10662
+ return;
10663
+ }
10664
+ onCommit(parsed);
10665
+ }, [draft, onCancel, onCommit]);
10666
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
10667
+ "input",
10668
+ {
10669
+ ref: inputRef,
10670
+ value: draft,
10671
+ className: "gantt-pf-editor",
10672
+ inputMode: "decimal",
10673
+ onChange: (event) => setDraft(event.target.value),
10674
+ onBlur: commit,
10675
+ onKeyDown: (event) => {
10676
+ if (event.key === "Escape") {
10677
+ event.preventDefault();
10678
+ onCancel();
10679
+ return;
10680
+ }
10681
+ if (event.key === "Enter") {
10682
+ event.preventDefault();
10683
+ if (event.ctrlKey || event.metaKey) {
10684
+ const parsed = parseNumberInput(draft);
10685
+ if (parsed === null) {
10686
+ onCancel();
10687
+ return;
10688
+ }
10689
+ onCommitRange?.(parsed);
10690
+ } else {
10691
+ commit();
10692
+ }
10693
+ }
10694
+ }
10695
+ }
10696
+ );
10697
+ }
10698
+ function PlanFactMatrix({
10699
+ tasks,
10700
+ allTasks = tasks,
10701
+ dateRange,
10702
+ dayWidth,
10703
+ rowHeight,
10704
+ headerHeight,
10705
+ bodyMinHeight,
10706
+ selectedTaskId,
10707
+ onTaskSelect,
10708
+ onTasksChange,
10709
+ onCellCommit,
10710
+ highlightedTaskIds,
10711
+ filterMode = "highlight"
10712
+ }) {
10713
+ const [activeCell, setActiveCell] = (0, import_react17.useState)(null);
10714
+ const [editingCell, setEditingCell] = (0, import_react17.useState)(null);
10715
+ const [selectedRange, setSelectedRange] = (0, import_react17.useState)(null);
10716
+ const [fillRange, setFillRange] = (0, import_react17.useState)(null);
10717
+ const [overflowTooltip, setOverflowTooltip] = (0, import_react17.useState)(null);
10718
+ const isSelectingRef = (0, import_react17.useRef)(false);
10719
+ const isFillDraggingRef = (0, import_react17.useRef)(false);
10720
+ const didDragSelectRef = (0, import_react17.useRef)(false);
10721
+ const rootRef = (0, import_react17.useRef)(null);
10722
+ const bodyRef = (0, import_react17.useRef)(null);
10723
+ const totalWidth = dateRange.length * dayWidth;
10724
+ const subrowHeight = rowHeight / 2;
10725
+ const parentTaskIds = (0, import_react17.useMemo)(() => {
10726
+ const ids = /* @__PURE__ */ new Set();
10727
+ for (const task of allTasks) {
10728
+ if (task.parentId) ids.add(task.parentId);
10729
+ }
10730
+ return ids;
10731
+ }, [allTasks]);
10732
+ const dateKeys = (0, import_react17.useMemo)(() => dateRange.map(formatDateKey), [dateRange]);
10733
+ const taskIndexById = (0, import_react17.useMemo)(() => {
10734
+ const indexById = /* @__PURE__ */ new Map();
10735
+ tasks.forEach((task, index) => indexById.set(task.id, index));
10736
+ return indexById;
10737
+ }, [tasks]);
10738
+ const focusCell = (0, import_react17.useCallback)((cell) => {
10739
+ window.requestAnimationFrame(() => {
10740
+ const selector = [
10741
+ `[data-plan-fact-task-id="${escapeAttributeValue(cell.taskId)}"]`,
10742
+ `[data-plan-fact-date-index="${cell.dateIndex}"]`,
10743
+ `[data-plan-fact-kind="${cell.kind}"]`
10744
+ ].join("");
10745
+ bodyRef.current?.querySelector(selector)?.focus();
10746
+ });
10747
+ }, []);
10748
+ const clearSelection = (0, import_react17.useCallback)(() => {
10749
+ isSelectingRef.current = false;
10750
+ isFillDraggingRef.current = false;
10751
+ didDragSelectRef.current = false;
10752
+ setActiveCell(null);
10753
+ setEditingCell(null);
10754
+ setSelectedRange(null);
10755
+ setFillRange(null);
10756
+ setOverflowTooltip(null);
10757
+ }, []);
10758
+ const hideOverflowTooltip = (0, import_react17.useCallback)(() => {
10759
+ setOverflowTooltip(null);
10760
+ }, []);
10761
+ const showOverflowTooltip = (0, import_react17.useCallback)((target, label, force = false) => {
10762
+ if (!rootRef.current || !label) return;
10763
+ if (!force && target.scrollWidth <= target.clientWidth) {
10764
+ setOverflowTooltip(null);
10765
+ return;
10766
+ }
10767
+ const rootRect = rootRef.current.getBoundingClientRect();
10768
+ const targetRect = target.getBoundingClientRect();
10769
+ setOverflowTooltip({
10770
+ label,
10771
+ left: targetRect.left - rootRect.left + targetRect.width / 2,
10772
+ top: targetRect.top - rootRect.top
10773
+ });
10774
+ }, []);
10775
+ const selectSingleCell = (0, import_react17.useCallback)((cell) => {
10776
+ setActiveCell(cell);
10777
+ setSelectedRange({ anchor: cell, focus: cell });
10778
+ setFillRange(null);
10779
+ }, []);
10780
+ const getRangeBounds = (0, import_react17.useCallback)((range) => {
10781
+ const anchorTaskIndex = taskIndexById.get(range.anchor.taskId);
10782
+ const focusTaskIndex = taskIndexById.get(range.focus.taskId);
10783
+ if (anchorTaskIndex === void 0 || focusTaskIndex === void 0) {
10784
+ return null;
10785
+ }
10786
+ return {
10787
+ fromDateIndex: Math.min(range.anchor.dateIndex, range.focus.dateIndex),
10788
+ toDateIndex: Math.max(range.anchor.dateIndex, range.focus.dateIndex),
10789
+ fromSubrowIndex: Math.min(
10790
+ getSubrowIndex(anchorTaskIndex, range.anchor.kind),
10791
+ getSubrowIndex(focusTaskIndex, range.focus.kind)
10792
+ ),
10793
+ toSubrowIndex: Math.max(
10794
+ getSubrowIndex(anchorTaskIndex, range.anchor.kind),
10795
+ getSubrowIndex(focusTaskIndex, range.focus.kind)
10796
+ )
10797
+ };
10798
+ }, [taskIndexById]);
10799
+ const getCellFromPosition = (0, import_react17.useCallback)((subrowIndex, dateIndex) => {
10800
+ const task = tasks[Math.floor(subrowIndex / 2)];
10801
+ if (!task) return null;
10802
+ return {
10803
+ taskId: task.id,
10804
+ dateIndex,
10805
+ kind: subrowIndex % 2 === 0 ? "plan" : "fact"
10806
+ };
10807
+ }, [tasks]);
10808
+ const isCellInRange = (0, import_react17.useCallback)((cell, range) => {
10809
+ const bounds = getRangeBounds(range);
10810
+ if (!bounds) return false;
10811
+ const cellTaskIndex = taskIndexById.get(cell.taskId);
10812
+ if (cellTaskIndex === void 0) {
10813
+ return false;
10814
+ }
10815
+ const cellSubrowIndex = getSubrowIndex(cellTaskIndex, cell.kind);
10816
+ return cell.dateIndex >= bounds.fromDateIndex && cell.dateIndex <= bounds.toDateIndex && cellSubrowIndex >= bounds.fromSubrowIndex && cellSubrowIndex <= bounds.toSubrowIndex;
10817
+ }, [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
+ const commitCell = (0, import_react17.useCallback)((task, dateIndex, kind, value) => {
10846
+ const dateKey = dateKeys[dateIndex];
10847
+ const source = kind === "plan" ? task.planByDate : task.factByDate;
10848
+ const nextValues = { ...source ?? {} };
10849
+ if (value === void 0) {
10850
+ delete nextValues[dateKey];
10851
+ } else {
10852
+ nextValues[dateKey] = value;
10853
+ }
10854
+ const changedTask = {
10855
+ ...task,
10856
+ [kind === "plan" ? "planByDate" : "factByDate"]: nextValues
10857
+ };
10858
+ onTasksChange?.([changedTask]);
10859
+ onCellCommit?.({
10860
+ task,
10861
+ date: dateRange[dateIndex],
10862
+ dateKey,
10863
+ kind,
10864
+ value
10865
+ });
10866
+ }, [dateKeys, dateRange, onCellCommit, onTasksChange]);
10867
+ const clearSelectedCells = (0, import_react17.useCallback)(() => {
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
+ }
10875
+ const changedTasksById = /* @__PURE__ */ new Map();
10876
+ for (const task of tasks) {
10877
+ if (parentTaskIds.has(task.id)) continue;
10878
+ let nextPlanByDate = task.planByDate;
10879
+ let nextFactByDate = task.factByDate;
10880
+ let didChange = false;
10881
+ for (let dateIndex = 0; dateIndex < dateKeys.length; dateIndex += 1) {
10882
+ const dateKey = dateKeys[dateIndex];
10883
+ const planCell = { taskId: task.id, dateIndex, kind: "plan" };
10884
+ if (isCellInSelectedRange(planCell) && nextPlanByDate?.[dateKey] !== void 0) {
10885
+ nextPlanByDate = { ...nextPlanByDate ?? {} };
10886
+ delete nextPlanByDate[dateKey];
10887
+ didChange = true;
10888
+ }
10889
+ const factCell = { taskId: task.id, dateIndex, kind: "fact" };
10890
+ if (isCellInSelectedRange(factCell) && nextFactByDate?.[dateKey] !== void 0) {
10891
+ nextFactByDate = { ...nextFactByDate ?? {} };
10892
+ delete nextFactByDate[dateKey];
10893
+ didChange = true;
10894
+ }
10895
+ }
10896
+ if (didChange) {
10897
+ changedTasksById.set(task.id, {
10898
+ ...task,
10899
+ ...nextPlanByDate !== task.planByDate ? { planByDate: nextPlanByDate ?? {} } : {},
10900
+ ...nextFactByDate !== task.factByDate ? { factByDate: nextFactByDate ?? {} } : {}
10901
+ });
10902
+ }
10903
+ }
10904
+ const changedTasks = Array.from(changedTasksById.values());
10905
+ if (changedTasks.length > 0) {
10906
+ onTasksChange?.(changedTasks);
10907
+ }
10908
+ }, [activeCell, commitCell, dateKeys, isCellInSelectedRange, onTasksChange, parentTaskIds, selectedRange, tasks]);
10909
+ const commitSelectedCells = (0, import_react17.useCallback)((value) => {
10910
+ if (!selectedRange) {
10911
+ if (!activeCell) return;
10912
+ const task = tasks.find((candidate) => candidate.id === activeCell.taskId);
10913
+ if (!task || parentTaskIds.has(task.id)) return;
10914
+ commitCell(task, activeCell.dateIndex, activeCell.kind, value);
10915
+ return;
10916
+ }
10917
+ const changedTasksById = /* @__PURE__ */ new Map();
10918
+ for (const task of tasks) {
10919
+ if (parentTaskIds.has(task.id)) continue;
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);
10957
+ }
10958
+ }, [activeCell, commitCell, dateKeys, isCellInSelectedRange, onTasksChange, parentTaskIds, selectedRange, tasks]);
10959
+ const getCellValue = (0, import_react17.useCallback)((cell) => {
10960
+ const task = tasks.find((candidate) => candidate.id === cell.taskId);
10961
+ if (!task) return void 0;
10962
+ const dateKey = dateKeys[cell.dateIndex];
10963
+ return cell.kind === "plan" ? task.planByDate?.[dateKey] : task.factByDate?.[dateKey];
10964
+ }, [dateKeys, tasks]);
10965
+ const applyFillRange = (0, import_react17.useCallback)(() => {
10966
+ if (!selectedRange || !fillRange) return;
10967
+ const sourceBounds = getRangeBounds(selectedRange);
10968
+ const targetBounds = getRangeBounds(fillRange);
10969
+ if (!sourceBounds || !targetBounds) return;
10970
+ const sourceDateSpan = sourceBounds.toDateIndex - sourceBounds.fromDateIndex + 1;
10971
+ const sourceSubrowSpan = sourceBounds.toSubrowIndex - sourceBounds.fromSubrowIndex + 1;
10972
+ const changedTasksById = /* @__PURE__ */ new Map();
10973
+ for (let subrowIndex = targetBounds.fromSubrowIndex; subrowIndex <= targetBounds.toSubrowIndex; subrowIndex += 1) {
10974
+ const targetCellForRow = getCellFromPosition(subrowIndex, targetBounds.fromDateIndex);
10975
+ if (!targetCellForRow || parentTaskIds.has(targetCellForRow.taskId)) continue;
10976
+ const originalTask = tasks.find((task) => task.id === targetCellForRow.taskId);
10977
+ if (!originalTask) continue;
10978
+ let changedTask = changedTasksById.get(originalTask.id) ?? originalTask;
10979
+ let nextPlanByDate = changedTask.planByDate;
10980
+ let nextFactByDate = changedTask.factByDate;
10981
+ let didChange = false;
10982
+ for (let dateIndex = targetBounds.fromDateIndex; dateIndex <= targetBounds.toDateIndex; dateIndex += 1) {
10983
+ const targetCell = getCellFromPosition(subrowIndex, dateIndex);
10984
+ if (!targetCell || isCellInRange(targetCell, selectedRange)) continue;
10985
+ const sourceSubrowIndex = sourceBounds.fromSubrowIndex + ((subrowIndex - sourceBounds.fromSubrowIndex) % sourceSubrowSpan + sourceSubrowSpan) % sourceSubrowSpan;
10986
+ const sourceDateIndex = sourceBounds.fromDateIndex + ((dateIndex - sourceBounds.fromDateIndex) % sourceDateSpan + sourceDateSpan) % sourceDateSpan;
10987
+ const sourceCell = getCellFromPosition(sourceSubrowIndex, sourceDateIndex);
10988
+ if (!sourceCell) continue;
10989
+ const nextValue = getCellValue(sourceCell);
10990
+ const dateKey = dateKeys[dateIndex];
10991
+ const currentValues = targetCell.kind === "plan" ? nextPlanByDate : nextFactByDate;
10992
+ if (currentValues?.[dateKey] === nextValue) continue;
10993
+ if (targetCell.kind === "plan") {
10994
+ nextPlanByDate = { ...nextPlanByDate ?? {} };
10995
+ if (nextValue === void 0) {
10996
+ delete nextPlanByDate[dateKey];
10997
+ } else {
10998
+ nextPlanByDate[dateKey] = nextValue;
10999
+ }
11000
+ } else {
11001
+ nextFactByDate = { ...nextFactByDate ?? {} };
11002
+ if (nextValue === void 0) {
11003
+ delete nextFactByDate[dateKey];
11004
+ } else {
11005
+ nextFactByDate[dateKey] = nextValue;
11006
+ }
11007
+ }
11008
+ didChange = true;
11009
+ }
11010
+ if (didChange) {
11011
+ changedTasksById.set(originalTask.id, {
11012
+ ...changedTask,
11013
+ ...nextPlanByDate !== changedTask.planByDate ? { planByDate: nextPlanByDate ?? {} } : {},
11014
+ ...nextFactByDate !== changedTask.factByDate ? { factByDate: nextFactByDate ?? {} } : {}
11015
+ });
11016
+ }
11017
+ }
11018
+ const changedTasks = Array.from(changedTasksById.values());
11019
+ if (changedTasks.length > 0) {
11020
+ onTasksChange?.(changedTasks);
11021
+ }
11022
+ setSelectedRange(fillRange);
11023
+ setActiveCell(fillRange.anchor);
11024
+ setFillRange(null);
11025
+ }, [
11026
+ dateKeys,
11027
+ fillRange,
11028
+ getCellFromPosition,
11029
+ getCellValue,
11030
+ getRangeBounds,
11031
+ isCellInRange,
11032
+ onTasksChange,
11033
+ parentTaskIds,
11034
+ selectedRange,
11035
+ tasks
11036
+ ]);
11037
+ const moveActiveCell = (0, import_react17.useCallback)((cell, direction) => {
11038
+ const taskIndex = tasks.findIndex((task) => task.id === cell.taskId);
11039
+ if (taskIndex < 0) return;
11040
+ let nextCell = { ...cell };
11041
+ if (direction === "left") {
11042
+ nextCell.dateIndex = Math.max(0, cell.dateIndex - 1);
11043
+ } else if (direction === "right") {
11044
+ nextCell.dateIndex = Math.min(dateRange.length - 1, cell.dateIndex + 1);
11045
+ } else if (direction === "up") {
11046
+ if (cell.kind === "fact") {
11047
+ nextCell.kind = "plan";
11048
+ } else {
11049
+ const previousEditableTaskIndex = findEditableTaskIndex(tasks, parentTaskIds, taskIndex - 1, -1);
11050
+ if (previousEditableTaskIndex === null) return;
11051
+ const previousTask = tasks[previousEditableTaskIndex];
11052
+ nextCell = { taskId: previousTask.id, dateIndex: cell.dateIndex, kind: "fact" };
11053
+ }
11054
+ } else if (direction === "down") {
11055
+ if (cell.kind === "plan") {
11056
+ nextCell.kind = "fact";
11057
+ } else {
11058
+ const nextEditableTaskIndex = findEditableTaskIndex(tasks, parentTaskIds, taskIndex + 1, 1);
11059
+ if (nextEditableTaskIndex === null) return;
11060
+ const nextTask = tasks[nextEditableTaskIndex];
11061
+ nextCell = { taskId: nextTask.id, dateIndex: cell.dateIndex, kind: "plan" };
11062
+ }
11063
+ }
11064
+ setActiveCell(nextCell);
11065
+ setSelectedRange({ anchor: nextCell, focus: nextCell });
11066
+ onTaskSelect?.(nextCell.taskId);
11067
+ focusCell(nextCell);
11068
+ }, [dateRange.length, focusCell, onTaskSelect, parentTaskIds, tasks]);
11069
+ const extendSelectedRange = (0, import_react17.useCallback)((cell, direction) => {
11070
+ const taskIndex = tasks.findIndex((task) => task.id === cell.taskId);
11071
+ if (taskIndex < 0) return;
11072
+ let nextCell = { ...cell };
11073
+ if (direction === "left") {
11074
+ nextCell.dateIndex = Math.max(0, cell.dateIndex - 1);
11075
+ } else if (direction === "right") {
11076
+ nextCell.dateIndex = Math.min(dateRange.length - 1, cell.dateIndex + 1);
11077
+ } else if (direction === "up") {
11078
+ if (cell.kind === "fact") {
11079
+ nextCell.kind = "plan";
11080
+ } else {
11081
+ const previousEditableTaskIndex = findEditableTaskIndex(tasks, parentTaskIds, taskIndex - 1, -1);
11082
+ if (previousEditableTaskIndex === null) return;
11083
+ const previousTask = tasks[previousEditableTaskIndex];
11084
+ nextCell = { taskId: previousTask.id, dateIndex: cell.dateIndex, kind: "fact" };
11085
+ }
11086
+ } else if (direction === "down") {
11087
+ if (cell.kind === "plan") {
11088
+ nextCell.kind = "fact";
11089
+ } else {
11090
+ const nextEditableTaskIndex = findEditableTaskIndex(tasks, parentTaskIds, taskIndex + 1, 1);
11091
+ if (nextEditableTaskIndex === null) return;
11092
+ const nextTask = tasks[nextEditableTaskIndex];
11093
+ nextCell = { taskId: nextTask.id, dateIndex: cell.dateIndex, kind: "plan" };
11094
+ }
11095
+ }
11096
+ setActiveCell((currentActiveCell) => currentActiveCell ?? cell);
11097
+ setFillRange(null);
11098
+ setSelectedRange((currentRange) => ({
11099
+ anchor: currentRange?.anchor ?? cell,
11100
+ focus: nextCell
11101
+ }));
11102
+ onTaskSelect?.(nextCell.taskId);
11103
+ focusCell(selectedRange?.anchor ?? cell);
11104
+ }, [dateRange.length, focusCell, onTaskSelect, parentTaskIds, selectedRange, tasks]);
11105
+ (0, import_react17.useEffect)(() => {
11106
+ const endSelection = () => {
11107
+ if (isFillDraggingRef.current) {
11108
+ isFillDraggingRef.current = false;
11109
+ applyFillRange();
11110
+ }
11111
+ isSelectingRef.current = false;
11112
+ };
11113
+ window.addEventListener("mouseup", endSelection);
11114
+ return () => {
11115
+ window.removeEventListener("mouseup", endSelection);
11116
+ };
11117
+ }, [applyFillRange]);
11118
+ (0, import_react17.useEffect)(() => {
11119
+ const handleKeyDown = (event) => {
11120
+ if (event.key !== "Escape") return;
11121
+ clearSelection();
11122
+ };
11123
+ const handleMouseDown = (event) => {
11124
+ const target = event.target;
11125
+ if (target instanceof Node && rootRef.current?.contains(target)) {
11126
+ return;
11127
+ }
11128
+ clearSelection();
11129
+ };
11130
+ window.addEventListener("keydown", handleKeyDown);
11131
+ document.addEventListener("mousedown", handleMouseDown);
11132
+ return () => {
11133
+ window.removeEventListener("keydown", handleKeyDown);
11134
+ document.removeEventListener("mousedown", handleMouseDown);
11135
+ };
11136
+ }, [clearSelection]);
11137
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
11138
+ "div",
11139
+ {
11140
+ ref: rootRef,
11141
+ className: "gantt-pf-root",
11142
+ style: {
11143
+ width: `${totalWidth}px`,
11144
+ ["--gantt-pf-day-width"]: `${dayWidth}px`
11145
+ },
11146
+ children: [
11147
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "gantt-pf-header", style: { width: `${totalWidth}px`, height: `${headerHeight}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
11148
+ TimeScaleHeader_default,
11149
+ {
11150
+ days: dateRange,
11151
+ dayWidth,
11152
+ headerHeight: headerHeight - 1,
11153
+ viewMode: "day"
11154
+ }
11155
+ ) }),
11156
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
11157
+ "div",
11158
+ {
11159
+ ref: bodyRef,
11160
+ className: "gantt-pf-body",
11161
+ style: {
11162
+ height: `${tasks.length * rowHeight}px`,
11163
+ minHeight: bodyMinHeight,
11164
+ width: `${totalWidth}px`
11165
+ },
11166
+ children: tasks.map((task, rowIndex) => {
11167
+ const isParent = parentTaskIds.has(task.id);
11168
+ const isHighlighted = filterMode === "highlight" && !!highlightedTaskIds?.has(task.id);
11169
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
11170
+ "div",
11171
+ {
11172
+ "data-gantt-task-row-id": task.id,
11173
+ className: joinClasses2(
11174
+ "gantt-pf-row",
11175
+ isParent && "gantt-pf-row-parent",
11176
+ selectedTaskId === task.id && "gantt-pf-row-selected",
11177
+ isHighlighted && "gantt-pf-row-highlighted"
11178
+ ),
11179
+ style: {
11180
+ top: `${rowIndex * rowHeight}px`,
11181
+ height: `${rowHeight}px`,
11182
+ gridTemplateColumns: `repeat(${dateRange.length}, ${dayWidth}px)`
11183
+ },
11184
+ onClick: () => onTaskSelect?.(task.id),
11185
+ children: dateRange.map((date, dateIndex) => {
11186
+ const dateKey = dateKeys[dateIndex];
11187
+ const planned = isDateWithinTask(task, date);
11188
+ return ["plan", "fact"].map((kind) => {
11189
+ const planValue = task.planByDate?.[dateKey];
11190
+ const factValue = task.factByDate?.[dateKey];
11191
+ const value = kind === "plan" ? planValue : factValue;
11192
+ const factStatus = factValue === void 0 || planValue === void 0 ? null : factValue >= planValue ? "success" : "warning";
11193
+ const isActive = activeCell?.taskId === task.id && activeCell.dateIndex === dateIndex && activeCell.kind === kind;
11194
+ const isEditing = editingCell?.taskId === task.id && editingCell.dateIndex === dateIndex && editingCell.kind === kind;
11195
+ const currentCell = { taskId: task.id, dateIndex, kind };
11196
+ const isSelected = !isParent && isCellInSelectedRange(currentCell);
11197
+ const showFillHandle = !isParent && !isEditing && isFillHandleCell(currentCell);
11198
+ const isRangeAnchor = !showFillHandle && !isParent && selectedRange?.anchor.taskId === task.id && selectedRange.anchor.dateIndex === dateIndex && selectedRange.anchor.kind === kind;
11199
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
11200
+ "div",
11201
+ {
11202
+ "data-plan-fact-task-id": task.id,
11203
+ "data-plan-fact-date-index": dateIndex,
11204
+ "data-plan-fact-kind": kind,
11205
+ className: joinClasses2(
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
+ })
11362
+ },
11363
+ task.id
11364
+ );
11365
+ })
11366
+ }
11367
+ ),
11368
+ overflowTooltip && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
11369
+ "div",
11370
+ {
11371
+ className: "gantt-pf-overflowTooltip",
11372
+ role: "tooltip",
11373
+ "aria-hidden": "true",
11374
+ style: {
11375
+ left: `${overflowTooltip.left}px`,
11376
+ top: `${overflowTooltip.top}px`
11377
+ },
11378
+ children: overflowTooltip.label
11379
+ }
11380
+ )
11381
+ ]
11382
+ }
11383
+ );
11384
+ }
11385
+
10567
11386
  // src/components/GanttChart/print.ts
10568
11387
  function getPrintDocumentTitle({
10569
11388
  header,
@@ -10972,7 +11791,7 @@ function createTaskPreviewPositionStore() {
10972
11791
  }
10973
11792
 
10974
11793
  // src/components/GanttChart/GanttChart.tsx
10975
- var import_jsx_runtime18 = require("react/jsx-runtime");
11794
+ var import_jsx_runtime19 = require("react/jsx-runtime");
10976
11795
  var SCROLL_TO_ROW_CONTEXT_ROWS = 2;
10977
11796
  var TASK_ROW_OVERSCAN = 8;
10978
11797
  function arePositionMapsEqual(left, right) {
@@ -11011,9 +11830,9 @@ function arePreviewTaskMapsEqual(left, right) {
11011
11830
  }
11012
11831
  function GanttChartInner(props, ref) {
11013
11832
  if (props.mode === "resource-planner") {
11014
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ResourceTimelineChart, { ...props });
11833
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ResourceTimelineChart, { ...props });
11015
11834
  }
11016
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
11835
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11017
11836
  TaskGanttChart,
11018
11837
  {
11019
11838
  ...props,
@@ -11023,6 +11842,7 @@ function GanttChartInner(props, ref) {
11023
11842
  }
11024
11843
  function TaskGanttChartInner(props, ref) {
11025
11844
  const isTableMatrixMode = props.mode === "table-matrix";
11845
+ const isPlanFactMode = props.mode === "plan-fact";
11026
11846
  const {
11027
11847
  tasks,
11028
11848
  rowHeight = 40,
@@ -11072,47 +11892,48 @@ function TaskGanttChartInner(props, ref) {
11072
11892
  onTaskDateChangeModeChange: externalOnTaskDateChangeModeChange
11073
11893
  } = props;
11074
11894
  const dayWidth = !isTableMatrixMode ? props.dayWidth ?? 40 : 40;
11075
- const viewMode = !isTableMatrixMode ? props.viewMode ?? "day" : "day";
11076
- const customDays = !isTableMatrixMode ? props.customDays : void 0;
11077
- const isWeekend3 = !isTableMatrixMode ? props.isWeekend : void 0;
11078
- const businessDays = !isTableMatrixMode ? props.businessDays ?? true : true;
11895
+ const viewMode = !isTableMatrixMode && !isPlanFactMode ? props.viewMode ?? "day" : "day";
11896
+ const customDays = !isTableMatrixMode && !isPlanFactMode ? props.customDays : void 0;
11897
+ const isWeekend3 = !isTableMatrixMode && !isPlanFactMode ? props.isWeekend : void 0;
11898
+ const businessDays = !isTableMatrixMode && !isPlanFactMode ? props.businessDays ?? true : true;
11079
11899
  const matrixColumns = isTableMatrixMode ? props.matrixColumns : [];
11080
11900
  const matrixColumnGroups = isTableMatrixMode ? props.matrixColumnGroups : void 0;
11081
11901
  const onMatrixCellClick = isTableMatrixMode ? props.onMatrixCellClick : void 0;
11082
11902
  const matrixDateOverlay = isTableMatrixMode ? props.matrixDateOverlay : void 0;
11083
- const containerRef = (0, import_react17.useRef)(null);
11084
- const scrollContainerRef = (0, import_react17.useRef)(null);
11085
- const scrollContentRef = (0, import_react17.useRef)(null);
11086
- const clearSelectedTaskTimeoutRef = (0, import_react17.useRef)(null);
11087
- const hasAutoScrolledToTodayRef = (0, import_react17.useRef)(false);
11088
- const previewPositionStoreRef = (0, import_react17.useRef)(null);
11089
- const renderedTaskIdsRef = (0, import_react17.useRef)(/* @__PURE__ */ new Set());
11903
+ const onPlanFactCellCommit = isPlanFactMode ? props.onPlanFactCellCommit : void 0;
11904
+ const containerRef = (0, import_react18.useRef)(null);
11905
+ const scrollContainerRef = (0, import_react18.useRef)(null);
11906
+ const scrollContentRef = (0, import_react18.useRef)(null);
11907
+ const clearSelectedTaskTimeoutRef = (0, import_react18.useRef)(null);
11908
+ const hasAutoScrolledToTodayRef = (0, import_react18.useRef)(false);
11909
+ const previewPositionStoreRef = (0, import_react18.useRef)(null);
11910
+ const renderedTaskIdsRef = (0, import_react18.useRef)(/* @__PURE__ */ new Set());
11090
11911
  if (previewPositionStoreRef.current === null) {
11091
11912
  previewPositionStoreRef.current = createTaskPreviewPositionStore();
11092
11913
  }
11093
11914
  const previewPositionStore = previewPositionStoreRef.current;
11094
- const [selectedTaskId, setSelectedTaskId] = (0, import_react17.useState)(null);
11095
- const [taskListHasRightShadow, setTaskListHasRightShadow] = (0, import_react17.useState)(false);
11096
- const [internalTaskDateChangeMode, setInternalTaskDateChangeMode] = (0, import_react17.useState)("preserve-duration");
11097
- const [scrollViewport, setScrollViewport] = (0, import_react17.useState)({ scrollTop: 0, viewportHeight: 0 });
11098
- const [selectedChip, setSelectedChip] = (0, import_react17.useState)(null);
11099
- const [activeTimelineTooltip, setActiveTimelineTooltip] = (0, import_react17.useState)(null);
11100
- const [internalCollapsedParentIds, setInternalCollapsedParentIds] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
11915
+ const [selectedTaskId, setSelectedTaskId] = (0, import_react18.useState)(null);
11916
+ const [taskListHasRightShadow, setTaskListHasRightShadow] = (0, import_react18.useState)(false);
11917
+ const [internalTaskDateChangeMode, setInternalTaskDateChangeMode] = (0, import_react18.useState)("preserve-duration");
11918
+ const [scrollViewport, setScrollViewport] = (0, import_react18.useState)({ scrollTop: 0, viewportHeight: 0 });
11919
+ const [selectedChip, setSelectedChip] = (0, import_react18.useState)(null);
11920
+ const [activeTimelineTooltip, setActiveTimelineTooltip] = (0, import_react18.useState)(null);
11921
+ const [internalCollapsedParentIds, setInternalCollapsedParentIds] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
11101
11922
  const collapsedParentIds = externalCollapsedParentIds ?? internalCollapsedParentIds;
11102
- const [editingTaskId, setEditingTaskId] = (0, import_react17.useState)(null);
11923
+ const [editingTaskId, setEditingTaskId] = (0, import_react18.useState)(null);
11103
11924
  const taskDateChangeMode = externalTaskDateChangeMode ?? internalTaskDateChangeMode;
11104
11925
  const handleTaskDateChangeMode = externalOnTaskDateChangeModeChange ?? setInternalTaskDateChangeMode;
11105
- const resolvedRowContentLines = Math.max(1, Math.floor(rowContentLines));
11106
- const effectiveRowHeight = (0, import_react17.useMemo)(
11926
+ const resolvedRowContentLines = isPlanFactMode ? Math.max(2, Math.floor(rowContentLines)) : Math.max(1, Math.floor(rowContentLines));
11927
+ const effectiveRowHeight = (0, import_react18.useMemo)(
11107
11928
  () => Math.max(rowHeight, 10 + resolvedRowContentLines * 18),
11108
11929
  [resolvedRowContentLines, rowHeight]
11109
11930
  );
11110
- const normalizedTasks = (0, import_react17.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
11111
- const isCustomWeekend = (0, import_react17.useMemo)(
11931
+ const normalizedTasks = (0, import_react18.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
11932
+ const isCustomWeekend = (0, import_react18.useMemo)(
11112
11933
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
11113
11934
  [customDays, isWeekend3]
11114
11935
  );
11115
- const dateRangeTasks = (0, import_react17.useMemo)(() => {
11936
+ const dateRangeTasks = (0, import_react18.useMemo)(() => {
11116
11937
  if (!showBaseline) {
11117
11938
  return normalizedTasks;
11118
11939
  }
@@ -11122,21 +11943,21 @@ function TaskGanttChartInner(props, ref) {
11122
11943
  endDate: task.baselineEndDate && parseUTCDate(task.baselineEndDate).getTime() > parseUTCDate(task.endDate).getTime() ? task.baselineEndDate : task.endDate
11123
11944
  }));
11124
11945
  }, [normalizedTasks, showBaseline]);
11125
- const dateRange = (0, import_react17.useMemo)(() => getMultiMonthDays(dateRangeTasks), [dateRangeTasks]);
11126
- const [validationResult, setValidationResult] = (0, import_react17.useState)(null);
11127
- const [cascadeOverrides, setCascadeOverrides] = (0, import_react17.useState)(/* @__PURE__ */ new Map());
11128
- const gridWidth = (0, import_react17.useMemo)(
11946
+ const dateRange = (0, import_react18.useMemo)(() => getMultiMonthDays(dateRangeTasks), [dateRangeTasks]);
11947
+ const [validationResult, setValidationResult] = (0, import_react18.useState)(null);
11948
+ const [cascadeOverrides, setCascadeOverrides] = (0, import_react18.useState)(/* @__PURE__ */ new Map());
11949
+ const gridWidth = (0, import_react18.useMemo)(
11129
11950
  () => Math.round(dateRange.length * dayWidth),
11130
11951
  [dateRange.length, dayWidth]
11131
11952
  );
11132
- const matrixWidth = (0, import_react17.useMemo)(
11953
+ const matrixWidth = (0, import_react18.useMemo)(
11133
11954
  () => matrixColumns.reduce((sum, column) => {
11134
11955
  if (typeof column.width !== "number") return void 0;
11135
11956
  return sum !== void 0 ? sum + column.width : void 0;
11136
11957
  }, 0),
11137
11958
  [matrixColumns]
11138
11959
  );
11139
- const visibleTasks = (0, import_react17.useMemo)(() => {
11960
+ const visibleTasks = (0, import_react18.useMemo)(() => {
11140
11961
  const parentMap = new Map(normalizedTasks.map((t) => [t.id, t.parentId]));
11141
11962
  function isAnyAncestorCollapsed(parentId) {
11142
11963
  let current = parentId;
@@ -11152,11 +11973,11 @@ function TaskGanttChartInner(props, ref) {
11152
11973
  }
11153
11974
  return tasks2;
11154
11975
  }, [normalizedTasks, collapsedParentIds, filterMode, taskFilter]);
11155
- const matchedTaskIds = (0, import_react17.useMemo)(() => {
11976
+ const matchedTaskIds = (0, import_react18.useMemo)(() => {
11156
11977
  if (!taskFilter) return /* @__PURE__ */ new Set();
11157
11978
  return new Set(visibleTasks.filter(taskFilter).map((task) => task.id));
11158
11979
  }, [visibleTasks, taskFilter]);
11159
- const taskListHighlightedTaskIds = (0, import_react17.useMemo)(() => {
11980
+ const taskListHighlightedTaskIds = (0, import_react18.useMemo)(() => {
11160
11981
  if (filterMode === "hide") {
11161
11982
  return /* @__PURE__ */ new Set();
11162
11983
  }
@@ -11167,33 +11988,33 @@ function TaskGanttChartInner(props, ref) {
11167
11988
  matchedTaskIds.forEach((taskId) => mergedHighlightedTaskIds.add(taskId));
11168
11989
  return mergedHighlightedTaskIds;
11169
11990
  }, [filterMode, highlightedTaskIds, matchedTaskIds]);
11170
- const totalGridHeight = (0, import_react17.useMemo)(
11991
+ const totalGridHeight = (0, import_react18.useMemo)(
11171
11992
  () => visibleTasks.length * effectiveRowHeight,
11172
11993
  [effectiveRowHeight, visibleTasks.length]
11173
11994
  );
11174
11995
  const timelineHeaderHeight = headerHeight + 1;
11175
- const tableBodyMinHeight = (0, import_react17.useMemo)(() => {
11176
- if (!isTableMatrixMode || containerHeight === void 0) {
11996
+ const tableBodyMinHeight = (0, import_react18.useMemo)(() => {
11997
+ if (!isTableMatrixMode && !isPlanFactMode || containerHeight === void 0) {
11177
11998
  return void 0;
11178
11999
  }
11179
12000
  if (typeof containerHeight === "number") {
11180
12001
  return Math.max(0, containerHeight - timelineHeaderHeight);
11181
12002
  }
11182
12003
  return `calc(${containerHeight} - ${timelineHeaderHeight}px)`;
11183
- }, [containerHeight, isTableMatrixMode, timelineHeaderHeight]);
11184
- const monthStart = (0, import_react17.useMemo)(() => {
12004
+ }, [containerHeight, isPlanFactMode, isTableMatrixMode, timelineHeaderHeight]);
12005
+ const monthStart = (0, import_react18.useMemo)(() => {
11185
12006
  if (dateRange.length === 0) {
11186
12007
  return new Date(Date.UTC((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth(), 1));
11187
12008
  }
11188
12009
  const firstDay = dateRange[0];
11189
12010
  return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
11190
12011
  }, [dateRange]);
11191
- const todayInRange = (0, import_react17.useMemo)(() => {
12012
+ const todayInRange = (0, import_react18.useMemo)(() => {
11192
12013
  const now = /* @__PURE__ */ new Date();
11193
12014
  const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
11194
12015
  return dateRange.some((day) => day.getTime() === today.getTime());
11195
12016
  }, [dateRange]);
11196
- const visibleTimelineMarkers = (0, import_react17.useMemo)(() => {
12017
+ const visibleTimelineMarkers = (0, import_react18.useMemo)(() => {
11197
12018
  if (isTableMatrixMode || !timelineMarkers || timelineMarkers.length === 0 || dateRange.length === 0) {
11198
12019
  return [];
11199
12020
  }
@@ -11204,7 +12025,7 @@ function TaskGanttChartInner(props, ref) {
11204
12025
  return markerDate >= rangeStartMs && markerDate <= rangeEndMs;
11205
12026
  });
11206
12027
  }, [dateRange, isTableMatrixMode, timelineMarkers]);
11207
- (0, import_react17.useEffect)(() => {
12028
+ (0, import_react18.useEffect)(() => {
11208
12029
  if (isTableMatrixMode) return;
11209
12030
  if (hasAutoScrolledToTodayRef.current) return;
11210
12031
  const container = scrollContainerRef.current;
@@ -11219,7 +12040,7 @@ function TaskGanttChartInner(props, ref) {
11219
12040
  container.scrollLeft = Math.max(0, scrollLeft);
11220
12041
  hasAutoScrolledToTodayRef.current = true;
11221
12042
  }, [dateRange, dayWidth, isTableMatrixMode]);
11222
- (0, import_react17.useEffect)(() => {
12043
+ (0, import_react18.useEffect)(() => {
11223
12044
  const container = scrollContainerRef.current;
11224
12045
  if (!container) return;
11225
12046
  let frameId = null;
@@ -11255,7 +12076,7 @@ function TaskGanttChartInner(props, ref) {
11255
12076
  window.removeEventListener("resize", scheduleUpdate);
11256
12077
  };
11257
12078
  }, [timelineHeaderHeight]);
11258
- const scrollToToday = (0, import_react17.useCallback)(() => {
12079
+ const scrollToToday = (0, import_react18.useCallback)(() => {
11259
12080
  if (isTableMatrixMode) return;
11260
12081
  const container = scrollContainerRef.current;
11261
12082
  if (!container || dateRange.length === 0) return;
@@ -11268,7 +12089,7 @@ function TaskGanttChartInner(props, ref) {
11268
12089
  const scrollLeft = Math.round(todayOffset + dayWidth / 2 - containerWidth * 0.3);
11269
12090
  container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
11270
12091
  }, [dateRange, dayWidth, isTableMatrixMode]);
11271
- const scrollToTask = (0, import_react17.useCallback)((taskId) => {
12092
+ const scrollToTask = (0, import_react18.useCallback)((taskId) => {
11272
12093
  if (isTableMatrixMode) {
11273
12094
  const container2 = scrollContainerRef.current;
11274
12095
  if (!container2) return;
@@ -11295,7 +12116,7 @@ function TaskGanttChartInner(props, ref) {
11295
12116
  const scrollLeft = Math.round(taskOffset - dayWidth * 2);
11296
12117
  container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
11297
12118
  }, [dateRange, dayWidth, effectiveRowHeight, isTableMatrixMode, tasks, visibleTasks]);
11298
- const scrollToRow = (0, import_react17.useCallback)((taskId, options = {}) => {
12119
+ const scrollToRow = (0, import_react18.useCallback)((taskId, options = {}) => {
11299
12120
  const container = scrollContainerRef.current;
11300
12121
  if (!container) return;
11301
12122
  const task = tasks.find((t) => t.id === taskId);
@@ -11324,21 +12145,21 @@ function TaskGanttChartInner(props, ref) {
11324
12145
  }
11325
12146
  container.scrollTo({ top: scrollTop, behavior });
11326
12147
  }, [effectiveRowHeight, tasks, visibleTasks]);
11327
- const [dragGuideLines, setDragGuideLines] = (0, import_react17.useState)(null);
11328
- const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react17.useState)(null);
11329
- const [previewTasksById, setPreviewTasksById] = (0, import_react17.useState)(/* @__PURE__ */ new Map());
11330
- (0, import_react17.useEffect)(() => {
12148
+ const [dragGuideLines, setDragGuideLines] = (0, import_react18.useState)(null);
12149
+ const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react18.useState)(null);
12150
+ const [previewTasksById, setPreviewTasksById] = (0, import_react18.useState)(/* @__PURE__ */ new Map());
12151
+ (0, import_react18.useEffect)(() => {
11331
12152
  const result = validateDependencies(tasks);
11332
12153
  setValidationResult(result);
11333
12154
  onValidateDependencies?.(result);
11334
12155
  }, [tasks, onValidateDependencies]);
11335
- (0, import_react17.useEffect)(() => () => {
12156
+ (0, import_react18.useEffect)(() => () => {
11336
12157
  if (clearSelectedTaskTimeoutRef.current !== null) {
11337
12158
  window.clearTimeout(clearSelectedTaskTimeoutRef.current);
11338
12159
  }
11339
12160
  previewPositionStore.clear();
11340
12161
  }, [previewPositionStore]);
11341
- const handleTaskChange = (0, import_react17.useCallback)((updatedTasks) => {
12162
+ const handleTaskChange = (0, import_react18.useCallback)((updatedTasks) => {
11342
12163
  const updatedTask = updatedTasks[0];
11343
12164
  if (!updatedTask) return;
11344
12165
  const originalTask = tasks.find((t) => t.id === updatedTask.id);
@@ -11384,7 +12205,7 @@ function TaskGanttChartInner(props, ref) {
11384
12205
  const cascadedTasks = disableConstraints ? [updatedTask] : universalCascade(updatedTask, newStart, newEnd, sourceTasks, businessDays, isCustomWeekend);
11385
12206
  onTasksChange?.(cascadedTasks);
11386
12207
  }, [tasks, onTasksChange, disableConstraints, editingTaskId, businessDays, isCustomWeekend]);
11387
- const handleDelete = (0, import_react17.useCallback)((taskId) => {
12208
+ const handleDelete = (0, import_react18.useCallback)((taskId) => {
11388
12209
  const toDelete = /* @__PURE__ */ new Set([taskId]);
11389
12210
  function collectDescendants(parentId) {
11390
12211
  const children = getChildren(parentId, tasks);
@@ -11409,10 +12230,10 @@ function TaskGanttChartInner(props, ref) {
11409
12230
  }
11410
12231
  toDelete.forEach((id) => onDelete?.(id));
11411
12232
  }, [tasks, onTasksChange, onDelete]);
11412
- const handleInsertAfter = (0, import_react17.useCallback)((taskId, newTask) => {
12233
+ const handleInsertAfter = (0, import_react18.useCallback)((taskId, newTask) => {
11413
12234
  onInsertAfter?.(taskId, newTask);
11414
12235
  }, [onInsertAfter]);
11415
- const handleReorder = (0, import_react17.useCallback)((reorderedTasks, movedTaskId, inferredParentId) => {
12236
+ const handleReorder = (0, import_react18.useCallback)((reorderedTasks, movedTaskId, inferredParentId) => {
11416
12237
  let updated = reorderedTasks;
11417
12238
  if (movedTaskId) {
11418
12239
  updated = updated.map((t) => {
@@ -11429,7 +12250,7 @@ function TaskGanttChartInner(props, ref) {
11429
12250
  }
11430
12251
  onTasksChange?.(normalized);
11431
12252
  }, [onTasksChange, onReorder]);
11432
- const dependencyOverrides = (0, import_react17.useMemo)(() => {
12253
+ const dependencyOverrides = (0, import_react18.useMemo)(() => {
11433
12254
  const map = new Map(cascadeOverrides);
11434
12255
  if (draggedTaskOverride) {
11435
12256
  map.set(draggedTaskOverride.taskId, {
@@ -11439,7 +12260,7 @@ function TaskGanttChartInner(props, ref) {
11439
12260
  }
11440
12261
  return map;
11441
12262
  }, [cascadeOverrides, draggedTaskOverride]);
11442
- const handleCascadeProgress = (0, import_react17.useCallback)((overrides, previewTasks = []) => {
12263
+ const handleCascadeProgress = (0, import_react18.useCallback)((overrides, previewTasks = []) => {
11443
12264
  previewPositionStore.setPositions(overrides);
11444
12265
  const renderedTaskIds = renderedTaskIdsRef.current;
11445
12266
  const renderedOverrides = /* @__PURE__ */ new Map();
@@ -11454,26 +12275,26 @@ function TaskGanttChartInner(props, ref) {
11454
12275
  return arePreviewTaskMapsEqual(current, next) ? current : next;
11455
12276
  });
11456
12277
  }, [previewPositionStore]);
11457
- const previewNormalizedTasks = (0, import_react17.useMemo)(() => {
12278
+ const previewNormalizedTasks = (0, import_react18.useMemo)(() => {
11458
12279
  if (previewTasksById.size === 0) return normalizedTasks;
11459
12280
  return normalizedTasks.map((task) => previewTasksById.get(task.id) ?? task);
11460
12281
  }, [normalizedTasks, previewTasksById]);
11461
- const previewVisibleTasks = (0, import_react17.useMemo)(() => {
12282
+ const previewVisibleTasks = (0, import_react18.useMemo)(() => {
11462
12283
  if (previewTasksById.size === 0) return visibleTasks;
11463
12284
  return visibleTasks.map((task) => previewTasksById.get(task.id) ?? task);
11464
12285
  }, [visibleTasks, previewTasksById]);
11465
- const visibleTaskIndexMap = (0, import_react17.useMemo)(
12286
+ const visibleTaskIndexMap = (0, import_react18.useMemo)(
11466
12287
  () => new Map(visibleTasks.map((task, index) => [task.id, index])),
11467
12288
  [visibleTasks]
11468
12289
  );
11469
- const forcedRenderedTaskIds = (0, import_react17.useMemo)(() => {
12290
+ const forcedRenderedTaskIds = (0, import_react18.useMemo)(() => {
11470
12291
  const ids = /* @__PURE__ */ new Set();
11471
12292
  if (draggedTaskOverride) {
11472
12293
  ids.add(draggedTaskOverride.taskId);
11473
12294
  }
11474
12295
  return ids;
11475
12296
  }, [draggedTaskOverride]);
11476
- const visibleTaskWindowIndices = (0, import_react17.useMemo)(() => {
12297
+ const visibleTaskWindowIndices = (0, import_react18.useMemo)(() => {
11477
12298
  const totalTasks = visibleTasks.length;
11478
12299
  if (totalTasks === 0) {
11479
12300
  return [];
@@ -11499,35 +12320,35 @@ function TaskGanttChartInner(props, ref) {
11499
12320
  }
11500
12321
  return Array.from(indices).sort((left, right) => left - right);
11501
12322
  }, [effectiveRowHeight, forcedRenderedTaskIds, scrollViewport, visibleTaskIndexMap, visibleTasks.length]);
11502
- const renderedChartTasks = (0, import_react17.useMemo)(
12323
+ const renderedChartTasks = (0, import_react18.useMemo)(
11503
12324
  () => visibleTaskWindowIndices.map((index) => {
11504
12325
  const task = previewVisibleTasks[index];
11505
12326
  return task ? { index, task } : null;
11506
12327
  }).filter((entry) => entry !== null),
11507
12328
  [previewVisibleTasks, visibleTaskWindowIndices]
11508
12329
  );
11509
- const renderedDependencyTasks = (0, import_react17.useMemo)(
12330
+ const renderedDependencyTasks = (0, import_react18.useMemo)(
11510
12331
  () => renderedChartTasks.map(({ task }) => task),
11511
12332
  [renderedChartTasks]
11512
12333
  );
11513
- renderedTaskIdsRef.current = (0, import_react17.useMemo)(
12334
+ renderedTaskIdsRef.current = (0, import_react18.useMemo)(
11514
12335
  () => new Set(renderedChartTasks.map(({ task }) => task.id)),
11515
12336
  [renderedChartTasks]
11516
12337
  );
11517
- const handleCascade = (0, import_react17.useCallback)((cascadedTasks) => {
12338
+ const handleCascade = (0, import_react18.useCallback)((cascadedTasks) => {
11518
12339
  onTasksChange?.(cascadedTasks);
11519
12340
  }, [tasks, onTasksChange]);
11520
- const handleTaskSelect = (0, import_react17.useCallback)((taskId) => {
12341
+ const handleTaskSelect = (0, import_react18.useCallback)((taskId) => {
11521
12342
  setSelectedTaskId(taskId);
11522
12343
  }, []);
11523
- const hoveredRowElementsRef = (0, import_react17.useRef)([]);
11524
- const clearHoveredRows = (0, import_react17.useCallback)(() => {
12344
+ const hoveredRowElementsRef = (0, import_react18.useRef)([]);
12345
+ const clearHoveredRows = (0, import_react18.useCallback)(() => {
11525
12346
  for (const element of hoveredRowElementsRef.current) {
11526
- element.classList.remove("gantt-tl-row-hovered", "gantt-tr-row-hovered", "gantt-mx-row-hovered");
12347
+ element.classList.remove("gantt-tl-row-hovered", "gantt-tr-row-hovered", "gantt-mx-row-hovered", "gantt-pf-row-hovered");
11527
12348
  }
11528
12349
  hoveredRowElementsRef.current = [];
11529
12350
  }, []);
11530
- const applyHoveredRows = (0, import_react17.useCallback)((taskId) => {
12351
+ const applyHoveredRows = (0, import_react18.useCallback)((taskId) => {
11531
12352
  const root = scrollContentRef.current;
11532
12353
  if (!root) return;
11533
12354
  clearHoveredRows();
@@ -11544,10 +12365,13 @@ function TaskGanttChartInner(props, ref) {
11544
12365
  if (element.classList.contains("gantt-mx-row")) {
11545
12366
  element.classList.add("gantt-mx-row-hovered");
11546
12367
  }
12368
+ if (element.classList.contains("gantt-pf-row")) {
12369
+ element.classList.add("gantt-pf-row-hovered");
12370
+ }
11547
12371
  }
11548
12372
  hoveredRowElementsRef.current = nextHoveredRows;
11549
12373
  }, [clearHoveredRows]);
11550
- const handleSharedRowHover = (0, import_react17.useCallback)((event) => {
12374
+ const handleSharedRowHover = (0, import_react18.useCallback)((event) => {
11551
12375
  const target = event.target;
11552
12376
  const row = target.closest("[data-gantt-task-row-id]");
11553
12377
  const taskId = row?.dataset.ganttTaskRowId;
@@ -11557,7 +12381,7 @@ function TaskGanttChartInner(props, ref) {
11557
12381
  }
11558
12382
  applyHoveredRows(taskId);
11559
12383
  }, [applyHoveredRows]);
11560
- const handleToggleCollapse = externalOnToggleCollapse ?? (0, import_react17.useCallback)((parentId) => {
12384
+ const handleToggleCollapse = externalOnToggleCollapse ?? (0, import_react18.useCallback)((parentId) => {
11561
12385
  setInternalCollapsedParentIds((prev) => {
11562
12386
  const next = new Set(prev);
11563
12387
  if (next.has(parentId)) {
@@ -11568,20 +12392,20 @@ function TaskGanttChartInner(props, ref) {
11568
12392
  return next;
11569
12393
  });
11570
12394
  }, []);
11571
- const allParentIds = (0, import_react17.useMemo)(() => {
12395
+ const allParentIds = (0, import_react18.useMemo)(() => {
11572
12396
  return new Set(
11573
12397
  normalizedTasks.filter((t) => isTaskParent(t.id, normalizedTasks)).map((t) => t.id)
11574
12398
  );
11575
12399
  }, [normalizedTasks]);
11576
- const handleCollapseAll = (0, import_react17.useCallback)(() => {
12400
+ const handleCollapseAll = (0, import_react18.useCallback)(() => {
11577
12401
  if (externalCollapsedParentIds) return;
11578
12402
  setInternalCollapsedParentIds(allParentIds);
11579
12403
  }, [allParentIds, externalCollapsedParentIds]);
11580
- const handleExpandAll = (0, import_react17.useCallback)(() => {
12404
+ const handleExpandAll = (0, import_react18.useCallback)(() => {
11581
12405
  if (externalCollapsedParentIds) return;
11582
12406
  setInternalCollapsedParentIds(/* @__PURE__ */ new Set());
11583
12407
  }, [externalCollapsedParentIds]);
11584
- const exportToPdf = (0, import_react17.useCallback)(async (options) => {
12408
+ const exportToPdf = (0, import_react18.useCallback)(async (options) => {
11585
12409
  const sourceContainer = containerRef.current;
11586
12410
  const sourceContent = scrollContentRef.current;
11587
12411
  if (!sourceContainer || !sourceContent || typeof window === "undefined" || typeof document === "undefined") {
@@ -11617,7 +12441,7 @@ function TaskGanttChartInner(props, ref) {
11617
12441
  orientation: options?.orientation
11618
12442
  });
11619
12443
  }, [showTaskList, showChart]);
11620
- (0, import_react17.useImperativeHandle)(
12444
+ (0, import_react18.useImperativeHandle)(
11621
12445
  ref,
11622
12446
  () => ({
11623
12447
  scrollToToday,
@@ -11640,7 +12464,7 @@ function TaskGanttChartInner(props, ref) {
11640
12464
  }
11641
12465
  return depth;
11642
12466
  }
11643
- const handlePromoteTask = (0, import_react17.useCallback)((taskId) => {
12467
+ const handlePromoteTask = (0, import_react18.useCallback)((taskId) => {
11644
12468
  if (onPromoteTask) {
11645
12469
  onPromoteTask(taskId);
11646
12470
  return;
@@ -11672,7 +12496,7 @@ function TaskGanttChartInner(props, ref) {
11672
12496
  ]);
11673
12497
  onTasksChange?.(reorderedTasks);
11674
12498
  }, [tasks, onTasksChange, onPromoteTask]);
11675
- const handleDemoteTask = (0, import_react17.useCallback)((taskId, newParentId) => {
12499
+ const handleDemoteTask = (0, import_react18.useCallback)((taskId, newParentId) => {
11676
12500
  if (onDemoteTask) {
11677
12501
  onDemoteTask(taskId, newParentId);
11678
12502
  return;
@@ -11713,7 +12537,7 @@ function TaskGanttChartInner(props, ref) {
11713
12537
  };
11714
12538
  onTasksChange?.([updatedDemotedTask]);
11715
12539
  }, [tasks, onTasksChange, onDemoteTask]);
11716
- const handleUngroupTask = (0, import_react17.useCallback)((taskId) => {
12540
+ const handleUngroupTask = (0, import_react18.useCallback)((taskId) => {
11717
12541
  if (onUngroupTask) {
11718
12542
  onUngroupTask(taskId);
11719
12543
  return;
@@ -11738,8 +12562,8 @@ function TaskGanttChartInner(props, ref) {
11738
12562
  onTasksChange?.(changedTasks);
11739
12563
  }
11740
12564
  }, [tasks, onTasksChange, onUngroupTask]);
11741
- const panStateRef = (0, import_react17.useRef)(null);
11742
- const handlePanStart = (0, import_react17.useCallback)((e) => {
12565
+ const panStateRef = (0, import_react18.useRef)(null);
12566
+ const handlePanStart = (0, import_react18.useCallback)((e) => {
11743
12567
  if (e.button !== 0) return;
11744
12568
  const target = e.target;
11745
12569
  if (target.closest("[data-taskbar]")) return;
@@ -11760,7 +12584,7 @@ function TaskGanttChartInner(props, ref) {
11760
12584
  container.style.cursor = "grabbing";
11761
12585
  e.preventDefault();
11762
12586
  }, []);
11763
- (0, import_react17.useEffect)(() => {
12587
+ (0, import_react18.useEffect)(() => {
11764
12588
  const handlePanMove = (e) => {
11765
12589
  const pan = panStateRef.current;
11766
12590
  if (!pan?.active) return;
@@ -11782,19 +12606,19 @@ function TaskGanttChartInner(props, ref) {
11782
12606
  window.removeEventListener("mouseup", handlePanEnd);
11783
12607
  };
11784
12608
  }, []);
11785
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12609
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11786
12610
  "div",
11787
12611
  {
11788
12612
  ref: containerRef,
11789
- className: isTableMatrixMode ? "gantt-container gantt-container-tableMatrix" : "gantt-container",
11790
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12613
+ className: isTableMatrixMode ? "gantt-container gantt-container-tableMatrix" : isPlanFactMode ? "gantt-container gantt-container-planFact" : "gantt-container",
12614
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11791
12615
  "div",
11792
12616
  {
11793
12617
  ref: scrollContainerRef,
11794
12618
  className: "gantt-scrollContainer",
11795
12619
  style: { height: containerHeight ?? "auto", cursor: "grab" },
11796
12620
  onMouseDown: handlePanStart,
11797
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
12621
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
11798
12622
  "div",
11799
12623
  {
11800
12624
  ref: scrollContentRef,
@@ -11802,7 +12626,7 @@ function TaskGanttChartInner(props, ref) {
11802
12626
  onMouseOver: handleSharedRowHover,
11803
12627
  onMouseLeave: clearHoveredRows,
11804
12628
  children: [
11805
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12629
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11806
12630
  TaskList,
11807
12631
  {
11808
12632
  tasks: normalizedTasks,
@@ -11855,17 +12679,17 @@ function TaskGanttChartInner(props, ref) {
11855
12679
  visibleRowIndices: visibleTaskWindowIndices
11856
12680
  }
11857
12681
  ),
11858
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12682
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11859
12683
  "div",
11860
12684
  {
11861
- className: isTableMatrixMode || showChart ? "gantt-chartSurface" : "gantt-chartSurface gantt-chart-hidden",
12685
+ className: isTableMatrixMode || isPlanFactMode || showChart ? "gantt-chartSurface" : "gantt-chartSurface gantt-chart-hidden",
11862
12686
  style: {
11863
- minWidth: isTableMatrixMode ? matrixWidth !== void 0 ? `${matrixWidth}px` : void 0 : `${gridWidth}px`,
11864
- width: isTableMatrixMode ? matrixWidth !== void 0 ? `${matrixWidth}px` : "max-content" : void 0,
11865
- flex: isTableMatrixMode ? "0 0 auto" : 1,
11866
- display: isTableMatrixMode || showChart ? void 0 : "none"
12687
+ minWidth: isTableMatrixMode ? matrixWidth !== void 0 ? `${matrixWidth}px` : void 0 : isPlanFactMode ? `${gridWidth}px` : `${gridWidth}px`,
12688
+ width: isTableMatrixMode ? matrixWidth !== void 0 ? `${matrixWidth}px` : "max-content" : isPlanFactMode ? `${gridWidth}px` : void 0,
12689
+ flex: isTableMatrixMode || isPlanFactMode ? "0 0 auto" : 1,
12690
+ display: isTableMatrixMode || isPlanFactMode || showChart ? void 0 : "none"
11867
12691
  },
11868
- children: isTableMatrixMode ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12692
+ children: isTableMatrixMode ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11869
12693
  TableMatrix,
11870
12694
  {
11871
12695
  tasks: visibleTasks,
@@ -11882,14 +12706,31 @@ function TaskGanttChartInner(props, ref) {
11882
12706
  highlightedTaskIds: taskListHighlightedTaskIds,
11883
12707
  filterMode
11884
12708
  }
11885
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
11886
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
12709
+ ) : isPlanFactMode ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
12710
+ PlanFactMatrix,
12711
+ {
12712
+ tasks: visibleTasks,
12713
+ allTasks: normalizedTasks,
12714
+ dateRange,
12715
+ dayWidth,
12716
+ rowHeight: effectiveRowHeight,
12717
+ headerHeight: timelineHeaderHeight,
12718
+ bodyMinHeight: tableBodyMinHeight,
12719
+ selectedTaskId,
12720
+ onTaskSelect: handleTaskSelect,
12721
+ onTasksChange: handleTaskChange,
12722
+ onCellCommit: onPlanFactCellCommit,
12723
+ highlightedTaskIds: taskListHighlightedTaskIds,
12724
+ filterMode
12725
+ }
12726
+ ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
12727
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
11887
12728
  "div",
11888
12729
  {
11889
12730
  className: "gantt-stickyHeader",
11890
12731
  style: { width: `${gridWidth}px`, height: `${timelineHeaderHeight}px` },
11891
12732
  children: [
11892
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12733
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11893
12734
  TimeScaleHeader_default,
11894
12735
  {
11895
12736
  days: dateRange,
@@ -11902,7 +12743,7 @@ function TaskGanttChartInner(props, ref) {
11902
12743
  onTimelineHoverEnd: () => setActiveTimelineTooltip(null)
11903
12744
  }
11904
12745
  ),
11905
- activeTimelineTooltip && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "gantt-timelineTooltipLayer", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12746
+ activeTimelineTooltip && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "gantt-timelineTooltipLayer", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11906
12747
  "div",
11907
12748
  {
11908
12749
  className: "gantt-timelineTooltip",
@@ -11916,7 +12757,7 @@ function TaskGanttChartInner(props, ref) {
11916
12757
  ]
11917
12758
  }
11918
12759
  ),
11919
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
12760
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
11920
12761
  "div",
11921
12762
  {
11922
12763
  className: "gantt-taskArea",
@@ -11926,7 +12767,7 @@ function TaskGanttChartInner(props, ref) {
11926
12767
  height: `${totalGridHeight}px`
11927
12768
  },
11928
12769
  children: [
11929
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12770
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11930
12771
  GridBackground_default,
11931
12772
  {
11932
12773
  dateRange,
@@ -11936,7 +12777,7 @@ function TaskGanttChartInner(props, ref) {
11936
12777
  isCustomWeekend
11937
12778
  }
11938
12779
  ),
11939
- todayInRange && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12780
+ todayInRange && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11940
12781
  TodayIndicator_default,
11941
12782
  {
11942
12783
  monthStart,
@@ -11945,7 +12786,7 @@ function TaskGanttChartInner(props, ref) {
11945
12786
  onHoverEnd: () => setActiveTimelineTooltip(null)
11946
12787
  }
11947
12788
  ),
11948
- visibleTimelineMarkers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12789
+ visibleTimelineMarkers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11949
12790
  TimelineMarkers_default,
11950
12791
  {
11951
12792
  rangeStart: monthStart,
@@ -11956,7 +12797,7 @@ function TaskGanttChartInner(props, ref) {
11956
12797
  onHoverEnd: () => setActiveTimelineTooltip(null)
11957
12798
  }
11958
12799
  ),
11959
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12800
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11960
12801
  DependencyLines_default,
11961
12802
  {
11962
12803
  tasks: renderedDependencyTasks,
@@ -11974,7 +12815,7 @@ function TaskGanttChartInner(props, ref) {
11974
12815
  weekendPredicate: isCustomWeekend
11975
12816
  }
11976
12817
  ),
11977
- dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12818
+ dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11978
12819
  DragGuideLines_default,
11979
12820
  {
11980
12821
  isDragging: dragGuideLines.isDragging,
@@ -11984,7 +12825,7 @@ function TaskGanttChartInner(props, ref) {
11984
12825
  totalHeight: totalGridHeight
11985
12826
  }
11986
12827
  ),
11987
- renderedChartTasks.map(({ task, index }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12828
+ renderedChartTasks.map(({ task, index }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11988
12829
  "div",
11989
12830
  {
11990
12831
  style: {
@@ -11994,7 +12835,7 @@ function TaskGanttChartInner(props, ref) {
11994
12835
  right: 0,
11995
12836
  height: `${effectiveRowHeight}px`
11996
12837
  },
11997
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
12838
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11998
12839
  TaskRow_default,
11999
12840
  {
12000
12841
  task,
@@ -12045,14 +12886,14 @@ function TaskGanttChartInner(props, ref) {
12045
12886
  }
12046
12887
  );
12047
12888
  }
12048
- var TaskGanttChart = (0, import_react17.forwardRef)(TaskGanttChartInner);
12049
- var GanttChart = (0, import_react17.forwardRef)(GanttChartInner);
12889
+ var TaskGanttChart = (0, import_react18.forwardRef)(TaskGanttChartInner);
12890
+ var GanttChart = (0, import_react18.forwardRef)(GanttChartInner);
12050
12891
  GanttChart.displayName = "GanttChart";
12051
12892
 
12052
12893
  // src/components/ui/Button.tsx
12053
- var import_react18 = __toESM(require("react"));
12054
- var import_jsx_runtime19 = require("react/jsx-runtime");
12055
- var Button = import_react18.default.forwardRef(
12894
+ var import_react19 = __toESM(require("react"));
12895
+ var import_jsx_runtime20 = require("react/jsx-runtime");
12896
+ var Button = import_react19.default.forwardRef(
12056
12897
  ({ className, variant = "default", size = "default", children, ...props }, ref) => {
12057
12898
  const classes = [
12058
12899
  "gantt-btn",
@@ -12060,7 +12901,7 @@ var Button = import_react18.default.forwardRef(
12060
12901
  size !== "default" ? `gantt-btn-${size}` : "",
12061
12902
  className || ""
12062
12903
  ].filter(Boolean).join(" ");
12063
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { ref, className: classes, ...props, children });
12904
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { ref, className: classes, ...props, children });
12064
12905
  }
12065
12906
  );
12066
12907
  Button.displayName = "Button";
@@ -12108,6 +12949,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
12108
12949
  GanttChart,
12109
12950
  GridBackground,
12110
12951
  Input,
12952
+ PlanFactMatrix,
12111
12953
  Popover,
12112
12954
  PopoverContent,
12113
12955
  PopoverTrigger,