Skip to content

Commit ce92d15

Browse files
committed
Merge branch 'feature/09-reimplement-zoom' into develop
2 parents 43b8482 + 107172b commit ce92d15

File tree

3 files changed

+51
-26
lines changed

3 files changed

+51
-26
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class MyComponent extends Component {
8383
| `collapsible` | `bool` | | | `true` | Toggles ability to collapse/expand the tree's nodes by clicking them. |
8484
| `initialDepth` | `number` | `0..n` | | `undefined` | Sets the maximum node depth to which the tree is expanded on its initial render. <br /> Tree renders to full depth if prop is omitted. |
8585
| `depthFactor` | `number` | `-n..0..n` | | `undefined` | Ensures the tree takes up a fixed amount of space (`node.y = node.depth * depthFactor`), regardless of tree depth. <br /> **TIP**: Negative values invert the tree's direction. |
86+
| `zoomable` | `bool` | | | `true` | Toggles ability to zoom in/out on the Tree by scaling it according to `props.scaleExtent`. |
87+
| `scaleExtent` | `object` | | | `{min: 0.1, max: 1}` | Sets the minimum/maximum extent to which the tree can be scaled if `props.zoomable` is true. |
8688
| `transitionDuration` | `number` | `0..n` | | `500` | Sets the animation duration (in ms) of each expansion/collapse of a tree node. <br /><br /> Set this to `0` to deactivate animations completely. |
8789

8890
## External data sources

src/Tree/index.js

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { PropTypes } from 'react';
22
import { TransitionGroup } from 'react-transition-group';
3-
import { layout } from 'd3';
3+
import { layout, select, behavior, event } from 'd3';
44
import clone from 'clone';
55
import uuid from 'uuid';
66

@@ -15,7 +15,6 @@ export default class Tree extends React.Component {
1515
this.state = {
1616
initialRender: true,
1717
data: this.assignInternalProperties(clone(props.data)),
18-
zoom: undefined,
1918
};
2019
this.findNodesById = this.findNodesById.bind(this);
2120
this.collapseNode = this.collapseNode.bind(this);
@@ -24,6 +23,7 @@ export default class Tree extends React.Component {
2423

2524

2625
componentDidMount() {
26+
this.bindZoomListener();
2727
// TODO find better way of setting initialDepth, re-render here is suboptimal
2828
this.setState({ initialRender: false }); // eslint-disable-line
2929
}
@@ -54,28 +54,31 @@ export default class Tree extends React.Component {
5454
}
5555

5656

57-
// TODO Refactor zoom functionality & reimplement
5857
/**
5958
* bindZoomListener - If `props.zoomable`, binds a listener for
6059
* "zoom" events to the SVG and sets scaleExtent to min/max
6160
* specified in `props.scaleExtent`.
6261
*
6362
* @return {void}
6463
*/
65-
// bindZoomListener() {
66-
// const { zoomable, scaleExtent } = this.props;
67-
// const svg = select('.svg');
68-
//
69-
// if (zoomable) {
70-
// this.setState({ zoom: 'scale(1)' });
71-
// svg.call(behavior.zoom()
72-
// .scaleExtent([scaleExtent.min, scaleExtent.max])
73-
// .on('zoom', () => {
74-
// this.setState({ zoom: `scale(${event.scale})` });
75-
// })
76-
// );
77-
// }
78-
// }
64+
bindZoomListener() {
65+
const { zoomable, scaleExtent, translate } = this.props;
66+
const svg = select('.rd3t-svg');
67+
const g = select('.rd3t-g');
68+
69+
if (zoomable) {
70+
svg.call(behavior.zoom()
71+
.scaleExtent([scaleExtent.min, scaleExtent.max])
72+
.on('zoom', () => {
73+
g.attr('transform',
74+
`translate(${event.translate}) scale(${event.scale})`
75+
);
76+
})
77+
// Offset so that first pan and zoom does not jump back to [0,0] coords
78+
.translate([translate.x, translate.y])
79+
);
80+
}
81+
}
7982

8083

8184
/**
@@ -215,14 +218,22 @@ export default class Tree extends React.Component {
215218
}
216219

217220
render() {
218-
const { orientation, translate, pathFunc, depthFactor, transitionDuration } = this.props;
219221
const { nodes, links } = this.generateTree();
222+
const {
223+
orientation,
224+
translate,
225+
pathFunc,
226+
depthFactor,
227+
transitionDuration,
228+
zoomable,
229+
} = this.props;
220230

221231
return (
222-
<div className="rd3t-tree-container">
232+
<div className={`rd3t-tree-container ${zoomable ? 'grabbable' : undefined}`}>
223233
<svg className="rd3t-svg" width="100%" height="100%">
224234
<TransitionGroup
225235
component="g"
236+
className="rd3t-g"
226237
transform={`translate(${translate.x},${translate.y})`}
227238
>
228239
{nodes.map((nodeData) =>
@@ -263,8 +274,8 @@ Tree.defaultProps = {
263274
depthFactor: undefined,
264275
collapsible: true,
265276
initialDepth: undefined,
266-
// zoomable: true,
267-
// scaleExtent: { min: 0.1, max: 1 },
277+
zoomable: true,
278+
scaleExtent: { min: 0.1, max: 1 },
268279
};
269280

270281
Tree.propTypes = {
@@ -285,9 +296,9 @@ Tree.propTypes = {
285296
depthFactor: PropTypes.number,
286297
collapsible: PropTypes.bool,
287298
initialDepth: PropTypes.number,
288-
// zoomable: PropTypes.bool,
289-
// scaleExtent: PropTypes.shape({
290-
// min: PropTypes.number,
291-
// max: PropTypes.number,
292-
// }),
299+
zoomable: PropTypes.bool,
300+
scaleExtent: PropTypes.shape({
301+
min: PropTypes.number,
302+
max: PropTypes.number,
303+
}),
293304
};

src/Tree/style.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,15 @@
22
width: 100%;
33
height: 100%;
44
}
5+
6+
.grabbable {
7+
cursor: move; /* fallback if grab cursor is unsupported */
8+
cursor: grab;
9+
cursor: -moz-grab;
10+
cursor: -webkit-grab;
11+
}
12+
.grabbable:active {
13+
cursor: grabbing;
14+
cursor: -moz-grabbing;
15+
cursor: -webkit-grabbing;
16+
}

0 commit comments

Comments
 (0)