gantt-task-react-v 1.3.6 → 1.4.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.
@@ -4688,9 +4688,9 @@ const getStartAndEnd = (containerEl, property, cellSize) => {
4688
4688
  const scrollValue = property === "scrollLeft" ? el.scrollLeft : el.scrollTop;
4689
4689
  const maxScrollValue = property === "scrollLeft" ? el.scrollWidth : el.scrollHeight;
4690
4690
  const fullValue = property === "scrollLeft" ? el.clientWidth : el.clientHeight;
4691
- const firstIndex = Math.max(0, Math.floor(scrollValue / cellSize));
4692
4691
  const visibleCount = Math.max(1, Math.ceil(fullValue / cellSize));
4693
4692
  const overscan = Math.min(100, Math.max(10, Math.ceil(visibleCount * 0.5)));
4693
+ const firstIndex = Math.max(0, Math.floor(scrollValue / cellSize) - overscan);
4694
4694
  const lastIndex = Math.floor((scrollValue + fullValue) / cellSize) + overscan;
4695
4695
  const isStartOfScroll = scrollValue < DELTA;
4696
4696
  const isEndOfScroll = scrollValue + fullValue > maxScrollValue - DELTA;
@@ -5536,6 +5536,37 @@ const TaskListTableHeadersDefaultInner = ({
5536
5536
  canMoveTasks,
5537
5537
  onColumnResizeStart
5538
5538
  }) => {
5539
+ const pinnedStyles = useMemo(() => {
5540
+ const result = {};
5541
+ let leftOffset = 0;
5542
+ if (canMoveTasks) {
5543
+ leftOffset = 24;
5544
+ }
5545
+ for (let i = 0; i < columns.length; i++) {
5546
+ if (columns[i].pinned === "left") {
5547
+ result[i] = {
5548
+ position: "sticky",
5549
+ left: leftOffset,
5550
+ zIndex: 2,
5551
+ backgroundColor: "var(--gantt-table-header-background-color, #fff)"
5552
+ };
5553
+ leftOffset += columns[i].width;
5554
+ }
5555
+ }
5556
+ let rightOffset = 0;
5557
+ for (let i = columns.length - 1; i >= 0; i--) {
5558
+ if (columns[i].pinned === "right") {
5559
+ result[i] = {
5560
+ position: "sticky",
5561
+ right: rightOffset,
5562
+ zIndex: 2,
5563
+ backgroundColor: "var(--gantt-table-header-background-color, #fff)"
5564
+ };
5565
+ rightOffset += columns[i].width;
5566
+ }
5567
+ }
5568
+ return result;
5569
+ }, [columns, canMoveTasks]);
5539
5570
  return /* @__PURE__ */ jsx(
5540
5571
  "div",
5541
5572
  {
@@ -5572,7 +5603,8 @@ const TaskListTableHeadersDefaultInner = ({
5572
5603
  className: styles$h.ganttTable_HeaderItem,
5573
5604
  style: {
5574
5605
  minWidth: width,
5575
- maxWidth: width
5606
+ maxWidth: width,
5607
+ ...pinnedStyles[index2]
5576
5608
  },
5577
5609
  children: [
5578
5610
  title,
@@ -10135,6 +10167,37 @@ const TaskListTableRowInner = forwardRef(
10135
10167
  }
10136
10168
  return classNames.join(" ");
10137
10169
  }, [isCut2, moveOverPosition, isOverlay2, isDragging]);
10170
+ const pinnedStyles = useMemo(() => {
10171
+ const result = {};
10172
+ let leftOffset = 0;
10173
+ if (moveHandleProps || !isOverlay2 && task.type !== "project" && task.id !== "no-project-asigned") {
10174
+ leftOffset = 24;
10175
+ }
10176
+ for (let i = 0; i < columns.length; i++) {
10177
+ if (columns[i].pinned === "left") {
10178
+ result[i] = {
10179
+ position: "sticky",
10180
+ left: leftOffset,
10181
+ zIndex: 1,
10182
+ backgroundColor: "inherit"
10183
+ };
10184
+ leftOffset += columns[i].width;
10185
+ }
10186
+ }
10187
+ let rightOffset = 0;
10188
+ for (let i = columns.length - 1; i >= 0; i--) {
10189
+ if (columns[i].pinned === "right") {
10190
+ result[i] = {
10191
+ position: "sticky",
10192
+ right: rightOffset,
10193
+ zIndex: 1,
10194
+ backgroundColor: "inherit"
10195
+ };
10196
+ rightOffset += columns[i].width;
10197
+ }
10198
+ }
10199
+ return result;
10200
+ }, [columns, moveHandleProps, isOverlay2, task.type, task.id]);
10138
10201
  return /* @__PURE__ */ jsxs(
10139
10202
  "div",
10140
10203
  {
@@ -10156,7 +10219,8 @@ const TaskListTableRowInner = forwardRef(
10156
10219
  className: styles$f.taskListCell,
10157
10220
  style: {
10158
10221
  minWidth: width,
10159
- maxWidth: width
10222
+ maxWidth: width,
10223
+ ...pinnedStyles[index2]
10160
10224
  },
10161
10225
  children: /* @__PURE__ */ jsx(Component, { data: columnData })
10162
10226
  },
@@ -13115,19 +13179,73 @@ const TaskGanttContentInner = (props) => {
13115
13179
  const safeProgressWidth = isNaN(progressWidth) || !isFinite(progressWidth) ? 0 : Math.max(progressWidth, 0);
13116
13180
  const safeInnerX1 = isNaN(innerX1) || !isFinite(innerX1) ? 0 : innerX1;
13117
13181
  const safeInnerX2 = isNaN(innerX2) || !isFinite(innerX2) ? safeInnerX1 + safeWidth : innerX2;
13118
- let comparisonBarElement = null;
13182
+ tasksRes.push(
13183
+ /* @__PURE__ */ jsx(
13184
+ "svg",
13185
+ {
13186
+ id: task.id,
13187
+ className: `${styles$4.TaskItemWrapper} TaskItemWrapper`,
13188
+ x: Math.max(safeContainerX + (additionalLeftSpace || 0), 0),
13189
+ y: safeLevelY,
13190
+ width: Math.max(safeContainerWidth, 0),
13191
+ height: fullRowHeight,
13192
+ children: /* @__PURE__ */ jsx(
13193
+ TaskItem,
13194
+ {
13195
+ movingAction: taskBarMovingAction(task),
13196
+ allowMoveTaskBar,
13197
+ hasChildren: checkHasChildren(task, childTasksMap),
13198
+ progressWidth: safeProgressWidth,
13199
+ progressX: rtl ? safeInnerX2 : safeInnerX1,
13200
+ onSelectTaskOnMouseDown: selectTaskOnMouseDown,
13201
+ task,
13202
+ taskYOffset,
13203
+ width: safeWidth,
13204
+ x1: safeInnerX1,
13205
+ x2: safeInnerX2,
13206
+ distances,
13207
+ taskHeight,
13208
+ taskHalfHeight,
13209
+ isProgressChangeable: (t) => isProgressChangeable(t) && !waitCommitTasks,
13210
+ isDateChangeable: (t) => isDateChangeable(t) && !waitCommitTasks,
13211
+ isRelationChangeable: (t) => isRelationChangeable(t) && !waitCommitTasks,
13212
+ authorizedRelations,
13213
+ ganttRelationEvent,
13214
+ canDelete: !task.isDisabled && !waitCommitTasks,
13215
+ onDoubleClick,
13216
+ onClick,
13217
+ onEventStart: onTaskBarDragStart,
13218
+ onTooltipTask,
13219
+ onRelationStart: onTaskBarRelationStart,
13220
+ isSelected: Boolean(selectedIdsMirror[taskId]),
13221
+ isCritical,
13222
+ rtl,
13223
+ onDeleteTask,
13224
+ renderCustomLabel,
13225
+ viewMode,
13226
+ showProgress,
13227
+ progressColor
13228
+ }
13229
+ )
13230
+ },
13231
+ key2
13232
+ )
13233
+ );
13119
13234
  if (task.comparisonDates && comparisonDates) {
13120
13235
  const safeComparisonX = isNaN(comparisonDates.x) || !isFinite(comparisonDates.x) ? 0 : comparisonDates.x;
13121
- const safeComparisonWidth = isNaN(comparisonDates.width) || !isFinite(comparisonDates.width) ? 10 : Math.max(comparisonDates.width, 3);
13122
- const safeComparisonHeight = isNaN(comparisonDates.height) || !isFinite(comparisonDates.height) ? 6 : Math.max(comparisonDates.height, 3);
13123
- const comparisonRight = safeComparisonX + safeComparisonWidth;
13124
- const isVisible = comparisonRight >= 0 && safeComparisonX <= 1e4;
13125
- if (isVisible) {
13126
- const relativeComparisonX = safeComparisonX - safeContainerX;
13127
- comparisonBarElement = /* @__PURE__ */ jsx(
13128
- "g",
13236
+ const safeComparisonY = isNaN(comparisonDates.y) || !isFinite(comparisonDates.y) ? safeLevelY : comparisonDates.y;
13237
+ const safeComparisonWidth = isNaN(comparisonDates.width) || !isFinite(comparisonDates.width) ? 0 : Math.max(comparisonDates.width, 0);
13238
+ const safeComparisonHeight = isNaN(comparisonDates.height) || !isFinite(comparisonDates.height) ? 0 : Math.max(comparisonDates.height, 0);
13239
+ tasksRes.push(
13240
+ /* @__PURE__ */ jsx(
13241
+ "svg",
13129
13242
  {
13130
- transform: `translate(${relativeComparisonX}, ${taskHeight + 2})`,
13243
+ id: task.id + "_comparison",
13244
+ className: "TaskItemWrapperComparison",
13245
+ x: Math.max(safeComparisonX + (additionalLeftSpace || 0), 0),
13246
+ y: safeComparisonY,
13247
+ width: safeComparisonWidth,
13248
+ height: safeComparisonHeight * 2,
13131
13249
  children: /* @__PURE__ */ jsx(
13132
13250
  BarComparison,
13133
13251
  {
@@ -13139,70 +13257,16 @@ const TaskGanttContentInner = (props) => {
13139
13257
  height: safeComparisonHeight,
13140
13258
  width: safeComparisonWidth,
13141
13259
  borderHeight: distances.barComparisonTaskBorderHeight,
13142
- yOffset: 0,
13260
+ yOffset: distances.barComparisonTaskYOffset,
13143
13261
  task,
13144
13262
  onTooltipTask
13145
13263
  }
13146
13264
  )
13147
- }
13148
- );
13149
- }
13265
+ },
13266
+ key2 + "_comparison"
13267
+ )
13268
+ );
13150
13269
  }
13151
- tasksRes.push(
13152
- /* @__PURE__ */ jsxs(
13153
- "svg",
13154
- {
13155
- id: task.id,
13156
- className: `${styles$4.TaskItemWrapper} TaskItemWrapper`,
13157
- x: Math.max(safeContainerX + (additionalLeftSpace || 0), 0),
13158
- y: safeLevelY,
13159
- width: Math.max(safeContainerWidth, 0),
13160
- height: fullRowHeight,
13161
- children: [
13162
- /* @__PURE__ */ jsx(
13163
- TaskItem,
13164
- {
13165
- movingAction: taskBarMovingAction(task),
13166
- allowMoveTaskBar,
13167
- hasChildren: checkHasChildren(task, childTasksMap),
13168
- progressWidth: safeProgressWidth,
13169
- progressX: rtl ? safeInnerX2 : safeInnerX1,
13170
- onSelectTaskOnMouseDown: selectTaskOnMouseDown,
13171
- task,
13172
- taskYOffset,
13173
- width: safeWidth,
13174
- x1: safeInnerX1,
13175
- x2: safeInnerX2,
13176
- distances,
13177
- taskHeight,
13178
- taskHalfHeight,
13179
- isProgressChangeable: (t) => isProgressChangeable(t) && !waitCommitTasks,
13180
- isDateChangeable: (t) => isDateChangeable(t) && !waitCommitTasks,
13181
- isRelationChangeable: (t) => isRelationChangeable(t) && !waitCommitTasks,
13182
- authorizedRelations,
13183
- ganttRelationEvent,
13184
- canDelete: !task.isDisabled && !waitCommitTasks,
13185
- onDoubleClick,
13186
- onClick,
13187
- onEventStart: onTaskBarDragStart,
13188
- onTooltipTask,
13189
- onRelationStart: onTaskBarRelationStart,
13190
- isSelected: Boolean(selectedIdsMirror[taskId]),
13191
- isCritical,
13192
- rtl,
13193
- onDeleteTask,
13194
- renderCustomLabel,
13195
- viewMode,
13196
- showProgress,
13197
- progressColor
13198
- }
13199
- ),
13200
- comparisonBarElement
13201
- ]
13202
- },
13203
- key2
13204
- )
13205
- );
13206
13270
  const addedDependenciesAtLevel = addedDependencies[comparisonLevel] || {};
13207
13271
  if (!addedDependencies[comparisonLevel]) {
13208
13272
  addedDependencies[comparisonLevel] = addedDependenciesAtLevel;
@@ -13860,12 +13924,10 @@ const countTaskCoordinates = (task, taskToRowIndexMap, startDate, viewMode, rtl,
13860
13924
  );
13861
13925
  cx1 = isNaN(cx1) || !isFinite(cx1) ? x1 : cx1;
13862
13926
  cx2 = isNaN(cx2) || !isFinite(cx2) ? x2 : cx2;
13863
- const comparisonWidth = Math.max(Math.abs(cx2 - cx1), 3);
13864
- const comparisonX = Math.min(cx1, cx2);
13865
13927
  comparisonDates = {
13866
- x: comparisonX,
13928
+ x: cx1,
13867
13929
  y: y + taskHeight,
13868
- width: comparisonWidth,
13930
+ width: Math.max(cx2 - cx1, 0),
13869
13931
  height: barComparisonTaskHeight
13870
13932
  };
13871
13933
  }
@@ -19059,7 +19121,8 @@ const Gantt = (props) => {
19059
19121
  todayLabel = "Today",
19060
19122
  dataDateLabel = "Data Date",
19061
19123
  showProgress = true,
19062
- progressColor
19124
+ progressColor,
19125
+ scrollToTaskId
19063
19126
  } = props;
19064
19127
  const ganttSVGRef = useRef(null);
19065
19128
  const wrapperRef = useRef(null);
@@ -19367,6 +19430,44 @@ const Gantt = (props) => {
19367
19430
  },
19368
19431
  [mapTaskToCoordinates, setScrollXProgrammatically]
19369
19432
  );
19433
+ const prevScrollToTaskIdRef = useRef(void 0);
19434
+ useEffect(() => {
19435
+ if (!scrollToTaskId || scrollToTaskId === prevScrollToTaskIdRef.current) {
19436
+ return;
19437
+ }
19438
+ prevScrollToTaskIdRef.current = scrollToTaskId;
19439
+ for (const [comparisonLevel, levelMap] of tasksMap) {
19440
+ const task = levelMap.get(scrollToTaskId);
19441
+ if (!task || task.type === "empty") {
19442
+ continue;
19443
+ }
19444
+ const { x1 } = getTaskCoordinates(task, mapTaskToCoordinates);
19445
+ setScrollXProgrammatically(Math.max(0, x1 - 100));
19446
+ const rowIndexMap = taskToRowIndexMap.get(comparisonLevel);
19447
+ if (rowIndexMap) {
19448
+ const rowIndex = rowIndexMap.get(scrollToTaskId);
19449
+ if (typeof rowIndex === "number") {
19450
+ const targetScrollY = rowIndex * fullRowHeight - ganttHeight / 2 + fullRowHeight / 2;
19451
+ setScrollYProgrammatically(
19452
+ Math.max(0, Math.min(targetScrollY, ganttFullHeight - ganttHeight))
19453
+ );
19454
+ }
19455
+ }
19456
+ selectTask(scrollToTaskId);
19457
+ break;
19458
+ }
19459
+ }, [
19460
+ scrollToTaskId,
19461
+ tasksMap,
19462
+ mapTaskToCoordinates,
19463
+ taskToRowIndexMap,
19464
+ fullRowHeight,
19465
+ ganttHeight,
19466
+ ganttFullHeight,
19467
+ setScrollXProgrammatically,
19468
+ setScrollYProgrammatically,
19469
+ selectTask
19470
+ ]);
19370
19471
  const { contextMenu, handleCloseContextMenu, handleOpenContextMenu } = useContextMenu(wrapperRef, scrollToTask);
19371
19472
  const [ganttContextMenu, setGanttContextMenu] = useState({
19372
19473
  task: null,
@@ -4705,9 +4705,9 @@
4705
4705
  const scrollValue = property === "scrollLeft" ? el.scrollLeft : el.scrollTop;
4706
4706
  const maxScrollValue = property === "scrollLeft" ? el.scrollWidth : el.scrollHeight;
4707
4707
  const fullValue = property === "scrollLeft" ? el.clientWidth : el.clientHeight;
4708
- const firstIndex = Math.max(0, Math.floor(scrollValue / cellSize));
4709
4708
  const visibleCount = Math.max(1, Math.ceil(fullValue / cellSize));
4710
4709
  const overscan = Math.min(100, Math.max(10, Math.ceil(visibleCount * 0.5)));
4710
+ const firstIndex = Math.max(0, Math.floor(scrollValue / cellSize) - overscan);
4711
4711
  const lastIndex = Math.floor((scrollValue + fullValue) / cellSize) + overscan;
4712
4712
  const isStartOfScroll = scrollValue < DELTA;
4713
4713
  const isEndOfScroll = scrollValue + fullValue > maxScrollValue - DELTA;
@@ -5553,6 +5553,37 @@
5553
5553
  canMoveTasks,
5554
5554
  onColumnResizeStart
5555
5555
  }) => {
5556
+ const pinnedStyles = React.useMemo(() => {
5557
+ const result = {};
5558
+ let leftOffset = 0;
5559
+ if (canMoveTasks) {
5560
+ leftOffset = 24;
5561
+ }
5562
+ for (let i = 0; i < columns.length; i++) {
5563
+ if (columns[i].pinned === "left") {
5564
+ result[i] = {
5565
+ position: "sticky",
5566
+ left: leftOffset,
5567
+ zIndex: 2,
5568
+ backgroundColor: "var(--gantt-table-header-background-color, #fff)"
5569
+ };
5570
+ leftOffset += columns[i].width;
5571
+ }
5572
+ }
5573
+ let rightOffset = 0;
5574
+ for (let i = columns.length - 1; i >= 0; i--) {
5575
+ if (columns[i].pinned === "right") {
5576
+ result[i] = {
5577
+ position: "sticky",
5578
+ right: rightOffset,
5579
+ zIndex: 2,
5580
+ backgroundColor: "var(--gantt-table-header-background-color, #fff)"
5581
+ };
5582
+ rightOffset += columns[i].width;
5583
+ }
5584
+ }
5585
+ return result;
5586
+ }, [columns, canMoveTasks]);
5556
5587
  return /* @__PURE__ */ jsxRuntime.jsx(
5557
5588
  "div",
5558
5589
  {
@@ -5589,7 +5620,8 @@
5589
5620
  className: styles$h.ganttTable_HeaderItem,
5590
5621
  style: {
5591
5622
  minWidth: width,
5592
- maxWidth: width
5623
+ maxWidth: width,
5624
+ ...pinnedStyles[index2]
5593
5625
  },
5594
5626
  children: [
5595
5627
  title,
@@ -10152,6 +10184,37 @@
10152
10184
  }
10153
10185
  return classNames.join(" ");
10154
10186
  }, [isCut2, moveOverPosition, isOverlay2, isDragging]);
10187
+ const pinnedStyles = React.useMemo(() => {
10188
+ const result = {};
10189
+ let leftOffset = 0;
10190
+ if (moveHandleProps || !isOverlay2 && task.type !== "project" && task.id !== "no-project-asigned") {
10191
+ leftOffset = 24;
10192
+ }
10193
+ for (let i = 0; i < columns.length; i++) {
10194
+ if (columns[i].pinned === "left") {
10195
+ result[i] = {
10196
+ position: "sticky",
10197
+ left: leftOffset,
10198
+ zIndex: 1,
10199
+ backgroundColor: "inherit"
10200
+ };
10201
+ leftOffset += columns[i].width;
10202
+ }
10203
+ }
10204
+ let rightOffset = 0;
10205
+ for (let i = columns.length - 1; i >= 0; i--) {
10206
+ if (columns[i].pinned === "right") {
10207
+ result[i] = {
10208
+ position: "sticky",
10209
+ right: rightOffset,
10210
+ zIndex: 1,
10211
+ backgroundColor: "inherit"
10212
+ };
10213
+ rightOffset += columns[i].width;
10214
+ }
10215
+ }
10216
+ return result;
10217
+ }, [columns, moveHandleProps, isOverlay2, task.type, task.id]);
10155
10218
  return /* @__PURE__ */ jsxRuntime.jsxs(
10156
10219
  "div",
10157
10220
  {
@@ -10173,7 +10236,8 @@
10173
10236
  className: styles$f.taskListCell,
10174
10237
  style: {
10175
10238
  minWidth: width,
10176
- maxWidth: width
10239
+ maxWidth: width,
10240
+ ...pinnedStyles[index2]
10177
10241
  },
10178
10242
  children: /* @__PURE__ */ jsxRuntime.jsx(Component, { data: columnData })
10179
10243
  },
@@ -13132,19 +13196,73 @@
13132
13196
  const safeProgressWidth = isNaN(progressWidth) || !isFinite(progressWidth) ? 0 : Math.max(progressWidth, 0);
13133
13197
  const safeInnerX1 = isNaN(innerX1) || !isFinite(innerX1) ? 0 : innerX1;
13134
13198
  const safeInnerX2 = isNaN(innerX2) || !isFinite(innerX2) ? safeInnerX1 + safeWidth : innerX2;
13135
- let comparisonBarElement = null;
13199
+ tasksRes.push(
13200
+ /* @__PURE__ */ jsxRuntime.jsx(
13201
+ "svg",
13202
+ {
13203
+ id: task.id,
13204
+ className: `${styles$4.TaskItemWrapper} TaskItemWrapper`,
13205
+ x: Math.max(safeContainerX + (additionalLeftSpace || 0), 0),
13206
+ y: safeLevelY,
13207
+ width: Math.max(safeContainerWidth, 0),
13208
+ height: fullRowHeight,
13209
+ children: /* @__PURE__ */ jsxRuntime.jsx(
13210
+ TaskItem,
13211
+ {
13212
+ movingAction: taskBarMovingAction(task),
13213
+ allowMoveTaskBar,
13214
+ hasChildren: checkHasChildren(task, childTasksMap),
13215
+ progressWidth: safeProgressWidth,
13216
+ progressX: rtl ? safeInnerX2 : safeInnerX1,
13217
+ onSelectTaskOnMouseDown: selectTaskOnMouseDown,
13218
+ task,
13219
+ taskYOffset,
13220
+ width: safeWidth,
13221
+ x1: safeInnerX1,
13222
+ x2: safeInnerX2,
13223
+ distances,
13224
+ taskHeight,
13225
+ taskHalfHeight,
13226
+ isProgressChangeable: (t) => isProgressChangeable(t) && !waitCommitTasks,
13227
+ isDateChangeable: (t) => isDateChangeable(t) && !waitCommitTasks,
13228
+ isRelationChangeable: (t) => isRelationChangeable(t) && !waitCommitTasks,
13229
+ authorizedRelations,
13230
+ ganttRelationEvent,
13231
+ canDelete: !task.isDisabled && !waitCommitTasks,
13232
+ onDoubleClick,
13233
+ onClick,
13234
+ onEventStart: onTaskBarDragStart,
13235
+ onTooltipTask,
13236
+ onRelationStart: onTaskBarRelationStart,
13237
+ isSelected: Boolean(selectedIdsMirror[taskId]),
13238
+ isCritical,
13239
+ rtl,
13240
+ onDeleteTask,
13241
+ renderCustomLabel,
13242
+ viewMode,
13243
+ showProgress,
13244
+ progressColor
13245
+ }
13246
+ )
13247
+ },
13248
+ key2
13249
+ )
13250
+ );
13136
13251
  if (task.comparisonDates && comparisonDates) {
13137
13252
  const safeComparisonX = isNaN(comparisonDates.x) || !isFinite(comparisonDates.x) ? 0 : comparisonDates.x;
13138
- const safeComparisonWidth = isNaN(comparisonDates.width) || !isFinite(comparisonDates.width) ? 10 : Math.max(comparisonDates.width, 3);
13139
- const safeComparisonHeight = isNaN(comparisonDates.height) || !isFinite(comparisonDates.height) ? 6 : Math.max(comparisonDates.height, 3);
13140
- const comparisonRight = safeComparisonX + safeComparisonWidth;
13141
- const isVisible = comparisonRight >= 0 && safeComparisonX <= 1e4;
13142
- if (isVisible) {
13143
- const relativeComparisonX = safeComparisonX - safeContainerX;
13144
- comparisonBarElement = /* @__PURE__ */ jsxRuntime.jsx(
13145
- "g",
13253
+ const safeComparisonY = isNaN(comparisonDates.y) || !isFinite(comparisonDates.y) ? safeLevelY : comparisonDates.y;
13254
+ const safeComparisonWidth = isNaN(comparisonDates.width) || !isFinite(comparisonDates.width) ? 0 : Math.max(comparisonDates.width, 0);
13255
+ const safeComparisonHeight = isNaN(comparisonDates.height) || !isFinite(comparisonDates.height) ? 0 : Math.max(comparisonDates.height, 0);
13256
+ tasksRes.push(
13257
+ /* @__PURE__ */ jsxRuntime.jsx(
13258
+ "svg",
13146
13259
  {
13147
- transform: `translate(${relativeComparisonX}, ${taskHeight + 2})`,
13260
+ id: task.id + "_comparison",
13261
+ className: "TaskItemWrapperComparison",
13262
+ x: Math.max(safeComparisonX + (additionalLeftSpace || 0), 0),
13263
+ y: safeComparisonY,
13264
+ width: safeComparisonWidth,
13265
+ height: safeComparisonHeight * 2,
13148
13266
  children: /* @__PURE__ */ jsxRuntime.jsx(
13149
13267
  BarComparison,
13150
13268
  {
@@ -13156,70 +13274,16 @@
13156
13274
  height: safeComparisonHeight,
13157
13275
  width: safeComparisonWidth,
13158
13276
  borderHeight: distances.barComparisonTaskBorderHeight,
13159
- yOffset: 0,
13277
+ yOffset: distances.barComparisonTaskYOffset,
13160
13278
  task,
13161
13279
  onTooltipTask
13162
13280
  }
13163
13281
  )
13164
- }
13165
- );
13166
- }
13282
+ },
13283
+ key2 + "_comparison"
13284
+ )
13285
+ );
13167
13286
  }
13168
- tasksRes.push(
13169
- /* @__PURE__ */ jsxRuntime.jsxs(
13170
- "svg",
13171
- {
13172
- id: task.id,
13173
- className: `${styles$4.TaskItemWrapper} TaskItemWrapper`,
13174
- x: Math.max(safeContainerX + (additionalLeftSpace || 0), 0),
13175
- y: safeLevelY,
13176
- width: Math.max(safeContainerWidth, 0),
13177
- height: fullRowHeight,
13178
- children: [
13179
- /* @__PURE__ */ jsxRuntime.jsx(
13180
- TaskItem,
13181
- {
13182
- movingAction: taskBarMovingAction(task),
13183
- allowMoveTaskBar,
13184
- hasChildren: checkHasChildren(task, childTasksMap),
13185
- progressWidth: safeProgressWidth,
13186
- progressX: rtl ? safeInnerX2 : safeInnerX1,
13187
- onSelectTaskOnMouseDown: selectTaskOnMouseDown,
13188
- task,
13189
- taskYOffset,
13190
- width: safeWidth,
13191
- x1: safeInnerX1,
13192
- x2: safeInnerX2,
13193
- distances,
13194
- taskHeight,
13195
- taskHalfHeight,
13196
- isProgressChangeable: (t) => isProgressChangeable(t) && !waitCommitTasks,
13197
- isDateChangeable: (t) => isDateChangeable(t) && !waitCommitTasks,
13198
- isRelationChangeable: (t) => isRelationChangeable(t) && !waitCommitTasks,
13199
- authorizedRelations,
13200
- ganttRelationEvent,
13201
- canDelete: !task.isDisabled && !waitCommitTasks,
13202
- onDoubleClick,
13203
- onClick,
13204
- onEventStart: onTaskBarDragStart,
13205
- onTooltipTask,
13206
- onRelationStart: onTaskBarRelationStart,
13207
- isSelected: Boolean(selectedIdsMirror[taskId]),
13208
- isCritical,
13209
- rtl,
13210
- onDeleteTask,
13211
- renderCustomLabel,
13212
- viewMode,
13213
- showProgress,
13214
- progressColor
13215
- }
13216
- ),
13217
- comparisonBarElement
13218
- ]
13219
- },
13220
- key2
13221
- )
13222
- );
13223
13287
  const addedDependenciesAtLevel = addedDependencies[comparisonLevel] || {};
13224
13288
  if (!addedDependencies[comparisonLevel]) {
13225
13289
  addedDependencies[comparisonLevel] = addedDependenciesAtLevel;
@@ -13877,12 +13941,10 @@
13877
13941
  );
13878
13942
  cx1 = isNaN(cx1) || !isFinite(cx1) ? x1 : cx1;
13879
13943
  cx2 = isNaN(cx2) || !isFinite(cx2) ? x2 : cx2;
13880
- const comparisonWidth = Math.max(Math.abs(cx2 - cx1), 3);
13881
- const comparisonX = Math.min(cx1, cx2);
13882
13944
  comparisonDates = {
13883
- x: comparisonX,
13945
+ x: cx1,
13884
13946
  y: y + taskHeight,
13885
- width: comparisonWidth,
13947
+ width: Math.max(cx2 - cx1, 0),
13886
13948
  height: barComparisonTaskHeight
13887
13949
  };
13888
13950
  }
@@ -19076,7 +19138,8 @@
19076
19138
  todayLabel = "Today",
19077
19139
  dataDateLabel = "Data Date",
19078
19140
  showProgress = true,
19079
- progressColor
19141
+ progressColor,
19142
+ scrollToTaskId
19080
19143
  } = props;
19081
19144
  const ganttSVGRef = React.useRef(null);
19082
19145
  const wrapperRef = React.useRef(null);
@@ -19384,6 +19447,44 @@
19384
19447
  },
19385
19448
  [mapTaskToCoordinates, setScrollXProgrammatically]
19386
19449
  );
19450
+ const prevScrollToTaskIdRef = React.useRef(void 0);
19451
+ React.useEffect(() => {
19452
+ if (!scrollToTaskId || scrollToTaskId === prevScrollToTaskIdRef.current) {
19453
+ return;
19454
+ }
19455
+ prevScrollToTaskIdRef.current = scrollToTaskId;
19456
+ for (const [comparisonLevel, levelMap] of tasksMap) {
19457
+ const task = levelMap.get(scrollToTaskId);
19458
+ if (!task || task.type === "empty") {
19459
+ continue;
19460
+ }
19461
+ const { x1 } = getTaskCoordinates(task, mapTaskToCoordinates);
19462
+ setScrollXProgrammatically(Math.max(0, x1 - 100));
19463
+ const rowIndexMap = taskToRowIndexMap.get(comparisonLevel);
19464
+ if (rowIndexMap) {
19465
+ const rowIndex = rowIndexMap.get(scrollToTaskId);
19466
+ if (typeof rowIndex === "number") {
19467
+ const targetScrollY = rowIndex * fullRowHeight - ganttHeight / 2 + fullRowHeight / 2;
19468
+ setScrollYProgrammatically(
19469
+ Math.max(0, Math.min(targetScrollY, ganttFullHeight - ganttHeight))
19470
+ );
19471
+ }
19472
+ }
19473
+ selectTask(scrollToTaskId);
19474
+ break;
19475
+ }
19476
+ }, [
19477
+ scrollToTaskId,
19478
+ tasksMap,
19479
+ mapTaskToCoordinates,
19480
+ taskToRowIndexMap,
19481
+ fullRowHeight,
19482
+ ganttHeight,
19483
+ ganttFullHeight,
19484
+ setScrollXProgrammatically,
19485
+ setScrollYProgrammatically,
19486
+ selectTask
19487
+ ]);
19387
19488
  const { contextMenu, handleCloseContextMenu, handleOpenContextMenu } = useContextMenu(wrapperRef, scrollToTask);
19388
19489
  const [ganttContextMenu, setGanttContextMenu] = React.useState({
19389
19490
  task: null,
@@ -368,6 +368,11 @@ export interface GanttProps {
368
368
  * Custom color for progress bars. If not provided, theme progress colors are used.
369
369
  */
370
370
  progressColor?: string;
371
+ /**
372
+ * When set (or changed), the gantt will scroll to reveal and select this task.
373
+ * Set to a task id to scroll both horizontally and vertically to that task.
374
+ */
375
+ scrollToTaskId?: TaskId;
371
376
  }
372
377
  export interface GanttTaskBarActions {
373
378
  allowMoveTaskBar?: (action: TaskBarMoveAction, task: RenderTask) => boolean;
@@ -397,6 +402,11 @@ export type Column = {
397
402
  width: number;
398
403
  title?: ReactNode;
399
404
  canResize?: boolean;
405
+ /**
406
+ * Pin column to the left or right side of the table.
407
+ * Pinned columns stay visible while scrolling horizontally.
408
+ */
409
+ pinned?: "left" | "right";
400
410
  };
401
411
  export type OnResizeColumn = (nextColumns: readonly Column[], columnIndex: number, deltaWidth: number) => void;
402
412
  export type ChangeAction = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gantt-task-react-v",
3
- "version": "1.3.6",
3
+ "version": "1.4.0",
4
4
  "description": "Interactive Gantt Chart for React with TypeScript.",
5
5
  "author": "aguilanbon",
6
6
  "homepage": "https://github.com/aguilanbon/gantt-task-react-v",