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.mjs
CHANGED
|
@@ -7023,6 +7023,7 @@ var TaskList = ({
|
|
|
7023
7023
|
[resolvedColumns]
|
|
7024
7024
|
);
|
|
7025
7025
|
const effectiveTaskListWidth = Math.max(taskListWidth, MIN_TASK_LIST_WIDTH, resolvedColumnWidthTotal);
|
|
7026
|
+
const tableHeaderHeight = headerHeight + 1;
|
|
7026
7027
|
return /* @__PURE__ */ jsx14(
|
|
7027
7028
|
"div",
|
|
7028
7029
|
{
|
|
@@ -7030,7 +7031,7 @@ var TaskList = ({
|
|
|
7030
7031
|
className: `gantt-tl-overlay${show ? "" : " gantt-tl-hidden"}${hasRightShadow ? " gantt-tl-overlay-shadowed" : ""}`,
|
|
7031
7032
|
style: { "--tasklist-width": `${effectiveTaskListWidth}px` },
|
|
7032
7033
|
children: /* @__PURE__ */ jsxs11("div", { className: "gantt-tl-table", children: [
|
|
7033
|
-
/* @__PURE__ */ jsx14("div", { className: "gantt-tl-header", style: { height: `${
|
|
7034
|
+
/* @__PURE__ */ jsx14("div", { className: "gantt-tl-header", style: { height: `${tableHeaderHeight}px` }, children: resolvedColumns.map((col) => {
|
|
7034
7035
|
if (col.id === "dependencies") {
|
|
7035
7036
|
return /* @__PURE__ */ jsxs11(
|
|
7036
7037
|
"div",
|
|
@@ -7214,7 +7215,7 @@ var TaskList = ({
|
|
|
7214
7215
|
};
|
|
7215
7216
|
|
|
7216
7217
|
// src/components/ResourceTimelineChart/ResourceTimelineChart.tsx
|
|
7217
|
-
import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo9, useRef as useRef8, useState as useState8 } from "react";
|
|
7218
|
+
import React12, { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo9, useRef as useRef8, useState as useState8 } from "react";
|
|
7218
7219
|
|
|
7219
7220
|
// src/utils/resourceTimelineLayout.ts
|
|
7220
7221
|
var isInvalidDate = (date) => Number.isNaN(date.getTime());
|
|
@@ -7634,6 +7635,7 @@ var DEFAULT_HEADER_HEIGHT = 40;
|
|
|
7634
7635
|
var DEFAULT_LANE_HEIGHT = 40;
|
|
7635
7636
|
var DEFAULT_ROW_HEADER_WIDTH = 420;
|
|
7636
7637
|
var DEFAULT_RESOURCE_ROW_GAP = 8;
|
|
7638
|
+
var DEFAULT_RESOURCE_GROUP_HEIGHT = 28;
|
|
7637
7639
|
var ITEM_OUTER_VERTICAL_INSET = 2;
|
|
7638
7640
|
var ITEM_INNER_VERTICAL_INSET = 1;
|
|
7639
7641
|
var ITEM_START_HORIZONTAL_INSET = 2;
|
|
@@ -7732,6 +7734,30 @@ var RESOURCE_TYPE_CLASS_NAMES = {
|
|
|
7732
7734
|
\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B: "Materials",
|
|
7733
7735
|
\u0414\u0440\u0443\u0433\u043E\u0435: "Other"
|
|
7734
7736
|
};
|
|
7737
|
+
var RESOURCE_TYPE_ORDER = {
|
|
7738
|
+
\u041B\u044E\u0434\u0438: 0,
|
|
7739
|
+
\u041E\u0431\u043E\u0440\u0443\u0434\u043E\u0432\u0430\u043D\u0438\u0435: 1,
|
|
7740
|
+
\u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u044B: 2,
|
|
7741
|
+
\u0414\u0440\u0443\u0433\u043E\u0435: 3
|
|
7742
|
+
};
|
|
7743
|
+
var RESOURCE_SCOPE_ORDER = {
|
|
7744
|
+
Shared: 0,
|
|
7745
|
+
Project: 1
|
|
7746
|
+
};
|
|
7747
|
+
var getResourceType = (resource) => resource.type ?? "\u0414\u0440\u0443\u0433\u043E\u0435";
|
|
7748
|
+
var getResourceScopeOrder = (resource) => RESOURCE_SCOPE_ORDER[resource.scope ?? "Project"] ?? 99;
|
|
7749
|
+
var orderResourcesByType = (resources) => {
|
|
7750
|
+
return resources.map((resource, index) => ({ resource, index })).sort((left, right) => {
|
|
7751
|
+
const leftType = getResourceType(left.resource);
|
|
7752
|
+
const rightType = getResourceType(right.resource);
|
|
7753
|
+
const typeDiff = (RESOURCE_TYPE_ORDER[leftType] ?? 99) - (RESOURCE_TYPE_ORDER[rightType] ?? 99);
|
|
7754
|
+
if (typeDiff !== 0) {
|
|
7755
|
+
return typeDiff;
|
|
7756
|
+
}
|
|
7757
|
+
const scopeDiff = getResourceScopeOrder(left.resource) - getResourceScopeOrder(right.resource);
|
|
7758
|
+
return scopeDiff !== 0 ? scopeDiff : left.index - right.index;
|
|
7759
|
+
}).map(({ resource }) => resource);
|
|
7760
|
+
};
|
|
7735
7761
|
var ResourceTypeIcon = ({ type }) => {
|
|
7736
7762
|
if (type === "\u041B\u044E\u0434\u0438") {
|
|
7737
7763
|
return /* @__PURE__ */ jsxs12("svg", { className: "gantt-resourceTimeline-resourceTypeIcon gantt-resourceTimeline-resourceTypeIconPeople", width: "16", height: "16", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
@@ -7772,12 +7798,16 @@ var ResourceHeader = ({
|
|
|
7772
7798
|
paddingBottom,
|
|
7773
7799
|
menuCommands,
|
|
7774
7800
|
onResourceChange,
|
|
7801
|
+
onResourceNameClick,
|
|
7775
7802
|
onConflictBadgeClick
|
|
7776
7803
|
}) => {
|
|
7777
7804
|
const [menuOpen, setMenuOpen] = useState8(false);
|
|
7778
7805
|
const [typeMenuOpen, setTypeMenuOpen] = useState8(false);
|
|
7779
7806
|
const [scopeMenuOpen, setScopeMenuOpen] = useState8(false);
|
|
7780
|
-
const [
|
|
7807
|
+
const [editingName, setEditingName] = useState8(false);
|
|
7808
|
+
const [nameValue, setNameValue] = useState8(resource.name);
|
|
7809
|
+
const nameInputRef = useRef8(null);
|
|
7810
|
+
const nameConfirmedRef = useRef8(false);
|
|
7781
7811
|
const visibleCommands = useMemo9(
|
|
7782
7812
|
() => menuCommands.filter((command) => command.isVisible?.(resource) ?? true),
|
|
7783
7813
|
[menuCommands, resource]
|
|
@@ -7786,31 +7816,20 @@ var ResourceHeader = ({
|
|
|
7786
7816
|
const type = resource.type ?? "\u0414\u0440\u0443\u0433\u043E\u0435";
|
|
7787
7817
|
const scope = resource.scope ?? "Project";
|
|
7788
7818
|
const scopeLabel = RESOURCE_SCOPE_LABELS[scope] ?? scope;
|
|
7789
|
-
useEffect8(() => {
|
|
7790
|
-
setDraftName(resource.name);
|
|
7791
|
-
}, [resource.name]);
|
|
7792
7819
|
const applyResourcePatch = useCallback7((patch) => {
|
|
7793
7820
|
onResourceChange?.({ ...resource, ...patch });
|
|
7794
7821
|
}, [onResourceChange, resource]);
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
setDraftName(resource.name);
|
|
7799
|
-
return;
|
|
7800
|
-
}
|
|
7801
|
-
if (nextName !== resource.name) {
|
|
7802
|
-
applyResourcePatch({ name: nextName });
|
|
7822
|
+
useEffect8(() => {
|
|
7823
|
+
if (!editingName) {
|
|
7824
|
+
setNameValue(resource.name);
|
|
7803
7825
|
}
|
|
7804
|
-
}, [
|
|
7805
|
-
|
|
7806
|
-
if (
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
} else if (event.key === "Escape") {
|
|
7810
|
-
setDraftName(resource.name);
|
|
7811
|
-
event.currentTarget.blur();
|
|
7826
|
+
}, [editingName, resource.name]);
|
|
7827
|
+
useEffect8(() => {
|
|
7828
|
+
if (editingName && nameInputRef.current) {
|
|
7829
|
+
nameInputRef.current.focus();
|
|
7830
|
+
nameInputRef.current.select();
|
|
7812
7831
|
}
|
|
7813
|
-
}, [
|
|
7832
|
+
}, [editingName]);
|
|
7814
7833
|
const handleCommandClick = (command, event) => {
|
|
7815
7834
|
event.stopPropagation();
|
|
7816
7835
|
if (command.closeOnSelect !== false) {
|
|
@@ -7818,6 +7837,42 @@ var ResourceHeader = ({
|
|
|
7818
7837
|
}
|
|
7819
7838
|
command.onSelect(resource);
|
|
7820
7839
|
};
|
|
7840
|
+
const handleNameDoubleClick = useCallback7((event) => {
|
|
7841
|
+
if (!onResourceChange) {
|
|
7842
|
+
return;
|
|
7843
|
+
}
|
|
7844
|
+
event.stopPropagation();
|
|
7845
|
+
nameConfirmedRef.current = false;
|
|
7846
|
+
setNameValue(resource.name);
|
|
7847
|
+
setEditingName(true);
|
|
7848
|
+
}, [onResourceChange, resource.name]);
|
|
7849
|
+
const handleNameSave = useCallback7(() => {
|
|
7850
|
+
if (nameConfirmedRef.current) {
|
|
7851
|
+
nameConfirmedRef.current = false;
|
|
7852
|
+
return;
|
|
7853
|
+
}
|
|
7854
|
+
const nextName = nameValue.trim();
|
|
7855
|
+
if (nextName && nextName !== resource.name) {
|
|
7856
|
+
applyResourcePatch({ name: nextName });
|
|
7857
|
+
}
|
|
7858
|
+
setEditingName(false);
|
|
7859
|
+
}, [applyResourcePatch, nameValue, resource.name]);
|
|
7860
|
+
const handleNameCancel = useCallback7(() => {
|
|
7861
|
+
setNameValue(resource.name);
|
|
7862
|
+
setEditingName(false);
|
|
7863
|
+
}, [resource.name]);
|
|
7864
|
+
const handleNameKeyDown = useCallback7((event) => {
|
|
7865
|
+
if (event.key === "Enter") {
|
|
7866
|
+
nameConfirmedRef.current = true;
|
|
7867
|
+
const nextName = nameValue.trim();
|
|
7868
|
+
if (nextName && nextName !== resource.name) {
|
|
7869
|
+
applyResourcePatch({ name: nextName });
|
|
7870
|
+
}
|
|
7871
|
+
setEditingName(false);
|
|
7872
|
+
} else if (event.key === "Escape") {
|
|
7873
|
+
handleNameCancel();
|
|
7874
|
+
}
|
|
7875
|
+
}, [applyResourcePatch, handleNameCancel, nameValue, resource.name]);
|
|
7821
7876
|
return /* @__PURE__ */ jsxs12(
|
|
7822
7877
|
"div",
|
|
7823
7878
|
{
|
|
@@ -7864,18 +7919,31 @@ var ResourceHeader = ({
|
|
|
7864
7919
|
option
|
|
7865
7920
|
)) })
|
|
7866
7921
|
] }),
|
|
7867
|
-
/* @__PURE__ */ jsx15(
|
|
7868
|
-
|
|
7922
|
+
editingName ? /* @__PURE__ */ jsx15(
|
|
7923
|
+
Input,
|
|
7869
7924
|
{
|
|
7870
|
-
|
|
7871
|
-
value:
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
rows: 2,
|
|
7875
|
-
onChange: (event) => setDraftName(event.target.value),
|
|
7876
|
-
onBlur: handleNameCommit,
|
|
7925
|
+
ref: nameInputRef,
|
|
7926
|
+
value: nameValue,
|
|
7927
|
+
onChange: (event) => setNameValue(event.target.value),
|
|
7928
|
+
onBlur: handleNameSave,
|
|
7877
7929
|
onKeyDown: handleNameKeyDown,
|
|
7878
|
-
onClick: (event) => event.stopPropagation()
|
|
7930
|
+
onClick: (event) => event.stopPropagation(),
|
|
7931
|
+
"aria-label": `\u041D\u043E\u0432\u043E\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}`,
|
|
7932
|
+
className: "gantt-tl-name-input gantt-resourceTimeline-resourceNameInput"
|
|
7933
|
+
}
|
|
7934
|
+
) : /* @__PURE__ */ jsx15(
|
|
7935
|
+
"button",
|
|
7936
|
+
{
|
|
7937
|
+
type: "button",
|
|
7938
|
+
className: "gantt-resourceTimeline-resourceNameButton",
|
|
7939
|
+
"aria-label": `\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u0430 ${resource.name}`,
|
|
7940
|
+
title: resource.name,
|
|
7941
|
+
onClick: (event) => {
|
|
7942
|
+
event.stopPropagation();
|
|
7943
|
+
onResourceNameClick?.(resourceId);
|
|
7944
|
+
},
|
|
7945
|
+
onDoubleClick: handleNameDoubleClick,
|
|
7946
|
+
children: resource.name
|
|
7879
7947
|
}
|
|
7880
7948
|
)
|
|
7881
7949
|
] }),
|
|
@@ -7919,15 +7987,15 @@ var ResourceHeader = ({
|
|
|
7919
7987
|
"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.`,
|
|
7920
7988
|
children: [
|
|
7921
7989
|
/* @__PURE__ */ jsxs12("span", { className: "gantt-resourceTimeline-resourceWorkedDays", children: [
|
|
7922
|
-
workedDays,
|
|
7923
|
-
" \u0434\u043D."
|
|
7990
|
+
/* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceMetricValue", children: workedDays > 0 ? workedDays : "-" }),
|
|
7991
|
+
/* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceMetricLabel", children: workedDays > 0 ? "\u0434\u043D." : "" })
|
|
7924
7992
|
] }),
|
|
7925
7993
|
/* @__PURE__ */ jsxs12("span", { className: "gantt-resourceTimeline-resourceAssignmentCount", children: [
|
|
7926
|
-
/* @__PURE__ */
|
|
7994
|
+
/* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceMetricValue", children: assignmentCount > 0 ? assignmentCount : "" }),
|
|
7995
|
+
assignmentCount > 0 && /* @__PURE__ */ jsxs12("svg", { className: "gantt-resourceTimeline-resourceAssignmentIcon", width: "14", height: "14", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
7927
7996
|
/* @__PURE__ */ jsx15("rect", { width: "15", height: "5", x: "4", y: "5", rx: "2" }),
|
|
7928
7997
|
/* @__PURE__ */ jsx15("rect", { width: "10", height: "5", x: "4", y: "14", rx: "2" })
|
|
7929
|
-
] })
|
|
7930
|
-
/* @__PURE__ */ jsx15("span", { children: assignmentCount })
|
|
7998
|
+
] })
|
|
7931
7999
|
] })
|
|
7932
8000
|
]
|
|
7933
8001
|
}
|
|
@@ -8052,6 +8120,7 @@ function ResourceTimelineChart({
|
|
|
8052
8120
|
customDays,
|
|
8053
8121
|
isWeekend: isWeekend3,
|
|
8054
8122
|
businessDays = true,
|
|
8123
|
+
resourceGrouping = false,
|
|
8055
8124
|
readonly,
|
|
8056
8125
|
disableResourceReassignment,
|
|
8057
8126
|
renderItem,
|
|
@@ -8069,9 +8138,12 @@ function ResourceTimelineChart({
|
|
|
8069
8138
|
const gridRef = useRef8(null);
|
|
8070
8139
|
const panStateRef = useRef8(null);
|
|
8071
8140
|
const conflictNavigationIndexRef = useRef8(/* @__PURE__ */ new Map());
|
|
8141
|
+
const resourceNavigationIndexRef = useRef8(/* @__PURE__ */ new Map());
|
|
8072
8142
|
const conflictHighlightTimeoutRef = useRef8(null);
|
|
8073
8143
|
const [isCreatingResource, setIsCreatingResource] = useState8(false);
|
|
8144
|
+
const [creatingResourceGroupType, setCreatingResourceGroupType] = useState8(null);
|
|
8074
8145
|
const [activeConflictItemId, setActiveConflictItemId] = useState8(null);
|
|
8146
|
+
const [resourceColumnHasRightShadow, setResourceColumnHasRightShadow] = useState8(false);
|
|
8075
8147
|
const validItems = useMemo9(() => collectValidItems(resources), [resources]);
|
|
8076
8148
|
const dateRange = useMemo9(() => getMultiMonthDays(validItems), [validItems]);
|
|
8077
8149
|
const monthStart = useMemo9(() => {
|
|
@@ -8084,6 +8156,10 @@ function ResourceTimelineChart({
|
|
|
8084
8156
|
);
|
|
8085
8157
|
const gridWidth = useMemo9(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
|
|
8086
8158
|
const effectiveRowHeaderWidth = Math.max(rowHeaderWidth, DEFAULT_ROW_HEADER_WIDTH);
|
|
8159
|
+
const orderedResources = useMemo9(
|
|
8160
|
+
() => resourceGrouping === "type" ? orderResourcesByType(resources) : resources,
|
|
8161
|
+
[resourceGrouping, resources]
|
|
8162
|
+
);
|
|
8087
8163
|
const workedDaysByResourceId = useMemo9(() => {
|
|
8088
8164
|
const map = /* @__PURE__ */ new Map();
|
|
8089
8165
|
for (const resource of resources) {
|
|
@@ -8104,14 +8180,87 @@ function ResourceTimelineChart({
|
|
|
8104
8180
|
return map;
|
|
8105
8181
|
}, [businessDays, resources, weekendPredicate]);
|
|
8106
8182
|
const layout = useMemo9(
|
|
8107
|
-
() => layoutResourceTimelineItems(
|
|
8183
|
+
() => layoutResourceTimelineItems(orderedResources, {
|
|
8108
8184
|
monthStart,
|
|
8109
8185
|
dayWidth,
|
|
8110
8186
|
laneHeight,
|
|
8111
8187
|
rowGap: DEFAULT_RESOURCE_ROW_GAP
|
|
8112
8188
|
}),
|
|
8113
|
-
[
|
|
8189
|
+
[orderedResources, monthStart, dayWidth, laneHeight]
|
|
8114
8190
|
);
|
|
8191
|
+
const canAddResource = enableAddResource && Boolean(onAddResource);
|
|
8192
|
+
const resourceAddRowHeight = laneHeight + DEFAULT_RESOURCE_ROW_GAP;
|
|
8193
|
+
const displayLayout = useMemo9(() => {
|
|
8194
|
+
if (resourceGrouping !== "type") {
|
|
8195
|
+
return {
|
|
8196
|
+
rows: layout.rows,
|
|
8197
|
+
items: layout.items,
|
|
8198
|
+
groupHeaders: [],
|
|
8199
|
+
groupAddRows: [],
|
|
8200
|
+
totalHeight: layout.totalHeight
|
|
8201
|
+
};
|
|
8202
|
+
}
|
|
8203
|
+
const countsByType = /* @__PURE__ */ new Map();
|
|
8204
|
+
for (const row of layout.rows) {
|
|
8205
|
+
const type = getResourceType(row.resource);
|
|
8206
|
+
countsByType.set(type, (countsByType.get(type) ?? 0) + 1);
|
|
8207
|
+
}
|
|
8208
|
+
let offset = 0;
|
|
8209
|
+
let previousType = null;
|
|
8210
|
+
const rowTopOffsets = /* @__PURE__ */ new Map();
|
|
8211
|
+
const groupHeaders = [];
|
|
8212
|
+
const groupAddRows = [];
|
|
8213
|
+
const rows = layout.rows.map((row) => {
|
|
8214
|
+
const type = getResourceType(row.resource);
|
|
8215
|
+
if (type !== previousType) {
|
|
8216
|
+
if (previousType !== null && creatingResourceGroupType === previousType) {
|
|
8217
|
+
groupAddRows.push({
|
|
8218
|
+
key: previousType,
|
|
8219
|
+
top: row.resourceRowTop + offset,
|
|
8220
|
+
height: resourceAddRowHeight
|
|
8221
|
+
});
|
|
8222
|
+
offset += resourceAddRowHeight;
|
|
8223
|
+
}
|
|
8224
|
+
groupHeaders.push({
|
|
8225
|
+
key: type,
|
|
8226
|
+
label: type,
|
|
8227
|
+
count: countsByType.get(type) ?? 0,
|
|
8228
|
+
top: row.resourceRowTop + offset,
|
|
8229
|
+
height: DEFAULT_RESOURCE_GROUP_HEIGHT
|
|
8230
|
+
});
|
|
8231
|
+
offset += DEFAULT_RESOURCE_GROUP_HEIGHT;
|
|
8232
|
+
previousType = type;
|
|
8233
|
+
}
|
|
8234
|
+
rowTopOffsets.set(row.resourceId, offset);
|
|
8235
|
+
return {
|
|
8236
|
+
...row,
|
|
8237
|
+
resourceRowTop: row.resourceRowTop + offset
|
|
8238
|
+
};
|
|
8239
|
+
});
|
|
8240
|
+
if (previousType !== null && creatingResourceGroupType === previousType) {
|
|
8241
|
+
groupAddRows.push({
|
|
8242
|
+
key: previousType,
|
|
8243
|
+
top: layout.totalHeight + offset,
|
|
8244
|
+
height: resourceAddRowHeight
|
|
8245
|
+
});
|
|
8246
|
+
offset += resourceAddRowHeight;
|
|
8247
|
+
}
|
|
8248
|
+
const items = layout.items.map((item) => {
|
|
8249
|
+
const itemOffset = rowTopOffsets.get(item.resourceId) ?? 0;
|
|
8250
|
+
return {
|
|
8251
|
+
...item,
|
|
8252
|
+
resourceRowTop: item.resourceRowTop + itemOffset,
|
|
8253
|
+
top: item.top + itemOffset
|
|
8254
|
+
};
|
|
8255
|
+
});
|
|
8256
|
+
return {
|
|
8257
|
+
rows,
|
|
8258
|
+
items,
|
|
8259
|
+
groupHeaders,
|
|
8260
|
+
groupAddRows,
|
|
8261
|
+
totalHeight: layout.totalHeight + offset
|
|
8262
|
+
};
|
|
8263
|
+
}, [creatingResourceGroupType, layout, resourceAddRowHeight, resourceGrouping]);
|
|
8115
8264
|
const todayInRange = useMemo9(() => {
|
|
8116
8265
|
const now = /* @__PURE__ */ new Date();
|
|
8117
8266
|
const today = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
|
|
@@ -8119,16 +8268,16 @@ function ResourceTimelineChart({
|
|
|
8119
8268
|
}, [dateRange]);
|
|
8120
8269
|
const itemsByResourceId = useMemo9(() => {
|
|
8121
8270
|
const map = /* @__PURE__ */ new Map();
|
|
8122
|
-
for (const item of
|
|
8271
|
+
for (const item of displayLayout.items) {
|
|
8123
8272
|
const next = map.get(item.resourceId) ?? [];
|
|
8124
8273
|
next.push(item);
|
|
8125
8274
|
map.set(item.resourceId, next);
|
|
8126
8275
|
}
|
|
8127
8276
|
return map;
|
|
8128
|
-
}, [
|
|
8277
|
+
}, [displayLayout.items]);
|
|
8129
8278
|
const conflictItemsByResourceId = useMemo9(() => {
|
|
8130
8279
|
const map = /* @__PURE__ */ new Map();
|
|
8131
|
-
for (const item of
|
|
8280
|
+
for (const item of displayLayout.items) {
|
|
8132
8281
|
if (item.conflictRanges.length === 0) {
|
|
8133
8282
|
continue;
|
|
8134
8283
|
}
|
|
@@ -8142,21 +8291,28 @@ function ResourceTimelineChart({
|
|
|
8142
8291
|
);
|
|
8143
8292
|
}
|
|
8144
8293
|
return map;
|
|
8145
|
-
}, [
|
|
8146
|
-
const
|
|
8147
|
-
const
|
|
8148
|
-
const displayTotalHeight = layout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
|
|
8294
|
+
}, [displayLayout.items]);
|
|
8295
|
+
const displayTotalHeight = displayLayout.totalHeight + (canAddResource ? resourceAddRowHeight : 0);
|
|
8296
|
+
const timelineHeaderHeight = headerHeight + 1;
|
|
8149
8297
|
const handleConfirmNewResource = useCallback7((name) => {
|
|
8150
8298
|
onAddResource?.({
|
|
8151
8299
|
id: crypto.randomUUID(),
|
|
8152
8300
|
name,
|
|
8153
|
-
type: "\u0414\u0440\u0443\u0433\u043E\u0435",
|
|
8301
|
+
type: creatingResourceGroupType ?? "\u0414\u0440\u0443\u0433\u043E\u0435",
|
|
8154
8302
|
scope: "Project",
|
|
8155
8303
|
items: []
|
|
8156
8304
|
});
|
|
8157
8305
|
setIsCreatingResource(false);
|
|
8158
|
-
|
|
8159
|
-
|
|
8306
|
+
setCreatingResourceGroupType(null);
|
|
8307
|
+
}, [creatingResourceGroupType, onAddResource]);
|
|
8308
|
+
const handleStartAddResourceToGroup = useCallback7((type) => {
|
|
8309
|
+
setIsCreatingResource(false);
|
|
8310
|
+
setCreatingResourceGroupType(type);
|
|
8311
|
+
}, []);
|
|
8312
|
+
const handleCancelNewResource = useCallback7(() => {
|
|
8313
|
+
setIsCreatingResource(false);
|
|
8314
|
+
setCreatingResourceGroupType(null);
|
|
8315
|
+
}, []);
|
|
8160
8316
|
const handleConflictBadgeClick = useCallback7((resourceId) => {
|
|
8161
8317
|
const conflictItems = conflictItemsByResourceId.get(resourceId) ?? [];
|
|
8162
8318
|
if (conflictItems.length === 0) {
|
|
@@ -8182,10 +8338,31 @@ function ResourceTimelineChart({
|
|
|
8182
8338
|
conflictHighlightTimeoutRef.current = null;
|
|
8183
8339
|
}, 1600);
|
|
8184
8340
|
}, [conflictItemsByResourceId, dayWidth, laneHeight]);
|
|
8341
|
+
const handleResourceNameClick = useCallback7((resourceId) => {
|
|
8342
|
+
const resourceItems = itemsByResourceId.get(resourceId) ?? [];
|
|
8343
|
+
if (resourceItems.length === 0) {
|
|
8344
|
+
return;
|
|
8345
|
+
}
|
|
8346
|
+
const orderedItems = [...resourceItems].sort(
|
|
8347
|
+
(left, right) => left.left - right.left || left.top - right.top || left.itemId.localeCompare(right.itemId)
|
|
8348
|
+
);
|
|
8349
|
+
const currentIndex = resourceNavigationIndexRef.current.get(resourceId) ?? 0;
|
|
8350
|
+
const targetItem = orderedItems[currentIndex % orderedItems.length];
|
|
8351
|
+
resourceNavigationIndexRef.current.set(resourceId, (currentIndex + 1) % orderedItems.length);
|
|
8352
|
+
const container = scrollContainerRef.current;
|
|
8353
|
+
if (!container || !targetItem) {
|
|
8354
|
+
return;
|
|
8355
|
+
}
|
|
8356
|
+
container.scrollTo({
|
|
8357
|
+
left: Math.max(0, Math.round(targetItem.left - dayWidth * 2)),
|
|
8358
|
+
top: Math.max(0, Math.round(targetItem.top - laneHeight)),
|
|
8359
|
+
behavior: "smooth"
|
|
8360
|
+
});
|
|
8361
|
+
}, [dayWidth, itemsByResourceId, laneHeight]);
|
|
8185
8362
|
const { preview, startDrag } = useResourceItemDrag({
|
|
8186
8363
|
dayWidth,
|
|
8187
8364
|
monthStart,
|
|
8188
|
-
rows:
|
|
8365
|
+
rows: displayLayout.rows,
|
|
8189
8366
|
gridElementRef: gridRef,
|
|
8190
8367
|
readonly,
|
|
8191
8368
|
disableResourceReassignment,
|
|
@@ -8193,6 +8370,18 @@ function ResourceTimelineChart({
|
|
|
8193
8370
|
weekendPredicate,
|
|
8194
8371
|
onResourceItemMove
|
|
8195
8372
|
});
|
|
8373
|
+
useEffect8(() => {
|
|
8374
|
+
const container = scrollContainerRef.current;
|
|
8375
|
+
if (!container) return;
|
|
8376
|
+
const updateShadow = () => {
|
|
8377
|
+
setResourceColumnHasRightShadow(container.scrollLeft > 0);
|
|
8378
|
+
};
|
|
8379
|
+
updateShadow();
|
|
8380
|
+
container.addEventListener("scroll", updateShadow, { passive: true });
|
|
8381
|
+
return () => {
|
|
8382
|
+
container.removeEventListener("scroll", updateShadow);
|
|
8383
|
+
};
|
|
8384
|
+
}, []);
|
|
8196
8385
|
const handlePanStart = useCallback7((event) => {
|
|
8197
8386
|
if (event.button !== 0) {
|
|
8198
8387
|
return;
|
|
@@ -8250,6 +8439,16 @@ function ResourceTimelineChart({
|
|
|
8250
8439
|
window.removeEventListener("mouseup", handlePanEnd);
|
|
8251
8440
|
};
|
|
8252
8441
|
}, [allowVerticalPan]);
|
|
8442
|
+
useEffect8(() => {
|
|
8443
|
+
const nextNavigation = /* @__PURE__ */ new Map();
|
|
8444
|
+
for (const [resourceId, resourceItems] of itemsByResourceId.entries()) {
|
|
8445
|
+
if (resourceItems.length === 0) {
|
|
8446
|
+
continue;
|
|
8447
|
+
}
|
|
8448
|
+
nextNavigation.set(resourceId, (resourceNavigationIndexRef.current.get(resourceId) ?? 0) % resourceItems.length);
|
|
8449
|
+
}
|
|
8450
|
+
resourceNavigationIndexRef.current = nextNavigation;
|
|
8451
|
+
}, [itemsByResourceId]);
|
|
8253
8452
|
useEffect8(() => {
|
|
8254
8453
|
return () => {
|
|
8255
8454
|
if (conflictHighlightTimeoutRef.current) {
|
|
@@ -8273,14 +8472,14 @@ function ResourceTimelineChart({
|
|
|
8273
8472
|
/* @__PURE__ */ jsxs12(
|
|
8274
8473
|
"div",
|
|
8275
8474
|
{
|
|
8276
|
-
className:
|
|
8475
|
+
className: `gantt-resourceTimeline-resourceColumn${resourceColumnHasRightShadow ? " gantt-resourceTimeline-resourceColumnShadowed" : ""}`,
|
|
8277
8476
|
style: { width: `${effectiveRowHeaderWidth}px`, minWidth: `${effectiveRowHeaderWidth}px` },
|
|
8278
8477
|
children: [
|
|
8279
8478
|
/* @__PURE__ */ jsxs12(
|
|
8280
8479
|
"div",
|
|
8281
8480
|
{
|
|
8282
8481
|
className: "gantt-resourceTimeline-corner",
|
|
8283
|
-
style: { height: `${
|
|
8482
|
+
style: { height: `${timelineHeaderHeight}px` },
|
|
8284
8483
|
children: [
|
|
8285
8484
|
/* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderNumber", children: "#" }),
|
|
8286
8485
|
/* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceHeaderCell gantt-resourceTimeline-resourceHeaderName", children: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435" }),
|
|
@@ -8290,7 +8489,62 @@ function ResourceTimelineChart({
|
|
|
8290
8489
|
]
|
|
8291
8490
|
}
|
|
8292
8491
|
),
|
|
8293
|
-
|
|
8492
|
+
displayLayout.groupHeaders.length > 0 ? displayLayout.groupHeaders.map((group) => /* @__PURE__ */ jsxs12(React12.Fragment, { children: [
|
|
8493
|
+
/* @__PURE__ */ jsxs12(
|
|
8494
|
+
"div",
|
|
8495
|
+
{
|
|
8496
|
+
className: "gantt-resourceTimeline-resourceGroupHeader",
|
|
8497
|
+
style: { height: `${group.height}px` },
|
|
8498
|
+
children: [
|
|
8499
|
+
/* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceGroupTitle", children: group.label }),
|
|
8500
|
+
/* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceGroupCount", children: group.count }),
|
|
8501
|
+
canAddResource && /* @__PURE__ */ jsx15(
|
|
8502
|
+
"button",
|
|
8503
|
+
{
|
|
8504
|
+
type: "button",
|
|
8505
|
+
className: "gantt-resourceTimeline-resourceGroupAddButton",
|
|
8506
|
+
"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}`,
|
|
8507
|
+
title: `\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441 \u0432 \u0433\u0440\u0443\u043F\u043F\u0443 ${group.label}`,
|
|
8508
|
+
onClick: (event) => {
|
|
8509
|
+
event.stopPropagation();
|
|
8510
|
+
handleStartAddResourceToGroup(group.key);
|
|
8511
|
+
},
|
|
8512
|
+
children: "+"
|
|
8513
|
+
}
|
|
8514
|
+
)
|
|
8515
|
+
]
|
|
8516
|
+
}
|
|
8517
|
+
),
|
|
8518
|
+
displayLayout.rows.filter((row) => getResourceType(row.resource) === group.key).map((row) => {
|
|
8519
|
+
const rowIndex = displayLayout.rows.findIndex((candidate) => candidate.resourceId === row.resourceId);
|
|
8520
|
+
return /* @__PURE__ */ jsx15(
|
|
8521
|
+
ResourceHeader,
|
|
8522
|
+
{
|
|
8523
|
+
resource: row.resource,
|
|
8524
|
+
resourceId: row.resourceId,
|
|
8525
|
+
rowIndex,
|
|
8526
|
+
conflictCount: row.conflictCount,
|
|
8527
|
+
workedDays: workedDaysByResourceId.get(row.resourceId) ?? 0,
|
|
8528
|
+
assignmentCount: row.resource.items.length,
|
|
8529
|
+
height: row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP,
|
|
8530
|
+
paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
|
|
8531
|
+
menuCommands: resourceMenuCommands,
|
|
8532
|
+
onResourceChange,
|
|
8533
|
+
onResourceNameClick: handleResourceNameClick,
|
|
8534
|
+
onConflictBadgeClick: handleConflictBadgeClick
|
|
8535
|
+
},
|
|
8536
|
+
row.resourceId
|
|
8537
|
+
);
|
|
8538
|
+
}),
|
|
8539
|
+
displayLayout.groupAddRows.some((row) => row.key === group.key) && /* @__PURE__ */ jsx15(
|
|
8540
|
+
NewResourceRow,
|
|
8541
|
+
{
|
|
8542
|
+
height: resourceAddRowHeight,
|
|
8543
|
+
onConfirm: handleConfirmNewResource,
|
|
8544
|
+
onCancel: handleCancelNewResource
|
|
8545
|
+
}
|
|
8546
|
+
)
|
|
8547
|
+
] }, group.key)) : displayLayout.rows.map((row, rowIndex) => /* @__PURE__ */ jsx15(
|
|
8294
8548
|
ResourceHeader,
|
|
8295
8549
|
{
|
|
8296
8550
|
resource: row.resource,
|
|
@@ -8303,6 +8557,7 @@ function ResourceTimelineChart({
|
|
|
8303
8557
|
paddingBottom: DEFAULT_RESOURCE_ROW_GAP,
|
|
8304
8558
|
menuCommands: resourceMenuCommands,
|
|
8305
8559
|
onResourceChange,
|
|
8560
|
+
onResourceNameClick: handleResourceNameClick,
|
|
8306
8561
|
onConflictBadgeClick: handleConflictBadgeClick
|
|
8307
8562
|
},
|
|
8308
8563
|
row.resourceId
|
|
@@ -8320,7 +8575,10 @@ function ResourceTimelineChart({
|
|
|
8320
8575
|
className: "gantt-resourceTimeline-addResourceButton",
|
|
8321
8576
|
type: "button",
|
|
8322
8577
|
style: { height: `${resourceAddRowHeight}px` },
|
|
8323
|
-
onClick: () =>
|
|
8578
|
+
onClick: () => {
|
|
8579
|
+
setCreatingResourceGroupType(null);
|
|
8580
|
+
setIsCreatingResource(true);
|
|
8581
|
+
},
|
|
8324
8582
|
children: "+ \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441"
|
|
8325
8583
|
}
|
|
8326
8584
|
))
|
|
@@ -8333,16 +8591,23 @@ function ResourceTimelineChart({
|
|
|
8333
8591
|
className: "gantt-resourceTimeline-chartSurface",
|
|
8334
8592
|
style: { minWidth: `${gridWidth}px` },
|
|
8335
8593
|
children: [
|
|
8336
|
-
/* @__PURE__ */ jsx15(
|
|
8337
|
-
|
|
8594
|
+
/* @__PURE__ */ jsx15(
|
|
8595
|
+
"div",
|
|
8338
8596
|
{
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8597
|
+
className: "gantt-resourceTimeline-stickyHeader",
|
|
8598
|
+
style: { width: `${gridWidth}px`, height: `${timelineHeaderHeight}px` },
|
|
8599
|
+
children: /* @__PURE__ */ jsx15(
|
|
8600
|
+
TimeScaleHeader_default,
|
|
8601
|
+
{
|
|
8602
|
+
days: dateRange,
|
|
8603
|
+
dayWidth,
|
|
8604
|
+
headerHeight,
|
|
8605
|
+
viewMode,
|
|
8606
|
+
isCustomWeekend: weekendPredicate
|
|
8607
|
+
}
|
|
8608
|
+
)
|
|
8344
8609
|
}
|
|
8345
|
-
)
|
|
8610
|
+
),
|
|
8346
8611
|
/* @__PURE__ */ jsxs12(
|
|
8347
8612
|
"div",
|
|
8348
8613
|
{
|
|
@@ -8361,7 +8626,19 @@ function ResourceTimelineChart({
|
|
|
8361
8626
|
}
|
|
8362
8627
|
),
|
|
8363
8628
|
todayInRange && /* @__PURE__ */ jsx15(TodayIndicator_default, { monthStart, dayWidth }),
|
|
8364
|
-
|
|
8629
|
+
displayLayout.groupHeaders.map((group) => /* @__PURE__ */ jsx15(
|
|
8630
|
+
"div",
|
|
8631
|
+
{
|
|
8632
|
+
className: "gantt-resourceTimeline-row gantt-resourceTimeline-groupRow",
|
|
8633
|
+
"data-resource-group": group.key,
|
|
8634
|
+
style: {
|
|
8635
|
+
top: `${group.top}px`,
|
|
8636
|
+
height: `${group.height}px`
|
|
8637
|
+
}
|
|
8638
|
+
},
|
|
8639
|
+
`group-row-${group.key}`
|
|
8640
|
+
)),
|
|
8641
|
+
displayLayout.rows.map((row) => /* @__PURE__ */ jsx15(
|
|
8365
8642
|
"div",
|
|
8366
8643
|
{
|
|
8367
8644
|
className: "gantt-resourceTimeline-row",
|
|
@@ -8379,11 +8656,23 @@ function ResourceTimelineChart({
|
|
|
8379
8656
|
className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
|
|
8380
8657
|
"data-resource-add-row": "true",
|
|
8381
8658
|
style: {
|
|
8382
|
-
top: `${
|
|
8659
|
+
top: `${displayLayout.totalHeight}px`,
|
|
8383
8660
|
height: `${resourceAddRowHeight}px`
|
|
8384
8661
|
}
|
|
8385
8662
|
}
|
|
8386
8663
|
),
|
|
8664
|
+
displayLayout.groupAddRows.map((row) => /* @__PURE__ */ jsx15(
|
|
8665
|
+
"div",
|
|
8666
|
+
{
|
|
8667
|
+
className: "gantt-resourceTimeline-row gantt-resourceTimeline-rowNew",
|
|
8668
|
+
"data-resource-group-add-row": row.key,
|
|
8669
|
+
style: {
|
|
8670
|
+
top: `${row.top}px`,
|
|
8671
|
+
height: `${row.height}px`
|
|
8672
|
+
}
|
|
8673
|
+
},
|
|
8674
|
+
`group-add-row-${row.key}`
|
|
8675
|
+
)),
|
|
8387
8676
|
Array.from(itemsByResourceId.values()).flatMap(
|
|
8388
8677
|
(resourceItems) => resourceItems.map((layoutItem) => {
|
|
8389
8678
|
const customClassName = getItemClassName?.(layoutItem.item);
|
|
@@ -8990,6 +9279,7 @@ function TaskGanttChartInner(props, ref) {
|
|
|
8990
9279
|
() => visibleTasks.length * rowHeight,
|
|
8991
9280
|
[visibleTasks.length, rowHeight]
|
|
8992
9281
|
);
|
|
9282
|
+
const timelineHeaderHeight = headerHeight + 1;
|
|
8993
9283
|
const monthStart = useMemo10(() => {
|
|
8994
9284
|
if (dateRange.length === 0) {
|
|
8995
9285
|
return new Date(Date.UTC((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth(), 1));
|
|
@@ -9472,16 +9762,23 @@ function TaskGanttChartInner(props, ref) {
|
|
|
9472
9762
|
className: showChart ? "gantt-chartSurface" : "gantt-chartSurface gantt-chart-hidden",
|
|
9473
9763
|
style: { minWidth: `${gridWidth}px`, flex: 1, display: showChart ? void 0 : "none" },
|
|
9474
9764
|
children: [
|
|
9475
|
-
/* @__PURE__ */ jsx16(
|
|
9476
|
-
|
|
9765
|
+
/* @__PURE__ */ jsx16(
|
|
9766
|
+
"div",
|
|
9477
9767
|
{
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9768
|
+
className: "gantt-stickyHeader",
|
|
9769
|
+
style: { width: `${gridWidth}px`, height: `${timelineHeaderHeight}px` },
|
|
9770
|
+
children: /* @__PURE__ */ jsx16(
|
|
9771
|
+
TimeScaleHeader_default,
|
|
9772
|
+
{
|
|
9773
|
+
days: dateRange,
|
|
9774
|
+
dayWidth,
|
|
9775
|
+
headerHeight,
|
|
9776
|
+
viewMode,
|
|
9777
|
+
isCustomWeekend
|
|
9778
|
+
}
|
|
9779
|
+
)
|
|
9483
9780
|
}
|
|
9484
|
-
)
|
|
9781
|
+
),
|
|
9485
9782
|
/* @__PURE__ */ jsxs13(
|
|
9486
9783
|
"div",
|
|
9487
9784
|
{
|