gantt-lib 0.80.1 → 0.81.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
@@ -7840,7 +7840,8 @@ var ResourceHeader = ({
7840
7840
  conflictCount,
7841
7841
  height,
7842
7842
  paddingBottom,
7843
- menuCommands
7843
+ menuCommands,
7844
+ onConflictBadgeClick
7844
7845
  }) => {
7845
7846
  const [menuOpen, setMenuOpen] = (0, import_react14.useState)(false);
7846
7847
  const visibleCommands = (0, import_react14.useMemo)(
@@ -7867,11 +7868,16 @@ var ResourceHeader = ({
7867
7868
  children: [
7868
7869
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceName", children: resource.name }),
7869
7870
  conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7870
- "span",
7871
+ "button",
7871
7872
  {
7873
+ type: "button",
7872
7874
  className: "gantt-resourceTimeline-conflictBadge",
7873
7875
  "aria-label": formatOverlapCount(conflictCount),
7874
7876
  title: formatOverlapCount(conflictCount),
7877
+ onClick: (event) => {
7878
+ event.stopPropagation();
7879
+ onConflictBadgeClick?.(resourceId);
7880
+ },
7875
7881
  children: conflictCount
7876
7882
  }
7877
7883
  ),
@@ -7981,6 +7987,8 @@ function ResourceTimelineChart({
7981
7987
  renderItem,
7982
7988
  getItemClassName,
7983
7989
  onResourceItemClick,
7990
+ onResourceItemMenuClick,
7991
+ activeResourceItemId,
7984
7992
  onResourceItemMove,
7985
7993
  onAddResource,
7986
7994
  enableAddResource = true,
@@ -7989,7 +7997,10 @@ function ResourceTimelineChart({
7989
7997
  const scrollContainerRef = (0, import_react14.useRef)(null);
7990
7998
  const gridRef = (0, import_react14.useRef)(null);
7991
7999
  const panStateRef = (0, import_react14.useRef)(null);
8000
+ const conflictNavigationIndexRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
8001
+ const conflictHighlightTimeoutRef = (0, import_react14.useRef)(null);
7992
8002
  const [isCreatingResource, setIsCreatingResource] = (0, import_react14.useState)(false);
8003
+ const [activeConflictItemId, setActiveConflictItemId] = (0, import_react14.useState)(null);
7993
8004
  const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
7994
8005
  const dateRange = (0, import_react14.useMemo)(() => getMultiMonthDays(validItems), [validItems]);
7995
8006
  const monthStart = (0, import_react14.useMemo)(() => {
@@ -8024,6 +8035,23 @@ function ResourceTimelineChart({
8024
8035
  }
8025
8036
  return map;
8026
8037
  }, [layout.items]);
8038
+ const conflictItemsByResourceId = (0, import_react14.useMemo)(() => {
8039
+ const map = /* @__PURE__ */ new Map();
8040
+ for (const item of layout.items) {
8041
+ if (item.conflictRanges.length === 0) {
8042
+ continue;
8043
+ }
8044
+ const next = map.get(item.resourceId) ?? [];
8045
+ next.push(item);
8046
+ map.set(item.resourceId, next);
8047
+ }
8048
+ for (const items of map.values()) {
8049
+ items.sort(
8050
+ (left, right) => left.top - right.top || left.left - right.left || left.itemId.localeCompare(right.itemId)
8051
+ );
8052
+ }
8053
+ return map;
8054
+ }, [layout.items]);
8027
8055
  const canAddResource = enableAddResource && Boolean(onAddResource);
8028
8056
  const resourceAddRowHeight = laneHeight + DEFAULT_RESOURCE_ROW_GAP;
8029
8057
  const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
@@ -8036,6 +8064,31 @@ function ResourceTimelineChart({
8036
8064
  setIsCreatingResource(false);
8037
8065
  }, [onAddResource]);
8038
8066
  const handleCancelNewResource = (0, import_react14.useCallback)(() => setIsCreatingResource(false), []);
8067
+ const handleConflictBadgeClick = (0, import_react14.useCallback)((resourceId) => {
8068
+ const conflictItems = conflictItemsByResourceId.get(resourceId) ?? [];
8069
+ if (conflictItems.length === 0) {
8070
+ return;
8071
+ }
8072
+ const currentIndex = conflictNavigationIndexRef.current.get(resourceId) ?? 0;
8073
+ const target = conflictItems[currentIndex % conflictItems.length];
8074
+ conflictNavigationIndexRef.current.set(resourceId, (currentIndex + 1) % conflictItems.length);
8075
+ const container = scrollContainerRef.current;
8076
+ if (container) {
8077
+ container.scrollTo({
8078
+ left: Math.max(0, Math.round(target.left - dayWidth * 2)),
8079
+ top: Math.max(0, Math.round(target.top - laneHeight)),
8080
+ behavior: "smooth"
8081
+ });
8082
+ }
8083
+ setActiveConflictItemId(target.itemId);
8084
+ if (conflictHighlightTimeoutRef.current) {
8085
+ window.clearTimeout(conflictHighlightTimeoutRef.current);
8086
+ }
8087
+ conflictHighlightTimeoutRef.current = window.setTimeout(() => {
8088
+ setActiveConflictItemId((current) => current === target.itemId ? null : current);
8089
+ conflictHighlightTimeoutRef.current = null;
8090
+ }, 1600);
8091
+ }, [conflictItemsByResourceId, dayWidth, laneHeight]);
8039
8092
  const { preview, startDrag } = useResourceItemDrag({
8040
8093
  dayWidth,
8041
8094
  monthStart,
@@ -8104,6 +8157,13 @@ function ResourceTimelineChart({
8104
8157
  window.removeEventListener("mouseup", handlePanEnd);
8105
8158
  };
8106
8159
  }, [allowVerticalPan]);
8160
+ (0, import_react14.useEffect)(() => {
8161
+ return () => {
8162
+ if (conflictHighlightTimeoutRef.current) {
8163
+ window.clearTimeout(conflictHighlightTimeoutRef.current);
8164
+ }
8165
+ };
8166
+ }, []);
8107
8167
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-container gantt-resourceTimeline", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8108
8168
  "div",
8109
8169
  {
@@ -8138,7 +8198,8 @@ function ResourceTimelineChart({
8138
8198
  conflictCount: row.conflictCount,
8139
8199
  height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
8140
8200
  paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
8141
- menuCommands: resourceMenuCommands
8201
+ menuCommands: resourceMenuCommands,
8202
+ onConflictBadgeClick: handleConflictBadgeClick
8142
8203
  },
8143
8204
  row.resourceId
8144
8205
  )),
@@ -8223,9 +8284,14 @@ function ResourceTimelineChart({
8223
8284
  (resourceItems) => resourceItems.map((layoutItem) => {
8224
8285
  const customClassName = getItemClassName?.(layoutItem.item);
8225
8286
  const isDraggingItem = preview?.itemId === layoutItem.itemId;
8287
+ const hasItemMenu = Boolean(onResourceItemMenuClick);
8288
+ const isActiveItem = activeResourceItemId === layoutItem.itemId;
8226
8289
  const className = [
8227
8290
  "gantt-resourceTimeline-item",
8291
+ hasItemMenu && "gantt-resourceTimeline-itemHasMenu",
8292
+ isActiveItem && "gantt-resourceTimeline-itemActive",
8228
8293
  isDraggingItem && "gantt-resourceTimeline-itemDragging",
8294
+ activeConflictItemId === layoutItem.itemId && "gantt-resourceTimeline-itemConflictActive",
8229
8295
  (readonly || layoutItem.item.locked) && "gantt-resourceTimeline-itemDisabled",
8230
8296
  customClassName
8231
8297
  ].filter(Boolean).join(" ");
@@ -8324,6 +8390,22 @@ function ResourceTimelineChart({
8324
8390
  },
8325
8391
  `conflict-overlay-${index}`
8326
8392
  )),
8393
+ hasItemMenu && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8394
+ "button",
8395
+ {
8396
+ type: "button",
8397
+ className: "gantt-resourceTimeline-itemMenuButton",
8398
+ "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F",
8399
+ onMouseDown: (event) => {
8400
+ event.stopPropagation();
8401
+ },
8402
+ onClick: (event) => {
8403
+ event.stopPropagation();
8404
+ onResourceItemMenuClick?.(layoutItem.item);
8405
+ },
8406
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u22EE" })
8407
+ }
8408
+ ),
8327
8409
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-resourceTimeline-itemInner", children: renderItem ? renderItem(layoutItem.item, renderContext) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "gantt-resourceTimeline-defaultItemContent", children: [
8328
8410
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "gantt-resourceTimeline-defaultItemMain", children: [
8329
8411
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(