Skip to content

Commit

Permalink
feat(bar): switch from react-motion to react-spring
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze committed Jun 28, 2021
1 parent dfd0099 commit abef1fa
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 140 deletions.
4 changes: 2 additions & 2 deletions packages/bar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
168 changes: 51 additions & 117 deletions packages/bar/src/Bar.tsx
Original file line number Diff line number Diff line change
@@ -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<BarDatum>['motionDamping']
stiffness: BarSvgProps<BarDatum>['motionStiffness']
}>

const barWillEnterHorizontal = ({ style }: Record<string, any>) => ({
x: style.x.val,
y: style.y.val,
width: 0,
height: style.height.val,
})

const barWillEnterVertical = ({ style }: Record<string, any>) => ({
x: style.x.val,
y: style.y.val + style.height.val,
width: style.width.val,
height: 0,
})

const barWillLeaveHorizontal = (springConfig: SpringConfig) => ({
style,
}: Record<string, any>) => ({
x: style.x,
y: style.y,
width: spring(0, springConfig),
height: style.height,
})

const barWillLeaveVertical = (springConfig: SpringConfig) => ({ style }: Record<string, any>) => ({
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 = <RawDatum extends BarDatum>({
data,
Expand Down Expand Up @@ -113,8 +78,7 @@ export const Bar = <RawDatum extends BarDatum>({
legends = svgDefaultProps.legends,

animate = svgDefaultProps.animate,
motionStiffness = svgDefaultProps.motionStiffness,
motionDamping = svgDefaultProps.motionDamping,
motionConfig = svgDefaultProps.motionConfig,

renderWrapper,
role = svgDefaultProps.role,
Expand Down Expand Up @@ -160,16 +124,31 @@ export const Bar = <RawDatum extends BarDatum>({
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 }) => {
Expand All @@ -187,9 +166,7 @@ export const Bar = <RawDatum extends BarDatum>({
})

return (
<LegacyContainer
{...{ animate, isInteractive, motionStiffness, motionDamping, renderWrapper, theme }}
>
<LegacyContainer {...{ animate, isInteractive, motionConfig, renderWrapper, theme }}>
{({ showTooltip, hideTooltip }: TooltipHandlers) => {
const commonProps = {
borderRadius,
Expand All @@ -207,64 +184,6 @@ export const Bar = <RawDatum extends BarDatum>({
tooltip,
}

let bars
if (animate === true) {
bars = (
<TransitionMotion
key="bars"
willEnter={willEnter}
willLeave={willLeave}
styles={result.bars
.filter(bar => 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 => (
<g>
{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),
})
})}
</g>
)}
</TransitionMotion>
)
} 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: (
<Grid
Expand All @@ -290,7 +209,22 @@ export const Bar = <RawDatum extends BarDatum>({
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: (
<CartesianMarkers
key="markers"
Expand Down
25 changes: 12 additions & 13 deletions packages/bar/src/BarItem.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { BarDatum, BarItemProps } from './types'
import { animated } from '@react-spring/web'
import { createElement, useCallback } from 'react'
import { useTheme } from '@nivo/core'

export const BarItem = <RawDatum extends BarDatum>({
data,
color,

style: { height, transform, width, x, y, ...style },

x,
y,
width,
height,
borderRadius,
color,
borderWidth,
borderColor,

Expand Down Expand Up @@ -64,13 +63,13 @@ export const BarItem = <RawDatum extends BarDatum>({
)

return (
<g transform={`translate(${x}, ${y})`}>
<rect
<animated.g transform={transform}>
<animated.rect
width={width}
height={height}
rx={borderRadius}
ry={borderRadius}
fill={data.fill ?? color}
fill={data.fill ?? style.color}
strokeWidth={borderWidth}
stroke={borderColor}
onMouseEnter={handleMouseEnter}
Expand All @@ -79,9 +78,9 @@ export const BarItem = <RawDatum extends BarDatum>({
onClick={handleClick}
/>
{shouldRenderLabel && (
<text
x={width / 2}
y={height / 2}
<animated.text
x={x}
y={y}
textAnchor="middle"
dominantBaseline="central"
style={{
Expand All @@ -91,8 +90,8 @@ export const BarItem = <RawDatum extends BarDatum>({
}}
>
{label}
</text>
</animated.text>
)}
</g>
</animated.g>
)
}
10 changes: 4 additions & 6 deletions packages/bar/src/props.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -48,17 +48,15 @@ export const defaultProps = {
tooltip: BarTooltip,
tooltipLabel: (datum: ComputedDatum<any>) => `${datum.id} - ${datum.indexValue}`,

animate: true,
motionStiffness: 90,
motionDamping: 15,

legends: [],

annotations: [],
}

export const svgDefaultProps = {
...defaultProps,
animate: true,
motionConfig: 'gentle',
role: 'img',
}

Expand Down
14 changes: 12 additions & 2 deletions packages/bar/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Box,
CartesianMarkerProps,
Dimensions,
MotionProps,
ModernMotionProps,
PropertyAccessor,
SvgDefsAndFill,
Theme,
Expand All @@ -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
Expand Down Expand Up @@ -114,6 +115,15 @@ export interface BarItemProps<RawDatum>
BarHandlers<RawDatum, SVGRectElement> {
borderColor: string

style: SpringValues<{
color: string
height: number
transform: string
width: number
x: number
y: number
}>

label: string
labelColor: string
shouldRenderLabel: boolean
Expand Down Expand Up @@ -199,7 +209,7 @@ export type BarSvgProps<RawDatum extends BarDatum> = Partial<BarCommonProps<RawD
BarHandlers<RawDatum, SVGRectElement> &
SvgDefsAndFill<ComputedBarDatum<RawDatum>> &
Dimensions &
MotionProps &
ModernMotionProps &
Partial<{
layers: BarLayer<RawDatum>[]
role: string
Expand Down

0 comments on commit abef1fa

Please sign in to comment.