diff --git a/packages/axes/package.json b/packages/axes/package.json
index 716954fee..2c1088c84 100644
--- a/packages/axes/package.json
+++ b/packages/axes/package.json
@@ -21,15 +21,14 @@
"dist/"
],
"dependencies": {
- "@nivo/core": "0.62.0",
"@nivo/scales": "0.62.0",
"d3-format": "^1.4.4",
"d3-time": "^1.0.11",
"d3-time-format": "^2.1.3",
- "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"
},
diff --git a/packages/axes/src/components/Axis.js b/packages/axes/src/components/Axis.js
index 6cb01b0a5..8a77bb191 100644
--- a/packages/axes/src/components/Axis.js
+++ b/packages/axes/src/components/Axis.js
@@ -6,28 +6,14 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-import React, { memo, Fragment, useMemo } from 'react'
+import React, { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { Motion, TransitionMotion, spring } from 'react-motion'
+import { useSpring, useTransition, animated } from 'react-spring'
import { useTheme, useMotionConfig } from '@nivo/core'
import { computeCartesianTicks, getFormatter } from '../compute'
import { axisPropTypes } from '../props'
import AxisTick from './AxisTick'
-const willEnter = () => ({
- rotate: 0,
- opacity: 0,
- x: 0,
- y: 0,
-})
-
-const willLeave = springConfig => ({ style: { x, y, rotate } }) => ({
- rotate,
- opacity: spring(0, springConfig),
- x: spring(x.val, springConfig),
- y: spring(y.val, springConfig),
-})
-
const defaultTickRenderer = props =>
const Axis = ({
@@ -49,7 +35,6 @@ const Axis = ({
onClick,
}) => {
const theme = useTheme()
- const { animate, springConfig } = useMotionConfig()
const formatValue = useMemo(() => getFormatter(format, scale), [format, scale])
@@ -109,80 +94,68 @@ const Axis = ({
)
}
- if (animate !== true) {
- return (
-
- {ticks.map((tick, tickIndex) =>
- React.createElement(renderTick, {
- tickIndex,
- format: formatValue,
- rotate: tickRotation,
- textBaseline,
- textAnchor: textAlign,
- ...tick,
- ...(onClick ? { onClick } : {}),
- })
- )}
-
- {legendNode}
-
- )
- }
+ const { animate, config: springConfig } = useMotionConfig()
+
+ const animatedProps = useSpring({
+ transform: `translate(${x},${y})`,
+ lineX2: axis === 'x' ? length : 0,
+ lineY2: axis === 'x' ? 0 : length,
+ config: springConfig,
+ immediate: !animate,
+ })
+
+ const transitions = useTransition(ticks, tick => tick.key, {
+ initial: tick => ({
+ opacity: 1,
+ transform: `translate(${tick.x},${tick.y})`,
+ textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
+ }),
+ from: tick => ({
+ opacity: 0,
+ transform: `translate(${tick.x},${tick.y})`,
+ textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
+ }),
+ enter: tick => ({
+ opacity: 1,
+ transform: `translate(${tick.x},${tick.y})`,
+ textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
+ }),
+ update: tick => ({
+ opacity: 1,
+ transform: `translate(${tick.x},${tick.y})`,
+ textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
+ }),
+ leave: {
+ opacity: 0,
+ },
+ config: springConfig,
+ immediate: !animate,
+ })
return (
-
- {xy => (
-
- ({
- key: `${tick.key}`,
- data: tick,
- style: {
- opacity: spring(1, springConfig),
- x: spring(tick.x, springConfig),
- y: spring(tick.y, springConfig),
- rotate: spring(tickRotation, springConfig),
- },
- }))}
- >
- {interpolatedStyles => (
-
- {interpolatedStyles.map(({ style, data: tick }, tickIndex) =>
- React.createElement(renderTick, {
- tickIndex,
- format: formatValue,
- textBaseline,
- textAnchor: textAlign,
- ...tick,
- ...style,
- ...(onClick ? { onClick } : {}),
- })
- )}
-
- )}
-
-
- {values => (
-
- )}
-
- {legendNode}
-
- )}
-
+
+ {transitions.map(({ item: tick, props: transitionProps, key }, tickIndex) => {
+ return React.createElement(renderTick, {
+ tickIndex,
+ format: formatValue,
+ rotate: tickRotation,
+ textBaseline,
+ textAnchor: textAlign,
+ animatedProps: transitionProps,
+ ...tick,
+ ...(onClick ? { onClick } : {}),
+ key,
+ })
+ })}
+
+ {legendNode}
+
)
}
diff --git a/packages/axes/src/components/AxisTick.js b/packages/axes/src/components/AxisTick.js
index 47d3bdbad..c708cf2f5 100644
--- a/packages/axes/src/components/AxisTick.js
+++ b/packages/axes/src/components/AxisTick.js
@@ -7,23 +7,19 @@
* file that was distributed with this source code.
*/
import React, { memo } from 'react'
+import { animated } from 'react-spring'
import PropTypes from 'prop-types'
import { useTheme } from '@nivo/core'
const AxisTick = ({
value: _value,
- x,
- y,
- opacity,
- rotate,
format,
lineX,
lineY,
onClick,
- textX,
- textY,
textBaseline,
textAnchor,
+ animatedProps,
}) => {
const theme = useTheme()
@@ -32,27 +28,27 @@ const AxisTick = ({
value = format(value)
}
- let gStyle = { opacity }
+ let gStyle = { opacity: animatedProps.opacity }
if (onClick) {
gStyle['cursor'] = 'pointer'
}
return (
- onClick(e, value) } : {})}
style={gStyle}
>
-
{value}
-
-
+
+
)
}
@@ -71,6 +67,7 @@ AxisTick.propTypes = {
opacity: PropTypes.number.isRequired,
rotate: PropTypes.number.isRequired,
onClick: PropTypes.func,
+ animatedProps: PropTypes.object.isRequired,
}
AxisTick.defaultProps = {
opacity: 1,
diff --git a/packages/axes/src/components/GridLine.js b/packages/axes/src/components/GridLine.js
index 6300f032c..1e3dbafa9 100644
--- a/packages/axes/src/components/GridLine.js
+++ b/packages/axes/src/components/GridLine.js
@@ -8,14 +8,21 @@
*/
import React, { memo } from 'react'
import PropTypes from 'prop-types'
+import { animated } from 'react-spring'
+import { useTheme } from '@nivo/core'
-const GridLine = props =>
+const GridLine = ({ animatedProps }) => {
+ const theme = useTheme()
+
+ return
+}
GridLine.propTypes = {
x1: PropTypes.number.isRequired,
x2: PropTypes.number.isRequired,
y1: PropTypes.number.isRequired,
y2: PropTypes.number.isRequired,
+ animatedProps: PropTypes.object.isRequired,
}
GridLine.defaultProps = {
x1: 0,
diff --git a/packages/axes/src/components/GridLines.js b/packages/axes/src/components/GridLines.js
index 3bfd866e9..cc1942cef 100644
--- a/packages/axes/src/components/GridLines.js
+++ b/packages/axes/src/components/GridLines.js
@@ -6,75 +6,57 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-import React, { memo, useMemo } from 'react'
+import React, { memo } from 'react'
import PropTypes from 'prop-types'
-import { TransitionMotion, spring } from 'react-motion'
-import { useTheme, useMotionConfig } from '@nivo/core'
+import { useTransition } from 'react-spring'
+import { useMotionConfig } from '@nivo/core'
import GridLine from './GridLine'
-const GridLines = ({ type, lines }) => {
- const theme = useTheme()
- const { animate, springConfig } = useMotionConfig()
+const GridLines = ({ lines }) => {
+ const { animate, config: springConfig } = useMotionConfig()
- const lineWillEnter = useMemo(
- () => ({ style }) => ({
+ const transitions = useTransition(lines, line => line.key, {
+ initial: line => ({
+ opacity: 1,
+ x1: line.x1,
+ x2: line.x2,
+ y1: line.y1,
+ y2: line.y2,
+ }),
+ from: line => ({
opacity: 0,
- x1: type === 'x' ? 0 : style.x1.val,
- x2: type === 'x' ? 0 : style.x2.val,
- y1: type === 'y' ? 0 : style.y1.val,
- y2: type === 'y' ? 0 : style.y2.val,
+ x1: line.x1,
+ x2: line.x2,
+ y1: line.y1,
+ y2: line.y2,
}),
- [type]
- )
-
- const lineWillLeave = useMemo(
- () => ({ style }) => ({
- opacity: spring(0, springConfig),
- x1: spring(style.x1.val, springConfig),
- x2: spring(style.x2.val, springConfig),
- y1: spring(style.y1.val, springConfig),
- y2: spring(style.y2.val, springConfig),
+ enter: line => ({
+ opacity: 1,
+ x1: line.x1,
+ x2: line.x2,
+ y1: line.y1,
+ y2: line.y2,
}),
- [springConfig]
- )
-
- if (!animate) {
- return (
-
- {lines.map(line => (
-
- ))}
-
- )
- }
+ update: line => ({
+ opacity: 1,
+ x1: line.x1,
+ x2: line.x2,
+ y1: line.y1,
+ y2: line.y2,
+ }),
+ leave: {
+ opacity: 0,
+ },
+ config: springConfig,
+ immediate: !animate,
+ })
return (
- {
- return {
- key: line.key,
- style: {
- opacity: spring(1, springConfig),
- x1: spring(line.x1 || 0, springConfig),
- x2: spring(line.x2 || 0, springConfig),
- y1: spring(line.y1 || 0, springConfig),
- y2: spring(line.y2 || 0, springConfig),
- },
- }
- })}
- >
- {interpolatedStyles => (
-
- {interpolatedStyles.map(interpolatedStyle => {
- const { key, style } = interpolatedStyle
-
- return
- })}
-
- )}
-
+
+ {transitions.map(({ item: line, props: animatedProps, key }) => (
+
+ ))}
+
)
}
diff --git a/packages/axes/src/compute.js b/packages/axes/src/compute.js
index 6625de147..106b06e39 100644
--- a/packages/axes/src/compute.js
+++ b/packages/axes/src/compute.js
@@ -6,7 +6,6 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-import isNumber from 'lodash/isNumber'
import {
timeMillisecond,
utcMillisecond,
@@ -77,6 +76,9 @@ const timeByType = {
const timeTypes = Object.keys(timeByType)
const timeIntervalRegexp = new RegExp(`^every\\s*(\\d+)?\\s*(${timeTypes.join('|')})s?$`, 'i')
+const isInteger = value =>
+ typeof value === 'number' && isFinite(value) && Math.floor(value) === value
+
export const getScaleTicks = (scale, spec) => {
// specific values
if (Array.isArray(spec)) {
@@ -91,7 +93,7 @@ export const getScaleTicks = (scale, spec) => {
}
// specific tick count
- if (isNumber(spec)) {
+ if (isInteger(spec)) {
return scale.ticks(spec)
}
@@ -208,7 +210,7 @@ export const getFormatter = (format, scale) => {
export const computeGridLines = ({ width, height, scale, axis, values: _values }) => {
const lineValues = Array.isArray(_values) ? _values : undefined
- const lineCount = isNumber(_values) ? _values : undefined
+ const lineCount = isInteger(_values) ? _values : undefined
const values = lineValues || getScaleTicks(scale, lineCount)
diff --git a/packages/bar/tests/Bar.test.js b/packages/bar/tests/Bar.test.js
index 62a0ed875..cfb192b7d 100644
--- a/packages/bar/tests/Bar.test.js
+++ b/packages/bar/tests/Bar.test.js
@@ -22,6 +22,7 @@ it('should render a basic bar chart', () => {
{ id: 'two', value: 20 },
{ id: 'three', value: 30 },
]}
+ animate={false}
/>
)
@@ -40,6 +41,7 @@ it('should allow to disable labels', () => {
{ id: 'two', value: 20 },
{ id: 'three', value: 30 },
]}
+ animate={false}
/>
)
@@ -59,6 +61,7 @@ it('should allow grouped mode', () => {
{ id: 'two', value: 20 },
{ id: 'three', value: 30 },
]}
+ animate={false}
/>
)
@@ -78,6 +81,7 @@ it('should allow horizontal layout', () => {
{ id: 'two', value: 20 },
{ id: 'three', value: 30 },
]}
+ animate={false}
/>
)
@@ -98,6 +102,7 @@ it('should allow grouped horizontal layout', () => {
{ id: 'two', value: 20 },
{ id: 'three', value: 30 },
]}
+ animate={false}
/>
)
@@ -125,6 +130,7 @@ it(`should reverse legend items if chart layout is vertical`, () => {
itemHeight: 20,
},
]}
+ animate={false}
/>
)
@@ -157,6 +163,7 @@ it(`should not reverse legend items if chart layout is vertical reversed`, () =>
itemHeight: 20,
},
]}
+ animate={false}
/>
)
@@ -188,6 +195,7 @@ it(`should not reverse legend items if chart layout is horizontal`, () => {
itemHeight: 20,
},
]}
+ animate={false}
/>
)
@@ -220,6 +228,7 @@ it(`should reverse legend items if chart layout is horizontal reversed`, () => {
itemHeight: 20,
},
]}
+ animate={false}
/>
)
diff --git a/packages/bar/tests/__snapshots__/Bar.test.js.snap b/packages/bar/tests/__snapshots__/Bar.test.js.snap
index c95f48383..c36a1f5ae 100644
--- a/packages/bar/tests/__snapshots__/Bar.test.js.snap
+++ b/packages/bar/tests/__snapshots__/Bar.test.js.snap
@@ -748,58 +748,56 @@ exports[`should allow grouped horizontal layout 1`] = `
y2={300}
/>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -1671,58 +1669,56 @@ exports[`should allow grouped mode 1`] = `
y2={300}
/>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -2477,58 +2473,56 @@ exports[`should allow horizontal layout 1`] = `
y2={300}
/>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -3400,58 +3394,56 @@ exports[`should allow to disable labels 1`] = `
y2={300}
/>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -4323,106 +4315,104 @@ exports[`should render a basic bar chart 1`] = `
y2={300}
/>
-
-
-
-
+
+
- 10
-
-
-
-
-
+
+
+
+
- 20
-
-
-
-
-
+
+
+
+
- 30
-
-
+ }
+ textAnchor="middle"
+ x={72.5}
+ y={150}
+ >
+ 30
+
diff --git a/packages/line/tests/Line.test.js b/packages/line/tests/Line.test.js
index 33e3622f3..0a610bbc8 100644
--- a/packages/line/tests/Line.test.js
+++ b/packages/line/tests/Line.test.js
@@ -17,7 +17,7 @@ it('should render a basic line chart', () => {
],
},
]
- const component = renderer.create()
+ const component = renderer.create()
let tree = component.toJSON()
expect(tree).toMatchSnapshot()
@@ -46,7 +46,7 @@ it('should support multiple lines', () => {
],
},
]
- const component = renderer.create()
+ const component = renderer.create()
let tree = component.toJSON()
expect(tree).toMatchSnapshot()
@@ -65,7 +65,9 @@ it('should create slice for each x value', () => {
],
},
]
- const wrapper = mount()
+ const wrapper = mount(
+
+ )
const slices = wrapper.find(SlicesItem)
expect(slices).toHaveLength(5)
@@ -89,7 +91,7 @@ it('should have left and bottom axis by default', () => {
],
},
]
- const wrapper = mount()
+ const wrapper = mount()
const axes = wrapper.find('Axis')
expect(axes).toHaveLength(2)
diff --git a/packages/scatterplot/tests/ScatterPlot.test.js b/packages/scatterplot/tests/ScatterPlot.test.js
index 8239f1835..15ed6e056 100644
--- a/packages/scatterplot/tests/ScatterPlot.test.js
+++ b/packages/scatterplot/tests/ScatterPlot.test.js
@@ -13,7 +13,12 @@ const sampleData = [
it('should render a basic scatterplot chart', () => {
const component = renderer.create(
-
+
)
const tree = component.toJSON()
@@ -29,6 +34,7 @@ it('should allow to render several series', () => {
{ id: 'default', data: sampleData },
{ id: 'extra', data: sampleData },
]}
+ animate={false}
/>
)
@@ -43,6 +49,7 @@ it('should allow to customize node size', () => {
height={300}
nodeSize={12}
data={[{ id: 'default', data: sampleData }]}
+ animate={false}
/>
)
@@ -74,6 +81,7 @@ it('should allow to use a varying node size', () => {
values: [0, 10],
sizes: [0, 20],
}}
+ animate={false}
/>
)
@@ -93,6 +101,7 @@ it('should allow to use a custom node', () => {
height={300}
data={[{ id: 'default', data: sampleData }]}
renderNode={CustomNode}
+ animate={false}
/>
)
@@ -119,6 +128,7 @@ it('should allow to disable interactivity', () => {
data={[{ id: 'default', data: sampleData }]}
isInteractive={false}
onClick={() => {}}
+ animate={false}
/>
)