diff --git a/packages/bar/package.json b/packages/bar/package.json index 85966b2a2a..effe18de52 100644 --- a/packages/bar/package.json +++ b/packages/bar/package.json @@ -36,10 +36,10 @@ "@nivo/recompose": "0.71.0", "@nivo/scales": "0.71.0", "@nivo/tooltip": "0.71.0", + "@react-spring/web": "9.2.0", "d3-scale": "^3.2.3", "d3-shape": "^1.2.2", - "lodash": "^4.17.21", - "react-motion": "^0.5.2" + "lodash": "^4.17.21" }, "devDependencies": { "@nivo/core": "0.71.0" diff --git a/packages/bar/src/Bar.tsx b/packages/bar/src/Bar.tsx index 022666f44c..6442cc77e5 100644 --- a/packages/bar/src/Bar.tsx +++ b/packages/bar/src/Bar.tsx @@ -1,58 +1,23 @@ -import { createElement, Fragment, useCallback, useState } from 'react' -import { TransitionMotion, spring } from 'react-motion' +import { Axes, Grid } from '@nivo/axes' +import { BarAnnotations } from './BarAnnotations' +import { BarDatum, BarLayer, BarSvgProps, ComputedBarDatum, TooltipHandlers } from './types' +import { BoxLegendSvg } from '@nivo/legends' import { // @ts-ignore - bindDefs, + CartesianMarkers, // @ts-ignore LegacyContainer, SvgWrapper, // @ts-ignore - CartesianMarkers, + bindDefs, defaultMargin, usePropertyAccessor, } from '@nivo/core' -import { Axes, Grid } from '@nivo/axes' -import { BarAnnotations } from './BarAnnotations' -import { BarDatum, BarLayer, BarSvgProps, ComputedBarDatum, TooltipHandlers } from './types' -import { BoxLegendSvg } from '@nivo/legends' +import { Fragment, createElement, useCallback, useMemo, useState } from 'react' import { generateGroupedBars, generateStackedBars, getLegendData } from './compute' import { svgDefaultProps } from './props' import { useInheritedColor, useOrdinalColorScale } from '@nivo/colors' - -type SpringConfig = Required<{ - damping: BarSvgProps['motionDamping'] - stiffness: BarSvgProps['motionStiffness'] -}> - -const barWillEnterHorizontal = ({ style }: Record) => ({ - x: style.x.val, - y: style.y.val, - width: 0, - height: style.height.val, -}) - -const barWillEnterVertical = ({ style }: Record) => ({ - x: style.x.val, - y: style.y.val + style.height.val, - width: style.width.val, - height: 0, -}) - -const barWillLeaveHorizontal = (springConfig: SpringConfig) => ({ - style, -}: Record) => ({ - x: style.x, - y: style.y, - width: spring(0, springConfig), - height: style.height, -}) - -const barWillLeaveVertical = (springConfig: SpringConfig) => ({ style }: Record) => ({ - x: style.x, - y: spring(style.y.val + style.height.val, springConfig), - width: style.width, - height: spring(0, springConfig), -}) +import { useTransition } from '@react-spring/web' export const Bar = ({ data, @@ -113,8 +78,7 @@ export const Bar = ({ legends = svgDefaultProps.legends, animate = svgDefaultProps.animate, - motionStiffness = svgDefaultProps.motionStiffness, - motionDamping = svgDefaultProps.motionDamping, + motionConfig = svgDefaultProps.motionConfig, renderWrapper, role = svgDefaultProps.role, @@ -160,16 +124,31 @@ export const Bar = ({ hiddenIds, }) - const springConfig = { - damping: motionDamping, - stiffness: motionStiffness, - } - - const willEnter = layout === 'vertical' ? barWillEnterVertical : barWillEnterHorizontal - const willLeave = - layout === 'vertical' - ? barWillLeaveVertical(springConfig) - : barWillLeaveHorizontal(springConfig) + const barsWithValue = useMemo(() => result.bars.filter(bar => bar.data.value !== null), [ + result.bars, + ]) + + const transition = useTransition(barsWithValue, { + keys: bar => bar.key, + enter: bar => ({ + x: bar.width / 2, + y: bar.height / 2, + width: bar.width, + height: bar.height, + color: bar.color, + transform: `translate(${bar.x}, ${bar.y})`, + }), + update: bar => ({ + x: bar.width / 2, + y: bar.height / 2, + width: bar.width, + height: bar.height, + color: bar.color, + transform: `translate(${bar.x}, ${bar.y})`, + }), + config: motionConfig as any, + immediate: !animate, + }) const shouldRenderLabel = useCallback( ({ width, height }: { height: number; width: number }) => { @@ -187,9 +166,7 @@ export const Bar = ({ }) return ( - + {({ showTooltip, hideTooltip }: TooltipHandlers) => { const commonProps = { borderRadius, @@ -207,64 +184,6 @@ export const Bar = ({ tooltip, } - let bars - if (animate === true) { - bars = ( - bar.data.value !== null) - .map(bar => ({ - key: bar.key, - data: bar, - style: { - x: spring(bar.x, springConfig), - y: spring(bar.y, springConfig), - width: spring(bar.width, springConfig), - height: spring(bar.height, springConfig), - }, - }))} - > - {interpolatedStyles => ( - - {interpolatedStyles.map(({ key, style, data: bar }) => { - const baseProps = { ...bar, ...style } - - return createElement(barComponent, { - key, - ...baseProps, - ...commonProps, - shouldRenderLabel: shouldRenderLabel(baseProps), - width: Math.max(style.width, 0), - height: Math.max(style.height, 0), - label: getLabel(bar.data), - // @ts-ignore fix theme - labelColor: getLabelColor(baseProps, theme), - borderColor: getBorderColor(baseProps), - }) - })} - - )} - - ) - } else { - bars = result.bars - .filter(bar => bar.data.value !== null) - .map(d => - createElement(barComponent, { - ...d, - ...commonProps, - label: getLabel(d.data), - shouldRenderLabel: shouldRenderLabel(d), - // @ts-ignore fix theme - labelColor: getLabelColor(d, theme), - borderColor: getBorderColor(d), - }) - ) - } - const layerById = { grid: ( ({ left={axisLeft} /> ), - bars, + bars: ( + <> + {transition((style, bar) => + createElement(barComponent, { + ...bar, + ...commonProps, + style, + shouldRenderLabel: shouldRenderLabel(bar), + label: getLabel(bar.data), + // @ts-ignore fix theme + labelColor: getLabelColor(bar, theme), + borderColor: getBorderColor(bar), + }) + )} + + ), markers: ( ({ data, + color, + + style: { height, transform, width, x, y, ...style }, - x, - y, - width, - height, borderRadius, - color, borderWidth, borderColor, @@ -64,13 +63,13 @@ export const BarItem = ({ ) return ( - - + ({ onClick={handleClick} /> {shouldRenderLabel && ( - ({ }} > {label} - + )} - + ) } diff --git a/packages/bar/src/props.ts b/packages/bar/src/props.ts index a606f4f80e..77a2a7d697 100644 --- a/packages/bar/src/props.ts +++ b/packages/bar/src/props.ts @@ -1,8 +1,8 @@ +import { BarItem } from './BarItem' import { BarTooltip } from './BarTooltip' import { ComputedDatum } from './types' -import { ScaleSpec, ScaleBandSpec } from '@nivo/scales' import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors' -import { BarItem } from './BarItem' +import { ScaleBandSpec, ScaleSpec } from '@nivo/scales' export const defaultProps = { indexBy: 'id', @@ -48,10 +48,6 @@ export const defaultProps = { tooltip: BarTooltip, tooltipLabel: (datum: ComputedDatum) => `${datum.id} - ${datum.indexValue}`, - animate: true, - motionStiffness: 90, - motionDamping: 15, - legends: [], annotations: [], @@ -59,6 +55,8 @@ export const defaultProps = { export const svgDefaultProps = { ...defaultProps, + animate: true, + motionConfig: 'gentle', role: 'img', } diff --git a/packages/bar/src/types.ts b/packages/bar/src/types.ts index d87f125bf7..e78bb565b7 100644 --- a/packages/bar/src/types.ts +++ b/packages/bar/src/types.ts @@ -5,7 +5,7 @@ import { Box, CartesianMarkerProps, Dimensions, - MotionProps, + ModernMotionProps, PropertyAccessor, SvgDefsAndFill, Theme, @@ -14,6 +14,7 @@ import { import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors' import { LegendProps } from '@nivo/legends' import { Scale, ScaleSpec, ScaleBandSpec } from '@nivo/scales' +import { SpringValues } from '@react-spring/web' export interface BarDatum { [key: string]: string | number @@ -114,6 +115,15 @@ export interface BarItemProps BarHandlers { borderColor: string + style: SpringValues<{ + color: string + height: number + transform: string + width: number + x: number + y: number + }> + label: string labelColor: string shouldRenderLabel: boolean @@ -199,7 +209,7 @@ export type BarSvgProps = Partial & SvgDefsAndFill> & Dimensions & - MotionProps & + ModernMotionProps & Partial<{ layers: BarLayer[] role: string