Skip to content

Commit 51a8587

Browse files
committed
feat(core): adds better typing support for bim-table
1 parent 5b53403 commit 51a8587

File tree

7 files changed

+155
-126
lines changed

7 files changed

+155
-126
lines changed

packages/core/src/components/Table/index.ts

Lines changed: 59 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,23 @@ import { LitElement, css, html } from "lit";
22
import { property, state } from "lit/decorators.js";
33
import { styles } from "../../core/Manager/src/styles";
44
import {
5+
TableChildren,
56
TableDataTransform,
67
TableGroupData,
8+
TableLoadFunction,
9+
TableRow,
710
TableRowData,
811
TableRowTemplate,
12+
ColumnData,
913
} from "./src";
1014
import { evalCondition, getQuery } from "../../core/utils";
1115
import { loadingSkeleton } from "./src/loading-skeleton";
1216
import { processingBar } from "./src/processing-bar";
1317

14-
/**
15-
* Represents a column in the table.
16-
*
17-
* @property name - The name of the column.
18-
* @property width - The width of the column.
19-
*/
20-
export interface ColumnData {
21-
/** The name of the column. */
22-
name: string;
23-
24-
/** The width of the column. */
25-
width: string;
26-
27-
forceDataTransform?: boolean;
28-
}
29-
3018
/**
3119
* A custom table web component for BIM applications. HTML tag: bim-table
3220
*/
33-
export class Table extends LitElement {
21+
export class Table<T extends TableRowData = TableRowData> extends LitElement {
3422
/**
3523
* CSS styles for the component.
3624
*/
@@ -76,10 +64,8 @@ export class Table extends LitElement {
7664
`,
7765
];
7866

79-
private _columnsChange = new Event("columnschange");
80-
8167
@state()
82-
private _filteredData: TableGroupData[] = [];
68+
private _filteredData: TableGroupData<T>[] = [];
8369

8470
/**
8571
* A boolean property that determines whether the table headers are hidden.
@@ -108,7 +94,7 @@ export class Table extends LitElement {
10894
@property({ type: String, attribute: "min-col-width", reflect: true })
10995
minColWidth = "4rem";
11096

111-
private _columns: ColumnData[] = [];
97+
private _columns: ColumnData<T>[] = [];
11298

11399
/**
114100
* Sets the columns for the table.
@@ -130,33 +116,33 @@ export class Table extends LitElement {
130116
* ```
131117
*/
132118
@property({ type: Array, attribute: false })
133-
set columns(value: (string | ColumnData)[]) {
134-
const columns: ColumnData[] = [];
119+
set columns(value: (keyof T | ColumnData<T>)[]) {
120+
const columns: ColumnData<T>[] = [];
135121
for (const header of value) {
136122
const column =
137123
typeof header === "string"
138124
? { name: header, width: `minmax(${this.minColWidth}, 1fr)` }
139-
: header;
125+
: (header as ColumnData<T>);
140126
columns.push(column);
141127
}
142128
this._columns = columns;
143129
this.computeMissingColumns(this.data);
144-
this.dispatchEvent(this._columnsChange);
130+
this.dispatchEvent(new Event("columnschange"));
145131
}
146132

147-
get columns(): ColumnData[] {
133+
get columns(): ColumnData<T>[] {
148134
return this._columns;
149135
}
150136

151137
private get _headerRowData() {
152-
const data: TableRowData = {};
138+
const data: Partial<T> = {};
153139
for (const column of this.columns) {
154-
if (typeof column === "string") {
155-
data[column] = column;
156-
} else {
157-
const { name } = column;
158-
data[name] = name;
159-
}
140+
// if (typeof column === "string") {
141+
// data[column] = column;
142+
// } else {
143+
const { name } = column;
144+
(data[name] as any) = String(name);
145+
// }
160146
}
161147
return data;
162148
}
@@ -212,7 +198,7 @@ export class Table extends LitElement {
212198
return this._queryString;
213199
}
214200

215-
private _data: TableGroupData[] = [];
201+
private _data: TableGroupData<T>[] = [];
216202

217203
/**
218204
* Sets the data for the table.
@@ -232,7 +218,7 @@ export class Table extends LitElement {
232218
* ```
233219
*/
234220
@property({ type: Array, attribute: false })
235-
set data(value: TableGroupData[]) {
221+
set data(value: TableGroupData<T>[]) {
236222
this._data = value;
237223
this.updateFilteredData();
238224
const computed = this.computeMissingColumns(value);
@@ -302,13 +288,13 @@ export class Table extends LitElement {
302288
*
303289
* @defaultValue An empty object.
304290
*/
305-
dataTransform: TableDataTransform = {};
291+
dataTransform: TableDataTransform<T> = {};
306292

307293
@property({ type: Boolean, reflect: true, attribute: "selectable-rows" })
308294
selectableRows = false;
309295

310296
@property({ attribute: false })
311-
selection: Set<TableRowData> = new Set();
297+
selection: Set<Partial<T>> = new Set();
312298

313299
@property({ type: Boolean, attribute: "no-indentation", reflect: true })
314300
noIndentation = false;
@@ -323,8 +309,6 @@ export class Table extends LitElement {
323309

324310
private _hiddenColumns: string[] = [];
325311

326-
loadingErrorElement: HTMLElement | null = null;
327-
328312
set hiddenColumns(value: string[]) {
329313
this._hiddenColumns = value;
330314
setTimeout(() => {
@@ -364,7 +348,7 @@ export class Table extends LitElement {
364348
}
365349
}
366350

367-
private computeMissingColumns(row: TableGroupData[]): boolean {
351+
private computeMissingColumns(row: TableGroupData<T>[]): boolean {
368352
let computed = false;
369353
for (const data of row) {
370354
const { children, data: rowData } = data;
@@ -456,17 +440,19 @@ export class Table extends LitElement {
456440
return this.generateText("tab");
457441
}
458442

459-
applyDataTransform(data: TableRowData) {
460-
const declaration: TableRowTemplate = {};
443+
applyDataTransform(data: Partial<T>) {
444+
const declaration: TableRowTemplate<T> = {};
461445
for (const key of Object.keys(this.dataTransform)) {
462446
const columnConfig = this.columns.find((column) => column.name === key);
463447
if (!(columnConfig && columnConfig.forceDataTransform)) continue;
464-
if (!(key in data)) data[key] = "";
448+
// TODO: Review data[key] as any. Is weird.
449+
if (!(key in data)) (data[key] as any) = "";
465450
}
466-
for (const key in data) {
451+
const _data = data as T;
452+
for (const key in _data) {
467453
const rowDeclaration = this.dataTransform[key];
468454
if (rowDeclaration) {
469-
declaration[key] = rowDeclaration(data[key], data);
455+
declaration[key] = rowDeclaration(_data[key], data);
470456
} else {
471457
declaration[key] = data[key];
472458
}
@@ -513,7 +499,7 @@ export class Table extends LitElement {
513499
}
514500

515501
getRowIndentation(
516-
target: TableRowData,
502+
target: Partial<T>,
517503
tableGroups = this.value,
518504
level = 0,
519505
): number | null {
@@ -532,7 +518,7 @@ export class Table extends LitElement {
532518
}
533519

534520
getGroupIndentation(
535-
target: TableGroupData,
521+
target: TableGroupData<T>,
536522
tableGroups = this.value,
537523
level = 0,
538524
): number | null {
@@ -563,7 +549,7 @@ export class Table extends LitElement {
563549
/**
564550
* The function to be executed when loading async data using Table.loadData
565551
*/
566-
loadFunction?: () => Promise<TableGroupData[]>;
552+
loadFunction?: TableLoadFunction<T>;
567553

568554
/**
569555
* Asynchronously loads data into the table based on Table.loadFunction.
@@ -592,12 +578,17 @@ export class Table extends LitElement {
592578
} catch (error: any) {
593579
this.loading = false;
594580
if (this._filteredData.length !== 0) return false; // Do nothing in case the table already had values
581+
const errorSlot = this.querySelector("[slot='error-loading']");
582+
const errorMessageElement = errorSlot?.querySelector(
583+
"[data-table-element='error-message']",
584+
);
595585
if (
596586
error instanceof Error &&
597-
this.loadingErrorElement &&
587+
errorMessageElement &&
598588
error.message.trim() !== ""
599-
)
600-
this.loadingErrorElement.textContent = error.message;
589+
) {
590+
errorMessageElement.textContent = error.message;
591+
}
601592
this._errorLoading = true;
602593
return false;
603594
}
@@ -614,19 +605,22 @@ export class Table extends LitElement {
614605
* If the function returns `true`, the data row will be included in the filtered results.
615606
* If the function returns `false`, the data row will be excluded from the filtered results.
616607
*/
617-
filterFunction?: (queryString: string, data: TableGroupData) => boolean;
608+
filterFunction?: (queryString: string, data: TableGroupData<T>) => boolean;
618609

619610
private _stringFilterFunction = (
620611
queryString: string,
621-
data: TableGroupData,
612+
data: TableGroupData<T>,
622613
) => {
623614
const valueMatch = Object.values(data.data).some((val) => {
624615
return String(val).toLowerCase().includes(queryString.toLowerCase());
625616
});
626617
return valueMatch;
627618
};
628619

629-
private _queryFilterFunction = (queryString: string, row: TableGroupData) => {
620+
private _queryFilterFunction = (
621+
queryString: string,
622+
row: TableGroupData<T>,
623+
) => {
630624
let valueFoundInData = false;
631625
const query = getQuery(queryString) ?? [];
632626
for (const search of query) {
@@ -641,11 +635,11 @@ export class Table extends LitElement {
641635
key = _key;
642636
const keys = Object.keys(row.data).filter((key) => key.includes(_key));
643637
const tests = keys.map((key) =>
644-
evalCondition(row.data[key], condition, value),
638+
evalCondition(row.data[key]!, condition, value),
645639
);
646640
valueFoundInData = tests.some((test) => test);
647641
} else {
648-
valueFoundInData = evalCondition(row.data[key], condition, value);
642+
valueFoundInData = evalCondition(row.data[key]!, condition, value);
649643
}
650644
if (!valueFoundInData) break;
651645
}
@@ -656,13 +650,13 @@ export class Table extends LitElement {
656650
queryString: string,
657651
filterFunction = this.filterFunction ?? this._stringFilterFunction,
658652
data = this.data,
659-
): TableGroupData[] {
660-
const results: TableGroupData[] = [];
653+
): TableGroupData<T>[] {
654+
const results: TableGroupData<T>[] = [];
661655
for (const row of data) {
662656
const valueMatch = filterFunction(queryString, row);
663657
if (valueMatch) {
664658
if (this.preserveStructureOnFilter) {
665-
const rowToAdd: TableGroupData = { data: row.data };
659+
const rowToAdd: TableGroupData<T> = { data: row.data };
666660
if (row.children) {
667661
const childResults = this.filter(
668662
queryString,
@@ -716,7 +710,8 @@ export class Table extends LitElement {
716710
return html`<slot name="missing-data"></slot>`;
717711
}
718712

719-
const header = document.createElement("bim-table-row");
713+
// @ts-ignore
714+
const header = document.createElement("bim-table-row") as TableRow<T>;
720715
header.table = this;
721716
header.isHeader = true;
722717
header.data = this._headerRowData;
@@ -725,7 +720,10 @@ export class Table extends LitElement {
725720
header.style.top = "0";
726721
header.style.zIndex = "5";
727722

728-
const children = document.createElement("bim-table-children");
723+
// @ts-ignore
724+
const children = document.createElement(
725+
"bim-table-children",
726+
) as TableChildren<T>;
729727
children.table = this;
730728
children.data = this.value;
731729
children.style.gridArea = "Body";

packages/core/src/components/Table/src/TableCell.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { LitElement, css, html } from "lit";
22
import { property } from "lit/decorators.js";
3-
import { TableRowData } from ".";
3+
import { TableRowData } from "./types";
44

5-
export class TableCell extends LitElement {
5+
export class TableCell<T extends TableRowData> extends LitElement {
66
/**
77
* CSS styles for the component.
88
*/
@@ -40,11 +40,11 @@ export class TableCell extends LitElement {
4040
`;
4141

4242
@property({ type: String, reflect: true })
43-
column = "";
43+
column: keyof T = "";
4444

4545
columnIndex = 0;
4646

47-
rowData: TableRowData = {};
47+
rowData: Partial<T> = {};
4848

4949
get data() {
5050
if (this.column) return this.rowData[this.column];

packages/core/src/components/Table/src/TableChildren.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { LitElement, css, html } from "lit";
22
import { property } from "lit/decorators.js";
33
import { Table } from "../index";
44
import { TableGroup } from "./TableGroup";
5-
import { TableGroupData } from "./types";
5+
import { TableGroupData, TableRowData } from "./types";
66

7-
export class TableChildren extends LitElement {
7+
export class TableChildren<T extends TableRowData> extends LitElement {
88
/**
99
* CSS styles for the component.
1010
*/
@@ -25,12 +25,12 @@ export class TableChildren extends LitElement {
2525
}
2626
`;
2727

28-
private _groups: TableGroup[] = [];
28+
private _groups: TableGroup<T>[] = [];
2929

3030
@property({ type: Array, attribute: false })
31-
data: TableGroupData[] = [];
31+
data: TableGroupData<T>[] = [];
3232

33-
table = this.closest<Table>("bim-table");
33+
table = this.closest<Table<T>>("bim-table");
3434

3535
toggleGroups(force?: boolean, recursive = false) {
3636
for (const group of this._groups) {
@@ -45,9 +45,10 @@ export class TableChildren extends LitElement {
4545
return html`
4646
<slot></slot>
4747
${this.data.map((group) => {
48+
// @ts-ignore
4849
const tableGroup = document.createElement(
4950
"bim-table-group",
50-
) as TableGroup;
51+
) as TableGroup<T>;
5152
this._groups.push(tableGroup);
5253
tableGroup.table = this.table;
5354
tableGroup.data = group;

0 commit comments

Comments
 (0)