diff --git a/src/components/charts/Container.js b/src/components/charts/Container.js
index 9cbdb892b..df055281e 100644
--- a/src/components/charts/Container.js
+++ b/src/components/charts/Container.js
@@ -15,6 +15,7 @@ const containerStyle = {
}
const tooltipStyle = {
+ pointerEvents: 'none',
position: 'absolute',
zIndex: 10,
top: 0,
diff --git a/src/components/charts/heatmap/HeatMap.js b/src/components/charts/heatmap/HeatMap.js
index 30a82bc71..ff01dd6ab 100644
--- a/src/components/charts/heatmap/HeatMap.js
+++ b/src/components/charts/heatmap/HeatMap.js
@@ -6,11 +6,12 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-import React from 'react'
-import { min, max, isEqual } from 'lodash'
-import { TransitionMotion, spring } from 'react-motion'
+import React, { Component } from 'react'
+import { partial } from 'lodash'
+import { TransitionMotion } from 'react-motion'
import { colorMotionSpring, getInterpolatedColor } from '../../../lib/colors'
import { HeatMapPropTypes } from './props'
+import { computeNodes } from '../../../lib/charts/heatmap'
import enhance from './enhance'
import Container from '../Container'
import SvgWrapper from '../SvgWrapper'
@@ -18,191 +19,196 @@ import Grid from '../../axes/Grid'
import Axes from '../../axes/Axes'
import HeatMapCellRect from './HeatMapCellRect'
import HeatMapCellCircle from './HeatMapCellCircle'
+import HeatMapCellTooltip from './HeatMapCellTooltip'
-const HeatMap = ({
- data,
- getIndex,
- keys,
-
- cellWidth,
- cellHeight,
- sizeScale,
- xScale,
- yScale,
- offsetX,
- offsetY,
-
- margin,
- width,
- height,
- outerWidth,
- outerHeight,
-
- // cells
- cellShape,
- cellOpacity,
- cellBorderWidth,
- getCellBorderColor,
-
- // axes & grid
- axisTop,
- axisRight,
- axisBottom,
- axisLeft,
- enableGridX,
- enableGridY,
-
- // labels
- getLabelTextColor,
-
- // theming
- theme,
- colorScale,
-
- // motion
- animate,
- motionStiffness,
- motionDamping,
-
- // interactivity
- isInteractive,
-}) => {
- let Cell
- if (cellShape === 'rect') {
- Cell = HeatMapCellRect
- } else if (cellShape === 'circle') {
- Cell = HeatMapCellCircle
- } else {
- Cell = cellShape
+class HeatMap extends Component {
+ static propTypes = HeatMapPropTypes
+
+ handleNodeHover = (showTooltip, node, event) => {
+ const { setCurrentNode, theme } = this.props
+ setCurrentNode(node)
+ showTooltip(, event)
}
- const nodes = data.reduce((acc, d) => {
- keys.forEach(key => {
- acc.push({
- key: `${key}.${getIndex(d)}`,
- xKey: key,
- yKey: getIndex(d),
- x: xScale(key),
- y: yScale(getIndex(d)),
- width: sizeScale ? Math.min(sizeScale(d[key]) * cellWidth, cellWidth) : cellWidth,
- height: sizeScale
- ? Math.min(sizeScale(d[key]) * cellHeight, cellHeight)
- : cellHeight,
- value: d[key],
- color: colorScale(d[key]),
- })
- })
-
- return acc
- }, [])
-
- const motionProps = {
- animate,
- motionDamping,
- motionStiffness,
+ handleNodeLeave = hideTooltip => {
+ this.props.setCurrentNode(null)
+ hideTooltip()
}
- let cells
- if (animate === true) {
- cells = (
- {
- return {
- key: node.key,
- data: node,
- style: {
- x: spring(node.x, motionProps),
- y: spring(node.y, motionProps),
- width: spring(node.width, motionProps),
- height: spring(node.height, motionProps),
- ...colorMotionSpring(node.color, motionProps),
- },
- }
- })}
- >
- {interpolatedStyles => {
+ render() {
+ const {
+ xScale,
+ yScale,
+ offsetX,
+ offsetY,
+
+ margin,
+ width,
+ height,
+ outerWidth,
+ outerHeight,
+
+ // cells
+ cellShape,
+ cellBorderWidth,
+ getCellBorderColor,
+
+ // axes & grid
+ axisTop,
+ axisRight,
+ axisBottom,
+ axisLeft,
+ enableGridX,
+ enableGridY,
+
+ // labels
+ enableLabels,
+ getLabelTextColor,
+
+ // theming
+ theme,
+
+ // motion
+ animate,
+ motionStiffness,
+ motionDamping,
+ boundSpring,
+
+ // interactivity
+ isInteractive,
+ } = this.props
+
+ let Cell
+ if (cellShape === 'rect') {
+ Cell = HeatMapCellRect
+ } else if (cellShape === 'circle') {
+ Cell = HeatMapCellCircle
+ } else {
+ Cell = cellShape
+ }
+
+ const nodes = computeNodes(this.props)
+
+ const motionProps = {
+ animate,
+ motionDamping,
+ motionStiffness,
+ }
+
+ return (
+
+ {({ showTooltip, hideTooltip }) => {
+ const onHover = partial(this.handleNodeHover, showTooltip)
+ const onLeave = partial(this.handleNodeLeave, hideTooltip)
+
return (
-
- {interpolatedStyles.map(({ key, style, data: node }) => {
- const color = getInterpolatedColor(style)
-
- return React.createElement(Cell, {
- key,
- value: node.value,
- x: style.x,
- y: style.y,
- width: Math.max(style.width, 0),
- height: Math.max(style.height, 0),
- color,
- opacity: cellOpacity,
- borderWidth: cellBorderWidth,
- borderColor: getCellBorderColor({ ...node, color }),
- textColor: getLabelTextColor({ ...node, color }),
- })
+
+ >
+
+
+ {!animate &&
+ nodes.map(node =>
+ React.createElement(Cell, {
+ key: node.key,
+ value: node.value,
+ x: node.x,
+ y: node.y,
+ width: node.width,
+ height: node.height,
+ color: node.color,
+ opacity: node.opacity,
+ borderWidth: cellBorderWidth,
+ borderColor: getCellBorderColor(node),
+ textColor: getLabelTextColor(node),
+ onHover: partial(onHover, node),
+ onLeave,
+ })
+ )}
+
+ {animate === true &&
+ {
+ return {
+ key: node.key,
+ data: node,
+ style: {
+ x: boundSpring(node.x),
+ y: boundSpring(node.y),
+ width: boundSpring(node.width),
+ height: boundSpring(node.height),
+ opacity: boundSpring(node.opacity),
+ ...colorMotionSpring(node.color, {
+ damping: motionDamping,
+ stiffness: motionStiffness,
+ }),
+ },
+ }
+ })}
+ >
+ {interpolatedStyles => {
+ return (
+
+ {interpolatedStyles.map(
+ ({ key, style, data: node }) => {
+ const color = getInterpolatedColor(style)
+
+ return React.createElement(Cell, {
+ key,
+ value: node.value,
+ x: style.x,
+ y: style.y,
+ width: Math.max(style.width, 0),
+ height: Math.max(style.height, 0),
+ color,
+ opacity: style.opacity,
+ borderWidth: cellBorderWidth,
+ borderColor: getCellBorderColor({
+ ...node,
+ color,
+ }),
+ textColor: getLabelTextColor({
+ ...node,
+ color,
+ }),
+ onHover: partial(onHover, node),
+ onLeave,
+ })
+ }
+ )}
+
+ )
+ }}
+ }
+
)
}}
-
+
)
- } else {
- cells = nodes.map(node => {
- return React.createElement(Cell, {
- key: node.key,
- value: node.value,
- x: node.x,
- y: node.y,
- width: node.width,
- height: node.height,
- color: node.color,
- opacity: cellOpacity,
- borderWidth: cellBorderWidth,
- borderColor: getCellBorderColor(node),
- textColor: getLabelTextColor(node),
- })
- })
}
-
- return (
-
- {({ showTooltip, hideTooltip }) => {
- return (
-
-
-
- {cells}
-
- )
- }}
-
- )
}
-HeatMap.propTypes = HeatMapPropTypes
-
export default enhance(HeatMap)
diff --git a/src/components/charts/heatmap/HeatMapCanvas.js b/src/components/charts/heatmap/HeatMapCanvas.js
index e999f3867..bbbf3a2b4 100644
--- a/src/components/charts/heatmap/HeatMapCanvas.js
+++ b/src/components/charts/heatmap/HeatMapCanvas.js
@@ -12,7 +12,7 @@ import { renderAxes } from '../../../lib/canvas/axes'
import { getRelativeCursor, cursorInRect } from '../../../lib/interactivity'
import { renderRect, renderCircle } from '../../../lib/canvas/charts/heatmap'
import { computeNodes } from '../../../lib/charts/heatmap'
-import BasicTooltip from '../../tooltip/BasicTooltip'
+import HeatMapCellTooltip from './HeatMapCellTooltip'
import Container from '../Container'
import { HeatMapPropTypes } from './props'
import enhance from './enhance'
@@ -114,16 +114,7 @@ class HeatMapCanvas extends Component {
if (node !== undefined) {
setCurrentNode(node)
- showTooltip(
- ,
- event
- )
+ showTooltip(, event)
} else {
setCurrentNode(null)
hideTooltip()
diff --git a/src/components/charts/heatmap/HeatMapCellCircle.js b/src/components/charts/heatmap/HeatMapCellCircle.js
index 1a7ebc873..5f95fb6b5 100644
--- a/src/components/charts/heatmap/HeatMapCellCircle.js
+++ b/src/components/charts/heatmap/HeatMapCellCircle.js
@@ -10,6 +10,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import pure from 'recompose/pure'
+const style = { cursor: 'pointer' }
+
const HeatMapCellCircle = ({
value,
x,
@@ -21,22 +23,33 @@ const HeatMapCellCircle = ({
borderWidth,
borderColor,
textColor,
-}) => {
- return (
-
-
-
- {value}
-
-
- )
-}
+ onHover,
+ onLeave,
+}) =>
+
+
+
+ {value}
+
+
HeatMapCellCircle.propTypes = {
value: PropTypes.number.isRequired,
@@ -49,6 +62,8 @@ HeatMapCellCircle.propTypes = {
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.string.isRequired,
textColor: PropTypes.string.isRequired,
+ onHover: PropTypes.func.isRequired,
+ onLeave: PropTypes.func.isRequired,
}
export default pure(HeatMapCellCircle)
diff --git a/src/components/charts/heatmap/HeatMapCellRect.js b/src/components/charts/heatmap/HeatMapCellRect.js
index dd9fc6a20..546e2de8c 100644
--- a/src/components/charts/heatmap/HeatMapCellRect.js
+++ b/src/components/charts/heatmap/HeatMapCellRect.js
@@ -10,6 +10,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import pure from 'recompose/pure'
+const style = { cursor: 'pointer' }
+
const HeatMapCellRect = ({
value,
x,
@@ -21,25 +23,36 @@ const HeatMapCellRect = ({
borderWidth,
borderColor,
textColor,
-}) => {
- return (
-
-
-
- {value}
-
-
- )
-}
+ onHover,
+ onLeave,
+}) =>
+
+
+
+ {value}
+
+
HeatMapCellRect.propTypes = {
value: PropTypes.number.isRequired,
@@ -52,6 +65,8 @@ HeatMapCellRect.propTypes = {
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.string.isRequired,
textColor: PropTypes.string.isRequired,
+ onHover: PropTypes.func.isRequired,
+ onLeave: PropTypes.func.isRequired,
}
export default pure(HeatMapCellRect)
diff --git a/src/components/charts/heatmap/HeatMapCellTooltip.js b/src/components/charts/heatmap/HeatMapCellTooltip.js
new file mode 100644
index 000000000..c6b58ab75
--- /dev/null
+++ b/src/components/charts/heatmap/HeatMapCellTooltip.js
@@ -0,0 +1,22 @@
+/*
+ * 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 HeatMapCellTooltip = ({ node, theme }) =>
+
+
+export default pure(HeatMapCellTooltip)
diff --git a/src/hocs/withMotion.js b/src/hocs/withMotion.js
index 2d0fddd95..12bd52979 100644
--- a/src/hocs/withMotion.js
+++ b/src/hocs/withMotion.js
@@ -6,10 +6,12 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-import { isEqual } from 'lodash'
+import { partialRight } from 'lodash'
import compose from 'recompose/compose'
import defaultProps from 'recompose/defaultProps'
+import withPropsOnChange from 'recompose/withPropsOnChange'
import setPropTypes from 'recompose/setPropTypes'
+import { spring } from 'react-motion'
import { motionPropTypes } from '../props'
import Nivo from '../Nivo'
@@ -18,7 +20,16 @@ export default () =>
setPropTypes(motionPropTypes),
defaultProps({
animate: true,
- motionStiffness: Nivo.defaults.motionStiffness,
motionDamping: Nivo.defaults.motionDamping,
- })
+ motionStiffness: Nivo.defaults.motionStiffness,
+ }),
+ withPropsOnChange(
+ ['motionDamping', 'motionStiffness'],
+ ({ motionDamping, motionStiffness }) => ({
+ boundSpring: partialRight(spring, {
+ damping: motionDamping,
+ stiffness: motionStiffness,
+ }),
+ })
+ )
)
diff --git a/src/lib/charts/heatmap/index.js b/src/lib/charts/heatmap/index.js
index 1e6c185b4..cd648a1ee 100644
--- a/src/lib/charts/heatmap/index.js
+++ b/src/lib/charts/heatmap/index.js
@@ -21,6 +21,7 @@ export const computeNodes = ({
xScale,
yScale,
sizeScale,
+ cellOpacity,
cellWidth,
cellHeight,
colorScale,
@@ -41,6 +42,7 @@ export const computeNodes = ({
: cellHeight
const node = {
+ key: `${key}.${getIndex(d)}`,
xKey: key,
yKey: getIndex(d),
x: xScale(key),
@@ -51,7 +53,7 @@ export const computeNodes = ({
color: colorScale(d[key]),
}
- let opacity = 1
+ let opacity = cellOpacity
if (currentNode) {
opacity = isHoverTarget(node, currentNode)
? cellHoverOpacity