@iyulab/data-components 0.3.0 → 0.3.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.
@@ -1,5 +1,618 @@
1
- import { URichTable } from './URichTable.component.js';
2
-
3
- URichTable.define("u-rich-table");
4
-
1
+ import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
3
+ import { richTableStyles } from "./styles.js";
4
+ import { parseTSV, toTSV } from "./utils/clipboard.js";
5
+ import { LitElement, html } from "lit";
6
+ import { customElement, property, state } from "lit/decorators.js";
7
+ //#region src/components/u-rich-table/URichTable.ts
8
+ var URichTable = class URichTable extends LitElement {
9
+ constructor(..._args) {
10
+ super(..._args);
11
+ this.columns = [];
12
+ this.data = [];
13
+ this.totalCount = 0;
14
+ this.pageSize = 25;
15
+ this.currentPage = 1;
16
+ this.loading = false;
17
+ this.emptyMessage = "데이터가 없습니다";
18
+ this.selectable = false;
19
+ this.editable = false;
20
+ this.addable = false;
21
+ this.filterable = false;
22
+ this.expandable = false;
23
+ this.selectedIds = /* @__PURE__ */ new Set();
24
+ this.focusedCell = null;
25
+ this.editingCell = null;
26
+ this.editValue = "";
27
+ this.expandedIds = /* @__PURE__ */ new Set();
28
+ this.sort = null;
29
+ this.filters = {};
30
+ this.validationErrors = /* @__PURE__ */ new Map();
31
+ this.rowErrors = /* @__PURE__ */ new Map();
32
+ this._lastSelectedIndex = -1;
33
+ this._onGlobalKeyDown = (e) => {
34
+ if (e.ctrlKey || e.metaKey) {
35
+ if (e.key === "c") this._handleCopy();
36
+ else if (e.key === "v") this._handlePaste();
37
+ else if (e.key === "a" && !this.editingCell) {
38
+ e.preventDefault();
39
+ this._selectAll();
40
+ }
41
+ }
42
+ if (!this.editingCell && this.focusedCell) {
43
+ if (e.key === "ArrowUp") {
44
+ e.preventDefault();
45
+ this._moveFocus(0, -1);
46
+ }
47
+ if (e.key === "ArrowDown") {
48
+ e.preventDefault();
49
+ this._moveFocus(0, 1);
50
+ }
51
+ if (e.key === "ArrowLeft") {
52
+ e.preventDefault();
53
+ this._moveFocus(-1, 0);
54
+ }
55
+ if (e.key === "ArrowRight") {
56
+ e.preventDefault();
57
+ this._moveFocus(1, 0);
58
+ }
59
+ if (e.key === "Enter") {
60
+ const col = this.columns[this.focusedCell.colIndex];
61
+ if (col?.editable) {
62
+ const value = this.data[this.focusedCell.rowIndex]?.[col.key];
63
+ this._onCellDblClick(this.focusedCell.rowIndex, this.focusedCell.colIndex, value);
64
+ }
65
+ }
66
+ if (e.key === " " && this.selectable) {
67
+ e.preventDefault();
68
+ const rowId = this.data[this.focusedCell.rowIndex]?._id;
69
+ if (rowId) this._onRowSelect(rowId);
70
+ }
71
+ if (e.key === "Delete" && this.selectedIds.size > 0) for (const row of this.getSelectedRows()) this.dispatchEvent(new CustomEvent("row-delete", {
72
+ detail: { row },
73
+ bubbles: true,
74
+ composed: true
75
+ }));
76
+ }
77
+ };
78
+ }
79
+ static {
80
+ this.styles = richTableStyles;
81
+ }
82
+ revertRow(_rowId) {}
83
+ setRowError(rowId, message) {
84
+ this.rowErrors = new Map(this.rowErrors).set(rowId, message);
85
+ }
86
+ clearRowError(rowId) {
87
+ const next = new Map(this.rowErrors);
88
+ next.delete(rowId);
89
+ this.rowErrors = next;
90
+ }
91
+ getSelectedRows() {
92
+ return this.data.filter((row) => this.selectedIds.has(row._id));
93
+ }
94
+ render() {
95
+ return html`
96
+ ${this._renderToolbar()}
97
+ <table>
98
+ ${this._renderHeader()}
99
+ <tbody>
100
+ ${this.filterable ? this._renderFilterRow() : ""}
101
+ ${this._renderBody()}
102
+ ${this.addable ? this._renderNewRow() : ""}
103
+ </tbody>
104
+ </table>
105
+ ${this._renderPagination()}
106
+ `;
107
+ }
108
+ _renderToolbar() {
109
+ const selectedCount = this.selectedIds.size;
110
+ return html`
111
+ <div class="toolbar">
112
+ ${this.selectable ? html`
113
+ <div class="selection-info">
114
+ <input type="checkbox"
115
+ .checked=${selectedCount > 0 && selectedCount === this.data.length}
116
+ .indeterminate=${selectedCount > 0 && selectedCount < this.data.length}
117
+ @change=${this._onSelectAll} />
118
+ ${selectedCount > 0 ? html`<span>${selectedCount}건 선택됨</span>` : ""}
119
+ </div>
120
+ ${selectedCount > 0 ? html`<slot name="bulk-actions"></slot>` : ""}
121
+ ` : ""}
122
+ <div style="flex:1"></div>
123
+ <slot name="toolbar-end"></slot>
124
+ ${this.addable ? html`
125
+ <button class="btn btn-success" @click=${this._onAddRowClick}>+ 새 행</button>
126
+ ` : ""}
127
+ </div>
128
+ `;
129
+ }
130
+ _renderHeader() {
131
+ return html`
132
+ <thead>
133
+ <tr>
134
+ ${this.selectable ? html`<th class="checkbox-cell"></th>` : ""}
135
+ ${this.expandable ? html`<th class="expand-cell"></th>` : ""}
136
+ ${this.columns.map((col) => html`
137
+ <th
138
+ class=${col.sortable ? "sortable" : ""}
139
+ style=${col.width ? `width: ${col.width}` : ""}
140
+ @click=${() => col.sortable && this._onSortClick(col.key)}>
141
+ ${col.label}
142
+ ${this.sort?.field === col.key ? html`
143
+ <span class="sort-indicator">${this.sort.direction === "asc" ? "▲" : "▼"}</span>
144
+ ` : ""}
145
+ </th>
146
+ `)}
147
+ <th class="actions-cell"></th>
148
+ </tr>
149
+ </thead>
150
+ `;
151
+ }
152
+ _renderFilterRow() {
153
+ return html`
154
+ <tr class="filter-row">
155
+ ${this.selectable ? html`<td></td>` : ""}
156
+ ${this.expandable ? html`<td></td>` : ""}
157
+ ${this.columns.map((col) => html`
158
+ <td>
159
+ ${col.filterable !== false ? col.filterType === "select" && col.options ? html`<select @change=${(e) => this._onFilterChange(col.key, e.target.value)}>
160
+ <option value="">전체</option>
161
+ ${col.options.map((o) => html`<option value=${o.value}>${o.label}</option>`)}
162
+ </select>` : html`<input
163
+ placeholder="필터..."
164
+ @input=${(e) => this._onFilterChange(col.key, e.target.value)} />` : ""}
165
+ </td>
166
+ `)}
167
+ <td></td>
168
+ </tr>
169
+ `;
170
+ }
171
+ _renderBody() {
172
+ if (this.loading) return html`<tr><td colspan=${this._colSpan()}><div class="loading-overlay">로딩 중...</div></td></tr>`;
173
+ if (this.data.length === 0) return html`<tr><td colspan=${this._colSpan()}><div class="empty-message">${this.emptyMessage}</div></td></tr>`;
174
+ return this.data.map((row, rowIdx) => {
175
+ const rowId = row._id;
176
+ const isSelected = this.selectedIds.has(rowId);
177
+ const isExpanded = this.expandedIds.has(rowId);
178
+ const hasError = this.rowErrors.has(rowId);
179
+ return html`
180
+ <tr class="${isSelected ? "selected" : ""} ${hasError ? "error" : ""} ${this.editingCell?.rowIndex === rowIdx ? "editing" : ""}">
181
+ ${this.selectable ? html`
182
+ <td class="checkbox-cell">
183
+ <input type="checkbox" .checked=${isSelected}
184
+ @change=${() => this._onRowSelect(rowId)}
185
+ @click=${(e) => e.shiftKey && this._onShiftSelect(rowIdx)} />
186
+ </td>
187
+ ` : ""}
188
+ ${this.expandable ? html`
189
+ <td class="expand-cell" @click=${() => this._onExpandToggle(rowId)}>
190
+ ${isExpanded ? "▼" : "▶"}
191
+ </td>
192
+ ` : ""}
193
+ ${this.columns.map((col, colIdx) => this._renderCell(row, rowIdx, col, colIdx))}
194
+ <td class="actions-cell">
195
+ <span style="cursor:pointer;color:#94a3b8" @click=${() => this._onRowMenu(row)}>⋯</span>
196
+ </td>
197
+ </tr>
198
+ ${isExpanded && this.detailRenderer ? html`
199
+ <tr class="detail-row">
200
+ <td colspan=${this._colSpan()}>${this.detailRenderer(row)}</td>
201
+ </tr>
202
+ ` : ""}
203
+ ${hasError ? html`
204
+ <tr><td colspan=${this._colSpan()} style="padding:2px 8px;background:#fef2f2;color:#ef4444;font-size:11px;">
205
+ ${this.rowErrors.get(rowId)}
206
+ </td></tr>
207
+ ` : ""}
208
+ `;
209
+ });
210
+ }
211
+ _renderCell(row, rowIdx, col, colIdx) {
212
+ const isEditing = this.editingCell?.rowIndex === rowIdx && this.editingCell?.colIndex === colIdx;
213
+ const isFocused = this.focusedCell?.rowIndex === rowIdx && this.focusedCell?.colIndex === colIdx;
214
+ const value = row[col.key];
215
+ if (isEditing && col.editable) {
216
+ const validationError = this.validationErrors.get(`${rowIdx}-${colIdx}`);
217
+ if (col.type === "select" && col.options) return html`
218
+ <td>
219
+ <select class="cell-edit-input" @change=${this._onCellEditConfirm} @keydown=${this._onEditKeyDown}>
220
+ ${col.options.map((o) => html`<option value=${o.value} ?selected=${o.value === String(value)}>${o.label}</option>`)}
221
+ </select>
222
+ </td>
223
+ `;
224
+ return html`
225
+ <td>
226
+ <input class="cell-edit-input ${validationError ? "invalid" : ""}"
227
+ type=${col.type === "number" ? "number" : col.type === "date" ? "date" : "text"}
228
+ .value=${this.editValue}
229
+ @input=${(e) => this.editValue = e.target.value}
230
+ @keydown=${this._onEditKeyDown}
231
+ @blur=${this._onCellEditConfirm} />
232
+ ${validationError ? html`<div class="validation-error">${validationError}</div>` : ""}
233
+ </td>
234
+ `;
235
+ }
236
+ return html`
237
+ <td class=${isFocused ? "focused-cell" : ""}
238
+ style=${col.align ? `text-align: ${col.align}` : ""}
239
+ @click=${() => this._onCellClick(rowIdx, colIdx)}
240
+ @dblclick=${() => col.editable && this._onCellDblClick(rowIdx, colIdx, value)}>
241
+ ${this._renderCellContent(col, value, row)}
242
+ </td>
243
+ `;
244
+ }
245
+ _renderCellContent(col, value, row) {
246
+ if (col.render) {
247
+ const result = col.render(value, row);
248
+ if (typeof result === "string") return result;
249
+ if (result instanceof HTMLElement) {
250
+ const container = document.createElement("span");
251
+ container.appendChild(result);
252
+ return html`${container}`;
253
+ }
254
+ return String(value ?? "");
255
+ }
256
+ if (col.type === "badge" && col.badgeColors) return html`<span class="badge" style="background:${col.badgeColors[String(value)] ?? "#f3f4f6"}">${this._getOptionLabel(col, value)}</span>`;
257
+ if (col.type === "select" && col.options) return this._getOptionLabel(col, value);
258
+ if (col.type === "number" && value != null) return Number(value).toLocaleString();
259
+ return String(value ?? "");
260
+ }
261
+ _renderNewRow() {
262
+ return html`
263
+ <tr class="new-row">
264
+ ${this.selectable ? html`<td class="checkbox-cell"><span style="color:#86efac">+</span></td>` : ""}
265
+ ${this.expandable ? html`<td></td>` : ""}
266
+ ${this.columns.map((col, colIdx) => html`
267
+ <td>
268
+ ${col.editable !== false ? html`
269
+ <input placeholder=${col.label}
270
+ data-new-col=${colIdx}
271
+ @keydown=${this._onNewRowKeyDown}
272
+ @focus=${this._onNewRowFocus} />
273
+ ` : html`<span></span>`}
274
+ </td>
275
+ `)}
276
+ <td></td>
277
+ </tr>
278
+ `;
279
+ }
280
+ _renderPagination() {
281
+ if (this.totalCount <= 0) return html``;
282
+ const totalPages = Math.ceil(this.totalCount / this.pageSize);
283
+ const start = (this.currentPage - 1) * this.pageSize + 1;
284
+ const end = Math.min(this.currentPage * this.pageSize, this.totalCount);
285
+ return html`
286
+ <div class="pagination">
287
+ <span>전체 ${this.totalCount.toLocaleString()}건 중 ${start}-${end} 표시</span>
288
+ <div class="page-buttons">
289
+ <button ?disabled=${this.currentPage <= 1} @click=${() => this._onPageChange(this.currentPage - 1)}>◀</button>
290
+ ${this._getPageNumbers(totalPages).map((p) => html`
291
+ <button class=${p === this.currentPage ? "active" : ""} @click=${() => this._onPageChange(p)}>${p}</button>
292
+ `)}
293
+ <button ?disabled=${this.currentPage >= totalPages} @click=${() => this._onPageChange(this.currentPage + 1)}>▶</button>
294
+ <select @change=${(e) => this._onPageSizeChange(Number(e.target.value))}>
295
+ ${[
296
+ 25,
297
+ 50,
298
+ 100
299
+ ].map((s) => html`<option value=${s} ?selected=${s === this.pageSize}>${s}행</option>`)}
300
+ </select>
301
+ </div>
302
+ </div>
303
+ `;
304
+ }
305
+ _onSelectAll(e) {
306
+ if (e.target.checked) this.selectedIds = new Set(this.data.map((r) => r._id));
307
+ else this.selectedIds = /* @__PURE__ */ new Set();
308
+ this._fireSelectionChange();
309
+ }
310
+ _onRowSelect(rowId) {
311
+ const next = new Set(this.selectedIds);
312
+ if (next.has(rowId)) next.delete(rowId);
313
+ else next.add(rowId);
314
+ this.selectedIds = next;
315
+ this._fireSelectionChange();
316
+ }
317
+ _onShiftSelect(rowIdx) {
318
+ if (this._lastSelectedIndex < 0) return;
319
+ const start = Math.min(this._lastSelectedIndex, rowIdx);
320
+ const end = Math.max(this._lastSelectedIndex, rowIdx);
321
+ const next = new Set(this.selectedIds);
322
+ for (let i = start; i <= end; i++) next.add(this.data[i]._id);
323
+ this.selectedIds = next;
324
+ this._fireSelectionChange();
325
+ }
326
+ _onSortClick(field) {
327
+ if (this.sort?.field === field) if (this.sort.direction === "asc") this.sort = {
328
+ field,
329
+ direction: "desc"
330
+ };
331
+ else this.sort = null;
332
+ else this.sort = {
333
+ field,
334
+ direction: "asc"
335
+ };
336
+ this.dispatchEvent(new CustomEvent("sort-change", {
337
+ detail: this.sort ? {
338
+ field: this.sort.field,
339
+ direction: this.sort.direction
340
+ } : {
341
+ field,
342
+ direction: null
343
+ },
344
+ bubbles: true,
345
+ composed: true
346
+ }));
347
+ }
348
+ _onFilterChange(field, value) {
349
+ if (value) this.filters = {
350
+ ...this.filters,
351
+ [field]: value
352
+ };
353
+ else {
354
+ const { [field]: _, ...rest } = this.filters;
355
+ this.filters = rest;
356
+ }
357
+ this.dispatchEvent(new CustomEvent("filter-change", {
358
+ detail: { filters: this.filters },
359
+ bubbles: true,
360
+ composed: true
361
+ }));
362
+ }
363
+ _onCellClick(rowIdx, colIdx) {
364
+ this.focusedCell = {
365
+ rowIndex: rowIdx,
366
+ colIndex: colIdx
367
+ };
368
+ this._lastSelectedIndex = rowIdx;
369
+ }
370
+ _onCellDblClick(rowIdx, colIdx, value) {
371
+ this.editingCell = {
372
+ rowIndex: rowIdx,
373
+ colIndex: colIdx
374
+ };
375
+ this.editValue = String(value ?? "");
376
+ this.requestUpdate();
377
+ requestAnimationFrame(() => {
378
+ const input = this.shadowRoot?.querySelector(".cell-edit-input");
379
+ input?.focus();
380
+ input?.select();
381
+ });
382
+ }
383
+ _onEditKeyDown(e) {
384
+ if (e.key === "Enter") {
385
+ e.preventDefault();
386
+ this._onCellEditConfirm();
387
+ if (this.editingCell && this.editingCell.rowIndex < this.data.length - 1) {
388
+ const nextRow = this.editingCell.rowIndex + 1;
389
+ const col = this.editingCell.colIndex;
390
+ const nextValue = this.data[nextRow][this.columns[col].key];
391
+ this._onCellDblClick(nextRow, col, nextValue);
392
+ }
393
+ } else if (e.key === "Escape") {
394
+ this.editingCell = null;
395
+ this.editValue = "";
396
+ } else if (e.key === "Tab") {
397
+ e.preventDefault();
398
+ this._onCellEditConfirm();
399
+ this._moveToNextEditableCell(e.shiftKey);
400
+ }
401
+ }
402
+ _onCellEditConfirm() {
403
+ if (!this.editingCell) return;
404
+ const { rowIndex, colIndex } = this.editingCell;
405
+ const col = this.columns[colIndex];
406
+ const row = this.data[rowIndex];
407
+ const oldValue = row[col.key];
408
+ let newValue = this.editValue;
409
+ if (col.type === "number") newValue = Number(newValue);
410
+ if (col.required && !newValue && newValue !== 0) {
411
+ this.validationErrors = new Map(this.validationErrors).set(`${rowIndex}-${colIndex}`, "필수 항목입니다");
412
+ return;
413
+ }
414
+ if (col.validator) {
415
+ const error = col.validator(newValue, row);
416
+ if (error) {
417
+ this.validationErrors = new Map(this.validationErrors).set(`${rowIndex}-${colIndex}`, error);
418
+ return;
419
+ }
420
+ }
421
+ const nextErrors = new Map(this.validationErrors);
422
+ nextErrors.delete(`${rowIndex}-${colIndex}`);
423
+ this.validationErrors = nextErrors;
424
+ if (newValue !== oldValue) this.dispatchEvent(new CustomEvent("row-update", {
425
+ detail: {
426
+ row,
427
+ field: col.key,
428
+ value: newValue,
429
+ oldValue
430
+ },
431
+ bubbles: true,
432
+ composed: true
433
+ }));
434
+ this.editingCell = null;
435
+ this.editValue = "";
436
+ }
437
+ _onExpandToggle(rowId) {
438
+ const next = new Set(this.expandedIds);
439
+ const expanded = !next.has(rowId);
440
+ if (expanded) next.add(rowId);
441
+ else next.delete(rowId);
442
+ this.expandedIds = next;
443
+ this.dispatchEvent(new CustomEvent("row-expand", {
444
+ detail: {
445
+ row: this.data.find((r) => r._id === rowId),
446
+ expanded
447
+ },
448
+ bubbles: true,
449
+ composed: true
450
+ }));
451
+ }
452
+ _onAddRowClick() {
453
+ (this.shadowRoot?.querySelector(".new-row input"))?.focus();
454
+ }
455
+ _onNewRowFocus() {}
456
+ _onNewRowKeyDown(e) {
457
+ if (e.key === "Enter") {
458
+ e.preventDefault();
459
+ const inputs = Array.from(this.shadowRoot?.querySelectorAll(".new-row input") ?? []);
460
+ const newRow = {};
461
+ this.columns.forEach((col, i) => {
462
+ if (col.editable !== false && inputs[i]) {
463
+ let val = inputs[i].value;
464
+ if (col.type === "number") val = Number(val);
465
+ newRow[col.key] = val;
466
+ }
467
+ });
468
+ this.dispatchEvent(new CustomEvent("row-create", {
469
+ detail: { row: newRow },
470
+ bubbles: true,
471
+ composed: true
472
+ }));
473
+ inputs.forEach((input) => input.value = "");
474
+ inputs[0]?.focus();
475
+ } else if (e.key === "Tab" && !e.shiftKey) {
476
+ const target = e.target;
477
+ if (Number(target.dataset.newCol) >= this.columns.filter((c) => c.editable !== false).length - 1) {
478
+ e.preventDefault();
479
+ this._onNewRowKeyDown(new KeyboardEvent("keydown", { key: "Enter" }));
480
+ }
481
+ }
482
+ }
483
+ _onRowMenu(row) {
484
+ this.dispatchEvent(new CustomEvent("row-delete", {
485
+ detail: { row },
486
+ bubbles: true,
487
+ composed: true
488
+ }));
489
+ }
490
+ _onPageChange(page) {
491
+ this.dispatchEvent(new CustomEvent("page-change", {
492
+ detail: {
493
+ page,
494
+ pageSize: this.pageSize
495
+ },
496
+ bubbles: true,
497
+ composed: true
498
+ }));
499
+ }
500
+ _onPageSizeChange(pageSize) {
501
+ this.dispatchEvent(new CustomEvent("page-change", {
502
+ detail: {
503
+ page: 1,
504
+ pageSize
505
+ },
506
+ bubbles: true,
507
+ composed: true
508
+ }));
509
+ }
510
+ _colSpan() {
511
+ let span = this.columns.length + 1;
512
+ if (this.selectable) span++;
513
+ if (this.expandable) span++;
514
+ return span;
515
+ }
516
+ _getOptionLabel(col, value) {
517
+ return col.options?.find((o) => o.value === String(value))?.label ?? String(value ?? "");
518
+ }
519
+ _getPageNumbers(totalPages) {
520
+ const pages = [];
521
+ const start = Math.max(1, this.currentPage - 2);
522
+ const end = Math.min(totalPages, start + 4);
523
+ for (let i = start; i <= end; i++) pages.push(i);
524
+ return pages;
525
+ }
526
+ _moveToNextEditableCell(reverse) {
527
+ if (!this.editingCell) return;
528
+ let { rowIndex, colIndex } = this.editingCell;
529
+ const editableCols = this.columns.map((c, i) => c.editable ? i : -1).filter((i) => i >= 0);
530
+ const currentIdx = editableCols.indexOf(colIndex);
531
+ if (reverse) {
532
+ if (currentIdx > 0) colIndex = editableCols[currentIdx - 1];
533
+ else if (rowIndex > 0) {
534
+ rowIndex--;
535
+ colIndex = editableCols[editableCols.length - 1];
536
+ }
537
+ } else if (currentIdx < editableCols.length - 1) colIndex = editableCols[currentIdx + 1];
538
+ else if (rowIndex < this.data.length - 1) {
539
+ rowIndex++;
540
+ colIndex = editableCols[0];
541
+ }
542
+ const value = this.data[rowIndex]?.[this.columns[colIndex]?.key];
543
+ this._onCellDblClick(rowIndex, colIndex, value);
544
+ }
545
+ _fireSelectionChange() {
546
+ this.dispatchEvent(new CustomEvent("selection-change", {
547
+ detail: { selectedRows: this.getSelectedRows() },
548
+ bubbles: true,
549
+ composed: true
550
+ }));
551
+ }
552
+ connectedCallback() {
553
+ super.connectedCallback();
554
+ this.addEventListener("keydown", this._onGlobalKeyDown);
555
+ }
556
+ disconnectedCallback() {
557
+ super.disconnectedCallback();
558
+ this.removeEventListener("keydown", this._onGlobalKeyDown);
559
+ }
560
+ async _handleCopy() {
561
+ const rows = this.getSelectedRows();
562
+ if (rows.length === 0) return;
563
+ const tsv = toTSV(rows, this.columns);
564
+ await navigator.clipboard.writeText(tsv);
565
+ }
566
+ async _handlePaste() {
567
+ if (this.editingCell) return;
568
+ const text = await navigator.clipboard.readText();
569
+ if (!text.trim()) return;
570
+ const parsedRows = parseTSV(text, this.columns);
571
+ if (parsedRows.length === 0) return;
572
+ this.dispatchEvent(new CustomEvent("paste", {
573
+ detail: { rows: parsedRows },
574
+ bubbles: true,
575
+ composed: true
576
+ }));
577
+ }
578
+ _moveFocus(dx, dy) {
579
+ if (!this.focusedCell) return;
580
+ const newCol = Math.max(0, Math.min(this.columns.length - 1, this.focusedCell.colIndex + dx));
581
+ this.focusedCell = {
582
+ rowIndex: Math.max(0, Math.min(this.data.length - 1, this.focusedCell.rowIndex + dy)),
583
+ colIndex: newCol
584
+ };
585
+ }
586
+ _selectAll() {
587
+ this.selectedIds = new Set(this.data.map((r) => r._id));
588
+ this._fireSelectionChange();
589
+ }
590
+ static define(tagName = "u-rich-table") {
591
+ if (!customElements.get(tagName)) customElements.define(tagName, this);
592
+ }
593
+ };
594
+ __decorate([property({ type: Array }), __decorateMetadata("design:type", Array)], URichTable.prototype, "columns", void 0);
595
+ __decorate([property({ type: Array }), __decorateMetadata("design:type", Array)], URichTable.prototype, "data", void 0);
596
+ __decorate([property({ type: Number }), __decorateMetadata("design:type", Object)], URichTable.prototype, "totalCount", void 0);
597
+ __decorate([property({ type: Number }), __decorateMetadata("design:type", Object)], URichTable.prototype, "pageSize", void 0);
598
+ __decorate([property({ type: Number }), __decorateMetadata("design:type", Object)], URichTable.prototype, "currentPage", void 0);
599
+ __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "loading", void 0);
600
+ __decorate([property({ type: String }), __decorateMetadata("design:type", Object)], URichTable.prototype, "emptyMessage", void 0);
601
+ __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "selectable", void 0);
602
+ __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "editable", void 0);
603
+ __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "addable", void 0);
604
+ __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "filterable", void 0);
605
+ __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "expandable", void 0);
606
+ __decorate([property({ attribute: false }), __decorateMetadata("design:type", Function)], URichTable.prototype, "detailRenderer", void 0);
607
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "selectedIds", void 0);
608
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "focusedCell", void 0);
609
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "editingCell", void 0);
610
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "editValue", void 0);
611
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "expandedIds", void 0);
612
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "sort", void 0);
613
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "filters", void 0);
614
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "validationErrors", void 0);
615
+ __decorate([state(), __decorateMetadata("design:type", Object)], URichTable.prototype, "rowErrors", void 0);
616
+ URichTable = __decorate([customElement("u-rich-table")], URichTable);
617
+ //#endregion
5
618
  export { URichTable };
@@ -1,6 +1,6 @@
1
- import { css } from 'lit';
2
-
3
- const richTableStyles = css`
1
+ import { css } from "lit";
2
+ //#region src/components/u-rich-table/styles.ts
3
+ var richTableStyles = css`
4
4
  :host {
5
5
  display: block;
6
6
  font-family: system-ui, -apple-system, sans-serif;
@@ -429,5 +429,5 @@ const richTableStyles = css`
429
429
  color: var(--u-txt-color-disabled, #525252);
430
430
  }
431
431
  `;
432
-
432
+ //#endregion
433
433
  export { richTableStyles };