gantt-lib 0.142.2 → 0.143.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
@@ -4773,6 +4773,7 @@ function createTaskPreviewPositionStore() {
4773
4773
  }
4774
4774
  function createActivityPreviewStore() {
4775
4775
  let overridesByTask = /* @__PURE__ */ new Map();
4776
+ let draggedRange;
4776
4777
  const listenersByTaskId = /* @__PURE__ */ new Map();
4777
4778
  function notify(taskIds) {
4778
4779
  for (const taskId of taskIds) {
@@ -4793,6 +4794,16 @@ function createActivityPreviewStore() {
4793
4794
  }
4794
4795
  };
4795
4796
  },
4797
+ getActivityRange(taskId, activityId) {
4798
+ return draggedRange?.taskId === taskId && draggedRange.activityId === activityId ? draggedRange : void 0;
4799
+ },
4800
+ setActivityRange(taskId, activityId, range) {
4801
+ if (draggedRange?.taskId === taskId && draggedRange.activityId === activityId && draggedRange.startDate.getTime() === range.startDate.getTime() && draggedRange.endDate.getTime() === range.endDate.getTime()) return;
4802
+ const changed = /* @__PURE__ */ new Set([taskId]);
4803
+ if (draggedRange) changed.add(draggedRange.taskId);
4804
+ draggedRange = { taskId, activityId, ...range };
4805
+ notify(changed);
4806
+ },
4796
4807
  getTaskOverrides(taskId) {
4797
4808
  return overridesByTask.get(taskId);
4798
4809
  },
@@ -4813,10 +4824,10 @@ function createActivityPreviewStore() {
4813
4824
  notify(changedTaskIds);
4814
4825
  },
4815
4826
  clear() {
4816
- if (overridesByTask.size === 0) {
4817
- return;
4818
- }
4827
+ if (overridesByTask.size === 0 && !draggedRange) return;
4819
4828
  const changedTaskIds = new Set(overridesByTask.keys());
4829
+ if (draggedRange) changedTaskIds.add(draggedRange.taskId);
4830
+ draggedRange = void 0;
4820
4831
  overridesByTask = /* @__PURE__ */ new Map();
4821
4832
  notify(changedTaskIds);
4822
4833
  }
@@ -5162,13 +5173,6 @@ var TaskRow = import_react3.default.memo(
5162
5173
  const activityDragStart = (0, import_react3.useRef)(null);
5163
5174
  const activityConstraintRef = (0, import_react3.useRef)(null);
5164
5175
  const [activityBlockedBy, setActivityBlockedBy] = (0, import_react3.useState)(null);
5165
- const visibleActivitySegments = (0, import_react3.useMemo)(() => {
5166
- if (!activityLayout) return [];
5167
- const window2 = horizontalWindow;
5168
- if (!window2) return activityLayout.segments;
5169
- const draggedId = activityDrag?.id;
5170
- return activityLayout.segments.filter((segment) => segment.id === draggedId || segment.left <= window2.endPx && segment.left + segment.width >= window2.startPx);
5171
- }, [activityLayout, horizontalWindow, activityDrag?.id]);
5172
5176
  const subscribeActivityOverrides = (0, import_react3.useCallback)(
5173
5177
  (listener) => activityPreviewStore?.subscribeTask(task.id, listener) ?? (() => {
5174
5178
  }),
@@ -5183,6 +5187,15 @@ var TaskRow = import_react3.default.memo(
5183
5187
  getActivityOverridesSnapshot,
5184
5188
  () => void 0
5185
5189
  );
5190
+ const visibleActivitySegments = (0, import_react3.useMemo)(() => {
5191
+ if (!activityLayout) return [];
5192
+ if (!horizontalWindow) return activityLayout.segments;
5193
+ return activityLayout.segments.filter((segment) => {
5194
+ if (segment.id === activityDrag?.id) return true;
5195
+ const left2 = segment.left + (activityOverrides?.get(segment.id) ?? 0) * dayWidth;
5196
+ return left2 <= horizontalWindow.endPx && left2 + segment.width >= horizontalWindow.startPx;
5197
+ });
5198
+ }, [activityLayout, horizontalWindow, activityDrag?.id, activityOverrides, dayWidth]);
5186
5199
  const subscribeActivityBlock = (0, import_react3.useCallback)(
5187
5200
  (listener) => activityBlockStore?.subscribeTask(task.id, listener) ?? (() => {
5188
5201
  }),
@@ -5308,6 +5321,11 @@ var TaskRow = import_react3.default.memo(
5308
5321
  });
5309
5322
  }
5310
5323
  publishChainPreview(activityDrag.id, activityDrag.mode, clamped);
5324
+ const sourceActivity = task.activities?.find((activity) => activity.id === activityDrag.id);
5325
+ if (sourceActivity) activityPreviewStore?.setActivityRange?.(task.id, activityDrag.id, {
5326
+ startDate: shiftActivityDay(sourceActivity.startDate, activityDrag.mode === "resize-right" ? 0 : clamped),
5327
+ endDate: shiftActivityDay(sourceActivity.endDate, activityDrag.mode === "resize-left" ? 0 : clamped)
5328
+ });
5311
5329
  };
5312
5330
  const onUp = () => {
5313
5331
  activityPreviewStore?.clear();
@@ -5413,6 +5431,53 @@ var TaskRow = import_react3.default.memo(
5413
5431
  }
5414
5432
  return { left: segment.left, width: segment.width + activityDrag.dayDelta * dayWidth };
5415
5433
  };
5434
+ const activityLinks = (0, import_react3.useMemo)(() => {
5435
+ const gaps = [];
5436
+ const elbows = [];
5437
+ if (!showActivityLinks || !renderActivities || !activityLayout) return { gaps, elbows };
5438
+ const activities = task.activities ?? [];
5439
+ const segmentById = new Map(activityLayout.segments.map((segment) => [segment.id, segment]));
5440
+ const visibleIds = new Set(visibleActivitySegments.map((segment) => segment.id));
5441
+ const laneTopY = (lane) => activityTopOffset + lane * ACTIVITY_LANE_STEP + (ACTIVITY_LANE_STEP - ACTIVITY_LANE_BAR_HEIGHT) / 2;
5442
+ const liveRect = (segment) => {
5443
+ const geometry = liveActivityGeometry(segment);
5444
+ const chainDelta = activityOverrides?.get(segment.id) ?? 0;
5445
+ const left2 = geometry.left + (chainDelta !== 0 ? chainDelta * dayWidth : 0);
5446
+ return { left: left2, right: left2 + geometry.width };
5447
+ };
5448
+ for (let index = 0; index < activities.length - 1; index += 1) {
5449
+ const predecessor = segmentById.get(activities[index].id);
5450
+ const successor = segmentById.get(activities[index + 1].id);
5451
+ if (!predecessor || !successor) continue;
5452
+ if (!visibleIds.has(predecessor.id) || !visibleIds.has(successor.id)) continue;
5453
+ const lag = activities[index + 1].lag ?? 0;
5454
+ const predRect = liveRect(predecessor);
5455
+ const succRect = liveRect(successor);
5456
+ if (predecessor.lane === successor.lane && succRect.left >= predRect.right) {
5457
+ const width2 = succRect.left - predRect.right;
5458
+ if (width2 >= 1) {
5459
+ gaps.push({ key: `${predecessor.id}->${successor.id}`, left: predRect.right, width: width2, lane: predecessor.lane, lag });
5460
+ }
5461
+ continue;
5462
+ }
5463
+ const predecessorLaneTop = laneTopY(predecessor.lane);
5464
+ const successorLaneTop = laneTopY(successor.lane);
5465
+ const sameLane = predecessor.lane === successor.lane;
5466
+ const reverseOrder = !sameLane && successorLaneTop < predecessorLaneTop;
5467
+ const fromY = reverseOrder ? predecessorLaneTop + 6 : predecessorLaneTop + ACTIVITY_LANE_BAR_HEIGHT - 6;
5468
+ const toY = reverseOrder ? successorLaneTop + ACTIVITY_LANE_BAR_HEIGHT - 6 : successorLaneTop + (sameLane ? ACTIVITY_LANE_STEP : 0) + 6;
5469
+ const fromX = predRect.right;
5470
+ const toX = succRect.left;
5471
+ elbows.push({
5472
+ key: `${predecessor.id}->${successor.id}`,
5473
+ d: calculateDependencyPath({ x: fromX, y: fromY }, { x: toX, y: toY }, false),
5474
+ lagX: toX - 14,
5475
+ lagY: reverseOrder ? fromY - 4 : fromY + 12,
5476
+ lag
5477
+ });
5478
+ }
5479
+ return { gaps, elbows };
5480
+ }, [showActivityLinks, renderActivities, activityLayout, visibleActivitySegments, activityTopOffset, activityOverrides, activityDrag, dayWidth, task.activities]);
5416
5481
  const activityTipData = (0, import_react3.useMemo)(() => {
5417
5482
  if (!activityTipId || !task.activities) return null;
5418
5483
  const activity = task.activities.find((item) => item.id === activityTipId);
@@ -5484,72 +5549,121 @@ var TaskRow = import_react3.default.memo(
5484
5549
  }
5485
5550
  }
5486
5551
  ),
5487
- renderActivities && activityLayout && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-activityLanes", children: visibleActivitySegments.map((segment) => {
5488
- const live = liveActivityGeometry(segment);
5489
- const chainDelta = activityOverrides?.get(segment.id) ?? 0;
5490
- const activityProgress = segment.progress === void 0 ? 0 : Math.min(100, Math.max(0, Math.round(segment.progress)));
5491
- const liveLeft = live.left + (chainDelta !== 0 ? chainDelta * dayWidth : 0);
5492
- const isDraggingActivity = activityDrag?.id === segment.id;
5493
- const isBlockingActivity = blockedActivityIds?.has(segment.id) ?? false;
5494
- const isAtLimit = isDraggingActivity && activityBlockedBy !== null;
5495
- const isActiveActivity = activationVisualEnabled && activeActivityKey === activityOwnerKey(task.id, segment.id);
5496
- const isSameWorkType = activeWork !== null && (segment.id === activeWork.id || activeWork.name !== null && segment.name === activeWork.name);
5497
- const isDimmed = dimOthers && activeWork !== null && !isSameWorkType;
5498
- const isChainHighlighted = highlightChain && activeWork !== null && isSameWorkType;
5499
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5500
- "div",
5501
- {
5502
- "data-activity-id": segment.id,
5503
- className: `gantt-tr-activityBar${isDraggingActivity ? " gantt-tr-activityBar-dragging" : ""}${isBlockingActivity ? " gantt-tr-activityBar-blocking" : ""}${isAtLimit ? " gantt-tr-activityBar-atLimit" : ""}${isActiveActivity ? " gantt-tr-activityBar-active" : ""}${isDimmed ? " gantt-tr-activityBar-dimmed" : ""}${isChainHighlighted ? " gantt-tr-activityBar-chain" : ""}${activityDragEnabled ? "" : " gantt-tr-activityBar-locked"}`,
5504
- style: {
5505
- left: `${liveLeft}px`,
5506
- width: `${live.width}px`,
5507
- top: `${activityTopOffset + segment.lane * ACTIVITY_LANE_STEP + (ACTIVITY_LANE_STEP - ACTIVITY_LANE_BAR_HEIGHT) / 2}px`,
5508
- height: `${ACTIVITY_LANE_BAR_HEIGHT}px`,
5509
- backgroundColor: segment.color || "var(--gantt-task-bar-default-color)"
5510
- },
5511
- onMouseDown: (event) => {
5512
- if (!activityDragEnabled) return;
5513
- const activationKey = activityOwnerKey(task.id, segment.id);
5514
- const isActive = activityActivationStore?.getActiveKey() === activationKey;
5515
- if (activationVisualEnabled && !isActive) {
5516
- activityActivationStore?.setActiveKey(activationKey);
5552
+ renderActivities && activityLayout && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gantt-tr-activityLanes", children: [
5553
+ activityLinks.elbows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "gantt-tr-activityLinksSvg", width: "100%", height: "100%", "aria-hidden": "true", children: [
5554
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("marker", { id: "arrowhead", markerWidth: "8", markerHeight: "6", markerUnits: "userSpaceOnUse", refX: "7", refY: "3", orient: "auto", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: "0 0, 8 3, 0 6", fill: "var(--gantt-dependency-line-color, #666666)" }) }) }),
5555
+ activityLinks.elbows.map((link) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
5556
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { className: "gantt-dependency-path", d: link.d, markerEnd: "url(#arrowhead)" }),
5557
+ showActivityLag && link.lag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5558
+ "text",
5559
+ {
5560
+ className: "gantt-dependency-lag-label",
5561
+ x: link.lagX,
5562
+ y: link.lagY,
5563
+ textAnchor: "middle",
5564
+ fontSize: "10",
5565
+ fill: "var(--gantt-dependency-line-color, #666666)",
5566
+ children: link.lag > 0 ? `+${link.lag}` : `${link.lag}`
5517
5567
  }
5518
- if (activityClickToDrag && !isActive) return;
5519
- const target = event.target;
5520
- const mode = target.closest(".gantt-tr-resizeHandleLeft") ? "resize-left" : target.closest(".gantt-tr-resizeHandleRight") ? "resize-right" : "move";
5521
- handleActivityPointerDown(segment, mode)(event);
5522
- },
5523
- onMouseEnter: (event) => {
5524
- if (activityDrag || activityDragOwner?.get()) return;
5525
- setActivityTipPosition(anchorActivityTip(event.currentTarget.getBoundingClientRect()));
5526
- setActivityTipId(segment.id);
5568
+ )
5569
+ ] }, link.key))
5570
+ ] }),
5571
+ visibleActivitySegments.map((segment) => {
5572
+ const live = liveActivityGeometry(segment);
5573
+ const chainDelta = activityOverrides?.get(segment.id) ?? 0;
5574
+ const activityProgress = segment.progress === void 0 ? 0 : Math.min(100, Math.max(0, Math.round(segment.progress)));
5575
+ const liveLeft = live.left + (chainDelta !== 0 ? chainDelta * dayWidth : 0);
5576
+ const isDraggingActivity = activityDrag?.id === segment.id;
5577
+ const isBlockingActivity = blockedActivityIds?.has(segment.id) ?? false;
5578
+ const isAtLimit = isDraggingActivity && activityBlockedBy !== null;
5579
+ const isActiveActivity = activationVisualEnabled && activeActivityKey === activityOwnerKey(task.id, segment.id);
5580
+ const isSameWorkType = activeWork !== null && (segment.id === activeWork.id || activeWork.name !== null && segment.name === activeWork.name);
5581
+ const isDimmed = dimOthers && activeWork !== null && !isSameWorkType;
5582
+ const isChainHighlighted = highlightChain && activeWork !== null && isSameWorkType;
5583
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5584
+ "div",
5585
+ {
5586
+ "data-activity-id": segment.id,
5587
+ className: `gantt-tr-activityBar${isDraggingActivity ? " gantt-tr-activityBar-dragging" : ""}${isBlockingActivity ? " gantt-tr-activityBar-blocking" : ""}${isAtLimit ? " gantt-tr-activityBar-atLimit" : ""}${isActiveActivity ? " gantt-tr-activityBar-active" : ""}${isDimmed ? " gantt-tr-activityBar-dimmed" : ""}${isChainHighlighted ? " gantt-tr-activityBar-chain" : ""}${activityDragEnabled ? "" : " gantt-tr-activityBar-locked"}`,
5588
+ style: {
5589
+ left: `${liveLeft}px`,
5590
+ width: `${live.width}px`,
5591
+ top: `${activityTopOffset + segment.lane * ACTIVITY_LANE_STEP + (ACTIVITY_LANE_STEP - ACTIVITY_LANE_BAR_HEIGHT) / 2}px`,
5592
+ height: `${ACTIVITY_LANE_BAR_HEIGHT}px`,
5593
+ backgroundColor: segment.color || "var(--gantt-task-bar-default-color)"
5594
+ },
5595
+ onMouseDown: (event) => {
5596
+ if (!activityDragEnabled) return;
5597
+ const activationKey = activityOwnerKey(task.id, segment.id);
5598
+ const isActive = activityActivationStore?.getActiveKey() === activationKey;
5599
+ if (activationVisualEnabled && !isActive) {
5600
+ activityActivationStore?.setActiveKey(activationKey);
5601
+ }
5602
+ if (activityClickToDrag && !isActive) return;
5603
+ const target = event.target;
5604
+ const mode = target.closest(".gantt-tr-resizeHandleLeft") ? "resize-left" : target.closest(".gantt-tr-resizeHandleRight") ? "resize-right" : "move";
5605
+ handleActivityPointerDown(segment, mode)(event);
5606
+ },
5607
+ onMouseEnter: (event) => {
5608
+ if (activityDrag || activityDragOwner?.get()) return;
5609
+ setActivityTipPosition(anchorActivityTip(event.currentTarget.getBoundingClientRect()));
5610
+ setActivityTipId(segment.id);
5611
+ },
5612
+ onMouseLeave: () => {
5613
+ if (!activityDrag) {
5614
+ setActivityTipId((current) => current === segment.id ? null : current);
5615
+ activityTipAnchor.current = null;
5616
+ setActivityTipPosition(null);
5617
+ }
5618
+ },
5619
+ children: [
5620
+ activityProgress > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5621
+ "span",
5622
+ {
5623
+ className: "gantt-tr-activityProgress",
5624
+ style: { width: `${activityProgress}%` }
5625
+ }
5626
+ ),
5627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gantt-tr-activityName", style: { color: activityTextColor(segment.color) }, children: segment.name }),
5628
+ activityDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
5629
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleLeft" }),
5630
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleRight" })
5631
+ ] })
5632
+ ]
5527
5633
  },
