@iyulab/data-components 0.7.0 → 0.8.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.
@@ -9,6 +9,19 @@ export declare class URichTable extends LitElement {
9
9
  currentPage: number;
10
10
  loading: boolean;
11
11
  emptyMessage: string;
12
+ /** 로딩 표시 문구 */
13
+ loadingMessage: string;
14
+ /** 필터 입력 placeholder */
15
+ filterPlaceholder: string;
16
+ /** 필터 select 의 "전체" 항목 문구 */
17
+ filterAllLabel: string;
18
+ /** 새 행 추가 버튼 문구 */
19
+ addRowLabel: string;
20
+ /**
21
+ * 페이지 정보 문구. (전체, 시작, 끝) 을 받아 문자열을 만든다.
22
+ * 언어마다 어순이 달라 템플릿 문자열이 아니라 함수로 연다.
23
+ */
24
+ pageInfoFormatter: (total: number, start: number, end: number) => string;
12
25
  selectable: boolean;
13
26
  editable: boolean;
14
27
  addable: boolean;
@@ -27,6 +40,18 @@ export declare class URichTable extends LitElement {
27
40
  revertRow(_rowId: string): void;
28
41
  setRowError(rowId: string, message: string): void;
29
42
  clearRowError(rowId: string): void;
43
+ /**
44
+ * 행의 식별자. 선택·확장·행 오류 상태가 전부 이 값으로 추적된다.
45
+ *
46
+ * ⚠`_id` 는 이 컴포넌트가 **부여하지 않는다.** 소비자가 넣어 주지 않으면 모든 행의
47
+ * `_id` 가 `undefined` 가 되고, Set 은 그 하나만 담으므로 **한 행을 고르면 전부
48
+ * 골라진다.** 그래서 없을 때는 위치를 대신 쓴다 — 다만 위치 기반 식별은 데이터가
49
+ * 재정렬·재페이징되면 **선택이 다른 행으로 옮겨간다.** 정렬/필터/페이지가 소비자
50
+ * 책임인 컴포넌트이므로, 실제 사용에서는 `_id` 를 주는 것이 옳다.
51
+ */
52
+ private _rowId;
53
+ private _warnedMissingRowId;
54
+ private _warnMissingRowId;
30
55
  getSelectedRows(): Record<string, unknown>[];
31
56
  render(): TemplateResult;
32
57
  private _renderToolbar;
@@ -1,5 +1,5 @@
1
- import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.142.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.142.0/helpers/esm/decorate.js";
3
3
  import { richTableStyles } from "./styles.js";
4
4
  import { parseTSV, toTSV } from "./utils/clipboard.js";
5
5
  import { LitElement, html } from "lit";
@@ -15,6 +15,11 @@ var URichTable = class URichTable extends LitElement {
15
15
  this.currentPage = 1;
16
16
  this.loading = false;
17
17
  this.emptyMessage = "데이터가 없습니다";
18
+ this.loadingMessage = "로딩 중...";
19
+ this.filterPlaceholder = "필터...";
20
+ this.filterAllLabel = "전체";
21
+ this.addRowLabel = "+ 새 행";
22
+ this.pageInfoFormatter = (total, start, end) => `전체 ${total.toLocaleString()}건 중 ${start}-${end} 표시`;
18
23
  this.selectable = false;
19
24
  this.editable = false;
20
25
  this.addable = false;
@@ -29,6 +34,7 @@ var URichTable = class URichTable extends LitElement {
29
34
  this.filters = {};
30
35
  this.validationErrors = /* @__PURE__ */ new Map();
31
36
  this.rowErrors = /* @__PURE__ */ new Map();
37
+ this._warnedMissingRowId = false;
32
38
  this._lastSelectedIndex = -1;
33
39
  this._onGlobalKeyDown = (e) => {
34
40
  if (e.ctrlKey || e.metaKey) {
@@ -65,7 +71,8 @@ var URichTable = class URichTable extends LitElement {
65
71
  }
66
72
  if (e.key === " " && this.selectable) {
67
73
  e.preventDefault();
68
- const rowId = this.data[this.focusedCell.rowIndex]?._id;
74
+ const r = this.data[this.focusedCell.rowIndex];
75
+ const rowId = r ? this._rowId(r, this.focusedCell.rowIndex) : void 0;
69
76
  if (rowId) this._onRowSelect(rowId);
70
77
  }
71
78
  if (e.key === "Delete" && this.selectedIds.size > 0) for (const row of this.getSelectedRows()) this.dispatchEvent(new CustomEvent("row-delete", {
@@ -88,8 +95,28 @@ var URichTable = class URichTable extends LitElement {
88
95
  next.delete(rowId);
89
96
  this.rowErrors = next;
90
97
  }
98
+ /**
99
+ * 행의 식별자. 선택·확장·행 오류 상태가 전부 이 값으로 추적된다.
100
+ *
101
+ * ⚠`_id` 는 이 컴포넌트가 **부여하지 않는다.** 소비자가 넣어 주지 않으면 모든 행의
102
+ * `_id` 가 `undefined` 가 되고, Set 은 그 하나만 담으므로 **한 행을 고르면 전부
103
+ * 골라진다.** 그래서 없을 때는 위치를 대신 쓴다 — 다만 위치 기반 식별은 데이터가
104
+ * 재정렬·재페이징되면 **선택이 다른 행으로 옮겨간다.** 정렬/필터/페이지가 소비자
105
+ * 책임인 컴포넌트이므로, 실제 사용에서는 `_id` 를 주는 것이 옳다.
106
+ */
107
+ _rowId(row, index) {
108
+ const id = row._id;
109
+ if (id !== void 0 && id !== null) return String(id);
110
+ this._warnMissingRowId();
111
+ return `#${index}`;
112
+ }
113
+ _warnMissingRowId() {
114
+ if (this._warnedMissingRowId) return;
115
+ this._warnedMissingRowId = true;
116
+ console.warn("[@iyulab/data-components] u-rich-table: 행에 `_id` 가 없어 **위치**로 식별합니다. 데이터가 재정렬·재페이징되면 선택 상태가 다른 행으로 옮겨갑니다. 각 행에 고유한 `_id` 를 부여하세요.");
117
+ }
91
118
  getSelectedRows() {
92
- return this.data.filter((row) => this.selectedIds.has(row._id));
119
+ return this.data.filter((row, i) => this.selectedIds.has(this._rowId(row, i)));
93
120
  }
94
121
  render() {
95
122
  return html`
@@ -122,7 +149,7 @@ var URichTable = class URichTable extends LitElement {
122
149
  <div style="flex:1"></div>
123
150
  <slot name="toolbar-end"></slot>
124
151
  ${this.addable ? html`
125
- <button class="btn btn-success" @click=${this._onAddRowClick}>+ 새 행</button>
152
+ <button class="btn btn-success" @click=${this._onAddRowClick}>${this.addRowLabel}</button>
126
153
  ` : ""}
127
154
  </div>
128
155
  `;
@@ -157,10 +184,10 @@ var URichTable = class URichTable extends LitElement {
157
184
  ${this.columns.map((col) => html`
158
185
  <td>
159
186
  ${col.filterable !== false ? col.filterType === "select" && col.options ? html`<select @change=${(e) => this._onFilterChange(col.key, e.target.value)}>
160
- <option value="">전체</option>
187
+ <option value="">${this.filterAllLabel}</option>
161
188
  ${col.options.map((o) => html`<option value=${o.value}>${o.label}</option>`)}
162
189
  </select>` : html`<input
163
- placeholder="필터..."
190
+ placeholder=${this.filterPlaceholder}
164
191
  @input=${(e) => this._onFilterChange(col.key, e.target.value)} />` : ""}
165
192
  </td>
166
193
  `)}
@@ -169,10 +196,10 @@ var URichTable = class URichTable extends LitElement {
169
196
  `;
170
197
  }
171
198
  _renderBody() {
172
- if (this.loading) return html`<tr><td colspan=${this._colSpan()}><div class="loading-overlay">로딩 중...</div></td></tr>`;
199
+ if (this.loading) return html`<tr><td colspan=${this._colSpan()}><div class="loading-overlay">${this.loadingMessage}</div></td></tr>`;
173
200
  if (this.data.length === 0) return html`<tr><td colspan=${this._colSpan()}><div class="empty-message">${this.emptyMessage}</div></td></tr>`;
174
201
  return this.data.map((row, rowIdx) => {
175
- const rowId = row._id;
202
+ const rowId = this._rowId(row, rowIdx);
176
203
  const isSelected = this.selectedIds.has(rowId);
177
204
  const isExpanded = this.expandedIds.has(rowId);
178
205
  const hasError = this.rowErrors.has(rowId);
@@ -192,7 +219,7 @@ var URichTable = class URichTable extends LitElement {
192
219
  ` : ""}
193
220
  ${this.columns.map((col, colIdx) => this._renderCell(row, rowIdx, col, colIdx))}
194
221
  <td class="actions-cell">
195
- <span style="cursor:pointer;color:#94a3b8" @click=${() => this._onRowMenu(row)}>⋯</span>
222
+ <span class="row-menu" @click=${() => this._onRowMenu(row)}>⋯</span>
196
223
  </td>
197
224
  </tr>
198
225
  ${isExpanded && this.detailRenderer ? html`
@@ -201,7 +228,7 @@ var URichTable = class URichTable extends LitElement {
201
228
  </tr>
202
229
  ` : ""}
203
230
  ${hasError ? html`
204
- <tr><td colspan=${this._colSpan()} style="padding:2px 8px;background:#fef2f2;color:#ef4444;font-size:11px;">
231
+ <tr><td colspan=${this._colSpan()} class="row-error-cell">
205
232
  ${this.rowErrors.get(rowId)}
206
233
  </td></tr>
207
234
  ` : ""}
@@ -253,7 +280,10 @@ var URichTable = class URichTable extends LitElement {
253
280
  }
254
281
  return String(value ?? "");
255
282
  }
256
- if (col.type === "badge" && col.badgeColors) return html`<span class="badge" style="background:${col.badgeColors[String(value)] ?? "#f3f4f6"}">${this._getOptionLabel(col, value)}</span>`;
283
+ if (col.type === "badge" && col.badgeColors) {
284
+ const color = col.badgeColors[String(value)] ?? "#f3f4f6";
285
+ return html`<span class="badge" style="background:${color}">${this._getOptionLabel(col, value)}</span>`;
286
+ }
257
287
  if (col.type === "select" && col.options) return this._getOptionLabel(col, value);
258
288
  if (col.type === "number" && value != null) return Number(value).toLocaleString();
259
289
  return String(value ?? "");
@@ -261,7 +291,7 @@ var URichTable = class URichTable extends LitElement {
261
291
  _renderNewRow() {
262
292
  return html`
263
293
  <tr class="new-row">
264
- ${this.selectable ? html`<td class="checkbox-cell"><span style="color:#86efac">+</span></td>` : ""}
294
+ ${this.selectable ? html`<td class="checkbox-cell"><span class="new-row-marker">+</span></td>` : ""}
265
295
  ${this.expandable ? html`<td></td>` : ""}
266
296
  ${this.columns.map((col, colIdx) => html`
267
297
  <td>
@@ -284,7 +314,7 @@ var URichTable = class URichTable extends LitElement {
284
314
  const end = Math.min(this.currentPage * this.pageSize, this.totalCount);
285
315
  return html`
286
316
  <div class="pagination">
287
- <span>전체 ${this.totalCount.toLocaleString()}건 ${start}-${end} 표시</span>
317
+ <span>${this.pageInfoFormatter(this.totalCount, start, end)}</span>
288
318
  <div class="page-buttons">
289
319
  <button ?disabled=${this.currentPage <= 1} @click=${() => this._onPageChange(this.currentPage - 1)}>◀</button>
290
320
  ${this._getPageNumbers(totalPages).map((p) => html`
@@ -303,7 +333,7 @@ var URichTable = class URichTable extends LitElement {
303
333
  `;
304
334
  }
305
335
  _onSelectAll(e) {
306
- if (e.target.checked) this.selectedIds = new Set(this.data.map((r) => r._id));
336
+ if (e.target.checked) this.selectedIds = new Set(this.data.map((r, i) => this._rowId(r, i)));
307
337
  else this.selectedIds = /* @__PURE__ */ new Set();
308
338
  this._fireSelectionChange();
309
339
  }
@@ -319,7 +349,7 @@ var URichTable = class URichTable extends LitElement {
319
349
  const start = Math.min(this._lastSelectedIndex, rowIdx);
320
350
  const end = Math.max(this._lastSelectedIndex, rowIdx);
321
351
  const next = new Set(this.selectedIds);
322
- for (let i = start; i <= end; i++) next.add(this.data[i]._id);
352
+ for (let i = start; i <= end; i++) next.add(this._rowId(this.data[i], i));
323
353
  this.selectedIds = next;
324
354
  this._fireSelectionChange();
325
355
  }
@@ -442,7 +472,7 @@ var URichTable = class URichTable extends LitElement {
442
472
  this.expandedIds = next;
443
473
  this.dispatchEvent(new CustomEvent("row-expand", {
444
474
  detail: {
445
- row: this.data.find((r) => r._id === rowId),
475
+ row: this.data.find((r, i) => this._rowId(r, i) === rowId),
446
476
  expanded
447
477
  },
448
478
  bubbles: true,
@@ -585,7 +615,7 @@ var URichTable = class URichTable extends LitElement {
585
615
  };
586
616
  }
587
617
  _selectAll() {
588
- this.selectedIds = new Set(this.data.map((r) => r._id));
618
+ this.selectedIds = new Set(this.data.map((r, i) => this._rowId(r, i)));
589
619
  this._fireSelectionChange();
590
620
  }
591
621
  static define(tagName = "u-rich-table") {
@@ -599,6 +629,11 @@ __decorate([property({ type: Number }), __decorateMetadata("design:type", Object
599
629
  __decorate([property({ type: Number }), __decorateMetadata("design:type", Object)], URichTable.prototype, "currentPage", void 0);
600
630
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "loading", void 0);
601
631
  __decorate([property({ type: String }), __decorateMetadata("design:type", Object)], URichTable.prototype, "emptyMessage", void 0);
632
+ __decorate([property({ type: String }), __decorateMetadata("design:type", Object)], URichTable.prototype, "loadingMessage", void 0);
633
+ __decorate([property({ type: String }), __decorateMetadata("design:type", Object)], URichTable.prototype, "filterPlaceholder", void 0);
634
+ __decorate([property({ type: String }), __decorateMetadata("design:type", Object)], URichTable.prototype, "filterAllLabel", void 0);
635
+ __decorate([property({ type: String }), __decorateMetadata("design:type", Object)], URichTable.prototype, "addRowLabel", void 0);
636
+ __decorate([property({ attribute: false }), __decorateMetadata("design:type", Function)], URichTable.prototype, "pageInfoFormatter", void 0);
602
637
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "selectable", void 0);
603
638
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "editable", void 0);
604
639
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "addable", void 0);