gantt-lib 0.80.1 → 0.82.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.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-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';
2
+ import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-BPStPBtF.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-BPStPBtF.mjs';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import * as RadixPopover from '@radix-ui/react-popover';
6
6
 
@@ -549,7 +549,7 @@ declare const GanttChart: <TTask extends Task = Task, TItem extends ResourceTime
549
549
  ref?: React$1.Ref<GanttChartHandle>;
550
550
  }) => React$1.ReactElement;
551
551
 
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;
552
+ declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, viewMode, rowHeaderWidth, laneHeight, headerHeight, containerHeight, allowVerticalPan, customDays, isWeekend, businessDays, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemClick, onResourceItemMenuClick, activeResourceItemId, onResourceItemMove, onResourceChange, onAddResource, enableAddResource, resourceMenuCommands, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
553
553
 
554
554
  interface TaskRowProps {
555
555
  /** Task data to render */
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-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';
2
+ import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-BPStPBtF.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-BPStPBtF.js';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import * as RadixPopover from '@radix-ui/react-popover';
6
6
 
@@ -549,7 +549,7 @@ declare const GanttChart: <TTask extends Task = Task, TItem extends ResourceTime
549
549
  ref?: React$1.Ref<GanttChartHandle>;
550
550
  }) => React$1.ReactElement;
551
551
 
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;
552
+ declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, viewMode, rowHeaderWidth, laneHeight, headerHeight, containerHeight, allowVerticalPan, customDays, isWeekend, businessDays, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemClick, onResourceItemMenuClick, activeResourceItemId, onResourceItemMove, onResourceChange, onAddResource, enableAddResource, resourceMenuCommands, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
553
553
 
554
554
  interface TaskRowProps {
555
555
  /** Task data to render */
package/dist/index.js CHANGED
@@ -7746,7 +7746,7 @@ var import_jsx_runtime15 = require("react/jsx-runtime");
7746
7746
  var DEFAULT_DAY_WIDTH = 40;
7747
7747
  var DEFAULT_HEADER_HEIGHT = 40;
7748
7748
  var DEFAULT_LANE_HEIGHT = 40;
7749
- var DEFAULT_ROW_HEADER_WIDTH = 240;
7749
+ var DEFAULT_ROW_HEADER_WIDTH = 420;
7750
7750
  var DEFAULT_RESOURCE_ROW_GAP = 8;
7751
7751
  var ITEM_OUTER_VERTICAL_INSET = 2;
7752
7752
  var ITEM_INNER_VERTICAL_INSET = 1;
@@ -7834,20 +7834,97 @@ var clampOverlaySegments = (segments2, maxWidth) => segments2.flatMap((segment)
7834
7834
  return width > 0 ? [{ left, width }] : [];
7835
7835
  });
