diff --git a/package.json b/package.json index ab4e1e36f..ad95b4fa6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nivo", - "version": "0.0.0", + "version": "2.0.0", "author": { "name": "Raphaël Benitte", "url": "https://github.com/plouc" @@ -10,7 +10,7 @@ "d3": "3.5.16", "invariant": "2.2.1", "lodash": "4.11.1", - "react-dimensions": "0.1.1" + "react-dimensions": "1.0.2" }, "devDependencies": { "ava": "0.14.0", @@ -18,10 +18,11 @@ "babel-preset-es2015": "6.6.0", "babel-preset-react": "6.5.0", "babel-preset-stage-0": "6.5.0", - "react": "^0.13.3" + "react": "^15.0.1", + "react-dom": "15.0.1" }, "peerDependencies": { - "react": "^0.13.3" + "react": "^15.0.1" }, "main": "lib", "scripts": { diff --git a/src/components/axes/AxisX.js b/src/components/axes/AxisX.js index cbec8c35f..2bf52c0a7 100644 --- a/src/components/axes/AxisX.js +++ b/src/components/axes/AxisX.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import d3 from 'd3'; class AxisX extends Component { @@ -13,7 +14,7 @@ class AxisX extends Component { transitionEasing } = props; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); element.attr('transform', `translate(0, ${height})`); const axis = d3.svg.axis() diff --git a/src/components/axes/AxisY.js b/src/components/axes/AxisY.js index 99b53a984..bd97a8310 100644 --- a/src/components/axes/AxisY.js +++ b/src/components/axes/AxisY.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import d3 from 'd3'; class AxisY extends Component { @@ -15,7 +16,7 @@ class AxisY extends Component { transitionEasing } = props; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); const axis = d3.svg.axis() .scale(yScale) diff --git a/src/components/layouts/Bubble.js b/src/components/layouts/Bubble.js index 54799ec1d..57f087a4a 100644 --- a/src/components/layouts/Bubble.js +++ b/src/components/layouts/Bubble.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import _ from 'lodash'; import d3 from 'd3'; import Dimensions from 'react-dimensions'; @@ -24,7 +25,7 @@ class Bubble extends Component { const width = containerWidth - margin.left - margin.right; const height = containerHeight - margin.top - margin.bottom; - const element = d3.select(React.findDOMNode(this)).attr({ + const element = d3.select(findDOMNode(this)).attr({ width: containerWidth, height: containerHeight }); diff --git a/src/components/layouts/Pie.js b/src/components/layouts/Pie.js index 7a51a50a0..9cd750a49 100644 --- a/src/components/layouts/Pie.js +++ b/src/components/layouts/Pie.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import _ from 'lodash'; import d3 from 'd3'; import Nivo from '../../Nivo'; @@ -21,7 +22,7 @@ class Pie extends Component { const identity = d => d.data[keyProp]; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); const container = element.select('.nivo_pie_slices'); element.attr('transform', `translate(${width / 2}, ${height / 2})`); diff --git a/src/components/layouts/RadialStack.js b/src/components/layouts/RadialStack.js index 9c98c1385..51de9ce75 100644 --- a/src/components/layouts/RadialStack.js +++ b/src/components/layouts/RadialStack.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import d3 from 'd3'; import Nivo from '../../Nivo'; import { lineInterpolation } from '../../PropTypes'; @@ -16,7 +17,7 @@ class RadialStack extends Component { transitionDuration, transitionEasing } = nextProps; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); element.attr('transform', `translate(${width / 2}, ${height / 2})`); const stack = d3.layout.stack().offset(offset); diff --git a/src/components/layouts/Stack.js b/src/components/layouts/Stack.js index 085e041a0..1404f403d 100644 --- a/src/components/layouts/Stack.js +++ b/src/components/layouts/Stack.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import d3 from 'd3'; import Nivo from '../../Nivo'; import { lineInterpolation } from '../../PropTypes'; @@ -42,7 +43,7 @@ class Stack extends Component { .y1(d => yScale(d.y0 + d.y)) ; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); let paths = element.selectAll('.stack_area').data(stacked); paths.enter().append('path') diff --git a/src/components/layouts/TreeMap.js b/src/components/layouts/TreeMap.js index 0ec90d0cd..5c637d162 100644 --- a/src/components/layouts/TreeMap.js +++ b/src/components/layouts/TreeMap.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import _ from 'lodash'; import d3 from 'd3'; import Dimensions from 'react-dimensions'; @@ -41,7 +42,7 @@ class TreeMap extends Component { //.padding(10) ; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); const wrapper = element.select('.nivo_treemap_wrapper') .style({ top: `${margin.top}px`, diff --git a/src/components/layouts/tm.js b/src/components/layouts/tm.js new file mode 100644 index 000000000..4fd459280 --- /dev/null +++ b/src/components/layouts/tm.js @@ -0,0 +1,179 @@ + + +var margin = {top: 20, right: 0, bottom: 0, left: 0}, + width = 960, + height = 500 - margin.top - margin.bottom, + formatNumber = d3.format(",d"), + transitioning; + +var x = d3.scale.linear() + .domain([0, width]) + .range([0, width]); + +var y = d3.scale.linear() + .domain([0, height]) + .range([0, height]); + +var treemap = d3.layout.treemap() + .children(function(d, depth) { return depth ? null : d._children; }) + .sort(function(a, b) { return a.value - b.value; }) + .ratio(height / width * 0.5 * (1 + Math.sqrt(5))) + .round(false); + +var svg = d3.select("#chart").append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.bottom + margin.top) + .style("margin-left", -margin.left + "px") + .style("margin.right", -margin.right + "px") + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")") + .style("shape-rendering", "crispEdges"); + +var grandparent = svg.append("g") + .attr("class", "grandparent"); + +grandparent.append("rect") + .attr("y", -margin.top) + .attr("width", width) + .attr("height", margin.top); + +grandparent.append("text") + .attr("x", 6) + .attr("y", 6 - margin.top) + .attr("dy", ".75em"); + +d3.json("flare.json", function(root) { + initialize(root); + accumulate(root); + layout(root); + display(root); + + function initialize(root) { + root.x = root.y = 0; + root.dx = width; + root.dy = height; + root.depth = 0; + } + + // Aggregate the values for internal nodes. This is normally done by the + // treemap layout, but not here because of our custom implementation. + // We also take a snapshot of the original children (_children) to avoid + // the children being overwritten when when layout is computed. + function accumulate(d) { + return (d._children = d.children) + ? d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0) + : d.value; + } + + // Compute the treemap layout recursively such that each group of siblings + // uses the same size (1×1) rather than the dimensions of the parent cell. + // This optimizes the layout for the current zoom state. Note that a wrapper + // object is created for the parent node for each group of siblings so that + // the parent’s dimensions are not discarded as we recurse. Since each group + // of sibling was laid out in 1×1, we must rescale to fit using absolute + // coordinates. This lets us use a viewport to zoom. + function layout(d) { + if (d._children) { + treemap.nodes({_children: d._children}); + d._children.forEach(function(c) { + c.x = d.x + c.x * d.dx; + c.y = d.y + c.y * d.dy; + c.dx *= d.dx; + c.dy *= d.dy; + c.parent = d; + layout(c); + }); + } + } + + function display(d) { + grandparent + .datum(d.parent) + .on("click", transition) + .select("text") + .text(name(d)); + + var g1 = svg.insert("g", ".grandparent") + .datum(d) + .attr("class", "depth"); + + var g = g1.selectAll("g") + .data(d._children) + .enter().append("g"); + + g.filter(function(d) { return d._children; }) + .classed("children", true) + .on("click", transition); + + g.selectAll(".child") + .data(function(d) { return d._children || [d]; }) + .enter().append("rect") + .attr("class", "child") + .call(rect); + + g.append("rect") + .attr("class", "parent") + .call(rect) + .append("title") + .text(function(d) { return formatNumber(d.value); }); + + g.append("text") + .attr("dy", ".75em") + .text(function(d) { return d.name; }) + .call(text); + + function transition(d) { + if (transitioning || !d) return; + transitioning = true; + + var g2 = display(d), + t1 = g1.transition().duration(750), + t2 = g2.transition().duration(750); + + // Update the domain only after entering new elements. + x.domain([d.x, d.x + d.dx]); + y.domain([d.y, d.y + d.dy]); + + // Enable anti-aliasing during the transition. + svg.style("shape-rendering", null); + + // Draw child nodes on top of parent nodes. + svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; }); + + // Fade-in entering text. + g2.selectAll("text").style("fill-opacity", 0); + + // Transition to the new view. + t1.selectAll("text").call(text).style("fill-opacity", 0); + t2.selectAll("text").call(text).style("fill-opacity", 1); + t1.selectAll("rect").call(rect); + t2.selectAll("rect").call(rect); + + // Remove the old node when the transition is finished. + t1.remove().each("end", function() { + svg.style("shape-rendering", "crispEdges"); + transitioning = false; + }); + } + + return g; + } + + function text(text) { + text.attr("x", function(d) { return x(d.x) + 6; }) + .attr("y", function(d) { return y(d.y) + 6; }); + } + + function rect(rect) { + rect.attr("x", function(d) { return x(d.x); }) + .attr("y", function(d) { return y(d.y); }) + .attr("width", function(d) { return x(d.x + d.dx) - x(d.x); }) + .attr("height", function(d) { return y(d.y + d.dy) - y(d.y); }); + } + + function name(d) { + return d.parent + ? name(d.parent) + "." + d.name + : d.name; + } +}); diff --git a/src/components/shapes/Area.js b/src/components/shapes/Area.js index 9e8229570..cdbd1d38f 100644 --- a/src/components/shapes/Area.js +++ b/src/components/shapes/Area.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import d3 from 'd3'; import Nivo from '../../Nivo'; @@ -15,7 +16,7 @@ class Area extends Component { transitionEasing } = props; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); const area = d3.svg.area() .x((d, i) => xScale(xAccessor(d, i))) diff --git a/src/components/shapes/Line.js b/src/components/shapes/Line.js index 1afab6f3d..361c5a30f 100644 --- a/src/components/shapes/Line.js +++ b/src/components/shapes/Line.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; import d3 from 'd3'; import Nivo from '../../Nivo'; @@ -14,7 +15,7 @@ class Line extends Component { transitionEasing } = props; - const element = d3.select(React.findDOMNode(this)); + const element = d3.select(findDOMNode(this)); const line = d3.svg.line() .x((d, i) => xScale(xAccessor(d, i)))