gantt-lib 0.79.0 → 0.80.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.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-CZzZrxn-.mjs';
3
- export { D as DAY_MS, c as DependencyError, G as GanttChartMode, d as GanttDateRange, e as GridConfig, f as GridLine, L as LinkType, M as MonthSpan, g as ResourceTimelineMove, h as TaskBarGeometry, W as WeekendBlock, i as alignToWorkingDay, j as areTasksHierarchicallyRelated, k as buildAdjacencyList, l as buildTaskRangeFromEnd, m as buildTaskRangeFromStart, n as calculateSuccessorDate, o as cascadeByLinks, p as clampTaskRangeForIncomingFS, q as computeLagFromDates, r as computeParentDates, s as computeParentProgress, t as detectCycles, u as findParentId, v as getAllDependencyEdges, w as getAllDescendants, x as getBusinessDayOffset, y as getChildren, z as getDependencyLag, A as getSuccessorChain, B as getTaskDuration, C as getTransitiveCascadeChain, E as isAncestorTask, F as isTaskParent, H as moveTaskRange, I as moveTaskWithCascade, J as normalizeDependencyLag, K as normalizePredecessorDates, N as normalizeUTCDate, O as parseDateOnly, P as recalculateIncomingLags, Q as recalculateProjectSchedule, S as recalculateTaskFromDependencies, U as reflowTasksOnModeSwitch, X as removeDependenciesBetweenTasks, Y as resizeTaskWithCascade, Z as shiftBusinessDayOffset, _ as universalCascade, $ as validateDependencies } from './index-CZzZrxn-.mjs';
2
+ import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-DAfJyZbj.mjs';
3
+ export { D as DAY_MS, c as DependencyError, G as GanttChartMode, d as GanttDateRange, e as GridConfig, f as GridLine, L as LinkType, M as MonthSpan, g as ResourceTimelineMove, h as ResourceTimelineResourceMenuCommand, i as TaskBarGeometry, W as WeekendBlock, j as alignToWorkingDay, k as areTasksHierarchicallyRelated, l as buildAdjacencyList, m as buildTaskRangeFromEnd, n as buildTaskRangeFromStart, o as calculateSuccessorDate, p as cascadeByLinks, q as clampTaskRangeForIncomingFS, r as computeLagFromDates, s as computeParentDates, t as computeParentProgress, u as detectCycles, v as findParentId, w as getAllDependencyEdges, x as getAllDescendants, y as getBusinessDayOffset, z as getChildren, A as getDependencyLag, B as getSuccessorChain, C as getTaskDuration, E as getTransitiveCascadeChain, F as isAncestorTask, H as isTaskParent, I as moveTaskRange, J as moveTaskWithCascade, K as normalizeDependencyLag, N as normalizePredecessorDates, O as normalizeUTCDate, P as parseDateOnly, Q as recalculateIncomingLags, S as recalculateProjectSchedule, U as recalculateTaskFromDependencies, X as reflowTasksOnModeSwitch, Y as removeDependenciesBetweenTasks, Z as resizeTaskWithCascade, _ as shiftBusinessDayOffset, $ as universalCascade, a0 as validateDependencies } from './index-DAfJyZbj.mjs';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import * as RadixPopover from '@radix-ui/react-popover';
6
6
 
@@ -263,6 +263,10 @@ declare const not: (predicate: TaskPredicate) => TaskPredicate;
263
263
  interface WithoutDepsOptions {
264
264
  /** If true, only child tasks (tasks with parentId) can match. */
265
265
  onlyChildren?: boolean;
266
+ /** Full task list, required when excluding parent rows. */
267
+ tasks?: Task$1[];
268
+ /** If true, only non-parent regular tasks can match. Requires tasks. */
269
+ onlyLeafTasks?: boolean;
266
270
  }
267
271
  /**
268
272
  * Filter tasks that have no dependencies
@@ -545,7 +549,7 @@ declare const GanttChart: <TTask extends Task = Task, TItem extends ResourceTime
545
549
  ref?: React$1.Ref<GanttChartHandle>;
546
550
  }) => React$1.ReactElement;
547
551
 
548
- declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, viewMode, rowHeaderWidth, laneHeight, headerHeight, containerHeight, allowVerticalPan, customDays, isWeekend, businessDays, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemClick, onResourceItemMove, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
552
+ declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, viewMode, rowHeaderWidth, laneHeight, headerHeight, containerHeight, allowVerticalPan, customDays, isWeekend, businessDays, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemClick, onResourceItemMove, onAddResource, enableAddResource, resourceMenuCommands, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
549
553
 
550
554
  interface TaskRowProps {
551
555
  /** Task data to render */
