@duskmoon-dev/el-table 0.4.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.
@@ -0,0 +1,872 @@
1
+ // src/el-dm-table.ts
2
+ import { BaseElement as BaseElement2, css } from "@duskmoon-dev/el-core";
3
+
4
+ // src/el-dm-table-column.ts
5
+ import { BaseElement } from "@duskmoon-dev/el-core";
6
+
7
+ class ElDmTableColumn extends BaseElement {
8
+ static properties = {
9
+ key: { type: String, reflect: true },
10
+ label: { type: String, reflect: true },
11
+ sortable: { type: Boolean, reflect: true },
12
+ width: { type: String, reflect: true },
13
+ align: { type: String, reflect: true, default: "left" },
14
+ hidden: { type: Boolean, reflect: true }
15
+ };
16
+ connectedCallback() {
17
+ super.connectedCallback();
18
+ this._notifyParent();
19
+ }
20
+ disconnectedCallback() {
21
+ super.disconnectedCallback();
22
+ this._notifyParent();
23
+ }
24
+ _notifyParent() {
25
+ this.dispatchEvent(new CustomEvent("table-column-change", {
26
+ bubbles: true,
27
+ composed: true
28
+ }));
29
+ }
30
+ toColumnDef() {
31
+ return {
32
+ key: this.key,
33
+ label: this.label || this.key || "",
34
+ sortable: this.sortable || false,
35
+ width: this.width,
36
+ align: this.align || "left",
37
+ hidden: this.hidden || false
38
+ };
39
+ }
40
+ render() {
41
+ return `<slot></slot>`;
42
+ }
43
+ }
44
+ function registerTableColumn() {
45
+ if (!customElements.get("el-dm-table-column")) {
46
+ customElements.define("el-dm-table-column", ElDmTableColumn);
47
+ }
48
+ }
49
+
50
+ // src/el-dm-table.ts
51
+ var ICONS = {
52
+ sortAsc: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="M11 4h4"/><path d="M11 8h7"/><path d="M11 12h10"/></svg>`,
53
+ sortDesc: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 8 4-4 4 4"/><path d="M7 4v16"/><path d="M11 12h4"/><path d="M11 16h7"/><path d="M11 20h10"/></svg>`,
54
+ sort: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="m21 8-4-4-4 4"/><path d="M17 4v16"/></svg>`,
55
+ chevronLeft: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>`,
56
+ chevronRight: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>`,
57
+ chevronFirst: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 18-6-6 6-6"/><path d="M7 6v12"/></svg>`,
58
+ chevronLast: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 18 6-6-6-6"/><path d="M17 6v12"/></svg>`
59
+ };
60
+ var styles = css`
61
+ :host {
62
+ display: block;
63
+ font-family: inherit;
64
+ }
65
+
66
+ :host([hidden]) {
67
+ display: none !important;
68
+ }
69
+
70
+ /* Main wrapper */
71
+ .table-wrapper {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: 0.75rem;
75
+ }
76
+
77
+ /* Header/Footer action slots */
78
+ .table-header-actions,
79
+ .table-footer-actions {
80
+ display: flex;
81
+ align-items: center;
82
+ gap: 0.5rem;
83
+ }
84
+
85
+ .table-header-actions:empty,
86
+ .table-footer-actions:empty {
87
+ display: none;
88
+ }
89
+
90
+ /* Container for scrolling */
91
+ .table-container {
92
+ overflow-x: auto;
93
+ border: 1px solid var(--dm-color-outline, #e0e0e0);
94
+ border-radius: var(--dm-radius-md, 0.5rem);
95
+ }
96
+
97
+ /* Sticky header variant */
98
+ :host([sticky-header]) .table-container {
99
+ max-height: var(--table-max-height, 400px);
100
+ overflow-y: auto;
101
+ }
102
+
103
+ :host([sticky-header]) .table thead {
104
+ position: sticky;
105
+ top: 0;
106
+ z-index: 1;
107
+ }
108
+
109
+ /* Base table */
110
+ .table {
111
+ width: 100%;
112
+ border-collapse: collapse;
113
+ font-size: 0.875rem;
114
+ background-color: var(--dm-color-surface, #ffffff);
115
+ }
116
+
117
+ /* Header styles */
118
+ .table-th {
119
+ padding: 0.75rem 1rem;
120
+ text-align: left;
121
+ font-weight: 600;
122
+ color: var(--dm-color-on-surface, #1a1a1a);
123
+ background-color: var(--dm-color-surface-variant, #f5f5f5);
124
+ border-bottom: 2px solid var(--dm-color-outline, #e0e0e0);
125
+ white-space: nowrap;
126
+ }
127
+
128
+ .table-th.sortable {
129
+ cursor: pointer;
130
+ user-select: none;
131
+ }
132
+
133
+ .table-th.sortable:hover {
134
+ background-color: var(--dm-color-surface-container, #ebebeb);
135
+ }
136
+
137
+ .table-th.sortable:focus-visible {
138
+ outline: 2px solid var(--dm-color-primary, #6366f1);
139
+ outline-offset: -2px;
140
+ }
141
+
142
+ .table-th.sorted {
143
+ color: var(--dm-color-primary, #6366f1);
144
+ }
145
+
146
+ .th-content {
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 0.5rem;
150
+ }
151
+
152
+ .sort-icon {
153
+ display: flex;
154
+ width: 1rem;
155
+ height: 1rem;
156
+ opacity: 0.5;
157
+ flex-shrink: 0;
158
+ }
159
+
160
+ .table-th.sorted .sort-icon {
161
+ opacity: 1;
162
+ }
163
+
164
+ /* Cell styles */
165
+ .table-td {
166
+ padding: 0.75rem 1rem;
167
+ border-bottom: 1px solid var(--dm-color-outline-variant, #e8e8e8);
168
+ color: var(--dm-color-on-surface, #1a1a1a);
169
+ }
170
+
171
+ /* Row styles */
172
+ .table-row {
173
+ transition: background-color 150ms ease;
174
+ }
175
+
176
+ :host([hoverable]) .table-row:hover {
177
+ background-color: var(--dm-color-surface-variant, #f5f5f5);
178
+ }
179
+
180
+ .table-row.selected {
181
+ background-color: var(--dm-color-primary-container, #e8e8ff);
182
+ }
183
+
184
+ :host([hoverable]) .table-row.selected:hover {
185
+ background-color: color-mix(
186
+ in srgb,
187
+ var(--dm-color-primary-container, #e8e8ff) 90%,
188
+ var(--dm-color-primary, #6366f1)
189
+ );
190
+ }
191
+
192
+ /* Selection column */
193
+ .select-cell {
194
+ width: 48px;
195
+ text-align: center;
196
+ padding: 0.5rem;
197
+ }
198
+
199
+ .select-cell input {
200
+ cursor: pointer;
201
+ width: 18px;
202
+ height: 18px;
203
+ accent-color: var(--dm-color-primary, #6366f1);
204
+ }
205
+
206
+ /* Striped variant */
207
+ :host([striped]) .table tbody tr:nth-child(even) {
208
+ background-color: var(--dm-color-surface-variant, #fafafa);
209
+ }
210
+
211
+ :host([striped]) .table tbody tr.selected:nth-child(even) {
212
+ background-color: var(--dm-color-primary-container, #e8e8ff);
213
+ }
214
+
215
+ /* Bordered variant */
216
+ :host([bordered]) .table-td,
217
+ :host([bordered]) .table-th {
218
+ border: 1px solid var(--dm-color-outline, #e0e0e0);
219
+ }
220
+
221
+ /* Compact variant */
222
+ :host([compact]) .table-th,
223
+ :host([compact]) .table-td {
224
+ padding: 0.5rem 0.75rem;
225
+ font-size: 0.8125rem;
226
+ }
227
+
228
+ :host([compact]) .select-cell {
229
+ padding: 0.375rem 0.5rem;
230
+ }
231
+
232
+ /* Loading state */
233
+ .loading-row td {
234
+ padding: 3rem;
235
+ text-align: center;
236
+ color: var(--dm-color-on-surface-variant, #666);
237
+ }
238
+
239
+ .loading-spinner {
240
+ display: inline-block;
241
+ width: 24px;
242
+ height: 24px;
243
+ border: 2px solid var(--dm-color-outline, #e0e0e0);
244
+ border-top-color: var(--dm-color-primary, #6366f1);
245
+ border-radius: 50%;
246
+ animation: spin 0.8s linear infinite;
247
+ }
248
+
249
+ @keyframes spin {
250
+ to {
251
+ transform: rotate(360deg);
252
+ }
253
+ }
254
+
255
+ /* Empty state */
256
+ .empty-row td {
257
+ padding: 3rem;
258
+ text-align: center;
259
+ color: var(--dm-color-on-surface-variant, #666);
260
+ }
261
+
262
+ /* Pagination styles */
263
+ .table-pagination {
264
+ display: flex;
265
+ align-items: center;
266
+ justify-content: space-between;
267
+ flex-wrap: wrap;
268
+ gap: 1rem;
269
+ padding: 0.75rem 1rem;
270
+ border: 1px solid var(--dm-color-outline, #e0e0e0);
271
+ border-top: none;
272
+ border-radius: 0 0 var(--dm-radius-md, 0.5rem) var(--dm-radius-md, 0.5rem);
273
+ background-color: var(--dm-color-surface-variant, #f5f5f5);
274
+ font-size: 0.875rem;
275
+ }
276
+
277
+ .pagination-info {
278
+ color: var(--dm-color-on-surface-variant, #666);
279
+ }
280
+
281
+ .pagination-controls {
282
+ display: flex;
283
+ align-items: center;
284
+ gap: 1rem;
285
+ }
286
+
287
+ .pagination-nav {
288
+ display: flex;
289
+ align-items: center;
290
+ gap: 0.25rem;
291
+ }
292
+
293
+ .pagination-btn {
294
+ display: inline-flex;
295
+ align-items: center;
296
+ justify-content: center;
297
+ width: 32px;
298
+ height: 32px;
299
+ border: 1px solid var(--dm-color-outline, #e0e0e0);
300
+ border-radius: var(--dm-radius-sm, 0.25rem);
301
+ background: var(--dm-color-surface, #ffffff);
302
+ cursor: pointer;
303
+ transition: all 150ms ease;
304
+ color: var(--dm-color-on-surface, #1a1a1a);
305
+ }
306
+
307
+ .pagination-btn:hover:not(:disabled) {
308
+ background: var(--dm-color-surface-container, #ebebeb);
309
+ }
310
+
311
+ .pagination-btn:focus-visible {
312
+ outline: 2px solid var(--dm-color-primary, #6366f1);
313
+ outline-offset: 2px;
314
+ }
315
+
316
+ .pagination-btn:disabled {
317
+ opacity: 0.5;
318
+ cursor: not-allowed;
319
+ }
320
+
321
+ .pagination-current {
322
+ padding: 0 0.75rem;
323
+ color: var(--dm-color-on-surface, #1a1a1a);
324
+ min-width: 100px;
325
+ text-align: center;
326
+ }
327
+
328
+ .page-size-select {
329
+ padding: 0.375rem 0.75rem;
330
+ border: 1px solid var(--dm-color-outline, #e0e0e0);
331
+ border-radius: var(--dm-radius-sm, 0.25rem);
332
+ background: var(--dm-color-surface, #ffffff);
333
+ font-size: 0.875rem;
334
+ cursor: pointer;
335
+ }
336
+
337
+ .page-size-select:focus-visible {
338
+ outline: 2px solid var(--dm-color-primary, #6366f1);
339
+ outline-offset: 2px;
340
+ }
341
+
342
+ /* Clickable rows */
343
+ :host([selection-mode='single']) .table-row,
344
+ :host([selection-mode='multiple']) .table-row {
345
+ cursor: pointer;
346
+ }
347
+ `;
348
+
349
+ class ElDmTable extends BaseElement2 {
350
+ static properties = {
351
+ columns: { type: Array, attribute: false },
352
+ data: { type: Array, attribute: false },
353
+ sortColumn: { type: String, reflect: true, attribute: "sort-column" },
354
+ sortDirection: {
355
+ type: String,
356
+ reflect: true,
357
+ attribute: "sort-direction",
358
+ default: "asc"
359
+ },
360
+ paginated: { type: Boolean, reflect: true },
361
+ page: { type: Number, reflect: true, default: 1 },
362
+ pageSize: { type: Number, reflect: true, attribute: "page-size", default: 10 },
363
+ pageSizeOptions: { type: Array, attribute: false },
364
+ selectionMode: {
365
+ type: String,
366
+ reflect: true,
367
+ attribute: "selection-mode",
368
+ default: "none"
369
+ },
370
+ selectedIds: { type: Array, attribute: false },
371
+ striped: { type: Boolean, reflect: true },
372
+ bordered: { type: Boolean, reflect: true },
373
+ hoverable: { type: Boolean, reflect: true, default: true },
374
+ compact: { type: Boolean, reflect: true },
375
+ stickyHeader: { type: Boolean, reflect: true, attribute: "sticky-header" },
376
+ loading: { type: Boolean, reflect: true },
377
+ emptyMessage: {
378
+ type: String,
379
+ reflect: true,
380
+ attribute: "empty-message",
381
+ default: "No data available"
382
+ }
383
+ };
384
+ _internalSelectedIds = new Set;
385
+ constructor() {
386
+ super();
387
+ this.attachStyles(styles);
388
+ this.columns = [];
389
+ this.data = [];
390
+ this.selectedIds = [];
391
+ this.pageSizeOptions = [5, 10, 25, 50];
392
+ }
393
+ connectedCallback() {
394
+ super.connectedCallback();
395
+ this.addEventListener("table-column-change", this._handleColumnChange);
396
+ }
397
+ disconnectedCallback() {
398
+ super.disconnectedCallback();
399
+ this.removeEventListener("table-column-change", this._handleColumnChange);
400
+ }
401
+ update() {
402
+ super.update();
403
+ this._attachEventListeners();
404
+ }
405
+ _handleColumnChange = () => {
406
+ this.update();
407
+ };
408
+ _attachEventListeners() {
409
+ this.shadowRoot?.querySelectorAll(".table-th.sortable").forEach((th) => {
410
+ th.addEventListener("click", this._handleHeaderClick);
411
+ th.addEventListener("keydown", this._handleHeaderKeydown);
412
+ });
413
+ this.shadowRoot?.querySelectorAll(".table-row").forEach((row) => {
414
+ row.addEventListener("click", this._handleRowClick);
415
+ });
416
+ this.shadowRoot?.querySelectorAll(".row-select").forEach((input) => {
417
+ input.addEventListener("change", this._handleRowSelectChange);
418
+ });
419
+ const selectAll = this.shadowRoot?.querySelector(".select-all");
420
+ selectAll?.addEventListener("change", this._handleSelectAllChange);
421
+ this.shadowRoot?.querySelectorAll(".pagination-btn").forEach((btn) => {
422
+ btn.addEventListener("click", this._handlePaginationClick);
423
+ });
424
+ const pageSizeSelect = this.shadowRoot?.querySelector(".page-size-select");
425
+ pageSizeSelect?.addEventListener("change", this._handlePageSizeChange);
426
+ }
427
+ _handleHeaderClick = (e) => {
428
+ const th = e.currentTarget.closest("[data-column]");
429
+ const column = th?.getAttribute("data-column");
430
+ if (column) {
431
+ this.sort(column);
432
+ }
433
+ };
434
+ _handleHeaderKeydown = (e) => {
435
+ const keyEvent = e;
436
+ if (keyEvent.key === "Enter" || keyEvent.key === " ") {
437
+ keyEvent.preventDefault();
438
+ this._handleHeaderClick(e);
439
+ }
440
+ };
441
+ sort(column, direction) {
442
+ if (direction) {
443
+ this.sortColumn = column;
444
+ this.sortDirection = direction;
445
+ } else if (this.sortColumn === column) {
446
+ this.sortDirection = this.sortDirection === "asc" ? "desc" : "asc";
447
+ } else {
448
+ this.sortColumn = column;
449
+ this.sortDirection = "asc";
450
+ }
451
+ this.emit("sort", {
452
+ column: this.sortColumn,
453
+ direction: this.sortDirection
454
+ });
455
+ }
456
+ _sortData(data) {
457
+ if (!this.sortColumn)
458
+ return data;
459
+ return [...data].sort((a, b) => {
460
+ const aVal = a[this.sortColumn];
461
+ const bVal = b[this.sortColumn];
462
+ let comparison = 0;
463
+ if (aVal === null || aVal === undefined)
464
+ comparison = 1;
465
+ else if (bVal === null || bVal === undefined)
466
+ comparison = -1;
467
+ else if (typeof aVal === "string" && typeof bVal === "string") {
468
+ comparison = aVal.localeCompare(bVal);
469
+ } else if (typeof aVal === "number" && typeof bVal === "number") {
470
+ comparison = aVal - bVal;
471
+ } else {
472
+ comparison = String(aVal).localeCompare(String(bVal));
473
+ }
474
+ return this.sortDirection === "desc" ? -comparison : comparison;
475
+ });
476
+ }
477
+ _handlePaginationClick = (e) => {
478
+ const btn = e.currentTarget;
479
+ const action = btn.getAttribute("data-action");
480
+ switch (action) {
481
+ case "first":
482
+ this.goToPage(1);
483
+ break;
484
+ case "prev":
485
+ this.goToPage(this.page - 1);
486
+ break;
487
+ case "next":
488
+ this.goToPage(this.page + 1);
489
+ break;
490
+ case "last":
491
+ this.goToPage(this._getTotalPages());
492
+ break;
493
+ }
494
+ };
495
+ _handlePageSizeChange = (e) => {
496
+ const select = e.target;
497
+ const newPageSize = parseInt(select.value, 10);
498
+ this.pageSize = newPageSize;
499
+ this.page = 1;
500
+ this.emit("page-change", {
501
+ page: this.page,
502
+ pageSize: this.pageSize
503
+ });
504
+ };
505
+ goToPage(page) {
506
+ const totalPages = this._getTotalPages();
507
+ const newPage = Math.max(1, Math.min(page, totalPages));
508
+ if (newPage !== this.page) {
509
+ this.page = newPage;
510
+ this.emit("page-change", {
511
+ page: this.page,
512
+ pageSize: this.pageSize
513
+ });
514
+ }
515
+ }
516
+ _getTotalPages() {
517
+ return Math.max(1, Math.ceil(this.data.length / this.pageSize));
518
+ }
519
+ _paginateData(data) {
520
+ if (!this.paginated)
521
+ return data;
522
+ const start = (this.page - 1) * this.pageSize;
523
+ const end = start + this.pageSize;
524
+ return data.slice(start, end);
525
+ }
526
+ _getStartRow() {
527
+ if (this.data.length === 0)
528
+ return 0;
529
+ return (this.page - 1) * this.pageSize + 1;
530
+ }
531
+ _getEndRow() {
532
+ return Math.min(this.page * this.pageSize, this.data.length);
533
+ }
534
+ _handleRowClick = (e) => {
535
+ const row = e.target.closest(".table-row");
536
+ if (!row)
537
+ return;
538
+ if (e.target.closest(".select-cell"))
539
+ return;
540
+ const rowId = row.getAttribute("data-row-id");
541
+ const rowIndex = parseInt(row.getAttribute("data-row-index") || "0", 10);
542
+ if (rowId !== null) {
543
+ const id = this._parseId(rowId);
544
+ const rowData = this.data.find((r) => r.id === id);
545
+ if (rowData) {
546
+ this.emit("row-click", {
547
+ row: rowData,
548
+ rowIndex
549
+ });
550
+ if (this.selectionMode !== "none") {
551
+ this.toggleRowSelection(id);
552
+ }
553
+ }
554
+ }
555
+ };
556
+ _handleRowSelectChange = (e) => {
557
+ const input = e.target;
558
+ const rowId = input.getAttribute("data-row-id");
559
+ if (rowId !== null) {
560
+ const id = this._parseId(rowId);
561
+ if (input.checked) {
562
+ this.selectRow(id);
563
+ } else {
564
+ this.deselectRow(id);
565
+ }
566
+ }
567
+ };
568
+ _handleSelectAllChange = (e) => {
569
+ const input = e.target;
570
+ if (input.checked) {
571
+ this.selectAll();
572
+ } else {
573
+ this.deselectAll();
574
+ }
575
+ };
576
+ _parseId(idStr) {
577
+ const num = Number(idStr);
578
+ return isNaN(num) ? idStr : num;
579
+ }
580
+ selectRow(id) {
581
+ if (this.selectionMode === "single") {
582
+ this._internalSelectedIds.clear();
583
+ }
584
+ this._internalSelectedIds.add(id);
585
+ this._emitSelectionChange();
586
+ }
587
+ deselectRow(id) {
588
+ this._internalSelectedIds.delete(id);
589
+ this._emitSelectionChange();
590
+ }
591
+ toggleRowSelection(id) {
592
+ if (this._internalSelectedIds.has(id)) {
593
+ this.deselectRow(id);
594
+ } else {
595
+ this.selectRow(id);
596
+ }
597
+ }
598
+ selectAll() {
599
+ const visibleData = this._getProcessedData();
600
+ visibleData.forEach((row) => this._internalSelectedIds.add(row.id));
601
+ this._emitSelectionChange();
602
+ }
603
+ deselectAll() {
604
+ this._internalSelectedIds.clear();
605
+ this._emitSelectionChange();
606
+ }
607
+ getSelectedRows() {
608
+ return this.data.filter((row) => this._internalSelectedIds.has(row.id));
609
+ }
610
+ _emitSelectionChange() {
611
+ this.selectedIds = Array.from(this._internalSelectedIds);
612
+ this.emit("select", {
613
+ selectedIds: this.selectedIds,
614
+ selectedRows: this.getSelectedRows()
615
+ });
616
+ this.update();
617
+ }
618
+ _isRowSelected(row) {
619
+ return this._internalSelectedIds.has(row.id);
620
+ }
621
+ _isAllSelected() {
622
+ const visibleData = this._getProcessedData();
623
+ if (visibleData.length === 0)
624
+ return false;
625
+ return visibleData.every((row) => this._internalSelectedIds.has(row.id));
626
+ }
627
+ _isSomeSelected() {
628
+ const visibleData = this._getProcessedData();
629
+ return visibleData.some((row) => this._internalSelectedIds.has(row.id)) && !this._isAllSelected();
630
+ }
631
+ _getEffectiveColumns() {
632
+ if (this.columns && this.columns.length > 0) {
633
+ return this.columns.filter((col) => !col.hidden);
634
+ }
635
+ const columnElements = this.querySelectorAll("el-dm-table-column");
636
+ const cols = [];
637
+ columnElements.forEach((el) => {
638
+ if (el instanceof ElDmTableColumn) {
639
+ const colDef = el.toColumnDef();
640
+ if (!colDef.hidden) {
641
+ cols.push(colDef);
642
+ }
643
+ }
644
+ });
645
+ return cols;
646
+ }
647
+ _getProcessedData() {
648
+ let result = [...this.data || []];
649
+ result = this._sortData(result);
650
+ result = this._paginateData(result);
651
+ return result;
652
+ }
653
+ getVisibleData() {
654
+ return this._getProcessedData();
655
+ }
656
+ refresh() {
657
+ this.update();
658
+ }
659
+ _formatCellValue(value) {
660
+ if (value === null || value === undefined)
661
+ return "";
662
+ if (typeof value === "boolean")
663
+ return value ? "Yes" : "No";
664
+ if (value instanceof Date)
665
+ return value.toLocaleDateString();
666
+ return String(value);
667
+ }
668
+ _renderHeaderCell(column) {
669
+ const sortable = column.sortable;
670
+ const isSorted = this.sortColumn === column.key;
671
+ const sortIcon = isSorted ? this.sortDirection === "asc" ? ICONS.sortAsc : ICONS.sortDesc : ICONS.sort;
672
+ return `
673
+ <th
674
+ class="table-th ${sortable ? "sortable" : ""} ${isSorted ? "sorted" : ""}"
675
+ part="th"
676
+ data-column="${column.key}"
677
+ style="text-align: ${column.align || "left"}; ${column.width ? `width: ${column.width};` : ""}"
678
+ scope="col"
679
+ ${sortable ? `role="columnheader" aria-sort="${isSorted ? this.sortDirection === "asc" ? "ascending" : "descending" : "none"}" tabindex="0"` : ""}
680
+ >
681
+ <span class="th-content">
682
+ <span>${column.label}</span>
683
+ ${sortable ? `<span class="sort-icon">${sortIcon}</span>` : ""}
684
+ </span>
685
+ </th>
686
+ `;
687
+ }
688
+ _renderSelectAllCell() {
689
+ if (this.selectionMode !== "multiple") {
690
+ return '<th class="table-th select-cell" part="th"></th>';
691
+ }
692
+ const isAllSelected = this._isAllSelected();
693
+ const isSomeSelected = this._isSomeSelected();
694
+ return `
695
+ <th class="table-th select-cell" part="th">
696
+ <input
697
+ type="checkbox"
698
+ class="select-all"
699
+ ${isAllSelected ? "checked" : ""}
700
+ ${isSomeSelected ? 'data-indeterminate="true"' : ""}
701
+ aria-label="Select all rows"
702
+ />
703
+ </th>
704
+ `;
705
+ }
706
+ _renderSelectCell(row) {
707
+ const isSelected = this._isRowSelected(row);
708
+ const inputType = this.selectionMode === "single" ? "radio" : "checkbox";
709
+ return `
710
+ <td class="table-td select-cell" part="td">
711
+ <input
712
+ type="${inputType}"
713
+ class="row-select"
714
+ name="table-selection"
715
+ data-row-id="${row.id}"
716
+ ${isSelected ? "checked" : ""}
717
+ aria-label="Select row"
718
+ />
719
+ </td>
720
+ `;
721
+ }
722
+ _renderDataRow(row, index, columns) {
723
+ const isSelected = this._isRowSelected(row);
724
+ const showSelectionColumn = this.selectionMode !== "none";
725
+ return `
726
+ <tr
727
+ class="table-row ${isSelected ? "selected" : ""}"
728
+ part="row"
729
+ data-row-id="${row.id}"
730
+ data-row-index="${index}"
731
+ ${isSelected ? 'aria-selected="true"' : ""}
732
+ >
733
+ ${showSelectionColumn ? this._renderSelectCell(row) : ""}
734
+ ${columns.map((col) => `
735
+ <td
736
+ class="table-td"
737
+ part="td"
738
+ style="text-align: ${col.align || "left"};"
739
+ >
740
+ ${this._formatCellValue(row[col.key])}
741
+ </td>
742
+ `).join("")}
743
+ </tr>
744
+ `;
745
+ }
746
+ _renderLoadingRow(colCount) {
747
+ const totalCols = this.selectionMode !== "none" ? colCount + 1 : colCount;
748
+ return `
749
+ <tr class="loading-row">
750
+ <td colspan="${totalCols}">
751
+ <div class="loading-spinner"></div>
752
+ <div>Loading...</div>
753
+ </td>
754
+ </tr>
755
+ `;
756
+ }
757
+ _renderEmptyRow(colCount) {
758
+ const totalCols = this.selectionMode !== "none" ? colCount + 1 : colCount;
759
+ return `
760
+ <tr class="empty-row">
761
+ <td colspan="${totalCols}">
762
+ <slot name="empty">${this.emptyMessage}</slot>
763
+ </td>
764
+ </tr>
765
+ `;
766
+ }
767
+ _renderPagination() {
768
+ const totalPages = this._getTotalPages();
769
+ const showPageSizeSelect = this.pageSizeOptions && this.pageSizeOptions.length > 0;
770
+ return `
771
+ <div class="table-pagination" part="pagination">
772
+ <div class="pagination-info">
773
+ Showing ${this._getStartRow()} to ${this._getEndRow()} of ${this.data.length} entries
774
+ </div>
775
+ <div class="pagination-controls">
776
+ ${showPageSizeSelect ? `
777
+ <select class="page-size-select" aria-label="Rows per page">
778
+ ${this.pageSizeOptions.map((size) => `
779
+ <option value="${size}" ${size === this.pageSize ? "selected" : ""}>
780
+ ${size} / page
781
+ </option>
782
+ `).join("")}
783
+ </select>
784
+ ` : ""}
785
+ <div class="pagination-nav">
786
+ <button
787
+ class="pagination-btn"
788
+ data-action="first"
789
+ ${this.page <= 1 ? "disabled" : ""}
790
+ aria-label="First page"
791
+ >${ICONS.chevronFirst}</button>
792
+ <button
793
+ class="pagination-btn"
794
+ data-action="prev"
795
+ ${this.page <= 1 ? "disabled" : ""}
796
+ aria-label="Previous page"
797
+ >${ICONS.chevronLeft}</button>
798
+ <span class="pagination-current">
799
+ Page ${this.page} of ${totalPages}
800
+ </span>
801
+ <button
802
+ class="pagination-btn"
803
+ data-action="next"
804
+ ${this.page >= totalPages ? "disabled" : ""}
805
+ aria-label="Next page"
806
+ >${ICONS.chevronRight}</button>
807
+ <button
808
+ class="pagination-btn"
809
+ data-action="last"
810
+ ${this.page >= totalPages ? "disabled" : ""}
811
+ aria-label="Last page"
812
+ >${ICONS.chevronLast}</button>
813
+ </div>
814
+ </div>
815
+ </div>
816
+ `;
817
+ }
818
+ render() {
819
+ const columns = this._getEffectiveColumns();
820
+ const processedData = this._getProcessedData();
821
+ const showSelectionColumn = this.selectionMode !== "none";
822
+ return `
823
+ <div class="table-wrapper" part="wrapper">
824
+ <div class="table-header-actions" part="header-actions">
825
+ <slot name="header-actions"></slot>
826
+ </div>
827
+
828
+ <div class="table-container" part="container">
829
+ <table class="table" part="table" role="grid" aria-busy="${this.loading}">
830
+ <thead part="thead">
831
+ <tr part="header-row">
832
+ ${showSelectionColumn ? this._renderSelectAllCell() : ""}
833
+ ${columns.map((col) => this._renderHeaderCell(col)).join("")}
834
+ </tr>
835
+ </thead>
836
+ <tbody part="tbody">
837
+ ${this.loading ? this._renderLoadingRow(columns.length) : ""}
838
+ ${!this.loading && processedData.length === 0 ? this._renderEmptyRow(columns.length) : ""}
839
+ ${!this.loading ? processedData.map((row, idx) => this._renderDataRow(row, idx, columns)).join("") : ""}
840
+ </tbody>
841
+ </table>
842
+ </div>
843
+
844
+ ${this.paginated ? this._renderPagination() : ""}
845
+
846
+ <div class="table-footer-actions" part="footer-actions">
847
+ <slot name="footer-actions"></slot>
848
+ </div>
849
+ </div>
850
+ `;
851
+ }
852
+ }
853
+ function registerTable() {
854
+ if (!customElements.get("el-dm-table")) {
855
+ customElements.define("el-dm-table", ElDmTable);
856
+ }
857
+ }
858
+ // src/index.ts
859
+ function register() {
860
+ registerTable();
861
+ registerTableColumn();
862
+ }
863
+ export {
864
+ registerTableColumn,
865
+ registerTable,
866
+ register,
867
+ ElDmTableColumn,
868
+ ElDmTable
869
+ };
870
+
871
+ //# debugId=38CAEB22D9F53FAD64756E2164756E21
872
+ //# sourceMappingURL=index.js.map