@spectric/ui 0.0.15 → 0.0.17
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/classes/DisposibleElement.d.ts +1 -0
- package/dist/classes/index.d.ts +2 -0
- package/dist/components/color_picker/ColorPicker.d.ts +59 -0
- package/dist/components/color_picker/index.d.ts +0 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/input.d.ts +3 -1
- package/dist/components/table/body.d.ts +2 -2
- package/dist/components/table/cell.d.ts +5 -2
- package/dist/components/table/header.d.ts +9 -3
- package/dist/components/table/table.d.ts +18 -6
- package/dist/components/table/virtualBody.d.ts +2 -2
- package/dist/components/tooltip/popover.d.ts +85 -0
- package/dist/components/tooltip/tooltip.d.ts +14 -15
- package/dist/custom-elements.json +83 -8
- package/dist/index.d.ts +58 -0
- package/dist/index.es.js +2640 -2243
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +183 -132
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/package.json +6 -2
- package/src/classes/DisposibleElement.ts +3 -0
- package/src/classes/index.ts +2 -0
- package/src/components/color_picker/ColorPicker.css +4 -0
- package/src/components/color_picker/ColorPicker.ts +244 -0
- package/src/components/color_picker/index.ts +1 -0
- package/src/components/index.ts +3 -1
- package/src/components/input.css +38 -1
- package/src/components/input.ts +34 -7
- package/src/components/table/__tests__/table.spec.ts +91 -0
- package/src/components/table/body.ts +2 -2
- package/src/components/table/cell.ts +34 -8
- package/src/components/table/header.css +54 -0
- package/src/components/table/header.ts +144 -49
- package/src/components/table/table.css +20 -33
- package/src/components/table/table.ts +50 -16
- package/src/components/table/virtualBody.ts +2 -2
- package/src/components/tooltip/popover.ts +221 -0
- package/src/components/tooltip/tooltip.css +21 -16
- package/src/components/tooltip/tooltip.ts +17 -124
- package/src/index.ts +8 -1
- package/src/stories/fixtures/ExampleContent.ts +3 -3
- package/src/stories/table.stories.ts +5 -5
- package/src/utils/index.ts +3 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { html, LitElement, PropertyValues } from 'lit';
|
|
2
|
-
import { customElement, property, query, } from 'lit/decorators.js';
|
|
2
|
+
import { customElement, property, query, state, } from 'lit/decorators.js';
|
|
3
3
|
import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from '../types';
|
|
4
4
|
export const TableCellElementTag = "spectric-table-cell"
|
|
5
|
-
import { ColumnSettings,
|
|
5
|
+
import { ColumnSettings, DomRenderable, SpectricTableElement } from './table';
|
|
6
6
|
import { cache } from 'lit/directives/cache.js';
|
|
7
7
|
import { StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
|
8
8
|
|
|
@@ -31,8 +31,10 @@ export class TableCellElement<T> extends LitElement implements CellProps<T> {
|
|
|
31
31
|
@property({ type: Object, attribute: false })
|
|
32
32
|
column!: ColumnSettings<T>;
|
|
33
33
|
columns!: ColumnSettings<T>[];
|
|
34
|
-
table!:
|
|
34
|
+
table!: SpectricTableElement<T>
|
|
35
35
|
|
|
36
|
+
@state()
|
|
37
|
+
overflow: DomRenderable = ""
|
|
36
38
|
@query("td")
|
|
37
39
|
td!: HTMLTableCellElement
|
|
38
40
|
styleRules: StyleInfo = { whiteSpace: "", width: "" };
|
|
@@ -74,7 +76,27 @@ export class TableCellElement<T> extends LitElement implements CellProps<T> {
|
|
|
74
76
|
column: this.column
|
|
75
77
|
})
|
|
76
78
|
}
|
|
77
|
-
|
|
79
|
+
_displayTooltip = () => {
|
|
80
|
+
let div = this.querySelector("div.cell-contents")
|
|
81
|
+
let span = this.querySelector("span")
|
|
82
|
+
if (div && span) {
|
|
83
|
+
let spanBounds = span.getBoundingClientRect()
|
|
84
|
+
let divBounds = div.getBoundingClientRect()
|
|
85
|
+
if ((spanBounds.width * spanBounds.height) > (divBounds.width * divBounds.height)) {
|
|
86
|
+
console.log("We need to show a tooltip witht he content because we are overflowing")
|
|
87
|
+
this.overflow = this.getRenderedValue()
|
|
88
|
+
this.updateComplete.then(() => {
|
|
89
|
+
let tooltip = this.querySelector("spectric-tooltip")
|
|
90
|
+
if (tooltip) {
|
|
91
|
+
tooltip.showToolTip()
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
} else {
|
|
95
|
+
this.overflow = ""
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
getRenderedValue() {
|
|
78
100
|
let rendered
|
|
79
101
|
if (this.column.render) {
|
|
80
102
|
rendered = this.column.render(this.row, this.index, this.table)
|
|
@@ -83,6 +105,10 @@ export class TableCellElement<T> extends LitElement implements CellProps<T> {
|
|
|
83
105
|
} else {
|
|
84
106
|
rendered = html`error`
|
|
85
107
|
}
|
|
108
|
+
return rendered
|
|
109
|
+
}
|
|
110
|
+
protected render(): unknown {
|
|
111
|
+
let rendered = this.getRenderedValue()
|
|
86
112
|
let classes = ["cell-contents"]
|
|
87
113
|
if (this.column.filterable) {
|
|
88
114
|
classes.push("filterable")
|
|
@@ -95,13 +121,13 @@ export class TableCellElement<T> extends LitElement implements CellProps<T> {
|
|
|
95
121
|
: null
|
|
96
122
|
)
|
|
97
123
|
this.styleRules = {
|
|
98
|
-
width: this.column.width ? this.column.width + "px" :
|
|
99
|
-
whiteSpace: this.column.whiteSpace ||
|
|
124
|
+
width: this.column.width ? this.column.width + "px" : null,
|
|
125
|
+
whiteSpace: this.column.whiteSpace || null
|
|
100
126
|
}
|
|
101
127
|
return html`
|
|
102
|
-
<td style=${styleMap(this.styleRules)}>
|
|
128
|
+
<td style=${styleMap(this.styleRules)} @mouseenter=${this._displayTooltip}>
|
|
103
129
|
${filterButtons}
|
|
104
|
-
<div class=${classes.join(" ")}>${rendered}</div>
|
|
130
|
+
<div class=${classes.join(" ")}>${this.overflow ? html`<spectric-tooltip .text=${this.overflow}></spectric-tooltip>` : null}<span>${rendered}</span></div>
|
|
105
131
|
</td>
|
|
106
132
|
`
|
|
107
133
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
spectric-table-header{
|
|
3
|
+
display: table-header-group;
|
|
4
|
+
font-weight: bold;
|
|
5
|
+
position: sticky;
|
|
6
|
+
top: 0px;
|
|
7
|
+
left: 0px;
|
|
8
|
+
z-index: 1;
|
|
9
|
+
background: var(--spectric-background, #ffffff);
|
|
10
|
+
}
|
|
11
|
+
spectric-table-header td {
|
|
12
|
+
vertical-align: middle;
|
|
13
|
+
position: relative;
|
|
14
|
+
}
|
|
15
|
+
spectric-table-header .header-contents {
|
|
16
|
+
position: relative;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
spectric-table-header .header-contents .sort-direction {
|
|
20
|
+
position: absolute;
|
|
21
|
+
right: 2px;
|
|
22
|
+
top: calc(50% - 8px)
|
|
23
|
+
}
|
|
24
|
+
spectric-table-header .header-contents.sortable {
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
padding-right:15px
|
|
27
|
+
}
|
|
28
|
+
spectric-table-header .header-contents.resizing {
|
|
29
|
+
user-select: none;
|
|
30
|
+
}
|
|
31
|
+
spectric-table-header .header-contents.sortable:hover .sort-direction.none::before
|
|
32
|
+
{
|
|
33
|
+
content: "\2B81";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
spectric-table-header td .header-resize-handle{
|
|
38
|
+
width: 2px;
|
|
39
|
+
position: absolute;
|
|
40
|
+
right: -1px;
|
|
41
|
+
top: 1px;
|
|
42
|
+
visibility: hidden;
|
|
43
|
+
background-color: var(--spectric-primary, #1ea7fd);
|
|
44
|
+
height: 100%;
|
|
45
|
+
cursor: ew-resize;
|
|
46
|
+
z-index: 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
spectric-table-header td:hover .header-resize-handle{
|
|
50
|
+
visibility: visible;
|
|
51
|
+
}
|
|
52
|
+
spectric-table-header td:hover:has(*.header-resize-handle) {
|
|
53
|
+
border-bottom: 1px solid var(--spectric-primary, #1ea7fd);
|
|
54
|
+
}
|
|
@@ -1,86 +1,177 @@
|
|
|
1
|
-
import { html
|
|
1
|
+
import { html } from "lit";
|
|
2
2
|
import "../pagination";
|
|
3
|
-
import { customElement, property, } from
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
4
|
+
import {
|
|
5
|
+
HTMLElementTagWithEvents,
|
|
6
|
+
ReactElementWithPropsAndEvents,
|
|
7
|
+
} from "../types";
|
|
8
|
+
import "./table.css";
|
|
9
|
+
import "./header.css";
|
|
10
|
+
export const TableHeaderElementTag = "spectric-table-header";
|
|
11
|
+
import { ColumnSettings, TableSortDirection } from "./table";
|
|
12
|
+
import { DisposableElement } from "../../classes";
|
|
9
13
|
|
|
10
14
|
interface HeaderProps<T> {
|
|
11
|
-
columns: ColumnSettings<T>[]
|
|
15
|
+
columns: ColumnSettings<T>[];
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* Pagination Element
|
|
16
20
|
*/
|
|
17
21
|
@customElement(TableHeaderElementTag)
|
|
18
|
-
export class TableHeaderElement<T>
|
|
22
|
+
export class TableHeaderElement<T>
|
|
23
|
+
extends DisposableElement
|
|
24
|
+
implements HeaderProps<T> {
|
|
19
25
|
@property({ type: Array, attribute: false })
|
|
20
26
|
columns: ColumnSettings<T>[] = [];
|
|
21
|
-
|
|
27
|
+
@state()
|
|
28
|
+
private _resizeStart?: { event: MouseEvent; column: ColumnSettings<T> };
|
|
29
|
+
constructor() {
|
|
30
|
+
super();
|
|
31
|
+
this.addDisposableListener(
|
|
32
|
+
() => document.body,
|
|
33
|
+
"mouseup",
|
|
34
|
+
this._handleResizeEnd
|
|
35
|
+
);
|
|
36
|
+
}
|
|
22
37
|
protected createRenderRoot(): HTMLElement | DocumentFragment {
|
|
23
|
-
return this
|
|
38
|
+
return this;
|
|
24
39
|
}
|
|
25
40
|
_handleSortChange = (column: ColumnSettings<T>) => {
|
|
26
|
-
|
|
41
|
+
if (this._resizeStart) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
column = JSON.parse(JSON.stringify(column)); // Clone the column to not mutate the original object
|
|
27
45
|
if (!column.sortable) {
|
|
28
|
-
return
|
|
46
|
+
return;
|
|
29
47
|
}
|
|
30
|
-
if (
|
|
31
|
-
column.sortDirection
|
|
48
|
+
if (
|
|
49
|
+
column.sortDirection === TableSortDirection.none ||
|
|
50
|
+
column.sortDirection === undefined
|
|
51
|
+
) {
|
|
52
|
+
column.sortDirection = TableSortDirection.ascending;
|
|
32
53
|
} else if (column.sortDirection === TableSortDirection.ascending) {
|
|
33
|
-
column.sortDirection = TableSortDirection.descending
|
|
54
|
+
column.sortDirection = TableSortDirection.descending;
|
|
34
55
|
} else if (column.sortDirection === TableSortDirection.descending) {
|
|
35
|
-
column.sortDirection = TableSortDirection.none
|
|
56
|
+
column.sortDirection = TableSortDirection.none;
|
|
36
57
|
}
|
|
37
|
-
this.dispatchEvent(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
this.dispatchEvent(
|
|
59
|
+
new CustomEvent<ColumnSettings<T>>("sortChange", {
|
|
60
|
+
composed: true,
|
|
61
|
+
bubbles: true,
|
|
62
|
+
detail: column,
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
_handleResizeStart = (event: MouseEvent, column: ColumnSettings<T>) => {
|
|
67
|
+
this._resizeStart = { event, column };
|
|
68
|
+
};
|
|
69
|
+
_handleResizeEnd = (e: MouseEvent) => {
|
|
70
|
+
if (!this._resizeStart) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let delta = e.pageX - this._resizeStart?.event.pageX;
|
|
74
|
+
let before = this._resizeStart.column.width;
|
|
75
|
+
console.log(delta);
|
|
76
|
+
if (this._resizeStart.column.width) {
|
|
77
|
+
this._resizeStart.column.width = this._resizeStart.column.width + delta;
|
|
78
|
+
} else {
|
|
79
|
+
let cell = (this._resizeStart.event.target as HTMLDivElement).closest(
|
|
80
|
+
"td"
|
|
81
|
+
) as HTMLTableCellElement;
|
|
82
|
+
this._resizeStart.column.width =
|
|
83
|
+
cell.getBoundingClientRect().width + delta;
|
|
84
|
+
}
|
|
85
|
+
console.log(before, this._resizeStart.column.width);
|
|
86
|
+
this.dispatchEvent(
|
|
87
|
+
new CustomEvent("columnResize", { detail: this._resizeStart.column })
|
|
88
|
+
);
|
|
89
|
+
//We wrap this to ensure any sort events get fired before we are done FIXME?
|
|
90
|
+
requestAnimationFrame(() => {
|
|
91
|
+
this._resizeStart = undefined;
|
|
92
|
+
//Clear any text that got selected during column resize
|
|
93
|
+
if (window.getSelection) {
|
|
94
|
+
let selection = window.getSelection();
|
|
95
|
+
if (selection) {
|
|
96
|
+
selection.removeAllRanges();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
};
|
|
43
101
|
protected render(): unknown {
|
|
44
102
|
return html`
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
103
|
+
<tr>
|
|
104
|
+
${this.columns.map(column => this.renderCell(column))}
|
|
105
|
+
</tr>
|
|
106
|
+
`;
|
|
107
|
+
}
|
|
108
|
+
private renderCell(column: ColumnSettings<T>) {
|
|
109
|
+
let classes = ["header-contents"];
|
|
110
|
+
if (column.filterable) {
|
|
111
|
+
//classes.push("filterable")
|
|
112
|
+
}
|
|
113
|
+
if (column.sortable) {
|
|
114
|
+
classes.push("sortable");
|
|
115
|
+
}
|
|
116
|
+
if (this._resizeStart) {
|
|
117
|
+
classes.push("resizing");
|
|
118
|
+
}
|
|
119
|
+
let columnWidth = column.width
|
|
120
|
+
? `min-width:${column.width}px;max-width:${column.width}px;`
|
|
121
|
+
: "";
|
|
122
|
+
let sortDirection =
|
|
123
|
+
column.sortDirection === TableSortDirection.ascending
|
|
124
|
+
? `🠉`
|
|
125
|
+
: column.sortDirection == TableSortDirection.descending
|
|
126
|
+
? `🠋`
|
|
127
|
+
: ``;
|
|
128
|
+
let sortClass = column.sortDirection || TableSortDirection.none;
|
|
129
|
+
let resizable =
|
|
130
|
+
column.allowResize || column.allowResize === undefined;
|
|
131
|
+
let resizeHandle = resizable
|
|
132
|
+
? html`<div
|
|
133
|
+
class="header-resize-handle"
|
|
134
|
+
@mousedown=${(e: MouseEvent) => {
|
|
135
|
+
this._handleResizeStart(e, column);
|
|
136
|
+
}}
|
|
137
|
+
@mouseup=${this._handleResizeEnd}
|
|
138
|
+
></div>`
|
|
139
|
+
: null
|
|
140
|
+
return html` <td
|
|
141
|
+
@click=${() => this._handleSortChange(column)}
|
|
142
|
+
style="${columnWidth}"
|
|
143
|
+
>
|
|
144
|
+
${resizeHandle}
|
|
145
|
+
<div class=${classes.join(" ")}>
|
|
146
|
+
${column.title || column.key}
|
|
147
|
+
<span class="sort-direction ${sortClass}">${sortDirection}</span>
|
|
148
|
+
</div>
|
|
149
|
+
</td>`;
|
|
66
150
|
}
|
|
67
151
|
}
|
|
68
152
|
|
|
69
153
|
interface TableHeaderEvents {
|
|
70
|
-
|
|
154
|
+
sortChange: (event: CustomEvent<ColumnSettings<any>>) => void; //TODO sort events
|
|
155
|
+
columnResize: (event: CustomEvent<ColumnSettings<any>>) => void;
|
|
71
156
|
}
|
|
72
157
|
|
|
73
158
|
declare global {
|
|
74
159
|
interface HTMLElementTagNameMap {
|
|
75
|
-
[TableHeaderElementTag]: HTMLElementTagWithEvents<
|
|
76
|
-
|
|
160
|
+
[TableHeaderElementTag]: HTMLElementTagWithEvents<
|
|
161
|
+
TableHeaderElement<any>,
|
|
162
|
+
TableHeaderEvents
|
|
163
|
+
>;
|
|
77
164
|
}
|
|
78
165
|
namespace JSX {
|
|
79
166
|
interface IntrinsicElements {
|
|
80
167
|
/**
|
|
81
168
|
* @see {@link DialogElement}
|
|
82
169
|
*/
|
|
83
|
-
[TableHeaderElementTag]: ReactElementWithPropsAndEvents<
|
|
170
|
+
[TableHeaderElementTag]: ReactElementWithPropsAndEvents<
|
|
171
|
+
TableHeaderElement<any>,
|
|
172
|
+
HeaderProps<any>,
|
|
173
|
+
TableHeaderEvents
|
|
174
|
+
>;
|
|
84
175
|
}
|
|
85
176
|
}
|
|
86
177
|
namespace React {
|
|
@@ -89,7 +180,11 @@ declare global {
|
|
|
89
180
|
/**
|
|
90
181
|
* @see {@link DialogElement}
|
|
91
182
|
*/
|
|
92
|
-
[TableHeaderElementTag]: ReactElementWithPropsAndEvents<
|
|
183
|
+
[TableHeaderElementTag]: ReactElementWithPropsAndEvents<
|
|
184
|
+
TableHeaderElement<any>,
|
|
185
|
+
HeaderProps<any>,
|
|
186
|
+
TableHeaderEvents
|
|
187
|
+
>;
|
|
93
188
|
}
|
|
94
189
|
}
|
|
95
190
|
}
|
|
@@ -2,6 +2,7 @@ spectric-table{
|
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
4
|
overflow: hidden;
|
|
5
|
+
line-height: 1;
|
|
5
6
|
}
|
|
6
7
|
spectric-table .table-wrapper{
|
|
7
8
|
overflow: auto;
|
|
@@ -15,44 +16,19 @@ spectric-table tr{
|
|
|
15
16
|
spectric-table tr.odd{
|
|
16
17
|
background-color: color-mix(in srgb, var(--spectric-primary, #1ea7fd), transparent 90%);
|
|
17
18
|
}
|
|
18
|
-
spectric-table tr:hover{
|
|
19
|
+
spectric-table spectric-table-virtual-body tr:hover{
|
|
19
20
|
background-color: color-mix(in srgb, var(--spectric-primary, #1ea7fd), transparent 70%)
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
display: table-header-group;
|
|
23
|
-
font-weight: bold;
|
|
24
|
-
position: sticky;
|
|
25
|
-
top: 0px;
|
|
26
|
-
left: 0px;
|
|
27
|
-
z-index: 1;
|
|
28
|
-
background: var(--spectric-background, #ffffff);
|
|
29
|
-
}
|
|
30
|
-
spectric-table-header td {
|
|
31
|
-
vertical-align: middle;
|
|
32
|
-
}
|
|
22
|
+
|
|
33
23
|
spectric-table tr {
|
|
34
|
-
|
|
24
|
+
height: var(--rowHeight);
|
|
35
25
|
}
|
|
36
26
|
spectric-table td{
|
|
37
27
|
height: var(--rowHeight);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
position: relative;
|
|
28
|
+
padding:1px;
|
|
29
|
+
border: 1px solid transparent;
|
|
41
30
|
}
|
|
42
31
|
|
|
43
|
-
spectric-table-header .header-contents .sort-direction {
|
|
44
|
-
position: absolute;
|
|
45
|
-
right: 0;
|
|
46
|
-
top: calc(50% - 8px)
|
|
47
|
-
}
|
|
48
|
-
spectric-table-header .header-contents.sortable {
|
|
49
|
-
cursor: pointer;
|
|
50
|
-
padding-right:15px
|
|
51
|
-
}
|
|
52
|
-
spectric-table-header .header-contents.sortable:hover .sort-direction.none::before
|
|
53
|
-
{
|
|
54
|
-
content: "\2B81";
|
|
55
|
-
}
|
|
56
32
|
spectric-table div[role="table"]{
|
|
57
33
|
display: table;
|
|
58
34
|
min-width: 100%;
|
|
@@ -69,9 +45,7 @@ spectric-table-cell td{
|
|
|
69
45
|
spectric-table td:hover:has(.filterable) {
|
|
70
46
|
border: 1px solid var(--spectric-primary, #1ea7fd);
|
|
71
47
|
}
|
|
72
|
-
|
|
73
|
-
border: 1px solid transparent;
|
|
74
|
-
}
|
|
48
|
+
|
|
75
49
|
spectric-table-cell .table-cell-actions{
|
|
76
50
|
position: absolute;
|
|
77
51
|
display: flex;
|
|
@@ -79,6 +53,7 @@ spectric-table-cell .table-cell-actions{
|
|
|
79
53
|
flex-direction: row-reverse;
|
|
80
54
|
visibility: hidden;
|
|
81
55
|
top: -10px;
|
|
56
|
+
z-index: 1;
|
|
82
57
|
}
|
|
83
58
|
spectric-table-cell td:hover .table-cell-actions{
|
|
84
59
|
visibility: unset;
|
|
@@ -101,4 +76,16 @@ spectric-input.table-checkbox-single[checked] spectric-button::before {
|
|
|
101
76
|
border-radius: 50%;
|
|
102
77
|
z-index: 1;
|
|
103
78
|
box-shadow: 0px 0px 0px 4px var(--input-color);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
spectric-table .cell-contents{
|
|
83
|
+
display: -webkit-box;
|
|
84
|
+
-webkit-box-orient: vertical;
|
|
85
|
+
-webkit-line-clamp: var(--lineClamp,1);
|
|
86
|
+
-webkit-box-pack: center;
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
text-overflow: ellipsis;
|
|
89
|
+
font-size: var(--fontSize);
|
|
90
|
+
height: calc(var(--lineClamp) * var(--fontSize));
|
|
104
91
|
}
|
|
@@ -34,6 +34,10 @@ export enum TableSortDirection {
|
|
|
34
34
|
export type TableSortDirectionTypes = `${TableSortDirection}`
|
|
35
35
|
export type ColumnSettings<T> = {
|
|
36
36
|
width?: number
|
|
37
|
+
/**
|
|
38
|
+
* Enabled/disables resizing by dragging column header Default true
|
|
39
|
+
*/
|
|
40
|
+
allowResize?: boolean
|
|
37
41
|
whiteSpace?: "nowrap";
|
|
38
42
|
hidden?: boolean
|
|
39
43
|
sortable?: boolean
|
|
@@ -47,7 +51,7 @@ export type ColumnSettings<T> = {
|
|
|
47
51
|
/**
|
|
48
52
|
* Render function to render a table cell for displaying custom html
|
|
49
53
|
*/
|
|
50
|
-
render?: (row: T, index: number, table:
|
|
54
|
+
render?: (row: T, index: number, table: SpectricTableElement<T>) => DomRenderable
|
|
51
55
|
/**
|
|
52
56
|
* Custom comparator function for sorting
|
|
53
57
|
*/
|
|
@@ -57,6 +61,7 @@ export type TableSelectOptionsTypes = `${TableSelectOptions}`
|
|
|
57
61
|
export interface TableDataOptions<T> {
|
|
58
62
|
pagination?: PaginationProps
|
|
59
63
|
columns: ColumnSettings<T>[]
|
|
64
|
+
sortOrder?: string[]
|
|
60
65
|
}
|
|
61
66
|
interface TableProps<T> extends TableDataOptions<T> {
|
|
62
67
|
data: T[]
|
|
@@ -65,7 +70,7 @@ interface TableProps<T> extends TableDataOptions<T> {
|
|
|
65
70
|
rowHeight?: number
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
type DomEvent<T> = Event & {
|
|
73
|
+
export type DomEvent<T> = Event & {
|
|
69
74
|
target: T
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
@@ -74,7 +79,7 @@ type DomEvent<T> = Event & {
|
|
|
74
79
|
*
|
|
75
80
|
*/
|
|
76
81
|
@customElement(TableElementTag)
|
|
77
|
-
export class
|
|
82
|
+
export class SpectricTableElement<T = any> extends LitElement implements TableProps<T> {
|
|
78
83
|
@property({ type: Array, attribute: false })
|
|
79
84
|
data: T[] = [];
|
|
80
85
|
@property({ type: Object, attribute: false })
|
|
@@ -86,15 +91,23 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
86
91
|
@property({ type: String, reflect: true })
|
|
87
92
|
sort: TableSortOptionTypes = TableSortOption.single;
|
|
88
93
|
|
|
94
|
+
@property({ type: Array, reflect: false })
|
|
95
|
+
sortOrder: string[] = [];
|
|
89
96
|
/**
|
|
90
97
|
* Needed for virtualization
|
|
91
98
|
*/
|
|
92
99
|
@property({ type: Number, reflect: true })
|
|
93
100
|
rowHeight: number = 25;
|
|
101
|
+
/**
|
|
102
|
+
* Needed for virtualization
|
|
103
|
+
*/
|
|
104
|
+
@property({ type: Number, reflect: true })
|
|
105
|
+
fontSize: number = 16;
|
|
94
106
|
|
|
95
107
|
static getDefaultDataSorterAndPaginatior<T>(data: T[]) {
|
|
96
108
|
return (props: TableDataOptions<T>) => {
|
|
97
|
-
let sorts = props.columns.filter(column => column.sortable && column.sortDirection && column.sortDirection !== TableSortDirection.none)
|
|
109
|
+
//let sorts = props.columns.filter(column => column.sortable && column.sortDirection && column.sortDirection !== TableSortDirection.none)
|
|
110
|
+
let sorts = (props.sortOrder || []).map(key => props.columns.find(col => col.key === key) as ColumnSettings<T>)
|
|
98
111
|
let rows = [...data] // Need to copy the array to prevent mutating the ordering of the original data
|
|
99
112
|
if (sorts.length) {
|
|
100
113
|
let sortChain = createSortChain(sorts)
|
|
@@ -136,7 +149,7 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
136
149
|
private _handleSortChange = (e: CustomEvent<ColumnSettings<T>>) => {
|
|
137
150
|
let columnSetting = e.detail
|
|
138
151
|
let column = this.columns.find(col => col.key == columnSetting.key)
|
|
139
|
-
if (!column) {
|
|
152
|
+
if (!column || column.key === undefined) {
|
|
140
153
|
console.warn("Unable to find sort column")
|
|
141
154
|
return
|
|
142
155
|
}
|
|
@@ -145,15 +158,34 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
145
158
|
this.columns.forEach(col => {
|
|
146
159
|
col.sortDirection = TableSortDirection.none
|
|
147
160
|
})
|
|
161
|
+
this.sortOrder = []
|
|
148
162
|
}
|
|
149
163
|
column.sortDirection = columnSetting.sortDirection;
|
|
164
|
+
if (!column.sortDirection || column.sortDirection === "none") {
|
|
165
|
+
let index = this.sortOrder.findIndex(col => col === column.key)
|
|
166
|
+
if (index !== -1) {
|
|
167
|
+
this.sortOrder.splice(index, 1)
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
let index = this.sortOrder.findIndex(col => col === column.key)
|
|
171
|
+
if (index === -1) {
|
|
172
|
+
this.sortOrder = [...this.sortOrder, column.key]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
this.columns = [...this.columns]
|
|
176
|
+
this._emitChange()
|
|
177
|
+
}
|
|
178
|
+
private _handleColumnResize = (e: CustomEvent<ColumnSettings<T>>) => {
|
|
179
|
+
let columnSetting = e.detail
|
|
180
|
+
let index = this.columns.findIndex(col => col.key == columnSetting.key)
|
|
181
|
+
this.columns[index] = { ...this.columns[index] }
|
|
150
182
|
this.columns = [...this.columns]
|
|
151
183
|
this._emitChange()
|
|
152
184
|
}
|
|
153
185
|
|
|
154
186
|
private _emitChange = () => {
|
|
155
|
-
let { pagination, columns } = this
|
|
156
|
-
this.dispatchEvent(new CustomEvent<TableDataOptions<T>>("change", { detail: { pagination, columns } }))
|
|
187
|
+
let { pagination, columns, sortOrder } = this
|
|
188
|
+
this.dispatchEvent(new CustomEvent<TableDataOptions<T>>("change", { detail: { pagination, columns, sortOrder } }))
|
|
157
189
|
}
|
|
158
190
|
//@ts-ignore
|
|
159
191
|
private __DO_NOT_USE_filter = () => {
|
|
@@ -225,14 +257,15 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
225
257
|
})
|
|
226
258
|
}
|
|
227
259
|
const tdBorderAndPadding = 4;
|
|
260
|
+
let rowHeight = this.rowHeight - tdBorderAndPadding;
|
|
261
|
+
if (rowHeight < this.fontSize + tdBorderAndPadding) {
|
|
262
|
+
rowHeight = this.fontSize + tdBorderAndPadding
|
|
263
|
+
}
|
|
228
264
|
return html`
|
|
229
|
-
<div class="table-wrapper" style="--rowHeight:${this.rowHeight
|
|
265
|
+
<div class="table-wrapper" style="--rowHeight:${rowHeight}px;--fontSize:${this.fontSize}px;--lineClamp:${Math.floor(rowHeight / this.fontSize)}">
|
|
230
266
|
<div role="table">
|
|
231
|
-
<spectric-table-header .columns=${columns} @sortChange=${this._handleSortChange}></spectric-table-header>
|
|
232
|
-
|
|
233
|
-
html`<spectric-table-virtual-body .columns=${columns} .data=${this.data} .table=${this} .rowHeight=${this.rowHeight}></spectric-table-virtual-body>` :
|
|
234
|
-
html`<spectric-table-body .columns=${columns} .data=${this.data} .table=${this}></spectric-table-body>`
|
|
235
|
-
}
|
|
267
|
+
<spectric-table-header .columns=${columns} @sortChange=${this._handleSortChange} @columnResize=${this._handleColumnResize}></spectric-table-header>
|
|
268
|
+
<spectric-table-virtual-body .columns=${columns} .data=${this.data} .table=${this} .rowHeight=${rowHeight}></spectric-table-virtual-body>
|
|
236
269
|
</div>
|
|
237
270
|
</div>
|
|
238
271
|
${this.pagination ? html`<spectric-pagination ${spreadProps(this.pagination)} @change=${this._handlePaginationChange}></spectric-pagination>` : null}
|
|
@@ -245,11 +278,12 @@ interface TableEvents {
|
|
|
245
278
|
'paginationChange': (event: CustomEvent<PaginationChangeProps>) => void;
|
|
246
279
|
'filter': (event: CustomEvent<FilterEvent<any>>) => void;
|
|
247
280
|
'sortChange': (event: CustomEvent<ColumnSettings<any>>) => void;
|
|
281
|
+
'selected': (event: CustomEvent<any[]>) => void;
|
|
248
282
|
}
|
|
249
283
|
|
|
250
284
|
declare global {
|
|
251
285
|
interface HTMLElementTagNameMap {
|
|
252
|
-
[TableElementTag]: HTMLElementTagWithEvents<
|
|
286
|
+
[TableElementTag]: HTMLElementTagWithEvents<SpectricTableElement<any>, TableEvents>
|
|
253
287
|
|
|
254
288
|
}
|
|
255
289
|
namespace JSX {
|
|
@@ -257,7 +291,7 @@ declare global {
|
|
|
257
291
|
/**
|
|
258
292
|
* @see {@link TableElement}
|
|
259
293
|
*/
|
|
260
|
-
[TableElementTag]: ReactElementWithPropsAndEvents<
|
|
294
|
+
[TableElementTag]: ReactElementWithPropsAndEvents<SpectricTableElement<any>, TableProps<any>, TableEvents>;
|
|
261
295
|
}
|
|
262
296
|
}
|
|
263
297
|
namespace React {
|
|
@@ -266,7 +300,7 @@ declare global {
|
|
|
266
300
|
/**
|
|
267
301
|
* @see {@link TableElement}
|
|
268
302
|
*/
|
|
269
|
-
[TableElementTag]: ReactElementWithPropsAndEvents<
|
|
303
|
+
[TableElementTag]: ReactElementWithPropsAndEvents<SpectricTableElement<any>, TableProps<any>, TableEvents>
|
|
270
304
|
}
|
|
271
305
|
}
|
|
272
306
|
}
|
|
@@ -3,7 +3,7 @@ import { customElement, property, } from 'lit/decorators.js';
|
|
|
3
3
|
import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from '../types';
|
|
4
4
|
export const TableBodyElementTag = "spectric-table-virtual-body"
|
|
5
5
|
import "./cell"
|
|
6
|
-
import { ColumnSettings,
|
|
6
|
+
import { ColumnSettings, SpectricTableElement } from './table';
|
|
7
7
|
import { repeat } from 'lit/directives/repeat.js';
|
|
8
8
|
import { DisposableElement } from '../../classes/DisposibleElement';
|
|
9
9
|
import "./virtualBody.css"
|
|
@@ -29,7 +29,7 @@ export class TableVirtualBodyElement<T> extends DisposableElement implements Bod
|
|
|
29
29
|
@property({ type: Number, state: true })
|
|
30
30
|
startIndex: number = 0
|
|
31
31
|
|
|
32
|
-
table!:
|
|
32
|
+
table!: SpectricTableElement<T>
|
|
33
33
|
constructor() {
|
|
34
34
|
super()
|
|
35
35
|
this.addDisposableListener(() => this.table.querySelector(".table-wrapper")!, "scroll", () => {
|