Add helper class to manage a set of stacked bars

It also automatically generates and manages the legend.
This commit is contained in:
Starbeamrainbowlabs 2022-05-07 01:58:26 +01:00
parent 0f37853aba
commit 6b2aab3391
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
2 changed files with 55 additions and 7 deletions

View file

@ -15,28 +15,30 @@ class ChartStackedBar {
* @type {Object[]} * @type {Object[]}
*/ */
this.categories = [ this.categories = [
// { colour: "hsl(46, 80%, 50%)", weight: 4 } // { name: "Some category name", colour: "hsl(46, 80%, 50%)", weight: 4 }
// .... // ....
]; ];
this.auto_render = true; this.auto_render = true;
this.#svg = SVG().size("100%", "100%"); this.svg = SVG().size("100%", "100%");
this.sym_bar = Symbol(); this.sym_bar = Symbol();
} }
/** /**
* Adds a new category to the bar. * Adds a new category to the bar.
* @param {string} name A display name for the category.
* @param {string} colour The colour to assign to the category. * @param {string} colour The colour to assign to the category.
* @param {number} weight The weighting value to assign to the category. * @param {number} weight The weighting value to assign to the category.
* @returns {Symbol} A unique id for the new category.
*/ */
category_add(colour, weight) { category_add(name, colour, weight) {
const id = Symbol(); const id = Symbol();
this.categories.push({ this.categories.push({
id, id,
name,
colour, colour,
weight, weight,
[this.sym_bar]: null [this.sym_bar]: null
@ -61,7 +63,7 @@ class ChartStackedBar {
for(const cat of this.categories) { for(const cat of this.categories) {
const percent_this = this.#weight_percent(cat.weight); const percent_this = this.#weight_percent(cat.weight);
if(cat[this.sym_bar] === null) { if(cat[this.sym_bar] === null) {
cat[this.sym_bar] = this.#svg.rect("100%", "10%") cat[this.sym_bar] = this.svg.rect("100%", "10%")
.fill(cat.colour) .fill(cat.colour)
} }
cat[this.sym_bar].height = `${percent_this}%`; cat[this.sym_bar].height = `${percent_this}%`;

View file

@ -0,0 +1,46 @@
"use strict";
import Emel from 'emel';
class ChartStackedBarSet {
constructor() {
this.emel = new Emel().emel;
this.el = this.emel(`div[class="stacked-bar-set"]>div[class=legend]+div[class=bars]`);
this.bars = [];
}
add_bar(i, bar) {
this.bars.splice(i, 0, bar);
this.#update_cats();
}
#update_cats() {
const el_legend = this.el.querySelector(`.legend`);
const els_legend_items = [...el_legend.querySelectorAll(`.legend-item`)];
// map → flatten → uniq
let cats = this.bars.map(bar => bar.categories).flat()
.filter((cat, i) => cats.find(scat => cat.name === scat.name) === i);
for(const cat of cats) {
const el_legend_item = els_legend_items.find(el => el.dataset.name === cat.name);
if(typeof el_legend_item === "undefined") {
// Nope, didn't find it - create it
el_legend.appendChild(this.#make_legend_item(cat.colour, cat.name));
}
else {
// Yep, we found it - update the names
el_legend_item.querySelector(".legend-item-spot").style.background = cat.colour;
el_legend_item.querySelector(".legend-item-text").replaceChildren(document.createTextNode(cat.name));
}
}
}
#make_legend_item(colour, name) {
return this.emel(`span[class="legend-item"]>span[style="background: ${colour};" class="legend-item-spot"]+span[class="legend-item-text"]{${name}}`);
}
}
export default ChartStackedBarSet;