@redvars/peacock 3.3.0 → 3.3.2

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.
Files changed (159) hide show
  1. package/dist/{IndividualComponent-tDnXrOLV.js → IndividualComponent-Dt5xirYG.js} +2 -2
  2. package/dist/{IndividualComponent-tDnXrOLV.js.map → IndividualComponent-Dt5xirYG.js.map} +1 -1
  3. package/dist/array-D5vjT2Xm.js +14 -0
  4. package/dist/array-D5vjT2Xm.js.map +1 -0
  5. package/dist/assets/components.css +1 -1
  6. package/dist/assets/components.css.map +1 -1
  7. package/dist/assets/styles.css +1 -1
  8. package/dist/assets/styles.css.map +1 -1
  9. package/dist/{button-BGFJfbT2.js → button-ClzS8JLq.js} +3 -4
  10. package/dist/{button-BGFJfbT2.js.map → button-ClzS8JLq.js.map} +1 -1
  11. package/dist/button-group-BMS5WvaF.js +292 -0
  12. package/dist/button-group-BMS5WvaF.js.map +1 -0
  13. package/dist/button-group.js +6 -107
  14. package/dist/button-group.js.map +1 -1
  15. package/dist/button.js +3 -4
  16. package/dist/button.js.map +1 -1
  17. package/dist/card.js +104 -0
  18. package/dist/card.js.map +1 -0
  19. package/dist/chart-bar-DbnXQgvS.js +1121 -0
  20. package/dist/chart-bar-DbnXQgvS.js.map +1 -0
  21. package/dist/chart-bar.js +259 -0
  22. package/dist/chart-bar.js.map +1 -0
  23. package/dist/chart-donut.js +4 -2
  24. package/dist/chart-donut.js.map +1 -1
  25. package/dist/chart-doughnut.js +4 -2
  26. package/dist/chart-doughnut.js.map +1 -1
  27. package/dist/chart-pie.js +4 -2
  28. package/dist/chart-pie.js.map +1 -1
  29. package/dist/chart-stacked-bar.js +401 -0
  30. package/dist/chart-stacked-bar.js.map +1 -0
  31. package/dist/{class-map-DpeNtqCn.js → class-map-59YGWLnx.js} +9 -3
  32. package/dist/class-map-59YGWLnx.js.map +1 -0
  33. package/dist/clock.js +1 -1
  34. package/dist/code-editor.js +7 -7
  35. package/dist/code-editor.js.map +1 -1
  36. package/dist/code-highlighter.js +7 -25
  37. package/dist/code-highlighter.js.map +1 -1
  38. package/dist/custom-elements-jsdocs.json +8824 -5047
  39. package/dist/custom-elements.json +7468 -4147
  40. package/dist/index.js +16 -10
  41. package/dist/index.js.map +1 -1
  42. package/dist/number-counter.js +2 -2
  43. package/dist/{observe-theme-change-BISF-Gl5.js → observe-theme-change-pALI5fmV.js} +2 -2
  44. package/dist/{observe-theme-change-BISF-Gl5.js.map → observe-theme-change-pALI5fmV.js.map} +1 -1
  45. package/dist/peacock-loader.js +42 -1016
  46. package/dist/peacock-loader.js.map +1 -1
  47. package/dist/pie-Dz0IDiPt.js +537 -0
  48. package/dist/pie-Dz0IDiPt.js.map +1 -0
  49. package/dist/{slider-Dk9CFWTG.js → snackbar-74YCdMPL.js} +6205 -3206
  50. package/dist/snackbar-74YCdMPL.js.map +1 -0
  51. package/dist/src/accordion/accordion-item.d.ts +1 -0
  52. package/dist/src/breadcrumb/breadcrumb/breadcrumb.d.ts +2 -0
  53. package/dist/src/breadcrumb/breadcrumb-item/breadcrumb-item.d.ts +1 -0
  54. package/dist/src/button/button-group/button-group.d.ts +4 -0
  55. package/dist/src/card/card.d.ts +27 -0
  56. package/dist/src/card/index.d.ts +1 -0
  57. package/dist/src/chart-bar/chart-bar.d.ts +53 -0
  58. package/dist/src/chart-bar/chart-stacked-bar.d.ts +78 -0
  59. package/dist/src/chart-bar/index.d.ts +2 -0
  60. package/dist/src/code-editor/code-editor.d.ts +4 -3
  61. package/dist/src/code-highlighter/code-highlighter.d.ts +4 -7
  62. package/dist/src/index.d.ts +9 -0
  63. package/dist/src/menu/index.d.ts +3 -0
  64. package/dist/src/menu/menu/MenuSurfaceController.d.ts +18 -0
  65. package/dist/src/menu/menu/menu.d.ts +54 -12
  66. package/dist/src/menu/menu-item/menu-item.d.ts +12 -5
  67. package/dist/src/menu/sub-menu/sub-menu.d.ts +36 -0
  68. package/dist/src/pagination/index.d.ts +1 -0
  69. package/dist/src/pagination/pagination.d.ts +38 -0
  70. package/dist/src/popover/PopoverController.d.ts +4 -1
  71. package/dist/src/snackbar/index.d.ts +1 -0
  72. package/dist/src/snackbar/snackbar.d.ts +40 -0
  73. package/dist/src/table/index.d.ts +1 -0
  74. package/dist/src/table/table.d.ts +110 -0
  75. package/dist/src/tabs/tab-group.d.ts +5 -1
  76. package/dist/src/tabs/tab-panel.d.ts +2 -0
  77. package/dist/src/tabs/tab.d.ts +3 -1
  78. package/dist/src/tabs/tabs.d.ts +2 -0
  79. package/dist/src/tooltip/tooltip.d.ts +1 -3
  80. package/dist/src/tree-view/index.d.ts +2 -0
  81. package/dist/src/tree-view/tree-node.d.ts +69 -0
  82. package/dist/src/tree-view/tree-view.d.ts +40 -0
  83. package/dist/src/tree-view/wc-tree-view.d.ts +6 -0
  84. package/dist/{style-map-CfNHEkQp.js → style-map-DcB52w-l.js} +2 -2
  85. package/dist/{style-map-CfNHEkQp.js.map → style-map-DcB52w-l.js.map} +1 -1
  86. package/dist/test/card.test.d.ts +1 -0
  87. package/dist/test/chart-bar.test.d.ts +1 -0
  88. package/dist/test/icon.test.d.ts +1 -1
  89. package/dist/test/menu.test.d.ts +1 -0
  90. package/dist/test/snackbar.test.d.ts +1 -0
  91. package/dist/test/sub-menu.test.d.ts +1 -0
  92. package/dist/test/tree-view.test.d.ts +1 -0
  93. package/dist/{transform-DRuHEvar.js → transform-DSwFSqzD.js} +13 -558
  94. package/dist/transform-DSwFSqzD.js.map +1 -0
  95. package/dist/tsconfig.tsbuildinfo +1 -1
  96. package/dist/{unsafe-html-CV6Je6HL.js → unsafe-html-C2r3PyzF.js} +2 -2
  97. package/dist/{unsafe-html-CV6Je6HL.js.map → unsafe-html-C2r3PyzF.js.map} +1 -1
  98. package/package.json +1 -1
  99. package/readme.md +40 -40
  100. package/src/accordion/accordion-item.ts +2 -1
  101. package/src/breadcrumb/breadcrumb/breadcrumb.ts +3 -0
  102. package/src/breadcrumb/breadcrumb-item/breadcrumb-item.ts +1 -0
  103. package/src/button/button-group/button-group.ts +6 -0
  104. package/src/card/card.scss +61 -0
  105. package/src/card/card.ts +38 -0
  106. package/src/card/index.ts +1 -0
  107. package/src/chart-bar/chart-bar.scss +58 -0
  108. package/src/chart-bar/chart-bar.ts +306 -0
  109. package/src/chart-bar/chart-stacked-bar.ts +402 -0
  110. package/src/chart-bar/index.ts +2 -0
  111. package/src/code-editor/code-editor.ts +4 -3
  112. package/src/code-highlighter/code-highlighter.ts +4 -22
  113. package/src/divider/divider.scss +2 -2
  114. package/src/empty-state/empty-state.scss +1 -1
  115. package/src/empty-state/empty-state.ts +1 -1
  116. package/src/index.ts +11 -2
  117. package/src/menu/index.ts +3 -0
  118. package/src/menu/menu/MenuSurfaceController.ts +61 -0
  119. package/src/menu/{menu-list/menu-list.scss → menu/menu.scss} +19 -4
  120. package/src/menu/menu/menu.ts +389 -81
  121. package/src/menu/menu-item/menu-item.ts +115 -36
  122. package/src/menu/sub-menu/sub-menu.scss +7 -0
  123. package/src/menu/sub-menu/sub-menu.ts +243 -0
  124. package/src/pagination/index.ts +1 -0
  125. package/src/pagination/pagination.scss +59 -0
  126. package/src/pagination/pagination.ts +135 -0
  127. package/src/peacock-loader.ts +39 -11
  128. package/src/popover/PopoverController.ts +13 -7
  129. package/src/snackbar/demo/index.html +29 -0
  130. package/src/snackbar/index.ts +1 -0
  131. package/src/snackbar/snackbar.scss +73 -0
  132. package/src/snackbar/snackbar.ts +151 -0
  133. package/src/table/index.ts +1 -0
  134. package/src/table/table.scss +174 -0
  135. package/src/table/table.ts +475 -0
  136. package/src/tabs/tab-group.ts +63 -28
  137. package/src/tabs/tab-panel.scss +3 -3
  138. package/src/tabs/tab-panel.ts +3 -0
  139. package/src/tabs/tab.scss +76 -2
  140. package/src/tabs/tab.ts +29 -6
  141. package/src/tabs/tabs.scss +6 -5
  142. package/src/tabs/tabs.ts +19 -5
  143. package/src/text/text.css-component.scss +6 -3
  144. package/src/tooltip/tooltip.scss +16 -13
  145. package/src/tooltip/tooltip.ts +7 -9
  146. package/src/tree-view/demo/index.html +57 -0
  147. package/src/tree-view/index.ts +2 -0
  148. package/src/tree-view/tree-node.scss +101 -0
  149. package/src/tree-view/tree-node.ts +268 -0
  150. package/src/tree-view/tree-view.scss +12 -0
  151. package/src/tree-view/tree-view.ts +182 -0
  152. package/src/tree-view/wc-tree-view.ts +9 -0
  153. package/dist/class-map-DpeNtqCn.js.map +0 -1
  154. package/dist/slider-Dk9CFWTG.js.map +0 -1
  155. package/dist/src/menu/menu-list/menu-list.d.ts +0 -22
  156. package/dist/state-8v48Exzh.js +0 -10
  157. package/dist/state-8v48Exzh.js.map +0 -1
  158. package/dist/transform-DRuHEvar.js.map +0 -1
  159. package/src/menu/menu-list/menu-list.ts +0 -48
