gantt-lib 0.17.2 → 0.19.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
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  "use strict";
2
4
  "use client";
3
5
  var __create = Object.create;
@@ -34,6 +36,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
34
36
  // src/utils/dateUtils.ts
35
37
  var dateUtils_exports = {};
36
38
  __export(dateUtils_exports, {
39
+ createDateKey: () => createDateKey,
40
+ createIsWeekendPredicate: () => createIsWeekendPredicate,
37
41
  formatDateLabel: () => formatDateLabel,
38
42
  getDayOffset: () => getDayOffset,
39
43
  getMonthBlocks: () => getMonthBlocks,
@@ -48,7 +52,7 @@ __export(dateUtils_exports, {
48
52
  normalizeTaskDates: () => normalizeTaskDates,
49
53
  parseUTCDate: () => parseUTCDate
50
54
  });
51
- var parseUTCDate, getMonthDays, getDayOffset, isToday, isWeekend, getMultiMonthDays, getMonthSpans, formatDateLabel, getWeekBlocks, getWeekSpans, getMonthBlocks, getYearSpans, normalizeTaskDates;
55
+ var parseUTCDate, getMonthDays, getDayOffset, isToday, isWeekend, createDateKey, createIsWeekendPredicate, getMultiMonthDays, getMonthSpans, formatDateLabel, getWeekBlocks, getWeekSpans, getMonthBlocks, getYearSpans, normalizeTaskDates;
52
56
  var init_dateUtils = __esm({
53
57
  "src/utils/dateUtils.ts"() {
54
58
  "use strict";
@@ -105,6 +109,41 @@ var init_dateUtils = __esm({
105
109
  const day = date.getUTCDay();
106
110
  return day === 0 || day === 6;
107
111
  };
112
+ createDateKey = (date) => {
113
+ return `${date.getUTCFullYear()}-${date.getUTCMonth()}-${date.getUTCDate()}`;
114
+ };
115
+ createIsWeekendPredicate = (config) => {
116
+ const { weekends, workdays, isWeekend: customPredicate } = config;
117
+ if (customPredicate) {
118
+ return customPredicate;
119
+ }
120
+ if (workdays && workdays.length > 0) {
121
+ const workdaySet = new Set(workdays.map(createDateKey));
122
+ return (date) => {
123
+ const key = createDateKey(date);
124
+ if (workdaySet.has(key)) {
125
+ return false;
126
+ }
127
+ const dayOfWeek = date.getUTCDay();
128
+ return dayOfWeek === 0 || dayOfWeek === 6;
129
+ };
130
+ }
131
+ if (weekends && weekends.length > 0) {
132
+ const weekendSet = new Set(weekends.map(createDateKey));
133
+ return (date) => {
134
+ const key = createDateKey(date);
135
+ if (weekendSet.has(key)) {
136
+ return true;
137
+ }
138
+ const dayOfWeek = date.getUTCDay();
139
+ return dayOfWeek === 0 || dayOfWeek === 6;
140
+ };
141
+ }
142
+ return (date) => {
143
+ const dayOfWeek = date.getUTCDay();
144
+ return dayOfWeek === 0 || dayOfWeek === 6;
145
+ };
146
+ };
108
147
  getMultiMonthDays = (tasks) => {
109
148
  if (!tasks || tasks.length === 0) {
110
149
  return getMonthDays(/* @__PURE__ */ new Date());
@@ -341,6 +380,8 @@ __export(index_exports, {
341
380
  computeLagFromDates: () => computeLagFromDates,
342
381
  computeParentDates: () => computeParentDates,
343
382
  computeParentProgress: () => computeParentProgress,
383
+ createDateKey: () => createDateKey,
384
+ createIsWeekendPredicate: () => createIsWeekendPredicate,
344
385
  detectCycles: () => detectCycles,
345
386
  detectEdgeZone: () => detectEdgeZone,
346
387
  findParentId: () => findParentId,
@@ -894,7 +935,8 @@ var TimeScaleHeader = ({
894
935
  days,
895
936
  dayWidth,
896
937
  headerHeight,
897
- viewMode = "day"
938
+ viewMode = "day",
939
+ isCustomWeekend
898
940
  }) => {
899
941
  const monthSpans = (0, import_react.useMemo)(() => getMonthSpans(days), [days]);
900
942
  const rowHeight = headerHeight / 2;
@@ -1058,12 +1100,12 @@ var TimeScaleHeader = ({
1058
1100
  ) : (
1059
1101
  // Day-view row 2: individual day numbers (existing code)
1060
1102
  days.map((day, index) => {
1061
- const isWeekend3 = day.getDay() === 0 || day.getDay() === 6;
1103
+ const isWeekendDay = isCustomWeekend ? isCustomWeekend(day) : day.getUTCDay() === 0 || day.getUTCDay() === 6;
1062
1104
  const prevDay = days[index - 1];
1063
- const isMonthBoundary = index > 0 && prevDay && prevDay.getMonth() !== day.getMonth();
1105
+ const isMonthBoundary = index > 0 && prevDay && prevDay.getUTCMonth() !== day.getUTCMonth();
1064
1106
  const now = /* @__PURE__ */ new Date();
1065
1107
  const isTodayDate = day.getUTCFullYear() === now.getFullYear() && day.getUTCMonth() === now.getMonth() && day.getUTCDate() === now.getDate();
1066
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `gantt-tsh-dayCell ${isWeekend3 ? "gantt-tsh-weekendDay" : ""} ${isTodayDate ? "gantt-tsh-today" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "gantt-tsh-dayLabel", children: (0, import_date_fns.format)(day, "d") }) }, `day-${index}`);
1108
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `gantt-tsh-dayCell ${isWeekendDay ? "gantt-tsh-weekendDay" : ""} ${isTodayDate ? "gantt-tsh-today" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "gantt-tsh-dayLabel", children: (0, import_date_fns.format)(day, "d") }) }, `day-${index}`);
1067
1109
  })
1068
1110
  )
1069
1111
  }
@@ -1156,14 +1198,13 @@ var calculateGridLines = (dateRange, dayWidth) => {
1156
1198
  }
1157
1199
  return lines;
1158
1200
  };
1159
- var calculateWeekendBlocks = (dateRange, dayWidth) => {
1201
+ var calculateWeekendBlocks = (dateRange, dayWidth, isCustomWeekend) => {
1160
1202
  const blocks = [];
1161
1203
  let inWeekend = false;
1162
1204
  let weekendStartIndex = -1;
1163
1205
  for (let i = 0; i < dateRange.length; i++) {
1164
1206
  const date = dateRange[i];
1165
- const dayOfWeek = date.getUTCDay();
1166
- const isWeekend3 = dayOfWeek === 0 || dayOfWeek === 6;
1207
+ const isWeekend3 = isCustomWeekend ? isCustomWeekend(date) : date.getUTCDay() === 0 || date.getUTCDay() === 6;
1167
1208
  if (isWeekend3 && !inWeekend) {
1168
1209
  inWeekend = true;
1169
1210
  weekendStartIndex = i;
@@ -1979,10 +2020,10 @@ var import_react5 = __toESM(require("react"));
1979
2020
  var import_jsx_runtime4 = require("react/jsx-runtime");
1980
2021
  var arePropsEqual2 = (prevProps, nextProps) => {
1981
2022
  return prevProps.dayWidth === nextProps.dayWidth && prevProps.dateRange.length === nextProps.dateRange.length && prevProps.totalHeight === nextProps.totalHeight && // skip re-render only when totalHeight unchanged
1982
- prevProps.viewMode === nextProps.viewMode;
2023
+ prevProps.viewMode === nextProps.viewMode && prevProps.isCustomWeekend === nextProps.isCustomWeekend;
1983
2024
  };
1984
2025
  var GridBackground = import_react5.default.memo(
1985
- ({ dateRange, dayWidth, totalHeight, viewMode = "day" }) => {
2026
+ ({ dateRange, dayWidth, totalHeight, viewMode = "day", isCustomWeekend }) => {
1986
2027
  const weekGridLines = (0, import_react5.useMemo)(() => {
1987
2028
  if (viewMode !== "week") return [];
1988
2029
  return calculateWeekGridLines(dateRange, dayWidth);
@@ -1997,8 +2038,8 @@ var GridBackground = import_react5.default.memo(
1997
2038
  }, [dateRange, dayWidth, viewMode]);
1998
2039
  const weekendBlocks = (0, import_react5.useMemo)(() => {
1999
2040
  if (viewMode === "week" || viewMode === "month") return [];
2000
- return calculateWeekendBlocks(dateRange, dayWidth);
2001
- }, [dateRange, dayWidth, viewMode]);
2041
+ return calculateWeekendBlocks(dateRange, dayWidth, isCustomWeekend);
2042
+ }, [dateRange, dayWidth, viewMode, isCustomWeekend]);
2002
2043
  const gridWidth = (0, import_react5.useMemo)(() => {
2003
2044
  return Math.round(dateRange.length * dayWidth);
2004
2045
  }, [dateRange.length, dayWidth]);
@@ -2495,12 +2536,13 @@ var import_date_fns3 = require("date-fns");
2495
2536
  var import_react8 = require("react");
2496
2537
  var import_date_fns2 = require("date-fns");
2497
2538
  var import_locale2 = require("date-fns/locale");
2539
+ init_dateUtils();
2498
2540
  var import_jsx_runtime9 = require("react/jsx-runtime");
2499
- function getDayClassName(day, selected) {
2541
+ function getDayClassName(day, selected, isWeekendProp) {
2500
2542
  const classes = ["gantt-day-btn"];
2501
2543
  if (selected && (0, import_date_fns2.isSameDay)(day, selected)) classes.push("selected");
2502
2544
  if ((0, import_date_fns2.isToday)(day)) classes.push("today");
2503
- if ((0, import_date_fns2.isWeekend)(day)) classes.push("weekend");
2545
+ if (isWeekendProp ? isWeekendProp(day) : (0, import_date_fns2.isWeekend)(day)) classes.push("weekend");
2504
2546
  if ((0, import_date_fns2.isBefore)(day, (0, import_date_fns2.startOfDay)(/* @__PURE__ */ new Date())) && !(0, import_date_fns2.isToday)(day)) classes.push("past");
2505
2547
  return classes.join(" ");
2506
2548
  }
@@ -2509,9 +2551,22 @@ var Calendar = ({
2509
2551
  onSelect,
2510
2552
  initialDate,
2511
2553
  mode = "single",
2512
- disabled = false
2554
+ disabled = false,
2555
+ isWeekend: isWeekendProp,
2556
+ weekends,
2557
+ workdays
2513
2558
  }) => {
2514
2559
  const scrollRef = (0, import_react8.useRef)(null);
2560
+ const derivedWeekendPredicate = (0, import_react8.useMemo)(() => {
2561
+ if (isWeekendProp) return isWeekendProp;
2562
+ if (weekends || workdays) {
2563
+ return createIsWeekendPredicate({
2564
+ weekends: weekends ?? [],
2565
+ workdays: workdays ?? []
2566
+ });
2567
+ }
2568
+ return void 0;
2569
+ }, [isWeekendProp, weekends, workdays]);
2515
2570
  const initialMonth = (0, import_react8.useMemo)(
2516
2571
  () => (0, import_date_fns2.startOfMonth)(initialDate ?? selected ?? /* @__PURE__ */ new Date()),
2517
2572
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -2565,11 +2620,13 @@ var Calendar = ({
2565
2620
  const emptyDays = ((0, import_date_fns2.getDay)(firstDay) + 6) % 7;
2566
2621
  const monthKey = (0, import_date_fns2.format)(month, "yyyy-MM");
2567
2622
  const monthLabel = (0, import_date_fns2.format)(month, "LLLL yyyy", { locale: import_locale2.ru });
2623
+ const weekdayLabels = ["\u041F\u043D", "\u0412\u0442", "\u0421\u0440", "\u0427\u0442", "\u041F\u0442", "\u0421\u0431", "\u0412\u0441"];
2624
+ const weekdayHeaders = weekdayLabels.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "gantt-cal-weekday", children: label }, `wd-${i}`));
2568
2625
  const emptyCells = Array.from({ length: emptyDays }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "gantt-cal-empty-day" }, `e-${i}`));
2569
2626
  const dayCells = Array.from({ length: totalDays }, (_, i) => {
2570
2627
  const dayNum = i + 1;
2571
- const day = new Date(month.getFullYear(), month.getMonth(), dayNum);
2572
- const className = getDayClassName(day, selected);
2628
+ const day = new Date(Date.UTC(month.getFullYear(), month.getMonth(), dayNum));
2629
+ const className = getDayClassName(day, selected, derivedWeekendPredicate);
2573
2630
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2574
2631
  "button",
2575
2632
  {
@@ -2578,7 +2635,7 @@ var Calendar = ({
2578
2635
  disabled,
2579
2636
  onClick: () => {
2580
2637
  if (!disabled && onSelect) {
2581
- onSelect(new Date(month.getFullYear(), month.getMonth(), dayNum));
2638
+ onSelect(new Date(Date.UTC(month.getFullYear(), month.getMonth(), dayNum)));
2582
2639
  }
2583
2640
  },
2584
2641
  children: dayNum
@@ -2589,12 +2646,13 @@ var Calendar = ({
2589
2646
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "gantt-cal-month", "data-month": monthKey, children: [
2590
2647
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "gantt-cal-month-header", children: monthLabel }),
2591
2648
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "gantt-cal-month-days", children: [
2649
+ weekdayHeaders,
2592
2650
  emptyCells,
2593
2651
  dayCells
2594
2652
  ] })
2595
2653
  ] }, monthKey);
2596
2654
  },
2597
- [selected, onSelect, disabled]
2655
+ [selected, onSelect, disabled, derivedWeekendPredicate]
2598
2656
  );
2599
2657
  const renderedMonths = (0, import_react8.useMemo)(
2600
2658
  () => months.map(renderMonth),
@@ -2618,7 +2676,10 @@ var DatePicker = ({
2618
2676
  placeholder = "Pick a date",
2619
2677
  portal = true,
2620
2678
  className,
2621
- disabled = false
2679
+ disabled = false,
2680
+ weekends,
2681
+ workdays,
2682
+ isWeekend: isWeekend3
2622
2683
  }) => {
2623
2684
  const [open, setOpen] = (0, import_react9.useState)(false);
2624
2685
  const [inputValue, setInputValue] = (0, import_react9.useState)("");
@@ -2838,7 +2899,10 @@ var DatePicker = ({
2838
2899
  mode: "single",
2839
2900
  selected: selectedDate,
2840
2901
  onSelect: handleCalendarSelect,
2841
- initialDate: selectedDate
2902
+ initialDate: selectedDate,
2903
+ weekends,
2904
+ workdays,
2905
+ isWeekend: isWeekend3
2842
2906
  }
2843
2907
  )
2844
2908
  ]
@@ -2887,18 +2951,49 @@ var DAY_MS2 = 24 * 60 * 60 * 1e3;
2887
2951
  var getInclusiveDurationDays = (startDate, endDate) => {
2888
2952
  const start = parseUTCDate(startDate);
2889
2953
  const end = parseUTCDate(endDate);
2890
- return Math.max(1, Math.round((end.getTime() - start.getTime()) / DAY_MS2) + 1);
2954
+ return Math.max(
2955
+ 1,
2956
+ Math.round((end.getTime() - start.getTime()) / DAY_MS2) + 1
2957
+ );
2891
2958
  };
2892
2959
  var getEndDateFromDuration = (startDate, durationDays) => {
2893
2960
  const start = parseUTCDate(startDate);
2894
2961
  return new Date(start.getTime() + (durationDays - 1) * DAY_MS2).toISOString().split("T")[0];
2895
2962
  };
2896
- var TrashIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("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: [
2897
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
2898
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3 6h18" }),
2899
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
2900
- ] });
2901
- 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" }) });
2963
+ var TrashIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2964
+ "svg",
2965
+ {
2966
+ xmlns: "http://www.w3.org/2000/svg",
2967
+ width: "12",
2968
+ height: "12",
2969
+ viewBox: "0 0 24 24",
2970
+ fill: "none",
2971
+ stroke: "currentColor",
2972
+ strokeWidth: "2",
2973
+ strokeLinecap: "round",
2974
+ strokeLinejoin: "round",
2975
+ children: [
2976
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
2977
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3 6h18" }),
2978
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
2979
+ ]
2980
+ }
2981
+ );
2982
+ var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2983
+ "svg",
2984
+ {
2985
+ xmlns: "http://www.w3.org/2000/svg",
2986
+ width: "14",
2987
+ height: "14",
2988
+ viewBox: "0 0 24 24",
2989
+ fill: "none",
2990
+ stroke: "currentColor",
2991
+ strokeWidth: "2",
2992
+ strokeLinecap: "round",
2993
+ strokeLinejoin: "round",
2994
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 5v14M5 12h14" })
2995
+ }
2996
+ );
2902
2997
  var DragHandleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "10", height: "14", viewBox: "0 0 10 14", fill: "currentColor", children: [
2903
2998
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "2", cy: "2", r: "1.5" }),
2904
2999
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "8", cy: "2", r: "1.5" }),
@@ -2907,51 +3002,94 @@ var DragHandleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg",
2907
3002
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "2", cy: "12", r: "1.5" }),
2908
3003
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "8", cy: "12", r: "1.5" })
2909
3004
  ] });
