@spectric/ui 0.0.24 → 0.0.26
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/input.d.ts +5 -0
- package/dist/components/query_bar/QueryBar.d.ts +4 -4
- package/dist/components/table/table.d.ts +5 -1
- package/dist/components/table/virtualBody.d.ts +2 -1
- package/dist/custom-elements.json +11 -5
- package/dist/index.es.js +1643 -1559
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +187 -183
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/input.ts +12 -1
- package/src/components/query_bar/QueryBar.css +67 -64
- package/src/components/query_bar/QueryBar.ts +24 -14
- package/src/components/query_bar/__tests__/test_highlighting.spec.ts +101 -0
- package/src/components/query_bar/querylanguage/kuery/ast/_generated_/kuery.js +7 -3
- package/src/components/query_bar/querylanguage/kuery/ast/{kuery.peg → kuery.peggy} +7 -3
- package/src/components/query_bar/querylanguage/outputTypes/toHTML.ts +118 -22
- package/src/components/table/table.css +81 -71
- package/src/components/table/table.ts +50 -20
- package/src/components/table/virtualBody.ts +21 -18
- package/src/stories/QueryBar.stories.ts +9 -8
- package/src/stories/fixtures/data.ts +14 -17
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FieldTypes, GeospatialOperators, KueryNode } from "../..";
|
|
2
|
+
import { LiteralTypeBuildNode } from "../kuery";
|
|
2
3
|
import { wildcardSymbol } from "../kuery/node_types/wildcard";
|
|
3
4
|
|
|
4
5
|
export const KQL_WILDCARD_SYMBOL = wildcardSymbol;
|
|
@@ -13,31 +14,32 @@ export type FunctionName =
|
|
|
13
14
|
| "nested";
|
|
14
15
|
const and = (node: KueryNode, fields?: FieldTypes[]) => {
|
|
15
16
|
const children = node.arguments || [];
|
|
16
|
-
|
|
17
|
+
let html =
|
|
17
18
|
"<span class='and-expression'>" +
|
|
18
19
|
children
|
|
19
20
|
.map((child: KueryNode) => {
|
|
20
21
|
return toHTML(child, fields);
|
|
21
22
|
})
|
|
22
23
|
.join(" AND ") +
|
|
23
|
-
"</span>"
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
"</span>";
|
|
25
|
+
if (node.group !== undefined) {
|
|
26
|
+
try {
|
|
27
|
+
let { fieldName, values } = groupVariables(children);
|
|
28
|
+
let operator = ":";
|
|
29
|
+
if (fieldName === extractFieldName({ type: "literal", value: null })) {
|
|
30
|
+
//This is a multimatch search ("test","test1") are the values searched acrall all fields
|
|
31
|
+
fieldName = "";
|
|
32
|
+
operator = "";
|
|
33
|
+
}
|
|
34
|
+
return `<span class="and-expression">${fieldName}${operator}(${values.join(
|
|
35
|
+
" AND "
|
|
36
|
+
)})</span>`;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.log(error);
|
|
39
|
+
}
|
|
40
|
+
return `( ${html} )`;
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
return `<span class="is-expression"><span class="field-name">${fieldName}</span>${operator}<span class="expression-value">${value}</span></span>`;
|
|
42
|
+
return html;
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
const or = (node: KueryNode, fields?: FieldTypes[]) => {
|
|
@@ -47,13 +49,105 @@ const or = (node: KueryNode, fields?: FieldTypes[]) => {
|
|
|
47
49
|
return toHTML(child, fields);
|
|
48
50
|
})
|
|
49
51
|
.join(" OR ");
|
|
50
|
-
|
|
52
|
+
let html = `<span class="or-expression">${args}</span>`;
|
|
53
|
+
if (node.group !== undefined) {
|
|
54
|
+
try {
|
|
55
|
+
let { fieldName, values } = groupVariables(children);
|
|
56
|
+
let operator = ":";
|
|
57
|
+
if (fieldName === extractFieldName({ type: "literal", value: null })) {
|
|
58
|
+
//This is a multimatch search ("test","test1") are the values searched acrall all fields
|
|
59
|
+
fieldName = "";
|
|
60
|
+
operator = "";
|
|
61
|
+
}
|
|
62
|
+
return `<span class="or-expression">${fieldName}${operator}(${values.join(
|
|
63
|
+
" OR "
|
|
64
|
+
)})</span>`;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.log(error);
|
|
67
|
+
}
|
|
68
|
+
return `( ${html} )`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return html;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const extractLiteralValue = (
|
|
75
|
+
fieldName: string,
|
|
76
|
+
valueArg: LiteralTypeBuildNode,
|
|
77
|
+
isPhrase: LiteralTypeBuildNode,
|
|
78
|
+
fields?: FieldTypes[]
|
|
79
|
+
) => {
|
|
80
|
+
let value = toHTML(valueArg);
|
|
81
|
+
if (isPhrase.value) {
|
|
82
|
+
value = `"${value}"`;
|
|
83
|
+
}
|
|
84
|
+
if (fields && fields.find((f) => f.name === fieldName)?.type === "boolean") {
|
|
85
|
+
value = `<span class="value-constant">${valueArg.value}</span>`;
|
|
86
|
+
}
|
|
87
|
+
return `<span class="expression-value">${value}</span>`;
|
|
88
|
+
};
|
|
89
|
+
const extractFieldName = (fieldNameArg: any) => {
|
|
90
|
+
let fieldName = toHTML(fieldNameArg);
|
|
91
|
+
return `<span class="field-name">${fieldName}</span>`;
|
|
92
|
+
};
|
|
93
|
+
const is = (node: KueryNode, fields?: FieldTypes[]) => {
|
|
94
|
+
var {
|
|
95
|
+
arguments: [fieldNameArg, valueArg, isPhrase],
|
|
96
|
+
} = node;
|
|
97
|
+
let operator = ":";
|
|
98
|
+
let fieldName = extractFieldName(fieldNameArg);
|
|
99
|
+
if (fieldName === extractFieldName({ type: "literal", value: null })) {
|
|
100
|
+
//This is a multimatch search ("test","test1") are the values searched acrall all fields
|
|
101
|
+
fieldName = "";
|
|
102
|
+
operator = "";
|
|
103
|
+
}
|
|
104
|
+
let value = extractLiteralValue(fieldName, valueArg, isPhrase, fields);
|
|
105
|
+
return `<span class="is-expression">${fieldName}${operator}${value}</span>`;
|
|
51
106
|
};
|
|
107
|
+
|
|
52
108
|
const not = (node: KueryNode, fields?: FieldTypes[]) => {
|
|
53
109
|
const [argument] = node.arguments;
|
|
54
110
|
return `<span class="not-expression">not ${toHTML(argument, fields)}</span>`;
|
|
55
111
|
};
|
|
56
112
|
|
|
113
|
+
const groupVariables = (
|
|
114
|
+
nodes: KueryNode[],
|
|
115
|
+
fieldName: string | undefined = undefined,
|
|
116
|
+
fields?: FieldTypes[]
|
|
117
|
+
) => {
|
|
118
|
+
let values: string[] = [];
|
|
119
|
+
if (!fieldName && nodes[0].function == "is") {
|
|
120
|
+
fieldName = extractFieldName(nodes[0].arguments[0]);
|
|
121
|
+
}
|
|
122
|
+
if (!fieldName) {
|
|
123
|
+
throw Error(
|
|
124
|
+
"Cannot group variables because they don't have a groupable field"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
for (let node of nodes) {
|
|
128
|
+
var {
|
|
129
|
+
arguments: [fieldNameArg, valueArg, isPhrase],
|
|
130
|
+
} = node;
|
|
131
|
+
if (node.function == "is") {
|
|
132
|
+
if (extractFieldName(fieldNameArg) !== fieldName) {
|
|
133
|
+
throw Error(
|
|
134
|
+
"Cannot group variables because they aren't for the same field"
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
let value = extractLiteralValue(fieldName, valueArg, isPhrase, fields);
|
|
138
|
+
values.push(value);
|
|
139
|
+
} else if (node.function === "or" || node.function === "and") {
|
|
140
|
+
let grouped = groupVariables(node.arguments, fieldName, fields);
|
|
141
|
+
values.push(...grouped.values);
|
|
142
|
+
} else {
|
|
143
|
+
throw Error(
|
|
144
|
+
"Cannot group variables because they aren't for the same field"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return { fieldName, values };
|
|
149
|
+
};
|
|
150
|
+
|
|
57
151
|
const AST_TO_CQL = {
|
|
58
152
|
gt: ">",
|
|
59
153
|
lt: "<",
|
|
@@ -70,7 +164,11 @@ const range = (node: KueryNode) => {
|
|
|
70
164
|
value = `${value}`;
|
|
71
165
|
}
|
|
72
166
|
//double check that its a number else quote it
|
|
73
|
-
|
|
167
|
+
//parseFloat("2025-Not a float") parses as a float... so we double check with regex
|
|
168
|
+
if (
|
|
169
|
+
Number.isNaN(parseFloat(value)) ||
|
|
170
|
+
!value.match(/^[-+]?([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)$/)
|
|
171
|
+
) {
|
|
74
172
|
value = `<span class="expression-value">"${value}"</span>`;
|
|
75
173
|
}
|
|
76
174
|
return `<span class="range-expression"><span class="field-name">${fieldNameArg.value}</span> ${opsign} <span class="field-numeric">${value}</span></span>`;
|
|
@@ -90,8 +188,6 @@ const geospatial = (node: KueryNode) => {
|
|
|
90
188
|
console.log(fieldName, operator, value);
|
|
91
189
|
let opsine = GeospatialOperators[operator].value;
|
|
92
190
|
return `<span><span class="field-name">${fieldName.value}</span> ${opsine}<span title="${value.value}">--GEOMETRY--</span></span>`;
|
|
93
|
-
if (operator === "within") return `WITHIN(${fieldName.value},${value.value})`;
|
|
94
|
-
throw Error(`Unsupported GEO Operator:${operator}`);
|
|
95
191
|
};
|
|
96
192
|
export const functions = {
|
|
97
193
|
is,
|
|
@@ -1,99 +1,109 @@
|
|
|
1
|
-
spectric-table{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
spectric-table {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
overflow: hidden;
|
|
5
|
+
line-height: 1;
|
|
6
6
|
}
|
|
7
|
-
spectric-table .table-wrapper{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
spectric-table .table-wrapper {
|
|
8
|
+
overflow: auto;
|
|
9
|
+
flex-grow: 1;
|
|
10
|
+
position: relative;
|
|
11
11
|
}
|
|
12
|
-
spectric-table tr{
|
|
13
|
-
|
|
12
|
+
spectric-table tr {
|
|
13
|
+
text-align: center;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
spectric-table tr.odd{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
spectric-
|
|
20
|
-
|
|
16
|
+
spectric-table tr.odd {
|
|
17
|
+
background-color: color-mix(
|
|
18
|
+
in srgb,
|
|
19
|
+
var(--spectric-primary, #1ea7fd),
|
|
20
|
+
transparent 90%
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
spectric-table spectric-table-virtual-body tr:hover {
|
|
24
|
+
background-color: color-mix(
|
|
25
|
+
in srgb,
|
|
26
|
+
var(--spectric-primary, #1ea7fd),
|
|
27
|
+
transparent 70%
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
spectric-table spectric-table-virtual-body tr.virtual-scroll-spacer:hover {
|
|
31
|
+
background-color: unset;
|
|
21
32
|
}
|
|
22
33
|
|
|
23
34
|
spectric-table spectric-table-virtual-body tr {
|
|
24
|
-
|
|
35
|
+
height: var(--rowHeight);
|
|
25
36
|
}
|
|
26
|
-
spectric-table td{
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
spectric-table td {
|
|
38
|
+
padding: 1px;
|
|
39
|
+
border: 1px solid transparent;
|
|
29
40
|
}
|
|
30
|
-
spectric-table spectric-table-virtual-body td{
|
|
31
|
-
|
|
41
|
+
spectric-table spectric-table-virtual-body td {
|
|
42
|
+
height: var(--rowHeight);
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
spectric-table div[role="table"]{
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
spectric-table div[role="table"] {
|
|
46
|
+
display: table;
|
|
47
|
+
min-width: 100%;
|
|
37
48
|
}
|
|
38
49
|
|
|
39
|
-
spectric-table-cell{
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
spectric-table-cell {
|
|
51
|
+
display: contents;
|
|
52
|
+
vertical-align: middle;
|
|
42
53
|
}
|
|
43
|
-
spectric-table-cell td{
|
|
44
|
-
|
|
54
|
+
spectric-table-cell td {
|
|
55
|
+
position: relative;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
spectric-table td:hover:has(.hasActions) {
|
|
48
|
-
|
|
59
|
+
border: 1px solid var(--spectric-primary, #1ea7fd);
|
|
49
60
|
}
|
|
50
61
|
|
|
51
|
-
spectric-table-cell .table-cell-actions{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
spectric-table-cell .table-cell-actions {
|
|
63
|
+
position: absolute;
|
|
64
|
+
display: flex;
|
|
65
|
+
width: 100%;
|
|
66
|
+
flex-direction: row-reverse;
|
|
67
|
+
visibility: hidden;
|
|
68
|
+
z-index: 1;
|
|
58
69
|
}
|
|
59
|
-
spectric-table-cell .table-cell-actions.tiny{
|
|
60
|
-
|
|
70
|
+
spectric-table-cell .table-cell-actions.tiny {
|
|
71
|
+
top: -10px;
|
|
61
72
|
}
|
|
62
|
-
spectric-table-cell .table-cell-actions.xxsmall{
|
|
63
|
-
|
|
73
|
+
spectric-table-cell .table-cell-actions.xxsmall {
|
|
74
|
+
top: -16px;
|
|
64
75
|
}
|
|
65
|
-
spectric-table-cell td:hover .table-cell-actions{
|
|
66
|
-
|
|
76
|
+
spectric-table-cell td:hover .table-cell-actions {
|
|
77
|
+
visibility: unset;
|
|
67
78
|
}
|
|
68
|
-
spectric-table .table-checkbox-single spectric-button{
|
|
69
|
-
|
|
79
|
+
spectric-table .table-checkbox-single spectric-button {
|
|
80
|
+
--button-border-radius: 50%;
|
|
70
81
|
}
|
|
71
|
-
spectric-input.table-checkbox-single[checked] spectric-button{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
spectric-input.table-checkbox-single[checked] spectric-button {
|
|
83
|
+
--text-on-color: transparent;
|
|
84
|
+
border-radius: 50%;
|
|
85
|
+
position: relative;
|
|
75
86
|
}
|
|
76
87
|
spectric-input.table-checkbox-single[checked] spectric-button::before {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
position: absolute;
|
|
89
|
+
content: " ";
|
|
90
|
+
height: 50%;
|
|
91
|
+
width: 50%;
|
|
92
|
+
left: 25%;
|
|
93
|
+
top: 25%;
|
|
94
|
+
border-radius: 50%;
|
|
95
|
+
z-index: 1;
|
|
96
|
+
box-shadow: 0px 0px 0px 4px var(--input-color);
|
|
86
97
|
}
|
|
87
98
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
99
|
+
spectric-table .cell-contents {
|
|
100
|
+
display: -webkit-box;
|
|
101
|
+
-webkit-box-orient: vertical;
|
|
102
|
+
-webkit-line-clamp: var(--lineClamp, 1);
|
|
103
|
+
-webkit-box-pack: center;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
text-overflow: ellipsis;
|
|
106
|
+
font-size: var(--fontSize);
|
|
107
|
+
line-height: calc(var(--lineClamp) * var(--fontSize));
|
|
108
|
+
height: var(--rowHeight);
|
|
109
|
+
}
|
|
@@ -21,6 +21,7 @@ export type { TableProps, TableEvents };
|
|
|
21
21
|
export type DomRenderable =
|
|
22
22
|
| HTMLElement
|
|
23
23
|
| TemplateResult
|
|
24
|
+
| TemplateResult<any>[]
|
|
24
25
|
| string
|
|
25
26
|
| number
|
|
26
27
|
| null
|
|
@@ -72,7 +73,7 @@ export type ColumnSettings<T> = {
|
|
|
72
73
|
render?: (
|
|
73
74
|
row: T,
|
|
74
75
|
index: number,
|
|
75
|
-
table: SpectricTableElement<T
|
|
76
|
+
table: SpectricTableElement<T>,
|
|
76
77
|
) => DomRenderable;
|
|
77
78
|
/**
|
|
78
79
|
* Custom comparator function for sorting
|
|
@@ -98,7 +99,7 @@ export type DomEvent<T> = Event & {
|
|
|
98
99
|
|
|
99
100
|
export const TD_BorderAndPadding = 4;
|
|
100
101
|
const TABLE_CREATED_SELECTION_COLUMN = Symbol(
|
|
101
|
-
"spectric-table-selection-column"
|
|
102
|
+
"spectric-table-selection-column",
|
|
102
103
|
);
|
|
103
104
|
/**
|
|
104
105
|
* React example
|
|
@@ -134,6 +135,14 @@ export class SpectricTableElement<T = any>
|
|
|
134
135
|
@property({ type: Number, reflect: true })
|
|
135
136
|
fontSize: number = 16;
|
|
136
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Function that allows you to set the row class name programatically
|
|
140
|
+
*/
|
|
141
|
+
@property({ type: Object, attribute: false })
|
|
142
|
+
getRowClass = (_row: T, index: number) => {
|
|
143
|
+
return index % 2 === 0 ? "odd" : "";
|
|
144
|
+
};
|
|
145
|
+
|
|
137
146
|
protected cellActionButtonSize: ButtonSizesTypes = "xxsmall";
|
|
138
147
|
getCellActionButtonSize() {
|
|
139
148
|
return this.cellActionButtonSize;
|
|
@@ -143,7 +152,7 @@ export class SpectricTableElement<T = any>
|
|
|
143
152
|
//let sorts = props.columns.filter(column => column.sortable && column.sortDirection && column.sortDirection !== TableSortDirection.none)
|
|
144
153
|
let sorts = (props.sortOrder || []).map(
|
|
145
154
|
(key) =>
|
|
146
|
-
props.columns.find((col) => col.key === key) as ColumnSettings<T
|
|
155
|
+
props.columns.find((col) => col.key === key) as ColumnSettings<T>,
|
|
147
156
|
);
|
|
148
157
|
let rows = [...data]; // Need to copy the array to prevent mutating the ordering of the original data
|
|
149
158
|
if (sorts.length) {
|
|
@@ -190,7 +199,7 @@ export class SpectricTableElement<T = any>
|
|
|
190
199
|
this.dispatchEvent(
|
|
191
200
|
new CustomEvent<PaginationChangeProps>("paginationChange", {
|
|
192
201
|
detail: e.detail,
|
|
193
|
-
})
|
|
202
|
+
}),
|
|
194
203
|
);
|
|
195
204
|
this._emitChange();
|
|
196
205
|
};
|
|
@@ -236,7 +245,7 @@ export class SpectricTableElement<T = any>
|
|
|
236
245
|
this.dispatchEvent(
|
|
237
246
|
new CustomEvent<TableDataOptions<T>>("change", {
|
|
238
247
|
detail: { pagination, columns, sortOrder },
|
|
239
|
-
})
|
|
248
|
+
}),
|
|
240
249
|
);
|
|
241
250
|
};
|
|
242
251
|
//@ts-ignore
|
|
@@ -254,10 +263,11 @@ export class SpectricTableElement<T = any>
|
|
|
254
263
|
// Because lit reuses dom elements for speed/effeciency it wont update unless the props are a different object.
|
|
255
264
|
// So we set the selection colum to a "new object" to force the rerender
|
|
256
265
|
let index = this.columns.findIndex(
|
|
257
|
-
(col) => col[TABLE_CREATED_SELECTION_COLUMN]
|
|
266
|
+
(col) => col[TABLE_CREATED_SELECTION_COLUMN],
|
|
258
267
|
);
|
|
259
268
|
if (index !== -1) {
|
|
260
269
|
this.columns[index] = { ...this.selectColumnConfig };
|
|
270
|
+
this.columns = [...this.columns];
|
|
261
271
|
}
|
|
262
272
|
}
|
|
263
273
|
setSelected(selected: T[]) {
|
|
@@ -268,13 +278,11 @@ export class SpectricTableElement<T = any>
|
|
|
268
278
|
return this.selected;
|
|
269
279
|
}
|
|
270
280
|
deselectAll() {
|
|
271
|
-
this.
|
|
272
|
-
this.forceRefreshofSelectionColumn();
|
|
281
|
+
this.setSelected([]);
|
|
273
282
|
this.dispatchEvent(new CustomEvent("selected", { detail: this.selected }));
|
|
274
283
|
}
|
|
275
284
|
selectAll() {
|
|
276
|
-
this.
|
|
277
|
-
this.forceRefreshofSelectionColumn();
|
|
285
|
+
this.setSelected([...this.data]);
|
|
278
286
|
this.dispatchEvent(new CustomEvent("selected", { detail: this.selected }));
|
|
279
287
|
}
|
|
280
288
|
async scrollToRow(row: T | number) {
|
|
@@ -294,7 +302,7 @@ export class SpectricTableElement<T = any>
|
|
|
294
302
|
});
|
|
295
303
|
//Wait for the smooth scroll to complete. Scroll to doesn't return a promise so we must manually check periodically
|
|
296
304
|
for (let wait = 0; wait < 100; wait++) {
|
|
297
|
-
await new Promise((resolve) =>
|
|
305
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
298
306
|
if (wrapper.scrollTop == scrollPosition) {
|
|
299
307
|
console.log("Scroll complete");
|
|
300
308
|
break;
|
|
@@ -305,12 +313,12 @@ export class SpectricTableElement<T = any>
|
|
|
305
313
|
requestAnimationFrame(() => {
|
|
306
314
|
let rows = [...body.querySelectorAll("tr")];
|
|
307
315
|
rows = rows.filter(
|
|
308
|
-
(row) => row.querySelector("spectric-table-cell")?.index === index
|
|
316
|
+
(row) => row.querySelector("spectric-table-cell")?.index === index,
|
|
309
317
|
);
|
|
310
318
|
if (rows.length) {
|
|
311
319
|
rows[0].animate(
|
|
312
320
|
[{ backgroundColor: "red" }, { backgroundColor: "unset" }],
|
|
313
|
-
{ duration: 200, iterations: 5 }
|
|
321
|
+
{ duration: 200, iterations: 5 },
|
|
314
322
|
);
|
|
315
323
|
}
|
|
316
324
|
});
|
|
@@ -337,11 +345,14 @@ export class SpectricTableElement<T = any>
|
|
|
337
345
|
allowResize: false,
|
|
338
346
|
filterable: false,
|
|
339
347
|
width: 39,
|
|
340
|
-
title: (table) => {
|
|
341
|
-
return table.select ===
|
|
348
|
+
title: (table: SpectricTableElement<T>) => {
|
|
349
|
+
return table.select === TableSelectOptions.multi
|
|
342
350
|
? html`<spectric-input
|
|
343
351
|
variant="checkbox"
|
|
344
352
|
@change=${table._handleSelectAllChange}
|
|
353
|
+
${spreadProps({
|
|
354
|
+
checked: table.selected.length === table.data.length,
|
|
355
|
+
})}
|
|
345
356
|
.helperText=${"Select All"}
|
|
346
357
|
></spectric-input>`
|
|
347
358
|
: null;
|
|
@@ -366,12 +377,12 @@ export class SpectricTableElement<T = any>
|
|
|
366
377
|
if (e.target.checked) {
|
|
367
378
|
if (table.select === "single") {
|
|
368
379
|
table.selected = [];
|
|
369
|
-
table.forceRefreshofSelectionColumn();
|
|
370
380
|
}
|
|
371
381
|
table.selected.push(row);
|
|
372
382
|
}
|
|
383
|
+
table.setSelected([...table.selected]);
|
|
373
384
|
table.dispatchEvent(
|
|
374
|
-
new CustomEvent("selected", { detail: [...table.selected] })
|
|
385
|
+
new CustomEvent("selected", { detail: [...table.selected] }),
|
|
375
386
|
);
|
|
376
387
|
}}
|
|
377
388
|
></spectric-input>`;
|
|
@@ -395,10 +406,29 @@ export class SpectricTableElement<T = any>
|
|
|
395
406
|
}
|
|
396
407
|
}
|
|
397
408
|
if (changedProperties.has("select")) {
|
|
398
|
-
|
|
399
|
-
|
|
409
|
+
let selectIndex = this.columns.findIndex(
|
|
410
|
+
(col) => col[TABLE_CREATED_SELECTION_COLUMN],
|
|
411
|
+
);
|
|
412
|
+
if (this.select !== TableSelectOptions.none) {
|
|
413
|
+
this.forceRefreshofSelectionColumn();
|
|
414
|
+
if (!this.columns.find((col) => col[TABLE_CREATED_SELECTION_COLUMN])) {
|
|
415
|
+
this.columns.unshift(this.selectColumnConfig);
|
|
416
|
+
this.columns = [...this.columns];
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
if (selectIndex !== -1) {
|
|
420
|
+
this.columns.splice(selectIndex, 1);
|
|
421
|
+
this.columns = [...this.columns];
|
|
422
|
+
this.setSelected([]);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (
|
|
426
|
+
this.select === TableSelectOptions.single &&
|
|
427
|
+
this.selected.length > 1
|
|
428
|
+
) {
|
|
429
|
+
this.setSelected([this.selected[0]]);
|
|
400
430
|
this.dispatchEvent(
|
|
401
|
-
new CustomEvent("selected", { detail: [
|
|
431
|
+
new CustomEvent("selected", { detail: [this.selected[0]] }),
|
|
402
432
|
);
|
|
403
433
|
}
|
|
404
434
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { html } from "lit";
|
|
1
|
+
import { html, PropertyValues } from "lit";
|
|
2
2
|
import { customElement, property } from "lit/decorators.js";
|
|
3
3
|
import {
|
|
4
4
|
HTMLElementTagWithEvents,
|
|
@@ -56,10 +56,10 @@ export class TableVirtualBodyElement<T>
|
|
|
56
56
|
}
|
|
57
57
|
lastScroll = scrollTop;
|
|
58
58
|
this.startIndex = Math.floor(scrollTop / this.rowHeight);
|
|
59
|
-
}
|
|
59
|
+
},
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
|
-
protected
|
|
62
|
+
protected update(changedProperties: PropertyValues): void {
|
|
63
63
|
if (this.columnsMeasured === false) {
|
|
64
64
|
let tr = this.querySelector("tr");
|
|
65
65
|
let cells = tr?.querySelectorAll("spectric-table-cell td");
|
|
@@ -78,7 +78,9 @@ export class TableVirtualBodyElement<T>
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
+
super.update(changedProperties);
|
|
81
82
|
}
|
|
83
|
+
|
|
82
84
|
protected createRenderRoot(): HTMLElement | DocumentFragment {
|
|
83
85
|
return this;
|
|
84
86
|
}
|
|
@@ -86,12 +88,12 @@ export class TableVirtualBodyElement<T>
|
|
|
86
88
|
let totalRows = this.data.length;
|
|
87
89
|
let buffer = 10; // Have a buffer of rows to prevent column jitter
|
|
88
90
|
let headerHeight = this.table.querySelector(
|
|
89
|
-
"spectric-table-header"
|
|
91
|
+
"spectric-table-header",
|
|
90
92
|
)!.clientHeight;
|
|
91
93
|
let viewport = this.table.querySelector(".table-wrapper")!;
|
|
92
94
|
let startIndex = Math.max(this.startIndex, 0);
|
|
93
95
|
const rowsThatFit = Math.ceil(
|
|
94
|
-
(viewport.clientHeight - headerHeight) / this.rowHeight
|
|
96
|
+
(viewport.clientHeight - headerHeight) / this.rowHeight,
|
|
95
97
|
);
|
|
96
98
|
const endIndex = Math.min(startIndex + rowsThatFit + buffer, totalRows);
|
|
97
99
|
const visibleRows = endIndex - startIndex;
|
|
@@ -132,19 +134,20 @@ export class TableVirtualBodyElement<T>
|
|
|
132
134
|
>
|
|
133
135
|
${repeat(
|
|
134
136
|
this.data.slice(Math.max(startIndex, 0), endIndex),
|
|
135
|
-
(row, index) =>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
(row, index) =>
|
|
138
|
+
html` <tr
|
|
139
|
+
class="${this.table.getRowClass(row, index + startIndex)}"
|
|
140
|
+
>
|
|
141
|
+
${repeat(this.columns, (col) => {
|
|
142
|
+
return html`<spectric-table-cell
|
|
143
|
+
.column=${col}
|
|
144
|
+
.row=${row}
|
|
145
|
+
.index=${index + startIndex}
|
|
146
|
+
.columns=${this.columns}
|
|
147
|
+
.table=${this.table}
|
|
148
|
+
></spectric-table-cell>`;
|
|
149
|
+
})}
|
|
150
|
+
</tr>`,
|
|
148
151
|
)}
|
|
149
152
|
</div>
|
|
150
153
|
<tbody>
|
|
@@ -9,17 +9,18 @@ import { html } from "lit";
|
|
|
9
9
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
10
10
|
import { useArgs } from "@storybook/client-api";
|
|
11
11
|
import { filterByColumn } from "./fixtures/data";
|
|
12
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
12
13
|
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
13
|
-
var code = "";
|
|
14
14
|
|
|
15
15
|
const meta = {
|
|
16
16
|
title: "UI/Query",
|
|
17
17
|
tags: ["autodocs"],
|
|
18
18
|
component: "spectric-query",
|
|
19
19
|
render: (args) => {
|
|
20
|
+
const code = createRef<HTMLPreElement>();
|
|
20
21
|
const [_, updateArgs] = useArgs();
|
|
21
22
|
const fakevalues = async (field, text) => {
|
|
22
|
-
if (!args.fields.find((f) => f.name === field)) {
|
|
23
|
+
if (!args.fields || !args.fields.find((f) => f.name === field)) {
|
|
23
24
|
return [];
|
|
24
25
|
}
|
|
25
26
|
return filterByColumn(field, text);
|
|
@@ -30,16 +31,15 @@ const meta = {
|
|
|
30
31
|
.getValuesForField=${fakevalues}
|
|
31
32
|
value=${ifDefined(args.value)}
|
|
32
33
|
@change=${(e: CustomEvent<any>) => {
|
|
33
|
-
code
|
|
34
|
+
if (code.value) {
|
|
35
|
+
code.value.innerText = JSON.stringify(e.detail, null, 2);
|
|
36
|
+
}
|
|
34
37
|
updateArgs({ ...args });
|
|
35
38
|
}}
|
|
36
|
-
outputLanguage=${args.outputLanguage}
|
|
39
|
+
.outputLanguage=${args.outputLanguage}
|
|
37
40
|
>
|
|
38
41
|
</spectric-query>
|
|
39
|
-
<pre>
|
|
40
|
-
${JSON.stringify(code, null, 2)}
|
|
41
|
-
</pre
|
|
42
|
-
>
|
|
42
|
+
<pre ${ref(code)}></pre>
|
|
43
43
|
`;
|
|
44
44
|
},
|
|
45
45
|
argTypes: {
|
|
@@ -50,6 +50,7 @@ const meta = {
|
|
|
50
50
|
},
|
|
51
51
|
args: {
|
|
52
52
|
outputLanguage: "toDSL",
|
|
53
|
+
value: "",
|
|
53
54
|
fields: [
|
|
54
55
|
{ name: "test", type: "string" },
|
|
55
56
|
{ name: "test_num", type: "number" },
|