react-d3-tree
Version:
React component to create interactive D3 tree hierarchies
118 lines (117 loc) • 5.01 kB
JavaScript
import React from 'react';
import { select } from 'd3-selection';
import DefaultNodeElement from './DefaultNodeElement.js';
export default class Node extends React.Component {
constructor() {
super(...arguments);
this.nodeRef = null;
this.state = {
transform: this.setTransform(this.props.position, this.props.parent, this.props.orientation, true),
initialStyle: {
opacity: 0,
},
wasClicked: false,
};
this.shouldNodeTransform = (ownProps, nextProps, ownState, nextState) => nextProps.subscriptions !== ownProps.subscriptions ||
nextProps.position.x !== ownProps.position.x ||
nextProps.position.y !== ownProps.position.y ||
nextProps.orientation !== ownProps.orientation ||
nextState.wasClicked !== ownState.wasClicked;
// TODO: needs tests
this.renderNodeElement = () => {
const { data, hierarchyPointNode, renderCustomNodeElement } = this.props;
const renderNode = typeof renderCustomNodeElement === 'function' ? renderCustomNodeElement : DefaultNodeElement;
const nodeProps = {
hierarchyPointNode: hierarchyPointNode,
nodeDatum: data,
toggleNode: this.handleNodeToggle,
onNodeClick: this.handleOnClick,
onNodeMouseOver: this.handleOnMouseOver,
onNodeMouseOut: this.handleOnMouseOut,
addChildren: this.handleAddChildren,
};
return renderNode(nodeProps);
};
this.handleNodeToggle = () => {
this.setState({ wasClicked: true });
this.props.onNodeToggle(this.props.data.__rd3t.id);
};
this.handleOnClick = evt => {
this.setState({ wasClicked: true });
this.props.onNodeClick(this.props.hierarchyPointNode, evt);
};
this.handleOnMouseOver = evt => {
this.props.onNodeMouseOver(this.props.hierarchyPointNode, evt);
};
this.handleOnMouseOut = evt => {
this.props.onNodeMouseOut(this.props.hierarchyPointNode, evt);
};
this.handleAddChildren = childrenData => {
this.props.handleAddChildrenToNode(this.props.data.__rd3t.id, childrenData);
};
}
componentDidMount() {
this.commitTransform();
}
componentDidUpdate() {
if (this.state.wasClicked) {
this.props.centerNode(this.props.hierarchyPointNode);
this.setState({ wasClicked: false });
}
this.commitTransform();
}
shouldComponentUpdate(nextProps, nextState) {
return this.shouldNodeTransform(this.props, nextProps, this.state, nextState);
}
setTransform(position, parent, orientation, shouldTranslateToOrigin = false) {
if (shouldTranslateToOrigin) {
const hasParent = parent !== null && parent !== undefined;
const originX = hasParent ? parent.x : 0;
const originY = hasParent ? parent.y : 0;
return orientation === 'horizontal'
? `translate(${originY},${originX})`
: `translate(${originX},${originY})`;
}
return orientation === 'horizontal'
? `translate(${position.y},${position.x})`
: `translate(${position.x},${position.y})`;
}
applyTransform(transform, transitionDuration, opacity = 1, done = () => { }) {
if (this.props.enableLegacyTransitions) {
select(this.nodeRef)
// @ts-ignore
.transition()
.duration(transitionDuration)
.attr('transform', transform)
.style('opacity', opacity)
.on('end', done);
}
else {
select(this.nodeRef)
.attr('transform', transform)
.style('opacity', opacity);
done();
}
}
commitTransform() {
const { orientation, transitionDuration, position, parent } = this.props;
const transform = this.setTransform(position, parent, orientation);
this.applyTransform(transform, transitionDuration);
}
componentWillLeave(done) {
const { orientation, transitionDuration, position, parent } = this.props;
const transform = this.setTransform(position, parent, orientation, true);
this.applyTransform(transform, transitionDuration, 0, done);
}
render() {
const { data, nodeClassName } = this.props;
return (React.createElement("g", { id: data.__rd3t.id, ref: n => {
this.nodeRef = n;
}, style: this.state.initialStyle, className: [
data.children && data.children.length > 0 ? 'rd3t-node' : 'rd3t-leaf-node',
nodeClassName,
]
.join(' ')
.trim(), transform: this.state.transform }, this.renderNodeElement()));
}
}