Skip to content

Commit

Permalink
feat(treemap): add TreeMapCanvas component
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Sep 19, 2017
1 parent 1972414 commit e12a126
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 224 deletions.
35 changes: 24 additions & 11 deletions src/components/charts/bar/BarCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import { partial } from 'lodash'
import { generateGroupedBars, generateStackedBars } from '../../../lib/charts/bar'
import { renderAxes } from '../../../lib/canvas/axes'
import { getRelativeCursor, isCursorInRect } from '../../../lib/interactivity'
import Container from '../Container'
import BasicTooltip from '../../tooltip/BasicTooltip'
import { BarPropTypes } from './props'
import enhance from './enhance'
import { getRelativeCursor, isCursorInRect } from '../../../lib/interactivity'

const findNodeUnderCursor = (nodes, margin, x, y) =>
nodes.find(node =>
isCursorInRect(node.x + margin.left, node.y + margin.top, node.width, node.height, x, y)
)

class BarCanvas extends Component {
componentDidMount() {
Expand Down Expand Up @@ -120,15 +124,13 @@ class BarCanvas extends Component {
})
}

handleMouseHover = (showTooltip, hideTooltip, event) => {
handleMouseHover = (showTooltip, hideTooltip) => event => {
if (!this.bars) return

const { margin, theme } = this.props
const [x, y] = getRelativeCursor(this.surface, event)

const { margin, theme } = this.props
const bar = this.bars.find(bar =>
isCursorInRect(bar.x + margin.left, bar.y + margin.top, bar.width, bar.height, x, y)
)
const bar = findNodeUnderCursor(this.bars, margin, x, y)

if (bar !== undefined) {
showTooltip(
Expand All @@ -146,10 +148,20 @@ class BarCanvas extends Component {
}
}

handleMouseLeave = hideTooltip => {
handleMouseLeave = hideTooltip => () => {
hideTooltip()
}

handleClick = event => {
if (!this.bars) return

const { margin, onClick } = this.props
const [x, y] = getRelativeCursor(this.surface, event)

const node = findNodeUnderCursor(this.bars, margin, x, y)
if (node !== undefined) onClick(node.data, event)
}

render() {
const { outerWidth, outerHeight, pixelRatio, isInteractive, theme } = this.props

Expand All @@ -166,9 +178,10 @@ class BarCanvas extends Component {
width: outerWidth,
height: outerHeight,
}}
onMouseEnter={partial(this.handleMouseHover, showTooltip, hideTooltip)}
onMouseMove={partial(this.handleMouseHover, showTooltip, hideTooltip)}
onMouseLeave={partial(this.handleMouseLeave, hideTooltip)}
onMouseEnter={this.handleMouseHover(showTooltip, hideTooltip)}
onMouseMove={this.handleMouseHover(showTooltip, hideTooltip)}
onMouseLeave={this.handleMouseLeave(hideTooltip)}
onClick={this.handleClick}
/>
)}
</Container>
Expand Down
1 change: 0 additions & 1 deletion src/components/charts/bubble/BubbleCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import _ from 'lodash'
import Container from '../Container'
import enhance from './enhance'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
*/
import React from 'react'
import ResponsiveWrapper from '../ResponsiveWrapper'
import TreeMapPlaceholders from './TreeMapPlaceholders'
import TreeMapCanvas from './TreeMapCanvas'

export default props => (
<ResponsiveWrapper>
{({ width, height }) => <TreeMapPlaceholders width={width} height={height} {...props} />}
{({ width, height }) => <TreeMapCanvas width={width} height={height} {...props} />}
</ResponsiveWrapper>
)
153 changes: 153 additions & 0 deletions src/components/charts/treemap/TreeMapCanvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* 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, { Component } from 'react'
import { degreesToRadians } from '../../../lib/polar'
import { getRelativeCursor, isCursorInRect } from '../../../lib/interactivity'
import Container from '../Container'
import enhance from './enhance'
import TreeMapNodeTooltip from './TreeMapNodeTooltip'

const findNodeUnderCursor = (nodes, margin, x, y) =>
nodes.find(node =>
isCursorInRect(node.x + margin.left, node.y + margin.top, node.width, node.height, x, y)
)

class TreeMapCanvas extends Component {
componentDidMount() {
this.ctx = this.surface.getContext('2d')
this.draw(this.props)
}

componentDidUpdate() {
this.ctx = this.surface.getContext('2d')
this.draw(this.props)
}

draw(props) {
const {
nodes,

pixelRatio,

// dimensions
margin,
outerWidth,
outerHeight,

// styling
borderWidth,
getBorderColor,

// labels
enableLabel,
getLabelTextColor,
orientLabel,
} = props

this.surface.width = outerWidth * pixelRatio
this.surface.height = outerHeight * pixelRatio

this.ctx.scale(pixelRatio, pixelRatio)
this.ctx.clearRect(0, 0, outerWidth, outerHeight)
this.ctx.translate(margin.left, margin.top)

nodes.forEach(node => {
this.ctx.fillStyle = node.color
this.ctx.fillRect(node.x, node.y, node.width, node.height)

if (borderWidth > 0) {
this.ctx.strokeStyle = getBorderColor(node)
this.ctx.lineWidth = borderWidth
this.ctx.strokeRect(node.x, node.y, node.width, node.height)
}
})

if (enableLabel) {
this.ctx.textAlign = 'center'
this.ctx.textBaseline = 'middle'

// draw labels on top
nodes.filter(({ label }) => label !== undefined).forEach(node => {
const labelTextColor = getLabelTextColor(node)

const rotate = orientLabel && node.height > node.width

this.ctx.save()
this.ctx.translate(node.x + node.width / 2, node.y + node.height / 2)
this.ctx.rotate(degreesToRadians(rotate ? -90 : 0))

this.ctx.fillStyle = labelTextColor
this.ctx.fillText(node.label, 0, 0)

this.ctx.restore()
})
}
}

handleMouseHover = (showTooltip, hideTooltip) => event => {
const { isInteractive, nodes, margin, theme } = this.props

if (!isInteractive) return

const [x, y] = getRelativeCursor(this.surface, event)

const node = findNodeUnderCursor(nodes, margin, x, y)

if (node !== undefined) {
showTooltip(<TreeMapNodeTooltip node={node} theme={theme} />, event)
} else {
hideTooltip()
}
}

handleMouseLeave = hideTooltip => () => {
hideTooltip()
}

handleClick = event => {
const { isInteractive, nodes, margin, onClick } = this.props

if (!isInteractive) return

const [x, y] = getRelativeCursor(this.surface, event)

const node = findNodeUnderCursor(nodes, margin, x, y)
if (node !== undefined) onClick(node, event)
}

render() {
const { outerWidth, outerHeight, pixelRatio, isInteractive, theme } = this.props

return (
<Container isInteractive={isInteractive} theme={theme}>
{({ showTooltip, hideTooltip }) => (
<canvas
ref={surface => {
this.surface = surface
}}
width={outerWidth * pixelRatio}
height={outerHeight * pixelRatio}
style={{
width: outerWidth,
height: outerHeight,
}}
onMouseEnter={this.handleMouseHover(showTooltip, hideTooltip)}
onMouseMove={this.handleMouseHover(showTooltip, hideTooltip)}
onMouseLeave={this.handleMouseLeave(hideTooltip)}
onClick={this.handleClick}
/>
)}
</Container>
)
}
}

TreeMapCanvas.displayName = 'TreeMapCanvas'

export default enhance(TreeMapCanvas)
23 changes: 23 additions & 0 deletions src/components/charts/treemap/TreeMapNodeTooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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'
import pure from 'recompose/pure'
import BasicTooltip from '../../tooltip/BasicTooltip'

const TreeMapNodeTooltip = ({ node, theme }) => (
<BasicTooltip
id={node.id}
value={node.value}
enableChip={true}
color={node.color}
theme={theme}
/>
)

export default pure(TreeMapNodeTooltip)

0 comments on commit e12a126

Please sign in to comment.