gantt-lib 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -78,7 +78,7 @@ __export(index_exports, {
78
78
  module.exports = __toCommonJS(index_exports);
79
79
 
80
80
  // src/components/GanttChart/GanttChart.tsx
81
- var import_react12 = require("react");
81
+ var import_react13 = require("react");
82
82
 
83
83
  // src/utils/dateUtils.ts
84
84
  var parseUTCDate = (date) => {
@@ -119,9 +119,9 @@ var getDayOffset = (date, monthStart) => {
119
119
  var isToday = (date) => {
120
120
  const now = /* @__PURE__ */ new Date();
121
121
  const today = new Date(Date.UTC(
122
- now.getUTCFullYear(),
123
- now.getUTCMonth(),
124
- now.getUTCDate()
122
+ now.getFullYear(),
123
+ now.getMonth(),
124
+ now.getDate()
125
125
  ));
126
126
  const compareDate = new Date(Date.UTC(
127
127
  date.getUTCFullYear(),
@@ -600,7 +600,7 @@ var calculateGridLines = (dateRange, dayWidth) => {
600
600
  for (let i = 0; i < dateRange.length; i++) {
601
601
  const date = dateRange[i];
602
602
  const x = Math.round(i * dayWidth);
603
- const isMonthStart = date.getUTCDate() === 1;
603
+ const isMonthStart = i === 0 ? false : date.getUTCDate() === 1;
604
604
  const isWeekStart = date.getUTCDay() === 1;
605
605
  lines.push({ x, isMonthStart, isWeekStart });
606
606
  }
@@ -1179,7 +1179,7 @@ var TaskRow = import_react3.default.memo(
1179
1179
  const isExpired = (0, import_react3.useMemo)(() => {
1180
1180
  if (!highlightExpiredTasks) return false;
1181
1181
  const now = /* @__PURE__ */ new Date();
1182
- const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
1182
+ const today = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
1183
1183
  const taskStart = parseUTCDate(task.startDate);
1184
1184
  const taskEnd = parseUTCDate(task.endDate);
1185
1185
  const actualProgress = task.progress ?? 0;
@@ -1399,7 +1399,7 @@ var TodayIndicator_default = TodayIndicator;
1399
1399
  var import_react5 = __toESM(require("react"));
1400
1400
  var import_jsx_runtime4 = require("react/jsx-runtime");
1401
1401
  var arePropsEqual2 = (prevProps, nextProps) => {
1402
- return prevProps.dayWidth === nextProps.dayWidth && prevProps.dateRange.length === nextProps.dateRange.length && prevProps.totalHeight !== nextProps.totalHeight;
1402
+ return prevProps.dayWidth === nextProps.dayWidth && prevProps.dateRange.length === nextProps.dateRange.length && prevProps.totalHeight === nextProps.totalHeight;
1403
1403
  };
1404
1404
  var GridBackground = import_react5.default.memo(
1405
1405
  ({ dateRange, dayWidth, totalHeight }) => {
@@ -1686,7 +1686,7 @@ DependencyLines.displayName = "DependencyLines";
1686
1686
  var DependencyLines_default = DependencyLines;
1687
1687
 
1688
1688
  // src/components/TaskList/TaskList.tsx
1689
- var import_react11 = __toESM(require("react"));
1689
+ var import_react12 = __toESM(require("react"));
1690
1690
 
1691
1691
  // src/components/ui/Popover.tsx
1692
1692
  var RadixPopover = __toESM(require("@radix-ui/react-popover"));
@@ -1855,7 +1855,73 @@ var Calendar = ({
1855
1855
  () => months.map(renderMonth),
1856
1856
  [months, renderMonth]
1857
1857
  );
1858
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: scrollRef, className: "gantt-cal-container", children: renderedMonths });
1858
+ const handleDayShift = (0, import_react8.useCallback)(
1859
+ (deltaDays) => {
1860
+ if (!onSelect || disabled) return;
1861
+ const baseDate = selected ?? /* @__PURE__ */ new Date();
1862
+ onSelect((0, import_date_fns2.addDays)(baseDate, deltaDays));
1863
+ },
1864
+ [onSelect, selected, disabled]
1865
+ );
1866
+ const handleToday = (0, import_react8.useCallback)(() => {
1867
+ if (!onSelect || disabled) return;
1868
+ onSelect(/* @__PURE__ */ new Date());
1869
+ }, [onSelect, disabled]);
1870
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref: scrollRef, className: "gantt-cal-container", children: [
1871
+ renderedMonths,
1872
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "gantt-cal-nav", children: [
1873
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1874
+ "button",
1875
+ {
1876
+ type: "button",
1877
+ className: "gantt-btn gantt-btn-sm",
1878
+ onClick: () => handleDayShift(-7),
1879
+ disabled,
1880
+ children: "-7"
1881
+ }
1882
+ ),
1883
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1884
+ "button",
1885
+ {
1886
+ type: "button",
1887
+ className: "gantt-btn gantt-btn-sm",
1888
+ onClick: () => handleDayShift(-1),
1889
+ disabled,
1890
+ children: "-1"
1891
+ }
1892
+ ),
1893
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1894
+ "button",
1895
+ {
1896
+ type: "button",
1897
+ className: "gantt-btn gantt-btn-sm",
1898
+ onClick: handleToday,
1899
+ disabled,
1900
+ children: "\u0421\u0435\u0433\u043E\u0434\u043D\u044F"
1901
+ }
1902
+ ),
1903
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1904
+ "button",
1905
+ {
1906
+ type: "button",
1907
+ className: "gantt-btn gantt-btn-sm",
1908
+ onClick: () => handleDayShift(1),
1909
+ disabled,
1910
+ children: "+1"
1911
+ }
1912
+ ),
1913
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1914
+ "button",
1915
+ {
1916
+ type: "button",
1917
+ className: "gantt-btn gantt-btn-sm",
1918
+ onClick: () => handleDayShift(7),
1919
+ disabled,
1920
+ children: "+7"
1921
+ }
1922
+ )
1923
+ ] })
1924
+ ] });
1859
1925
  };
1860
1926
  Calendar.displayName = "Calendar";
1861
1927
 
@@ -1963,6 +2029,7 @@ var TrashIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { xm
1963
2029
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3 6h18" }),
1964
2030
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
1965
2031
  ] });
