@spectric/ui 0.0.25 → 0.0.27

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.
@@ -39,7 +39,7 @@ export class TableHeaderElement<T>
39
39
  this.addDisposableListener(
40
40
  () => document.body,
41
41
  "mouseup",
42
- this._handleResizeEnd
42
+ this._handleResizeEnd,
43
43
  );
44
44
  }
45
45
  protected createRenderRoot(): HTMLElement | DocumentFragment {
@@ -68,7 +68,7 @@ export class TableHeaderElement<T>
68
68
  composed: true,
69
69
  bubbles: true,
70
70
  detail: column,
71
- })
71
+ }),
72
72
  );
73
73
  };
74
74
  _handleResizeStart = (event: MouseEvent, column: ColumnSettings<T>) => {
@@ -83,13 +83,13 @@ export class TableHeaderElement<T>
83
83
  this._resizeStart.column.width = this._resizeStart.column.width + delta;
84
84
  } else {
85
85
  let cell = (this._resizeStart.event.target as HTMLDivElement).closest(
86
- "td"
86
+ "th",
87
87
  ) as HTMLTableCellElement;
88
88
  this._resizeStart.column.width =
89
89
  cell.getBoundingClientRect().width + delta;
90
90
  }
91
91
  this.dispatchEvent(
92
- new CustomEvent("columnResize", { detail: this._resizeStart.column })
92
+ new CustomEvent("columnResize", { detail: this._resizeStart.column }),
93
93
  );
94
94
  //We wrap this to ensure any sort events get fired before we are done FIXME?
95
95
  requestAnimationFrame(() => {
@@ -128,8 +128,8 @@ export class TableHeaderElement<T>
128
128
  column.sortDirection === TableSortDirection.ascending
129
129
  ? ARROW_UP
130
130
  : column.sortDirection == TableSortDirection.descending
131
- ? ARROW_DOWN
132
- : ``;
131
+ ? ARROW_DOWN
132
+ : ``;
133
133
  let sortClass = column.sortDirection || TableSortDirection.none;
134
134
  let resizable = column.allowResize || column.allowResize === undefined;
135
135
  let resizeHandle = resizable
@@ -145,7 +145,7 @@ export class TableHeaderElement<T>
145
145
  if (typeof title == "function") {
146
146
  title = title(this.table) || undefined;
147
147
  }
148
- return html` <td
148
+ return html` <th
149
149
  @click=${() => this._handleSortChange(column)}
150
150
  style="${columnWidth}"
151
151
  >
@@ -154,7 +154,7 @@ export class TableHeaderElement<T>
154
154
  ${title}
155
155
  <span class="sort-direction ${sortClass}">${sortDirection}</span>
156
156
  </div>
157
- </td>`;
157
+ </th>`;
158
158
  }
159
159
  }
160
160
 
@@ -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,17 @@ export class SpectricTableElement<T = any>
134
135
  @property({ type: Number, reflect: true })
135
136
  fontSize: number = 16;
136
137
 
138
+ @property({ type: Number, reflect: false })
139
+ virtualBodyBuffer: number = 100;
140
+
141
+ /**
142
+ * Function that allows you to set the row class name programatically
143
+ */
144
+ @property({ type: Object, attribute: false })
145
+ getRowClass = (_row: T, index: number) => {
146
+ return index % 2 === 0 ? "odd" : "";
147
+ };
148
+
137
149
  protected cellActionButtonSize: ButtonSizesTypes = "xxsmall";
138
150
  getCellActionButtonSize() {
139
151
  return this.cellActionButtonSize;
@@ -143,7 +155,7 @@ export class SpectricTableElement<T = any>
143
155
  //let sorts = props.columns.filter(column => column.sortable && column.sortDirection && column.sortDirection !== TableSortDirection.none)
144
156
  let sorts = (props.sortOrder || []).map(
145
157
  (key) =>
146
- props.columns.find((col) => col.key === key) as ColumnSettings<T>
158
+ props.columns.find((col) => col.key === key) as ColumnSettings<T>,
147
159
  );
148
160
  let rows = [...data]; // Need to copy the array to prevent mutating the ordering of the original data
149
161
  if (sorts.length) {
@@ -190,7 +202,7 @@ export class SpectricTableElement<T = any>
190
202
  this.dispatchEvent(
191
203
  new CustomEvent<PaginationChangeProps>("paginationChange", {
192
204
  detail: e.detail,
193
- })
205
+ }),
194
206
  );
195
207
  this._emitChange();
196
208
  };
