gantt-lib 0.88.2 → 0.90.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.d.mts CHANGED
@@ -467,7 +467,7 @@ interface GanttModeProps<TTask extends Task = Task> {
467
467
  onPromoteTask?: (taskId: string) => void;
468
468
  /** Callback when a task is demoted (parentId set). If not provided, default internal logic is used. */
469
469
  onDemoteTask?: (taskId: string, newParentId: string) => void;
470
- /** Callback when a parent task is ungrouped (removed while direct children move one level up). */
470
+ /** Callback when a parent task is ungrouped while direct children move one level up and the parent remains. */
471
471
  onUngroupTask?: (taskId: string) => void;
472
472
  /** Enable add task button at bottom of task list (default: true) */
473
473
  enableAddTask?: boolean;
@@ -543,13 +543,21 @@ interface ExportToPdfHeaderOptions {
543
543
  /** Export date shown on the right; string is rendered as-is */
544
544
  exportDate?: string | Date;
545
545
  }
546
+ interface ScrollToRowOptions {
547
+ /** Keep built-in row selection styling after scroll (default: true) */
548
+ select?: boolean;
549
+ /** Browser scroll behavior for the vertical scroll action (default: 'smooth') */
550
+ behavior?: ScrollBehavior;
551
+ /** Automatically clear built-in row selection after N milliseconds */
552
+ clearSelectionAfterMs?: number;
553
+ }
546
554
  /**
547
555
  * Ref handle type for GanttChart — exposes imperative scroll methods.
548
556
  */
549
557
  interface GanttChartHandle {
550
558
  scrollToToday: () => void;
551
559
  scrollToTask: (taskId: string) => void;
552
- scrollToRow: (taskId: string) => void;
560
+ scrollToRow: (taskId: string, options?: ScrollToRowOptions) => void;
553
561
  collapseAll: () => void;
554
562
  expandAll: () => void;
555
563
  exportToPdf: (options?: ExportToPdfOptions) => Promise<void>;
@@ -764,7 +772,7 @@ interface TaskListProps {
764
772
  onPromoteTask?: (taskId: string) => void;
765
773
  /** Callback when task is demoted (parentId set to previous task) */
766
774
  onDemoteTask?: (taskId: string, newParentId: string) => void;
767
- /** Callback when parent task is ungrouped (removed while direct children move one level up) */
775
+ /** Callback when a parent task is ungrouped while direct children move one level up and the parent remains */
768
776
  onUngroupTask?: (taskId: string) => void;
769
777
  /** Custom day configurations for date picker */
770
778
  customDays?: CustomDayConfig[];
package/dist/index.d.ts CHANGED
@@ -467,7 +467,7 @@ interface GanttModeProps<TTask extends Task = Task> {
467
467
  onPromoteTask?: (taskId: string) => void;
468
468
  /** Callback when a task is demoted (parentId set). If not provided, default internal logic is used. */
469
469
  onDemoteTask?: (taskId: string, newParentId: string) => void;
470
- /** Callback when a parent task is ungrouped (removed while direct children move one level up). */
470
+ /** Callback when a parent task is ungrouped while direct children move one level up and the parent remains. */
471
471
  onUngroupTask?: (taskId: string) => void;
472
472
  /** Enable add task button at bottom of task list (default: true) */
473
473
  enableAddTask?: boolean;
@@ -543,13 +543,21 @@ interface ExportToPdfHeaderOptions {
543
543
  /** Export date shown on the right; string is rendered as-is */
544
544
  exportDate?: string | Date;
545
545
  }
546
+ interface ScrollToRowOptions {
547
+ /** Keep built-in row selection styling after scroll (default: true) */
548
+ select?: boolean;
549
+ /** Browser scroll behavior for the vertical scroll action (default: 'smooth') */
550
+ behavior?: ScrollBehavior;
551
+ /** Automatically clear built-in row selection after N milliseconds */
552
+ clearSelectionAfterMs?: number;
553
+ }
546
554
  /**
547
555
  * Ref handle type for GanttChart — exposes imperative scroll methods.
548
556
  */
549
557
  interface GanttChartHandle {
550
558
  scrollToToday: () => void;
551
559
  scrollToTask: (taskId: string) => void;
552
- scrollToRow: (taskId: string) => void;
560
+ scrollToRow: (taskId: string, options?: ScrollToRowOptions) => void;
553
561
  collapseAll: () => void;
554
562
  expandAll: () => void;
555
563
  exportToPdf: (options?: ExportToPdfOptions) => Promise<void>;
@@ -764,7 +772,7 @@ interface TaskListProps {
764
772
  onPromoteTask?: (taskId: string) => void;
765
773
  /** Callback when task is demoted (parentId set to previous task) */
766
774
  onDemoteTask?: (taskId: string, newParentId: string) => void;
767
- /** Callback when parent task is ungrouped (removed while direct children move one level up) */
775
+ /** Callback when a parent task is ungrouped while direct children move one level up and the parent remains */
768
776
  onUngroupTask?: (taskId: string) => void;
769
777
  /** Custom day configurations for date picker */
770
778
  customDays?: CustomDayConfig[];
package/dist/index.js CHANGED
@@ -5142,10 +5142,13 @@ var TaskListRow = import_react10.default.memo(
5142
5142
  }
5143
5143
  const clampedValue = Math.max(0, Math.min(100, progressValue));
5144
5144
  if ((clampedValue === 100 || clampedValue === 0) && isTaskParent(task.id, allTasks)) {
5145
- const children = getChildren(task.id, allTasks);
5145
+ const descendants = getAllDescendants(task.id, allTasks);
5146
5146
  const updatedTasks = [
5147
5147
  { ...task, progress: clampedValue },
5148
- ...children.map((child) => ({ ...child, progress: clampedValue }))
5148
+ ...descendants.map((descendant) => ({
5149
+ ...descendant,
5150
+ progress: clampedValue
5151
+ }))
5149
5152
  ];
5150
5153
  onTasksChange?.(updatedTasks);
5151
5154
  } else {
@@ -5168,11 +5171,11 @@ var TaskListRow = import_react10.default.memo(
5168
5171
  progressConfirmedRef.current = true;
5169
5172
  const clampedValue = Math.max(0, Math.min(100, progressValue));
5170
5173
  if ((clampedValue === 100 || clampedValue === 0) && isTaskParent(task.id, allTasks)) {
5171
- const children = getChildren(task.id, allTasks);
5174
+ const descendants = getAllDescendants(task.id, allTasks);
5172
5175
  const updatedTasks = [
5173
5176
  { ...task, progress: clampedValue },
5174
- ...children.map((child) => ({
5175
- ...child,
5177
+ ...descendants.map((descendant) => ({
5178
+ ...descendant,
5176
5179
  progress: clampedValue
5177
5180
  }))
5178
5181
  ];
@@ -5379,16 +5382,9 @@ var TaskListRow = import_react10.default.memo(
5379
5382
  const handleApplyColor = (0, import_react10.useCallback)(
5380
5383
  (color) => {
5381
5384
  if (!onTasksChange) return;
5382
- const descendantIds = /* @__PURE__ */ new Set();
5383
- if (isParent) {
5384
- const stack = getChildren(task.id, allTasks);
5385
- while (stack.length > 0) {
5386
- const current = stack.shift();
5387
- if (!current || descendantIds.has(current.id)) continue;
5388
- descendantIds.add(current.id);
5389
- stack.push(...getChildren(current.id, allTasks));
5390
- }
5391
- }
5385
+ const descendantIds = new Set(
5386
+ isParent ? getAllDescendants(task.id, allTasks).map((descendant) => descendant.id) : []
5387
+ );
5392
5388
  const updatedTasks = [
5393
5389
  { ...task, color },
5394
5390
  ...allTasks.filter((candidate) => descendantIds.has(candidate.id)).map((candidate) => ({ ...candidate, color }))
@@ -9622,6 +9618,7 @@ function TaskGanttChartInner(props, ref) {
9622
9618
  const containerRef = (0, import_react15.useRef)(null);
9623
9619
  const scrollContainerRef = (0, import_react15.useRef)(null);
9624
9620
  const scrollContentRef = (0, import_react15.useRef)(null);
9621
+ const clearSelectedTaskTimeoutRef = (0, import_react15.useRef)(null);
9625
9622
  const [selectedTaskId, setSelectedTaskId] = (0, import_react15.useState)(null);
9626
9623
  const [taskListHasRightShadow, setTaskListHasRightShadow] = (0, import_react15.useState)(false);
9627
9624
  const [selectedChip, setSelectedChip] = (0, import_react15.useState)(null);
@@ -9633,7 +9630,17 @@ function TaskGanttChartInner(props, ref) {
9633
9630
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
9634
9631
  [customDays, isWeekend3]
9635
9632
  );
9636
- const dateRange = (0, import_react15.useMemo)(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
9633
+ const dateRangeTasks = (0, import_react15.useMemo)(() => {
9634
+ if (!showBaseline) {
9635
+ return normalizedTasks;
9636
+ }
9637
+ return normalizedTasks.map((task) => ({
9638
+ ...task,
9639
+ startDate: task.baselineStartDate && parseUTCDate(task.baselineStartDate).getTime() < parseUTCDate(task.startDate).getTime() ? task.baselineStartDate : task.startDate,
9640
+ endDate: task.baselineEndDate && parseUTCDate(task.baselineEndDate).getTime() > parseUTCDate(task.endDate).getTime() ? task.baselineEndDate : task.endDate
9641
+ }));
9642
+ }, [normalizedTasks, showBaseline]);
9643
+ const dateRange = (0, import_react15.useMemo)(() => getMultiMonthDays(dateRangeTasks), [dateRangeTasks]);
9637
9644
  const [validationResult, setValidationResult] = (0, import_react15.useState)(null);
9638
9645
  const [cascadeOverrides, setCascadeOverrides] = (0, import_react15.useState)(/* @__PURE__ */ new Map());
9639
9646
  const gridWidth = (0, import_react15.useMemo)(
@@ -9741,7 +9748,7 @@ function TaskGanttChartInner(props, ref) {
9741
9748
  const scrollLeft = Math.round(taskOffset - dayWidth * 2);
9742
9749
  container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
9743
9750
  }, [tasks, dateRange, dayWidth]);
9744
- const scrollToRow = (0, import_react15.useCallback)((taskId) => {
9751
+ const scrollToRow = (0, import_react15.useCallback)((taskId, options = {}) => {
9745
9752
  const container = scrollContainerRef.current;
9746
9753
  if (!container) return;
9747
9754
  const task = tasks.find((t) => t.id === taskId);
@@ -9750,8 +9757,25 @@ function TaskGanttChartInner(props, ref) {
9750
9757
  if (rowIndex === -1) return;
9751
9758
  const paddedRowIndex = Math.max(0, rowIndex - SCROLL_TO_ROW_CONTEXT_ROWS);
9752
9759
  const scrollTop = Math.max(0, rowHeight * paddedRowIndex);
9753
- setSelectedTaskId(taskId);
9754
- container.scrollTo({ top: scrollTop, behavior: "smooth" });
9760
+ const {
9761
+ select = true,
9762
+ behavior = "smooth",
9763
+ clearSelectionAfterMs
9764
+ } = options;
9765
+ if (clearSelectedTaskTimeoutRef.current !== null) {
9766
+ window.clearTimeout(clearSelectedTaskTimeoutRef.current);
9767
+ clearSelectedTaskTimeoutRef.current = null;
9768
+ }
9769
+ if (select) {
9770
+ setSelectedTaskId(taskId);
9771
+ if (typeof clearSelectionAfterMs === "number" && clearSelectionAfterMs >= 0) {
9772
+ clearSelectedTaskTimeoutRef.current = window.setTimeout(() => {
9773
+ setSelectedTaskId((current) => current === taskId ? null : current);
9774
+ clearSelectedTaskTimeoutRef.current = null;
9775
+ }, clearSelectionAfterMs);
9776
+ }
9777
+ }
9778
+ container.scrollTo({ top: scrollTop, behavior });
9755
9779
  }, [tasks, visibleTasks, rowHeight]);
9756
9780
  const [dragGuideLines, setDragGuideLines] = (0, import_react15.useState)(null);
9757
9781
  const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react15.useState)(null);
@@ -9761,6 +9785,11 @@ function TaskGanttChartInner(props, ref) {
9761
9785
  setValidationResult(result);
9762
9786
  onValidateDependencies?.(result);
9763
9787
  }, [tasks, onValidateDependencies]);
9788
+ (0, import_react15.useEffect)(() => () => {
9789
+ if (clearSelectedTaskTimeoutRef.current !== null) {
9790
+ window.clearTimeout(clearSelectedTaskTimeoutRef.current);
9791
+ }
9792
+ }, []);
9764
9793
  const handleTaskChange = (0, import_react15.useCallback)((updatedTasks) => {
9765
9794
  const updatedTask = updatedTasks[0];
9766
9795
  if (!updatedTask) return;
@@ -10079,7 +10108,6 @@ function TaskGanttChartInner(props, ref) {
10079
10108
  if (!hasDirectChildren) return;
10080
10109
  const changedTasks = [];
10081
10110
  for (const task of tasks) {
10082
- if (task.id === taskId) continue;
10083
10111
  const nextParentId = task.parentId === taskId ? parentTask.parentId : task.parentId;
10084
10112
  const nextDependencies = task.dependencies?.filter((dep) => dep.taskId !== taskId);
10085
10113
  if (nextParentId !== task.parentId || nextDependencies?.length !== task.dependencies?.length) {
@@ -10093,8 +10121,7 @@ function TaskGanttChartInner(props, ref) {
10093
10121
  if (changedTasks.length > 0) {
10094
10122
  onTasksChange?.(changedTasks);
10095
10123
  }
10096
- onDelete?.(taskId);
10097
- }, [tasks, onTasksChange, onDelete, onUngroupTask]);
10124
+ }, [tasks, onTasksChange, onUngroupTask]);
10098
10125
  const panStateRef = (0, import_react15.useRef)(null);
10099
10126
  const handlePanStart = (0, import_react15.useCallback)((e) => {
10100
10127
  if (e.button !== 0) return;