Skip to content

Commit

Permalink
feat(radar): replace react-motion by react-spring
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Jun 20, 2020
1 parent 62b2e59 commit 9e9c498
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 278 deletions.
44 changes: 28 additions & 16 deletions packages/core/src/components/dots/DotsItem.js
Expand Up @@ -8,7 +8,9 @@
*/
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { useSpring, animated } from 'react-spring'
import { dotsThemePropType } from '../../theming'
import { useMotionConfig } from '../../motion'
import DotsItemSymbol from './DotsItemSymbol'

const DotsItem = ({
Expand All @@ -24,22 +26,32 @@ const DotsItem = ({
labelTextAnchor,
labelYOffset,
theme,
}) => (
<g transform={`translate(${x}, ${y})`} style={{ pointerEvents: 'none' }}>
{React.createElement(symbol, {
size,
color,
datum,
borderWidth,
borderColor,
})}
{label && (
<text textAnchor={labelTextAnchor} y={labelYOffset} style={theme.dots.text}>
{label}
</text>
)}
</g>
)
}) => {
const { animate, config: springConfig } = useMotionConfig()

const animatedProps = useSpring({
transform: `translate(${x}, ${y})`,
config: springConfig,
immediate: !animate,
})

return (
<animated.g transform={animatedProps.transform} style={{ pointerEvents: 'none' }}>
{React.createElement(symbol, {
size,
color,
datum,
borderWidth,
borderColor,
})}
{label && (
<text textAnchor={labelTextAnchor} y={labelYOffset} style={theme.dots.text}>
{label}
</text>
)}
</animated.g>
)
}

DotsItem.propTypes = {
x: PropTypes.number.isRequired,
Expand Down
4 changes: 2 additions & 2 deletions packages/radar/package.json
Expand Up @@ -24,16 +24,16 @@
],
"dependencies": {
"@nivo/colors": "0.62.0",
"@nivo/core": "0.62.0",
"@nivo/legends": "0.62.0",
"@nivo/tooltip": "0.62.0",
"d3-format": "^1.4.4",
"d3-scale": "^3.0.0",
"d3-shape": "^1.3.5",
"lodash": "^4.17.11",
"react-motion": "^0.5.2"
"react-spring": "^8.0.27"
},
"peerDependencies": {
"@nivo/core": "0.62.0",
"prop-types": ">= 15.5.10 < 16.0.0",
"react": ">= 16.8.4 < 17.0.0"
},
Expand Down
152 changes: 57 additions & 95 deletions packages/radar/src/RadarDots.js
Expand Up @@ -6,142 +6,104 @@
* 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, { useMemo } from 'react'
import PropTypes from 'prop-types'
import { TransitionMotion, spring } from 'react-motion'
import {
useTheme,
useMotionConfig,
positionFromAngle,
getLabelGenerator,
DotsItem,
} from '@nivo/core'
import { useTheme, positionFromAngle, getLabelGenerator, DotsItem } from '@nivo/core'
import { getInheritedColorGenerator, inheritedColorPropType } from '@nivo/colors'

