gantt-lib 0.81.0 → 0.83.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/core/scheduling/index.d.mts +1 -1
- package/dist/core/scheduling/index.d.ts +1 -1
- package/dist/{index-p-aPnDwQ.d.mts → index-DGOZyXZt.d.mts} +5 -0
- package/dist/{index-p-aPnDwQ.d.ts → index-DGOZyXZt.d.ts} +5 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +594 -79
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +594 -79
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +408 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7137,6 +7137,7 @@ var TaskList = ({
|
|
|
7137
7137
|
[resolvedColumns]
|
|
7138
7138
|
);
|
|
7139
7139
|
const effectiveTaskListWidth = Math.max(taskListWidth, MIN_TASK_LIST_WIDTH, resolvedColumnWidthTotal);
|
|
7140
|
+
const tableHeaderHeight = headerHeight + 1;
|
|
7140
7141
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
7141
7142
|
"div",
|
|
7142
7143
|
{
|
|
@@ -7144,7 +7145,7 @@ var TaskList = ({
|
|
|
7144
7145
|
className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}${hasRightShadow ? " gantt-tl-overlay-shadowed" : ""}`,
|
|
7145
7146
|
style: { "--tasklist-width": `${effectiveTaskListWidth}px` },
|
|
7146
7147
|
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gantt-tl-table", children: [
|
|
7147
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-header", style: { height: `${
|
|
7148
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gantt-tl-header", style: { height: `${tableHeaderHeight}px` }, children: resolvedColumns.map((col) => {
|
|
7148
7149
|
if (col.id === "dependencies") {
|
|
7149
7150
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
7150
7151
|
"div",
|
|
@@ -7328,7 +7329,7 @@ var TaskList = ({
|
|
|
7328
7329
|
};
|
|
7329
7330
|
|
|
7330
7331
|
// src/components/ResourceTimelineChart/ResourceTimelineChart.tsx
|
|
7331
|
-
var import_react14 = require("react");
|
|
7332
|
+
var import_react14 = __toESM(require("react"));
|
|
7332
7333
|
|
|
7333
7334
|
// src/utils/resourceTimelineLayout.ts
|
|
7334
7335
|
var isInvalidDate = (date) => Number.isNaN(date.getTime());
|
|
@@ -7746,8 +7747,9 @@ var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
|
7746
7747
|
var DEFAULT_DAY_WIDTH = 40;
|
|
7747
7748
|
var DEFAULT_HEADER_HEIGHT = 40;
|
|
7748
7749
|
var DEFAULT_LANE_HEIGHT = 40;
|
|
7749
|
-
var DEFAULT_ROW_HEADER_WIDTH =
|
|
7750
|
+
var DEFAULT_ROW_HEADER_WIDTH = 420;
|
|
7750
7751
|
var DEFAULT_RESOURCE_ROW_GAP = 8;
|
|
7752
|
+
var DEFAULT_RESOURCE_GROUP_HEIGHT = 28;
|
|
7751
7753
|
var ITEM_OUTER_VERTICAL_INSET = 2;
|
|
7752
7754
|
var ITEM_INNER_VERTICAL_INSET = 1;
|
|
7753
7755
|
var ITEM_START_HORIZONTAL_INSET = 2;
|
|
@@ -7834,21 +7836,114 @@ var clampOverlaySegments = (segments2, maxWidth) => segments2.flatMap((segment)
|
|
|
7834
7836
|
return width > 0 ? [{ left, width }] : [];
|
|
7835
7837
|
});
|
|
7836
7838
|
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);
|
|
7839
|
+
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"];
|
|
7840
|
+
var RESOURCE_SCOPE_OPTIONS = ["Shared", "Project"];
|
|
7841
|
+
var RESOURCE_SCOPE_LABELS = {
|
|
7842
|
+
Shared: "\u041E\u0431\u0449\u0438\u0439",
|
|
7843
|
+
Project: "\u041F\u0440\u043E\u0435\u043A\u0442"
|
|
7844
|
+
};
|
|
7845
|
+
var RESOURCE_TYPE_CLASS_NAMES = {
|
|
7846
|
+
\u041B\u044E\u0434\u0438: "People",
|
|
7847
|
+
\u041E\u0431\u043E\u0440\u0443\u0434\u043E\u0432\u0430\u043D\u0438\u0435: "Equipment",
|
|
7848
|
+
\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B: "Materials",
|
|
7849
|
+
\u0414\u0440\u0443\u0433\u043E\u0435: "Other"
|
|
7850
|
+
};
|
|
7851
|
+
var RESOURCE_TYPE_ORDER = {
|
|
7852
|
+
\u041B\u044E\u0434\u0438: 0,
|
|
7853
|
+
\u041E\u0431\u043E\u0440\u0443\u0434\u043E\u0432\u0430\u043D\u0438\u0435: 1,
|
|
7854
|
+
\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B: 2,
|
|
7855
|
+
\u0414\u0440\u0443\u0433\u043E\u0435: 3
|
|
7856
|
+
};
|
|
7857
|
+
var RESOURCE_SCOPE_ORDER = {
|
|
7858
|
+
Shared: 0,
|
|
7859
|
+
Project: 1
|
|
7860
|
+
};
|
|
7861
|
+
var getResourceType = (resource) => resource.type ?? "\u0414\u0440\u0443\u0433\u043E\u0435";
|
|
7862
|
+
var getResourceScopeOrder = (resource) => RESOURCE_SCOPE_ORDER[resource.scope ?? "Project"] ?? 99;
|
|
7863
|
+
var orderResourcesByType = (resources) => {
|
|
7864
|
+
return resources.map((resource, index) => ({ resource, index })).sort((left, right) => {
|
|
7865
|
+
const leftType = getResourceType(left.resource);
|
|
7866
|
+
const rightType = getResourceType(right.resource);
|
|
7867
|
+
const typeDiff = (RESOURCE_TYPE_ORDER[leftType] ?? 99) - (RESOURCE_TYPE_ORDER[rightType] ?? 99);
|
|
7868
|
+
if (typeDiff !== 0) {
|
|
7869
|
+
return typeDiff;
|
|
7870
|
+
}
|
|
7871
|
+
const scopeDiff = getResourceScopeOrder(left.resource) - getResourceScopeOrder(right.resource);
|
|
7872
|
+
return scopeDiff !== 0 ? scopeDiff : left.index - right.index;
|
|
7873
|
+
}).map(({ resource }) => resource);
|
|
7874
|
+
};
|
|
7875
|
+
var ResourceTypeIcon = ({ type }) => {
|
|
7876
|
+
if (type === "\u041B\u044E\u0434\u0438") {
|
|
7877
|
+
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: [
|
|
7878
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
|
|
7879
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "9", cy: "7", r: "4" }),
|
|
7880
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }),
|
|
7881
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" })
|
|
7882
|
+
] });
|
|
7883
|
+
}
|
|
7884
|
+
if (type === "\u041E\u0431\u043E\u0440\u0443\u0434\u043E\u0432\u0430\u043D\u0438\u0435") {
|
|
7885
|
+
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: [
|
|
7886
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m15 12-9.373 9.373a1 1 0 0 1-3.001-3L12 9" }),
|
|
7887
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m18 15 4-4" }),
|
|
7888
|
+
/* @__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" })
|
|
7889
|
+
] });
|
|
7890
|
+
}
|
|
7891
|
+
if (type === "\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B") {
|
|
7892
|
+
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: [
|
|
7893
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M10 22v-8" }),
|
|
7894
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M2.336 8.89 10 14l11.715-7.029" }),
|
|
7895
|
+
/* @__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" })
|
|
7896
|
+
] });
|
|
7897
|
+
}
|
|
7898
|
+
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: [
|
|
7899
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "9" }),
|
|
7900
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 8v4" }),
|
|
7901
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 16h.01" })
|
|
7902
|
+
] });
|
|
7903
|
+
};
|
|
7837
7904
|
var ResourceHeader = ({
|
|
7838
7905
|
resource,
|
|
7839
7906
|
resourceId,
|
|
7907
|
+
rowIndex,
|
|
7840
7908
|
conflictCount,
|
|
7909
|
+
workedDays,
|
|
7910
|
+
assignmentCount,
|
|
7841
7911
|
height,
|
|
7842
7912
|
paddingBottom,
|
|
7843
7913
|
menuCommands,
|
|
7914
|
+
onResourceChange,
|
|
7915
|
+
onResourceNameClick,
|
|
7844
7916
|
onConflictBadgeClick
|
|
7845
7917
|
}) => {
|
|
7846
7918
|
const [menuOpen, setMenuOpen] = (0, import_react14.useState)(false);
|
|
7919
|
+
const [typeMenuOpen, setTypeMenuOpen] = (0, import_react14.useState)(false);
|
|
7920
|
+
const [scopeMenuOpen, setScopeMenuOpen] = (0, import_react14.useState)(false);
|
|
7921
|
+
const [editingName, setEditingName] = (0, import_react14.useState)(false);
|
|
7922
|
+
const [nameValue, setNameValue] = (0, import_react14.useState)(resource.name);
|
|
7923
|
+
const nameInputRef = (0, import_react14.useRef)(null);
|
|
7924
|
+
const nameConfirmedRef = (0, import_react14.useRef)(false);
|
|
7847
7925
|
const visibleCommands = (0, import_react14.useMemo)(
|
|
7848
7926
|
() => menuCommands.filter((command) => command.isVisible?.(resource) ?? true),
|
|
7849
7927
|
[menuCommands, resource]
|
|
7850
7928
|
);
|
|
7851
7929
|
const hasMenu = visibleCommands.length > 0;
|
|
7930
|
+
const type = resource.type ?? "\u0414\u0440\u0443\u0433\u043E\u0435";
|
|
7931
|
+
const scope = resource.scope ?? "Project";
|
|
7932
|
+
const scopeLabel = RESOURCE_SCOPE_LABELS[scope] ?? scope;
|
|
7933
|
+
const applyResourcePatch = (0, import_react14.useCallback)((patch) => {
|
|
7934
|
+
onResourceChange?.({ ...resource, ...patch });
|
|
7935
|
+
}, [onResourceChange, resource]);
|
|
7936
|
+
(0, import_react14.useEffect)(() => {
|
|
7937
|
+
if (!editingName) {
|
|
7938
|
+
setNameValue(resource.name);
|
|
7939
|
+
}
|
|
7940
|
+
}, [editingName, resource.name]);
|
|
7941
|
+
(0, import_react14.useEffect)(() => {
|
|
7942
|
+
if (editingName && nameInputRef.current) {
|
|
7943
|
+
nameInputRef.current.focus();
|
|
7944
|
+
nameInputRef.current.select();
|
|
7945
|
+
}
|
|
7946
|
+
}, [editingName]);
|
|
7852
7947
|
const handleCommandClick = (command, event) => {
|
|
7853
7948
|
event.stopPropagation();
|
|
7854
7949
|
if (command.closeOnSelect !== false) {
|
|
@@ -7856,6 +7951,42 @@ var ResourceHeader = ({
|
|
|
7856
7951
|
}
|
|
7857
7952
|
command.onSelect(resource);
|
|
7858
7953
|
};
|
|
7954
|
+
const handleNameDoubleClick = (0, import_react14.useCallback)((event) => {
|
|
7955
|
+
if (!onResourceChange) {
|
|
7956
|
+
return;
|
|
7957
|
+
}
|
|
7958
|
+
event.stopPropagation();
|
|
7959
|
+
nameConfirmedRef.current = false;
|
|
7960
|
+
setNameValue(resource.name);
|
|
7961
|
+
setEditingName(true);
|
|
7962
|
+
}, [onResourceChange, resource.name]);
|
|
7963
|
+
const handleNameSave = (0, import_react14.useCallback)(() => {
|
|
7964
|
+
if (nameConfirmedRef.current) {
|
|
7965
|
+
nameConfirmedRef.current = false;
|
|
7966
|
+
return;
|
|
7967
|
+
}
|
|
7968
|
+
const nextName = nameValue.trim();
|
|
7969
|
+
if (nextName && nextName !== resource.name) {
|
|
7970
|
+
applyResourcePatch({ name: nextName });
|
|
7971
|
+
}
|
|
7972
|
+
setEditingName(false);
|
|
7973
|
+
}, [applyResourcePatch, nameValue, resource.name]);
|
|
7974
|
+
const handleNameCancel = (0, import_react14.useCallback)(() => {
|
|
7975
|
+
setNameValue(resource.name);
|
|
7976
|
+
setEditingName(false);
|
|
7977
|
+
}, [resource.name]);
|
|
7978
|
+
const handleNameKeyDown = (0, import_react14.useCallback)((event) => {
|
|
7979
|
+
if (event.key === "Enter") {
|
|
7980
|
+
nameConfirmedRef.current = true;
|
|
7981
|
+
const nextName = nameValue.trim();
|
|
7982
|
+
if (nextName && nextName !== resource.name) {
|
|
7983
|
+
applyResourcePatch({ name: nextName });
|
|
7984
|
+
}
|
|
7985
|
+
setEditingName(false);
|
|
7986
|
+
} else if (event.key === "Escape") {
|
|
7987
|
+
handleNameCancel();
|
|
7988
|
+
}
|
|
7989
|
+
}, [applyResourcePatch, handleNameCancel, nameValue, resource.name]);
|
|
7859
7990
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7860
7991
|
"div",
|
|
7861
7992
|
{
|
|
@@ -7866,49 +7997,167 @@ var ResourceHeader = ({
|
|
|
7866
7997
|
paddingBottom: `${paddingBottom}px`
|
|
7867
7998
|
},
|
|
7868
7999
|
children: [
|
|
7869
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
8000
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceNumber", children: rowIndex + 1 }),
|
|
8001
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceName", children: [
|
|
8002
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: typeMenuOpen, onOpenChange: setTypeMenuOpen, children: [
|
|
8003
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8004
|
+
"button",
|
|
8005
|
+
{
|
|
8006
|
+
type: "button",
|
|
8007
|
+
className: `gantt-resourceTimeline-resourceTypeIconButton gantt-resourceTimeline-resourceTypeIconButton${RESOURCE_TYPE_CLASS_NAMES[type] ?? "Other"}`,
|
|
8008
|
+
disabled: !onResourceChange,
|
|
8009
|
+
"aria-label": `\u0422\u0438\u043F \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}: ${type}`,
|
|
8010
|
+
title: type,
|
|
8011
|
+
onClick: (event) => {
|
|
8012
|
+
event.stopPropagation();
|
|
8013
|
+
setTypeMenuOpen((open) => !open);
|
|
8014
|
+
},
|
|
8015
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ResourceTypeIcon, { type })
|
|
8016
|
+
}
|
|
8017
|
+
) }),
|
|
8018
|
+
/* @__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)(
|
|
8019
|
+
"button",
|
|
8020
|
+
{
|
|
8021
|
+
type: "button",
|
|
8022
|
+
className: `gantt-resourceTimeline-resourceOption${type === option ? " gantt-resourceTimeline-resourceOptionActive" : ""}`,
|
|
8023
|
+
onClick: (event) => {
|
|
8024
|
+
event.stopPropagation();
|
|
8025
|
+
setTypeMenuOpen(false);
|
|
8026
|
+
applyResourcePatch({ type: option });
|
|
8027
|
+
},
|
|
8028
|
+
children: [
|
|
8029
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceOptionIcon", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ResourceTypeIcon, { type: option }) }),
|
|
8030
|
+
option
|
|
8031
|
+
]
|
|
8032
|
+
},
|
|
8033
|
+
option
|
|
8034
|
+
)) })
|
|
8035
|
+
] }),
|
|
8036
|
+
editingName ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8037
|
+
Input,
|
|
8038
|
+
{
|
|
8039
|
+
ref: nameInputRef,
|
|
8040
|
+
value: nameValue,
|
|
8041
|
+
onChange: (event) => setNameValue(event.target.value),
|
|
8042
|
+
onBlur: handleNameSave,
|
|
8043
|
+
onKeyDown: handleNameKeyDown,
|
|
8044
|
+
onClick: (event) => event.stopPropagation(),
|
|
8045
|
+
"aria-label": `\u041D\u043E\u0432\u043E\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}`,
|
|
8046
|
+
className: "gantt-tl-name-input gantt-resourceTimeline-resourceNameInput"
|
|
8047
|
+
}
|
|
8048
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8049
|
+
"button",
|
|
8050
|
+
{
|
|
8051
|
+
type: "button",
|
|
8052
|
+
className: "gantt-resourceTimeline-resourceNameButton",
|
|
8053
|
+
"aria-label": `\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}`,
|
|
8054
|
+
title: resource.name,
|
|
8055
|
+
onClick: (event) => {
|
|
8056
|
+
event.stopPropagation();
|
|
8057
|
+
onResourceNameClick?.(resourceId);
|
|
8058
|
+
},
|
|
8059
|
+
onDoubleClick: handleNameDoubleClick,
|
|
8060
|
+
children: resource.name
|
|
8061
|
+
}
|
|
8062
|
+
)
|
|
8063
|
+
] }),
|
|
8064
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: scopeMenuOpen, onOpenChange: setScopeMenuOpen, children: [
|
|
7885
8065
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
7886
8066
|
"button",
|
|
7887
8067
|
{
|
|
7888
|
-
className: "gantt-resourceTimeline-resourceMenuButton",
|
|
7889
8068
|
type: "button",
|
|
7890
|
-
|
|
8069
|
+
className: `gantt-resourceTimeline-resourceScopeChip gantt-resourceTimeline-resourceScope${scope === "Shared" ? "Shared" : "Project"}`,
|
|
8070
|
+
disabled: !onResourceChange,
|
|
8071
|
+
"aria-label": `\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}`,
|
|
7891
8072
|
onClick: (event) => {
|
|
7892
8073
|
event.stopPropagation();
|
|
7893
|
-
|
|
8074
|
+
setScopeMenuOpen((open) => !open);
|
|
7894
8075
|
},
|
|
7895
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", {
|
|
8076
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: scopeLabel })
|
|
7896
8077
|
}
|
|
7897
8078
|
) }),
|
|
7898
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverContent, { className: "gantt-resourceTimeline-
|
|
8079
|
+
/* @__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)(
|
|
7899
8080
|
"button",
|
|
7900
8081
|
{
|
|
7901
8082
|
type: "button",
|
|
7902
|
-
className: `gantt-resourceTimeline-
|
|
7903
|
-
|
|
7904
|
-
|
|
8083
|
+
className: `gantt-resourceTimeline-resourceOption${scope === option ? " gantt-resourceTimeline-resourceOptionActive" : ""}`,
|
|
8084
|
+
onClick: (event) => {
|
|
8085
|
+
event.stopPropagation();
|
|
8086
|
+
setScopeMenuOpen(false);
|
|
8087
|
+
applyResourcePatch({ scope: option });
|
|
8088
|
+
},
|
|
7905
8089
|
children: [
|
|
7906
|
-
|
|
7907
|
-
|
|
8090
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `gantt-resourceTimeline-resourceOptionScopeDot gantt-resourceTimeline-resourceScope${option}` }),
|
|
8091
|
+
RESOURCE_SCOPE_LABELS[option] ?? option
|
|
7908
8092
|
]
|
|
7909
8093
|
},
|
|
7910
|
-
|
|
8094
|
+
option
|
|
7911
8095
|
)) })
|
|
8096
|
+
] }),
|
|
8097
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8098
|
+
"span",
|
|
8099
|
+
{
|
|
8100
|
+
className: "gantt-resourceTimeline-resourceAssignments",
|
|
8101
|
+
"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.`,
|
|
8102
|
+
children: [
|
|
8103
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceWorkedDays", children: [
|
|
8104
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceMetricValue", children: workedDays > 0 ? workedDays : "-" }),
|
|
8105
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceMetricLabel", children: workedDays > 0 ? "\u0434\u043D." : "" })
|
|
8106
|
+
] }),
|
|
8107
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceAssignmentCount", children: [
|
|
8108
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceMetricValue", children: assignmentCount > 0 ? assignmentCount : "" }),
|
|
8109
|
+
assignmentCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "gantt-resourceTimeline-resourceAssignmentIcon", width: "14", height: "14", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
8110
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("rect", { width: "15", height: "5", x: "4", y: "5", rx: "2" }),
|
|
8111
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("rect", { width: "10", height: "5", x: "4", y: "14", rx: "2" })
|
|
8112
|
+
] })
|
|
8113
|
+
] })
|
|
8114
|
+
]
|
|
8115
|
+
}
|
|
8116
|
+
),
|
|
8117
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceActions", children: [
|
|
8118
|
+
conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8119
|
+
"button",
|
|
8120
|
+
{
|
|
8121
|
+
type: "button",
|
|
8122
|
+
className: "gantt-resourceTimeline-conflictBadge",
|
|
8123
|
+
"aria-label": formatOverlapCount(conflictCount),
|
|
8124
|
+
title: formatOverlapCount(conflictCount),
|
|
8125
|
+
onClick: (event) => {
|
|
8126
|
+
event.stopPropagation();
|
|
8127
|
+
onConflictBadgeClick?.(resourceId);
|
|
8128
|
+
},
|
|
8129
|
+
children: conflictCount
|
|
8130
|
+
}
|
|
8131
|
+
),
|
|
8132
|
+
hasMenu && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Popover, { open: menuOpen, onOpenChange: setMenuOpen, children: [
|
|
8133
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8134
|
+
"button",
|
|
8135
|
+
{
|
|
8136
|
+
className: "gantt-resourceTimeline-resourceMenuButton",
|
|
8137
|
+
type: "button",
|
|
8138
|
+
"aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
|
|
8139
|
+
onClick: (event) => {
|
|
8140
|
+
event.stopPropagation();
|
|
8141
|
+
setMenuOpen((open) => !open);
|
|
8142
|
+
},
|
|
8143
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u22EE" })
|
|
8144
|
+
}
|
|
8145
|
+
) }),
|
|
8146
|
+
/* @__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)(
|
|
8147
|
+
"button",
|
|
8148
|
+
{
|
|
8149
|
+
type: "button",
|
|
8150
|
+
className: `gantt-resourceTimeline-resourceMenuItem${command.danger ? " gantt-resourceTimeline-resourceMenuItemDanger" : ""}`,
|
|
8151
|
+
disabled: command.isDisabled?.(resource) ?? false,
|
|
8152
|
+
onClick: (event) => handleCommandClick(command, event),
|
|
8153
|
+
children: [
|
|
8154
|
+
command.icon,
|
|
8155
|
+
command.label
|
|
8156
|
+
]
|
|
8157
|
+
},
|
|
8158
|
+
command.id
|
|
8159
|
+
)) })
|
|
8160
|
+
] })
|
|
7912
8161
|
] })
|
|
7913
8162
|
]
|
|
7914
8163
|
}
|
|
@@ -7947,7 +8196,7 @@ var NewResourceRow = ({ height, onConfirm, onCancel }) => {
|
|
|
7947
8196
|
handleConfirm();
|
|
7948
8197
|
}
|
|
7949
8198
|
};
|
|
7950
|
-
return /* @__PURE__ */ (0, import_jsx_runtime15.
|
|
8199
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7951
8200
|
"div",
|
|
7952
8201
|
{
|
|
7953
8202
|
className: "gantt-resourceTimeline-resourceHeader gantt-resourceTimeline-resourceHeaderNew",
|
|
@@ -7955,18 +8204,21 @@ var NewResourceRow = ({ height, onConfirm, onCancel }) => {
|
|
|
7955
8204
|
height: `${height}px`,
|
|
7956
8205
|
paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
|
|
7957
8206
|
},
|
|
7958
|
-
children:
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
8207
|
+
children: [
|
|
8208
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceNumber", children: "+" }),
|
|
8209
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8210
|
+
Input,
|
|
8211
|
+
{
|
|
8212
|
+
ref: inputRef,
|
|
8213
|
+
value: nameValue,
|
|
8214
|
+
onChange: (event) => setNameValue(event.target.value),
|
|
8215
|
+
onKeyDown: handleKeyDown,
|
|
8216
|
+
onBlur: handleBlur,
|
|
8217
|
+
placeholder: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430",
|
|
8218
|
+
className: "gantt-resourceTimeline-resourceInput"
|
|
8219
|
+
}
|
|
8220
|
+
)
|
|
8221
|
+
]
|
|
7970
8222
|
}
|
|
7971
8223
|
);
|
|
7972
8224
|
};
|
|
@@ -7982,6 +8234,7 @@ function ResourceTimelineChart({
|
|
|
7982
8234
|
customDays,
|
|
7983
8235
|
isWeekend: isWeekend3,
|
|
7984
8236
|
businessDays = true,
|
|
8237
|
+
resourceGrouping = false,
|
|
7985
8238
|
readonly,
|
|
7986
8239
|
disableResourceReassignment,
|
|
7987
8240
|
renderItem,
|
|
@@ -7990,6 +8243,7 @@ function ResourceTimelineChart({
|
|
|
7990
8243
|
onResourceItemMenuClick,
|
|
7991
8244
|
activeResourceItemId,
|
|
7992
8245
|
onResourceItemMove,
|
|
8246
|
+
onResourceChange,
|
|
7993
8247
|
onAddResource,
|
|
7994
8248
|
enableAddResource = true,
|
|
7995
8249
|
resourceMenuCommands = []
|
|
@@ -7998,9 +8252,12 @@ function ResourceTimelineChart({
|
|
|
7998
8252
|
const gridRef = (0, import_react14.useRef)(null);
|
|
7999
8253
|
const panStateRef = (0, import_react14.useRef)(null);
|
|
8000
8254
|
const conflictNavigationIndexRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
|
|
8255
|
+
const resourceNavigationIndexRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
|
|
8001
8256
|
const conflictHighlightTimeoutRef = (0, import_react14.useRef)(null);
|
|
8002
8257
|
const [isCreatingResource, setIsCreatingResource] = (0, import_react14.useState)(false);
|
|
8258
|
+
const [creatingResourceGroupType, setCreatingResourceGroupType] = (0, import_react14.useState)(null);
|
|
8003
8259
|
const [activeConflictItemId, setActiveConflictItemId] = (0, import_react14.useState)(null);
|
|
8260
|
+
const [resourceColumnHasRightShadow, setResourceColumnHasRightShadow] = (0, import_react14.useState)(false);
|
|
8004
8261
|
const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
|
|
8005
8262
|
const dateRange = (0, import_react14.useMemo)(() => getMultiMonthDays(validItems), [validItems]);
|
|
8006
8263
|
const monthStart = (0, import_react14.useMemo)(() => {
|
|
@@ -8012,15 +8269,112 @@ function ResourceTimelineChart({
|
|
|
8012
8269
|
[customDays, isWeekend3]
|
|
8013
8270
|
);
|
|
8014
8271
|
const gridWidth = (0, import_react14.useMemo)(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
|
|
8272
|
+
const effectiveRowHeaderWidth = Math.max(rowHeaderWidth, DEFAULT_ROW_HEADER_WIDTH);
|
|
8273
|
+
const orderedResources = (0, import_react14.useMemo)(
|
|
8274
|
+
() => resourceGrouping === "type" ? orderResourcesByType(resources) : resources,
|
|
8275
|
+
[resourceGrouping, resources]
|
|
8276
|
+
);
|
|
8277
|
+
const workedDaysByResourceId = (0, import_react14.useMemo)(() => {
|
|
8278
|
+
const map = /* @__PURE__ */ new Map();
|
|
8279
|
+
for (const resource of resources) {
|
|
8280
|
+
const workedDays = resource.items.reduce((sum, item) => {
|
|
8281
|
+
try {
|
|
8282
|
+
const startDate = parseUTCDate(item.startDate);
|
|
8283
|
+
const endDate = parseUTCDate(item.endDate);
|
|
8284
|
+
if (!isValidDate(startDate) || !isValidDate(endDate)) {
|
|
8285
|
+
return sum;
|
|
8286
|
+
}
|
|
8287
|
+
return sum + getDurationValue(startDate, endDate, businessDays, weekendPredicate);
|
|
8288
|
+
} catch {
|
|
8289
|
+
return sum;
|
|
8290
|
+
}
|
|
8291
|
+
}, 0);
|
|
8292
|
+
map.set(resource.id, workedDays);
|
|
8293
|
+
}
|
|
8294
|
+
return map;
|
|
8295
|
+
}, [businessDays, resources, weekendPredicate]);
|
|
8015
8296
|
const layout = (0, import_react14.useMemo)(
|
|
8016
|
-
() => layoutResourceTimelineItems(
|
|
8297
|
+
() => layoutResourceTimelineItems(orderedResources, {
|
|
8017
8298
|
monthStart,
|
|
8018
8299
|
dayWidth,
|
|
8019
8300
|
laneHeight,
|
|
8020
8301
|
rowGap: DEFAULT_RESOURCE_ROW_GAP
|
|
8021
8302
|
}),
|
|
8022
|
-
[
|
|
8303
|
+
[orderedResources, monthStart, dayWidth, laneHeight]
|
|
8023
8304
|
);
|
|
8305
|
+
const canAddResource = enableAddResource && Boolean(onAddResource);
|
|
8306
|
+
const resourceAddRowHeight = laneHeight + DEFAULT_RESOURCE_ROW_GAP;
|
|
8307
|
+
const displayLayout = (0, import_react14.useMemo)(() => {
|
|
8308
|
+
if (resourceGrouping !== "type") {
|
|
8309
|
+
return {
|
|
8310
|
+
rows: layout.rows,
|
|
8311
|
+
items: layout.items,
|
|
8312
|
+
groupHeaders: [],
|
|
8313
|
+
groupAddRows: [],
|
|
8314
|
+
totalHeight: layout.totalHeight
|
|
8315
|
+
};
|
|
8316
|
+
}
|
|
8317
|
+
const countsByType = /* @__PURE__ */ new Map();
|
|
8318
|
+
for (const row of layout.rows) {
|
|
8319
|
+
const type = getResourceType(row.resource);
|
|
8320
|
+
countsByType.set(type, (countsByType.get(type) ?? 0) + 1);
|
|
8321
|
+
}
|
|
8322
|
+
let offset = 0;
|
|
8323
|
+
let previousType = null;
|
|
8324
|
+
const rowTopOffsets = /* @__PURE__ */ new Map();
|
|
8325
|
+
const groupHeaders = [];
|
|
8326
|
+
const groupAddRows = [];
|
|
8327
|
+
const rows = layout.rows.map((row) => {
|
|
8328
|
+
const type = getResourceType(row.resource);
|
|
8329
|
+
if (type !== previousType) {
|
|
8330
|
+
if (previousType !== null && creatingResourceGroupType === previousType) {
|
|
8331
|
+
groupAddRows.push({
|
|
8332
|
+
key: previousType,
|
|
8333
|
+
top: row.resourceRowTop + offset,
|
|
8334
|
+
height: resourceAddRowHeight
|
|
8335
|
+
});
|
|
8336
|
+
offset += resourceAddRowHeight;
|
|
8337
|
+
}
|
|
8338
|
+
groupHeaders.push({
|
|
8339
|
+
key: type,
|
|
8340
|
+
label: type,
|
|
8341
|
+
count: countsByType.get(type) ?? 0,
|
|
8342
|
+
top: row.resourceRowTop + offset,
|
|
8343
|
+
height: DEFAULT_RESOURCE_GROUP_HEIGHT
|
|
8344
|
+
});
|
|
8345
|
+
offset += DEFAULT_RESOURCE_GROUP_HEIGHT;
|
|
8346
|
+
previousType = type;
|
|
8347
|
+
}
|
|
8348
|
+
rowTopOffsets.set(row.resourceId, offset);
|
|
8349
|
+
return {
|
|
8350
|
+
...row,
|
|
8351
|
+
resourceRowTop: row.resourceRowTop + offset
|
|
8352
|
+
};
|
|
8353
|
+
});
|
|
8354
|
+
if (previousType !== null && creatingResourceGroupType === previousType) {
|
|
8355
|
+
groupAddRows.push({
|
|
8356
|
+
key: previousType,
|
|
8357
|
+
top: layout.totalHeight + offset,
|
|
8358
|
+
height: resourceAddRowHeight
|
|
8359
|
+
});
|
|
8360
|
+
offset += resourceAddRowHeight;
|
|
8361
|
+
}
|
|
8362
|
+
const items = layout.items.map((item) => {
|
|
8363
|
+
const itemOffset = rowTopOffsets.get(item.resourceId) ?? 0;
|
|
8364
|
+
return {
|
|
8365
|
+
...item,
|
|
8366
|
+
resourceRowTop: item.resourceRowTop + itemOffset,
|
|
8367
|
+
top: item.top + itemOffset
|
|
8368
|
+
};
|
|
8369
|
+
});
|
|
8370
|
+
return {
|
|
8371
|
+
rows,
|
|
8372
|
+
items,
|
|
8373
|
+
groupHeaders,
|
|
8374
|
+
groupAddRows,
|
|
8375
|
+
totalHeight: layout.totalHeight + offset
|
|
8376
|
+
};
|
|
8377
|
+
}, [creatingResourceGroupType, layout, resourceAddRowHeight, resourceGrouping]);
|
|
8024
8378
|
const todayInRange = (0, import_react14.useMemo)(() => {
|
|
8025
8379
|
const now = /* @__PURE__ */ new Date();
|
|
8026
8380
|
const today = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
|
|
@@ -8028,16 +8382,16 @@ function ResourceTimelineChart({
|
|
|
8028
8382
|
}, [dateRange]);
|
|
8029
8383
|
const itemsByResourceId = (0, import_react14.useMemo)(() => {
|
|
8030
8384
|
const map = /* @__PURE__ */ new Map();
|
|
8031
|
-
for (const item of
|
|
8385
|
+
for (const item of displayLayout.items) {
|
|
8032
8386
|
const next = map.get(item.resourceId) ?? [];
|
|
8033
8387
|
next.push(item);
|
|
8034
8388
|
map.set(item.resourceId, next);
|
|
8035
8389
|
}
|
|
8036
8390
|
return map;
|
|
8037
|
-
}, [
|
|
8391
|
+
}, [displayLayout.items]);
|
|
8038
8392
|
const conflictItemsByResourceId = (0, import_react14.useMemo)(() => {
|
|
8039
8393
|
const map = /* @__PURE__ */ new Map();
|
|
8040
|
-
for (const item of
|
|
8394
|
+
for (const item of displayLayout.items) {
|
|
8041
8395
|
if (item.conflictRanges.length === 0) {
|
|
8042
8396
|
continue;
|
|
8043
8397
|
}
|
|
@@ -8051,19 +8405,28 @@ function ResourceTimelineChart({
|
|
|
8051
8405
|
);
|
|
8052
8406
|
}
|
|
8053
8407
|
return map;
|
|
8054
|
-
}, [
|
|
8055
|
-
const
|
|
8056
|
-
const
|
|
8057
|
-
const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
|
|
8408
|
+
}, [displayLayout.items]);
|
|
8409
|
+
const displayTotalHeight = displayLayout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
|
|
8410
|
+
const timelineHeaderHeight = headerHeight + 1;
|
|
8058
8411
|
const handleConfirmNewResource = (0, import_react14.useCallback)((name) => {
|
|
8059
8412
|
onAddResource?.({
|
|
8060
8413
|
id: crypto.randomUUID(),
|
|
8061
8414
|
name,
|
|
8415
|
+
type: creatingResourceGroupType ?? "\u0414\u0440\u0443\u0433\u043E\u0435",
|
|
8416
|
+
scope: "Project",
|
|
8062
8417
|
items: []
|
|
8063
8418
|
});
|
|
8064
8419
|
setIsCreatingResource(false);
|
|
8065
|
-
|
|
8066
|
-
|
|
8420
|
+
setCreatingResourceGroupType(null);
|
|
8421
|
+
}, [creatingResourceGroupType, onAddResource]);
|
|
8422
|
+
const handleStartAddResourceToGroup = (0, import_react14.useCallback)((type) => {
|
|
8423
|
+
setIsCreatingResource(false);
|
|
8424
|
+
setCreatingResourceGroupType(type);
|
|
8425
|
+
}, []);
|
|
8426
|
+
const handleCancelNewResource = (0, import_react14.useCallback)(() => {
|
|
8427
|
+
setIsCreatingResource(false);
|
|
8428
|
+
setCreatingResourceGroupType(null);
|
|
8429
|
+
}, []);
|
|
8067
8430
|
const handleConflictBadgeClick = (0, import_react14.useCallback)((resourceId) => {
|
|
8068
8431
|
const conflictItems = conflictItemsByResourceId.get(resourceId) ?? [];
|
|
8069
8432
|
if (conflictItems.length === 0) {
|
|
@@ -8089,10 +8452,31 @@ function ResourceTimelineChart({
|
|
|
8089
8452
|
conflictHighlightTimeoutRef.current = null;
|
|
8090
8453
|
}, 1600);
|
|
8091
8454
|
}, [conflictItemsByResourceId, dayWidth, laneHeight]);
|
|
8455
|
+
const handleResourceNameClick = (0, import_react14.useCallback)((resourceId) => {
|
|
8456
|
+
const resourceItems = itemsByResourceId.get(resourceId) ?? [];
|
|
8457
|
+
if (resourceItems.length === 0) {
|
|
8458
|
+
return;
|
|
8459
|
+
}
|
|
8460
|
+
const orderedItems = [...resourceItems].sort(
|
|
8461
|
+
(left, right) => left.left - right.left || left.top - right.top || left.itemId.localeCompare(right.itemId)
|
|
8462
|
+
);
|
|
8463
|
+
const currentIndex = resourceNavigationIndexRef.current.get(resourceId) ?? 0;
|
|
8464
|
+
const targetItem = orderedItems[currentIndex % orderedItems.length];
|
|
8465
|
+
resourceNavigationIndexRef.current.set(resourceId, (currentIndex + 1) % orderedItems.length);
|
|
8466
|
+
const container = scrollContainerRef.current;
|
|
8467
|
+
if (!container || !targetItem) {
|
|
8468
|
+
return;
|
|
8469
|
+
}
|
|
8470
|
+
container.scrollTo({
|
|
8471
|
+
left: Math.max(0, Math.round(targetItem.left - dayWidth * 2)),
|
|
8472
|
+
top: Math.max(0, Math.round(targetItem.top - laneHeight)),
|
|
8473
|
+
behavior: "smooth"
|
|
8474
|
+
});
|
|
8475
|
+
}, [dayWidth, itemsByResourceId, laneHeight]);
|
|
8092
8476
|
const { preview, startDrag } = useResourceItemDrag({
|
|
8093
8477
|
dayWidth,
|
|
8094
8478
|
monthStart,
|
|
8095
|
-
rows:
|
|
8479
|
+
rows: displayLayout.rows,
|
|
8096
8480
|
gridElementRef: gridRef,
|
|
8097
8481
|
readonly,
|
|
8098
8482
|
disableResourceReassignment,
|
|
@@ -8100,6 +8484,18 @@ function ResourceTimelineChart({
|
|
|
8100
8484
|
weekendPredicate,
|
|
8101
8485
|
onResourceItemMove
|
|
8102
8486
|
});
|
|
8487
|
+
(0, import_react14.useEffect)(() => {
|
|
8488
|
+
const container = scrollContainerRef.current;
|
|
8489
|
+
if (!container) return;
|
|
8490
|
+
const updateShadow = () => {
|
|
8491
|
+
setResourceColumnHasRightShadow(container.scrollLeft > 0);
|
|
8492
|
+
};
|
|
8493
|
+
updateShadow();
|
|
8494
|
+
container.addEventListener("scroll", updateShadow, { passive: true });
|
|
8495
|
+
return () => {
|
|
8496
|
+
container.removeEventListener("scroll", updateShadow);
|
|
8497
|
+
};
|
|
8498
|
+
}, []);
|
|
8103
8499
|
const handlePanStart = (0, import_react14.useCallback)((event) => {
|
|
8104
8500
|
if (event.button !== 0) {
|
|
8105
8501
|
return;
|
|
@@ -8157,6 +8553,16 @@ function ResourceTimelineChart({
|
|
|
8157
8553
|
window.removeEventListener("mouseup", handlePanEnd);
|
|
8158
8554
|
};
|
|
8159
8555
|
}, [allowVerticalPan]);
|
|
8556
|
+
(0, import_react14.useEffect)(() => {
|
|
8557
|
+
const nextNavigation = /* @__PURE__ */ new Map();
|
|
8558
|
+
for (const [resourceId, resourceItems] of itemsByResourceId.entries()) {
|
|
8559
|
+
if (resourceItems.length === 0) {
|
|
8560
|
+
continue;
|
|
8561
|
+
}
|
|
8562
|
+
nextNavigation.set(resourceId, (resourceNavigationIndexRef.current.get(resourceId) ?? 0) % resourceItems.length);
|
|
8563
|
+
}
|
|
8564
|
+
resourceNavigationIndexRef.current = nextNavigation;
|
|
8565
|
+
}, [itemsByResourceId]);
|
|
8160
8566
|
(0, import_react14.useEffect)(() => {
|
|
8161
8567
|
return () => {
|
|
8162
8568
|
if (conflictHighlightTimeoutRef.current) {
|
|
@@ -8180,25 +8586,92 @@ function ResourceTimelineChart({
|
|
|
8180
8586
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8181
8587
|
"div",
|
|
8182
8588
|
{
|
|
8183
|
-
className:
|
|
8184
|
-
style: { width: `${
|
|
8589
|
+
className: `gantt-resourceTimeline-resourceColumn${resourceColumnHasRightShadow ? " gantt-resourceTimeline-resourceColumnShadowed" : ""}`,
|
|
8590
|
+
style: { width: `${effectiveRowHeaderWidth}px`, minWidth: `${effectiveRowHeaderWidth}px` },
|
|
8185
8591
|
children: [
|
|
8186
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.
|
|
8592
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8187
8593
|
"div",
|
|
8188
8594
|
{
|
|
8189
8595
|
className: "gantt-resourceTimeline-corner",
|
|
8190
|
-
style: { height: `${
|
|
8596
|
+
style: { height: `${timelineHeaderHeight}px` },
|
|
8597
|
+
children: [
|
|
8598
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderNumber", children: "#" }),
|
|
8599
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderName", children: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435" }),
|
|
8600
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell", "aria-label": "\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C" }),
|
|
8601
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell", children: "\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F" }),
|
|
8602
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderActions", "aria-label": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F" })
|
|
8603
|
+
]
|
|
8191
8604
|
}
|
|
8192
8605
|
),
|
|
8193
|
-
|
|
8606
|
+
displayLayout.groupHeaders.length > 0 ? displayLayout.groupHeaders.map((group) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react14.default.Fragment, { children: [
|
|
8607
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8608
|
+
"div",
|
|
8609
|
+
{
|
|
8610
|
+
className: "gantt-resourceTimeline-resourceGroupHeader",
|
|
8611
|
+
style: { height: `${group.height}px` },
|
|
8612
|
+
children: [
|
|
8613
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceGroupTitle", children: group.label }),
|
|
8614
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceGroupCount", children: group.count }),
|
|
8615
|
+
canAddResource && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8616
|
+
"button",
|
|
8617
|
+
{
|
|
8618
|
+
type: "button",
|
|
8619
|
+
className: "gantt-resourceTimeline-resourceGroupAddButton",
|
|
8620
|
+
"aria-label": `\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441 \u0432 \u0433\u0440\u0443\u043F\u043F\u0443 ${group.label}`,
|
|
8621
|
+
title: `\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441 \u0432 \u0433\u0440\u0443\u043F\u043F\u0443 ${group.label}`,
|
|
8622
|
+
onClick: (event) => {
|
|
8623
|
+
event.stopPropagation();
|
|
8624
|
+
handleStartAddResourceToGroup(group.key);
|
|
8625
|
+
},
|
|
8626
|
+
children: "+"
|
|
8627
|
+
}
|
|
8628
|
+
)
|
|
8629
|
+
]
|
|
8630
|
+
}
|
|
8631
|
+
),
|
|
8632
|
+
displayLayout.rows.filter((row) => getResourceType(row.resource) === group.key).map((row) => {
|
|
8633
|
+
const rowIndex = displayLayout.rows.findIndex((candidate) => candidate.resourceId === row.resourceId);
|
|
8634
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8635
|
+
ResourceHeader,
|
|
8636
|
+
{
|
|
8637
|
+
resource: row.resource,
|
|
8638
|
+
resourceId: row.resourceId,
|
|
8639
|
+
rowIndex,
|
|
8640
|
+
conflictCount: row.conflictCount,
|
|
8641
|
+
workedDays: workedDaysByResourceId.get(row.resourceId) ?? 0,
|
|
8642
|
+
assignmentCount: row.resource.items.length,
|
|
8643
|
+
height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
|
|
8644
|
+
paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
|
|
8645
|
+
menuCommands: resourceMenuCommands,
|
|
8646
|
+
onResourceChange,
|
|
8647
|
+
onResourceNameClick: handleResourceNameClick,
|
|
8648
|
+
onConflictBadgeClick: handleConflictBadgeClick
|
|
8649
|
+
},
|
|
8650
|
+
row.resourceId
|
|
8651
|
+
);
|
|
8652
|
+
}),
|
|
8653
|
+
displayLayout.groupAddRows.some((row) => row.key === group.key) && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8654
|
+
NewResourceRow,
|
|
8655
|
+
{
|
|
8656
|
+
height: resourceAddRowHeight,
|
|
8657
|
+
onConfirm: handleConfirmNewResource,
|
|
8658
|
+
onCancel: handleCancelNewResource
|
|
8659
|
+
}
|
|
8660
|
+
)
|
|
8661
|
+
] }, group.key)) : displayLayout.rows.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8194
8662
|
ResourceHeader,
|
|
8195
8663
|
{
|
|
8196
8664
|
resource: row.resource,
|
|
8197
8665
|
resourceId: row.resourceId,
|
|
8666
|
+
rowIndex,
|
|
8198
8667
|
conflictCount: row.conflictCount,
|
|
8668
|
+
workedDays: workedDaysByResourceId.get(row.resourceId) ?? 0,
|
|
8669
|
+
assignmentCount: row.resource.items.length,
|
|
8199
8670
|
height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
|
|
8200
8671
|
paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
|
|
8201
8672
|
menuCommands: resourceMenuCommands,
|
|
8673
|
+
onResourceChange,
|
|
8674
|
+
onResourceNameClick: handleResourceNameClick,
|
|
8202
8675
|
onConflictBadgeClick: handleConflictBadgeClick
|
|
8203
8676
|
},
|
|
8204
8677
|
row.resourceId
|
|
@@ -8216,7 +8689,10 @@ function ResourceTimelineChart({
|
|
|
8216
8689
|
className: "gantt-resourceTimeline-addResourceButton",
|
|
8217
8690
|
type: "button",
|
|
8218
8691
|
style: { height: `${resourceAddRowHeight}px` },
|
|
8219
|
-
onClick: () =>
|
|
8692
|
+
onClick: () => {
|
|
8693
|
+
setCreatingResourceGroupType(null);
|
|
8694
|
+
setIsCreatingResource(true);
|
|
8695
|
+
},
|
|
8220
8696
|
children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441"
|
|
8221
8697
|
}
|
|
8222
8698
|
))
|
|
@@ -8229,16 +8705,23 @@ function ResourceTimelineChart({
|
|
|
8229
8705
|
className: "gantt-resourceTimeline-chartSurface",
|
|
8230
8706
|
style: { minWidth: `${gridWidth}px` },
|
|
8231
8707
|
children: [
|
|
8232
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8233
|
-
|
|
8708
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8709
|
+
"div",
|
|
8234
8710
|
{
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8711
|
+
className: "gantt-resourceTimeline-stickyHeader",
|
|
8712
|
+
style: { width: `${gridWidth}px`, height: `${timelineHeaderHeight}px` },
|
|
8713
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8714
|
+
TimeScaleHeader_default,
|
|
8715
|
+
{
|
|
8716
|
+
days: dateRange,
|
|
8717
|
+
dayWidth,
|
|
8718
|
+
headerHeight,
|
|
8719
|
+
viewMode,
|
|
8720
|
+
isCustomWeekend: weekendPredicate
|
|
8721
|
+
}
|
|
8722
|
+
)
|
|
8240
8723
|
}
|
|
8241
|
-
)
|
|
8724
|
+
),
|
|
8242
8725
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8243
8726
|
"div",
|
|
8244
8727
|
{
|
|
@@ -8257,7 +8740,19 @@ function ResourceTimelineChart({
|
|
|
8257
8740
|
}
|
|
8258
8741
|
),
|
|
8259
8742
|
todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
|
|
8260
|
-
|
|
8743
|
+
displayLayout.groupHeaders.map((group) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8744
|
+
"div",
|
|
8745
|
+
{
|
|
8746
|
+
className: "gantt-resourceTimeline-row gantt-resourceTimeline-groupRow",
|
|
8747
|
+
"data-resource-group": group.key,
|
|
8748
|
+
style: {
|
|
8749
|
+
top: `${group.top}px`,
|
|
8750
|
+
height: `${group.height}px`
|
|
8751
|
+
}
|
|
8752
|
+
},
|
|
8753
|
+
`group-row-${group.key}`
|
|
8754
|
+
)),
|
|
8755
|
+
displayLayout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8261
8756
|
"div",
|
|
8262
8757
|
{
|
|
8263
8758
|
className: "gantt-resourceTimeline-row",
|
|
@@ -8275,11 +8770,23 @@ function ResourceTimelineChart({
|
|
|
8275
8770
|
className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
|
|
8276
8771
|
"data-resource-add-row": "true",
|
|
8277
8772
|
style: {
|
|
8278
|
-
top: `${
|
|
8773
|
+
top: `${displayLayout.totalHeight}px`,
|
|
8279
8774
|
height: `${resourceAddRowHeight}px`
|
|
8280
8775
|
}
|
|
8281
8776
|
}
|
|
8282
8777
|
),
|
|
8778
|
+
displayLayout.groupAddRows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8779
|
+
"div",
|
|
8780
|
+
{
|
|
8781
|
+
className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
|
|
8782
|
+
"data-resource-group-add-row": row.key,
|
|
8783
|
+
style: {
|
|
8784
|
+
top: `${row.top}px`,
|
|
8785
|
+
height: `${row.height}px`
|
|
8786
|
+
}
|
|
8787
|
+
},
|
|
8788
|
+
`group-add-row-${row.key}`
|
|
8789
|
+
)),
|
|
8283
8790
|
Array.from(itemsByResourceId.values()).flatMap(
|
|
8284
8791
|
(resourceItems) => resourceItems.map((layoutItem) => {
|
|
8285
8792
|
const customClassName = getItemClassName?.(layoutItem.item);
|
|
@@ -8886,6 +9393,7 @@ function TaskGanttChartInner(props, ref) {
|
|
|
8886
9393
|
() => visibleTasks.length * rowHeight,
|
|
8887
9394
|
[visibleTasks.length, rowHeight]
|
|
8888
9395
|
);
|
|
9396
|
+
const timelineHeaderHeight = headerHeight + 1;
|
|
8889
9397
|
const monthStart = (0, import_react15.useMemo)(() => {
|
|
8890
9398
|
if (dateRange.length === 0) {
|
|
8891
9399
|
return new Date(Date.UTC((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth(), 1));
|
|
@@ -9368,16 +9876,23 @@ function TaskGanttChartInner(props, ref) {
|
|
|
9368
9876
|
className: showChart ? "gantt-chartSurface" : "gantt-chartSurface gantt-chart-hidden",
|
|
9369
9877
|
style: { minWidth: `${gridWidth}px`, flex: 1, display: showChart ? void 0 : "none" },
|
|
9370
9878
|
children: [
|
|
9371
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
9372
|
-
|
|
9879
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
9880
|
+
"div",
|
|
9373
9881
|
{
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9882
|
+
className: "gantt-stickyHeader",
|
|
9883
|
+
style: { width: `${gridWidth}px`, height: `${timelineHeaderHeight}px` },
|
|
9884
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
9885
|
+
TimeScaleHeader_default,
|
|
9886
|
+
{
|
|
9887
|
+
days: dateRange,
|
|
9888
|
+
dayWidth,
|
|
9889
|
+
headerHeight,
|
|
9890
|
+
viewMode,
|
|
9891
|
+
isCustomWeekend
|
|
9892
|
+
}
|
|
9893
|
+
)
|
|
9379
9894
|
}
|
|
9380
|
-
)
|
|
9895
|
+
),
|
|
9381
9896
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
9382
9897
|
"div",
|
|
9383
9898
|
{
|