5528
- onMouseLeave: () => {
5529
- if (!activityDrag) {
5530
- setActivityTipId((current) => current === segment.id ? null : current);
5531
- activityTipAnchor.current = null;
5532
- setActivityTipPosition(null);
5634
+ segment.id
5635
+ );
5636
+ }),
5637
+ activityLinks.gaps.map((gap) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react3.default.Fragment, { children: [
5638
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5639
+ "span",
5640
+ {
5641
+ className: "gantt-tr-activityLink",
5642
+ "aria-hidden": "true",
5643
+ style: {
5644
+ left: `${gap.left}px`,
5645
+ width: `${gap.width}px`,
5646
+ top: `${activityTopOffset + gap.lane * ACTIVITY_LANE_STEP + ACTIVITY_LANE_STEP / 2}px`
5533
5647
  }
5534
- },
5535
- children: [
5536
- activityProgress > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5537
- "span",
5538
- {
5539
- className: "gantt-tr-activityProgress",
5540
- style: { width: `${activityProgress}%` }
5541
- }
5542
- ),
5543
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gantt-tr-activityName", style: { color: activityTextColor(segment.color) }, children: segment.name }),
5544
- activityDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
5545
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleLeft" }),
5546
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gantt-tr-resizeHandle gantt-tr-resizeHandleRight" })
5547
- ] })
5548
- ]
5549
- },
5550
- segment.id
5551
- );
5552
- }) }),
5648
+ }
5649
+ ),
5650
+ showActivityLag && gap.lag > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5651
+ "span",
5652
+ {
5653
+ className: "gantt-tr-activityLag",
5654
+ title: `\u0417\u0430\u0437\u043E\u0440 ${gap.lag} \u0434`,
5655
+ style: {
5656
+ left: `${gap.left + gap.width - 14}px`,
5657
+ top: `${activityTopOffset + gap.lane * ACTIVITY_LANE_STEP + ACTIVITY_LANE_STEP / 2}px`
5658
+ },
5659
+ children: [
5660
+ "+",
5661
+ gap.lag
5662
+ ]
5663
+ }
5664
+ )
5665
+ ] }, gap.key))
5666
+ ] }),
5553
5667
  !renderActivities && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5554