2910
- var ChevronRightIcon = () => /* @__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: "m9 18 6-6-6-6" }) });
2911
- var HierarchyConnectorIcon = ({ isLastChild }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: `gantt-tl-hierarchy-connector${isLastChild ? " gantt-tl-hierarchy-connector--last" : ""}`, "aria-hidden": "true", children: [
2912
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-hc-vline" }),
2913
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-hc-hline" }),
2914
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-hc-dot" })
2915
- ] });
3005
+ var ChevronRightIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3006
+ "svg",
3007
+ {
3008
+ xmlns: "http://www.w3.org/2000/svg",
3009
+ width: "14",
3010
+ height: "14",
3011
+ viewBox: "0 0 24 24",
3012
+ fill: "none",
3013
+ stroke: "currentColor",
3014
+ strokeWidth: "2",
3015
+ strokeLinecap: "round",
3016
+ strokeLinejoin: "round",
3017
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m9 18 6-6-6-6" })
3018
+ }
3019
+ );
3020
+ var ArrowLeft = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3021
+ "svg",
3022
+ {
3023
+ xmlns: "http://www.w3.org/2000/svg",
3024
+ width: "16",
3025
+ height: "16",
3026
+ viewBox: "0 0 24 24",
3027
+ fill: "none",
3028
+ stroke: "currentColor",
3029
+ strokeWidth: "2",
3030
+ strokeLinecap: "round",
3031
+ strokeLinejoin: "round",
3032
+ children: [
3033
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 19-7-7 7-7" }),
3034
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M19 12H5" })
3035
+ ]
3036
+ }
3037
+ );
3038
+ var ArrowRight = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3039
+ "svg",
3040
+ {
3041
+ xmlns: "http://www.w3.org/2000/svg",
3042
+ width: "16",
3043
+ height: "16",
3044
+ viewBox: "0 0 24 24",
3045
+ fill: "none",
3046
+ stroke: "currentColor",
3047
+ strokeWidth: "2",
3048
+ strokeLinecap: "round",
3049
+ strokeLinejoin: "round",
3050
+ children: [
3051
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M5 12h14" }),
3052
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 5 7 7-7 7" })
3053
+ ]
3054
+ }
3055
+ );
2916
3056
  var HierarchyButton = ({
2917
3057
  isChild,
2918
- isParent,
2919
- rowIndex,
3058
+ rowIndex: _rowIndex,
2920
3059
  onPromote,
2921
3060
  onDemote
2922
3061
  }) => {
2923
3062
  const canPromote = isChild && onPromote;
2924
- const canDemote = !isParent && onDemote && rowIndex > 0;
2925
- if (!canPromote && !canDemote) {
2926
- return null;
2927
- }
2928
- const handleClick = (e) => {
2929
- e.stopPropagation();
2930
- if (canPromote) {
2931
- onPromote(e);
2932
- } else if (canDemote) {
2933
- onDemote(e);
2934
- }
2935
- };
2936
- const title = canPromote ? "\u041F\u043E\u0432\u044B\u0441\u0438\u0442\u044C (\u0441\u0434\u0435\u043B\u0430\u0442\u044C \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439)" : "\u041F\u043E\u043D\u0438\u0437\u0438\u0442\u044C (\u0441\u0434\u0435\u043B\u0430\u0442\u044C \u043F\u043E\u0434\u0447\u0438\u043D\u0435\u043D\u043D\u043E\u0439)";
2937
- const ArrowLeft = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2938
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 19-7-7 7-7" }),
2939
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M19 12H5" })
2940
- ] });
2941
- const ArrowRight = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2942
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M5 12h14" }),
2943
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 5 7 7-7 7" })
3063
+ const canDemote = !!onDemote;
3064
+ if (!canPromote && !canDemote) return null;
3065
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3066
+ canPromote && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3067
+ "button",
3068
+ {
3069
+ type: "button",
3070
+ className: "gantt-tl-name-action-btn gantt-tl-action-hierarchy",
3071
+ onClick: (e) => {
3072
+ e.stopPropagation();
3073
+ onPromote(e);
3074
+ },
3075
+ title: "\u041F\u043E\u0432\u044B\u0441\u0438\u0442\u044C \u0443\u0440\u043E\u0432\u0435\u043D\u044C",
3076
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ArrowLeft, {})
3077
+ }
3078
+ ),
3079
+ canDemote && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3080
+ "button",
3081
+ {
3082
+ type: "button",
3083
+ className: "gantt-tl-name-action-btn gantt-tl-action-hierarchy",
3084
+ onClick: (e) => {
3085
+ e.stopPropagation();
3086
+ onDemote(e);
3087
+ },
3088
+ title: "\u041F\u043E\u043D\u0438\u0437\u0438\u0442\u044C \u0443\u0440\u043E\u0432\u0435\u043D\u044C",
3089
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ArrowRight, {})
3090
+ }
3091
+ )
2944
3092
  ] });
2945
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2946
- "button",
2947
- {
2948
- type: "button",
2949
- className: "gantt-tl-name-action-btn gantt-tl-action-hierarchy",
2950
- onClick: handleClick,
2951
- title,
2952
- children: canPromote ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ArrowLeft, {}) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ArrowRight, {})
2953
- }
2954
- );
2955
3093
  };
2956
3094
  var DepChip = ({
2957
3095
  lag,
@@ -2983,71 +3121,91 @@ var DepChip = ({
2983
3121
  const nextOpen = !popoverOpen;
2984
3122
  setPopoverOpen(nextOpen);
2985
3123
  if (nextOpen) {
2986
- onChipSelect?.({ successorId: taskId, predecessorId: dep.taskId, linkType: dep.type });
3124
+ onChipSelect?.({
3125
+ successorId: taskId,
3126
+ predecessorId: dep.taskId,
3127
+ linkType: dep.type
3128
+ });
2987
3129
  onScrollToTask?.(dep.taskId);
2988
3130
  } else {
2989
3131
  onChipSelect?.(null);
2990
3132
  }
2991
3133
  };
2992
- const handleOpenChange = (0, import_react10.useCallback)((open) => {
2993
- setPopoverOpen(open);
2994
- if (!open) {
2995
- onChipSelect?.(null);
2996
- }
2997
- }, [onChipSelect]);
3134
+ const handleOpenChange = (0, import_react10.useCallback)(
3135
+ (open) => {
3136
+ setPopoverOpen(open);
3137
+ if (!open) {
3138
+ onChipSelect?.(null);
3139
+ }
3140
+ },
3141
+ [onChipSelect]
3142
+ );
2998
3143
  const handleTrashClick = (e) => {
2999
3144
  e.stopPropagation();
3000
3145
  onRemoveDependency?.(taskId, dep.taskId, dep.type);
3001
3146
  onChipSelectClear();
3002
3147
  setPopoverOpen(false);
3003
3148
  };
3004
- const handleLagChange = (0, import_react10.useCallback)((newLag) => {
3005
- if (!onTasksChange || !allTasks) return;
3006
- const taskById = new Map(allTasks.map((t) => [t.id, t]));
3007
- const predecessor = taskById.get(dep.taskId);
3008
- if (!predecessor) return;
3009
- const predStart = parseUTCDate(predecessor.startDate);
3010
- const predEnd = parseUTCDate(predecessor.endDate);
3011
- const origStart = parseUTCDate(task.startDate);
3012
- const origEnd = parseUTCDate(task.endDate);
3013
- const durationMs = origEnd.getTime() - origStart.getTime();
3014
- const constraintDate = calculateSuccessorDate(predStart, predEnd, dep.type, newLag);
3015
- let newStart, newEnd;
3016
- if (dep.type === "FS" || dep.type === "SS") {
3017
- newStart = constraintDate;
3018
- newEnd = new Date(constraintDate.getTime() + durationMs);
3019
- } else {
3020
- newEnd = constraintDate;
3021
- newStart = new Date(constraintDate.getTime() - durationMs);
3022
- }
3023
- onTasksChange([{
3024
- ...task,
3025
- startDate: newStart.toISOString().split("T")[0],
3026
- endDate: newEnd.toISOString().split("T")[0]
3027
- }]);
3028
- }, [dep, task, allTasks, onTasksChange]);
3029
- const handleInputCommit = (0, import_react10.useCallback)((raw) => {
3030
- if (raw === "") {
3031
- handleLagChange(0);
3032
- return;
3033
- }
3034
- const parsed = parseInt(raw, 10);
3035
- const effectiveLag2 = lag ?? 0;
3036
- if (isNaN(parsed)) {
3037
- const abs = Math.abs(effectiveLag2);
3038
- setInputAbs(abs === 0 ? "" : String(abs));
3039
- return;
3040
- }
3041
- let newLag;
3042
- if (parsed === 0) {
3043
- newLag = 0;
3044
- } else if (dep.type === "SF") {
3045
- newLag = -Math.abs(parsed);
3046
- } else {
3047
- newLag = parsed;
3048
- }
3049
- if (newLag !== effectiveLag2) handleLagChange(newLag);
3050
- }, [lag, dep.type, handleLagChange]);
3149
+ const handleLagChange = (0, import_react10.useCallback)(
3150
+ (newLag) => {
3151
+ if (!onTasksChange || !allTasks) return;
3152
+ const taskById = new Map(allTasks.map((t) => [t.id, t]));
3153
+ const predecessor = taskById.get(dep.taskId);
3154
+ if (!predecessor) return;
3155
+ const predStart = parseUTCDate(predecessor.startDate);
3156
+ const predEnd = parseUTCDate(predecessor.endDate);
3157
+ const origStart = parseUTCDate(task.startDate);
3158
+ const origEnd = parseUTCDate(task.endDate);
3159
+ const durationMs = origEnd.getTime() - origStart.getTime();
3160
+ const constraintDate = calculateSuccessorDate(
3161
+ predStart,
3162
+ predEnd,
3163
+ dep.type,
3164
+ newLag
3165
+ );
3166
+ let newStart, newEnd;
3167
+ if (dep.type === "FS" || dep.type === "SS") {
3168
+ newStart = constraintDate;
3169
+ newEnd = new Date(constraintDate.getTime() + durationMs);
3170
+ } else {
3171
+ newEnd = constraintDate;
3172
+ newStart = new Date(constraintDate.getTime() - durationMs);
3173
+ }
3174
+ onTasksChange([
3175
+ {
3176
+ ...task,
3177
+ startDate: newStart.toISOString().split("T")[0],
3178
+ endDate: newEnd.toISOString().split("T")[0]
3179
+ }
3180
+ ]);
3181
+ },
3182
+ [dep, task, allTasks, onTasksChange]
3183
+ );
3184
+ const handleInputCommit = (0, import_react10.useCallback)(
3185
+ (raw) => {
3186
+ if (raw === "") {
3187
+ handleLagChange(0);
3188
+ return;
3189
+ }
3190
+ const parsed = parseInt(raw, 10);
3191
+ const effectiveLag2 = lag ?? 0;
3192
+ if (isNaN(parsed)) {
3193
+ const abs = Math.abs(effectiveLag2);
3194
+ setInputAbs(abs === 0 ? "" : String(abs));
3195
+ return;
3196
+ }
3197
+ let newLag;
3198
+ if (parsed === 0) {
3199
+ newLag = 0;
3200
+ } else if (dep.type === "SF") {
3201
+ newLag = -Math.abs(parsed);
3202
+ } else {
3203
+ newLag = parsed;
3204
+ }
3205
+ if (newLag !== effectiveLag2) handleLagChange(newLag);
3206
+ },
3207
+ [lag, dep.type, handleLagChange]
3208
+ );
3051
3209
  const Icon = LINK_TYPE_ICONS[dep.type];
3052
3210
  const depName = predecessorName ?? dep.taskId;
3053
3211
  const effectiveLag = lag ?? 0;
@@ -3086,67 +3244,92 @@ var DepChip = ({
3086
3244
  ]
3087
3245
  }
3088
3246
  ) }),
3089
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverContent, { className: "gantt-tl-dep-edit-popover", portal: true, align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { onClick: (e) => e.stopPropagation(), children: [
3090
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-dep-edit-task", children: task.name }),
3091
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-dep-edit-row", children: [
3092
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "gantt-tl-dep-edit-label", children: [
3093
- actionVerb,
3094
- preWord ? ` ${preWord}` : ""
3095
- ] }),
3096
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "gantt-tl-dep-edit-btn", onClick: () => handleLagChange(effectiveLag - 1), children: "\u2212" }),
3097
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3098
- "input",
3099
- {
3100
- type: "number",
3101
- className: "gantt-tl-dep-edit-input",
3102
- value: inputAbs,
3103
- placeholder: zeroPlaceholder,
3104
- min: "0",
3105
- onChange: (e) => setInputAbs(e.target.value),
3106
- onFocus: (e) => e.target.select(),
3107
- onBlur: (e) => handleInputCommit(e.target.value),
3108
- onKeyDown: (e) => {
3109
- if (e.key === "Enter") handleInputCommit(inputAbs);
3110
- }
3111
- }
3112
- ),
3113
- !(dep.type === "SF" && effectiveLag === 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "gantt-tl-dep-edit-btn", onClick: () => handleLagChange(effectiveLag + 1), children: "+" }),
3114
- effectiveLag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "\u0434." }),
3115
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: afterWhat })
3116
- ] }),
3117
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-dep-edit-pred", children: depName }),
3118
- !disableDependencyEditing && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3119
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("hr", { className: "gantt-tl-dep-edit-divider" }),
3120
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-dep-edit-actions", children: [
3121
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3122
- "button",
3123
- {
3124
- type: "button",
3125
- className: "gantt-tl-dep-edit-close",
3126
- onClick: () => {
3127
- setPopoverOpen(false);
3128
- onChipSelectClear();
3129
- },
3130
- children: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"
3131
- }
3132
- ),
3133
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3134
- "button",
3135
- {
3136
- type: "button",
3137
- className: "gantt-tl-dep-edit-delete",
3138
- onClick: handleTrashClick,
3139
- children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C"
3140
- }
3141
- )
3247
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3248
+ PopoverContent,
3249
+ {
3250
+ className: "gantt-tl-dep-edit-popover",
3251
+ portal: true,
3252
+ align: "start",
3253
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { onClick: (e) => e.stopPropagation(), children: [
3254
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-dep-edit-task", children: task.name }),
3255
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-dep-edit-row", children: [
3256
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "gantt-tl-dep-edit-label", children: [
3257
+ actionVerb,
3258
+ preWord ? ` ${preWord}` : ""
3259
+ ] }),
3260
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3261
+ "button",
3262
+ {
3263
+ type: "button",
3264
+ className: "gantt-tl-dep-edit-btn",
3265
+ onClick: () => handleLagChange(effectiveLag - 1),
3266
+ children: "\u2212"
3267
+ }
3268
+ ),
3269
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3270
+ "input",
3271
+ {
3272
+ type: "number",
3273
+ className: "gantt-tl-dep-edit-input",
3274
+ value: inputAbs,
3275
+ placeholder: zeroPlaceholder,
3276
+ min: "0",
3277
+ onChange: (e) => setInputAbs(e.target.value),
3278
+ onFocus: (e) => e.target.select(),
3279
+ onBlur: (e) => handleInputCommit(e.target.value),
3280
+ onKeyDown: (e) => {
3281
+ if (e.key === "Enter") handleInputCommit(inputAbs);
3282
+ }
3283
+ }
3284
+ ),
3285
+ !(dep.type === "SF" && effectiveLag === 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3286
+ "button",
3287
+ {
3288
+ type: "button",
3289
+ className: "gantt-tl-dep-edit-btn",
3290
+ onClick: () => handleLagChange(effectiveLag + 1),
3291
+ children: "+"
3292
+ }
3293
+ ),
3294
+ effectiveLag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "\u0434." }),
3295
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: afterWhat })
3296
+ ] }),
3297
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-dep-edit-pred", children: depName }),
3298
+ !disableDependencyEditing && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3299
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("hr", { className: "gantt-tl-dep-edit-divider" }),
3300
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-dep-edit-actions", children: [
3301
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3302
+ "button",
3303
+ {
3304
+ type: "button",
3305
+ className: "gantt-tl-dep-edit-close",
3306
+ onClick: () => {
3307
+ setPopoverOpen(false);
3308
+ onChipSelectClear();
3309
+ },
3310
+ children: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"
3311
+ }
3312
+ ),
3313
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3314
+ "button",
3315
+ {
3316
+ type: "button",
3317
+ className: "gantt-tl-dep-edit-delete",
3318
+ onClick: handleTrashClick,
3319
+ children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C"
3320
+ }
3321
+ )
3322
+ ] })
3323
+ ] })
3142
3324
  ] })
3143
- ] })
3144
- ] }) })
3325
+ }
3326
+ )
3145
3327
  ] });
3146
3328
  };
3147
3329
  var toISODate = (value) => {
3148
3330
  if (value instanceof Date) return value.toISOString().split("T")[0];
3149
- if (typeof value === "string" && value.includes("T")) return value.split("T")[0];
3331
+ if (typeof value === "string" && value.includes("T"))
3332
+ return value.split("T")[0];
3150
3333
  return value;
3151
3334
  };
3152
3335
  var TaskListRow = import_react10.default.memo(
@@ -3183,13 +3366,20 @@ var TaskListRow = import_react10.default.memo(
3183
3366
  onToggleCollapse,
3184
3367
  onPromoteTask,
3185
3368
  onDemoteTask,
3186
- isLastChild = true
3369
+ isLastChild = true,
3370
+ nestingDepth = 0,
3371
+ ancestorContinues = [],
3372
+ weekends,
3373
+ workdays,
3374
+ isWeekend: isWeekend3
3187
3375
  }) => {
3188
3376
  const [editingName, setEditingName] = (0, import_react10.useState)(false);
3189
3377
  const [nameValue, setNameValue] = (0, import_react10.useState)("");
3190
3378
  const nameInputRef = (0, import_react10.useRef)(null);
3191
3379
  const [editingDuration, setEditingDuration] = (0, import_react10.useState)(false);
3192
- const [durationValue, setDurationValue] = (0, import_react10.useState)(getInclusiveDurationDays(task.startDate, task.endDate));
3380
+ const [durationValue, setDurationValue] = (0, import_react10.useState)(
3381
+ getInclusiveDurationDays(task.startDate, task.endDate)
3382
+ );
3193
3383
  const durationInputRef = (0, import_react10.useRef)(null);
3194
3384
  const [editingProgress, setEditingProgress] = (0, import_react10.useState)(false);
3195
3385
  const [progressValue, setProgressValue] = (0, import_react10.useState)(0);
@@ -3199,11 +3389,16 @@ var TaskListRow = import_react10.default.memo(
3199
3389
  const durationConfirmedRef = (0, import_react10.useRef)(false);
3200
3390
  const progressConfirmedRef = (0, import_react10.useRef)(false);
3201
3391
  const autoEditedForRef = (0, import_react10.useRef)(null);
3202
- const editTriggerRef = (0, import_react10.useRef)("doubleclick");
3392
+ const editTriggerRef = (0, import_react10.useRef)(
3393
+ "doubleclick"
3394
+ );
3203
3395
  const [deletePending, setDeletePending] = (0, import_react10.useState)(false);
3204
3396
  const deleteButtonRef = (0, import_react10.useRef)(null);
3205
3397
  const isSelected = selectedTaskId === task.id;
3206
- const isParent = (0, import_react10.useMemo)(() => isTaskParent(task.id, allTasks), [task.id, allTasks]);
3398
+ const isParent = (0, import_react10.useMemo)(
3399
+ () => isTaskParent(task.id, allTasks),
3400
+ [task.id, allTasks]
3401
+ );
3207
3402
  const isChild = task.parentId !== void 0;
3208
3403
  const isCollapsed = collapsedParentIds.has(task.id);
3209
3404
  const isPicking = selectingPredecessorFor != null;
@@ -3258,31 +3453,40 @@ var TaskListRow = import_react10.default.memo(
3258
3453
  setEditingName(true);
3259
3454
  }
3260
3455
  }, [editingTaskId, task.id, disableTaskNameEditing]);
3261
- const handleNameClick = (0, import_react10.useCallback)((e) => {
3262
- if (disableTaskNameEditing) return;
3263
- e.stopPropagation();
3264
- onRowClick?.(task.id);
3265
- onScrollToTask?.(task.id);
3266
- }, [task.id, disableTaskNameEditing, onRowClick, onScrollToTask]);
3267
- const handleNameDoubleClick = (0, import_react10.useCallback)((e) => {
3268
- if (disableTaskNameEditing) return;
3269
- e.stopPropagation();
3270
- nameConfirmedRef.current = false;
3271
- editTriggerRef.current = "doubleclick";
3272
- setNameValue(task.name);
3273
- setEditingName(true);
3274
- }, [task.name, disableTaskNameEditing]);
3275
- const handleRowKeyDown = (0, import_react10.useCallback)((e) => {
3276
- if (editingProgress) return;
3277
- if (!editingName && !disableTaskNameEditing && e.key === "F2") {
3278
- e.preventDefault();
3456
+ const handleNameClick = (0, import_react10.useCallback)(
3457
+ (e) => {
3458
+ if (disableTaskNameEditing) return;
3459
+ e.stopPropagation();
3460
+ onRowClick?.(task.id);
3461
+ onScrollToTask?.(task.id);
3462
+ },
3463
+ [task.id, disableTaskNameEditing, onRowClick, onScrollToTask]
3464
+ );
3465
+ const handleNameDoubleClick = (0, import_react10.useCallback)(
3466
+ (e) => {
3467
+ if (disableTaskNameEditing) return;
3468
+ e.stopPropagation();
3279
3469
  nameConfirmedRef.current = false;
3280
- editTriggerRef.current = "keypress";
3470
+ editTriggerRef.current = "doubleclick";
3281
3471
  setNameValue(task.name);
3282
3472
  setEditingName(true);
3283
- return;
3284
- }
3285
- }, [editingName, disableTaskNameEditing, task.name]);
3473
+ },
3474
+ [task.name, disableTaskNameEditing]
3475
+ );
3476
+ const handleRowKeyDown = (0, import_react10.useCallback)(
3477
+ (e) => {
3478
+ if (editingProgress) return;
3479
+ if (!editingName && !disableTaskNameEditing && e.key === "F2") {
3480
+ e.preventDefault();
3481
+ nameConfirmedRef.current = false;
3482
+ editTriggerRef.current = "keypress";
3483
+ setNameValue(task.name);
3484
+ setEditingName(true);
3485
+ return;
3486
+ }
3487
+ },
3488
+ [editingName, disableTaskNameEditing, task.name]
3489
+ );
3286
3490
  const handleNameSave = (0, import_react10.useCallback)(() => {
3287
3491
  if (nameConfirmedRef.current) {
3288
3492
  nameConfirmedRef.current = false;
@@ -3296,24 +3500,32 @@ var TaskListRow = import_react10.default.memo(
3296
3500
  const handleNameCancel = (0, import_react10.useCallback)(() => {
3297
3501
  setEditingName(false);
3298
3502
  }, []);
3299
- const handleNameKeyDown = (0, import_react10.useCallback)((e) => {
3300
- if (e.key === "Enter") {
3301
- nameConfirmedRef.current = true;
3302
- if (nameValue.trim()) {
3303
- onTasksChange?.([{ ...task, name: nameValue.trim() }]);
3503
+ const handleNameKeyDown = (0, import_react10.useCallback)(
3504
+ (e) => {
3505
+ if (e.key === "Enter") {
3506
+ nameConfirmedRef.current = true;
3507
+ if (nameValue.trim()) {
3508
+ onTasksChange?.([{ ...task, name: nameValue.trim() }]);
3509
+ }
3510
+ setEditingName(false);
3511
+ } else if (e.key === "Escape") {
3512
+ handleNameCancel();
3304
3513
  }
3305
- setEditingName(false);
3306
- } else if (e.key === "Escape") {
3307
- handleNameCancel();
3308
- }
3309
- }, [nameValue, task, onTasksChange, handleNameCancel]);
3310
- const handleDurationClick = (0, import_react10.useCallback)((e) => {
3311
- if (task.locked) return;
3312
- e.stopPropagation();
3313
- durationConfirmedRef.current = false;
3314
- setDurationValue(getInclusiveDurationDays(task.startDate, task.endDate));
3315
- setEditingDuration(true);
3316
- }, [task.locked, task.startDate, task.endDate]);
3514
+ },
3515
+ [nameValue, task, onTasksChange, handleNameCancel]
3516
+ );
3517
+ const handleDurationClick = (0, import_react10.useCallback)(
3518
+ (e) => {
3519
+ if (task.locked) return;
3520
+ e.stopPropagation();
3521
+ durationConfirmedRef.current = false;
3522
+ setDurationValue(
3523
+ getInclusiveDurationDays(task.startDate, task.endDate)
3524
+ );
3525
+ setEditingDuration(true);
3526
+ },
3527
+ [task.locked, task.startDate, task.endDate]
3528
+ );
3317
3529
  const applyDurationChange = (0, import_react10.useCallback)((nextDuration) => {
3318
3530
  const normalizedDuration = Math.max(1, Math.round(nextDuration) || 1);
3319
3531
  setDurationValue(normalizedDuration);
@@ -3324,34 +3536,59 @@ var TaskListRow = import_react10.default.memo(
3324
3536
  return;
3325
3537
  }
3326
3538
  const normalizedDuration = Math.max(1, Math.round(durationValue) || 1);
3327
- onTasksChange?.([{ ...task, endDate: getEndDateFromDuration(task.startDate, normalizedDuration) }]);
3539
+ onTasksChange?.([
3540
+ {
3541
+ ...task,
3542
+ endDate: getEndDateFromDuration(task.startDate, normalizedDuration)
3543
+ }
3544
+ ]);
3328
3545
  setEditingDuration(false);
3329
3546
  }, [durationValue, task, onTasksChange]);
3330
3547
  const handleDurationCancel = (0, import_react10.useCallback)(() => {
3331
3548
  setDurationValue(getInclusiveDurationDays(task.startDate, task.endDate));
3332
3549
  setEditingDuration(false);
3333
3550
  }, [task.startDate, task.endDate]);
3334
- const handleDurationAdjust = (0, import_react10.useCallback)((delta) => {
3335
- applyDurationChange(durationValue + delta);
3336
- }, [applyDurationChange, durationValue]);
3337
- const handleDurationKeyDown = (0, import_react10.useCallback)((e) => {
3338
- e.stopPropagation();
3339
- if (e.key === "Enter") {
3340
- durationConfirmedRef.current = true;
3341
- const normalizedDuration = Math.max(1, Math.round(durationValue) || 1);
3342
- onTasksChange?.([{ ...task, endDate: getEndDateFromDuration(task.startDate, normalizedDuration) }]);
3343
- setEditingDuration(false);
3344
- } else if (e.key === "Escape") {
3345
- handleDurationCancel();
3346
- }
3347
- }, [durationValue, task, onTasksChange, handleDurationCancel]);
3348
- const handleProgressClick = (0, import_react10.useCallback)((e) => {
3349
- if (task.locked) return;
3350
- e.stopPropagation();
3351
- progressConfirmedRef.current = false;
3352
- setProgressValue(task.progress ?? 0);
3353
- setEditingProgress(true);
3354
- }, [task.progress, task.locked]);
3551
+ const handleDurationAdjust = (0, import_react10.useCallback)(
3552
+ (delta) => {
3553
+ applyDurationChange(durationValue + delta);
3554
+ },
3555
+ [applyDurationChange, durationValue]
3556
+ );
3557
+ const handleDurationKeyDown = (0, import_react10.useCallback)(
3558
+ (e) => {
3559
+ e.stopPropagation();
3560
+ if (e.key === "Enter") {
3561
+ durationConfirmedRef.current = true;
3562
+ const normalizedDuration = Math.max(
3563
+ 1,
3564
+ Math.round(durationValue) || 1
3565
+ );
3566
+ onTasksChange?.([
3567
+ {
3568
+ ...task,
3569
+ endDate: getEndDateFromDuration(
3570
+ task.startDate,
3571
+ normalizedDuration
3572
+ )
3573
+ }
3574
+ ]);
3575
+ setEditingDuration(false);
3576
+ } else if (e.key === "Escape") {
3577
+ handleDurationCancel();
3578
+ }
3579
+ },
3580
+ [durationValue, task, onTasksChange, handleDurationCancel]
3581
+ );
3582
+ const handleProgressClick = (0, import_react10.useCallback)(
3583
+ (e) => {
3584
+ if (task.locked) return;
3585
+ e.stopPropagation();
3586
+ progressConfirmedRef.current = false;
3587
+ setProgressValue(task.progress ?? 0);
3588
+ setEditingProgress(true);
3589
+ },
3590
+ [task.progress, task.locked]
3591
+ );
3355
3592
  const handleProgressSave = (0, import_react10.useCallback)(() => {
3356
3593
  if (progressConfirmedRef.current) {
3357
3594
  progressConfirmedRef.current = false;
@@ -3374,28 +3611,36 @@ var TaskListRow = import_react10.default.memo(
3374
3611
  setEditingProgress(false);
3375
3612
  }, []);
3376
3613
  const handleProgressAdjust = (0, import_react10.useCallback)((delta) => {
3377
- setProgressValue((current) => Math.max(0, Math.min(100, current + delta)));
3614
+ setProgressValue(
3615
+ (current) => Math.max(0, Math.min(100, current + delta))
3616
+ );
3378
3617
  }, []);
3379
- const handleProgressKeyDown = (0, import_react10.useCallback)((e) => {
3380
- e.stopPropagation();
3381
- if (e.key === "Enter") {
3382
- progressConfirmedRef.current = true;
3383
- const clampedValue = Math.max(0, Math.min(100, progressValue));
3384
- if ((clampedValue === 100 || clampedValue === 0) && isTaskParent(task.id, allTasks)) {
3385
- const children = getChildren(task.id, allTasks);
3386
- const updatedTasks = [
3387
- { ...task, progress: clampedValue },
3388
- ...children.map((child) => ({ ...child, progress: clampedValue }))
3389
- ];
3390
- onTasksChange?.(updatedTasks);
3391
- } else {
3392
- onTasksChange?.([{ ...task, progress: clampedValue }]);
3618
+ const handleProgressKeyDown = (0, import_react10.useCallback)(
3619
+ (e) => {
3620
+ e.stopPropagation();
3621
+ if (e.key === "Enter") {
3622
+ progressConfirmedRef.current = true;
3623
+ const clampedValue = Math.max(0, Math.min(100, progressValue));
3624
+ if ((clampedValue === 100 || clampedValue === 0) && isTaskParent(task.id, allTasks)) {
3625
+ const children = getChildren(task.id, allTasks);
3626
+ const updatedTasks = [
3627
+ { ...task, progress: clampedValue },
3628
+ ...children.map((child) => ({
3629
+ ...child,
3630
+ progress: clampedValue
3631
+ }))
3632
+ ];
3633
+ onTasksChange?.(updatedTasks);
3634
+ } else {
3635
+ onTasksChange?.([{ ...task, progress: clampedValue }]);
3636
+ }
3637
+ setEditingProgress(false);
3638
+ } else if (e.key === "Escape") {
3639
+ handleProgressCancel();
3393
3640
  }
3394
- setEditingProgress(false);
3395
- } else if (e.key === "Escape") {
3396
- handleProgressCancel();
3397
- }
3398
- }, [progressValue, task, onTasksChange, handleProgressCancel, allTasks]);
3641
+ },
3642
+ [progressValue, task, onTasksChange, handleProgressCancel, allTasks]
3643
+ );
3399
3644
  (0, import_react10.useEffect)(() => {
3400
3645
  if (editingProgress && progressInputRef.current) {
3401
3646
  progressInputRef.current.focus();
@@ -3411,77 +3656,111 @@ var TaskListRow = import_react10.default.memo(
3411
3656
  durationInputRef.current.select();
3412
3657
  }
3413
3658
  }, [editingDuration]);
3414
- const handleStartDateChange = (0, import_react10.useCallback)((newDateISO) => {
3415
- if (!newDateISO) return;
3416
- const origStart = parseUTCDate(task.startDate);
3417
- const origEnd = parseUTCDate(task.endDate);
3418
- const durationMs = origEnd.getTime() - origStart.getTime();
3419
- const newStart = /* @__PURE__ */ new Date(newDateISO + "T00:00:00Z");
3420
- const newEnd = new Date(newStart.getTime() + durationMs);
3421
- const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(
3422
- newDateISO,
3423
- newEnd.toISOString().split("T")[0]
3424
- );
3425
- onTasksChange?.([{ ...task, startDate: normalizedStart, endDate: normalizedEnd }]);
3426
- }, [task, onTasksChange]);
3427
- const handleEndDateChange = (0, import_react10.useCallback)((newDateISO) => {
3428
- if (!newDateISO) return;
3429
- const origStart = parseUTCDate(task.startDate);
3430
- const origEnd = parseUTCDate(task.endDate);
3431
- const durationMs = origEnd.getTime() - origStart.getTime();
3432
- const newEnd = /* @__PURE__ */ new Date(newDateISO + "T00:00:00Z");
3433
- const newStart = new Date(newEnd.getTime() - durationMs);
3434
- const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(
3435
- newStart.toISOString().split("T")[0],
3436
- newDateISO
3437
- );
3438
- onTasksChange?.([{ ...task, startDate: normalizedStart, endDate: normalizedEnd }]);
3439
- }, [task, onTasksChange]);
3659
+ const handleStartDateChange = (0, import_react10.useCallback)(
3660
+ (newDateISO) => {
3661
+ if (!newDateISO) return;
3662
+ const origStart = parseUTCDate(task.startDate);
3663
+ const origEnd = parseUTCDate(task.endDate);
3664
+ const durationMs = origEnd.getTime() - origStart.getTime();
3665
+ const newStart = /* @__PURE__ */ new Date(newDateISO + "T00:00:00Z");
3666
+ const newEnd = new Date(newStart.getTime() + durationMs);
3667
+ const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(newDateISO, newEnd.toISOString().split("T")[0]);
3668
+ onTasksChange?.([
3669
+ { ...task, startDate: normalizedStart, endDate: normalizedEnd }
3670
+ ]);
3671
+ },
3672
+ [task, onTasksChange]
3673
+ );
3674
+ const handleEndDateChange = (0, import_react10.useCallback)(
3675
+ (newDateISO) => {
3676
+ if (!newDateISO) return;
3677
+ const origStart = parseUTCDate(task.startDate);
3678
+ const origEnd = parseUTCDate(task.endDate);
3679
+ const durationMs = origEnd.getTime() - origStart.getTime();
3680
+ const newEnd = /* @__PURE__ */ new Date(newDateISO + "T00:00:00Z");
3681
+ const newStart = new Date(newEnd.getTime() - durationMs);
3682
+ const { startDate: normalizedStart, endDate: normalizedEnd } = normalizeTaskDates(newStart.toISOString().split("T")[0], newDateISO);
3683
+ onTasksChange?.([
3684
+ { ...task, startDate: normalizedStart, endDate: normalizedEnd }
3685
+ ]);
3686
+ },
3687
+ [task, onTasksChange]
3688
+ );
3440
3689
  const handleRowClickInternal = (0, import_react10.useCallback)(() => {
3441
3690
  onRowClick?.(task.id);
3442
3691
  }, [task.id, onRowClick]);