@@ -648,6 +652,12 @@ interface GridBackgroundProps {
648
652
  viewMode?: 'day' | 'week' | 'month';
649
653
  /** Optional predicate for custom weekend logic (e.g., holidays, shift patterns) */
650
654
  isCustomWeekend?: (date: Date) => boolean;
655
+ /** Optional extra class for layered rendering contexts. */
656
+ className?: string;
657
+ /** Whether to render weekend background blocks. */
658
+ showWeekendBlocks?: boolean;
659
+ /** Whether to render vertical grid lines. */
660
+ showGridLines?: boolean;
651
661
  }
652
662
  /**
653
663
  * GridBackground component - renders vertical grid lines and weekend background highlighting
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-CZzZrxn-.js';
3
- export { D as DAY_MS, c as DependencyError, G as GanttChartMode, d as GanttDateRange, e as GridConfig, f as GridLine, L as LinkType, M as MonthSpan, g as ResourceTimelineMove, h as TaskBarGeometry, W as WeekendBlock, i as alignToWorkingDay, j as areTasksHierarchicallyRelated, k as buildAdjacencyList, l as buildTaskRangeFromEnd, m as buildTaskRangeFromStart, n as calculateSuccessorDate, o as cascadeByLinks, p as clampTaskRangeForIncomingFS, q as computeLagFromDates, r as computeParentDates, s as computeParentProgress, t as detectCycles, u as findParentId, v as getAllDependencyEdges, w as getAllDescendants, x as getBusinessDayOffset, y as getChildren, z as getDependencyLag, A as getSuccessorChain, B as getTaskDuration, C as getTransitiveCascadeChain, E as isAncestorTask, F as isTaskParent, H as moveTaskRange, I as moveTaskWithCascade, J as normalizeDependencyLag, K as normalizePredecessorDates, N as normalizeUTCDate, O as parseDateOnly, P as recalculateIncomingLags, Q as recalculateProjectSchedule, S as recalculateTaskFromDependencies, U as reflowTasksOnModeSwitch, X as removeDependenciesBetweenTasks, Y as resizeTaskWithCascade, Z as shiftBusinessDayOffset, _ as universalCascade, $ as validateDependencies } from './index-CZzZrxn-.js';
2
+ import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-DAfJyZbj.js';
3
+ export { D as DAY_MS, c as DependencyError, G as GanttChartMode, d as GanttDateRange, e as GridConfig, f as GridLine, L as LinkType, M as MonthSpan, g as ResourceTimelineMove, h as ResourceTimelineResourceMenuCommand, i as TaskBarGeometry, W as WeekendBlock, j as alignToWorkingDay, k as areTasksHierarchicallyRelated, l as buildAdjacencyList, m as buildTaskRangeFromEnd, n as buildTaskRangeFromStart, o as calculateSuccessorDate, p as cascadeByLinks, q as clampTaskRangeForIncomingFS, r as computeLagFromDates, s as computeParentDates, t as computeParentProgress, u as detectCycles, v as findParentId, w as getAllDependencyEdges, x as getAllDescendants, y as getBusinessDayOffset, z as getChildren, A as getDependencyLag, B as getSuccessorChain, C as getTaskDuration, E as getTransitiveCascadeChain, F as isAncestorTask, H as isTaskParent, I as moveTaskRange, J as moveTaskWithCascade, K as normalizeDependencyLag, N as normalizePredecessorDates, O as normalizeUTCDate, P as parseDateOnly, Q as recalculateIncomingLags, S as recalculateProjectSchedule, U as recalculateTaskFromDependencies, X as reflowTasksOnModeSwitch, Y as removeDependenciesBetweenTasks, Z as resizeTaskWithCascade, _ as shiftBusinessDayOffset, $ as universalCascade, a0 as validateDependencies } from './index-DAfJyZbj.js';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import * as RadixPopover from '@radix-ui/react-popover';
6
6
 
@@ -263,6 +263,10 @@ declare const not: (predicate: TaskPredicate) => TaskPredicate;
263
263
  interface WithoutDepsOptions {
264
264
  /** If true, only child tasks (tasks with parentId) can match. */