5668
  "div",
5555
5669
  {
@@ -6122,6 +6236,74 @@ function areBothHiddenInSameParent(predecessorId, successorId, collapsedParentId
6122
6236
  return predVisibleAncestor.id === succVisibleAncestor.id;
6123
6237
  }
6124
6238
  var EMPTY_COLLAPSED_PARENT_IDS = /* @__PURE__ */ new Set();
6239
+ var DependencyLineView = import_react7.default.memo(function DependencyLineView2({
6240
+ line,
6241
+ hovered,
6242
+ selected,
6243
+ showLag,
6244
+ markerPrefix,
6245
+ onLineHover,
6246
+ onLineLeave,
6247
+ onDependencyClick
6248
+ }) {
6249
+ const { id, predecessorId, successorId, linkType, path, hasCycle, lag, toX, fromY, reverseOrder, isVirtual, isCoupling } = line;
6250
+ const interactive = !isCoupling && onDependencyClick !== void 0;
6251
+ let pathClassName = "gantt-dependency-path";
6252
+ if (selected) pathClassName += " gantt-dependency-selected";
6253
+ else if (hasCycle) pathClassName += " gantt-dependency-cycle";
6254
+ else if (line.isCritical) pathClassName += " gantt-dependency-critical";
6255
+ if (isVirtual && !selected) pathClassName += " gantt-dependency-virtual";
6256
+ let markerEnd;
6257
+ if (hovered) markerEnd = `url(#${markerPrefix}-arrowhead-hover)`;
6258
+ else if (selected) markerEnd = `url(#${markerPrefix}-arrowhead-selected)`;
6259
+ else if (hasCycle) markerEnd = `url(#${markerPrefix}-arrowhead-cycle)`;
6260
+ else if (line.isCritical) markerEnd = `url(#${markerPrefix}-arrowhead-critical)`;
6261
+ else markerEnd = `url(#${markerPrefix}-arrowhead)`;
6262
+ const lagColor = selected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : line.isCritical ? "var(--gantt-critical-path-color, #dc2626)" : "var(--gantt-dependency-line-color, #666666)";
6263
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
6264
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6265
+ "g",
6266
+ {
6267
+ className: `gantt-dependency-line${interactive ? "" : " gantt-dependency-line-static"}${hovered && interactive ? " gantt-dependency-line-hovered" : ""}`,
6268
+ onPointerEnter: interactive ? () => onLineHover(id) : void 0,
6269
+ onPointerLeave: interactive ? () => onLineLeave(id) : void 0,
6270
+ onClick: interactive ? (event) => {
6271
+ event.stopPropagation();
6272
+ onDependencyClick?.({
6273
+ predecessorId,
6274
+ successorId,
6275
+ linkType,
6276
+ x: toX,
6277
+ y: reverseOrder ? fromY - 4 : fromY + 12
6278
+ });
6279
+ } : void 0,
6280
+ role: interactive ? "button" : void 0,
6281
+ "aria-label": interactive ? `\u0421\u0432\u044F\u0437\u044C ${linkType}` : void 0,
6282
+ children: isCoupling ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: toX - 4.5, y1: fromY, x2: toX + 4.5, y2: fromY, className: `${pathClassName} gantt-dependency-coupling` }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
6283
+ interactive && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: path, className: "gantt-dependency-hit-area" }),
6284
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: path, className: pathClassName, markerEnd })
6285
+ ] })
6286
+ }
6287
+ ),
6288
+ showLag && lag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6289
+ "text",
6290
+ {
6291
+ className: "gantt-dependency-lag-label",
6292
+ x: lag < 0 ? toX + 14 : toX - 14,
6293
+ y: reverseOrder ? fromY - 4 : fromY + 12,
6294
+ textAnchor: "middle",
6295
+ fontSize: "10",
6296
+ fill: lagColor,
6297
+ children: lag > 0 ? `+${lag}` : `${lag}`
6298
+ }
6299
+ )
6300
+ ] });
6301
+ }, (prev, next) => (
6302
+ // Сравнение по значению, а не по ссылке линии: пересчёт геометрии на каждом
6303
+ // бакете вертикальной прокрутки создаёт новые объекты линий с той же
6304
+ // геометрией — пере-рендерить их нельзя, иначе пан дёргается.
6305
+ prev.line.id === next.line.id && prev.line.predecessorId === next.line.predecessorId && prev.line.successorId === next.line.successorId && prev.line.linkType === next.line.linkType && prev.line.path === next.line.path && prev.line.hasCycle === next.line.hasCycle && prev.line.lag === next.line.lag && prev.line.toX === next.line.toX && prev.line.fromY === next.line.fromY && prev.line.reverseOrder === next.line.reverseOrder && prev.line.isVirtual === next.line.isVirtual && prev.line.isCritical === next.line.isCritical && prev.line.isCoupling === next.line.isCoupling && prev.hovered === next.hovered && prev.selected === next.selected && prev.showLag === next.showLag && prev.markerPrefix === next.markerPrefix && prev.onLineHover === next.onLineHover && prev.onLineLeave === next.onLineLeave && prev.onDependencyClick === next.onDependencyClick
6306
+ ));
6125
6307
  var DependencyLines = import_react7.default.memo(({
6126
6308
  tasks,
6127
6309
  allTasks,
@@ -6137,6 +6319,7 @@ var DependencyLines = import_react7.default.memo(({
6137
6319
  dragOverrides,
6138
6320
  selectedDep,
6139
6321
  criticalTaskIds,
6322
+ cycleTaskIds,
6140
6323
  showLag = true,
6141
6324
  activityEndpoints = false,
6142
6325
  businessDays = true,
@@ -6147,13 +6330,16 @@ var DependencyLines = import_react7.default.memo(({
6147
6330
  const markerPrefix = `gantt-dependency-${(0, import_react7.useId)().replace(/[^a-zA-Z0-9_-]/g, "")}`;
6148
6331
  const markerId = (kind) => `${markerPrefix}-${kind}`;
6149
6332
  const [hoveredLineId, setHoveredLineId] = (0, import_react7.useState)(null);
6333
+ const handleLineHover = (0, import_react7.useCallback)((id) => setHoveredLineId(id), []);
6334
+ const handleLineLeave = (0, import_react7.useCallback)((id) => setHoveredLineId((current) => current === id ? null : current), []);
6150
6335
  const tasksForPositions = allTasks ?? tasks;
6336
+ const geometryTasks = activityEndpoints ? tasksForPositions : tasks;
6151
6337
  const { taskPositions, taskIndices, hiddenTaskIds } = (0, import_react7.useMemo)(() => {
6152
6338
  const positions = /* @__PURE__ */ new Map();
6153
6339
  const indices = /* @__PURE__ */ new Map();
6154
6340
  const hidden = /* @__PURE__ */ new Set();
6155
6341
  const taskMap = new Map(tasksForPositions.map((t) => [t.id, t]));
6156
- tasks.forEach((task, index) => {
6342
+ geometryTasks.forEach((task, index) => {
6157
6343
  const override = dragOverrides?.get(task.id);
6158
6344
  const computed = resolveTaskHorizontalGeometry(task, monthStart, dayWidth, override);
6159
6345
  const rowIndex = rowIndexByTaskId?.get(task.id) ?? index;
@@ -6209,18 +6395,19 @@ var DependencyLines = import_react7.default.memo(({
6209
6395
  }
6210
6396
  }
6211
6397
  return { taskPositions: positions, taskIndices: indices, hiddenTaskIds: hidden };
6212
- }, [tasks, tasksForPositions, allTasks, collapsedParentIds, monthStart, dayWidth, rowHeight, rowTops, rowHeights, dragOverrides, rowIndexByTaskId]);
6398
+ }, [geometryTasks, tasksForPositions, allTasks, collapsedParentIds, monthStart, dayWidth, rowHeight, rowTops, rowHeights, dragOverrides, rowIndexByTaskId]);
6213
6399
  const tasksForCycleDetection = allTasks ?? tasks;
6214
6400
  const cycleInfo = (0, import_react7.useMemo)(() => {
6401
+ if (cycleTaskIds) return cycleTaskIds;
6215
6402
  const result = detectCycles(tasksForCycleDetection);
6216
- const cycleTaskIds = new Set(result.cyclePath || []);
6217
- return cycleTaskIds;
6218
- }, [tasksForCycleDetection]);
6403
+ const detectedCycleIds = new Set(result.cyclePath || []);
6404
+ return detectedCycleIds;
6405
+ }, [tasksForCycleDetection, cycleTaskIds]);
6219
6406
  const lines = (0, import_react7.useMemo)(() => {
6220
- const tasksForEdges = allTasks ?? tasks;
6407
+ const tasksForEdges = allTasks ?? geometryTasks;
6221
6408
  const taskMap = new Map(tasksForEdges.map((task) => [task.id, task]));
6222
6409
  const positionedTaskIds = Array.from(taskPositions.keys());
6223
- const renderedTaskIdSet = new Set(tasks.map((t) => t.id));
6410
+ const renderedTaskIdSet = new Set(geometryTasks.map((t) => t.id));
6224
6411
  const lines2 = [];
6225
6412
  for (const successorId of positionedTaskIds) {
6226
6413
  const successorTaskForEdges = taskMap.get(successorId);
@@ -6315,16 +6502,20 @@ var DependencyLines = import_react7.default.memo(({
6315
6502
  }
6316
6503
  }
6317
6504
  return lines2;
6318
- }, [tasks, allTasks, taskPositions, taskIndices, cycleInfo, collapsedParentIds, criticalTaskIds, activityEndpoints]);
6505
+ }, [geometryTasks, allTasks, taskPositions, taskIndices, cycleInfo, collapsedParentIds, criticalTaskIds, activityEndpoints]);
6319
6506
  const visibleLines = (0, import_react7.useMemo)(() => {
6320
- if (!horizontalWindow) return lines;
6321
- return lines.filter((line) => Math.max(line.fromX, line.toX) >= horizontalWindow.startPx && Math.min(line.fromX, line.toX) <= horizontalWindow.endPx);
6322
- }, [lines, horizontalWindow?.startPx, horizontalWindow?.endPx]);
6507
+ const renderedIds = activityEndpoints ? new Set(tasks.map((task) => task.id)) : void 0;
6508
+ return lines.filter((line) => {
6509
+ if (renderedIds && !renderedIds.has(line.predecessorId) && !renderedIds.has(line.successorId)) return false;
6510
+ return !horizontalWindow || Math.max(line.fromX, line.toX) >= horizontalWindow.startPx && Math.min(line.fromX, line.toX) <= horizontalWindow.endPx;
6511
+ });
6512
+ }, [lines, activityEndpoints, tasks, horizontalWindow?.startPx, horizontalWindow?.endPx]);
6323
6513
  const svgHeight = totalHeight ?? tasks.length * rowHeight;
6514
+ const interactive = onDependencyClick !== void 0;
6324
6515
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
6325
6516
  "svg",
6326
6517
  {
6327
- className: "gantt-dependencies-svg",
6518
+ className: `gantt-dependencies-svg${interactive ? "" : " gantt-dependencies-svg-static"}`,
6328
6519
  "data-testid": "dependency-lines-svg",
6329
6520
  width: gridWidth,
6330
6521
  height: svgHeight,
@@ -6427,63 +6618,22 @@ var DependencyLines = import_react7.default.memo(({
6427
6618
  }
6428
6619
  )
6429
6620
  ] }),
6430
- visibleLines.map(({ id, predecessorId, successorId, linkType, path, hasCycle, lag, fromX, toX, fromY, reverseOrder, isVirtual, isCritical, isCoupling }) => {
6431
- const isHovered = hoveredLineId === id;
6432
- const isSelected = selectedDep != null && id === `${selectedDep.predecessorId}-${selectedDep.successorId}-${selectedDep.linkType}`;
6433
- let pathClassName = "gantt-dependency-path";
6434
- if (isSelected) pathClassName += " gantt-dependency-selected";
6435
- else if (hasCycle) pathClassName += " gantt-dependency-cycle";
6436
- else if (isCritical) pathClassName += " gantt-dependency-critical";
6437
- if (isVirtual && !isSelected) pathClassName += " gantt-dependency-virtual";
6438
- let markerEnd;
6439
- if (isHovered) markerEnd = `url(#${markerId("arrowhead-hover")})`;
6440
- else if (isSelected) markerEnd = `url(#${markerId("arrowhead-selected")})`;
6441
- else if (hasCycle) markerEnd = `url(#${markerId("arrowhead-cycle")})`;
6442
- else if (isCritical) markerEnd = `url(#${markerId("arrowhead-critical")})`;
6443
- else markerEnd = `url(#${markerId("arrowhead")})`;
6444
- const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : isCritical ? "var(--gantt-critical-path-color, #dc2626)" : "var(--gantt-dependency-line-color, #666666)";
6445
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
6446
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6447
- "g",
6448
- {
6449
- className: `gantt-dependency-line${isHovered ? " gantt-dependency-line-hovered" : ""}`,
6450
- onPointerEnter: () => setHoveredLineId(id),
6451
- onPointerLeave: () => setHoveredLineId((current) => current === id ? null : current),
6452
- onClick: (event) => {
6453
- event.stopPropagation();
6454
- onDependencyClick?.({
6455
- predecessorId,
6456
- successorId,
6457
- linkType,
6458
- x: toX,
6459
- y: reverseOrder ? fromY - 4 : fromY + 12
6460
- });
6461
- },
6462
- role: "button",
6463
- "aria-label": `\u0421\u0432\u044F\u0437\u044C ${linkType}`,
6464
- children: isCoupling ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
6465
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("title", { children: "\u041F\u043E\u0441\u043B\u0435 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F \xB7 \u0431\u0435\u0437 \u043B\u0430\u0433\u0430" }),
6466
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: toX, cy: fromY, r: 12, className: "gantt-dependency-coupling-hit-area" }),
6467
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: toX, cy: fromY, r: 4, className: `${pathClassName} gantt-dependency-coupling` })
6468
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
6469
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: path, className: "gantt-dependency-hit-area" }),
6470
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: path, className: pathClassName, markerEnd })
6471
- ] })
6472
- }
6473
- ),
6474
- showLag && lag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6475
- "text",
6476
- {
6477
- className: "gantt-dependency-lag-label",
6478
- x: lag < 0 ? toX + 14 : toX - 14,
6479
- y: reverseOrder ? fromY - 4 : fromY + 12,
6480
- textAnchor: "middle",
6481
- fontSize: "10",
6482
- fill: lagColor,
6483
- children: lag > 0 ? `+${lag}` : `${lag}`
6484
- }
6485
- )
6486
- ] }, id);
6621
+ visibleLines.map((line) => {
6622
+ const isSelected = selectedDep != null && line.id === `${selectedDep.predecessorId}-${selectedDep.successorId}-${selectedDep.linkType}`;
6623
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6624
+ DependencyLineView,
6625
+ {
6626
+ line,
6627
+ hovered: hoveredLineId === line.id,
6628
+ selected: isSelected,
6629
+ showLag,
6630
+ markerPrefix,
6631
+ onLineHover: handleLineHover,
6632
+ onLineLeave: handleLineLeave,
6633
+ onDependencyClick
6634
+ },
6635
+ line.id
6636
+ );
6487
6637
  })