7836
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 RESOURCE_TYPE_OPTIONS = ["\u041B\u044E\u0434\u0438", "\u041E\u0431\u043E\u0440\u0443\u0434\u043E\u0432\u0430\u043D\u0438\u0435", "\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B", "\u0414\u0440\u0443\u0433\u043E\u0435"];
7838
+ var RESOURCE_SCOPE_OPTIONS = ["Shared", "Project"];
7839
+ var RESOURCE_SCOPE_LABELS = {
7840
+ Shared: "\u041E\u0431\u0449\u0438\u0439",
7841
+ Project: "\u041F\u0440\u043E\u0435\u043A\u0442"
7842
+ };
7843
+ var RESOURCE_TYPE_CLASS_NAMES = {
7844
+ \u041B\u044E\u0434\u0438: "People",
7845
+ \u041E\u0431\u043E\u0440\u0443\u0434\u043E\u0432\u0430\u043D\u0438\u0435: "Equipment",
7846
+ \u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B: "Materials",
7847
+ \u0414\u0440\u0443\u0433\u043E\u0435: "Other"
7848
+ };
7849
+ var ResourceTypeIcon = ({ type }) => {
7850
+ if (type === "\u041B\u044E\u0434\u0438") {
7851
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "gantt-resourceTimeline-resourceTypeIcon gantt-resourceTimeline-resourceTypeIconPeople", width: "16", height: "16", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
7852
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
7853
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "9", cy: "7", r: "4" }),
7854
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }),
7855
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" })
7856
+ ] });
7857
+ }
7858
+ if (type === "\u041E\u0431\u043E\u0440\u0443\u0434\u043E\u0432\u0430\u043D\u0438\u0435") {
7859
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "gantt-resourceTimeline-resourceTypeIcon gantt-resourceTimeline-resourceTypeIconEquipment", width: "16", height: "16", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
7860
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m15 12-9.373 9.373a1 1 0 0 1-3.001-3L12 9" }),
7861
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m18 15 4-4" }),
7862
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m21.5 11.5-1.914-1.914A2 2 0 0 1 19 8.172v-.344a2 2 0 0 0-.586-1.414l-1.657-1.657A6 6 0 0 0 12.516 3H9l1.243 1.243A6 6 0 0 1 12 8.485V10l2 2h1.172a2 2 0 0 1 1.414.586L18.5 14.5" })
7863
+ ] });
7864
+ }
7865
+ if (type === "\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B") {
7866
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "gantt-resourceTimeline-resourceTypeIcon gantt-resourceTimeline-resourceTypeIconMaterials", width: "16", height: "16", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
7867
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M10 22v-8" }),
7868
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M2.336 8.89 10 14l11.715-7.029" }),
7869
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M22 14a2 2 0 0 1-.971 1.715l-10 6a2 2 0 0 1-2.138-.05l-6-4A2 2 0 0 1 2 16v-6a2 2 0 0 1 .971-1.715l10-6a2 2 0 0 1 2.138.05l6 4A2 2 0 0 1 22 8z" })
7870
+ ] });
7871
+ }
7872
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "gantt-resourceTimeline-resourceTypeIcon gantt-resourceTimeline-resourceTypeIconOther", width: "16", height: "16", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
7873
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "9" }),
7874
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 8v4" }),
7875
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 16h.01" })
7876
+ ] });
7877
+ };
7837
7878
  var ResourceHeader = ({
7838
7879
  resource,
7839
7880
  resourceId,
7881
+ rowIndex,
7840
7882
  conflictCount,
7883
+ workedDays,
7884
+ assignmentCount,
7841
7885
  height,
7842
7886
  paddingBottom,
7843
- menuCommands
7887
+ menuCommands,
7888
+ onResourceChange,
7889
+ onConflictBadgeClick
7844
7890
  }) => {
7845
7891
  const [menuOpen, setMenuOpen] = (0, import_react14.useState)(false);
7892
+ const [typeMenuOpen, setTypeMenuOpen] = (0, import_react14.useState)(false);
7893
+ const [scopeMenuOpen, setScopeMenuOpen] = (0, import_react14.useState)(false);
7894
+ const [draftName, setDraftName] = (0, import_react14.useState)(resource.name);
7846
7895
  const visibleCommands = (0, import_react14.useMemo)(
7847
7896
  () => menuCommands.filter((command) => command.isVisible?.(resource) ?? true),
7848
7897
  [menuCommands, resource]
7849
7898
  );
7850
7899
  const hasMenu = visibleCommands.length > 0;
7900
+ const type = resource.type ?? "\u0414\u0440\u0443\u0433\u043E\u0435";
7901
+ const scope = resource.scope ?? "Project";
7902
+ const scopeLabel = RESOURCE_SCOPE_LABELS[scope] ?? scope;
7903
+ (0, import_react14.useEffect)(() => {
7904
+ setDraftName(resource.name);
7905
+ }, [resource.name]);
7906
+ const applyResourcePatch = (0, import_react14.useCallback)((patch) => {
7907
+ onResourceChange?.({ ...resource, ...patch });
7908
+ }, [onResourceChange, resource]);
7909
+ const handleNameCommit = (0, import_react14.useCallback)(() => {
7910
+ const nextName = draftName.trim();
7911
+ if (!nextName) {
7912
+ setDraftName(resource.name);
7913
+ return;
7914
+ }
7915
+ if (nextName !== resource.name) {
7916
+ applyResourcePatch({ name: nextName });
7917
+ }
7918
+ }, [applyResourcePatch, draftName, resource.name]);
7919
+ const handleNameKeyDown = (0, import_react14.useCallback)((event) => {
7920
+ if (event.key === "Enter") {
7921
+ event.preventDefault();
7922
+ event.currentTarget.blur();
7923
+ } else if (event.key === "Escape") {
7924
+ setDraftName(resource.name);
7925
+ event.currentTarget.blur();
7926
+ }
7927
+ }, [resource.name]);
7851
7928
  const handleCommandClick = (command, event) => {
7852
7929
  event.stopPropagation();
7853
7930
  if (command.closeOnSelect !== false) {
@@ -7865,44 +7942,154 @@ var ResourceHeader = ({
7865
7942
  paddingBottom: `${paddingBottom}px`
7866
7943
  },
7867
7944
  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: [
7945
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceNumber", children: rowIndex + 1 }),
7946
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceName", children: [
7947
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
7948
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7949
+ "button",
7950
+ {
7951
+ type: "button",
7952
+ className: `gantt-resourceTimeline-resourceTypeIconButton gantt-resourceTimeline-resourceTypeIconButton${RESOURCE_TYPE_CLASS_NAMES[type] ?? "Other"}`,
7953
+ disabled: !onResourceChange,
7954
+ "aria-label": `\u0422\u0438\u043F \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}: ${type}`,
7955
+ title: type,
7956
+ onClick: (event) => {
7957
+ event.stopPropagation();
7958
+ setTypeMenuOpen((open) => !open);
7959
+ },
7960
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ResourceTypeIcon, { type })
7961
+ }
7962
+ ) }),
7963
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverContent, { className: "gantt-resourceTimeline-resourceOptionMenu", portal: true, align: "start", children: RESOURCE_TYPE_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7964
+ "button",
7965
+ {
7966
+ type: "button",
7967
+ className: `gantt-resourceTimeline-resourceOption${type === option ? " gantt-resourceTimeline-resourceOptionActive" : ""}`,
7968
+ onClick: (event) => {
7969
+ event.stopPropagation();
7970
+ setTypeMenuOpen(false);
7971
+ applyResourcePatch({ type: option });
7972
+ },
7973
+ children: [
7974
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceOptionIcon", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ResourceTypeIcon, { type: option }) }),
7975
+ option
7976
+ ]
7977
+ },
7978
+ option
7979
+ )) })
7980
+ ] }),
7981
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7982
+ "textarea",
7983
+ {
7984
+ className: "gantt-resourceTimeline-resourceNameInput",
7985
+ value: draftName,
7986
+ disabled: !onResourceChange,
7987
+ "aria-label": `\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}`,
7988
+ rows: 2,
7989
+ onChange: (event) => setDraftName(event.target.value),
7990
+ onBlur: handleNameCommit,
7991
+ onKeyDown: handleNameKeyDown,
7992
+ onClick: (event) => event.stopPropagation()
7993
+ }
7994
+ )
7995
+ ] }),
7996
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: scopeMenuOpen, onOpenChange: setScopeMenuOpen, children: [
7879
7997
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7880
7998
  "button",
7881
7999
  {
7882
- className: "gantt-resourceTimeline-resourceMenuButton",
7883
8000
  type: "button",
7884
- "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
8001
+ className: `gantt-resourceTimeline-resourceScopeChip gantt-resourceTimeline-resourceScope${scope === "Shared" ? "Shared" : "Project"}`,
8002
+ disabled: !onResourceChange,
8003
+ "aria-label": `\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}`,
7885
8004
  onClick: (event) => {
7886
8005
  event.stopPropagation();
7887
- setMenuOpen((open) => !open);
8006
+ setScopeMenuOpen((open) => !open);
7888
8007
  },
7889
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u22EE" })
8008
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: scopeLabel })
7890
8009
  }
