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.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
  );
@@ -3489,6 +3489,25 @@ var DependencyLines = import_react6.default.memo(({
3489
3489
  }
3490
3490
  )
3491
3491
  }
3492
+ ),
3493
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3494
+ "marker",
3495
+ {
3496
+ id: "arrowhead-hover",
3497
+ markerWidth: "8",
3498
+ markerHeight: "6",
3499
+ markerUnits: "userSpaceOnUse",
3500
+ refX: "7",
3501
+ refY: "3",
3502
+ orient: "auto",
3503
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3504
+ "polygon",
3505
+ {
3506
+ points: "0 0, 8 3, 0 6",
3507
+ fill: "var(--gantt-dependency-hover-color, #ef4444)"
3508
+ }
3509
+ )
3510
+ }
3492
3511
  )
3493
3512
  ] }),
3494
3513
  lines.map(({ id, path, hasCycle, lag, fromX, toX, fromY, reverseOrder, isVirtual }) => {
@@ -3503,14 +3522,23 @@ var DependencyLines = import_react6.default.memo(({
3503
3522
  else markerEnd = "url(#arrowhead)";
3504
3523
  const lagColor = isSelected ? "#ef4444" : hasCycle ? "var(--gantt-dependency-cycle-color, #ef4444)" : "var(--gantt-dependency-line-color, #666666)";
3505
3524
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react6.default.Fragment, { children: [
3506
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3507
- "path",
3508
- {
3509
- d: path,
3510
- className: pathClassName,
3511
- markerEnd
3512
- }
3513
- ),
3525
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { className: "gantt-dependency-line", children: [
3526
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3527
+ "path",
3528
+ {
3529
+ d: path,
3530
+ className: "gantt-dependency-hit-area"
3531
+ }
3532
+ ),
3533
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3534
+ "path",
3535
+ {
3536
+ d: path,
3537
+ className: pathClassName,
3538
+ markerEnd
3539
+ }
3540
+ )
3541
+ ] }),
3514
3542
  lag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3515
3543
  "text",
3516
3544
  {
@@ -4560,12 +4588,20 @@ var DepChip = ({
4560
4588
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4561
4589
  "span",
4562
4590
  {
4563
- className: `gantt-tl-dep-chip${isSelected ? " gantt-tl-dep-chip-selected" : ""}`,
4591
+ className: `gantt-tl-dep-chip-with-number${isSelected ? " gantt-tl-dep-chip-with-number-selected" : ""}`,
4564
4592
  onClick: handleClick,
4565
- title: `[${LINK_TYPE_LABELS_RU[dep.type]}] ${formatTaskNumberLabel(predecessorTaskNumber)}${depName}`,
4593
+ "aria-label": `[${LINK_TYPE_LABELS_RU[dep.type]}] ${formatTaskNumberLabel(predecessorTaskNumber)}${depName}`,
4566
4594
  children: [
4567
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, {}),
4568
- effectiveLag !== 0 ? effectiveLag > 0 ? `+${effectiveLag}` : `${effectiveLag}` : ""
4595
+ predecessorTaskNumber && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-chip-task-number", children: predecessorTaskNumber }),
4596
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-chip", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, {}) }),
4597
+ effectiveLag !== 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "gantt-tl-dep-chip-lag", children: effectiveLag > 0 ? `+${effectiveLag}` : `${effectiveLag}` }),
4598
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "gantt-tl-dep-chip-tooltip", role: "tooltip", children: [
4599
+ "[",
4600
+ LINK_TYPE_LABELS_RU[dep.type],
4601
+ "] ",
4602
+ formatTaskNumberLabel(predecessorTaskNumber),
4603
+ depName
4604
+ ] })
4569
4605
  ]
4570
4606
  }
4571
4607
  ) }),
@@ -6410,7 +6446,7 @@ var BUILT_IN_COLUMN_WIDTHS = {
6410
6446
  endDate: 90,
6411
6447
  duration: 60,
6412
6448
  progress: 50,
6413
- dependencies: 120,
6449
+ dependencies: 128,
6414
6450
  actions: 80
6415
6451
  };