2032
+ var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 5v14M5 12h14" }) });
1966
2033
  function formatDepDescription(type, lag) {
1967
2034
  const effectiveLag = lag ?? 0;
1968
2035
  if (type === "FS") {
@@ -2004,12 +2071,19 @@ var DepChip = ({
2004
2071
  const handleClick = (e) => {
2005
2072
  e.stopPropagation();
2006
2073
  if (disableDependencyEditing) return;
2007
- onChipSelect?.(isSelected ? null : { successorId: taskId, predecessorId: dep.taskId, linkType: dep.type });
2008
- if (!isSelected) {
2009
- onRowClick?.(taskId);
2010
- onScrollToTask?.(taskId);
2074
+ if (isSelected) {
2075
+ e.preventDefault();
2076
+ onChipSelect?.(null);
2077
+ return;
2011
2078
  }
2079
+ onChipSelect?.({ successorId: taskId, predecessorId: dep.taskId, linkType: dep.type });
2080
+ onScrollToTask?.(dep.taskId);
2012
2081
  };
2082
+ const handleOpenChange = (0, import_react10.useCallback)((open) => {
2083
+ if (!open) {
2084
+ onChipSelect?.(null);
2085
+ }
2086
+ }, [onChipSelect]);
2013
2087
  const handleTrashClick = (e) => {
2014
2088
  e.stopPropagation();
2015
2089
  onRemoveDependency?.(taskId, dep.taskId, dep.type);
@@ -2018,50 +2092,26 @@ var DepChip = ({
2018
2092
  const Icon = LINK_TYPE_ICONS[dep.type];
2019
2093
  const depPrefix = formatDepDescription(dep.type, lag);
2020
2094
  const depName = predecessorName ?? dep.taskId;
2021
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: isSelected, onOpenChange: (open) => {
2022
- }, children: [
2023
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "gantt-tl-dep-chip-wrapper", children: [
2024
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2025
- "span",
2026
- {
2027
- className: `gantt-tl-dep-chip${isSelected ? " gantt-tl-dep-chip-selected" : ""}`,
2028
- onClick: handleClick,
2029
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2030
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, {}),
2031
- lag != null && lag !== 0 ? lag > 0 ? `+${lag}` : `${lag}` : ""
2032
- ] })
2033
- }
2034
- ),
2035
- !disableDependencyEditing && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2036
- "button",
2037
- {
2038
- type: "button",
2039
- className: "gantt-tl-dep-chip-trash",
2040
- "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
2041
- onClick: handleTrashClick,
2042
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TrashIcon, {})
2043
- }
2044
- )
2045
- ] }) }),
2046
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2047
- PopoverContent,
2095
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "gantt-tl-dep-chip-wrapper", children: [
2096
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2097
+ "span",
2048
2098
  {
2049
- portal: true,
2050
- side: "bottom",
2051
- align: "start",
2052
- className: "gantt-tl-dep-info-popover",
2053
- onInteractOutside: (event) => {
2054
- const target = event.target;
2055
- if (target?.closest?.(".gantt-tl-dep-chip") || target?.closest?.(".gantt-tl-dep-delete-label") || target?.closest?.(".gantt-tl-dep-chip-trash")) {
2056
- event.preventDefault();
2057
- } else {
2058
- onChipSelectClear();
2059
- }
2060
- },
2061
- children: [
2062
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-info-prefix", children: depPrefix }),
2063
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-info-name", children: depName })
2064
- ]
2099
+ className: `gantt-tl-dep-chip${isSelected ? " gantt-tl-dep-chip-selected" : ""}`,
2100
+ onClick: handleClick,
2101
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2102
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, {}),
2103
+ lag != null && lag !== 0 ? lag > 0 ? `+${lag}` : `${lag}` : ""
2104
+ ] })
2105
+ }
2106
+ ),
2107
+ !disableDependencyEditing && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2108
+ "button",
2109
+ {
2110
+ type: "button",
2111
+ className: "gantt-tl-dep-chip-trash",
2112
+ "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
2113
+ onClick: handleTrashClick,
2114
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TrashIcon, {})
2065
2115
  }
2066
2116
  )
2067
2117
  ] });
