Compare commits

...

4 Commits

Author SHA1 Message Date
CentreMetre
f5833fcbf8 Fix doc typos and add Table class summary line
Correct 'A object' -> 'An object' and 'if i iss not set' -> 'if it is
not set', and add a one-line summary to the Table class JSDoc.
2026-06-15 21:15:44 +01:00
CentreMetre
95254027c4 Use a passed HTMLTableCellElement as the cell directly
When a cell value is a <td>/<th>, append it to the row as-is instead of
wrapping it in a generated <td>, which would have produced invalid nested
cells. Any other HTMLElement still gets wrapped as before.
2026-06-15 21:15:34 +01:00
CentreMetre
436217a8ff Add TypeScript build config for .d.ts declaration output
Set up tsconfig.json (declaration: true, DOM lib), package.json with
main/types/files pointing at dist, and .gitignore for node_modules/dist
so the library can be consumed with type declarations.
2026-06-15 17:30:46 +01:00
CentreMetre
8c561385b8 Add licence 2026-06-15 16:49:38 +01:00
6 changed files with 99 additions and 5 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
dist

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Martin McLaren (CentreMetre)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -4,7 +4,7 @@
*/
export type TableOptions<TRow extends object> = {
/**
* A object with the same properties, but all string type for the header cell values.
* An object with the same properties, but all string type for the header cell values.
* Used by passing an object with the keys of {@link TRow} and the name preferred. E.g. for a user:
* ```{ id: "ID", name: "Name" }```, this makes the columns appear in the order of ID then Name.
*/
@@ -23,6 +23,7 @@ export type TableOptions<TRow extends object> = {
};
/**
* Abstract base class that renders typed row data as an HTML `<table>` on a web page.
* @template TRow A type that the shape of a row should be. E.g.
* ```
* type User = {
@@ -143,7 +144,8 @@ export abstract class Table<TRow extends object> {
* Creates a row, populated with data, for displaying in the table.
* @param data The data in the shape of `TRow`, with values populated.
* @return A HTMLTableRowElement that can then be set on a table.
* @remarks If a cell value is an HTMLElement it is appended to the cell. A DOM node can only
* @remarks If a cell value is an HTMLTableCellElement (a `<td>`/`<th>`) it is used as the cell
* directly; any other HTMLElement is appended inside a generated `<td>`. A DOM node can only
* exist in one place, so passing the same element instance to multiple rows moves it to the
* last row only. Pass a fresh element (or `el.cloneNode(true)`) per row. But keep in mind that
* el.cloneNode(true) does not keep event listeners, so if event listeners are used it is best
@@ -153,14 +155,20 @@ export abstract class Table<TRow extends object> {
const row = document.createElement("tr");
for (const column of this.columns) {
const cell: HTMLTableCellElement = document.createElement("td");
const value = data[column.key]
if (value instanceof HTMLTableCellElement) {
row.append(value); // already a cell, use it as-is
continue;
}
const cell: HTMLTableCellElement = document.createElement("td");
// Element values are inserted as children, not stringified.
if (value instanceof HTMLElement) {
cell.appendChild(value);
row.append(cell);
continue; // skip the textContent path that would wipe it
continue; // skip the textContent path that would wipe it
}
let cellValue = "";
@@ -226,7 +234,7 @@ export abstract class Table<TRow extends object> {
/**
* Get the message element for the table.
* @return The HTMLElement that is used for showing messages. Or null if i iss not set.
* @return The HTMLElement that is used for showing messages. Or null if it is not set.
*/
getMessageElement(): HTMLElement | null {
return this.messageEl;

29
package-lock.json generated Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "abstract-ts-table",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "abstract-ts-table",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"typescript": "^6.0.3"
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
}
}
}

22
package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "abstract-ts-table",
"version": "1.0.0",
"description": "A utility class for creating HTML tables.",
"type": "module",
"main": "dist/abstract-table.js",
"types": "dist/abstract-table.d.ts",
"files": ["dist"],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc"
},
"repository": {
"type": "git",
"url": "https://git.centremetre.com/CentreMetre/abstract-ts-table"
},
"author": "CentreMetre",
"license": "MIT",
"devDependencies": {
"typescript": "^6.0.3"
}
}

12
tsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"lib": ["ES2020", "DOM"],
"declaration": true,
"outDir": "dist",
"strict": true
},
"include": ["abstract-table.ts"]
}