3443
- const handleNumberClick = (0, import_react10.useCallback)((e) => {
3444
- e.stopPropagation();
3445
- onRowClick?.(task.id);
3446
- }, [task.id, onRowClick]);
3447
- const handleToggleCollapse = (0, import_react10.useCallback)((e) => {
3448
- e.stopPropagation();
3449
- onToggleCollapse?.(task.id);
3450
- }, [task.id, onToggleCollapse]);
3451
- const handlePromote = (0, import_react10.useCallback)((e) => {
3452
- e.stopPropagation();
3453
- onPromoteTask?.(task.id);
3454
- }, [task.id, onPromoteTask]);
3455
- const handleDemote = (0, import_react10.useCallback)((e) => {
3456
- e.stopPropagation();
3457
- const currentIndex = allTasks.findIndex((t) => t.id === task.id);
3458
- if (currentIndex > 0) {
3459
- const previousTask = allTasks[currentIndex - 1];
3460
- const targetParentId = previousTask.parentId || previousTask.id;
3461
- onDemoteTask?.(task.id, targetParentId);
3462
- }
3463
- }, [task.id, allTasks, onDemoteTask]);
3464
- const handleAddClick = (0, import_react10.useCallback)((e) => {
3465
- e.stopPropagation();
3466
- onSetSelectingPredecessorFor?.(task.id);
3467
- }, [task.id, onSetSelectingPredecessorFor]);
3468
- const handlePredecessorPick = (0, import_react10.useCallback)((e) => {
3469
- e.stopPropagation();
3470
- if (!isPicking || isSourceRow) return;
3471
- if (!selectingPredecessorFor || !activeLinkType) return;
3472
- onAddDependency?.(task.id, selectingPredecessorFor, activeLinkType);
3473
- }, [isPicking, isSourceRow, selectingPredecessorFor, task.id, activeLinkType, onAddDependency]);
3474
- const handleCancelPicking = (0, import_react10.useCallback)((e) => {
3475
- e.stopPropagation();
3476
- onSetSelectingPredecessorFor?.(null);
3477
- }, [onSetSelectingPredecessorFor]);
3692
+ const handleNumberClick = (0, import_react10.useCallback)(
3693
+ (e) => {
3694
+ e.stopPropagation();
3695
+ onRowClick?.(task.id);
3696
+ },
3697
+ [task.id, onRowClick]
3698
+ );
3699
+ const handleToggleCollapse = (0, import_react10.useCallback)(
3700
+ (e) => {
3701
+ e.stopPropagation();
3702
+ onToggleCollapse?.(task.id);
3703
+ },
3704
+ [task.id, onToggleCollapse]
3705
+ );
3706
+ const handlePromote = (0, import_react10.useCallback)(
3707
+ (e) => {
3708
+ e.stopPropagation();
3709
+ onPromoteTask?.(task.id);
3710
+ },
3711
+ [task.id, onPromoteTask]
3712
+ );
3713
+ const handleDemote = (0, import_react10.useCallback)(
3714
+ (e) => {
3715
+ e.stopPropagation();
3716
+ onDemoteTask?.(task.id, "");
3717
+ },
3718
+ [task.id, onDemoteTask]
3719
+ );
3720
+ const handleAddClick = (0, import_react10.useCallback)(
3721
+ (e) => {
3722
+ e.stopPropagation();
3723
+ onSetSelectingPredecessorFor?.(task.id);
3724
+ },
3725
+ [task.id, onSetSelectingPredecessorFor]
3726
+ );
3727
+ const handlePredecessorPick = (0, import_react10.useCallback)(
3728
+ (e) => {
3729
+ e.stopPropagation();
3730
+ if (!isPicking || isSourceRow) return;
3731
+ if (!selectingPredecessorFor || !activeLinkType) return;
3732
+ onAddDependency?.(task.id, selectingPredecessorFor, activeLinkType);
3733
+ },
3734
+ [
3735
+ isPicking,
3736
+ isSourceRow,
3737
+ selectingPredecessorFor,
3738
+ task.id,
3739
+ activeLinkType,
3740
+ onAddDependency
3741
+ ]
3742
+ );
3743
+ const handleCancelPicking = (0, import_react10.useCallback)(
3744
+ (e) => {
3745
+ e.stopPropagation();
3746
+ onSetSelectingPredecessorFor?.(null);
3747
+ },
3748
+ [onSetSelectingPredecessorFor]
3749
+ );
3478
3750
  const isSelectedPredecessor = selectedChip != null && selectedChip.predecessorId === task.id;