6488
6638
  ]
6489
6639
  }
@@ -6496,6 +6646,14 @@ var DependencyLines_default = DependencyLines;
6496
6646
  var import_react8 = require("react");
6497
6647
  var import_jsx_runtime8 = require("react/jsx-runtime");
6498
6648
  var activityKey = (taskId, activityId) => JSON.stringify([taskId, activityId]);
6649
+ var sameDependencies = (left, right) => {
6650
+ if (left === right) return true;
6651
+ if (!left || !right || left.length !== right.length) return false;
6652
+ return left.every((dep, index) => {
6653
+ const other = right[index];
6654
+ return dep.taskId === other.taskId && dep.type === other.type && dep.lag === other.lag && dep.hidden === other.hidden;
6655
+ });
6656
+ };
6499
6657
  function ActivityDependencyLines(props) {
6500
6658
  const { tasks, dependencies, monthStart, dayWidth, previewStore } = props;
6501
6659
  const [previewVersion, refresh] = (0, import_react8.useReducer)((value) => value + 1, 0);
@@ -6503,15 +6661,29 @@ function ActivityDependencyLines(props) {
6503
6661
  const cleanups = tasks.filter((task) => task.activities?.length).map((task) => previewStore?.subscribeTask(task.id, refresh));
6504
6662
  return () => cleanups.forEach((cleanup) => cleanup?.());
6505
6663
  }, [tasks, previewStore]);
