@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.
@@ -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
- return (
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
- const is = (node: KueryNode, fields?: FieldTypes[]) => {
27
- var {
28
- arguments: [fieldNameArg, valueArg, isPhrase],
29
- } = node;
30
- let operator = ":";
31
- let value = toHTML(valueArg);
32
- if (isPhrase.value) {
33
- value = `"${value}"`;
34
- }
35
- let fieldName = toHTML(fieldNameArg);
36
- if (fields && fields.find((f) => f.name === fieldName)?.type === "boolean") {
37
- value = `<span class="value-constant">${valueArg.value}</span>`;
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
- return `<span class="or-expression">${args}</span>`;
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
- if (Number.isNaN(parseFloat(value))) {
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
- display: flex;
3
- flex-direction: column;
4
- overflow: hidden;
5
- line-height: 1;
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
- overflow: auto;
9
- flex-grow: 1;
10
- position: relative;
7
+ spectric-table .table-wrapper {
8
+ overflow: auto;
9
+ flex-grow: 1;
10
+ position: relative;
11
11
  }
12
- spectric-table tr{
13
- text-align: center;
12
+ spectric-table tr {
13
+ text-align: center;
14
14
  }
15
15
 
16
- spectric-table tr.odd{
17
- background-color: color-mix(in srgb, var(--spectric-primary, #1ea7fd), transparent 90%);
18
- }
19
- spectric-table spectric-table-virtual-body tr:hover{
20
- background-color: color-mix(in srgb, var(--spectric-primary, #1ea7fd), transparent 70%)
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
- height: var(--rowHeight);
35
+ height: var(--rowHeight);
25
36
  }
26
- spectric-table td{
27
- padding:1px;
28
- border: 1px solid transparent;
37
+ spectric-table td {
38
+ padding: 1px;
39
+ border: 1px solid transparent;
29
40
  }
30
- spectric-table spectric-table-virtual-body td{
31
- height: var(--rowHeight);
41
+ spectric-table spectric-table-virtual-body td {
42
+ height: var(--rowHeight);
32
43
  }
33
44
 
34
- spectric-table div[role="table"]{
35
- display: table;
36
- min-width: 100%;
45
+ spectric-table div[role="table"] {
46
+ display: table;
47
+ min-width: 100%;
37
48
  }
38
49
 
39
- spectric-table-cell{
40
- display: contents;
41
- vertical-align: middle;
50
+ spectric-table-cell {
51
+ display: contents;
52
+ vertical-align: middle;
42
53
  }
43
- spectric-table-cell td{
44
- position: relative;
54
+ spectric-table-cell td {
55
+ position: relative;
45
56
  }
46
57
 
47
58
  spectric-table td:hover:has(.hasActions) {
48
- border: 1px solid var(--spectric-primary, #1ea7fd);
59
+ border: 1px solid var(--spectric-primary, #1ea7fd);
49
60
  }
50
61
 
51
- spectric-table-cell .table-cell-actions{
52
- position: absolute;
53
- display: flex;
54
- width: 100%;
55
- flex-direction: row-reverse;
56
- visibility: hidden;
57
- z-index: 1;
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
- top: -10px;
70
+ spectric-table-cell .table-cell-actions.tiny {
71
+ top: -10px;
61
72
  }
62
- spectric-table-cell .table-cell-actions.xxsmall{
63
- top: -16px;
73
+ spectric-table-cell .table-cell-actions.xxsmall {
74
+ top: -16px;
64
75
  }
65
- spectric-table-cell td:hover .table-cell-actions{
66
- visibility: unset;
76
+ spectric-table-cell td:hover .table-cell-actions {
77
+ visibility: unset;
67
78
  }
68
- spectric-table .table-checkbox-single spectric-button{
69
- --button-border-radius: 50%;
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
- --text-on-color: transparent;
73
- border-radius: 50%;
74
- position: relative;
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
- position: absolute;
78
- content: " ";
79
- height: 50%;
80
- width: 50%;
81
- left: 25%;
82
- top: 25%;
83
- border-radius: 50%;
84
- z-index: 1;
85
- box-shadow: 0px 0px 0px 4px var(--input-color);
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
- spectric-table .cell-contents{
90
- display: -webkit-box;
91
- -webkit-box-orient: vertical;
92
- -webkit-line-clamp: var(--lineClamp,1);
93
- -webkit-box-pack: center;
94
- overflow: hidden;
95
- text-overflow: ellipsis;
96
- font-size: var(--fontSize);
97
- line-height: calc(var(--lineClamp) * var(--fontSize));
98
- height: var(--rowHeight);
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.selected = [];
272
- this.forceRefreshofSelectionColumn();
281
+ this.setSelected([]);
273
282
  this.dispatchEvent(new CustomEvent("selected", { detail: this.selected }));
274
283
  }
275
284
  selectAll() {
276
- this.selected = [...this.data];
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) => setTimeout(resolve, 10));
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 === "multi"
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
- if (this.select === "single" && this.selected.length > 1) {
399
- this.selected = [this.selected[0]];
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: [...this.selected] })
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 updated(): void {
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) => html` <tr
136
- class="${(index + startIndex) % 2 === 0 ? "odd" : ""}"
137
- >
138
- ${repeat(this.columns, (col) => {
139
- return html`<spectric-table-cell
140
- .column=${col}
141
- .row=${row}
142
- .index=${index + startIndex}
143
- .columns=${this.columns}
144
- .table=${this.table}
145
- ></spectric-table-cell>`;
146
- })}
147
- </tr>`
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 = e.detail;
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" },