The new and improved pnut
Flexible chart building blocks for React. _(Somewhere between d3 and a charting library)_
* Basics
* Design Choices
* API
* Series
* Grouped Series
* Single Series
* Scales
* Continuous Scale
* Categorical Scale
* Color Scale
* Layout
* Renderables
* Axis
* Chart
* Column
* Interaction
* Line
* Scatter
* Examples
* Line
* Multi Line
* Stacked Area
* Column
* Stacked Column
* Grouped Column
* Scatter
* Bubble
* Todo
1. A Series for your data
2. Some Scales
3. Components to render
``jsx
import {Chart, Line, SingleSeries, ContinuousScale, ColorScale, Axis, layout} from './src/index';
function SavingsOverTime() {
const data = [
{day: 1, savings: 0},
{day: 2, savings: 10},
{day: 3, savings: 20},
{day: 4, savings: 15},
{day: 5, savings: 200}
];
// Define our series with day as the primary dimension
const series = SingleSeries({data});
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x, y and color
const x = ContinuousScale({series, key: 'day', range: ll.xRange});
const y = ContinuousScale({series, key: 'savings', range: ll.yRange, zero: true});
const color = ColorScale({series, key: 'savings', set: ['#ee4400']});
// create a scales object for each of our renderable components
const scales = {series, x, y, color};
// render a chart with two axis and a line
return
}
`
`js`
// good
[
{value: 10, type: 'apples'},
{value: 20, type: 'oranges'},
]
// bad
[
{apples: 10, oranges: 20}
]
`jsx
const data = [
{day: 1, type: 'apples', value: 0},
{day: 2, type: 'apples', value: 10},
{day: 3, type: 'apples', value: 20},
{day: 4, type: 'apples', value: 15},
{day: 5, type: 'apples', value: 200},
{day: 1, type: 'oranges', value: 200},
{day: 2, type: 'oranges', value: 50},
{day: 3, type: 'oranges', value: 30},
{day: 4, type: 'oranges', value: 24},
{day: 5, type: 'oranges', value: 150}
];
const series = new GroupedSeries({groupKey: 'type', pointKey: 'day', data});
`
`jsx
const data = [
{day: 1, type: 'apples', value: 0},
{day: 2, type: 'apples', value: 10},
{day: 3, type: 'apples', value: 20},
{day: 4, type: 'apples', value: 15},
{day: 5, type: 'apples', value: 200}
];
const series = new SingleSeries({data});
`
* A line chart needs a continuous x scale, a continuous y scale, and a color scale.
* A column chart needs a categorical x scale, a continuous y scale, and a color scale.
* A bubble chart needs a continuous scale for x,y and radius, and a color scale.
`ts
type ContinuousScaleConfig = {
series: Series, // A series object
key: string, // Which key on your data points
range: [number, number] // The min and max this scale should map to. (Often layout.yRange)
zero?: boolean, // Force the scale to start at zero
clamp?: boolean // Clamp values outside the series to min and max
};
// Examples
const y = ContinuousScale({series, key: 'value', range: layout.yRange, zero: true});
const x = ContinuousScale({series, key: 'date', range: layout.xRange});
`
`ts
type CategoricalScaleConfig = {
series: Series, // A series object
key: string, // Which key on your data points
padding?: number, // How much space to place between categories (Only needs for column charts)
range: [number, number] // The min and max this scale should map to. (Often layout.xRange)
};
// Examples
const x = ContinuousScale({series, key: 'favouriteColor', range: layout.xRange, padding: 0.1});
`
There are four types:
* Key - _Use the color from a data point_
* Set - _Assign a specific color palette to each distinct item in the data. This should pair with a CategoricalScale._
* Range - _Assign a range of colors and interpolate between them based on a continuous metric. This should pair with a ContinuousScale._
* Interpolated - _Take control of the colors by providing your own interpolator. interpolate is given a scaled value from 0 to 1._
`tspoint.myColor
// Get the color from
const key = ColorScale({series, key: 'myColor'});
// Assign either red, green or blue based on point.type
const set = ColorScale({series, key: 'type', set: ['red', 'green', 'blue']});
// Blend age values from grey to red as they get older
const range = ColorScale({series, key: 'age', range: ['#ccc', 'red']});
// Apply custom interpolation to make the top half of values red
const interpolated = ColorScale({series, key: 'type', interpolate: type => {
return type >= 0.5 ? 'red' : '#ccc';
});
`
`tsx
type layout = (LayoutConfig) => LayoutReturn;
type LayoutConfig = {
width: number,
height: number,
top?: number,
bottom?: number,
left?: number,
right?: number
};
type LayoutReturn = {
width: number, // Original width - left and right
height: number, // Original height - top and bottom
padding: {
top: number,
bottom: number,
left: number,
right: number
},
xRange: [number, number], // tuple from 0 to processed width
yRange: [number, number], // flipped tuple from processed height to zero
};
// example
import {layout} from 'pnut';
const ll = layout({width: 1280, height: 720, top: 32, bottom: 32, left: 32, right: 32});
const x = ContinuousScale({series, key: 'day', range: ll.xRange});
const y = ContinuousScale({series, key: 'savings', range: ll.yRange, zero: true});
return
`
ts
type Props = {
// required
position: Position,
scales: {
series: Series,
x: ContinuousScale|CategoricalScale,
y: ContinuousScale|CategoricalScale
}, // optional
location?: number | string | Date,
// style
strokeWidth: number,
strokeColor: string,
textColor: string,
textSize: number,
textOffset: number,
textFormat: (mixed) => string,
ticks: Function,
tickLength: number,
// component
renderText?: Function,
renderAxisLine?: Function,
renderTickLine?: Function
};
`
$3
Chart wraps your renderables in an svg tag and applies widths and padding.`ts
type Props = {
children: Node,
height: number,
padding?: {top?: number, bottom?: number, left?: number, right?: number},
style?: Object,
width: number
};
`$3
Render a column for each point in your series`ts
type Props = {
// Required scales. Must have a categorical x and a continuous y
scales: {
x: CategoricalScale,
y: ContinuousScale,
color: ColorScale,
series: Series
},
strokeWidth?: string,
stroke?: string,
renderPoint?: Function
};
`$3
Get information about the closest data points relative to the mouse position.
`ts
type Props = {
scales: {
series: Series,
x: ContinuousScale|CategoricalScale,
y: ContinuousScale|CategoricalScale
}, // The height and width from your layout
height: number,
width: number,
// How many times per second to update changes and call props.children or props.onChange
fps?: number,
// A render function that is given the current InteractionData.
children: (InteractionData) => Node
// A callback fired when the user clicks on the chart somewhere.
onClick?: (InteractionData) => void,
// A callback that is fired when the user moves their mouse.
onChange?: (InteractionData) => void,
};
// An array of points that are close on the x axis to the mouse.
// Useful for stacked charts to show all values in a tooltip.
xPoints: Array,
// Details on the current mouse position
position: {
x: number,
y: number,
pageX: number,
pageY: number,
clientX: number,
clientY: number,
screenX: number,
screenY: number,
elementWidth: number,
elementHeight: number,
isOver: boolean,
isDown: boolean
},
};
`$3
Render a set of lines for each group in your series
`ts
type Props = {
scales: {
x: ContinuousScale,
y: ContinuousScale,
color: ColorScale,
series: Series
}, // Render line as an area chart
area?: boolean,
// A function that returns the chosen d3 curve generator
curve?: Function,
// Set the width of the line that is drawn
strokeWidth?: string,
renderGroup?: Function
};
`$3
Render a series of circles at each data point in your series
`ts
type Props = {
scales: {
x: ContinuousScale,
y: ContinuousScale,
radius: ContinuousScale,
color: CategoricalScale,
series: Series
}, // Set the outline width and color of each circle
strokeColor?: string,
strokeWidth?: string,
renderPoint?: Function
};
`
Examples
Line
`jsx
import {SingleSeries, ContinuousScale, ColorScale, Axis, Line, layout} from 'pnut';function SavingsOverTime() {
const {data} = props;
// Define our series with day as the primary dimension
const series = new SingleSeries({data});
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x, y and color
const x = ContinuousScale({series, key: 'day', range: ll.xRange});
const y = ContinuousScale({series, key: 'savings', range: ll.yRange, zero: true});
const color = ColorScale({series, key: 'savings', set: ['red']});
// create a scales object for each of our renderable components
const scales = {series, x, y, color};
// render a chart with two axis and a line
return
;
}
`
Multi Line
`jsx
import {Chart, Line, Series, ContinuousScale, ColorScale, Axis, layout} from './src/index';function MultiLine() {
const data = [
{day: 1, type: 'apples', value: 0},
{day: 2, type: 'apples', value: 10},
{day: 3, type: 'apples', value: 20},
{day: 4, type: 'apples', value: 15},
{day: 5, type: 'apples', value: 200},
{day: 1, type: 'oranges', value: 200},
{day: 2, type: 'oranges', value: 50},
{day: 3, type: 'oranges', value: 30},
{day: 4, type: 'oranges', value: 24},
{day: 5, type: 'oranges', value: 150}
];
// Define our series with type as the grouping and day as the primary dimension
const series = new GroupedSeries({groupKey: 'type', pointKey: 'day', data});
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x, y and color
const x = ContinuousScale({series, key: 'day', range: ll.xRange});
const y = ContinuousScale({series, key: 'value', range: ll.yRange, zero: true});
const color = ColorScale({series, key: 'type', set: ['red', 'orange']});
// create a scales object for each of our renderable components
const scales = {series, x, y, color};
// render a chart with two axis and a line
return
;
}
`
Stacked Area
`jsx
import {Chart, Line, Series, ContinuousScale, ColorScale, Axis, layout, stack} from './src/index';function StackedArea() {
const data = [
{day: 1, type: 'apples', value: 0},
{day: 2, type: 'apples', value: 10},
{day: 3, type: 'apples', value: 20},
{day: 4, type: 'apples', value: 15},
{day: 5, type: 'apples', value: 200},
{day: 1, type: 'oranges', value: 200},
{day: 2, type: 'oranges', value: 50},
{day: 3, type: 'oranges', value: 30},
{day: 4, type: 'oranges', value: 24},
{day: 5, type: 'oranges', value: 150}
];
// Define our series with type as the grouping and day as the primary dimension
const series = new GroupedSeries({groupKey: 'type', pointKey: 'day', data});
.update(stack({key: 'value'})); // stack savings metric
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x, y and color
const x = ContinuousScale({series, key: 'day', range: ll.xRange});
const y = ContinuousScale({series, key: 'value', range: ll.yRange, zero: true});
const color = ColorScale({series, key: 'type', set: ['red', 'orange']});
// create a scales object for each of our renderable components
const scales = {series, x, y, color};
// render a chart with two axis and a line
return
;
}
`
Column
`jsx
import {Chart, Column, SingleSeries, CategoricalScale, ContinuousScale, ColorScale, Axis, layout} from './src/index';function ColumnChart() {
const data = [
{fruit: 'apple', count: 20},
{fruit: 'pears', count: 10},
{fruit: 'strawberry', count: 30}
];
// Define our series with fruit as the primary dimension
const series = new SingleSeries({data});
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x,y and color
const x = CategoricalScale({series, key: 'fruit', range: ll.xRange, padding: 0.1});
const y = ContinuousScale({series, key: 'count', range: ll.yRange, zero: true});
const color = ColorScale({series, key: 'fruit', set: ['red', 'green', 'blue']});
// create a scales object for each of our renderable components
const scales = {series, x, y, color};
// render a chart with two axis and a line
return
;
}
`
Stacked Column
`jsx
import {Chart, Column, Series, CategoricalScale, ContinuousScale, ColorScale, Axis, layout, stack} from './src/index';function StackedColumn() {
const data = [
{day: 1, type: 'apples', value: 10},
{day: 2, type: 'apples', value: 10},
{day: 3, type: 'apples', value: 20},
{day: 4, type: 'apples', value: 15},
{day: 5, type: 'apples', value: 200},
{day: 1, type: 'oranges', value: 200},
{day: 2, type: 'oranges', value: 50},
{day: 3, type: 'oranges', value: 30},
{day: 4, type: 'oranges', value: 24},
{day: 5, type: 'oranges', value: 150}
];
// Define our series with day as the primary dimension
const series = new GroupedSeries({groupKey: 'type', pointKey: 'day', data});
.update(stack({key: 'value'})); // stack savings metric
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x,y and color
const x = CategoricalScale({series, key: 'day', range: ll.xRange, padding: 0.1});
const y = ContinuousScale({series, key: 'value', range: ll.yRange, zero: true});
const color = ColorScale({series, key: 'type', set: ['red', 'green']});
// create a scales object for each of our renderable components
const scales = {series, x, y, color};
// render a chart with two axis and a line
return
;
}
`
Grouped Column
`jsx
import {Chart, Column, Series, CategoricalScale, ContinuousScale, ColorScale, Axis, layout} from './src/index';function GroupedColumn() {
const data = [
{day: 1, type: 'apples', value: 10},
{day: 2, type: 'apples', value: 10},
{day: 3, type: 'apples', value: 20},
{day: 4, type: 'apples', value: 15},
{day: 5, type: 'apples', value: 200},
{day: 1, type: 'oranges', value: 200},
{day: 2, type: 'oranges', value: 50},
{day: 3, type: 'oranges', value: 30},
{day: 4, type: 'oranges', value: 24},
{day: 5, type: 'oranges', value: 150}
];
// Define our series with day as the primary dimension
const series = new GroupedSeries({groupKey: 'type', pointKey: 'day', data});
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x,y and color
const x = CategoricalScale({series, key: 'day', range: ll.xRange, padding: 0.1});
const y = ContinuousScale({series, key: 'value', range: ll.yRange, zero: true});
const color = ColorScale({series, key: 'type', set: ['red', 'green']});
// create a scales object for each of our renderable components
const scales = {series, x, y, color};
// render a chart with two axis and a line
return
;
}
`Scatter
`jsx
import {Chart, Scatter, Series, ContinuousScale, ColorScale, Axis, layout} from './src/index';function ScatterChart() {
const data = [
{day: 1, type: 'apples', value: 0},
{day: 2, type: 'apples', value: 10},
{day: 3, type: 'apples', value: 20},
{day: 4, type: 'apples', value: 15},
{day: 5, type: 'apples', value: 200},
{day: 1, type: 'oranges', value: 200},
{day: 2, type: 'oranges', value: 50},
{day: 3, type: 'oranges', value: 30},
{day: 4, type: 'oranges', value: 24},
{day: 5, type: 'oranges', value: 150}
];
// Define our series with day as the primary dimension
const series = new GroupedSeries({groupKey: 'type', pointKey: 'day', data});
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x, y and color
const x = ContinuousScale({series, key: 'day', range: ll.xRange});
const y = ContinuousScale({series, key: 'value', range: ll.yRange});
const radius = ContinuousScale({series, key: 'value', range: [2, 2]});
const color = ColorScale({series, key: 'type', set: ['red', 'orange']});
// create a scales object for each of our renderable components
const scales = {series, x, y, radius, color};
// render a chart with two axis and a line
return
;
}
`
Bubble
`jsx
import {Chart, Scatter, Series, ContinuousScale, ColorScale, Axis, layout} from './src/index';function BubbleChart() {
const data = [
{day: 1, size: 200, value: 0},
{day: 2, size: 800, value: 10},
{day: 3, size: 900, value: 20},
{day: 4, size: 200, value: 15},
{day: 5, size: 300, value: 200},
{day: 6, size: 400, value: 100},
{day: 7, size: 300, value: 20}
];
// Define our series with type as the grouping and day as the primary dimension
const series = new GroupedSeries({groupKey: 'type', pointKey: 'day', data});
// calculate chart width, height and padding
const ll = layout({width: 400, height: 400, left: 32, bottom: 32});
// Set up scales to define our x, y and color
const x = ContinuousScale({series, key: 'day', range: ll.xRange});
const y = ContinuousScale({series, key: 'value', range: ll.yRange});
const radius = ContinuousScale({series, key: 'size', range: [2, 10]});
const color = ColorScale({series, key: 'type', set: ['orange']});
// create a scales object for each of our renderable components
const scales = {series, x, y, radius, color};
// render a chart with two axis and a line
return
;
}
``