6664
+ const cycleTaskIds = (0, import_react8.useMemo)(() => {
6665
+ const incoming = /* @__PURE__ */ new Map();
6666
+ for (const edge of dependencies) {
6667
+ const id = activityKey(edge.successorTaskId, edge.successorActivityId);
6668
+ const values = incoming.get(id) ?? [];
6669
+ values.push({ taskId: activityKey(edge.predecessorTaskId, edge.predecessorActivityId), type: edge.type, lag: edge.lag ?? 0 });
6670
+ incoming.set(id, values);
6671
+ }
6672
+ const nodes = tasks.flatMap((task) => (task.activities ?? []).map((activity) => ({ id: activityKey(task.id, activity.id), name: activity.name, startDate: activity.startDate, endDate: activity.endDate, dependencies: incoming.get(activityKey(task.id, activity.id)) })));
6673
+ return new Set(detectCycles(nodes).cyclePath ?? []);
6674
+ }, [tasks, dependencies]);
6675
+ const layoutCacheRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
6506
6676
  const layout = (0, import_react8.useMemo)(() => {
6507
- const flattened = [], rendered = [], tops = [], heights = [];
6677
+ const flattened = [], tops = [], heights = [];
6678
+ const nodesByTaskId = /* @__PURE__ */ new Map();
6508
6679
  const indices = /* @__PURE__ */ new Map();
6509
6680
  const neededRows = /* @__PURE__ */ new Set();
6510
6681
  const incoming = /* @__PURE__ */ new Map();
6511
6682
  const selected = props.highlight ? new Set(props.highlight.activities.map((activity) => activityKey(activity.taskId, activity.activityId))) : void 0;
6512
6683
  const highlightMode = props.highlight?.mode ?? "chain";
6513
6684
  for (const edge of dependencies) {
6514
- if (!props.renderedTaskIds.has(edge.predecessorTaskId) && !props.renderedTaskIds.has(edge.successorTaskId)) continue;
6685
+ const isCrossRow = edge.predecessorTaskId !== edge.successorTaskId;
6686
+ if (isCrossRow && !props.highlight) continue;
6515
6687
  if (selected) {
6516
6688
  const fromSelected = selected.has(activityKey(edge.predecessorTaskId, edge.predecessorActivityId));
6517
6689
  const toSelected = selected.has(activityKey(edge.successorTaskId, edge.successorActivityId));
@@ -6524,33 +6696,75 @@ function ActivityDependencyLines(props) {
6524
6696
  neededRows.add(edge.successorTaskId);
6525
6697
  const id = activityKey(edge.successorTaskId, edge.successorActivityId);
6526
6698
  const values = incoming.get(id) ?? [];
6527
- values.push({ taskId: activityKey(edge.predecessorTaskId, edge.predecessorActivityId), type: edge.type, lag: edge.lag });
6699
+ values.push({ taskId: activityKey(edge.predecessorTaskId, edge.predecessorActivityId), type: edge.type, lag: edge.lag ?? 0 });
6528
6700
  incoming.set(id, values);
6529
6701
  }
6702
+ const monthStartMs = monthStart.getTime();
6703
+ const cache = layoutCacheRef.current;
6530
6704
  for (const task of tasks) {
6531
6705
  if (!neededRows.has(task.id)) continue;
6532
6706
  const row = props.rowIndexByTaskId.get(task.id);
6533
6707
  if (row === void 0 || !task.activities?.length) continue;
6534
6708
  const height = props.rowHeights[row] ?? props.rowHeight;
6535
6709
  if (height < props.rowHeight) continue;
6536
- const packed = computeActivityLanes(task.activities, monthStart, dayWidth);
6537
- const offset = Math.max(0, (height - packed.laneCount * ACTIVITY_LANE_STEP) / 2);
6538
- const segments2 = new Map(packed.segments.map((segment) => [segment.id, segment]));
6539
- for (const activity of task.activities) {
6540
- const segment = segments2.get(activity.id);
6541
- const id = activityKey(task.id, activity.id), index = flattened.length;
6542
- const delta = previewStore?.getTaskOverrides(task.id)?.get(activity.id) ?? 0;
6543
- const node = { id, name: activity.name, startDate: shiftActivityDate(activity.startDate, delta), endDate: shiftActivityDate(activity.endDate, delta), dependencies: incoming.get(id) };
6710
+ const activities = task.activities;
6711
+ const overrides = previewStore?.getTaskOverrides(task.id);
6712
+ let range;
6713
+ for (const activity of activities) {
6714
+ const current = previewStore?.getActivityRange?.(task.id, activity.id);
6715
+ if (current) {
6716
+ range = current;
6717
+ break;
6718
+ }
6719
+ }
6720
+ let entry = cache.get(task.id);
6721
+ const reusable = entry && entry.activities === activities && entry.monthStartMs === monthStartMs && entry.dayWidth === dayWidth && entry.overrides === overrides && entry.range === range && entry.nodes.length === activities.length && entry.nodes.every((node, index) => node.name === activities[index].name && node.startDate === activities[index].startDate && node.endDate === activities[index].endDate && sameDependencies(node.dependencies, incoming.get(node.id)));
6722
+ if (!entry || !reusable) {
6723
+ const liveActivities = activities.map((activity) => {
6724
+ const activityRange = previewStore?.getActivityRange?.(task.id, activity.id);
6725
+ if (activityRange) return { ...activity, startDate: activityRange.startDate, endDate: activityRange.endDate };
6726
+ const delta = overrides?.get(activity.id) ?? 0;
6727
+ if (delta === 0) return activity;
6728
+ return { ...activity, startDate: shiftActivityDate(activity.startDate, delta), endDate: shiftActivityDate(activity.endDate, delta) };
6729
+ });
6730
+ const packed = computeActivityLanes(liveActivities, monthStart, dayWidth);
6731
+ const lanes = new Map(packed.segments.map((segment) => [segment.id, segment.lane]));
6732
+ const rawIds = liveActivities.map((activity) => activity.id);
6733
+ const nodes = liveActivities.map((activity) => ({
6734
+ id: activityKey(task.id, activity.id),
6735
+ name: activity.name,
6736
+ startDate: activity.startDate,
6737
+ endDate: activity.endDate,
6738
+ dependencies: incoming.get(activityKey(task.id, activity.id))
6739
+ }));
6740
+ entry = { activities, monthStartMs, dayWidth, overrides, range, laneCount: packed.laneCount, lanes, rawIds, nodes };
6741
+ cache.set(task.id, entry);
6742
+ }
6743
+ const offset = Math.max(0, (height - entry.laneCount * ACTIVITY_LANE_STEP) / 2);
6744
+ nodesByTaskId.set(task.id, entry.nodes);
6745
+ entry.nodes.forEach((node, nodeIndex) => {
6746
+ const lane = entry.lanes.get(entry.rawIds[nodeIndex]) ?? 0;
6747
+ const index = flattened.length;
6544
6748
  flattened.push(node);
6545
- if (props.renderedTaskIds.has(task.id)) rendered.push(node);
6546
- indices.set(id, index);
6547
- tops.push((props.rowTops[row] ?? row * props.rowHeight) + offset + segment.lane * ACTIVITY_LANE_STEP + (ACTIVITY_LANE_STEP - ACTIVITY_LANE_BAR_HEIGHT) / 2);
6749
+ indices.set(node.id, index);
6750
+ tops.push((props.rowTops[row] ?? row * props.rowHeight) + offset + lane * ACTIVITY_LANE_STEP + (ACTIVITY_LANE_STEP - ACTIVITY_LANE_BAR_HEIGHT) / 2);
6548
6751
  heights.push(ACTIVITY_LANE_BAR_HEIGHT);
6549
- }
6752
+ });
6753
+ }
6754
+ for (const taskId of cache.keys()) {
6755
+ if (!nodesByTaskId.has(taskId)) cache.delete(taskId);
6550
6756
  }
6551
- return { flattened, rendered, indices, tops, heights };
6552
- }, [tasks, dependencies, props.highlight, monthStart, dayWidth, props.rowIndexByTaskId, props.rowTops, props.rowHeights, props.rowHeight, props.renderedTaskIds, previewStore, previewVersion]);
6553
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DependencyLines, { tasks: layout.rendered, allTasks: layout.flattened, monthStart, dayWidth, rowHeight: ACTIVITY_LANE_BAR_HEIGHT, gridWidth: props.gridWidth, totalHeight: props.totalHeight, rowIndexByTaskId: layout.indices, rowTops: layout.tops, rowHeights: layout.heights, horizontalWindow: props.horizontalWindow, showLag: props.showLag, activityEndpoints: true });
6757
+ return { flattened, nodesByTaskId, indices, tops, heights };
6758
+ }, [tasks, dependencies, props.highlight, monthStart, dayWidth, props.rowIndexByTaskId, props.rowTops, props.rowHeights, props.rowHeight, previewStore, previewVersion]);
6759
+ const rendered = (0, import_react8.useMemo)(() => {
6760
+ const nodes = [];
6761
+ for (const taskId of props.renderedTaskIds) {
6762
+ const rowNodes = layout.nodesByTaskId.get(taskId);
6763
+ if (rowNodes) nodes.push(...rowNodes);
6764
+ }
6765
+ return nodes;
6766
+ }, [layout, props.renderedTaskIds]);
6767
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DependencyLines, { tasks: rendered, allTasks: layout.flattened, monthStart, dayWidth, rowHeight: ACTIVITY_LANE_BAR_HEIGHT, gridWidth: props.gridWidth, totalHeight: props.totalHeight, rowIndexByTaskId: layout.indices, rowTops: layout.tops, rowHeights: layout.heights, horizontalWindow: props.horizontalWindow, showLag: props.showLag, cycleTaskIds, activityEndpoints: true });
6554
6768
  }