3479
- const handleDeleteSelected = (0, import_react10.useCallback)((e) => {
3480
- e.stopPropagation();
3481
- if (!selectedChip) return;
3482
- onRemoveDependency?.(selectedChip.successorId, selectedChip.predecessorId, selectedChip.linkType);
3483
- onChipSelect?.(null);
3484
- }, [selectedChip, onRemoveDependency, onChipSelect]);
3751
+ const handleDeleteSelected = (0, import_react10.useCallback)(
3752
+ (e) => {
3753
+ e.stopPropagation();
3754
+ if (!selectedChip) return;
3755
+ onRemoveDependency?.(
3756
+ selectedChip.successorId,
3757
+ selectedChip.predecessorId,
3758
+ selectedChip.linkType
3759
+ );
3760
+ onChipSelect?.(null);
3761
+ },
3762
+ [selectedChip, onRemoveDependency, onChipSelect]
3763
+ );
3485
3764
  const startDateISO = toISODate(task.startDate);
3486
3765
  const endDateISO = editingDuration ? getEndDateFromDuration(task.startDate, durationValue) : toISODate(task.endDate);
3487
3766
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
@@ -3534,13 +3813,78 @@ var TaskListRow = import_react10.default.memo(
3534
3813
  }
3535
3814
  ),
