gantt-lib 0.79.0 → 0.80.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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
  );
@@ -5542,19 +5542,6 @@ var TaskListRow = import_react10.default.memo(
5542
5542
  );
5543
5543
  const isSelectedPredecessor = selectedChip != null && selectedChip.predecessorId === task.id;
5544
5544
  const isSelectedDependencyOwner = selectedChip != null && selectedChip.successorId === task.id;
5545
- const handleDeleteSelected = (0, import_react10.useCallback)(
5546
- (e) => {
5547
- e.stopPropagation();
5548
- if (!selectedChip) return;
5549
- onRemoveDependency?.(
5550
- selectedChip.successorId,
5551
- selectedChip.predecessorId,
5552
- selectedChip.linkType
5553
- );
5554
- onChipSelect?.(null);
5555
- },
5556
- [selectedChip?.successorId, selectedChip?.predecessorId, selectedChip?.linkType, onRemoveDependency, onChipSelect]
5557
- );
5558
5545
  const startDateISO = toISODate2(normalizedTask.startDate);
5559
5546
  const endDateISO = editingDuration ? getEndDate(normalizedTask.startDate, durationValue) : toISODate2(normalizedTask.endDate);
5560
5547
  const numberCell = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
@@ -5766,7 +5753,7 @@ var TaskListRow = import_react10.default.memo(
5766
5753
  "aria-hidden": "true"
5767
5754
  }
5768
5755
  ),
5769
- !editingName && (onInsertAfter || onDelete || onPromoteTask || onDemoteTask || onUngroupTask || onDuplicateTask || onTasksChange || hasContextMenu) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-name-actions", children: [
5756
+ !editingName && (onInsertAfter || onDelete || onPromoteTask || onDemoteTask || onUngroupTask || onDuplicateTask || onTasksChange || hasContextMenu) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `gantt-tl-name-actions${contextMenuOpen ? " gantt-tl-name-actions-open" : ""}`, children: [
5770
5757
  onInsertAfter && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5771
5758
  "button",
5772
5759
  {
@@ -6182,22 +6169,7 @@ var TaskListRow = import_react10.default.memo(
6182
6169
  }
6183
6170
  ),
6184
6171
  sourcePickerContent
6185
- ] }) : isSelectedPredecessor && !disableDependencyEditing ? (
6186
- /* Full-replacement: "Зависит от [name]" → hover → "Удалить" */
6187
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6188
- "button",
6189
- {
6190
- type: "button",
6191
- className: "gantt-tl-dep-delete-label",
6192
- onClick: handleDeleteSelected,
6193
- "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u044F\u0437\u044C",
6194
- children: [
6195
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-delete-label-default", children: "\u0421\u0432\u044F\u0437\u0430\u043D\u043E \u0441" }),
6196
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-delete-label-hover", children: "\xD7 \u0443\u0434\u0430\u043B\u0438\u0442\u044C" })
6197
- ]
6198
- }
6199
- )
6200
- ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
6172
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
6201
6173
  chips.length >= 2 ? (
6202
6174
  /* 2+ deps — show only "N связей" summary chip that opens a popover */
6203
6175
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: overflowOpen, onOpenChange: setOverflowOpen, children: [
@@ -7447,6 +7419,33 @@ var calculateConflictInfo = (parsedItems) => {
7447
7419
  }
7448
7420
  return result;
7449
7421
  };
7422
+ var countConflictOverlaps = (items) => {
7423
+ const conflictsByItemId = new Map(items.map((item) => [item.itemId, item.conflictsWith]));
7424
+ const visited = /* @__PURE__ */ new Set();
7425
+ let overlapCount = 0;
7426
+ for (const item of items) {
7427
+ if (visited.has(item.itemId) || item.conflictsWith.length === 0) {
7428
+ continue;
7429
+ }
7430
+ const stack = [item.itemId];
7431
+ let componentSize = 0;
7432
+ while (stack.length > 0) {
7433
+ const itemId = stack.pop();
7434
+ if (visited.has(itemId)) {
7435
+ continue;
7436
+ }
7437
+ visited.add(itemId);
7438
+ componentSize += 1;
7439
+ for (const conflictId of conflictsByItemId.get(itemId) ?? []) {
7440
+ if (!visited.has(conflictId)) {
7441
+ stack.push(conflictId);
7442
+ }
7443
+ }
7444
+ }
7445
+ overlapCount += Math.max(0, componentSize - 1);
7446
+ }
7447
+ return overlapCount;
7448
+ };
7450
7449
  var layoutResourceTimelineItems = (resources, options) => {
7451
7450
  const rows = [];
7452
7451
  const items = [];
@@ -7506,7 +7505,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7506
7505
  }
7507
7506
  const laneCount = Math.max(1, laneEndDays.length);
7508
7507
  const resourceRowHeight = laneCount * options.laneHeight;
7509
- const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7508
+ const conflictCount = countConflictOverlaps(laidOutItems);
7510
7509
  const row = {
7511
7510
  resource,
7512
7511
  resourceId: resource.id,
@@ -7751,7 +7750,8 @@ var DEFAULT_ROW_HEADER_WIDTH = 240;
7751
7750
  var DEFAULT_RESOURCE_ROW_GAP = 8;
7752
7751
  var ITEM_OUTER_VERTICAL_INSET = 2;
7753
7752
  var ITEM_INNER_VERTICAL_INSET = 1;
7754
- var ITEM_HORIZONTAL_INSET = 1;
7753
+ var ITEM_START_HORIZONTAL_INSET = 2;
7754
+ var ITEM_END_HORIZONTAL_INSET = 1;
7755
7755
  var isValidDate = (date) => !Number.isNaN(date.getTime());
7756
7756
  var collectValidItems = (resources) => {
7757
7757
  return resources.flatMap(
@@ -7773,12 +7773,18 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7773
7773
  const topInset = laneIndex === 0 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7774
7774
  const bottomInset = laneIndex === laneCount - 1 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7775
7775
  return {
7776
- left: geometry.left + ITEM_HORIZONTAL_INSET,
7776
+ left: geometry.left + ITEM_START_HORIZONTAL_INSET,
7777
7777
  top: geometry.top + topInset,
7778
- width: Math.max(0, geometry.width - ITEM_HORIZONTAL_INSET * 2),
7778
+ width: Math.max(1, geometry.width - ITEM_START_HORIZONTAL_INSET - ITEM_END_HORIZONTAL_INSET),
7779
7779
  height: Math.max(0, geometry.height - topInset - bottomInset)
7780
7780
  };
7781
7781
  };
7782
+ var formatOverlapCount = (count) => {
7783
+ const mod10 = count % 10;
7784
+ const mod100 = count % 100;
7785
+ 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";
7786
+ return `${count} ${word}`;
7787
+ };
7782
7788
  var getWeekendOverlaySegments = (startDate, endDate, dayWidth, weekendPredicate) => {
7783
7789
  const segments2 = [];
7784
7790
  const current = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
@@ -7821,7 +7827,143 @@ var getRangeOverlaySegments = (itemStartDate, ranges, dayWidth) => {
7821
7827
  };
7822
7828
  });
7823
7829
  };
7830
+ var clampOverlaySegments = (segments2, maxWidth) => segments2.flatMap((segment) => {
7831
+ const left = Math.max(0, Math.min(segment.left, maxWidth));
7832
+ const right = Math.max(left, Math.min(segment.left + segment.width, maxWidth));
7833
+ const width = right - left;
7834
+ return width > 0 ? [{ left, width }] : [];
7835
+ });
7824
7836
  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);