6555
6769
 
6556
6770
  // src/components/DependencyCreationOverlay/DependencyCreationOverlay.tsx
@@ -15442,17 +15656,6 @@ function TaskGanttChartInner(props, ref) {
15442
15656
  onTaskDateChangeModeChange: externalOnTaskDateChangeModeChange,
15443
15657
  printDependencyLabels = false
15444
15658
  } = props;
15445
- const resolvedActivityDependencies = (0, import_react20.useMemo)(() => {
15446
- if (activityDependencies !== void 0) return activityDependencies;
15447
- return tasks.flatMap((task) => (task.activities ?? []).slice(1).map((successor, index) => ({
15448
- predecessorTaskId: task.id,
15449
- predecessorActivityId: task.activities[index].id,
15450
- successorTaskId: task.id,
15451
- successorActivityId: successor.id,
15452
- type: "FS",
15453
- lag: successor.lag ?? 0
15454
- })));
15455
- }, [tasks, activityDependencies]);
15456
15659
  const dayWidth = !isTableMatrixMode ? props.dayWidth ?? 40 : 40;
15457
15660
  const criticalPathMode = !isTableMatrixMode && !isPlanFactMode ? props.criticalPathMode : void 0;
15458
15661
  const onSubtreeScaleResult = !isTableMatrixMode && !isPlanFactMode ? props.onSubtreeScaleResult : void 0;
