Skip to content

Commit

Permalink
feat(heatmap): add hover behavior on HeatMapCanvas
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 29, 2017
1 parent 036179c commit 37974a9
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 7 deletions.
5 changes: 4 additions & 1 deletion src/components/charts/heatmap/HeatMapCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class HeatMapCanvas extends Component {

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

const { margin, offsetX, offsetY, theme } = this.props
const { margin, offsetX, offsetY, theme, setCurrentNode } = this.props
const node = this.nodes.find(node =>
cursorInRect(
node.x + margin.left + offsetX - node.width / 2,
Expand All @@ -113,6 +113,7 @@ class HeatMapCanvas extends Component {
)

if (node !== undefined) {
setCurrentNode(node)
showTooltip(
<BasicTooltip
id={`${node.yKey} - ${node.xKey}`}
Expand All @@ -124,11 +125,13 @@ class HeatMapCanvas extends Component {
event
)
} else {
setCurrentNode(null)
hideTooltip()
}
}

handleMouseLeave = hideTooltip => {
this.props.setCurrentNode(null)
hideTooltip()
}

Expand Down
2 changes: 2 additions & 0 deletions src/components/charts/heatmap/enhance.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { min, max, isEqual } from 'lodash'
import compose from 'recompose/compose'
import defaultProps from 'recompose/defaultProps'
import withPropsOnChange from 'recompose/withPropsOnChange'
import withState from 'recompose/withState'
import pure from 'recompose/pure'
import { scaleOrdinal, scaleLinear } from 'd3-scale'
import { withTheme, withDimensions, withMotion } from '../../../hocs'
Expand All @@ -27,6 +28,7 @@ const computeY = (row, cellHeight, padding) => {
export default Component =>
compose(
defaultProps(HeatMapDefaultProps),
withState('currentNode', 'setCurrentNode', null),
withTheme(),
withDimensions(),
withMotion(),
Expand Down
8 changes: 6 additions & 2 deletions src/components/charts/heatmap/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export const HeatMapPropTypes = {
cellShape: PropTypes.oneOfType([PropTypes.oneOf(['rect', 'circle']), PropTypes.func])
.isRequired,
cellOpacity: PropTypes.number.isRequired,
cellHoverOpacity: PropTypes.number.isRequired,
cellBorderWidth: PropTypes.number.isRequired,
cellBorderColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
getCellBorderColor: PropTypes.func.isRequired, // computed
Expand All @@ -51,6 +50,9 @@ export const HeatMapPropTypes = {

// interactivity
isInteractive: PropTypes.bool,
hoverTarget: PropTypes.oneOf(['cell', 'row', 'column', 'rowColumn']).isRequired,
cellHoverOpacity: PropTypes.number.isRequired,
cellHoverOthersOpacity: PropTypes.number.isRequired,

// canvas specific
pixelRatio: PropTypes.number.isRequired,
Expand All @@ -69,7 +71,6 @@ export const HeatMapDefaultProps = {
// cells
cellShape: 'rect',
cellOpacity: 0.85,
cellHoverOpacity: 1,
cellBorderWidth: 0,
cellBorderColor: 'inherit',

Expand All @@ -88,6 +89,9 @@ export const HeatMapDefaultProps = {

// interactivity
isInteractive: true,
hoverTarget: 'rowColumn',
cellHoverOpacity: 1,
cellHoverOthersOpacity: 0.35,

// canvas specific
pixelRatio: window && window.devicePixelRatio ? window.devicePixelRatio : 1,
Expand Down
19 changes: 17 additions & 2 deletions src/lib/canvas/charts/heatmap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@
* @param {number} width
* @param {number} height
* @param {string) color
* @param {number} opacity
* @param {string} labelTextColor
* @param {number} value
*/
export const renderRect = (ctx, { x, y, width, height, color, labelTextColor, value }) => {
export const renderRect = (ctx, { x, y, width, height, color, opacity, labelTextColor, value }) => {
ctx.save()
ctx.globalAlpha = opacity

ctx.fillStyle = color
ctx.fillRect(x - width / 2, y - height / 2, width, height)

ctx.fillStyle = labelTextColor
ctx.fillText(value, x, y)

ctx.restore()
}

/**
Expand All @@ -36,10 +42,17 @@ export const renderRect = (ctx, { x, y, width, height, color, labelTextColor, va
* @param {number} width
* @param {number} height
* @param {string) color
* @param {number} opacity
* @param {string} labelTextColor
* @param {number} value
*/
export const renderCircle = (ctx, { x, y, width, height, color, labelTextColor, value }) => {
export const renderCircle = (
ctx,
{ x, y, width, height, color, opacity, labelTextColor, value }
) => {
ctx.save()
ctx.globalAlpha = opacity

const radius = Math.min(width, height) / 2

ctx.fillStyle = color
Expand All @@ -49,4 +62,6 @@ export const renderCircle = (ctx, { x, y, width, height, color, labelTextColor,

ctx.fillStyle = labelTextColor
ctx.fillText(value, x, y)

ctx.restore()
}
28 changes: 26 additions & 2 deletions src/lib/charts/heatmap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

const isHoverTargetByType = {
cell: (node, current) => node.xKey === current.xKey && node.yKey === current.yKey,
row: (node, current) => node.yKey === current.yKey,
column: (node, current) => node.xKey === current.xKey,
rowColumn: (node, current) => node.xKey === current.xKey || node.yKey === current.yKey,
}

export const computeNodes = ({
data,
keys,
Expand All @@ -17,8 +25,15 @@ export const computeNodes = ({
cellHeight,
colorScale,
getLabelTextColor,
}) =>
data.reduce((acc, d) => {

currentNode,
hoverTarget,
cellHoverOpacity,
cellHoverOthersOpacity,
}) => {
const isHoverTarget = isHoverTargetByType[hoverTarget]

return data.reduce((acc, d) => {
keys.forEach(key => {
const width = sizeScale ? Math.min(sizeScale(d[key]) * cellWidth, cellWidth) : cellWidth
const height = sizeScale
Expand All @@ -36,12 +51,21 @@ export const computeNodes = ({
color: colorScale(d[key]),
}

let opacity = 1
if (currentNode) {
opacity = isHoverTarget(node, currentNode)
? cellHoverOpacity
: cellHoverOthersOpacity
}

acc.push(
Object.assign(node, {
labelTextColor: getLabelTextColor(node),
opacity,
})
)
})

return acc
}, [])
}

0 comments on commit 37974a9

Please sign in to comment.