Skip to content

Commit

Permalink
feat(pie): restore Pie chart
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 4, 2017
1 parent 8e723f1 commit 7eb8596
Show file tree
Hide file tree
Showing 26 changed files with 392 additions and 229 deletions.
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nivo",
"version": "0.1.0",
"version": "0.6.0",
"licenses": [
{
"type": "MIT",
Expand All @@ -13,8 +13,16 @@
},
"keywords": [],
"dependencies": {
"d3": "^4.10.0",
"d3-chord": "^1.0.4",
"d3-color": "^1.0.3",
"d3-format": "^1.2.0",
"d3-hierarchy": "^1.1.5",
"d3-scale": "^1.0.6",
"d3-scale-chromatic": "^1.1.1",
"d3-shape": "^1.2.0",
"d3-time": "^1.0.7",
"d3-time-format": "^2.0.5",
"d3-voronoi": "^1.1.2",
"invariant": "^2.2.2",
"lodash": "^4.17.4",
"lodash-es": "^4.17.4",
Expand Down Expand Up @@ -74,7 +82,7 @@
"fmt": "prettier --print-width=100 --tab-width=4 --bracket-spacing --no-semi --trailing-comma es5 --single-quote --color --write \"{src,specs,test}/**/*.js\"",
"fmt:check": "prettier --print-width=100 --tab-width=4 --bracket-spacing --no-semi --trailing-comma es5 --single-quote --list-different \"{src,specs,test}/**/*.js\"",
"version": "echo ${npm_package_version}",
"prebublishOnly": "npm test && npm run build",
"prepublishOnly": "npm test && npm run build",
"precommit": "lint-staged",
"commitmsg": "validate-commit-msg"
},
Expand Down
7 changes: 5 additions & 2 deletions src/ArcUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import d3 from 'd3'

export const degreesToRadians = degrees => degrees * Math.PI / 180

export const radiansToDegrees = radians => 180 * radians / Math.PI
Expand Down Expand Up @@ -89,3 +87,8 @@ export const findFollowing = (i, identity, prevData, newData) => {
}

export const midAngle = arc => arc.startAngle + (arc.endAngle - arc.startAngle) / 2

export const positionFromAngle = (angle, distance) => ({
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance,
})
43 changes: 41 additions & 2 deletions src/ColorUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
*/
import _ from 'lodash'
import { spring } from 'react-motion'
import { rgb } from 'd3-color'
import {
scaleOrdinal,
schemeCategory10,
schemeCategory20,
schemeCategory20b,
schemeCategory20c,
rgb,
} from 'd3'
} from 'd3-scale'
import {
schemeAccent,
schemeDark2,
Expand Down Expand Up @@ -137,3 +137,42 @@ export const getColorsGenerator = (colors, colorBy) => {

return d => scale(getColorId(d))
}

/**
* Memoize both color generator & color generator result.
*/
const memoizedColorModifier = _.memoize((method, _amount) => {
const amount = parseFloat(_amount)

return _.memoize(d => rgb(d.color)[method](amount), d => d.color)
}, (method, amount) => `${method}.${amount}`)

const noneGenerator = () => 'none'
const inheritGenerator = d => d.color

/**
* @param {string|Function} instruction
* @param {string} [themeKey]
* @return {Function}
*/
export const getInheritedColorGenerator = (instruction, themeKey) => {
if (instruction === 'none') return noneGenerator

if (_.isFunction(instruction)) return instruction

if (instruction === 'theme') {
return (d, theme) => _.get(theme, themeKey)
}

if (instruction === 'inherit') return inheritGenerator

const inheritMatches = instruction.match(/inherit:(darker|brighter)\(([0-9.]+)\)/)
if (inheritMatches) {
const method = inheritMatches[1]
const amount = inheritMatches[2]

return memoizedColorModifier(method, amount)
}

return () => instruction
}
3 changes: 1 addition & 2 deletions src/Nivo.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { scaleOrdinal } from 'd3'
import { scaleOrdinal } from 'd3-scale'
import { schemeSet3 } from 'd3-scale-chromatic'
import { nivoCategoricalColors } from './ColorUtils'

Expand Down
2 changes: 1 addition & 1 deletion src/components/charts/bubble/BubblePlaceholders.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/
import React, { Component } from 'react'
import { TransitionMotion, spring } from 'react-motion'
import { rgb } from 'd3'
import { rgb } from 'd3-color'
import _ from 'lodash'
import Nivo from '../../../Nivo'
import { getColorsGenerator, extractRGB } from '../../../ColorUtils'
Expand Down
2 changes: 1 addition & 1 deletion src/components/charts/bubble/BubbleProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const bubblePropTypes = {

// labels
enableLabel: PropTypes.bool.isRequired,
label: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
labelFormat: PropTypes.string,
labelTextColor: PropTypes.any.isRequired,
labelTextDY: PropTypes.number.isRequired,
Expand Down
4 changes: 2 additions & 2 deletions src/components/charts/calendar/MotionCalendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import d3 from 'd3'
import { timeFormat } from 'd3-time-format'
import { TransitionMotion, spring } from 'react-motion'
import { DIRECTION_HORIZONTAL } from '../../../constants/directions'
import CalendarDay from './CalendarDay'
Expand Down Expand Up @@ -97,7 +97,7 @@ class MotionCalendar extends Component {
motionDamping,
} = this.props

const monthLegendFormat = d3.time.format('%b')
const monthLegendFormat = timeFormat('%b')

const stiffness = motionStiffness
const damping = motionDamping
Expand Down
2 changes: 1 addition & 1 deletion src/components/charts/calendar/StaticCalendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import { timeFormat } from 'd3'
import { timeFormat } from 'd3-time-format'
import { DIRECTION_HORIZONTAL } from '../../../constants/directions'
import CalendarDay from './CalendarDay'
import CalendarMonthPath from './CalendarMonthPath'
Expand Down
5 changes: 3 additions & 2 deletions src/components/charts/chord/Chord.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import PropTypes from 'prop-types'
import Nivo from '../../../Nivo'
import { margin as marginPropType } from '../../../PropTypes'
import { getColorRange } from '../../../ColorUtils'

import { chord as d3Chord, arc as Arc, ribbon as Ribbon, rgb } from 'd3'
import { chord as d3Chord, ribbon as Ribbon } from 'd3-chord'
import { arc as Arc } from 'd3-shape'
import { rgb } from 'd3-color'

class Chord extends Component {
render() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/charts/line/Line.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { merge } from 'lodash'
import { line } from 'd3'
import { line } from 'd3-shape'
import Nivo, { defaultTheme } from '../../../Nivo'
import { margin as marginPropType, motion as motionPropTypes } from '../../../PropTypes'
import { getColorsGenerator, getColorGenerator } from '../../../ColorUtils'
Expand Down
118 changes: 108 additions & 10 deletions src/components/charts/pie/Pie.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import _ from 'lodash'
import PropTypes from 'prop-types'
import { merge } from 'lodash'
import { Motion, TransitionMotion, spring } from 'react-motion'
import Nivo, { defaultTheme } from '../../../Nivo'
import { margin as marginPropType, motion as motionPropTypes } from '../../../PropTypes'
import { getColorsGenerator } from '../../../ColorUtils'
import { getColorsGenerator, getInheritedColorGenerator } from '../../../ColorUtils'
import { getLabelGenerator } from '../../../lib/propertiesConverters'
import { degreesToRadians } from '../../../ArcUtils'
import SvgWrapper from '../SvgWrapper'
import * as d3 from 'd3'
import { pie as d3Pie, arc as d3Arc } from 'd3-shape'
import PieRadialLabels from './PieRadialLabels'
import PieSlicesLabels from './PieSlicesLabels'

export default class Pie extends Component {
static propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
label: PropTypes.string,
})
).isRequired,

Expand All @@ -37,6 +38,26 @@ export default class Pie extends Component {
padAngle: PropTypes.number.isRequired,
cornerRadius: PropTypes.number.isRequired,

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

// radial labels
enableRadialLabels: PropTypes.bool.isRequired,
radialLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
radialLabelsTextXOffset: PropTypes.number,
radialLabelsTextColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
radialLabelsLinkOffset: PropTypes.number,
radialLabelsLinkDiagonalLength: PropTypes.number,
radialLabelsLinkHorizontalLength: PropTypes.number,
radialLabelsLinkStrokeWidth: PropTypes.number,
radialLabelsLinkColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

// slices labels
enableSlicesLabels: PropTypes.bool.isRequired,
sliceLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
slicesLabelsTextColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

// theming
theme: PropTypes.object.isRequired,
colors: PropTypes.any.isRequired,
Expand All @@ -54,6 +75,21 @@ export default class Pie extends Component {
padAngle: 0,
cornerRadius: 0,

// border
borderWidth: 0,
borderColor: 'inherit:darker(1)',

// radial labels
enableRadialLabels: true,
radialLabel: 'id',
radialLabelsTextColor: 'theme',
radialLabelsLinkColor: 'theme',

// slices labels
enableSlicesLabels: true,
sliceLabel: 'value',
slicesLabelsTextColor: 'theme',

// theming
theme: {},
colors: Nivo.defaults.colorRange,
Expand All @@ -78,6 +114,26 @@ export default class Pie extends Component {
padAngle: _padAngle,
cornerRadius,

// border
borderWidth,
borderColor: _borderColor,

// radial labels
enableRadialLabels,
radialLabel,
radialLabelsLinkOffset,
radialLabelsLinkDiagonalLength,
radialLabelsLinkHorizontalLength,
radialLabelsLinkStrokeWidth,
radialLabelsTextXOffset,
radialLabelsTextColor,
radialLabelsLinkColor,

// slices labels
enableSlicesLabels,
sliceLabel,
slicesLabelsTextColor,

// theming
theme: _theme,
colors,
Expand All @@ -99,21 +155,38 @@ export default class Pie extends Component {

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

const motionProps = {
animate,
motionDamping,
motionStiffness,
}

const radius = 160
const radialLabelsProps = {
label: getLabelGenerator(radialLabel),
linkOffset: radialLabelsLinkOffset,
linkDiagonalLength: radialLabelsLinkDiagonalLength,
linkHorizontalLength: radialLabelsLinkHorizontalLength,
linkStrokeWidth: radialLabelsLinkStrokeWidth,
textXOffset: radialLabelsTextXOffset,
textColor: getInheritedColorGenerator(radialLabelsTextColor, 'axis.textColor'),
linkColor: getInheritedColorGenerator(radialLabelsLinkColor, 'axis.tickColor'),
}

const slicesLabelsProps = {
label: getLabelGenerator(sliceLabel),
textColor: getInheritedColorGenerator(slicesLabelsTextColor, 'axis.textColor'),
}

const radius = Math.min(width, height) / 2
const innerRadius = radius * Math.min(_innerRadius, 1)

const pie = d3.pie()
const pie = d3Pie()
pie.value(d => d.value)

const arc = d3.arc()
arc.outerRadius(160)
const arc = d3Arc()
arc.outerRadius(radius)

return (
<SvgWrapper width={_width} height={_height} margin={margin}>
Expand All @@ -132,19 +205,44 @@ export default class Pie extends Component {
.cornerRadius(interpolatingStyle.cornerRadius)
.innerRadius(interpolatingStyle.innerRadius)

const arcsData = interpolatedPie(data).map(d => ({
...d,
data: {
...d.data,
color: color(d.data),
},
}))

return (
<g
transform={`translate(${interpolatingStyle.centerX}, ${interpolatingStyle.centerY})`}
>
{interpolatedPie(data).map(d => {
{arcsData.map(d => {
return (
<path
key={d.data.id}
d={interpolatedArc(d)}
fill={color(d.data)}
fill={d.data.color}
strokeWidth={borderWidth}
stroke={borderColor(d.data)}
/>
)
})}
{enableSlicesLabels &&
<PieSlicesLabels
data={arcsData}
radius={radius}
innerRadius={interpolatingStyle.innerRadius}
theme={theme}
{...slicesLabelsProps}
/>}
{enableRadialLabels &&
<PieRadialLabels
data={arcsData}
radius={radius}
theme={theme}
{...radialLabelsProps}
/>}
</g>
)
}}
Expand Down
Loading

0 comments on commit 7eb8596

Please sign in to comment.