@@ -16359,7 +16562,7 @@ function TaskGanttChartInner(props, ref) {
16359
16562
  }
16360
16563
  }
16361
16564
  return Array.from(indices).sort((left, right) => left - right);
16362
- }, [compositeRowLayout, forceFullRenderForPrint, forcedRenderedTaskIds, scrollViewport, visibleTaskIndexMap, visibleTasks.length]);
16565
+ }, [compositeRowLayout, forceFullRenderForPrint, forcedRenderedTaskIds, scrollViewport.scrollTop, scrollViewport.viewportHeight, visibleTaskIndexMap, visibleTasks.length]);
16363
16566
  const visiblePlanFactDateIndices = (0, import_react20.useMemo)(() => {
16364
16567
  if (forceFullRenderForPrint) {
16365
16568
  return void 0;
@@ -16673,6 +16876,7 @@ function TaskGanttChartInner(props, ref) {
16673
16876
  if (document.activeElement instanceof HTMLElement) {
16674
16877
  document.activeElement.blur();
16675
16878
  }
16879
+ container.setAttribute("data-panning", "true");
16676
16880
  container.style.cursor = "grabbing";
16677
16881
  e.preventDefault();
16678
16882
  }, [activityActivationStore]);
@@ -16710,7 +16914,10 @@ function TaskGanttChartInner(props, ref) {
16710
16914
  }
16711
16915
  panStateRef.current = null;
16712
16916
  const container = scrollContainerRef.current;
16713
- if (container) container.style.cursor = "";
16917
+ if (container) {
16918
+ container.removeAttribute("data-panning");
16919
+ container.style.cursor = "";
16920
+ }
16714
16921
  };
16715
16922
  window.addEventListener("mousemove", handlePanMove);
16716
16923
  window.addEventListener("mouseup", handlePanEnd);
@@ -16929,7 +17136,7 @@ function TaskGanttChartInner(props, ref) {
16929
17136
  onHoverEnd: () => setActiveTimelineTooltip(null)
16930
17137
  }
16931
17138
  ),