3536
3815
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-cell gantt-tl-cell-name", children: [
3537
- isChild && !editingName && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(HierarchyConnectorIcon, { isLastChild }),
3816
+ isChild && !editingName && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3817
+ ancestorContinues.map(
3818
+ (continues, idx) => continues ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3819
+ "span",
3820
+ {
3821
+ style: {
3822
+ position: "absolute",
3823
+ left: `${idx * 20 + 9}px`,
3824
+ top: 0,
3825
+ height: `${rowHeight}px`,
3826
+ width: "1.5px",
3827
+ background: "#d4bceb",
3828
+ borderRadius: "1px",
3829
+ pointerEvents: "none"
3830
+ }
3831
+ },
3832
+ idx
3833
+ ) : null
3834
+ ),
3835
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3836
+ "span",
3837
+ {
3838
+ style: {
3839
+ position: "absolute",
3840
+ left: `${(nestingDepth - 1) * 20 + 9}px`,
3841
+ top: 0,
3842
+ height: isLastChild ? `${rowHeight / 2}px` : `${rowHeight}px`,
3843
+ width: "1.5px",
3844
+ background: "#d4bceb",
3845
+ borderRadius: "1px",
3846
+ pointerEvents: "none"
3847
+ }
3848
+ }
3849
+ ),
3850
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3851
+ "span",
3852
+ {
3853
+ style: {
3854
+ position: "absolute",
3855
+ left: `${(nestingDepth - 1) * 20 + 9}px`,
3856
+ top: `${rowHeight / 2 - 0.75}px`,
3857
+ width: "8px",
3858
+ height: "1.5px",
3859
+ background: "#d4bceb",
3860
+ borderRadius: "1px",
3861
+ pointerEvents: "none"
3862
+ }
3863
+ }
3864
+ ),
3865
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3866
+ "span",
3867
+ {
3868
+ style: {
3869
+ position: "absolute",
3870
+ left: `${(nestingDepth - 1) * 20 + 15}px`,
3871
+ top: `${rowHeight / 2 - 2}px`,
3872
+ width: "4px",
3873
+ height: "4px",
3874
+ borderRadius: "50%",
3875
+ background: "#d4bceb",
3876
+ pointerEvents: "none"
3877
+ }
3878
+ }
3879
+ )
3880
+ ] }),
3538
3881
  isParent && !editingName && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3539