@@ -2089,12 +2139,19 @@ var TaskListRow = import_react10.default.memo(
2089
2139
  onRemoveDependency,
2090
2140
  selectedChip,
2091
2141
  onChipSelect,
2092
- onScrollToTask
2142
+ onScrollToTask,
2143
+ onDelete,
2144
+ onAdd,
2145
+ onInsertAfter,
2146
+ editingTaskId
2093
2147
  }) => {
2094
2148
  const [editingName, setEditingName] = (0, import_react10.useState)(false);
2095
2149
  const [nameValue, setNameValue] = (0, import_react10.useState)("");
2096
2150
  const nameInputRef = (0, import_react10.useRef)(null);
2097
2151
  const [overflowOpen, setOverflowOpen] = (0, import_react10.useState)(false);
2152
+ const confirmedRef = (0, import_react10.useRef)(false);
2153
+ const autoEditedForRef = (0, import_react10.useRef)(null);
2154
+ const editTriggerRef = (0, import_react10.useRef)("doubleclick");
2098
2155
  const isSelected = selectedTaskId === task.id;
2099
2156
  const isPicking = selectingPredecessorFor != null;
2100
2157
  const isSourceRow = isPicking && selectingPredecessorFor === task.id;
@@ -2118,15 +2175,59 @@ var TaskListRow = import_react10.default.memo(
2118
2175
  (0, import_react10.useEffect)(() => {
2119
2176
  if (editingName && nameInputRef.current) {
2120
2177
  nameInputRef.current.focus();
2178
+ if (editTriggerRef.current === "keypress") {
2179
+ const len = nameInputRef.current.value.length;
2180
+ nameInputRef.current.setSelectionRange(len, len);
2181
+ } else {
2182
+ nameInputRef.current.select();
2183
+ }
2121
2184
  }
2122
2185
  }, [editingName]);
2186
+ (0, import_react10.useEffect)(() => {
2187
+ if (editingTaskId === task.id && !disableTaskNameEditing && autoEditedForRef.current !== editingTaskId) {
2188
+ autoEditedForRef.current = editingTaskId;
2189
+ confirmedRef.current = false;
2190
+ editTriggerRef.current = "autoedit";
2191
+ setNameValue(task.name);
2192
+ setEditingName(true);
2193
+ }
2194
+ }, [editingTaskId, task.id, disableTaskNameEditing]);
2123
2195
  const handleNameClick = (0, import_react10.useCallback)((e) => {
2124
2196
  if (disableTaskNameEditing) return;
2125
2197
  e.stopPropagation();
2198
+ onRowClick?.(task.id);
2199
+ onScrollToTask?.(task.id);
2200
+ }, [task.id, disableTaskNameEditing, onRowClick, onScrollToTask]);
2201
+ const handleNameDoubleClick = (0, import_react10.useCallback)((e) => {
2202
+ if (disableTaskNameEditing) return;
2203
+ e.stopPropagation();
2204
+ confirmedRef.current = false;
2205
+ editTriggerRef.current = "doubleclick";
2126
2206
  setNameValue(task.name);
2127
2207
  setEditingName(true);
2128
2208
  }, [task.name, disableTaskNameEditing]);
2209
+ const handleRowKeyDown = (0, import_react10.useCallback)((e) => {
2210
+ if (!editingName && !disableTaskNameEditing && e.key === "F2") {
2211
+ e.preventDefault();
2212
+ confirmedRef.current = false;
2213
+ editTriggerRef.current = "keypress";
2214
+ setNameValue(task.name);
2215
+ setEditingName(true);
2216
+ return;
2217
+ }
2218
+ if (!editingName && !disableTaskNameEditing && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
2219
+ e.preventDefault();
2220
+ confirmedRef.current = false;
2221
+ editTriggerRef.current = "keypress";
2222
+ setNameValue(e.key);
2223
+ setEditingName(true);
2224
+ }
2225
+ }, [editingName, disableTaskNameEditing, task.name]);
2129
2226
  const handleNameSave = (0, import_react10.useCallback)(() => {
2227
+ if (confirmedRef.current) {
2228
+ confirmedRef.current = false;
2229
+ return;
2230
+ }
2130
2231
  if (nameValue.trim()) {
2131
2232
  onTaskChange?.({ ...task, name: nameValue.trim() });
2132
2233
  }
@@ -2136,9 +2237,16 @@ var TaskListRow = import_react10.default.memo(
2136
2237
  setEditingName(false);
2137
2238
  }, []);
2138
2239
  const handleNameKeyDown = (0, import_react10.useCallback)((e) => {
2139
- if (e.key === "Enter") handleNameSave();
2140
- else if (e.key === "Escape") handleNameCancel();
2141
- }, [handleNameSave, handleNameCancel]);
2240
+ if (e.key === "Enter") {
2241
+ confirmedRef.current = true;
2242
+ if (nameValue.trim()) {
2243
+ onTaskChange?.({ ...task, name: nameValue.trim() });
2244
+ }
2245
+ setEditingName(false);
2246
+ } else if (e.key === "Escape") {
2247
+ handleNameCancel();
2248
+ }
2249
+ }, [nameValue, task, onTaskChange, handleNameCancel]);
2142
2250
  const handleStartDateChange = (0, import_react10.useCallback)((newDateISO) => {
2143
2251
  if (!newDateISO) return;
2144
2252
  const origStart = parseUTCDate(task.startDate);
@@ -2163,8 +2271,7 @@ var TaskListRow = import_react10.default.memo(
2163
2271
  const handleNumberClick = (0, import_react10.useCallback)((e) => {
2164
2272
  e.stopPropagation();
2165
2273
  onRowClick?.(task.id);
2166
- onScrollToTask?.(task.id);
2167
- }, [task.id, onRowClick, onScrollToTask]);
2274
+ }, [task.id, onRowClick]);
2168
2275
  const handleAddClick = (0, import_react10.useCallback)((e) => {
2169
2276
  e.stopPropagation();
2170
2277
  onSetSelectingPredecessorFor?.(task.id);
@@ -2175,6 +2282,10 @@ var TaskListRow = import_react10.default.memo(
2175
2282
  if (!selectingPredecessorFor || !activeLinkType) return;
2176
2283
  onAddDependency?.(task.id, selectingPredecessorFor, activeLinkType);
2177
2284
  }, [isPicking, isSourceRow, selectingPredecessorFor, task.id, activeLinkType, onAddDependency]);
2285
+ const handleCancelPicking = (0, import_react10.useCallback)((e) => {
2286
+ e.stopPropagation();
2287
+ onSetSelectingPredecessorFor?.(null);
2288
+ }, [onSetSelectingPredecessorFor]);
2178
2289
  const isSelectedPredecessor = selectedChip != null && selectedChip.predecessorId === task.id;
2179
2290
  const handleDeleteSelected = (0, import_react10.useCallback)((e) => {
2180
2291
  e.stopPropagation();
@@ -2193,23 +2304,17 @@ var TaskListRow = import_react10.default.memo(
2193
2304
  isPicking && !isSourceRow ? "gantt-tl-row-picking" : "",
2194
2305
  isSourceRow ? "gantt-tl-row-picking-self" : ""
2195
2306
  ].filter(Boolean).join(" "),
2196
- style: { minHeight: `${rowHeight}px` },
2307
+ style: { minHeight: `${rowHeight}px`, position: "relative" },
2197
2308
  onClick: handleRowClickInternal,
2309
+ onKeyDown: handleRowKeyDown,
2310
+ tabIndex: isSelected ? 0 : -1,
2198
2311
  children: [
2199
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2312
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2200
2313
  "div",
2201
2314
  {
2202
2315
  className: "gantt-tl-cell gantt-tl-cell-number",
2203
2316
  onClick: handleNumberClick,
2204
- title: "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043A \u0440\u0430\u0431\u043E\u0442\u0435",
2205
- children: [
2206
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-num-label", children: rowIndex + 1 }),
2207
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { className: "gantt-tl-num-icon", 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: [
2208
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M17 12H3" }),
2209
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m11 18 6-6-6-6" }),
2210
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M21 5v14" })
2211
- ] })
2212
- ]
2317
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-num-label", children: rowIndex + 1 })
2213
2318
  }
2214
2319
  ),
2215
2320
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-cell gantt-tl-cell-name", children: [
@@ -2232,10 +2337,56 @@ var TaskListRow = import_react10.default.memo(
2232
2337
  type: "button",
2233
2338
  className: `gantt-tl-name-trigger ${disableTaskNameEditing ? "gantt-tl-name-locked" : ""}`,
2234
2339
  onClick: handleNameClick,
2340
+ onDoubleClick: handleNameDoubleClick,
2235
2341
  style: editingName ? { visibility: "hidden", pointerEvents: "none" } : void 0,
2236
2342
  children: task.name
2237
2343
  }
2238
- )
2344
+ ),
2345
+ !editingName && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-name-actions", children: [
2346
+ onInsertAfter && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2347
+ "button",
2348
+ {
2349
+ type: "button",
2350
+ className: "gantt-tl-name-action-btn gantt-tl-action-insert",
2351
+ onClick: (e) => {
2352
+ e.stopPropagation();
2353
+ const now = /* @__PURE__ */ new Date();
2354
+ const todayISO = new Date(Date.UTC(
2355
+ now.getUTCFullYear(),
2356
+ now.getUTCMonth(),
2357
+ now.getUTCDate()
2358
+ )).toISOString().split("T")[0];
2359
+ const endISO = new Date(Date.UTC(
2360
+ now.getUTCFullYear(),
2361
+ now.getUTCMonth(),
2362
+ now.getUTCDate() + 7
2363
+ )).toISOString().split("T")[0];
2364
+ const newTask = {
2365
+ id: crypto.randomUUID(),
2366
+ name: "\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430",
2367
+ startDate: todayISO,
2368
+ endDate: endISO
2369
+ };
2370
+ onInsertAfter(task.id, newTask);
2371
+ },
2372
+ "aria-label": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u043F\u043E\u0441\u043B\u0435 \u044D\u0442\u043E\u0439",
2373
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlusIcon, {})
2374
+ }
2375
+ ),
2376
+ onDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2377
+ "button",
2378
+ {
2379
+ type: "button",
2380
+ className: "gantt-tl-name-action-btn gantt-tl-action-delete",
2381
+ onClick: (e) => {
2382
+ e.stopPropagation();
2383
+ onDelete(task.id);
2384
+ },
2385
+ "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443",
2386
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TrashIcon, {})
2387
+ }
2388
+ )
2389
+ ] })
2239
2390
  ] }),
