@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.
@@ -1,188 +0,0 @@
1
- import * as helpers from '../utils/index';
2
- import { renderLines } from './renderLines';
3
- import { onClick } from './onClick';
4
- import { iconLink } from './components/iconLink';
5
- const CHART_NODE_CLASS = 'org-chart-node';
6
- const ENTITY_LINK_CLASS = 'org-chart-entity-link';
7
- const ENTITY_NAME_CLASS = 'org-chart-entity-name';
8
- const ENTITY_TITLE_CLASS = 'org-chart-entity-title';
9
- const COUNTS_CLASS = 'org-chart-counts';
10
- export function render(config) {
11
- 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, getTitle, getCount, onNameClick, onCountClick, treeMap, } = config;
12
- // Compute the new tree layout.
13
- const data = treeMap(tree);
14
- const nodes = data.descendants();
15
- const links = data.links();
16
- // Collapse all of the children on initial load
17
- // nodes.forEach(collapse);
18
- config.links = links;
19
- config.nodes = nodes;
20
- // Normalize for fixed-depth.
21
- nodes.forEach(function (d) {
22
- d.y = d.depth * lineDepthY;
23
- });
24
- // Update the nodes
25
- let node = svg.selectAll('g.' + CHART_NODE_CLASS).data(nodes, n => n.data.id);
26
- let parentNode = sourceNode || nodes[0];
27
- // Enter any new nodes at the parent's previous position.
28
- const nodeEnter = node
29
- .enter()
30
- .append('g')
31
- .attr('class', CHART_NODE_CLASS)
32
- .attr('transform', () => {
33
- return `translate(${parentNode.x0 || parentNode.x}, ${parentNode.y0 || parentNode.y})`;
34
- })
35
- .on('click', onClick(config));
36
- // Entity Card Shadow
37
- nodeEnter
38
- .append('rect')
39
- .attr('width', nodeWidth)
40
- .attr('height', nodeHeight)
41
- .attr('fill', backgroundColor)
42
- .attr('stroke', borderColor)
43
- .attr('rx', nodeBorderRadius)
44
- .attr('ry', nodeBorderRadius)
45
- .attr('fill-opacity', 0.05)
46
- .attr('stroke-opacity', 0.025)
47
- .attr('filter', 'url(#boxShadow)');
48
- // Entity Card Container
49
- nodeEnter
50
- .append('rect')
51
- .attr('width', nodeWidth)
52
- .attr('height', nodeHeight)
53
- .attr('id', d => d.data.id)
54
- .attr('fill', backgroundColor)
55
- .attr('stroke', borderColor)
56
- .attr('rx', nodeBorderRadius)
57
- .attr('ry', nodeBorderRadius)
58
- .style('cursor', helpers.getCursorForNode);
59
- let namePos = {
60
- x: nodeWidth / 2,
61
- y: nodePaddingY * 1.8 + avatarWidth,
62
- };
63
- let avatarPos = {
64
- x: nodeWidth / 2 - avatarWidth / 2,
65
- y: nodePaddingY / 2,
66
- };
67
- // Entity's Name
68
- nodeEnter
69
- .append('text')
70
- .attr('class', `${ENTITY_NAME_CLASS} unedited`)
71
- .attr('x', namePos.x)
72
- .attr('y', namePos.y)
73
- .attr('dy', '.3em')
74
- .style('cursor', 'pointer')
75
- .style('fill', nameColor)
76
- .style('font-size', nameFontSize)
77
- .text(d => (typeof getName === 'function' ? getName(d) : helpers.getName(d)))
78
- .on('click', helpers.customOnClick(onNameClick, onClick, config));
79
- // Title
80
- nodeEnter
81
- .append('text')
82
- .attr('class', `${ENTITY_TITLE_CLASS} unedited`)
83
- .attr('x', nodeWidth / 2)
84
- .attr('y', namePos.y + nodePaddingY + titleYTopDistance)
85
- .attr('dy', '0.1em')
86
- .style('font-size', titleFontSize)
87
- .style('cursor', 'pointer')
88
- .style('fill', titleColor)
89
- .text(d => (typeof getTitle === 'function' ? getTitle(d) : helpers.getTitle(d)));
90
- // Count
91
- nodeEnter
92
- .append('text')
93
- .attr('class', `${COUNTS_CLASS} unedited`)
94
- .attr('x', nodeWidth / 2)
95
- .attr('y', namePos.y + nodePaddingY + countYTopDistance)
96
- .attr('dy', '.9em')
97
- .style('font-size', countFontSize)
98
- .style('font-weight', 400)
99
- .style('cursor', 'pointer')
100
- .style('fill', reportsColor)
101
- .text(d => (typeof getCount === 'function' ? getCount(d) : helpers.getCount(d)))
102
- .on('click', helpers.customOnClick(onCountClick, onClick, config));
103
- // Entity's Avatar
104
- nodeEnter
105
- .append('image')
106
- .attr('id', d => `image-${d.data.id}`)
107
- .attr('width', avatarWidth)
108
- .attr('height', avatarWidth)
109
- .attr('x', avatarPos.x)
110
- .attr('y', avatarPos.y)
111
- .attr('stroke', borderColor)
112
- .attr('src', d => d.data.entity.avatar)
113
- .attr('href', d => d.data.entity.avatar)
114
- .attr('clip-path', 'url(#avatarClip)');
115
- // Entity's Link
116
- let nodeLink = nodeEnter
117
- .append('a')
118
- .attr('class', ENTITY_LINK_CLASS)
119
- .attr('display', d => (d.data.entity.link ? '' : 'none'))
120
- .attr('xlink:href', d => d.data.entity.link)
121
- .on('click', helpers.customOnClick(onEntityLinkClick, onClick, config));
122
- iconLink({
123
- svg: nodeLink,
124
- x: nodeWidth - 20,
125
- y: 8,
126
- });
127
- var nodeUpdate = nodeEnter.merge(node);
128
- // Transition nodes to their new position.
129
- nodeUpdate
130
- .transition()
131
- .duration(animationDuration)
132
- .attr('transform', d => {
133
- return `translate(${d.x},${d.y})`;
134
- });
135
- nodeUpdate.select('rect.box').attr('fill', backgroundColor).attr('stroke', borderColor);
136
- // Transition exiting nodes to the parent's new position.
137
- node
138
- .exit()
139
- .transition()
140
- .duration(animationDuration)
141
- .attr('transform', () => `translate(${parentNode.x},${parentNode.y})`)
142
- .remove();
143
- // Update the links
144
- svg.selectAll('path.link').data(links, function (d) {
145
- return d.id;
146
- });
147
- [
148
- { cls: ENTITY_NAME_CLASS, max: maxNameWordLength },
149
- { cls: ENTITY_TITLE_CLASS, max: maxTitleWordLength },
150
- { cls: COUNTS_CLASS, max: maxCountWordLength },
151
- ].forEach(({ cls, max }) => {
152
- // Svg.selectAll(`text.unedited.${cls}`).call(wrapText);
153
- svg.selectAll(`text.unedited.${cls}`).call(helpers.wrapText, nodeWidth - 12, // Adjust with some padding
154
- // name should wrap at 3 lines max
155
- cls === ENTITY_NAME_CLASS ? 3 : 2, max);
156
- });
157
- // Add Tooltips
158
- svg
159
- .selectAll(`text.${ENTITY_NAME_CLASS}`)
160
- .append('svg:title')
161
- .text(d => (getName ? getName(d) : helpers.getName(d)));
162
- svg
163
- .selectAll(`text.${ENTITY_TITLE_CLASS}`)
164
- .append('svg:title')
165
- .text(d => (getTitle ? getTitle(d) : helpers.getTitle(d)));
166
- svg
167
- .selectAll(`text.${COUNTS_CLASS}`)
168
- .append('svg:title')
169
- .text(d => (getCount ? getCount(d) : helpers.getCount(d)));
170
- // Render lines connecting nodes
171
- renderLines(config);
172
- // Stash the old positions for transition.
173
- nodes.forEach(d => {
174
- d.x0 = d.x;
175
- d.y0 = d.y;
176
- });
177
- var nodeLeftX = -70;
178
- var nodeRightX = 70;
179
- var nodeY = 200;
180
- nodes.forEach(d => {
181
- nodeLeftX = d.x < nodeLeftX ? d.x : nodeLeftX;
182
- nodeRightX = d.x > nodeRightX ? d.x : nodeRightX;
183
- nodeY = d.y > nodeY ? d.y : nodeY;
184
- });
185
- config.nodeRightX = nodeRightX;
186
- config.nodeY = nodeY;
187
- config.nodeLeftX = nodeLeftX * -1;
188
- }
@@ -1,98 +0,0 @@
1
- import { line, curveLinear } from 'd3-shape';
2
- const margin = 10;
3
- export function renderLines(config) {
4
- const { svg, links, nodeWidth, nodeHeight, borderColor, sourceNode, treeData, animationDuration, } = config;
5
- const parentNode = sourceNode || treeData;
6
- // Select all the links to render the lines
7
- const link = svg.selectAll('path.link').data(links, ({ source, target }) => {
8
- return `${source.data.id}-${target.data.id}`;
9
- });
10
- // Define the angled line function
11
- const angle = line()
12
- .x(d => d.x)
13
- .y(d => d.y)
14
- .curve(curveLinear);
15
- // Enter any new links at the parent's previous position.
16
- var linkEnter = link
17
- .enter()
18
- .insert('path', 'g')
19
- .attr('class', 'link')
20
- .attr('fill', 'none')
21
- .attr('stroke', borderColor)
22
- .attr('stroke-opacity', 1)
23
- .attr('stroke-width', 1.25)
24
- .attr('d', d => {
25
- const linePoints = [
26
- {
27
- x: d.source.x + parseInt(nodeWidth / 2, 10),
28
- y: d.source.y + margin,
29
- },
30
- {
31
- x: d.source.x + parseInt(nodeWidth / 2, 10),
32
- y: d.source.y + margin,
33
- },
34
- {
35
- x: d.source.x + parseInt(nodeWidth / 2, 10),
36
- y: d.source.y + margin,
37
- },
38
- {
39
- x: d.source.x + parseInt(nodeWidth / 2, 10),
40
- y: d.source.y + margin,
41
- },
42
- ];
43
- return angle(linePoints);
44
- });
45
- var linkUpdate = linkEnter.merge(link);
46
- // Transition links to their new position.
47
- linkUpdate
48
- .transition()
49
- .duration(animationDuration)
50
- .attr('d', d => {
51
- const linePoints = [
52
- {
53
- x: d.source.x + parseInt(nodeWidth / 2, 10),
54
- y: d.source.y + nodeHeight,
55
- },
56
- {
57
- x: d.source.x + parseInt(nodeWidth / 2, 10),
58
- y: d.target.y - margin,
59
- },
60
- {
61
- x: d.target.x + parseInt(nodeWidth / 2, 10),
62
- y: d.target.y - margin,
63
- },
64
- {
65
- x: d.target.x + parseInt(nodeWidth / 2, 10),
66
- y: d.target.y,
67
- },
68
- ];
69
- return angle(linePoints);
70
- });
71
- // Animate the existing links to the parent's new position
72
- link
73
- .exit()
74
- .transition()
75
- .duration(animationDuration)
76
- .attr('d', () => {
77
- const lineNode = config.sourceNode ? config.sourceNode : parentNode;
78
- const linePoints = [
79
- {
80
- x: lineNode.x + parseInt(nodeWidth / 2, 10),
81
- y: lineNode.y + nodeHeight + 2,
82
- },
83
- {
84
- x: lineNode.x + parseInt(nodeWidth / 2, 10),
85
- y: lineNode.y + nodeHeight + 2,
86
- },
87
- {
88
- x: lineNode.x + parseInt(nodeWidth / 2, 10),
89
- y: lineNode.y + nodeHeight + 2,
90
- },
91
- {
92
- x: lineNode.x + parseInt(nodeWidth / 2, 10),
93
- y: lineNode.y + nodeHeight + 2,
94
- },
95
- ];
96
- return angle(linePoints);
97
- });
98
- }
package/dist-src/index.js DELETED
@@ -1 +0,0 @@
1
- export * from './orgChart';
@@ -1,33 +0,0 @@
1
- import React from 'react';
2
- import { init } from './chart/index';
3
- import { config as defaultConfig } from './chart/config';
4
- const defaultId = 'react-org-chart';
5
- export class OrgChart extends React.PureComponent {
6
- constructor() {
7
- super(...arguments);
8
- this.anchor = React.createRef();
9
- }
10
- componentDidMount() {
11
- const { id = defaultId, disableCanvasMouseMove = false, disableCanvasMouseWheelZoom = false, tree, ...options } = this.props;
12
- this.onDestroy = init({
13
- ...defaultConfig,
14
- id: `#${id}`,
15
- elem: this.anchor.current,
16
- data: tree,
17
- disableCanvasMouseMove,
18
- disableCanvasMouseWheelZoom,
19
- ...options,
20
- });
21
- }
22
- componentWillUnmount() {
23
- this.onDestroy();
24
- }
25
- render() {
26
- const { id = defaultId } = this.props;
27
- return React.createElement('div', {
28
- id,
29
- ref: this.anchor,
30
- style: { width: '100%', height: '100%' },
31
- });
32
- }
33
- }
@@ -1,7 +0,0 @@
1
- export function collapse(d) {
2
- // Check if this node has children
3
- if (d.children) {
4
- d._children = d.children;
5
- d.children = null;
6
- }
7
- }
@@ -1,20 +0,0 @@
1
- export const getName = data => data.data.entity && data.data.entity.name;
2
- export const getTitle = data => data.data.entity && data.data.entity.title;
3
- export const getCount = data => {
4
- let children = (data.children || []).length || (data._children || []).length;
5
- if (!children) {
6
- return '';
7
- }
8
- return `Team (${children})`;
9
- };
10
- export const getCursorForNode = data => data.data.children || data.data._children ? 'pointer' : 'default';
11
- export const customOnClick = (fn, onClick, config) => (event, data) => {
12
- if (typeof fn === 'function') {
13
- if (fn(data, event)) {
14
- onClick(config);
15
- }
16
- else {
17
- event.stopPropagation();
18
- }
19
- }
20
- };
@@ -1,3 +0,0 @@
1
- export * from './collapse';
2
- export * from './wrapText';
3
- export * from './helpers';
@@ -1,63 +0,0 @@
1
- import { select } from 'd3-selection';
2
- import truncate from 'lodash.truncate';
3
- let getTruncatedText = (text, maxWordLength) => truncate(text, { length: maxWordLength });
4
- // One way of achieving text-wrapping capability in SVG
5
- // Text is broken down to words, each word is added to a line and then the lines width is checked
6
- // If the line width is less than the max we move to the next word, if more we add new line etc
7
- // until the max number of lines is reached.
8
- export function wrapText(text, maxLineWidth, maxNumberOfLines = 3, maxWordLength = 17) {
9
- if (!text._groups || !text._groups[0] || !text._groups[0].length) {
10
- return '';
11
- }
12
- let editedClass = '';
13
- text._groups[0].forEach(textNode => {
14
- const text = select(textNode);
15
- const x = text.attr('x');
16
- const y = text.attr('y');
17
- const dy = parseFloat(text.attr('dy'));
18
- const lineHeight = 1.1;
19
- const words = text.text().split(/\s+/).reverse();
20
- let lineNumber = 0;
21
- let curLineWidth;
22
- let word;
23
- let line = [];
24
- let tspan = text
25
- .text(null)
26
- .append('tspan')
27
- .style('text-anchor', 'middle')
28
- .attr('x', x)
29
- .attr('y', y)
30
- .attr('dy', `${dy}em`);
31
- while (lineNumber < maxNumberOfLines && words.length) {
32
- word = words.pop();
33
- line.push(word);
34
- tspan.text(line.join(' '));
35
- curLineWidth = tspan.node().getComputedTextLength();
36
- if (curLineWidth > maxLineWidth) {
37
- if (lineNumber + 1 === maxNumberOfLines) {
38
- tspan.text(getTruncatedText(line.join(' '), maxWordLength));
39
- break;
40
- }
41
- else {
42
- line.pop();
43
- tspan.text(line.join(' '));
44
- line = [word];
45
- tspan = text
46
- .append('tspan')
47
- .style('text-anchor', 'middle')
48
- .attr('x', x)
49
- .attr('y', y)
50
- .attr('dy', ++lineNumber * lineHeight + dy + 'em')
51
- .text(getTruncatedText(word, maxWordLength));
52
- }
53
- if (word.length > maxWordLength) {
54
- break;
55
- }
56
- }
57
- }
58
- if (!editedClass) {
59
- editedClass = text.attr('class').replace(' unedited', '');
60
- }
61
- text.attr('class', editedClass);
62
- });
63
- }
@@ -1,5 +0,0 @@
1
- export function iconLink({ svg, x, y }: {
2
- svg: any;
3
- x?: number | undefined;
4
- y?: number | undefined;
5
- }): void;
@@ -1,32 +0,0 @@
1
- export interface Config {
2
- animationDuration: number;
3
- nodeWidth: number;
4
- nodeHeight: number;
5
- nodeSpacing: number;
6
- nodePaddingX: number;
7
- nodePaddingY: number;
8
- nodeBorderRadius: number;
9
- avatarWidth: number;
10
- lineDepthY: number;
11
- backgroundColor: string;
12
- borderColor: string;
13
- nameColor: string;
14
- titleColor: string;
15
- reportsColor: string;
16
- shouldResize: boolean;
17
- nameFontSize: number;
18
- titleFontSize: number;
19
- titleYTopDistance: number;
20
- countFontSize: number;
21
- countYTopDistance: number;
22
- maxNameWordLength: number;
23
- maxTitleWordLength: number;
24
- maxCountWordLength: number;
25
- onEntityLinkClick?: (data: any, event: any) => void;
26
- onNameClick?: (data: any, event: any) => void;
27
- onCountClick?: (data: any, event: any) => void;
28
- getName?: (data: any) => string;
29
- getTitle?: (data: any) => string;
30
- getCount?: (data: any) => string;
31
- }
32
- export declare const config: Config;
@@ -1 +0,0 @@
1
- export function init(options: any): () => void;
@@ -1 +0,0 @@
1
- export function onClick(config: any): (event: any, datum: any) => void;
@@ -1 +0,0 @@
1
- export function render(config: any): void;
@@ -1 +0,0 @@
1
- export function renderLines(config: any): void;
@@ -1 +0,0 @@
1
- export * from './orgChart';
@@ -1,36 +0,0 @@
1
- import React from 'react';
2
- import { Config } from './chart/config';
3
- export interface TreeItem {
4
- [key: string]: TreeItem[] | any;
5
- id?: string | number;
6
- parentId?: string | number | null;
7
- children?: TreeItem[] | null;
8
- entity?: {
9
- [key: string]: any;
10
- avatar?: string;
11
- link?: string;
12
- name?: string;
13
- title?: string;
14
- };
15
- }
16
- declare type Props = Partial<Config> & {
17
- id?: string;
18
- disableCanvasMouseMove?: boolean;
19
- disableCanvasMouseWheelZoom?: boolean;
20
- tree: TreeItem[] | TreeItem;
21
- };
22
- export declare class OrgChart extends React.PureComponent<Props> {
23
- anchor: React.RefObject<unknown>;
24
- onDestroy: () => void;
25
- componentDidMount(): void;
26
- componentWillUnmount(): void;
27
- render(): React.ReactElement<{
28
- id: string;
29
- ref: React.RefObject<unknown>;
30
- style: {
31
- width: string;
32
- height: string;
33
- };
34
- }, string | ((props: any) => React.ReactElement<any, string | any | (new (props: any) => React.Component<any, any, any>)> | null) | (new (props: any) => React.Component<any, any, any>)>;
35
- }
36
- export {};
@@ -1 +0,0 @@
1
- export function collapse(d: any): void;
@@ -1,5 +0,0 @@
1
- export function getName(data: any): any;
2
- export function getTitle(data: any): any;
3
- export function getCount(data: any): string;
4
- export function getCursorForNode(data: any): "pointer" | "default";
5
- export function customOnClick(fn: any, onClick: any, config: any): (event: any, data: any) => void;
@@ -1,3 +0,0 @@
1
- export * from "./collapse";
2
- export * from "./wrapText";
3
- export * from "./helpers";
@@ -1 +0,0 @@
1
- export function wrapText(text: any, maxLineWidth: any, maxNumberOfLines?: number, maxWordLength?: number): "" | undefined;