3882
  "button",
3540
3883
  {
3541
3884
  type: "button",
3542
3885
  className: `gantt-tl-collapse-btn ${isCollapsed ? "gantt-tl-collapse-btn-collapsed" : ""}`,
3543
3886
  onClick: handleToggleCollapse,
3887
+ style: { left: `${nestingDepth * 20 + 4}px` },
3544
3888
  "aria-label": isCollapsed ? "Expand children" : "Collapse children",
3545
3889
  children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChevronRightIcon, {})
3546
3890
  }
@@ -3554,7 +3898,10 @@ var TaskListRow = import_react10.default.memo(
3554
3898
  onChange: (e) => setNameValue(e.target.value),
3555
3899
  onBlur: handleNameSave,
3556
3900
  onKeyDown: handleNameKeyDown,
3557
- className: ["gantt-tl-name-input", isChild ? "gantt-tl-name-input-child" : ""].filter(Boolean).join(" "),
3901
+ className: "gantt-tl-name-input",
3902
+ style: {
3903
+ paddingLeft: nestingDepth > 0 ? `${nestingDepth * 20 + 24}px` : void 0
3904
+ },
3558
3905
  onClick: (e) => e.stopPropagation()
3559
3906
  }
3560
3907
  ),
@@ -3564,14 +3911,15 @@ var TaskListRow = import_react10.default.memo(
3564
3911
  type: "button",
3565
3912
  className: [
3566
3913
  "gantt-tl-name-trigger",
3567
- disableTaskNameEditing ? "gantt-tl-name-locked" : "",
3568
- isParent ? "gantt-tl-name-trigger-parent" : "",
3569
- isChild ? "gantt-tl-name-trigger-child" : ""
3914
+ disableTaskNameEditing ? "gantt-tl-name-locked" : ""
3570
3915
  ].filter(Boolean).join(" "),
3571
3916
  title: task.name,
3572
3917
  onClick: handleNameClick,
3573
3918
  onDoubleClick: handleNameDoubleClick,
3574
- style: editingName ? { visibility: "hidden", pointerEvents: "none" } : void 0,
3919
+ style: {
3920
+ paddingLeft: nestingDepth > 0 ? `${nestingDepth * 20 + (isParent ? 26 : 8)}px` : isParent ? "26px" : void 0,
3921
+ ...editingName ? { visibility: "hidden", pointerEvents: "none" } : void 0
3922
+ },
3575
3923
  children: task.name
3576
3924
  }
3577
3925
  ),
@@ -3584,16 +3932,20 @@ var TaskListRow = import_react10.default.memo(
3584
3932
  onClick: (e) => {
3585
3933
  e.stopPropagation();
3586
3934
  const now = /* @__PURE__ */ new Date();
3587
- const todayISO = new Date(Date.UTC(
3588
- now.getUTCFullYear(),
3589
- now.getUTCMonth(),
3590
- now.getUTCDate()
3591
- )).toISOString().split("T")[0];
3592
- const endISO = new Date(Date.UTC(
3593
- now.getUTCFullYear(),
3594
- now.getUTCMonth(),
3595
- now.getUTCDate() + 7
3596
- )).toISOString().split("T")[0];
3935
+ const todayISO = new Date(
3936
+ Date.UTC(
3937
+ now.getUTCFullYear(),
3938
+ now.getUTCMonth(),
3939
+ now.getUTCDate()
3940
+ )
3941
+ ).toISOString().split("T")[0];
3942
+ const endISO = new Date(
3943
+ Date.UTC(
3944
+ now.getUTCFullYear(),
3945
+ now.getUTCMonth(),
3946
+ now.getUTCDate() + 7
3947
+ )
3948
+ ).toISOString().split("T")[0];
3597
3949
  const newTask = {
3598
3950
  id: crypto.randomUUID(),
3599
3951
  name: "\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430",
@@ -3629,7 +3981,6 @@ var TaskListRow = import_react10.default.memo(
3629
3981
  HierarchyButton,
3630
3982
  {
3631
3983
  isChild,
3632
- isParent,
3633
3984
  rowIndex,
3634
3985
  onPromote: onPromoteTask ? handlePromote : void 0,
3635
3986
  onDemote: onDemoteTask ? handleDemote : void 0
@@ -3637,113 +3988,234 @@ var TaskListRow = import_react10.default.memo(
3637
3988
  )
3638
3989
  ] })
3639
3990
  ] }),