7891
8010
  ) }),
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)(
8011
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverContent, { className: "gantt-resourceTimeline-resourceOptionMenu", portal: true, align: "start", children: RESOURCE_SCOPE_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7893
8012
  "button",
7894
8013
  {
7895
8014
  type: "button",
7896
- className: `gantt-resourceTimeline-resourceMenuItem${command.danger ? " gantt-resourceTimeline-resourceMenuItemDanger" : ""}`,
7897
- disabled: command.isDisabled?.(resource) ?? false,
7898
- onClick: (event) => handleCommandClick(command, event),
8015
+ className: `gantt-resourceTimeline-resourceOption${scope === option ? " gantt-resourceTimeline-resourceOptionActive" : ""}`,
8016
+ onClick: (event) => {
8017
+ event.stopPropagation();
8018
+ setScopeMenuOpen(false);
8019
+ applyResourcePatch({ scope: option });
8020
+ },
7899
8021
  children: [
7900
- command.icon,
7901
- command.label
8022
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `gantt-resourceTimeline-resourceOptionScopeDot gantt-resourceTimeline-resourceScope${option}` }),
8023
+ RESOURCE_SCOPE_LABELS[option] ?? option
7902
8024
  ]
7903
8025
  },
7904
- command.id
8026
+ option
7905
8027
  )) })
