Skip to content

Instantly share code, notes, and snippets.

@junkycoder
Last active August 29, 2015 14:26
Show Gist options
  • Save junkycoder/8a04cb49a892349dd73a to your computer and use it in GitHub Desktop.
Save junkycoder/8a04cb49a892349dd73a to your computer and use it in GitHub Desktop.
Reusable D3 chart
import d3 from 'd3';
import {responsivefy} from './lib';
export default function Chart(node, options, initialData) {
const [width, height] = [
options.width - options.margin.left - options.margin.right,
options.height - options.margin.top - options.margin.bottom
];
function chart(data) {
d3.select(node).selectAll('svg').data([data]).call(update)
.enter().call(create);
}
function create(selection) {
const svg = selection.append('svg')
.attr({width: options.width, height: options.height})
.call(responsivefy)
.append('g').attr(
'transform',
`translate(${options.margin.left}, ${options.margin.top})`
);
svg.append('text').attr({class: 'test', x: 0, y: 12, fill: '#fff'});
svg.call(update);
}
function update(selection) {
selection.each(function(data) {
d3.select(this).select('.test').text(data);
});
}
chart.remove = function() {
d3.select('svg', node).remove();
};
chart(initialData);
return chart;
}
import d3 from 'd3';
import {offset, remove, responsivefy} from './lib';
function LineChart(selection, options, data) {
function chart() {
}
chart.name = options.name;
}
function BarChart(selection, options, data) {
function chart() {
}
chart.name = options.name;
}
function Axes(selection, options, data) {
}
function Overlay(selection, options, data) {
}
export default function Chart(node, options, initialData) {
const [width, height] = [
options.width - options.margin.left - options.margin.right,
options.height - options.margin.top - options.margin.bottom
];
const selection = d3.select('svg', node).call(remove).append('svg')
.attr({width: options.width, height: options.heigh})
.call(responsivefy)
::offset([options.margin.left, options.margin.right]);
const [holder, _axes, _overlay] = [
selection.append('g'),
Axes(selection, {width, height}),
Overlay(selection, {width, height, ...options.overlay})
];
const scale = {
x: d3.time.scale().range([0, width]),
y: d3.scale.linear().rande([height, 0])
};
function processData(raw) {
const [minTime, maxTime, interval] = raw.period;
const dataset = data.sets;
return {
minTime, maxTime, interval, dataset,
maxValue: dataset ? d3.max(dataset, (x) => d3.max(x.data)) : void 0
};
}
let _chart = undefined;
function chart(raw) {
const data = processData(raw),
const type = data.interval <= 90000 ? 'lines' : 'bars';
scale.x.domain([data.minTime, data.maxTime]);
scale.y.domain([0, data.maxValue]);
if(_chart && _chart.name !== type) {
_chart.remove();
}
if(!_chart) {
_chart = type === 'lines' ?
LineChart(selection, {width, height, scale, name: 'lines'}) :
BarChart(selection, {width, height, scale, name: 'bars'});
}
_chart(data);
_axes(data);
}
chart(initialData);
return chart;
}
import Chart from './chart.d3';
const margin = {top: 12, right: 12, bottom: 24, left: 72};
const chart = Chart(
document.getElementById('example'),
{width: 1140, height: 310, margin},
'Hello world!'
);
setTimeout(() => {
chart('New data');
}, 1000)
// Remove chart
// chart.remove();
// Source http://www.brendansudol.com/posts/responsive-d3/
import d3 from 'd3';
export function responsivefy(svg) {
// avoid error due to empty selection
// for example: selection.enter().call(responsivefy)
if(svg.empty()) {
return;
}
// get container + svg aspect ratio
var container = d3.select(svg.node().parentNode),
width = parseInt(svg.style("width")),
height = parseInt(svg.style("height")),
aspect = width / height;
// add viewBox and preserveAspectRatio properties,
// and call resize so that svg resizes on inital page load
svg.attr("viewBox", "0 0 " + width + " " + height)
.attr("perserveAspectRatio", "xMinYMid")
.call(resize);
// to register multiple listeners for same event type,
// you need to add namespace, i.e., 'click.foo'
// necessary if you call invoke this function for multiple svgs
// api docs: https://github.com/mbostock/d3/wiki/Selections#on
d3.select(window).on("resize." + container.attr("id"), resize);
// get width of container and resize svg to fit it
function resize() {
var targetWidth = parseInt(container.style("width"));
svg.attr("width", targetWidth);
svg.attr("height", Math.round(targetWidth / aspect));
}
}
export function remove(selections, transition) {
if(transition) {
selection = selection.transition().style('opacity', 0)
}
selection.remove();
}
export function offset(coordinates) {
this.append('g').attr({
class: 'offset',
transform: `translate(${options.margin.left}, ${options.margin.top})`
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment