Skip to content

Commit

Permalink
feat(line): improve Line component
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Jul 31, 2017
1 parent 786dc9a commit 63ee809
Show file tree
Hide file tree
Showing 6 changed files with 361 additions and 63 deletions.
87 changes: 87 additions & 0 deletions src/components/axes/Axes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* This file is part of the nivo project.
*
* Copyright 2016-present, Raphaël Benitte.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Axis from './Axis'

const horizontalPositions = ['top', 'bottom']
const verticalPositions = ['left', 'right']
const positions = [...horizontalPositions, ...verticalPositions]

const axisPropType = PropTypes.shape({
tickSize: PropTypes.number,
tickPadding: PropTypes.number,
format: PropTypes.func,
})

export default class Axes extends Component {
static propTypes = {
xScale: PropTypes.func.isRequired,
yScale: PropTypes.func.isRequired,

width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,

axes: PropTypes.shape({
top: axisPropType,
right: axisPropType,
bottom: axisPropType,
left: axisPropType,
}).isRequired,

theme: PropTypes.object.isRequired,
}

static defaultProps = {}

render() {
const {
xScale,
yScale,
width,
height,
axes,
theme,
animate,
motionStiffness,
motionDamping,
} = this.props

return (
<g>
{positions.map(position => {
if (!axes[position]) return null

const axis = axes[position]
if (axis.enabled !== undefined && axis.enabled === false)
return null

const scale = horizontalPositions.includes(position)
? xScale
: yScale

return (
<Axis
theme={theme}
{...axis}
key={position}
width={width}
height={height}
position={position}
scale={scale}
animate={animate}
motionDamping={motionDamping}
motionStiffness={motionStiffness}
/>
)
})}
</g>
)
}
}
4 changes: 3 additions & 1 deletion src/components/axes/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ export default class Grid extends Component {
static propTypes = {
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,

xScale: PropTypes.func,
yScale: PropTypes.func,

theme: PropTypes.object.isRequired,

// motion
animate: PropTypes.bool.isRequired,
motionStiffness: PropTypes.number.isRequired,
Expand All @@ -43,7 +46,6 @@ export default class Grid extends Component {
const {
width,
height,
scales,
xScale,
yScale,
theme,
Expand Down
1 change: 1 addition & 0 deletions src/components/axes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
* file that was distributed with this source code.
*/
export Axis from './Axis'
export Axes from './Axes'
export Grid from './Grid'
53 changes: 17 additions & 36 deletions src/components/charts/bars/Bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,11 @@ import {
generateStackedBars,
} from '../../../lib/charts/bar'
import SvgWrapper from '../SvgWrapper'
import Axis from '../../axes/Axis'
import Axes from '../../axes/Axes'
import Grid from '../../axes/Grid'
import BarItem from './BarItem'
import BarItemLabel from './BarItemLabel'

const axisPropType = PropTypes.shape({
tickSize: PropTypes.number,
tickPadding: PropTypes.number,
format: PropTypes.func,
})

export default class Bar extends Component {
static propTypes = {
// data
Expand Down Expand Up @@ -50,12 +44,7 @@ export default class Bar extends Component {
xPadding: PropTypes.number.isRequired,

// axes
axes: PropTypes.shape({
top: axisPropType,
right: axisPropType,
bottom: axisPropType,
left: axisPropType,
}),
axes: PropTypes.object.isRequired,
enableGridX: PropTypes.bool.isRequired,
enableGridY: PropTypes.bool.isRequired,

Expand All @@ -81,8 +70,12 @@ export default class Bar extends Component {
xPadding: 0.1,
enableLabels: true,
axes: {
left: {},
bottom: {},
left: {
enabled: true,
},
bottom: {
enabled: true,
},
},
enableGridX: false,
enableGridY: true,
Expand Down Expand Up @@ -187,28 +180,16 @@ export default class Bar extends Component {
height={height}
xScale={enableGridX ? result.xScale : null}
yScale={enableGridY ? result.yScale : null}
{...motionProps}
/>
<Axes
axes={axes}
xScale={result.xScale}
yScale={result.yScale}
width={width}
height={height}
theme={theme}
/>
{['top', 'right', 'bottom', 'left'].map(position => {
if (!axes[position]) return null

const axis = axes[position]
const scale = ['top', 'bottom'].includes(position)
? result.xScale
: result.yScale

return (
<Axis
theme={theme}
{...motionProps}
{...axis}
key={position}
width={width}
height={height}
position={position}
scale={scale}
/>
)
})}
{bars}
{enableLabels &&
result.bars.map(d => <BarItemLabel {...d} key={d.key} />)}
Expand Down
150 changes: 124 additions & 26 deletions src/components/charts/line/Line.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,156 @@
*/
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import { curvePropMapping, curvePropType } from '../../../properties/curve'
import { merge } from 'lodash'
import { line } from 'd3'
import Nivo, { defaultTheme } from '../../../Nivo'
import { margin as marginPropType } from '../../../PropTypes'
import { getColorRange } from '../../../ColorUtils'
import SvgWrapper from '../SvgWrapper'
import {
generateLines,
generateStackedLines,
} from '../../../lib/charts/line'
import { curvePropMapping, curvePropType } from '../../../properties/curve'
import Axes from '../../axes/Axes'
import Grid from '../../axes/Grid'

export default class Line extends Component {
static propTypes = {
scales: PropTypes.object.isRequired,
// data
data: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
data: PropTypes.arrayOf(
PropTypes.shape({
x: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]).isRequired,
y: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]).isRequired,
})
).isRequired,
})
).isRequired,

stacked: PropTypes.bool.isRequired,
curve: curvePropType.isRequired,
color: PropTypes.string.isRequired,

width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
margin: marginPropType,

// axes
axes: PropTypes.object.isRequired,
enableGridX: PropTypes.bool.isRequired,
enableGridY: PropTypes.bool.isRequired,

theme: PropTypes.object.isRequired,
colors: PropTypes.any.isRequired,

// motion
animate: PropTypes.bool.isRequired,
motionStiffness: PropTypes.number.isRequired,
motionDamping: PropTypes.number.isRequired,
}

static defaultProps = {
scales: {},
stacked: false,
curve: 'linear',
color: '#000',
margin: Nivo.defaults.margin,
axes: {
left: {
enabled: true,
},
bottom: {
enabled: true,
},
},
enableGridX: true,
enableGridY: true,
colors: Nivo.defaults.colorRange,
theme: {},
animate: true,
motionStiffness: Nivo.defaults.motionStiffness,
motionDamping: Nivo.defaults.motionDamping,
}

render() {
const {
data,
scales,
xScale: _xScale,
yScale: _yScale,
x,
y,
stacked,
curve,
color,
margin: _margin,
width: _width,
height: _height,
axes,
enableGridX,
enableGridY,
theme: _theme,
colors,
animate,
motionStiffness,
motionDamping,
} = this.props

const xScale = scales[_xScale]
const yScale = scales[_yScale]
const margin = Object.assign({}, Nivo.defaults.margin, _margin)
const width = _width - margin.left - margin.right
const height = _height - margin.top - margin.bottom

const getX = _.isFunction(x) ? x : d => d[x]
const getY = _.isFunction(y) ? y : d => d[y]
const theme = merge({}, defaultTheme, _theme)
const color = getColorRange(colors)

const points = data.map(d => ({
x: xScale(getX(d)),
y: yScale(getY(d)),
}))
const motionProps = {
animate,
motionDamping,
motionStiffness,
}

let result
if (stacked === true) {
result = generateStackedLines(data, width, height)
} else {
result = generateLines(data, width, height)
}

const lineGenerator = line()
.x(d => d.x)
.y(d => d.y)
.curve(curvePropMapping[curve])

const { xScale, yScale, lines } = result

return (
<path
d={lineGenerator(points)}
fill="none"
strokeWidth={2}
stroke={color}
/>
<SvgWrapper width={_width} height={_height} margin={margin}>
<Grid
theme={theme}
width={width}
height={height}
xScale={enableGridX ? xScale : null}
yScale={enableGridY ? yScale : null}
{...motionProps}
/>
<Axes
axes={axes}
xScale={xScale}
yScale={yScale}
width={width}
height={height}
theme={theme}
/>
{lines.map(({ id, points }) => (
<path
key={id}
d={lineGenerator(points)}
fill="none"
strokeWidth={2}
stroke={color(id)}
/>
))}
</SvgWrapper>
)
}
}

0 comments on commit 63ee809

Please sign in to comment.