Skip to content

Commit

Permalink
feat(bubble): add zooming ability to Bubble components
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 20, 2017
1 parent 10aa40f commit a231c07
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 19 deletions.
9 changes: 5 additions & 4 deletions src/components/charts/Container.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import noop from '../../lib/noop'

const containerStyle = {
position: 'relative',
Expand All @@ -25,9 +26,9 @@ const Tooltip = ({ x, y, children, theme }) =>
{children}
</div>

const noop = {
showTooltip: () => {},
hideTooltip: () => {},
const noopHandlers = {
showTooltip: noop,
hideTooltip: noop,
}

export default class Container extends Component {
Expand Down Expand Up @@ -68,7 +69,7 @@ export default class Container extends Component {
const { children, isInteractive, theme } = this.props
const { isTooltipVisible, tooltipContent, tooltipX, tooltipY } = this.state

if (!isInteractive) return children(noop)
if (!isInteractive) return children(noopHandlers)

return (
<div
Expand Down
1 change: 1 addition & 0 deletions src/components/charts/bubble/Bubble.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const createNodes = ({
onMouseEnter={handleTooltip}
onMouseMove={handleTooltip}
onMouseLeave={hideTooltip}
onClick={node.zoom}
style={{
fill: node.style.color,
stroke: borderColorFn(node.style),
Expand Down
26 changes: 26 additions & 0 deletions src/components/charts/bubble/BubbleNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* This file is part of the nivo project.
*
* Copyright 2016-present, Raphaël Benitte.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React from 'react'

const BubbleNode = ({ radius, x, y, color, borderWidth, borderColor }) => {
return (
<circle
r={radius}
transform={`translate(${x},${y})`}
//onMouseEnter={handleTooltip}
//onMouseMove={handleTooltip}
//onMouseLeave={hideTooltip}
fill={color}
strokeWidth={borderWidth}
stroke={borderColor}
/>
)
}

export default BubbleNode
72 changes: 65 additions & 7 deletions src/components/charts/bubble/BubblePlaceholders.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import { TransitionMotion, spring } from 'react-motion'
import _ from 'lodash'
import compose from 'recompose/compose'
import withPropsOnChange from 'recompose/withPropsOnChange'
import withStateHandlers from 'recompose/withStateHandlers'
import pure from 'recompose/pure'
import { pack } from 'd3-hierarchy'
import { withHierarchy, withTheme, withColors, withDimensions, withMotion } from '../../../hocs'
import { extractRGB } from '../../../lib/colorUtils'
import noop from '../../../lib/noop'
import Container from '../Container'
import { getAccessorFor } from '../../../lib/propertiesConverters'
import { bubblePropTypes, bubbleDefaultProps } from './BubbleProps'
Expand All @@ -33,6 +35,9 @@ const ignoreProps = [
'transitionEasing',
]

const computeNodeUID = (node, getIdentity) =>
node.ancestors().map(ancestor => getIdentity(ancestor.data)).join('.')

const nodeWillEnter = ({ data: node }) => ({
r: 0,
x: node.x,
Expand All @@ -46,6 +51,20 @@ const nodeWillLeave = styleThatLeft => ({
y: spring(styleThatLeft.data.y),
})

const computeZoom = (nodes, currentNodeUid, width, height) => {
const currentNode = nodes.find(({ uid }) => uid === currentNodeUid)
if (currentNode) {
const ratio = Math.min(width, height) / (currentNode.r * 2)
const offsetX = width / 2 - currentNode.x * ratio
const offsetY = height / 2 - currentNode.y * ratio
nodes.forEach(node => {
node.r = node.r * ratio
node.x = node.x * ratio + offsetX
node.y = node.y * ratio + offsetY
})
}
}

const BubblePlaceholders = ({
root,
getIdentity,
Expand All @@ -54,38 +73,56 @@ const BubblePlaceholders = ({
namespace,

pack,

// dimensions
width,
height,
margin,
outerWidth,
outerHeight,

padding,

// theming
theme,
getColor,

// motion
animate,
motionStiffness,
motionDamping,
children,

// interactivity
isInteractive,

children,

// zooming
isZoomable,
zoomToNode,
currentNodeUid,
}) => {
// assign a unique id depending on node path to each node
root.each(node => {
node.uid = computeNodeUID(node, getIdentity)
})

pack(root)

let nodes = leavesOnly ? root.leaves() : root.descendants()
nodes = nodes.map(d => {
d.color = getColor({ ...d.data, depth: d.depth })
nodes = nodes.map(node => {
node.color = getColor({ ...node.data, depth: node.depth })
// if (d.depth > 1) {
// d.color = color(d.parentId)
// } else {
// d.color = color(identity(d.data))
// }

d.data.key = d.ancestors().map(a => getIdentity(a.data)).join('.')
node.data.key = node.ancestors().map(a => getIdentity(a.data)).join('.')

return d
return node
})

if (currentNodeUid) computeZoom(nodes, currentNodeUid, width, height)

let wrapperTag
let containerTag

Expand Down Expand Up @@ -178,6 +215,13 @@ const BubblePlaceholders = ({
colorR
)},${Math.round(colorG)},${Math.round(colorB)})`

if (isInteractive && isZoomable) {
interpolatedStyle.zoom = () =>
zoomToNode(interpolatedStyle.data.uid)
} else {
interpolatedStyle.zoom = noop
}

return interpolatedStyle
}),
{ showTooltip, hideTooltip }
Expand Down Expand Up @@ -208,6 +252,20 @@ const enhance = compose(
withPropsOnChange(['width', 'height', 'padding'], ({ width, height, padding }) => ({
pack: pack().size([width, height]).padding(padding),
})),
withStateHandlers(
({ currentNodeUid = null }) => ({
currentNodeUid,
}),
{
zoomToNode: ({ currentNodeUid }) => uid => {
if (uid === currentNodeUid) {
return { currentNodeUid: null }
}

return { currentNodeUid: uid }
},
}
),
pure
)

Expand Down
13 changes: 6 additions & 7 deletions src/components/charts/bubble/BubbleProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
* file that was distributed with this source code.
*/
import PropTypes from 'prop-types'
import Nivo from '../../../Nivo'
import { marginPropType, motionPropTypes } from '../../../props'

export const bubblePropTypes = {
// data
Expand Down Expand Up @@ -37,7 +35,6 @@ export const bubblePropTypes = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
labelFormat: PropTypes.string,
labelTextColor: PropTypes.any.isRequired,
labelTextDY: PropTypes.number.isRequired,
labelSkipRadius: PropTypes.number.isRequired,

// transitions
Expand All @@ -46,6 +43,9 @@ export const bubblePropTypes = {

// interactivity
isInteractive: PropTypes.bool.isRequired,

// zooming
isZoomable: PropTypes.bool.isRequired,
}

export const bubbleDefaultProps = {
Expand All @@ -68,10 +68,9 @@ export const bubbleDefaultProps = {
labelTextColor: 'inherit:darker(1)',
labelSkipRadius: 0,

// transitions
transitionDuration: Nivo.defaults.transitionDuration, // d3 transitions
transitionEasing: Nivo.defaults.transitionEasing, // d3 transitions

// interactivity
isInteractive: true,

// zooming
isZoomable: true,
}
9 changes: 9 additions & 0 deletions src/lib/noop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* This file is part of the nivo project.
*
* Copyright 2016-present, Raphaël Benitte.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export default () => {}
1 change: 0 additions & 1 deletion src/lib/propertiesConverters.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import _ from 'lodash'
import { format } from 'd3-format'

Expand Down

0 comments on commit a231c07

Please sign in to comment.