mapcomplete/UI/Base/Table.ts

136 lines
4.8 KiB
TypeScript
Raw Normal View History

2022-09-08 21:40:48 +02:00
import BaseUIElement from "../BaseUIElement"
import { Utils } from "../../Utils"
import Translations from "../i18n/Translations"
import { UIEventSource } from "../../Logic/UIEventSource"
2021-06-15 00:55:12 +02:00
export default class Table extends BaseUIElement {
2022-09-08 21:40:48 +02:00
private readonly _header: BaseUIElement[]
private readonly _contents: BaseUIElement[][]
private readonly _contentStyle: string[][]
private readonly _sortable: boolean
constructor(
header: (BaseUIElement | string)[],
contents: (BaseUIElement | string)[][],
options?: {
contentStyle?: string[][]
sortable?: false | boolean
}
) {
super()
this._contentStyle = options?.contentStyle ?? [["min-width: 9rem"]]
this._header = header?.map(Translations.W)
this._contents = contents.map((row) => row.map(Translations.W))
this._sortable = options?.sortable ?? false
2021-06-15 00:55:12 +02:00
}
2021-06-21 03:13:05 +02:00
AsMarkdown(): string {
2022-09-08 21:40:48 +02:00
const headerMarkdownParts = this._header.map((hel) => hel?.AsMarkdown() ?? " ")
2022-12-06 03:42:32 +01:00
const header = Utils.NoNull(headerMarkdownParts).join(" | ")
2022-09-08 21:40:48 +02:00
const headerSep = headerMarkdownParts.map((part) => "-".repeat(part.length + 2)).join(" | ")
const table = this._contents
2022-12-06 03:42:32 +01:00
.map((row) => row.map((el) => el?.AsMarkdown()?.replace("|", "\\|") ?? " ").join(" | "))
2022-09-08 21:40:48 +02:00
.join("\n")
2021-06-21 03:13:05 +02:00
return "\n\n" + [header, headerSep, table, ""].join("\n")
2021-06-21 03:13:05 +02:00
}
2021-06-15 00:55:12 +02:00
protected InnerConstructElement(): HTMLElement {
const table = document.createElement("table")
/**
2022-09-08 21:40:48 +02:00
* Sortmode: i: sort column i ascending;
* if i is negative : sort column (-i - 1) descending
*/
2022-09-08 21:40:48 +02:00
const sortmode = new UIEventSource<number>(undefined)
const self = this
const headerElems = Utils.NoNull(
(this._header ?? []).map((elem, i) => {
if (self._sortable) {
elem.onClick(() => {
const current = sortmode.data
if (current == i) {
sortmode.setData(-1 - i)
} else {
sortmode.setData(i)
}
})
}
return elem.ConstructElement()
})
)
2021-06-15 00:55:12 +02:00
if (headerElems.length > 0) {
2021-06-21 03:13:05 +02:00
const thead = document.createElement("thead")
2022-09-08 21:40:48 +02:00
const tr = document.createElement("tr")
headerElems.forEach((headerElem) => {
2021-06-15 00:55:12 +02:00
const td = document.createElement("th")
td.appendChild(headerElem)
tr.appendChild(td)
})
2021-06-21 03:13:05 +02:00
thead.appendChild(tr)
table.appendChild(thead)
2021-06-15 00:55:12 +02:00
}
2021-06-21 03:13:05 +02:00
for (let i = 0; i < this._contents.length; i++) {
2022-09-08 21:40:48 +02:00
let row = this._contents[i]
2021-06-15 00:55:12 +02:00
const tr = document.createElement("tr")
2021-06-21 03:13:05 +02:00
for (let j = 0; j < row.length; j++) {
try {
2022-09-08 21:40:48 +02:00
let elem = row[j]
if (elem?.ConstructElement === undefined) {
2023-06-01 02:52:21 +02:00
continue
}
const htmlElem = elem?.ConstructElement()
if (htmlElem === undefined) {
2022-09-08 21:40:48 +02:00
continue
}
2022-09-08 21:40:48 +02:00
let style = undefined
if (
this._contentStyle !== undefined &&
this._contentStyle[i] !== undefined &&
this._contentStyle[j] !== undefined
) {
style = this._contentStyle[i][j]
}
const td = document.createElement("td")
2022-09-08 21:40:48 +02:00
td.style.cssText = style
td.appendChild(htmlElem)
tr.appendChild(td)
} catch (e) {
console.error("Could not render an element in a table due to", e, row[j])
2021-06-15 00:55:12 +02:00
}
}
table.appendChild(tr)
}
2022-09-08 21:40:48 +02:00
sortmode.addCallback((sortCol) => {
if (sortCol === undefined) {
return
}
const descending = sortCol < 0
2022-09-08 21:40:48 +02:00
const col = descending ? -sortCol - 1 : sortCol
let rows: HTMLTableRowElement[] = Array.from(table.rows)
2022-09-08 21:40:48 +02:00
rows.splice(0, 1) // remove header row
rows = rows.sort((a, b) => {
const ac = a.cells[col]?.textContent?.toLowerCase()
const bc = b.cells[col]?.textContent?.toLowerCase()
2022-09-08 21:40:48 +02:00
if (ac === bc) {
return 0
}
2022-09-08 21:40:48 +02:00
return ac < bc !== descending ? -1 : 1
})
2022-09-08 21:40:48 +02:00
for (let j = rows.length; j > 1; j--) {
table.deleteRow(j)
}
for (const row of rows) {
table.appendChild(row)
}
})
2021-06-15 00:55:12 +02:00
2022-09-08 21:40:48 +02:00
return table
2021-06-15 00:55:12 +02:00
}
2022-09-08 21:40:48 +02:00
}