6416
6452
  function createBuiltInColumns(opts) {
@@ -7411,6 +7447,33 @@ var calculateConflictInfo = (parsedItems) => {
7411
7447
  }
7412
7448
  return result;
7413
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
+ };
7414
7477
  var layoutResourceTimelineItems = (resources, options) => {
7415
7478
  const rows = [];
7416
7479
  const items = [];
@@ -7470,7 +7533,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7470
7533
  }
7471
7534
  const laneCount = Math.max(1, laneEndDays.length);
7472
7535
  const resourceRowHeight = laneCount * options.laneHeight;
7473
- const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7536
+ const conflictCount = countConflictOverlaps(laidOutItems);
7474
7537
  const row = {
7475
7538
  resource,
7476
7539
  resourceId: resource.id,
@@ -7715,7 +7778,8 @@ var DEFAULT_ROW_HEADER_WIDTH = 240;
7715
7778
  var DEFAULT_RESOURCE_ROW_GAP = 8;
7716
7779
  var ITEM_OUTER_VERTICAL_INSET = 2;
7717
7780
  var ITEM_INNER_VERTICAL_INSET = 1;
7718
- var ITEM_HORIZONTAL_INSET = 1;
7781
+ var ITEM_START_HORIZONTAL_INSET = 2;
7782
+ var ITEM_END_HORIZONTAL_INSET = 1;
7719
7783
  var isValidDate = (date) => !Number.isNaN(date.getTime());
7720
7784
  var collectValidItems = (resources) => {
7721
7785
  return resources.flatMap(
@@ -7737,12 +7801,18 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7737
7801
  const topInset = laneIndex === 0 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7738
7802
  const bottomInset = laneIndex === laneCount - 1 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7739
7803
  return {
7740
- left: geometry.left + ITEM_HORIZONTAL_INSET,
7804
+ left: geometry.left + ITEM_START_HORIZONTAL_INSET,
7741
7805
  top: geometry.top + topInset,
7742
- 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),
7743
7807
  height: Math.max(0, geometry.height - topInset - bottomInset)
7744
7808
  };
7745
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
+ };
7746
7816
  var getWeekendOverlaySegments = (startDate, endDate, dayWidth, weekendPredicate) => {
7747
7817
  const segments2 = [];
7748
7818
  const current = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
@@ -7785,7 +7855,143 @@ var getRangeOverlaySegments = (itemStartDate, ranges, dayWidth) => {
7785
7855
  };
7786
7856
  });