const RadarDots = ({
data,
keys,
getIndex,

colorByKey,

radiusScale,
angleStep,

symbol,
size,
color,
borderWidth,
borderColor,

enableLabel,
label,
labelFormat,
labelYOffset,
}) => {
const theme = useTheme()
const { animate, springConfig } = useMotionConfig()
const fillColor = getInheritedColorGenerator(color, theme)
const strokeColor = getInheritedColorGenerator(borderColor, theme)
const getLabel = getLabelGenerator(label, labelFormat)

const points = data.reduce((acc, datum, i) => {
const index = getIndex(datum)
keys.forEach(key => {
const pointData = {
index,
key,
value: datum[key],
color: colorByKey[key],
}
acc.push({
key: `${key}.${index}`,
label: enableLabel ? getLabel(pointData) : null,
style: {
fill: fillColor(pointData),
stroke: strokeColor(pointData),
...positionFromAngle(angleStep * i - Math.PI / 2, radiusScale(datum[key])),
},
data: pointData,
})
})

return acc
}, [])

if (animate !== true) {
return (
<g>
{points.map(point => (
<DotsItem
key={point.key}
x={point.style.x}
y={point.style.y}
symbol={symbol}
size={size}
color={point.style.fill}
borderWidth={borderWidth}
borderColor={point.style.stroke}
label={point.label}
labelYOffset={labelYOffset}
theme={theme}
datum={point.data}
/>
))}
</g>
)
}
const points = useMemo(
() =>
data.reduce((acc, datum, i) => {
const index = getIndex(datum)
keys.forEach(key => {
const pointData = {
index,
key,
value: datum[key],
color: colorByKey[key],
}
acc.push({
key: `${key}.${index}`,
label: enableLabel ? getLabel(pointData) : null,
style: {
fill: fillColor(pointData),
stroke: strokeColor(pointData),
...positionFromAngle(
angleStep * i - Math.PI / 2,
radiusScale(datum[key])
),
},
data: pointData,
})
})

return (
<TransitionMotion
styles={points.map(point => ({
key: point.key,
data: point,
style: {
x: spring(point.style.x, springConfig),
y: spring(point.style.y, springConfig),
size: spring(size, springConfig),
},
}))}
>
{interpolatedStyles => (
<g>
{interpolatedStyles.map(({ key, style, data: point }) => (
<DotsItem
key={key}
{...style}
symbol={symbol}
color={point.style.fill}
borderWidth={borderWidth}
borderColor={point.style.stroke}
label={point.label}
labelYOffset={labelYOffset}
theme={theme}
datum={point.data}
/>
))}
</g>
)}
</TransitionMotion>
return acc
}, []),
[
data,
getIndex,
colorByKey,
enableLabel,
getLabel,
fillColor,
strokeColor,
angleStep,
radiusScale,
]
)

return points.map(point => (
<DotsItem
key={point.key}
x={point.style.x}
y={point.style.y}
symbol={symbol}
size={size}
color={point.style.fill}
borderWidth={borderWidth}
borderColor={point.style.stroke}
label={point.label}
labelYOffset={labelYOffset}
theme={theme}
datum={point.data}
/>
))
}

RadarDots.propTypes = {
data: PropTypes.arrayOf(PropTypes.object).isRequired,
keys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).isRequired,
getIndex: PropTypes.func.isRequired,

colorByKey: PropTypes.object.isRequired,

radiusScale: PropTypes.func.isRequired,
angleStep: PropTypes.number.isRequired,

symbol: PropTypes.func,
size: PropTypes.number.isRequired,
color: inheritedColorPropType.isRequired,
borderWidth: PropTypes.number.isRequired,
borderColor: inheritedColorPropType.isRequired,

enableLabel: PropTypes.bool.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
labelFormat: PropTypes.string,
Expand Down
7 changes: 3 additions & 4 deletions packages/radar/src/RadarGrid.js
Expand Up @@ -8,7 +8,6 @@
*/
import React, { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import range from 'lodash/range'
import { positionFromAngle, useTheme } from '@nivo/core'
import RadialGridLabels from './RadarGridLabels'
import RadarGridLevels from './RadarGridLevels'
Expand All @@ -17,10 +16,10 @@ const RadarGrid = memo(({ indices, levels, shape, radius, angleStep, label, labe
const theme = useTheme()
const { radii, angles } = useMemo(() => {
return {
radii: range(levels)
.map(i => (radius / levels) * (i + 1))
radii: Array.from({ length: levels })
.map((_, i) => (radius / levels) * (i + 1))
.reverse(),
angles: range(indices.length).map(i => i * angleStep - Math.PI / 2),
angles: Array.from({ length: indices.length }, (_, i) => i * angleStep - Math.PI / 2),
}
}, [indices, levels, radius, angleStep])

Expand Down
53 changes: 21 additions & 32 deletions packages/radar/src/RadarGridLabels.js
Expand Up @@ -8,7 +8,7 @@
*/
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { TransitionMotion, spring } from 'react-motion'
import { useSprings, animated } from 'react-spring'
import { useTheme, useMotionConfig, positionFromAngle, radiansToDegrees } from '@nivo/core'

const textAnchorFromAngle = _angle => {
Expand All @@ -19,9 +19,8 @@ const textAnchorFromAngle = _angle => {
}

const renderLabel = (label, theme, labelComponent) => {
let labelNode
if (labelComponent === undefined) {
labelNode = (
return (
<text
style={theme.axis.ticks.text}
dominantBaseline="central"
Expand All @@ -30,20 +29,14 @@ const renderLabel = (label, theme, labelComponent) => {
{label.id}
</text>
)
} else {
labelNode = React.createElement(labelComponent, label)
}

return (
<g key={label.id} transform={`translate(${label.x}, ${label.y})`}>
{labelNode}
</g>
)
return React.createElement(labelComponent, label)
}

const RadarGridLabels = memo(({ radius, angles, indices, label: labelComponent, labelOffset }) => {
const theme = useTheme()
const { animate, springConfig } = useMotionConfig()
const { animate, config: springConfig } = useMotionConfig()

const labels = indices.map((index, i) => {
const position = positionFromAngle(angles[i], radius + labelOffset)
Expand All @@ -57,28 +50,24 @@ const RadarGridLabels = memo(({ radius, angles, indices, label: labelComponent,
}
})

if (animate !== true) {
return <g>{labels.map(label => renderLabel(label, theme, labelComponent))}</g>
}

return (
<TransitionMotion
styles={labels.map(label => ({
key: label.id,
data: label,
style: {
x: spring(label.x, springConfig),
y: spring(label.y, springConfig),
},
}))}
>
{interpolatedStyles => (
<g>
{interpolatedStyles.map(({ data }) => renderLabel(data, theme, labelComponent))}
</g>
)}
</TransitionMotion>
const springs = useSprings(
labels.length,
labels.map(label => ({
transform: `translate(${label.x}, ${label.y})`,
config: springConfig,
immediate: !animate,
}))
)

return springs.map((props, index) => {
const label = labels[index]

return (
<animated.g key={label.id} transform={props.transform}>
{renderLabel(label, theme, labelComponent)}
</animated.g>
)
})
})

RadarGridLabels.displayName = 'RadarGridLabels'
Expand Down

0 comments on commit 9e9c498

Please sign in to comment.