@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/dist-web/index.js
DELETED
|
@@ -1,658 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { select } from 'd3-selection';
|
|
3
|
-
import { zoom, zoomIdentity } from 'd3-zoom';
|
|
4
|
-
import { hierarchy, tree } from 'd3-hierarchy';
|
|
5
|
-
import truncate from 'lodash.truncate';
|
|
6
|
-
import { line, curveLinear } from 'd3-shape';
|
|
7
|
-
|
|
8
|
-
function collapse(d) {
|
|
9
|
-
// Check if this node has children
|
|
10
|
-
if (d.children) {
|
|
11
|
-
d._children = d.children;
|
|
12
|
-
d.children = null;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let getTruncatedText = (text, maxWordLength) => truncate(text, { length: maxWordLength });
|
|
17
|
-
// One way of achieving text-wrapping capability in SVG
|
|
18
|
-
// Text is broken down to words, each word is added to a line and then the lines width is checked
|
|
19
|
-
// If the line width is less than the max we move to the next word, if more we add new line etc
|
|
20
|
-
// until the max number of lines is reached.
|
|
21
|
-
function wrapText(text, maxLineWidth, maxNumberOfLines = 3, maxWordLength = 17) {
|
|
22
|
-
if (!text._groups || !text._groups[0] || !text._groups[0].length) {
|
|
23
|
-
return '';
|
|
24
|
-
}
|
|
25
|
-
let editedClass = '';
|
|
26
|
-
text._groups[0].forEach(textNode => {
|
|
27
|
-
const text = select(textNode);
|
|
28
|
-
const x = text.attr('x');
|
|
29
|
-
const y = text.attr('y');
|
|
30
|
-
const dy = parseFloat(text.attr('dy'));
|
|
31
|
-
const lineHeight = 1.1;
|
|
32
|
-
const words = text.text().split(/\s+/).reverse();
|
|
33
|
-
let lineNumber = 0;
|
|
34
|
-
let curLineWidth;
|
|
35
|
-
let word;
|
|
36
|
-
let line = [];
|
|
37
|
-
let tspan = text
|
|
38
|
-
.text(null)
|
|
39
|
-
.append('tspan')
|
|
40
|
-
.style('text-anchor', 'middle')
|
|
41
|
-
.attr('x', x)
|
|
42
|
-
.attr('y', y)
|
|
43
|
-
.attr('dy', `${dy}em`);
|
|
44
|
-
while (lineNumber < maxNumberOfLines && words.length) {
|
|
45
|
-
word = words.pop();
|
|
46
|
-
line.push(word);
|
|
47
|
-
tspan.text(line.join(' '));
|
|
48
|
-
curLineWidth = tspan.node().getComputedTextLength();
|
|
49
|
-
if (curLineWidth > maxLineWidth) {
|
|
50
|
-
if (lineNumber + 1 === maxNumberOfLines) {
|
|
51
|
-
tspan.text(getTruncatedText(line.join(' '), maxWordLength));
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
line.pop();
|
|
56
|
-
tspan.text(line.join(' '));
|
|
57
|
-
line = [word];
|
|
58
|
-
tspan = text
|
|
59
|
-
.append('tspan')
|
|
60
|
-
.style('text-anchor', 'middle')
|
|
61
|
-
.attr('x', x)
|
|
62
|
-
.attr('y', y)
|
|
63
|
-
.attr('dy', ++lineNumber * lineHeight + dy + 'em')
|
|
64
|
-
.text(getTruncatedText(word, maxWordLength));
|
|
65
|
-
}
|
|
66
|
-
if (word.length > maxWordLength) {
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (!editedClass) {
|
|
72
|
-
editedClass = text.attr('class').replace(' unedited', '');
|
|
73
|
-
}
|
|
74
|
-
text.attr('class', editedClass);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const getName = data => data.data.entity && data.data.entity.name;
|
|
79
|
-
const getTitle = data => data.data.entity && data.data.entity.title;
|
|
80
|
-
const getCount = data => {
|
|
81
|
-
let children = (data.children || []).length || (data._children || []).length;
|
|
82
|
-
if (!children) {
|
|
83
|
-
return '';
|
|
84
|
-
}
|
|
85
|
-
return `Team (${children})`;
|
|
86
|
-
};
|
|
87
|
-
const getCursorForNode = data => data.data.children || data.data._children ? 'pointer' : 'default';
|
|
88
|
-
const customOnClick = (fn, onClick, config) => (event, data) => {
|
|
89
|
-
if (typeof fn === 'function') {
|
|
90
|
-
if (fn(data, event)) {
|
|
91
|
-
onClick(config);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
event.stopPropagation();
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const margin = 10;
|
|
100
|
-
function renderLines(config) {
|
|
101
|
-
const { svg, links, nodeWidth, nodeHeight, borderColor, sourceNode, treeData, animationDuration, } = config;
|
|
102
|
-
const parentNode = sourceNode || treeData;
|
|
103
|
-
// Select all the links to render the lines
|
|
104
|
-
const link = svg.selectAll('path.link').data(links, ({ source, target }) => {
|
|
105
|
-
return `${source.data.id}-${target.data.id}`;
|
|
106
|
-
});
|
|
107
|
-
// Define the angled line function
|
|
108
|
-
const angle = line()
|
|
109
|
-
.x(d => d.x)
|
|
110
|
-
.y(d => d.y)
|
|
111
|
-
.curve(curveLinear);
|
|
112
|
-
// Enter any new links at the parent's previous position.
|
|
113
|
-
var linkEnter = link
|
|
114
|
-
.enter()
|
|
115
|
-
.insert('path', 'g')
|
|
116
|
-
.attr('class', 'link')
|
|
117
|
-
.attr('fill', 'none')
|
|
118
|
-
.attr('stroke', borderColor)
|
|
119
|
-
.attr('stroke-opacity', 1)
|
|
120
|
-
.attr('stroke-width', 1.25)
|
|
121
|
-
.attr('d', d => {
|
|
122
|
-
const linePoints = [
|
|
123
|
-
{
|
|
124
|
-
x: d.source.x + parseInt(nodeWidth / 2, 10),
|
|
125
|
-
y: d.source.y + margin,
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
x: d.source.x + parseInt(nodeWidth / 2, 10),
|
|
129
|
-
y: d.source.y + margin,
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
x: d.source.x + parseInt(nodeWidth / 2, 10),
|
|
133
|
-
y: d.source.y + margin,
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
x: d.source.x + parseInt(nodeWidth / 2, 10),
|
|
137
|
-
y: d.source.y + margin,
|
|
138
|
-
},
|
|
139
|
-
];
|
|
140
|
-
return angle(linePoints);
|
|
141
|
-
});
|
|
142
|
-
var linkUpdate = linkEnter.merge(link);
|
|
143
|
-
// Transition links to their new position.
|
|
144
|
-
linkUpdate
|
|
145
|
-
.transition()
|
|
146
|
-
.duration(animationDuration)
|
|
147
|
-
.attr('d', d => {
|
|
148
|
-
const linePoints = [
|
|
149
|
-
{
|
|
150
|
-
x: d.source.x + parseInt(nodeWidth / 2, 10),
|
|
151
|
-
y: d.source.y + nodeHeight,
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
x: d.source.x + parseInt(nodeWidth / 2, 10),
|
|
155
|
-
y: d.target.y - margin,
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
x: d.target.x + parseInt(nodeWidth / 2, 10),
|
|
159
|
-
y: d.target.y - margin,
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
x: d.target.x + parseInt(nodeWidth / 2, 10),
|
|
163
|
-
y: d.target.y,
|
|
164
|
-
},
|
|
165
|
-
];
|
|
166
|
-
return angle(linePoints);
|
|
167
|
-
});
|
|
168
|
-
// Animate the existing links to the parent's new position
|
|
169
|
-
link
|
|
170
|
-
.exit()
|
|
171
|
-
.transition()
|
|
172
|
-
.duration(animationDuration)
|
|
173
|
-
.attr('d', () => {
|
|
174
|
-
const lineNode = config.sourceNode ? config.sourceNode : parentNode;
|
|
175
|
-
const linePoints = [
|
|
176
|
-
{
|
|
177
|
-
x: lineNode.x + parseInt(nodeWidth / 2, 10),
|
|
178
|
-
y: lineNode.y + nodeHeight + 2,
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
x: lineNode.x + parseInt(nodeWidth / 2, 10),
|
|
182
|
-
y: lineNode.y + nodeHeight + 2,
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
x: lineNode.x + parseInt(nodeWidth / 2, 10),
|
|
186
|
-
y: lineNode.y + nodeHeight + 2,
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
x: lineNode.x + parseInt(nodeWidth / 2, 10),
|
|
190
|
-
y: lineNode.y + nodeHeight + 2,
|
|
191
|
-
},
|
|
192
|
-
];
|
|
193
|
-
return angle(linePoints);
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function onClick(config) {
|
|
198
|
-
const { render } = config;
|
|
199
|
-
return (event, datum) => {
|
|
200
|
-
if (event.defaultPrevented) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
const link = event && event.target && event.target.closest('a');
|
|
204
|
-
if (link && link.href) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if (!datum.children && !datum._children) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (datum.children) {
|
|
211
|
-
// Collapse the children
|
|
212
|
-
config.callerNode = datum;
|
|
213
|
-
datum._children = datum.children;
|
|
214
|
-
datum.children = null;
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
// Expand the children
|
|
218
|
-
config.callerNode = null;
|
|
219
|
-
datum.children = datum._children;
|
|
220
|
-
datum._children = null;
|
|
221
|
-
}
|
|
222
|
-
// Pass in the clicked datum as the sourceNode which
|
|
223
|
-
// tells the child nodes where to animate in from
|
|
224
|
-
render({
|
|
225
|
-
...config,
|
|
226
|
-
sourceNode: datum,
|
|
227
|
-
});
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const iconLink = ({ svg, x = 5, y = 5 }) => {
|
|
232
|
-
const container = svg
|
|
233
|
-
.append('g')
|
|
234
|
-
.attr('stroke', 'none')
|
|
235
|
-
.attr('fill', 'none')
|
|
236
|
-
.style('cursor', 'pointer')
|
|
237
|
-
.append('g');
|
|
238
|
-
const icon = container
|
|
239
|
-
.append('g')
|
|
240
|
-
.attr('id', 'icon')
|
|
241
|
-
.attr('fill', '#9585A3')
|
|
242
|
-
.attr('transform', `translate(${x}, ${y})`);
|
|
243
|
-
const arrow = icon
|
|
244
|
-
.append('g')
|
|
245
|
-
.attr('id', 'arrow')
|
|
246
|
-
.attr('transform', 'translate(7.000000, 7.000000) scale(-1, 1) translate(-7.000000, -7.000000)');
|
|
247
|
-
arrow
|
|
248
|
-
.append('path')
|
|
249
|
-
.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');
|
|
250
|
-
arrow
|
|
251
|
-
.append('path')
|
|
252
|
-
// .attr('opacity', 0.7)
|
|
253
|
-
.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');
|
|
254
|
-
icon
|
|
255
|
-
.append('rect')
|
|
256
|
-
.attr('id', 'bounds')
|
|
257
|
-
.attr('x', 0)
|
|
258
|
-
.attr('y', 0)
|
|
259
|
-
.attr('width', 24)
|
|
260
|
-
.attr('height', 24)
|
|
261
|
-
.attr('fill', 'transparent');
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const CHART_NODE_CLASS = 'org-chart-node';
|
|
265
|
-
const ENTITY_LINK_CLASS = 'org-chart-entity-link';
|
|
266
|
-
const ENTITY_NAME_CLASS = 'org-chart-entity-name';
|
|
267
|
-
const ENTITY_TITLE_CLASS = 'org-chart-entity-title';
|
|
268
|
-
const COUNTS_CLASS = 'org-chart-counts';
|
|
269
|
-
function render(config) {
|
|
270
|
-
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;
|
|
271
|
-
// Compute the new tree layout.
|
|
272
|
-
const data = treeMap(tree);
|
|
273
|
-
const nodes = data.descendants();
|
|
274
|
-
const links = data.links();
|
|
275
|
-
// Collapse all of the children on initial load
|
|
276
|
-
// nodes.forEach(collapse);
|
|
277
|
-
config.links = links;
|
|
278
|
-
config.nodes = nodes;
|
|
279
|
-
// Normalize for fixed-depth.
|
|
280
|
-
nodes.forEach(function (d) {
|
|
281
|
-
d.y = d.depth * lineDepthY;
|
|
282
|
-
});
|
|
283
|
-
// Update the nodes
|
|
284
|
-
let node = svg.selectAll('g.' + CHART_NODE_CLASS).data(nodes, n => n.data.id);
|
|
285
|
-
let parentNode = sourceNode || nodes[0];
|
|
286
|
-
// Enter any new nodes at the parent's previous position.
|
|
287
|
-
const nodeEnter = node
|
|
288
|
-
.enter()
|
|
289
|
-
.append('g')
|
|
290
|
-
.attr('class', CHART_NODE_CLASS)
|
|
291
|
-
.attr('transform', () => {
|
|
292
|
-
return `translate(${parentNode.x0 || parentNode.x}, ${parentNode.y0 || parentNode.y})`;
|
|
293
|
-
})
|
|
294
|
-
.on('click', onClick(config));
|
|
295
|
-
// Entity Card Shadow
|
|
296
|
-
nodeEnter
|
|
297
|
-
.append('rect')
|
|
298
|
-
.attr('width', nodeWidth)
|
|
299
|
-
.attr('height', nodeHeight)
|
|
300
|
-
.attr('fill', backgroundColor)
|
|
301
|
-
.attr('stroke', borderColor)
|
|
302
|
-
.attr('rx', nodeBorderRadius)
|
|
303
|
-
.attr('ry', nodeBorderRadius)
|
|
304
|
-
.attr('fill-opacity', 0.05)
|
|
305
|
-
.attr('stroke-opacity', 0.025)
|
|
306
|
-
.attr('filter', 'url(#boxShadow)');
|
|
307
|
-
// Entity Card Container
|
|
308
|
-
nodeEnter
|
|
309
|
-
.append('rect')
|
|
310
|
-
.attr('width', nodeWidth)
|
|
311
|
-
.attr('height', nodeHeight)
|
|
312
|
-
.attr('id', d => d.data.id)
|
|
313
|
-
.attr('fill', backgroundColor)
|
|
314
|
-
.attr('stroke', borderColor)
|
|
315
|
-
.attr('rx', nodeBorderRadius)
|
|
316
|
-
.attr('ry', nodeBorderRadius)
|
|
317
|
-
.style('cursor', getCursorForNode);
|
|
318
|
-
let namePos = {
|
|
319
|
-
x: nodeWidth / 2,
|
|
320
|
-
y: nodePaddingY * 1.8 + avatarWidth,
|
|
321
|
-
};
|
|
322
|
-
let avatarPos = {
|
|
323
|
-
x: nodeWidth / 2 - avatarWidth / 2,
|
|
324
|
-
y: nodePaddingY / 2,
|
|
325
|
-
};
|
|
326
|
-
// Entity's Name
|
|
327
|
-
nodeEnter
|
|
328
|
-
.append('text')
|
|
329
|
-
.attr('class', `${ENTITY_NAME_CLASS} unedited`)
|
|
330
|
-
.attr('x', namePos.x)
|
|
331
|
-
.attr('y', namePos.y)
|
|
332
|
-
.attr('dy', '.3em')
|
|
333
|
-
.style('cursor', 'pointer')
|
|
334
|
-
.style('fill', nameColor)
|
|
335
|
-
.style('font-size', nameFontSize)
|
|
336
|
-
.text(d => (typeof getName$1 === 'function' ? getName$1(d) : getName(d)))
|
|
337
|
-
.on('click', customOnClick(onNameClick, onClick, config));
|
|
338
|
-
// Title
|
|
339
|
-
nodeEnter
|
|
340
|
-
.append('text')
|
|
341
|
-
.attr('class', `${ENTITY_TITLE_CLASS} unedited`)
|
|
342
|
-
.attr('x', nodeWidth / 2)
|
|
343
|
-
.attr('y', namePos.y + nodePaddingY + titleYTopDistance)
|
|
344
|
-
.attr('dy', '0.1em')
|
|
345
|
-
.style('font-size', titleFontSize)
|
|
346
|
-
.style('cursor', 'pointer')
|
|
347
|
-
.style('fill', titleColor)
|
|
348
|
-
.text(d => (typeof getTitle$1 === 'function' ? getTitle$1(d) : getTitle(d)));
|
|
349
|
-
// Count
|
|
350
|
-
nodeEnter
|
|
351
|
-
.append('text')
|
|
352
|
-
.attr('class', `${COUNTS_CLASS} unedited`)
|
|
353
|
-
.attr('x', nodeWidth / 2)
|
|
354
|
-
.attr('y', namePos.y + nodePaddingY + countYTopDistance)
|
|
355
|
-
.attr('dy', '.9em')
|
|
356
|
-
.style('font-size', countFontSize)
|
|
357
|
-
.style('font-weight', 400)
|
|
358
|
-
.style('cursor', 'pointer')
|
|
359
|
-
.style('fill', reportsColor)
|
|
360
|
-
.text(d => (typeof getCount$1 === 'function' ? getCount$1(d) : getCount(d)))
|
|
361
|
-
.on('click', customOnClick(onCountClick, onClick, config));
|
|
362
|
-
// Entity's Avatar
|
|
363
|
-
nodeEnter
|
|
364
|
-
.append('image')
|
|
365
|
-
.attr('id', d => `image-${d.data.id}`)
|
|
366
|
-
.attr('width', avatarWidth)
|
|
367
|
-
.attr('height', avatarWidth)
|
|
368
|
-
.attr('x', avatarPos.x)
|
|
369
|
-
.attr('y', avatarPos.y)
|
|
370
|
-
.attr('stroke', borderColor)
|
|
371
|
-
.attr('src', d => d.data.entity.avatar)
|
|
372
|
-
.attr('href', d => d.data.entity.avatar)
|
|
373
|
-
.attr('clip-path', 'url(#avatarClip)');
|
|
374
|
-
// Entity's Link
|
|
375
|
-
let nodeLink = nodeEnter
|
|
376
|
-
.append('a')
|
|
377
|
-
.attr('class', ENTITY_LINK_CLASS)
|
|
378
|
-
.attr('display', d => (d.data.entity.link ? '' : 'none'))
|
|
379
|
-
.attr('xlink:href', d => d.data.entity.link)
|
|
380
|
-
.on('click', customOnClick(onEntityLinkClick, onClick, config));
|
|
381
|
-
iconLink({
|
|
382
|
-
svg: nodeLink,
|
|
383
|
-
x: nodeWidth - 20,
|
|
384
|
-
y: 8,
|
|
385
|
-
});
|
|
386
|
-
var nodeUpdate = nodeEnter.merge(node);
|
|
387
|
-
// Transition nodes to their new position.
|
|
388
|
-
nodeUpdate
|
|
389
|
-
.transition()
|
|
390
|
-
.duration(animationDuration)
|
|
391
|
-
.attr('transform', d => {
|
|
392
|
-
return `translate(${d.x},${d.y})`;
|
|
393
|
-
});
|
|
394
|
-
nodeUpdate.select('rect.box').attr('fill', backgroundColor).attr('stroke', borderColor);
|
|
395
|
-
// Transition exiting nodes to the parent's new position.
|
|
396
|
-
node
|
|
397
|
-
.exit()
|
|
398
|
-
.transition()
|
|
399
|
-
.duration(animationDuration)
|
|
400
|
-
.attr('transform', () => `translate(${parentNode.x},${parentNode.y})`)
|
|
401
|
-
.remove();
|
|
402
|
-
// Update the links
|
|
403
|
-
svg.selectAll('path.link').data(links, function (d) {
|
|
404
|
-
return d.id;
|
|
405
|
-
});
|
|
406
|
-
[
|
|
407
|
-
{ cls: ENTITY_NAME_CLASS, max: maxNameWordLength },
|
|
408
|
-
{ cls: ENTITY_TITLE_CLASS, max: maxTitleWordLength },
|
|
409
|
-
{ cls: COUNTS_CLASS, max: maxCountWordLength },
|
|
410
|
-
].forEach(({ cls, max }) => {
|
|
411
|
-
// Svg.selectAll(`text.unedited.${cls}`).call(wrapText);
|
|
412
|
-
svg.selectAll(`text.unedited.${cls}`).call(wrapText, nodeWidth - 12, // Adjust with some padding
|
|
413
|
-
// name should wrap at 3 lines max
|
|
414
|
-
cls === ENTITY_NAME_CLASS ? 3 : 2, max);
|
|
415
|
-
});
|
|
416
|
-
// Add Tooltips
|
|
417
|
-
svg
|
|
418
|
-
.selectAll(`text.${ENTITY_NAME_CLASS}`)
|
|
419
|
-
.append('svg:title')
|
|
420
|
-
.text(d => (getName$1 ? getName$1(d) : getName(d)));
|
|
421
|
-
svg
|
|
422
|
-
.selectAll(`text.${ENTITY_TITLE_CLASS}`)
|
|
423
|
-
.append('svg:title')
|
|
424
|
-
.text(d => (getTitle$1 ? getTitle$1(d) : getTitle(d)));
|
|
425
|
-
svg
|
|
426
|
-
.selectAll(`text.${COUNTS_CLASS}`)
|
|
427
|
-
.append('svg:title')
|
|
428
|
-
.text(d => (getCount$1 ? getCount$1(d) : getCount(d)));
|
|
429
|
-
// Render lines connecting nodes
|
|
430
|
-
renderLines(config);
|
|
431
|
-
// Stash the old positions for transition.
|
|
432
|
-
nodes.forEach(d => {
|
|
433
|
-
d.x0 = d.x;
|
|
434
|
-
d.y0 = d.y;
|
|
435
|
-
});
|
|
436
|
-
var nodeLeftX = -70;
|
|
437
|
-
var nodeRightX = 70;
|
|
438
|
-
var nodeY = 200;
|
|
439
|
-
nodes.forEach(d => {
|
|
440
|
-
nodeLeftX = d.x < nodeLeftX ? d.x : nodeLeftX;
|
|
441
|
-
nodeRightX = d.x > nodeRightX ? d.x : nodeRightX;
|
|
442
|
-
nodeY = d.y > nodeY ? d.y : nodeY;
|
|
443
|
-
});
|
|
444
|
-
config.nodeRightX = nodeRightX;
|
|
445
|
-
config.nodeY = nodeY;
|
|
446
|
-
config.nodeLeftX = nodeLeftX * -1;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
function init(options) {
|
|
450
|
-
// Merge options with the default config
|
|
451
|
-
const config = {
|
|
452
|
-
...options,
|
|
453
|
-
treeData: options.data,
|
|
454
|
-
};
|
|
455
|
-
if (!config.id) {
|
|
456
|
-
throw new Error('missing id for svg root');
|
|
457
|
-
}
|
|
458
|
-
const { elem, treeData, nodeWidth, nodeHeight, nodeSpacing, shouldResize, disableCanvasMouseWheelZoom, disableCanvasMouseMove, } = config;
|
|
459
|
-
// Calculate how many pixel nodes to be spaced based on the
|
|
460
|
-
// type of line that needs to be rendered
|
|
461
|
-
config.lineDepthY = nodeHeight + 40;
|
|
462
|
-
if (!elem) {
|
|
463
|
-
throw new Error('No root elem');
|
|
464
|
-
}
|
|
465
|
-
// Reset in case there's any existing DOM
|
|
466
|
-
elem.innerHTML = '';
|
|
467
|
-
const elemWidth = elem.offsetWidth;
|
|
468
|
-
const elemHeight = elem.offsetHeight;
|
|
469
|
-
// Setup the d3 tree layout
|
|
470
|
-
config.tree = hierarchy(treeData, function (d) {
|
|
471
|
-
return d.children;
|
|
472
|
-
});
|
|
473
|
-
config.treeMap = tree(config.tree).nodeSize([nodeWidth + nodeSpacing, nodeHeight + nodeSpacing]);
|
|
474
|
-
// Collapse tree on load
|
|
475
|
-
config.treeMap(config.tree).descendants().slice(1).forEach(collapse);
|
|
476
|
-
// Calculate width of a node with expanded children
|
|
477
|
-
// const childrenWidth = parseInt((treeData.children.length * nodeWidth) / 2)
|
|
478
|
-
// <svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" xml:space="preserve" viewBox="0 0 193 260" enable-background=" new 0 0 193 260" height="260" width="193"
|
|
479
|
-
// Add svg root for d3
|
|
480
|
-
const svgroot = select(elem)
|
|
481
|
-
.append('svg')
|
|
482
|
-
.attr('id', 'svg')
|
|
483
|
-
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
|
484
|
-
.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink')
|
|
485
|
-
.attr('x', '0px')
|
|
486
|
-
.attr('y', '0px')
|
|
487
|
-
.attr('xml:space', 'preserve')
|
|
488
|
-
.attr('viewBox', `0 0 ${elemWidth} ${elemHeight}`)
|
|
489
|
-
.attr('enable-background', ` new 0 0 ${elemWidth} ${elemHeight}`)
|
|
490
|
-
.attr('width', elemWidth)
|
|
491
|
-
.attr('height', elemHeight);
|
|
492
|
-
// Graph center point
|
|
493
|
-
const centerPoint = elemWidth / 2 - nodeWidth / 2 + 15;
|
|
494
|
-
// Add our base svg group to transform when a user zooms/pans
|
|
495
|
-
const svg = svgroot.append('g');
|
|
496
|
-
// Connect core variables to config so that they can be
|
|
497
|
-
// used in internal rendering functions
|
|
498
|
-
config.svg = svg;
|
|
499
|
-
config.svgroot = svgroot;
|
|
500
|
-
config.elemWidth = elemWidth;
|
|
501
|
-
config.elemHeight = elemHeight;
|
|
502
|
-
config.render = render;
|
|
503
|
-
// Defined zoom behavior
|
|
504
|
-
const zoom$1 = zoom()
|
|
505
|
-
.scaleExtent([0.1, 1.5])
|
|
506
|
-
.duration(50)
|
|
507
|
-
.on('zoom', zoomEvent => {
|
|
508
|
-
svg.attr('transform', () => {
|
|
509
|
-
return zoomEvent.transform;
|
|
510
|
-
});
|
|
511
|
-
});
|
|
512
|
-
svgroot.call(zoom$1.transform, zoomIdentity.translate(centerPoint, 48).scale(0.8));
|
|
513
|
-
const zoomedRoot = svgroot.call(zoom$1);
|
|
514
|
-
// Disable the Mouse Wheel Zooming
|
|
515
|
-
if (disableCanvasMouseWheelZoom) {
|
|
516
|
-
zoomedRoot.on('wheel.zoom', null);
|
|
517
|
-
}
|
|
518
|
-
// Disable the Mouse Wheel Canvas Content Moving
|
|
519
|
-
if (disableCanvasMouseMove) {
|
|
520
|
-
zoomedRoot
|
|
521
|
-
.on('mousedown.zoom', null)
|
|
522
|
-
.on('touchstart.zoom', null)
|
|
523
|
-
.on('touchmove.zoom', null)
|
|
524
|
-
.on('touchend.zoom', null);
|
|
525
|
-
}
|
|
526
|
-
// Add avatar clip path
|
|
527
|
-
const defs = svgroot.append('svg:defs');
|
|
528
|
-
defs
|
|
529
|
-
.append('clipPath')
|
|
530
|
-
.attr('id', 'avatarClip')
|
|
531
|
-
.append('circle')
|
|
532
|
-
.attr('cx', 70)
|
|
533
|
-
.attr('cy', 32)
|
|
534
|
-
.attr('r', 24);
|
|
535
|
-
// Add boxshadow
|
|
536
|
-
const filter = svgroot
|
|
537
|
-
.append('svg:defs')
|
|
538
|
-
.append('svg:filter')
|
|
539
|
-
.attr('id', 'boxShadow')
|
|
540
|
-
.attr('height', '150%')
|
|
541
|
-
.attr('width', '150%');
|
|
542
|
-
filter
|
|
543
|
-
.append('svg:feGaussianBlur')
|
|
544
|
-
.attr('in', 'SourceAlpha')
|
|
545
|
-
.attr('stdDeviation', 1) // blur amount
|
|
546
|
-
.attr('result', 'blurOut');
|
|
547
|
-
filter
|
|
548
|
-
.append('svg:feOffset')
|
|
549
|
-
.attr('in', 'blurOut')
|
|
550
|
-
.attr('dx', 0)
|
|
551
|
-
.attr('dy', 2)
|
|
552
|
-
.attr('result', 'offsetOut');
|
|
553
|
-
const feMerge = filter.append('feMerge');
|
|
554
|
-
feMerge.append('feMergeNode').attr('in', 'offsetOut');
|
|
555
|
-
feMerge.append('feMergeNode').attr('in', 'SourceGraphic');
|
|
556
|
-
// Add listener for when the browser or parent node resizes
|
|
557
|
-
const resize = () => {
|
|
558
|
-
if (!elem) {
|
|
559
|
-
window.removeEventListener('resize', resize);
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
svgroot.attr('width', elem.offsetWidth).attr('height', elem.offsetHeight);
|
|
563
|
-
};
|
|
564
|
-
if (shouldResize) {
|
|
565
|
-
window.addEventListener('resize', resize);
|
|
566
|
-
}
|
|
567
|
-
// Start initial render
|
|
568
|
-
render(config);
|
|
569
|
-
// return OnDestroy fn
|
|
570
|
-
return () => {
|
|
571
|
-
svgroot.remove();
|
|
572
|
-
if (shouldResize) {
|
|
573
|
-
window.removeEventListener('resize', resize);
|
|
574
|
-
}
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const animationDuration = 350;
|
|
579
|
-
const shouldResize = true;
|
|
580
|
-
// Nodes
|
|
581
|
-
const nodeWidth = 140;
|
|
582
|
-
const nodeHeight = 190;
|
|
583
|
-
const nodeSpacing = 12;
|
|
584
|
-
const nodePaddingX = 16;
|
|
585
|
-
const nodePaddingY = 16;
|
|
586
|
-
const avatarWidth = 48;
|
|
587
|
-
const nodeBorderRadius = 4;
|
|
588
|
-
// Lines
|
|
589
|
-
/* Height of the line for child nodes */
|
|
590
|
-
const lineDepthY = 120;
|
|
591
|
-
// Colors
|
|
592
|
-
const backgroundColor = '#fff';
|
|
593
|
-
// Theme.borderlight
|
|
594
|
-
const borderColor = '#E7E1EC';
|
|
595
|
-
// Theme.gray800
|
|
596
|
-
const nameColor = '#302839';
|
|
597
|
-
// Theme.gray600
|
|
598
|
-
const titleColor = '#645574';
|
|
599
|
-
const reportsColor = '#92A0AD';
|
|
600
|
-
const config = {
|
|
601
|
-
animationDuration,
|
|
602
|
-
nodeWidth,
|
|
603
|
-
nodeHeight,
|
|
604
|
-
nodeSpacing,
|
|
605
|
-
nodePaddingX,
|
|
606
|
-
nodePaddingY,
|
|
607
|
-
nodeBorderRadius,
|
|
608
|
-
avatarWidth,
|
|
609
|
-
lineDepthY,
|
|
610
|
-
backgroundColor,
|
|
611
|
-
borderColor,
|
|
612
|
-
nameColor,
|
|
613
|
-
titleColor,
|
|
614
|
-
reportsColor,
|
|
615
|
-
shouldResize,
|
|
616
|
-
nameFontSize: 14,
|
|
617
|
-
titleFontSize: 13,
|
|
618
|
-
titleYTopDistance: 25,
|
|
619
|
-
countFontSize: 14,
|
|
620
|
-
countYTopDistance: 72,
|
|
621
|
-
maxNameWordLength: 16,
|
|
622
|
-
maxTitleWordLength: 17,
|
|
623
|
-
maxCountWordLength: 17,
|
|
624
|
-
};
|
|
625
|
-
|
|
626
|
-
const defaultId = 'react-org-chart';
|
|
627
|
-
class OrgChart extends React.PureComponent {
|
|
628
|
-
constructor() {
|
|
629
|
-
super(...arguments);
|
|
630
|
-
this.anchor = React.createRef();
|
|
631
|
-
}
|
|
632
|
-
componentDidMount() {
|
|
633
|
-
const { id = defaultId, disableCanvasMouseMove = false, disableCanvasMouseWheelZoom = false, tree, ...options } = this.props;
|
|
634
|
-
this.onDestroy = init({
|
|
635
|
-
...config,
|
|
636
|
-
id: `#${id}`,
|
|
637
|
-
elem: this.anchor.current,
|
|
638
|
-
data: tree,
|
|
639
|
-
disableCanvasMouseMove,
|
|
640
|
-
disableCanvasMouseWheelZoom,
|
|
641
|
-
...options,
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
componentWillUnmount() {
|
|
645
|
-
this.onDestroy();
|
|
646
|
-
}
|
|
647
|
-
render() {
|
|
648
|
-
const { id = defaultId } = this.props;
|
|
649
|
-
return React.createElement('div', {
|
|
650
|
-
id,
|
|
651
|
-
ref: this.anchor,
|
|
652
|
-
style: { width: '100%', height: '100%' },
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
export { OrgChart };
|
|
658
|
-
//# sourceMappingURL=index.js.map
|