@krumio/trailhand-ui 1.4.1 → 1.5.0

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 (70) hide show
  1. package/README.md +119 -52
  2. package/dist/components/action-menu/action-menu.d.ts +79 -0
  3. package/dist/components/action-menu/action-menu.d.ts.map +1 -0
  4. package/dist/components/action-menu/action-menu.js +333 -0
  5. package/dist/components/action-menu/action-menu.js.map +1 -0
  6. package/dist/components/action-menu/action-menu.stories.d.ts +261 -0
  7. package/dist/components/action-menu/action-menu.stories.d.ts.map +1 -0
  8. package/dist/components/action-menu/action-menu.stories.js +363 -0
  9. package/dist/components/action-menu/action-menu.stories.js.map +1 -0
  10. package/dist/components/action-menu/index.d.ts +2 -0
  11. package/dist/components/action-menu/index.d.ts.map +1 -0
  12. package/dist/components/action-menu/index.js +2 -0
  13. package/dist/components/action-menu/index.js.map +1 -0
  14. package/dist/components/data-table/data-table.d.ts +191 -0
  15. package/dist/components/data-table/data-table.d.ts.map +1 -0
  16. package/dist/components/data-table/data-table.js +857 -0
  17. package/dist/components/data-table/data-table.js.map +1 -0
  18. package/dist/components/data-table/data-table.stories.d.ts +507 -0
  19. package/dist/components/data-table/data-table.stories.d.ts.map +1 -0
  20. package/dist/components/data-table/data-table.stories.js +601 -0
  21. package/dist/components/data-table/data-table.stories.js.map +1 -0
  22. package/dist/components/data-table/index.d.ts +2 -0
  23. package/dist/components/data-table/index.d.ts.map +1 -0
  24. package/dist/components/data-table/index.js +2 -0
  25. package/dist/components/data-table/index.js.map +1 -0
  26. package/dist/components/th-card/index.d.ts +3 -0
  27. package/dist/components/th-card/index.d.ts.map +1 -0
  28. package/dist/components/th-card/index.js +2 -0
  29. package/dist/components/th-card/index.js.map +1 -0
  30. package/dist/components/th-card/th-card.d.ts +78 -0
  31. package/dist/components/th-card/th-card.d.ts.map +1 -0
  32. package/dist/components/th-card/th-card.js +449 -0
  33. package/dist/components/th-card/th-card.js.map +1 -0
  34. package/dist/components/th-card/th-card.stories.d.ts +232 -0
  35. package/dist/components/th-card/th-card.stories.d.ts.map +1 -0
  36. package/dist/components/th-card/th-card.stories.js +385 -0
  37. package/dist/components/th-card/th-card.stories.js.map +1 -0
  38. package/dist/components/th-tag/index.d.ts +3 -0
  39. package/dist/components/th-tag/index.d.ts.map +1 -0
  40. package/dist/components/th-tag/index.js +2 -0
  41. package/dist/components/th-tag/index.js.map +1 -0
  42. package/dist/components/th-tag/th-tag.d.ts +65 -0
  43. package/dist/components/th-tag/th-tag.d.ts.map +1 -0
  44. package/dist/components/th-tag/th-tag.js +307 -0
  45. package/dist/components/th-tag/th-tag.js.map +1 -0
  46. package/dist/components/th-tag/th-tag.stories.d.ts +277 -0
  47. package/dist/components/th-tag/th-tag.stories.d.ts.map +1 -0
  48. package/dist/components/th-tag/th-tag.stories.js +415 -0
  49. package/dist/components/th-tag/th-tag.stories.js.map +1 -0
  50. package/dist/components/toggle-switch/index.d.ts +2 -0
  51. package/dist/components/toggle-switch/index.d.ts.map +1 -0
  52. package/dist/components/toggle-switch/index.js +2 -0
  53. package/dist/components/toggle-switch/index.js.map +1 -0
  54. package/dist/components/toggle-switch/toggle-switch.d.ts +38 -0
  55. package/dist/components/toggle-switch/toggle-switch.d.ts.map +1 -0
  56. package/dist/components/toggle-switch/toggle-switch.js +175 -0
  57. package/dist/components/toggle-switch/toggle-switch.js.map +1 -0
  58. package/dist/components/toggle-switch/toggle-switch.stories.d.ts +239 -0
  59. package/dist/components/toggle-switch/toggle-switch.stories.d.ts.map +1 -0
  60. package/dist/components/toggle-switch/toggle-switch.stories.js +408 -0
  61. package/dist/components/toggle-switch/toggle-switch.stories.js.map +1 -0
  62. package/dist/design-system/color-palette.stories.d.ts +24 -0
  63. package/dist/design-system/color-palette.stories.d.ts.map +1 -0
  64. package/dist/design-system/color-palette.stories.js +361 -0
  65. package/dist/design-system/color-palette.stories.js.map +1 -0
  66. package/dist/index.d.ts +4 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +5 -0
  69. package/dist/index.js.map +1 -0
  70. package/package.json +11 -8