7787
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
+ });
7788
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
+ };
7789
7995
  function ResourceTimelineChart({
7790
7996
  resources,
7791
7997
  dayWidth = DEFAULT_DAY_WIDTH,
@@ -7803,11 +8009,15 @@ function ResourceTimelineChart({
7803
8009
  renderItem,
7804
8010
  getItemClassName,
7805
8011
  onResourceItemClick,
7806
- onResourceItemMove
8012
+ onResourceItemMove,
8013
+ onAddResource,
8014
+ enableAddResource = true,
8015
+ resourceMenuCommands = []
7807
8016
  }) {
7808
8017
  const scrollContainerRef = (0, import_react14.useRef)(null);
7809
8018
  const gridRef = (0, import_react14.useRef)(null);
7810
8019
  const panStateRef = (0, import_react14.useRef)(null);
8020
+ const [isCreatingResource, setIsCreatingResource] = (0, import_react14.useState)(false);
7811
8021
  const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
7812
8022
  const dateRange = (0, import_react14.useMemo)(() => getMultiMonthDays(validItems), [validItems]);
7813
8023
  const monthStart = (0, import_react14.useMemo)(() => {
@@ -7842,6 +8052,18 @@ function ResourceTimelineChart({
7842
8052
  }
7843
8053
  return map;
7844
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), []);
7845
8067
  const { preview, startDrag } = useResourceItemDrag({
7846
8068
  dayWidth,
7847
8069
  monthStart,
@@ -7936,28 +8158,34 @@ function ResourceTimelineChart({
7936
8158
  style: { height: `${headerHeight}px` }
7937
8159
  }
7938
8160
  ),
7939
- layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7940
- "div",
8161
+ layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8162
+ ResourceHeader,
7941
8163
  {
7942
- className: "gantt-resourceTimeline-resourceHeader",
7943
- "data-resource-row-id": row.resourceId,
7944
- style: {
7945
- height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`,
7946
- paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7947
- },
7948
- children: [
7949
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name }),
7950
- row.conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7951
- "span",
7952
- {
7953
- className: "gantt-resourceTimeline-conflictBadge",
7954
- "aria-label": `${row.conflictCount} \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u043E\u0432`,
7955
- children: row.conflictCount
7956
- }
7957
- )
7958
- ]
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
7959
8170
  },
7960
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
+ }
7961
8189
  ))
7962
8190
  ]
7963
8191
  }
@@ -7983,14 +8211,14 @@ function ResourceTimelineChart({
7983
8211
  {
7984
8212
  ref: gridRef,
7985
8213
  className: "gantt-resourceTimeline-grid",
7986
- style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
8214
+ style: { width: `${gridWidth}px`, height: `${displayTotalHeight}px` },
7987
8215
  children: [
7988
8216
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7989
8217
  GridBackground_default,
7990
8218
  {
7991
8219
  dateRange,
7992
8220
  dayWidth,
7993
- totalHeight: layout.totalHeight,
8221
+ totalHeight: displayTotalHeight,
7994
8222
  viewMode,
7995
8223
  isCustomWeekend: weekendPredicate
7996
8224
  }
@@ -8008,6 +8236,17 @@ function ResourceTimelineChart({
8008
8236
  },
8009
8237
  row.resourceId
8010
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
+ ),
8011
8250
  Array.from(itemsByResourceId.values()).flatMap(
8012
8251
  (resourceItems) => resourceItems.map((layoutItem) => {
8013
8252
  const customClassName = getItemClassName?.(layoutItem.item);
@@ -8033,11 +8272,17 @@ function ResourceTimelineChart({
8033
8272
  }, layoutItem.laneIndex, laneCount);
8034
8273
  const overlayStartDate = isDraggingItem ? preview.startDate : layoutItem.startDate;
8035
8274
  const overlayEndDate = isDraggingItem ? preview.endDate : layoutItem.endDate;
8036
- const weekendOverlaySegments = businessDays ? getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate) : [];
8037
- const conflictOverlaySegments = getRangeOverlaySegments(
8038
- overlayStartDate,
8039
- isDraggingItem ? [] : layoutItem.conflictRanges,
8040
- 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
8041
8286
  );
8042
8287
  const durationValue = getDurationValue(
8043
8288
  overlayStartDate,
@@ -8056,6 +8301,7 @@ function ResourceTimelineChart({
8056
8301
  {
8057
8302
  className,
8058
8303
  "data-resource-item-id": layoutItem.itemId,
8304
+ "data-resource-item-tooltip": layoutItem.item.title,
8059
8305
  onMouseDown: (event) => startDrag(event, layoutItem),
8060
8306
  onClick: () => onResourceItemClick?.(layoutItem.item),
8061
8307
  onKeyDown: (event) => {
@@ -9192,7 +9438,16 @@ Button.displayName = "Button";
9192
9438
  var and = (...predicates) => (task) => predicates.every((p) => p(task));
9193
9439
  var or = (...predicates) => (task) => predicates.some((p) => p(task));
9194
9440
  var not = (predicate) => (task) => !predicate(task);
9195
- var withoutDeps = () => (task) => !!task && (!task.dependencies || task.dependencies.length === 0);
9441
+ var withoutDeps = (options = {}) => (task) => {
9442
+ if (!task) return false;
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
+ }
9449
+ return !task.dependencies || task.dependencies.length === 0;
9450
+ };
9196
9451
  var expired = (referenceDate = /* @__PURE__ */ new Date()) => (task) => isTaskExpired(task, referenceDate);
9197
9452
  var inDateRange = (rangeStart, rangeEnd) => (task) => {
9198
9453
  if (!task) return false;