Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(scales): add support for time scale
Browse files Browse the repository at this point in the history
Raphaël Benitte committed Dec 16, 2017
1 parent 22c186a commit 28e8ebf
Showing 9 changed files with 163 additions and 51 deletions.
2 changes: 0 additions & 2 deletions packages/nivo-scales/package.json
Original file line number Diff line number Diff line change
@@ -32,8 +32,6 @@
},
"scripts": {
"lint": "eslint src stories tests",
"test": "jest --verbose ./tests",
"test:cover": "jest --verbose --coverage ./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",
9 changes: 5 additions & 4 deletions packages/nivo-scales/src/Scales.js
Original file line number Diff line number Diff line change
@@ -12,16 +12,17 @@ import withPropsOnChange from 'recompose/withPropsOnChange'
import setDisplayName from 'recompose/setDisplayName'
import pure from 'recompose/pure'
import PropTypes from 'prop-types'
import { LinearScalePropType, PointScalePropType } from './propsTypes'
import { scalesFromConfig } from './compute'
import { LinearScalePropType, PointScalePropType, TimeScalePropType } from './propsTypes'
import { scalesFromConfig } from './scaleByType'

const Scales = ({ computedScales, children }) => <Fragment>{children(computedScales)}</Fragment>

Scales.propTypes = {
children: PropTypes.func.isRequired,
computedScales: PropTypes.object.isRequired,
scales: PropTypes.arrayOf(PropTypes.oneOfType([LinearScalePropType, PointScalePropType]))
.isRequired,
scales: PropTypes.arrayOf(
PropTypes.oneOfType([LinearScalePropType, PointScalePropType, TimeScalePropType])
).isRequired,
}

const enhance = compose(
4 changes: 3 additions & 1 deletion packages/nivo-scales/src/index.js
Original file line number Diff line number Diff line change
@@ -7,4 +7,6 @@
* file that was distributed with this source code.
*/
export { default as Scales } from './Scales'
export * from './compute'
export * from './linearScale'
export * from './pointScale'
export * from './scaleByType'
Original file line number Diff line number Diff line change
@@ -8,9 +8,20 @@
*/
import getMin from 'lodash/min'
import getMax from 'lodash/max'
import uniq from 'lodash/uniq'
import { scaleLinear, scalePoint } from 'd3-scale'
import { scaleLinear } from 'd3-scale'

/**
* Compute a linear scale from config and data set.
*
* @param {Array.<Array>} data
* @param {'auto'|number} min
* @param {'auto'|number} max
* @param {string|number} property
* @param {Array.<number>} range
* @param {boolean} stacked
*
* @return {Object}
*/
export const computeLinearScale = ({
data,
min = 'auto',
@@ -47,44 +58,3 @@ export const computeLinearScale = ({
.domain([domainMin, domainMax])
.range(range)
}

export const computePointScale = ({
data,
domain: _domain,
range,
property,
checkConsistency = false,
}) => {
if (checkConsistency === true) {
const uniqLengths = uniq(data.map(({ data }) => data.length))
if (uniqLengths.length > 1) {
throw new Error(
[
`Found inconsistent data for '${property}',`,
`expecting all series to have same length`,
`but found: ${uniqLengths.join(', ')}`,
].join(' ')
)
}
}

const domain = _domain !== undefined ? _domain : data[0].map(d => d[property])

return scalePoint()
.range(range)
.domain(domain)
}

const computeFunctionByType = {
linear: computeLinearScale,
point: computePointScale,
}

export const scalesFromConfig = scaleConfigs => {
const computedScales = {}
scaleConfigs.forEach(scaleConfig => {
computedScales[scaleConfig.id] = computeFunctionByType[scaleConfig.type](scaleConfig)
})

return computedScales
}
37 changes: 37 additions & 0 deletions packages/nivo-scales/src/pointScale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 uniq from 'lodash/uniq'
import { scalePoint } from 'd3-scale'

export const computePointScale = ({
data,
domain: _domain,
range,
property,
checkConsistency = false,
}) => {
if (checkConsistency === true) {
const uniqLengths = uniq(data.map(({ data }) => data.length))
if (uniqLengths.length > 1) {
throw new Error(
[
`Found inconsistent data for '${property}',`,
`expecting all series to have same length`,
`but found: ${uniqLengths.join(', ')}`,
].join(' ')
)
}
}

const domain = _domain !== undefined ? _domain : data[0].map(d => d[property])

return scalePoint()
.range(range)
.domain(domain)
}
9 changes: 9 additions & 0 deletions packages/nivo-scales/src/propsTypes.js
Original file line number Diff line number Diff line change
@@ -25,3 +25,12 @@ export const PointScalePropType = PropTypes.shape({
domain: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
range: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).isRequired,
})

export const TimeScalePropType = PropTypes.shape({
type: PropTypes.oneOf(['time']),
data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.object)).isRequired,
property: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
min: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]),
max: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]),
range: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).isRequired,
})
26 changes: 26 additions & 0 deletions packages/nivo-scales/src/scaleByType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 { computeLinearScale } from './linearScale'
import { computePointScale } from './pointScale'
import { computeTimeScale } from './timeScale'

export const scaleByType = {
linear: computeLinearScale,
point: computePointScale,
time: computeTimeScale,
}

export const scalesFromConfig = scaleConfigs => {
const computedScales = {}
scaleConfigs.forEach(scaleConfig => {
computedScales[scaleConfig.id] = scaleByType[scaleConfig.type](scaleConfig)
})

return computedScales
}
66 changes: 66 additions & 0 deletions packages/nivo-scales/src/timeScale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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 uniq from 'lodash/uniq'
import { scaleTime } from 'd3-scale'

/**
* Add auto date conversion to d3 `timeScale`,
* it helps to be able to pas raw data without having
* to deal with Date objects and pre-process the data.
*
* @param {Object} scale
*
* @return {Object}
*/
const enhanceScale = scale => {
const enhancedScale = function(dateStr) {
return scale(new Date(dateStr))
}

// @todo: fix copy()
Object.keys(scale).forEach(key => {
enhancedScale[key] = scale[key].bind(scale)
})

return enhancedScale
}

/**
* Compute a time scale from config and data set.
*
* @param {Array.<Array>} data
* @param {'auto'|string} min
* @param {'auto'|string} max
* @param {string|number} property
* @param {Array.<number>} range
*
* @return {Object}
*/
export const computeTimeScale = ({ data, min = 'auto', max = 'auto', property, range }) => {
let domainMin = min !== 'auto' ? new Date(min) : min
let domainMax = max !== 'auto' ? new Date(max) : max

if (min === 'auto' || max === 'auto') {
const allDates = uniq(
data.reduce((agg, serie) => [...agg, ...serie.map(d => d[property])], [])
)
.map(dateStr => new Date(dateStr))
.sort((a, b) => b - a)
.reverse()

if (min === 'auto') domainMin = allDates[0]
if (max === 'auto') domainMax = allDates[allDates.length - 1]
}

const scale = scaleTime()
.domain([domainMin, domainMax])
.range(range)

return enhanceScale(scale)
}
Original file line number Diff line number Diff line change
@@ -26,6 +26,9 @@ export default () => {
yRand: Math.random() * 2,
easing: 'random',
xGaps: gaps[key] || [],
}),
}).map(d => ({
...d,
y: d.y < 0 ? 0 : d.y,
})),
}))
}

0 comments on commit 28e8ebf

Please sign in to comment.