From 6b2aab339155a812b9ca0773e04bdcf0f5cea2a7 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Sat, 7 May 2022 01:58:26 +0100 Subject: [PATCH] Add helper class to manage a set of stacked bars It also automatically generates and manages the legend. --- src/static/js/ui/charts/ChartStackedBar.mjs | 16 ++++--- .../js/ui/charts/ChartStackedBarSet.mjs | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 src/static/js/ui/charts/ChartStackedBarSet.mjs diff --git a/src/static/js/ui/charts/ChartStackedBar.mjs b/src/static/js/ui/charts/ChartStackedBar.mjs index 75422df..40d98b7 100644 --- a/src/static/js/ui/charts/ChartStackedBar.mjs +++ b/src/static/js/ui/charts/ChartStackedBar.mjs @@ -15,28 +15,30 @@ class ChartStackedBar { * @type {Object[]} */ 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.#svg = SVG().size("100%", "100%"); + this.svg = SVG().size("100%", "100%"); this.sym_bar = Symbol(); - } /** * Adds a new category to the bar. - * @param {string} colour The colour to assign to the category. - * @param {number} weight The weighting value to assign to the category. + * @param {string} name A display name for the category. + * @param {string} colour The colour 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(); this.categories.push({ id, + name, colour, weight, [this.sym_bar]: null @@ -61,7 +63,7 @@ class ChartStackedBar { for(const cat of this.categories) { const percent_this = this.#weight_percent(cat.weight); 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) } cat[this.sym_bar].height = `${percent_this}%`; diff --git a/src/static/js/ui/charts/ChartStackedBarSet.mjs b/src/static/js/ui/charts/ChartStackedBarSet.mjs new file mode 100644 index 0000000..d7a386b --- /dev/null +++ b/src/static/js/ui/charts/ChartStackedBarSet.mjs @@ -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;