@@ -0,0 +1,475 @@
1
+ import { html, LitElement, nothing } from 'lit';
2
+ import { property, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
5
+ import { throttle } from '../utils.js';
6
+ import styles from './table.scss';
7
+
8
+ const DEFAULT_CELL_WIDTH = 16; // in rem
9
+
10
+ export interface TableColumn {
11
+ name: string;
12
+ label: string;
13
+ width?: number;
14
+ fixed?: boolean;
15
+ template?: (row: any, column: TableColumn) => string;
16
+ }
17
+
18
+ /**
19
+ * @label Table
20
+ * @tag wc-table
21
+ * @rawTag table
22
+ * @summary A configurable component for displaying tabular data.
23
+ * @overview
24
+ * <p>The table component displays rows of data with support for sorting, pagination, row selection, and fixed columns.</p>
25
+ *
26
+ * @fires {CustomEvent} cell-click - Dispatched when a table cell is clicked.
27
+ * @fires {CustomEvent} selection-change - Dispatched when the row selection changes.
28
+ * @fires {CustomEvent} sort - Dispatched when the table is sorted.
29
+ * @fires {CustomEvent} page - Dispatched when the page or page size changes.
30
+ *
31
+ * @example
32
+ * ```html
33
+ * <wc-table columns="[{'name': 'name','label': 'Name','width': 16},{'name': 'age','label': 'Age','width': 7}]" data="[{'name': 'John','age': 30},{'name': 'Jane','age': 25}]"></wc-table>
34
+ * ```
35
+ * @tags display, data
36
+ */
37
+ export class Table extends LitElement {
38
+ static styles = [styles];
39
+
40
+ /**
41
+ * Grid columns configuration.
42
+ * Each column can have: name, label, width (px), fixed (boolean), template (function).
43
+ */
44
+ @property({ type: Array })
45
+ columns: TableColumn[] = [];
46
+
47
+ /**
48
+ * Grid data to display on table.
49
+ */
50
+ @property({ type: Array })
51
+ data: any[] = [];
52
+
53
+ /**
54
+ * Row selection type. Set to `"checkbox"` to enable checkbox selection.
55
+ */
56
+ @property({ type: String, attribute: 'selection-type' })
57
+ selectionType: 'checkbox' | undefined;
58
+
59
+ /**
60
+ * Array of selected row key values.
61
+ */
62
+ @property({ type: Array, attribute: 'selected-row-keys' })
63
+ selectedRowKeys: string[] = [];
64
+
65
+ /**
66
+ * The field name used as the unique key for each row. Defaults to `"id"`.
67
+ */
68
+ @property({ type: String, attribute: 'key-field' })
69
+ keyField: string = 'id';
70
+
71
+ /**
72
+ * If true, sorting and pagination are managed externally (controlled mode).
73
+ */
74
+ @property({ type: Boolean })
75
+ managed: boolean = false;
76
+
77
+ /**
78
+ * If true, columns are sortable. Defaults to `true`.
79
+ */
80
+ @property({ type: Boolean })
81
+ sortable: boolean = true;
82
+
83
+ /**
84
+ * The field name currently used for sorting.
85
+ */
86
+ @property({ type: String, attribute: 'sort-by' })
87
+ sortBy: string = '';
88
+
89
+ /**
90
+ * The current sort order. Possible values are `"asc"` and `"desc"`. Defaults to `"asc"`.
91
+ */
92
+ @property({ type: String, attribute: 'sort-order' })
93
+ sortOrder: 'asc' | 'desc' = 'asc';
94
+
95
+ /**
96
+ * If true, pagination is enabled. Defaults to `true`.
97
+ */
98
+ @property({ type: Boolean })
99
+ paginate: boolean = false;
100
+
101
+ /**
102
+ * The current page number (1-based). Defaults to `1`.
103
+ */
104
+ @property({ type: Number })
105
+ page: number = 1;
106
+
107
+ /**
108
+ * The number of rows per page. Defaults to `10`.
109
+ */
110
+ @property({ type: Number, attribute: 'page-size' })
111
+ pageSize: number = 10;
112
+
113
+ /**
114
+ * Total number of items (used in managed/controlled mode).
115
+ */
116
+ @property({ type: Number, attribute: 'total-items' })
117
+ totalItems: number | undefined;
118
+
119
+ /**
120
+ * Headline text shown when the table has no data.
121
+ */
122
+ @property({ type: String, attribute: 'empty-state-headline' })
123
+ emptyStateHeadline: string = 'No items';
124
+
125
+ /**
126
+ * Description text shown when the table has no data.
127
+ */
128
+ @property({ type: String, attribute: 'empty-state-description' })
129
+ emptyStateDescription: string = 'There are no items to display';
130
+
131
+ @state()
132
+ private hoveredCell: { row?: any; column?: any } = {};
133
+
134
+ @state()
135
+ private isSelectAll: boolean = false;
136
+
137
+ @state()
138
+ private isSelectAllIntermediate: boolean = false;
139
+
140
+ @state()
141
+ private isHorizontallyScrolled: boolean = false;
142
+
143
+ private onCellMouseOverThrottled = throttle((row: any, column: any) => {
144
+ this.hoveredCell = { row, column };
145
+ }, 30);
146
+
147
+ private onSelectAllClick = () => {
148
+ this.isSelectAll = !this.isSelectAll;
149
+ let selectedRowKeys: string[] = [];
150
+ if (this.isSelectAll) {
151
+ selectedRowKeys = this.data
152
+ .slice((this.page - 1) * this.pageSize, this.page * this.pageSize)
153
+ .map(row => row[this.keyField]);
154
+ }
155
+ this.onSelectChange(selectedRowKeys);
156
+ };
157
+
158
+ private onRowSelectClick = (row: any) => {
159
+ let selectedRowKeys = [...this.selectedRowKeys];
160
+ if (selectedRowKeys.includes(row[this.keyField])) {
161
+ this.isSelectAll = false;
162
+ selectedRowKeys = selectedRowKeys.filter(
163
+ rowId => rowId !== row[this.keyField],
164
+ );
165
+ } else {
166
+ selectedRowKeys.push(row[this.keyField]);
167
+ }
168
+ this.onSelectChange(selectedRowKeys);
169
+ };
170
+
171
+ private onSelectChange(selectedRowKeys: string[]) {
172
+ this.selectedRowKeys = selectedRowKeys;
173
+ this.dispatchEvent(
174
+ new CustomEvent('selection-change', {
175
+ detail: {
176
+ value: this.selectedRowKeys,
177
+ isSelectAll: this.isSelectAll,
178
+ },
179
+ bubbles: true,
180
+ composed: true,
181
+ }),
182
+ );
183
+ }
184
+
185
+ private onCellClick(row: any, col: TableColumn, evt: MouseEvent) {
186
+ this.dispatchEvent(
187
+ new CustomEvent('cell-click', {
188
+ detail: {
189
+ record: row,
190
+ column: col,
191
+ altKey: evt.altKey,
192
+ ctrlKey: evt.ctrlKey,
193
+ metaKey: evt.metaKey,
194
+ shiftKey: evt.shiftKey,
195
+ },
196
+ bubbles: true,
197
+ composed: true,
198
+ }),
199
+ );
200
+ }
201
+
202
+ private onScrollContainer = (event: Event) => {
203
+ const target = event.target as HTMLElement;
204
+ this.isHorizontallyScrolled = !!target.scrollLeft;
205
+ };
206
+
207
+ private get totalColumnsWidth(): number {
208
+ let total = 0;
209
+ if (this.selectionType === 'checkbox') {
210
+ total += 3; // approximate checkbox column width in rem
211
+ }
212
+ this.columns.forEach(col => {
213
+ total += col.width ?? DEFAULT_CELL_WIDTH;
214
+ });
215
+ return total;
216
+ }
217
+
218
+ private getTotalItems(): number {
219
+ if (this.paginate && !this.managed) return this.data.length;
220
+ return this.totalItems ?? 0;
221
+ }
222
+
223
+ private getSortIcon(col: TableColumn): string {
224
+ if (this.sortBy === col.name) {
225
+ return this.sortOrder === 'asc' ? 'keyboard_arrow_up' : 'keyboard_arrow_down';
226
+ }
227
+ return '';
228
+ }
229
+
230
+ private onSortClick(col: TableColumn) {
231
+ if (this.sortBy === col.name) {
232
+ if (this.sortOrder === 'asc') {
233
+ this.sortOrder = 'desc';
234
+ } else {
235
+ this.sortBy = '';
236
+ }
237
+ } else {
238
+ this.sortBy = col.name;
239
+ this.sortOrder = 'asc';
240
+ }
241
+ this.dispatchEvent(
242
+ new CustomEvent('sort', {
243
+ detail: { sortBy: this.sortBy, sortOrder: this.sortOrder },
244
+ bubbles: true,
245
+ composed: true,
246
+ }),
247
+ );
248
+ }
249
+
250
+ private renderHeader() {
251
+ const fixedCols: any[] = [];
252
+ const scrollCols: any[] = [];
253
+
254
+ if (this.selectionType === 'checkbox') {
255
+ fixedCols.push(html`
256
+ <div class="col col-checkbox center">
257
+ <div class="col-content">
258
+ <wc-checkbox
259
+ class="checkbox"
260
+ .value=${this.isSelectAll}
261
+ .indeterminate=${this.isSelectAllIntermediate}
262
+ @change=${this.onSelectAllClick}
263
+ ></wc-checkbox>
264
+ </div>
265
+ </div>
266
+ `);
267
+ }
268
+
269
+ this.columns.forEach(col => {
270
+ const colWidth = col.width
271
+ ? parseInt(String(col.width), 10)
272
+ : DEFAULT_CELL_WIDTH;
273
+ const colEl = html`
274
+ <div
275
+ class=${classMap({ col: true, sort: this.sortBy === col.name })}
276
+ style="width: ${colWidth}rem"
277
+ >
278
+ <div class="col-content">
279
+ <div class="col-text">${col.label}</div>
280
+ <div class="col-actions">
281
+ ${this.sortable
282
+ ? html`
283
+ <wc-button
284
+ class="col-action"
285
+ color="secondary"
286
+ variant="text"
287
+ @click=${() => this.onSortClick(col)}
288
+ >
289
+ <wc-icon
290
+ slot="icon"
291
+ name=${this.getSortIcon(col)}
292
+ ></wc-icon>
293
+ </wc-button>
294
+ `
295
+ : nothing}
296
+ </div>
297
+ </div>
298
+ </div>
299
+ `;
300
+ if (col.fixed) {
301
+ fixedCols.push(colEl);
302
+ } else {
303
+ scrollCols.push(colEl);
304
+ }
305
+ });
306
+
307
+ return html`
308
+ <div class="header">
309
+ <div class="row" style="min-width: ${this.totalColumnsWidth}rem">
310
+ <div class="fixed-columns columns-container">${fixedCols}</div>
311
+ <div class="scrollable-columns columns-container">${scrollCols}</div>
312
+ </div>
313
+ </div>
314
+ `;
315
+ }
316
+
317
+ private renderBody() {
318
+ let data = [...this.data];
319
+
320
+ if (!this.managed) {
321
+ if (this.sortable && this.sortBy) {
322
+ data = data.sort((a, b) => {
323
+ if (a[this.sortBy] < b[this.sortBy])
324
+ return this.sortOrder === 'asc' ? -1 : 1;
325
+ if (a[this.sortBy] > b[this.sortBy])
326
+ return this.sortOrder === 'asc' ? 1 : -1;
327
+ return 0;
328
+ });
329
+ }
330
+ if (this.paginate) {
331
+ data = data.slice(
332
+ (this.page - 1) * this.pageSize,
333
+ this.page * this.pageSize,
334
+ );
335
+ }
336
+ }
337
+
338
+ const rows = data.map(row => {
339
+ const fixedCols: any[] = [];
340
+ const scrollCols: any[] = [];
341
+
342
+ if (this.selectionType === 'checkbox') {
343
+ fixedCols.push(html`
344
+ <div class="col center col-checkbox">
345
+ <div class="col-content">
346
+ <wc-checkbox
347
+ class="checkbox"
348
+ .value=${this.selectedRowKeys.includes(row[this.keyField])}
349
+ @change=${() => this.onRowSelectClick(row)}
350
+ ></wc-checkbox>
351
+ </div>
352
+ </div>
353
+ `);
354
+ }
355
+
356
+ this.columns.forEach(column => {
357
+ const colWidth = column.width
358
+ ? parseInt(String(column.width), 10)
359
+ : DEFAULT_CELL_WIDTH;
360
+ const colEl = html`
361
+ <div
362
+ tabindex="0"
363
+ class=${classMap({
364
+ col: true,
365
+ 'col-hover':
366
+ this.hoveredCell.row === row &&
367
+ this.hoveredCell.column === column,
368
+ })}
369
+ style="width: ${colWidth}rem"
370
+ @mouseover=${() => this.onCellMouseOverThrottled(row, column)}
371
+ @focus=${() => this.onCellMouseOverThrottled(row, column)}
372
+ @keydown=${(event: KeyboardEvent) => {
373
+ if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
374
+ const elem = event.target as HTMLElement;
375
+ window.navigator.clipboard
376
+ .writeText(elem.innerText)
377
+ .catch(() => {});
378
+ }
379
+ }}
380
+ @click=${(evt: MouseEvent) => {
381
+ const selection = window.getSelection();
382
+ if (selection?.type !== 'Range') {
383
+ this.onCellClick(row, column, evt);
384
+ }
385
+ }}
386
+ >
387
+ <div class="col-content">
388
+ ${column.template
389
+ ? html`<div class="col-template">
390
+ ${unsafeHTML(column.template(row, column))}
391
+ </div>`
392
+ : html`<div class="col-text" title=${row?.[column.name] ?? ''}>
393
+ ${row?.[column.name]}
394
+ </div>`}
395
+ </div>
396
+ </div>
397
+ `;
398
+ if (column.fixed) {
399
+ fixedCols.push(colEl);
400
+ } else {
401
+ scrollCols.push(colEl);
402
+ }
403
+ });
404
+
405
+ return html`
406
+ <div
407
+ class=${classMap({
408
+ row: true,
409
+ 'row-hover': this.hoveredCell.row === row,
410
+ })}
411
+ style="min-width: ${this.totalColumnsWidth}rem"
412
+ >
413
+ <div class="fixed-columns columns-container">${fixedCols}</div>
414
+ <div class="scrollable-columns columns-container">${scrollCols}</div>
415
+ </div>
416
+ `;
417
+ });
418
+
419
+ return html`<div class="body">${rows}</div>`;
420
+ }
421
+
422
+ private renderEmptyState() {
423
+ return html`
424
+ <div class="empty-table">
425
+ <wc-empty-state
426
+ class="empty-state content-center"
427
+ headline=${this.emptyStateHeadline}
428
+ description=${this.emptyStateDescription}
429
+ ></wc-empty-state>
430
+ </div>
431
+ `;
432
+ }
433
+
434
+ private renderPagination() {
435
+ if (!this.paginate) return nothing;
436
+
437
+ return html`
438
+ <wc-pagination
439
+ .page=${this.page}
440
+ .pageSize=${this.pageSize}
441
+ .totalItems=${this.getTotalItems()}
442
+ @page=${(e: CustomEvent) => {
443
+ this.page = e.detail.page;
444
+ this.pageSize = e.detail.pageSize;
445
+ this.dispatchEvent(
446
+ new CustomEvent('page', {
447
+ detail: { page: this.page, pageSize: this.pageSize },
448
+ bubbles: true,
449
+ composed: true,
450
+ }),
451
+ );
452
+ }}
453
+ ></wc-pagination>
454
+ `;
455
+ }
456
+
457
+ render() {
458
+ const tableClasses = {
459
+ table: true,
460
+ sortable: this.sortable,
461
+ paginate: this.paginate,
462
+ 'horizontal-scrolled': this.isHorizontallyScrolled,
463
+ };
464
+
465
+ return html`
466
+ <div class=${classMap(tableClasses)}>
467
+ <div class="table-scroll-container" @scroll=${this.onScrollContainer}>
468
+ ${this.renderHeader()}
469
+ ${this.data.length ? this.renderBody() : this.renderEmptyState()}
470
+ </div>
471
+ <div class="table-footer">${this.renderPagination()}</div>
472
+ </div>
473
+ `;
474
+ }
475
+ }
@@ -1,6 +1,8 @@
1
1
  import { html, LitElement } from 'lit';
2
2
  import { property } from 'lit/decorators.js';
3
3
  import styles from './tab-group.scss';
4
+ import { Tabs } from './tabs.js';
5
+ import { TabPanel } from './tab-panel.js';
4
6
 
5
7
  /**
6
8
  * @label Tab Group
@@ -29,18 +31,20 @@ import styles from './tab-group.scss';
29
31
  export class TabGroup extends LitElement {
30
32
  static styles = [styles];
31
33
 
34
+ static Tabs = Tabs;
35
+
36
+ static TabPanel = TabPanel;
37
+
32
38
  @property({ reflect: true })
33
39
  variant: 'line' | 'line-secondary' | 'contained' | 'pill' = 'line';
34
40
 
35
- private uid = crypto.randomUUID();
36
-
37
41
  connectedCallback() {
38
42
  super.connectedCallback();
39
- this.addEventListener('base-tab-click', this.onTabClick as EventListener);
43
+ this.addEventListener('tab-click', this.onTabClick);
40
44
  }
41
45
 
42
46
  disconnectedCallback() {
43
- this.removeEventListener('base-tab-click', this.onTabClick as EventListener);
47
+ this.removeEventListener('tab-click', this.onTabClick);
44
48
  super.disconnectedCallback();
45
49
  }
46
50
 
@@ -54,15 +58,18 @@ export class TabGroup extends LitElement {
54
58
  const targetValue = custom.detail?.target || custom.detail?.value;
55
59
  if (targetValue) {
56
60
  this.selectTab(targetValue);
61
+ } else if (typeof custom.detail?.index === 'number') {
62
+ this.selectTabByIndex(custom.detail.index);
57
63
  }
58
64
  };
59
65
 
60
66
  selectTab(target: string) {
61
67
  const tabs = this.getTabs();
62
- tabs.forEach((tab: HTMLElement) => {
68
+ for (const tab of tabs) {
69
+ (tab as any).active = false;
63
70
  (tab as any).selected = false;
64
71
  tab.classList.remove('previous-tab', 'next-tab');
65
- });
72
+ }
66
73
 
67
74
  let selectedIndex = -1;
68
75
  tabs.forEach((tab: HTMLElement, index: number) => {
@@ -74,6 +81,7 @@ export class TabGroup extends LitElement {
74
81
 
75
82
  if (selectedIndex >= 0) {
76
83
  const selectedTab = tabs[selectedIndex];
84
+ (selectedTab as any).active = true;
77
85
  (selectedTab as any).selected = true;
78
86
  if (tabs[selectedIndex - 1]) {
79
87
  tabs[selectedIndex - 1].classList.add('previous-tab');
@@ -84,49 +92,76 @@ export class TabGroup extends LitElement {
84
92
  }
85
93
 
86
94
  const panels = this.getTabPanels();
87
- panels.forEach(panel => {
95
+ for (const panel of panels) {
88
96
  const panelValue = panel.getAttribute('value');
89
97
  (panel as any).active = panelValue === target;
90
- });
98
+ }
91
99
  }
92
100
 
93
- private getTabs(): NodeListOf<HTMLElement> {
94
- return this.querySelectorAll(':scope > tabs-list base-tab');
101
+ selectTabByIndex(index: number) {
102
+ const tabs = this.getTabs();
103
+ for (const tab of tabs) {
104
+ (tab as any).active = false;
105
+ (tab as any).selected = false;
106
+ tab.classList.remove('previous-tab', 'next-tab');
107
+ }
108
+
109
+ if (index >= 0 && index < tabs.length) {
110
+ (tabs[index] as any).active = true;
111
+ (tabs[index] as any).selected = true;
112
+ if (tabs[index - 1]) tabs[index - 1].classList.add('previous-tab');
113
+ if (tabs[index + 1]) tabs[index + 1].classList.add('next-tab');
114
+ }
115
+
116
+ const panels = this.getTabPanels();
117
+ for (let i = 0; i < panels.length; i += 1) {
118
+ const panel = panels[i];
119
+ (panel as any).active = i === index;
120
+ }
95
121
  }
96
122
 
97
- private getTabPanels(): NodeListOf<HTMLElement> {
98
- return this.querySelectorAll(':scope > base-tab-panel');
123
+ private getTabs(): HTMLElement[] {
124
+ return Array.from(this.querySelectorAll(':scope > wc-tabs wc-tab'));
125
+ }
126
+
127
+ private getTabPanels(): HTMLElement[] {
128
+ return Array.from(this.querySelectorAll(':scope > wc-tab-panel'));
99
129
  }
100
130
 
101
131
  private getTabList(): HTMLElement | null {
102
- return this.querySelector(':scope > tabs-list');
132
+ return this.querySelector(':scope > wc-tabs');
103
133
  }
104
134
 
105
135
  private tabsHaveTarget(): boolean {
106
- return !!this.querySelector(':scope > tabs-list base-tab[target]');
136
+ return !!this.querySelector(':scope > wc-tabs wc-tab[target]');
107
137
  }
108
138
 
109
139
  private initializeTabs() {
110
140
  const tabs = Array.from(this.getTabs());
111
141
  if (!this.tabsHaveTarget()) {
112
-
113
- this.getTabPanels().forEach((panel, index) => {
114
- if (!panel.getAttribute('value')) {
115
- panel.setAttribute('value', `tab-${this.uid}-${index}`);
116
- }
117
- });
118
-
119
- if (tabs.length > 0) {
120
- const firstTarget = tabs[0].getAttribute('target');
121
- if (firstTarget) {
122
- this.selectTab(firstTarget);
123
- }
124
- }
142
+ // No target/value attributes — use index-based activation
143
+ const selectedIndex = tabs.findIndex(
144
+ tab =>
145
+ tab.hasAttribute('active') ||
146
+ (tab as any).active ||
147
+ tab.hasAttribute('selected') ||
148
+ (tab as any).selected,
149
+ );
150
+ this.selectTabByIndex(selectedIndex >= 0 ? selectedIndex : 0);
125
151
  } else {
126
- const selectedTab = this.querySelector(':scope > tabs-list base-tab[selected]') as HTMLElement;
152
+ const selectedTab = tabs.find(
153
+ tab =>
154
+ tab.hasAttribute('active') ||
155
+ (tab as any).active ||
156
+ tab.hasAttribute('selected') ||
157
+ (tab as any).selected,
158
+ );
127
159
  if (selectedTab) {
128
160
  const selectedTarget = selectedTab.getAttribute('target');
129
161
  if (selectedTarget) this.selectTab(selectedTarget);
162
+ } else if (tabs.length > 0) {
163
+ const firstTarget = tabs[0].getAttribute('target');
164
+ if (firstTarget) this.selectTab(firstTarget);
130
165
  }
131
166
  }
132
167
  }
@@ -3,10 +3,10 @@
3
3
  @include mixin.base-styles;
4
4
 
5
5
  :host {
6
- display: block;
6
+ display: none;
7
7
  flex: 1;
8
8
  }
9
9
 
10
- :host([hidden]) {
11
- display: none;
10
+ :host([active]) {
11
+ display: block;
12
12
  }
@@ -6,6 +6,7 @@ import styles from './tab-panel.scss';
6
6
  * @label Tab Panel
7
7
  * @tag wc-tab-panel
8
8
  * @rawTag tab-panel
9
+ * @parentRawTag tab-group
9
10
  *
10
11
  * @summary Content panel for tabs.
11
12
  * @overview
@@ -22,6 +23,8 @@ export class TabPanel extends LitElement {
22
23
 
23
24
  @property({ reflect: true }) value?: string;
24
25
 
26
+ @property({ type: Boolean, reflect: true }) active = false;
27
+
25
28
  render() {
26
29
  return html`<slot></slot>`;
27
30
  }