@krumio/trailhand-ui 1.4.1

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.
@@ -0,0 +1,796 @@
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';
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' || linkUrl.startsWith('http://') || linkUrl.startsWith('https://')) {
180
+ const rel = target === '_blank' ? 'noopener noreferrer' : '';
181
+ return html `<a href="${linkUrl}" target="${target}" rel="${rel}">${value}</a>`;
182
+ }
183
+ // For internal links, use a clickable element that emits a navigation event
184
+ return html `<a
185
+ href="${linkUrl}"
186
+ @click="${(e) => this._handleLinkClick(e, linkUrl, row)}"
187
+ >${value}</a>`;
188
+ }
189
+ return value;
190
+ }
191
+ /**
192
+ * Handle link click - emit navigation event instead of following link
193
+ * @param event - The click event
194
+ * @param url - The URL to navigate to
195
+ * @param row - The row data
196
+ * @private
197
+ */
198
+ _handleLinkClick(event, url, row) {
199
+ event.preventDefault();
200
+ // Emit custom event for navigation
201
+ this.dispatchEvent(new CustomEvent('navigate', {
202
+ detail: { url, row },
203
+ bubbles: true,
204
+ composed: true
205
+ }));
206
+ }
207
+ /**
208
+ * Get filtered rows based on search query
209
+ * @returns Filtered rows
210
+ * @private
211
+ */
212
+ get _filteredRows() {
213
+ if (!this._searchQuery || !this.searchable) {
214
+ return this.rows;
215
+ }
216
+ const query = this._searchQuery.toLowerCase();
217
+ return this.rows.filter(row => {
218
+ return this.columns.some(column => {
219
+ if (column.searchable === false) {
220
+ return false;
221
+ }
222
+ const value = this._getNestedValue(row, column.field);
223
+ return String(value).toLowerCase().includes(query);
224
+ });
225
+ });
226
+ }
227
+ /**
228
+ * Get sorted rows
229
+ * @returns Sorted rows
230
+ * @private
231
+ */
232
+ get _sortedRows() {
233
+ if (!this._sortColumn || !this.sortable) {
234
+ return this._filteredRows;
235
+ }
236
+ const column = this.columns.find(col => col.field === this._sortColumn);
237
+ if (!column || column.sortable === false) {
238
+ return this._filteredRows;
239
+ }
240
+ return [...this._filteredRows].sort((a, b) => {
241
+ const aValue = this._getNestedValue(a, this._sortColumn);
242
+ const bValue = this._getNestedValue(b, this._sortColumn);
243
+ // Handle custom sort function
244
+ if (column.sortFn) {
245
+ return column.sortFn(a, b, this._sortDirection);
246
+ }
247
+ // Default sorting logic
248
+ let comparison = 0;
249
+ if (aValue < bValue) {
250
+ comparison = -1;
251
+ }
252
+ else if (aValue > bValue) {
253
+ comparison = 1;
254
+ }
255
+ return this._sortDirection === 'asc' ? comparison : -comparison;
256
+ });
257
+ }
258
+ /**
259
+ * Get paginated rows
260
+ * @returns Paginated rows
261
+ * @private
262
+ */
263
+ get _paginatedRows() {
264
+ if (!this.paginated) {
265
+ return this._sortedRows;
266
+ }
267
+ const start = (this._currentPage - 1) * this.rowsPerPage;
268
+ const end = start + this.rowsPerPage;
269
+ return this._sortedRows.slice(start, end);
270
+ }
271
+ /**
272
+ * Get total number of pages
273
+ * @returns Total pages
274
+ * @private
275
+ */
276
+ get _totalPages() {
277
+ if (!this.paginated) {
278
+ return 1;
279
+ }
280
+ return Math.ceil(this._sortedRows.length / this.rowsPerPage);
281
+ }
282
+ /**
283
+ * Get pagination info
284
+ * @returns Pagination info
285
+ * @private
286
+ */
287
+ get _paginationInfo() {
288
+ const start = (this._currentPage - 1) * this.rowsPerPage + 1;
289
+ const end = Math.min(this._currentPage * this.rowsPerPage, this._sortedRows.length);
290
+ return {
291
+ start,
292
+ end,
293
+ total: this._sortedRows.length
294
+ };
295
+ }
296
+ /**
297
+ * Handle search input
298
+ * @param e - The input event
299
+ * @private
300
+ */
301
+ _handleSearch(e) {
302
+ const target = e.target;
303
+ this._searchQuery = target.value;
304
+ this._currentPage = 1;
305
+ }
306
+ /**
307
+ * Handle column sort
308
+ * @param columnField - The column field to sort by
309
+ * @private
310
+ */
311
+ _handleSort(columnField) {
312
+ const column = this.columns.find(col => col.field === columnField);
313
+ if (!column || column.sortable === false || !this.sortable) {
314
+ return;
315
+ }
316
+ if (this._sortColumn === columnField) {
317
+ // Toggle direction
318
+ this._sortDirection = this._sortDirection === 'asc' ? 'desc' : 'asc';
319
+ }
320
+ else {
321
+ // New column, default to ascending
322
+ this._sortColumn = columnField;
323
+ this._sortDirection = 'asc';
324
+ }
325
+ this._currentPage = 1;
326
+ }
327
+ /**
328
+ * Navigate to a specific page
329
+ * @param page - The page number
330
+ */
331
+ goToPage(page) {
332
+ if (page >= 1 && page <= this._totalPages) {
333
+ this._currentPage = page;
334
+ }
335
+ }
336
+ /**
337
+ * Navigate to the next page
338
+ */
339
+ nextPage() {
340
+ this.goToPage(this._currentPage + 1);
341
+ }
342
+ /**
343
+ * Navigate to the previous page
344
+ */
345
+ prevPage() {
346
+ this.goToPage(this._currentPage - 1);
347
+ }
348
+ /**
349
+ * Reset search query
350
+ */
351
+ resetSearch() {
352
+ this._searchQuery = '';
353
+ }
354
+ /**
355
+ * Reset sort to default state
356
+ */
357
+ resetSort() {
358
+ this._sortColumn = null;
359
+ this._sortDirection = 'asc';
360
+ }
361
+ /**
362
+ * Render sort icon
363
+ * @param column - The column definition
364
+ * @returns TemplateResult
365
+ * @private
366
+ */
367
+ _renderSortIcon(column) {
368
+ if (!this.sortable || column.sortable === false) {
369
+ return '';
370
+ }
371
+ const isSorted = this._sortColumn === column.field;
372
+ if (isSorted) {
373
+ if (this._sortDirection === 'asc') {
374
+ return html `
375
+ <iconify-icon class="data-table__sort-icon" icon="heroicons:chevron-up-20-solid"></iconify-icon>
376
+ `;
377
+ }
378
+ else {
379
+ return html `
380
+ <iconify-icon class="data-table__sort-icon" icon="heroicons:chevron-down-20-solid"></iconify-icon>
381
+ `;
382
+ }
383
+ }
384
+ return html `
385
+ <iconify-icon class="data-table__sort-icon" icon="heroicons:chevron-up-20-solid" style="opacity: 0.3"></iconify-icon>
386
+ `;
387
+ }
388
+ /**
389
+ * Render chevron left icon
390
+ * @returns TemplateResult
391
+ * @private
392
+ */
393
+ _renderChevronLeft() {
394
+ return html `
395
+ <iconify-icon class="data-table__pagination-icon" icon="heroicons:chevron-left-20-solid"></iconify-icon>
396
+ `;
397
+ }
398
+ /**
399
+ * Render chevron right icon
400
+ * @returns TemplateResult
401
+ * @private
402
+ */
403
+ _renderChevronRight() {
404
+ return html `
405
+ <iconify-icon class="data-table__pagination-icon" icon="heroicons:chevron-right-20-solid"></iconify-icon>
406
+ `;
407
+ }
408
+ /**
409
+ * Render the component
410
+ * @returns TemplateResult
411
+ */
412
+ render() {
413
+ return html `
414
+ <div class="data-table">
415
+ <!-- Search bar -->
416
+ ${this.searchable ? html `
417
+ <div class="data-table__search">
418
+ <input
419
+ type="text"
420
+ class="data-table__search-input"
421
+ placeholder="Search..."
422
+ .value=${this._searchQuery}
423
+ @input=${this._handleSearch}
424
+ >
425
+ </div>
426
+ ` : ''}
427
+
428
+ <!-- Loading state -->
429
+ ${this.loading ? html `
430
+ <div class="data-table__loading">
431
+ <div class="data-table__spinner"></div>
432
+ <span>Loading...</span>
433
+ </div>
434
+ ` : html `
435
+ <!-- Table -->
436
+ <div class="data-table__wrapper">
437
+ <table class="data-table__table">
438
+ <thead class="data-table__thead">
439
+ <tr>
440
+ ${this.columns.map(column => html `
441
+ <th
442
+ class="data-table__th ${this.sortable && column.sortable !== false ? 'data-table__th--sortable' : ''} ${this._sortColumn === column.field ? 'data-table__th--sorted' : ''}"
443
+ style=${column.width ? `width: ${column.width}` : ''}
444
+ @click=${() => this._handleSort(column.field)}
445
+ >
446
+ <div class="data-table__th-content">
447
+ <span>${column.label}</span>
448
+ ${this._renderSortIcon(column)}
449
+ </div>
450
+ </th>
451
+ `)}
452
+ ${this.rowActions ? html `
453
+ <th class="data-table__th data-table__th--actions" style="width: ${this.rowActionsWidth}px"></th>
454
+ ` : ''}
455
+ </tr>
456
+ </thead>
457
+ <tbody class="data-table__tbody">
458
+ ${this._paginatedRows.length === 0 ? html `
459
+ <tr class="data-table__tr">
460
+ <td class="data-table__td data-table__td--empty" colspan=${this.rowActions ? this.columns.length + 1 : this.columns.length}>
461
+ <slot name="empty">
462
+ ${this._searchQuery ? this.noResultsMessage : this.emptyMessage}
463
+ </slot>
464
+ </td>
465
+ </tr>
466
+ ` : this._paginatedRows.map((row) => html `
467
+ <tr class="data-table__tr">
468
+ ${this.columns.map(column => html `
469
+ <td class="data-table__td">
470
+ <slot name="cell:${column.field}" .row=${row} .value=${this._getNestedValue(row, column.field)} .column=${column}>
471
+ ${this._renderCellContent(row, column)}
472
+ </slot>
473
+ </td>
474
+ `)}
475
+ ${this.rowActions ? html `
476
+ <td class="data-table__td data-table__td--actions">
477
+ <slot name="actions" .row=${row}>
478
+ <action-menu .resource=${row}></action-menu>
479
+ </slot>
480
+ </td>
481
+ ` : ''}
482
+ </tr>
483
+ `)}
484
+ </tbody>
485
+ </table>
486
+ </div>
487
+
488
+ <!-- Pagination -->
489
+ ${this.paginated && !this.loading && this._totalPages > 1 ? html `
490
+ <div class="data-table__pagination">
491
+ <div class="data-table__pagination-info">
492
+ ${this._paginationInfo.start}-${this._paginationInfo.end} of ${this._paginationInfo.total}
493
+ </div>
494
+ <div class="data-table__pagination-controls">
495
+ <button
496
+ class="data-table__pagination-btn"
497
+ ?disabled=${this._currentPage === 1}
498
+ @click=${this.prevPage}
499
+ aria-label="Previous page"
500
+ >
501
+ ${this._renderChevronLeft()}
502
+ </button>
503
+
504
+ <span class="data-table__pagination-current">
505
+ ${this._currentPage} / ${this._totalPages}
506
+ </span>
507
+
508
+ <button
509
+ class="data-table__pagination-btn"
510
+ ?disabled=${this._currentPage === this._totalPages}
511
+ @click=${this.nextPage}
512
+ aria-label="Next page"
513
+ >
514
+ ${this._renderChevronRight()}
515
+ </button>
516
+ </div>
517
+ </div>
518
+ ` : ''}
519
+ `}
520
+ </div>
521
+ `;
522
+ }
523
+ }
524
+ DataTable.styles = css `
525
+ :host {
526
+ display: block;
527
+ width: 100%;
528
+ }
529
+
530
+ .data-table {
531
+ display: flex;
532
+ flex-direction: column;
533
+ gap: 1rem;
534
+ width: 100%;
535
+ }
536
+
537
+ .data-table__search {
538
+ display: flex;
539
+ justify-content: flex-end;
540
+ padding: 0.5rem 0;
541
+ }
542
+
543
+ .data-table__search-input {
544
+ width: 100%;
545
+ max-width: 300px;
546
+ padding: 0.5rem 1rem;
547
+ border: 1px solid var(--border, #ddd);
548
+ border-radius: 4px;
549
+ background-color: var(--input-bg, #fff);
550
+ color: var(--input-text, #333);
551
+ font-size: 14px;
552
+ }
553
+
554
+ .data-table__search-input:focus {
555
+ outline: none;
556
+ border-color: var(--primary, #007bff);
557
+ }
558
+
559
+ .data-table__search-input::placeholder {
560
+ color: var(--input-placeholder, #999);
561
+ }
562
+
563
+ .data-table__loading {
564
+ display: flex;
565
+ flex-direction: column;
566
+ align-items: center;
567
+ justify-content: center;
568
+ padding: 3rem;
569
+ gap: 1rem;
570
+ color: var(--body-text, #333);
571
+ }
572
+
573
+ .data-table__spinner {
574
+ width: 40px;
575
+ height: 40px;
576
+ border: 4px solid var(--border, #ddd);
577
+ border-top-color: var(--primary, #007bff);
578
+ border-radius: 50%;
579
+ animation: spin 1s linear infinite;
580
+ }
581
+
582
+ @keyframes spin {
583
+ to {
584
+ transform: rotate(360deg);
585
+ }
586
+ }
587
+
588
+ .data-table__wrapper {
589
+ border: 1px solid var(--border, #ddd);
590
+ border-radius: 4px;
591
+ }
592
+
593
+ .data-table__table {
594
+ width: 100%;
595
+ border-collapse: collapse;
596
+ background-color: var(--body-bg, #fff);
597
+ }
598
+
599
+ .data-table__thead {
600
+ background-color: var(--sortable-table-header-bg, #f8f9fa);
601
+ border-bottom: 1px solid var(--border, #ddd);
602
+ }
603
+
604
+ .data-table__th {
605
+ padding: 0.75rem 1rem;
606
+ text-align: left;
607
+ font-weight: 600;
608
+ color: var(--body-text, #333);
609
+ white-space: nowrap;
610
+ border-bottom: 1px solid var(--border, #ddd);
611
+ }
612
+
613
+ .data-table__th--sortable {
614
+ cursor: pointer;
615
+ user-select: none;
616
+ }
617
+
618
+ .data-table__th--sortable:hover {
619
+ background-color: var(--sortable-table-header-hover-bg, #e9ecef);
620
+ }
621
+
622
+ .data-table__th--sorted {
623
+ background-color: var(--sortable-table-header-sorted-bg, #e2e6ea);
624
+ }
625
+
626
+ .data-table__th--actions {
627
+ width: 40px;
628
+ padding: 0.75rem 0.5rem;
629
+ }
630
+
631
+ .data-table__th-content {
632
+ display: flex;
633
+ align-items: center;
634
+ gap: 0.5rem;
635
+ }
636
+
637
+ .data-table__sort-icon {
638
+ display: inline-flex;
639
+ align-items: center;
640
+ color: var(--muted, #6c757d);
641
+ width: 16px;
642
+ height: 16px;
643
+ font-size: 16px;
644
+ }
645
+
646
+ .data-table__tbody {
647
+ background-color: var(--body-bg, #fff);
648
+ }
649
+
650
+ .data-table__tr {
651
+ border-bottom: 1px solid var(--border, #ddd);
652
+ }
653
+
654
+ .data-table__tr:hover {
655
+ background-color: var(--sortable-table-row-hover-bg, #f8f9fa);
656
+ }
657
+
658
+ .data-table__tr:last-child {
659
+ border-bottom: none;
660
+ }
661
+
662
+ .data-table__td {
663
+ padding: 0.75rem 1rem;
664
+ color: var(--body-text, #333);
665
+ }
666
+
667
+ .data-table__td a {
668
+ color: var(--link, #007bff);
669
+ text-decoration: none;
670
+ }
671
+
672
+ .data-table__td a:hover {
673
+ text-decoration: underline;
674
+ }
675
+
676
+ .data-table__td--empty {
677
+ text-align: center;
678
+ padding: 2rem;
679
+ color: var(--muted, #6c757d);
680
+ }
681
+
682
+ .data-table__td--actions {
683
+ width: 40px;
684
+ padding: 0.5rem;
685
+ text-align: center;
686
+ vertical-align: middle;
687
+ }
688
+
689
+ .data-table__pagination {
690
+ display: flex;
691
+ justify-content: space-between;
692
+ align-items: center;
693
+ padding: 1rem 0;
694
+ gap: 1rem;
695
+ flex-wrap: wrap;
696
+ }
697
+
698
+ .data-table__pagination-info {
699
+ color: var(--muted, #6c757d);
700
+ font-size: 13px;
701
+ }
702
+
703
+ .data-table__pagination-controls {
704
+ display: flex;
705
+ align-items: center;
706
+ gap: 1rem;
707
+ }
708
+
709
+ .data-table__pagination-current {
710
+ color: var(--body-text, #333);
711
+ font-size: 13px;
712
+ min-width: 60px;
713
+ text-align: center;
714
+ }
715
+
716
+ .data-table__pagination-btn {
717
+ display: flex;
718
+ align-items: center;
719
+ justify-content: center;
720
+ width: 32px;
721
+ height: 32px;
722
+ padding: 0;
723
+ border: 1px solid var(--border, #ddd);
724
+ border-radius: 4px;
725
+ background-color: var(--body-bg, #fff);
726
+ color: var(--body-text, #333);
727
+ cursor: pointer;
728
+ transition: all 0.2s;
729
+ }
730
+
731
+ .data-table__pagination-btn:hover:not(:disabled) {
732
+ background-color: var(--sortable-table-row-hover-bg, #f8f9fa);
733
+ border-color: var(--link, #007bff);
734
+ }
735
+
736
+ .data-table__pagination-btn:disabled {
737
+ opacity: 0.4;
738
+ cursor: not-allowed;
739
+ }
740
+
741
+ .data-table__pagination-icon {
742
+ width: 16px;
743
+ height: 16px;
744
+ }
745
+ `;
746
+ __decorate([
747
+ property({ type: Array })
748
+ ], DataTable.prototype, "columns", void 0);
749
+ __decorate([
750
+ property({ type: Array })
751
+ ], DataTable.prototype, "rows", void 0);
752
+ __decorate([
753
+ property({ type: Number, attribute: 'rows-per-page' })
754
+ ], DataTable.prototype, "rowsPerPage", void 0);
755
+ __decorate([
756
+ property({ type: Boolean })
757
+ ], DataTable.prototype, "searchable", void 0);
758
+ __decorate([
759
+ property({ type: Boolean })
760
+ ], DataTable.prototype, "sortable", void 0);
761
+ __decorate([
762
+ property({ type: Boolean })
763
+ ], DataTable.prototype, "paginated", void 0);
764
+ __decorate([
765
+ property({ type: Boolean })
766
+ ], DataTable.prototype, "loading", void 0);
767
+ __decorate([
768
+ property({ type: String, attribute: 'key-field' })
769
+ ], DataTable.prototype, "keyField", void 0);
770
+ __decorate([
771
+ property({ type: Boolean, attribute: 'row-actions' })
772
+ ], DataTable.prototype, "rowActions", void 0);
773
+ __decorate([
774
+ property({ type: Number, attribute: 'row-actions-width' })
775
+ ], DataTable.prototype, "rowActionsWidth", void 0);
776
+ __decorate([
777
+ property({ type: String, attribute: 'empty-message' })
778
+ ], DataTable.prototype, "emptyMessage", void 0);
779
+ __decorate([
780
+ property({ type: String, attribute: 'no-results-message' })
781
+ ], DataTable.prototype, "noResultsMessage", void 0);
782
+ __decorate([
783
+ state()
784
+ ], DataTable.prototype, "_searchQuery", void 0);
785
+ __decorate([
786
+ state()
787
+ ], DataTable.prototype, "_currentPage", void 0);
788
+ __decorate([
789
+ state()
790
+ ], DataTable.prototype, "_sortColumn", void 0);
791
+ __decorate([
792
+ state()
793
+ ], DataTable.prototype, "_sortDirection", void 0);
794
+ // Register the element
795
+ customElements.define('data-table', DataTable);
796
+ //# sourceMappingURL=data-table.js.map