3640
- /* @__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)(
3641
- DatePicker,
3991
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3992
+ "div",
3642
3993
  {
3643
- value: startDateISO,
3644
- onChange: handleStartDateChange,
3645
- format: "dd.MM.yy",
3646
- portal: true,
3647
- disabled: task.locked
3994
+ className: "gantt-tl-cell gantt-tl-cell-date",
3995
+ onClick: (e) => e.stopPropagation(),
3996
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3997
+ DatePicker,
3998
+ {
3999
+ value: startDateISO,
4000
+ onChange: handleStartDateChange,
4001
+ format: "dd.MM.yy",
4002
+ portal: true,
4003
+ disabled: task.locked,
4004
+ weekends,
4005
+ workdays,
4006
+ isWeekend: isWeekend3
4007
+ }
4008
+ )
3648
4009
  }
3649
- ) }),
3650
- /* @__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)(
3651
- DatePicker,
4010
+ ),
4011
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4012
+ "div",
3652
4013
  {
3653
- value: endDateISO,
3654
- onChange: handleEndDateChange,
3655
- format: "dd.MM.yy",
3656
- portal: true,
3657
- disabled: task.locked
3658
- }
3659
- ) }),
3660
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-cell gantt-tl-cell-duration", onClick: handleDurationClick, children: [
3661
- editingDuration && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-number-editor", onClick: (e) => e.stopPropagation(), children: [
3662
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3663
- Input,
4014
+ className: "gantt-tl-cell gantt-tl-cell-date",
4015
+ onClick: (e) => e.stopPropagation(),
4016
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4017
+ DatePicker,
3664
4018
  {
3665
- ref: durationInputRef,
3666
- type: "number",
3667
- min: 1,
3668
- step: 1,
3669
- value: durationValue,
3670
- onChange: (e) => applyDurationChange(parseInt(e.target.value, 10) || 1),
3671
- onBlur: handleDurationSave,
3672
- onKeyDown: handleDurationKeyDown,
3673
- className: "gantt-tl-number-input"
4019
+ value: endDateISO,
4020
+ onChange: handleEndDateChange,
4021
+ format: "dd.MM.yy",
4022
+ portal: true,
4023
+ disabled: task.locked,
4024
+ weekends,
4025
+ workdays,
4026
+ isWeekend: isWeekend3
3674
4027
  }
3675
- ),
3676
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-number-steppers", "aria-hidden": "true", children: [
3677
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3678
- "button",
4028
+ )
4029
+ }
4030
+ ),
4031
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4032
+ "div",
4033
+ {
4034
+ className: "gantt-tl-cell gantt-tl-cell-duration",
4035
+ onClick: handleDurationClick,
4036
+ children: [
4037
+ editingDuration && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4038
+ "div",
3679
4039
  {
3680
- type: "button",
3681
- className: "gantt-tl-number-stepper",
3682
- tabIndex: -1,
3683
- onMouseDown: (e) => e.preventDefault(),
3684
- onClick: () => handleDurationAdjust(1),
3685
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m18 15-6-6-6 6" }) })
4040
+ className: "gantt-tl-number-editor",
4041
+ onClick: (e) => e.stopPropagation(),
4042
+ children: [
4043
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4044
+ Input,
4045
+ {
4046
+ ref: durationInputRef,
4047
+ type: "number",
4048
+ min: 1,
4049
+ step: 1,
4050
+ value: durationValue,
4051
+ onChange: (e) => applyDurationChange(parseInt(e.target.value, 10) || 1),
4052
+ onBlur: handleDurationSave,
4053
+ onKeyDown: handleDurationKeyDown,
4054
+ className: "gantt-tl-number-input"
4055
+ }
4056
+ ),
4057
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-number-steppers", "aria-hidden": "true", children: [
4058
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4059
+ "button",
4060
+ {
4061
+ type: "button",
4062
+ className: "gantt-tl-number-stepper",
4063
+ tabIndex: -1,
4064
+ onMouseDown: (e) => e.preventDefault(),
4065
+ onClick: () => handleDurationAdjust(1),
4066
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4067
+ "svg",
4068
+ {
4069
+ xmlns: "http://www.w3.org/2000/svg",
4070
+ width: "10",
4071
+ height: "10",
4072
+ viewBox: "0 0 24 24",
4073
+ fill: "none",
4074
+ stroke: "currentColor",
4075
+ strokeWidth: "2",
4076
+ strokeLinecap: "round",
4077
+ strokeLinejoin: "round",
4078
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m18 15-6-6-6 6" })
4079
+ }
4080
+ )
4081
+ }
4082
+ ),
4083
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4084
+ "button",
4085
+ {
4086
+ type: "button",
4087
+ className: "gantt-tl-number-stepper",
4088
+ tabIndex: -1,
4089
+ onMouseDown: (e) => e.preventDefault(),
4090
+ onClick: () => handleDurationAdjust(-1),
4091
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4092
+ "svg",
4093
+ {
4094
+ xmlns: "http://www.w3.org/2000/svg",
4095
+ width: "10",
4096
+ height: "10",
4097
+ viewBox: "0 0 24 24",
4098
+ fill: "none",
4099
+ stroke: "currentColor",
4100
+ strokeWidth: "2",
4101
+ strokeLinecap: "round",
4102
+ strokeLinejoin: "round",
4103
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" })
4104
+ }
4105
+ )
4106
+ }
4107
+ )
4108
+ ] })
4109
+ ]
3686
4110
  }
3687
4111
  ),
3688
4112
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3689
- "button",
4113
+ "span",
3690
4114
  {
3691
- type: "button",
3692
- className: "gantt-tl-number-stepper",
3693
- tabIndex: -1,
3694
- onMouseDown: (e) => e.preventDefault(),
3695
- onClick: () => handleDurationAdjust(-1),
3696
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" }) })
4115
+ style: editingDuration ? { visibility: "hidden", pointerEvents: "none" } : void 0,
4116
+ children: getInclusiveDurationDays(task.startDate, task.endDate)
3697
4117
  }
3698
4118
  )
3699
- ] })
3700
- ] }),
3701
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: editingDuration ? { visibility: "hidden", pointerEvents: "none" } : void 0, children: getInclusiveDurationDays(task.startDate, task.endDate) })
3702
- ] }),
3703
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-cell gantt-tl-cell-progress", onClick: handleProgressClick, children: [
3704
- editingProgress && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-number-editor", onClick: (e) => e.stopPropagation(), children: [
3705
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3706
- Input,
3707
- {
3708
- ref: progressInputRef,
3709
- type: "number",
3710
- min: 0,
3711
- max: 100,
3712
- step: 1,
3713
- value: progressValue,
3714
- onChange: (e) => setProgressValue(parseInt(e.target.value, 10) || 0),
3715
- onBlur: handleProgressSave,
3716
- onKeyDown: handleProgressKeyDown,
3717
- className: "gantt-tl-number-input"
3718
- }
3719
- ),
3720
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-number-steppers", "aria-hidden": "true", children: [
3721
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3722
- "button",
4119
+ ]
4120
+ }
4121
+ ),
4122
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4123
+ "div",
4124
+ {
4125
+ className: "gantt-tl-cell gantt-tl-cell-progress",
4126
+ onClick: handleProgressClick,
4127
+ children: [
4128
+ editingProgress && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4129
+ "div",
3723
4130
  {
3724
- type: "button",
3725
- className: "gantt-tl-number-stepper",
3726
- tabIndex: -1,
3727
- onMouseDown: (e) => e.preventDefault(),
3728
- onClick: () => handleProgressAdjust(1),
3729
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m18 15-6-6-6 6" }) })
4131
+ className: "gantt-tl-number-editor",
4132
+ onClick: (e) => e.stopPropagation(),
4133
+ children: [
4134
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4135
+ Input,
4136
+ {
4137
+ ref: progressInputRef,
4138
+ type: "number",
4139
+ min: 0,
4140
+ max: 100,
4141
+ step: 1,
4142
+ value: progressValue,
4143
+ onChange: (e) => setProgressValue(parseInt(e.target.value, 10) || 0),
4144
+ onBlur: handleProgressSave,
4145
+ onKeyDown: handleProgressKeyDown,
4146
+ className: "gantt-tl-number-input"
4147
+ }
4148
+ ),
4149
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-number-steppers", "aria-hidden": "true", children: [
4150
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4151
+ "button",
4152
+ {
4153
+ type: "button",
4154
+ className: "gantt-tl-number-stepper",
4155
+ tabIndex: -1,
4156
+ onMouseDown: (e) => e.preventDefault(),
4157
+ onClick: () => handleProgressAdjust(1),
4158
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4159
+ "svg",
4160
+ {
4161
+ xmlns: "http://www.w3.org/2000/svg",
4162
+ width: "10",
4163
+ height: "10",
4164
+ viewBox: "0 0 24 24",
4165
+ fill: "none",
4166
+ stroke: "currentColor",
4167
+ strokeWidth: "2",
4168
+ strokeLinecap: "round",
4169
+ strokeLinejoin: "round",
4170
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m18 15-6-6-6 6" })
4171
+ }
4172
+ )
4173
+ }
4174
+ ),
4175
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4176
+ "button",
4177
+ {
4178
+ type: "button",
4179
+ className: "gantt-tl-number-stepper",
4180
+ tabIndex: -1,
4181
+ onMouseDown: (e) => e.preventDefault(),
4182
+ onClick: () => handleProgressAdjust(-1),
4183
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4184
+ "svg",
4185
+ {
4186
+ xmlns: "http://www.w3.org/2000/svg",
4187
+ width: "10",
4188
+ height: "10",
4189
+ viewBox: "0 0 24 24",
4190
+ fill: "none",
4191
+ stroke: "currentColor",
4192
+ strokeWidth: "2",
4193
+ strokeLinecap: "round",
4194
+ strokeLinejoin: "round",
4195
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" })
4196
+ }
4197
+ )
4198
+ }
4199
+ )
4200
+ ] })
4201
+ ]
3730
4202
  }
3731
4203
  ),
3732
4204
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3733
- "button",
4205
+ "span",
3734
4206
  {
3735
- type: "button",
3736
- className: "gantt-tl-number-stepper",
3737
- tabIndex: -1,
3738
- onMouseDown: (e) => e.preventDefault(),
3739
- onClick: () => handleProgressAdjust(-1),
3740
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" }) })
4207
+ style: editingProgress ? { visibility: "hidden", pointerEvents: "none" } : task.progress === 100 ? {
4208
+ backgroundColor: "#17c864",
4209
+ borderRadius: "4px",
4210
+ padding: "2px 4px",
4211
+ color: "#ffffff"
4212
+ } : void 0,
4213
+ children: task.progress ? Math.round(task.progress) === 100 ? "100" : `${Math.round(task.progress)}%` : "0%"
3741
4214
  }
3742
4215
  )
3743
- ] })
3744
- ] }),
3745
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: editingProgress ? { visibility: "hidden", pointerEvents: "none" } : void 0, children: task.progress ? `${Math.round(task.progress)}%` : "0%" })
3746
- ] }),
4216
+ ]
4217
+ }
4218
+ ),
3747
4219
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3748
4220
  "div",
3749
4221
  {
@@ -3784,26 +4256,33 @@ var TaskListRow = import_react10.default.memo(
3784
4256
  ]
3785
4257
  }
3786
4258
  ) }),
3787
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "gantt-tl-dep-overflow-list", onClick: (e) => e.stopPropagation(), children: chips.map(({ dep, lag, predecessorName }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3788
- DepChip,
4259
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverContent, { portal: true, align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4260
+ "div",
3789
4261
  {
3790
- lag,
3791
- dep,
3792
- taskId: task.id,
3793
- predecessorName,
3794
- selectedChip,
3795
- disableDependencyEditing,
3796
- onChipSelect,
3797
- onRowClick,
3798
- onScrollToTask,
3799
- onRemoveDependency,
3800
- onChipSelectClear: () => onChipSelect?.(null),
3801
- task,
3802
- allTasks,
3803
- onTasksChange
3804
- },
3805
- `${dep.taskId}-${dep.type}`
3806
- )) }) })
4262
+ className: "gantt-tl-dep-overflow-list",
4263
+ onClick: (e) => e.stopPropagation(),
4264
+ children: chips.map(({ dep, lag, predecessorName }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4265
+ DepChip,
4266
+ {
4267
+ lag,
4268
+ dep,
4269
+ taskId: task.id,
4270
+ predecessorName,
4271
+ selectedChip,
4272
+ disableDependencyEditing,
4273
+ onChipSelect,
4274
+ onRowClick,
4275
+ onScrollToTask,
4276
+ onRemoveDependency,
4277
+ onChipSelectClear: () => onChipSelect?.(null),
4278
+ task,
4279
+ allTasks,
4280
+ onTasksChange
4281
+ },
4282
+ `${dep.taskId}-${dep.type}`
4283
+ ))
4284
+ }
4285
+ ) })
3807
4286
  ] })
3808
4287
  ) : chips.length === 1 ? (
3809
4288
  /* Single chip — unified DepChip */
