From be9306135d85569d9ad95da00e5266718bff9efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Benitte?= Date: Wed, 13 Dec 2017 02:24:42 +0900 Subject: [PATCH] feat(line): add LineChartCanvas component --- packages/nivo-axes/src/canvas/render.js | 4 + packages/nivo-line/.eslintrc.yml | 21 + packages/nivo-line/package.json | 4 + packages/nivo-line/src/LineAreas.js | 36 -- packages/nivo-line/src/Lines.js | 35 -- .../nivo-line/src/canvas/CanvasWrapper.js | 49 --- .../nivo-line/src/canvas/LineAreaCanvas.js | 34 -- packages/nivo-line/src/canvas/LineCanvas.js | 35 -- .../nivo-line/src/canvas/LineChartCanvas.js | 330 ++++++++++++++++ .../src/canvas/ResponsiveLineChartCanvas.js | 19 + packages/nivo-line/src/canvas/index.js | 4 +- packages/nivo-line/src/compute.js | 78 ++-- packages/nivo-line/src/index.js | 3 - packages/nivo-line/src/props.js | 5 +- packages/nivo-line/src/svg/LineChartSvg.js | 6 +- packages/nivo-line/stories/.eslintrc.yml | 5 + packages/nivo-line/stories/DualYAxis.js | 2 +- packages/nivo-line/stories/EmptyValues.js | 2 +- packages/nivo-line/stories/Simple.js | 31 +- packages/nivo-line/stories/line.stories.js | 1 - website/README.md | 11 - .../src/components/charts/line/Line/Code.js | 72 ---- .../charts/line/Line/defaultSettings.js | 2 + .../charts/line/Line/generateData.js | 24 +- .../src/components/charts/line/Line/index.js | 2 +- website/src/components/charts/line/LineAPI.js | 9 - .../defaultSettings.js} | 63 +-- .../charts/line/LineCanvas/generateData.js | 25 ++ .../charts/line/LineCanvas/index.js | 227 +---------- .../charts/line/LineComposite/Code.js | 72 ---- .../charts/line/LineComposite/index.js | 362 ------------------ website/src/components/charts/line/props.js | 22 +- .../src/components/charts/line/propsMapper.js | 1 + 33 files changed, 563 insertions(+), 1033 deletions(-) create mode 100644 packages/nivo-line/.eslintrc.yml delete mode 100644 packages/nivo-line/src/LineAreas.js delete mode 100644 packages/nivo-line/src/Lines.js delete mode 100644 packages/nivo-line/src/canvas/CanvasWrapper.js delete mode 100644 packages/nivo-line/src/canvas/LineAreaCanvas.js delete mode 100644 packages/nivo-line/src/canvas/LineCanvas.js create mode 100644 packages/nivo-line/src/canvas/LineChartCanvas.js create mode 100644 packages/nivo-line/src/canvas/ResponsiveLineChartCanvas.js create mode 100644 packages/nivo-line/stories/.eslintrc.yml delete mode 100644 website/README.md delete mode 100644 website/src/components/charts/line/Line/Code.js rename website/src/components/charts/line/{defaultProps.js => LineCanvas/defaultSettings.js} (61%) create mode 100644 website/src/components/charts/line/LineCanvas/generateData.js delete mode 100644 website/src/components/charts/line/LineComposite/Code.js delete mode 100644 website/src/components/charts/line/LineComposite/index.js diff --git a/packages/nivo-axes/src/canvas/render.js b/packages/nivo-axes/src/canvas/render.js index f5c9d4fc52..5a6a2b7c75 100644 --- a/packages/nivo-axes/src/canvas/render.js +++ b/packages/nivo-axes/src/canvas/render.js @@ -22,6 +22,8 @@ export const renderAxisToCanvas = ( scale, // ticks + tickValues, + tickCount, tickSize = 5, tickPadding = 5, tickRotation = 0, @@ -33,6 +35,8 @@ export const renderAxisToCanvas = ( height, position, scale, + tickValues, + tickCount, tickSize, tickPadding, tickRotation, diff --git a/packages/nivo-line/.eslintrc.yml b/packages/nivo-line/.eslintrc.yml new file mode 100644 index 0000000000..1c7d407a1f --- /dev/null +++ b/packages/nivo-line/.eslintrc.yml @@ -0,0 +1,21 @@ +parser: babel-eslint + +parserOptions: + ecmaVersion: 6 + sourceType: module + ecmaFeatures: + jsx: true + experimentalObjectRestSpread: true + +env: + browser: true + +globals: + global: true + +extends: + - eslint:recommended + - plugin:react/recommended + +rules: + react/jsx-key: 0 \ No newline at end of file diff --git a/packages/nivo-line/package.json b/packages/nivo-line/package.json index 1344330a86..6018c0bbd8 100644 --- a/packages/nivo-line/package.json +++ b/packages/nivo-line/package.json @@ -20,8 +20,11 @@ "@nivo/babel-preset": "0.33.0", "@nivo/generators": "0.33.0", "babel-cli": "^6.26.0", + "babel-eslint": "^8.0.3", "babel-jest": "^20.0.3", "cross-env": "^5.0.5", + "eslint": "^4.12.1", + "eslint-plugin-react": "^7.5.1", "jest": "^21.0.1", "react": "^16.2.0", "react-dom": "^16.2.0" @@ -34,6 +37,7 @@ "access": "public" }, "scripts": { + "lint": "eslint src stories tests", "build:commonjs": "rm -rf lib && cross-env NODE_ENV=commonjs babel src --out-dir lib", "build:commonjs:watch": "npm run build:commonjs -- --watch", "build:es": "rm -rf es && cross-env NODE_ENV=es babel src --out-dir es", diff --git a/packages/nivo-line/src/LineAreas.js b/packages/nivo-line/src/LineAreas.js deleted file mode 100644 index af30d1bf39..0000000000 --- a/packages/nivo-line/src/LineAreas.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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, { PureComponent } from 'react' -import compose from 'recompose/compose' -import defaultProps from 'recompose/defaultProps' -import withPropsOnChange from 'recompose/withPropsOnChange' -import { area } from 'd3-shape' -import { curveFromProp } from '@nivo/core' - -const LinesAreas = ({ generator, children: render }) => render(generator) - -LinesAreas.defaultProps = { - curve: 'linear', -} - -const enhance = compose( - defaultProps({ - curve: 'linear', - }), - withPropsOnChange(['curve', 'height'], ({ curve, height }) => ({ - generator: area() - .defined(d => d && d.x !== null && d.y !== null) - .x(d => d.x) - .y0(height) - .y1(d => d.y) - .curve(curveFromProp(curve)), - })) -) - -export default enhance(LinesAreas) diff --git a/packages/nivo-line/src/Lines.js b/packages/nivo-line/src/Lines.js deleted file mode 100644 index b524a77720..0000000000 --- a/packages/nivo-line/src/Lines.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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, { PureComponent } from 'react' -import compose from 'recompose/compose' -import defaultProps from 'recompose/defaultProps' -import withPropsOnChange from 'recompose/withPropsOnChange' -import { line } from 'd3-shape' -import { curveFromProp } from '@nivo/core' - -const Lines = ({ generator, children: render }) => render(generator) - -Lines.defaultProps = { - curve: 'linear', -} - -const enhance = compose( - defaultProps({ - curve: 'linear', - }), - withPropsOnChange(['curve'], props => ({ - generator: line() - .defined(d => d && d.x !== null && d.y !== null) - .x(d => d.x) - .y(d => d.y) - .curve(curveFromProp(props.curve)), - })) -) - -export default enhance(Lines) diff --git a/packages/nivo-line/src/canvas/CanvasWrapper.js b/packages/nivo-line/src/canvas/CanvasWrapper.js deleted file mode 100644 index edeac126d9..0000000000 --- a/packages/nivo-line/src/canvas/CanvasWrapper.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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, Fragment } from 'react' - -class CanvasWrapper extends Component { - state = {} - - componentDidMount() { - this.setState({ - ctx: this.surface.getContext('2d'), - }) - } - - render() { - const { width, height, pixelRatio, children } = this.props - const { ctx } = this.state - - if (ctx) ctx.scale(pixelRatio, pixelRatio) - - return ( - - { - this.surface = surface - }} - width={width * pixelRatio} - height={height * pixelRatio} - style={{ - width, - height, - }} - //onMouseEnter={this.handleMouseHover(showTooltip, hideTooltip)} - //onMouseMove={this.handleMouseHover(showTooltip, hideTooltip)} - //onMouseLeave={this.handleMouseLeave(hideTooltip)} - //onClick={this.handleClick} - /> - {ctx && children(ctx)} - - ) - } -} - -export default CanvasWrapper diff --git a/packages/nivo-line/src/canvas/LineAreaCanvas.js b/packages/nivo-line/src/canvas/LineAreaCanvas.js deleted file mode 100644 index 13773ec274..0000000000 --- a/packages/nivo-line/src/canvas/LineAreaCanvas.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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' - -const LineAreaCanvas = ({ ctx, data, generator, xScale, yScale }) => { - ctx.beginPath() - generator.context(ctx)( - data.map(d => ({ - x: d.x !== null ? xScale(d.x) : null, - y: d.y !== null ? yScale(d.y) : null, - })) - ) - ctx.fillStyle = 'rgba(0, 0, 0, 0.2)' - ctx.fill() - - return null -} - -LineAreaCanvas.propTypes = { - ctx: PropTypes.object.isRequired, - data: PropTypes.array.isRequired, - generator: PropTypes.func.isRequired, - xScale: PropTypes.func.isRequired, - yScale: PropTypes.func.isRequired, -} - -export default LineAreaCanvas diff --git a/packages/nivo-line/src/canvas/LineCanvas.js b/packages/nivo-line/src/canvas/LineCanvas.js deleted file mode 100644 index c6d58c5ea8..0000000000 --- a/packages/nivo-line/src/canvas/LineCanvas.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 from 'react' -import PropTypes from 'prop-types' - -const LineCanvas = ({ ctx, data, generator, xScale, yScale }) => { - ctx.beginPath() - generator.context(ctx)( - data.map(d => ({ - x: d.x !== null ? xScale(d.x) : null, - y: d.y !== null ? yScale(d.y) : null, - })) - ) - ctx.lineWidth = 2 - ctx.strokeStyle = 'steelblue' - ctx.stroke() - - return null -} - -LineCanvas.propTypes = { - ctx: PropTypes.object.isRequired, - data: PropTypes.array.isRequired, - generator: PropTypes.func.isRequired, - xScale: PropTypes.func.isRequired, - yScale: PropTypes.func.isRequired, -} - -export default LineCanvas diff --git a/packages/nivo-line/src/canvas/LineChartCanvas.js b/packages/nivo-line/src/canvas/LineChartCanvas.js new file mode 100644 index 0000000000..253491d648 --- /dev/null +++ b/packages/nivo-line/src/canvas/LineChartCanvas.js @@ -0,0 +1,330 @@ +/* + * 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 { sortBy } from 'lodash' +import { line, area } from 'd3-shape' +import compose from 'recompose/compose' +import pure from 'recompose/pure' +import withPropsOnChange from 'recompose/withPropsOnChange' +import defaultProps from 'recompose/defaultProps' +import setDisplayName from 'recompose/setDisplayName' +import { renderAxesToCanvas, renderGridLinesToCanvas } from '@nivo/axes' +import { withTheme, withColors, withDimensions, curveFromProp, Container } from '@nivo/core' +import { renderLegendToCanvas } from '@nivo/legends' +import { getScales, getStackedScales, generateLines, generateStackedLines } from '../compute' +import { LinePropTypes, LineDefaultProps } from '../props' + +class LineChartCanvas extends Component { + componentDidMount() { + this.ctx = this.surface.getContext('2d') + this.draw(this.props) + } + + shouldComponentUpdate(props) { + if ( + this.props.outerWidth !== props.outerWidth || + this.props.outerHeight !== props.outerHeight || + this.props.isInteractive !== props.isInteractive || + this.props.theme !== props.theme + ) { + return true + } else { + this.draw(props) + return false + } + } + + componentDidUpdate() { + this.ctx = this.surface.getContext('2d') + this.draw(this.props) + } + + renderGrid(ctx, { enableGridX, enableGridY, xScale, yScale, width, height }) { + ctx.strokeStyle = '#dddddd' + enableGridX && + renderGridLinesToCanvas(ctx, { + width, + height, + scale: xScale, + axis: 'x', + }) + enableGridY && + renderGridLinesToCanvas(ctx, { + width, + height, + scale: yScale, + axis: 'y', + }) + } + + renderAxes(ctx, { xScale, yScale, width, height, axisTop, axisRight, axisBottom, axisLeft }) { + this.ctx.strokeStyle = '#000000' + renderAxesToCanvas(ctx, { + xScale, + yScale, + width, + height, + top: axisTop, + right: axisRight, + bottom: axisBottom, + left: axisLeft, + }) + } + + renderAreas(ctx, { generator, lines, xScale, yScale, opacity }) { + const boundGenerator = generator.context(ctx) + + ctx.save() + ctx.globalAlpha = opacity + + lines.forEach(line => { + ctx.beginPath() + boundGenerator( + line.points.map(d => ({ + x: d.x !== null ? xScale(d.x) : null, + y: d.y !== null ? yScale(d.y) : null, + })) + ) + ctx.fillStyle = line.color + ctx.fill() + }) + + ctx.restore() + } + + renderLines(ctx, { generator, lines, xScale, yScale, lineWidth }) { + const boundGenerator = generator.context(ctx) + + lines.forEach(line => { + ctx.beginPath() + boundGenerator( + line.points.map(d => ({ + x: d.x !== null ? xScale(d.x) : null, + y: d.y !== null ? yScale(d.y) : null, + })) + ) + ctx.lineWidth = lineWidth + ctx.strokeStyle = line.color + ctx.stroke() + }) + } + + renderLegends(ctx, { + lines, + legends, + width, + height, + }) { + const legendData = lines.map(line => ({ + label: line.id, + fill: line.color, + })) + + legends.forEach(legend => { + renderLegendToCanvas(this.ctx, { + ...legend, + data: legendData, + containerWidth: width, + containerHeight: height, + }) + }) + } + + draw(props) { + const { + lines, + + xScale, + yScale, + + areaGenerator, + lineGenerator, + + lineWidth, + + enableArea, + areaOpacity, + + // dimensions + width, + height, + outerWidth, + outerHeight, + pixelRatio, + margin, + + // axes + axisTop, + axisRight, + axisBottom, + axisLeft, + enableGridX, + enableGridY, + + // symbols + symbolSize, + + // theming + getColor, + + legends, + } = props + + this.surface.width = outerWidth * pixelRatio + this.surface.height = outerHeight * pixelRatio + + this.ctx.scale(pixelRatio, pixelRatio) + + this.ctx.clearRect(0, 0, outerWidth, outerHeight) + this.ctx.translate(margin.left, margin.top) + + this.renderGrid(this.ctx, { + enableGridX, + enableGridY, + xScale, + yScale, + width, + height, + }) + + this.renderAxes(this.ctx, { + xScale, + yScale, + width, + height, + axisTop, + axisRight, + axisBottom, + axisLeft, + }) + + enableArea && this.renderAreas(this.ctx, { + lines, + generator: areaGenerator, + xScale, + yScale, + opacity: areaOpacity, + }) + + this.renderLines(this.ctx, { + lines, + generator: lineGenerator, + xScale, + yScale, + lineWidth, + }) + + this.renderLegends(this.ctx, { + lines, + legends, + width, + height, + }) + } + + render() { + const { outerWidth, outerHeight, pixelRatio, isInteractive, theme } = this.props + + return ( + + {({ showTooltip, hideTooltip }) => ( + { + this.surface = surface + }} + width={outerWidth * pixelRatio} + height={outerHeight * pixelRatio} + style={{ + width: outerWidth, + height: outerHeight, + }} + //onMouseEnter={this.handleMouseHover(showTooltip, hideTooltip)} + //onMouseMove={this.handleMouseHover(showTooltip, hideTooltip)} + //onMouseLeave={this.handleMouseLeave(hideTooltip)} + //onClick={this.handleClick} + /> + )} + + ) + } +} + +LineChartCanvas.propTypes = LinePropTypes + +const enhance = compose( + defaultProps(LineDefaultProps), + withTheme(), + withColors(), + withDimensions(), + withPropsOnChange(['curve', 'height'], ({ curve, height }) => ({ + areaGenerator: area() + .x(d => d.x) + .y0(height) + .y1(d => d.y) + .curve(curveFromProp(curve)), + lineGenerator: line() + .defined(d => d.value !== null) + .x(d => d.x) + .y(d => d.y) + .curve(curveFromProp(curve)), + })), + withPropsOnChange( + ['data', 'stacked', 'width', 'height', 'minY', 'maxY', 'xScaleType'], + ({ data, stacked, width, height, margin, minY, maxY, xScaleType }) => { + let scales + const args = { data, width, height, minY, maxY, xScaleType } + if (stacked === true) { + scales = getStackedScales(args) + } else { + scales = getScales(args) + } + + return { + margin, + width, + height, + ...scales, + } + } + ), + withPropsOnChange( + ['getColor', 'xScale', 'yScale'], + ({ data, stacked, xScale, yScale, getColor }) => { + let lines + if (stacked === true) { + lines = generateStackedLines(data, xScale, yScale, getColor) + } else { + lines = generateLines(data, xScale, yScale, getColor) + } + + const slices = xScale.domain().map((id, i) => { + let points = sortBy( + lines.map(line => ({ + id: line.id, + value: line.points[i].value, + y: line.points[i].y, + color: line.color, + })), + 'y' + ) + + return { + id, + x: xScale(id), + points, + } + }) + + return { lines, slices } + } + ), + pure +) + +export default setDisplayName('LineChartCanvas')(enhance(LineChartCanvas)) diff --git a/packages/nivo-line/src/canvas/ResponsiveLineChartCanvas.js b/packages/nivo-line/src/canvas/ResponsiveLineChartCanvas.js new file mode 100644 index 0000000000..063541b807 --- /dev/null +++ b/packages/nivo-line/src/canvas/ResponsiveLineChartCanvas.js @@ -0,0 +1,19 @@ +/* + * 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 from 'react' +import { ResponsiveWrapper } from '@nivo/core' +import LineChartCanvas from './LineChartCanvas' + +const ResponsiveLineChartCanvas = props => ( + + {({ width, height }) => } + +) + +export default ResponsiveLineChartCanvas diff --git a/packages/nivo-line/src/canvas/index.js b/packages/nivo-line/src/canvas/index.js index 6c982a132e..d4b6c641a3 100644 --- a/packages/nivo-line/src/canvas/index.js +++ b/packages/nivo-line/src/canvas/index.js @@ -6,5 +6,5 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -export { default as LineCanvas } from './LineCanvas' -export { default as LineAreaCanvas } from './LineAreaCanvas' +export { default as LineChartCanvas } from './LineChartCanvas' +export { default as ResponsiveLineChartCanvas } from './ResponsiveLineChartCanvas' diff --git a/packages/nivo-line/src/compute.js b/packages/nivo-line/src/compute.js index d01986b9fe..42fd9babbe 100644 --- a/packages/nivo-line/src/compute.js +++ b/packages/nivo-line/src/compute.js @@ -6,31 +6,44 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { range, min, max, maxBy, sumBy, uniq } from 'lodash' +import { range, min, max, sumBy, uniq } from 'lodash' import { scalePoint, scaleLinear } from 'd3-scale' /** * Generates X scale. * - * @param {Array.} data - * @param {number} width + * @param {Array.} data + * @param {number} width + * @param {('linear'|'ordinal')} type + * * @returns {Function} */ -export const getXScale = (data, width) => { - const xLengths = uniq(data.map(({ data }) => data.length)) - if (xLengths.length > 1) { - throw new Error( - [ - `Found inconsitent data for x,`, - `expecting all series to have same length`, - `but found: ${xLengths.join(', ')}`, - ].join(' ') - ) +export const getXScale = (data, width, type) => { + if (type === 'linear') { + const minX = min(data.map(({data}) => min(data.map(d => d.x)))) + const maxX = max(data.map(({data}) => max(data.map(d => d.x)))) + + return scaleLinear() + .range([0, width]) + .domain([minX, maxX]) + } else if (type === 'ordinal') { + const xLengths = uniq(data.map(({ data }) => data.length)) + if (xLengths.length > 1) { + throw new Error( + [ + `Found inconsitent data for x,`, + `expecting all series to have same length`, + `but found: ${xLengths.join(', ')}`, + ].join(' ') + ) + } + + return scalePoint() + .range([0, width]) + .domain(data[0].data.map(({ x }) => x)) } - return scalePoint() - .range([0, width]) - .domain(data[0].data.map(({ x }) => x)) + throw new TypeError(`Invalid scale type '${type}', must be one of: 'linear', 'ordinal'`) } /** @@ -87,15 +100,17 @@ export const getStackedYScale = (data, xScale, height, minValue, maxValue) => { /** * Generates stacked x/y scales. * - * @param {Array} data - * @param {number} width - * @param {number} height - * @param {number|string} minY - * @param {number|string} maxY + * @param {Array} data + * @param {number} width + * @param {number} height + * @param {number|string} minY + * @param {number|string} maxY + * @param {('linear'|'ordinal')} xScaleType + * * @return {{ xScale: Function, yScale: Function }} */ -export const getStackedScales = ({ data, width, height, minY, maxY }) => { - const xScale = getXScale(data, width) +export const getStackedScales = ({ data, width, height, minY, maxY, xScaleType }) => { + const xScale = getXScale(data, width, xScaleType) const yScale = getStackedYScale(data, xScale, height, minY, maxY) return { xScale, yScale } @@ -104,15 +119,17 @@ export const getStackedScales = ({ data, width, height, minY, maxY }) => { /** * Generates non stacked x/ scales * - * @param {Array} data - * @param {number} width - * @param {number} height - * @param {number|string} minY - * @param {number|string} maxY + * @param {Array} data + * @param {number} width + * @param {number} height + * @param {number|string} minY + * @param {number|string} maxY + * @param {('linear'|'ordinal')} xScaleType + * * @return {{ xScale: Function, yScale: Function }} */ -export const getScales = ({ data, width, height, minY, maxY }) => { - const xScale = getXScale(data, width) +export const getScales = ({ data, width, height, minY, maxY, xScaleType }) => { + const xScale = getXScale(data, width, xScaleType) const yScale = getYScale(data, height, minY, maxY) return { xScale, yScale } @@ -152,6 +169,7 @@ export const generateLines = (data, xScale, yScale, color) => * @param {Function} xScale * @param {Function} yScale * @param {Function} color + * * @return {{ xScale: Function, yScale: Function, lines: Array. }} */ export const generateStackedLines = (data, xScale, yScale, color) => diff --git a/packages/nivo-line/src/index.js b/packages/nivo-line/src/index.js index b9165cd9d6..15d0886db8 100644 --- a/packages/nivo-line/src/index.js +++ b/packages/nivo-line/src/index.js @@ -7,8 +7,5 @@ * file that was distributed with this source code. */ export * from './props' -export { default as Lines } from './Lines' -export { default as LineAreas } from './LineAreas' -export { default as CanvasWrapper } from './canvas/CanvasWrapper' export * from './svg' export * from './canvas' diff --git a/packages/nivo-line/src/props.js b/packages/nivo-line/src/props.js index babd83a2cb..22cebe9a63 100644 --- a/packages/nivo-line/src/props.js +++ b/packages/nivo-line/src/props.js @@ -7,7 +7,7 @@ * file that was distributed with this source code. */ import PropTypes from 'prop-types' -import { lineCurvePropType, scalesPropType } from '@nivo/core' +import { lineCurvePropType } from '@nivo/core' import { LegendPropShape } from '@nivo/legends' export const LinePropTypes = { @@ -34,6 +34,8 @@ export const LinePropTypes = { .isRequired, maxY: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.oneOf(['auto'])]) .isRequired, + + xScaleType: PropTypes.oneOf(['linear', 'ordinal']).isRequired, xScale: PropTypes.func.isRequired, // computed yScale: PropTypes.func.isRequired, // computed @@ -90,6 +92,7 @@ export const LineDefaultProps = { curve: 'linear', // scales + xScaleType: 'linear', minY: 0, maxY: 'auto', diff --git a/packages/nivo-line/src/svg/LineChartSvg.js b/packages/nivo-line/src/svg/LineChartSvg.js index 65ea7cff18..cec69acd32 100644 --- a/packages/nivo-line/src/svg/LineChartSvg.js +++ b/packages/nivo-line/src/svg/LineChartSvg.js @@ -216,10 +216,10 @@ const enhance = compose( withDimensions(), withMotion(), withPropsOnChange( - ['data', 'stacked', 'width', 'height', 'minY', 'maxY'], - ({ data, stacked, width, height, margin, minY, maxY }) => { + ['data', 'stacked', 'width', 'height', 'minY', 'maxY', 'xScaleType'], + ({ data, stacked, width, height, margin, minY, maxY, xScaleType }) => { let scales - const args = { data, width, height, minY, maxY } + const args = { data, width, height, minY, maxY, xScaleType } if (stacked === true) { scales = getStackedScales(args) } else { diff --git a/packages/nivo-line/stories/.eslintrc.yml b/packages/nivo-line/stories/.eslintrc.yml new file mode 100644 index 0000000000..b86501d4a1 --- /dev/null +++ b/packages/nivo-line/stories/.eslintrc.yml @@ -0,0 +1,5 @@ +globals: + module: true + +rules: + react/prop-types: 0 \ No newline at end of file diff --git a/packages/nivo-line/stories/DualYAxis.js b/packages/nivo-line/stories/DualYAxis.js index fffdc4975c..782e0f678d 100644 --- a/packages/nivo-line/stories/DualYAxis.js +++ b/packages/nivo-line/stories/DualYAxis.js @@ -1,4 +1,4 @@ -import React, { Fragment } from 'react' +import React from 'react' import { withInfo } from '@storybook/addon-info' import { LineSvg } from '../es' import { defaultTheme } from '@nivo/core' diff --git a/packages/nivo-line/stories/EmptyValues.js b/packages/nivo-line/stories/EmptyValues.js index 4a0a9522f5..b3b4661cce 100644 --- a/packages/nivo-line/stories/EmptyValues.js +++ b/packages/nivo-line/stories/EmptyValues.js @@ -1,4 +1,4 @@ -import React, { Fragment } from 'react' +import React from 'react' import { withInfo } from '@storybook/addon-info' import { LineSvg, LineAreaSvg } from '../es' import { defaultTheme } from '@nivo/core' diff --git a/packages/nivo-line/stories/Simple.js b/packages/nivo-line/stories/Simple.js index defd0c0580..4c72bdbdec 100644 --- a/packages/nivo-line/stories/Simple.js +++ b/packages/nivo-line/stories/Simple.js @@ -1,26 +1,21 @@ -import React, { Fragment } from 'react' +import React from 'react' import { withInfo } from '@storybook/addon-info' -import { generatePointsSerie} from '@nivo/generators' +import { generatePointsSerie } from '@nivo/generators' import { LineChartSvg } from '../es' const margin = { top: 20, right: 160, bottom: 60, left: 80 } -const data = [{ - id: 'default', - data: generatePointsSerie({ - easing: 'random', - xStep: 10, - yRand: 10, - }) -}] +const data = [ + { + id: 'default', + data: generatePointsSerie({ + easing: 'random', + xStep: 10, + yRand: 10, + }), + }, +] -const Simple = () => ( - -) +const Simple = () => const documentation = ` A simple line chart. diff --git a/packages/nivo-line/stories/line.stories.js b/packages/nivo-line/stories/line.stories.js index a8487cf97f..9251ea55b1 100644 --- a/packages/nivo-line/stories/line.stories.js +++ b/packages/nivo-line/stories/line.stories.js @@ -36,7 +36,6 @@ stories.add('with custom curve', () => ( )) - /* stories.add('with area', () => ( { - return dedent(` - import React, { Fragment } from 'react' - import { - Scales, LinearScaleX, LinearScaleY, - AxisX, AxisY, - Lines, Line, - LineAreas, LineArea, - } from '@nivo/line' - - , - , - - ]}> - {scales => ( - - - - - - {generator => ( - - - - - )} - - - {generator => ( - - - - - )} - - - )} - - `) -} diff --git a/website/src/components/charts/line/Line/defaultSettings.js b/website/src/components/charts/line/Line/defaultSettings.js index 5b1644bc7f..4a48414ce1 100644 --- a/website/src/components/charts/line/Line/defaultSettings.js +++ b/website/src/components/charts/line/Line/defaultSettings.js @@ -20,6 +20,7 @@ export default { 'enable axisTop': false, axisTop: { format: d => `${d} km`, + tickCount: 10, tickSize: 5, tickPadding: 5, tickRotation: 0, @@ -39,6 +40,7 @@ export default { 'enable axisBottom': true, axisBottom: { format: d => `${d} km`, + tickCount: 10, tickSize: 5, tickPadding: 5, tickRotation: 0, diff --git a/website/src/components/charts/line/Line/generateData.js b/website/src/components/charts/line/Line/generateData.js index 35f51e1d79..bb9cf19af7 100644 --- a/website/src/components/charts/line/Line/generateData.js +++ b/website/src/components/charts/line/Line/generateData.js @@ -11,17 +11,15 @@ import { generatePointsSerie } from '@nivo/generators' export default () => { const keys = ['A', 'B', 'C', 'D', 'E'] - return keys.map((key, i) => { - return { - id: key, - data: generatePointsSerie({ - x1: 100, - xStep: 10, - y0: Math.random() * 80, - y1: Math.random() * 80, - yRand: 3, - easing: 'random', - }), - } - }) + return keys.map(key => ({ + id: `item ${key}`, + data: generatePointsSerie({ + x1: 200, + xStep: 10, + y0: Math.random() * 80, + y1: Math.random() * 80, + yRand: 3, + easing: 'random', + }), + })) } diff --git a/website/src/components/charts/line/Line/index.js b/website/src/components/charts/line/Line/index.js index 6ae0aed991..9c22d43e12 100644 --- a/website/src/components/charts/line/Line/index.js +++ b/website/src/components/charts/line/Line/index.js @@ -18,8 +18,8 @@ import ComponentPropsDocumentation from '../../../properties/ComponentPropsDocum import properties from '../props' import config from '../../../../config' import propsMapper from '../propsMapper' -import defaultSettings from './defaultSettings' import generateData from './generateData' +import defaultSettings from './defaultSettings' export default class LinePage extends Component { state = { diff --git a/website/src/components/charts/line/LineAPI.js b/website/src/components/charts/line/LineAPI.js index fd5c53c1a5..d03bbda518 100644 --- a/website/src/components/charts/line/LineAPI.js +++ b/website/src/components/charts/line/LineAPI.js @@ -7,11 +7,9 @@ * file that was distributed with this source code. */ import React from 'react' -import omit from 'lodash/omit' import APIClient from '../../api-client/APIClient' import LineControls from './LineControls' import propsMapper from './propsMapper' -import defaultProps from './defaultProps' const LineAPI = ({ data }) => ( ( controls={LineControls} propsMapper={propsMapper} defaultProps={{ - ...omit(defaultProps, [ - 'animate', - 'motionDamping', - 'motionStiffness', - 'isInteractive', - 'enableStackTooltip', - ]), data: JSON.stringify(data, null, ' '), }} /> diff --git a/website/src/components/charts/line/defaultProps.js b/website/src/components/charts/line/LineCanvas/defaultSettings.js similarity index 61% rename from website/src/components/charts/line/defaultProps.js rename to website/src/components/charts/line/LineCanvas/defaultSettings.js index 6f927224ad..df2408d00d 100644 --- a/website/src/components/charts/line/defaultProps.js +++ b/website/src/components/charts/line/LineCanvas/defaultSettings.js @@ -6,16 +6,12 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ - export default { - width: 600, - height: 400, - margin: { - top: 50, + top: 60, right: 110, - bottom: 50, - left: 60, + bottom: 60, + left: 70, }, curve: 'linear', @@ -23,44 +19,43 @@ export default { // axes 'enable axisTop': false, axisTop: { - scale: 'x', - orient: 'top', + format: d => `${d} km`, + tickCount: 10, tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: '', - legendOffset: 36, + legend: 'distance', + legendOffset: -36, + legendPosition: 'center', }, - 'enable axisRight': true, + 'enable axisRight': false, axisRight: { - scale: 'y2', - orient: 'right', + format: d => `${d} $`, tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: '', + legend: 'cost', legendOffset: 0, }, 'enable axisBottom': true, axisBottom: { - scale: 'x', - orient: 'bottom', + format: d => `${d} km`, + tickCount: 10, tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: 'country code', - legendOffset: 36, + legend: 'distance', + legendOffset: 40, legendPosition: 'center', }, 'enable axisLeft': true, axisLeft: { - scale: 'y1', - orient: 'left', + format: d => `${d} $`, tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: 'count', - legendOffset: -40, + legend: 'cost', + legendOffset: -45, legendPosition: 'center', }, @@ -68,12 +63,12 @@ export default { enableGridX: true, enableGridY: true, - colors: 'nivo', + colors: 'd320b', colorBy: 'id', lineWidth: 2, // dots - enableDots: true, + enableDots: false, dotSize: 10, dotColor: { type: 'inherit:darker', gamma: 0.3 }, dotBorderWidth: 2, @@ -83,9 +78,8 @@ export default { dotLabelYOffset: -12, // area - enableArea: true, - enableAreas: true, - areaOpacity: 0.2, + enableArea: false, + areaOpacity: 0.1, // motion animate: true, @@ -95,4 +89,17 @@ export default { // interactivity isInteractive: true, enableStackTooltip: true, + + legends: [ + { + anchor: 'bottom-right', + direction: 'column', + translateX: 100, + itemWidth: 80, + itemHeight: 12, + itemsSpacing: 3, + symbolSize: 12, + symbolShape: 'circle', + }, + ], } diff --git a/website/src/components/charts/line/LineCanvas/generateData.js b/website/src/components/charts/line/LineCanvas/generateData.js new file mode 100644 index 0000000000..834caf7cc1 --- /dev/null +++ b/website/src/components/charts/line/LineCanvas/generateData.js @@ -0,0 +1,25 @@ +/* + * 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 { generatePointsSerie } from '@nivo/generators' + +export default () => { + const keys = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N'] + + return keys.map((key, i) => ({ + id: `item ${key}`, + data: generatePointsSerie({ + x1: 200, + xStep: 0.5, + y0: Math.random() * 80, + y1: Math.random() * 80, + yRand: Math.random() * 3, + easing: 'random', + }), + })) +} diff --git a/website/src/components/charts/line/LineCanvas/index.js b/website/src/components/charts/line/LineCanvas/index.js index a7aa58a298..c7daed8f8c 100644 --- a/website/src/components/charts/line/LineCanvas/index.js +++ b/website/src/components/charts/line/LineCanvas/index.js @@ -6,22 +6,10 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import React, { Component, Fragment } from 'react' -import omit from 'lodash/omit' +import React, { Component } from 'react' import { Link } from 'react-router-dom' import MediaQuery from 'react-responsive' -import { generatePointsSerie } from '@nivo/generators' -import { ResponsiveWrapper } from '@nivo/core' -import { Scales, LinearScaleX, LinearScaleY } from '@nivo/scales' -import { GridCanvas, XAxisCanvas, YAxisCanvas } from '@nivo/axes' -import { - LineDefaultProps, - Lines, - LineAreas, - CanvasWrapper, - LineCanvas, - LineAreaCanvas, -} from '@nivo/line' +import { LineDefaultProps, ResponsiveLineChartCanvas } from '@nivo/line' import ChartHeader from '../../../ChartHeader' import ChartTabs from '../../../ChartTabs' import LineControls from '../LineControls' @@ -29,25 +17,18 @@ import generateCode from '../../../../lib/generateChartCode' import ComponentPropsDocumentation from '../../../properties/ComponentPropsDocumentation' import properties from '../props' import config from '../../../../config' -import defaultProps from '../defaultProps' import propsMapper from '../propsMapper' +import generateData from './generateData' +import defaultSettings from './defaultSettings' export default class LinePage extends Component { state = { - settings: { - ...omit(defaultProps, ['width', 'height']), - legends: [ - { - anchor: 'bottom-right', - direction: 'column', - translateX: 100, - itemWidth: 80, - itemHeight: 20, - symbolSize: 12, - symbolShape: 'circle', - }, - ], - }, + data: generateData(), + settings: { ...defaultSettings }, + } + + diceRoll = () => { + this.setState({ data: generateData() }) } handleSettingsUpdate = settings => { @@ -55,8 +36,7 @@ export default class LinePage extends Component { } render() { - const { /*data,*/ diceRoll } = this.props - const { settings } = this.state + const { data, settings } = this.state const mappedSettings = propsMapper(settings) @@ -69,41 +49,10 @@ export default class LinePage extends Component { ) - const dataA = generatePointsSerie({ - x1: 120, - xStep: 0.5, - y0: 30, - y1: 80, - yRand: 2, - easing: 'random', - }) - - const dataB = generatePointsSerie({ - x1: 90, - xStep: 0.2, - y0: 0, - y1: 42, - yRand: 4, - easing: 'random', - }) - - const dataC = generatePointsSerie({ - x0: 15, - x1: 120, - xStep: 1, - y0: 24, - y1: 2, - yRand: 2, - easing: 'random', - xGaps: [[35, 45], [85, 95]], - }) - - const data = [] - const description = (

Line chart with stacking ability.

@@ -158,157 +107,7 @@ export default class LinePage extends Component { {description} - - {({ width, height }) => ( - - {ctx => ( - , - , - , - ]} - > - {scales => ( - - - - - - {generator => ( - - - - - - )} - - - {generator => ( - - - - - - )} - - - )} - - )} - - )} - + { - return dedent(` - import React, { Fragment } from 'react' - import { - Scales, LinearScaleX, LinearScaleY, - AxisX, AxisY, - Lines, Line, - LineAreas, LineArea, - } from '@nivo/line' - - , - , - - ]}> - {scales => ( - - - - - - {generator => ( - - - - - )} - - - {generator => ( - - - - - )} - - - )} - - `) -} diff --git a/website/src/components/charts/line/LineComposite/index.js b/website/src/components/charts/line/LineComposite/index.js deleted file mode 100644 index 3e17dfc1b2..0000000000 --- a/website/src/components/charts/line/LineComposite/index.js +++ /dev/null @@ -1,362 +0,0 @@ -/* - * 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, Fragment } from 'react' -import omit from 'lodash/omit' -import { Link } from 'react-router-dom' -import MediaQuery from 'react-responsive' -import { generatePointsSerie } from '@nivo/generators' -import { ResponsiveWrapper } from '@nivo/core' -import { Scales, LinearScaleX, LinearScaleY } from '@nivo/scales' -import { XAxis, YAxis, Grid } from '@nivo/axes' -import { LineSvg, LineDefaultProps, LineAreas, LineAreaSvg } from '@nivo/line' -import { defaultTheme } from '@nivo/core' -import ChartHeader from '../../../ChartHeader' -import ChartTabs from '../../../ChartTabs' -import LineControls from '../LineControls' -import generateCode from '../../../../lib/generateChartCode' -import ComponentPropsDocumentation from '../../../properties/ComponentPropsDocumentation' -import properties from '../props' -import config from '../../../../config' -import defaultProps from '../defaultProps' -import propsMapper from '../propsMapper' - -const generateData = () => { - const a = generatePointsSerie({ - x1: 120, - xStep: 2, - y0: 10, - y1: 80, - yRand: 3, - easing: 'random', - }) - - const b = generatePointsSerie({ - x1: 90, - xStep: 1, - y0: 0, - y1: 42, - yRand: 12, - easing: 'random', - }) - - const c = generatePointsSerie({ - x0: 5, - x1: 120, - xStep: 3, - y0: 24, - y1: 2, - yRand: 3, - easing: 'random', - xGaps: [[35, 45], [85, 95]], - }) - - return { a, b, c } -} - -export default class LinePage extends Component { - state = { - data: generateData(), - settings: { - ...omit(defaultProps, ['width', 'height']), - legends: [ - { - anchor: 'bottom-right', - direction: 'column', - translateX: 100, - itemWidth: 80, - itemHeight: 20, - symbolSize: 12, - symbolShape: 'circle', - }, - ], - }, - } - - diceRoll = () => { - this.setState({ data: generateData() }) - } - - handleSettingsUpdate = settings => { - this.setState({ settings }) - } - - render() { - const { data, settings } = this.state - - const mappedSettings = propsMapper(settings) - - const code = generateCode('ResponsiveLine', mappedSettings, { - pkg: '@nivo/line', - defaults: LineDefaultProps, - }) - - const header = ( - - ) - - const description = ( -
-

Line chart with stacking ability.

-

- Given an array of data series having an id and a nested array of points (with x, - y properties), it will compute the line for each data serie.  If stacked is - true, y values will be automatically aggregated.
- This component also accept empty values, but please note that they must be - explicitly defined though, eg. {'{ x: 10, y: null }'}. -

-

- The responsive alternative of this component is ResponsiveLine. -

-

- This component is available in the{' '} - - nivo-api - , see{' '} - - sample - {' '} - or try it using the API client. You can also see - more example usages in{' '} - - the storybook - . -

-

- See the dedicated guide on how to setup - legends for this component. -

-
- ) - - return ( -
-
- - {header} - {description} - - - - {({ width, height }) => ( - - , - , - , - ]} - > - {scales => ( - - - - - - - - - - - - - )} - - - )} - - - - -
-
- - {header} - {description} - -
-
- ) - } -} diff --git a/website/src/components/charts/line/props.js b/website/src/components/charts/line/props.js index ac68c9d0ae..d0aad89c32 100644 --- a/website/src/components/charts/line/props.js +++ b/website/src/components/charts/line/props.js @@ -43,6 +43,14 @@ export default [ ), required: true, }, + { + key: 'xScaleType', + type: `{('linear'|'ordinal')}`, + scopes: '*', + default: defaults.xScaleType, + description: `Defines x scale type, if you have numeric data, then you should use 'linear', otherwise 'ordinal'`, + required: false, + }, { key: 'width', scopes: ['api'], @@ -85,6 +93,16 @@ export default [ step: 5, }, }, + { + key: 'stacked', + scopes: '*', + description: 'Enable/disable stacked mode.', + type: '{boolean}', + required: false, + default: defaults.stacked, + controlType: 'switch', + controlGroup: 'Base', + }, { key: 'curve', scopes: '*', @@ -165,14 +183,14 @@ export default [ controlGroup: 'Areas', }, { - key: 'areasOpacity', + key: 'areaOpacity', scopes: '*', description: 'Area opacity (0~1), depends on enableArea.', required: false, default: defaults.areaOpacity, type: '{number}', controlType: 'range', - controlGroup: 'Style', + controlGroup: 'Areas', controlOptions: { min: 0, max: 1, diff --git a/website/src/components/charts/line/propsMapper.js b/website/src/components/charts/line/propsMapper.js index 13f0a05696..975ead612f 100644 --- a/website/src/components/charts/line/propsMapper.js +++ b/website/src/components/charts/line/propsMapper.js @@ -6,6 +6,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +import omit from 'lodash/omit' import { settingsMapper, mapAxis, mapInheritedColor } from '../../../lib/settings' export default settingsMapper(