@@ -236,7 +248,7 @@ export class SpectricTableElement<T = any>
236
248
  this.dispatchEvent(
237
249
  new CustomEvent<TableDataOptions<T>>("change", {
238
250
  detail: { pagination, columns, sortOrder },
239
- })
251
+ }),
240
252
  );
241
253
  };
242
254
  //@ts-ignore
@@ -254,7 +266,7 @@ export class SpectricTableElement<T = any>
254
266
  // Because lit reuses dom elements for speed/effeciency it wont update unless the props are a different object.
255
267
  // So we set the selection colum to a "new object" to force the rerender
256
268
  let index = this.columns.findIndex(
257
- (col) => col[TABLE_CREATED_SELECTION_COLUMN]
269
+ (col) => col[TABLE_CREATED_SELECTION_COLUMN],
258
270
  );
259
271
  if (index !== -1) {
260
272
  this.columns[index] = { ...this.selectColumnConfig };
@@ -293,7 +305,7 @@ export class SpectricTableElement<T = any>
293
305
  });
294
306
  //Wait for the smooth scroll to complete. Scroll to doesn't return a promise so we must manually check periodically
295
307
  for (let wait = 0; wait < 100; wait++) {
296
- await new Promise((resolve) => setTimeout(resolve, 10));
308
+ await new Promise((resolve) => requestAnimationFrame(resolve));
297
309
  if (wrapper.scrollTop == scrollPosition) {
298
310
  console.log("Scroll complete");
299
311
  break;
@@ -304,12 +316,12 @@ export class SpectricTableElement<T = any>
304
316
  requestAnimationFrame(() => {
305
317
  let rows = [...body.querySelectorAll("tr")];
306
318
  rows = rows.filter(
307
- (row) => row.querySelector("spectric-table-cell")?.index === index
319
+ (row) => row.querySelector("spectric-table-cell")?.index === index,
308
320
  );
309
321
  if (rows.length) {
310
322
  rows[0].animate(
311
323
  [{ backgroundColor: "red" }, { backgroundColor: "unset" }],
312
- { duration: 200, iterations: 5 }
324
+ { duration: 200, iterations: 5 },
313
325
  );
314
326
  }
315
327
  });
@@ -373,7 +385,7 @@ export class SpectricTableElement<T = any>
373
385
  }
374
386
  table.setSelected([...table.selected]);
375
387
  table.dispatchEvent(
376
- new CustomEvent("selected", { detail: [...table.selected] })
388
+ new CustomEvent("selected", { detail: [...table.selected] }),
377
389
  );
378
390
  }}
