Skip to content

Commit

Permalink
feat(heatmap): move cells computation to main hook
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Jun 20, 2020
1 parent f823ea6 commit 80701b1
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 211 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/hocs/withMotion.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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 { motionPropTypes } from '../motion'
import { defaultAnimate, defaultMotionDamping, defaultMotionStiffness } from '../defaults'

export default () =>
Expand Down
21 changes: 14 additions & 7 deletions packages/core/src/motion/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ export const MotionConfigProvider = ({ children, animate, stiffness, damping, co
return <motionConfigContext.Provider value={value}>{children}</motionConfigContext.Provider>
}

MotionConfigProvider.propTypes = {
children: PropTypes.node.isRequired,
export const motionPropTypes = {
animate: PropTypes.bool,
stiffness: PropTypes.number,
damping: PropTypes.number,
config: PropTypes.oneOfType([
motionStiffness: PropTypes.number,
motionDamping: PropTypes.number,
motionConfig: PropTypes.oneOfType([
PropTypes.oneOf(Object.keys(presets)),
PropTypes.shape({
mass: PropTypes.number,
Expand All @@ -51,11 +50,19 @@ MotionConfigProvider.propTypes = {
]),
}

export const MotionDefaultProps = {
MotionConfigProvider.propTypes = {
children: PropTypes.node.isRequired,
animate: motionPropTypes.animate,
stiffness: motionPropTypes.motionStiffness,
damping: motionPropTypes.motionDamping,
config: motionPropTypes.motionConfig,
}

export const motionDefaultProps = {
animate: true,
stiffness: 90,
damping: 15,
config: 'default',
}

MotionConfigProvider.defaultProps = MotionDefaultProps
MotionConfigProvider.defaultProps = motionDefaultProps
8 changes: 0 additions & 8 deletions packages/core/src/props/index.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 PropTypes from 'prop-types'
import { MotionDefaultProps } from '../motion'

export const marginPropType = PropTypes.shape({
top: PropTypes.number,
Expand All @@ -16,13 +15,6 @@ export const marginPropType = PropTypes.shape({
left: PropTypes.number,
}).isRequired

export const motionPropTypes = {
animate: MotionDefaultProps.animate,
motionStiffness: MotionDefaultProps.stiffness,
motionDamping: MotionDefaultProps.damping,
motionConfig: MotionDefaultProps.config,
}

export const blendModes = [
'normal',
'multiply',
Expand Down
75 changes: 29 additions & 46 deletions packages/heatmap/src/HeatMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +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 React, { useCallback } from 'react'
import { SvgWrapper, withContainer, useDimensions } from '@nivo/core'
import { Axes, Grid } from '@nivo/axes'
import { useTooltip } from '@nivo/tooltip'
import { HeatMapPropTypes, HeatMapDefaultProps } from './props'
import { useHeatMap } from './hooks'
import computeNodes from './computeNodes'
import HeatMapCells from './HeatMapCells'
import HeatMapCellRect from './HeatMapCellRect'
import HeatMapCellCircle from './HeatMapCellCircle'
Expand Down Expand Up @@ -52,39 +51,19 @@ const HeatMap = ({
tooltipFormat,
tooltip,
}) => {
const { showTooltipFromEvent, hideTooltip } = useTooltip()

const handleNodeHover = (node, event) => {
setCurrentNode(node)
showTooltipFromEvent(
<HeatMapCellTooltip node={node} format={tooltipFormat} tooltip={tooltip} />,
event
)
}

const handleNodeLeave = () => {
setCurrentNode(null)
hideTooltip()
}

const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
width,
height,
partialMargin
)

const {
getIndex,
cells,
xScale,
yScale,
cellWidth,
cellHeight,
offsetX,
offsetY,
sizeScale,
currentNode,
setCurrentNode,
colorScale,
setCurrentCell,
getCellBorderColor,
getLabelTextColor,
} = useHeatMap({
Expand All @@ -99,10 +78,33 @@ const HeatMap = ({
forceSquare,
sizeVariation,
colors,
nanColor,
cellOpacity,
cellBorderColor,
labelTextColor,
hoverTarget,
cellHoverOpacity,
cellHoverOthersOpacity,
})

const { showTooltipFromEvent, hideTooltip } = useTooltip()

const handleCellHover = useCallback(
(cell, event) => {
setCurrentCell(cell)
showTooltipFromEvent(
<HeatMapCellTooltip cell={cell} format={tooltipFormat} tooltip={tooltip} />,
event
)
},
[setCurrentCell, showTooltipFromEvent, tooltipFormat, tooltip]
)

const handleCellLeave = useCallback(() => {
setCurrentCell(null)
hideTooltip()
}, [setCurrentCell, hideTooltip])

let cellComponent
if (cellShape === 'rect') {
cellComponent = HeatMapCellRect
Expand All @@ -112,25 +114,6 @@ const HeatMap = ({
cellComponent = cellShape
}

const nodes = computeNodes({
data,
keys,
getIndex,
xScale,
yScale,
sizeScale,
cellOpacity,
cellWidth,
cellHeight,
colorScale,
nanColor,
getLabelTextColor,
currentNode,
hoverTarget,
cellHoverOpacity,
cellHoverOthersOpacity,
})

return (
<SvgWrapper
width={outerWidth}
Expand All @@ -157,14 +140,14 @@ const HeatMap = ({
left={axisLeft}
/>
<HeatMapCells
nodes={nodes}
cells={cells}
cellComponent={cellComponent}
cellBorderWidth={cellBorderWidth}
getCellBorderColor={getCellBorderColor}
enableLabels={enableLabels}
getLabelTextColor={getLabelTextColor}
handleNodeHover={isInteractive ? handleNodeHover : undefined}
handleNodeLeave={isInteractive ? handleNodeLeave : undefined}
handleCellHover={isInteractive ? handleCellHover : undefined}
handleCellLeave={isInteractive ? handleCellLeave : undefined}
onClick={isInteractive ? onClick : undefined}
/>
</SvgWrapper>
Expand Down
70 changes: 21 additions & 49 deletions packages/heatmap/src/HeatMapCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { useTooltip } from '@nivo/tooltip'
import { useHeatMap } from './hooks'
import { HeatMapDefaultProps, HeatMapPropTypes } from './props'
import { renderRect, renderCircle } from './canvas'
import computeNodes from './computeNodes'
import HeatMapCellTooltip from './HeatMapCellTooltip'

const HeatMapCanvas = ({
Expand Down Expand Up @@ -61,20 +60,7 @@ const HeatMapCanvas = ({
partialMargin
)

const {
getIndex,
xScale,
yScale,
cellWidth,
cellHeight,
offsetX,
offsetY,
sizeScale,
currentNode,
setCurrentNode,
colorScale,
getLabelTextColor,
} = useHeatMap({
const { cells, xScale, yScale, offsetX, offsetY, currentCell, setCurrentCell } = useHeatMap({
data,
keys,
indexBy,
Expand All @@ -86,24 +72,10 @@ const HeatMapCanvas = ({
forceSquare,
sizeVariation,
colors,
nanColor,
cellOpacity,
cellBorderColor,
labelTextColor,
})

const nodes = computeNodes({
data,
keys,
getIndex,
xScale,
yScale,
sizeScale,
cellOpacity,
cellWidth,
cellHeight,
colorScale,
nanColor,
getLabelTextColor,
currentNode,
hoverTarget,
cellHoverOpacity,
cellHoverOthersOpacity,
Expand Down Expand Up @@ -138,18 +110,18 @@ const HeatMapCanvas = ({
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'

let renderNode
let renderCell
if (cellShape === 'rect') {
renderNode = renderRect
renderCell = renderRect
} else {
renderNode = renderCircle
renderCell = renderCircle
}
nodes.forEach(node => {
renderNode(ctx, { enableLabels, theme }, node)
cells.forEach(cell => {
renderCell(ctx, { enableLabels, theme }, cell)
})
}, [
canvasEl,
nodes,
cells,
outerWidth,
outerHeight,
innerWidth,
Expand All @@ -173,7 +145,7 @@ const HeatMapCanvas = ({
event => {
const [x, y] = getRelativeCursor(canvasEl.current, event)

const node = nodes.find(node =>
const cell = cells.find(node =>
isCursorInRect(
node.x + margin.left + offsetX - node.width / 2,
node.y + margin.top + offsetY - node.height / 2,
Expand All @@ -183,42 +155,42 @@ const HeatMapCanvas = ({
y
)
)
if (node !== undefined) {
setCurrentNode(node)
if (cell !== undefined) {
setCurrentCell(cell)
showTooltipFromEvent(
<HeatMapCellTooltip node={node} tooltip={tooltip} format={tooltipFormat} />,
<HeatMapCellTooltip cell={cell} tooltip={tooltip} format={tooltipFormat} />,
event
)
} else {
setCurrentNode(null)
setCurrentCell(null)
hideTooltip()
}
},
[
canvasEl,
nodes,
cells,
margin,
offsetX,
offsetY,
setCurrentNode,
setCurrentCell,
showTooltipFromEvent,
hideTooltip,
tooltip,
]
)

const handleMouseLeave = useCallback(() => {
setCurrentNode(null)
setCurrentCell(null)
hideTooltip()
}, [setCurrentNode, hideTooltip])
}, [setCurrentCell, hideTooltip])

const handleClick = useCallback(
event => {
if (currentNode === null) return
if (currentCell === null) return

onClick(currentNode, event)
onClick(currentCell, event)
},
[currentNode, onClick]
[currentCell, onClick]
)

return (
Expand Down
12 changes: 6 additions & 6 deletions packages/heatmap/src/HeatMapCellTooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { BasicTooltip } from '@nivo/tooltip'

const HeatMapCellTooltip = ({ node, format, tooltip }) => (
const HeatMapCellTooltip = ({ cell, format, tooltip }) => (
<BasicTooltip
id={`${node.yKey} - ${node.xKey}`}
value={node.value}
id={`${cell.yKey} - ${cell.xKey}`}
value={cell.value}
enableChip={true}
color={node.color}
color={cell.color}
format={format}
renderContent={typeof tooltip === 'function' ? tooltip.bind(null, { ...node }) : null}
renderContent={typeof tooltip === 'function' ? tooltip.bind(null, { ...cell }) : null}
/>
)

HeatMapCellTooltip.propTypes = {
node: PropTypes.shape({
cell: PropTypes.shape({
xKey: PropTypes.string.isRequired,
yKey: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
Expand Down
Loading

0 comments on commit 80701b1

Please sign in to comment.