gantt-lib 0.89.0 → 0.91.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
@@ -3807,7 +3807,8 @@ var DatePicker = ({
3807
3807
  className,
3808
3808
  disabled = false,
3809
3809
  isWeekend: isWeekend3,
3810
- businessDays = true
3810
+ businessDays = true,
3811
+ footer
3811
3812
  }) => {
3812
3813
  const [open, setOpen] = useState3(false);
3813
3814
  const [inputValue, setInputValue] = useState3("");
@@ -4085,7 +4086,8 @@ var DatePicker = ({
4085
4086
  initialDate: activeDate,
4086
4087
  isWeekend: isWeekend3
4087
4088
  }
4088
- )
4089
+ ),
4090
+ footer ? /* @__PURE__ */ jsx10("div", { className: "gantt-datepicker-footer", children: footer }) : null
4089
4091
  ]
4090
4092
  }
4091
4093
  )
@@ -4727,7 +4729,9 @@ var TaskListRow = React9.memo(
4727
4729
  resolvedColumns,
4728
4730
  isTaskSelected = false,
4729
4731
  onTaskSelectionChange,
4730
- taskListMenuCommands = []
4732
+ taskListMenuCommands = [],
4733
+ taskDateChangeMode = "preserve-duration",
4734
+ onTaskDateChangeModeChange
4731
4735
  }) => {
4732
4736
  const [editingColumnId, setEditingColumnId] = useState4(null);
4733
4737
  const editingName = editingColumnId === "name";
@@ -5027,10 +5031,13 @@ var TaskListRow = React9.memo(
5027
5031
  }
5028
5032
  const clampedValue = Math.max(0, Math.min(100, progressValue));
5029
5033
  if ((clampedValue === 100 || clampedValue === 0) && isTaskParent(task.id, allTasks)) {
5030
- const children = getChildren(task.id, allTasks);
5034
+ const descendants = getAllDescendants(task.id, allTasks);
5031
5035
  const updatedTasks = [
5032
5036
  { ...task, progress: clampedValue },
5033
- ...children.map((child) => ({ ...child, progress: clampedValue }))
5037
+ ...descendants.map((descendant) => ({
5038
+ ...descendant,
5039
+ progress: clampedValue
5040
+ }))
5034
5041
  ];
5035
5042
  onTasksChange?.(updatedTasks);
5036
5043
  } else {
@@ -5053,11 +5060,11 @@ var TaskListRow = React9.memo(
5053
5060
  progressConfirmedRef.current = true;
5054
5061
  const clampedValue = Math.max(0, Math.min(100, progressValue));
5055
5062
  if ((clampedValue === 100 || clampedValue === 0) && isTaskParent(task.id, allTasks)) {
5056
- const children = getChildren(task.id, allTasks);
5063
+ const descendants = getAllDescendants(task.id, allTasks);
5057
5064
  const updatedTasks = [
5058
5065
  { ...task, progress: clampedValue },
5059
- ...children.map((child) => ({
5060
- ...child,
5066
+ ...descendants.map((descendant) => ({
5067
+ ...descendant,
5061
5068
  progress: clampedValue
5062
5069
  }))
5063
5070
  ];
@@ -5127,24 +5134,22 @@ var TaskListRow = React9.memo(
5127
5134
  emitMilestoneDateChange(newDateISO);
5128
5135
  return;
5129
5136
  }
5130
- let nextEndISO;
5131
5137
  const normalizedInputStart = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`), 1, weekendPredicate) : /* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`);
5132
- if (businessDays) {
5133
- const duration = getDuration(task.startDate, task.endDate);
5134
- nextEndISO = buildTaskRangeFromStart(
5138
+ const { startDate: normalizedStart, endDate: normalizedEnd } = taskDateChangeMode === "free" ? normalizeTaskDates(
5139
+ normalizedInputStart,
5140
+ normalizedInputStart.getTime() > parseUTCDate(task.endDate).getTime() ? normalizedInputStart : parseUTCDate(task.endDate)
5141
+ ) : normalizeTaskDates(
5142
+ normalizedInputStart,
5143
+ businessDays ? buildTaskRangeFromStart(
5135
5144
  normalizedInputStart,
5136
- duration,
5145
+ getDuration(task.startDate, task.endDate),
5137
5146
  true,
5138
5147
  weekendPredicate,
5139
5148
  1
5140
- ).end.toISOString().split("T")[0];
5141
- } else {
5142
- const origStart = parseUTCDate(task.startDate);
5143
- const origEnd = parseUTCDate(task.endDate);
5144
- const durationMs = origEnd.getTime() - origStart.getTime();
5145
- nextEndISO = new Date(normalizedInputStart.getTime() + durationMs).toISOString().split("T")[0];
5146
- }
5147
- const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(normalizedInputStart, nextEndISO);
5149
+ ).end.toISOString().split("T")[0] : new Date(
5150
+ normalizedInputStart.getTime() + (parseUTCDate(task.endDate).getTime() - parseUTCDate(task.startDate).getTime())
5151
+ ).toISOString().split("T")[0]
5152
+ );
5148
5153
  const clampedRange = clampTaskRangeForIncomingFS(
5149
5154
  task,
5150
5155
  /* @__PURE__ */ new Date(`${normalizedStart}T00:00:00.000Z`),
@@ -5173,7 +5178,7 @@ var TaskListRow = React9.memo(
5173
5178
  }
5174
5179
  ]);
5175
5180
  },
5176
- [task, onTasksChange, businessDays, getDuration, getEndDate, allTasks, weekendPredicate, isMilestone, emitMilestoneDateChange]
5181
+ [task, onTasksChange, businessDays, getDuration, allTasks, weekendPredicate, isMilestone, emitMilestoneDateChange, taskDateChangeMode]
5177
5182
  );
5178
5183
  const handleEndDateChange = useCallback4(
5179
5184
  (newDateISO) => {
@@ -5182,24 +5187,22 @@ var TaskListRow = React9.memo(
5182
5187
  emitMilestoneDateChange(newDateISO);
5183
5188
  return;
5184
5189
  }
5185
- let nextStartISO;
5186
5190
  const normalizedInputEnd = businessDays ? alignToWorkingDay(/* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`), -1, weekendPredicate) : /* @__PURE__ */ new Date(`${newDateISO}T00:00:00.000Z`);
5187
- if (businessDays) {
5188
- const duration = getDuration(task.startDate, task.endDate);
5189
- nextStartISO = buildTaskRangeFromEnd(
5191
+ const { startDate: normalizedStart, endDate: normalizedEnd } = taskDateChangeMode === "free" ? normalizeTaskDates(
5192
+ normalizedInputEnd.getTime() < parseUTCDate(task.startDate).getTime() ? normalizedInputEnd : parseUTCDate(task.startDate),
5193
+ normalizedInputEnd
5194
+ ) : normalizeTaskDates(
5195
+ businessDays ? buildTaskRangeFromEnd(
5190
5196
  normalizedInputEnd,
5191
- duration,
5197
+ getDuration(task.startDate, task.endDate),
5192
5198
  true,
5193
5199
  weekendPredicate,
5194
5200
  -1
5195
- ).start.toISOString().split("T")[0];
5196
- } else {
5197
- const origStart = parseUTCDate(task.startDate);
5198
- const origEnd = parseUTCDate(task.endDate);
5199
- const durationMs = origEnd.getTime() - origStart.getTime();
5200
- nextStartISO = new Date(normalizedInputEnd.getTime() - durationMs).toISOString().split("T")[0];
5201
- }
5202
- const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(nextStartISO, normalizedInputEnd);
5201
+ ).start.toISOString().split("T")[0] : new Date(
5202
+ normalizedInputEnd.getTime() - (parseUTCDate(task.endDate).getTime() - parseUTCDate(task.startDate).getTime())
5203
+ ).toISOString().split("T")[0],
5204
+ normalizedInputEnd
5205
+ );
5203
5206
  const clampedRange = clampTaskRangeForIncomingFS(
5204
5207
  task,
5205
5208
  /* @__PURE__ */ new Date(`${normalizedStart}T00:00:00.000Z`),
@@ -5228,8 +5231,19 @@ var TaskListRow = React9.memo(
5228
5231
  }
5229
5232
  ]);
5230
5233
  },
5231
- [task, onTasksChange, businessDays, getDuration, weekendPredicate, allTasks, isMilestone, emitMilestoneDateChange]
5234
+ [task, onTasksChange, businessDays, getDuration, weekendPredicate, allTasks, isMilestone, emitMilestoneDateChange, taskDateChangeMode]
5232
5235
  );
5236
+ const datePickerFooter = onTaskDateChangeModeChange ? /* @__PURE__ */ jsxs9("label", { className: "gantt-datepicker-mode-checkbox", children: [
5237
+ /* @__PURE__ */ jsx12(
5238
+ "input",
5239
+ {
5240
+ type: "checkbox",
5241
+ checked: taskDateChangeMode === "preserve-duration",
5242
+ onChange: (event) => onTaskDateChangeModeChange(event.target.checked ? "preserve-duration" : "free")
5243
+ }
5244
+ ),
5245
+ /* @__PURE__ */ jsx12("span", { children: "\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0442\u044C \u0434\u043B\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C" })
5246
+ ] }) : null;
5233
5247
  const handleRowClickInternal = useCallback4(() => {
5234
5248
  onRowClick?.(task.id);
5235
5249
  }, [task.id, onRowClick]);
@@ -5264,16 +5278,9 @@ var TaskListRow = React9.memo(
5264
5278
  const handleApplyColor = useCallback4(
5265
5279
  (color) => {
5266
5280
  if (!onTasksChange) return;
5267
- const descendantIds = /* @__PURE__ */ new Set();
5268
- if (isParent) {
5269
- const stack = getChildren(task.id, allTasks);
5270
- while (stack.length > 0) {
5271
- const current = stack.shift();
5272
- if (!current || descendantIds.has(current.id)) continue;
5273
- descendantIds.add(current.id);
5274
- stack.push(...getChildren(current.id, allTasks));
5275
- }
5276
- }
5281
+ const descendantIds = new Set(
5282
+ isParent ? getAllDescendants(task.id, allTasks).map((descendant) => descendant.id) : []
5283
+ );
5277
5284
  const updatedTasks = [
5278
5285
  { ...task, color },
5279
5286
  ...allTasks.filter((candidate) => descendantIds.has(candidate.id)).map((candidate) => ({ ...candidate, color }))
@@ -5935,7 +5942,8 @@ var TaskListRow = React9.memo(
5935
5942
  portal: true,
5936
5943
  disabled: task.locked,
5937
5944
  isWeekend: weekendPredicate,
5938
- businessDays
5945
+ businessDays,
5946
+ footer: datePickerFooter
5939
5947
  }
5940
5948
  )
5941
5949
  }
@@ -5954,7 +5962,8 @@ var TaskListRow = React9.memo(
5954
5962
  portal: true,
5955
5963
  disabled: task.locked,
5956
5964
  isWeekend: weekendPredicate,
5957
- businessDays
5965
+ businessDays,
5966
+ footer: datePickerFooter
5958
5967
  }
5959
5968
  )
5960
5969
  }
@@ -6634,7 +6643,9 @@ var TaskList = ({
6634
6643
  isFilterActive = false,
6635
6644
  additionalColumns,
6636
6645
  hiddenTaskListColumns,
6637
- taskListMenuCommands
6646
+ taskListMenuCommands,
6647
+ taskDateChangeMode = "preserve-duration",
6648
+ onTaskDateChangeModeChange
6638
6649
  }) => {
6639
6650
  const [internalSelectedTaskIds, setInternalSelectedTaskIds] = useState6(/* @__PURE__ */ new Set());
6640
6651
  const effectiveSelectedTaskIds = selectedTaskIds ?? internalSelectedTaskIds;
@@ -7370,7 +7381,9 @@ var TaskList = ({
7370
7381
  resolvedColumns,
7371
7382
  isTaskSelected: effectiveSelectedTaskIds.has(task.id),
7372
7383
  onTaskSelectionChange: handleToggleTaskSelection,
7373
- taskListMenuCommands
7384
+ taskListMenuCommands,
7385
+ taskDateChangeMode,
7386
+ onTaskDateChangeModeChange
7374
7387
  }
7375
7388
  ),
7376
7389
  pendingInsertDisplayTaskId === task.id && /* @__PURE__ */ jsx14(
@@ -9502,7 +9515,9 @@ function TaskGanttChartInner(props, ref) {
9502
9515
  showChart = true,
9503
9516
  additionalColumns,
9504
9517
  hiddenTaskListColumns,
9505
- taskListMenuCommands
9518
+ taskListMenuCommands,
9519
+ taskDateChangeMode: externalTaskDateChangeMode,
9520
+ onTaskDateChangeModeChange: externalOnTaskDateChangeModeChange
9506
9521
  } = props;
9507
9522
  const containerRef = useRef9(null);
9508
9523
  const scrollContainerRef = useRef9(null);
@@ -9510,16 +9525,29 @@ function TaskGanttChartInner(props, ref) {
9510
9525
  const clearSelectedTaskTimeoutRef = useRef9(null);
9511
9526
  const [selectedTaskId, setSelectedTaskId] = useState9(null);
9512
9527
  const [taskListHasRightShadow, setTaskListHasRightShadow] = useState9(false);
9528
+ const [internalTaskDateChangeMode, setInternalTaskDateChangeMode] = useState9("preserve-duration");
9513
9529
  const [selectedChip, setSelectedChip] = useState9(null);
9514
9530
  const [internalCollapsedParentIds, setInternalCollapsedParentIds] = useState9(/* @__PURE__ */ new Set());
9515
9531
  const collapsedParentIds = externalCollapsedParentIds ?? internalCollapsedParentIds;
9516
9532
  const [editingTaskId, setEditingTaskId] = useState9(null);
9533
+ const taskDateChangeMode = externalTaskDateChangeMode ?? internalTaskDateChangeMode;
9534
+ const handleTaskDateChangeMode = externalOnTaskDateChangeModeChange ?? setInternalTaskDateChangeMode;
9517
9535
  const normalizedTasks = useMemo10(() => normalizeHierarchyTasks(tasks), [tasks]);
9518
9536
  const isCustomWeekend = useMemo10(
9519
9537
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
9520
9538
  [customDays, isWeekend3]
9521
9539
  );
9522
- const dateRange = useMemo10(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
9540
+ const dateRangeTasks = useMemo10(() => {
9541
+ if (!showBaseline) {
9542
+ return normalizedTasks;
9543
+ }
9544
+ return normalizedTasks.map((task) => ({
9545
+ ...task,
9546
+ startDate: task.baselineStartDate && parseUTCDate(task.baselineStartDate).getTime() < parseUTCDate(task.startDate).getTime() ? task.baselineStartDate : task.startDate,
9547
+ endDate: task.baselineEndDate && parseUTCDate(task.baselineEndDate).getTime() > parseUTCDate(task.endDate).getTime() ? task.baselineEndDate : task.endDate
9548
+ }));
9549
+ }, [normalizedTasks, showBaseline]);
9550
+ const dateRange = useMemo10(() => getMultiMonthDays(dateRangeTasks), [dateRangeTasks]);
9523
9551
  const [validationResult, setValidationResult] = useState9(null);
9524
9552
  const [cascadeOverrides, setCascadeOverrides] = useState9(/* @__PURE__ */ new Map());
9525
9553
  const gridWidth = useMemo10(
@@ -9987,7 +10015,6 @@ function TaskGanttChartInner(props, ref) {
9987
10015
  if (!hasDirectChildren) return;
9988
10016
  const changedTasks = [];
9989
10017
  for (const task of tasks) {
9990
- if (task.id === taskId) continue;
9991
10018
  const nextParentId = task.parentId === taskId ? parentTask.parentId : task.parentId;
9992
10019
  const nextDependencies = task.dependencies?.filter((dep) => dep.taskId !== taskId);
9993
10020
  if (nextParentId !== task.parentId || nextDependencies?.length !== task.dependencies?.length) {
@@ -10001,8 +10028,7 @@ function TaskGanttChartInner(props, ref) {
10001
10028
  if (changedTasks.length > 0) {
10002
10029
  onTasksChange?.(changedTasks);
10003
10030
  }
10004
- onDelete?.(taskId);
10005
- }, [tasks, onTasksChange, onDelete, onUngroupTask]);
10031
+ }, [tasks, onTasksChange, onUngroupTask]);
10006
10032
  const panStateRef = useRef9(null);
10007
10033
  const handlePanStart = useCallback8((e) => {
10008
10034
  if (e.button !== 0) return;
@@ -10102,7 +10128,9 @@ function TaskGanttChartInner(props, ref) {
10102
10128
  isFilterActive: !!taskFilter,
10103
10129
  additionalColumns,
10104
10130
  hiddenTaskListColumns,
10105
- taskListMenuCommands
10131
+ taskListMenuCommands,
10132
+ taskDateChangeMode,
10133
+ onTaskDateChangeModeChange: handleTaskDateChangeMode
10106
10134
  }
10107
10135
  ),
10108
10136
  /* @__PURE__ */ jsxs13(