@@ -0,0 +1,857 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { LitElement, html, css } from 'lit';
8
+ import { property, state } from 'lit/decorators.js';
9
+ import '../action-menu/action-menu';
10
+ import 'iconify-icon';
11
+ import { addIcon } from 'iconify-icon';
12
+ import chevronUp from '@iconify/icons-heroicons/chevron-up-20-solid';
13
+ import chevronDown from '@iconify/icons-heroicons/chevron-down-20-solid';
14
+ import chevronLeft from '@iconify/icons-heroicons/chevron-left-20-solid';
15
+ import chevronRight from '@iconify/icons-heroicons/chevron-right-20-solid';
16
+ // Pre-load icons to avoid CDN delay
17
+ addIcon('heroicons:chevron-up-20-solid', chevronUp);
18
+ addIcon('heroicons:chevron-down-20-solid', chevronDown);
19
+ addIcon('heroicons:chevron-left-20-solid', chevronLeft);
20
+ addIcon('heroicons:chevron-right-20-solid', chevronRight);
21
+ /**
22
+ * Data table formatters for common column types
23
+ */
24
+ export const dataTableFormatters = {
25
+ /**
26
+ * Format a date value as relative time (e.g., "5d", "3h", "15m")
27
+ */
28
+ age: (value) => {
29
+ if (!value)
30
+ return '-';
31
+ const date = new Date(value);
32
+ const now = new Date();
33
+ const diffMs = now.getTime() - date.getTime();
34
+ const diffMins = Math.floor(diffMs / 60000);
35
+ const diffHours = Math.floor(diffMs / 3600000);
36
+ const diffDays = Math.floor(diffMs / 86400000);
37
+ if (diffDays > 0)
38
+ return `${diffDays}d`;
39
+ if (diffHours > 0)
40
+ return `${diffHours}h`;
41
+ if (diffMins > 0)
42
+ return `${diffMins}m`;
43
+ return 'Just now';
44
+ },
45
+ /**
46
+ * Format a date as a localized date string
47
+ */
48
+ date: (value) => {
49
+ if (!value)
50
+ return '-';
51
+ return new Date(value).toLocaleDateString();
52
+ },
53
+ /**
54
+ * Format a date as a localized date and time string
55
+ */
56
+ dateTime: (value) => {
57
+ if (!value)
58
+ return '-';
59
+ return new Date(value).toLocaleString();
60
+ },
61
+ /**
62
+ * Format memory bytes to human readable format (B, KB, MB, GB, TB)
63
+ */
64
+ memory: (value) => {
65
+ if (!value || value === 0)
66
+ return '0 B';
67
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
68
+ let val = Number(value);
69
+ let unitIndex = 0;
70
+ while (val >= 1024 && unitIndex < units.length - 1) {
71
+ val = val / 1024;
72
+ unitIndex++;
73
+ }
74
+ return `${Math.round(val)} ${units[unitIndex]}`;
75
+ },
76
+ /**
77
+ * Format millicpus value
78
+ */
79
+ milliCPUs: (value) => {
80
+ if (!value || value === 0)
81
+ return '0';
82
+ const formatted = Math.round(Number(value)).toString();
83
+ return formatted === '0' ? '0' : formatted;
84
+ },
85
+ };
86
+ /**
87
+ * A feature-rich data table component with sorting, filtering, and pagination.
88
+ * Supports custom cell rendering, row actions, and various formatters.
89
+ */
90
+ export class DataTable extends LitElement {
91
+ constructor() {
92
+ super(...arguments);
93
+ this.columns = [];
94
+ this.rows = [];
95
+ this.rowsPerPage = 10;
96
+ this.searchable = true;
97
+ this.sortable = true;
98
+ this.paginated = true;
99
+ this.loading = false;
100
+ this.keyField = 'id';
101
+ this.rowActions = true;
102
+ this.rowActionsWidth = 40;
103
+ this.emptyMessage = 'No data available';
104
+ this.noResultsMessage = 'No results found';
105
+ // Internal state
106
+ this._searchQuery = '';
107
+ this._currentPage = 1;
108
+ this._sortColumn = null;
109
+ this._sortDirection = 'asc';
110
+ }
111
+ /**
112
+ * Get nested value from object using dot notation
113
+ * @param obj - The object to extract value from
114
+ * @param path - The path (e.g., 'user.name')
115
+ * @returns The value at the path
116
+ * @private
117
+ */
118
+ _getNestedValue(obj, path) {
119
+ return path.split('.').reduce((current, key) => current?.[key], obj);
120
+ }
121
+ /**
122
+ * Format a cell value using column formatter
123
+ * @param row - The row data
124
+ * @param column - The column definition
125
+ * @returns The formatted value
126
+ * @private
127
+ */
128
+ _formatValue(row, column) {
129
+ const value = this._getNestedValue(row, column.field);
130
+ if (column.formatter) {
131
+ // If formatter is a string, use dataTableFormatters
132
+ if (typeof column.formatter === 'string') {
133
+ const formatter = dataTableFormatters[column.formatter];
134
+ if (formatter) {
135
+ return formatter(value);
136
+ }
137
+ }
138
+ else if (typeof column.formatter === 'function') {
139
+ // Otherwise, use custom formatter function
140
+ return column.formatter(value, row);
141
+ }
142
+ }
143
+ return value;
144
+ }
145
+ /**
146
+ * Get link URL for a cell if column has link property
147
+ * @param row - The row data
148
+ * @param column - The column definition
149
+ * @returns The link URL or null
150
+ * @private
151
+ */
152
+ _getLinkUrl(row, column) {
153
+ if (!column.link) {
154
+ return null;
155
+ }
156
+ if (typeof column.link === 'string') {
157
+ // Link is a field name in the row data
158
+ return this._getNestedValue(row, column.link);
159
+ }
160
+ else if (typeof column.link === 'function') {
161
+ // Link is a function that takes the row and returns a URL
162
+ return column.link(row);
163
+ }
164
+ return null;
165
+ }
166
+ /**
167
+ * Render cell content with optional link
168
+ * @param row - The row data
169
+ * @param column - The column definition
170
+ * @returns The rendered cell content
171
+ * @private
172
+ */
173
+ _renderCellContent(row, column) {
174
+ const value = this._formatValue(row, column);
175
+ const linkUrl = this._getLinkUrl(row, column);
176
+ if (linkUrl) {
177
+ const target = column.linkTarget || '_self';
178
+ // For external links or _blank target, use regular <a> tag
179
+ if (target === '_blank' ||
180
+ linkUrl.startsWith('http://') ||
181
+ linkUrl.startsWith('https://')) {
182
+ const rel = target === '_blank' ? 'noopener noreferrer' : '';
183
+ return html `<a href="${linkUrl}" target="${target}" rel="${rel}"
184
+ >${value}</a
185
+ >`;
186
+ }
187
+ // For internal links, use a clickable element that emits a navigation event
188
+ return html `<a
189
+ href="${linkUrl}"
190
+ @click="${(e) => this._handleLinkClick(e, linkUrl, row)}"
191
+ >${value}</a
192
+ >`;
193
+ }
194
+ return value;
195
+ }
196
+ /**
197
+ * Handle link click - emit navigation event instead of following link
198
+ * @param event - The click event
199
+ * @param url - The URL to navigate to
200
+ * @param row - The row data
201
+ * @private
202
+ */
203
+ _handleLinkClick(event, url, row) {
204
+ event.preventDefault();
205
+ // Emit custom event for navigation
206
+ this.dispatchEvent(new CustomEvent('navigate', {
207
+ detail: { url, row },
208
+ bubbles: true,
209
+ composed: true,
210
+ }));
211
+ }
212
+ /**
213
+ * Get filtered rows based on search query
214
+ * @returns Filtered rows
215
+ * @private
216
+ */
217
+ get _filteredRows() {
218
+ if (!this._searchQuery || !this.searchable) {
219
+ return this.rows;
220
+ }
221
+ const query = this._searchQuery.toLowerCase();
222
+ return this.rows.filter((row) => {
223
+ return this.columns.some((column) => {
224
+ if (column.searchable === false) {
225
+ return false;
226
+ }
227
+ const value = this._getNestedValue(row, column.field);
228
+ return String(value).toLowerCase().includes(query);
229
+ });
230
+ });
231
+ }
232
+ /**
233
+ * Get sorted rows
234
+ * @returns Sorted rows
235
+ * @private
236
+ */
237
+ get _sortedRows() {
238
+ if (!this._sortColumn || !this.sortable) {
239
+ return this._filteredRows;
240
+ }
241
+ const column = this.columns.find((col) => col.field === this._sortColumn);
242
+ if (!column || column.sortable === false) {
243
+ return this._filteredRows;
244
+ }
245
+ return [...this._filteredRows].sort((a, b) => {
246
+ const aValue = this._getNestedValue(a, this._sortColumn);
247
+ const bValue = this._getNestedValue(b, this._sortColumn);
248
+ // Handle custom sort function
249
+ if (column.sortFn) {
250
+ return column.sortFn(a, b, this._sortDirection);
251
+ }
252
+ // Default sorting logic
253
+ let comparison = 0;
254
+ if (aValue < bValue) {
255
+ comparison = -1;
256
+ }
257
+ else if (aValue > bValue) {
258
+ comparison = 1;
259
+ }
260
+ return this._sortDirection === 'asc' ? comparison : -comparison;
261
+ });
262
+ }
263
+ /**
264
+ * Get paginated rows
265
+ * @returns Paginated rows
266
+ * @private
267
+ */
268
+ get _paginatedRows() {
269
+ if (!this.paginated) {
270
+ return this._sortedRows;
271
+ }
272
+ const start = (this._currentPage - 1) * this.rowsPerPage;
273
+ const end = start + this.rowsPerPage;
274
+ return this._sortedRows.slice(start, end);
275
+ }
276
+ /**
277
+ * Get total number of pages
278
+ * @returns Total pages
279
+ * @private
280
+ */
281
+ get _totalPages() {
282
+ if (!this.paginated) {
283
+ return 1;
284
+ }
285
+ return Math.ceil(this._sortedRows.length / this.rowsPerPage);
286
+ }
287
+ /**
288
+ * Get pagination info
289
+ * @returns Pagination info
290
+ * @private
291
+ */
292
+ get _paginationInfo() {
293
+ const start = (this._currentPage - 1) * this.rowsPerPage + 1;
294
+ const end = Math.min(this._currentPage * this.rowsPerPage, this._sortedRows.length);
295
+ return {
296
+ start,
297
+ end,
298
+ total: this._sortedRows.length,
299
+ };
300
+ }
301
+ /**
302
+ * Handle search input
303
+ * @param e - The input event
304
+ * @private
305
+ */
306
+ _handleSearch(e) {
307
+ const target = e.target;
308
+ this._searchQuery = target.value;
309
+ this._currentPage = 1;
310
+ }
311
+ /**
312
+ * Handle column sort
313
+ * @param columnField - The column field to sort by
314
+ * @private
315
+ */
316
+ _handleSort(columnField) {
317
+ const column = this.columns.find((col) => col.field === columnField);
318
+ if (!column || column.sortable === false || !this.sortable) {
319
+ return;
320
+ }
321
+ if (this._sortColumn === columnField) {
322
+ // Toggle direction
323
+ this._sortDirection = this._sortDirection === 'asc' ? 'desc' : 'asc';
324
+ }
325
+ else {
326
+ // New column, default to ascending
327
+ this._sortColumn = columnField;
328
+ this._sortDirection = 'asc';
329
+ }
330
+ this._currentPage = 1;
331
+ }
332
+ /**
333
+ * Navigate to a specific page
334
+ * @param page - The page number
335
+ */
336
+ goToPage(page) {
337
+ if (page >= 1 && page <= this._totalPages) {
338
+ this._currentPage = page;
339
+ }
340
+ }
341
+ /**
342
+ * Navigate to the next page
343
+ */
344
+ nextPage() {
345
+ this.goToPage(this._currentPage + 1);
346
+ }
347
+ /**
348
+ * Navigate to the previous page
349
+ */
350
+ prevPage() {
351
+ this.goToPage(this._currentPage - 1);
352
+ }
353
+ /**
354
+ * Reset search query
355
+ */
356
+ resetSearch() {
357
+ this._searchQuery = '';
358
+ }
359
+ /**
360
+ * Reset sort to default state
361
+ */
362
+ resetSort() {
363
+ this._sortColumn = null;
364
+ this._sortDirection = 'asc';
365
+ }
366
+ /**
367
+ * Render sort icon
368
+ * @param column - The column definition
369
+ * @returns TemplateResult
370
+ * @private
371
+ */
372
+ _renderSortIcon(column) {
373
+ if (!this.sortable || column.sortable === false) {
374
+ return '';
375
+ }
376
+ const isSorted = this._sortColumn === column.field;
377
+ if (isSorted) {
378
+ if (this._sortDirection === 'asc') {
379
+ return html `
380
+ <iconify-icon
381
+ class="data-table__sort-icon"
382
+ icon="heroicons:chevron-up-20-solid"
383
+ ></iconify-icon>
384
+ `;
385
+ }
386
+ else {
387
+ return html `
388
+ <iconify-icon
389
+ class="data-table__sort-icon"
390
+ icon="heroicons:chevron-down-20-solid"
391
+ ></iconify-icon>
392
+ `;
393
+ }
394
+ }
395
+ return html `
396
+ <iconify-icon
397
+ class="data-table__sort-icon"
398
+ icon="heroicons:chevron-up-20-solid"
399
+ style="opacity: 0.3"
400
+ ></iconify-icon>
401
+ `;
402
+ }
403
+ /**
404
+ * Render chevron left icon
405
+ * @returns TemplateResult
406
+ * @private
407
+ */
408
+ _renderChevronLeft() {
409
+ return html `
410
+ <iconify-icon
411
+ class="data-table__pagination-icon"
412
+ icon="heroicons:chevron-left-20-solid"
413
+ ></iconify-icon>
414
+ `;
415
+ }
416
+ /**
417
+ * Render chevron right icon
418
+ * @returns TemplateResult
419
+ * @private
420
+ */
421
+ _renderChevronRight() {
422
+ return html `
423
+ <iconify-icon
424
+ class="data-table__pagination-icon"
425
+ icon="heroicons:chevron-right-20-solid"
426
+ ></iconify-icon>
427
+ `;
428
+ }
429
+ /**
430
+ * Render the component
431
+ * @returns TemplateResult
432
+ */
433
+ render() {
434
+ return html `
435
+ <div class="data-table">
436
+ <!-- Search bar -->
437
+ ${this.searchable
438
+ ? html `
439
+ <div class="data-table__search">
440
+ <input
441
+ type="text"
442
+ class="data-table__search-input"
443
+ placeholder="Search..."
444
+ .value=${this._searchQuery}
445
+ @input=${this._handleSearch}
446
+ />
447
+ </div>
448
+ `
449
+ : ''}
450
+
451
+ <!-- Loading state -->
452
+ ${this.loading
453
+ ? html `
454
+ <div class="data-table__loading">
455
+ <div class="data-table__spinner"></div>
456
+ <span>Loading...</span>
457
+ </div>
458
+ `
459
+ : html `
460
+ <!-- Table -->
461
+ <div class="data-table__wrapper">
462
+ <table class="data-table__table">
463
+ <thead class="data-table__thead">
464
+ <tr>
465
+ ${this.columns.map((column) => html `
466
+ <th
467
+ class="data-table__th ${this.sortable &&
468
+ column.sortable !== false
469
+ ? 'data-table__th--sortable'
470
+ : ''} ${this._sortColumn === column.field
471
+ ? 'data-table__th--sorted'
472
+ : ''}"
473
+ style=${column.width
474
+ ? `width: ${column.width}`
475
+ : ''}
476
+ @click=${() => this._handleSort(column.field)}
477
+ >
478
+ <div class="data-table__th-content">
479
+ <span>${column.label}</span>
480
+ ${this._renderSortIcon(column)}
481
+ </div>
482
+ </th>
483
+ `)}
484
+ ${this.rowActions
485
+ ? html `
486
+ <th
487
+ class="data-table__th data-table__th--actions"
488
+ style="width: ${this.rowActionsWidth}px"
489
+ ></th>
490
+ `
491
+ : ''}
492
+ </tr>
493
+ </thead>
494
+ <tbody class="data-table__tbody">
495
+ ${this._paginatedRows.length === 0
496
+ ? html `
497
+ <tr class="data-table__tr">
498
+ <td
499
+ class="data-table__td data-table__td--empty"
500
+ colspan=${this.rowActions
501
+ ? this.columns.length + 1
502
+ : this.columns.length}
503
+ >
504
+ <slot name="empty">
505
+ ${this._searchQuery
506
+ ? this.noResultsMessage
507
+ : this.emptyMessage}
508
+ </slot>
509
+ </td>
510
+ </tr>
511
+ `
512
+ : this._paginatedRows.map((row) => html `
513
+ <tr class="data-table__tr">
514
+ ${this.columns.map((column) => html `
515
+ <td class="data-table__td">
516
+ <slot
517
+ name="cell:${column.field}"
518
+ .row=${row}
519
+ .value=${this._getNestedValue(row, column.field)}
520
+ .column=${column}
521
+ >
522
+ ${this._renderCellContent(row, column)}
523
+ </slot>
524
+ </td>
525
+ `)}
526
+ ${this.rowActions
527
+ ? html `
528
+ <td
529
+ class="data-table__td data-table__td--actions"
530
+ >
531
+ <slot name="actions" .row=${row}>
532
+ <action-menu
533
+ .resource=${row}
534
+ ></action-menu>
535
+ </slot>
536
+ </td>
537
+ `
538
+ : ''}
539
+ </tr>
540
+ `)}
541
+ </tbody>
542
+ </table>
543
+ </div>
544
+
545
+ <!-- Pagination -->
546
+ ${this.paginated && !this.loading && this._totalPages > 1
547
+ ? html `
548
+ <div class="data-table__pagination">
549
+ <div class="data-table__pagination-info">
550
+ ${this._paginationInfo.start}-${this._paginationInfo
551
+ .end}
552
+ of ${this._paginationInfo.total}
553
+ </div>
554
+ <div class="data-table__pagination-controls">
555
+ <button
556
+ class="data-table__pagination-btn"
557
+ ?disabled=${this._currentPage === 1}
558
+ @click=${this.prevPage}
559
+ aria-label="Previous page"
560
+ >
561
+ ${this._renderChevronLeft()}
562
+ </button>
563
+
564
+ <span class="data-table__pagination-current">
565
+ ${this._currentPage} / ${this._totalPages}
566
+ </span>
567
+
568
+ <button
569
+ class="data-table__pagination-btn"
570
+ ?disabled=${this._currentPage === this._totalPages}
571
+ @click=${this.nextPage}
572
+ aria-label="Next page"
573
+ >
574
+ ${this._renderChevronRight()}
575
+ </button>
576
+ </div>
577
+ </div>
578
+ `
579
+ : ''}
580
+ `}
581
+ </div>
582
+ `;
583
+ }
584
+ }
585
+ DataTable.styles = css `
586
+ :host {
587
+ display: block;
588
+ width: 100%;
589
+ }
590
+
591
+ .data-table {
592
+ display: flex;
593
+ flex-direction: column;
594
+ gap: 1rem;
595
+ width: 100%;
596
+ }
597
+
598
+ .data-table__search {
599
+ display: flex;
600
+ justify-content: flex-end;
601
+ padding: 0.5rem 0;
602
+ }
603
+
604
+ .data-table__search-input {
605
+ width: 100%;
606
+ max-width: 300px;
607
+ padding: 0.5rem 1rem;
608
+ border: 1px solid var(--border, var(--color-border, #D7D7D7));
609
+ border-radius: 4px;
610
+ background-color: var(--input-bg, var(--color-white, #FFFFFF));
611
+ color: var(--input-text, var(--color-text-primary, #212121));
612
+ font-size: 14px;
613
+ }
614
+
615
+ .data-table__search-input:focus {
616
+ outline: none;
617
+ border-color: var(--primary, var(--color-primary, #3d98d3));
618
+ }
619
+
620
+ .data-table__search-input::placeholder {
621
+ color: var(--input-placeholder, var(--color-text-muted, #8D8D8D));
622
+ }
623
+
624
+ .data-table__loading {
625
+ display: flex;
626
+ flex-direction: column;
627
+ align-items: center;
628
+ justify-content: center;
629
+ padding: 3rem;
630
+ gap: 1rem;
631
+ color: var(--body-text, var(--color-text-primary, #212121));
632
+ }
633
+
634
+ .data-table__spinner {
635
+ width: 40px;
636
+ height: 40px;
637
+ border: 4px solid var(--border, var(--color-border, #D7D7D7));
638
+ border-top-color: var(--primary, var(--color-primary, #3d98d3));
639
+ border-radius: 50%;
640
+ animation: spin 1s linear infinite;
641
+ }
642
+
643
+ @keyframes spin {
644
+ to {
645
+ transform: rotate(360deg);
646
+ }
647
+ }
648
+
649
+ .data-table__wrapper {
650
+ border: 1px solid var(--border, var(--color-border, #D7D7D7));
651
+ border-radius: 4px;
652
+ }
653
+
654
+ .data-table__table {
655
+ width: 100%;
656
+ border-collapse: collapse;
657
+ background-color: var(--body-bg, var(--color-white, #FFFFFF));
658
+ }
659
+
660
+ .data-table__thead {
661
+ background-color: var(--sortable-table-header-bg, var(--color-grey-100, #FAFAFA));
662
+ border-bottom: 1px solid var(--border, var(--color-border, #D7D7D7));
663
+ }
664
+
665
+ .data-table__th {
666
+ padding: 0.75rem 1rem;
667
+ text-align: left;
668
+ font-weight: 600;
669
+ color: var(--body-text, var(--color-text-primary, #212121));
670
+ white-space: nowrap;
671
+ border-bottom: 1px solid var(--border, var(--color-border, #D7D7D7));
672
+ }
673
+
674
+ .data-table__th--sortable {
675
+ cursor: pointer;
676
+ user-select: none;
677
+ }
678
+
679
+ .data-table__th--sortable:hover {
680
+ background-color: var(--sortable-table-header-hover-bg, var(--color-grey-200, #EBEBEB));
681
+ }
682
+
683
+ .data-table__th--sorted {
684
+ background-color: var(--sortable-table-header-sorted-bg, var(--color-grey-200, #EBEBEB));
685
+ }
686
+
687
+ .data-table__th--actions {
688
+ width: 40px;
689
+ padding: 0.75rem 0.5rem;
690
+ }
691
+
692
+ .data-table__th-content {
693
+ display: flex;
694
+ align-items: center;
695
+ gap: 0.5rem;
696
+ }
697
+
698
+ .data-table__sort-icon {
699
+ display: inline-flex;
700
+ align-items: center;
701
+ color: var(--muted, var(--color-text-muted, #8D8D8D));
702
+ width: 16px;
703
+ height: 16px;
704
+ font-size: 16px;
705
+ }
706
+
707
+ .data-table__tbody {
708
+ background-color: var(--body-bg, var(--color-white, #FFFFFF));
709
+ }
710
+
711
+ .data-table__tr {
712
+ border-bottom: 1px solid var(--border, var(--color-border, #D7D7D7));
713
+ }
714
+
715
+ .data-table__tr:hover {
716
+ background-color: var(--sortable-table-row-hover-bg, var(--color-grey-100, #FAFAFA));
717
+ }
718
+
719
+ .data-table__tr:last-child {
720
+ border-bottom: none;
721
+ }
722
+
723
+ .data-table__td {
724
+ padding: 0.75rem 1rem;
725
+ color: var(--body-text, var(--color-text-primary, #212121));
726
+ }
727
+
728
+ .data-table__td a {
729
+ color: var(--link, var(--color-primary, #3d98d3));
730
+ text-decoration: none;
731
+ }
732
+
733
+ .data-table__td a:hover {
734
+ text-decoration: underline;
735
+ }
736
+
737
+ .data-table__td--empty {
738
+ text-align: center;
739
+ padding: 2rem;
740
+ color: var(--muted, var(--color-text-muted, #8D8D8D));
741
+ }
742
+
743
+ .data-table__td--actions {
744
+ width: 40px;
745
+ padding: 0.5rem;
746
+ text-align: center;
747
+ vertical-align: middle;
748
+ }
749
+
750
+ .data-table__pagination {
751
+ display: flex;
752
+ justify-content: space-between;
753
+ align-items: center;
754
+ padding: 1rem 0;
755
+ gap: 1rem;
756
+ flex-wrap: wrap;
757
+ }
758
+
759
+ .data-table__pagination-info {
760
+ color: var(--muted, var(--color-text-muted, #8D8D8D));
761
+ font-size: 13px;
762
+ }
763
+
764
+ .data-table__pagination-controls {
765
+ display: flex;
766
+ align-items: center;
767
+ gap: 1rem;
768
+ }
769
+
770
+ .data-table__pagination-current {
771
+ color: var(--body-text, var(--color-text-primary, #212121));
772
+ font-size: 13px;
773
+ min-width: 60px;
774
+ text-align: center;
775
+ }
776
+
777
+ .data-table__pagination-btn {
778
+ display: flex;
779
+ align-items: center;
780
+ justify-content: center;
781
+ width: 32px;
782
+ height: 32px;
783
+ padding: 0;
784
+ border: 1px solid var(--border, var(--color-border, #D7D7D7));
785
+ border-radius: 4px;
786
+ background-color: var(--body-bg, var(--color-white, #FFFFFF));
787
+ color: var(--body-text, var(--color-text-primary, #212121));
788
+ cursor: pointer;
789
+ transition: all 0.2s;
790
+ }
791
+
792
+ .data-table__pagination-btn:hover:not(:disabled) {
793
+ background-color: var(--sortable-table-row-hover-bg, var(--color-grey-100, #FAFAFA));
794
+ border-color: var(--link, var(--color-primary, #3d98d3));
795
+ }
796
+
797
+ .data-table__pagination-btn:disabled {
798
+ opacity: 0.4;
799
+ cursor: not-allowed;
800
+ }
801
+
802
+ .data-table__pagination-icon {
803
+ width: 16px;
804
+ height: 16px;
805
+ }
806
+ `;
807
+ __decorate([
808
+ property({ type: Array })
809
+ ], DataTable.prototype, "columns", void 0);
810
+ __decorate([
811
+ property({ type: Array })
812
+ ], DataTable.prototype, "rows", void 0);
813
+ __decorate([
814
+ property({ type: Number, attribute: 'rows-per-page' })
815
+ ], DataTable.prototype, "rowsPerPage", void 0);
816
+ __decorate([
817
+ property({ type: Boolean })
818
+ ], DataTable.prototype, "searchable", void 0);
819
+ __decorate([
820
+ property({ type: Boolean })
821
+ ], DataTable.prototype, "sortable", void 0);
822
+ __decorate([
823
+ property({ type: Boolean })
824
+ ], DataTable.prototype, "paginated", void 0);
825
+ __decorate([
826
+ property({ type: Boolean })
827
+ ], DataTable.prototype, "loading", void 0);
828
+ __decorate([
829
+ property({ type: String, attribute: 'key-field' })
830
+ ], DataTable.prototype, "keyField", void 0);
831
+ __decorate([
832
+ property({ type: Boolean, attribute: 'row-actions' })
833
+ ], DataTable.prototype, "rowActions", void 0);
834
+ __decorate([
835
+ property({ type: Number, attribute: 'row-actions-width' })
836
+ ], DataTable.prototype, "rowActionsWidth", void 0);
837
+ __decorate([
838
+ property({ type: String, attribute: 'empty-message' })
839
+ ], DataTable.prototype, "emptyMessage", void 0);
840
+ __decorate([
841
+ property({ type: String, attribute: 'no-results-message' })
842
+ ], DataTable.prototype, "noResultsMessage", void 0);
843
+ __decorate([
844
+ state()
845
+ ], DataTable.prototype, "_searchQuery", void 0);
846
+ __decorate([
847
+ state()
848
+ ], DataTable.prototype, "_currentPage", void 0);
849
+ __decorate([
850
+ state()
851
+ ], DataTable.prototype, "_sortColumn", void 0);
852
+ __decorate([
853
+ state()
854
+ ], DataTable.prototype, "_sortDirection", void 0);
855
+ // Register the element
856
+ customElements.define('data-table', DataTable);
857
+ //# sourceMappingURL=data-table.js.map