@iyulab/data-components 0.7.1 → 0.9.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.
@@ -12,14 +12,14 @@ var baseStyles = css`
12
12
  height: 100%;
13
13
  overflow: hidden;
14
14
  outline: none;
15
- border: 1px solid var(--u-border-color, #e2e8f0);
15
+ border: 1px solid var(--u-border-color, #E0E0E0);
16
16
  border-radius: 4px;
17
- background: var(--u-bg-color, #fff);
17
+ background: var(--u-bg-color, #FFFFFF);
18
18
  }
19
19
 
20
20
  .sheet-container:focus-within {
21
- border-color: var(--u-blue-500, #3b82f6);
22
- box-shadow: 0 0 0 2px var(--u-blue-100, #dbeafe);
21
+ border-color: var(--u-blue-500, #2196F3);
22
+ box-shadow: 0 0 0 2px var(--u-blue-100, #BBDEFB);
23
23
  }
24
24
 
25
25
  .sheet-scroll {
@@ -43,15 +43,15 @@ var baseStyles = css`
43
43
  z-index: 4;
44
44
  width: 48px;
45
45
  min-width: 48px;
46
- background: var(--u-neutral-100, #f1f5f9);
47
- border-right: 1px solid var(--u-border-color, #e2e8f0);
48
- border-bottom: 2px solid var(--u-border-color, #e2e8f0);
46
+ background: var(--u-neutral-100, #F5F5F5);
47
+ border-right: 1px solid var(--u-border-color, #E0E0E0);
48
+ border-bottom: 2px solid var(--u-border-color, #E0E0E0);
49
49
  cursor: pointer;
50
50
  user-select: none;
51
51
  }
52
52
 
53
53
  .corner:hover {
54
- background: var(--u-neutral-200, #e2e8f0);
54
+ background: var(--u-neutral-200, #EEEEEE);
55
55
  }
56
56
 
57
57
  /* Column headers (A, B, C...) */
@@ -59,14 +59,14 @@ var baseStyles = css`
59
59
  position: sticky;
60
60
  top: 0;
61
61
  z-index: 2;
62
- background: var(--u-neutral-100, #f1f5f9);
62
+ background: var(--u-neutral-100, #F5F5F5);
63
63
  padding: 4px 8px;
64
64
  text-align: center;
65
65
  font-size: 12px;
66
66
  font-weight: 600;
67
- color: var(--u-txt-color-weak, #64748b);
68
- border-right: 1px solid var(--u-border-color, #e2e8f0);
69
- border-bottom: 2px solid var(--u-border-color, #e2e8f0);
67
+ color: var(--u-txt-color-weak, #757575);
68
+ border-right: 1px solid var(--u-border-color, #E0E0E0);
69
+ border-bottom: 2px solid var(--u-border-color, #E0E0E0);
70
70
  white-space: nowrap;
71
71
  min-width: 80px;
72
72
  cursor: pointer;
@@ -75,8 +75,8 @@ var baseStyles = css`
75
75
  }
76
76
 
77
77
  .col-header.col-selected {
78
- background: var(--u-blue-200, #bfdbfe);
79
- color: var(--u-blue-800, #1e40af);
78
+ background: var(--u-blue-200, #90CAF9);
79
+ color: var(--u-blue-800, #1565C0);
80
80
  }
81
81
 
82
82
  .resize-handle {
@@ -102,7 +102,7 @@ var baseStyles = css`
102
102
  }
103
103
 
104
104
  .resize-handle:hover::after {
105
- background: var(--u-blue-500, #3b82f6);
105
+ background: var(--u-blue-500, #2196F3);
106
106
  }
107
107
 
108
108
  .sheet-container.is-resizing {
@@ -124,28 +124,31 @@ var baseStyles = css`
124
124
  text-align: right;
125
125
  padding: 0 6px;
126
126
  font-size: 11px;
127
- color: var(--u-txt-color-weak, #64748b);
128
- background: var(--u-neutral-50, #f8fafc);
129
- border-right: 1px solid var(--u-border-color, #e2e8f0);
130
- border-bottom: 1px solid var(--u-border-color-weak, #f1f5f9);
127
+ color: var(--u-txt-color-weak, #757575);
128
+ /* 예외 — 표면 높이 축이 없다. 행번호 여백은 "비활성"이 아니라 시트 바탕보다
129
+ 내려간 면이다. --u-bg-color-disabled 를 쓰면 다크에서 바탕(neutral-100)
130
+ 같은 값이 되어 여백 구분이 사라진다 — 스냅샷이 그것을 잡았다. */
131
+ background: var(--u-neutral-50, #FAFAFA);
132
+ border-right: 1px solid var(--u-border-color, #E0E0E0);
133
+ border-bottom: 1px solid var(--u-border-color-weak, #EEEEEE);
131
134
  cursor: pointer;
132
135
  user-select: none;
133
136
  vertical-align: middle;
134
137
  }
135
138
 
136
139
  .row-num.row-selected {
137
- background: var(--u-blue-100, #dbeafe);
138
- color: var(--u-blue-800, #1e40af);
140
+ background: var(--u-blue-100, #BBDEFB);
141
+ color: var(--u-blue-800, #1565C0);
139
142
  font-weight: 600;
140
143
  }
141
144
 
142
145
  /* Data cells */
143
146
  .cell {
144
147
  padding: 0 6px;
145
- border-right: 1px solid var(--u-border-color-weak, #f1f5f9);
146
- border-bottom: 1px solid var(--u-border-color-weak, #f1f5f9);
148
+ border-right: 1px solid var(--u-border-color-weak, #EEEEEE);
149
+ border-bottom: 1px solid var(--u-border-color-weak, #EEEEEE);
147
150
  font-size: 13px;
148
- color: var(--u-txt-color, #0f172a);
151
+ color: var(--u-txt-color, #212121);
149
152
  min-width: 80px;
150
153
  overflow: hidden;
151
154
  text-overflow: ellipsis;
@@ -159,13 +162,13 @@ var baseStyles = css`
159
162
  }
160
163
 
161
164
  .cell.selected {
162
- background: var(--u-blue-50, #eff6ff);
165
+ background: var(--u-primary-bg-color, #E3F2FD);
163
166
  }
164
167
 
165
168
  /* The anchor cell (top-left of selection) */
166
169
  .cell.anchor {
167
- background: var(--u-bg-color, #fff);
168
- outline: 2px solid var(--u-blue-500, #3b82f6);
170
+ background: var(--u-bg-color, #FFFFFF);
171
+ outline: 2px solid var(--u-blue-500, #2196F3);
169
172
  outline-offset: -2px;
170
173
  overflow: visible;
171
174
  z-index: 1;
@@ -185,16 +188,16 @@ var baseStyles = css`
185
188
  min-width: 100%;
186
189
  height: calc(100% + 2px);
187
190
  border: none;
188
- outline: 2px solid var(--u-blue-500, #3b82f6);
191
+ outline: 2px solid var(--u-blue-500, #2196F3);
189
192
  outline-offset: -1px;
190
193
  padding: 0 6px;
191
194
  font-size: 13px;
192
195
  font-family: inherit;
193
- color: var(--u-txt-color, #0f172a);
194
- background: var(--u-bg-color, #fff);
196
+ color: var(--u-txt-color, #212121);
197
+ background: var(--u-bg-color, #FFFFFF);
195
198
  box-sizing: border-box;
196
199
  z-index: 10;
197
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
200
+ box-shadow: 0 2px 8px var(--u-shadow-color-strong, rgba(0, 0, 0, 0.16));
198
201
  }
199
202
 
200
203
  /* Dropdown overlay */
@@ -206,18 +209,18 @@ var baseStyles = css`
206
209
  min-width: 120px;
207
210
  max-height: 200px;
208
211
  overflow-y: auto;
209
- background: var(--u-bg-color, #fff);
210
- border: 1px solid var(--u-border-color, #e2e8f0);
212
+ background: var(--u-bg-color, #FFFFFF);
213
+ border: 1px solid var(--u-border-color, #E0E0E0);
211
214
  border-top: none;
212
215
  border-radius: 0 0 4px 4px;
213
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
216
+ box-shadow: 0 4px 12px var(--u-shadow-color-normal, rgba(0, 0, 0, 0.12));
214
217
  z-index: 20;
215
218
  }
216
219
 
217
220
  .dropdown-item {
218
221
  padding: 4px 8px;
219
222
  font-size: 13px;
220
- color: var(--u-txt-color, #0f172a);
223
+ color: var(--u-txt-color, #212121);
221
224
  cursor: pointer;
222
225
  white-space: nowrap;
223
226
  overflow: hidden;
@@ -225,18 +228,18 @@ var baseStyles = css`
225
228
  }
226
229
 
227
230
  .dropdown-item:hover {
228
- background: var(--u-neutral-100, #f1f5f9);
231
+ background: var(--u-neutral-100, #F5F5F5);
229
232
  }
230
233
 
231
234
  .dropdown-item.highlighted {
232
- background: var(--u-blue-50, #eff6ff);
233
- color: var(--u-blue-800, #1e40af);
235
+ background: var(--u-primary-bg-color, #E3F2FD);
236
+ color: var(--u-blue-800, #1565C0);
234
237
  }
235
238
 
236
239
  .dropdown-empty {
237
240
  padding: 6px 8px;
238
241
  font-size: 12px;
239
- color: var(--u-txt-color-weak, #64748b);
242
+ color: var(--u-txt-color-weak, #757575);
240
243
  font-style: italic;
241
244
  }
242
245
 
@@ -247,33 +250,33 @@ var baseStyles = css`
247
250
 
248
251
  /* Readonly column/cell */
249
252
  .cell.cell-readonly {
250
- background: var(--u-neutral-50, #f8fafc);
251
- color: var(--u-txt-color-weak, #64748b);
253
+ background: var(--u-bg-color-disabled, #FAFAFA);
254
+ color: var(--u-txt-color-weak, #757575);
252
255
  cursor: default;
253
256
  }
254
257
 
255
258
  .cell.cell-readonly.selected {
256
- background: var(--u-neutral-100, #f1f5f9);
259
+ background: var(--u-neutral-100, #F5F5F5);
257
260
  }
258
261
 
259
262
  .cell.cell-readonly.anchor {
260
- background: var(--u-neutral-50, #f8fafc);
263
+ background: var(--u-bg-color-disabled, #FAFAFA);
261
264
  }
262
265
 
263
266
  /* Computed column/cell */
264
267
  .cell.cell-computed {
265
- background: var(--u-blue-50, #eff6ff);
266
- color: var(--u-txt-color, #0f172a);
268
+ background: var(--u-primary-bg-color, #E3F2FD);
269
+ color: var(--u-txt-color, #212121);
267
270
  font-style: italic;
268
271
  cursor: default;
269
272
  }
270
273
 
271
274
  .cell.cell-computed.selected {
272
- background: var(--u-blue-100, #dbeafe);
275
+ background: var(--u-blue-100, #BBDEFB);
273
276
  }
274
277
 
275
278
  .cell.cell-computed.anchor {
276
- background: var(--u-blue-50, #eff6ff);
279
+ background: var(--u-primary-bg-color, #E3F2FD);
277
280
  }
278
281
 
279
282
  /* Readonly mode (whole sheet) */
@@ -288,122 +291,23 @@ var DARK_PREFIXES = [
288
291
  ":host-context([data-theme=\"dark\"])"
289
292
  ];
290
293
  var darkRules = (host) => `
291
- ${host} .sheet-container {
292
- background: var(--u-bg-color, #121212);
293
- border-color: var(--u-border-color, #3D3D3D);
294
- }
295
-
296
- ${host} .sheet-container:focus-within {
297
- border-color: var(--u-blue-500, #6ba3e3);
298
- box-shadow: 0 0 0 2px var(--u-blue-100, #1e3a5f);
299
- }
300
-
301
- ${host} .corner {
302
- background: var(--u-neutral-100, #121212);
303
- border-color: var(--u-border-color, #3D3D3D);
304
- }
305
-
306
- ${host} .corner:hover {
307
- background: var(--u-neutral-200, #1E1E1E);
308
- }
309
-
310
- ${host} .col-header {
311
- background: var(--u-neutral-100, #121212);
312
- color: var(--u-txt-color-weak, #8A8A8A);
313
- border-color: var(--u-border-color, #3D3D3D);
314
- }
315
-
316
294
  ${host} .col-header.col-selected {
317
- background: var(--u-blue-100, #1e3a5f);
318
- color: var(--u-blue-800, #c2deff);
319
- }
320
-
321
- ${host} .row-num {
322
- background: var(--u-neutral-50, #0A0A0A);
323
- color: var(--u-txt-color-weak, #8A8A8A);
324
- border-right-color: var(--u-border-color, #3D3D3D);
325
- border-bottom-color: var(--u-border-color-weak, #2A2A2A);
326
- }
327
-
328
- ${host} .row-num.row-selected {
329
- background: var(--u-blue-100, #1e3a5f);
330
- color: var(--u-blue-800, #c2deff);
331
- }
332
-
333
- ${host} .cell {
334
- color: var(--u-txt-color, #D4D4D4);
335
- border-right-color: var(--u-border-color-weak, #2A2A2A);
336
- border-bottom-color: var(--u-border-color-weak, #2A2A2A);
337
- }
338
-
339
- ${host} .cell.cell-readonly {
340
- background: var(--u-neutral-100, #1E1E1E);
341
- color: var(--u-txt-color-weak, #8A8A8A);
295
+ background: var(--u-blue-100, #BBDEFB);
342
296
  }
343
297
 
344
298
  ${host} .cell.cell-readonly.selected {
345
- background: var(--u-neutral-200, #2A2A2A);
346
- }
347
-
348
- ${host} .cell.cell-readonly.anchor {
349
- background: var(--u-neutral-100, #1E1E1E);
299
+ background: var(--u-neutral-200, #EEEEEE);
350
300
  }
351
301
 
352
- ${host} .cell.cell-computed {
353
- background: var(--u-blue-100, #1e3a5f);
354
- color: var(--u-txt-color, #D4D4D4);
355
- }
356
302
 
357
303
  ${host} .cell.cell-computed.selected {
358
- background: var(--u-blue-200, #2a4a6f);
304
+ background: var(--u-blue-200, #90CAF9);
359
305
  }
360
306
 
361
307
  ${host} .cell.cell-computed.anchor {
362
- background: var(--u-blue-100, #1e3a5f);
363
- }
364
-
365
- ${host} .cell.selected {
366
- background: var(--u-blue-0, #0a1e3d);
367
- }
368
-
369
- ${host} .cell.anchor {
370
- background: var(--u-bg-color, #121212);
371
- outline-color: var(--u-blue-500, #6ba3e3);
372
- }
373
-
374
- ${host} .resize-handle:hover::after {
375
- background: var(--u-blue-500, #6ba3e3);
376
- }
377
-
378
- ${host} .cell-input {
379
- background: var(--u-bg-color, #121212);
380
- color: var(--u-txt-color, #D4D4D4);
381
- outline-color: var(--u-blue-500, #6ba3e3);
382
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
308
+ background: var(--u-blue-100, #BBDEFB);
383
309
  }
384
310
 
385
- ${host} .cell-dropdown {
386
- background: var(--u-bg-color, #121212);
387
- border-color: var(--u-border-color, #3D3D3D);
388
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
389
- }
390
-
391
- ${host} .dropdown-item {
392
- color: var(--u-txt-color, #D4D4D4);
393
- }
394
-
395
- ${host} .dropdown-item:hover {
396
- background: var(--u-neutral-100, #1E1E1E);
397
- }
398
-
399
- ${host} .dropdown-item.highlighted {
400
- background: var(--u-blue-100, #1e3a5f);
401
- color: var(--u-blue-800, #c2deff);
402
- }
403
-
404
- ${host} .dropdown-empty {
405
- color: var(--u-txt-color-weak, #8A8A8A);
406
- }
407
311
  `;
408
312
  var styles = [baseStyles, unsafeCSS(DARK_PREFIXES.map(darkRules).join("\n"))];
409
313
  //#endregion
@@ -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;
@@ -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
  ` : ""}
@@ -264,7 +291,7 @@ var URichTable = class URichTable extends LitElement {
264
291
  _renderNewRow() {
265
292
  return html`
266
293
  <tr class="new-row">
267
- ${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>` : ""}
268
295
  ${this.expandable ? html`<td></td>` : ""}
269
296
  ${this.columns.map((col, colIdx) => html`
270
297
  <td>
@@ -287,7 +314,7 @@ var URichTable = class URichTable extends LitElement {
287
314
  const end = Math.min(this.currentPage * this.pageSize, this.totalCount);
288
315
  return html`
289
316
  <div class="pagination">
290
- <span>전체 ${this.totalCount.toLocaleString()}건 ${start}-${end} 표시</span>
317
+ <span>${this.pageInfoFormatter(this.totalCount, start, end)}</span>
291
318
  <div class="page-buttons">
292
319
  <button ?disabled=${this.currentPage <= 1} @click=${() => this._onPageChange(this.currentPage - 1)}>◀</button>
293
320
  ${this._getPageNumbers(totalPages).map((p) => html`
@@ -306,7 +333,7 @@ var URichTable = class URichTable extends LitElement {
306
333
  `;
307
334
  }
308
335
  _onSelectAll(e) {
309
- 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)));
310
337
  else this.selectedIds = /* @__PURE__ */ new Set();
311
338
  this._fireSelectionChange();
312
339
  }
@@ -322,7 +349,7 @@ var URichTable = class URichTable extends LitElement {
322
349
  const start = Math.min(this._lastSelectedIndex, rowIdx);
323
350
  const end = Math.max(this._lastSelectedIndex, rowIdx);
324
351
  const next = new Set(this.selectedIds);
325
- 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));
326
353
  this.selectedIds = next;
327
354
  this._fireSelectionChange();
328
355
  }
@@ -445,7 +472,7 @@ var URichTable = class URichTable extends LitElement {
445
472
  this.expandedIds = next;
446
473
  this.dispatchEvent(new CustomEvent("row-expand", {
447
474
  detail: {
448
- row: this.data.find((r) => r._id === rowId),
475
+ row: this.data.find((r, i) => this._rowId(r, i) === rowId),
449
476
  expanded
450
477
  },
451
478
  bubbles: true,
@@ -588,7 +615,7 @@ var URichTable = class URichTable extends LitElement {
588
615
  };
589
616
  }
590
617
  _selectAll() {
591
- this.selectedIds = new Set(this.data.map((r) => r._id));
618
+ this.selectedIds = new Set(this.data.map((r, i) => this._rowId(r, i)));
592
619
  this._fireSelectionChange();
593
620
  }
594
621
  static define(tagName = "u-rich-table") {
@@ -602,6 +629,11 @@ __decorate([property({ type: Number }), __decorateMetadata("design:type", Object
602
629
  __decorate([property({ type: Number }), __decorateMetadata("design:type", Object)], URichTable.prototype, "currentPage", void 0);
603
630
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "loading", void 0);
604
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);
605
637
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "selectable", void 0);
606
638
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "editable", void 0);
607
639
  __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Object)], URichTable.prototype, "addable", void 0);