gantt-lib 0.78.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
  );
@@ -3351,6 +3351,25 @@ var DependencyLines = React5.memo(({
3351
3351
  }
3352
3352
  )
3353
3353
  }
3354
+ ),
3355
+ /* @__PURE__ */ jsx6(
3356
+ "marker",
3357
+ {
3358
+ id: "arrowhead-hover",
3359
+ markerWidth: "8",
3360
+ markerHeight: "6",
3361
+ markerUnits: "userSpaceOnUse",
3362
+ refX: "7",
3363
+ refY: "3",
3364
+ orient: "auto",
3365
+ children: /* @__PURE__ */ jsx6(
3366
+ "polygon",
3367
+ {
3368
+ points: "0 0, 8 3, 0 6",
3369
+ fill: "var(--gantt-dependency-hover-color, #ef4444)"
3370
+ }
3371
+ )
3372
+ }
3354
3373
  )
3355
3374
  ] }),
3356
3375
  lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder, isVirtual }) => {
@@ -3365,14 +3384,23 @@ var DependencyLines = React5.memo(({
3365
3384
  else markerEnd = "url(#arrowhead)";
3366
3385
  const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : "var(--gantt-dependency-line-color, #666666)";
3367
3386
  return /* @__PURE__ */ jsxs5(React5.Fragment, { children: [
3368
- /* @__PURE__ */ jsx6(
3369
- "path",
3370
- {
3371
- d: path,
3372
- className: pathClassName,
3373
- markerEnd
3374
- }
3375
- ),
3387
+ /* @__PURE__ */ jsxs5("g", { className: "gantt-dependency-line", children: [
3388
+ /* @__PURE__ */ jsx6(
3389
+ "path",
3390
+ {
3391
+ d: path,
3392
+ className: "gantt-dependency-hit-area"
3393
+ }
3394
+ ),
3395
+ /* @__PURE__ */ jsx6(
3396
+ "path",
3397
+ {
3398
+ d: path,
3399
+ className: pathClassName,
3400
+ markerEnd
3401
+ }
3402
+ )
3403
+ ] }),
3376
3404
  lag !== 0 && /* @__PURE__ */ jsx6(
3377
3405
  "text",
3378
3406
  {
@@ -4446,12 +4474,20 @@ var DepChip = ({
4446
4474
  /* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
4447
4475
  "span",
4448
4476
  {
4449
- className: `gantt-tl-dep-chip${isSelected ? " gantt-tl-dep-chip-selected" : ""}`,
4477
+ className: `gantt-tl-dep-chip-with-number${isSelected ? " gantt-tl-dep-chip-with-number-selected" : ""}`,
4450
4478
  onClick: handleClick,
4451
- title: `[${LINK_TYPE_LABELS_RU[dep.type]}] ${formatTaskNumberLabel(predecessorTaskNumber)}${depName}`,
4479
+ "aria-label": `[${LINK_TYPE_LABELS_RU[dep.type]}] ${formatTaskNumberLabel(predecessorTaskNumber)}${depName}`,
4452
4480
  children: [
4453
- /* @__PURE__ */ jsx12(Icon, {}),
4454
- effectiveLag !== 0 ? effectiveLag > 0 ? `+${effectiveLag}` : `${effectiveLag}` : ""
4481
+ predecessorTaskNumber && /* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-chip-task-number", children: predecessorTaskNumber }),
4482
+ /* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-chip", children: /* @__PURE__ */ jsx12(Icon, {}) }),
4483
+ effectiveLag !== 0 && /* @__PURE__ */ jsx12("span", { className: "gantt-tl-dep-chip-lag", children: effectiveLag > 0 ? `+${effectiveLag}` : `${effectiveLag}` }),
4484
+ /* @__PURE__ */ jsxs9("span", { className: "gantt-tl-dep-chip-tooltip", role: "tooltip", children: [
4485
+ "[",
4486
+ LINK_TYPE_LABELS_RU[dep.type],
4487
+ "] ",
4488
+ formatTaskNumberLabel(predecessorTaskNumber),
4489
+ depName
4490
+ ] })
4455
4491
  ]
4456
4492
  }
4457
4493
  ) }),
@@ -6296,7 +6332,7 @@ var BUILT_IN_COLUMN_WIDTHS = {
6296
6332
  endDate: 90,
6297
6333
  duration: 60,
6298
6334
  progress: 50,
6299
- dependencies: 120,
6335
+ dependencies: 128,
6300
6336
  actions: 80
6301
6337
  };
6302
6338
  function createBuiltInColumns(opts) {
@@ -7206,7 +7242,7 @@ var TaskList = ({
7206
7242
  };
7207
7243
 
7208
7244
  // src/components/ResourceTimelineChart/ResourceTimelineChart.tsx
7209
- 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";
7210
7246
 
7211
7247
  // src/utils/resourceTimelineLayout.ts
7212
7248
  var isInvalidDate = (date) => Number.isNaN(date.getTime());
@@ -7297,6 +7333,33 @@ var calculateConflictInfo = (parsedItems) => {
7297
7333
  }
7298
7334
  return result;
7299
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
+ };
7300
7363
  var layoutResourceTimelineItems = (resources, options) => {
7301
7364
  const rows = [];
7302
7365
  const items = [];
@@ -7356,7 +7419,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7356
7419
  }
7357
7420
  const laneCount = Math.max(1, laneEndDays.length);
7358
7421
  const resourceRowHeight = laneCount * options.laneHeight;
7359
- const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7422
+ const conflictCount = countConflictOverlaps(laidOutItems);
7360
7423
  const row = {
7361
7424
  resource,
7362
7425
  resourceId: resource.id,
@@ -7601,7 +7664,8 @@ var DEFAULT_ROW_HEADER_WIDTH = 240;
7601
7664
  var DEFAULT_RESOURCE_ROW_GAP = 8;
7602
7665
  var ITEM_OUTER_VERTICAL_INSET = 2;
7603
7666
  var ITEM_INNER_VERTICAL_INSET = 1;
7604
- var ITEM_HORIZONTAL_INSET = 1;
7667
+ var ITEM_START_HORIZONTAL_INSET = 2;
7668
+ var ITEM_END_HORIZONTAL_INSET = 1;
7605
7669
  var isValidDate = (date) => !Number.isNaN(date.getTime());
7606
7670
  var collectValidItems = (resources) => {
7607
7671
  return resources.flatMap(
@@ -7623,12 +7687,18 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7623
7687
  const topInset = laneIndex === 0 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7624
7688
  const bottomInset = laneIndex === laneCount - 1 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7625
7689
  return {
7626
- left: geometry.left + ITEM_HORIZONTAL_INSET,
7690
+ left: geometry.left + ITEM_START_HORIZONTAL_INSET,
7627
7691
  top: geometry.top + topInset,
7628
- 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),
7629
7693
  height: Math.max(0, geometry.height - topInset - bottomInset)
7630
7694
  };
7631
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
+ };
7632
7702
  var getWeekendOverlaySegments = (startDate, endDate, dayWidth, weekendPredicate) => {
7633
7703
  const segments2 = [];
7634
7704
  const current = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
@@ -7671,7 +7741,143 @@ var getRangeOverlaySegments = (itemStartDate, ranges, dayWidth) => {
7671
7741
  };
7672
7742
  });
7673
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
+ });
7674
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
+ };
7675
7881
  function ResourceTimelineChart({
7676
7882
  resources,
7677
7883
  dayWidth = DEFAULT_DAY_WIDTH,
@@ -7689,11 +7895,15 @@ function ResourceTimelineChart({
7689
7895
  renderItem,
7690
7896
  getItemClassName,
7691
7897
  onResourceItemClick,
7692
- onResourceItemMove
7898
+ onResourceItemMove,
7899
+ onAddResource,
7900
+ enableAddResource = true,
7901
+ resourceMenuCommands = []
7693
7902
  }) {
7694
7903
  const scrollContainerRef = useRef8(null);
7695
7904
  const gridRef = useRef8(null);
7696
7905
  const panStateRef = useRef8(null);
7906
+ const [isCreatingResource, setIsCreatingResource] = useState8(false);
7697
7907
  const validItems = useMemo9(() => collectValidItems(resources), [resources]);
7698
7908
  const dateRange = useMemo9(() => getMultiMonthDays(validItems), [validItems]);
7699
7909
  const monthStart = useMemo9(() => {
@@ -7728,6 +7938,18 @@ function ResourceTimelineChart({
7728
7938
  }
7729
7939
  return map;
7730
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), []);
7731
7953
  const { preview, startDrag } = useResourceItemDrag({
7732
7954
  dayWidth,
7733
7955
  monthStart,
@@ -7822,28 +8044,34 @@ function ResourceTimelineChart({
7822
8044
  style: { height: `${headerHeight}px` }
7823
8045
  }
7824
8046
  ),
7825
- layout.rows.map((row) => /* @__PURE__ */ jsxs12(
7826
- "div",
8047
+ layout.rows.map((row) => /* @__PURE__ */ jsx15(
8048
+ ResourceHeader,
7827
8049
  {
7828
- className: "gantt-resourceTimeline-resourceHeader",
7829
- "data-resource-row-id": row.resourceId,
7830
- style: {
7831
- height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`,
7832
- paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7833
- },
7834
- children: [
7835
- /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name }),
7836
- row.conflictCount > 0 && /* @__PURE__ */ jsx15(
7837
- "span",
7838
- {
7839
- className: "gantt-resourceTimeline-conflictBadge",
7840
- "aria-label": `${row.conflictCount} \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u043E\u0432`,
7841
- children: row.conflictCount
7842
- }
7843
- )
7844
- ]
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
7845
8056
  },
7846
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
+ }
7847
8075
  ))
7848
8076
  ]
7849
8077
  }
@@ -7869,14 +8097,14 @@ function ResourceTimelineChart({
7869
8097
  {
7870
8098
  ref: gridRef,
7871
8099
  className: "gantt-resourceTimeline-grid",
7872
- style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
8100
+ style: { width: `${gridWidth}px`, height: `${displayTotalHeight}px` },
7873
8101
  children: [
7874
8102
  /* @__PURE__ */ jsx15(
7875
8103
  GridBackground_default,
7876
8104
  {
7877
8105
  dateRange,
7878
8106
  dayWidth,
7879
- totalHeight: layout.totalHeight,
8107
+ totalHeight: displayTotalHeight,
7880
8108
  viewMode,
7881
8109
  isCustomWeekend: weekendPredicate
7882
8110
  }
@@ -7894,6 +8122,17 @@ function ResourceTimelineChart({
7894
8122
  },
7895
8123
  row.resourceId
7896
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
+ ),
7897
8136
  Array.from(itemsByResourceId.values()).flatMap(
7898
8137
  (resourceItems) => resourceItems.map((layoutItem) => {
7899
8138
  const customClassName = getItemClassName?.(layoutItem.item);
@@ -7919,11 +8158,17 @@ function ResourceTimelineChart({
7919
8158
  }, layoutItem.laneIndex, laneCount);
7920
8159
  const overlayStartDate = isDraggingItem ? preview.startDate : layoutItem.startDate;
7921
8160
  const overlayEndDate = isDraggingItem ? preview.endDate : layoutItem.endDate;
7922
- const weekendOverlaySegments = businessDays ? getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate) : [];
7923
- const conflictOverlaySegments = getRangeOverlaySegments(
7924
- overlayStartDate,
7925
- isDraggingItem ? [] : layoutItem.conflictRanges,
7926
- 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
7927
8172
  );
7928
8173
  const durationValue = getDurationValue(
7929
8174
  overlayStartDate,
@@ -7942,6 +8187,7 @@ function ResourceTimelineChart({
7942
8187
  {
7943
8188
  className,
7944
8189
  "data-resource-item-id": layoutItem.itemId,
8190
+ "data-resource-item-tooltip": layoutItem.item.title,
7945
8191
  onMouseDown: (event) => startDrag(event, layoutItem),
7946
8192
  onClick: () => onResourceItemClick?.(layoutItem.item),
7947
8193
  onKeyDown: (event) => {
@@ -8419,20 +8665,20 @@ function TaskGanttChartInner(props, ref) {
8419
8665
  const containerRef = useRef9(null);
8420
8666
  const scrollContainerRef = useRef9(null);
8421
8667
  const scrollContentRef = useRef9(null);
8422
- const [selectedTaskId, setSelectedTaskId] = useState8(null);
8423
- const [taskListHasRightShadow, setTaskListHasRightShadow] = useState8(false);
8424
- const [selectedChip, setSelectedChip] = useState8(null);
8425
- 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());
8426
8672
  const collapsedParentIds = externalCollapsedParentIds ?? internalCollapsedParentIds;
8427
- const [editingTaskId, setEditingTaskId] = useState8(null);
8673
+ const [editingTaskId, setEditingTaskId] = useState9(null);
8428
8674
  const normalizedTasks = useMemo10(() => normalizeHierarchyTasks(tasks), [tasks]);
8429
8675
  const isCustomWeekend = useMemo10(
8430
8676
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
8431
8677
  [customDays, isWeekend3]
8432
8678
  );
8433
8679
  const dateRange = useMemo10(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
8434
- const [validationResult, setValidationResult] = useState8(null);
8435
- const [cascadeOverrides, setCascadeOverrides] = useState8(/* @__PURE__ */ new Map());
8680
+ const [validationResult, setValidationResult] = useState9(null);
8681
+ const [cascadeOverrides, setCascadeOverrides] = useState9(/* @__PURE__ */ new Map());
8436
8682
  const gridWidth = useMemo10(
8437
8683
  () => Math.round(dateRange.length * dayWidth),
8438
8684
  [dateRange.length, dayWidth]
@@ -8549,9 +8795,9 @@ function TaskGanttChartInner(props, ref) {
8549
8795
  setSelectedTaskId(taskId);
8550
8796
  container.scrollTo({ top: scrollTop, behavior: "smooth" });
8551
8797
  }, [tasks, visibleTasks, rowHeight]);
8552
- const [dragGuideLines, setDragGuideLines] = useState8(null);
8553
- const [draggedTaskOverride, setDraggedTaskOverride] = useState8(null);
8554
- 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());
8555
8801
  useEffect9(() => {
8556
8802
  const result = validateDependencies(tasks);
8557
8803
  setValidationResult(result);
@@ -9078,7 +9324,16 @@ Button.displayName = "Button";
9078
9324
  var and = (...predicates) => (task) => predicates.every((p) => p(task));
9079
9325
  var or = (...predicates) => (task) => predicates.some((p) => p(task));
9080
9326
  var not = (predicate) => (task) => !predicate(task);
9081
- var withoutDeps = () => (task) => !!task && (!task.dependencies || task.dependencies.length === 0);
9327
+ var withoutDeps = (options = {}) => (task) => {
9328
+ if (!task) return false;
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
+ }
9335
+ return !task.dependencies || task.dependencies.length === 0;
9336
+ };
9082
9337
  var expired = (referenceDate = /* @__PURE__ */ new Date()) => (task) => isTaskExpired(task, referenceDate);
9083
9338
  var inDateRange = (rangeStart, rangeEnd) => (task) => {
9084
9339
  if (!task) return false;