gantt-lib 0.82.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-BPStPBtF.d.mts → index-DGOZyXZt.d.mts} +1 -0
- package/dist/{index-BPStPBtF.d.ts → index-DGOZyXZt.d.ts} +1 -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 +370 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +370 -73
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +141 -24
- 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());
|
|
@@ -7748,6 +7749,7 @@ var DEFAULT_HEADER_HEIGHT = 40;
|
|
|
7748
7749
|
var DEFAULT_LANE_HEIGHT = 40;
|
|
7749
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;
|
|
@@ -7846,6 +7848,30 @@ var RESOURCE_TYPE_CLASS_NAMES = {
|
|
|
7846
7848
|
\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B: "Materials",
|
|
7847
7849
|
\u0414\u0440\u0443\u0433\u043E\u0435: "Other"
|
|
7848
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
|
+
};
|
|
7849
7875
|
var ResourceTypeIcon = ({ type }) => {
|
|
7850
7876
|
if (type === "\u041B\u044E\u0434\u0438") {
|
|
7851
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: [
|
|
@@ -7886,12 +7912,16 @@ var ResourceHeader = ({
|
|
|
7886
7912
|
paddingBottom,
|
|
7887
7913
|
menuCommands,
|
|
7888
7914
|
onResourceChange,
|
|
7915
|
+
onResourceNameClick,
|
|
7889
7916
|
onConflictBadgeClick
|
|
7890
7917
|
}) => {
|
|
7891
7918
|
const [menuOpen, setMenuOpen] = (0, import_react14.useState)(false);
|
|
7892
7919
|
const [typeMenuOpen, setTypeMenuOpen] = (0, import_react14.useState)(false);
|
|
7893
7920
|
const [scopeMenuOpen, setScopeMenuOpen] = (0, import_react14.useState)(false);
|
|
7894
|
-
const [
|
|
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);
|
|
7895
7925
|
const visibleCommands = (0, import_react14.useMemo)(
|
|
7896
7926
|
() => menuCommands.filter((command) => command.isVisible?.(resource) ?? true),
|
|
7897
7927
|
[menuCommands, resource]
|
|
@@ -7900,31 +7930,20 @@ var ResourceHeader = ({
|
|
|
7900
7930
|
const type = resource.type ?? "\u0414\u0440\u0443\u0433\u043E\u0435";
|
|
7901
7931
|
const scope = resource.scope ?? "Project";
|
|
7902
7932
|
const scopeLabel = RESOURCE_SCOPE_LABELS[scope] ?? scope;
|
|
7903
|
-
(0, import_react14.useEffect)(() => {
|
|
7904
|
-
setDraftName(resource.name);
|
|
7905
|
-
}, [resource.name]);
|
|
7906
7933
|
const applyResourcePatch = (0, import_react14.useCallback)((patch) => {
|
|
7907
7934
|
onResourceChange?.({ ...resource, ...patch });
|
|
7908
7935
|
}, [onResourceChange, resource]);
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
setDraftName(resource.name);
|
|
7913
|
-
return;
|
|
7914
|
-
}
|
|
7915
|
-
if (nextName !== resource.name) {
|
|
7916
|
-
applyResourcePatch({ name: nextName });
|
|
7936
|
+
(0, import_react14.useEffect)(() => {
|
|
7937
|
+
if (!editingName) {
|
|
7938
|
+
setNameValue(resource.name);
|
|
7917
7939
|
}
|
|
7918
|
-
}, [
|
|
7919
|
-
|
|
7920
|
-
if (
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
} else if (event.key === "Escape") {
|
|
7924
|
-
setDraftName(resource.name);
|
|
7925
|
-
event.currentTarget.blur();
|
|
7940
|
+
}, [editingName, resource.name]);
|
|
7941
|
+
(0, import_react14.useEffect)(() => {
|
|
7942
|
+
if (editingName && nameInputRef.current) {
|
|
7943
|
+
nameInputRef.current.focus();
|
|
7944
|
+
nameInputRef.current.select();
|
|
7926
7945
|
}
|
|
7927
|
-
}, [
|
|
7946
|
+
}, [editingName]);
|
|
7928
7947
|
const handleCommandClick = (command, event) => {
|
|
7929
7948
|
event.stopPropagation();
|
|
7930
7949
|
if (command.closeOnSelect !== false) {
|
|
@@ -7932,6 +7951,42 @@ var ResourceHeader = ({
|
|
|
7932
7951
|
}
|
|
7933
7952
|
command.onSelect(resource);
|
|
7934
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]);
|
|
7935
7990
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7936
7991
|
"div",
|
|
7937
7992
|
{
|
|
@@ -7978,18 +8033,31 @@ var ResourceHeader = ({
|
|
|
7978
8033
|
option
|
|
7979
8034
|
)) })
|
|
7980
8035
|
] }),
|
|
7981
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
7982
|
-
|
|
8036
|
+
editingName ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8037
|
+
Input,
|
|
7983
8038
|
{
|
|
7984
|
-
|
|
7985
|
-
value:
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
rows: 2,
|
|
7989
|
-
onChange: (event) => setDraftName(event.target.value),
|
|
7990
|
-
onBlur: handleNameCommit,
|
|
8039
|
+
ref: nameInputRef,
|
|
8040
|
+
value: nameValue,
|
|
8041
|
+
onChange: (event) => setNameValue(event.target.value),
|
|
8042
|
+
onBlur: handleNameSave,
|
|
7991
8043
|
onKeyDown: handleNameKeyDown,
|
|
7992
|
-
onClick: (event) => event.stopPropagation()
|
|
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
|
|
7993
8061
|
}
|
|
7994
8062
|
)
|
|
7995
8063
|
] }),
|
|
@@ -8033,15 +8101,15 @@ var ResourceHeader = ({
|
|
|
8033
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.`,
|
|
8034
8102
|
children: [
|
|
8035
8103
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceWorkedDays", children: [
|
|
8036
|
-
workedDays,
|
|
8037
|
-
" \u0434\u043D."
|
|
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." : "" })
|
|
8038
8106
|
] }),
|
|
8039
8107
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "gantt-resourceTimeline-resourceAssignmentCount", children: [
|
|
8040
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.
|
|
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: [
|
|
8041
8110
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("rect", { width: "15", height: "5", x: "4", y: "5", rx: "2" }),
|
|
8042
8111
|
/* @__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 })
|
|
8112
|
+
] })
|
|
8045
8113
|
] })
|
|
8046
8114
|
]
|
|
8047
8115
|
}
|
|
@@ -8166,6 +8234,7 @@ function ResourceTimelineChart({
|
|
|
8166
8234
|
customDays,
|
|
8167
8235
|
isWeekend: isWeekend3,
|
|
8168
8236
|
businessDays = true,
|
|
8237
|
+
resourceGrouping = false,
|
|
8169
8238
|
readonly,
|
|
8170
8239
|
disableResourceReassignment,
|
|
8171
8240
|
renderItem,
|
|
@@ -8183,9 +8252,12 @@ function ResourceTimelineChart({
|
|
|
8183
8252
|
const gridRef = (0, import_react14.useRef)(null);
|
|
8184
8253
|
const panStateRef = (0, import_react14.useRef)(null);
|
|
8185
8254
|
const conflictNavigationIndexRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
|
|
8255
|
+
const resourceNavigationIndexRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
|
|
8186
8256
|
const conflictHighlightTimeoutRef = (0, import_react14.useRef)(null);
|
|
8187
8257
|
const [isCreatingResource, setIsCreatingResource] = (0, import_react14.useState)(false);
|
|
8258
|
+
const [creatingResourceGroupType, setCreatingResourceGroupType] = (0, import_react14.useState)(null);
|
|
8188
8259
|
const [activeConflictItemId, setActiveConflictItemId] = (0, import_react14.useState)(null);
|
|
8260
|
+
const [resourceColumnHasRightShadow, setResourceColumnHasRightShadow] = (0, import_react14.useState)(false);
|
|
8189
8261
|
const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
|
|
8190
8262
|
const dateRange = (0, import_react14.useMemo)(() => getMultiMonthDays(validItems), [validItems]);
|
|
8191
8263
|
const monthStart = (0, import_react14.useMemo)(() => {
|
|
@@ -8198,6 +8270,10 @@ function ResourceTimelineChart({
|
|
|
8198
8270
|
);
|
|
8199
8271
|
const gridWidth = (0, import_react14.useMemo)(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
|
|
8200
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
|
+
);
|
|
8201
8277
|
const workedDaysByResourceId = (0, import_react14.useMemo)(() => {
|
|
8202
8278
|
const map = /* @__PURE__ */ new Map();
|
|
8203
8279
|
for (const resource of resources) {
|
|
@@ -8218,14 +8294,87 @@ function ResourceTimelineChart({
|
|
|
8218
8294
|
return map;
|
|
8219
8295
|
}, [businessDays, resources, weekendPredicate]);
|
|
8220
8296
|
const layout = (0, import_react14.useMemo)(
|
|
8221
|
-
() => layoutResourceTimelineItems(
|
|
8297
|
+
() => layoutResourceTimelineItems(orderedResources, {
|
|
8222
8298
|
monthStart,
|
|
8223
8299
|
dayWidth,
|
|
8224
8300
|
laneHeight,
|
|
8225
8301
|
rowGap: DEFAULT_RESOURCE_ROW_GAP
|
|
8226
8302
|
}),
|
|
8227
|
-
[
|
|
8303
|
+
[orderedResources, monthStart, dayWidth, laneHeight]
|
|
8228
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]);
|
|
8229
8378
|
const todayInRange = (0, import_react14.useMemo)(() => {
|
|
8230
8379
|
const now = /* @__PURE__ */ new Date();
|
|
8231
8380
|
const today = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
|
|
@@ -8233,16 +8382,16 @@ function ResourceTimelineChart({
|
|
|
8233
8382
|
}, [dateRange]);
|
|
8234
8383
|
const itemsByResourceId = (0, import_react14.useMemo)(() => {
|
|
8235
8384
|
const map = /* @__PURE__ */ new Map();
|
|
8236
|
-
for (const item of
|
|
8385
|
+
for (const item of displayLayout.items) {
|
|
8237
8386
|
const next = map.get(item.resourceId) ?? [];
|
|
8238
8387
|
next.push(item);
|
|
8239
8388
|
map.set(item.resourceId, next);
|
|
8240
8389
|
}
|
|
8241
8390
|
return map;
|
|
8242
|
-
}, [
|
|
8391
|
+
}, [displayLayout.items]);
|
|
8243
8392
|
const conflictItemsByResourceId = (0, import_react14.useMemo)(() => {
|
|
8244
8393
|
const map = /* @__PURE__ */ new Map();
|
|
8245
|
-
for (const item of
|
|
8394
|
+
for (const item of displayLayout.items) {
|
|
8246
8395
|
if (item.conflictRanges.length === 0) {
|
|
8247
8396
|
continue;
|
|
8248
8397
|
}
|
|
@@ -8256,21 +8405,28 @@ function ResourceTimelineChart({
|
|
|
8256
8405
|
);
|
|
8257
8406
|
}
|
|
8258
8407
|
return map;
|
|
8259
|
-
}, [
|
|
8260
|
-
const
|
|
8261
|
-
const
|
|
8262
|
-
const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
|
|
8408
|
+
}, [displayLayout.items]);
|
|
8409
|
+
const displayTotalHeight = displayLayout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
|
|
8410
|
+
const timelineHeaderHeight = headerHeight + 1;
|
|
8263
8411
|
const handleConfirmNewResource = (0, import_react14.useCallback)((name) => {
|
|
8264
8412
|
onAddResource?.({
|
|
8265
8413
|
id: crypto.randomUUID(),
|
|
8266
8414
|
name,
|
|
8267
|
-
type: "\u0414\u0440\u0443\u0433\u043E\u0435",
|
|
8415
|
+
type: creatingResourceGroupType ?? "\u0414\u0440\u0443\u0433\u043E\u0435",
|
|
8268
8416
|
scope: "Project",
|
|
8269
8417
|
items: []
|
|
8270
8418
|
});
|
|
8271
8419
|
setIsCreatingResource(false);
|
|
8272
|
-
|
|
8273
|
-
|
|
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
|
+
}, []);
|
|
8274
8430
|
const handleConflictBadgeClick = (0, import_react14.useCallback)((resourceId) => {
|
|
8275
8431
|
const conflictItems = conflictItemsByResourceId.get(resourceId) ?? [];
|
|
8276
8432
|
if (conflictItems.length === 0) {
|
|
@@ -8296,10 +8452,31 @@ function ResourceTimelineChart({
|
|
|
8296
8452
|
conflictHighlightTimeoutRef.current = null;
|
|
8297
8453
|
}, 1600);
|
|
8298
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]);
|
|
8299
8476
|
const { preview, startDrag } = useResourceItemDrag({
|
|
8300
8477
|
dayWidth,
|
|
8301
8478
|
monthStart,
|
|
8302
|
-
rows:
|
|
8479
|
+
rows: displayLayout.rows,
|
|
8303
8480
|
gridElementRef: gridRef,
|
|
8304
8481
|
readonly,
|
|
8305
8482
|
disableResourceReassignment,
|
|
@@ -8307,6 +8484,18 @@ function ResourceTimelineChart({
|
|
|
8307
8484
|
weekendPredicate,
|
|
8308
8485
|
onResourceItemMove
|
|
8309
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
|
+
}, []);
|
|
8310
8499
|
const handlePanStart = (0, import_react14.useCallback)((event) => {
|
|
8311
8500
|
if (event.button !== 0) {
|
|
8312
8501
|
return;
|
|
@@ -8364,6 +8553,16 @@ function ResourceTimelineChart({
|
|
|
8364
8553
|
window.removeEventListener("mouseup", handlePanEnd);
|
|
8365
8554
|
};
|
|
8366
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]);
|
|
8367
8566
|
(0, import_react14.useEffect)(() => {
|
|
8368
8567
|
return () => {
|
|
8369
8568
|
if (conflictHighlightTimeoutRef.current) {
|
|
@@ -8387,14 +8586,14 @@ function ResourceTimelineChart({
|
|
|
8387
8586
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8388
8587
|
"div",
|
|
8389
8588
|
{
|
|
8390
|
-
className:
|
|
8589
|
+
className: `gantt-resourceTimeline-resourceColumn${resourceColumnHasRightShadow ? " gantt-resourceTimeline-resourceColumnShadowed" : ""}`,
|
|
8391
8590
|
style: { width: `${effectiveRowHeaderWidth}px`, minWidth: `${effectiveRowHeaderWidth}px` },
|
|
8392
8591
|
children: [
|
|
8393
8592
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8394
8593
|
"div",
|
|
8395
8594
|
{
|
|
8396
8595
|
className: "gantt-resourceTimeline-corner",
|
|
8397
|
-
style: { height: `${
|
|
8596
|
+
style: { height: `${timelineHeaderHeight}px` },
|
|
8398
8597
|
children: [
|
|
8399
8598
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderNumber", children: "#" }),
|
|
8400
8599
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderName", children: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435" }),
|
|
@@ -8404,7 +8603,62 @@ function ResourceTimelineChart({
|
|
|
8404
8603
|
]
|
|
8405
8604
|
}
|
|
8406
8605
|
),
|
|
8407
|
-
|
|
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)(
|
|
8408
8662
|
ResourceHeader,
|
|
8409
8663
|
{
|
|
8410
8664
|
resource: row.resource,
|
|
@@ -8417,6 +8671,7 @@ function ResourceTimelineChart({
|
|
|
8417
8671
|
paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
|
|
8418
8672
|
menuCommands: resourceMenuCommands,
|
|
8419
8673
|
onResourceChange,
|
|
8674
|
+
onResourceNameClick: handleResourceNameClick,
|
|
8420
8675
|
onConflictBadgeClick: handleConflictBadgeClick
|
|
8421
8676
|
},
|
|
8422
8677
|
row.resourceId
|
|
@@ -8434,7 +8689,10 @@ function ResourceTimelineChart({
|
|
|
8434
8689
|
className: "gantt-resourceTimeline-addResourceButton",
|
|
8435
8690
|
type: "button",
|
|
8436
8691
|
style: { height: `${resourceAddRowHeight}px` },
|
|
8437
|
-
onClick: () =>
|
|
8692
|
+
onClick: () => {
|
|
8693
|
+
setCreatingResourceGroupType(null);
|
|
8694
|
+
setIsCreatingResource(true);
|
|
8695
|
+
},
|
|
8438
8696
|
children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441"
|
|
8439
8697
|
}
|
|
8440
8698
|
))
|
|
@@ -8447,16 +8705,23 @@ function ResourceTimelineChart({
|
|
|
8447
8705
|
className: "gantt-resourceTimeline-chartSurface",
|
|
8448
8706
|
style: { minWidth: `${gridWidth}px` },
|
|
8449
8707
|
children: [
|
|
8450
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8451
|
-
|
|
8708
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
8709
|
+
"div",
|
|
8452
8710
|
{
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
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
|
+
)
|
|
8458
8723
|
}
|
|
8459
|
-
)
|
|
8724
|
+
),
|
|
8460
8725
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
8461
8726
|
"div",
|
|
8462
8727
|
{
|
|
@@ -8475,7 +8740,19 @@ function ResourceTimelineChart({
|
|
|
8475
8740
|
}
|
|
8476
8741
|
),
|
|
8477
8742
|
todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
|
|
8478
|
-
|
|
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)(
|
|
8479
8756
|
"div",
|
|
8480
8757
|
{
|
|
8481
8758
|
className: "gantt-resourceTimeline-row",
|
|
@@ -8493,11 +8770,23 @@ function ResourceTimelineChart({
|
|
|
8493
8770
|
className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
|
|
8494
8771
|
"data-resource-add-row": "true",
|
|
8495
8772
|
style: {
|
|
8496
|
-
top: `${
|
|
8773
|
+
top: `${displayLayout.totalHeight}px`,
|
|
8497
8774
|
height: `${resourceAddRowHeight}px`
|
|
8498
8775
|
}
|
|
8499
8776
|
}
|
|
8500
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
|
+
)),
|
|
8501
8790
|
Array.from(itemsByResourceId.values()).flatMap(
|
|
8502
8791
|
(resourceItems) => resourceItems.map((layoutItem) => {
|
|
8503
8792
|
const customClassName = getItemClassName?.(layoutItem.item);
|
|
@@ -9104,6 +9393,7 @@ function TaskGanttChartInner(props, ref) {
|
|
|
9104
9393
|
() => visibleTasks.length * rowHeight,
|
|
9105
9394
|
[visibleTasks.length, rowHeight]
|
|
9106
9395
|
);
|
|
9396
|
+
const timelineHeaderHeight = headerHeight + 1;
|
|
9107
9397
|
const monthStart = (0, import_react15.useMemo)(() => {
|
|
9108
9398
|
if (dateRange.length === 0) {
|
|
9109
9399
|
return new Date(Date.UTC((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth(), 1));
|
|
@@ -9586,16 +9876,23 @@ function TaskGanttChartInner(props, ref) {
|
|
|
9586
9876
|
className: showChart ? "gantt-chartSurface" : "gantt-chartSurface gantt-chart-hidden",
|
|
9587
9877
|
style: { minWidth: `${gridWidth}px`, flex: 1, display: showChart ? void 0 : "none" },
|
|
9588
9878
|
children: [
|
|
9589
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
9590
|
-
|
|
9879
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
9880
|
+
"div",
|
|
9591
9881
|
{
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
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
|
+
)
|
|
9597
9894
|
}
|
|
9598
|
-
)
|
|
9895
|
+
),
|
|
9599
9896
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
9600
9897
|
"div",
|
|
9601
9898
|
{
|