Compare commits

..

5 Commits

Author SHA1 Message Date
CentreMetre
026de5987b Add explanation comment 2026-06-14 17:00:35 +01:00
CentreMetre
59174d3c88 Change R generic to TRow 2026-06-14 16:31:45 +01:00
CentreMetre
071c5fc08c Fix typo 2026-06-14 16:29:53 +01:00
CentreMetre
3b0cb99334 Remove mention of Heist for User 2026-06-14 16:29:47 +01:00
CentreMetre
bc602c7bd8 Remove specific imports and specific functionality 2026-06-14 16:29:24 +01:00

View File

@@ -1,36 +1,34 @@
import {isIsoDateWithMs, padIsoMilliseconds} from "./util.js";
/** /**
* @template R A type that the shape of a row should be. E.g. * @template TRow A type that the shape of a row should be. E.g.
* ``` * ```
* type Heist = { * type User = {
* id: number; * id: number;
* name: string; * name: string;
* } * }
* ``` * ```
* Then `Heist` would be passed as the generic. * Then `User` would be passed as the generic.
*/ */
export abstract class Table<R extends Object> { export abstract class Table<TRow extends Object> {
table: HTMLTableElement; table: HTMLTableElement;
tableBody: HTMLTableSectionElement; tableBody: HTMLTableSectionElement;
headers: Header<R>[]; headers: Header<TRow>[];
messageEl: HTMLElement | null; messageEl: HTMLElement | null;
/** /**
* Creates a list of Header objects to be used in a table for the header cell values and column types. * Creates a list of Header objects to be used in a table for the header cell values and column types.
* @param exampleObject An instance of the type to be displaying. Can be an object with default values such as * @param exampleObject An instance of the type to be displaying. Can be an object with default/zero values for that type
* `0`, `""`, `false` or `true`. E.g. for a heist ```{ id: 0; name: ""; }``` * such as `0`, `""`, `false` or `true`. E.g. for a user ```{ id: 0; name: ""; }``` would be passed
* @param displayNamesAndOrder A object with the same properties, but all string type for the header cell values. * @param displayNamesAndOrder A object with the same properties, but all string type for the header cell values.
* Used by passing an object with the keys of R and the name preferred. E.g. for a heist: * Used by passing an object with the keys of R and the name preferred. E.g. for a user:
* ```{ id: "ID", name: "Heist Name" }```, this makes the columns appear in the order of ID then Heist Name. * ```{ id: "ID", name: "Name" }```, this makes the columns appear in the order of ID then Name.
* @param messageElement Used to display messaged related to data output. Optional. * @param messageElement Used to display messaged related to data output. Optional.
* Omit (undefined) to use the class provided HTMLParagraphElement. * Omit (undefined) to use the class provided HTMLParagraphElement.
* Pass an HTMLElement tp ise a custom element. * Pass an HTMLElement to use a custom element.
* Pass null to disable automatic message output. * Pass null to disable automatic message output.
*/ */
constructor(exampleObject: R, displayNamesAndOrder?: Record<keyof R, string>, messageElement?: HTMLElement | null | undefined) { constructor(exampleObject: TRow, displayNamesAndOrder?: Record<keyof TRow, string>, messageElement?: HTMLElement | null | undefined) {
this.table = document.createElement("table") this.table = document.createElement("table")
this.tableBody = document.createElement("tbody"); this.tableBody = document.createElement("tbody");
@@ -46,17 +44,18 @@ export abstract class Table<R extends Object> {
} }
createHeadersFromType( createHeadersFromType(
exampleObject: R, // Needed for runtime, since types don't exist in JS. exampleObject: TRow, // Needed for runtime, since types don't exist in JS.
displayNamesAndOrder?: Record<keyof R, string> displayNamesAndOrder?: Record<keyof TRow, string>
): Header<R>[] { ): Header<TRow>[] {
// Checks if display names and order was passed. If not, derives the keys to use for the header from the example object.
const keys = displayNamesAndOrder const keys = displayNamesAndOrder
? (Object.keys(displayNamesAndOrder) as (keyof R)[]) ? (Object.keys(displayNamesAndOrder) as (keyof TRow)[])
: (Object.keys(exampleObject) as (keyof R)[]); : (Object.keys(exampleObject) as (keyof TRow)[]);
return keys.map(key => ({ return keys.map(key => ({
key, key, // Raw property name from TRow
value: displayNamesAndOrder?.[key] ?? String(key), value: displayNamesAndOrder?.[key] ?? String(key), // The value of the th html element, with a nullish coalescing operator for if its null
type: typeof exampleObject[key], type: typeof exampleObject[key], // The type of the column
})); }));
} }
@@ -96,12 +95,12 @@ export abstract class Table<R extends Object> {
* Appends one more row to the current rows. * Appends one more row to the current rows.
* @param data Data to append as a row. * @param data Data to append as a row.
*/ */
appendRow(data: R): void { appendRow(data: TRow): void {
const row = this.createPopulatedRow(data) const row = this.createPopulatedRow(data)
this.tableBody.appendChild(row) this.tableBody.appendChild(row)
} }
appendRows(data: R[]): void { appendRows(data: TRow[]): void {
if (data.length === 0) { if (data.length === 0) {
this.setMessage("No data to append. Empty list provided.") this.setMessage("No data to append. Empty list provided.")
return; return;
@@ -116,7 +115,7 @@ export abstract class Table<R extends Object> {
* @param data The data in the shape of `R`, with values populated. * @param data The data in the shape of `R`, with values populated.
* @return A HTMLTableRowElement that can then be set on a table. * @return A HTMLTableRowElement that can then be set on a table.
*/ */
createPopulatedRow(data: R): HTMLTableRowElement { createPopulatedRow(data: TRow): HTMLTableRowElement {
const row = document.createElement("tr"); const row = document.createElement("tr");
for (const header of this.headers) { for (const header of this.headers) {
@@ -139,17 +138,7 @@ export abstract class Table<R extends Object> {
default: default:
const strValue = String(value) const strValue = String(value)
if (isIsoDateWithMs(strValue))
{
const strValueDateTime = padIsoMilliseconds(strValue);
cell.classList.add("table-cell-datetime")
const date = strValueDateTime.split("T")[0]
const time = strValueDateTime.split("T")[1]
cellValue = `${date} ${time}`;
}
else {
cellValue = value != null ? String(value) : ""; cellValue = value != null ? String(value) : "";
}
} }
cell.textContent = cellValue; cell.textContent = cellValue;
row.append(cell); row.append(cell);
@@ -169,18 +158,18 @@ export abstract class Table<R extends Object> {
/** /**
* Deletes all current rows and sets a single row to the data in data param. * Deletes all current rows and sets a single row to the data in data param.
* @param data The data to set the table to. Should be of shape {@link R} * @param data The data to set the table to. Should be of shape {@link TRow}
*/ */
setSingleRow(data: R): void { setSingleRow(data: TRow): void {
this.clearRows() this.clearRows()
this.appendRow(data) this.appendRow(data)
} }
/** /**
* Deletes all current rows and sets the rows to the data in data param. * Deletes all current rows and sets the rows to the data in data param.
* @param data The data to set the table to. Should be of shape {@link R} * @param data The data to set the table to. Should be of shape {@link TRow}
*/ */
setRows(data: R[]): void { setRows(data: TRow[]): void {
this.clearRows() this.clearRows()
if (data.length === 0) { if (data.length === 0) {
this.setMessage("No data to show. Empty list provided.") this.setMessage("No data to show. Empty list provided.")