d3.compose
d3.compose adds advanced chart layout and composition to d3 and d3.chart. Create rich, data-bound charts by composing charts (like Line and Bars) and components (like Axis, Title, and Legend).
Downloads & Dependencies
d3.compose-allIncludes all charts, components, extensions, and mixins. |
|
|---|---|
| d3.compose-all.js | 186kb Development: Full source with tons of comments. |
| d3.compose-all.min.js | 15.2kb Production: minified and gzipped (Source Map) |
| d3.compose.css | Base styling for d3.compose charts/components |
d3.compose-mixinsUse if you're creating your own charts/components and want to use d3.compose's mixins to help. |
|
| d3.compose-mixins.js | 114kb Development |
| d3.compose-mixins.min.js | 9.3kb Production (Source Map) |
d3.composeUse if you're using your own charts/components and don't need mixins, charts, or components provided by d3.compose. |
|
| d3.compose.js | 84kb Development |
| d3.compose.min.js | 7.5kb Production (Source Map) |
d3.compose depends on the following libraries:
Introduction
When creating complex charts with D3.js and d3.chart, laying out and sizing parts of the chart are often manual processes and creating composable and reusable components becomes very difficult. d3.compose creates a standard base that charts and components are built on top of that automatically handles layout and sizing and can be composed and layered to created rich, data-bound charts.
Usage
Add d3.compose and its dependencies to your page:
<!doctype html>
<html>
<head>
<!-- ... -->
<link rel="stylesheet" type="text/css" href="d3.compose.css">
</head>
<body>
<!-- ... -->
<script src="d3.js"></script>
<script src="d3.chart.js"></script>
<script src="d3.compose-all.js"></script>
<!-- Your code -->
</body>
</html>Compose
Compose rich, data-bound charts from charts (like Lines and Bars) and components (like Axis, Title, and Legend) with d3 and d3.chart.
Using the options property, charts and components can be bound to data and customized to create dynamic charts.
<div id="#chart"></div>
var chart = d3.select('#chart').chart('Compose', function(data) {
// Process data...
// Create shared scales
var scales = {
x: {data: data.input, key: 'x', adjacent: true},
y: {data: data.input, key: 'y'},
y2: {data: data.output, key: 'y'}
};
// Setup charts and components
var charts = [
d3c.bars('input', {data: data.input, xScale: scales.x, yScale: scales.y}),
d3c.lines('output', {data: data.output, xScale: scales.x, yScale: scales.y2})
];
var title = d3c.title('d3.compose');
var xAxis = d3c.axis('xAxis', {scale: scales.x});
var yAxis = d3c.axis('yAxis', {scale: scales.y});
var y2Axis = d3c.axis('y2Axis', {scale: scales.y2});
// Layout charts and components
return [
title,
[yAxis, d3c.layered(charts), y2Axis],
xAxis
];;
});
chart.draw({input: [...], output: [...]});
options
{Function|Object}
Get/set the options object/function for the chart that takes data and
returns [...layout] for composing child charts and components.
// get
chart.options();
// set (static)
chart.options([
// ...
]);
// set (dynamic, takes data and returns options)
chart.options(function(data) {
// process data...
return [
// ...
];
});
// Set directly from d3.chart creation
d3.select('#chart')
.chart('Compose', function(data) {
// ...
});
margins
{Object {top, right, bottom, left}} [{top: 10, right: 10, bottom: 10, left: 10}]
Margins between edge of container and components/chart
chart.margins({top: 10, right: 20, bottom: 10, left: 20});
width
{Number}
Get/set overall width of chart
height
{Number}
Get/set overall height of chart
responsive
{Boolean} [true]
Enable responsive container + viewBox so that chart scales to fill width (only works if selection is not an svg)
charts
{Array}
Set charts from options or get chart instances.
Each chart should use a unique key so that updates are passed to the existing chart
(otherwise they are recreated on update).
The type option must be a registered d3.chart and all other options are passed to the chart.
chart.charts([
{id: 'input', type: 'Bars'}, // options to pass to Bars chart
{id: 'output', type: 'Lines'} // options to pass to Lines chart
]);
components
{Array}
Set components from options or get components instances.
Each component should use a unique key so that updates are passed to the existing chart
(otherwise they are recreated on update).
The type option must be a registered d3.chart and all other options are passed to the component.
chart.components([
{id: 'axis.y', type: 'Axis'}, // options to pass to Axis component
{id: 'title', type: 'Title'} // options to pass to Title component
])
delay
{Number|Function} [d3 default: 0]
Delay start of transition by specified milliseconds. (applied to all charts and components as default)
duration
{Number|Function} [d3 default: 250ms]
Transition duration in milliseconds. (applied to all charts and components as default)
ease
{String|Function} [d3 default: 'cubic-in-out']
Transition ease function. (applied to all charts and components as default)
- See: Transitions#ease
- Note: arguments to pass to
d3.easeare not supported
draw
draw(data)
-
data(Any)
Draw chart with given data
var chart = d3.select('#chart')
.chart('Compose', function(data) {
// ...
});
chart.draw([1, 2, 3]);
chart.draw({values: [1, 2, 3]});
chart.draw([
{values: [1, 2, 3]},
{values: [4, 5, 6]}
]);
redraw
Redraw chart with current data
Inherits
Chart
Common base for creating charts.
Standard d3.chart charts can be used with d3.compose, but extending d3.chart('Chart') includes helpers for properties and “di” functions.
Extending
To take advantage of “di”-binding (automatically injects chart into “di” methods)
and automatically setting properties from options, use d3.compose.helpers.di
and d3.compose.helpers.property when creating your chart.
var helpers = d3.compose.helpers;
d3.chart('Chart').extend('Pie', {
initialize: function() {
// same as d3.chart
},
transform: function(data) {
// same as d3.chart
},
color: helpers.di(function(chart, d, i) {
// "di" function with parent chart injected ("this" = element)
}),
centered: helpers.property({
default_value: true
// can be automatically set from options object
})
});
z_index Static
Default z-index for chart (Components are 50 by default, so Chart = 100 is above component by default)
d3.chart('Chart').extend('BelowComponentLayers', {
// ...
}, {
z_index: 40
});
Inherits
Component
Common base for creating components that includes helpers for positioning and layout.
Extending
d3.chart('Component') contains intelligent defaults and there are no required overrides.
Create a component just like a chart, by creating layers in the initialize method in extend.
- To adjust layout calculation, use
prepareLayout,getLayout, andsetLayout. - To layout a component within the chart, use
skip_layout: trueand the staticlayer_type: 'chart'
d3.chart('Component').extend('Key', {
initialize: function() {
this.layer('Key', this.base, {
dataBind: function(data) {
return this.selectAll('text')
.data(data);
},
insert: function() {
return this.append('text');
},
events: {
merge: function() {
this.text(this.chart().keyText)
}
}
})
},
keyText: helpers.di(function(chart, d, i) {
return d.abbr + ' = ' + d.value;
})
});
position
{String} ['top']
Component’s position relative to chart (top, right, bottom, left)
width
{Number} [(actual width)]
Get/set the width of the component (in pixels) (used in layout calculations)
height
{Number} [(actual height)]
Get/set the height of the component (in pixels) (used in layout calculations)
margins
{Object} [{top: 0, right: 0, bottom: 0, left: 0}]
Margins (in pixels) around component
centered
{Boolean} [false]
Center the component vertically/horizontally (depending on position)
skip_layout
Skip component during layout calculations and positioning (override in prototype of extension)
d3.chart('Component').extend('NotLaidOut', {
skip_layout: true
});
prepareLayout
prepareLayout(data)
-
data(Any)
Perform any layout preparation required before getLayout (default is draw) (override in prototype of extension)
Note: By default, components are double-drawn; for every draw, they are drawn once to determine the layout size of the component and a second time for display with the calculated layout. This can cause issues if the component uses transitions. See Axis for an example of a Component with transitions.
d3.chart('Component').extend('Custom', {
prepareLayout: function(data) {
// default: this.draw(data);
// so that getLayout has real dimensions
// -> custom preparation (if necessary)
}
})
getLayout
getLayout(data)
-
data(Any)
position, width, and height for layout
Get layout details for use when laying out component (override in prototype of extension)
d3.chart('Component').extend('Custom', {
getLayout: function(data) {
var calculated_width, calculated_height;
// Perform custom calculations...
// Must return position, width, and height
return {
position: this.position(),
width: calculated_width,
height: calculated_height
};
}
});
setLayout
setLayout(x, y, options)
-
x(Number)position of base top-left
-
y(Number)position of base top-left
-
options(Object)-
[height](Object)height of component in layout
-
[width](Object)width of component in layout
-
Set layout of underlying base (override in prototype of extension)
d3.chart('Component').extend('Custom', {
setLayout: function(x, y, options) {
// Set layout of this.base...
// (the following is the default implementation)
var margins = this.margins();
// (handle this.centered())
this.base
.attr('transform', helpers.translate(x + margins.left, y + margins.top));
this.height(options && options.height);
this.width(options && options.width);
}
});
z_index Static
Default z-index for component (Charts are 100 by default, so Component = 50 is below chart by default)
d3.chart('Component').extend('AboveChartLayers', {
// ...
}, {
z_index: 150
});
layer_type Static
Set to 'chart' to use chart layer for component.
(e.g. Axis uses chart layer to position with charts, but includes layout for ticks)
d3.chart('Component').extend('ChartComponent', {
// ...
}, {
layer_type: 'chart'
});
Inherits
Overlay
Common base for creating overlays that includes helpers for positioning and show/hide.
Extending
Create an overlay just like a chart, by creating layers in the initialize method in extend.
- To adjust positioning, override
position - To adjust show/hide behavior, override
show/hide
d3.chart('Overlay').extend('ClosestPoints', {
// TODO
});
x
{Number} [0]
Overlay’s top-left x-position in px from left
y
{Number} [0]
Overlay’s top-left y-position in px from top
hidden
{Boolean} [true]
Whether overlay is currently hidden
style
{String} [set from x, y, and hidden]
Overlays base styling (default includes position and hidden)
position
position(position, [y])
-
position(Object|Number){x,y}, {container: {x,y}}, {chart: {x,y}} or x in px from left
-
[y](Number)in px from top
Position overlay layer at given x,y coordinates
// Absolute, x: 100, y: 50
overlay.position(100, 50);
overlay.position({x: 100, y: 50});
// Relative-to-chart, x: 50, y: 40
overlay.position({chart: {x: 50, y: 40}});
// Relative-to-container, x: 75, y: 50
overlay.position({container: {x: 75, y: 50}});
show
Show overlay (with display: block)
hide
Hide overlay (with display: none)
getAbsolutePosition
getAbsolutePosition(container_position)
-
container_position(Object)({x, y})
absolute {x, y} relative to container div
Get absolute position from container position (needed since container position uses viewBox and needs to be scaled to absolute position)
Inherits
Base
Shared functionality between all charts and components.
- Set properties automatically from
options, - Store fully transformed data
- Adds
"before:draw"and"draw"events - Standard
widthandheightcalculations
data
{Any}
Store fully-transformed data for direct access from the chart
options
{Object}
Overall options for chart/component, automatically setting any matching properties.
var property = d3.compose.helpers.property;
d3.chart('Base').extend('HasProperties', {
a: property(),
b: property({
set: function(value) {
return {
override: value + '!'
};
}
})
});
var instance = d3.select('#chart')
.chart('HasProperties', {
a: 123,
b: 'Howdy',
c: true
});
// Equivalent to:
// d3.select(...)
// .chart('HasProperties')
// .options({...});
console.log(instance.a()); // -> 123
console.log(instance.b()); // -> Howdy!
console.log(instance.options().c); // -> true
width
Get width of this.base.
(Does not include set for setting width of this.base)
height
Get height of this.base.
(Does not include set for setting height of this.base)
Lines
Create an XY Lines chart with single or series data.
Extending
Great care has been taken in making the standard charts in d3.compose extensible.
To extend the Lines chart, the following methods are available:
createLinelineKeylineDataonDataBindonInsertonEnteronEnterTransitiononUpdateonUpdateTransitiononMergeonMergeTransitiononExitonExitTransition
View the Lines.js source for the default implementation and more information on these methods.
var chart = d3.select('#chart').chart('Compose', function(data) {
return {
charts: {
input: {
type: 'Lines'
data: data.input,
// xScale: ...,
// yScale: ...,
// other properties...
}
}
};
});
// Single y-values
chart.draw([1, 2, 3]);
// Series (x,y) values
chart.draw([
{values: [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]}
{values: [{x: 0, y: 3}, {x: 1, y: 2}, {x: 2, y: 1}]}
]);
interpolate
{String} [monotone]
Set interpolation mode for line
- See: SVG-Shapes#line_interpolate
- Set to
nullor'linear'for no interpolation
Inherits
Bars
Bars chart with centered or adjacent display for single or series data.
To display bars for different series next to each other (adjacent),
use the adjacent option when creating the xScale (see example below).
Extending
To extend the Bars chart, the following methods are available:
barHeightbarWidthbarXbarYbarClassonDataBindonInsertonEnteronEnterTransitiononUpdateonUpdateTransitiononMergeonMergeTransitiononExitonExitTransition
var chart = d3.select('#chart').chart('Compose', function(data) {
// Display bars for different series next to each other (adjacent: true)
var xScale = {type: 'ordinal', adjacent: true, domain: [0, 1, 2]};
return {
charts: {
output: {
type: 'Bars',
data: data.output,
xScale: xScale,
// yScale: ...,
// other properties...
}
}
};
});
// Single y-values
chart.draw([10, 20, 30]);
// Series (x,y) values
chart.draw([
{values: [{x: 0, y: 10}, {x: 1, y: 20}, {x: 2, y: 30}]},
{values: [{x: 0, y: 30}, {x: 1, y: 20}, {x: 2, y: 10}]}
]);
Inherits
StackedBars
Bars chart with values stacked on top of each other
(See Bars for extensibility details)
var chart = d3.select('#chart').chart('Compose', function(data) {
// Display bars for different series next to each other (adjacent: true)
var xScale = {type: 'ordinal', adjacent: true, domain: [0, 1, 2]};
return {
charts: {
stacked_output: {
type: 'StackedBars',
data: data.output,
xScale: xScale,
// yScale: ...,
// other properties...
}
}
};
});
// Single y-values
chart.draw([10, 20, 30]);
// Series (x,y) values
chart.draw([
{values: [{x: 0, y: 10}, {x: 1, y: 20}, {x: 2, y: 30}]},
{values: [{x: 0, y: 30}, {x: 1, y: 20}, {x: 2, y: 10}]}
]);
Inherits
HorizontalBars
Bars chart with bars that group from left-to-right
(See Bars for extensibility details)
var chart = d3.select('#chart').chart('Compose', function(data) {
// Display bars for different series next to each other (adjacent: true)
var xScale = {type: 'ordinal', adjacent: true, domain: [0, 1, 2]};
return {
charts: {
output: {
type: 'HorizontalBars',
data: data.output,
xScale: xScale,
// yScale: ...,
// other properties...
}
}
};
});
// Single y-values
chart.draw([10, 20, 30]);
// Series (x,y) values
chart.draw([
{values: [{x: 0, y: 10}, {x: 1, y: 20}, {x: 2, y: 30}]},
{values: [{x: 0, y: 30}, {x: 1, y: 20}, {x: 2, y: 10}]}
]);
Inherits
HorizontalStackedBars
Horizontal Stacked Bars
(See Bars for extensibility details)
var chart = d3.select('#chart').chart('Compose', function(data) {
// Display bars for different series next to each other (adjacent: true)
var xScale = {type: 'ordinal', adjacent: true, domain: [0, 1, 2]};
return {
charts: {
output: {
type: 'HorizontalStackedBars',
data: data.output,
xScale: xScale,
// yScale: ...,
// other properties...
}
}
};
});
// Single y-values
chart.draw([10, 20, 30]);
// Series (x,y) values
chart.draw([
{values: [{x: 0, y: 10}, {x: 1, y: 20}, {x: 2, y: 30}]},
{values: [{x: 0, y: 30}, {x: 1, y: 20}, {x: 2, y: 10}]}
]);
Inherits
Labels
Standalone or “embeddable” labels (uses mixins.Labels and attachLabels to embed in chart)
Extending
To extend the Labels chart, the following methods are available:
insertLabelsmergeLabelslayoutLabelstransitionLabelsonDataBindonInsertonEnteronEnterTransitiononUpdateonUpdateTransitiononMergeonMergeTransitiononExitonExitTransition
View the Labels.js source for the default implementation and more information on these methods.
var chart = d3.select('#chart').chart('Compose', function(data) {
return {
charts: {
input: {
type: 'Lines',
data: data.input,
// xScale, yScale, other properties...
// Show labels with default properties
labels: true
},
output: {
type: 'Bars',
data: data.output,
// xScale, yScale, other properties...
// Pass options to labels
labels: {
offset: 2,
position: 'top',
style: {
'font-size': '14px'
},
format: d3.format(',0d')
}
},
labels: {
type: 'Labels',
data: data.labels,
// xScale, yScale, other properties...
}
}
};
});
chart.draw({
input: [1, 2, 3],
output: [10, 20, 30],
labels: [
{x: 0, y: 0},
{x: 0, y: 30, label: 'Override (y by default)'},
{x: 2, y: 0},
{x: 2, y: 30}
]
});
format
{String|Function}
Formatting function or string (string is passed to d3.format) for label values
position
{String|Function} [top|bottom]
Label position relative to data point (top, right, bottom, or left)
Additionally, (a)|(b) can be used to set position to a if y-value >= 0 and b otherwise,
where a and b are top, right, bottom, or left
For more advanced positioning, a “di” function can be specified to set position per label
labels.position('top'); // top for all values
labels.position('top|bottom'); // top for y-value >= 0, bottom otherwise
labels.position(function(d, i) { return d.x >= 0 ? 'right' : 'left'; });
offset
{Number|Object} [0]
Offset between data point and label
(if Number is given, offset is set based on position)
padding
{Number} [1]
Padding between text and label background
anchor
{String} [middle]
Define text anchor (start, middle, or end)
(set by default based on label position)
alignment
{String} [middle]
Define text-alignment (top, middle, or bottom)
(set by default based on label position)
labelText
labelText(d, i)
-
d(Any) -
i(Number)
Get label text for data-point (uses “label” property or y-value)
labelClass
labelClass(d, i)
-
d(Any) -
i(Number)
Get class for label group
Inherits
Axis
Axis component for XY data (wraps d3.axis).
Available d3.axis extensions:
tickstickValuestickSizeinnerTickSizeouterTickSizetickPaddingtickFormat
d3.select('#chart')
.chart('Compose', function(data) {
var scales = {
x: {data: data, key: 'x'},
y: {data: data, key: 'y'}
};
var charts = [];
var xAxis = d3c.axis({scale: scales.x});
var yAxis = d3c.axis({scale: scales.y});
return [
// Display y-axis to left of charts
[yAxis, d3c.layered(charts)],
// Display x-axis below charts
xAxis
];
});
scale
{Object|d3.scale}
Scale to pass to d3.axis
- If
xScale/yScaleare given,scaleis set automatically based onposition. - Can be
d3.scaleor, ifObjectis given,helpers.createScaleis used
// Set with d3.scale directly
axis.scale(d3.scale());
// or with Object passed helpers.createScale
axis.scale({data: data, key: 'x'});
// For x0/y0 position, both xScale and yScale needed
// (scale is automatically set by position)
axis.xScale({domain: [0, 100]});
axis.yScale({domain: [0, 10]});
axis.position('y0');
// -> axis.scale() -> axis.xScale by default
translation
{Object} [(set based on position)]
{x,y} translation of axis relative to chart (set by default based on position)
orient
{String} [(set based on position)]
Axis orient for ticks (set by default based on position)
orientation
{String} [(set based on position)]
Axis orientation (vertical or horizonal)
gridlines
{Boolean|Object} [false]
Attach gridlines for axis
(true to show with default options, {...} to pass options to Gridlines)
Inherits
Legend
Legend component that can automatically pull chart and series information from d3.compose
Notes:
- To exclude a chart from the legend, use
exclude_from_legend = truein chart prototype or options - To exclude a series from the legend, use
exclude_from_legend = truein series object - To add swatch for custom chart, use
Legend.registerSwatch()
d3.select('#chart')
.chart('Compose', function(data) {
var input = [{key: 'input', name: 'Input', values: data.input}];
var output = [
{key: 'output1', name: 'Output 1', values: data.output1},
{key: 'output2', name: 'Output 2', values: data.output2}
];
var charts = [
d3c.lines('a', {data: input}), // ...
d3c.bars('b', {data: output}) // ...
];
var legend = d3c.legend({charts: ['a', 'b']});
return [
[d3c.layered(charts), legend]
];
});
// -> automatically creates legend from series data for 'a' and 'b'
// (Lines Swatch) Input
// (Bars Swatch) Output 1
// (Bars Swatch) Output 2
// or, manually set data for legend
return [
d3c.legend({
data: [
{type: 'Lines', text: 'Input', class: 'series-index-0'},
{type: 'Bars', text: 'Output 1', class: 'series-index-0'},
{type: 'Bars', text: 'Output 2', class: 'series-index-1'},
]
})
};
charts
{Array}
Array of chart keys from container to display in legend
d3.select('#chart')
.chart('Compose', function(data) {
var charts = [
{id: 'a'},
{id: 'b'},
{id: 'c'}
];
var legend = d3c.legend({charts: ['a', 'c']});
return [
[d3c.layered(charts), legend]
];
});
swatchDimensions
{Object} [{width: 20, height: 20}]
Dimensions of “swatch” in px
margins
{Object} [{top: 8, right: 8, bottom: 8, left: 8}]
Margins (in pixels) around legend
stackDirection
{String} [(based on position)]
Direction to “stack” legend, “vertical” or “horizontal”. (Default is set based on position: top/bottom = “horizontal”, left/right = “vertical”)
registerSwatch
registerSwatch(types, create)
Static
-
types(Array|String)Chart type(s)
-
create(Function)“di” function that inserts swatch
Register a swatch create function for the given chart type
d3.chart('Legend').registerSwatch(['Lines'], function(chart, d, i) {
var dimensions = chart.swatchDimensions();
return this.append('line')
.attr('x1', 0).attr('y1', dimensions.height / 2)
.attr('x2', dimensions.width).attr('y2', dimensions.height / 2)
.attr('class', 'chart-line');
});
Inherits
InsetLegend
Legend positioned within chart bounds.
translation
{Object {x,y}} [{x: 10, y: 10, relative_to: 'left-top'}]
Position legend within chart layer {x, y, relative_to}
Use relative_to to use x,y values relative to x-y origin
(e.g. "left-top" is default)
d3.select('#chart')
.chart('Compose', function(data) {
return {
components: {
legend: {
type: 'InsetLegend',
// Position legend 10px away from right-bottom corner of chart
translation: {x: 10, y: 10, relative_to: 'right-bottom'}
}
}
}
});
Inherits
Text
Add text to a chart.
d3.select('#chart')
.chart('Compose', function(data) {
return {
components: {
title: {
type: 'Text',
position: 'top'
text: 'Main Title',
textAlign: 'left',
'class': 'title'
},
notes: {
type: 'Text',
position: 'bottom',
text: 'Notes',
'class': 'notes'
}
}
};
});
text
{String}
Text to display
rotation
{Number} [0]
Rotation of text
textAlign
{String} ["center"]
Horizontal text-alignment of text ("left", "center", or "right")
anchor
{String} [(set by `textAlign`)]
text-anchor for text ("start", "middle", or "end")
verticalAlign
{String} ["middle"]
Vertical aligment for text ("top", "middle", "bottom")
style
{Object} [{}]
Style object containing styles for text
Inherits
Title
Title component that extends Text with defaults (styling, sensible margins, and rotated when positioned left or right)
margins
{Object} [(set based on `position`)]
Margins (in pixels) around Title
rotation
{Number} [(set based on `position`)]
Rotation of title. (Default is -90 for position = "right", 90 for position = "left", and 0 otherwise).
Inherits
AxisTitle
Axis title component that extends Title with defaults (styling)
margins
{Object} [(set based on `position`)]
Margins (in pixels) around axis title
Inherits
Gridlines
Gridlines component that draws major ticks for chart
Uses d3.axis extensions for ticks:
tickstickValues
Extending
To extend the Gridlines component, the following methods are available
onInsertonEnteronEnterTransitiononUpdateonUpdateTransitiononMergeonMergeTransitiononExitonExitTransition
d3.select('#chart').chart('Compose', function(data) {
var scales = {
x: {data: data, key: 'x'},
y: {data: data, key: 'y'}
};
var vertical = d3c.gridlines({
scale: scales.x,
orientation: 'vertical'
});
var horizontal = d3c.gridlines({
scale: scales.y,
orientation: 'horizontal'
});
return [
vertical,
horizontal
];
});
orientation
{String} [horizontal]
Use horizontal, vertical gridlines
scale
{Object|d3.scale}
Scale to use for gridlines.
Can be d3.scale or, if Object is given, helpers.createScale is used.
// Set with d3.scale directly
gridlines.scale(d3.scale());
// or with Object passed to helpers.createScale
gridlines.scale({data: data, key: 'x'});
helpers
d3.compose.helpers includes general purpose helpers that are used throughout d3.compose.
Includes convenience functions for create charts/components (property, di, and mixin),
helpful calculations (dimensions, max, and min) and other common behavior.
createScale
createScale(options)
-
options(Object|Function)(passing in
Functionreturns original function with no changes)-
[type = 'linear'](String)Any available
d3.scale("linear","ordinal","log", etc.) or"time" -
[domain](Array)Domain for scale
-
[range](Array)Range for scale
-
[data](Any)Used to dynamically set domain (with given value or key)
-
[value](Function)“di”-function for getting value for data
-
[key](String)Data key to extract value
-
[centered](Boolean)For “ordinal” scales, use centered x-values
-
[adjacent](Boolean)For “ordinal” + centered, set x-values for different series next to each other
- Requires series-index as second argument to scale, otherwise centered x-value is used
- Requires “data” or “series” options to determine number of series
-
[series](Number)Used with “adjacent” if no “data” is given to set series count
-
[padding = 0.1](Number)For “ordinal” scales, set padding between different x-values
-
[...](Array...)Set any other scale properties with array of arguments to pass to property
-
Create scale from options
// Simple type, range, and domain
var scale = createScale({
type: 'linear',
domain: [0, 100],
range: [0, 500]
});
// Calculate domain for data
var scale = createScale({
type: 'log',
data: [{y: 1}, {y: 100}, {y: 2000}, {y: 5000}],
key: 'y'
});
// Scale is passed through
var original = d3.scale.linear();
var scale = createScale(original);
scale === original;
// Set other properties by passing in "arguments" array
var scale = createScale({
type: 'ordinal',
domain: ['a', 'b', 'c', 'd', 'e'],
rangeRoundBands: [[0, 100], 0.1, 0.05] // -> rangeRoundBands([0, 100], 0.1, 0.05)
});
// Use ordinal + adjacent for bar charts
var scale = createScale({
type: 'ordinal',
adjacent: true,
domain: ['a', 'b', 'c'],
series: 2 // Series count is required for adjacent if data isn't given
})
dimensions
dimensions(selection)
-
selection(d3.Selection)
{width, height}
Helper for robustly determining width/height of given selector. Checks dimensions from css, attributes, and bounding box.
mixin
mixin(Parent, mixins)
-
Parent(Function) -
mixins(...Object)
Combine mixins with Parent super class for extension
var a = {transform: function() {}, a: 1};
var b = {initialize: function() {}, b: 2};
var c = {c: 3};
var Custom = mixin(Chart, a, b, c).extend({
initialize: function(options) {
this._super.initialize.call(this, options);
// d
},
transform: function(data) {
data = this._super.transform.call(this, data);
// d
}
});
// initialize: Chart, b, d
// transform: Chart, a, d
property
property([options])
-
[options](Object)-
[default_value](Any)default value for property (when set value is
undefined). If default value is a function, wrap in another function as default_value is evaluated by default. -
[get](Function)function(value) {return ...}getter, wherevalueis the stored value and return desired value -
[set](Function)function(value, previous) {return {override, after}}. Returnoverrideto override stored value andafter()to run after set -
[context = this](Object)context to evaluate get/set/after functions
-
(): get, (value): set
Helper for creating properties for charts/components
var Custom = d3.chart('Chart').extend('Custom', {
// Create property that's stored internally as 'simple'
simple: property()
});
var custom; // = new Custom(...);
// set
custom.simple('Howdy');
// get
console.log(custom.simple()); // -> 'Howdy'
// Advanced
// --------
// Default values:
Custom.prototype.message = property({
default_value: 'Howdy!'
});
console.log(custom.message()); // -> 'Howdy!'
custom.message('Goodbye');
console.log(custom.message()); // -> 'Goodbye'
// Set to undefined to reset to default value
custom.message(undefined);
console.log(custom.message()); // -> 'Howdy!'
// Computed default value:
Custom.property.computed = property({
default_value: function() {
// "this" = Custom instance
return this.message();
}
});
// Function default value:
// For function default_values, wrap in function to differentiate from computed
Custom.property.fn = property({
default_value: function() {
return function defaultFn() {};
}
// The following would be incorrectly evaluated
// default_value: function defaultFn() {}
})
// Custom getter:
Custom.prototype.exclaimed = property({
get: function(value) {
// Value is the underlying set value
return value + '!';
}
});
custom.exclaimed('Howdy');
console.log(custom.exclaimed()); // -> 'Howdy!'
// Custom setter:
Custom.prototype.feeling = property({
set: function(value, previous) {
if (value == 'Hate') {
// To override value, return Object with override specified
return {
override: 'Love',
// To do something after override, use after callback
after: function() {
console.log('After: ' + this.feeling()); // -> 'After: Love'
}
};
}
}
custom.feeling('Hate'); // -> 'After: Love'
console.log(custom.feeling()); // -> 'Love'
});
stack
stack([options])
-
[options](Object)-
[direction = vertical](String)"vertical"or"horizontal" -
[origin](String)"top","right","bottom", or"left"(by default,"top"for"vertical"and"left"for"horizontal") -
[padding = 0](Number)padding (in px) between elements
-
[min_height = 0](Number)minimum spacing height (for vertical stacking)
-
[min_width = 0](Number)minimum spacing width (for horizontal stacking)
-
Stack given array of elements vertically or horizontally
// Stack all text elements vertically, from the top, with 0px padding
d3.selectAll('text').call(helpers.stack())
// Stack all text elements horizontally, from the right, with 5px padding
d3.selectAll('text').call(helpers.stack({
direction: 'horizontal',
origin: 'right',
padding: 5
}));
translate
translate([x], [y])
-
[x](Number|Object)value or
{x, y} -
[y](Number)
Translate by (x, y) distance
translate(10, 15) == 'translate(10, 15)'
translate({x: 10, y: 15}) == 'translate(10, 15)'
rotate
rotate(degrees, [center = {x: 0, y: 0}])
-
degrees(Number) -
[center = {x: 0, y: 0}](Object)
Rotate by degrees, with optional center
alignText
alignText(element, [line_height])
-
element(Element) -
[line_height](Number)
offset
Find vertical offset to vertically align text
(needed due to lack of alignment-baseline support in Firefox)
var label = d3.select('text');
// Place label vertically so that origin is top-left
var offset = alignText(label);
label.attr('transform', translate(0, offset));
// Center label for line-height of 20px
var offset = alignText(label, 20);
label.attr('transform', translate(0, offset));
isSeriesData
isSeriesData(data)
-
data(Array)
Determine if given data is likely series data
max
max(data, getValue)
-
data(Array) -
getValue(Function)di function that returns value for given (d, i)
Get max for array/series by value di
var data = [
{values: [{y: 1}, {y: 2}, {y: 3}]},
{values: [{y: 4}, {y: 2}, {y: 0}]}
];
max(data, function(d, i) { return d.y; }); // -> 4
min
min(data, getValue)
-
data(Array) -
getValue(Function)di function that returns value for given (d, i)
Get min for array/series by value di
var data = [
{values: [{x: 1}, {x: 2}, {x: 3}]},
{values: [{x: 4}, {x: 2}, {x: 0}]}
];
min(data, function(d, i) { return d.x; }); // -> 0
getMargins
getMargins(margins, default_margins)
-
margins(Number|Object) -
default_margins(Object)
Get formatted margins for varying input
getMargins(4);
// -> {top: 4, right: 4, bottom: 4, left: 4}
getMargins({top: 20}, {top: 8, bottom: 8});
// -> {top: 20, right: 0, bottom: 8, left: 0}
di
di(callback)
-
callback(Function)with
(chart, d, i)arguments
Create wrapped (d, i) function that adds chart instance as first argument.
Wrapped function uses standard d3 arguments and context.
Note: in order to pass proper context to di-functions called within di-function
use .call(this, d, i) (where “this” is d3 context)
d3.chart('Base').extend('Custom', {
initialize: function() {
this.base.select('point')
.attr('cx', this.x);
// -> (d, i) and "this" used from d3, "chart" injected automatically
},
x: di(function(chart, d, i) {
// "this" is standard d3 context: node
return chart.xScale()(chart.xValue.call(this, d, i));
})
// xScale, xValue...
});
getParentData
getParentData(element)
-
element(Element)
Get parent data for element (used to get parent series for data point)
var data = [{
name: 'Input',
values: [1, 2, 3]
}];
d3.selectAll('g')
.data(data)
.enter().append('g')
.selectAll('text')
.data(function(d) { return d.values; })
.enter().append('text')
.text(function(d) {
var series_data = getParentData(this);
return series_data.name + ': ' + d;
});
// Input: 1, Input: 2, Input: 3
mixins
mixins.Series
Mixin for handling series data
seriesKey
seriesKey(d)
-
d(Any)Series object with
key
Get key for given series data
seriesValues
seriesValues(d)
-
d(Any)Series object with
valuesarray
Get values for given series data
seriesClass
seriesClass(d, i)
-
d(Any) -
i(Number)
Get class for given series data
seriesIndex
seriesIndex(d, i)
-
d(Any) -
i(Number)
Get index for given data-point of series
seriesData
Get parent series data for given data-point
itemStyle
itemStyle(d, [i], [j])
-
d(Any) -
[i](Number) -
[j](Number)
(di) Get style given series data or data-point
(Uses “style” object on d, if defined)
seriesCount
Get series count for chart
seriesLayer
seriesLayer(name, selection, options)
-
name(String) -
selection(Selection) -
options(Object)(
dataBindandinsertrequired)
Extension of layer() that handles data-binding and layering for series data.
- Updates
dataBindmethod to access underlying series values - Creates group layer for each series in chart
- Should be used just like layer()
d3.chart('Chart').extend('Custom', helpers.mixin(mixins.Series, {
initialize: function() {
this.seriesLayer('Circles', this.base, {
// Create group for each series on this.base
// and calls the following for each series item
// (entire layer is called twice: series-1 and series-2)
dataBind: function(data) {
// 1. data = [1, 2, 3]
// 2. data = [4, 5, 6]
},
insert: function() {
// Same as chart.layer
// (where "this" is series group layer)
},
events: {
// Same as chart.layer
}
});
}
}));
// ...
chart.draw([
{key: 'series-1', values: [1, 2, 3]},
{key: 'series-2', values: [4, 5, 6]}
]);
mixins.XY
Mixin for handling XY data
xScale
{Object|d3.scale}
Get/set x-scale with d3.scale or with object (uses helpers.createScale)
xScale
{Object|d3.scale}
Get/set yscale with d3.scale or with object (uses helpers.createScale)
xKey
{String} ['x']
Key on data object for x-value
yKey
{String} ['y']
Key on data object for y-value
x
x(d, i)
-
d(Any) -
i(Number)
Get scaled x-value for given data-point
y
y(d, i)
-
d(Any) -
i(Number)
Get scaled y-value for given data-point
key
key(d, i)
-
d(Any) -
i(Number)
Get key for data-point. Looks for “key” on d first, otherwise uses x-value.
x0
Get scaled x = 0 value
x0
Get scaled y = 0 value
xValue
xValue(d)
-
d(Any)
Get x-value for data-point. Checks for xKey() on d first, otherwise uses d[0].
xValue({x: 10, y: 20}); // -> 10
xValue([10, 20]); // -> 10
yValue
yValue(d)
-
d(Any)
Get y-value for data-point. Checks for yKey() on d first, otherwise uses d[1].
yValue({x: 10, y: 20}); // -> 20
yValue([10, 20]); // -> 20
setScales
Set x- and y-scale ranges (using setXScaleRange and setYScaleRange)
setXScaleRange
setXScaleRange(x_scale)
-
x_scale(d3.scale)
Set range (0, width) for given x-scale
setYScaleRange
setYScaleRange(y_scale)
-
y_scale(d3.scale)
Set range(height, 0) for given y-scale
getDefaultXScale
Get default x-scale: {data: this.data(), key: 'x'}
getDefaultYScale
Get default y-scale: {data: this.data(), key: 'y'}
mixins.XYValues
Mixin for charts of centered key,value data (x: index, y: value, key)
adjacentWidth
Determine width of data-point when displayed adjacent
layeredWidth
Determine layered width (width of group for adjacent)
itemWidth
Determine item width based on series display type (adjacent or layered)
getDefaultYScale
Override default x-scale to use ordinal type: {type: 'ordinal', data: this.data(), key: 'y', centered: true}
mixins.XYInverted
Mixin for inverting XY calculations with x vertical, increasing bottom-to-top and y horizontal, increasing left-to-right
x
x(d, i)
-
d(Any) -
i(Number)
Get x-value for plotting (scaled y-value)
y
y(d, i)
-
d(Any) -
i(Number)
Get y-value for plotting (scaled x-value)
x0
Get scaled y = 0 value (along x-axis)
x0
Get scaled x = 0 value (along y-axis)
setXScaleRange
setXScaleRange(x_scale)
-
x_scale(d3.scale)
Set range (height, 0) for given x-scale
setYScaleRange
setYScaleRange(y_scale)
-
y_scale(d3.scale)
Set range (0, width) for given y-scale
mixins.Labels
Mixin for handling labels in charts
attachLabels
Call during chart initialization to add labels to chart
d3.chart('Chart').extend('Custom', helpers.mixin(Labels, {
initialize: function() {
// this.layer()...
// Attach labels layer
this.attachLabels();
}
}));
labels
{Object}
Options passed to labels chart
d3.chart('Chart').extend('Custom', helpers.mixin(Labels, {
// ...
}));
// ...
chart.labels(true); // -> display labels with defaults
chart.labels(false); // -> hide labels
chart.labels({offset: 10}); // -> pass options to labels chart
d3.select('#chart')
.chart('Compose', function(data) {
return {
charts: {
custom: {labels: {offset: 10}}
}
};
});
mixins.LabelsXY
Mixin for handling labels in XY charts
(proxies x and y to properly place labels for XY charts)
Inherits
mixins.Hover
Mixin for handling common hover behavior that adds standard onMouseEnter, onMouseMove, and onMouseLeave handlers
and getPoint helper for adding helpful meta information to raw data point.
getPoint
getPoint(d, i, j)
-
d(Any) -
i(Number) -
j(Number)
}
Get point information for given data-point
mouseEnterPoint
mouseEnterPoint(d, i, j)
-
d(Any) -
i(Number) -
j(Number)
Call to trigger mouseenter:point when mouse enters data-point
d3.chart('Chart').extend('Bars', helpers.mixin(Hover, {
initialize: function() {
this.layer('bars', this.base, {
// dataBind...
insert: function() {
// Want to trigger enter/leave point
// when mouse enter/leaves bar (rect)
var chart = this.chart();
return this.append('rect')
.on('mouseenter', chart.mouseEnterPoint)
.on('mouseleave', chart.mouseLeavePoint);
}
// events...
})
}
}));
mouseleavePoint
mouseleavePoint(d, i, j)
-
d(Any) -
i(Number) -
j(Number)
Call to trigger mouseleave:point when mouse leaves data-point
d3.chart('Chart').extend('Bars', helpers.mixin(Hover, {
initialize: function() {
this.layer('bars', this.base, {
// dataBind...
insert: function() {
// Want to trigger enter/leave point
// when mouse enter/leaves bar (rect)
var chart = this.chart();
return this.append('rect')
.on('mouseenter', chart.mouseEnterPoint)
.on('mouseleave', chart.mouseLeavePoint);
}
// events...
})
}
}));
onMouseEnter
onMouseEnter(position)
-
position(Object)(chart and container {x,y} position of mouse)
-
chart(Object){x, y} position relative to chart origin
-
container(Object){x, y} position relative to container origin
-
(Override) Called when mouse enters container
onMouseMove
onMouseMove(position)
-
position(Object)(chart and container {x,y} position of mouse)
-
chart(Object){x, y} position relative to chart origin
-
container(Object){x, y} position relative to container origin
-
(Override) Called when mouse moves within container
onMouseLeave
(Override) Called when mouse leaves container
mixins.HoverPoints
Mixin for automatically triggering “mouseenter:point”/”mouseleave:point” for chart data points that are within given hoverTolerance.
hoverTolerance
{Number} [Infinity]
Hover tolerance (in px) for calculating close points
mixins.Transition
Mixin for handling common transition behaviors
delay
{Number|Function} [(use container value, if available)]
Delay start of transition by specified milliseconds.
duration
{Number|Function} [(use container value, if available)]
Transition duration in milliseconds.
ease
{String|Function} [(use container value, if available)]
Transition ease function
- See: Transitions#ease
- Note: arguments to pass to
d3.easeare not supported
setupTransition
setupTransition(selection)
-
selection(d3.selection)
Setup delay, duration, and ease for transition
d3.chart('Chart').extend('Custom', helpers.mixin(Transition, {
initialize: function() {
this.layer('circles', this.base, {
// ...
events: {
'merge:transition': function() {
// Set delay, duration, and ease from properties
this.chart().setupTransition(this);
}
}
});
}
}));
mixins.StandardLayer
Mixin to create standard layer to make extending charts straightforward.
d3.chart('Chart').extend('Custom', helpers.mixin(StandardLayer, {
initialize: function() {
this.standardLayer('main', this.base.append('g'))
// dataBind, insert, events are defined on prototype
},
onDataBind: function(selection, data) {
// ...
},
onInsert: function(selection) {
// ...
},
onEnter: function(selection) {
// ...
},
onUpdateTransition: function(selection) {
// ...
},
// all d3.chart events are available: onMerge, onExit, ...
}));
standardLayer
standardLayer(name, selection)
-
name(String) -
selection(d3.selection)
extension of layer() that uses standard methods on prototype for extensibility.
d3.chart('Chart').extend('Custom', helpers.mixin(StandardLayer, {
initialize: function() {
this.standardLayer('circles', this.base.append('g'));
}
// onDataBind, onInsert, etc. work with "circles" layer
}));
standardSeriesLayer
standardSeriesLayer(name, selection)
-
name(String) -
selection(d3.selection)
extension of seriesLayer() that uses standard methods on prototype for extensibility.
d3.chart('Chart').extend('Custom', helpers.mixin(StandardLayer, {
initialize: function() {
this.standardSeriesLayer('circles', this.base.append('g'));
},
// onDataBind, onInsert, etc. work with "circles" seriesLayer
}));
onDataBind
onDataBind(selection, data)
-
selection(d3.selection) -
data(Any)
Called for standard layer’s dataBind
onInsert
onInsert(selection)
-
selection(d3.selection)
Called for standard layer’s insert
onEnter
onEnter(selection)
-
selection(d3.selection)
Call for standard layer’s events['enter']
onEnterTransition
onEnterTransition(selection)
-
selection(d3.selection)
Call for standard layer’s events['enter:transition']
onUpdate
onUpdate(selection)
-
selection(d3.selection)
Call for standard layer’s events['update']
onUpdateTransition
onUpdateTransition(selection)
-
selection(d3.selection)
Call for standard layer’s events['update']
onMerge
onMerge(selection)
-
selection(d3.selection)
Call for standard layer’s events['merge']
onMergeTransition
onMergeTransition(selection)
-
selection(d3.selection)
Call for standard layer’s events['merge:transition']
onExit
onExit(selection)
-
selection(d3.selection)
Call for standard layer’s events['exit']
onExitTransition
onExitTransition(selection)
-
selection(d3.selection)
Call for standard layer’s events['exit:transition']
Change Log
0.15.0
- Add Gridlines component
- Update and simplify
property - Update label positioning
- Update class system to remove implicit initialize and transform cascade
- Remove deprecated xy extension and Object-style layout
- 0.15.1 Fix Overlay.position and labels with stacked bars bugs
- 0.15.2 Fix Gridlines onExit bug and slide down Bars on exit
- 0.15.3 Fix layout calculation and axis transition bugs
- 0.15.4 Fix component centering and mousemove in IE bugs
- 0.15.5 Fix
__proto__and Overlay transform issues - 0.15.6 Fix Legend type not updating bug
- 0.15.7 Add banner to dist css and tweak label defaults
- 0.15.8 Fix margins issue for centered components
- 0.15.9 Fix delayed gridlines attachment bug and fix undefined config issue
- 0.15.10 Fix legend expanding on redraw issue
- 0.15.11 Update build (remove grunt), start new draw architecture, convert tests to mocha + jsdom
- 0.15.12 Include license/version in minified dist
- 0.15.13 Improve backwards compatibility with new architecture
0.14.0
- New layout system
- Remove dependency on Underscore
- Only set
chartsandcomponentsfrom options function - Convert
Compose.chartsandCompose.componentsto arrays - Add helpers for charts and components
- Update mouse listening
- Update overlay positioning
- New module system
- 0.14.1 Fix undefined
XYInvertedbug - 0.14.2 Expose d3c global
- 0.14.3 Add
TextandAxisTitlecomponents, update default margins, addcenteredcomponent property - 0.14.4 Set transitions on
Compose, updatestack, and add hover listeners toLegend - 0.14.5 Disable x0 and y0 position for axes until tested more thoroughly, refactor layout
- 0.14.6 Fix vertical Axis bug, absolute positioning Compose bug, unknown position bug, and property.previous bug
- 0.14.7 (Temporarily) Re-instantiate component on position change to avoid nasty side effects
0.13.0
- Add responsive support for
divselections, withwidthandheightused forviewBox - Add
StandardLayerto improve extensibility in standard charts and components - Add
Transitionmixin for standard approach to addingduration,delay, andeaseto charts/layers - 0.13.1 Add
Overlaycomponent and layer type - 0.13.2 Add
xKeyandyKeytomixins.XYand lots of docs updates - 0.13.3 For
xy, add given components after generated - 0.13.4 Clear unset options on set and fix axis layout issue for inverted
- 0.13.5 Fix bars axis offset issue
- 0.13.6 Additional bar offset tweaks
0.12.0
- Add
centeredandadjacentoptions forcreateScale - Move x calculations for Values to
createScale - Remove
LineValuesandAxisValuesto use scale instead - Rename
LinetoLines - Bugfixes in
Legend,Axis, andMulti - Refactor
Lineschart - Add hover to labels
- Move hover points into separate
HoverPointsmixin - Refactor mouse events
- 0.12.1 Complete rename to d3.compose
- 0.12.2 Move xy to d3.compose namespace
- 0.12.3 Fix version number
- 0.12.4 Fix data handling bug in Compose.draw, fix examples, and move
stackback to helpers - 0.12.5 Handle
line-height: normalinalignText - 0.12.6 Split merge and layout in labels for better override
- 0.12.7 Fix cached config bug
- 0.12.8 Fix improperly updating series class/style bug
- 0.12.9 Fix Lines indexing bug and set class/style for charts on merge instead of insert
- 0.12.10 Allow 0/null for transition (delay, duration, ease) values
- 0.12.11 Use utils instead of underscore
- 0.12.12 Fix label collisions bug
- 0.12.13 Add
HorizontalBarandHorizontalStackedBars; makeBars,Lines, andTitlemore extensible; fixundefineddata bug; namespace charts - 0.12.14 Add horizontal bars swatch to legend
0.11.0
- New legend data format
- Fix creating legend from
xyextension - New
registerSwatchmethod (move method out of charts) - More options
- Split build into three parts: core, core + mixins, core + mixins + charts/components
- Simplify and cleanup
- 0.11.1 Labels extension bugfix and Bars offset bugfix
0.10.0
- Simplify charts and components
- Simplify draw/redraw
- Simplify attach/detach
- Simplify properties
- Merge
ContainerandMulti - Move axes, title, and legend out of Multi and into
xyextension - Move z-index out of helpers
- Isolate underscore in utils