gantt-lib 0.61.0 → 0.63.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.mjs CHANGED
@@ -1547,65 +1547,6 @@ function validateDependencies(tasks) {
1547
1547
  };
1548
1548
  }
1549
1549
 
1550
- // src/adapters/scheduling/drag.ts
1551
- init_dateMath();
1552
- function resolveDateRangeFromPixels(mode, left, width, monthStart, dayWidth, task, businessDays, weekendPredicate) {
1553
- const dayOffset = Math.round(left / dayWidth);
1554
- const rawStartDate = new Date(Date.UTC(
1555
- monthStart.getUTCFullYear(),
1556
- monthStart.getUTCMonth(),
1557
- monthStart.getUTCDate() + dayOffset
1558
- ));
1559
- const rawEndOffset = dayOffset + Math.round(width / dayWidth) - 1;
1560
- const rawEndDate = new Date(Date.UTC(
1561
- monthStart.getUTCFullYear(),
1562
- monthStart.getUTCMonth(),
1563
- monthStart.getUTCDate() + rawEndOffset
1564
- ));
1565
- if (!(businessDays && weekendPredicate)) {
1566
- return { start: rawStartDate, end: rawEndDate };
1567
- }
1568
- if (mode === "move") {
1569
- const originalStart2 = new Date(task.startDate);
1570
- const snapDirection2 = rawStartDate.getTime() >= originalStart2.getTime() ? 1 : -1;
1571
- return moveTaskRange(
1572
- task.startDate,
1573
- task.endDate,
1574
- rawStartDate,
1575
- true,
1576
- weekendPredicate,
1577
- snapDirection2
1578
- );
1579
- }
1580
- if (mode === "resize-right") {
1581
- const fixedStart = new Date(task.startDate);
1582
- const originalEnd = new Date(task.endDate);
1583
- const snapDirection2 = rawEndDate.getTime() >= originalEnd.getTime() ? 1 : -1;
1584
- const alignedEnd = alignToWorkingDay(rawEndDate, snapDirection2, weekendPredicate);
1585
- const duration2 = Math.max(1, getBusinessDaysCount(fixedStart, alignedEnd, weekendPredicate));
1586
- return buildTaskRangeFromStart(fixedStart, duration2, true, weekendPredicate);
1587
- }
1588
- const fixedEnd = new Date(task.endDate);
1589
- const originalStart = new Date(task.startDate);
1590
- const snapDirection = rawStartDate.getTime() >= originalStart.getTime() ? 1 : -1;
1591
- const alignedStart = alignToWorkingDay(rawStartDate, snapDirection, weekendPredicate);
1592
- const duration = Math.max(1, getBusinessDaysCount(alignedStart, fixedEnd, weekendPredicate));
1593
- return buildTaskRangeFromEnd(fixedEnd, duration, true, weekendPredicate);
1594
- }
1595
- function clampDateRangeForIncomingFS(task, range, allTasks, mode, businessDays, weekendPredicate) {
1596
- if (mode === "resize-right") {
1597
- return range;
1598
- }
1599
- return clampTaskRangeForIncomingFS(
1600
- task,
1601
- range.start,
1602
- range.end,
1603
- allTasks,
1604
- businessDays,
1605
- weekendPredicate
1606
- );
1607
- }
1608
-
1609
1550
  // src/utils/hierarchyOrder.ts
1610
1551
  init_dateUtils();
