diff --git a/src/components/charts/bubble/Bubble.js b/src/components/charts/bubble/Bubble.js index df2ec3f7f..c0e4d7e9f 100644 --- a/src/components/charts/bubble/Bubble.js +++ b/src/components/charts/bubble/Bubble.js @@ -8,16 +8,18 @@ */ 'use strict'; -import React, { Component, PropTypes } from 'react'; +import React, { Component } from 'react'; import _ from 'lodash'; -import Nivo from '../../../Nivo'; import { getLabelGenerator } from '../../../lib/LabelHelper'; import { bubblePropTypes, bubbleDefaultProps } from './BubbleProps'; import BubblePlaceholders from './BubblePlaceholders'; +import { getColorGenerator } from '../../../ColorUtils'; -const createNodes = ({ label, labelFormat, skipRadius }) => { - const labelFn = getLabelGenerator(label, labelFormat); +const createNodes = ({ borderWidth, borderColor, label, labelFormat, labelSkipRadius, labelTextColor }) => { + const labelFn = getLabelGenerator(label, labelFormat); + const borderColorFn = getColorGenerator(borderColor); + const textColorFn = getColorGenerator(labelTextColor); return nodes => { const renderedNodes = []; @@ -29,14 +31,18 @@ const createNodes = ({ label, labelFormat, skipRadius }) => { r={node.style.r} className="nivo_bubble_node" transform={`translate(${node.style.x},${node.style.y})`} - style={{ fill: node.style.color }} + style={{ + fill: node.style.color, + stroke: borderColorFn(node.style), + strokeWidth: borderWidth, + }} /> ); }); nodes .filter(node => { - return skipRadius === 0 || node.data.r >= skipRadius; + return labelSkipRadius === 0 || node.data.r >= labelSkipRadius; }) .forEach(node => { renderedNodes.push( @@ -44,6 +50,9 @@ const createNodes = ({ label, labelFormat, skipRadius }) => { key={`${node.key}.text`} transform={`translate(${node.style.x},${node.style.y})`} textAnchor={'middle'} + style={{ + fill: textColorFn(node.style) + }} > {labelFn(node.data)} @@ -69,25 +78,18 @@ class Bubble extends Component { } } -const { number, string, any } = PropTypes; +Bubble.propTypes = _.omit(bubblePropTypes, [ + 'children', + 'namespace', + 'transitionDuration', + 'transitionEasing', +]); -Bubble.propTypes = _.assign({}, bubblePropTypes, { - label: string.isRequired, - labelFormat: string, - textColor: any.isRequired, - skipRadius: number.isRequired, - width: number.isRequired, - height: number.isRequired, - stiffness: number.isRequired, // react-motion - damping: number.isRequired, // react-motion -}); - -Bubble.defaultProps = _.assign({}, bubbleDefaultProps, { - label: 'name', - skipRadius: 0, - stiffness: Nivo.defaults.motionStiffness, - damping: Nivo.defaults.motionDamping, -}); +Bubble.defaultProps = _.omit(bubbleDefaultProps, [ + 'namespace', + 'transitionDuration', + 'transitionEasing', +]); export default Bubble; diff --git a/src/components/charts/bubble/BubbleD3.js b/src/components/charts/bubble/BubbleD3.js index 15e4f2e28..1376f5d30 100644 --- a/src/components/charts/bubble/BubbleD3.js +++ b/src/components/charts/bubble/BubbleD3.js @@ -8,7 +8,7 @@ */ 'use strict'; -import React, { Component, PropTypes } from 'react'; +import React, { Component } from 'react'; import { findDOMNode } from 'react-dom'; import _ from 'lodash'; import { bubblePropTypes, bubbleDefaultProps } from './BubbleProps'; @@ -41,19 +41,18 @@ class BubbleD3 extends Component { } } -const { number, string } = PropTypes; - -BubbleD3.propTypes = _.assign({}, bubblePropTypes, { - width: number.isRequired, - height: number.isRequired, - transitionDuration: number.isRequired, - transitionEasing: string.isRequired, -}); - -BubbleD3.defaultProps = _.assign({}, bubbleDefaultProps, { - transitionDuration: Nivo.defaults.transitionDuration, - transitionEasing: Nivo.defaults.transitionEasing, -}); +BubbleD3.propTypes = _.omit(bubblePropTypes, [ + 'children', + 'namespace', + 'motionStiffness', + 'motionDamping', +]); + +BubbleD3.defaultProps = _.omit(bubbleDefaultProps, [ + 'namespace', + 'motionStiffness', + 'motionDamping', +]); export default BubbleD3; diff --git a/src/components/charts/bubble/BubbleLegends.js b/src/components/charts/bubble/BubbleLegends.js deleted file mode 100644 index dca451739..000000000 --- a/src/components/charts/bubble/BubbleLegends.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of the nivo library. - * - * (c) Raphaël Benitte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import invariant from 'invariant'; -import Nivo from '../../../Nivo'; -import { getColorStyleObject } from '../../../ColorUtils'; - - -class BubbleLegends extends Component { - static decorateBubble(element) { - const { props } = element; - - const { textColor, labelAccessor, skipRadius } = props; - - const textColorStyle = getColorStyleObject(textColor, 'fill'); - - return ({ element, data, identity, transitionDuration, transitionEasing }) => { - - if (skipRadius > 0) { - data = data.filter(d => d.r >= skipRadius); - } - - const legends = element.selectAll('.bubble_legend').data(data, identity); - - 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() - .transition() - .duration(transitionDuration) - .ease(transitionEasing) - .style('opacity', 0) - .remove() - ; - }; - } - - render() { - invariant( - false, - ' 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; diff --git a/src/components/charts/bubble/BubblePlaceholders.js b/src/components/charts/bubble/BubblePlaceholders.js index 7d0f3c4d0..fdaf06c56 100644 --- a/src/components/charts/bubble/BubblePlaceholders.js +++ b/src/components/charts/bubble/BubblePlaceholders.js @@ -8,7 +8,7 @@ */ 'use strict'; -import React, { Component, PropTypes } from 'react'; +import React, { Component } from 'react'; import { TransitionMotion, spring } from 'react-motion'; import d3 from 'd3'; import _ from 'lodash'; @@ -38,7 +38,7 @@ class BubblePlaceholders extends Component { identityProperty, value, padding, colors, - stiffness, damping + motionStiffness, motionDamping } = this.props; const valueAccessor = d => d[value]; @@ -101,6 +101,9 @@ class BubblePlaceholders extends Component { containerProps.style = margin; } + const stiffness = motionStiffness; + const damping = motionDamping; + return React.createElement(wrapperTag, wrapperProps, ( { @@ -32,6 +33,8 @@ const BubbleD3Svg = domRoot => { width, height, margin, padding, colors, + borderWidth, borderColor, + label, labelFormat, labelSkipRadius, labelTextColor, transitionDuration, transitionEasing } = props; @@ -49,7 +52,8 @@ const BubbleD3Svg = domRoot => { transform: `translate(${margin.left},${margin.top})` }); - const color = getColorRange(colors); + const color = getColorRange(colors); + const borderColorFn = getColorGenerator(borderColor); const bubbled = bubble.compute({ width: useWidth, @@ -60,25 +64,42 @@ const BubbleD3Svg = domRoot => { color }); + + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // NODES + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— const nodes = wrapper.selectAll('.nivo_bubble_node').data(bubbled, identity); + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // ENTER: creates new nodes + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— nodes .enter().append('circle') .attr('class', 'nivo_bubble_node') .attr('r', 2) .style('fill', d => d.color) + .style('stroke', borderColorFn) + .style('stroke-width', borderWidth) .attr('transform', d => `translate(${d.x},${d.y})`) ; + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // UPDATE: updates existing nodes + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— nodes .transition() .duration(transitionDuration) .ease(transitionEasing) .style('fill', d => d.color) + .style('stroke', borderColorFn) + .style('stroke-width', borderWidth) .attr('r', d => d.r) .attr('transform', d => `translate(${d.x},${d.y})`) ; + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // EXIT: removes stale nodes + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— nodes.exit() .transition() .duration(transitionDuration) @@ -87,6 +108,57 @@ const BubbleD3Svg = domRoot => { .remove() ; + + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // LABELS + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + const labelFn = getLabelGenerator(label, labelFormat); + const textColorStyle = getColorStyleObject(labelTextColor, 'fill'); + + + let legendsData = bubbled; + if (labelSkipRadius > 0) { + legendsData = bubbled.filter(d => d.r >= labelSkipRadius); + } + + const legends = wrapper.selectAll('.nivo_bubble_legend').data(legendsData, identity); + + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // ENTER: creates new labels + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + legends.enter().append('text') + .attr('class', 'nivo_bubble_legend') + .style('text-anchor', 'middle') + .style(textColorStyle) + .style('opacity', 0) + .text(labelFn) + .attr('transform', d => `translate(${d.x},${d.y})`) + ; + + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // UPDATE: updates existing labels + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + legends + .text(labelFn) + .transition() + .duration(transitionDuration) + .ease(transitionEasing) + .style(textColorStyle) + .style('opacity', 1) + .attr('transform', d => `translate(${d.x},${d.y})`) + ; + + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + // EXIT: removes stale labels + // ————————————————————————————————————————————————————————————————————————————————————————————————————————— + legends.exit() + .transition() + .duration(transitionDuration) + .ease(transitionEasing) + .style('opacity', 0) + .remove() + ; + const bubbleContext = { element: wrapper, width: useWidth,