7837
+ var ResourceHeader = ({
7838
+ resource,
7839
+ resourceId,
7840
+ conflictCount,
7841
+ height,
7842
+ paddingBottom,
7843
+ menuCommands
7844
+ }) => {
7845
+ const [menuOpen, setMenuOpen] = (0, import_react14.useState)(false);
7846
+ const visibleCommands = (0, import_react14.useMemo)(
7847
+ () => menuCommands.filter((command) => command.isVisible?.(resource) ?? true),
7848
+ [menuCommands, resource]
7849
+ );
7850
+ const hasMenu = visibleCommands.length > 0;
7851
+ const handleCommandClick = (command, event) => {
7852
+ event.stopPropagation();
7853
+ if (command.closeOnSelect !== false) {
7854
+ setMenuOpen(false);
7855
+ }
7856
+ command.onSelect(resource);
7857
+ };
7858
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7859
+ "div",
7860
+ {
7861
+ className: `gantt-resourceTimeline-resourceHeader${menuOpen ? " gantt-resourceTimeline-resourceHeaderMenuOpen" : ""}`,
7862
+ "data-resource-row-id": resourceId,
7863
+ style: {
7864
+ height: `${height}px`,
7865
+ paddingBottom: `${paddingBottom}px`
7866
+ },
7867
+ children: [
7868
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceName", children: resource.name }),
7869
+ conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7870
+ "span",
7871
+ {
7872
+ className: "gantt-resourceTimeline-conflictBadge",
7873
+ "aria-label": formatOverlapCount(conflictCount),
7874
+ title: formatOverlapCount(conflictCount),
7875
+ children: conflictCount
7876
+ }
7877
+ ),
7878
+ hasMenu && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: menuOpen, onOpenChange: setMenuOpen, children: [
7879
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7880
+ "button",
7881
+ {
7882
+ className: "gantt-resourceTimeline-resourceMenuButton",
7883
+ type: "button",
7884
+ "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
7885
+ onClick: (event) => {
7886
+ event.stopPropagation();
7887
+ setMenuOpen((open) => !open);
7888
+ },
7889
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u22EE" })
7890
+ }
7891
+ ) }),
7892
+ /* @__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)(
7893
+ "button",
7894
+ {
7895
+ type: "button",
7896
+ className: `gantt-resourceTimeline-resourceMenuItem${command.danger ? " gantt-resourceTimeline-resourceMenuItemDanger" : ""}`,
7897
+ disabled: command.isDisabled?.(resource) ?? false,
7898
+ onClick: (event) => handleCommandClick(command, event),
7899
+ children: [
7900
+ command.icon,
7901
+ command.label
7902
+ ]
7903
+ },
7904
+ command.id
7905
+ )) })
7906
+ ] })
7907
+ ]
7908
+ }
7909
+ );
7910
+ };
7911
+ var NewResourceRow = ({ height, onConfirm, onCancel }) => {
7912
+ const [nameValue, setNameValue] = (0, import_react14.useState)("");
7913
+ const inputRef = (0, import_react14.useRef)(null);
7914
+ const confirmedRef = (0, import_react14.useRef)(false);
7915
+ (0, import_react14.useEffect)(() => {
7916
+ inputRef.current?.focus();
7917
+ inputRef.current?.select();
7918
+ }, []);
7919
+ const handleCancel = () => {
7920
+ confirmedRef.current = true;
7921
+ onCancel();
7922
+ };
7923
+ const handleConfirm = () => {
7924
+ const name = nameValue.trim();
7925
+ if (!name) {
7926
+ handleCancel();
7927
+ return;
7928
+ }
7929
+ confirmedRef.current = true;
7930
+ onConfirm(name);
7931
+ };
7932
+ const handleKeyDown = (event) => {
7933
+ if (event.key === "Enter") {
7934
+ handleConfirm();
7935
+ } else if (event.key === "Escape") {
7936
+ handleCancel();
7937
+ }
7938
+ };
7939
+ const handleBlur = () => {
7940
+ if (!confirmedRef.current) {
7941
+ handleConfirm();
7942
+ }
7943
+ };
7944
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7945
+ "div",
7946
+ {
7947
+ className: "gantt-resourceTimeline-resourceHeader gantt-resourceTimeline-resourceHeaderNew",
7948
+ style: {
7949
+ height: `${height}px`,
7950
+ paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7951
+ },
7952
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7953
+ Input,
7954
+ {
7955
+ ref: inputRef,
7956
+ value: nameValue,
7957
+ onChange: (event) => setNameValue(event.target.value),
7958
+ onKeyDown: handleKeyDown,
7959
+ onBlur: handleBlur,
7960
+ placeholder: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
7961
+ className: "gantt-resourceTimeline-resourceInput"
7962
+ }
7963
+ )
7964
+ }
7965
+ );
7966
+ };
7825
7967
  function ResourceTimelineChart({
7826
7968
  resources,
7827
7969
  dayWidth = DEFAULT_DAY_WIDTH,
@@ -7839,11 +7981,15 @@ function ResourceTimelineChart({
7839
7981
  renderItem,
7840
7982
  getItemClassName,
7841
7983
  onResourceItemClick,
7842
- onResourceItemMove
7984
+ onResourceItemMove,
7985
+ onAddResource,
7986
+ enableAddResource = true,
7987
+ resourceMenuCommands = []
7843
7988
  }) {
7844
7989
  const scrollContainerRef = (0, import_react14.useRef)(null);
7845
7990
  const gridRef = (0, import_react14.useRef)(null);
7846
7991
  const panStateRef = (0, import_react14.useRef)(null);
7992
+ const [isCreatingResource, setIsCreatingResource] = (0, import_react14.useState)(false);
7847
7993
  const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
7848
7994
  const dateRange = (0, import_react14.useMemo)(() => getMultiMonthDays(validItems), [validItems]);
7849
7995
  const monthStart = (0, import_react14.useMemo)(() => {
@@ -7878,6 +8024,18 @@ function ResourceTimelineChart({
7878
8024
  }
7879
8025
  return map;
7880
8026
  }, [layout.items]);
8027
+ const canAddResource = enableAddResource && Boolean(onAddResource);
8028
+ const resourceAddRowHeight = laneHeight + DEFAULT_RESOURCE_ROW_GAP;
8029
+ const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
8030
+ const handleConfirmNewResource = (0, import_react14.useCallback)((name) => {
8031
+ onAddResource?.({
8032
+ id: crypto.randomUUID(),
8033
+ name,
8034
+ items: []
8035
+ });
8036
+ setIsCreatingResource(false);
8037
+ }, [onAddResource]);
8038
+ const handleCancelNewResource = (0, import_react14.useCallback)(() => setIsCreatingResource(false), []);
7881
8039
  const { preview, startDrag } = useResourceItemDrag({
7882
8040
  dayWidth,
7883
8041
  monthStart,
@@ -7972,28 +8130,34 @@ function ResourceTimelineChart({
7972
8130
  style: { height: `${headerHeight}px` }
7973
8131
  }
7974
8132
  ),
7975
- layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7976
- "div",
8133
+ layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8134
+ ResourceHeader,
7977
8135
  {
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
- ]
8136
+ resource: row.resource,
8137
+ resourceId: row.resourceId,
8138
+ conflictCount: row.conflictCount,
8139
+ height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
8140
+ paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
8141
+ menuCommands: resourceMenuCommands
7995
8142
  },
7996
8143
  row.resourceId
8144
+ )),
8145
+ canAddResource && (isCreatingResource ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8146
+ NewResourceRow,
8147
+ {
8148
+ height: resourceAddRowHeight,
8149
+ onConfirm: handleConfirmNewResource,
8150
+ onCancel: handleCancelNewResource
8151
+ }
8152
+ ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8153
+ "button",
8154
+ {
8155
+ className: "gantt-resourceTimeline-addResourceButton",
8156
+ type: "button",
8157
+ style: { height: `${resourceAddRowHeight}px` },
8158
+ onClick: () => setIsCreatingResource(true),
8159
+ children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441"
8160
+ }
7997
8161
  ))
7998
8162
  ]
7999
8163
  }
@@ -8019,14 +8183,14 @@ function ResourceTimelineChart({
8019
8183
  {
8020
8184
  ref: gridRef,
8021
8185
  className: "gantt-resourceTimeline-grid",
8022
- style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
8186
+ style: { width: `${gridWidth}px`, height: `${displayTotalHeight}px` },
8023
8187
  children: [
8024
8188
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8025
8189
  GridBackground_default,
8026
8190
  {
8027
8191
  dateRange,
8028
8192
  dayWidth,
8029
- totalHeight: layout.totalHeight,
8193
+ totalHeight: displayTotalHeight,
8030
8194
  viewMode,
8031
8195
  isCustomWeekend: weekendPredicate
8032
8196
  }
@@ -8044,6 +8208,17 @@ function ResourceTimelineChart({
8044
8208
  },
8045
8209
  row.resourceId
8046
8210
  )),
8211
+ canAddResource && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8212
+ "div",
8213
+ {
8214
+ className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
8215
+ "data-resource-add-row": "true",
8216
+ style: {
8217
+ top: `${layout.totalHeight}px`,
8218
+ height: `${resourceAddRowHeight}px`
8219
+ }
8220
+ }
8221
+ ),
8047
8222
  Array.from(itemsByResourceId.values()).flatMap(
8048
8223
  (resourceItems) => resourceItems.map((layoutItem) => {
8049
8224
  const customClassName = getItemClassName?.(layoutItem.item);
@@ -8069,11 +8244,17 @@ function ResourceTimelineChart({
8069
8244
  }, layoutItem.laneIndex, laneCount);
8070
8245
  const overlayStartDate = isDraggingItem ? preview.startDate : layoutItem.startDate;
8071
8246
  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
8247
+ const weekendOverlaySegments = businessDays ? clampOverlaySegments(
8248
+ getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate),
8249
+ itemGeometry.width
8250
+ ) : [];
8251
+ const conflictOverlaySegments = clampOverlaySegments(
8252
+ getRangeOverlaySegments(
8253
+ overlayStartDate,
8254
+ isDraggingItem ? [] : layoutItem.conflictRanges,
8255
+ dayWidth
8256
+ ),
8257
+ itemGeometry.width
8077
8258
  );
8078
8259
  const durationValue = getDurationValue(
8079
8260
  overlayStartDate,
@@ -9232,6 +9413,11 @@ var not = (predicate) => (task) => !predicate(task);
9232
9413
  var withoutDeps = (options = {}) => (task) => {
9233
9414
  if (!task) return false;
9234
9415
  if (options.onlyChildren && !task.parentId) return false;
9416
+ if (options.onlyLeafTasks) {
9417
+ const isParent = (options.tasks ?? []).some((candidate) => candidate.parentId === task.id);
9418
+ const isRegularTask = task.type == null || task.type === "task";
9419
+ if (isParent || !isRegularTask) return false;
9420
+ }
9235
9421
  return !task.dependencies || task.dependencies.length === 0;
9236
9422
  };
9237
9423
  var expired = (referenceDate = /* @__PURE__ */ new Date()) => (task) => isTaskExpired(task, referenceDate);