1611
1552
  function flattenHierarchy(tasks) {
@@ -2087,6 +2028,67 @@ var isTaskExpired = (task, referenceDate = /* @__PURE__ */ new Date()) => {
2087
2028
 
2088
2029
  // src/hooks/useTaskDrag.ts
2089
2030
  import { useEffect, useRef, useState, useCallback } from "react";
2031
+
2032
+ // src/adapters/scheduling/drag.ts
2033
+ init_dateMath();
2034
+ function resolveDateRangeFromPixels(mode, left, width, monthStart, dayWidth, task, businessDays, weekendPredicate) {
2035
+ const dayOffset = Math.round(left / dayWidth);
2036
+ const rawStartDate = new Date(Date.UTC(
2037
+ monthStart.getUTCFullYear(),
2038
+ monthStart.getUTCMonth(),
2039
+ monthStart.getUTCDate() + dayOffset
2040
+ ));
2041
+ const rawEndOffset = dayOffset + Math.round(width / dayWidth) - 1;
2042
+ const rawEndDate = new Date(Date.UTC(
2043
+ monthStart.getUTCFullYear(),
2044
+ monthStart.getUTCMonth(),
2045
+ monthStart.getUTCDate() + rawEndOffset
2046
+ ));
2047
+ if (!(businessDays && weekendPredicate)) {
2048
+ return { start: rawStartDate, end: rawEndDate };
2049
+ }
2050
+ if (mode === "move") {
2051
+ const originalStart2 = new Date(task.startDate);
2052
+ const snapDirection2 = rawStartDate.getTime() >= originalStart2.getTime() ? 1 : -1;
2053
+ return moveTaskRange(
2054
+ task.startDate,
2055
+ task.endDate,
2056
+ rawStartDate,
2057
+ true,
2058
+ weekendPredicate,
2059
+ snapDirection2
2060
+ );
2061
+ }
2062
+ if (mode === "resize-right") {
2063
+ const fixedStart = new Date(task.startDate);
2064
+ const originalEnd = new Date(task.endDate);
2065
+ const snapDirection2 = rawEndDate.getTime() >= originalEnd.getTime() ? 1 : -1;
2066
+ const alignedEnd = alignToWorkingDay(rawEndDate, snapDirection2, weekendPredicate);
2067
+ const duration2 = Math.max(1, getBusinessDaysCount(fixedStart, alignedEnd, weekendPredicate));
2068
+ return buildTaskRangeFromStart(fixedStart, duration2, true, weekendPredicate);
2069
+ }
2070
+ const fixedEnd = new Date(task.endDate);
2071
+ const originalStart = new Date(task.startDate);
2072
+ const snapDirection = rawStartDate.getTime() >= originalStart.getTime() ? 1 : -1;
2073
+ const alignedStart = alignToWorkingDay(rawStartDate, snapDirection, weekendPredicate);
2074
+ const duration = Math.max(1, getBusinessDaysCount(alignedStart, fixedEnd, weekendPredicate));
2075
+ return buildTaskRangeFromEnd(fixedEnd, duration, true, weekendPredicate);
2076
+ }
2077
+ function clampDateRangeForIncomingFS(task, range, allTasks, mode, businessDays, weekendPredicate) {
2078
+ if (mode === "resize-right") {
2079
+ return range;
2080
+ }
2081
+ return clampTaskRangeForIncomingFS(
2082
+ task,
2083
+ range.start,
2084
+ range.end,
2085
+ allTasks,
2086
+ businessDays,
2087
+ weekendPredicate
2088
+ );
2089
+ }
2090
+
2091
+ // src/hooks/useTaskDrag.ts
2090
2092
  var globalActiveDrag = null;
2091
2093
  var globalRafId = null;
2092
2094
  function getDayOffsetFromMonthStart(date, monthStart) {
@@ -5340,7 +5342,8 @@ var TaskListRow = React9.memo(
5340
5342
  id: crypto.randomUUID(),
5341
5343
  name: "\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430",
5342
5344
  startDate: todayISO,
5343
- endDate: endISO
5345
+ endDate: endISO,
5346
+ parentId: task.parentId
5344
5347
  };
5345
5348
  onInsertAfter(task.id, newTask);
5346
5349
  },
@@ -5908,10 +5911,17 @@ TaskListRow.displayName = "TaskListRow";
5908
5911
  // src/components/TaskList/NewTaskRow.tsx
5909
5912
  import { useState as useState5, useRef as useRef5, useEffect as useEffect5 } from "react";
5910
5913
  import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
5911
- var NewTaskRow = ({ rowHeight, onConfirm, onCancel }) => {
5914
+ var NewTaskRow = ({
5915
+ rowHeight,
5916
+ onConfirm,
5917
+ onCancel,
5918
+ nestingDepth = 0
5919
+ }) => {
5912
5920
  const [nameValue, setNameValue] = useState5("");
5913
5921
  const inputRef = useRef5(null);
5914
5922
  const confirmedRef = useRef5(false);
5923
+ const inputInnerPadding = 4;
5924
+ const levelOffset = nestingDepth > 0 ? nestingDepth * 20 + 8 : 0;
5915
5925
  useEffect5(() => {
5916
5926
  if (inputRef.current) {
5917
5927
  inputRef.current.focus();
@@ -5950,7 +5960,12 @@ var NewTaskRow = ({ rowHeight, onConfirm, onCancel }) => {
5950
5960
  onKeyDown: handleKeyDown,
5951
5961
  onBlur: handleBlur,
5952
5962
  placeholder: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435",
5953
- className: "gantt-tl-name-input"
5963
+ className: "gantt-tl-name-input",
5964
+ style: {
5965
+ left: `${Math.max(0, levelOffset - inputInnerPadding)}px`,
5966
+ width: levelOffset > 0 ? `calc(100% - ${Math.max(0, levelOffset - inputInnerPadding)}px)` : "100%",
5967
+ paddingLeft: `${inputInnerPadding}px`
5968
+ }
5954
5969
  }
5955
5970
  ) }),
5956
5971
  /* @__PURE__ */ jsx13("div", { className: "gantt-tl-cell" }),
@@ -6374,6 +6389,7 @@ var TaskList = ({
6374
6389
  onTasksChange?.([{ ...task, dependencies: updatedDeps }]);
6375
6390
  }, [tasks, onTasksChange]);
6376
6391
  const [isCreating, setIsCreating] = useState6(false);
6392
+ const [pendingInsert, setPendingInsert] = useState6(null);
6377
6393
  const [draggingIndex, setDraggingIndex] = useState6(null);
6378
6394
  const [dragOverIndex, setDragOverIndex] = useState6(null);
6379
6395
  const dragOriginIndexRef = useRef6(null);
@@ -6532,6 +6548,81 @@ var TaskList = ({
6532
6548
  setIsCreating(false);
6533
6549
  }, [onAdd]);
6534
6550
  const handleCancelNewTask = useCallback5(() => setIsCreating(false), []);
6551
+ const findInsertAfterTaskId = useCallback5((anchorTaskId) => {
6552
+ const anchorIndex = orderedTasks.findIndex((task) => task.id === anchorTaskId);
6553
+ if (anchorIndex === -1) {
6554
+ return anchorTaskId;
6555
+ }
6556
+ const taskById = new Map(orderedTasks.map((task) => [task.id, task]));
6557
+ let insertAfterTaskId = anchorTaskId;
6558
+ for (let index = anchorIndex + 1; index < orderedTasks.length; index += 1) {
6559
+ let currentParentId = orderedTasks[index]?.parentId;
6560
+ let isDescendant = false;
6561
+ while (currentParentId) {
6562
+ if (currentParentId === anchorTaskId) {
6563
+ isDescendant = true;
6564
+ break;
6565
+ }
6566
+ currentParentId = taskById.get(currentParentId)?.parentId;
6567
+ }
6568
+ if (!isDescendant) {
6569
+ break;
6570
+ }
6571
+ insertAfterTaskId = orderedTasks[index].id;
6572
+ }
6573
+ return insertAfterTaskId;
6574
+ }, [orderedTasks]);
6575
+ const pendingInsertDisplayTaskId = useMemo8(() => {
6576
+ if (!pendingInsert) {
6577
+ return null;
6578
+ }
6579
+ const taskById = new Map(visibleTasks.map((task) => [task.id, task]));
6580
+ if (!taskById.has(pendingInsert.anchorTaskId)) {
6581
+ return null;
6582
+ }
6583
+ let displayTaskId = pendingInsert.anchorTaskId;
6584
+ for (const task of visibleTasks) {
6585
+ let currentParentId = task.parentId;
6586
+ while (currentParentId) {
6587
+ if (currentParentId === pendingInsert.anchorTaskId) {
6588
+ displayTaskId = task.id;
6589
+ break;
6590
+ }
6591
+ currentParentId = taskById.get(currentParentId)?.parentId;
6592
+ }
6593
+ }
6594
+ return displayTaskId;
6595
+ }, [pendingInsert, visibleTasks]);
6596
+ const handleStartInsertAfter = useCallback5((taskId, newTask) => {
6597
+ const anchorTask = orderedTasks.find((task) => task.id === taskId);
6598
+ if (!anchorTask) {
6599
+ return;
6600
+ }
6601
+ setIsCreating(false);
6602
+ setPendingInsert({
6603
+ anchorTaskId: taskId,
6604
+ insertAfterTaskId: findInsertAfterTaskId(taskId),
6605
+ parentId: anchorTask.parentId,
6606
+ startDate: newTask.startDate,
6607
+ endDate: newTask.endDate,
6608
+ nestingDepth: nestingDepthMap.get(taskId) ?? 0
6609
+ });
6610
+ }, [findInsertAfterTaskId, nestingDepthMap, orderedTasks]);
6611
+ const handleConfirmInsertedTask = useCallback5((name) => {
6612
+ if (!pendingInsert) {
6613
+ return;
6614
+ }
6615
+ const newTask = {
6616
+ id: crypto.randomUUID(),
6617
+ name,
6618
+ startDate: pendingInsert.startDate,
6619
+ endDate: pendingInsert.endDate,
6620
+ parentId: pendingInsert.parentId
6621
+ };
6622
+ onInsertAfter?.(pendingInsert.insertAfterTaskId, newTask);
6623
+ setPendingInsert(null);
6624
+ }, [onInsertAfter, pendingInsert]);
6625
+ const handleCancelInsertedTask = useCallback5(() => setPendingInsert(null), []);
6535
6626
  function getTaskDepth(task, tasks2) {
6536
6627
  if (!task) return 0;
6537
6628
  let depth = 0;
@@ -6671,73 +6762,87 @@ var TaskList = ({
6671
6762
  /* @__PURE__ */ jsx14("div", { className: "gantt-tl-body", style: { height: `${totalHeight}px` }, children: visibleTasks.map((task, index) => {
6672
6763
  const previousVisibleTask = index > 0 ? visibleTasks[index - 1] : void 0;
6673
6764
  const canDemoteTask = index === 0 || !task.parentId || previousVisibleTask?.id !== task.parentId;
6674
- return /* @__PURE__ */ jsx14(
6675
- TaskListRow,
6676
- {
6677
- task,
6678
- rowIndex: index,
6679
- taskNumber: originalTaskNumberMap[task.id] || "",
6680
- taskNumberMap: originalTaskNumberMap,
6681
- rowHeight,
6682
- onTasksChange,
6683
- selectedTaskId,
6684
- onRowClick: handleRowClick,
6685
- disableTaskNameEditing,
6686
- disableDependencyEditing,
6687
- allTasks: tasks,
6688
- activeLinkType,
6689
- onSetActiveLinkType: setActiveLinkType,
6690
- selectingPredecessorFor,
6691
- dependencyPickMode,
6692
- onSetDependencyPickMode: setDependencyPickMode,
6693
- onSetSelectingPredecessorFor: setSelectingPredecessorFor,
6694
- onAddDependency: handleAddDependency,
6695
- onRemoveDependency: handleRemoveDependency,
6696
- selectedChip,
6697
- onChipSelect: handleChipSelect,
6698
- onScrollToTask,
6699
- onDelete,
6700
- onAdd,
6701
- onInsertAfter,
6702
- editingTaskId: propEditingTaskId,
6703
- isDragging: draggingIndex === index,
6704
- isDragOver: dragOverIndex === index,
6705
- onDragStart: handleDragStart,
6706
- onDragOver: handleDragOver,
6707
- onDrop: handleDrop,
6708
- onDragEnd: handleDragEnd,
6709
- collapsedParentIds,
6710
- onToggleCollapse: handleToggleCollapse,
6711
- onPromoteTask,
6712
- onDemoteTask: onDemoteTask ? handleDemoteWrapper : void 0,
6713
- onDuplicateTask: onReorder ? handleDuplicateTask : void 0,
6714
- canDemoteTask,
6715
- isLastChild: lastChildIds.has(task.id),
6716
- nestingDepth: nestingDepthMap.get(task.id) ?? 0,
6717
- ancestorContinues: ancestorContinuesMap.get(task.id) ?? [],
6718
- customDays,
6719
- isWeekend: isWeekend3,
6720
- businessDays,
6721
- isFilterMatch: filterMode === "highlight" ? highlightedTaskIds.has(task.id) : false,
6722
- isFilterHideMode: filterMode === "hide" && isFilterActive,
6723
- resolvedColumns
6724
- },
6725
- task.id
6726
- );
6765
+ return /* @__PURE__ */ jsxs11(React11.Fragment, { children: [
6766
+ /* @__PURE__ */ jsx14(
6767
+ TaskListRow,
6768
+ {
6769
+ task,
6770
+ rowIndex: index,
6771
+ taskNumber: originalTaskNumberMap[task.id] || "",
6772
+ taskNumberMap: originalTaskNumberMap,
6773
+ rowHeight,
6774
+ onTasksChange,
6775
+ selectedTaskId,
6776
+ onRowClick: handleRowClick,
6777
+ disableTaskNameEditing,
6778
+ disableDependencyEditing,
6779
+ allTasks: tasks,
6780
+ activeLinkType,
6781
+ onSetActiveLinkType: setActiveLinkType,
6782
+ selectingPredecessorFor,
6783
+ dependencyPickMode,
6784
+ onSetDependencyPickMode: setDependencyPickMode,
6785
+ onSetSelectingPredecessorFor: setSelectingPredecessorFor,
6786
+ onAddDependency: handleAddDependency,
6787
+ onRemoveDependency: handleRemoveDependency,
6788
+ selectedChip,
6789
+ onChipSelect: handleChipSelect,
6790
+ onScrollToTask,
6791
+ onDelete,
6792
+ onAdd,
6793
+ onInsertAfter: handleStartInsertAfter,
6794
+ editingTaskId: propEditingTaskId,
6795
+ isDragging: draggingIndex === index,
6796
+ isDragOver: dragOverIndex === index,
6797
+ onDragStart: handleDragStart,
6798
+ onDragOver: handleDragOver,
6799
+ onDrop: handleDrop,
6800
+ onDragEnd: handleDragEnd,
6801
+ collapsedParentIds,
6802
+ onToggleCollapse: handleToggleCollapse,
6803
+ onPromoteTask,
6804
+ onDemoteTask: onDemoteTask ? handleDemoteWrapper : void 0,
6805
+ onDuplicateTask: onReorder ? handleDuplicateTask : void 0,
6806
+ canDemoteTask,
6807
+ isLastChild: lastChildIds.has(task.id),
6808
+ nestingDepth: nestingDepthMap.get(task.id) ?? 0,
6809
+ ancestorContinues: ancestorContinuesMap.get(task.id) ?? [],
6810
+ customDays,
6811
+ isWeekend: isWeekend3,
6812
+ businessDays,
6813
+ isFilterMatch: filterMode === "highlight" ? highlightedTaskIds.has(task.id) : false,
6814
+ isFilterHideMode: filterMode === "hide" && isFilterActive,
6815
+ resolvedColumns
6816
+ }
6817
+ ),
6818
+ pendingInsertDisplayTaskId === task.id && /* @__PURE__ */ jsx14(
6819
+ NewTaskRow,
6820
+ {
6821
+ rowHeight,
6822
+ onConfirm: handleConfirmInsertedTask,
6823
+ onCancel: handleCancelInsertedTask,
6824
+ nestingDepth: pendingInsert?.nestingDepth ?? 0
6825
+ }
6826
+ )
6827
+ ] }, task.id);
6727
6828
  }) }),
6728
- isCreating && /* @__PURE__ */ jsx14(
6829
+ isCreating && !pendingInsert && /* @__PURE__ */ jsx14(
6729
6830
  NewTaskRow,
6730
6831
  {
6731
6832
  rowHeight,
6732
6833
  onConfirm: handleConfirmNewTask,
6733
- onCancel: handleCancelNewTask
6834
+ onCancel: handleCancelNewTask,
6835
+ nestingDepth: 0
6734
6836
  }
6735
6837
  ),
6736
- enableAddTask && onAdd && !isCreating && /* @__PURE__ */ jsx14(
6838
+ enableAddTask && onAdd && !isCreating && !pendingInsert && /* @__PURE__ */ jsx14(
6737
6839
  "button",
6738
6840
  {
6739
6841
  className: `gantt-tl-add-btn${dragOverIndex === visibleTasks.length ? " gantt-tl-add-btn-drag-over" : ""}`,
6740
- onClick: () => setIsCreating(true),
6842
+ onClick: () => {
6843
+ setPendingInsert(null);
6844
+ setIsCreating(true);
6845
+ },
6741
6846
  onDragEnter: (e) => {
6742
6847
  e.preventDefault();
6743
6848
  setDragOverIndex(visibleTasks.length);
@@ -7014,7 +7119,6 @@ function GanttChartInner(props, ref) {
7014
7119
  toDelete.forEach((id) => onDelete?.(id));
7015
7120
  }, [tasks, onTasksChange, onDelete]);
7016
7121
  const handleInsertAfter = useCallback6((taskId, newTask) => {
7017
- setEditingTaskId(newTask.id);
7018
7122
  onInsertAfter?.(taskId, newTask);
7019
7123
  }, [onInsertAfter]);
7020
7124
  const handleReorder = useCallback6((reorderedTasks, movedTaskId, inferredParentId) => {
@@ -7028,8 +7132,11 @@ function GanttChartInner(props, ref) {
7028
7132
  });
7029
7133
  }
7030
7134
  const normalized = normalizeHierarchyTasks(updated);
7135
+ if (onReorder) {
7136
+ onReorder(normalized, movedTaskId, inferredParentId);
7137
+ return;
7138
+ }
7031
7139
  onTasksChange?.(normalized);
7032
- onReorder?.(normalized, movedTaskId, inferredParentId);
7033
7140
  }, [onTasksChange, onReorder]);
7034
7141
  const dependencyOverrides = useMemo9(() => {
7035
7142
  const map = new Map(cascadeOverrides);