@@ -3968,7 +4447,10 @@ var TaskList = ({
3968
4447
  collapsedParentIds: externalCollapsedParentIds,
3969
4448
  onToggleCollapse: externalOnToggleCollapse,
3970
4449
  onPromoteTask,
3971
- onDemoteTask
4450
+ onDemoteTask,
4451
+ weekends,
4452
+ workdays,
4453
+ isWeekend: isWeekend3
3972
4454
  }) => {
3973
4455
  const [internalCollapsedParentIds, setInternalCollapsedParentIds] = (0, import_react12.useState)(/* @__PURE__ */ new Set());
3974
4456
  const collapsedParentIds = externalCollapsedParentIds ?? internalCollapsedParentIds;
@@ -4002,6 +4484,25 @@ var TaskList = ({
4002
4484
  () => visibleTasks.length * rowHeight,
4003
4485
  [visibleTasks.length, rowHeight]
4004
4486
  );
4487
+ const nestingDepthMap = (0, import_react12.useMemo)(() => {
4488
+ const depthMap = /* @__PURE__ */ new Map();
4489
+ const taskById = new Map(tasks.map((t) => [t.id, t]));
4490
+ function getDepth(taskId) {
4491
+ if (depthMap.has(taskId)) return depthMap.get(taskId);
4492
+ const task = taskById.get(taskId);
4493
+ if (!task || !task.parentId || !taskById.has(task.parentId)) {
4494
+ depthMap.set(taskId, 0);
4495
+ return 0;
4496
+ }
4497
+ const depth = getDepth(task.parentId) + 1;
4498
+ depthMap.set(taskId, depth);
4499
+ return depth;
4500
+ }
4501
+ for (const task of tasks) {
4502
+ getDepth(task.id);
4503
+ }
4504
+ return depthMap;
4505
+ }, [tasks]);
4005
4506
  const lastChildIds = (0, import_react12.useMemo)(() => {
4006
4507
  const last = /* @__PURE__ */ new Set();
4007
4508
  const seenParents = /* @__PURE__ */ new Set();
@@ -4014,6 +4515,20 @@ var TaskList = ({
4014
4515
  }
4015
4516
  return last;
4016
4517
  }, [visibleTasks]);
4518
+ const ancestorContinuesMap = (0, import_react12.useMemo)(() => {
4519
+ const taskById = new Map(tasks.map((t) => [t.id, t]));
4520
+ const map = /* @__PURE__ */ new Map();
4521
+ for (const task of visibleTasks) {
4522
+ const continues = [];
4523
+ let current = taskById.get(task.id);
4524
+ while (current?.parentId && taskById.has(current.parentId)) {
4525
+ continues.unshift(!lastChildIds.has(current.id));
4526
+ current = taskById.get(current.parentId);
4527
+ }
4528
+ map.set(task.id, continues.slice(0, -1));
4529
+ }
4530
+ return map;
4531
+ }, [tasks, visibleTasks, lastChildIds]);
4017
4532
  const handleRowClick = (0, import_react12.useCallback)((taskId) => {
4018
4533
  onTaskSelect?.(taskId);
4019
4534
  }, [onTaskSelect]);
@@ -4124,9 +4639,6 @@ var TaskList = ({
4124
4639
  if (dropTarget.parentId === draggedTaskId) {
4125
4640
  return false;
4126
4641
  }
4127
- if (dropTarget.parentId) {
4128
- return false;
4129
- }
4130
4642
  const draggedTask = orderedTasks.find((t) => t.id === draggedTaskId);
4131
4643
  if (!draggedTask) return true;
4132
4644
  const descendants = getAllDescendants(draggedTaskId, orderedTasks);
@@ -4272,6 +4784,52 @@ var TaskList = ({
4272
4784
  setIsCreating(false);
4273
4785
  }, [onAdd]);
4274
4786
  const handleCancelNewTask = (0, import_react12.useCallback)(() => setIsCreating(false), []);
4787
+ function getTaskDepth(task, tasks2) {
4788
+ if (!task) return 0;
4789
+ let depth = 0;
4790
+ let current = task;
4791
+ while (current) {
4792
+ if (!current.parentId) break;
4793
+ depth++;
4794
+ const parentId = current.parentId;
4795
+ current = tasks2.find((t) => t.id === parentId);
4796
+ }
4797
+ return depth;
4798
+ }
4799
+ const handleDemoteWrapper = (0, import_react12.useCallback)((taskId, _newParentId) => {
4800
+ const taskIndex = visibleTasks.findIndex((t) => t.id === taskId);
4801
+ const currentTask = visibleTasks[taskIndex];
4802
+ const currentDepth = getTaskDepth(currentTask, orderedTasks);
4803
+ if (taskIndex > 0) {
4804
+ for (let i = taskIndex - 1; i >= 0; i--) {
4805
+ const previousTask = visibleTasks[i];
4806
+ const previousDepth = getTaskDepth(previousTask, orderedTasks);
4807
+ if (previousDepth === currentDepth) {
4808
+ onDemoteTask?.(taskId, previousTask.id);
4809
+ return;
4810
+ }
4811
+ if (previousDepth < currentDepth) {
4812
+ break;
4813
+ }
4814
+ }
4815
+ return;
4816
+ }
4817
+ const demotedTask = orderedTasks.find((t) => t.id === taskId);
4818
+ if (!demotedTask) return;
4819
+ const newSectionTask = {
4820
+ id: crypto.randomUUID(),
4821
+ name: "\u041D\u043E\u0432\u044B\u0439 \u0440\u0430\u0437\u0434\u0435\u043B",
4822
+ startDate: demotedTask.startDate,
4823
+ endDate: demotedTask.endDate
4824
+ };
4825
+ const updatedTasks = [
4826
+ newSectionTask,
4827
+ ...orderedTasks.map(
4828
+ (t) => t.id === taskId ? { ...t, parentId: newSectionTask.id } : t
4829
+ )
4830
+ ];
4831
+ onReorder?.(updatedTasks, taskId, newSectionTask.id);
4832
+ }, [visibleTasks, orderedTasks, onDemoteTask, onReorder]);
4275
4833
  const effectiveTaskListWidth = Math.max(taskListWidth, MIN_TASK_LIST_WIDTH);
4276
4834
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4277
4835
  "div",
@@ -4355,8 +4913,13 @@ var TaskList = ({
4355
4913
  collapsedParentIds,
4356
4914
  onToggleCollapse: handleToggleCollapse,
4357
4915
  onPromoteTask,
4358
- onDemoteTask,
4359
- isLastChild: lastChildIds.has(task.id)
4916
+ onDemoteTask: onDemoteTask ? handleDemoteWrapper : void 0,
4917
+ isLastChild: lastChildIds.has(task.id),
4918
+ nestingDepth: nestingDepthMap.get(task.id) ?? 0,
4919
+ ancestorContinues: ancestorContinuesMap.get(task.id) ?? [],
4920
+ weekends,
4921
+ workdays,
4922
+ isWeekend: isWeekend3
4360
4923
  },
4361
4924
  task.id
4362
4925
  )) }),
@@ -4424,7 +4987,10 @@ var GanttChart = (0, import_react13.forwardRef)(({
4424
4987
  onPromoteTask,
4425
4988
  onDemoteTask,
4426
4989
  enableAddTask = true,
4427
- viewMode = "day"
4990
+ viewMode = "day",
4991
+ weekends,
4992
+ workdays,
4993
+ isWeekend: isWeekend3
4428
4994
  }, ref) => {
4429
4995
  const scrollContainerRef = (0, import_react13.useRef)(null);
4430
4996
  const [selectedTaskId, setSelectedTaskId] = (0, import_react13.useState)(null);
@@ -4433,6 +4999,10 @@ var GanttChart = (0, import_react13.forwardRef)(({
4433
4999
  const [collapsedParentIds, setCollapsedParentIds] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
4434
5000
  const [editingTaskId, setEditingTaskId] = (0, import_react13.useState)(null);
4435
5001
  const normalizedTasks = (0, import_react13.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
5002
+ const isCustomWeekend = (0, import_react13.useMemo)(
5003
+ () => createIsWeekendPredicate({ weekends, workdays, isWeekend: isWeekend3 }),
5004
+ [weekends, workdays, isWeekend3]
5005
+ );
4436
5006
  const dateRange = (0, import_react13.useMemo)(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
4437
5007
  const [validationResult, setValidationResult] = (0, import_react13.useState)(null);
4438
5008
  const [cascadeOverrides, setCascadeOverrides] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
@@ -4665,6 +5235,17 @@ var GanttChart = (0, import_react13.forwardRef)(({
4665
5235
  }),
4666
5236
  [scrollToToday, scrollToTask, handleCollapseAll, handleExpandAll]
4667
5237
  );
5238
+ function getTaskDepth(taskId, tasks2) {
5239
+ let depth = 0;
5240
+ let current = tasks2.find((t) => t.id === taskId);
5241
+ while (current) {
5242
+ if (!current.parentId) break;
5243
+ depth++;
5244
+ const parentId = current.parentId;
5245
+ current = tasks2.find((t) => t.id === parentId);
5246
+ }
5247
+ return depth;
5248
+ }
4668
5249
  const handlePromoteTask = (0, import_react13.useCallback)((taskId) => {
4669
5250
  if (onPromoteTask) {
4670
5251
  onPromoteTask(taskId);
@@ -4674,20 +5255,20 @@ var GanttChart = (0, import_react13.forwardRef)(({
4674
5255
  if (!taskToPromote || !taskToPromote.parentId) {
4675
5256
  return;
4676
5257
  }
4677
- const parentId = taskToPromote.parentId;
4678
- const siblings = tasks.filter((t) => t.parentId === parentId);
5258
+ const depth = getTaskDepth(taskId, tasks);
5259
+ const grandparentId = depth > 1 ? tasks.find((t) => t.id === taskToPromote.parentId)?.parentId : void 0;
5260
+ const currentParentId = taskToPromote.parentId;
5261
+ const siblings = tasks.filter((t) => t.parentId === currentParentId);
5262
+ const promotedTask = { ...taskToPromote, parentId: grandparentId };
4679
5263
  if (siblings.length <= 1) {
4680
- const promotedTask2 = { ...taskToPromote, parentId: void 0 };
4681
- onTasksChange?.([promotedTask2]);
5264
+ onTasksChange?.([promotedTask]);
4682
5265
  return;
4683
5266
  }
4684
- const lastSiblingIndex = tasks.map((t, i) => ({ task: t, index: i })).filter(({ task }) => task.parentId === parentId).sort((a, b) => b.index - a.index)[0];
5267
+ const lastSiblingIndex = tasks.map((t, i) => ({ task: t, index: i })).filter(({ task }) => task.parentId === currentParentId).sort((a, b) => b.index - a.index)[0];
4685
5268
  if (!lastSiblingIndex) {
4686
- const promotedTask2 = { ...taskToPromote, parentId: void 0 };
4687
- onTasksChange?.([promotedTask2]);
5269
+ onTasksChange?.([promotedTask]);
4688
5270
  return;
4689
5271
  }
4690
- const promotedTask = { ...taskToPromote, parentId: void 0 };
4691
5272
  const reorderedTasks = normalizeHierarchyTasks([
4692
5273
  ...tasks.filter((t) => t.id !== taskId).slice(0, lastSiblingIndex.index + 1),
4693
5274
  promotedTask,
@@ -4813,7 +5394,10 @@ var GanttChart = (0, import_react13.forwardRef)(({
4813
5394
  collapsedParentIds,
4814
5395
  onToggleCollapse: handleToggleCollapse,
4815
5396
  onPromoteTask: onPromoteTask ?? handlePromoteTask,
4816
- onDemoteTask: onDemoteTask ?? handleDemoteTask
5397
+ onDemoteTask: onDemoteTask ?? handleDemoteTask,
5398
+ weekends,
5399
+ workdays,
5400
+ isWeekend: isWeekend3
4817
5401
  }
4818
5402
  ),
4819
5403
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { minWidth: `${gridWidth}px`, flex: 1 }, children: [
@@ -4823,7 +5407,8 @@ var GanttChart = (0, import_react13.forwardRef)(({
4823
5407
  days: dateRange,
4824
5408
  dayWidth,
4825
5409
  headerHeight,
4826
- viewMode
5410
+ viewMode,
5411
+ isCustomWeekend
4827
5412
  }
4828
5413
  ) }),
4829
5414
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
@@ -4841,7 +5426,8 @@ var GanttChart = (0, import_react13.forwardRef)(({
4841
5426
  dateRange,
4842
5427
  dayWidth,
4843
5428
  totalHeight: totalGridHeight,
4844
- viewMode
5429
+ viewMode,
5430
+ isCustomWeekend
4845
5431
  }
4846
5432
  ),
4847
5433
  todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
@@ -4956,6 +5542,8 @@ init_dateUtils();
4956
5542
  computeLagFromDates,
4957
5543
  computeParentDates,
4958
5544
  computeParentProgress,
5545
+ createDateKey,
5546
+ createIsWeekendPredicate,
4959
5547
  detectCycles,
4960
5548
  detectEdgeZone,
4961
5549
  findParentId,