You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
6.6 KiB
314 lines
6.6 KiB
// @flow
|
|
|
|
/* eslint-disable react/no-multi-comp */
|
|
|
|
import React, { Fragment, PureComponent } from 'react'
|
|
import {
|
|
VictoryChart,
|
|
VictoryArea,
|
|
VictoryAxis,
|
|
VictoryTooltip,
|
|
VictoryVoronoiContainer,
|
|
VictoryLabel,
|
|
} from 'victory'
|
|
|
|
import { radii, space, colors, fontSizes } from 'styles/theme'
|
|
import { ff } from 'styles/helpers'
|
|
|
|
import Box from 'components/base/Box'
|
|
|
|
const ANIMATION_DURATION = 600
|
|
const DEFAULT_PROPS = {
|
|
color: 'blue',
|
|
padding: 0,
|
|
}
|
|
|
|
type Props = {
|
|
height: number,
|
|
render: Function,
|
|
}
|
|
|
|
type State = {
|
|
isAnimationActive: boolean,
|
|
width: number,
|
|
}
|
|
|
|
export class WrapperChart extends PureComponent<Props, State> {
|
|
state = {
|
|
isAnimationActive: true,
|
|
width: 0,
|
|
}
|
|
|
|
componentDidMount() {
|
|
this._timeout = setTimeout(
|
|
() =>
|
|
this.setState({
|
|
isAnimationActive: false,
|
|
}),
|
|
ANIMATION_DURATION * 2,
|
|
)
|
|
|
|
if (this._node) {
|
|
this._ro = new ResizeObserver(entries => {
|
|
const entry = entries.find(entry => this._node === entry.target)
|
|
if (entry) {
|
|
this.setState({
|
|
width: entry.contentRect.width,
|
|
})
|
|
}
|
|
})
|
|
|
|
this._ro.observe(this._node)
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
clearTimeout(this._timeout)
|
|
|
|
if (this._ro) {
|
|
this._ro.disconnect()
|
|
}
|
|
}
|
|
|
|
_ro = undefined
|
|
_node = undefined
|
|
_timeout = undefined
|
|
|
|
render() {
|
|
const { render, height } = this.props
|
|
const { isAnimationActive, width } = this.state
|
|
return (
|
|
<Box ff="Open Sans" innerRef={n => (this._node = n)} style={{ height }}>
|
|
{render({ isAnimationActive, height, width })}
|
|
</Box>
|
|
)
|
|
}
|
|
}
|
|
|
|
function getLinearGradient({
|
|
linearGradient,
|
|
id,
|
|
color,
|
|
}: {
|
|
linearGradient: LinearGradient,
|
|
id: string,
|
|
color: string,
|
|
}) {
|
|
return linearGradient.length > 0 ? (
|
|
<svg style={{ height: 0 }}>
|
|
<defs>
|
|
<linearGradient id={id} x1="0" y1="0" x2="0" y2="100%">
|
|
{linearGradient.map((g, i) => (
|
|
<stop
|
|
key={i} // eslint-disable-line react/no-array-index-key
|
|
offset={`${g[0]}%`}
|
|
stopColor={color}
|
|
stopOpacity={g[1]}
|
|
/>
|
|
))}
|
|
</linearGradient>
|
|
</defs>
|
|
</svg>
|
|
) : null
|
|
}
|
|
|
|
type LinearGradient = Array<Array<*>>
|
|
|
|
type GenericChart = {
|
|
id: string,
|
|
linearGradient: LinearGradient,
|
|
strokeWidth: number,
|
|
height: number,
|
|
padding: Object | number,
|
|
color: string,
|
|
data: Array<Object>,
|
|
}
|
|
type Chart = GenericChart & {
|
|
renderLabels: Function,
|
|
renderTickX: Function,
|
|
renderTickY: Function,
|
|
}
|
|
|
|
export const SimpleAreaChart = ({
|
|
linearGradient,
|
|
height,
|
|
data,
|
|
strokeWidth,
|
|
id,
|
|
padding,
|
|
color,
|
|
}: GenericChart) => (
|
|
<WrapperChart
|
|
height={height}
|
|
render={({ width }) => (
|
|
<Fragment>
|
|
{getLinearGradient({
|
|
linearGradient,
|
|
id,
|
|
color,
|
|
})}
|
|
<VictoryArea
|
|
domainPadding={{
|
|
y: [0, space[1]],
|
|
}}
|
|
data={data}
|
|
x="name"
|
|
y="value"
|
|
style={{
|
|
data: {
|
|
stroke: color,
|
|
fill: `url(#${id})`,
|
|
strokeWidth,
|
|
},
|
|
}}
|
|
padding={padding}
|
|
height={height}
|
|
width={width}
|
|
/>
|
|
</Fragment>
|
|
)}
|
|
/>
|
|
)
|
|
|
|
SimpleAreaChart.defaultProps = {
|
|
height: 50,
|
|
id: 'simple-chart',
|
|
linearGradient: [],
|
|
strokeWidth: 1,
|
|
...DEFAULT_PROPS,
|
|
}
|
|
|
|
const areaChartTooltip = ({ renderLabels }: { renderLabels: Function }) => (
|
|
<VictoryTooltip
|
|
corderRadius={radii[1]}
|
|
pointerLength={0}
|
|
height={25}
|
|
labelComponent={
|
|
<VictoryLabel
|
|
style={{
|
|
...ff('Open Sans|SemiBold'),
|
|
fontSize: fontSizes[2],
|
|
fill: colors.white,
|
|
}}
|
|
/>
|
|
}
|
|
flyoutStyle={{
|
|
fill: colors.dark,
|
|
stroke: null,
|
|
}}
|
|
width={a => space[2] * 2 + renderLabels(a).length * 5.2} // Approximatif size of char for calculate Tooltip width
|
|
/>
|
|
)
|
|
|
|
const AreaChartContainer = <VictoryVoronoiContainer voronoiDimension="x" />
|
|
|
|
export class AreaChart extends PureComponent<Chart> {
|
|
static defaultProps = {
|
|
height: 100,
|
|
id: 'chart',
|
|
linearGradient: [[5, 0.2], [50, 0]],
|
|
strokeWidth: 2,
|
|
renderLabels: (d: Object) => d.y,
|
|
renderTickX: (t: any) => t,
|
|
renderTickY: (t: any) => t,
|
|
...DEFAULT_PROPS,
|
|
}
|
|
|
|
_tooltip = areaChartTooltip({
|
|
renderLabels: this.props.renderLabels,
|
|
})
|
|
|
|
render() {
|
|
const {
|
|
color,
|
|
data,
|
|
height,
|
|
id,
|
|
linearGradient,
|
|
padding,
|
|
renderLabels,
|
|
renderTickX,
|
|
renderTickY,
|
|
strokeWidth,
|
|
} = this.props
|
|
|
|
const tickLabelsStyle = {
|
|
fill: colors.grey,
|
|
fontSize: fontSizes[4],
|
|
fontFamily: 'inherit',
|
|
fontWeight: 'inherit',
|
|
}
|
|
|
|
return (
|
|
<WrapperChart
|
|
height={height}
|
|
render={({ width }) => (
|
|
<Fragment>
|
|
{getLinearGradient({
|
|
linearGradient,
|
|
id,
|
|
color,
|
|
})}
|
|
<VictoryChart
|
|
height={height}
|
|
width={width}
|
|
padding={padding}
|
|
domainPadding={{
|
|
y: [0, space[1]],
|
|
}}
|
|
containerComponent={AreaChartContainer}
|
|
>
|
|
<VictoryAxis
|
|
animate={false}
|
|
tickCount={6}
|
|
tickFormat={renderTickX}
|
|
style={{
|
|
axis: {
|
|
stroke: colors.fog,
|
|
},
|
|
tickLabels: {
|
|
...tickLabelsStyle,
|
|
padding: space[2],
|
|
},
|
|
}}
|
|
/>
|
|
<VictoryAxis
|
|
dependentAxis
|
|
tickCount={4}
|
|
tickFormat={renderTickY}
|
|
style={{
|
|
grid: {
|
|
stroke: colors.fog,
|
|
strokeDasharray: 5,
|
|
},
|
|
axis: {
|
|
stroke: null,
|
|
},
|
|
tickLabels: {
|
|
...tickLabelsStyle,
|
|
padding: space[4],
|
|
},
|
|
}}
|
|
/>
|
|
<VictoryArea
|
|
data={data}
|
|
x="name"
|
|
y="value"
|
|
labelComponent={this._tooltip}
|
|
labels={renderLabels}
|
|
style={{
|
|
data: {
|
|
stroke: color,
|
|
fill: `url(#${id})`,
|
|
strokeWidth,
|
|
},
|
|
}}
|
|
width={width}
|
|
/>
|
|
</VictoryChart>
|
|
</Fragment>
|
|
)}
|
|
/>
|
|
)
|
|
}
|
|
}
|
|
|