@spectric/ui 0.0.19 → 0.0.21
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.
- package/dist/components/Banner.d.ts +1 -1
- package/dist/components/dialog/dialog.d.ts +1 -1
- package/dist/components/query_bar/QueryBar.d.ts +1 -1
- package/dist/components/table/cell.d.ts +1 -1
- package/dist/components/table/header.d.ts +2 -1
- package/dist/components/table/table.d.ts +10 -7
- package/dist/custom-elements.json +4 -4
- package/dist/index.es.js +876 -741
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +213 -138
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Banner.ts +46 -31
- package/src/components/dialog/dialog.ts +163 -135
- package/src/components/query_bar/QueryBar.ts +7 -2
- package/src/components/table/__tests__/table.spec.ts +143 -55
- package/src/components/table/cell.ts +185 -146
- package/src/components/table/header.ts +162 -152
- package/src/components/table/table.ts +362 -265
- package/src/components/table/virtualBody.ts +165 -115
- package/src/stories/table.stories.ts +10 -5
|
@@ -1,139 +1,189 @@
|
|
|
1
|
-
import { html } from
|
|
2
|
-
import { customElement, property
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
import {
|
|
4
|
+
HTMLElementTagWithEvents,
|
|
5
|
+
ReactElementWithPropsAndEvents,
|
|
6
|
+
} from "../types";
|
|
7
|
+
export const TableBodyElementTag = "spectric-table-virtual-body";
|
|
8
|
+
import "./cell";
|
|
9
|
+
import {
|
|
10
|
+
ColumnSettings,
|
|
11
|
+
SpectricTableElement,
|
|
12
|
+
TD_BorderAndPadding,
|
|
13
|
+
} from "./table";
|
|
14
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
15
|
+
import { DisposableElement } from "../../classes/DisposibleElement";
|
|
16
|
+
import "./virtualBody.css";
|
|
10
17
|
interface BodyProps<T> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
columns: ColumnSettings<T>[];
|
|
19
|
+
data: T[];
|
|
20
|
+
rowHeight: number;
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
|
|
17
23
|
/**
|
|
18
24
|
* Table Body Element
|
|
19
25
|
*/
|
|
20
26
|
@customElement(TableBodyElementTag)
|
|
21
|
-
export class TableVirtualBodyElement<T>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
export class TableVirtualBodyElement<T>
|
|
28
|
+
extends DisposableElement
|
|
29
|
+
implements BodyProps<T>
|
|
30
|
+
{
|
|
31
|
+
@property({ type: Array, attribute: false })
|
|
32
|
+
data: T[] = [];
|
|
33
|
+
@property({ type: Array, attribute: false })
|
|
34
|
+
columns: ColumnSettings<T>[] = [];
|
|
35
|
+
@property({ type: Number, attribute: false })
|
|
36
|
+
rowHeight: number = 30;
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
@property({ type: Number, state: true })
|
|
39
|
+
startIndex: number = 0;
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
@property({ type: Object, attribute: false })
|
|
42
|
+
table!: SpectricTableElement<T>;
|
|
43
|
+
columnsMeasured: boolean = false;
|
|
44
|
+
constructor() {
|
|
45
|
+
super();
|
|
46
|
+
this.addDisposableListener(
|
|
47
|
+
() => this.table.querySelector(".table-wrapper")!,
|
|
48
|
+
"scroll",
|
|
49
|
+
() => {
|
|
50
|
+
const scrollTop = this.table.querySelector(".table-wrapper")!.scrollTop;
|
|
51
|
+
requestAnimationFrame(() => {
|
|
52
|
+
this.startIndex = Math.floor(scrollTop / this.rowHeight);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
protected updated(): void {
|
|
58
|
+
if (this.columnsMeasured === false) {
|
|
59
|
+
let tr = this.querySelector("tr");
|
|
60
|
+
let cells = tr?.querySelectorAll("spectric-table-cell td");
|
|
61
|
+
if (tr && cells && cells.length) {
|
|
62
|
+
this.columns.forEach((col, index) => {
|
|
63
|
+
if (!col.width || col.width === 0) {
|
|
64
|
+
let rect = cells[index].getBoundingClientRect();
|
|
65
|
+
if (rect.width > 0) {
|
|
66
|
+
this.columnsMeasured = true;
|
|
67
|
+
col.width = rect.width - TD_BorderAndPadding;
|
|
58
68
|
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (this.columnsMeasured) {
|
|
72
|
+
this.columns = [...this.columns];
|
|
59
73
|
}
|
|
74
|
+
}
|
|
60
75
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
let spacerElementAfter = html`
|
|
85
|
-
<tr style="height:${afterHeight}px" class="virtual-scroll-spacer">
|
|
86
|
-
<td colspan="${this.columns.length}"></td></tr>`
|
|
87
|
-
return html`
|
|
88
|
-
<div style="height:${totalHeight}px;position: absolute;overflow-x: hidden;overflow-y: hidden;z-index:-1;width:1px;"></div>
|
|
89
|
-
<tbody>
|
|
90
|
-
${spacerElementBefore}
|
|
91
|
-
</tbody>
|
|
92
|
-
<div style="display:table-row-group;max-height:${visibleHeight}px; overflow:hidden;">
|
|
93
|
-
|
|
94
|
-
${repeat(this.data.slice(Math.max(startIndex, 0), endIndex), (row, index) => html`
|
|
95
|
-
<tr class="${(index + startIndex) % 2 === 0 ? "odd" : ""}">
|
|
96
|
-
${repeat(this.columns, (col) => {
|
|
97
|
-
return html`<spectric-table-cell .column=${col} .row=${row} .index=${index + startIndex} .columns=${this.columns}></spectric-table-cell>`
|
|
98
|
-
})}
|
|
99
|
-
</tr>`)
|
|
100
|
-
}
|
|
101
|
-
</div>
|
|
102
|
-
<tbody>
|
|
103
|
-
${afterHeight > 0 ? spacerElementAfter : null}
|
|
104
|
-
</tbody>
|
|
105
|
-
|
|
106
|
-
`
|
|
107
|
-
|
|
76
|
+
}
|
|
77
|
+
protected createRenderRoot(): HTMLElement | DocumentFragment {
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
protected render(): unknown {
|
|
81
|
+
let totalRows = this.data.length;
|
|
82
|
+
let buffer = 10; // Have a buffer of rows to prevent column jitter
|
|
83
|
+
let headerHeight = this.table.querySelector(
|
|
84
|
+
"spectric-table-header"
|
|
85
|
+
)!.clientHeight;
|
|
86
|
+
let viewport = this.table.querySelector(".table-wrapper")!;
|
|
87
|
+
let startIndex = Math.max(this.startIndex, 0);
|
|
88
|
+
const rowsThatFit = Math.ceil(
|
|
89
|
+
(viewport.clientHeight - headerHeight) / this.rowHeight
|
|
90
|
+
);
|
|
91
|
+
const endIndex = Math.min(startIndex + rowsThatFit + buffer, totalRows);
|
|
92
|
+
const visibleRows = endIndex - startIndex;
|
|
93
|
+
let totalHeight = totalRows * this.rowHeight;
|
|
94
|
+
let beforeHeight =
|
|
95
|
+
startIndex * this.rowHeight +
|
|
96
|
+
(viewport.scrollTop - startIndex * this.rowHeight);
|
|
97
|
+
if (endIndex === totalRows) {
|
|
98
|
+
beforeHeight = startIndex * this.rowHeight;
|
|
108
99
|
}
|
|
100
|
+
let visibleHeight = visibleRows * this.rowHeight;
|
|
101
|
+
let afterHeight = totalHeight - beforeHeight - visibleHeight;
|
|
102
|
+
let spacerElementBefore =
|
|
103
|
+
startIndex != 0
|
|
104
|
+
? html` <tr
|
|
105
|
+
style="height:${beforeHeight}px"
|
|
106
|
+
class="virtual-scroll-spacer"
|
|
107
|
+
>
|
|
108
|
+
<td colspan="${this.columns.length}"></td>
|
|
109
|
+
</tr>`
|
|
110
|
+
: null;
|
|
109
111
|
|
|
112
|
+
let spacerElementAfter = html` <tr
|
|
113
|
+
style="height:${afterHeight}px"
|
|
114
|
+
class="virtual-scroll-spacer"
|
|
115
|
+
>
|
|
116
|
+
<td colspan="${this.columns.length}"></td>
|
|
117
|
+
</tr>`;
|
|
118
|
+
return html`
|
|
119
|
+
<div
|
|
120
|
+
style="height:${totalHeight}px;position: absolute;overflow-x: hidden;overflow-y: hidden;z-index:-1;width:1px;"
|
|
121
|
+
></div>
|
|
122
|
+
<tbody>
|
|
123
|
+
${spacerElementBefore}
|
|
124
|
+
</tbody>
|
|
125
|
+
<div
|
|
126
|
+
style="display:table-row-group;max-height:${visibleHeight}px; overflow:hidden;"
|
|
127
|
+
>
|
|
128
|
+
${repeat(
|
|
129
|
+
this.data.slice(Math.max(startIndex, 0), endIndex),
|
|
130
|
+
(row, index) => html` <tr
|
|
131
|
+
class="${(index + startIndex) % 2 === 0 ? "odd" : ""}"
|
|
132
|
+
>
|
|
133
|
+
${repeat(this.columns, (col) => {
|
|
134
|
+
return html`<spectric-table-cell
|
|
135
|
+
.column=${col}
|
|
136
|
+
.row=${row}
|
|
137
|
+
.index=${index + startIndex}
|
|
138
|
+
.columns=${this.columns}
|
|
139
|
+
.table=${this.table}
|
|
140
|
+
></spectric-table-cell>`;
|
|
141
|
+
})}
|
|
142
|
+
</tr>`
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
<tbody>
|
|
146
|
+
${afterHeight > 0 ? spacerElementAfter : null}
|
|
147
|
+
</tbody>
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
110
150
|
}
|
|
111
151
|
|
|
112
152
|
interface TableBodyEvents {
|
|
113
|
-
|
|
153
|
+
//'sort': (event: CustomEvent<ColumnSettings<any>>) => void; //TODO sort events
|
|
114
154
|
}
|
|
115
155
|
|
|
116
156
|
declare global {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
157
|
+
interface HTMLElementTagNameMap {
|
|
158
|
+
[TableBodyElementTag]: HTMLElementTagWithEvents<
|
|
159
|
+
TableVirtualBodyElement<any>,
|
|
160
|
+
TableBodyEvents
|
|
161
|
+
>;
|
|
162
|
+
}
|
|
163
|
+
namespace JSX {
|
|
164
|
+
interface IntrinsicElements {
|
|
165
|
+
/**
|
|
166
|
+
* @see {@link DialogElement}
|
|
167
|
+
*/
|
|
168
|
+
[TableBodyElementTag]: ReactElementWithPropsAndEvents<
|
|
169
|
+
TableVirtualBodyElement<any>,
|
|
170
|
+
BodyProps<any>,
|
|
171
|
+
TableBodyEvents
|
|
172
|
+
>;
|
|
120
173
|
}
|
|
174
|
+
}
|
|
175
|
+
namespace React {
|
|
121
176
|
namespace JSX {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* @see {@link DialogElement}
|
|
134
|
-
*/
|
|
135
|
-
[TableBodyElementTag]: ReactElementWithPropsAndEvents<TableVirtualBodyElement<any>, BodyProps<any>, TableBodyEvents>
|
|
136
|
-
}
|
|
137
|
-
}
|
|
177
|
+
interface IntrinsicElements {
|
|
178
|
+
/**
|
|
179
|
+
* @see {@link DialogElement}
|
|
180
|
+
*/
|
|
181
|
+
[TableBodyElementTag]: ReactElementWithPropsAndEvents<
|
|
182
|
+
TableVirtualBodyElement<any>,
|
|
183
|
+
BodyProps<any>,
|
|
184
|
+
TableBodyEvents
|
|
185
|
+
>;
|
|
186
|
+
}
|
|
138
187
|
}
|
|
188
|
+
}
|
|
139
189
|
}
|
|
@@ -52,7 +52,7 @@ const meta = {
|
|
|
52
52
|
size: "xsmall",
|
|
53
53
|
totalItems: data.length,
|
|
54
54
|
},
|
|
55
|
-
columns: columns,
|
|
55
|
+
columns: JSON.parse(JSON.stringify(columns)),
|
|
56
56
|
select: TableSelectOptions.none
|
|
57
57
|
},
|
|
58
58
|
} satisfies Meta<Props<typeof tabledata[0]>>;
|
|
@@ -68,26 +68,30 @@ export const Basic: Story = {
|
|
|
68
68
|
pageSize: 3,
|
|
69
69
|
size: "xsmall",
|
|
70
70
|
totalItems: data.length,
|
|
71
|
-
}
|
|
71
|
+
},
|
|
72
|
+
columns: JSON.parse(JSON.stringify(columns)),
|
|
72
73
|
},
|
|
73
74
|
};
|
|
74
75
|
|
|
75
76
|
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
|
76
77
|
export const NoPagination: Story = {
|
|
77
78
|
args: {
|
|
78
|
-
pagination: undefined
|
|
79
|
+
pagination: undefined,
|
|
80
|
+
columns: JSON.parse(JSON.stringify(columns)),
|
|
79
81
|
},
|
|
80
82
|
};
|
|
81
83
|
|
|
82
84
|
export const MiltiSelect: Story = {
|
|
83
85
|
args: {
|
|
84
|
-
select: "multi"
|
|
86
|
+
select: "multi",
|
|
87
|
+
columns: JSON.parse(JSON.stringify(columns)),
|
|
85
88
|
},
|
|
86
89
|
};
|
|
87
90
|
|
|
88
91
|
export const SingleSelect: Story = {
|
|
89
92
|
args: {
|
|
90
|
-
select: "single"
|
|
93
|
+
select: "single",
|
|
94
|
+
columns: JSON.parse(JSON.stringify(columns)),
|
|
91
95
|
},
|
|
92
96
|
};
|
|
93
97
|
|
|
@@ -98,6 +102,7 @@ export const SingleSelect: Story = {
|
|
|
98
102
|
export const MultiColumnSort: Story = {
|
|
99
103
|
args: {
|
|
100
104
|
sort: "multi",
|
|
105
|
+
columns: JSON.parse(JSON.stringify(columns)),
|
|
101
106
|
pagination: { pageSize: 20, page: 1 }
|
|
102
107
|
},
|
|
103
108
|
};
|