Skip to content

Commit

Permalink
feat(bubble): move Bubble legends in a dedicated component
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphael Benitte committed Apr 21, 2016
1 parent b8b8992 commit 08a7259
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 27 deletions.
71 changes: 44 additions & 27 deletions src/components/layouts/Bubble.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ import Dimensions from 'react-dimensions';
import Nivo from '../../Nivo';
import { margin as marginPropType } from '../../PropTypes';
import { flatten } from '../../DataUtils';
import { getColorStyleObject, getColorRange } from '../../ColorUtils';
import { getColorRange } from '../../ColorUtils';


class Bubble extends Component {
renderD3(nextProps) {
const {
root,
mode,
valueAccessor, labelFn,
containerWidth, containerHeight,
padding,
colors,
transitionDuration, transitionEasing
} = nextProps;
Expand All @@ -38,50 +37,57 @@ class Bubble extends Component {
const bubble = d3.layout.pack()
.sort(null)
.size([width, height])
.padding(1)
;

let nodes = wrapper.selectAll('.bubble_node')
.data(bubble.nodes(flatten(root))
.filter(d => !d.children))
.padding(padding)
;

const color = getColorRange(colors);

const flattened = flatten(root);
flattened.children.forEach(child => {
child.color = color(child.packageName);
});

const bubbled = bubble.nodes(flattened).filter(d => !d.children);
const nodes = wrapper.selectAll('.bubble_node').data(bubbled);

nodes
.enter().append('g')
.enter().append('circle')
.attr('class', 'bubble_node')
.attr('transform', d => `translate(${d.x},${d.y})`)
.append('circle')
.attr('r', 2)
.style('fill', d => color(d.packageName))
.attr('transform', d => `translate(${d.x},${d.y})`)
;

nodes
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.attr('r', d => d.r)
.attr('transform', d => `translate(${d.x},${d.y})`)
;

nodes.each(function (d) {
const el = d3.select(this);

el.select('circle')
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.attr('r', d => d.r)
;
});

nodes.exit()
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.attr('transform', `translate(${width / 2},${height / 2})`)
.remove()
;

const bubbleContext = {
element: wrapper,
width,
height,
rawData: root,
flatData: flattened,
data: bubbled,
transitionDuration,
transitionEasing
};

this.legends.forEach(legend => {
legend(bubbleContext);
});
}

shouldComponentUpdate(nextProps) {
Expand All @@ -95,6 +101,19 @@ class Bubble extends Component {
}

componentWillMount() {
const { children } = this.props;

const legends = [];

React.Children.forEach(children, element => {
if (React.isValidElement(element)) {
if (element.type.createBubbleLegendsFromReactElement) {
legends.push(element.type.createBubbleLegendsFromReactElement(element));
}
}
});

this.legends = legends;
}

render() {
Expand All @@ -113,17 +132,15 @@ Bubble.propTypes = {
containerHeight: number.isRequired,
margin: marginPropType,
root: object.isRequired,
valueAccessor: func.isRequired,
labelFn: func.isRequired,
padding: number.isRequired,
colors: any.isRequired,
transitionDuration: number.isRequired,
transitionEasing: string.isRequired
};

Bubble.defaultProps = {
margin: Nivo.defaults.margin,
valueAccessor: d => d.size,
labelFn: d => d.name,
padding: 1,
colors: Nivo.defaults.colorRange,
transitionDuration: Nivo.defaults.transitionDuration,
transitionEasing: Nivo.defaults.transitionEasing
Expand Down
71 changes: 71 additions & 0 deletions src/components/layouts/BubbleLegends.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { Component, PropTypes } from 'react';
import invariant from 'invariant';
import Nivo from '../../Nivo';
import { getColorStyleObject } from '../../ColorUtils';


class BubbleLegends extends Component {
static createBubbleLegendsFromReactElement(element) {
const { props } = element;

const { textColor, labelAccessor, skipRadius } = props;

const textColorStyle = getColorStyleObject(textColor, 'fill');

return ({ element, data, width, height, transitionDuration, transitionEasing }) => {

if (skipRadius > 0) {
data = data.filter(d => d.r >= skipRadius);
}

const legends = element.selectAll('.bubble_legend').data(data);

legends.enter().append('text')
.attr('class', 'bubble_legend')
.style('text-anchor', 'middle')
.style(textColorStyle)
.style('opacity', 0)
.text(labelAccessor)
.attr('transform', d => `translate(${d.x},${d.y})`)
;

legends
.text(labelAccessor)
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.style(textColorStyle)
.style('opacity', 1)
.attr('transform', d => `translate(${d.x},${d.y})`)
;

legends.exit()
.remove()
;
};
}

render() {
invariant(
false,
'<BubbleLegends> element is for Bubble configuration only and should not be rendered'
);
}
}

const { number, func, any } = PropTypes;

BubbleLegends.propTypes = {
labelAccessor: func.isRequired,
textColor: any.isRequired,
skipRadius: number.isRequired
};

BubbleLegends.defaultProps = {
labelAccessor: d => d.name,
textColor: 'none',
skipRadius: 0
};


export default BubbleLegends;
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export Stack from './components/layouts/Stack';
export RadialStack from './components/layouts/RadialStack';
export TreeMap from './components/layouts/TreeMap';
export Bubble from './components/layouts/Bubble';
export BubbleLegends from './components/layouts/BubbleLegends';

0 comments on commit 08a7259

Please sign in to comment.