16932
- showActivityLinks && resolvedActivityDependencies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ActivityDependencyLines, { tasks: visibleTasks, renderedTaskIds: renderedTaskIdsRef.current, dependencies: resolvedActivityDependencies, highlight: activityDependencyHighlight, monthStart, dayWidth, rowHeight: effectiveRowHeight, gridWidth: renderGridWidth, totalHeight: totalGridHeight, rowIndexByTaskId: visibleTaskIndexMap, rowTops: compositeRowLayout.tops, rowHeights: compositeRowLayout.heights, showLag: showActivityLag, previewStore: activityPreviewStore, horizontalWindow }),
17139
+ showActivityLinks && activityDependencies !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ActivityDependencyLines, { tasks: previewVisibleTasks, renderedTaskIds: renderedTaskIdsRef.current, dependencies: activityDependencies, highlight: activityDependencyHighlight, monthStart, dayWidth, rowHeight: effectiveRowHeight, gridWidth: renderGridWidth, totalHeight: totalGridHeight, rowIndexByTaskId: visibleTaskIndexMap, rowTops: compositeRowLayout.tops, rowHeights: compositeRowLayout.heights, showLag: showActivityLag, previewStore: activityPreviewStore, horizontalWindow }),
16933
17140
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
16934
17141
  DependencyLines_default,
16935
17142
  {
@@ -17072,10 +17279,10 @@ function TaskGanttChartInner(props, ref) {
17072
17279
  activityActivationStore,
17073
17280
  activityClickToDrag,
17074
17281
  activityActivationMode,
17075
- showActivityLinks: false,
17282
+ showActivityLinks: showActivityLinks && activityDependencies === void 0,
17076
17283
  showActivityLag,
17077
17284
  activityTooltip,
17078
- horizontalWindow,
17285
+ horizontalWindow: forceFullRenderForPrint ? void 0 : horizontalWindow,
17079
17286
  onCascadeProgress: handleCascadeProgress,
17080
17287
  onCascade: handleCascade,
17081
17288
  onScheduleIntent,