ChartStackedBar: implement, but make it a simple unlabelled bar.
We can put the legend elsewhere, and manage multiple bars with a CSS grid or something.
This commit is contained in:
parent
0e2f68a067
commit
0f37853aba
1 changed files with 61 additions and 19 deletions
|
@ -1,37 +1,79 @@
|
|||
"use strict";
|
||||
|
||||
import { SVG } from 'svg.js';
|
||||
|
||||
class ChartStackedBar {
|
||||
get weight_total() {
|
||||
return this.categories.reduce((cat, acc) => cat.weight + acc, 0);
|
||||
}
|
||||
|
||||
constructor(el) {
|
||||
this.el = el;
|
||||
|
||||
/**
|
||||
* The category names and their properties.
|
||||
* The categories of this bar.
|
||||
* @type {Object[]}
|
||||
*/
|
||||
this.categories = [
|
||||
// { name: "Some category name", colour: "hsl(46, 80%, 50%)" }
|
||||
// ....
|
||||
]
|
||||
/**
|
||||
* Whether to normalise all bars to be the same height or not.
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.percentage = false;
|
||||
/**
|
||||
* The bars to render.
|
||||
* Additional properties can be specified on the objects passed and
|
||||
* this class will ignore them. It's strongly recommended though that
|
||||
* any additional properties start with an underscore.
|
||||
* @type {Object[]}
|
||||
*/
|
||||
this.bars = [
|
||||
// { name: "bar name 1", values: [ { value: 3, cat: 1 }, .... ] }
|
||||
// { colour: "hsl(46, 80%, 50%)", weight: 4 }
|
||||
// ....
|
||||
];
|
||||
|
||||
this.auto_render = true;
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
category_add(colour, weight) {
|
||||
const id = Symbol();
|
||||
this.categories.push({
|
||||
id,
|
||||
colour,
|
||||
weight,
|
||||
[this.sym_bar]: null
|
||||
});
|
||||
if(this.auto_render) this.render();
|
||||
return id;
|
||||
}
|
||||
|
||||
category_remove(id) {
|
||||
for(const i in this.categories) {
|
||||
if(this.categories[i].id === id) {
|
||||
this.categories.splice(i, 1);
|
||||
if(this.auto_render) this.render();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let percent_so_far = 0;
|
||||
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%")
|
||||
.fill(cat.colour)
|
||||
}
|
||||
cat[this.sym_bar].height = `${percent_this}%`;
|
||||
cat[this.sym_bar] = cat[this.sym_bar].fill(cat.colour)
|
||||
.move("0%", `${percent_so_far}%`);
|
||||
|
||||
percent_so_far += percent_this;
|
||||
}
|
||||
}
|
||||
|
||||
#weight_percent(weight) {
|
||||
return (weight / this.weight_total) * 100
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue