Use Plot.ly for simple gauges

This commit is contained in:
Starbeamrainbowlabs 2022-05-25 02:47:06 +01:00
parent 31695e534d
commit 61c8b76719
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
6 changed files with 79 additions and 53 deletions

15
package-lock.json generated
View file

@ -27,7 +27,8 @@
"emel": "^2.5.1", "emel": "^2.5.1",
"esbuild": "^0.14.38", "esbuild": "^0.14.38",
"fork-awesome": "^1.2.0", "fork-awesome": "^1.2.0",
"nightink": "^1.0.1" "nightink": "^1.0.1",
"plotly.js-dist": "^2.12.1"
} }
}, },
"node_modules/@emmetio/abbreviation": { "node_modules/@emmetio/abbreviation": {
@ -576,6 +577,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/plotly.js-dist": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-2.12.1.tgz",
"integrity": "sha512-u3NumBhoNQw5fyr5sfK0jTIa4v1WwruAs0y5bSk055rKklFtyUgwwVZ8VEKCiWqfuPX7OPS3mE4PqvgP4mATOA==",
"dev": true
},
"node_modules/powahroot": { "node_modules/powahroot": {
"version": "1.2.6", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/powahroot/-/powahroot-1.2.6.tgz", "resolved": "https://registry.npmjs.org/powahroot/-/powahroot-1.2.6.tgz",
@ -960,6 +967,12 @@
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
"integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==" "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA=="
}, },
"plotly.js-dist": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-2.12.1.tgz",
"integrity": "sha512-u3NumBhoNQw5fyr5sfK0jTIa4v1WwruAs0y5bSk055rKklFtyUgwwVZ8VEKCiWqfuPX7OPS3mE4PqvgP4mATOA==",
"dev": true
},
"powahroot": { "powahroot": {
"version": "1.2.6", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/powahroot/-/powahroot-1.2.6.tgz", "resolved": "https://registry.npmjs.org/powahroot/-/powahroot-1.2.6.tgz",

View file

@ -37,7 +37,8 @@
"emel": "^2.5.1", "emel": "^2.5.1",
"esbuild": "^0.14.38", "esbuild": "^0.14.38",
"fork-awesome": "^1.2.0", "fork-awesome": "^1.2.0",
"nightink": "^1.0.1" "nightink": "^1.0.1",
"plotly.js-dist": "^2.12.1"
}, },
"___comment": "The browser field is used by esbuild. fs isn't a thing in the browser, ", "___comment": "The browser field is used by esbuild. fs isn't a thing in the browser, ",
"browser": { "browser": {

View file

@ -159,6 +159,9 @@ th {
.data-item > :not(:first-child) { .data-item > :not(:first-child) {
background: var(--tech-b); background: var(--tech-b);
} }
.container-gauge {
min-width: 30em;
}
.peer-name { .peer-name {
display: flex; display: flex;

View file

@ -6,7 +6,7 @@ export default {
items: [ items: [
{ {
name: "Frequency (GHz)", name: "Frequency (GHz)",
type: "gauge", type: "gauge_multi",
gauge: { min: 0, max: 6 }, gauge: { min: 0, max: 6 },
content: { content: {
"min": "frequency.min", "min": "frequency.min",

View file

@ -2,15 +2,16 @@
import Emel from 'emel'; import Emel from 'emel';
import forkawesome_emel from './forkawesome_emel.mjs'; import forkawesome_emel from './forkawesome_emel.mjs';
import ApexCharts from 'apexcharts'; // import ChartStackedBarSet from './charts/ChartStackedBarSet.mjs';
import Plotly from 'plotly.js-dist';
import AbstractUIItem from './AbstractUIItem.mjs'; import AbstractUIItem from './AbstractUIItem.mjs';
import find_in_obj from '../misc/find_in_obj.mjs'; import find_in_obj from '../misc/find_in_obj.mjs';
class UIGauge extends AbstractUIItem { class UIGauge extends AbstractUIItem {
get #chart_labels() { return this.chart_options.xaxis.categories; } #chart_data = [];
get #chart_data() { return this.chart_options.series[0].data; } #chart = null;
constructor(el, def) { constructor(el, def) {
super(el, def); super(el, def);
@ -20,68 +21,66 @@ class UIGauge extends AbstractUIItem {
placeholders: [ this.def.name ] placeholders: [ this.def.name ]
})); }));
this.chart_options = { this.el_gauge = this.el.querySelector(".container-gauge");
series: [{
name: def.name,
data: [] // TODO: Fill this with data values
}],
chart: {
type: "bar",
//height: 350,
},
plotOptions: { bar: {
horizontal: false,
} },
xaxis: {
categories: [], // TODO: Fill this with hostname / ids
},
fill: { opacity: 1 },
} }
this.chart = new ApexCharts(el.querySelector(".container-gauge"), this.chart_options); render() {
// Refuse to render an empty chart
if(this.#chart_data.length === 0) return;
const data = {
x: [], // Labels
y: [], // Data numbers
type: "bar"
};
for (const itemdef of this.#chart_data) {
let label_this = itemdef.label;
for (let i = 2; data.x.includes(label_this); i++)
label_this = `${itemdef.label} (${i})`;
data.x.push(label_this);
data.y.push(itemdef.data_item);
} }
#__update_chart() { console.log(`def ${this.def.name} plotly definition`, data);
this.chart.updateOptions(this.chart_options, true);
this.chart.render(); if(this.#chart === null) {
console.log(`def ${this.def.name} plotly NEW`);
this.#chart = Plotly.newPlot(this.el_gauge, [data]);
}
else {
console.log(`def ${this.def.name} plotly REACT`);
this.#chart = Plotly.react(this.el_gauge, [data]);
}
} }
#__insert_item(label, data_item) { #__insert_item(label, data_item) {
for(let i in this.chart_labels) {
if(this.#chart_labels[i] === label) {
this.#chart_labels[i] = label;
this.#chart_data[i] = data_item;
return;
}
}
const comparer = new Intl.Collator(navigator.language); const comparer = new Intl.Collator(navigator.language);
for(let i in this.#chart_labels) { for(let i in this.#chart_data) {
const comp_name = comparer.compare( const comp_name = comparer.compare(
label.toLowerCase(), label.toLowerCase(),
this.#chart_labels[i].toLowerCase() this.#chart_data[i].label.toLowerCase()
); );
if(comp_name < 0) { // Insert immediately before this index if(comp_name < 0) { // Insert immediately before this index
this.#chart_labels.splice(i, 0, label); this.#chart_data.splice(i, 0, {
this.#chart_data.splice(i, 0, data_item); label,
data_item
});
return; return;
} }
} }
this.#chart_labels.push(label); // Didn't manage to find a good sopt, put it on the end
this.#chart_data.push(data_item); this.#chart_data.push({ label, data_item });
} }
clear() { clear() {
super.clear(); super.clear();
this.el_gauge.replaceChildren();
this.chart_options.series[0].data.length = 0; this.chart = null;
this.chart_options.xaxis.categories.length = 0; this.#chart_data = [];
this.__update_chart();
} }
append(peer, table) { append(peer, table) {
@ -114,9 +113,8 @@ class UIGauge extends AbstractUIItem {
} }
this.#__insert_item(label, data_item); this.#__insert_item(label, data_item);
console.log(`def ${this.def.name} | chart_labels`, this.#chart_labels);
console.log(`def ${this.def.name} | chart_data`, this.#chart_data); console.log(`def ${this.def.name} | chart_data`, this.#chart_data);
this.#__update_chart(); this.render();
} }
} }

View file

@ -7,18 +7,29 @@ class ChartStackedBarSet {
this.emel = new Emel().emel; this.emel = new Emel().emel;
this.el = this.emel(`div[class="stacked-bar-set"]>div[class=legend]+div[class=bars]`); this.el = this.emel(`div[class="stacked-bar-set"]>div[class=legend]+div[class=bars]`);
this.el_legend = this.el.querySelector(`.legend`);
this.bars = []; this.bars = [];
} }
clear() {
this.el_legend.replaceChildren();
this.el.querySelector(".bars").replaceChildren();
this.bars = [];
}
/**
* Adds a new stacked bar to this set of stacked bars.
* @param {number} i The index in the list of stacked bars to insert it.
* @param {ChartStackedBar} bar The new stacked bar instance to add.
* @return {void}
*/
add_bar(i, bar) { add_bar(i, bar) {
this.bars.splice(i, 0, bar); this.bars.splice(i, 0, bar);
this.#update_cats();
} }
#update_cats() { #update_cats() {
const el_legend = this.el.querySelector(`.legend`); const els_legend_items = [...this.el_legend.querySelectorAll(`.legend-item`)];
const els_legend_items = [...el_legend.querySelectorAll(`.legend-item`)];
// map → flatten → uniq // map → flatten → uniq
let cats = this.bars.map(bar => bar.categories).flat() let cats = this.bars.map(bar => bar.categories).flat()