Skip to content

Commit

Permalink
feat(radar): add radar chart
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 8, 2017
1 parent 41e9e25 commit b0739b9
Show file tree
Hide file tree
Showing 17 changed files with 971 additions and 198 deletions.
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,39 +29,45 @@ Several libraries already exist for React d3 integration, but just a few provide
- Bar
- [`<Bar />`](http://nivo.rocks/#/bar)
- [`<ResponsiveBar />`](http://nivo.rocks/#/bar)
- Line
- [`<Line />`](http://nivo.rocks/#/line)
- [`<ResponsiveLine />`](http://nivo.rocks/#/line)
- Pie
- [`<Pie />`](http://nivo.rocks/#/pie)
- [`<ResponsivePie />`](http://nivo.rocks/#/pie)
- Bubble
- [`<Bubble />`](http://nivo.rocks/#/bubble)
- [`<ResponsiveBubble />`](http://nivo.rocks/#/bubble)
- [`<BubblePlaceholders />`](http://nivo.rocks/#/bubble/placeholders)
- [`<ResponsiveBubblePlaceholders />`](http://nivo.rocks/#/bubble/placeholders)
- Calendar
- [`<Calendar />`](http://nivo.rocks/#/calendar)
- [`<ResponsiveCalendar />`](http://nivo.rocks/#/calendar)
- Chord
- [`<Chord />`](http://nivo.rocks/#/chord)
- [`<ResponsiveChord />`](http://nivo.rocks/#/chord)
- Line
- [`<Line />`](http://nivo.rocks/#/line)
- [`<ResponsiveLine />`](http://nivo.rocks/#/line)
- Pie
- [`<Pie />`](http://nivo.rocks/#/pie)
- [`<ResponsivePie />`](http://nivo.rocks/#/pie)
- Radar
- [`<Radar />`](http://nivo.rocks/#/radar)
- [`<ResponsiveRadar />`](http://nivo.rocks/#/radar)
- TreeMap
- [`<TreeMap />`](http://nivo.rocks/#/treemap)
- [`<ResponsiveTreeMap />`](http://nivo.rocks/#/treemap)
- [`<TreeMapHTML />`](http://nivo.rocks/#/treemap/html)
- [`<ResponsiveTreeMapHTML />`](http://nivo.rocks/#/treemap/html)
- [`<TreeMapPlaceholders />`](http://nivo.rocks/#/treemap/placeholders)
- [`<ResponsiveTreeMapPlaceholders />`](http://nivo.rocks/#/treemap/placeholders)
- Calendar
- [`<Calendar />`](http://nivo.rocks/#/calendar)
- [`<ResponsiveCalendar />`](http://nivo.rocks/#/calendar)
- Chord
- [`<Chord />`](http://nivo.rocks/#/chord)
- [`<ResponsiveChord />`](http://nivo.rocks/#/chord)
- Voronoi
- [`<Voronoi />`](http://nivo.rocks/#/voronoi)
- [`<ResponsiveVoronoi />`](http://nivo.rocks/#/voronoi)

## [HTTP API](https://github.com/plouc/nivo-api)

- [`<Bar />`](https://nivo-api.herokuapp.com/samples/bar)
- [`<Bubble />`](https://nivo-api.herokuapp.com/samples/bubble)
- [`<Chord />`](https://nivo-api.herokuapp.com/samples/chord)
- [`<Line />`](https://nivo-api.herokuapp.com/samples/line)
- [`<Pie />`](https://nivo-api.herokuapp.com/samples/pie)
- [`<Bubble />`](https://nivo-api.herokuapp.com/samples/bubble)
- [`<TreeMap />`](https://nivo-api.herokuapp.com/samples/treemap)
- [`<Chord />`](https://nivo-api.herokuapp.com/samples/chord)

## Guides

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"d3-color": "^1.0.3",
"d3-format": "^1.2.0",
"d3-hierarchy": "^1.1.5",
"d3-interpolate": "^1.1.5",
"d3-scale": "^1.0.6",
"d3-scale-chromatic": "^1.1.1",
"d3-shape": "^1.2.0",
Expand Down
5 changes: 3 additions & 2 deletions src/Nivo.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ export const defaultTheme = {
legendFontSize: '11px',
},
grid: {
stroke: '#000',
strokeDasharray: '3,6',
stroke: '#ddd',
strokeWidth: 1,
strokeDasharray: '',
},
}

Expand Down
96 changes: 96 additions & 0 deletions src/components/SmartMotion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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.
*/

// credit to Tanner Linsey from this issue on react motion repository:
// https://github.com/chenglou/react-motion/issues/153

import React, { Component } from 'react'
import { Motion, spring } from 'react-motion'
import { interpolate } from 'd3-interpolate'

const enhancedSpring = (value, config) => {
if (typeof value !== 'number') {
return {
value,
config,
interpolator: config && config.interpolator ? config.interpolator : interpolate,
}
}
return spring(value, config)
}

export default class SmartMotion extends Component {
oldValues = {}
newInters = {}
currentStepValues = {}
stepValues = {}
stepInterpolators = {}

render() {
const { style, children, ...rest } = this.props

const resolvedStyle = style(enhancedSpring)

for (let key in resolvedStyle) {
if (
// If key is a non-numeric interpolation
resolvedStyle[key] &&
resolvedStyle[key].interpolator
) {
// Make sure the steps start at 0
this.currentStepValues[key] = this.currentStepValues[key] || 0
if (
// And the value has changed
typeof this.newInters[key] === 'undefined' ||
resolvedStyle[key].value !== this.newInters[key].value
) {
// Save the new value
this.newInters[key] = resolvedStyle[key]

// Increment the stepInterValue for this key by 1
this.stepValues[key] = this.currentStepValues[key] + 1

// Set up the new interpolator
this.stepInterpolators[key] = this.newInters[key].interpolator(
this.oldValues[key],
this.newInters[key].value
)
}
// Return the spring with the destination stepValue and spring config
resolvedStyle[key] = spring(this.stepValues[key], this.newInters[key].config)
// console.log(resolvedStyle[key])
}
}

return (
<Motion {...rest} style={resolvedStyle}>
{values => {
const newValues = {}
for (let key in values) {
if (this.stepValues[key]) {
// Save the currentStepValue
this.currentStepValues[key] = values[key]
// Figure the percentage
const percentage =
this.currentStepValues[key] - this.stepValues[key] + 1
// Save the current value and replace the value in the interpolated object
this.oldValues[key] = newValues[key] = this.stepInterpolators[key](
percentage
)
}
}
return children({
...values,
...newValues,
})
}}
</Motion>
)
}
}
198 changes: 198 additions & 0 deletions src/components/charts/radar/Radar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* 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 { range, max, maxBy, sumBy, uniq } from 'lodash'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { merge } from 'lodash'
import Nivo, { defaultTheme } from '../../../Nivo'
import { marginPropType, motionPropTypes, closedCurvePropType } from '../../../props'
import { getColorsGenerator } from '../../../lib/colorUtils'
import SvgWrapper from '../SvgWrapper'
import { scaleLinear } from 'd3-scale'
import RadarShapes from './RadarShapes'
import RadarGrid from './RadarGrid'
import RadarMarkers from './RadarMarkers'

export default class Radar extends Component {
static propTypes = {
// data
facets: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))
.isRequired,
data: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
data: PropTypes.arrayOf(PropTypes.number).isRequired,
})
).isRequired,

curve: closedCurvePropType.isRequired,

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

// border
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

// grid
gridLevels: PropTypes.number,
gridShape: PropTypes.oneOf(['circular', 'linear']),
gridLabelOffset: PropTypes.number,

// markers
enableMarkers: PropTypes.bool.isRequired,
markersSize: PropTypes.number,
markersColor: PropTypes.any,
markersBorderWidth: PropTypes.number,
markersBorderColor: PropTypes.any,

// theming
theme: PropTypes.object.isRequired,
colors: PropTypes.any.isRequired,
colorBy: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
fillOpacity: PropTypes.number.isRequired,

// motion
...motionPropTypes,
}

static defaultProps = {
// dimensions
margin: Nivo.defaults.margin,

curve: 'linearClosed',

// border
borderWidth: 2,
borderColor: 'inherit',

// grid
gridLevels: 5,
gridShape: 'circular',
gridLabelOffset: 16,

// markers
enableMarkers: true,

// theming
theme: {},
colors: Nivo.defaults.colorRange,
colorBy: 'id',
fillOpacity: 0.15,

// motion
animate: true,
motionStiffness: Nivo.defaults.motionStiffness,
motionDamping: Nivo.defaults.motionDamping,
}

render() {
const {
facets,
data: _data,

curve,

// dimensions
margin: _margin,
width: _width,
height: _height,

// border
borderWidth,
borderColor,

// grid
gridLevels,
gridShape,
gridLabelOffset,

// markers
enableMarkers,
markersSize,
markersColor,
markersBorderWidth,
markersBorderColor,

// theming
theme: _theme,
colors,
colorBy,
fillOpacity,

// motion
animate,
motionStiffness,
motionDamping,
} = this.props

const margin = Object.assign({}, Nivo.defaults.margin, _margin)
const width = _width - margin.left - margin.right
const height = _height - margin.top - margin.bottom
const centerX = width / 2
const centerY = height / 2

const theme = merge({}, defaultTheme, _theme)
const color = getColorsGenerator(colors, colorBy)

const data = _data.map(d => Object.assign({}, d, { color: color(d) }))

const maxValue = max(data.reduce((acc, serie) => [...acc, ...serie.data], []))

const radius = Math.min(width, height) / 2
const radiusScale = scaleLinear().range([0, radius]).domain([0, maxValue])
const angleStep = Math.PI * 2 / facets.length

const motionProps = {
animate,
motionDamping,
motionStiffness,
}

return (
<SvgWrapper width={_width} height={_height} margin={margin}>
<g transform={`translate(${centerX}, ${centerY})`}>
<RadarGrid
levels={gridLevels}
shape={gridShape}
radius={radius}
angleStep={angleStep}
theme={theme}
facets={facets}
labelOffset={gridLabelOffset}
{...motionProps}
/>
<RadarShapes
facets={facets}
data={data}
radiusScale={radiusScale}
curve={curve}
borderWidth={borderWidth}
borderColor={borderColor}
fillOpacity={fillOpacity}
{...motionProps}
/>
{enableMarkers &&
<RadarMarkers
data={data}
radiusScale={radiusScale}
angleStep={angleStep}
size={markersSize}
color={markersColor}
borderWidth={markersBorderWidth}
borderColor={markersBorderColor}
{...motionProps}
/>}
</g>
</SvgWrapper>
)
}
}
Loading

0 comments on commit b0739b9

Please sign in to comment.