forked from MapComplete/MapComplete
Add a histogram visualisation
This commit is contained in:
parent
962e364bad
commit
d4909734a1
2 changed files with 77 additions and 65 deletions
|
@ -8,7 +8,7 @@ export default class Table extends BaseUIElement {
|
||||||
private readonly _contents: BaseUIElement[][];
|
private readonly _contents: BaseUIElement[][];
|
||||||
private readonly _contentStyle: string[][];
|
private readonly _contentStyle: string[][];
|
||||||
|
|
||||||
constructor(header: (BaseUIElement | string)[],
|
constructor(header: (BaseUIElement | string)[],
|
||||||
contents: (BaseUIElement | string)[][],
|
contents: (BaseUIElement | string)[][],
|
||||||
contentStyle?: string[][]) {
|
contentStyle?: string[][]) {
|
||||||
super();
|
super();
|
||||||
|
@ -17,25 +17,39 @@ export default class Table extends BaseUIElement {
|
||||||
this._contents = contents.map(row => row.map(Translations.W));
|
this._contents = contents.map(row => row.map(Translations.W));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsMarkdown(): string {
|
||||||
|
|
||||||
|
const headerMarkdownParts = this._header.map(hel => hel?.AsMarkdown() ?? " ")
|
||||||
|
const header = headerMarkdownParts.join(" | ");
|
||||||
|
const headerSep = headerMarkdownParts.map(part => '-'.repeat(part.length + 2)).join("|")
|
||||||
|
const table = this._contents.map(row => row.map(el => el.AsMarkdown() ?? " ").join("|")).join("\n")
|
||||||
|
|
||||||
|
return [header, headerSep, table, ""].join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
protected InnerConstructElement(): HTMLElement {
|
protected InnerConstructElement(): HTMLElement {
|
||||||
const table = document.createElement("table")
|
const table = document.createElement("table")
|
||||||
|
|
||||||
const headerElems = Utils.NoNull((this._header ?? []).map(elems => elems.ConstructElement()))
|
const headerElems = Utils.NoNull((this._header ?? []).map(elems => elems.ConstructElement()))
|
||||||
if (headerElems.length > 0) {
|
if (headerElems.length > 0) {
|
||||||
|
|
||||||
|
const thead = document.createElement("thead")
|
||||||
|
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
headerElems.forEach(headerElem => {
|
headerElems.forEach(headerElem => {
|
||||||
const td = document.createElement("th")
|
const td = document.createElement("th")
|
||||||
td.appendChild(headerElem)
|
td.appendChild(headerElem)
|
||||||
tr.appendChild(td)
|
tr.appendChild(td)
|
||||||
})
|
})
|
||||||
table.appendChild(tr)
|
thead.appendChild(tr)
|
||||||
|
table.appendChild(thead)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this._contents.length; i++){
|
for (let i = 0; i < this._contents.length; i++) {
|
||||||
let row = this._contents[i];
|
let row = this._contents[i];
|
||||||
const tr = document.createElement("tr")
|
const tr = document.createElement("tr")
|
||||||
for (let j = 0; j < row.length; j++){
|
for (let j = 0; j < row.length; j++) {
|
||||||
let elem = row[j];
|
let elem = row[j];
|
||||||
const htmlElem = elem?.ConstructElement()
|
const htmlElem = elem?.ConstructElement()
|
||||||
if (htmlElem === undefined) {
|
if (htmlElem === undefined) {
|
||||||
|
@ -43,10 +57,10 @@ export default class Table extends BaseUIElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
let style = undefined;
|
let style = undefined;
|
||||||
if(this._contentStyle !== undefined && this._contentStyle[i] !== undefined && this._contentStyle[j]!== undefined){
|
if (this._contentStyle !== undefined && this._contentStyle[i] !== undefined && this._contentStyle[j] !== undefined) {
|
||||||
style = this._contentStyle[i][j]
|
style = this._contentStyle[i][j]
|
||||||
}
|
}
|
||||||
|
|
||||||
const td = document.createElement("td")
|
const td = document.createElement("td")
|
||||||
td.style.cssText = style;
|
td.style.cssText = style;
|
||||||
td.appendChild(htmlElem)
|
td.appendChild(htmlElem)
|
||||||
|
@ -57,15 +71,5 @@ export default class Table extends BaseUIElement {
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsMarkdown(): string {
|
|
||||||
|
|
||||||
const headerMarkdownParts = this._header.map(hel => hel?.AsMarkdown() ?? " ")
|
|
||||||
const header =headerMarkdownParts.join(" | ");
|
|
||||||
const headerSep = headerMarkdownParts.map(part => '-'.repeat(part.length + 2)).join("|")
|
|
||||||
const table = this._contents.map(row => row.map(el => el.AsMarkdown()?? " ").join("|")).join("\n")
|
|
||||||
|
|
||||||
return [header, headerSep, table, ""].join("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -18,9 +18,9 @@ import State from "../State";
|
||||||
import {ImageSearcher} from "../Logic/Actors/ImageSearcher";
|
import {ImageSearcher} from "../Logic/Actors/ImageSearcher";
|
||||||
import BaseUIElement from "./BaseUIElement";
|
import BaseUIElement from "./BaseUIElement";
|
||||||
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
||||||
import List from "./Base/List";
|
|
||||||
import Title from "./Base/Title";
|
import Title from "./Base/Title";
|
||||||
import Table from "./Base/Table";
|
import Table from "./Base/Table";
|
||||||
|
import Histogram from "./BigComponents/Histogram";
|
||||||
|
|
||||||
export default class SpecialVisualizations {
|
export default class SpecialVisualizations {
|
||||||
|
|
||||||
|
@ -147,61 +147,69 @@ export default class SpecialVisualizations {
|
||||||
return new VariableUiElement(source.map(data => data[neededValue] ?? "Loading..."));
|
return new VariableUiElement(source.map(data => data[neededValue] ?? "Loading..."));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
funcName: "histogram",
|
funcName: "histogram",
|
||||||
docs:"Create a histogram for a list of given values, read from the properties.",
|
docs: "Create a histogram for a list of given values, read from the properties.",
|
||||||
example: "`{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram",
|
example: "`{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram",
|
||||||
args:[
|
args: [
|
||||||
{
|
{
|
||||||
name: "key",
|
name: "key",
|
||||||
doc: "The key to be read and to generate a histogram from"
|
doc: "The key to be read and to generate a histogram from"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "title",
|
||||||
|
doc: "The text to put above the given values column",
|
||||||
|
defaultValue: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "countHeader",
|
||||||
|
doc: "The text to put above the counts",
|
||||||
|
defaultValue: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "colors",
|
||||||
|
doc: "(Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33`"
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
constr: (state: State, tagSource: UIEventSource<any>, args: string[]) =>{
|
constr: (state: State, tagSource: UIEventSource<any>, args: string[]) => {
|
||||||
return new VariableUiElement(
|
|
||||||
tagSource
|
|
||||||
.map(tags => tags[args[0]])
|
|
||||||
.map(listStr => {
|
|
||||||
try{
|
|
||||||
if("" === listStr ?? ""){
|
|
||||||
return "Nothing defined";
|
|
||||||
}
|
|
||||||
const list : string[] = JSON.parse(listStr)
|
|
||||||
|
|
||||||
if(list.length === 0){
|
let assignColors = undefined;
|
||||||
return "No values given";
|
if (args.length >= 3) {
|
||||||
}
|
const colors = [...args]
|
||||||
|
colors.splice(0, 3)
|
||||||
const counts = new Map<string, number>()
|
const mapping = colors.map(c => {
|
||||||
for (const key of list) {
|
const splitted = c.split(":");
|
||||||
if(key === null || key === undefined || key === ""){
|
const value = splitted.pop()
|
||||||
continue;
|
const regex = splitted.join(":")
|
||||||
}
|
return {regex: "^" + regex + "$", color: value}
|
||||||
|
|
||||||
if(!counts.has(key)){
|
|
||||||
counts.set(key, 1)
|
|
||||||
}else{
|
|
||||||
counts.set(key, counts.get(key) + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const keys = Array.from(counts.keys())
|
|
||||||
keys.sort()
|
|
||||||
|
|
||||||
return new List(keys.map(key=> new Combine[
|
|
||||||
new FixedUiElement(key).SetClass("font-bold"),
|
|
||||||
counts.get(key)
|
|
||||||
]));
|
|
||||||
|
|
||||||
|
|
||||||
}catch{
|
|
||||||
return "Could not generate histogram" // TODO translate
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
)
|
assignColors = (key) => {
|
||||||
}
|
for (const kv of mapping) {
|
||||||
|
if (key.match(kv.regex) !== null) {
|
||||||
|
return kv.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const listSource: UIEventSource<string[]> = tagSource
|
||||||
|
.map(tags => {
|
||||||
|
try {
|
||||||
|
const value = tags[args[0]]
|
||||||
|
if (value === "" || value === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Could not load histogram: parsing of the list failed: ", e)
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return new Histogram(listSource, args[1], args[2], assignColors)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
funcName: "share_link",
|
funcName: "share_link",
|
||||||
|
@ -262,9 +270,9 @@ export default class SpecialVisualizations {
|
||||||
[
|
[
|
||||||
new Title(viz.funcName, 3),
|
new Title(viz.funcName, 3),
|
||||||
viz.docs,
|
viz.docs,
|
||||||
new Table( ["name","default","description"],
|
new Table(["name", "default", "description"],
|
||||||
viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc])
|
viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc])
|
||||||
),
|
),
|
||||||
new Title("Example usage", 4),
|
new Title("Example usage", 4),
|
||||||
new FixedUiElement(
|
new FixedUiElement(
|
||||||
viz.example ?? "{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}"
|
viz.example ?? "{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}"
|
||||||
|
@ -275,7 +283,7 @@ export default class SpecialVisualizations {
|
||||||
|
|
||||||
|
|
||||||
return new Combine([
|
return new Combine([
|
||||||
new Title("Special tag renderings",3),
|
new Title("Special tag renderings", 3),
|
||||||
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
|
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
|
||||||
"General usage is <b>{func_name()}</b> or <b>{func_name(arg, someotherarg)}</b>. Note that you <i>do not</i> need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args",
|
"General usage is <b>{func_name()}</b> or <b>{func_name(arg, someotherarg)}</b>. Note that you <i>do not</i> need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args",
|
||||||
...helpTexts
|
...helpTexts
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue