gantt-lib 0.6.2 → 0.7.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.mjs CHANGED
@@ -142,6 +142,20 @@ var formatDateLabel = (date) => {
142
142
  const month = String(parsed.getUTCMonth() + 1).padStart(2, "0");
143
143
  return `${day}.${month}`;
144
144
  };
145
+ var normalizeTaskDates = (startDate, endDate) => {
146
+ const start = parseUTCDate(startDate);
147
+ const end = parseUTCDate(endDate);
148
+ if (end.getTime() < start.getTime()) {
149
+ return {
150
+ startDate: end.toISOString().split("T")[0],
151
+ endDate: start.toISOString().split("T")[0]
152
+ };
153
+ }
154
+ return {
155
+ startDate: start.toISOString().split("T")[0],
156
+ endDate: end.toISOString().split("T")[0]
157
+ };
158
+ };
145
159
 
146
160
  // src/utils/dependencyUtils.ts
147
161
  function buildAdjacencyList(tasks) {
@@ -196,33 +210,33 @@ function detectCycles(tasks) {
196
210
  return { hasCycle: false };
197
211
  }
198
212
  function computeLagFromDates(linkType, predStart, predEnd, succStart, succEnd) {
199
- const DAY_MS = 24 * 60 * 60 * 1e3;
213
+ const DAY_MS2 = 24 * 60 * 60 * 1e3;
200
214
  const pS = Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate());
201
215
  const pE = Date.UTC(predEnd.getUTCFullYear(), predEnd.getUTCMonth(), predEnd.getUTCDate());
202
216
  const sS = Date.UTC(succStart.getUTCFullYear(), succStart.getUTCMonth(), succStart.getUTCDate());
203
217
  const sE = Date.UTC(succEnd.getUTCFullYear(), succEnd.getUTCMonth(), succEnd.getUTCDate());
204
218
  switch (linkType) {
205
219
  case "FS":
206
- return Math.round((sS - pE) / DAY_MS) - 1;
220
+ return Math.round((sS - pE) / DAY_MS2) - 1;
207
221
  case "SS":
208
- return Math.round((sS - pS) / DAY_MS);
222
+ return Math.round((sS - pS) / DAY_MS2);
209
223
  case "FF":
210
- return Math.round((sE - pE) / DAY_MS);
224
+ return Math.round((sE - pE) / DAY_MS2);
211
225
  case "SF":
212
- return Math.round((sE - pS) / DAY_MS) + 1;
226
+ return Math.round((sE - pS) / DAY_MS2) + 1;
213
227
  }
214
228
  }
215
229
  function calculateSuccessorDate(predecessorStart, predecessorEnd, linkType, lag = 0) {
216
- const DAY_MS = 24 * 60 * 60 * 1e3;
230
+ const DAY_MS2 = 24 * 60 * 60 * 1e3;
217
231
  switch (linkType) {
218
232
  case "FS":
219
- return new Date(predecessorEnd.getTime() + (lag + 1) * DAY_MS);
233
+ return new Date(predecessorEnd.getTime() + (lag + 1) * DAY_MS2);
220
234
  case "SS":
221
- return new Date(predecessorStart.getTime() + lag * DAY_MS);
235
+ return new Date(predecessorStart.getTime() + lag * DAY_MS2);
222
236
  case "FF":
223
- return new Date(predecessorEnd.getTime() + lag * DAY_MS);
237
+ return new Date(predecessorEnd.getTime() + lag * DAY_MS2);
224
238
  case "SF":
225
- return new Date(predecessorStart.getTime() + (lag - 1) * DAY_MS);
239
+ return new Date(predecessorStart.getTime() + (lag - 1) * DAY_MS2);
226
240
  }
227
241
  }
