gantt-lib 0.104.2 → 0.105.1

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
@@ -99,6 +99,7 @@ __export(index_exports, {
99
99
  getSuccessorChain: () => getSuccessorChain,
100
100
  getTaskDuration: () => getTaskDuration,
101
101
  getTransitiveCascadeChain: () => getTransitiveCascadeChain,
102
+ getVisibleReorderPlan: () => getVisibleReorderPlan,
102
103
  getVisibleReorderPosition: () => getVisibleReorderPosition,
103
104
  getWeekBlocks: () => getWeekBlocks,
104
105
  getWeekSpans: () => getWeekSpans,
@@ -3670,6 +3671,29 @@ function getDescendantIds(taskId, tasks) {
3670
3671
  collect(taskId);
3671
3672
  return descendants;
3672
3673
  }
3674
+ function isTaskParent2(taskId, tasks) {
3675
+ return tasks.some((task) => task.parentId === taskId);
3676
+ }
3677
+ function getSubtreeEndIndex(taskId, tasks) {
3678
+ const descendantIds = new Set(getDescendantIds(taskId, tasks));
3679
+ const taskIndex = tasks.findIndex((task) => task.id === taskId);
3680
+ if (taskIndex === -1) return -1;
3681
+ let endIndex = taskIndex;
3682
+ for (let index = taskIndex + 1; index < tasks.length; index += 1) {
3683
+ if (!descendantIds.has(tasks[index].id)) break;
3684
+ endIndex = index;
3685
+ }
3686
+ return endIndex;
3687
+ }
3688
+ function getNearestVisibleSiblingBefore(visibleTasks, movedIds, startIndex) {
3689
+ for (let index = startIndex; index >= 0; index -= 1) {
3690
+ const candidate = visibleTasks[index];
3691
+ if (candidate && !movedIds.has(candidate.id)) {
3692
+ return candidate;
3693
+ }
3694
+ }
3695
+ return null;
3696
+ }
3673
3697
  function getVisibleReorderPosition(orderedTasks, visibleTasks, movedTaskId, originVisibleIndex, dropVisibleIndex) {
3674
3698
  const originOrderedIndex = orderedTasks.findIndex((task) => task.id === movedTaskId);
3675
3699
  if (originOrderedIndex === -1) {
@@ -3703,6 +3727,99 @@ function getVisibleReorderPosition(orderedTasks, visibleTasks, movedTaskId, orig
3703
3727
  insertIndex
3704
3728
  };
3705
3729
  }
3730
+ function getVisibleReorderPlan(orderedTasks, visibleTasks, movedTaskId, target) {
3731
+ const originOrderedIndex = orderedTasks.findIndex((task) => task.id === movedTaskId);
3732
+ if (originOrderedIndex === -1) {
3733
+ return null;
3734
+ }
3735
+ const descendantIds = getDescendantIds(movedTaskId, orderedTasks);
3736
+ const movedIds = /* @__PURE__ */ new Set([movedTaskId, ...descendantIds]);
3737
+ const reorderedWithoutMoved = orderedTasks.filter((task) => !movedIds.has(task.id));
3738
+ const movedTask = orderedTasks[originOrderedIndex];
3739
+ if (target.placement === "end" || target.index >= visibleTasks.length) {
3740
+ return {
3741
+ originOrderedIndex,
3742
+ insertIndex: reorderedWithoutMoved.length,
3743
+ inferredParentId: void 0
3744
+ };
3745
+ }
3746
+ const targetTask = visibleTasks[target.index];
3747
+ const previousVisibleTask = getNearestVisibleSiblingBefore(
3748
+ visibleTasks,
3749
+ movedIds,
3750
+ target.index - 1
3751
+ );
3752
+ if (!targetTask) {
3753
+ return null;
3754
+ }
3755
+ if (movedIds.has(targetTask.id)) {
3756
+ return null;
3757
+ }
3758
+ const targetIndex = reorderedWithoutMoved.findIndex((task) => task.id === targetTask.id);
3759
+ if (targetIndex === -1) {
3760
+ return null;
3761
+ }
3762
+ let insertIndex = targetIndex;
3763
+ let inferredParentId;
3764
+ switch (target.placement) {
3765
+ case "before": {
3766
+ insertIndex = targetIndex;
3767
+ inferredParentId = targetTask.parentId || void 0;
3768
+ if (!targetTask.parentId && previousVisibleTask?.parentId) {
3769
+ const isDetachingFromSameGroup = movedTask.parentId === previousVisibleTask.parentId;
3770
+ if (isDetachingFromSameGroup) {
3771
+ const previousEndIndex = getSubtreeEndIndex(previousVisibleTask.id, reorderedWithoutMoved);
3772
+ inferredParentId = void 0;
3773
+ insertIndex = previousEndIndex === -1 ? targetIndex : previousEndIndex + 1;
3774
+ }
3775
+ }
3776
+ break;
3777
+ }
3778
+ case "inside": {
3779
+ if (isTaskParent2(targetTask.id, orderedTasks)) {
3780
+ return null;
3781
+ }
3782
+ inferredParentId = targetTask.id;
3783
+ if (!inferredParentId || movedIds.has(inferredParentId)) {
3784
+ return null;
3785
+ }
3786
+ if (movedTask.parentId === inferredParentId) {
3787
+ return null;
3788
+ }
3789
+ const anchorEndIndex = getSubtreeEndIndex(targetTask.id, reorderedWithoutMoved);
3790
+ insertIndex = anchorEndIndex === -1 ? targetIndex + 1 : anchorEndIndex + 1;
3791
+ break;
3792
+ }
3793
+ case "after": {
3794
+ const nextVisibleTask = visibleTasks[target.index + 1];
3795
+ const hasVisibleChildBelow = nextVisibleTask?.parentId === targetTask.id;
3796
+ if (hasVisibleChildBelow) {
3797
+ inferredParentId = targetTask.id;
3798
+ insertIndex = targetIndex + 1;
3799
+ } else {
3800
+ inferredParentId = targetTask.parentId || void 0;
3801
+ const targetEndIndex = getSubtreeEndIndex(targetTask.id, reorderedWithoutMoved);
3802
+ insertIndex = targetEndIndex === -1 ? targetIndex + 1 : targetEndIndex + 1;
3803
+ }
3804
+ break;
3805
+ }
3806
+ default: {
3807
+ return {
3808
+ originOrderedIndex,
3809
+ insertIndex: reorderedWithoutMoved.length,
3810
+ inferredParentId: void 0
3811
+ };
3812
+ }
3813
+ }
3814
+ if (inferredParentId && movedIds.has(inferredParentId)) {
3815
+ return null;
3816
+ }
3817
+ return {
3818
+ originOrderedIndex,
3819
+ insertIndex,
3820
+ inferredParentId
3821
+ };
3822
+ }
3706
3823
 
3707
3824
  // src/components/ui/Popover.tsx
3708
3825
  var RadixPopover = __toESM(require("@radix-ui/react-popover"));
@@ -4821,6 +4938,9 @@ var TaskListRow = import_react10.default.memo(
4821
4938
  editingTaskId,
4822
4939
  isDragging = false,
4823
4940
  isDragOver = false,
4941
+ dragOverPlacement = null,
4942
+ isNestedDropTarget = false,
4943
+ isDirectChildDropTarget = false,
4824
4944
  onDragStart,
4825
4945
  onDragOver,
4826
4946
  onDrop,
@@ -5695,7 +5815,7 @@ var TaskListRow = import_react10.default.memo(
5695
5815
  const numberCell = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5696
5816
  "div",
5697
5817
  {
5698
- className: "gantt-tl-cell gantt-tl-cell-number",
5818
+ className: `gantt-tl-cell gantt-tl-cell-number${onDragStart ? " gantt-tl-cell-number-reorderable" : ""}`,
5699
5819
  style: getColumnStyle("number", 40),
5700
5820
  onClick: handleNumberClick,
5701
5821
  children: [
@@ -5719,6 +5839,16 @@ var TaskListRow = import_react10.default.memo(
5719
5839
  );
5720
5840
  const nameTriggerPaddingLeft = isParent ? `${nestingDepth * 20 + 28}px` : nestingDepth > 0 ? `${nestingDepth * 20 + 8}px` : void 0;
5721
5841
  const nameInputPaddingLeft = nestingDepth > 0 ? `${nestingDepth * 20 + 8}px` : void 0;
5842
+ const nestedDropIndicatorLeft = (() => {
5843
+ const numberColumnWidth = resolvedColumns?.find((col) => col.id === "number")?.width ?? 40;
5844
+ const nestedAxisDepth = Math.max(0, nestingDepth - 1);
5845
+ return `${numberColumnWidth + nestedAxisDepth * 20 + 9}px`;
5846
+ })();
5847
+ const nestedInsideDropIndicatorLeft = (() => {
5848
+ const numberColumnWidth = resolvedColumns?.find((col) => col.id === "number")?.width ?? 40;
5849
+ const nestedAxisDepth = Math.max(0, nestingDepth);
5850
+ return `${numberColumnWidth + nestedAxisDepth * 20 + 9}px`;
5851
+ })();
5722
5852
  const nameCell = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-cell gantt-tl-cell-name", style: getColumnStyle("name", 200), children: [
5723
5853
  isChild && !editingName && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
5724
5854
  !isFilterHideMode && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
@@ -6483,12 +6613,20 @@ var TaskListRow = import_react10.default.memo(
6483
6613
  isSourceRow ? "gantt-tl-row-picking-self" : "",
6484
6614
  isDragging ? "gantt-tl-row-dragging" : "",
6485
6615
  isDragOver ? "gantt-tl-row-drag-over" : "",
6616
+ isDragOver && dragOverPlacement ? `gantt-tl-row-drag-over-${dragOverPlacement}` : "",
6617
+ isDragOver && isNestedDropTarget ? "gantt-tl-row-drag-over-nested" : "",
6618
+ isDragOver && isDirectChildDropTarget ? "gantt-tl-row-drag-over-direct-child" : "",
6486
6619
  isChild ? "gantt-tl-row-child" : "",
6487
6620
  isParent ? "gantt-tl-row-parent" : "",
6488
6621
  `gantt-tl-row-level-${rowFillLevel}`,
6489
6622
  isTotalRow ? "gantt-tl-row-total" : ""
6490
6623
  ].filter(Boolean).join(" "),
6491
- style: { height: `${rowHeight}px`, position: "relative" },
6624
+ style: {
6625
+ height: `${rowHeight}px`,
6626
+ position: "relative",
6627
+ "--gantt-tl-nested-drop-left": nestedDropIndicatorLeft,
6628
+ "--gantt-tl-nested-inside-drop-left": nestedInsideDropIndicatorLeft
6629
+ },
6492
6630
  "data-gantt-task-row-id": task.id,
6493
6631
  onClick: handleRowClickInternal,
6494
6632
  onKeyDown: handleRowKeyDown,
@@ -6844,6 +6982,23 @@ function duplicateTaskSubtree(anchorTaskId, orderedTasks) {
6844
6982
  ...orderedTasks.slice(insertIndex)
6845
6983
  ];
6846
6984
  }
6985
+ var DRAG_ZONE_EDGE_RATIO = 0.28;
6986
+ function getDropPlacementFromEvent(e) {
6987
+ const rect = e.currentTarget.getBoundingClientRect();
6988
+ const ratio = rect.height > 0 ? (e.clientY - rect.top) / rect.height : 0.5;
6989
+ if (ratio <= DRAG_ZONE_EDGE_RATIO) return "before";
6990
+ if (ratio >= 1 - DRAG_ZONE_EDGE_RATIO) return "after";
6991
+ return "inside";
6992
+ }
6993
+ function getNearestVisibleDropAnchorBefore(visibleTasks, movedIds, startIndex) {
6994
+ for (let index = startIndex; index >= 0; index -= 1) {
6995
+ const candidate = visibleTasks[index];
6996
+ if (candidate && !movedIds.has(candidate.id)) {
6997
+ return { task: candidate, index };
6998
+ }
6999
+ }
7000
+ return null;
7001
+ }
6847
7002
  function getTaskNumber(tasks, taskIndex) {
6848
7003
  const task = tasks[taskIndex];
6849
7004
  if (!task) return "";
@@ -6930,6 +7085,7 @@ var TaskList = ({
6930
7085
  onInsertAfter,
6931
7086
  onReorder,
6932
7087
  disableTaskDrag = false,
7088
+ disableTaskListReorder = false,
6933
7089
  editingTaskId: propEditingTaskId,
6934
7090
  enableAddTask = true,
6935
7091
  defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
@@ -6959,6 +7115,7 @@ var TaskList = ({
6959
7115
  taskDateChangeMode = "preserve-duration",
6960
7116
  onTaskDateChangeModeChange
6961
7117
  }) => {
7118
+ const reorderDisabled = disableTaskListReorder || disableTaskDrag;
6962
7119
  const [internalSelectedTaskIds, setInternalSelectedTaskIds] = (0, import_react12.useState)(/* @__PURE__ */ new Set());
6963
7120
  const [activeCustomCell, setActiveCustomCell] = (0, import_react12.useState)(null);
6964
7121
  const [columnWidthOverrides, setColumnWidthOverrides] = (0, import_react12.useState)(() => normalizeColumnWidthMap(taskListColumnWidths));
@@ -7255,9 +7412,15 @@ var TaskList = ({
7255
7412
  const [isCreating, setIsCreating] = (0, import_react12.useState)(false);
7256
7413
  const [pendingInsert, setPendingInsert] = (0, import_react12.useState)(null);
7257
7414
  const [draggingIndex, setDraggingIndex] = (0, import_react12.useState)(null);
7258
- const [dragOverIndex, setDragOverIndex] = (0, import_react12.useState)(null);
7415
+ const [dragOverTarget, setDragOverTarget] = (0, import_react12.useState)(null);
7259
7416
  const dragOriginIndexRef = (0, import_react12.useRef)(null);
7260
7417
  const dragTaskIdRef = (0, import_react12.useRef)(null);
7418
+ const clearDragState = (0, import_react12.useCallback)(() => {
7419
+ setDraggingIndex(null);
7420
+ setDragOverTarget(null);
7421
+ dragOriginIndexRef.current = null;
7422
+ dragTaskIdRef.current = null;
7423
+ }, []);
7261
7424
  const isValidParentDrop = (0, import_react12.useCallback)((draggedTaskId, dropIndex) => {
7262
7425
  if (!isTaskParent(draggedTaskId, tasks)) {
7263
7426
  return true;
@@ -7282,59 +7445,129 @@ var TaskList = ({
7282
7445
  dragOriginIndexRef.current = index;
7283
7446
  dragTaskIdRef.current = visibleTasks[index]?.id ?? null;
7284
7447
  }, [visibleTasks]);
7448
+ const normalizeDropTarget = (0, import_react12.useCallback)((movedTaskId, target) => {
7449
+ if (target.placement !== "before" || target.index <= 0 || target.index >= visibleTasks.length) {
7450
+ return target;
7451
+ }
7452
+ const descendantIds = new Set(getAllDescendants2(movedTaskId, orderedTasks).map((task) => task.id));
7453
+ const movedIds = /* @__PURE__ */ new Set([movedTaskId, ...descendantIds]);
7454
+ const targetTask = visibleTasks[target.index];
7455
+ if (targetTask?.id === movedTaskId) {
7456
+ return target;
7457
+ }
7458
+ const previousAnchor = getNearestVisibleDropAnchorBefore(visibleTasks, movedIds, target.index - 1);
7459
+ if (!targetTask || !previousAnchor) {
7460
+ return target;
7461
+ }
7462
+ const isEquivalentSiblingGap = (targetTask.parentId || void 0) === (previousAnchor.task.parentId || void 0);
7463
+ if (isEquivalentSiblingGap) {
7464
+ return {
7465
+ index: previousAnchor.index,
7466
+ placement: "after"
7467
+ };
7468
+ }
7469
+ return target;
7470
+ }, [orderedTasks, visibleTasks]);
7285
7471
  const handleDragOver = (0, import_react12.useCallback)((index, e) => {
7286
7472
  e.preventDefault();
7287
7473
  const draggedTaskId = dragTaskIdRef.current;
7288
7474
  if (!draggedTaskId) return;
7289
- if (!isValidParentDrop(draggedTaskId, index)) {
7290
- setDragOverIndex(null);
7475
+ const rawPlacement = getDropPlacementFromEvent(e);
7476
+ const isSelfTopBefore = visibleTasks[index]?.id === draggedTaskId && rawPlacement === "before";
7477
+ if (isSelfTopBefore) {
7478
+ setDragOverTarget(null);
7479
+ e.dataTransfer.dropEffect = "none";
7480
+ return;
7481
+ }
7482
+ const isFirstChildTopAfterParent = rawPlacement === "before" && visibleTasks[index]?.parentId === visibleTasks[index - 1]?.id;
7483
+ if (isFirstChildTopAfterParent) {
7484
+ setDragOverTarget(null);
7485
+ e.dataTransfer.dropEffect = "none";
7486
+ return;
7487
+ }
7488
+ const normalizedTarget = normalizeDropTarget(draggedTaskId, {
7489
+ index,
7490
+ placement: rawPlacement
7491
+ });
7492
+ const targetTask = visibleTasks[normalizedTarget.index];
7493
+ if (!targetTask || !isValidParentDrop(draggedTaskId, normalizedTarget.index)) {
7494
+ setDragOverTarget(null);
7495
+ e.dataTransfer.dropEffect = "none";
7496
+ return;
7497
+ }
7498
+ const reorderPlan = getVisibleReorderPlan(
7499
+ orderedTasks,
7500
+ visibleTasks,
7501
+ draggedTaskId,
7502
+ normalizedTarget
7503
+ );
7504
+ if (!reorderPlan) {
7505
+ setDragOverTarget(null);
7506
+ e.dataTransfer.dropEffect = "none";
7507
+ return;
7508
+ }
7509
+ const moved = orderedTasks[reorderPlan.originOrderedIndex];
7510
+ const currentParentId = moved?.parentId || void 0;
7511
+ if (reorderPlan.insertIndex === reorderPlan.originOrderedIndex && reorderPlan.inferredParentId === currentParentId) {
7512
+ setDragOverTarget(null);
7291
7513
  e.dataTransfer.dropEffect = "none";
7292
7514
  return;
7293
7515
  }
7294
7516
  e.dataTransfer.dropEffect = "move";
7295
- setDragOverIndex(index);
7296
- }, [isValidParentDrop]);
7517
+ setDragOverTarget({
7518
+ ...normalizedTarget,
7519
+ inferredParentId: reorderPlan.inferredParentId,
7520
+ isDirectChildDrop: normalizedTarget.placement === "after" && reorderPlan.inferredParentId === targetTask.id
7521
+ });
7522
+ }, [isValidParentDrop, normalizeDropTarget, orderedTasks, visibleTasks]);
7297
7523
  const handleDrop = (0, import_react12.useCallback)((dropIndex, e) => {
7298
7524
  e.preventDefault();
7299
7525
  const originVisibleIndex = dragOriginIndexRef.current;
7300
7526
  const movedTaskId = dragTaskIdRef.current;
7301
- if (originVisibleIndex === null || movedTaskId === null || originVisibleIndex === dropIndex) {
7302
- setDraggingIndex(null);
7303
- setDragOverIndex(null);
7304
- dragOriginIndexRef.current = null;
7305
- dragTaskIdRef.current = null;
7527
+ if (originVisibleIndex === null || movedTaskId === null) {
7528
+ clearDragState();
7529
+ return;
7530
+ }
7531
+ const rawPlacement = dropIndex >= visibleTasks.length ? "end" : getDropPlacementFromEvent(e);
7532
+ const isSelfTopBefore = dropIndex < visibleTasks.length && visibleTasks[dropIndex]?.id === movedTaskId && rawPlacement === "before";
7533
+ if (isSelfTopBefore) {
7534
+ clearDragState();
7306
7535
  return;
7307
7536
  }
7308
- if (!isValidParentDrop(movedTaskId, dropIndex)) {
7309
- setDraggingIndex(null);
7310
- setDragOverIndex(null);
7311
- dragOriginIndexRef.current = null;
7312
- dragTaskIdRef.current = null;
7537
+ const isFirstChildTopAfterParent = dropIndex < visibleTasks.length && rawPlacement === "before" && visibleTasks[dropIndex]?.parentId === visibleTasks[dropIndex - 1]?.id;
7538
+ if (isFirstChildTopAfterParent) {
7539
+ clearDragState();
7313
7540
  return;
7314
7541
  }
7315
- const reorderPosition = getVisibleReorderPosition(
7542
+ const normalizedTarget = dropIndex >= visibleTasks.length ? { index: dropIndex, placement: "end" } : normalizeDropTarget(movedTaskId, {
7543
+ index: dropIndex,
7544
+ placement: rawPlacement
7545
+ });
7546
+ if (originVisibleIndex === normalizedTarget.index && normalizedTarget.placement === "before") {
7547
+ clearDragState();
7548
+ return;
7549
+ }
7550
+ if (!isValidParentDrop(movedTaskId, normalizedTarget.index)) {
7551
+ clearDragState();
7552
+ return;
7553
+ }
7554
+ const reorderPlan = getVisibleReorderPlan(
7316
7555
  orderedTasks,
7317
7556
  visibleTasks,
7318
7557
  movedTaskId,
7319
- originVisibleIndex,
7320
- dropIndex
7558
+ normalizedTarget
7321
7559
  );
7322
- if (!reorderPosition) {
7323
- setDraggingIndex(null);
7324
- setDragOverIndex(null);
7325
- dragOriginIndexRef.current = null;
7326
- dragTaskIdRef.current = null;
7560
+ if (!reorderPlan) {
7561
+ clearDragState();
7327
7562
  return;
7328
7563
  }
7329
- const { originOrderedIndex, insertIndex } = reorderPosition;
7330
- if (insertIndex === originOrderedIndex) {
7331
- setDraggingIndex(null);
7332
- setDragOverIndex(null);
7333
- dragOriginIndexRef.current = null;
7334
- dragTaskIdRef.current = null;
7564
+ const { originOrderedIndex, insertIndex, inferredParentId } = reorderPlan;
7565
+ const moved = orderedTasks[originOrderedIndex];
7566
+ const currentParentId = moved.parentId || void 0;
7567
+ if (insertIndex === originOrderedIndex && inferredParentId === currentParentId) {
7568
+ clearDragState();
7335
7569
  return;
7336
7570
  }
7337
- const moved = orderedTasks[originOrderedIndex];
7338
7571
  const hasChildren = isTaskParent(moved.id, orderedTasks);
7339
7572
  let subtree;
7340
7573
  let subtreeCount;
@@ -7349,47 +7582,14 @@ var TaskList = ({
7349
7582
  const reordered = [...orderedTasks];
7350
7583
  reordered.splice(originOrderedIndex, subtreeCount);
7351
7584
  const adjustedInsertIndex = insertIndex;
7352
- let inferredParentId;
7353
- if (moved.parentId) {
7354
- const parentIndex = reordered.findIndex((t) => t.id === moved.parentId);
7355
- if (parentIndex === -1) {
7356
- inferredParentId = void 0;
7357
- } else {
7358
- const numSiblings = reordered.filter((t) => t.parentId === moved.parentId).length;
7359
- const groupEnd = parentIndex + numSiblings;
7360
- if (adjustedInsertIndex <= parentIndex || adjustedInsertIndex > groupEnd) {
7361
- inferredParentId = void 0;
7362
- } else {
7363
- inferredParentId = moved.parentId;
7364
- }
7365
- }
7366
- } else {
7367
- }
7368
7585
  reordered.splice(adjustedInsertIndex, 0, ...subtree);
7369
- if (!moved.parentId && !hasChildren) {
7370
- const taskAbove = adjustedInsertIndex > 0 ? reordered[adjustedInsertIndex - 1] : null;
7371
- const taskBelow = adjustedInsertIndex < reordered.length - 1 ? reordered[adjustedInsertIndex + 1] : null;
7372
- if (taskAbove && taskBelow && taskBelow.parentId === taskAbove.id) {
7373
- inferredParentId = taskAbove.id;
7374
- } else if (taskAbove && taskBelow && taskAbove.parentId && taskAbove.parentId === taskBelow.parentId) {
7375
- inferredParentId = taskAbove.parentId;
7376
- } else if (!taskAbove && taskBelow && taskBelow.parentId) {
7377
- inferredParentId = taskBelow.parentId;
7378
- }
7379
- }
7380
7586
  onReorder?.(reordered, moved.id, inferredParentId);
7381
7587
  onTaskSelect?.(moved.id);
7382
- setDraggingIndex(null);
7383
- setDragOverIndex(null);
7384
- dragOriginIndexRef.current = null;
7385
- dragTaskIdRef.current = null;
7386
- }, [orderedTasks, visibleTasks, onReorder, onTaskSelect]);
7588
+ clearDragState();
7589
+ }, [orderedTasks, visibleTasks, onReorder, onTaskSelect, isValidParentDrop, clearDragState, normalizeDropTarget]);
7387
7590
  const handleDragEnd = (0, import_react12.useCallback)(() => {
7388
- setDraggingIndex(null);
7389
- setDragOverIndex(null);
7390
- dragOriginIndexRef.current = null;
7391
- dragTaskIdRef.current = null;
7392
- }, []);
7591
+ clearDragState();
7592
+ }, [clearDragState]);
7393
7593
  const handleConfirmNewTask = (0, import_react12.useCallback)((name) => {
7394
7594
  const range = buildDefaultTaskDateRange(getTodayISODate(), {
7395
7595
  businessDays,
@@ -7783,12 +7983,15 @@ var TaskList = ({
7783
7983
  onAdd,
7784
7984
  onInsertAfter: handleStartInsertAfter,
7785
7985
  editingTaskId: propEditingTaskId,
7786
- isDragging: !disableTaskDrag && draggingIndex === index,
7787
- isDragOver: !disableTaskDrag && dragOverIndex === index,
7788
- onDragStart: disableTaskDrag ? void 0 : handleDragStart,
7789
- onDragOver: disableTaskDrag ? void 0 : handleDragOver,
7790
- onDrop: disableTaskDrag ? void 0 : handleDrop,
7791
- onDragEnd: disableTaskDrag ? void 0 : handleDragEnd,
7986
+ isDragging: !reorderDisabled && draggingIndex === index,
7987
+ isDragOver: !reorderDisabled && dragOverTarget?.index === index,
7988
+ dragOverPlacement: !reorderDisabled && dragOverTarget?.index === index && dragOverTarget.placement !== "end" ? dragOverTarget.placement : null,
7989
+ isNestedDropTarget: !reorderDisabled && dragOverTarget?.index === index && !!dragOverTarget.inferredParentId,
7990
+ isDirectChildDropTarget: !reorderDisabled && dragOverTarget?.index === index && !!dragOverTarget.isDirectChildDrop,
7991
+ onDragStart: reorderDisabled ? void 0 : handleDragStart,
7992
+ onDragOver: reorderDisabled ? void 0 : handleDragOver,
7993
+ onDrop: reorderDisabled ? void 0 : handleDrop,
7994
+ onDragEnd: reorderDisabled ? void 0 : handleDragEnd,
7792
7995
  collapsedParentIds,
7793
7996
  onToggleCollapse: handleToggleCollapse,
7794
7997
  onPromoteTask,
@@ -7842,25 +8045,25 @@ var TaskList = ({
7842
8045
  enableAddTask && onAdd && !isCreating && !pendingInsert && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7843
8046
  "button",
7844
8047
  {
7845
- className: `gantt-tl-add-btn${!disableTaskDrag && dragOverIndex === visibleTasks.length ? " gantt-tl-add-btn-drag-over" : ""}`,
8048
+ className: `gantt-tl-add-btn${!reorderDisabled && dragOverTarget?.index === visibleTasks.length ? " gantt-tl-add-btn-drag-over" : ""}`,
7846
8049
  onClick: () => {
7847
8050
  setPendingInsert(null);
7848
8051
  setIsCreating(true);
7849
8052
  },
7850
- onDragEnter: disableTaskDrag ? void 0 : (e) => {
8053
+ onDragEnter: reorderDisabled ? void 0 : (e) => {
7851
8054
  e.preventDefault();
7852
- setDragOverIndex(visibleTasks.length);
8055
+ setDragOverTarget({ index: visibleTasks.length, placement: "end" });
7853
8056
  },
7854
- onDragOver: disableTaskDrag ? void 0 : (e) => {
8057
+ onDragOver: reorderDisabled ? void 0 : (e) => {
7855
8058
  e.preventDefault();
7856
8059
  e.dataTransfer.dropEffect = "move";
7857
- setDragOverIndex(visibleTasks.length);
8060
+ setDragOverTarget({ index: visibleTasks.length, placement: "end" });
7858
8061
  },
7859
- onDragLeave: disableTaskDrag ? void 0 : (e) => {
8062
+ onDragLeave: reorderDisabled ? void 0 : (e) => {
7860
8063
  e.preventDefault();
7861
- setDragOverIndex(null);
8064
+ setDragOverTarget(null);
7862
8065
  },
7863
- onDrop: disableTaskDrag ? void 0 : (e) => {
8066
+ onDrop: reorderDisabled ? void 0 : (e) => {
7864
8067
  e.preventDefault();
7865
8068
  handleDrop(visibleTasks.length, e);
7866
8069
  },
@@ -10285,6 +10488,7 @@ function TaskGanttChartInner(props, ref) {
10285
10488
  onDelete,
10286
10489
  onInsertAfter,
10287
10490
  onReorder,
10491
+ disableTaskListReorder = false,
10288
10492
  onPromoteTask,
10289
10493
  onDemoteTask,
10290
10494
  onUngroupTask,
@@ -10792,10 +10996,12 @@ function TaskGanttChartInner(props, ref) {
10792
10996
  onTasksChange?.([promotedTask]);
10793
10997
  return;
10794
10998
  }
10999
+ const tasksWithoutPromoted = tasks.filter((t) => t.id !== taskId);
11000
+ const insertIndex = lastSiblingIndex.index;
10795
11001
  const reorderedTasks = normalizeHierarchyTasks([
10796
- ...tasks.filter((t) => t.id !== taskId).slice(0, lastSiblingIndex.index + 1),
11002
+ ...tasksWithoutPromoted.slice(0, insertIndex),
10797
11003
  promotedTask,
10798
- ...tasks.filter((t) => t.id !== taskId).slice(lastSiblingIndex.index + 1)
11004
+ ...tasksWithoutPromoted.slice(insertIndex)
10799
11005
  ]);
10800
11006
  onTasksChange?.(reorderedTasks);
10801
11007
  }, [tasks, onTasksChange, onPromoteTask]);
@@ -10949,7 +11155,7 @@ function TaskGanttChartInner(props, ref) {
10949
11155
  onDelete: handleDelete,
10950
11156
  onInsertAfter: handleInsertAfter,
10951
11157
  onReorder: handleReorder,
10952
- disableTaskDrag,
11158
+ disableTaskDrag: disableTaskListReorder,
10953
11159
  editingTaskId,
10954
11160
  enableAddTask,
10955
11161
  defaultTaskDurationDays,
@@ -11241,6 +11447,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
11241
11447
  getSuccessorChain,
11242
11448
  getTaskDuration,
11243
11449
  getTransitiveCascadeChain,
11450
+ getVisibleReorderPlan,
11244
11451
  getVisibleReorderPosition,
11245
11452
  getWeekBlocks,
11246
11453
  getWeekSpans,