atlas-guide
Version:
Atlas is living style-guides & pattern library static site generator with extensive CSS monitoring and components info that could be used virtually with any scss/css project
180 lines (148 loc) • 5.83 kB
JavaScript
;
(function() {
function StackedChart(rawNode) {
this.instance = rawNode;
this.chartData = undefined;
this.getData();
this.initChart();
}
StackedChart.prototype.getData = function() {
const data = JSON.parse(this.instance.querySelector('defs').getAttribute('data-chart'));
data.sort((a, b) => b.zipped - a.zipped);
this.chartData = data;
};
StackedChart.prototype.initChart = function() {
const d3 = window.d3;
const margin = {top: 20, right: 0, bottom: 150, left: 30};
const width = (document.querySelector('.atlas-stat-sheet').offsetWidth) - margin.right - margin.left;
const height = 400;
const formatBites = d3.format('.2s');
const svg = d3.select(this.instance);
const data = this.chartData;
if (!data) {
return;
}
const max = d3.max(data.map(item => item.raw));
const mean = d3.mean(data.map(item => item.zipped));
const x = d3.scaleBand()
.domain(data.map(item => item.name))
.rangeRound([0, (data.length * 55) > width ? width : (data.length * 55)])
.paddingInner(0.05)
.align(0.05);
const y = d3.scaleLinear()
.domain([0, max])
.range([height, 0])
.nice();
const z = d3.scaleOrdinal() // fillScale
.range(['#307fe2', '#a7c6ed']);
// Generators
const sharedStack = d3.stack().keys(['zipped', 'raw'])
.value((d, key) => key === 'raw' ? (d.raw - d.zipped) : d.zipped);
// DOM
svg.attr('viewBox', '0 0 ' +
(width + margin.left + margin.right) + ' ' +
(height + margin.top + margin.bottom));
const inner = svg.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Bars
const stackedItems = inner.append('g')
.attr('class', 'inner')
.selectAll('g')
.data(sharedStack(data))
.enter().append('g')
.attr('fill', d => z(d.key));
stackedItems.selectAll('rect')
.data(d => d)
.enter()
.append('rect')
.attr('x', d => x(d.data.name))
.attr('y', d => y(d[1]))
.attr('height', d => {
const height = y(d[0]) - y(d[1]);
return height < 0 ? 0 : height;
})
.attr('width', x.bandwidth());
//Ticks
inner.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + height + ')')
.call(d3.axisBottom(x))
.selectAll('.tick').select('text')
.attr('text-anchor', 'start')
.attr('dx', 10)
.attr('transform', 'rotate(45)');
inner.append('g')
.attr('class', 'axis')
.call(d3.axisLeft(y)
.ticks(4)
.tickValues([0, 50000, 100000, mean, max])
.tickFormat(formatBites)
.tickSize(-width, 0, 0)
);
inner.selectAll('.axis')
.select('.domain')
.remove();
inner.selectAll('g.tick')
.filter(d => d === mean)
.attr('class', 'tick mean');
// Legend
const legend = inner.append('g')
.attr('class', 'atlas-stat-chart__legend')
.attr('text-anchor', 'end')
.attr('transform', 'translate(0,20)')
.selectAll('g')
.data(['zipped', 'raw'].slice().reverse()) //keys.slice
.enter().append('g')
.attr('transform', (d, i) => 'translate(0,' + i * 22 + ')');
legend.append('rect')
.attr('x', width - 19)
.attr('width', 19)
.attr('height', 19)
.attr('fill', z);
legend.append('text')
.attr('x', width - 24)
.attr('y', 9.5)
.attr('dy', '0.32em')
.text(d => d);
// Tooltip
const focus = inner.append('g')
.attr('class', 'focus');
focus.append('rect')
.attr('width', 1)
.attr('height', height)
.attr('style', 'opacity: 0.1')
.attr('fill', 'black');
focus.append('text')
.attr('y', -10);
const focusInner = focus.append('g')
.attr('class', 'focus-line');
focusInner.append('circle')
.attr('r', 3)
.attr('fill', 'black');
svg.on('mousemove', function () {
const mouseX = d3.mouse(this)[0] - margin.left - margin.right;
const itemIndex = Math.round((mouseX - margin.left - margin.right) / x.bandwidth());
if (itemIndex > data.length - 1 || itemIndex < 0) {
return;
}
const itemSizeRaw = formatBites(data[itemIndex].raw);
const itemSizeZipped = formatBites(data[itemIndex].zipped);
focus.attr('transform', 'translate(' + (x(data[itemIndex].name) + (x.bandwidth() / 2)) + ',0)');
focusInner.attr('transform', 'translate(' + 0 + ',' + y(data[itemIndex].zipped) + ')');
if (mouseX > width * 0.6) {
focus.select('text')
.attr('text-anchor', 'end')
.attr('x', 8)
.text(itemSizeRaw + '/' + itemSizeZipped);
} else {
focus.select('text')
.attr('text-anchor', 'start')
.attr('x', -8)
.text(itemSizeRaw + '/' + itemSizeZipped);
}
});
};
document.querySelectorAll('.js-stacked-chart').forEach(
item => new StackedChart(item)
);
}());