2240
2391
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-cell gantt-tl-cell-date", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2241
2392
  DatePicker,
@@ -2261,7 +2412,7 @@ var TaskListRow = import_react10.default.memo(
2261
2412
  "div",
2262
2413
  {
2263
2414
  className: "gantt-tl-cell gantt-tl-cell-deps",
2264
- onClick: isPicking && !isSourceRow ? handlePredecessorPick : void 0,
2415
+ onClick: isSourceRow ? handleCancelPicking : isPicking ? handlePredecessorPick : void 0,
2265
2416
  children: isSourceRow ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-source-hint", children: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0443" }) : isSelectedPredecessor && !disableDependencyEditing ? (
2266
2417
  /* Full-replacement: "Зависит от [name]" → hover → "Удалить" */
2267
2418
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
@@ -2338,7 +2489,7 @@ var TaskListRow = import_react10.default.memo(
2338
2489
  "button",
2339
2490
  {
2340
2491
  type: "button",
2341
- className: "gantt-tl-dep-add",
2492
+ className: `gantt-tl-dep-add gantt-tl-dep-add-hover${selectedChip ? " gantt-tl-dep-add-hidden" : ""}`,
2342
2493
  onClick: handleAddClick,
2343
2494
  "aria-label": "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
2344
2495
  children: "+"
@@ -2354,14 +2505,68 @@ var TaskListRow = import_react10.default.memo(
2354
2505
  );
2355
2506
  TaskListRow.displayName = "TaskListRow";
2356
2507
 
2357
- // src/components/TaskList/TaskList.tsx
2508
+ // src/components/TaskList/NewTaskRow.tsx
2509
+ var import_react11 = require("react");
2358
2510
  var import_jsx_runtime13 = require("react/jsx-runtime");
2511
+ var NewTaskRow = ({ rowHeight, onConfirm, onCancel }) => {
2512
+ const [nameValue, setNameValue] = (0, import_react11.useState)("");
2513
+ const inputRef = (0, import_react11.useRef)(null);
2514
+ const confirmedRef = (0, import_react11.useRef)(false);
2515
+ (0, import_react11.useEffect)(() => {
2516
+ if (inputRef.current) {
2517
+ inputRef.current.focus();
2518
+ inputRef.current.select();
2519
+ }
2520
+ }, []);
2521
+ const handleKeyDown = (e) => {
2522
+ if (e.key === "Enter") {
2523
+ if (nameValue.trim()) {
2524
+ confirmedRef.current = true;
2525
+ onConfirm(nameValue.trim());
2526
+ } else {
2527
+ onCancel();
2528
+ }
2529
+ } else if (e.key === "Escape") {
2530
+ onCancel();
2531
+ }
2532
+ };
2533
+ const handleBlur = () => {
2534
+ if (confirmedRef.current) return;
2535
+ if (nameValue.trim()) {
2536
+ confirmedRef.current = true;
2537
+ onConfirm(nameValue.trim());
2538
+ } else {
2539
+ onCancel();
2540
+ }
2541
+ };
2542
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-row gantt-tl-row-new", style: { minHeight: `${rowHeight}px` }, children: [
2543
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-cell gantt-tl-cell-number" }),
2544
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-cell gantt-tl-cell-name gantt-tl-cell-new-name", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2545
+ Input,
2546
+ {
2547
+ ref: inputRef,
2548
+ value: nameValue,
2549
+ onChange: (e) => setNameValue(e.target.value),
2550
+ onKeyDown: handleKeyDown,
2551
+ onBlur: handleBlur,
2552
+ placeholder: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435",
2553
+ className: "gantt-tl-name-input"
2554
+ }
2555
+ ) }),
2556
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-cell" }),
2557
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-cell" }),
2558
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-cell" })
2559
+ ] });
2560
+ };
2561
+
2562
+ // src/components/TaskList/TaskList.tsx
2563
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2359
2564
  var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