8028
+ ] }),
8029
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8030
+ "span",
8031
+ {
8032
+ className: "gantt-resourceTimeline-resourceAssignments",
8033
+ "aria-label": `\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}: ${assignmentCount}, ${workedDays} \u0434\u043D.`,
8034
+ children: [
8035
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceWorkedDays", children: [
8036
+ workedDays,
8037
+ " \u0434\u043D."
8038
+ ] }),
8039
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceAssignmentCount", children: [
8040
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "gantt-resourceTimeline-resourceAssignmentIcon", width: "14", height: "14", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
8041
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("rect", { width: "15", height: "5", x: "4", y: "5", rx: "2" }),
8042
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("rect", { width: "10", height: "5", x: "4", y: "14", rx: "2" })
8043
+ ] }),
8044
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: assignmentCount })
8045
+ ] })
8046
+ ]
8047
+ }
8048
+ ),
8049
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceActions", children: [
8050
+ conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8051
+ "button",
8052
+ {
8053
+ type: "button",
8054
+ className: "gantt-resourceTimeline-conflictBadge",
8055
+ "aria-label": formatOverlapCount(conflictCount),
8056
+ title: formatOverlapCount(conflictCount),
8057
+ onClick: (event) => {
8058
+ event.stopPropagation();
8059
+ onConflictBadgeClick?.(resourceId);
8060
+ },
8061
+ children: conflictCount
8062
+ }
8063
+ ),
8064
+ hasMenu && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: menuOpen, onOpenChange: setMenuOpen, children: [
8065
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8066
+ "button",
8067
+ {
8068
+ className: "gantt-resourceTimeline-resourceMenuButton",
8069
+ type: "button",
8070
+ "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
8071
+ onClick: (event) => {
8072
+ event.stopPropagation();
8073
+ setMenuOpen((open) => !open);
8074
+ },
8075
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u22EE" })
8076
+ }
8077
+ ) }),
8078
+ /* @__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)(
8079
+ "button",
8080
+ {
8081
+ type: "button",
8082
+ className: `gantt-resourceTimeline-resourceMenuItem${command.danger ? " gantt-resourceTimeline-resourceMenuItemDanger" : ""}`,
8083
+ disabled: command.isDisabled?.(resource) ?? false,
8084
+ onClick: (event) => handleCommandClick(command, event),
8085
+ children: [
8086
+ command.icon,
8087
+ command.label
8088
+ ]
8089
+ },
8090
+ command.id
8091
+ )) })
8092
+ ] })
7906
8093
  ] })
7907
8094
  ]
7908
8095
  }
@@ -7941,7 +8128,7 @@ var NewResourceRow = ({ height, onConfirm, onCancel }) => {
7941
8128
  handleConfirm();
7942
8129
  }
7943
8130
  };
7944
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8131
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7945
8132
  "div",
7946
8133
  {
7947
8134
  className: "gantt-resourceTimeline-resourceHeader gantt-resourceTimeline-resourceHeaderNew",
@@ -7949,18 +8136,21 @@ var NewResourceRow = ({ height, onConfirm, onCancel }) => {
7949
8136
  height: `${height}px`,
7950
8137
  paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7951
8138
  },
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
- )
8139
+ children: [
8140
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceNumber", children: "+" }),
8141
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8142
+ Input,
8143
+ {
8144
+ ref: inputRef,
8145
+ value: nameValue,
8146
+ onChange: (event) => setNameValue(event.target.value),
8147
+ onKeyDown: handleKeyDown,
8148
+ onBlur: handleBlur,
8149
+ placeholder: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
8150
+ className: "gantt-resourceTimeline-resourceInput"
8151
+ }
8152
+ )
8153
+ ]
7964
8154
  }
7965
8155
  );
7966
8156
  };
@@ -7981,7 +8171,10 @@ function ResourceTimelineChart({
7981
8171
  renderItem,
7982
8172
  getItemClassName,
7983
8173
  onResourceItemClick,
8174
+ onResourceItemMenuClick,
8175
+ activeResourceItemId,
7984
8176
  onResourceItemMove,
8177
+ onResourceChange,
7985
8178
  onAddResource,
7986
8179
  enableAddResource = true,
7987
8180
  resourceMenuCommands = []
@@ -7989,7 +8182,10 @@ function ResourceTimelineChart({
7989
8182
  const scrollContainerRef = (0, import_react14.useRef)(null);
7990
8183
  const gridRef = (0, import_react14.useRef)(null);
7991
8184
  const panStateRef = (0, import_react14.useRef)(null);
8185
+ const conflictNavigationIndexRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
8186
+ const conflictHighlightTimeoutRef = (0, import_react14.useRef)(null);
7992
8187
  const [isCreatingResource, setIsCreatingResource] = (0, import_react14.useState)(false);
8188
+ const [activeConflictItemId, setActiveConflictItemId] = (0, import_react14.useState)(null);
7993
8189
  const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
7994
8190
  const dateRange = (0, import_react14.useMemo)(() => getMultiMonthDays(validItems), [validItems]);
7995
8191
  const monthStart = (0, import_react14.useMemo)(() => {
@@ -8001,6 +8197,26 @@ function ResourceTimelineChart({
8001
8197
  [customDays, isWeekend3]
8002
8198
  );
8003
8199
  const gridWidth = (0, import_react14.useMemo)(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
8200
+ const effectiveRowHeaderWidth = Math.max(rowHeaderWidth, DEFAULT_ROW_HEADER_WIDTH);
8201
+ const workedDaysByResourceId = (0, import_react14.useMemo)(() => {
8202
+ const map = /* @__PURE__ */ new Map();
8203
+ for (const resource of resources) {
8204
+ const workedDays = resource.items.reduce((sum, item) => {
8205
+ try {
8206
+ const startDate = parseUTCDate(item.startDate);
8207
+ const endDate = parseUTCDate(item.endDate);
8208
+ if (!isValidDate(startDate) || !isValidDate(endDate)) {
8209
+ return sum;
8210
+ }
8211
+ return sum + getDurationValue(startDate, endDate, businessDays, weekendPredicate);
8212
+ } catch {
8213
+ return sum;
8214
+ }
8215
+ }, 0);
8216
+ map.set(resource.id, workedDays);
8217
+ }
8218
+ return map;
8219
+ }, [businessDays, resources, weekendPredicate]);
8004
8220
  const layout = (0, import_react14.useMemo)(
8005
8221
  () => layoutResourceTimelineItems(resources, {
8006
8222
  monthStart,
@@ -8024,6 +8240,23 @@ function ResourceTimelineChart({
8024
8240
  }
8025
8241
  return map;
8026
8242
  }, [layout.items]);
8243
+ const conflictItemsByResourceId = (0, import_react14.useMemo)(() => {
8244
+ const map = /* @__PURE__ */ new Map();
8245
+ for (const item of layout.items) {
8246
+ if (item.conflictRanges.length === 0) {
8247
+ continue;
8248
+ }
8249
+ const next = map.get(item.resourceId) ?? [];
8250
+ next.push(item);
8251
+ map.set(item.resourceId, next);
8252
+ }
8253
+ for (const items of map.values()) {
8254
+ items.sort(
8255
+ (left, right) => left.top - right.top || left.left - right.left || left.itemId.localeCompare(right.itemId)
8256
+ );
8257
+ }
8258
+ return map;
8259
+ }, [layout.items]);
8027
8260
  const canAddResource = enableAddResource && Boolean(onAddResource);
8028
8261
  const resourceAddRowHeight = laneHeight + DEFAULT_RESOURCE_ROW_GAP;
8029
8262
  const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
@@ -8031,11 +8264,38 @@ function ResourceTimelineChart({
8031
8264
  onAddResource?.({
8032
8265
  id: crypto.randomUUID(),
8033
8266
  name,
8267
+ type: "\u0414\u0440\u0443\u0433\u043E\u0435",
8268
+ scope: "Project",
8034
8269
  items: []
8035
8270
  });
8036
8271
  setIsCreatingResource(false);
8037
8272
  }, [onAddResource]);
8038
8273
  const handleCancelNewResource = (0, import_react14.useCallback)(() => setIsCreatingResource(false), []);
8274
+ const handleConflictBadgeClick = (0, import_react14.useCallback)((resourceId) => {
8275
+ const conflictItems = conflictItemsByResourceId.get(resourceId) ?? [];
8276
+ if (conflictItems.length === 0) {
8277
+ return;
8278
+ }
8279
+ const currentIndex = conflictNavigationIndexRef.current.get(resourceId) ?? 0;
8280
+ const target = conflictItems[currentIndex % conflictItems.length];
8281
+ conflictNavigationIndexRef.current.set(resourceId, (currentIndex + 1) % conflictItems.length);
8282
+ const container = scrollContainerRef.current;
8283
+ if (container) {
8284
+ container.scrollTo({
8285
+ left: Math.max(0, Math.round(target.left - dayWidth * 2)),
8286
+ top: Math.max(0, Math.round(target.top - laneHeight)),
8287
+ behavior: "smooth"
8288
+ });
8289
+ }
8290
+ setActiveConflictItemId(target.itemId);
8291
+ if (conflictHighlightTimeoutRef.current) {
8292
+ window.clearTimeout(conflictHighlightTimeoutRef.current);
8293
+ }
8294
+ conflictHighlightTimeoutRef.current = window.setTimeout(() => {
8295
+ setActiveConflictItemId((current) => current === target.itemId ? null : current);
8296
+ conflictHighlightTimeoutRef.current = null;
8297
+ }, 1600);
8298
+ }, [conflictItemsByResourceId, dayWidth, laneHeight]);
8039
8299
  const { preview, startDrag } = useResourceItemDrag({
8040
8300
  dayWidth,
8041
8301
  monthStart,
@@ -8104,6 +8364,13 @@ function ResourceTimelineChart({
8104
8364
  window.removeEventListener("mouseup", handlePanEnd);
8105
8365
  };
8106
8366
  }, [allowVerticalPan]);
8367
+ (0, import_react14.useEffect)(() => {
8368
+ return () => {
8369
+ if (conflictHighlightTimeoutRef.current) {
8370
+ window.clearTimeout(conflictHighlightTimeoutRef.current);
8371
+ }
8372
+ };
8373
+ }, []);
8107
8374
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-container gantt-resourceTimeline", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8108
8375
  "div",
8109
8376
  {
@@ -8121,24 +8388,36 @@ function ResourceTimelineChart({
8121
8388
  "div",
8122
8389
  {
8123
8390
  className: "gantt-resourceTimeline-resourceColumn",
8124
- style: { width: `${rowHeaderWidth}px`, minWidth: `${rowHeaderWidth}px` },
8391
+ style: { width: `${effectiveRowHeaderWidth}px`, minWidth: `${effectiveRowHeaderWidth}px` },
8125
8392
  children: [
8126
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8393
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8127
8394
  "div",
8128
8395
  {
8129
8396
  className: "gantt-resourceTimeline-corner",
8130
- style: { height: `${headerHeight}px` }
8397
+ style: { height: `${headerHeight + 0.5}px` },
8398
+ children: [
8399
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderNumber", children: "#" }),
8400
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderName", children: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435" }),
8401
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell", "aria-label": "\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C" }),
8402
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell", children: "\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F" }),
8403
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderActions", "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F" })
8404
+ ]
8131
8405
  }
8132
8406
  ),
8133
- layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8407
+ layout.rows.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8134
8408
  ResourceHeader,
8135
8409
  {
8136
8410
  resource: row.resource,
8137
8411
  resourceId: row.resourceId,
8412
+ rowIndex,
8138
8413
  conflictCount: row.conflictCount,
8414
+ workedDays: workedDaysByResourceId.get(row.resourceId) ?? 0,
8415
+ assignmentCount: row.resource.items.length,
8139
8416
  height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
8140
8417
  paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
8141
- menuCommands: resourceMenuCommands
8418
+ menuCommands: resourceMenuCommands,
8419
+ onResourceChange,
8420
+ onConflictBadgeClick: handleConflictBadgeClick
8142
8421
  },
8143
8422
  row.resourceId
8144
8423
  )),
@@ -8223,9 +8502,14 @@ function ResourceTimelineChart({
8223
8502
  (resourceItems) => resourceItems.map((layoutItem) => {
8224
8503
  const customClassName = getItemClassName?.(layoutItem.item);
8225
8504
  const isDraggingItem = preview?.itemId === layoutItem.itemId;
8505
+ const hasItemMenu = Boolean(onResourceItemMenuClick);
8506
+ const isActiveItem = activeResourceItemId === layoutItem.itemId;
8226
8507
  const className = [
8227
8508
  "gantt-resourceTimeline-item",
8509
+ hasItemMenu && "gantt-resourceTimeline-itemHasMenu",
8510
+ isActiveItem && "gantt-resourceTimeline-itemActive",
8228
8511
  isDraggingItem && "gantt-resourceTimeline-itemDragging",
8512
+ activeConflictItemId === layoutItem.itemId && "gantt-resourceTimeline-itemConflictActive",
8229
8513
  (readonly || layoutItem.item.locked) && "gantt-resourceTimeline-itemDisabled",
8230
8514
  customClassName
8231
8515
  ].filter(Boolean).join(" ");
@@ -8324,6 +8608,22 @@ function ResourceTimelineChart({
8324
8608
  },
8325
8609
  `conflict-overlay-${index}`
8326
8610
  )),
8611
+ hasItemMenu && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8612
+ "button",
8613
+ {
8614
+ type: "button",
8615
+ className: "gantt-resourceTimeline-itemMenuButton",
8616
+ "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F",
8617
+ onMouseDown: (event) => {
8618
+ event.stopPropagation();
8619
+ },
8620
+ onClick: (event) => {
8621
+ event.stopPropagation();
8622
+ onResourceItemMenuClick?.(layoutItem.item);
8623
+ },
8624
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u22EE" })
8625
+ }
8626
+ ),
8327
8627
  /* @__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
8628
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "gantt-resourceTimeline-defaultItemMain", children: [
8329
8629
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(