@ctrl/react-orgchart 1.2.0 → 1.4.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/README.md +3 -3
- package/dist/index.d.mts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +463 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +462 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +73 -40
- package/dist-src/chart/components/iconLink.js +0 -32
- package/dist-src/chart/config.js +0 -47
- package/dist-src/chart/index.js +0 -133
- package/dist-src/chart/onClick.js +0 -33
- package/dist-src/chart/render.js +0 -188
- package/dist-src/chart/renderLines.js +0 -98
- package/dist-src/index.js +0 -1
- package/dist-src/orgChart.js +0 -33
- package/dist-src/utils/collapse.js +0 -7
- package/dist-src/utils/helpers.js +0 -20
- package/dist-src/utils/index.js +0 -3
- package/dist-src/utils/wrapText.js +0 -63
- package/dist-types/chart/components/iconLink.d.ts +0 -5
- package/dist-types/chart/config.d.ts +0 -32
- package/dist-types/chart/index.d.ts +0 -1
- package/dist-types/chart/onClick.d.ts +0 -1
- package/dist-types/chart/render.d.ts +0 -1
- package/dist-types/chart/renderLines.d.ts +0 -1
- package/dist-types/index.d.ts +0 -1
- package/dist-types/orgChart.d.ts +0 -36
- package/dist-types/utils/collapse.d.ts +0 -1
- package/dist-types/utils/helpers.d.ts +0 -5
- package/dist-types/utils/index.d.ts +0 -3
- package/dist-types/utils/wrapText.d.ts +0 -1
- package/dist-web/index.js +0 -658
- package/dist-web/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# @ctrl/react-orgchart [](https://www.npmjs.com/package/@ctrl/react-orgchart)
|
|
1
|
+
# @ctrl/react-orgchart [](https://www.npmjs.com/package/@ctrl/react-orgchart)
|
|
2
2
|
|
|
3
3
|
> React wrapper around a [d3](https://d3js.org/) v6 based org chart.
|
|
4
4
|
|
|
5
5
|
This is based on [smartprocure/react-org-chart](https://github.com/smartprocure/react-org-chart) which itself is also a fork. This fork has been updated to use d3 v6 and expose typescript types.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Demo locally with `pnpm dev`.
|
|
8
8
|
|
|
9
9
|
### Install
|
|
10
10
|
|
|
11
11
|
```console
|
|
12
|
-
|
|
12
|
+
pnpm add @ctrl/react-orgchart
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
### Use
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as react from "react";
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/chart/config.d.ts
|
|
5
|
+
interface Config {
|
|
6
|
+
animationDuration: number;
|
|
7
|
+
nodeWidth: number;
|
|
8
|
+
nodeHeight: number;
|
|
9
|
+
nodeSpacing: number;
|
|
10
|
+
nodePaddingX: number;
|
|
11
|
+
nodePaddingY: number;
|
|
12
|
+
nodeBorderRadius: number;
|
|
13
|
+
avatarWidth: number;
|
|
14
|
+
lineDepthY: number;
|
|
15
|
+
backgroundColor: string;
|
|
16
|
+
borderColor: string;
|
|
17
|
+
nameColor: string;
|
|
18
|
+
titleColor: string;
|
|
19
|
+
reportsColor: string;
|
|
20
|
+
shouldResize: boolean;
|
|
21
|
+
nameFontSize: number;
|
|
22
|
+
titleFontSize: number;
|
|
23
|
+
titleYTopDistance: number;
|
|
24
|
+
countFontSize: number;
|
|
25
|
+
countYTopDistance: number;
|
|
26
|
+
maxNameWordLength: number;
|
|
27
|
+
maxTitleWordLength: number;
|
|
28
|
+
maxCountWordLength: number;
|
|
29
|
+
onEntityLinkClick?: (data: unknown, event: Event) => boolean | void;
|
|
30
|
+
onNameClick?: (data: unknown, event: Event) => boolean | void;
|
|
31
|
+
onCountClick?: (data: unknown, event: Event) => boolean | void;
|
|
32
|
+
getName?: (data: unknown) => string;
|
|
33
|
+
getTitle?: (data: unknown) => string;
|
|
34
|
+
getCount?: (data: unknown) => string;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/types.d.ts
|
|
38
|
+
interface TreeItemEntity {
|
|
39
|
+
avatar?: string;
|
|
40
|
+
link?: string;
|
|
41
|
+
name?: string;
|
|
42
|
+
title?: string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
interface TreeItem {
|
|
46
|
+
id?: string | number;
|
|
47
|
+
parentId?: string | number | null;
|
|
48
|
+
children?: TreeItem[] | null;
|
|
49
|
+
_children?: TreeItem[] | null;
|
|
50
|
+
entity?: TreeItemEntity;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/orgChart.d.ts
|
|
55
|
+
type OrgChartProps = Partial<Config> & {
|
|
56
|
+
id?: string;
|
|
57
|
+
disableCanvasMouseMove?: boolean;
|
|
58
|
+
disableCanvasMouseWheelZoom?: boolean;
|
|
59
|
+
tree: TreeItem | TreeItem[];
|
|
60
|
+
};
|
|
61
|
+
declare function OrgChartComponent(props: OrgChartProps): react_jsx_runtime0.JSX.Element;
|
|
62
|
+
declare const OrgChart: react.MemoExoticComponent<typeof OrgChartComponent>;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { OrgChart, OrgChartProps, type TreeItem };
|
|
65
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import * as react from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/chart/config.d.ts
|
|
5
|
+
interface Config {
|
|
6
|
+
animationDuration: number;
|
|
7
|
+
nodeWidth: number;
|
|
8
|
+
nodeHeight: number;
|
|
9
|
+
nodeSpacing: number;
|
|
10
|
+
nodePaddingX: number;
|
|
11
|
+
nodePaddingY: number;
|
|
12
|
+
nodeBorderRadius: number;
|
|
13
|
+
avatarWidth: number;
|
|
14
|
+
lineDepthY: number;
|
|
15
|
+
backgroundColor: string;
|
|
16
|
+
borderColor: string;
|
|
17
|
+
nameColor: string;
|
|
18
|
+
titleColor: string;
|
|
19
|
+
reportsColor: string;
|
|
20
|
+
shouldResize: boolean;
|
|
21
|
+
nameFontSize: number;
|
|
22
|
+
titleFontSize: number;
|
|
23
|
+
titleYTopDistance: number;
|
|
24
|
+
countFontSize: number;
|
|
25
|
+
countYTopDistance: number;
|
|
26
|
+
maxNameWordLength: number;
|
|
27
|
+
maxTitleWordLength: number;
|
|
28
|
+
maxCountWordLength: number;
|
|
29
|
+
onEntityLinkClick?: (data: unknown, event: Event) => boolean | void;
|
|
30
|
+
onNameClick?: (data: unknown, event: Event) => boolean | void;
|
|
31
|
+
onCountClick?: (data: unknown, event: Event) => boolean | void;
|
|
32
|
+
getName?: (data: unknown) => string;
|
|
33
|
+
getTitle?: (data: unknown) => string;
|
|
34
|
+
getCount?: (data: unknown) => string;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/types.d.ts
|
|
38
|
+
interface TreeItemEntity {
|
|
39
|
+
avatar?: string;
|
|
40
|
+
link?: string;
|
|
41
|
+
name?: string;
|
|
42
|
+
title?: string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
interface TreeItem {
|
|
46
|
+
id?: string | number;
|
|
47
|
+
parentId?: string | number | null;
|
|
48
|
+
children?: TreeItem[] | null;
|
|
49
|
+
_children?: TreeItem[] | null;
|
|
50
|
+
entity?: TreeItemEntity;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/orgChart.d.ts
|
|
55
|
+
type OrgChartProps = Partial<Config> & {
|
|
56
|
+
id?: string;
|
|
57
|
+
disableCanvasMouseMove?: boolean;
|
|
58
|
+
disableCanvasMouseWheelZoom?: boolean;
|
|
59
|
+
tree: TreeItem | TreeItem[];
|
|
60
|
+
};
|
|
61
|
+
declare function OrgChartComponent(props: OrgChartProps): react_jsx_runtime0.JSX.Element;
|
|
62
|
+
declare const OrgChart: react.MemoExoticComponent<typeof OrgChartComponent>;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { OrgChart, OrgChartProps, type TreeItem };
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let d3_hierarchy = require("d3-hierarchy");
|
|
4
|
+
let d3_selection = require("d3-selection");
|
|
5
|
+
let d3_zoom = require("d3-zoom");
|
|
6
|
+
let d3_shape = require("d3-shape");
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
|
|
9
|
+
//#region src/chart/config.ts
|
|
10
|
+
const animationDuration = 350;
|
|
11
|
+
const shouldResize = true;
|
|
12
|
+
const nodeWidth = 140;
|
|
13
|
+
const nodeHeight = 190;
|
|
14
|
+
const nodeSpacing = 12;
|
|
15
|
+
const nodePaddingX = 16;
|
|
16
|
+
const nodePaddingY = 16;
|
|
17
|
+
const avatarWidth = 48;
|
|
18
|
+
const nodeBorderRadius = 4;
|
|
19
|
+
const lineDepthY = 120;
|
|
20
|
+
const backgroundColor = "#fff";
|
|
21
|
+
const borderColor = "#E7E1EC";
|
|
22
|
+
const nameColor = "#302839";
|
|
23
|
+
const titleColor = "#645574";
|
|
24
|
+
const reportsColor = "#92A0AD";
|
|
25
|
+
const config = {
|
|
26
|
+
animationDuration,
|
|
27
|
+
nodeWidth,
|
|
28
|
+
nodeHeight,
|
|
29
|
+
nodeSpacing,
|
|
30
|
+
nodePaddingX,
|
|
31
|
+
nodePaddingY,
|
|
32
|
+
nodeBorderRadius,
|
|
33
|
+
avatarWidth,
|
|
34
|
+
lineDepthY,
|
|
35
|
+
backgroundColor,
|
|
36
|
+
borderColor,
|
|
37
|
+
nameColor,
|
|
38
|
+
titleColor,
|
|
39
|
+
reportsColor,
|
|
40
|
+
shouldResize,
|
|
41
|
+
nameFontSize: 14,
|
|
42
|
+
titleFontSize: 13,
|
|
43
|
+
titleYTopDistance: 25,
|
|
44
|
+
countFontSize: 14,
|
|
45
|
+
countYTopDistance: 72,
|
|
46
|
+
maxNameWordLength: 16,
|
|
47
|
+
maxTitleWordLength: 17,
|
|
48
|
+
maxCountWordLength: 17
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/utils/collapse.ts
|
|
53
|
+
function collapse(d) {
|
|
54
|
+
if (d.children) {
|
|
55
|
+
d._children = d.children;
|
|
56
|
+
d.children = void 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/utils/wrapText.ts
|
|
62
|
+
const getTruncatedText = (text, maxWordLength) => {
|
|
63
|
+
if (text.length <= maxWordLength) return text;
|
|
64
|
+
if (maxWordLength <= 3) return ".".repeat(maxWordLength);
|
|
65
|
+
return `${text.slice(0, maxWordLength - 3)}...`;
|
|
66
|
+
};
|
|
67
|
+
function wrapText(text, maxLineWidth, maxNumberOfLines = 3, maxWordLength = 17) {
|
|
68
|
+
let editedClass = "";
|
|
69
|
+
text.each(function eachTextNode() {
|
|
70
|
+
var _textSelection$attr;
|
|
71
|
+
const textSelection = (0, d3_selection.select)(this);
|
|
72
|
+
const x = textSelection.attr("x");
|
|
73
|
+
const y = textSelection.attr("y");
|
|
74
|
+
const dy = Number.parseFloat((_textSelection$attr = textSelection.attr("dy")) !== null && _textSelection$attr !== void 0 ? _textSelection$attr : "0");
|
|
75
|
+
const lineHeight = 1.1;
|
|
76
|
+
const words = textSelection.text().split(/\s+/).reverse();
|
|
77
|
+
let lineNumber = 0;
|
|
78
|
+
let curLineWidth = 0;
|
|
79
|
+
let word = "";
|
|
80
|
+
let line = [];
|
|
81
|
+
let tspan = textSelection.text(null).append("tspan").style("text-anchor", "middle").attr("x", x).attr("y", y).attr("dy", `${dy}em`);
|
|
82
|
+
while (lineNumber < maxNumberOfLines && words.length > 0) {
|
|
83
|
+
var _tspan$node$getComput, _tspan$node;
|
|
84
|
+
const nextWord = words.pop();
|
|
85
|
+
if (!nextWord) break;
|
|
86
|
+
word = nextWord;
|
|
87
|
+
line.push(word);
|
|
88
|
+
tspan.text(line.join(" "));
|
|
89
|
+
curLineWidth = (_tspan$node$getComput = (_tspan$node = tspan.node()) === null || _tspan$node === void 0 ? void 0 : _tspan$node.getComputedTextLength()) !== null && _tspan$node$getComput !== void 0 ? _tspan$node$getComput : 0;
|
|
90
|
+
if (curLineWidth > maxLineWidth) {
|
|
91
|
+
if (lineNumber + 1 === maxNumberOfLines) {
|
|
92
|
+
tspan.text(getTruncatedText(line.join(" "), maxWordLength));
|
|
93
|
+
break;
|
|
94
|
+
} else {
|
|
95
|
+
line.pop();
|
|
96
|
+
tspan.text(line.join(" "));
|
|
97
|
+
line = [word];
|
|
98
|
+
tspan = textSelection.append("tspan").style("text-anchor", "middle").attr("x", x).attr("y", y).attr("dy", `${++lineNumber * lineHeight + dy}em`).text(getTruncatedText(word, maxWordLength));
|
|
99
|
+
}
|
|
100
|
+
if (word.length > maxWordLength) break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!editedClass) {
|
|
104
|
+
var _textSelection$attr2;
|
|
105
|
+
editedClass = ((_textSelection$attr2 = textSelection.attr("class")) !== null && _textSelection$attr2 !== void 0 ? _textSelection$attr2 : "").replace(" unedited", "");
|
|
106
|
+
}
|
|
107
|
+
textSelection.attr("class", editedClass);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/utils/helpers.ts
|
|
113
|
+
const getName = (data) => {
|
|
114
|
+
var _data$data$entity$nam, _data$data$entity;
|
|
115
|
+
return (_data$data$entity$nam = (_data$data$entity = data.data.entity) === null || _data$data$entity === void 0 ? void 0 : _data$data$entity.name) !== null && _data$data$entity$nam !== void 0 ? _data$data$entity$nam : "";
|
|
116
|
+
};
|
|
117
|
+
const getTitle = (data) => {
|
|
118
|
+
var _data$data$entity$tit, _data$data$entity2;
|
|
119
|
+
return (_data$data$entity$tit = (_data$data$entity2 = data.data.entity) === null || _data$data$entity2 === void 0 ? void 0 : _data$data$entity2.title) !== null && _data$data$entity$tit !== void 0 ? _data$data$entity$tit : "";
|
|
120
|
+
};
|
|
121
|
+
const getCount = (data) => {
|
|
122
|
+
var _data$children$length, _data$children, _data$_children$lengt, _data$_children;
|
|
123
|
+
const visibleChildren = (_data$children$length = (_data$children = data.children) === null || _data$children === void 0 ? void 0 : _data$children.length) !== null && _data$children$length !== void 0 ? _data$children$length : 0;
|
|
124
|
+
const collapsedChildren = (_data$_children$lengt = (_data$_children = data._children) === null || _data$_children === void 0 ? void 0 : _data$_children.length) !== null && _data$_children$lengt !== void 0 ? _data$_children$lengt : 0;
|
|
125
|
+
const children = visibleChildren > 0 ? visibleChildren : collapsedChildren;
|
|
126
|
+
if (!children) return "";
|
|
127
|
+
return `Team (${children})`;
|
|
128
|
+
};
|
|
129
|
+
const getCursorForNode = (data) => data.children || data._children || data.data.children || data.data._children ? "pointer" : "default";
|
|
130
|
+
const customOnClick = (fn, onClick, config) => (event, data) => {
|
|
131
|
+
if (typeof fn === "function") if (fn(data, event)) onClick(config);
|
|
132
|
+
else event.stopPropagation();
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/chart/components/iconLink.ts
|
|
137
|
+
const iconLink = ({ svg, x = 5, y = 5 }) => {
|
|
138
|
+
const icon = svg.append("g").attr("stroke", "none").attr("fill", "none").style("cursor", "pointer").append("g").append("g").attr("id", "icon").attr("fill", "#9585A3").attr("transform", `translate(${x}, ${y})`);
|
|
139
|
+
const arrow = icon.append("g").attr("id", "arrow").attr("transform", "translate(7.000000, 7.000000) scale(-1, 1) translate(-7.000000, -7.000000)");
|
|
140
|
+
arrow.append("path").attr("d", "M3.41421356,2 L8.70710678,7.29289322 C9.09763107,7.68341751 9.09763107,8.31658249 8.70710678,8.70710678 C8.31658249,9.09763107 7.68341751,9.09763107 7.29289322,8.70710678 L2,3.41421356 L2,7 C2,7.55228475 1.55228475,8 1,8 C0.44771525,8 0,7.55228475 0,7 L0,1.49100518 C0,0.675320548 0.667758414,0 1.49100518,0 L7,0 C7.55228475,0 8,0.44771525 8,1 C8,1.55228475 7.55228475,2 7,2 L3.41421356,2 Z");
|
|
141
|
+
arrow.append("path").attr("d", "M12,2 L12,12 L2,12 L2,11 C2,10.4477153 1.55228475,10 1,10 C0.44771525,10 0,10.4477153 0,11 L0,12.4953156 C0,13.3242086 0.674596865,14 1.50034732,14 L12.4996527,14 C13.3281027,14 14,13.3234765 14,12.4996527 L14,1.50034732 C14,0.669321781 13.3358906,0 12.4953156,0 L11,0 C10.4477153,0 10,0.44771525 10,1 C10,1.55228475 10.4477153,2 11,2 L12,2 Z");
|
|
142
|
+
icon.append("rect").attr("id", "bounds").attr("x", 0).attr("y", 0).attr("width", 24).attr("height", 24).attr("fill", "transparent");
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/chart/onClick.ts
|
|
147
|
+
function onClick(config) {
|
|
148
|
+
const { render } = config;
|
|
149
|
+
return (event, datum) => {
|
|
150
|
+
if (event.defaultPrevented) return;
|
|
151
|
+
const target = event.target;
|
|
152
|
+
const link = target instanceof Element ? target.closest("a") : null;
|
|
153
|
+
if (link instanceof HTMLAnchorElement && link.href) return;
|
|
154
|
+
if (!datum.children && !datum._children) return;
|
|
155
|
+
if (datum.children) {
|
|
156
|
+
config.callerNode = datum;
|
|
157
|
+
datum._children = datum.children;
|
|
158
|
+
datum.children = void 0;
|
|
159
|
+
} else {
|
|
160
|
+
config.callerNode = void 0;
|
|
161
|
+
datum.children = datum._children;
|
|
162
|
+
datum._children = void 0;
|
|
163
|
+
}
|
|
164
|
+
render({
|
|
165
|
+
...config,
|
|
166
|
+
sourceNode: datum
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/chart/renderLines.ts
|
|
173
|
+
const margin = 10;
|
|
174
|
+
function renderLines(config) {
|
|
175
|
+
const { svg, links, nodeWidth, nodeHeight, borderColor, sourceNode, animationDuration } = config;
|
|
176
|
+
const link = svg.selectAll("path.link").data(links, ({ source, target }) => {
|
|
177
|
+
var _source$data$id, _target$data$id;
|
|
178
|
+
return `${(_source$data$id = source.data.id) !== null && _source$data$id !== void 0 ? _source$data$id : ""}-${(_target$data$id = target.data.id) !== null && _target$data$id !== void 0 ? _target$data$id : ""}`;
|
|
179
|
+
});
|
|
180
|
+
const angle = (0, d3_shape.line)().x((point) => point.x).y((point) => point.y).curve(d3_shape.curveLinear);
|
|
181
|
+
const halfNodeWidth = nodeWidth / 2;
|
|
182
|
+
link.enter().insert("path", "g").attr("class", "link").attr("fill", "none").attr("stroke", borderColor).attr("stroke-opacity", 1).attr("stroke-width", 1.25).attr("d", (linkDatum) => {
|
|
183
|
+
return angle([
|
|
184
|
+
{
|
|
185
|
+
x: linkDatum.source.x + halfNodeWidth,
|
|
186
|
+
y: linkDatum.source.y + margin
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
x: linkDatum.source.x + halfNodeWidth,
|
|
190
|
+
y: linkDatum.source.y + margin
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
x: linkDatum.source.x + halfNodeWidth,
|
|
194
|
+
y: linkDatum.source.y + margin
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
x: linkDatum.source.x + halfNodeWidth,
|
|
198
|
+
y: linkDatum.source.y + margin
|
|
199
|
+
}
|
|
200
|
+
]);
|
|
201
|
+
}).merge(link).transition().duration(animationDuration).attr("d", (linkDatum) => {
|
|
202
|
+
return angle([
|
|
203
|
+
{
|
|
204
|
+
x: linkDatum.source.x + halfNodeWidth,
|
|
205
|
+
y: linkDatum.source.y + nodeHeight
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
x: linkDatum.source.x + halfNodeWidth,
|
|
209
|
+
y: linkDatum.target.y - margin
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
x: linkDatum.target.x + halfNodeWidth,
|
|
213
|
+
y: linkDatum.target.y - margin
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
x: linkDatum.target.x + halfNodeWidth,
|
|
217
|
+
y: linkDatum.target.y
|
|
218
|
+
}
|
|
219
|
+
]);
|
|
220
|
+
});
|
|
221
|
+
link.exit().transition().duration(animationDuration).attr("d", () => {
|
|
222
|
+
var _sourceNode$x, _sourceNode$y;
|
|
223
|
+
const lineNodeX = (_sourceNode$x = sourceNode === null || sourceNode === void 0 ? void 0 : sourceNode.x) !== null && _sourceNode$x !== void 0 ? _sourceNode$x : 0;
|
|
224
|
+
const lineNodeY = (_sourceNode$y = sourceNode === null || sourceNode === void 0 ? void 0 : sourceNode.y) !== null && _sourceNode$y !== void 0 ? _sourceNode$y : 0;
|
|
225
|
+
return angle([
|
|
226
|
+
{
|
|
227
|
+
x: lineNodeX + halfNodeWidth,
|
|
228
|
+
y: lineNodeY + nodeHeight + 2
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
x: lineNodeX + halfNodeWidth,
|
|
232
|
+
y: lineNodeY + nodeHeight + 2
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
x: lineNodeX + halfNodeWidth,
|
|
236
|
+
y: lineNodeY + nodeHeight + 2
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
x: lineNodeX + halfNodeWidth,
|
|
240
|
+
y: lineNodeY + nodeHeight + 2
|
|
241
|
+
}
|
|
242
|
+
]);
|
|
243
|
+
}).remove();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/chart/render.ts
|
|
248
|
+
const CHART_NODE_CLASS = "org-chart-node";
|
|
249
|
+
const ENTITY_LINK_CLASS = "org-chart-entity-link";
|
|
250
|
+
const ENTITY_NAME_CLASS = "org-chart-entity-name";
|
|
251
|
+
const ENTITY_TITLE_CLASS = "org-chart-entity-title";
|
|
252
|
+
const COUNTS_CLASS = "org-chart-counts";
|
|
253
|
+
function render(config) {
|
|
254
|
+
const { svg, tree, animationDuration, nodeWidth, nodeHeight, nodePaddingY, nodeBorderRadius, backgroundColor, nameColor, titleColor, reportsColor, borderColor, avatarWidth, lineDepthY, sourceNode, onEntityLinkClick, nameFontSize = 14, titleFontSize = 13, titleYTopDistance = 25, countFontSize = 14, countYTopDistance = 72, maxNameWordLength = 16, maxTitleWordLength = 17, maxCountWordLength = 17, getName: getName$1, getTitle: getTitle$1, getCount: getCount$1, onNameClick, onCountClick, treeMap } = config;
|
|
255
|
+
const data = treeMap(tree);
|
|
256
|
+
const nodes = data.descendants();
|
|
257
|
+
const links = data.links();
|
|
258
|
+
config.links = links;
|
|
259
|
+
config.nodes = nodes;
|
|
260
|
+
nodes.forEach((node) => {
|
|
261
|
+
node.y = node.depth * lineDepthY;
|
|
262
|
+
});
|
|
263
|
+
const node = svg.selectAll(`g.${CHART_NODE_CLASS}`).data(nodes, (nodeDatum) => {
|
|
264
|
+
var _nodeDatum$data$id;
|
|
265
|
+
return (_nodeDatum$data$id = nodeDatum.data.id) !== null && _nodeDatum$data$id !== void 0 ? _nodeDatum$data$id : "";
|
|
266
|
+
});
|
|
267
|
+
const parentNode = sourceNode !== null && sourceNode !== void 0 ? sourceNode : nodes[0];
|
|
268
|
+
const nodeEnter = node.enter().append("g").attr("class", CHART_NODE_CLASS).attr("transform", () => {
|
|
269
|
+
var _parentNode$x, _parentNode$y;
|
|
270
|
+
return `translate(${(_parentNode$x = parentNode.x0) !== null && _parentNode$x !== void 0 ? _parentNode$x : parentNode.x}, ${(_parentNode$y = parentNode.y0) !== null && _parentNode$y !== void 0 ? _parentNode$y : parentNode.y})`;
|
|
271
|
+
}).on("click", onClick(config));
|
|
272
|
+
nodeEnter.append("rect").attr("width", nodeWidth).attr("height", nodeHeight).attr("fill", backgroundColor).attr("stroke", borderColor).attr("rx", nodeBorderRadius).attr("ry", nodeBorderRadius).attr("fill-opacity", .05).attr("stroke-opacity", .025).attr("filter", "url(#boxShadow)");
|
|
273
|
+
nodeEnter.append("rect").attr("width", nodeWidth).attr("height", nodeHeight).attr("id", (nodeDatum) => {
|
|
274
|
+
var _nodeDatum$data$id2;
|
|
275
|
+
return `${(_nodeDatum$data$id2 = nodeDatum.data.id) !== null && _nodeDatum$data$id2 !== void 0 ? _nodeDatum$data$id2 : ""}`;
|
|
276
|
+
}).attr("fill", backgroundColor).attr("stroke", borderColor).attr("rx", nodeBorderRadius).attr("ry", nodeBorderRadius).style("cursor", getCursorForNode);
|
|
277
|
+
const namePos = {
|
|
278
|
+
x: nodeWidth / 2,
|
|
279
|
+
y: nodePaddingY * 1.8 + avatarWidth
|
|
280
|
+
};
|
|
281
|
+
const avatarPos = {
|
|
282
|
+
x: nodeWidth / 2 - avatarWidth / 2,
|
|
283
|
+
y: nodePaddingY / 2
|
|
284
|
+
};
|
|
285
|
+
nodeEnter.append("text").attr("class", `${ENTITY_NAME_CLASS} unedited`).attr("x", namePos.x).attr("y", namePos.y).attr("dy", ".3em").style("cursor", "pointer").style("fill", nameColor).style("font-size", nameFontSize).text((nodeDatum) => typeof getName$1 === "function" ? getName$1(nodeDatum) : getName(nodeDatum)).on("click", customOnClick(onNameClick, onClick, config));
|
|
286
|
+
nodeEnter.append("text").attr("class", `${ENTITY_TITLE_CLASS} unedited`).attr("x", nodeWidth / 2).attr("y", namePos.y + nodePaddingY + titleYTopDistance).attr("dy", "0.1em").style("font-size", titleFontSize).style("cursor", "pointer").style("fill", titleColor).text((nodeDatum) => typeof getTitle$1 === "function" ? getTitle$1(nodeDatum) : getTitle(nodeDatum));
|
|
287
|
+
nodeEnter.append("text").attr("class", `${COUNTS_CLASS} unedited`).attr("x", nodeWidth / 2).attr("y", namePos.y + nodePaddingY + countYTopDistance).attr("dy", ".9em").style("font-size", countFontSize).style("font-weight", 400).style("cursor", "pointer").style("fill", reportsColor).text((nodeDatum) => typeof getCount$1 === "function" ? getCount$1(nodeDatum) : getCount(nodeDatum)).on("click", customOnClick(onCountClick, onClick, config));
|
|
288
|
+
nodeEnter.append("image").attr("id", (nodeDatum) => {
|
|
289
|
+
var _nodeDatum$data$id3;
|
|
290
|
+
return `image-${(_nodeDatum$data$id3 = nodeDatum.data.id) !== null && _nodeDatum$data$id3 !== void 0 ? _nodeDatum$data$id3 : ""}`;
|
|
291
|
+
}).attr("width", avatarWidth).attr("height", avatarWidth).attr("x", avatarPos.x).attr("y", avatarPos.y).attr("stroke", borderColor).attr("src", (nodeDatum) => {
|
|
292
|
+
var _nodeDatum$data$entit, _nodeDatum$data$entit2;
|
|
293
|
+
return (_nodeDatum$data$entit = (_nodeDatum$data$entit2 = nodeDatum.data.entity) === null || _nodeDatum$data$entit2 === void 0 ? void 0 : _nodeDatum$data$entit2.avatar) !== null && _nodeDatum$data$entit !== void 0 ? _nodeDatum$data$entit : "";
|
|
294
|
+
}).attr("href", (nodeDatum) => {
|
|
295
|
+
var _nodeDatum$data$entit3, _nodeDatum$data$entit4;
|
|
296
|
+
return (_nodeDatum$data$entit3 = (_nodeDatum$data$entit4 = nodeDatum.data.entity) === null || _nodeDatum$data$entit4 === void 0 ? void 0 : _nodeDatum$data$entit4.avatar) !== null && _nodeDatum$data$entit3 !== void 0 ? _nodeDatum$data$entit3 : "";
|
|
297
|
+
}).attr("clip-path", "url(#avatarClip)");
|
|
298
|
+
iconLink({
|
|
299
|
+
svg: nodeEnter.append("a").attr("class", ENTITY_LINK_CLASS).attr("display", (nodeDatum) => {
|
|
300
|
+
var _nodeDatum$data$entit5;
|
|
301
|
+
return ((_nodeDatum$data$entit5 = nodeDatum.data.entity) === null || _nodeDatum$data$entit5 === void 0 ? void 0 : _nodeDatum$data$entit5.link) ? "" : "none";
|
|
302
|
+
}).attr("xlink:href", (nodeDatum) => {
|
|
303
|
+
var _nodeDatum$data$entit6, _nodeDatum$data$entit7;
|
|
304
|
+
return (_nodeDatum$data$entit6 = (_nodeDatum$data$entit7 = nodeDatum.data.entity) === null || _nodeDatum$data$entit7 === void 0 ? void 0 : _nodeDatum$data$entit7.link) !== null && _nodeDatum$data$entit6 !== void 0 ? _nodeDatum$data$entit6 : "";
|
|
305
|
+
}).on("click", customOnClick(onEntityLinkClick, onClick, config)),
|
|
306
|
+
x: nodeWidth - 20,
|
|
307
|
+
y: 8
|
|
308
|
+
});
|
|
309
|
+
const nodeUpdate = nodeEnter.merge(node);
|
|
310
|
+
nodeUpdate.transition().duration(animationDuration).attr("transform", (nodeDatum) => {
|
|
311
|
+
return `translate(${nodeDatum.x},${nodeDatum.y})`;
|
|
312
|
+
});
|
|
313
|
+
nodeUpdate.select("rect.box").attr("fill", backgroundColor).attr("stroke", borderColor);
|
|
314
|
+
node.exit().transition().duration(animationDuration).attr("transform", () => `translate(${parentNode.x},${parentNode.y})`).remove();
|
|
315
|
+
svg.selectAll("path.link").data(links, (linkDatum) => {
|
|
316
|
+
var _linkDatum$target$dat;
|
|
317
|
+
return (_linkDatum$target$dat = linkDatum.target.data.id) !== null && _linkDatum$target$dat !== void 0 ? _linkDatum$target$dat : "";
|
|
318
|
+
});
|
|
319
|
+
[
|
|
320
|
+
{
|
|
321
|
+
cls: ENTITY_NAME_CLASS,
|
|
322
|
+
max: maxNameWordLength
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
cls: ENTITY_TITLE_CLASS,
|
|
326
|
+
max: maxTitleWordLength
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
cls: COUNTS_CLASS,
|
|
330
|
+
max: maxCountWordLength
|
|
331
|
+
}
|
|
332
|
+
].forEach(({ cls, max }) => {
|
|
333
|
+
svg.selectAll(`text.unedited.${cls}`).call(wrapText, nodeWidth - 12, cls === ENTITY_NAME_CLASS ? 3 : 2, max);
|
|
334
|
+
});
|
|
335
|
+
svg.selectAll(`text.${ENTITY_NAME_CLASS}`).append("svg:title").text((nodeDatum) => getName$1 ? getName$1(nodeDatum) : getName(nodeDatum));
|
|
336
|
+
svg.selectAll(`text.${ENTITY_TITLE_CLASS}`).append("svg:title").text((nodeDatum) => getTitle$1 ? getTitle$1(nodeDatum) : getTitle(nodeDatum));
|
|
337
|
+
svg.selectAll(`text.${COUNTS_CLASS}`).append("svg:title").text((nodeDatum) => getCount$1 ? getCount$1(nodeDatum) : getCount(nodeDatum));
|
|
338
|
+
renderLines(config);
|
|
339
|
+
nodes.forEach((nodeDatum) => {
|
|
340
|
+
nodeDatum.x0 = nodeDatum.x;
|
|
341
|
+
nodeDatum.y0 = nodeDatum.y;
|
|
342
|
+
});
|
|
343
|
+
let nodeLeftX = -70;
|
|
344
|
+
let nodeRightX = 70;
|
|
345
|
+
let nodeY = 200;
|
|
346
|
+
nodes.forEach((nodeDatum) => {
|
|
347
|
+
nodeLeftX = nodeDatum.x < nodeLeftX ? nodeDatum.x : nodeLeftX;
|
|
348
|
+
nodeRightX = nodeDatum.x > nodeRightX ? nodeDatum.x : nodeRightX;
|
|
349
|
+
nodeY = nodeDatum.y > nodeY ? nodeDatum.y : nodeY;
|
|
350
|
+
});
|
|
351
|
+
config.nodeRightX = nodeRightX;
|
|
352
|
+
config.nodeY = nodeY;
|
|
353
|
+
config.nodeLeftX = nodeLeftX * -1;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region src/chart/index.ts
|
|
358
|
+
function initializeOrgChart(options) {
|
|
359
|
+
if (!options.id) throw new Error("missing id for svg root");
|
|
360
|
+
const mergedConfig = {
|
|
361
|
+
...config,
|
|
362
|
+
...options
|
|
363
|
+
};
|
|
364
|
+
const { elem, nodeWidth, nodeHeight, nodeSpacing, shouldResize } = mergedConfig;
|
|
365
|
+
const treeData = mergedConfig.data;
|
|
366
|
+
const lineDepthY = nodeHeight + 40;
|
|
367
|
+
if (!elem) throw new Error("No root elem");
|
|
368
|
+
elem.innerHTML = "";
|
|
369
|
+
const elemWidth = elem.offsetWidth;
|
|
370
|
+
const elemHeight = elem.offsetHeight;
|
|
371
|
+
const treeRoot = (0, d3_hierarchy.hierarchy)(treeData, (d) => {
|
|
372
|
+
var _d$children;
|
|
373
|
+
return (_d$children = d.children) !== null && _d$children !== void 0 ? _d$children : void 0;
|
|
374
|
+
});
|
|
375
|
+
const treeMap = (0, d3_hierarchy.tree)().nodeSize([nodeWidth + nodeSpacing, nodeHeight + nodeSpacing]);
|
|
376
|
+
treeMap(treeRoot).descendants().slice(1).forEach((node) => collapse(node));
|
|
377
|
+
const svgroot = (0, d3_selection.select)(elem).append("svg").attr("id", "svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("xmlns:xlink", "http://www.w3.org/1999/xlink").attr("x", "0px").attr("y", "0px").attr("xml:space", "preserve").attr("viewBox", `0 0 ${elemWidth} ${elemHeight}`).attr("enable-background", ` new 0 0 ${elemWidth} ${elemHeight}`).attr("width", elemWidth).attr("height", elemHeight);
|
|
378
|
+
const centerPoint = elemWidth / 2 - nodeWidth / 2 + 15;
|
|
379
|
+
const svg = svgroot.append("g");
|
|
380
|
+
const config$1 = {
|
|
381
|
+
...mergedConfig,
|
|
382
|
+
data: treeData,
|
|
383
|
+
lineDepthY,
|
|
384
|
+
treeData,
|
|
385
|
+
tree: treeRoot,
|
|
386
|
+
treeMap,
|
|
387
|
+
svg,
|
|
388
|
+
svgroot,
|
|
389
|
+
render,
|
|
390
|
+
links: [],
|
|
391
|
+
nodes: [],
|
|
392
|
+
elemWidth,
|
|
393
|
+
elemHeight
|
|
394
|
+
};
|
|
395
|
+
const zoom = (0, d3_zoom.zoom)().scaleExtent([.1, 1.5]).duration(50).on("zoom", (zoomEvent) => {
|
|
396
|
+
svg.attr("transform", zoomEvent.transform.toString());
|
|
397
|
+
});
|
|
398
|
+
svgroot.call(zoom.transform, d3_zoom.zoomIdentity.translate(centerPoint, 48).scale(.8));
|
|
399
|
+
const zoomedRoot = svgroot.call(zoom);
|
|
400
|
+
if (config$1.disableCanvasMouseWheelZoom) zoomedRoot.on("wheel.zoom", null);
|
|
401
|
+
if (config$1.disableCanvasMouseMove) zoomedRoot.on("mousedown.zoom", null).on("touchstart.zoom", null).on("touchmove.zoom", null).on("touchend.zoom", null);
|
|
402
|
+
svgroot.append("svg:defs").append("clipPath").attr("id", "avatarClip").append("circle").attr("cx", 70).attr("cy", 32).attr("r", 24);
|
|
403
|
+
const filter = svgroot.append("svg:defs").append("svg:filter").attr("id", "boxShadow").attr("height", "150%").attr("width", "150%");
|
|
404
|
+
filter.append("svg:feGaussianBlur").attr("in", "SourceAlpha").attr("stdDeviation", 1).attr("result", "blurOut");
|
|
405
|
+
filter.append("svg:feOffset").attr("in", "blurOut").attr("dx", 0).attr("dy", 2).attr("result", "offsetOut");
|
|
406
|
+
const feMerge = filter.append("feMerge");
|
|
407
|
+
feMerge.append("feMergeNode").attr("in", "offsetOut");
|
|
408
|
+
feMerge.append("feMergeNode").attr("in", "SourceGraphic");
|
|
409
|
+
const resize = () => {
|
|
410
|
+
if (!elem) {
|
|
411
|
+
window.removeEventListener("resize", resize);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
svgroot.attr("width", elem.offsetWidth).attr("height", elem.offsetHeight);
|
|
415
|
+
};
|
|
416
|
+
if (shouldResize) window.addEventListener("resize", resize);
|
|
417
|
+
render(config$1);
|
|
418
|
+
return () => {
|
|
419
|
+
svgroot.remove();
|
|
420
|
+
if (shouldResize) window.removeEventListener("resize", resize);
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
//#endregion
|
|
425
|
+
//#region src/orgChart.tsx
|
|
426
|
+
const defaultId = "react-org-chart";
|
|
427
|
+
function OrgChartComponent(props) {
|
|
428
|
+
const { id = defaultId, disableCanvasMouseMove = false, disableCanvasMouseWheelZoom = false, tree } = props;
|
|
429
|
+
const anchor = (0, react.useRef)(null);
|
|
430
|
+
(0, react.useEffect)(() => {
|
|
431
|
+
const onDestroy = initializeOrgChart({
|
|
432
|
+
...config,
|
|
433
|
+
...props,
|
|
434
|
+
id: `#${id}`,
|
|
435
|
+
elem: anchor.current,
|
|
436
|
+
data: tree,
|
|
437
|
+
disableCanvasMouseMove,
|
|
438
|
+
disableCanvasMouseWheelZoom
|
|
439
|
+
});
|
|
440
|
+
return () => {
|
|
441
|
+
onDestroy();
|
|
442
|
+
};
|
|
443
|
+
}, [
|
|
444
|
+
props,
|
|
445
|
+
id,
|
|
446
|
+
tree,
|
|
447
|
+
disableCanvasMouseMove,
|
|
448
|
+
disableCanvasMouseWheelZoom
|
|
449
|
+
]);
|
|
450
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
451
|
+
id,
|
|
452
|
+
ref: anchor,
|
|
453
|
+
style: {
|
|
454
|
+
width: "100%",
|
|
455
|
+
height: "100%"
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
const OrgChart = (0, react.memo)(OrgChartComponent);
|
|
460
|
+
|
|
461
|
+
//#endregion
|
|
462
|
+
exports.OrgChart = OrgChart;
|
|
463
|
+
//# sourceMappingURL=index.js.map
|