@fluid-topics/ft-table 1.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,44 @@
1
+ import { nothing, PropertyValues, TemplateResult } from "lit";
2
+ import "@fluid-topics/ft-select";
3
+ import "@fluid-topics/ft-text-field";
4
+ import "@fluid-topics/ft-button";
5
+ import "@fluid-topics/ft-typography";
6
+ import { FtdsTableProperties, Sort } from "./ftds-table.properties";
7
+ import { ColumnConfiguration } from "./ftds-table.properties";
8
+ import { FtLitElement } from "@fluid-topics/ft-wc-utils";
9
+ export declare class FtdsTable<T extends Record<string, any>> extends FtLitElement implements FtdsTableProperties<T> {
10
+ static DEFAULT_RENDER: (v: any) => TemplateResult<1>;
11
+ static DEFAULT_COMPARATOR: (a: any, b: any) => number;
12
+ data: Array<T>;
13
+ columns: Array<ColumnConfiguration<T>>;
14
+ sort?: Sort;
15
+ stickyHeaders: boolean;
16
+ disableDataManipulation: boolean;
17
+ currentSort?: Sort;
18
+ ariaLabel: string | null;
19
+ clickableRows: boolean;
20
+ private activeRowIndex;
21
+ private rows?;
22
+ static styles: import("lit").CSSResult;
23
+ protected render(): TemplateResult<1>;
24
+ protected renderHeader(column: ColumnConfiguration<T>, index: number): TemplateResult<1>;
25
+ protected renderHeaderRow(): TemplateResult<1>;
26
+ protected renderRow(row: T, rowIndex: number): TemplateResult<1>;
27
+ protected renderTable(data: T[]): TemplateResult<1>;
28
+ protected renderSortButton(sortIcon: string, label: string, sort: () => void, index: number): TemplateResult<1>;
29
+ protected renderCell(column: ColumnConfiguration<T>, columnIndex: number, row: T, rowIndex: number): TemplateResult<1>;
30
+ protected renderColumnSort(column: ColumnConfiguration<T>, index: number): typeof nothing | TemplateResult<1>;
31
+ protected getSortIcon(isSorted: boolean | undefined): string;
32
+ private sortData;
33
+ protected update(props: PropertyValues): void;
34
+ private getOppositeSortOrder;
35
+ private getSortOrderText;
36
+ private dispatchChangeEvent;
37
+ protected columnPart(prefix: string, index: number): string;
38
+ protected getValue(column: ColumnConfiguration<T>, row: T): any;
39
+ private handleRowKeyDown;
40
+ private dispatchRowClickEvent;
41
+ private handleRowMouseDown;
42
+ private handleRowMouseUp;
43
+ focusRow(rowIndex: number): void;
44
+ }
@@ -0,0 +1,257 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html, nothing } from "lit";
8
+ import { repeat } from "lit/directives/repeat.js";
9
+ import "@fluid-topics/ft-select";
10
+ import "@fluid-topics/ft-text-field";
11
+ import "@fluid-topics/ft-button";
12
+ import "@fluid-topics/ft-typography";
13
+ import { FtdsTableChangeEvent } from "./ftds-table.properties";
14
+ import { property } from "lit/decorators.js";
15
+ import { RowClickEvent } from "./ftds-table.properties";
16
+ import { DesignSystemFamily } from "@fluid-topics/design-system-variables";
17
+ import { designSystemStyles } from "./ftds-table.styles";
18
+ import { classMap } from "lit/directives/class-map.js";
19
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
20
+ import { FtLitElement, jsonProperty } from "@fluid-topics/ft-wc-utils";
21
+ import { queryAll, state } from "lit/decorators.js";
22
+ class FtdsTable extends FtLitElement {
23
+ constructor() {
24
+ super(...arguments);
25
+ this.data = [];
26
+ this.columns = [];
27
+ this.stickyHeaders = false;
28
+ this.disableDataManipulation = false;
29
+ this.ariaLabel = null;
30
+ this.clickableRows = false;
31
+ this.activeRowIndex = -1;
32
+ }
33
+ render() {
34
+ let data = this.sortData(this.data);
35
+ return html `
36
+ <style>
37
+ .table {
38
+ grid-template-columns: ${this.columns.map(column => { var _a; return (_a = column.gridTemplateColumn) !== null && _a !== void 0 ? _a : "minmax(min-content, auto)"; }).join(" ")};
39
+ }
40
+ </style>
41
+ ${this.renderTable(data)}
42
+ `;
43
+ }
44
+ renderHeader(column, index) {
45
+ var _a, _b;
46
+ const classes = {
47
+ "header-cell": true,
48
+ "header-cell--sticky": this.stickyHeaders
49
+ };
50
+ let ariaSort;
51
+ if ((_a = column.sortable) !== null && _a !== void 0 ? _a : true) {
52
+ if (((_b = this.currentSort) === null || _b === void 0 ? void 0 : _b.column) === index) {
53
+ ariaSort = this.currentSort.order === "asc" ? "ascending" : "descending";
54
+ }
55
+ else {
56
+ ariaSort = "none";
57
+ }
58
+ }
59
+ return html `
60
+ <th class="${classMap(classes)}" role="columnheader" scope="col" aria-sort=${ariaSort !== null && ariaSort !== void 0 ? ariaSort : nothing}
61
+ part="${this.columnPart("header-cell", index)}">
62
+ <div class="column-title-container"
63
+ part="${this.columnPart("title-container", index)}">
64
+ <span class="column-title"
65
+ part="${this.columnPart("title", index)}">
66
+ <ft-typography variant="body-2-semibold">
67
+ ${column.title}
68
+ </ft-typography>
69
+ </span>
70
+ ${this.renderColumnSort(column, index)}
71
+ </div>
72
+ </th>
73
+ `;
74
+ }
75
+ renderHeaderRow() {
76
+ const classes = {
77
+ "header": true,
78
+ "header--sticky": this.stickyHeaders,
79
+ };
80
+ return html `
81
+ <thead>
82
+ <tr class=${classMap(classes)} role="row">
83
+ ${repeat(this.columns, (_, index) => "header-cell-" + index, (column, index) => this.renderHeader(column, index))}
84
+ </tr>
85
+ </thead>
86
+ `;
87
+ }
88
+ renderRow(row, rowIndex) {
89
+ const classes = {
90
+ "row": true,
91
+ "row--clickable": this.clickableRows,
92
+ "row--active": rowIndex === this.activeRowIndex
93
+ };
94
+ return html `
95
+ <tr class="${classMap(classes)}" role="row" tabindex=${this.clickableRows ? "0" : nothing}
96
+ part="table-row-${rowIndex}"
97
+ @keydown=${this.clickableRows ? (e) => this.handleRowKeyDown(e, row) : nothing}
98
+ @click=${this.clickableRows ? () => this.dispatchRowClickEvent(row) : nothing}
99
+ @mousedown=${() => this.handleRowMouseDown(rowIndex)}
100
+ @mouseup=${this.handleRowMouseUp}
101
+ @mouseleave=${this.handleRowMouseUp}
102
+ >
103
+ ${repeat(this.columns, (_, columnIndex) => "cell-" + columnIndex + "-" + rowIndex, (column, columnIndex) => this.renderCell(column, columnIndex, row, rowIndex))}
104
+ </tr>
105
+ `;
106
+ }
107
+ renderTable(data) {
108
+ var _a;
109
+ return html `
110
+ <table class="table"
111
+ role="table"
112
+ aria-label=${(_a = this.ariaLabel) !== null && _a !== void 0 ? _a : nothing}
113
+ >
114
+ ${this.renderHeaderRow()}
115
+ <tbody>
116
+ ${repeat(data, (_, rowIndex) => "row-" + rowIndex, (row, rowIndex) => this.renderRow(row, rowIndex))}
117
+ </tbody>
118
+ </table>
119
+ `;
120
+ }
121
+ renderSortButton(sortIcon, label, sort, index) {
122
+ return html `
123
+ <ftds-button
124
+ family=${DesignSystemFamily.neutral}
125
+ icon="${sortIcon}"
126
+ label="${label}"
127
+ @click=${sort}
128
+ part="${this.columnPart("sort-button", index)}"
129
+ ></ftds-button>
130
+ `;
131
+ }
132
+ renderCell(column, columnIndex, row, rowIndex) {
133
+ var _a;
134
+ const renderer = (_a = column.render) !== null && _a !== void 0 ? _a : FtdsTable.DEFAULT_RENDER;
135
+ const render = (value) => {
136
+ const rendered = renderer(value, rowIndex);
137
+ return typeof rendered === "string" ? unsafeHTML(rendered) : rendered;
138
+ };
139
+ return html `
140
+ <td class="cell" role="cell"
141
+ part="${this.columnPart("cell", columnIndex)} cell-row-${rowIndex} cell-column-${columnIndex}-row-${rowIndex}">
142
+ <ft-typography variant="caption-2-medium">${render(this.getValue(column, row))}</ft-typography>
143
+ </td>
144
+ `;
145
+ }
146
+ renderColumnSort(column, index) {
147
+ var _a, _b;
148
+ const isSorted = ((_a = this.currentSort) === null || _a === void 0 ? void 0 : _a.column) === index;
149
+ const sortIcon = this.getSortIcon(isSorted);
150
+ const sort = () => {
151
+ var _a, _b;
152
+ const defaultSort = (_a = column.defaultSortOrder) !== null && _a !== void 0 ? _a : "asc";
153
+ this.currentSort = {
154
+ column: index,
155
+ order: isSorted && ((_b = this.currentSort) === null || _b === void 0 ? void 0 : _b.order) === defaultSort ? this.getOppositeSortOrder(defaultSort) : defaultSort
156
+ };
157
+ this.dispatchChangeEvent();
158
+ };
159
+ const sortOrderText = this.getSortOrderText(isSorted, column);
160
+ const label = `Sort ${column.title} in ${sortOrderText} order`;
161
+ return ((_b = column.sortable) !== null && _b !== void 0 ? _b : true) ? this.renderSortButton(sortIcon, label, sort, index) : nothing;
162
+ }
163
+ getSortIcon(isSorted) {
164
+ return isSorted ? (this.currentSort.order === "asc" ? "sort_up" : "sort_down") : "sort_unsorted";
165
+ }
166
+ sortData(data) {
167
+ var _a;
168
+ if (this.currentSort && !this.disableDataManipulation) {
169
+ const column = this.columns[this.currentSort.column];
170
+ const ascComparator = (_a = column.comparator) !== null && _a !== void 0 ? _a : FtdsTable.DEFAULT_COMPARATOR;
171
+ const comparator = this.currentSort.order === "asc" ? ascComparator : ((a, b) => -ascComparator(a, b));
172
+ return [...data].sort((a, b) => comparator(this.getValue(column, a), this.getValue(column, b)));
173
+ }
174
+ return data;
175
+ }
176
+ update(props) {
177
+ super.update(props);
178
+ if (props.has("sort")) {
179
+ this.currentSort = this.sort;
180
+ }
181
+ }
182
+ getOppositeSortOrder(sortOrder) {
183
+ return sortOrder === "asc" ? "desc" : "asc";
184
+ }
185
+ getSortOrderText(isSorted, column) {
186
+ if (isSorted) {
187
+ return this.currentSort.order === "asc" ? "descending" : "ascending";
188
+ }
189
+ else {
190
+ return column.defaultSortOrder === "desc" ? "descending" : "ascending";
191
+ }
192
+ }
193
+ dispatchChangeEvent() {
194
+ this.dispatchEvent(new FtdsTableChangeEvent({
195
+ sort: this.currentSort
196
+ }));
197
+ }
198
+ columnPart(prefix, index) {
199
+ return `${prefix} ${prefix}-column-${index}`;
200
+ }
201
+ getValue(column, row) {
202
+ return typeof column.getter === "string" ? row[column.getter] : column.getter(row);
203
+ }
204
+ handleRowKeyDown(e, row) {
205
+ if (e.key === "Enter" || e.key === " ") {
206
+ this.dispatchRowClickEvent(row);
207
+ }
208
+ }
209
+ dispatchRowClickEvent(row) {
210
+ this.dispatchEvent(new RowClickEvent(row));
211
+ }
212
+ handleRowMouseDown(rowIndex) {
213
+ this.activeRowIndex = rowIndex;
214
+ }
215
+ handleRowMouseUp() {
216
+ this.activeRowIndex = -1;
217
+ }
218
+ focusRow(rowIndex) {
219
+ if (this.rows) {
220
+ this.rows[rowIndex].focus();
221
+ }
222
+ }
223
+ }
224
+ FtdsTable.DEFAULT_RENDER = (v) => html `${v}`;
225
+ FtdsTable.DEFAULT_COMPARATOR = (a, b) => a - b;
226
+ FtdsTable.styles = designSystemStyles;
227
+ __decorate([
228
+ jsonProperty([])
229
+ ], FtdsTable.prototype, "data", void 0);
230
+ __decorate([
231
+ jsonProperty([])
232
+ ], FtdsTable.prototype, "columns", void 0);
233
+ __decorate([
234
+ jsonProperty(undefined)
235
+ ], FtdsTable.prototype, "sort", void 0);
236
+ __decorate([
237
+ property({ type: Boolean })
238
+ ], FtdsTable.prototype, "stickyHeaders", void 0);
239
+ __decorate([
240
+ property({ type: Boolean })
241
+ ], FtdsTable.prototype, "disableDataManipulation", void 0);
242
+ __decorate([
243
+ state()
244
+ ], FtdsTable.prototype, "currentSort", void 0);
245
+ __decorate([
246
+ property({ attribute: "aria-label" })
247
+ ], FtdsTable.prototype, "ariaLabel", void 0);
248
+ __decorate([
249
+ property({ type: Boolean })
250
+ ], FtdsTable.prototype, "clickableRows", void 0);
251
+ __decorate([
252
+ property({ type: Number })
253
+ ], FtdsTable.prototype, "activeRowIndex", void 0);
254
+ __decorate([
255
+ queryAll("tbody .row")
256
+ ], FtdsTable.prototype, "rows", void 0);
257
+ export { FtdsTable };
@@ -0,0 +1,33 @@
1
+ import { TemplateResult } from "lit";
2
+ export type Getter<T> = (o: T) => any;
3
+ export interface ColumnConfiguration<T> {
4
+ title: string | TemplateResult;
5
+ getter: string | Getter<T>;
6
+ render?: (value: any, index: number) => string | TemplateResult;
7
+ sortable?: boolean;
8
+ defaultSortOrder?: "asc" | "desc";
9
+ comparator?: (a: any, b: any) => number;
10
+ gridTemplateColumn?: string;
11
+ }
12
+ export interface Sort {
13
+ column: number;
14
+ order: "asc" | "desc";
15
+ }
16
+ export declare class RowClickEvent<T> extends CustomEvent<T> {
17
+ constructor(data: T);
18
+ }
19
+ export interface FtdsTableChangeEventDetail {
20
+ sort?: Sort;
21
+ }
22
+ export declare class FtdsTableChangeEvent extends CustomEvent<FtdsTableChangeEventDetail> {
23
+ constructor(detail: FtdsTableChangeEventDetail);
24
+ }
25
+ export interface FtdsTableProperties<T extends Record<string, any>> {
26
+ data: Array<T>;
27
+ columns: Array<ColumnConfiguration<T>>;
28
+ sort?: Sort;
29
+ stickyHeaders: boolean;
30
+ disableDataManipulation: boolean;
31
+ clickableRows: boolean;
32
+ ariaLabel: string | null;
33
+ }
@@ -0,0 +1,10 @@
1
+ export class RowClickEvent extends CustomEvent {
2
+ constructor(data) {
3
+ super("row-click", { detail: data });
4
+ }
5
+ }
6
+ export class FtdsTableChangeEvent extends CustomEvent {
7
+ constructor(detail) {
8
+ super("change", { detail: detail });
9
+ }
10
+ }
@@ -0,0 +1,2 @@
1
+ export { table as FtdsTableCssVariables } from "@fluid-topics/design-system-variables";
2
+ export declare const designSystemStyles: import("lit").CSSResult;
@@ -0,0 +1,121 @@
1
+ import { css } from "lit";
2
+ import { table } from "@fluid-topics/design-system-variables";
3
+ export { table as FtdsTableCssVariables } from "@fluid-topics/design-system-variables";
4
+ //language=css
5
+ export const designSystemStyles = css `
6
+ :host {
7
+ display: block;
8
+ }
9
+
10
+ .table {
11
+ border-collapse: separate;
12
+ border-spacing: 0;
13
+ width: 100%;
14
+ }
15
+
16
+ .header {
17
+ color: ${table.headerRowColor};
18
+ }
19
+
20
+ .row {
21
+ color: ${table.dataRowColor};
22
+ position: relative;
23
+ background-color: ${table.dataRowBackgroundColor};
24
+ border-bottom: ${table.dataRowBorderWidth} solid ${table.dataRowBorderColor};
25
+ border-left: ${table.dataRowBorderWidth} solid ${table.dataRowBorderColor};
26
+ border-right: ${table.dataRowBorderWidth} solid ${table.dataRowBorderColor};
27
+ }
28
+
29
+ .row:hover {
30
+ background-color: ${table.dataRowDefaultHoverBackgroundColor};
31
+ }
32
+
33
+ .row.row--clickable:hover {
34
+ background-color: ${table.dataRowClickableHoverBackgroundColor};
35
+ }
36
+
37
+ .row.row--clickable:focus-within {
38
+ background-color: ${table.dataRowClickableFocusBackgroundColor};
39
+ outline: 1px solid ${table.dataRowClickableFocusBorderColor};
40
+ z-index: 15;
41
+ }
42
+
43
+ .row.row--clickable.row--active {
44
+ background-color: ${table.dataRowClickableActiveBackgroundColor};
45
+ }
46
+
47
+ .header {
48
+ background-color: ${table.headerRowBackgroundColor};
49
+ }
50
+
51
+ .header--sticky {
52
+ top: 0;
53
+ z-index: 2;
54
+ position: sticky;
55
+ }
56
+
57
+ .header-cell {
58
+ gap: ${table.headerCellHorizontalGap};
59
+ padding: ${table.headerCellVerticalPadding} ${table.headerCellHorizontalPadding};
60
+ vertical-align: baseline;
61
+ }
62
+
63
+ .header-cell--sticky {
64
+ top: 0;
65
+ z-index: 10;
66
+ position: sticky;
67
+
68
+ &:focus-within {
69
+ z-index: 11;
70
+ }
71
+
72
+ &:hover {
73
+ z-index: 12;
74
+ }
75
+ }
76
+
77
+ .header > .header-cell {
78
+ border-top: ${table.headerRowBorderWidth} solid ${table.headerRowBorderColor};
79
+ border-bottom: ${table.headerRowBorderWidth} solid ${table.headerRowBorderColor};
80
+ }
81
+
82
+ .header > .header-cell:first-of-type {
83
+ border-left: ${table.headerRowBorderWidth} solid ${table.headerRowBorderColor};
84
+ }
85
+
86
+ .header > .header-cell:last-of-type {
87
+ border-right: ${table.headerRowBorderWidth} solid ${table.headerRowBorderColor};
88
+ }
89
+
90
+ .column-title-container {
91
+ display: flex;
92
+ align-items: baseline;
93
+ }
94
+
95
+ .column-title {
96
+ white-space: normal;
97
+ margin: 0 .5rem 0 0;
98
+ height: 100%;
99
+ display: flex;
100
+ align-items: center;
101
+ text-align: start;
102
+ }
103
+
104
+ .cell {
105
+ padding: ${table.dataCellVerticalPadding} ${table.dataCellHorizontalPadding};
106
+ overflow-x: auto;
107
+ position: relative;
108
+ }
109
+
110
+ .row > .cell {
111
+ border-bottom: ${table.dataRowBorderWidth} solid ${table.dataRowBorderColor};
112
+ }
113
+
114
+ .row > .cell:first-of-type {
115
+ border-left: ${table.dataRowBorderWidth} solid ${table.dataRowBorderColor};
116
+ }
117
+
118
+ .row > .cell:last-of-type {
119
+ border-right: ${table.dataRowBorderWidth} solid ${table.dataRowBorderColor};
120
+ }
121
+ `;
@@ -0,0 +1,3 @@
1
+ export * from "./ftds-table.properties";
2
+ export * from "./ftds-table.styles";
3
+ export * from "./ftds-table";
package/build/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { customElement } from "@fluid-topics/ft-wc-utils";
2
+ import { FtdsTable } from "./ftds-table";
3
+ export * from "./ftds-table.properties";
4
+ export * from "./ftds-table.styles";
5
+ export * from "./ftds-table";
6
+ customElement("ftds-table")(FtdsTable);
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@fluid-topics/ft-table",
3
+ "version": "1.2.30",
4
+ "description": "A dynamic table",
5
+ "keywords": [
6
+ "Lit"
7
+ ],
8
+ "author": "Fluid Topics <devtopics@antidot.net>",
9
+ "license": "ISC",
10
+ "main": "build/index.js",
11
+ "web": "build/ft-filterable-table.min.js",
12
+ "typings": "build/index",
13
+ "files": [
14
+ "build/**/*.ts",
15
+ "build/**/*.js"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "ssh://git@scm.mrs.antidot.net:2222/fluidtopics/ft-web-components.git"
20
+ },
21
+ "scripts": {
22
+ "test": "echo \"Error: run tests from root\" && exit 1"
23
+ },
24
+ "dependencies": {
25
+ "@fluid-topics/design-system-variables": "0.1.91",
26
+ "@fluid-topics/ft-button": "1.2.30",
27
+ "@fluid-topics/ft-typography": "1.2.30",
28
+ "@fluid-topics/ft-wc-utils": "1.2.30",
29
+ "lit": "3.1.0"
30
+ },
31
+ "gitHead": "ac166f46dbdc65614893826ca64bbfc51e2e2b94"
32
+ }