@iyulab/flex-table 0.21.1 → 0.23.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.
- package/README.md +70 -17
- package/dist/{flex-table-DNUTTZ1m.js → flex-table-DReUAeUh.js} +219 -153
- package/dist/flex-table.d.ts +25 -1
- package/dist/flex-table.js +1 -1
- package/dist/react.js +1 -1
- package/dist/styles/density-tokens.test.d.ts +1 -0
- package/dist/styles/token-derivation.test.d.ts +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ npm install @iyulab/flex-table
|
|
|
67
67
|
|----------|-----------|------|---------|-------------|
|
|
68
68
|
| `columns` | — | `ColumnDefinition[]` | `[]` | Column definitions |
|
|
69
69
|
| `data` | — | `DataRow[]` | `[]` | Data rows (`Record<string, unknown>[]`) |
|
|
70
|
-
| `rowHeight` | `row-height` | `number` | `32` | Row height in pixels |
|
|
70
|
+
| `rowHeight` | `row-height` | `number` | `32` | Row height in pixels. Falls back to the `--ft-row-height` token when not set — see [Density](#density-and-header-hierarchy) |
|
|
71
71
|
| `showRowNumbers` | `show-row-numbers` | `boolean` | `false` | Show row number column |
|
|
72
72
|
| `theme` | `theme` | `'light' \| 'dark'` | auto | Force theme; auto-detects `prefers-color-scheme` |
|
|
73
73
|
| `editable` | `editable` | `boolean` | `true` | Global read-only mode when `false` |
|
|
@@ -269,30 +269,83 @@ All events use `CustomEvent` with `bubbles: true, composed: true`.
|
|
|
269
269
|
|
|
270
270
|
## CSS Custom Properties
|
|
271
271
|
|
|
272
|
-
All colors and styles are customizable via CSS custom properties
|
|
272
|
+
All colors and styles are customizable via CSS custom properties.
|
|
273
|
+
|
|
274
|
+
**Two levels, pick whichever fits.** Since 0.22.0 every `--ft-*` colour is derived from
|
|
275
|
+
the `@iyulab/components` design tokens, so if you already load that token sheet the table
|
|
276
|
+
follows your brand with no per-table configuration:
|
|
277
|
+
|
|
278
|
+
```css
|
|
279
|
+
:root { --u-primary-color: #7b1fa2; } /* the table's accent, selection and boolean
|
|
280
|
+
markers follow — so do the buttons and shell */
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Override an individual `--ft-*` when you want the table to differ from the rest of the app:
|
|
273
284
|
|
|
274
285
|
```css
|
|
275
286
|
flex-table {
|
|
276
287
|
--ft-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
277
288
|
--ft-font-size: 14px;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
--ft-
|
|
281
|
-
--ft-
|
|
282
|
-
--ft-
|
|
283
|
-
--ft-header-
|
|
284
|
-
--ft-
|
|
285
|
-
--ft-
|
|
286
|
-
--ft-row-
|
|
287
|
-
--ft-
|
|
288
|
-
--ft-
|
|
289
|
-
--ft-
|
|
290
|
-
--ft-sort-indicator-color: #5f6368
|
|
291
|
-
--ft-
|
|
292
|
-
|
|
289
|
+
|
|
290
|
+
/* Surfaces and text derived from */
|
|
291
|
+
--ft-bg: #fff; /* --u-bg-color */
|
|
292
|
+
--ft-text-color: #202124; /* --u-txt-color */
|
|
293
|
+
--ft-border-color: #e0e0e0; /* --u-border-color */
|
|
294
|
+
--ft-header-bg: #f8f9fa; /* --u-bg-color-raised */
|
|
295
|
+
--ft-header-hover-bg: #e8eaed; /* --u-bg-color-active */
|
|
296
|
+
--ft-header-text-color: #202124; /* --u-txt-color */
|
|
297
|
+
--ft-row-even-bg: #fff; /* --u-bg-color */
|
|
298
|
+
--ft-row-odd-bg: #fafafa; /* --u-bg-color-raised */
|
|
299
|
+
--ft-row-hover-bg: #f0f4ff; /* --u-bg-color-hover */
|
|
300
|
+
--ft-editor-bg: #fff; /* --u-input-bg-color */
|
|
301
|
+
--ft-sort-indicator-color: #5f6368;/* --u-txt-color-weak */
|
|
302
|
+
--ft-empty-color: #5f6368; /* --u-txt-color-weak */
|
|
303
|
+
|
|
304
|
+
/* Accent */
|
|
305
|
+
--ft-active-color: #1a73e8; /* --u-primary-color */
|
|
306
|
+
--ft-selection-bg: #e8f0fe; /* --u-primary-bg-color */
|
|
307
|
+
--ft-bool-color: #2196f3; /* --u-primary-color */
|
|
308
|
+
|
|
309
|
+
/* State overlays — these are translucent on purpose, so the row striping and
|
|
310
|
+
selection underneath stay visible. Set the *-color and the background follows. */
|
|
311
|
+
--ft-invalid-color: #d93025; /* --u-danger-color */
|
|
312
|
+
--ft-drop-color: #1a73e8; /* --u-primary-color */
|
|
313
|
+
--ft-find-color: #f9a825; /* --u-warning-color */
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Density and header hierarchy
|
|
318
|
+
|
|
319
|
+
Since 0.23.0 the vertical rhythm and the header's typographic weight are adjustable, so a
|
|
320
|
+
table can be matched to the other tables on the page:
|
|
321
|
+
|
|
322
|
+
```css
|
|
323
|
+
flex-table {
|
|
324
|
+
--ft-row-height: 32px; /* body row height */
|
|
325
|
+
--ft-cell-padding-block: 6px; /* text placement inside that height */
|
|
326
|
+
--ft-cell-padding-inline: 12px;
|
|
327
|
+
--ft-header-font-size: 14px; /* defaults to --ft-font-size */
|
|
328
|
+
--ft-header-font-weight: 600;
|
|
293
329
|
}
|
|
294
330
|
```
|
|
295
331
|
|
|
332
|
+
⚠ **`--ft-row-height` is read once, at first render.** The grid is virtualised — rows are
|
|
333
|
+
positioned at `index × rowHeight`, so the height cannot come from the cascade the way
|
|
334
|
+
padding does. Declare it in a stylesheet that is in effect before the table is attached; to
|
|
335
|
+
change it later, set the `rowHeight` property (or the `row-height` attribute) instead. An
|
|
336
|
+
explicitly set property or attribute always wins over the token, and a value in any unit
|
|
337
|
+
other than `px` is ignored.
|
|
338
|
+
|
|
339
|
+
⚠ **Row height is fixed, and padding places the text within it.** Cells are single-line
|
|
340
|
+
(`nowrap` + ellipsis) by design. If you reduce `--ft-row-height`, reduce
|
|
341
|
+
`--ft-cell-padding-block` to match — keep `padding-block × 2 + line box ≤ row-height`, or
|
|
342
|
+
the text is clipped at the bottom.
|
|
343
|
+
|
|
344
|
+
**Without the token sheet nothing changes.** Every reference carries the literal above as
|
|
345
|
+
its fallback, and the built-in dark theme (`prefers-color-scheme` or `theme="dark"`) still
|
|
346
|
+
applies — the table remains usable standalone. Referencing the tokens does not add a
|
|
347
|
+
package dependency; CSS custom properties are resolved at render time, not imported.
|
|
348
|
+
|
|
296
349
|
## Keyboard Shortcuts
|
|
297
350
|
|
|
298
351
|
| Key | Action |
|
|
@@ -6,64 +6,98 @@ var s = t`
|
|
|
6
6
|
:host {
|
|
7
7
|
--ft-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
8
8
|
--ft-font-size: 14px;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
--ft-
|
|
19
|
-
--ft-
|
|
20
|
-
--ft-
|
|
21
|
-
--ft-
|
|
22
|
-
--ft-
|
|
9
|
+
|
|
10
|
+
/* --- 치수·위계 축 (기본값 = 종전 렌더값) ---
|
|
11
|
+
이 표는 가상 스크롤이라 행 높이가 **JS 로 계산된다**(index * rowHeight).
|
|
12
|
+
그래서 --ft-row-height 는 CSS 만으로는 닿지 않고, 컴포넌트가 첫 렌더에서
|
|
13
|
+
판독해 가상화 계산에 넣는다. 그 결과가 rowHeight 프로퍼티의 기본값이다.
|
|
14
|
+
|
|
15
|
+
⚠여백과 행 높이의 관계: 행 높이는 **고정**이고 여백은 그 안에서 글자를
|
|
16
|
+
배치한다. 행 높이를 줄이면서 여백을 그대로 두면 아래가 잘린다
|
|
17
|
+
(padding-block * 2 + 한 줄 높이 <= row-height 를 유지할 것). */
|
|
18
|
+
--ft-row-height: 32px;
|
|
19
|
+
--ft-cell-padding-block: 6px;
|
|
20
|
+
--ft-cell-padding-inline: 12px;
|
|
21
|
+
--ft-header-font-size: var(--ft-font-size);
|
|
22
|
+
--ft-header-font-weight: 600;
|
|
23
|
+
--ft-border-color: var(--u-border-color, #e0e0e0);
|
|
24
|
+
--ft-bg: var(--u-bg-color, #fff);
|
|
25
|
+
--ft-text-color: var(--u-txt-color, #202124);
|
|
26
|
+
--ft-header-bg: var(--u-bg-color-raised, #f8f9fa);
|
|
27
|
+
--ft-header-hover-bg: var(--u-bg-color-active, #e8eaed);
|
|
28
|
+
--ft-header-text-color: var(--u-txt-color, #202124);
|
|
29
|
+
--ft-row-even-bg: var(--u-bg-color, #fff);
|
|
30
|
+
--ft-row-odd-bg: var(--u-bg-color-raised, #fafafa);
|
|
31
|
+
--ft-row-hover-bg: var(--u-bg-color-hover, #f0f4ff);
|
|
32
|
+
--ft-active-color: var(--u-primary-color, #1a73e8);
|
|
33
|
+
--ft-selection-bg: var(--u-primary-bg-color, #e8f0fe);
|
|
34
|
+
--ft-bool-color: var(--u-primary-color, #2196f3);
|
|
35
|
+
--ft-sort-indicator-color: var(--u-txt-color-weak, #5f6368);
|
|
36
|
+
--ft-editor-bg: var(--u-input-bg-color, #fff);
|
|
23
37
|
/* #999 was 2.85 against --ft-bg (#fff); AA needs 4.5. The dark value (#9aa0a6 on
|
|
24
38
|
#1e1e2e = 6.31) was already fine, so this was a light-only defect. #5f6368 is the
|
|
25
39
|
same grey the sort indicator uses, which keeps the palette to one family. */
|
|
26
|
-
--ft-empty-color: #5f6368;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
--ft-empty-color: var(--u-txt-color-weak, #5f6368);
|
|
41
|
+
|
|
42
|
+
/* 상태 색 — 종전에는 규칙 안에 리터럴로 박혀 있어 소비자가 덮을 경로가 없었고
|
|
43
|
+
테마도 따라오지 않았다(다크에서 라이트용 값 그대로).
|
|
44
|
+
|
|
45
|
+
★**셀 배경은 불투명 면이 아니라 반투명 겹침이다.** 줄무늬(홀/짝 행)와 선택 표시가
|
|
46
|
+
아래에 있고 그것이 비쳐야 "어느 행의 어떤 셀"인지 읽힌다. 그래서 면 토큰
|
|
47
|
+
(--u-*-bg-color)이 아니라 **상태 색에서 알파를 뽑는다** — 부수 효과로 두 테마
|
|
48
|
+
모두에서 성립하므로 아래 다크 블록에 이 셋을 다시 적을 필요가 없다. */
|
|
49
|
+
--ft-invalid-color: var(--u-danger-color, #d93025);
|
|
50
|
+
--ft-invalid-bg: color-mix(in srgb, var(--ft-invalid-color) 8%, transparent);
|
|
51
|
+
--ft-drop-color: var(--u-primary-color, #1a73e8);
|
|
52
|
+
--ft-drop-bg: color-mix(in srgb, var(--ft-drop-color) 15%, transparent);
|
|
53
|
+
--ft-find-color: var(--u-warning-color, #f9a825);
|
|
54
|
+
--ft-find-bg: color-mix(in srgb, var(--ft-find-color) 25%, transparent);
|
|
55
|
+
--ft-find-current-bg: color-mix(in srgb, var(--ft-find-color) 50%, transparent);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* --- Dark Theme (auto via prefers-color-scheme) ---
|
|
59
|
+
★**토큰 시트가 로드된 환경에서 아래 두 블록은 아무 일도 하지 않는다** — --u-* 가
|
|
60
|
+
이미 테마별로 다른 값을 가리키므로 base 규칙만으로 두 테마가 성립한다.
|
|
61
|
+
남겨 두는 이유는 **시트가 없는 소비자**다: 그때는 각 var() 의 리터럴 폴백이
|
|
62
|
+
발동하고, 아래 블록이 그 폴백을 다크 값으로 바꿔 준다.
|
|
63
|
+
⇒ 즉 이 표는 시트 없이도 종전처럼 자체 테마를 갖는다(그것이 이 패키지의 계약이었다). */
|
|
30
64
|
@media (prefers-color-scheme: dark) {
|
|
31
65
|
:host(:not([theme="light"])) {
|
|
32
|
-
--ft-border-color: #3c4043;
|
|
33
|
-
--ft-bg: #1e1e1e;
|
|
34
|
-
--ft-text-color: #e8eaed;
|
|
35
|
-
--ft-header-bg: #292a2d;
|
|
36
|
-
--ft-header-hover-bg: #3c4043;
|
|
37
|
-
--ft-header-text-color: #e8eaed;
|
|
38
|
-
--ft-row-even-bg: #1e1e1e;
|
|
39
|
-
--ft-row-odd-bg: #252526;
|
|
40
|
-
--ft-row-hover-bg: #2a2d2e;
|
|
41
|
-
--ft-active-color: #4da3ff;
|
|
42
|
-
--ft-selection-bg: #264f78;
|
|
43
|
-
--ft-bool-color: #64b5f6;
|
|
44
|
-
--ft-sort-indicator-color: #9aa0a6;
|
|
45
|
-
--ft-editor-bg: #2d2d2d;
|
|
46
|
-
--ft-empty-color: #9aa0a6;
|
|
66
|
+
--ft-border-color: var(--u-border-color, #3c4043);
|
|
67
|
+
--ft-bg: var(--u-bg-color, #1e1e1e);
|
|
68
|
+
--ft-text-color: var(--u-txt-color, #e8eaed);
|
|
69
|
+
--ft-header-bg: var(--u-bg-color-raised, #292a2d);
|
|
70
|
+
--ft-header-hover-bg: var(--u-bg-color-active, #3c4043);
|
|
71
|
+
--ft-header-text-color: var(--u-txt-color, #e8eaed);
|
|
72
|
+
--ft-row-even-bg: var(--u-bg-color, #1e1e1e);
|
|
73
|
+
--ft-row-odd-bg: var(--u-bg-color-raised, #252526);
|
|
74
|
+
--ft-row-hover-bg: var(--u-bg-color-hover, #2a2d2e);
|
|
75
|
+
--ft-active-color: var(--u-primary-color, #4da3ff);
|
|
76
|
+
--ft-selection-bg: var(--u-primary-bg-color, #264f78);
|
|
77
|
+
--ft-bool-color: var(--u-primary-color, #64b5f6);
|
|
78
|
+
--ft-sort-indicator-color: var(--u-txt-color-weak, #9aa0a6);
|
|
79
|
+
--ft-editor-bg: var(--u-input-bg-color, #2d2d2d);
|
|
80
|
+
--ft-empty-color: var(--u-txt-color-weak, #9aa0a6);
|
|
47
81
|
}
|
|
48
82
|
}
|
|
49
83
|
|
|
50
84
|
/* --- Force dark via attribute --- */
|
|
51
85
|
:host([theme="dark"]) {
|
|
52
|
-
--ft-border-color: #3c4043;
|
|
53
|
-
--ft-bg: #1e1e1e;
|
|
54
|
-
--ft-text-color: #e8eaed;
|
|
55
|
-
--ft-header-bg: #292a2d;
|
|
56
|
-
--ft-header-hover-bg: #3c4043;
|
|
57
|
-
--ft-header-text-color: #e8eaed;
|
|
58
|
-
--ft-row-even-bg: #1e1e1e;
|
|
59
|
-
--ft-row-odd-bg: #252526;
|
|
60
|
-
--ft-row-hover-bg: #2a2d2e;
|
|
61
|
-
--ft-active-color: #4da3ff;
|
|
62
|
-
--ft-selection-bg: #264f78;
|
|
63
|
-
--ft-bool-color: #64b5f6;
|
|
64
|
-
--ft-sort-indicator-color: #9aa0a6;
|
|
65
|
-
--ft-editor-bg: #2d2d2d;
|
|
66
|
-
--ft-empty-color: #9aa0a6;
|
|
86
|
+
--ft-border-color: var(--u-border-color, #3c4043);
|
|
87
|
+
--ft-bg: var(--u-bg-color, #1e1e1e);
|
|
88
|
+
--ft-text-color: var(--u-txt-color, #e8eaed);
|
|
89
|
+
--ft-header-bg: var(--u-bg-color-raised, #292a2d);
|
|
90
|
+
--ft-header-hover-bg: var(--u-bg-color-active, #3c4043);
|
|
91
|
+
--ft-header-text-color: var(--u-txt-color, #e8eaed);
|
|
92
|
+
--ft-row-even-bg: var(--u-bg-color, #1e1e1e);
|
|
93
|
+
--ft-row-odd-bg: var(--u-bg-color-raised, #252526);
|
|
94
|
+
--ft-row-hover-bg: var(--u-bg-color-hover, #2a2d2e);
|
|
95
|
+
--ft-active-color: var(--u-primary-color, #4da3ff);
|
|
96
|
+
--ft-selection-bg: var(--u-primary-bg-color, #264f78);
|
|
97
|
+
--ft-bool-color: var(--u-primary-color, #64b5f6);
|
|
98
|
+
--ft-sort-indicator-color: var(--u-txt-color-weak, #9aa0a6);
|
|
99
|
+
--ft-editor-bg: var(--u-input-bg-color, #2d2d2d);
|
|
100
|
+
--ft-empty-color: var(--u-txt-color-weak, #9aa0a6);
|
|
67
101
|
}
|
|
68
102
|
|
|
69
103
|
/* --- Layout --- */
|
|
@@ -89,10 +123,12 @@ var s = t`
|
|
|
89
123
|
.ft-header-cell {
|
|
90
124
|
position: absolute;
|
|
91
125
|
top: 0;
|
|
92
|
-
|
|
126
|
+
/* 머리행은 종전에도 본문보다 상하 2px 넉넉했다 — 그 관계를 유지한다. */
|
|
127
|
+
padding: calc(var(--ft-cell-padding-block) + 2px) var(--ft-cell-padding-inline);
|
|
93
128
|
background: var(--ft-header-bg);
|
|
94
129
|
color: var(--ft-header-text-color);
|
|
95
|
-
font-
|
|
130
|
+
font-size: var(--ft-header-font-size);
|
|
131
|
+
font-weight: var(--ft-header-font-weight);
|
|
96
132
|
user-select: none;
|
|
97
133
|
border-bottom: 2px solid var(--ft-border-color);
|
|
98
134
|
overflow: hidden;
|
|
@@ -194,7 +230,7 @@ var s = t`
|
|
|
194
230
|
.ft-cell {
|
|
195
231
|
position: absolute;
|
|
196
232
|
top: 0;
|
|
197
|
-
padding:
|
|
233
|
+
padding: var(--ft-cell-padding-block) var(--ft-cell-padding-inline);
|
|
198
234
|
border-bottom: 1px solid var(--ft-border-color);
|
|
199
235
|
overflow: hidden;
|
|
200
236
|
text-overflow: ellipsis;
|
|
@@ -218,9 +254,9 @@ var s = t`
|
|
|
218
254
|
}
|
|
219
255
|
|
|
220
256
|
.ft-cell.ft-invalid {
|
|
221
|
-
outline: 2px solid
|
|
257
|
+
outline: 2px solid var(--ft-invalid-color);
|
|
222
258
|
outline-offset: -2px;
|
|
223
|
-
background:
|
|
259
|
+
background: var(--ft-invalid-bg);
|
|
224
260
|
}
|
|
225
261
|
|
|
226
262
|
.ft-cell.ft-editing { padding: 0; overflow: visible; }
|
|
@@ -228,7 +264,8 @@ var s = t`
|
|
|
228
264
|
.ft-editor {
|
|
229
265
|
width: 100%; height: 100%;
|
|
230
266
|
border: none; outline: none;
|
|
231
|
-
|
|
267
|
+
/* 편집기는 셀 위에 겹쳐 뜬다 — 여백이 어긋나면 글자가 튄다. */
|
|
268
|
+
padding: var(--ft-cell-padding-block) var(--ft-cell-padding-inline);
|
|
232
269
|
font: inherit;
|
|
233
270
|
color: var(--ft-text-color);
|
|
234
271
|
background: var(--ft-editor-bg);
|
|
@@ -312,14 +349,14 @@ var s = t`
|
|
|
312
349
|
position: absolute;
|
|
313
350
|
inset: 0;
|
|
314
351
|
z-index: 100;
|
|
315
|
-
background:
|
|
316
|
-
border: 2px dashed
|
|
352
|
+
background: var(--ft-drop-bg);
|
|
353
|
+
border: 2px dashed var(--ft-drop-color);
|
|
317
354
|
display: flex;
|
|
318
355
|
align-items: center;
|
|
319
356
|
justify-content: center;
|
|
320
357
|
font-size: 14px;
|
|
321
358
|
font-weight: 500;
|
|
322
|
-
color:
|
|
359
|
+
color: var(--ft-drop-color);
|
|
323
360
|
pointer-events: none;
|
|
324
361
|
border-radius: 4px;
|
|
325
362
|
}
|
|
@@ -692,7 +729,7 @@ var s = t`
|
|
|
692
729
|
|
|
693
730
|
.ft-comment-popup-textarea:focus {
|
|
694
731
|
border-color: var(--ft-active-color, #3b82f6);
|
|
695
|
-
box-shadow: 0 0 0 2px
|
|
732
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ft-active-color) 20%, transparent);
|
|
696
733
|
}
|
|
697
734
|
|
|
698
735
|
.ft-comment-popup-buttons {
|
|
@@ -801,12 +838,12 @@ var s = t`
|
|
|
801
838
|
|
|
802
839
|
/* Find highlight */
|
|
803
840
|
.ft-cell.ft-find-match {
|
|
804
|
-
background:
|
|
841
|
+
background: var(--ft-find-bg) !important;
|
|
805
842
|
}
|
|
806
843
|
|
|
807
844
|
.ft-cell.ft-find-current {
|
|
808
|
-
background:
|
|
809
|
-
outline: 2px solid
|
|
845
|
+
background: var(--ft-find-current-bg) !important;
|
|
846
|
+
outline: 2px solid var(--ft-find-color);
|
|
810
847
|
outline-offset: -2px;
|
|
811
848
|
}
|
|
812
849
|
`;
|
|
@@ -1290,7 +1327,7 @@ function oe(e, t) {
|
|
|
1290
1327
|
<sheetData>${n.join("")}</sheetData>
|
|
1291
1328
|
</worksheet>`;
|
|
1292
1329
|
}
|
|
1293
|
-
function
|
|
1330
|
+
function se(e, t) {
|
|
1294
1331
|
let n = oe(e, t);
|
|
1295
1332
|
return ne([
|
|
1296
1333
|
{
|
|
@@ -1321,24 +1358,24 @@ function z(e, t) {
|
|
|
1321
1358
|
}
|
|
1322
1359
|
//#endregion
|
|
1323
1360
|
//#region src/export/export.ts
|
|
1324
|
-
function
|
|
1361
|
+
function z(e, t, n) {
|
|
1325
1362
|
switch (n) {
|
|
1326
|
-
case "csv": return
|
|
1327
|
-
case "tsv": return
|
|
1328
|
-
case "json": return
|
|
1329
|
-
case "xlsx": return
|
|
1363
|
+
case "csv": return V(e, t, ",");
|
|
1364
|
+
case "tsv": return V(e, t, " ");
|
|
1365
|
+
case "json": return U(e, t);
|
|
1366
|
+
case "xlsx": return se(e, t);
|
|
1330
1367
|
}
|
|
1331
1368
|
}
|
|
1332
|
-
function
|
|
1369
|
+
function B(e, t) {
|
|
1333
1370
|
return e == null ? "" : (t.type === "date" || t.type === "datetime") && e instanceof Date ? e.toISOString() : String(e);
|
|
1334
1371
|
}
|
|
1335
|
-
function
|
|
1336
|
-
return [t.map((e) =>
|
|
1372
|
+
function V(e, t, n) {
|
|
1373
|
+
return [t.map((e) => H(e.header, n)).join(n), ...e.map((e) => t.map((t) => H(B(e[t.key], t), n)).join(n))].join("\n");
|
|
1337
1374
|
}
|
|
1338
|
-
function
|
|
1375
|
+
function H(e, t) {
|
|
1339
1376
|
return e.includes(t) || e.includes("\"") || e.includes("\n") || e.includes("\r") ? "\"" + e.replace(/"/g, "\"\"") + "\"" : e;
|
|
1340
1377
|
}
|
|
1341
|
-
function
|
|
1378
|
+
function U(e, t) {
|
|
1342
1379
|
let n = e.map((e) => {
|
|
1343
1380
|
let n = {};
|
|
1344
1381
|
for (let r of t) {
|
|
@@ -1349,35 +1386,35 @@ function W(e, t) {
|
|
|
1349
1386
|
});
|
|
1350
1387
|
return JSON.stringify(n, null, 2);
|
|
1351
1388
|
}
|
|
1352
|
-
function
|
|
1389
|
+
function W(e, t, n) {
|
|
1353
1390
|
let r = new Blob([e], { type: n }), i = URL.createObjectURL(r), a = document.createElement("a");
|
|
1354
1391
|
a.href = i, a.download = t, a.style.display = "none", document.body.appendChild(a), a.click(), document.body.removeChild(a), setTimeout(() => URL.revokeObjectURL(i), 100);
|
|
1355
1392
|
}
|
|
1356
|
-
var
|
|
1393
|
+
var G = {
|
|
1357
1394
|
csv: "text/csv;charset=utf-8",
|
|
1358
1395
|
tsv: "text/tab-separated-values;charset=utf-8",
|
|
1359
1396
|
json: "application/json;charset=utf-8",
|
|
1360
1397
|
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
1361
|
-
},
|
|
1398
|
+
}, ce = {
|
|
1362
1399
|
csv: ".csv",
|
|
1363
1400
|
tsv: ".tsv",
|
|
1364
1401
|
json: ".json",
|
|
1365
1402
|
xlsx: ".xlsx"
|
|
1366
1403
|
};
|
|
1367
|
-
function ce(e) {
|
|
1368
|
-
return K[e];
|
|
1369
|
-
}
|
|
1370
1404
|
function le(e) {
|
|
1371
|
-
return
|
|
1405
|
+
return G[e];
|
|
1406
|
+
}
|
|
1407
|
+
function ue(e) {
|
|
1408
|
+
return ce[e];
|
|
1372
1409
|
}
|
|
1373
1410
|
//#endregion
|
|
1374
1411
|
//#region src/export/xlsx-reader.ts
|
|
1375
|
-
function
|
|
1412
|
+
function de(e) {
|
|
1376
1413
|
for (let t = e.byteLength - 22; t >= 0; t--) if (e.getUint32(t, !0) === 101010256) return t;
|
|
1377
1414
|
throw Error("XLSX: End of central directory not found — not a valid ZIP file");
|
|
1378
1415
|
}
|
|
1379
|
-
function
|
|
1380
|
-
let t =
|
|
1416
|
+
function fe(e) {
|
|
1417
|
+
let t = de(e), n = e.getUint32(t + 16, !0), r = e.getUint32(t + 12, !0), i = new TextDecoder("utf-8"), a = [], o = n, s = n + r;
|
|
1381
1418
|
for (; o < s && e.getUint32(o, !0) === 33639248;) {
|
|
1382
1419
|
let t = e.getUint16(o + 10, !0), n = e.getUint32(o + 20, !0), r = e.getUint32(o + 24, !0), s = e.getUint16(o + 28, !0), c = e.getUint16(o + 30, !0), l = e.getUint16(o + 32, !0), u = e.getUint32(o + 42, !0), d = i.decode(new Uint8Array(e.buffer, o + 46, s));
|
|
1383
1420
|
a.push({
|
|
@@ -1390,7 +1427,7 @@ function de(e) {
|
|
|
1390
1427
|
}
|
|
1391
1428
|
return a;
|
|
1392
1429
|
}
|
|
1393
|
-
async function
|
|
1430
|
+
async function pe(e) {
|
|
1394
1431
|
let t = new DecompressionStream("deflate-raw"), n = t.writable.getWriter(), r = t.readable.getReader();
|
|
1395
1432
|
n.write(e), n.close();
|
|
1396
1433
|
let i = [];
|
|
@@ -1403,40 +1440,40 @@ async function fe(e) {
|
|
|
1403
1440
|
for (let e of i) o.set(e, s), s += e.length;
|
|
1404
1441
|
return o;
|
|
1405
1442
|
}
|
|
1406
|
-
async function
|
|
1443
|
+
async function me(e, t) {
|
|
1407
1444
|
let n = t.localHeaderOffset, r = e.getUint16(n + 26, !0), i = e.getUint16(n + 28, !0), a = n + 30 + r + i, o = new Uint8Array(e.buffer, a, t.compressedSize);
|
|
1408
1445
|
if (t.compressionMethod === 0) return o.slice();
|
|
1409
|
-
if (t.compressionMethod === 8) return
|
|
1446
|
+
if (t.compressionMethod === 8) return pe(o);
|
|
1410
1447
|
throw Error(`XLSX: Unsupported compression method ${t.compressionMethod}`);
|
|
1411
1448
|
}
|
|
1412
|
-
async function
|
|
1413
|
-
let t = new DataView(e), n =
|
|
1414
|
-
for (let e of n) e.compressedSize === 0 && e.name.endsWith("/") || r.set(e.name, await
|
|
1449
|
+
async function he(e) {
|
|
1450
|
+
let t = new DataView(e), n = fe(t), r = /* @__PURE__ */ new Map();
|
|
1451
|
+
for (let e of n) e.compressedSize === 0 && e.name.endsWith("/") || r.set(e.name, await me(t, e));
|
|
1415
1452
|
return r;
|
|
1416
1453
|
}
|
|
1417
|
-
var
|
|
1418
|
-
function
|
|
1419
|
-
let t =
|
|
1454
|
+
var ge = new TextDecoder("utf-8");
|
|
1455
|
+
function K(e) {
|
|
1456
|
+
let t = ge.decode(e);
|
|
1420
1457
|
return new DOMParser().parseFromString(t, "application/xml");
|
|
1421
1458
|
}
|
|
1422
|
-
function
|
|
1459
|
+
function _e(e) {
|
|
1423
1460
|
if (!e) return [];
|
|
1424
|
-
let t =
|
|
1461
|
+
let t = K(e);
|
|
1425
1462
|
return Array.from(t.querySelectorAll("si")).map((e) => Array.from(e.querySelectorAll("t")).map((e) => e.textContent ?? "").join(""));
|
|
1426
1463
|
}
|
|
1427
|
-
function
|
|
1464
|
+
function ve(e) {
|
|
1428
1465
|
let t = (e - 1) * 864e5 + Date.UTC(1899, 11, 30);
|
|
1429
1466
|
return new Date(t).toISOString().slice(0, 10);
|
|
1430
1467
|
}
|
|
1431
|
-
function
|
|
1432
|
-
let n =
|
|
1468
|
+
function ye(e, t) {
|
|
1469
|
+
let n = K(e).querySelectorAll("row");
|
|
1433
1470
|
if (n.length === 0) return [];
|
|
1434
1471
|
let r = 0, i = /* @__PURE__ */ new Map();
|
|
1435
1472
|
for (let e of n) {
|
|
1436
1473
|
let n = parseInt(e.getAttribute("r") ?? "1", 10) - 1, a = /* @__PURE__ */ new Map();
|
|
1437
1474
|
i.set(n, a);
|
|
1438
1475
|
for (let n of e.querySelectorAll("c")) {
|
|
1439
|
-
let e =
|
|
1476
|
+
let e = be(n.getAttribute("r") ?? "");
|
|
1440
1477
|
e > r && (r = e);
|
|
1441
1478
|
let i = n.getAttribute("t") ?? "", o = n.getAttribute("s") ?? "", s = n.querySelector("v")?.textContent ?? "", c = n.querySelector("is > t")?.textContent ?? null, l;
|
|
1442
1479
|
if (i === "s") l = t[parseInt(s, 10)] ?? "";
|
|
@@ -1445,7 +1482,7 @@ function ve(e, t) {
|
|
|
1445
1482
|
else if (s === "") l = "";
|
|
1446
1483
|
else {
|
|
1447
1484
|
let e = parseFloat(s);
|
|
1448
|
-
l = !isNaN(e) && o !== "" && parseInt(o, 10) > 0 && e >= 1 && e <= 73050 ?
|
|
1485
|
+
l = !isNaN(e) && o !== "" && parseInt(o, 10) > 0 && e >= 1 && e <= 73050 ? ve(e) : s;
|
|
1449
1486
|
}
|
|
1450
1487
|
a.set(e, l);
|
|
1451
1488
|
}
|
|
@@ -1459,13 +1496,13 @@ function ve(e, t) {
|
|
|
1459
1496
|
}
|
|
1460
1497
|
return o;
|
|
1461
1498
|
}
|
|
1462
|
-
function
|
|
1499
|
+
function be(e) {
|
|
1463
1500
|
let t = 0, n = 0;
|
|
1464
1501
|
for (; n < e.length && e[n] >= "A" && e[n] <= "Z";) t = t * 26 + (e.charCodeAt(n) - 64), n++;
|
|
1465
1502
|
return t - 1;
|
|
1466
1503
|
}
|
|
1467
|
-
async function
|
|
1468
|
-
let t = await
|
|
1504
|
+
async function xe(e) {
|
|
1505
|
+
let t = await he(e), n = _e(t.get("xl/sharedStrings.xml")), r = t.get("xl/worksheets/sheet1.xml");
|
|
1469
1506
|
if (!r) {
|
|
1470
1507
|
for (let [e, n] of t) if (e.toLowerCase().includes("worksheets/sheet")) {
|
|
1471
1508
|
r = n;
|
|
@@ -1473,7 +1510,7 @@ async function be(e) {
|
|
|
1473
1510
|
}
|
|
1474
1511
|
}
|
|
1475
1512
|
if (!r) throw Error("XLSX: No worksheet found");
|
|
1476
|
-
let i =
|
|
1513
|
+
let i = ye(r, n);
|
|
1477
1514
|
return i.length === 0 ? {
|
|
1478
1515
|
headers: [],
|
|
1479
1516
|
rows: []
|
|
@@ -1483,8 +1520,8 @@ async function be(e) {
|
|
|
1483
1520
|
};
|
|
1484
1521
|
}
|
|
1485
1522
|
//#endregion
|
|
1486
|
-
//#region \0@oxc-project+runtime@0.
|
|
1487
|
-
function
|
|
1523
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorate.js
|
|
1524
|
+
function q(e, t, n, r) {
|
|
1488
1525
|
var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
|
|
1489
1526
|
if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
|
|
1490
1527
|
else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
|
|
@@ -1492,16 +1529,16 @@ function J(e, t, n, r) {
|
|
|
1492
1529
|
}
|
|
1493
1530
|
//#endregion
|
|
1494
1531
|
//#region src/flex-table.ts
|
|
1495
|
-
var
|
|
1532
|
+
var J = {
|
|
1496
1533
|
eq: "=",
|
|
1497
1534
|
neq: "≠",
|
|
1498
1535
|
gt: ">",
|
|
1499
1536
|
lt: "<",
|
|
1500
1537
|
gte: "≥",
|
|
1501
1538
|
lte: "≤"
|
|
1502
|
-
},
|
|
1539
|
+
}, Y = 120, X = 40, Z = 32, Q = 5, $ = class extends e {
|
|
1503
1540
|
constructor(...e) {
|
|
1504
|
-
super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this.
|
|
1541
|
+
super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = Z, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new y(), this._editing = new b(), this._rowSelection = new x(), this._undo = new D(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = null, this._commentPopup = null, this._viewDirty = !0, this._isDragging = !1, this._wasDrag = !1, this._resizing = null, this._resizeCleanup = null, this._colDrag = null, this._colDragIndicatorLeft = null, this._fillDrag = null, this._rowDrag = null, this._rowDragIndicatorY = null, this._findState = null, this._columnWidths = /* @__PURE__ */ new Map(), this._hostResizeObserver = null, this._comments = /* @__PURE__ */ new Map(), this._isDragOver = !1, this._onCommentPopupOutsideClick = () => {
|
|
1505
1542
|
this._commitCommentPopup();
|
|
1506
1543
|
}, this._invalidCells = /* @__PURE__ */ new Map(), this._textFilterState = /* @__PURE__ */ new Map(), this._numberFilterState = /* @__PURE__ */ new Map(), this._dateFilterState = /* @__PURE__ */ new Map(), this._emptyFilterState = /* @__PURE__ */ new Map();
|
|
1507
1544
|
}
|
|
@@ -1515,6 +1552,13 @@ var Y = {
|
|
|
1515
1552
|
let t = this._data, n = e !== t;
|
|
1516
1553
|
this._data = e, n && !this._inUndoRedo && (this.clearUndoOnDataChange && this._undo.clear(), this.clearSelectionOnDataChange && this._rowSelection.selectedCount > 0 && (this._rowSelection.deselectAll(), this._rowSelectionVersion++, this._dispatchRowSelectionEvent())), this.requestUpdate("data", t);
|
|
1517
1554
|
}
|
|
1555
|
+
get rowHeight() {
|
|
1556
|
+
return this._rowHeightExplicit ?? this._rowHeightFromCss ?? Z;
|
|
1557
|
+
}
|
|
1558
|
+
set rowHeight(e) {
|
|
1559
|
+
let t = this.rowHeight;
|
|
1560
|
+
this._rowHeightExplicit = e, this.requestUpdate("rowHeight", t);
|
|
1561
|
+
}
|
|
1518
1562
|
set selectionMode(e) {
|
|
1519
1563
|
this._rowSelection.mode = e, this.requestUpdate();
|
|
1520
1564
|
}
|
|
@@ -1850,12 +1894,12 @@ var Y = {
|
|
|
1850
1894
|
if (!t) return "";
|
|
1851
1895
|
let n = this.visibleColumns.slice(t.startCol, t.endCol + 1), r = [];
|
|
1852
1896
|
for (let e = t.startRow; e <= t.endRow; e++) r.push(this.data[this._toDataIndex(e)]);
|
|
1853
|
-
return
|
|
1897
|
+
return z(r, n, e);
|
|
1854
1898
|
}
|
|
1855
|
-
return
|
|
1899
|
+
return z(this._sortedIndices.map((e) => this.data[e]), this.visibleColumns, e);
|
|
1856
1900
|
}
|
|
1857
1901
|
exportToFile(e, t) {
|
|
1858
|
-
|
|
1902
|
+
W(this.exportToString(e), t ?? `export${ue(e)}`, le(e));
|
|
1859
1903
|
}
|
|
1860
1904
|
setComment(e, t, n) {
|
|
1861
1905
|
let r = this.getComment(e, t);
|
|
@@ -1912,7 +1956,7 @@ var Y = {
|
|
|
1912
1956
|
async importFromFile(e) {
|
|
1913
1957
|
let t = e.name.toLowerCase();
|
|
1914
1958
|
if (t.endsWith(".xlsx")) {
|
|
1915
|
-
let t = await
|
|
1959
|
+
let t = await xe(await e.arrayBuffer());
|
|
1916
1960
|
this._applyImportedSheet(t);
|
|
1917
1961
|
} else if (t.endsWith(".csv") || t.endsWith(".tsv")) {
|
|
1918
1962
|
let t = O(await e.text());
|
|
@@ -2004,8 +2048,8 @@ var Y = {
|
|
|
2004
2048
|
return this._columnWidths.get(e);
|
|
2005
2049
|
}
|
|
2006
2050
|
_getColWidth(e) {
|
|
2007
|
-
let t = this._columnWidths.get(e.key) ?? e.width ??
|
|
2008
|
-
return Math.max(t, e.minWidth ??
|
|
2051
|
+
let t = this._columnWidths.get(e.key) ?? e.width ?? Y;
|
|
2052
|
+
return Math.max(t, e.minWidth ?? X);
|
|
2009
2053
|
}
|
|
2010
2054
|
get headerHeight() {
|
|
2011
2055
|
return this.rowHeight + 8;
|
|
@@ -2036,7 +2080,15 @@ var Y = {
|
|
|
2036
2080
|
super.disconnectedCallback(), this.removeEventListener("scroll", this._onScroll), this.removeEventListener("keydown", this._onKeyDown), this.removeEventListener("contextmenu", this._onContextMenu), this.removeEventListener("dragover", this._onDragover), this.removeEventListener("dragleave", this._onDragleave), this.removeEventListener("drop", this._onDrop), document.removeEventListener("click", this._onDocumentClick), document.removeEventListener("mousedown", this._onCommentPopupOutsideClick), this._isDragging = !1, this._wasDrag = !1, this._resizeCleanup &&= (this._resizeCleanup(), null), this._hostResizeObserver &&= (this._hostResizeObserver.disconnect(), null);
|
|
2037
2081
|
}
|
|
2038
2082
|
firstUpdated() {
|
|
2039
|
-
this._measureViewport();
|
|
2083
|
+
this._readDensityTokens(), this._measureViewport();
|
|
2084
|
+
}
|
|
2085
|
+
_readDensityTokens() {
|
|
2086
|
+
let e = getComputedStyle(this).getPropertyValue("--ft-row-height").trim();
|
|
2087
|
+
if (!e) return;
|
|
2088
|
+
let t = parseFloat(e);
|
|
2089
|
+
if (!Number.isFinite(t) || t <= 0 || !/px\s*$/.test(e) || t === this._rowHeightFromCss) return;
|
|
2090
|
+
let n = this.rowHeight;
|
|
2091
|
+
this._rowHeightFromCss = t, this._rowHeightExplicit === void 0 && this.requestUpdate("rowHeight", n);
|
|
2040
2092
|
}
|
|
2041
2093
|
_updateColOffsets() {
|
|
2042
2094
|
let e = this.visibleColumns, t = [], n = this._prefixWidth;
|
|
@@ -2364,7 +2416,7 @@ var Y = {
|
|
|
2364
2416
|
"y"
|
|
2365
2417
|
].includes(t) && this._handleCtrlKey(e)) return;
|
|
2366
2418
|
}
|
|
2367
|
-
if (
|
|
2419
|
+
if (t.length !== 0 && this._visibleRowCount !== 0) {
|
|
2368
2420
|
if ((e.key === "Enter" || e.key === "F2") && this._activeCell) {
|
|
2369
2421
|
e.preventDefault(), this._startEdit();
|
|
2370
2422
|
return;
|
|
@@ -2418,7 +2470,7 @@ var Y = {
|
|
|
2418
2470
|
e.preventDefault();
|
|
2419
2471
|
let n = t[this._activeCell.col];
|
|
2420
2472
|
if (!n) return !0;
|
|
2421
|
-
let r = this._columnWidths.get(n.key) ?? n.width ??
|
|
2473
|
+
let r = this._columnWidths.get(n.key) ?? n.width ?? Y, i = e.key === "ArrowRight" ? 20 : -20, a = n.minWidth ?? X, o = Math.max(a, r + i);
|
|
2422
2474
|
return this._columnWidths.set(n.key, o), this.requestUpdate(), this.dispatchEvent(new CustomEvent("column-resize", {
|
|
2423
2475
|
detail: {
|
|
2424
2476
|
key: n.key,
|
|
@@ -2825,9 +2877,9 @@ var Y = {
|
|
|
2825
2877
|
if (!o) return r;
|
|
2826
2878
|
let s = this.data[t]?.[o.key], c = this._filters.some((e) => e.key === o.key), l = () => {
|
|
2827
2879
|
this._bodyContextMenu = null;
|
|
2828
|
-
};
|
|
2880
|
+
}, u = i + 200 > window.innerWidth ? i - 200 : i, d = a + 280 > window.innerHeight ? a - 280 : a;
|
|
2829
2881
|
return n`
|
|
2830
|
-
<div class="ft-body-context-menu" style="position: fixed; left: ${
|
|
2882
|
+
<div class="ft-body-context-menu" style="position: fixed; left: ${u}px; top: ${d}px; z-index: 200;"
|
|
2831
2883
|
@mousedown=${(e) => e.stopPropagation()}>
|
|
2832
2884
|
<div class="ft-context-menu-item"
|
|
2833
2885
|
@click=${() => {
|
|
@@ -2928,10 +2980,10 @@ var Y = {
|
|
|
2928
2980
|
}
|
|
2929
2981
|
_renderCommentPopup() {
|
|
2930
2982
|
if (!this._commentPopup) return r;
|
|
2931
|
-
let { x: e, y: t } = this._commentPopup;
|
|
2983
|
+
let { x: e, y: t } = this._commentPopup, i = e + 240 > window.innerWidth ? e - 240 : e, a = t + 150 > window.innerHeight ? t - 150 : t;
|
|
2932
2984
|
return n`
|
|
2933
2985
|
<div class="ft-comment-popup"
|
|
2934
|
-
style="position: fixed; left: ${
|
|
2986
|
+
style="position: fixed; left: ${i}px; top: ${a}px; z-index: 201;"
|
|
2935
2987
|
@mousedown=${(e) => e.stopPropagation()}>
|
|
2936
2988
|
<textarea class="ft-comment-popup-textarea"
|
|
2937
2989
|
rows="4"
|
|
@@ -3007,11 +3059,12 @@ var Y = {
|
|
|
3007
3059
|
return e === "empty" ? (e) => e == null || e === "" : (e) => e != null && e !== "";
|
|
3008
3060
|
}
|
|
3009
3061
|
_renderEmptyFilterRow(e, t) {
|
|
3062
|
+
let r = this._emptyFilterState.get(e) ?? "";
|
|
3010
3063
|
return n`
|
|
3011
3064
|
<div class="ft-filter-empty-row">
|
|
3012
3065
|
<label>Blank cells</label>
|
|
3013
3066
|
<select class="ft-filter-mode-select"
|
|
3014
|
-
.value=${
|
|
3067
|
+
.value=${r}
|
|
3015
3068
|
@change=${(n) => {
|
|
3016
3069
|
let r = n.target.value;
|
|
3017
3070
|
(r === "" || r === "empty" || r === "non-empty") && (r ? this._emptyFilterState.set(e, r) : this._emptyFilterState.delete(e), t?.(), this._applyFilterForKey(e));
|
|
@@ -3152,7 +3205,7 @@ var Y = {
|
|
|
3152
3205
|
@keydown=${(e) => {
|
|
3153
3206
|
e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
|
|
3154
3207
|
}}>
|
|
3155
|
-
${Object.keys(
|
|
3208
|
+
${Object.keys(J).map((e) => n`<option value=${e}>${J[e]}</option>`)}
|
|
3156
3209
|
</select>
|
|
3157
3210
|
<input class="ft-filter-input ft-num-cond-input" type="number" placeholder="Value"
|
|
3158
3211
|
.value=${r.value == null ? "" : String(r.value)}
|
|
@@ -3263,7 +3316,7 @@ var Y = {
|
|
|
3263
3316
|
if (!n) return;
|
|
3264
3317
|
let r = this.shadowRoot?.querySelectorAll(`.ft-cell[data-col-index="${t}"]`), i = 0, a = this.shadowRoot?.querySelector(`.ft-header-cell[data-col-index="${t}"]`);
|
|
3265
3318
|
if (a && (i = Math.max(i, a.scrollWidth)), r) for (let e of r) i = Math.max(i, e.scrollWidth);
|
|
3266
|
-
let o = n.minWidth ??
|
|
3319
|
+
let o = n.minWidth ?? X, s = Math.max(o, i + 8);
|
|
3267
3320
|
this._columnWidths.set(n.key, s), this.requestUpdate(), this.dispatchEvent(new CustomEvent("column-resize", {
|
|
3268
3321
|
detail: {
|
|
3269
3322
|
key: n.key,
|
|
@@ -3276,7 +3329,7 @@ var Y = {
|
|
|
3276
3329
|
}
|
|
3277
3330
|
_onResizeStart(e, t) {
|
|
3278
3331
|
e.preventDefault(), e.stopPropagation();
|
|
3279
|
-
let n = this.visibleColumns[t], r = this._columnWidths.get(n.key) ?? n.width ??
|
|
3332
|
+
let n = this.visibleColumns[t], r = this._columnWidths.get(n.key) ?? n.width ?? Y;
|
|
3280
3333
|
this._resizing = {
|
|
3281
3334
|
colIndex: t,
|
|
3282
3335
|
startX: e.clientX,
|
|
@@ -3284,13 +3337,13 @@ var Y = {
|
|
|
3284
3337
|
};
|
|
3285
3338
|
let i = (e) => {
|
|
3286
3339
|
if (!this._resizing) return;
|
|
3287
|
-
let t = e.clientX - this._resizing.startX, r = n.minWidth ??
|
|
3340
|
+
let t = e.clientX - this._resizing.startX, r = n.minWidth ?? X, i = Math.max(r, this._resizing.startWidth + t);
|
|
3288
3341
|
this._columnWidths.set(n.key, i), this.requestUpdate();
|
|
3289
3342
|
}, a = () => {
|
|
3290
3343
|
document.removeEventListener("mousemove", i), document.removeEventListener("mouseup", o), this._resizeCleanup = null;
|
|
3291
3344
|
}, o = () => {
|
|
3292
3345
|
if (a(), this._resizing) {
|
|
3293
|
-
let e = this._columnWidths.get(n.key) ??
|
|
3346
|
+
let e = this._columnWidths.get(n.key) ?? Y;
|
|
3294
3347
|
this.dispatchEvent(new CustomEvent("column-resize", {
|
|
3295
3348
|
detail: {
|
|
3296
3349
|
key: n.key,
|
|
@@ -3456,8 +3509,10 @@ var Y = {
|
|
|
3456
3509
|
let t = this.visibleColumns;
|
|
3457
3510
|
if (e.endCol >= t.length || e.endRow >= this._visibleRowCount) return "";
|
|
3458
3511
|
let r = (this._colLeftOffsets[e.endCol] ?? 0) + this._getColWidth(t[e.endCol]) - this._scrollLeft - 4, i = this.headerHeight + (e.endRow + 1) * this.rowHeight - this._scrollTop - 4;
|
|
3459
|
-
|
|
3460
|
-
|
|
3512
|
+
if (r < 0 || i < this.headerHeight) return "";
|
|
3513
|
+
let a = this._fillDrag?.active && this._fillDrag.targetRange ? this._renderFillPreview(this._fillDrag.targetRange) : "";
|
|
3514
|
+
return n`
|
|
3515
|
+
${a}
|
|
3461
3516
|
<div class="ft-fill-handle"
|
|
3462
3517
|
style="left:${r}px;top:${i}px;"
|
|
3463
3518
|
@mousedown=${(e) => this._onFillHandleMouseDown(e)}>
|
|
@@ -3466,7 +3521,9 @@ var Y = {
|
|
|
3466
3521
|
}
|
|
3467
3522
|
_renderFillPreview(e) {
|
|
3468
3523
|
let t = this.visibleColumns;
|
|
3469
|
-
|
|
3524
|
+
if (e.endCol >= t.length || e.endRow >= this._visibleRowCount) return "";
|
|
3525
|
+
let r = (this._colLeftOffsets[e.startCol] ?? 0) - this._scrollLeft, i = this.headerHeight + e.startRow * this.rowHeight - this._scrollTop, a = (this._colLeftOffsets[e.endCol] ?? 0) + this._getColWidth(t[e.endCol]) - (this._colLeftOffsets[e.startCol] ?? 0), o = (e.endRow - e.startRow + 1) * this.rowHeight;
|
|
3526
|
+
return n`<div class="ft-fill-preview" style="left:${r}px;top:${i}px;width:${a}px;height:${o}px;"></div>`;
|
|
3470
3527
|
}
|
|
3471
3528
|
_onRowNumMouseDown(e, t) {
|
|
3472
3529
|
if (e.button !== 0) return;
|
|
@@ -3716,15 +3773,18 @@ var Y = {
|
|
|
3716
3773
|
for (let t of l) m.push(this._renderHeaderCell(e[t], t));
|
|
3717
3774
|
for (let t = s; t < c; t++) m.push(this._renderHeaderCell(e[t], t));
|
|
3718
3775
|
let h = this._colDragIndicatorLeft == null ? "" : n`<div class="ft-drop-indicator" style="left:${this._colDragIndicatorLeft}px"></div>`;
|
|
3719
|
-
if (this.data.length === 0 || this._visibleRowCount === 0)
|
|
3776
|
+
if (this.data.length === 0 || this._visibleRowCount === 0) {
|
|
3777
|
+
let e = this.data.length === 0 ? "No data" : "No matching data";
|
|
3778
|
+
return n`
|
|
3720
3779
|
${i}
|
|
3721
3780
|
<div class="ft-header" role="row" style="width: ${o}px; height: ${a}px;">
|
|
3722
3781
|
${f}${p}
|
|
3723
3782
|
${m}
|
|
3724
3783
|
${h}
|
|
3725
3784
|
</div>
|
|
3726
|
-
<div class="ft-empty">${
|
|
3785
|
+
<div class="ft-empty">${e}</div>
|
|
3727
3786
|
`;
|
|
3787
|
+
}
|
|
3728
3788
|
let { start: g, end: _ } = this.visibleRange, v = this._frozenRowCount, y = [];
|
|
3729
3789
|
for (let e = g; e < _; e++) y.push(this._renderRow(e, s, c, l, (e - v) * this.rowHeight));
|
|
3730
3790
|
let b = this._renderFillHandle(), x = this._rowDragIndicatorY == null ? "" : n`<div class="ft-row-drop-indicator" style="top:${this._rowDragIndicatorY}px;width:${o + this._prefixWidth}px;"></div>`;
|
|
@@ -3875,18 +3935,24 @@ var Y = {
|
|
|
3875
3935
|
@keydown=${this._onEditorKeyDown}
|
|
3876
3936
|
@blur=${() => this._commitEdit()}>
|
|
3877
3937
|
`;
|
|
3878
|
-
if (t.type === "date")
|
|
3938
|
+
if (t.type === "date") {
|
|
3939
|
+
let e = this._toDateInputValue(i);
|
|
3940
|
+
return n`
|
|
3879
3941
|
<input class="ft-editor" type="date"
|
|
3880
|
-
.value=${
|
|
3942
|
+
.value=${e}
|
|
3881
3943
|
@keydown=${this._onEditorKeyDown}
|
|
3882
3944
|
@blur=${() => this._commitEdit()}>
|
|
3883
3945
|
`;
|
|
3884
|
-
|
|
3946
|
+
}
|
|
3947
|
+
if (t.type === "datetime") {
|
|
3948
|
+
let e = this._toDateTimeInputValue(i);
|
|
3949
|
+
return n`
|
|
3885
3950
|
<input class="ft-editor" type="datetime-local"
|
|
3886
|
-
.value=${
|
|
3951
|
+
.value=${e}
|
|
3887
3952
|
@keydown=${this._onEditorKeyDown}
|
|
3888
3953
|
@blur=${() => this._commitEdit()}>
|
|
3889
3954
|
`;
|
|
3955
|
+
}
|
|
3890
3956
|
if (t.type === "select" && t.options && t.options.length > 0) {
|
|
3891
3957
|
let e = t.options, r = typeof e[0] == "string";
|
|
3892
3958
|
return n`
|
|
@@ -3938,54 +4004,54 @@ var Y = {
|
|
|
3938
4004
|
return isNaN(t.getTime()) ? "" : `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, "0")}-${String(t.getDate()).padStart(2, "0")}T${String(t.getHours()).padStart(2, "0")}:${String(t.getMinutes()).padStart(2, "0")}`;
|
|
3939
4005
|
}
|
|
3940
4006
|
};
|
|
3941
|
-
|
|
4007
|
+
q([a({ type: Array })], $.prototype, "columns", void 0), q([a({
|
|
3942
4008
|
type: Array,
|
|
3943
4009
|
hasChanged: () => !0
|
|
3944
|
-
})], $.prototype, "data", null),
|
|
4010
|
+
})], $.prototype, "data", null), q([a({
|
|
3945
4011
|
type: Boolean,
|
|
3946
4012
|
attribute: "clear-undo-on-data-change"
|
|
3947
|
-
})], $.prototype, "clearUndoOnDataChange", void 0),
|
|
4013
|
+
})], $.prototype, "clearUndoOnDataChange", void 0), q([a({
|
|
3948
4014
|
type: Boolean,
|
|
3949
4015
|
attribute: "clear-selection-on-data-change"
|
|
3950
|
-
})], $.prototype, "clearSelectionOnDataChange", void 0),
|
|
4016
|
+
})], $.prototype, "clearSelectionOnDataChange", void 0), q([a({
|
|
3951
4017
|
type: Number,
|
|
3952
4018
|
attribute: "row-height"
|
|
3953
|
-
})], $.prototype, "rowHeight",
|
|
4019
|
+
})], $.prototype, "rowHeight", null), q([a({
|
|
3954
4020
|
type: Boolean,
|
|
3955
4021
|
attribute: "show-row-numbers"
|
|
3956
|
-
})], $.prototype, "showRowNumbers", void 0),
|
|
4022
|
+
})], $.prototype, "showRowNumbers", void 0), q([a({
|
|
3957
4023
|
type: String,
|
|
3958
4024
|
reflect: !0
|
|
3959
|
-
})], $.prototype, "theme", void 0),
|
|
4025
|
+
})], $.prototype, "theme", void 0), q([a({
|
|
3960
4026
|
type: Number,
|
|
3961
4027
|
attribute: "max-rows"
|
|
3962
|
-
})], $.prototype, "maxRows", void 0),
|
|
4028
|
+
})], $.prototype, "maxRows", void 0), q([a({ type: Boolean })], $.prototype, "editable", void 0), q([a({
|
|
3963
4029
|
type: Boolean,
|
|
3964
4030
|
attribute: "show-filters"
|
|
3965
|
-
})], $.prototype, "showFilters", void 0),
|
|
4031
|
+
})], $.prototype, "showFilters", void 0), q([a({
|
|
3966
4032
|
type: Boolean,
|
|
3967
4033
|
attribute: "show-context-menu"
|
|
3968
|
-
})], $.prototype, "showContextMenu", void 0),
|
|
4034
|
+
})], $.prototype, "showContextMenu", void 0), q([a({
|
|
3969
4035
|
type: Number,
|
|
3970
4036
|
attribute: "frozen-rows"
|
|
3971
|
-
})], $.prototype, "frozenRows", void 0),
|
|
4037
|
+
})], $.prototype, "frozenRows", void 0), q([a({
|
|
3972
4038
|
type: Boolean,
|
|
3973
4039
|
reflect: !0
|
|
3974
|
-
})], $.prototype, "loading", void 0),
|
|
4040
|
+
})], $.prototype, "loading", void 0), q([a({
|
|
3975
4041
|
type: Boolean,
|
|
3976
4042
|
attribute: "import-enabled"
|
|
3977
|
-
})], $.prototype, "importEnabled", void 0),
|
|
4043
|
+
})], $.prototype, "importEnabled", void 0), q([a({ type: Boolean })], $.prototype, "selectable", void 0), q([a({
|
|
3978
4044
|
type: String,
|
|
3979
4045
|
attribute: "selection-mode"
|
|
3980
|
-
})], $.prototype, "selectionMode", null),
|
|
4046
|
+
})], $.prototype, "selectionMode", null), q([a({
|
|
3981
4047
|
type: String,
|
|
3982
4048
|
attribute: "data-mode"
|
|
3983
|
-
})], $.prototype, "dataMode", void 0),
|
|
4049
|
+
})], $.prototype, "dataMode", void 0), q([a({
|
|
3984
4050
|
type: Object,
|
|
3985
4051
|
attribute: "footer-data"
|
|
3986
|
-
})], $.prototype, "footerData", void 0),
|
|
4052
|
+
})], $.prototype, "footerData", void 0), q([a({
|
|
3987
4053
|
type: Number,
|
|
3988
4054
|
attribute: "max-undo-size"
|
|
3989
|
-
})], $.prototype, "maxUndoSize", null),
|
|
4055
|
+
})], $.prototype, "maxUndoSize", null), q([o()], $.prototype, "_scrollTop", void 0), q([o()], $.prototype, "_scrollLeft", void 0), q([o()], $.prototype, "_viewportHeight", void 0), q([o()], $.prototype, "_viewportWidth", void 0), q([o()], $.prototype, "_activeCell", void 0), q([o()], $.prototype, "_editingCell", void 0), q([o()], $.prototype, "_sortCriteria", void 0), q([o()], $.prototype, "_openFilterKey", void 0), q([o()], $.prototype, "_rowSelectionVersion", void 0), q([o()], $.prototype, "_autocompleteState", void 0), q([o()], $.prototype, "_headerMenu", void 0), q([o()], $.prototype, "_bodyContextMenu", void 0), q([o()], $.prototype, "_commentPopup", void 0), q([o()], $.prototype, "_isDragOver", void 0), $ = q([i("flex-table")], $);
|
|
3990
4056
|
//#endregion
|
|
3991
|
-
export { f as a, x as i,
|
|
4057
|
+
export { f as a, x as i, z as n, D as r, $ as t };
|
package/dist/flex-table.d.ts
CHANGED
|
@@ -35,7 +35,23 @@ export declare class FlexTable extends LitElement {
|
|
|
35
35
|
* (e.g. server-mode grids refreshed via `useODataSource`).
|
|
36
36
|
*/
|
|
37
37
|
clearSelectionOnDataChange: boolean;
|
|
38
|
-
|
|
38
|
+
/**
|
|
39
|
+
* 행 높이(px).
|
|
40
|
+
*
|
|
41
|
+
* ⚠**이 표는 가상 스크롤이라 행 높이가 CSS 가 아니라 여기서 정해진다** — 행은
|
|
42
|
+
* `index * rowHeight` 로 절대 배치되고 셀 높이도 인라인으로 박힌다. 그래서 셀 여백을
|
|
43
|
+
* 아무리 조절해도 행 높이는 따라오지 않는다.
|
|
44
|
+
*
|
|
45
|
+
* 소비자가 표를 **문서 스코프 한 규칙**으로 맞출 수 있도록 CSS 토큰
|
|
46
|
+
* `--ft-row-height` 도 읽는다. 우선순위는 **프로퍼티/속성 > CSS 토큰 > 32px** 이다 —
|
|
47
|
+
* 명시적으로 지정한 쪽이 항상 이긴다.
|
|
48
|
+
*/
|
|
49
|
+
get rowHeight(): number;
|
|
50
|
+
set rowHeight(value: number);
|
|
51
|
+
/** 프로퍼티·속성으로 **명시 지정된** 값. 지정 전에는 undefined 라 CSS 가 이긴다. */
|
|
52
|
+
private _rowHeightExplicit?;
|
|
53
|
+
/** `--ft-row-height` 판독 결과. 판독 전·판독 실패 시 기본값. */
|
|
54
|
+
private _rowHeightFromCss;
|
|
39
55
|
showRowNumbers: boolean;
|
|
40
56
|
theme: 'light' | 'dark' | undefined;
|
|
41
57
|
maxRows: number;
|
|
@@ -256,6 +272,14 @@ export declare class FlexTable extends LitElement {
|
|
|
256
272
|
connectedCallback(): void;
|
|
257
273
|
disconnectedCallback(): void;
|
|
258
274
|
protected firstUpdated(): void;
|
|
275
|
+
/**
|
|
276
|
+
* `--ft-row-height` 를 판독해 가상화 계산에 넣는다.
|
|
277
|
+
*
|
|
278
|
+
* ⚠**`_measureViewport` 안에 두지 않는다** — 그쪽은 ResizeObserver 가 호스트 크기가
|
|
279
|
+
* 바뀔 때마다 부른다. `getComputedStyle` 은 그 빈도로 돌릴 것이 아니다. 이 토큰은
|
|
280
|
+
* 문서 스코프의 정적 선언으로 쓰이므로 첫 렌더에 한 번 읽으면 충분하다.
|
|
281
|
+
*/
|
|
282
|
+
private _readDensityTokens;
|
|
259
283
|
private _updateColOffsets;
|
|
260
284
|
private get visibleColRange();
|
|
261
285
|
protected willUpdate(changedProperties: Map<string, unknown>): void;
|
package/dist/flex-table.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, r, t as i } from "./flex-table-
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "./flex-table-DReUAeUh.js";
|
|
2
2
|
export { i as FlexTable, t as RowSelectionState, r as UndoStack, n as exportData, e as renderCell };
|
package/dist/react.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../flex-table';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/flex-table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "A minimalist, input-centric data grid web component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/flex-table.js",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@lit/react": "^1.0.8",
|
|
72
|
+
"@types/node": "^26.1.1",
|
|
72
73
|
"@types/react": "^19.2.17",
|
|
73
74
|
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
|
74
75
|
"@typescript-eslint/parser": "^8.64.0",
|