265
265
  onlyChildren?: boolean;
266
+ /** Full task list, required when excluding parent rows. */
267
+ tasks?: Task$1[];
268
+ /** If true, only non-parent regular tasks can match. Requires tasks. */
269
+ onlyLeafTasks?: boolean;
266
270
  }
267
271
  /**
268
272
  * Filter tasks that have no dependencies
@@ -545,7 +549,7 @@ declare const GanttChart: <TTask extends Task = Task, TItem extends ResourceTime
545
549
  ref?: React$1.Ref<GanttChartHandle>;
546
550
  }) => React$1.ReactElement;
547
551
 
548
- declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, viewMode, rowHeaderWidth, laneHeight, headerHeight, containerHeight, allowVerticalPan, customDays, isWeekend, businessDays, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemClick, onResourceItemMove, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
552
+ declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, viewMode, rowHeaderWidth, laneHeight, headerHeight, containerHeight, allowVerticalPan, customDays, isWeekend, businessDays, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemClick, onResourceItemMove, onAddResource, enableAddResource, resourceMenuCommands, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
549
553
 
550
554
  interface TaskRowProps {
551
555
  /** Task data to render */
@@ -648,6 +652,12 @@ interface GridBackgroundProps {
648
652
  viewMode?: 'day' | 'week' | 'month';
649
653
  /** Optional predicate for custom weekend logic (e.g., holidays, shift patterns) */
650
654
  isCustomWeekend?: (date: Date) => boolean;
655
+ /** Optional extra class for layered rendering contexts. */
656
+ className?: string;
657
+ /** Whether to render weekend background blocks. */
658
+ showWeekendBlocks?: boolean;
659
+ /** Whether to render vertical grid lines. */
660
+ showGridLines?: boolean;
651
661
  }
652
662
  /**
653
663
  * GridBackground component - renders vertical grid lines and weekend background highlighting
package/dist/index.js CHANGED
@@ -3137,7 +3137,7 @@ var arePropsEqual2 = (prevProps, nextProps) => {
3137
3137
  prevProps.viewMode === nextProps.viewMode && prevProps.isCustomWeekend === nextProps.isCustomWeekend;
3138
3138
  };
3139
3139
  var GridBackground = import_react5.default.memo(
3140
- ({ dateRange, dayWidth, totalHeight, viewMode = "day", isCustomWeekend }) => {
3140
+ ({ dateRange, dayWidth, totalHeight, viewMode = "day", isCustomWeekend, className, showWeekendBlocks = true, showGridLines = true }) => {
3141
3141
  const weekGridLines = (0, import_react5.useMemo)(() => {
3142
3142
  if (viewMode !== "week") return [];
3143
3143
  return calculateWeekGridLines(dateRange, dayWidth);
@@ -3160,13 +3160,13 @@ var GridBackground = import_react5.default.memo(
3160
3160
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3161
3161
  "div",
3162
3162
  {
3163
- className: "gantt-gb-gridBackground",
3163
+ className: ["gantt-gb-gridBackground", className].filter(Boolean).join(" "),
3164
3164
  style: {
3165
3165
  width: `${gridWidth}px`,
3166
3166
  height: `${totalHeight}px`
3167
3167
  },
3168
3168
  children: [
3169
- weekendBlocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3169
+ showWeekendBlocks && weekendBlocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3170
3170
  "div",
3171
3171
  {
3172
3172
  className: "gantt-gb-weekendBlock",
@@ -3177,7 +3177,7 @@ var GridBackground = import_react5.default.memo(
3177
3177
  },
3178
3178
  `weekend-${index}`
3179
3179
  )),
3180
- viewMode === "week" ? (
3180
+ showGridLines && (viewMode === "week" ? (
3181
3181
  // Week-view: one line per week column boundary
3182
3182
  weekGridLines.map((line, index) => {
3183
3183
  const lineClass = line.isMonthStart ? "gantt-gb-monthSeparator" : "gantt-gb-weekSeparator";
@@ -3218,7 +3218,7 @@ var GridBackground = import_react5.default.memo(
3218
3218
  `gridline-${index}`
3219
3219
  );
3220
3220
  })
3221
- )
3221
+ ))
3222
3222
  ]
3223
3223
  }
3224
3224
  );
@@ -7447,6 +7447,33 @@ var calculateConflictInfo = (parsedItems) => {
7447
7447
  }
7448
7448
  return result;
7449
7449
  };
7450
+ var countConflictOverlaps = (items) => {
7451
+ const conflictsByItemId = new Map(items.map((item) => [item.itemId, item.conflictsWith]));
7452
+ const visited = /* @__PURE__ */ new Set();
7453
+ let overlapCount = 0;
7454
+ for (const item of items) {
7455
+ if (visited.has(item.itemId) || item.conflictsWith.length === 0) {
7456
+ continue;
7457
+ }
7458
+ const stack = [item.itemId];
7459
+ let componentSize = 0;
7460
+ while (stack.length > 0) {
7461
+ const itemId = stack.pop();
7462
+ if (visited.has(itemId)) {
7463
+ continue;
7464
+ }
7465
+ visited.add(itemId);
7466
+ componentSize += 1;
7467
+ for (const conflictId of conflictsByItemId.get(itemId) ?? []) {
7468
+ if (!visited.has(conflictId)) {
7469
+ stack.push(conflictId);
7470
+ }
7471
+ }
7472
+ }
7473
+ overlapCount += Math.max(0, componentSize - 1);
7474
+ }
7475
+ return overlapCount;
7476
+ };
7450
7477
  var layoutResourceTimelineItems = (resources, options) => {
7451
7478
  const rows = [];
7452
7479
  const items = [];
@@ -7506,7 +7533,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7506
7533
  }