2360
2565
  var TaskList = ({
2361
2566
  tasks,
2362
2567
  rowHeight,
2363
2568
  headerHeight,
2364
- taskListWidth = 520,
2569
+ taskListWidth = 472,
2365
2570
  onTaskChange,
2366
2571
  selectedTaskId,
2367
2572
  onTaskSelect,
@@ -2369,26 +2574,30 @@ var TaskList = ({
2369
2574
  disableTaskNameEditing = false,
2370
2575
  disableDependencyEditing = false,
2371
2576
  onScrollToTask,
2372
- onSelectedChipChange
2577
+ onSelectedChipChange,
2578
+ onAdd,
2579
+ onDelete,
2580
+ onInsertAfter,
2581
+ editingTaskId: propEditingTaskId
2373
2582
  }) => {
2374
- const totalHeight = (0, import_react11.useMemo)(
2583
+ const totalHeight = (0, import_react12.useMemo)(
2375
2584
  () => tasks.length * rowHeight,
2376
2585
  [tasks.length, rowHeight]
2377
2586
  );
2378
- const handleRowClick = (0, import_react11.useCallback)((taskId) => {
2587
+ const handleRowClick = (0, import_react12.useCallback)((taskId) => {
2379
2588
  onTaskSelect?.(taskId);
2380
2589
  }, [onTaskSelect]);
2381
- const [activeLinkType, setActiveLinkType] = (0, import_react11.useState)("FS");
2382
- const [selectingPredecessorFor, setSelectingPredecessorFor] = (0, import_react11.useState)(null);
2383
- const [typeMenuOpen, setTypeMenuOpen] = (0, import_react11.useState)(false);
2384
- const [cycleError, setCycleError] = (0, import_react11.useState)(false);
2385
- const overlayRef = (0, import_react11.useRef)(null);
2386
- const [selectedChip, setSelectedChip] = (0, import_react11.useState)(null);
2387
- const handleChipSelect = (0, import_react11.useCallback)((chip) => {
2590
+ const [activeLinkType, setActiveLinkType] = (0, import_react12.useState)("FS");
2591
+ const [selectingPredecessorFor, setSelectingPredecessorFor] = (0, import_react12.useState)(null);
2592
+ const [typeMenuOpen, setTypeMenuOpen] = (0, import_react12.useState)(false);
2593
+ const [cycleError, setCycleError] = (0, import_react12.useState)(false);
2594
+ const overlayRef = (0, import_react12.useRef)(null);
2595
+ const [selectedChip, setSelectedChip] = (0, import_react12.useState)(null);
2596
+ const handleChipSelect = (0, import_react12.useCallback)((chip) => {
2388
2597
  setSelectedChip(chip);
2389
2598
  onSelectedChipChange?.(chip);
2390
2599
  }, [onSelectedChipChange]);
2391
- (0, import_react11.useEffect)(() => {
2600
+ (0, import_react12.useEffect)(() => {
2392
2601
  if (!selectingPredecessorFor && !selectedChip) return;
2393
2602
  const handleKeyDown = (e) => {
2394
2603
  if (e.key === "Escape") {
@@ -2412,7 +2621,7 @@ var TaskList = ({
2412
2621
  document.removeEventListener("mousedown", handleMouseDown, true);
2413
2622
  };
2414
2623
  }, [selectingPredecessorFor, selectedChip, onSelectedChipChange]);
2415
- const handleAddDependency = (0, import_react11.useCallback)((successorTaskId, predecessorTaskId, linkType) => {
2624
+ const handleAddDependency = (0, import_react12.useCallback)((successorTaskId, predecessorTaskId, linkType) => {
2416
2625
  if (successorTaskId === predecessorTaskId) return;
2417
2626
  const successor = tasks.find((t) => t.id === successorTaskId);
2418
2627
  if (!successor) return;
@@ -2461,7 +2670,7 @@ var TaskList = ({
2461
2670
  }
2462
2671
  setSelectingPredecessorFor(null);
2463
2672
  }, [tasks, onTaskChange]);
2464
- const handleRemoveDependency = (0, import_react11.useCallback)((taskId, predecessorTaskId, linkType) => {
2673
+ const handleRemoveDependency = (0, import_react12.useCallback)((taskId, predecessorTaskId, linkType) => {
2465
2674
  const task = tasks.find((t) => t.id === taskId);
2466
2675
  if (!task) return;
2467
2676
  const updatedDeps = (task.dependencies ?? []).filter(
@@ -2469,21 +2678,44 @@ var TaskList = ({
2469
2678
  );
2470
2679
  onTaskChange?.({ ...task, dependencies: updatedDeps });
2471
2680
  }, [tasks, onTaskChange]);
2472
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2681
+ const [isCreating, setIsCreating] = (0, import_react12.useState)(false);
2682
+ const handleConfirmNewTask = (0, import_react12.useCallback)((name) => {
2683
+ const now = /* @__PURE__ */ new Date();
2684
+ const todayISO = new Date(Date.UTC(
2685
+ now.getUTCFullYear(),
2686
+ now.getUTCMonth(),
2687
+ now.getUTCDate()
2688
+ )).toISOString().split("T")[0];
2689
+ const endISO = new Date(Date.UTC(
2690
+ now.getUTCFullYear(),
2691
+ now.getUTCMonth(),
2692
+ now.getUTCDate() + 7
2693
+ )).toISOString().split("T")[0];
2694
+ const newTask = {
2695
+ id: crypto.randomUUID(),
2696
+ name,
2697
+ startDate: todayISO,
2698
+ endDate: endISO
2699
+ };
2700
+ onAdd?.(newTask);
2701
+ setIsCreating(false);
2702
+ }, [onAdd]);
2703
+ const handleCancelNewTask = (0, import_react12.useCallback)(() => setIsCreating(false), []);
2704
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2473
2705
  "div",
2474
2706
  {
2475
2707
  ref: overlayRef,
2476
2708
  className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}`,
2477
2709
  style: { width: `${taskListWidth}px` },
2478
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-table", children: [
2479
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-header", style: { height: `${headerHeight + 0.5}px` }, children: [
2480
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-number", children: "\u2116" }),
2481
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-name", children: "\u0418\u043C\u044F" }),
2482
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
2483
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435" }),
2484
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gantt-tl-headerCell gantt-tl-cell-deps", style: { position: "relative" }, children: [
2485
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
2486
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2710
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gantt-tl-table", children: [
2711
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gantt-tl-header", style: { height: `${headerHeight + 0.5}px` }, children: [
2712
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-number", children: "\u2116" }),
2713
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-name", children: "\u0418\u043C\u044F" }),
2714
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
2715
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-headerCell gantt-tl-cell-date", children: "\u041E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435" }),
2716
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gantt-tl-headerCell gantt-tl-cell-deps", style: { position: "relative" }, children: [
2717
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
2718
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2487
2719
  "button",
2488
2720
  {
2489
2721
  className: "gantt-tl-dep-type-trigger",
@@ -2491,12 +2723,12 @@ var TaskList = ({
2491
2723
  onClick: (e) => e.stopPropagation(),
2492
2724
  children: [
2493
2725
  "\u0421\u0432\u044F\u0437\u0438 ",
2494
- import_react11.default.createElement(LINK_TYPE_ICONS[activeLinkType]),
2726
+ import_react12.default.createElement(LINK_TYPE_ICONS[activeLinkType]),
2495
2727
  " \u25BE"
2496
2728
  ]
2497
2729
  }
2498
2730
  ) }),
2499
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-dep-type-menu", children: LINK_TYPE_ORDER.map((lt) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2731
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-dep-type-menu", children: LINK_TYPE_ORDER.map((lt) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2500
2732
  "button",
2501
2733
  {
2502
2734
  className: `gantt-tl-dep-type-option${activeLinkType === lt ? " active" : ""}`,
@@ -2505,17 +2737,17 @@ var TaskList = ({
2505
2737
  setTypeMenuOpen(false);
2506
2738
  },
2507
2739
  children: [
2508
- import_react11.default.createElement(LINK_TYPE_ICONS[lt]),
2509
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: LINK_TYPE_LABELS[lt] })
2740
+ import_react12.default.createElement(LINK_TYPE_ICONS[lt]),
2741
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: LINK_TYPE_LABELS[lt] })
2510
2742
  ]
2511
2743
  },
2512
2744
  lt
2513
2745
  )) }) })
2514
2746
  ] }),
2515
- cycleError && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-dep-error", children: "\u0426\u0438\u043A\u043B \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439!" })
2747
+ cycleError && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-dep-error", children: "\u0426\u0438\u043A\u043B \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439!" })
2516
2748
  ] })
2517
2749
  ] }),
2518
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gantt-tl-body", style: { height: `${totalHeight}px` }, children: tasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2750
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-body", style: { height: `${totalHeight}px` }, children: tasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2519
2751
  TaskListRow,
2520
2752
  {
2521
2753
  task,
@@ -2534,18 +2766,39 @@ var TaskList = ({
2534
2766
  onRemoveDependency: handleRemoveDependency,
2535
2767
  selectedChip,
2536
2768
  onChipSelect: handleChipSelect,
2537
- onScrollToTask
2769
+ onScrollToTask,
2770
+ onDelete,
2771
+ onAdd,
2772
+ onInsertAfter,
2773
+ editingTaskId: propEditingTaskId
2538
2774
  },
2539
2775
  task.id
2540
- )) })
2776
+ )) }),
2777
+ isCreating && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2778
+ NewTaskRow,
2779
+ {
2780
+ rowHeight,
2781
+ onConfirm: handleConfirmNewTask,
2782
+ onCancel: handleCancelNewTask
2783
+ }
2784
+ ),
2785
+ onAdd && !isCreating && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2786
+ "button",
2787
+ {
2788
+ className: "gantt-tl-add-btn",
2789
+ onClick: () => setIsCreating(true),
2790
+ type: "button",
2791
+ children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443"
2792
+ }
2793
+ )
2541
2794
  ] })
2542
2795
  }
2543
2796
  );
2544
2797
  };
2545
2798
 
2546
2799
  // src/components/GanttChart/GanttChart.tsx
2547
- var import_jsx_runtime14 = require("react/jsx-runtime");
2548
- var GanttChart = (0, import_react12.forwardRef)(({
2800
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2801
+ var GanttChart = (0, import_react13.forwardRef)(({
2549
2802
  tasks,
2550
2803
  dayWidth = 40,
2551
2804
  rowHeight = 40,
@@ -2560,35 +2813,39 @@ var GanttChart = (0, import_react12.forwardRef)(({
2560
2813
  taskListWidth = 520,
2561
2814
  disableTaskNameEditing = false,
2562
2815
  disableDependencyEditing = false,
2563
- highlightExpiredTasks = false
2816
+ highlightExpiredTasks = false,
2817
+ onAdd,
2818
+ onDelete,
2819
+ onInsertAfter,
2820
+ editingTaskId
2564
2821
  }, ref) => {
2565
- const scrollContainerRef = (0, import_react12.useRef)(null);
2566
- const [selectedTaskId, setSelectedTaskId] = (0, import_react12.useState)(null);
2567
- const [selectedChip, setSelectedChip] = (0, import_react12.useState)(null);
2568
- const dateRange = (0, import_react12.useMemo)(() => getMultiMonthDays(tasks), [tasks]);
2569
- const [validationResult, setValidationResult] = (0, import_react12.useState)(null);
2570
- const [cascadeOverrides, setCascadeOverrides] = (0, import_react12.useState)(/* @__PURE__ */ new Map());
2571
- const gridWidth = (0, import_react12.useMemo)(
2822
+ const scrollContainerRef = (0, import_react13.useRef)(null);
2823
+ const [selectedTaskId, setSelectedTaskId] = (0, import_react13.useState)(null);
2824
+ const [selectedChip, setSelectedChip] = (0, import_react13.useState)(null);
2825
+ const dateRange = (0, import_react13.useMemo)(() => getMultiMonthDays(tasks), [tasks]);
2826
+ const [validationResult, setValidationResult] = (0, import_react13.useState)(null);
2827
+ const [cascadeOverrides, setCascadeOverrides] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
2828
+ const gridWidth = (0, import_react13.useMemo)(
2572
2829
  () => Math.round(dateRange.length * dayWidth),
2573
2830
  [dateRange.length, dayWidth]
2574
2831
  );
2575
- const totalGridHeight = (0, import_react12.useMemo)(
2832
+ const totalGridHeight = (0, import_react13.useMemo)(
2576
2833
  () => tasks.length * rowHeight,
2577
2834
  [tasks.length, rowHeight]
2578
2835
  );
2579
- const monthStart = (0, import_react12.useMemo)(() => {
2836
+ const monthStart = (0, import_react13.useMemo)(() => {
2580
2837
  if (dateRange.length === 0) {
2581
2838
  return new Date(Date.UTC((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth(), 1));
2582
2839
  }
2583
2840
  const firstDay = dateRange[0];
2584
2841
  return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
2585
2842
  }, [dateRange]);
2586
- const todayInRange = (0, import_react12.useMemo)(() => {
2843
+ const todayInRange = (0, import_react13.useMemo)(() => {
2587
2844
  const now = /* @__PURE__ */ new Date();
2588
2845
  const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
2589
2846
  return dateRange.some((day) => day.getTime() === today.getTime());
2590
2847
  }, [dateRange]);
2591
- (0, import_react12.useEffect)(() => {
2848
+ (0, import_react13.useEffect)(() => {
2592
2849
  const container = scrollContainerRef.current;
2593
2850
  if (!container || dateRange.length === 0) return;
2594
2851
  const now = /* @__PURE__ */ new Date();
@@ -2600,7 +2857,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
2600
2857
  const scrollLeft = Math.round(todayOffset - containerWidth / 2 + dayWidth / 2);
2601
2858
  container.scrollLeft = Math.max(0, scrollLeft);
2602
2859
  }, []);
2603
- const scrollToToday = (0, import_react12.useCallback)(() => {
2860
+ const scrollToToday = (0, import_react13.useCallback)(() => {
2604
2861
  const container = scrollContainerRef.current;
2605
2862
  if (!container || dateRange.length === 0) return;
2606
2863
  const now = /* @__PURE__ */ new Date();
@@ -2610,9 +2867,9 @@ var GanttChart = (0, import_react12.forwardRef)(({
2610
2867
  const todayOffset = todayIndex * dayWidth;
2611
2868
  const containerWidth = container.clientWidth;
2612
2869
  const scrollLeft = Math.round(todayOffset - containerWidth / 2 + dayWidth / 2);
2613
- container.scrollLeft = Math.max(0, scrollLeft);
2870
+ container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
2614
2871
  }, [dateRange, dayWidth]);
2615
- const scrollToTask = (0, import_react12.useCallback)((taskId) => {
2872
+ const scrollToTask = (0, import_react13.useCallback)((taskId) => {
2616
2873
  const container = scrollContainerRef.current;
2617
2874
  if (!container || dateRange.length === 0) return;
2618
2875
  const task = tasks.find((t) => t.id === taskId);
@@ -2627,9 +2884,9 @@ var GanttChart = (0, import_react12.forwardRef)(({
2627
2884
  if (taskIndex === -1) return;
2628
2885
  const taskOffset = taskIndex * dayWidth;
2629
2886
  const scrollLeft = Math.round(taskOffset - dayWidth * 2);
2630
- container.scrollLeft = Math.max(0, scrollLeft);
2887
+ container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
2631
2888
  }, [tasks, dateRange, dayWidth]);
2632
- (0, import_react12.useImperativeHandle)(
2889
+ (0, import_react13.useImperativeHandle)(
2633
2890
  ref,
2634
2891
  () => ({
2635
2892
  scrollToToday,
@@ -2637,14 +2894,14 @@ var GanttChart = (0, import_react12.forwardRef)(({
2637
2894
  }),
2638
2895
  [scrollToToday, scrollToTask]
2639
2896
  );
2640
- const [dragGuideLines, setDragGuideLines] = (0, import_react12.useState)(null);
2641
- const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react12.useState)(null);
2642
- (0, import_react12.useEffect)(() => {
2897
+ const [dragGuideLines, setDragGuideLines] = (0, import_react13.useState)(null);
2898
+ const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react13.useState)(null);
2899
+ (0, import_react13.useEffect)(() => {
2643
2900
  const result = validateDependencies(tasks);
2644
2901
  setValidationResult(result);
2645
2902
  onValidateDependencies?.(result);
2646
2903
  }, [tasks, onValidateDependencies]);
2647
- const handleTaskChange = (0, import_react12.useCallback)((updatedTask) => {
2904
+ const handleTaskChange = (0, import_react13.useCallback)((updatedTask) => {
2648
2905
  const originalTask = tasks.find((t) => t.id === updatedTask.id);
2649
2906
  if (!originalTask) {
2650
2907
  onChange?.((currentTasks) => currentTasks.map((t) => t.id === updatedTask.id ? updatedTask : t));
@@ -2655,41 +2912,16 @@ var GanttChart = (0, import_react12.forwardRef)(({
2655
2912
  const newStart = new Date(updatedTask.startDate);
2656
2913
  const newEnd = new Date(updatedTask.endDate);
2657
2914
  const datesChanged = origStart.getTime() !== newStart.getTime() || origEnd.getTime() !== newEnd.getTime();
2658
- const now = /* @__PURE__ */ new Date();
2659
- const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
2660
- const msPerDay = 1e3 * 60 * 60 * 24;
2661
- let tasksToProcess;
2662
- let cascadedTasksForCallback = [];
2663
2915
  if (!datesChanged) {
2664
2916
  onChange?.((currentTasks) => currentTasks.map((t) => t.id === updatedTask.id ? updatedTask : t));
2665
2917
  return;
2666
2918
  }
2919
+ let cascadedTasksForCallback;
2667
2920
  if (disableConstraints) {
2668
- tasksToProcess = [updatedTask];
2669
2921
  cascadedTasksForCallback = [updatedTask];
2670
2922
  } else {
2671
- const cascadedTask = updatedTask;
2672
2923
  const cascadedChain = cascadeByLinks(updatedTask.id, newStart, newEnd, tasks);
2673
- tasksToProcess = [cascadedTask, ...cascadedChain];
2674
- cascadedTasksForCallback = tasksToProcess;
2675
- }
2676
- console.log("[GanttChart handleTaskChange] IsExpired calculation:");
2677
- for (const t of tasksToProcess) {
2678
- const taskStart = new Date(t.startDate);
2679
- const taskEnd = new Date(t.endDate);
2680
- const actualProgress = t.progress ?? 0;
2681
- if (actualProgress >= 100) {
2682
- console.log(` [${t.id}] START=${t.startDate} END=${t.endDate} TODAY=${today.toISOString().split("T")[0]} PROGRESS=${actualProgress}% EXPECTED=N/A (completed) EXPIRED=NO`);
2683
- continue;
2684
- }
2685
- const duration = taskEnd.getTime() - taskStart.getTime() + msPerDay;
2686
- const elapsedFromToday = today.getTime() - taskStart.getTime();
2687
- const elapsed = Math.min(Math.max(0, elapsedFromToday), duration);
2688
- const expectedProgress = Math.min(100, Math.max(0, elapsed / duration * 100));
2689
- const isExpired = actualProgress < expectedProgress;
2690
- const durationDays = Math.round(duration / msPerDay);
2691
- const elapsedDays = Math.round(elapsed / msPerDay);
2692
- console.log(` [${t.id}] START=${t.startDate} END=${t.endDate} TODAY=${today.toISOString().split("T")[0]} PROGRESS=${actualProgress}% DURATION=${durationDays}d ELAPSED=${elapsedDays}d EXPECTED=${expectedProgress.toFixed(1)}% EXPIRED=${isExpired ? "YES" : "NO"}`);
2924
+ cascadedTasksForCallback = [updatedTask, ...cascadedChain];
2693
2925
  }
2694
2926
  if (disableConstraints) {
2695
2927
  onChange?.((currentTasks) => currentTasks.map((t) => t.id === updatedTask.id ? updatedTask : t));
@@ -2701,7 +2933,16 @@ var GanttChart = (0, import_react12.forwardRef)(({
2701
2933
  onCascade?.(cascadedTasksForCallback);
2702
2934
  }
2703
2935
  }, [tasks, onChange, disableConstraints, onCascade]);
2704
- const dependencyOverrides = (0, import_react12.useMemo)(() => {
2936
+ const handleDelete = (0, import_react13.useCallback)((taskId) => {
2937
+ onChange?.(
2938
+ (currentTasks) => currentTasks.filter((t) => t.id !== taskId).map((t) => ({
2939
+ ...t,
2940
+ dependencies: (t.dependencies ?? []).filter((d) => d.taskId !== taskId)
2941
+ }))
2942
+ );
2943
+ onDelete?.(taskId);
2944
+ }, [onChange, onDelete]);
2945
+ const dependencyOverrides = (0, import_react13.useMemo)(() => {
2705
2946
  const map = new Map(cascadeOverrides);
2706
2947
  if (draggedTaskOverride) {
2707
2948
  map.set(draggedTaskOverride.taskId, {
@@ -2711,21 +2952,21 @@ var GanttChart = (0, import_react12.forwardRef)(({
2711
2952
  }
2712
2953
  return map;
2713
2954
  }, [cascadeOverrides, draggedTaskOverride]);
2714
- const handleCascadeProgress = (0, import_react12.useCallback)((overrides) => {
2955
+ const handleCascadeProgress = (0, import_react13.useCallback)((overrides) => {
2715
2956
  setCascadeOverrides(new Map(overrides));
2716
2957
  }, []);
2717
- const handleCascade = (0, import_react12.useCallback)((cascadedTasks) => {
2958
+ const handleCascade = (0, import_react13.useCallback)((cascadedTasks) => {
2718
2959
  onChange?.((currentTasks) => {
2719
2960
  const cascadeMap = new Map(cascadedTasks.map((t) => [t.id, t]));
2720
2961
  return currentTasks.map((t) => cascadeMap.get(t.id) ?? t);
2721
2962
  });
2722
2963
  onCascade?.(cascadedTasks);
2723
2964
  }, [onChange, onCascade]);
2724
- const handleTaskSelect = (0, import_react12.useCallback)((taskId) => {
2965
+ const handleTaskSelect = (0, import_react13.useCallback)((taskId) => {
2725
2966
  setSelectedTaskId(taskId);
2726
2967
  }, []);
2727
- const panStateRef = (0, import_react12.useRef)(null);
2728
- const handlePanStart = (0, import_react12.useCallback)((e) => {
2968
+ const panStateRef = (0, import_react13.useRef)(null);
2969
+ const handlePanStart = (0, import_react13.useCallback)((e) => {
2729
2970
  if (e.button !== 0) return;
2730
2971
  const target = e.target;
2731
2972
  if (target.closest("[data-taskbar]")) return;
@@ -2746,7 +2987,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
2746
2987
  container.style.cursor = "grabbing";
2747
2988
  e.preventDefault();
2748
2989
  }, []);
2749
- (0, import_react12.useEffect)(() => {
2990
+ (0, import_react13.useEffect)(() => {
2750
2991
  const handlePanMove = (e) => {
2751
2992
  const pan = panStateRef.current;
2752
2993
  if (!pan?.active) return;
@@ -2768,15 +3009,15 @@ var GanttChart = (0, import_react12.forwardRef)(({
2768
3009
  window.removeEventListener("mouseup", handlePanEnd);
2769
3010
  };
2770
3011
  }, []);
2771
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-container", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3012
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-container", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2772
3013
  "div",
2773
3014
  {
2774
3015
  ref: scrollContainerRef,
2775
3016
  className: "gantt-scrollContainer",
2776
3017
  style: { height: containerHeight ?? "auto", cursor: "grab" },
2777
3018
  onMouseDown: handlePanStart,
2778
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gantt-scrollContent", children: [
2779
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3019
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "gantt-scrollContent", children: [
3020
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2780
3021
  TaskList,
2781
3022
  {
2782
3023
  tasks,
@@ -2790,11 +3031,15 @@ var GanttChart = (0, import_react12.forwardRef)(({
2790
3031
  disableTaskNameEditing,
2791
3032
  disableDependencyEditing,
2792
3033
  onScrollToTask: scrollToTask,
2793
- onSelectedChipChange: setSelectedChip
3034
+ onSelectedChipChange: setSelectedChip,
3035
+ onAdd,
3036
+ onDelete: handleDelete,
3037
+ onInsertAfter,
3038
+ editingTaskId
2794
3039
  }
2795
3040
  ),
2796
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { minWidth: `${gridWidth}px`, flex: 1 }, children: [
2797
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3041
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { minWidth: `${gridWidth}px`, flex: 1 }, children: [
3042
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2798
3043
  TimeScaleHeader_default,
2799
3044
  {
2800
3045
  days: dateRange,
@@ -2802,7 +3047,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
2802
3047
  headerHeight
2803
3048
  }
2804
3049
  ) }),
2805
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3050
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2806
3051
  "div",
2807
3052
  {
2808
3053
  className: "gantt-taskArea",
@@ -2811,7 +3056,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
2811
3056
  width: `${gridWidth}px`
2812
3057
  },
2813
3058
  children: [
2814
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3059
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2815
3060
  GridBackground_default,
2816
3061
  {
2817
3062
  dateRange,
@@ -2819,8 +3064,8 @@ var GanttChart = (0, import_react12.forwardRef)(({
2819
3064
  totalHeight: totalGridHeight
2820
3065
  }
2821
3066
  ),
2822
- todayInRange && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
2823
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3067
+ todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
3068
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2824
3069
  DependencyLines_default,
2825
3070
  {
2826
3071
  tasks,
@@ -2832,7 +3077,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
2832
3077
  selectedDep: selectedChip
2833
3078
  }
2834
3079
  ),
2835
- dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3080
+ dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2836
3081
  DragGuideLines_default,
2837
3082
  {
2838
3083
  isDragging: dragGuideLines.isDragging,
@@ -2842,7 +3087,7 @@ var GanttChart = (0, import_react12.forwardRef)(({
2842
3087
  totalHeight: totalGridHeight
2843
3088
  }
2844
3089
  ),
2845
- tasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3090
+ tasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2846
3091
  TaskRow_default,
2847
3092
  {
2848
3093
  task,
@@ -2881,9 +3126,9 @@ var GanttChart = (0, import_react12.forwardRef)(({
2881
3126
  GanttChart.displayName = "GanttChart";
2882
3127
 
2883
3128
  // src/components/ui/Button.tsx
2884
- var import_react13 = __toESM(require("react"));
2885
- var import_jsx_runtime15 = require("react/jsx-runtime");
2886
- var Button = import_react13.default.forwardRef(
3129
+ var import_react14 = __toESM(require("react"));
3130
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3131
+ var Button = import_react14.default.forwardRef(
2887
3132
  ({ className, variant = "default", size = "default", children, ...props }, ref) => {
2888
3133
  const classes = [
2889
3134
  "gantt-btn",
@@ -2891,7 +3136,7 @@ var Button = import_react13.default.forwardRef(
2891
3136
  size !== "default" ? `gantt-btn-${size}` : "",
2892
3137
  className || ""
2893
3138
  ].filter(Boolean).join(" ");
2894
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { ref, className: classes, ...props, children });
3139
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { ref, className: classes, ...props, children });
2895
3140
  }
2896
3141
  );
2897
3142
  Button.displayName = "Button";