379
391
  ></spectric-input>`;
@@ -398,7 +410,7 @@ export class SpectricTableElement<T = any>
398
410
  }
399
411
  if (changedProperties.has("select")) {
400
412
  let selectIndex = this.columns.findIndex(
401
- (col) => col[TABLE_CREATED_SELECTION_COLUMN]
413
+ (col) => col[TABLE_CREATED_SELECTION_COLUMN],
402
414
  );
403
415
  if (this.select !== TableSelectOptions.none) {
404
416
  this.forceRefreshofSelectionColumn();
@@ -419,7 +431,7 @@ export class SpectricTableElement<T = any>
419
431
  ) {
420
432
  this.setSelected([this.selected[0]]);
421
433
  this.dispatchEvent(
422
- new CustomEvent("selected", { detail: [this.selected[0]] })
434
+ new CustomEvent("selected", { detail: [this.selected[0]] }),
423
435
  );
424
436
  }
425
437
  }
@@ -442,6 +454,7 @@ export class SpectricTableElement<T = any>
442
454
  @columnResize=${this._handleColumnResize}
443
455
  ></spectric-table-header>
444
456
  <spectric-table-virtual-body
457
+ .buffer=${this.virtualBodyBuffer}
445
458
  .columns=${columns}
446
459
  .data=${this.data}
447
460
  .table=${this}
@@ -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,
@@ -41,6 +41,8 @@ export class TableVirtualBodyElement<T>
41
41
  @property({ type: Object, attribute: false })
42
42
  table!: SpectricTableElement<T>;
43
43
  columnsMeasured: boolean = false;
44
+ @property({ type: Number, attribute: false })
45
+ buffer: number = 100;
44
46
  constructor() {
45
47
  super();
46
48
  let lastScroll = 0;
@@ -56,10 +58,10 @@ export class TableVirtualBodyElement<T>
56
58
  }
57
59
  lastScroll = scrollTop;
58
60
  this.startIndex = Math.floor(scrollTop / this.rowHeight);
59
- }
61
+ },
60
62
  );
61
63
  }
62
- protected updated(): void {
64
+ protected update(changedProperties: PropertyValues): void {
63
65
  if (this.columnsMeasured === false) {
64
66
  let tr = this.querySelector("tr");
65
67
  let cells = tr?.querySelectorAll("spectric-table-cell td");
@@ -78,48 +80,34 @@ export class TableVirtualBodyElement<T>
78
80
  }
79
81
  }
80
82
  }
83
+ super.update(changedProperties);
81
84
  }
85
+
82
86
  protected createRenderRoot(): HTMLElement | DocumentFragment {
83
87
  return this;
84
88
  }
85
89
  protected render(): unknown {
86
90
  let totalRows = this.data.length;
87
- let buffer = 10; // Have a buffer of rows to prevent column jitter
88
- let headerHeight = this.table.querySelector(
89
- "spectric-table-header"
90
- )!.clientHeight;
91
+ let buffer = this.buffer / 2; // Have a buffer of rows to prevent column jitter
91
92
  let viewport = this.table.querySelector(".table-wrapper")!;
92
- let startIndex = Math.max(this.startIndex, 0);
93
- const rowsThatFit = Math.ceil(
94
- (viewport.clientHeight - headerHeight) / this.rowHeight
93
+ const scrollTop = viewport.scrollTop;
94
+ const start = Math.max(0, Math.floor(scrollTop / this.rowHeight) - buffer);
95
+ const end = Math.min(
96
+ totalRows,
97
+ Math.ceil((scrollTop + viewport.clientHeight) / this.rowHeight) + buffer,
95
98
  );
96
- const endIndex = Math.min(startIndex + rowsThatFit + buffer, totalRows);
97
- const visibleRows = endIndex - startIndex;
98
99
  let totalHeight = totalRows * this.rowHeight;
99
- let beforeHeight =
100
- startIndex * this.rowHeight +
101
- (viewport.scrollTop - startIndex * this.rowHeight);
102
- if (endIndex === totalRows) {
103
- beforeHeight = startIndex * this.rowHeight;
104
- }
105
- let visibleHeight = visibleRows * this.rowHeight;
106
- let afterHeight = totalHeight - beforeHeight - visibleHeight;
107
- let spacerElementBefore =
108
- startIndex != 0
109
- ? html` <tr
110
- style="height:${beforeHeight}px"
111
- class="virtual-scroll-spacer"
112
- >
113
- <td colspan="${this.columns.length}"></td>
114
- </tr>`
115
- : null;
100
+ let beforeHeight = start * this.rowHeight;
101
+ let afterHeight = (totalRows - end) * this.rowHeight;
116
102
 
103
+ let spacerElementBefore = html` <tr
104
+ style="height:${beforeHeight}px"
105
+ class="virtual-scroll-spacer"
106
+ ></tr>`;
117
107
  let spacerElementAfter = html` <tr
118
108
  style="height:${afterHeight}px"
119
109
  class="virtual-scroll-spacer"
120
- >
121
- <td colspan="${this.columns.length}"></td>
122
- </tr>`;
110
+ ></tr>`;
123
111
  return html`
124
112
  <div
125
113
  style="height:${totalHeight}px;position: absolute;overflow-x: hidden;overflow-y: hidden;z-index:-1;width:1px;"
@@ -127,28 +115,25 @@ export class TableVirtualBodyElement<T>
127
115
  <tbody>
128
116
  ${spacerElementBefore}
129
117
  </tbody>
130
- <div
131
- style="display:table-row-group;max-height:${visibleHeight}px; overflow:hidden;"
132
- >
118
+ <div style="display:table-row-group;overflow:hidden;">
133
119
  ${repeat(
134
- 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>`
120
+ this.data.slice(Math.max(start, 0), end),
121
+ (row, index) =>
122
+ html` <tr class="${this.table.getRowClass(row, index + start)}">
123
+ ${repeat(this.columns, (col) => {
124
+ return html`<spectric-table-cell
125
+ .column=${col}
126
+ .row=${row}
127
+ .index=${index + start}
128
+ .columns=${this.columns}
129
+ .table=${this.table}
130
+ ></spectric-table-cell>`;
131
+ })}
132
+ </tr>`,
148
133
  )}
149
134
  </div>
150
135
  <tbody>
151
- ${afterHeight > 0 ? spacerElementAfter : null}
136
+ ${spacerElementAfter}
152
137
  </tbody>
153
138
  `;
154
139
  }
@@ -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" },