7507
7534
  const laneCount = Math.max(1, laneEndDays.length);
7508
7535
  const resourceRowHeight = laneCount * options.laneHeight;
7509
- const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7536
+ const conflictCount = countConflictOverlaps(laidOutItems);
7510
7537
  const row = {
7511
7538
  resource,
7512
7539
  resourceId: resource.id,
@@ -7751,7 +7778,8 @@ var DEFAULT_ROW_HEADER_WIDTH = 240;
7751
7778
  var DEFAULT_RESOURCE_ROW_GAP = 8;
7752
7779
  var ITEM_OUTER_VERTICAL_INSET = 2;
7753
7780
  var ITEM_INNER_VERTICAL_INSET = 1;
7754
- var ITEM_HORIZONTAL_INSET = 1;
7781
+ var ITEM_START_HORIZONTAL_INSET = 2;
7782
+ var ITEM_END_HORIZONTAL_INSET = 1;
7755
7783
  var isValidDate = (date) => !Number.isNaN(date.getTime());
7756
7784
  var collectValidItems = (resources) => {
7757
7785
  return resources.flatMap(
@@ -7773,12 +7801,18 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7773
7801
  const topInset = laneIndex === 0 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7774
7802
  const bottomInset = laneIndex === laneCount - 1 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7775
7803
  return {
7776
- left: geometry.left + ITEM_HORIZONTAL_INSET,
7804
+ left: geometry.left + ITEM_START_HORIZONTAL_INSET,
7777
7805
  top: geometry.top + topInset,
7778
- width: Math.max(0, geometry.width - ITEM_HORIZONTAL_INSET * 2),
7806
+ width: Math.max(1, geometry.width - ITEM_START_HORIZONTAL_INSET - ITEM_END_HORIZONTAL_INSET),
7779
7807
  height: Math.max(0, geometry.height - topInset - bottomInset)
7780
7808
  };
7781
7809
  };
7810
+ var formatOverlapCount = (count) => {
7811
+ const mod10 = count % 10;
7812
+ const mod100 = count % 100;
7813
+ const word = mod10 === 1 && mod100 !== 11 ? "\u043D\u0430\u043B\u043E\u0436\u0435\u043D\u0438\u0435" : mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14) ? "\u043D\u0430\u043B\u043E\u0436\u0435\u043D\u0438\u044F" : "\u043D\u0430\u043B\u043E\u0436\u0435\u043D\u0438\u0439";
7814
+ return `${count} ${word}`;
7815
+ };
7782
7816
  var getWeekendOverlaySegments = (startDate, endDate, dayWidth, weekendPredicate) => {
7783
7817
  const segments2 = [];
7784
7818
  const current = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
@@ -7821,7 +7855,143 @@ var getRangeOverlaySegments = (itemStartDate, ranges, dayWidth) => {
7821
7855
  };
7822
7856
  });
