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.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  "use client";
4
4
 
5
5
  // src/components/GanttChart/GanttChart.tsx
6
- import { useMemo as useMemo10, useCallback as useCallback8, useRef as useRef9, useState as useState8, useEffect as useEffect9, useImperativeHandle, forwardRef } from "react";
6
+ import { useMemo as useMemo10, useCallback as useCallback8, useRef as useRef9, useState as useState9, useEffect as useEffect9, useImperativeHandle, forwardRef } from "react";
7
7
 
8
8
  // src/core/scheduling/dateMath.ts
9
9
  var DAY_MS = 24 * 60 * 60 * 1e3;
@@ -2999,7 +2999,7 @@ var arePropsEqual2 = (prevProps, nextProps) => {
2999
2999
  prevProps.viewMode === nextProps.viewMode && prevProps.isCustomWeekend === nextProps.isCustomWeekend;
3000
3000
  };
3001
3001
  var GridBackground = React4.memo(
3002
- ({ dateRange, dayWidth, totalHeight, viewMode = "day", isCustomWeekend }) => {
3002
+ ({ dateRange, dayWidth, totalHeight, viewMode = "day", isCustomWeekend, className, showWeekendBlocks = true, showGridLines = true }) => {
3003
3003
  const weekGridLines = useMemo4(() => {
3004
3004
  if (viewMode !== "week") return [];
3005
3005
  return calculateWeekGridLines(dateRange, dayWidth);
@@ -3022,13 +3022,13 @@ var GridBackground = React4.memo(
3022
3022
  return /* @__PURE__ */ jsxs3(
3023
3023
  "div",
3024
3024
  {
3025
- className: "gantt-gb-gridBackground",
3025
+ className: ["gantt-gb-gridBackground", className].filter(Boolean).join(" "),
3026
3026
  style: {
3027
3027
  width: `${gridWidth}px`,
3028
3028
  height: `${totalHeight}px`
3029
3029
  },
3030
3030
  children: [
3031
- weekendBlocks.map((block, index) => /* @__PURE__ */ jsx4(
3031
+ showWeekendBlocks && weekendBlocks.map((block, index) => /* @__PURE__ */ jsx4(
3032
3032
  "div",
3033
3033
  {
3034
3034
  className: "gantt-gb-weekendBlock",
@@ -3039,7 +3039,7 @@ var GridBackground = React4.memo(
3039
3039
  },
3040
3040
  `weekend-${index}`
3041
3041
  )),
3042
- viewMode === "week" ? (
3042
+ showGridLines && (viewMode === "week" ? (
3043
3043
  // Week-view: one line per week column boundary
3044
3044
  weekGridLines.map((line, index) => {
3045
3045
  const lineClass = line.isMonthStart ? "gantt-gb-monthSeparator" : "gantt-gb-weekSeparator";
@@ -3080,7 +3080,7 @@ var GridBackground = React4.memo(
3080
3080
  `gridline-${index}`
3081
3081
  );
3082
3082
  })
3083
- )
3083
+ ))
3084
3084
  ]
3085
3085
  }
3086
3086
  );
@@ -7242,7 +7242,7 @@ var TaskList = ({
7242
7242
  };
7243
7243
 
7244
7244
  // src/components/ResourceTimelineChart/ResourceTimelineChart.tsx
7245
- import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo9, useRef as useRef8 } from "react";
7245
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo9, useRef as useRef8, useState as useState8 } from "react";
7246
7246
 
7247
7247
  // src/utils/resourceTimelineLayout.ts
7248
7248
  var isInvalidDate = (date) => Number.isNaN(date.getTime());
@@ -7333,6 +7333,33 @@ var calculateConflictInfo = (parsedItems) => {
7333
7333
  }
7334
7334
  return result;
7335
7335
  };
7336
+ var countConflictOverlaps = (items) => {
7337
+ const conflictsByItemId = new Map(items.map((item) => [item.itemId, item.conflictsWith]));
7338
+ const visited = /* @__PURE__ */ new Set();
7339
+ let overlapCount = 0;
7340
+ for (const item of items) {
7341
+ if (visited.has(item.itemId) || item.conflictsWith.length === 0) {
7342
+ continue;
7343
+ }
7344
+ const stack = [item.itemId];
7345
+ let componentSize = 0;
7346
+ while (stack.length > 0) {
7347
+ const itemId = stack.pop();
7348
+ if (visited.has(itemId)) {
7349
+ continue;
7350
+ }
7351
+ visited.add(itemId);
7352
+ componentSize += 1;
7353
+ for (const conflictId of conflictsByItemId.get(itemId) ?? []) {
7354
+ if (!visited.has(conflictId)) {
7355
+ stack.push(conflictId);
7356
+ }
7357
+ }
7358
+ }
7359
+ overlapCount += Math.max(0, componentSize - 1);
7360
+ }
7361
+ return overlapCount;
7362
+ };
7336
7363
  var layoutResourceTimelineItems = (resources, options) => {
7337
7364
  const rows = [];
7338
7365
  const items = [];
@@ -7392,7 +7419,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7392
7419
  }
7393
7420
  const laneCount = Math.max(1, laneEndDays.length);
7394
7421
  const resourceRowHeight = laneCount * options.laneHeight;
7395
- const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7422
+ const conflictCount = countConflictOverlaps(laidOutItems);
7396
7423
  const row = {
7397
7424
  resource,
7398
7425
  resourceId: resource.id,
@@ -7637,7 +7664,8 @@ var DEFAULT_ROW_HEADER_WIDTH = 240;
7637
7664
  var DEFAULT_RESOURCE_ROW_GAP = 8;
7638
7665
  var ITEM_OUTER_VERTICAL_INSET = 2;
7639
7666
  var ITEM_INNER_VERTICAL_INSET = 1;
7640
- var ITEM_HORIZONTAL_INSET = 1;
7667
+ var ITEM_START_HORIZONTAL_INSET = 2;
7668
+ var ITEM_END_HORIZONTAL_INSET = 1;
7641
7669
  var isValidDate = (date) => !Number.isNaN(date.getTime());
7642
7670
  var collectValidItems = (resources) => {
7643
7671
  return resources.flatMap(
@@ -7659,12 +7687,18 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7659
7687
  const topInset = laneIndex === 0 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7660
7688
  const bottomInset = laneIndex === laneCount - 1 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7661
7689
  return {
7662
- left: geometry.left + ITEM_HORIZONTAL_INSET,
7690
+ left: geometry.left + ITEM_START_HORIZONTAL_INSET,
7663
7691
  top: geometry.top + topInset,
7664
- width: Math.max(0, geometry.width - ITEM_HORIZONTAL_INSET * 2),
7692
+ width: Math.max(1, geometry.width - ITEM_START_HORIZONTAL_INSET - ITEM_END_HORIZONTAL_INSET),
7665
7693
  height: Math.max(0, geometry.height - topInset - bottomInset)
7666
7694
  };
7667
7695
  };
7696
+ var formatOverlapCount = (count) => {
7697
+ const mod10 = count % 10;
7698
+ const mod100 = count % 100;
7699
+ 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";
7700
+ return `${count} ${word}`;
7701
+ };
7668
7702
  var getWeekendOverlaySegments = (startDate, endDate, dayWidth, weekendPredicate) => {
7669
7703
  const segments2 = [];
7670
7704
  const current = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
@@ -7707,7 +7741,143 @@ var getRangeOverlaySegments = (itemStartDate, ranges, dayWidth) => {
7707
7741
  };
7708
7742
  });
7709
7743
  };
7744
+ var clampOverlaySegments = (segments2, maxWidth) => segments2.flatMap((segment) => {
7745
+ const left = Math.max(0, Math.min(segment.left, maxWidth));
7746
+ const right = Math.max(left, Math.min(segment.left + segment.width, maxWidth));
7747
+ const width = right - left;
7748
+ return width > 0 ? [{ left, width }] : [];
7749
+ });
7710
7750
  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);
7751
+ var ResourceHeader = ({
7752
+ resource,
7753
+ resourceId,
7754
+ conflictCount,
7755
+ height,
7756
+ paddingBottom,
7757
+ menuCommands
7758
+ }) => {
7759
+ const [menuOpen, setMenuOpen] = useState8(false);
7760
+ const visibleCommands = useMemo9(
7761
+ () => menuCommands.filter((command) => command.isVisible?.(resource) ?? true),
7762
+ [menuCommands, resource]
7763
+ );
7764
+ const hasMenu = visibleCommands.length > 0;
7765
+ const handleCommandClick = (command, event) => {
7766
+ event.stopPropagation();
7767
+ if (command.closeOnSelect !== false) {
7768
+ setMenuOpen(false);
7769
+ }
7770
+ command.onSelect(resource);
7771
+ };
7772
+ return /* @__PURE__ */ jsxs12(
7773
+ "div",
7774
+ {
7775
+ className: `gantt-resourceTimeline-resourceHeader${menuOpen ? " gantt-resourceTimeline-resourceHeaderMenuOpen" : ""}`,
7776
+ "data-resource-row-id": resourceId,
7777
+ style: {
7778
+ height: `${height}px`,
7779
+ paddingBottom: `${paddingBottom}px`
7780
+ },
7781
+ children: [
7782
+ /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceName", children: resource.name }),
7783
+ conflictCount > 0 && /* @__PURE__ */ jsx15(
7784
+ "span",
7785
+ {
7786
+ className: "gantt-resourceTimeline-conflictBadge",
7787
+ "aria-label": formatOverlapCount(conflictCount),
7788
+ title: formatOverlapCount(conflictCount),
7789
+ children: conflictCount
7790
+ }
7791
+ ),
7792
+ hasMenu && /* @__PURE__ */ jsxs12(Popover, { open: menuOpen, onOpenChange: setMenuOpen, children: [
7793
+ /* @__PURE__ */ jsx15(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx15(
7794
+ "button",
7795
+ {
7796
+ className: "gantt-resourceTimeline-resourceMenuButton",
7797
+ type: "button",
7798
+ "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
7799
+ onClick: (event) => {
7800
+ event.stopPropagation();
7801
+ setMenuOpen((open) => !open);
7802
+ },
7803
+ children: /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", children: "\u22EE" })
7804
+ }
7805
+ ) }),
7806
+ /* @__PURE__ */ jsx15(PopoverContent, { className: "gantt-resourceTimeline-resourceMenu", portal: true, align: "end", children: visibleCommands.map((command) => /* @__PURE__ */ jsxs12(
7807
+ "button",
7808
+ {
7809
+ type: "button",
7810
+ className: `gantt-resourceTimeline-resourceMenuItem${command.danger ? " gantt-resourceTimeline-resourceMenuItemDanger" : ""}`,
7811
+ disabled: command.isDisabled?.(resource) ?? false,
7812
+ onClick: (event) => handleCommandClick(command, event),
7813
+ children: [
7814
+ command.icon,
7815
+ command.label
7816
+ ]
7817
+ },
7818
+ command.id
7819
+ )) })
7820
+ ] })
7821
+ ]
7822
+ }
7823
+ );
7824
+ };
7825
+ var NewResourceRow = ({ height, onConfirm, onCancel }) => {
7826
+ const [nameValue, setNameValue] = useState8("");
7827
+ const inputRef = useRef8(null);
7828
+ const confirmedRef = useRef8(false);
7829
+ useEffect8(() => {
7830
+ inputRef.current?.focus();
7831
+ inputRef.current?.select();
7832
+ }, []);
7833
+ const handleCancel = () => {
7834
+ confirmedRef.current = true;
7835
+ onCancel();
7836
+ };
7837
+ const handleConfirm = () => {
7838
+ const name = nameValue.trim();
7839
+ if (!name) {
7840
+ handleCancel();
7841
+ return;
7842
+ }
7843
+ confirmedRef.current = true;
7844
+ onConfirm(name);
7845
+ };
7846
+ const handleKeyDown = (event) => {
7847
+ if (event.key === "Enter") {
7848
+ handleConfirm();
7849
+ } else if (event.key === "Escape") {
7850
+ handleCancel();
7851
+ }
7852
+ };
7853
+ const handleBlur = () => {
7854
+ if (!confirmedRef.current) {
7855
+ handleConfirm();
7856
+ }
7857
+ };
7858
+ return /* @__PURE__ */ jsx15(
7859
+ "div",
7860
+ {
7861
+ className: "gantt-resourceTimeline-resourceHeader gantt-resourceTimeline-resourceHeaderNew",
7862
+ style: {
7863
+ height: `${height}px`,
7864
+ paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7865
+ },
7866
+ children: /* @__PURE__ */ jsx15(
7867
+ Input,
7868
+ {
7869
+ ref: inputRef,
7870
+ value: nameValue,
7871
+ onChange: (event) => setNameValue(event.target.value),
7872
+ onKeyDown: handleKeyDown,
7873
+ onBlur: handleBlur,
7874
+ placeholder: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
7875
+ className: "gantt-resourceTimeline-resourceInput"
7876
+ }
7877
+ )
7878
+ }
7879
+ );
7880
+ };
7711
7881
  function ResourceTimelineChart({
7712
7882
  resources,
7713
7883
  dayWidth = DEFAULT_DAY_WIDTH,
@@ -7725,11 +7895,15 @@ function ResourceTimelineChart({
7725
7895
  renderItem,
7726
7896
  getItemClassName,
7727
7897
  onResourceItemClick,
7728
- onResourceItemMove
7898
+ onResourceItemMove,
7899
+ onAddResource,
7900
+ enableAddResource = true,
7901
+ resourceMenuCommands = []
7729
7902
  }) {
7730
7903
  const scrollContainerRef = useRef8(null);
7731
7904
  const gridRef = useRef8(null);
7732
7905
  const panStateRef = useRef8(null);
7906
+ const [isCreatingResource, setIsCreatingResource] = useState8(false);
7733
7907
  const validItems = useMemo9(() => collectValidItems(resources), [resources]);
7734
7908
  const dateRange = useMemo9(() => getMultiMonthDays(validItems), [validItems]);
7735
7909
  const monthStart = useMemo9(() => {
@@ -7764,6 +7938,18 @@ function ResourceTimelineChart({
7764
7938
  }
7765
7939
  return map;
7766
7940
  }, [layout.items]);
7941
+ const canAddResource = enableAddResource && Boolean(onAddResource);
7942
+ const resourceAddRowHeight = laneHeight + DEFAULT_RESOURCE_ROW_GAP;
7943
+ const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
7944
+ const handleConfirmNewResource = useCallback7((name) => {
7945
+ onAddResource?.({
7946
+ id: crypto.randomUUID(),
7947
+ name,
7948
+ items: []
7949
+ });
7950
+ setIsCreatingResource(false);
7951
+ }, [onAddResource]);
7952
+ const handleCancelNewResource = useCallback7(() => setIsCreatingResource(false), []);
7767
7953
  const { preview, startDrag } = useResourceItemDrag({
7768
7954
  dayWidth,
7769
7955
  monthStart,
@@ -7858,28 +8044,34 @@ function ResourceTimelineChart({
7858
8044
  style: { height: `${headerHeight}px` }
7859
8045
  }
7860
8046
  ),
7861
- layout.rows.map((row) => /* @__PURE__ */ jsxs12(
7862
- "div",
8047
+ layout.rows.map((row) => /* @__PURE__ */ jsx15(
8048
+ ResourceHeader,
7863
8049
  {
7864
- className: "gantt-resourceTimeline-resourceHeader",
7865
- "data-resource-row-id": row.resourceId,
7866
- style: {
7867
- height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`,
7868
- paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7869
- },
7870
- children: [
7871
- /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name }),
7872
- row.conflictCount > 0 && /* @__PURE__ */ jsx15(
7873
- "span",
7874
- {
7875
- className: "gantt-resourceTimeline-conflictBadge",
7876
- "aria-label": `${row.conflictCount} \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u043E\u0432`,
7877
- children: row.conflictCount
7878
- }
7879
- )
7880
- ]
8050
+ resource: row.resource,
8051
+ resourceId: row.resourceId,
8052
+ conflictCount: row.conflictCount,
8053
+ height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
8054
+ paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
8055
+ menuCommands: resourceMenuCommands
7881
8056
  },
7882
8057
  row.resourceId
8058
+ )),
8059
+ canAddResource && (isCreatingResource ? /* @__PURE__ */ jsx15(
8060
+ NewResourceRow,
8061
+ {
8062
+ height: resourceAddRowHeight,
8063
+ onConfirm: handleConfirmNewResource,
8064
+ onCancel: handleCancelNewResource
8065
+ }
8066
+ ) : /* @__PURE__ */ jsx15(
8067
+ "button",
8068
+ {
8069
+ className: "gantt-resourceTimeline-addResourceButton",
8070
+ type: "button",
8071
+ style: { height: `${resourceAddRowHeight}px` },
8072
+ onClick: () => setIsCreatingResource(true),
8073
+ children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441"
8074
+ }
7883
8075
  ))
7884
8076
  ]
7885
8077
  }
@@ -7905,14 +8097,14 @@ function ResourceTimelineChart({
7905
8097
  {
7906
8098
  ref: gridRef,
7907
8099
  className: "gantt-resourceTimeline-grid",
7908
- style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
8100
+ style: { width: `${gridWidth}px`, height: `${displayTotalHeight}px` },
7909
8101
  children: [
7910
8102
  /* @__PURE__ */ jsx15(
7911
8103
  GridBackground_default,
7912
8104
  {
7913
8105
  dateRange,
7914
8106
  dayWidth,
7915
- totalHeight: layout.totalHeight,
8107
+ totalHeight: displayTotalHeight,
7916
8108
  viewMode,
7917
8109
  isCustomWeekend: weekendPredicate
7918
8110
  }
@@ -7930,6 +8122,17 @@ function ResourceTimelineChart({
7930
8122
  },
7931
8123
  row.resourceId
7932
8124
  )),
8125
+ canAddResource && /* @__PURE__ */ jsx15(
8126
+ "div",
8127
+ {
8128
+ className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
8129
+ "data-resource-add-row": "true",
8130
+ style: {
8131
+ top: `${layout.totalHeight}px`,
8132
+ height: `${resourceAddRowHeight}px`
8133
+ }
8134
+ }
8135
+ ),
7933
8136
  Array.from(itemsByResourceId.values()).flatMap(
7934
8137
  (resourceItems) => resourceItems.map((layoutItem) => {
7935
8138
  const customClassName = getItemClassName?.(layoutItem.item);
@@ -7955,11 +8158,17 @@ function ResourceTimelineChart({
7955
8158
  }, layoutItem.laneIndex, laneCount);
7956
8159
  const overlayStartDate = isDraggingItem ? preview.startDate : layoutItem.startDate;
7957
8160
  const overlayEndDate = isDraggingItem ? preview.endDate : layoutItem.endDate;
7958
- const weekendOverlaySegments = businessDays ? getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate) : [];
7959
- const conflictOverlaySegments = getRangeOverlaySegments(
7960
- overlayStartDate,
7961
- isDraggingItem ? [] : layoutItem.conflictRanges,
7962
- dayWidth
8161
+ const weekendOverlaySegments = businessDays ? clampOverlaySegments(
8162
+ getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate),
8163
+ itemGeometry.width
8164
+ ) : [];
8165
+ const conflictOverlaySegments = clampOverlaySegments(
8166
+ getRangeOverlaySegments(
8167
+ overlayStartDate,
8168
+ isDraggingItem ? [] : layoutItem.conflictRanges,
8169
+ dayWidth
8170
+ ),
8171
+ itemGeometry.width
7963
8172
  );
7964
8173
  const durationValue = getDurationValue(
7965
8174
  overlayStartDate,
@@ -8456,20 +8665,20 @@ function TaskGanttChartInner(props, ref) {
8456
8665
  const containerRef = useRef9(null);
8457
8666
  const scrollContainerRef = useRef9(null);
8458
8667
  const scrollContentRef = useRef9(null);
8459
- const [selectedTaskId, setSelectedTaskId] = useState8(null);
8460
- const [taskListHasRightShadow, setTaskListHasRightShadow] = useState8(false);
8461
- const [selectedChip, setSelectedChip] = useState8(null);
8462
- const [internalCollapsedParentIds, setInternalCollapsedParentIds] = useState8(/* @__PURE__ */ new Set());
8668
+ const [selectedTaskId, setSelectedTaskId] = useState9(null);
8669
+ const [taskListHasRightShadow, setTaskListHasRightShadow] = useState9(false);
8670
+ const [selectedChip, setSelectedChip] = useState9(null);
8671
+ const [internalCollapsedParentIds, setInternalCollapsedParentIds] = useState9(/* @__PURE__ */ new Set());
8463
8672
  const collapsedParentIds = externalCollapsedParentIds ?? internalCollapsedParentIds;
8464
- const [editingTaskId, setEditingTaskId] = useState8(null);
8673
+ const [editingTaskId, setEditingTaskId] = useState9(null);
8465
8674
  const normalizedTasks = useMemo10(() => normalizeHierarchyTasks(tasks), [tasks]);
8466
8675
  const isCustomWeekend = useMemo10(
8467
8676
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
8468
8677
  [customDays, isWeekend3]
8469
8678
  );
8470
8679
  const dateRange = useMemo10(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
8471
- const [validationResult, setValidationResult] = useState8(null);
8472
- const [cascadeOverrides, setCascadeOverrides] = useState8(/* @__PURE__ */ new Map());
8680
+ const [validationResult, setValidationResult] = useState9(null);
8681
+ const [cascadeOverrides, setCascadeOverrides] = useState9(/* @__PURE__ */ new Map());
8473
8682
  const gridWidth = useMemo10(
8474
8683
  () => Math.round(dateRange.length * dayWidth),
8475
8684
  [dateRange.length, dayWidth]
@@ -8586,9 +8795,9 @@ function TaskGanttChartInner(props, ref) {
8586
8795
  setSelectedTaskId(taskId);
8587
8796
  container.scrollTo({ top: scrollTop, behavior: "smooth" });
8588
8797
  }, [tasks, visibleTasks, rowHeight]);
8589
- const [dragGuideLines, setDragGuideLines] = useState8(null);
8590
- const [draggedTaskOverride, setDraggedTaskOverride] = useState8(null);
8591
- const [previewTasksById, setPreviewTasksById] = useState8(/* @__PURE__ */ new Map());
8798
+ const [dragGuideLines, setDragGuideLines] = useState9(null);
8799
+ const [draggedTaskOverride, setDraggedTaskOverride] = useState9(null);
8800
+ const [previewTasksById, setPreviewTasksById] = useState9(/* @__PURE__ */ new Map());
8592
8801
  useEffect9(() => {
8593
8802
  const result = validateDependencies(tasks);
8594
8803
  setValidationResult(result);
@@ -9118,6 +9327,11 @@ var not = (predicate) => (task) => !predicate(task);
9118
9327
  var withoutDeps = (options = {}) => (task) => {
9119
9328
  if (!task) return false;
9120
9329
  if (options.onlyChildren && !task.parentId) return false;
9330
+ if (options.onlyLeafTasks) {
9331
+ const isParent = (options.tasks ?? []).some((candidate) => candidate.parentId === task.id);
9332
+ const isRegularTask = task.type == null || task.type === "task";
9333
+ if (isParent || !isRegularTask) return false;
9334
+ }
9121
9335
  return !task.dependencies || task.dependencies.length === 0;
9122
9336
  };
9123
9337
  var expired = (referenceDate = /* @__PURE__ */ new Date()) => (task) => isTaskExpired(task, referenceDate);