228
242
  function validateDependencies(tasks) {
@@ -454,13 +468,13 @@ function computeParentProgress(parentId, tasks) {
454
468
  if (children.length === 0) {
455
469
  return 0;
456
470
  }
457
- const DAY_MS = 24 * 60 * 60 * 1e3;
471
+ const DAY_MS2 = 24 * 60 * 60 * 1e3;
458
472
  let totalWeight = 0;
459
473
  let weightedSum = 0;
460
474
  for (const child of children) {
461
475
  const start = new Date(child.startDate).getTime();
462
476
  const end = new Date(child.endDate).getTime();
463
- const duration = (end - start + DAY_MS) / DAY_MS;
477
+ const duration = (end - start + DAY_MS2) / DAY_MS2;
464
478
  const progress = child.progress ?? 0;
465
479
  totalWeight += duration;
466
480
  weightedSum += duration * progress;
@@ -522,7 +536,10 @@ function flattenHierarchy(tasks) {
522
536
  return result;
523
537
  }
524
538
  function normalizeHierarchyTasks(tasks) {
525
- const orderedTasks = flattenHierarchy(tasks).map((task) => ({ ...task }));
539
+ const orderedTasks = flattenHierarchy(tasks).map((task) => {
540
+ const { startDate, endDate } = normalizeTaskDates(task.startDate, task.endDate);
541
+ return { ...task, startDate, endDate };
542
+ });
526
543
  for (const task of [...orderedTasks].reverse()) {
527
544
  if (!isTaskParent(task.id, orderedTasks)) continue;
528
545
  const { startDate, endDate } = computeParentDates(task.id, orderedTasks);
@@ -1997,6 +2014,36 @@ var DependencyLines_default = DependencyLines;
1997
2014
  // src/components/TaskList/TaskList.tsx
1998
2015
  import React11, { useMemo as useMemo8, useCallback as useCallback5, useState as useState6, useEffect as useEffect5, useRef as useRef5 } from "react";
1999
2016
 
2017
+ // src/utils/taskListReorder.ts
2018
+ function getVisibleReorderPosition(orderedTasks, visibleTasks, movedTaskId, originVisibleIndex, dropVisibleIndex) {
2019
+ const originOrderedIndex = orderedTasks.findIndex((task) => task.id === movedTaskId);
2020
+ if (originOrderedIndex === -1) {
2021
+ return null;
2022
+ }
2023
+ const reorderedWithoutMoved = orderedTasks.filter((task) => task.id !== movedTaskId);
2024
+ const visibleWithoutMoved = visibleTasks.filter((task) => task.id !== movedTaskId);
2025
+ const visibleInsertIndex = originVisibleIndex < dropVisibleIndex ? dropVisibleIndex - 1 : dropVisibleIndex;
2026
+ if (visibleWithoutMoved.length === 0) {
2027
+ return { originOrderedIndex, insertIndex: 0 };
2028
+ }
2029
+ if (visibleInsertIndex <= 0) {
2030
+ return {
2031
+ originOrderedIndex,
2032
+ insertIndex: reorderedWithoutMoved.findIndex((task) => task.id === visibleWithoutMoved[0].id)
2033
+ };
2034
+ }
2035
+ if (visibleInsertIndex >= visibleWithoutMoved.length) {
2036
+ return {
2037
+ originOrderedIndex,
2038
+ insertIndex: reorderedWithoutMoved.length
2039
+ };
2040
+ }
2041
+ return {
2042
+ originOrderedIndex,
2043
+ insertIndex: reorderedWithoutMoved.findIndex((task) => task.id === visibleWithoutMoved[visibleInsertIndex].id)
2044
+ };
2045
+ }
2046
+
2000
2047
  // src/components/ui/Popover.tsx
2001
2048
  import * as RadixPopover from "@radix-ui/react-popover";
2002
2049
  import { jsx as jsx7 } from "react/jsx-runtime";
@@ -2352,6 +2399,16 @@ var LINK_TYPE_LABELS = {
2352
2399
 
2353
2400
  // src/components/TaskList/TaskListRow.tsx
2354
2401
  import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2402
+ var DAY_MS = 24 * 60 * 60 * 1e3;
2403
+ var getInclusiveDurationDays = (startDate, endDate) => {
2404
+ const start = parseUTCDate(startDate);
2405
+ const end = parseUTCDate(endDate);
2406
+ return Math.max(1, Math.round((end.getTime() - start.getTime()) / DAY_MS) + 1);
2407
+ };
2408
+ var getEndDateFromDuration = (startDate, durationDays) => {
2409
+ const start = parseUTCDate(startDate);
2410
+ return new Date(start.getTime() + (durationDays - 1) * DAY_MS).toISOString().split("T")[0];
2411
+ };
2355
2412
  var TrashIcon = () => /* @__PURE__ */ jsxs9("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2356
2413
  /* @__PURE__ */ jsx12("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
2357
2414
  /* @__PURE__ */ jsx12("path", { d: "M3 6h18" }),
@@ -2366,6 +2423,7 @@ var DragHandleIcon = () => /* @__PURE__ */ jsxs9("svg", { width: "10", height: "
2366
2423
  /* @__PURE__ */ jsx12("circle", { cx: "2", cy: "12", r: "1.5" }),
2367
2424
  /* @__PURE__ */ jsx12("circle", { cx: "8", cy: "12", r: "1.5" })
2368
2425
  ] });
2426
+ var ChevronRightIcon = () => /* @__PURE__ */ jsx12("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx12("path", { d: "m9 18 6-6-6-6" }) });
2369
2427
  var HierarchyButton = ({
2370
2428
  isChild,
2371
2429
  isParent,
@@ -2534,11 +2592,16 @@ var TaskListRow = React9.memo(
2534
2592
  const [editingName, setEditingName] = useState4(false);
2535
2593
  const [nameValue, setNameValue] = useState4("");
2536
2594
  const nameInputRef = useRef3(null);
2595
+ const [editingDuration, setEditingDuration] = useState4(false);
2596
+ const [durationValue, setDurationValue] = useState4(getInclusiveDurationDays(task.startDate, task.endDate));
2597
+ const durationInputRef = useRef3(null);
2537
2598
  const [editingProgress, setEditingProgress] = useState4(false);
2538
2599
  const [progressValue, setProgressValue] = useState4(0);
2539
2600
  const progressInputRef = useRef3(null);
2540
2601
  const [overflowOpen, setOverflowOpen] = useState4(false);
2541
- const confirmedRef = useRef3(false);
2602
+ const nameConfirmedRef = useRef3(false);
2603
+ const durationConfirmedRef = useRef3(false);
2604
+ const progressConfirmedRef = useRef3(false);
2542
2605
  const autoEditedForRef = useRef3(null);
2543
2606
  const editTriggerRef = useRef3("doubleclick");
2544
2607
  const [deletePending, setDeletePending] = useState4(false);
@@ -2593,7 +2656,7 @@ var TaskListRow = React9.memo(
2593
2656
  useEffect3(() => {
2594
2657
  if (editingTaskId === task.id && !disableTaskNameEditing && autoEditedForRef.current !== editingTaskId) {
2595
2658
  autoEditedForRef.current = editingTaskId;
2596
- confirmedRef.current = false;
2659
+ nameConfirmedRef.current = false;
2597
2660
  editTriggerRef.current = "autoedit";
2598
2661
  setNameValue(task.name);
2599
2662
  setEditingName(true);
@@ -2608,7 +2671,7 @@ var TaskListRow = React9.memo(
2608
2671
  const handleNameDoubleClick = useCallback4((e) => {
2609
2672
  if (disableTaskNameEditing) return;
2610
2673
  e.stopPropagation();
2611
- confirmedRef.current = false;
2674
+ nameConfirmedRef.current = false;
2612
2675
  editTriggerRef.current = "doubleclick";
2613
2676
  setNameValue(task.name);
2614
2677
  setEditingName(true);
@@ -2617,7 +2680,7 @@ var TaskListRow = React9.memo(
2617
2680
  if (editingProgress) return;
2618
2681
  if (!editingName && !disableTaskNameEditing && e.key === "F2") {
2619
2682
  e.preventDefault();
2620
- confirmedRef.current = false;
2683
+ nameConfirmedRef.current = false;
2621
2684
  editTriggerRef.current = "keypress";
2622
2685
  setNameValue(task.name);
2623
2686
  setEditingName(true);
@@ -2625,15 +2688,15 @@ var TaskListRow = React9.memo(
2625
2688
  }
2626
2689
  if (!editingName && !disableTaskNameEditing && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
2627
2690
  e.preventDefault();
2628
- confirmedRef.current = false;
2691
+ nameConfirmedRef.current = false;
2629
2692
  editTriggerRef.current = "keypress";
2630
2693
  setNameValue(e.key);
2631
2694
  setEditingName(true);
2632
2695
  }
2633
2696
  }, [editingName, disableTaskNameEditing, task.name]);
2634
2697
  const handleNameSave = useCallback4(() => {
2635
- if (confirmedRef.current) {
2636
- confirmedRef.current = false;
2698
+ if (nameConfirmedRef.current) {
2699
+ nameConfirmedRef.current = false;
2637
2700
  return;
2638
2701
  }
2639
2702
  if (nameValue.trim()) {
@@ -2646,7 +2709,7 @@ var TaskListRow = React9.memo(
2646
2709
  }, []);
2647
2710
  const handleNameKeyDown = useCallback4((e) => {
2648
2711
  if (e.key === "Enter") {
2649
- confirmedRef.current = true;
2712
+ nameConfirmedRef.current = true;
2650
2713
  if (nameValue.trim()) {
2651
2714
  onTaskChange?.({ ...task, name: nameValue.trim() });
2652
2715
  }
@@ -2655,15 +2718,54 @@ var TaskListRow = React9.memo(
2655
2718
  handleNameCancel();
2656
2719
  }
2657
2720
  }, [nameValue, task, onTaskChange, handleNameCancel]);
2721
+ const handleDurationClick = useCallback4((e) => {
2722
+ if (task.locked) return;
2723
+ e.stopPropagation();
2724
+ durationConfirmedRef.current = false;
2725
+ setDurationValue(getInclusiveDurationDays(task.startDate, task.endDate));
2726
+ setEditingDuration(true);
2727
+ }, [task.locked, task.startDate, task.endDate]);
2728
+ const applyDurationChange = useCallback4((nextDuration) => {
2729
+ const normalizedDuration = Math.max(1, Math.round(nextDuration) || 1);
2730
+ setDurationValue(normalizedDuration);
2731
+ }, []);
2732
+ const handleDurationSave = useCallback4(() => {
2733
+ if (durationConfirmedRef.current) {
2734
+ durationConfirmedRef.current = false;
2735
+ return;
2736
+ }
2737
+ const normalizedDuration = Math.max(1, Math.round(durationValue) || 1);
2738
+ onTaskChange?.({ ...task, endDate: getEndDateFromDuration(task.startDate, normalizedDuration) });
2739
+ setEditingDuration(false);
2740
+ }, [durationValue, task, onTaskChange]);
2741
+ const handleDurationCancel = useCallback4(() => {
2742
+ setDurationValue(getInclusiveDurationDays(task.startDate, task.endDate));
2743
+ setEditingDuration(false);
2744
+ }, [task.startDate, task.endDate]);
2745
+ const handleDurationAdjust = useCallback4((delta) => {
2746
+ applyDurationChange(durationValue + delta);
2747
+ }, [applyDurationChange, durationValue]);
2748
+ const handleDurationKeyDown = useCallback4((e) => {
2749
+ e.stopPropagation();
2750
+ if (e.key === "Enter") {
2751
+ durationConfirmedRef.current = true;
2752
+ const normalizedDuration = Math.max(1, Math.round(durationValue) || 1);
2753
+ onTaskChange?.({ ...task, endDate: getEndDateFromDuration(task.startDate, normalizedDuration) });
2754
+ setEditingDuration(false);
2755
+ } else if (e.key === "Escape") {
2756
+ handleDurationCancel();
2757
+ }
2758
+ }, [durationValue, task, onTaskChange, handleDurationCancel]);
2658
2759
  const handleProgressClick = useCallback4((e) => {
2760
+ if (task.locked) return;
2659
2761
  e.stopPropagation();
2660
- confirmedRef.current = false;
2762
+ progressConfirmedRef.current = false;
2661
2763
  setProgressValue(task.progress ?? 0);
2662
2764
  setEditingProgress(true);
2663
- }, [task.progress]);
2765
+ }, [task.progress, task.locked]);
2664
2766
  const handleProgressSave = useCallback4(() => {
2665
- if (confirmedRef.current) {
2666
- confirmedRef.current = false;
2767
+ if (progressConfirmedRef.current) {
2768
+ progressConfirmedRef.current = false;
2667
2769
  return;
2668
2770
  }
2669
2771
  const clampedValue = Math.max(0, Math.min(100, progressValue));
@@ -2673,10 +2775,13 @@ var TaskListRow = React9.memo(
2673
2775
  const handleProgressCancel = useCallback4(() => {
2674
2776
  setEditingProgress(false);
2675
2777
  }, []);
2778
+ const handleProgressAdjust = useCallback4((delta) => {
2779
+ setProgressValue((current) => Math.max(0, Math.min(100, current + delta)));
2780
+ }, []);
2676
2781
  const handleProgressKeyDown = useCallback4((e) => {
2677
2782
  e.stopPropagation();
2678
2783
  if (e.key === "Enter") {
2679
- confirmedRef.current = true;
2784
+ progressConfirmedRef.current = true;
2680
2785
  const clampedValue = Math.max(0, Math.min(100, progressValue));
2681
2786
  onTaskChange?.({ ...task, progress: clampedValue });
2682
2787
  setEditingProgress(false);
@@ -2690,6 +2795,15 @@ var TaskListRow = React9.memo(
2690
2795
  progressInputRef.current.select();
2691
2796
  }
2692
2797
  }, [editingProgress]);
2798
+ useEffect3(() => {
2799
+ setDurationValue(getInclusiveDurationDays(task.startDate, task.endDate));
2800
+ }, [task.startDate, task.endDate]);
2801
+ useEffect3(() => {
2802
+ if (editingDuration && durationInputRef.current) {
2803
+ durationInputRef.current.focus();
2804
+ durationInputRef.current.select();
2805
+ }
2806
+ }, [editingDuration]);
2693
2807
  const handleStartDateChange = useCallback4((newDateISO) => {
2694
2808
  if (!newDateISO) return;
2695
2809
  const origStart = parseUTCDate(task.startDate);
@@ -2697,7 +2811,11 @@ var TaskListRow = React9.memo(
2697
2811
  const durationMs = origEnd.getTime() - origStart.getTime();
2698
2812
  const newStart = /* @__PURE__ */ new Date(newDateISO + "T00:00:00Z");
2699
2813
  const newEnd = new Date(newStart.getTime() + durationMs);
2700
- onTaskChange?.({ ...task, startDate: newDateISO, endDate: newEnd.toISOString().split("T")[0] });
2814
+ const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(
2815
+ newDateISO,
2816
+ newEnd.toISOString().split("T")[0]
2817
+ );
2818
+ onTaskChange?.({ ...task, startDate: normalizedStart, endDate: normalizedEnd });
2701
2819
  }, [task, onTaskChange]);
2702
2820
  const handleEndDateChange = useCallback4((newDateISO) => {
2703
2821
  if (!newDateISO) return;
@@ -2706,7 +2824,11 @@ var TaskListRow = React9.memo(
2706
2824
  const durationMs = origEnd.getTime() - origStart.getTime();
2707
2825
  const newEnd = /* @__PURE__ */ new Date(newDateISO + "T00:00:00Z");
2708
2826
  const newStart = new Date(newEnd.getTime() - durationMs);
2709
- onTaskChange?.({ ...task, startDate: newStart.toISOString().split("T")[0], endDate: newDateISO });
2827
+ const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(
2828
+ newStart.toISOString().split("T")[0],
2829
+ newDateISO
2830
+ );
2831
+ onTaskChange?.({ ...task, startDate: normalizedStart, endDate: normalizedEnd });
2710
2832
  }, [task, onTaskChange]);
2711
2833
  const handleRowClickInternal = useCallback4(() => {
2712
2834
  onRowClick?.(task.id);
@@ -2754,7 +2876,7 @@ var TaskListRow = React9.memo(
2754
2876
  onChipSelect?.(null);
2755
2877
  }, [selectedChip, onRemoveDependency, onChipSelect]);
2756
2878
  const startDateISO = toISODate(task.startDate);
2757
- const endDateISO = toISODate(task.endDate);
2879
+ const endDateISO = editingDuration ? getEndDateFromDuration(task.startDate, durationValue) : toISODate(task.endDate);
2758
2880
  return /* @__PURE__ */ jsxs9(
2759
2881
  "div",
2760
2882
  {
@@ -2780,21 +2902,12 @@ var TaskListRow = React9.memo(
2780
2902
  },
2781
2903
  tabIndex: isSelected ? 0 : -1,
2782
2904
  children: [
2783
- /* @__PURE__ */ jsx12(
2905
+ /* @__PURE__ */ jsxs9(
2784
2906
  "div",
2785
2907
  {
2786
2908
  className: "gantt-tl-cell gantt-tl-cell-number",
2787
2909
  onClick: handleNumberClick,
2788
- children: isParent ? /* @__PURE__ */ jsx12(
2789
- "button",
2790
- {
2791
- type: "button",
2792
- className: "gantt-tl-collapse-btn",
2793
- onClick: handleToggleCollapse,
2794
- "aria-label": isCollapsed ? "\u0420\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u0442\u044C" : "\u0421\u0432\u0435\u0440\u043D\u0443\u0442\u044C",
2795
- children: isCollapsed ? "+" : "-"
2796
- }
2797
- ) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
2910
+ children: [
2798
2911
  /* @__PURE__ */ jsx12(
2799
2912
  "span",
2800
2913
  {
@@ -2810,10 +2923,20 @@ var TaskListRow = React9.memo(
2810
2923
  }
2811
2924
  ),
2812
2925
  /* @__PURE__ */ jsx12("span", { className: "gantt-tl-num-label", children: rowIndex + 1 })
2813
- ] })
2926
+ ]
2814
2927
  }
2815
2928
  ),
2816
2929
  /* @__PURE__ */ jsxs9("div", { className: "gantt-tl-cell gantt-tl-cell-name", children: [
2930
+ isParent && !editingName && /* @__PURE__ */ jsx12(
2931
+ "button",
2932
+ {
2933
+ type: "button",
2934
+ className: `gantt-tl-collapse-btn ${isCollapsed ? "gantt-tl-collapse-btn-collapsed" : ""}`,
2935
+ onClick: handleToggleCollapse,
2936
+ "aria-label": isCollapsed ? "Expand children" : "Collapse children",
2937
+ children: /* @__PURE__ */ jsx12(ChevronRightIcon, {})
2938
+ }
2939
+ ),
2817
2940
  editingName && /* @__PURE__ */ jsx12(
2818
2941
  Input,
2819
2942
  {
@@ -2823,7 +2946,7 @@ var TaskListRow = React9.memo(
2823
2946
  onChange: (e) => setNameValue(e.target.value),
2824
2947
  onBlur: handleNameSave,
2825
2948
  onKeyDown: handleNameKeyDown,
2826
- className: "gantt-tl-name-input",
2949
+ className: ["gantt-tl-name-input", isChild ? "gantt-tl-name-input-child" : ""].filter(Boolean).join(" "),
2827
2950
  onClick: (e) => e.stopPropagation()
2828
2951
  }
2829
2952
  ),
@@ -2831,7 +2954,13 @@ var TaskListRow = React9.memo(
2831
2954
  "button",
2832
2955
  {
2833
2956
  type: "button",
2834
- className: `gantt-tl-name-trigger ${disableTaskNameEditing ? "gantt-tl-name-locked" : ""}`,
2957
+ className: [
2958
+ "gantt-tl-name-trigger",
2959
+ disableTaskNameEditing ? "gantt-tl-name-locked" : "",
2960
+ isParent ? "gantt-tl-name-trigger-parent" : "",
2961
+ isChild ? "gantt-tl-name-trigger-child" : ""
2962
+ ].filter(Boolean).join(" "),
2963
+ title: task.name,
2835
2964
  onClick: handleNameClick,
2836
2965
  onDoubleClick: handleNameDoubleClick,
2837
2966
  style: editingName ? { visibility: "hidden", pointerEvents: "none" } : void 0,
@@ -2920,22 +3049,91 @@ var TaskListRow = React9.memo(
2920
3049
  disabled: task.locked
2921
3050
  }
2922
3051
  ) }),
3052
+ /* @__PURE__ */ jsxs9("div", { className: "gantt-tl-cell gantt-tl-cell-duration", onClick: handleDurationClick, children: [
3053
+ editingDuration && /* @__PURE__ */ jsxs9("div", { className: "gantt-tl-number-editor", onClick: (e) => e.stopPropagation(), children: [
3054
+ /* @__PURE__ */ jsx12(
3055
+ Input,
3056
+ {
3057
+ ref: durationInputRef,
3058
+ type: "number",
3059
+ min: 1,
3060
+ step: 1,
3061
+ value: durationValue,
3062
+ onChange: (e) => applyDurationChange(parseInt(e.target.value, 10) || 1),
3063
+ onBlur: handleDurationSave,
3064
+ onKeyDown: handleDurationKeyDown,
3065
+ className: "gantt-tl-number-input"
3066
+ }
3067
+ ),
3068
+ /* @__PURE__ */ jsxs9("div", { className: "gantt-tl-number-steppers", "aria-hidden": "true", children: [
3069
+ /* @__PURE__ */ jsx12(
3070
+ "button",
3071
+ {
3072
+ type: "button",
3073
+ className: "gantt-tl-number-stepper",
3074
+ tabIndex: -1,
3075
+ onMouseDown: (e) => e.preventDefault(),
3076
+ onClick: () => handleDurationAdjust(1),
3077
+ children: /* @__PURE__ */ jsx12("svg", { xmlns: "http://www.w3.org/2000/svg", width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx12("path", { d: "m18 15-6-6-6 6" }) })
3078
+ }
3079
+ ),
3080
+ /* @__PURE__ */ jsx12(
3081
+ "button",
3082
+ {
3083
+ type: "button",
3084
+ className: "gantt-tl-number-stepper",
3085
+ tabIndex: -1,
3086
+ onMouseDown: (e) => e.preventDefault(),
3087
+ onClick: () => handleDurationAdjust(-1),
3088
+ children: /* @__PURE__ */ jsx12("svg", { xmlns: "http://www.w3.org/2000/svg", width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) })
3089
+ }
3090
+ )
3091
+ ] })
3092
+ ] }),
3093
+ /* @__PURE__ */ jsx12("span", { style: editingDuration ? { visibility: "hidden", pointerEvents: "none" } : void 0, children: getInclusiveDurationDays(task.startDate, task.endDate) })
3094
+ ] }),
2923
3095
  /* @__PURE__ */ jsxs9("div", { className: "gantt-tl-cell gantt-tl-cell-progress", onClick: handleProgressClick, children: [
2924
- editingProgress && /* @__PURE__ */ jsx12(
2925
- Input,
2926
- {
2927
- ref: progressInputRef,
2928
- type: "number",
2929
- min: 0,
2930
- max: 100,
2931
- value: progressValue,
2932
- onChange: (e) => setProgressValue(parseInt(e.target.value) || 0),
2933
- onBlur: handleProgressSave,
2934
- onKeyDown: handleProgressKeyDown,
2935
- className: "gantt-tl-progress-input",
2936
- onClick: (e) => e.stopPropagation()
2937
- }
2938
- ),
3096
+ editingProgress && /* @__PURE__ */ jsxs9("div", { className: "gantt-tl-number-editor", onClick: (e) => e.stopPropagation(), children: [
3097
+ /* @__PURE__ */ jsx12(
3098
+ Input,
3099
+ {
3100
+ ref: progressInputRef,
3101
+ type: "number",
3102
+ min: 0,
3103
+ max: 100,
3104
+ step: 1,
3105
+ value: progressValue,
3106
+ onChange: (e) => setProgressValue(parseInt(e.target.value, 10) || 0),
3107
+ onBlur: handleProgressSave,
3108
+ onKeyDown: handleProgressKeyDown,
3109
+ className: "gantt-tl-number-input"
3110
+ }
3111
+ ),
3112
+ /* @__PURE__ */ jsxs9("div", { className: "gantt-tl-number-steppers", "aria-hidden": "true", children: [
3113
+ /* @__PURE__ */ jsx12(
3114
+ "button",
3115
+ {
3116
+ type: "button",
3117
+ className: "gantt-tl-number-stepper",
3118
+ tabIndex: -1,
3119
+ onMouseDown: (e) => e.preventDefault(),
3120
+ onClick: () => handleProgressAdjust(1),
3121
+ children: /* @__PURE__ */ jsx12("svg", { xmlns: "http://www.w3.org/2000/svg", width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx12("path", { d: "m18 15-6-6-6 6" }) })
3122
+ }
3123
+ ),
3124
+ /* @__PURE__ */ jsx12(
3125
+ "button",
3126
+ {
3127
+ type: "button",
3128
+ className: "gantt-tl-number-stepper",
3129
+ tabIndex: -1,
3130
+ onMouseDown: (e) => e.preventDefault(),
3131
+ onClick: () => handleProgressAdjust(-1),
3132
+ children: /* @__PURE__ */ jsx12("svg", { xmlns: "http://www.w3.org/2000/svg", width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) })
3133
+ }
3134
+ )
3135
+ ] })
3136
+ ] }),
2939
3137
  /* @__PURE__ */ jsx12("span", { style: editingProgress ? { visibility: "hidden", pointerEvents: "none" } : void 0, children: task.progress ? `${Math.round(task.progress)}%` : "0%" })
2940
3138
  ] }),
2941
3139
  /* @__PURE__ */ jsx12(
@@ -3092,15 +3290,17 @@ var NewTaskRow = ({ rowHeight, onConfirm, onCancel }) => {
3092
3290
  // src/components/TaskList/TaskList.tsx
3093
3291
  import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
3094
3292
  var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
3293
+ var MIN_TASK_LIST_WIDTH = 640;
3095
3294
  var TaskList = ({
3096
3295
  tasks,
3097
3296
  rowHeight,
3098
3297
  headerHeight,
3099
- taskListWidth = 542,
3298
+ taskListWidth = 640,
3100
3299
  onTaskChange,
3101
3300
  selectedTaskId,
3102
3301
  onTaskSelect,
3103
3302
  show = true,
3303
+ hasRightShadow = false,
3104
3304
  disableTaskNameEditing = false,
3105
3305
  disableDependencyEditing = false,
3106
3306
  onScrollToTask,
@@ -3239,11 +3439,13 @@ var TaskList = ({
3239
3439
  const [draggingIndex, setDraggingIndex] = useState6(null);
3240
3440
  const [dragOverIndex, setDragOverIndex] = useState6(null);
3241
3441
  const dragOriginIndexRef = useRef5(null);
3442
+ const dragTaskIdRef = useRef5(null);
3242
3443
  const handleDragStart = useCallback5((index, e) => {
3243
3444
  e.dataTransfer.effectAllowed = "move";
3244
3445
  setDraggingIndex(index);
3245
3446
  dragOriginIndexRef.current = index;
3246
- }, []);
3447
+ dragTaskIdRef.current = visibleTasks[index]?.id ?? null;
3448
+ }, [visibleTasks]);
3247
3449
  const handleDragOver = useCallback5((index, e) => {
3248
3450
  e.preventDefault();
3249
3451
  e.dataTransfer.dropEffect = "move";
@@ -3251,16 +3453,32 @@ var TaskList = ({
3251
3453
  }, []);
3252
3454
  const handleDrop = useCallback5((dropIndex, e) => {
3253
3455
  e.preventDefault();
3254
- const originIndex = dragOriginIndexRef.current;
3255
- if (originIndex === null || originIndex === dropIndex) {
3456
+ const originVisibleIndex = dragOriginIndexRef.current;
3457
+ const movedTaskId = dragTaskIdRef.current;
3458
+ if (originVisibleIndex === null || movedTaskId === null || originVisibleIndex === dropIndex) {
3459
+ setDraggingIndex(null);
3460
+ setDragOverIndex(null);
3461
+ dragOriginIndexRef.current = null;
3462
+ dragTaskIdRef.current = null;
3463
+ return;
3464
+ }
3465
+ const reorderPosition = getVisibleReorderPosition(
3466
+ orderedTasks,
3467
+ visibleTasks,
3468
+ movedTaskId,
3469
+ originVisibleIndex,
3470
+ dropIndex
3471
+ );
3472
+ if (!reorderPosition) {
3256
3473
  setDraggingIndex(null);
3257
3474
  setDragOverIndex(null);
3258
3475
  dragOriginIndexRef.current = null;
3476
+ dragTaskIdRef.current = null;
3259
3477
  return;
3260
3478
  }
3479
+ const { originOrderedIndex, insertIndex } = reorderPosition;
3261
3480
  const reordered = [...orderedTasks];
3262
- const [moved] = reordered.splice(originIndex, 1);
3263
- const insertIndex = dropIndex === visibleTasks.length ? visibleTasks.length - 1 : originIndex < dropIndex ? dropIndex - 1 : dropIndex;
3481
+ const [moved] = reordered.splice(originOrderedIndex, 1);
3264
3482
  const isChild = !!moved.parentId;
3265
3483
  const isParent = tasks.some((t) => t.parentId === moved.id);
3266
3484
  const taskType = isParent ? "PARENT" : isChild ? "CHILD" : "ROOT";
@@ -3270,10 +3488,11 @@ var TaskList = ({
3270
3488
  name: moved.name,
3271
3489
  type: taskType,
3272
3490
  parentId: moved.parentId,
3273
- originIndex,
3491
+ originIndex: originOrderedIndex,
3492
+ originVisibleIndex,
3274
3493
  dropIndex,
3275
3494
  insertIndex,
3276
- direction: originIndex < dropIndex ? "DOWN" : "UP"
3495
+ direction: originVisibleIndex < dropIndex ? "DOWN" : "UP"
3277
3496
  });
3278
3497
  console.log("[TASKS ARRAY LENGTH]", orderedTasks.length);
3279
3498
  let inferredParentId;
@@ -3360,11 +3579,13 @@ var TaskList = ({
3360
3579
  setDraggingIndex(null);
3361
3580
  setDragOverIndex(null);
3362
3581
  dragOriginIndexRef.current = null;
3363
- }, [orderedTasks, visibleTasks.length, onReorder, onTaskSelect]);
3582
+ dragTaskIdRef.current = null;
3583
+ }, [orderedTasks, visibleTasks, onReorder, onTaskSelect]);
3364
3584
  const handleDragEnd = useCallback5(() => {
3365
3585
  setDraggingIndex(null);
3366
3586
  setDragOverIndex(null);
3367
3587
  dragOriginIndexRef.current = null;
3588
+ dragTaskIdRef.current = null;
3368
3589
  }, []);
3369
3590
  const handleConfirmNewTask = useCallback5((name) => {
3370
3591
  const now = /* @__PURE__ */ new Date();
@@ -3388,18 +3609,20 @@ var TaskList = ({
3388
3609
  setIsCreating(false);
3389
3610
  }, [onAdd]);
3390
3611
  const handleCancelNewTask = useCallback5(() => setIsCreating(false), []);
3612
+ const effectiveTaskListWidth = Math.max(taskListWidth, MIN_TASK_LIST_WIDTH);
3391
3613
  return /* @__PURE__ */ jsx14(
3392
3614
  "div",
3393
3615
  {
3394
3616
  ref: overlayRef,
3395
- className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}`,
3396
- style: { width: `${taskListWidth}px` },
3617
+ className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}${hasRightShadow ? " gantt-tl-overlay-shadowed" : ""}`,
3618
+ style: { width: `${effectiveTaskListWidth}px`, minWidth: `${MIN_TASK_LIST_WIDTH}px` },
3397
3619
  children: /* @__PURE__ */ jsxs11("div", { className: "gantt-tl-table", children: [
3398
3620
  /* @__PURE__ */ jsxs11("div", { className: "gantt-tl-header", style: { height: `${headerHeight + 0.5}px` }, children: [
3399
3621
  /* @__PURE__ */ jsx14("div", { className: "gantt-tl-headerCell gantt-tl-cell-number", children: "\u2116" }),
3400
3622
  /* @__PURE__ */ jsx14("div", { className: "gantt-tl-headerCell gantt-tl-cell-name", children: "\u0418\u043C\u044F" }),
3401
3623
  /* @__PURE__ */ jsx14("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
3402
3624
  /* @__PURE__ */ jsx14("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435" }),
3625
+ /* @__PURE__ */ jsx14("div", { className: "gantt-tl-headerCell gantt-tl-cell-duration", children: "\u0414\u043D." }),
3403
3626
  /* @__PURE__ */ jsx14("div", { className: "gantt-tl-headerCell gantt-tl-cell-progress", children: "%" }),
3404
3627
  /* @__PURE__ */ jsxs11("div", { className: "gantt-tl-headerCell gantt-tl-cell-deps", style: { position: "relative" }, children: [
3405
3628
  /* @__PURE__ */ jsxs11(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
@@ -3525,7 +3748,7 @@ var GanttChart = forwardRef(({
3525
3748
  disableConstraints,
3526
3749
  onCascade,
3527
3750
  showTaskList = false,
3528
- taskListWidth = 520,
3751
+ taskListWidth = 660,
3529
3752
  disableTaskNameEditing = false,
3530
3753
  disableDependencyEditing = false,
3531
3754
  highlightExpiredTasks = false,
@@ -3537,6 +3760,7 @@ var GanttChart = forwardRef(({
3537
3760
  }, ref) => {
3538
3761
  const scrollContainerRef = useRef6(null);
3539
3762
  const [selectedTaskId, setSelectedTaskId] = useState7(null);
3763
+ const [taskListHasRightShadow, setTaskListHasRightShadow] = useState7(false);
3540
3764
  const [selectedChip, setSelectedChip] = useState7(null);
3541
3765
  const [collapsedParentIds, setCollapsedParentIds] = useState7(/* @__PURE__ */ new Set());
3542
3766
  const [editingTaskId, setEditingTaskId] = useState7(null);
@@ -3583,6 +3807,18 @@ var GanttChart = forwardRef(({
3583
3807
  const scrollLeft = Math.round(todayOffset - containerWidth / 2 + dayWidth / 2);
3584
3808
  container.scrollLeft = Math.max(0, scrollLeft);
3585
3809
  }, []);
3810
+ useEffect6(() => {
3811
+ const container = scrollContainerRef.current;
3812
+ if (!container) return;
3813
+ const updateShadow = () => {
3814
+ setTaskListHasRightShadow(container.scrollLeft > 0);
3815
+ };
3816
+ updateShadow();
3817
+ container.addEventListener("scroll", updateShadow, { passive: true });
3818
+ return () => {
3819
+ container.removeEventListener("scroll", updateShadow);
3820
+ };
3821
+ }, []);
3586
3822
  const scrollToToday = useCallback6(() => {
3587
3823
  const container = scrollContainerRef.current;
3588
3824
  if (!container || dateRange.length === 0) return;
@@ -3986,6 +4222,7 @@ var GanttChart = forwardRef(({
3986
4222
  selectedTaskId: selectedTaskId ?? void 0,
3987
4223
  onTaskSelect: handleTaskSelect,
3988
4224
  show: showTaskList,
4225
+ hasRightShadow: taskListHasRightShadow,
3989
4226
  disableTaskNameEditing,
3990
4227
  disableDependencyEditing,
3991
4228
  onScrollToTask: scrollToTask,
@@ -4148,10 +4385,12 @@ export {
4148
4385
  getMultiMonthDays,
4149
4386
  getSuccessorChain,
4150
4387
  getTransitiveCascadeChain,
4388
+ getVisibleReorderPosition,
4151
4389
  isTaskParent,
4152
4390
  isToday,
4153
4391
  isWeekend,
4154
4392
  normalizeHierarchyTasks,
4393
+ normalizeTaskDates,
4155
4394
  parseUTCDate,
4156
4395
  pixelsToDate,
4157
4396
  recalculateIncomingLags,