7823
7857
  };
7858
+ var clampOverlaySegments = (segments2, maxWidth) => segments2.flatMap((segment) => {
7859
+ const left = Math.max(0, Math.min(segment.left, maxWidth));
7860
+ const right = Math.max(left, Math.min(segment.left + segment.width, maxWidth));
7861
+ const width = right - left;
7862
+ return width > 0 ? [{ left, width }] : [];
7863
+ });
7824
7864
  var getDurationValue = (startDate, endDate, businessDays, weekendPredicate) => businessDays ? getBusinessDaysCount(startDate, endDate, weekendPredicate) : Math.max(1, Math.round((endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1e3)) + 1);
7865
+ var ResourceHeader = ({
7866
+ resource,
7867
+ resourceId,
7868
+ conflictCount,
7869
+ height,
7870
+ paddingBottom,
7871
+ menuCommands
7872
+ }) => {
7873
+ const [menuOpen, setMenuOpen] = (0, import_react14.useState)(false);
7874
+ const visibleCommands = (0, import_react14.useMemo)(
7875
+ () => menuCommands.filter((command) => command.isVisible?.(resource) ?? true),
7876
+ [menuCommands, resource]
7877
+ );
7878
+ const hasMenu = visibleCommands.length > 0;
7879
+ const handleCommandClick = (command, event) => {
7880
+ event.stopPropagation();
7881
+ if (command.closeOnSelect !== false) {
7882
+ setMenuOpen(false);
7883
+ }
7884
+ command.onSelect(resource);
7885
+ };
7886
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7887
+ "div",
7888
+ {
7889
+ className: `gantt-resourceTimeline-resourceHeader${menuOpen ? " gantt-resourceTimeline-resourceHeaderMenuOpen" : ""}`,
7890
+ "data-resource-row-id": resourceId,
7891
+ style: {
7892
+ height: `${height}px`,
7893
+ paddingBottom: `${paddingBottom}px`
7894
+ },
7895
+ children: [
7896
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceName", children: resource.name }),
7897
+ conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7898
+ "span",
7899
+ {
7900
+ className: "gantt-resourceTimeline-conflictBadge",
7901
+ "aria-label": formatOverlapCount(conflictCount),
7902
+ title: formatOverlapCount(conflictCount),
7903
+ children: conflictCount
7904
+ }
7905
+ ),
7906
+ hasMenu && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: menuOpen, onOpenChange: setMenuOpen, children: [
7907
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7908
+ "button",
7909
+ {
7910
+ className: "gantt-resourceTimeline-resourceMenuButton",
7911
+ type: "button",
7912
+ "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
7913
+ onClick: (event) => {
7914
+ event.stopPropagation();
7915
+ setMenuOpen((open) => !open);
7916
+ },
7917
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u22EE" })
7918
+ }
7919
+ ) }),
7920
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverContent, { className: "gantt-resourceTimeline-resourceMenu", portal: true, align: "end", children: visibleCommands.map((command) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7921
+ "button",
7922
+ {
7923
+ type: "button",
7924
+ className: `gantt-resourceTimeline-resourceMenuItem${command.danger ? " gantt-resourceTimeline-resourceMenuItemDanger" : ""}`,
7925
+ disabled: command.isDisabled?.(resource) ?? false,
7926
+ onClick: (event) => handleCommandClick(command, event),
7927
+ children: [
7928
+ command.icon,
7929
+ command.label
7930
+ ]
7931
+ },
7932
+ command.id
7933
+ )) })
7934
+ ] })
7935
+ ]
7936
+ }
7937
+ );
7938
+ };
7939
+ var NewResourceRow = ({ height, onConfirm, onCancel }) => {
7940
+ const [nameValue, setNameValue] = (0, import_react14.useState)("");
7941
+ const inputRef = (0, import_react14.useRef)(null);
7942
+ const confirmedRef = (0, import_react14.useRef)(false);
7943
+ (0, import_react14.useEffect)(() => {
7944
+ inputRef.current?.focus();
7945
+ inputRef.current?.select();
7946
+ }, []);
7947
+ const handleCancel = () => {
7948
+ confirmedRef.current = true;
7949
+ onCancel();
7950
+ };
7951
+ const handleConfirm = () => {
7952
+ const name = nameValue.trim();
7953
+ if (!name) {
7954
+ handleCancel();
7955
+ return;
7956
+ }
7957
+ confirmedRef.current = true;
7958
+ onConfirm(name);
7959
+ };
7960
+ const handleKeyDown = (event) => {
7961
+ if (event.key === "Enter") {
7962
+ handleConfirm();
7963
+ } else if (event.key === "Escape") {
7964
+ handleCancel();
7965
+ }
7966
+ };
7967
+ const handleBlur = () => {
7968
+ if (!confirmedRef.current) {
7969
+ handleConfirm();
7970
+ }
7971
+ };
7972
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7973
+ "div",
7974
+ {
7975
+ className: "gantt-resourceTimeline-resourceHeader gantt-resourceTimeline-resourceHeaderNew",
7976
+ style: {
7977
+ height: `${height}px`,
7978
+ paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7979
+ },
7980
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7981
+ Input,
7982
+ {
7983
+ ref: inputRef,
7984
+ value: nameValue,
7985
+ onChange: (event) => setNameValue(event.target.value),
7986
+ onKeyDown: handleKeyDown,
7987
+ onBlur: handleBlur,
7988
+ placeholder: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
7989
+ className: "gantt-resourceTimeline-resourceInput"
7990
+ }
7991
+ )
7992
+ }
7993
+ );
7994
+ };
7825
7995
  function ResourceTimelineChart({
7826
7996
  resources,
7827
7997
  dayWidth = DEFAULT_DAY_WIDTH,
@@ -7839,11 +8009,15 @@ function ResourceTimelineChart({
7839
8009
  renderItem,
7840
8010
  getItemClassName,
7841
8011
  onResourceItemClick,
7842
- onResourceItemMove
8012
+ onResourceItemMove,
8013
+ onAddResource,
8014
+ enableAddResource = true,
8015
+ resourceMenuCommands = []
7843
8016
  }) {
7844
8017
  const scrollContainerRef = (0, import_react14.useRef)(null);
7845
8018
  const gridRef = (0, import_react14.useRef)(null);
7846
8019
  const panStateRef = (0, import_react14.useRef)(null);
8020
+ const [isCreatingResource, setIsCreatingResource] = (0, import_react14.useState)(false);
7847
8021
  const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
7848
8022
  const dateRange = (0, import_react14.useMemo)(() => getMultiMonthDays(validItems), [validItems]);
7849
8023
  const monthStart = (0, import_react14.useMemo)(() => {
@@ -7878,6 +8052,18 @@ function ResourceTimelineChart({
7878
8052
  }
7879
8053
  return map;
7880
8054
  }, [layout.items]);
8055
+ const canAddResource = enableAddResource && Boolean(onAddResource);
8056
+ const resourceAddRowHeight = laneHeight + DEFAULT_RESOURCE_ROW_GAP;
8057
+ const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
8058
+ const handleConfirmNewResource = (0, import_react14.useCallback)((name) => {
8059
+ onAddResource?.({
8060
+ id: crypto.randomUUID(),
8061
+ name,
8062
+ items: []
8063
+ });
8064
+ setIsCreatingResource(false);
8065
+ }, [onAddResource]);
8066
+ const handleCancelNewResource = (0, import_react14.useCallback)(() => setIsCreatingResource(false), []);
7881
8067
  const { preview, startDrag } = useResourceItemDrag({
7882
8068
  dayWidth,
7883
8069
  monthStart,
@@ -7972,28 +8158,34 @@ function ResourceTimelineChart({
7972
8158
  style: { height: `${headerHeight}px` }
7973
8159
  }
7974
8160
  ),
7975
- layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7976
- "div",
8161
+ layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8162
+ ResourceHeader,
7977
8163
  {
7978
- className: "gantt-resourceTimeline-resourceHeader",
7979
- "data-resource-row-id": row.resourceId,
7980
- style: {
7981
- height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`,
7982
- paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7983
- },
7984
- children: [
7985
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name }),
7986
- row.conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7987
- "span",
7988
- {
7989
- className: "gantt-resourceTimeline-conflictBadge",
7990
- "aria-label": `${row.conflictCount} \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u043E\u0432`,
7991
- children: row.conflictCount
7992
- }
7993
- )
7994
- ]
8164
+ resource: row.resource,
8165
+ resourceId: row.resourceId,
8166
+ conflictCount: row.conflictCount,
8167
+ height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
8168
+ paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
8169
+ menuCommands: resourceMenuCommands
7995
8170
  },
7996
8171
  row.resourceId
8172
+ )),
8173
+ canAddResource && (isCreatingResource ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8174
+ NewResourceRow,
8175
+ {
8176
+ height: resourceAddRowHeight,
8177
+ onConfirm: handleConfirmNewResource,
8178
+ onCancel: handleCancelNewResource
8179
+ }
8180
+ ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8181
+ "button",
8182
+ {
8183
+ className: "gantt-resourceTimeline-addResourceButton",
8184
+ type: "button",
8185
+ style: { height: `${resourceAddRowHeight}px` },
8186
+ onClick: () => setIsCreatingResource(true),
8187
+ children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441"
8188
+ }
7997
8189
  ))
7998
8190
  ]
7999
8191
  }
@@ -8019,14 +8211,14 @@ function ResourceTimelineChart({
8019
8211
  {
8020
8212
  ref: gridRef,
8021
8213
  className: "gantt-resourceTimeline-grid",
8022
- style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
8214
+ style: { width: `${gridWidth}px`, height: `${displayTotalHeight}px` },
8023
8215
  children: [
8024
8216
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8025
8217
  GridBackground_default,
8026
8218
  {
8027
8219
  dateRange,
8028
8220
  dayWidth,
8029
- totalHeight: layout.totalHeight,
8221
+ totalHeight: displayTotalHeight,
8030
8222
  viewMode,
8031
8223
  isCustomWeekend: weekendPredicate
8032
8224
  }
@@ -8044,6 +8236,17 @@ function ResourceTimelineChart({
8044
8236
  },
8045
8237
  row.resourceId
8046
8238
  )),
8239
+ canAddResource && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8240
+ "div",
8241
+ {
8242
+ className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
8243
+ "data-resource-add-row": "true",
8244
+ style: {
8245
+ top: `${layout.totalHeight}px`,
8246
+ height: `${resourceAddRowHeight}px`
8247
+ }
8248
+ }
8249
+ ),
8047
8250
  Array.from(itemsByResourceId.values()).flatMap(
8048
8251
  (resourceItems) => resourceItems.map((layoutItem) => {
8049
8252
  const customClassName = getItemClassName?.(layoutItem.item);
@@ -8069,11 +8272,17 @@ function ResourceTimelineChart({
8069
8272
  }, layoutItem.laneIndex, laneCount);
8070
8273
  const overlayStartDate = isDraggingItem ? preview.startDate : layoutItem.startDate;
8071
8274
  const overlayEndDate = isDraggingItem ? preview.endDate : layoutItem.endDate;
8072
- const weekendOverlaySegments = businessDays ? getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate) : [];
8073
- const conflictOverlaySegments = getRangeOverlaySegments(
8074
- overlayStartDate,
8075
- isDraggingItem ? [] : layoutItem.conflictRanges,
8076
- dayWidth
8275
+ const weekendOverlaySegments = businessDays ? clampOverlaySegments(
8276
+ getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate),
8277
+ itemGeometry.width
8278
+ ) : [];
8279
+ const conflictOverlaySegments = clampOverlaySegments(
8280
+ getRangeOverlaySegments(
8281
+ overlayStartDate,
8282
+ isDraggingItem ? [] : layoutItem.conflictRanges,
8283
+ dayWidth
8284
+ ),
8285
+ itemGeometry.width
8077
8286
  );
8078
8287
  const durationValue = getDurationValue(
8079
8288
  overlayStartDate,
@@ -9232,6 +9441,11 @@ var not = (predicate) => (task) => !predicate(task);
9232
9441
  var withoutDeps = (options = {}) => (task) => {
9233
9442
  if (!task) return false;
9234
9443
  if (options.onlyChildren && !task.parentId) return false;
9444
+ if (options.onlyLeafTasks) {
9445
+ const isParent = (options.tasks ?? []).some((candidate) => candidate.parentId === task.id);
9446
+ const isRegularTask = task.type == null || task.type === "task";
9447
+ if (isParent || !isRegularTask) return false;
9448
+ }
9235
9449
  return !task.dependencies || task.dependencies.length === 0;
9236
9450
  };
9237
9451
  var expired = (referenceDate = /* @__PURE__ */ new Date()) => (task) => isTaskExpired(task, referenceDate);