@iyulab/flex-table 0.34.2 → 0.35.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/CHANGELOG.md +38 -0
- package/README.md +23 -3
- package/dist/{flex-table-C-uws8qc.js → flex-table-Y4GyDV0Y.js} +322 -251
- package/dist/flex-table.d.ts +31 -3
- package/dist/flex-table.js +1 -1
- package/dist/react.js +1 -1
- package/package.json +4 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.35.0] - 2026-09-15
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Every header now has a 24×24 column menu button (`⋮`), which replaces the 14px filter button.**
|
|
8
|
+
The menu — the same one a header right-click opens — gathers the per-column actions: **Sort
|
|
9
|
+
ascending** / **Sort descending** (for sortable columns — the keyboard path to sorting), **Filter…**
|
|
10
|
+
and **Clear filter** (with `show-filters`), **Hide column** / **Show: …**, **Auto-fit width**,
|
|
11
|
+
**Wider** and **Narrower** (±20px, the menu stays open for repeated steps). Opening a filter is now
|
|
12
|
+
two clicks. The button is highlighted while its column is filtered, as the filter button was.
|
|
13
|
+
The old filter button was below the WCAG 2.2 SC 2.5.8 minimum and sat right next to the resize handle;
|
|
14
|
+
the handle keeps its 6px width and the column menu is its equivalent, which also means resizing
|
|
15
|
+
no longer requires a drag (SC 2.5.7).
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Sorting from the cell context menu broke the `sort-change` contract.** It reported
|
|
20
|
+
`{ sortCriteria }` instead of `{ criteria }`, re-sorted the data locally even with
|
|
21
|
+
`data-mode="server"`, and ignored `sortable: false`. It now behaves exactly like a header click.
|
|
22
|
+
- **The header and cell context menus could not be used from the keyboard, and could open
|
|
23
|
+
off-screen.** Their items are now buttons in a `role="menu"`: arrow keys, Home and End move between
|
|
24
|
+
them, Escape closes and returns focus (to the column menu button, or to the grid). The cell menu
|
|
25
|
+
focuses its first item when it opens. Both menus are moved back inside the visible area once their
|
|
26
|
+
real size is known — the cell menu previously flipped on a guessed 200×280 size, and a menu fitted
|
|
27
|
+
to the window width could still sit under the vertical scrollbar.
|
|
28
|
+
|
|
29
|
+
## [0.34.3] - 2026-09-13
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **The "host has no height constraint" dev-mode warning was a complete no-op in every published
|
|
34
|
+
release** — the guard used `import.meta.env.DEV`, which Vite resolves statically at this
|
|
35
|
+
package's own build time, so the published dist always shipped with the check baked to `false`
|
|
36
|
+
and the warning body removed entirely by dead-code elimination. Replaced with
|
|
37
|
+
`process.env.NODE_ENV !== 'production'`, which survives this package's own build and is resolved
|
|
38
|
+
by each consumer's own bundler against its own dev/production build (see `@iyulab/components`
|
|
39
|
+
1.40.3, which had the identical defect in its shared dev-warning helper).
|
|
40
|
+
|
|
3
41
|
## [0.34.2] - 2026-09-13
|
|
4
42
|
|
|
5
43
|
### Added
|
package/README.md
CHANGED
|
@@ -52,7 +52,8 @@ npm install @iyulab/flex-table
|
|
|
52
52
|
- **Row Selection** — Checkbox-based row selection (`selectable`, single/multi mode)
|
|
53
53
|
- **Clipboard** — Ctrl+C/X/V with TSV format (Excel/Google Sheets compatible, RFC 4180)
|
|
54
54
|
- **Sorting** — Click header to sort (asc/desc/none), Shift+click for multi-sort
|
|
55
|
-
- **Column
|
|
55
|
+
- **Column Menu** — A 24×24 button in every header opens the column's menu: sort, filter, hide/show, auto-fit, wider/narrower (also opens on header right-click)
|
|
56
|
+
- **Column Resize** — Drag header border, double-click to auto-fit, Alt+Arrow keyboard resize, or the column menu
|
|
56
57
|
- **Column Operations** — `addColumn()`, `deleteColumn()`, `moveColumn()` with undo
|
|
57
58
|
- **Pinned Columns** — Freeze columns to left or right (`pinned: 'left' | 'right'`)
|
|
58
59
|
- **Filtering** — Programmatic API + built-in header filter UI (`show-filters`)
|
|
@@ -103,7 +104,7 @@ guarantee about a *constrained* host. `height-model.browser.test.ts` pins both s
|
|
|
103
104
|
| `showRowNumbers` | `show-row-numbers` | `boolean` | `false` | Show row number column |
|
|
104
105
|
| `theme` | `theme` | `'light' \| 'dark'` | auto | Force theme; auto-detects `prefers-color-scheme` |
|
|
105
106
|
| `editable` | `editable` | `boolean` | `true` | Global read-only mode when `false`. Defaults to `true` — a purely read-only grid should set this explicitly rather than relying on per-column `editable: false` alone, since it's also what makes Enter fire `row-activate` (see [Events](#events)) instead of entering edit mode |
|
|
106
|
-
| `showFilters` | `show-filters` | `boolean` | `false` |
|
|
107
|
+
| `showFilters` | `show-filters` | `boolean` | `false` | Offer the built-in filter dropdowns through each column menu («Filter…») |
|
|
107
108
|
| `maxRows` | `max-rows` | `number` | `0` | Max row count (0 = unlimited); blocks `addRow()` and paste expansion |
|
|
108
109
|
| `maxUndoSize` | `max-undo-size` | `number` | `100` | Max undo history stack size |
|
|
109
110
|
| `selectable` | `selectable` | `boolean` | `false` | Enable row-level checkbox selection |
|
|
@@ -404,6 +405,23 @@ package dependency; CSS custom properties are resolved at render time, not impor
|
|
|
404
405
|
| Ctrl+Shift+Z / Ctrl+Y | Redo |
|
|
405
406
|
| Alt+ArrowLeft / Alt+ArrowRight | Resize current column (±20px) |
|
|
406
407
|
| Ctrl+Click header | Select entire column |
|
|
408
|
+
| Enter / Space on a column menu button | Open the column menu |
|
|
409
|
+
| ArrowUp / ArrowDown / Home / End (column menu) | Move between menu items |
|
|
410
|
+
| Escape (column menu) | Close the menu and return focus to its button |
|
|
411
|
+
|
|
412
|
+
## Accessibility
|
|
413
|
+
|
|
414
|
+
The baseline is **WCAG 2.2**. The table lists what this package **measures in tests** — it is not a
|
|
415
|
+
conformance claim for the success criteria it does not list.
|
|
416
|
+
|
|
417
|
+
| Success criterion | Guarantee | Measured by |
|
|
418
|
+
|---|---|---|
|
|
419
|
+
| SC 2.5.8 Target Size (Minimum) | Sortable headers, column menu buttons, the open column menu, the open filter dropdown (text and number), the find/replace bar, the cell context menu and row selection checkboxes are at least 24×24 CSS px or meet the spacing exception (24px between centers, counting the neighbouring resize handles), and are actually hit at that position. Two small targets use the equivalent-control exception: the 6px column resize handle (every resize is also in the column menu) and the hidden-column marker (its column menu offers **Show: …**) | `tests/browser/target-size.browser.test.ts` (real Chromium) |
|
|
420
|
+
| SC 2.1.1 Keyboard (pointer-cursor check) | Nothing the grid renders shows a pointer cursor without being an interactive element. Sorting by header click also has a keyboard path — the column menu's **Sort ascending** / **Sort descending** (the check alone cannot see that, because the header cell wraps the menu button) | `tests/browser/target-size.browser.test.ts` · `src/flex-table.test.ts` |
|
|
421
|
+
| SC 2.5.7 Dragging Movements | Resizing never requires a drag — the column menu's **Auto-fit width**, **Wider** and **Narrower** are single clicks | `tests/browser/column-menu.browser.test.ts` |
|
|
422
|
+
|
|
423
|
+
Not yet measured: the boolean and date filter dropdowns and the comment popup. Color contrast comes
|
|
424
|
+
from the `@iyulab/components` tokens this package reads.
|
|
407
425
|
|
|
408
426
|
## Usage Guide
|
|
409
427
|
|
|
@@ -569,7 +587,9 @@ Use `updateRows()` for programmatic edits — it provides undo/redo and dispatch
|
|
|
569
587
|
|
|
570
588
|
### Built-in Filter UI
|
|
571
589
|
|
|
572
|
-
Enable with `show-filters` attribute.
|
|
590
|
+
Enable with `show-filters` attribute. Each column menu (the `⋮` button in the header) then offers **Filter…**,
|
|
591
|
+
which opens that column's filter dropdown, and **Clear filter** while the column is filtered. The menu
|
|
592
|
+
button is highlighted while its column has an active filter.
|
|
573
593
|
|
|
574
594
|
- **text**: case-insensitive substring search
|
|
575
595
|
- **number**: min/max range inputs
|
|
@@ -6,7 +6,7 @@ var l = r`
|
|
|
6
6
|
/* --- Theme Variables (Light defaults) --- */
|
|
7
7
|
:host {
|
|
8
8
|
--ft-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
9
|
-
/* 밀도 스위치 진입점 — components 의 --u-density 를 그대로
|
|
9
|
+
/* 밀도 스위치 진입점 — components 의 --u-density 를 그대로 읽는다. */
|
|
10
10
|
--ft-font-size: var(--u-density, 14px);
|
|
11
11
|
|
|
12
12
|
/* --- 치수·위계 축 (기본값 = 종전 렌더값) ---
|
|
@@ -33,7 +33,7 @@ var l = r`
|
|
|
33
33
|
--ft-row-hover-bg: var(--u-bg-color-hover, #F5F5F5);
|
|
34
34
|
/* ★홀수 행은 --ft-row-odd-bg(=raised) 위에 앉는다 — 짝수 행과 같은
|
|
35
35
|
--ft-row-hover-bg(=전역 hover) 를 쓰면 다크에서 raised==hover 라 «올려도 아무
|
|
36
|
-
일도 안 일어난다»(
|
|
36
|
+
일도 안 일어난다»(소비자 실측). --u-bg-color-raised-hover 는 다크에서
|
|
37
37
|
color-mix 로 새로 계산되고, 라이트에서는 기존 hover 에 별칭된다(components
|
|
38
38
|
light.css) — 두 테마 다 늘 선언돼 있으므로 폴백 리터럴은 시트 미로드 시에만
|
|
39
39
|
쓰인다(다른 토큰들과 같은 관례). */
|
|
@@ -447,20 +447,39 @@ var l = r`
|
|
|
447
447
|
|
|
448
448
|
/* --- Filter UI --- */
|
|
449
449
|
|
|
450
|
-
.
|
|
450
|
+
/* 열 메뉴 버튼 — 필터·숨기기·폭 조절이 모두 여기서 열린다. 24px 은 WCAG 2.2 SC 2.5.8 하한이고,
|
|
451
|
+
6px 리사이즈 핸들의 «등가 수단» 이 이 버튼의 메뉴다. */
|
|
452
|
+
.ft-column-menu-btn {
|
|
451
453
|
all: unset;
|
|
454
|
+
box-sizing: border-box;
|
|
455
|
+
display: inline-flex;
|
|
456
|
+
align-items: center;
|
|
457
|
+
justify-content: center;
|
|
458
|
+
inline-size: 24px;
|
|
459
|
+
block-size: 24px;
|
|
460
|
+
margin-left: auto;
|
|
461
|
+
flex-shrink: 0;
|
|
462
|
+
border-radius: 4px;
|
|
452
463
|
cursor: pointer;
|
|
453
|
-
font-size:
|
|
464
|
+
font-size: 16px;
|
|
465
|
+
line-height: 1;
|
|
454
466
|
color: var(--ft-sort-indicator-color);
|
|
455
|
-
padding: 0 2px;
|
|
456
|
-
margin-left: auto;
|
|
457
467
|
opacity: 0.7;
|
|
458
|
-
flex-shrink: 0;
|
|
459
468
|
}
|
|
460
469
|
|
|
461
|
-
.ft-
|
|
470
|
+
.ft-column-menu-btn:hover,
|
|
471
|
+
.ft-column-menu-btn[aria-expanded="true"] {
|
|
472
|
+
opacity: 1;
|
|
473
|
+
background: var(--ft-header-hover-bg);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.ft-column-menu-btn:focus-visible {
|
|
477
|
+
opacity: 1;
|
|
478
|
+
outline: 2px solid var(--ft-active-color);
|
|
479
|
+
outline-offset: -2px;
|
|
480
|
+
}
|
|
462
481
|
|
|
463
|
-
.ft-
|
|
482
|
+
.ft-column-menu-btn.ft-filter-active {
|
|
464
483
|
color: var(--ft-active-color);
|
|
465
484
|
opacity: 1;
|
|
466
485
|
}
|
|
@@ -639,21 +658,36 @@ var l = r`
|
|
|
639
658
|
min-width: 160px;
|
|
640
659
|
border-radius: 4px;
|
|
641
660
|
overflow: hidden;
|
|
661
|
+
display: flex;
|
|
662
|
+
flex-direction: column;
|
|
663
|
+
padding-block: 4px;
|
|
642
664
|
}
|
|
643
665
|
|
|
644
666
|
.ft-header-menu-item {
|
|
667
|
+
all: unset;
|
|
668
|
+
box-sizing: border-box;
|
|
669
|
+
display: block;
|
|
670
|
+
min-block-size: 28px;
|
|
645
671
|
padding: 6px 12px;
|
|
646
672
|
cursor: pointer;
|
|
647
673
|
font-size: var(--ft-font-size, 14px);
|
|
648
674
|
color: var(--ft-text-color);
|
|
649
675
|
white-space: nowrap;
|
|
676
|
+
text-align: start;
|
|
650
677
|
}
|
|
651
678
|
|
|
652
|
-
.ft-header-menu-item:hover
|
|
679
|
+
.ft-header-menu-item:hover,
|
|
680
|
+
.ft-header-menu-item:focus-visible {
|
|
653
681
|
background: var(--ft-selection-bg);
|
|
654
682
|
color: var(--ft-active-color);
|
|
655
683
|
}
|
|
656
684
|
|
|
685
|
+
.ft-header-menu-separator {
|
|
686
|
+
block-size: 1px;
|
|
687
|
+
margin-block: 4px;
|
|
688
|
+
background: var(--ft-border-color);
|
|
689
|
+
}
|
|
690
|
+
|
|
657
691
|
/* Hidden column indicator in header */
|
|
658
692
|
.ft-hidden-col-indicator {
|
|
659
693
|
position: absolute;
|
|
@@ -693,20 +727,28 @@ var l = r`
|
|
|
693
727
|
}
|
|
694
728
|
|
|
695
729
|
.ft-context-menu-item {
|
|
730
|
+
all: unset;
|
|
731
|
+
box-sizing: border-box;
|
|
732
|
+
display: block;
|
|
733
|
+
inline-size: 100%;
|
|
734
|
+
min-block-size: 28px;
|
|
696
735
|
padding: 6px 14px;
|
|
697
736
|
cursor: pointer;
|
|
698
737
|
font-size: var(--ft-font-size, 14px);
|
|
699
738
|
color: var(--ft-text-color);
|
|
700
739
|
white-space: nowrap;
|
|
701
740
|
user-select: none;
|
|
741
|
+
text-align: start;
|
|
702
742
|
}
|
|
703
743
|
|
|
704
|
-
.ft-context-menu-item:hover
|
|
744
|
+
.ft-context-menu-item:hover,
|
|
745
|
+
.ft-context-menu-item:focus-visible {
|
|
705
746
|
background: var(--ft-selection-bg);
|
|
706
747
|
color: var(--ft-active-color);
|
|
707
748
|
}
|
|
708
749
|
|
|
709
|
-
.ft-context-menu-item.ft-context-menu-danger:hover
|
|
750
|
+
.ft-context-menu-item.ft-context-menu-danger:hover,
|
|
751
|
+
.ft-context-menu-item.ft-context-menu-danger:focus-visible {
|
|
710
752
|
background: #fdd;
|
|
711
753
|
color: #c0392b;
|
|
712
754
|
}
|
|
@@ -1247,56 +1289,56 @@ var A = (() => {
|
|
|
1247
1289
|
}
|
|
1248
1290
|
return e;
|
|
1249
1291
|
})();
|
|
1250
|
-
function
|
|
1292
|
+
function ee(e) {
|
|
1251
1293
|
let t = 4294967295;
|
|
1252
1294
|
for (let n = 0; n < e.length; n++) t = A[(t ^ e[n]) & 255] ^ t >>> 8;
|
|
1253
1295
|
return (t ^ 4294967295) >>> 0;
|
|
1254
1296
|
}
|
|
1255
|
-
var
|
|
1256
|
-
function
|
|
1297
|
+
var j = new TextEncoder();
|
|
1298
|
+
function te(e) {
|
|
1257
1299
|
let t = [], n = [], r = 0;
|
|
1258
1300
|
for (let i of e) {
|
|
1259
|
-
let e =
|
|
1301
|
+
let e = j.encode(i.name), a = ee(i.data), o = i.data.length, s = new ArrayBuffer(30 + e.length), c = new DataView(s);
|
|
1260
1302
|
c.setUint32(0, 67324752, !0), c.setUint16(4, 20, !0), c.setUint16(6, 0, !0), c.setUint16(8, 0, !0), c.setUint16(10, 0, !0), c.setUint16(12, 33, !0), c.setUint32(14, a, !0), c.setUint32(18, o, !0), c.setUint32(22, o, !0), c.setUint16(26, e.length, !0), c.setUint16(28, 0, !0), new Uint8Array(s, 30).set(e), t.push(new Uint8Array(s)), t.push(i.data);
|
|
1261
1303
|
let l = new ArrayBuffer(46 + e.length), u = new DataView(l);
|
|
1262
1304
|
u.setUint32(0, 33639248, !0), u.setUint16(4, 20, !0), u.setUint16(6, 20, !0), u.setUint16(8, 0, !0), u.setUint16(10, 0, !0), u.setUint16(12, 0, !0), u.setUint16(14, 33, !0), u.setUint32(16, a, !0), u.setUint32(20, o, !0), u.setUint32(24, o, !0), u.setUint16(28, e.length, !0), u.setUint16(30, 0, !0), u.setUint16(32, 0, !0), u.setUint16(34, 0, !0), u.setUint16(36, 0, !0), u.setUint32(38, 0, !0), u.setUint32(42, r, !0), new Uint8Array(l, 46).set(e), n.push(new Uint8Array(l)), r += 30 + e.length + o;
|
|
1263
1305
|
}
|
|
1264
|
-
let i =
|
|
1265
|
-
return o.setUint32(0, 101010256, !0), o.setUint16(4, 0, !0), o.setUint16(6, 0, !0), o.setUint16(8, e.length, !0), o.setUint16(10, e.length, !0), o.setUint32(12, i.length, !0), o.setUint32(16, r, !0), o.setUint16(20, 0, !0),
|
|
1306
|
+
let i = M(n), a = /* @__PURE__ */ new ArrayBuffer(22), o = new DataView(a);
|
|
1307
|
+
return o.setUint32(0, 101010256, !0), o.setUint16(4, 0, !0), o.setUint16(6, 0, !0), o.setUint16(8, e.length, !0), o.setUint16(10, e.length, !0), o.setUint32(12, i.length, !0), o.setUint32(16, r, !0), o.setUint16(20, 0, !0), M([
|
|
1266
1308
|
...t,
|
|
1267
1309
|
i,
|
|
1268
1310
|
new Uint8Array(a)
|
|
1269
1311
|
]);
|
|
1270
1312
|
}
|
|
1271
|
-
function
|
|
1313
|
+
function M(e) {
|
|
1272
1314
|
let t = e.reduce((e, t) => e + t.length, 0), n = new Uint8Array(t), r = 0;
|
|
1273
1315
|
for (let t of e) n.set(t, r), r += t.length;
|
|
1274
1316
|
return n;
|
|
1275
1317
|
}
|
|
1276
|
-
function
|
|
1318
|
+
function N(e) {
|
|
1277
1319
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1278
1320
|
}
|
|
1279
|
-
function
|
|
1321
|
+
function P(e) {
|
|
1280
1322
|
let t = "";
|
|
1281
1323
|
for (e++; e > 0;) t = String.fromCharCode(64 + (e % 26 || 26)) + t, e = Math.floor((e - 1) / 26);
|
|
1282
1324
|
return t;
|
|
1283
1325
|
}
|
|
1284
|
-
function
|
|
1285
|
-
return
|
|
1326
|
+
function F(e, t) {
|
|
1327
|
+
return P(t) + (e + 1);
|
|
1286
1328
|
}
|
|
1287
|
-
function
|
|
1329
|
+
function ne(e) {
|
|
1288
1330
|
let t = Date.UTC(1899, 11, 30);
|
|
1289
1331
|
return (e.getTime() - t) / 864e5;
|
|
1290
1332
|
}
|
|
1291
|
-
var
|
|
1292
|
-
function
|
|
1293
|
-
let n = [], r = t.map((e, t) => `<c r="${
|
|
1333
|
+
var re = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n <Default Extension=\"xml\" ContentType=\"application/xml\"/>\n <Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>\n <Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>\n <Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/>\n</Types>", ie = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"xl/workbook.xml\"/>\n</Relationships>", ae = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\n <sheets><sheet name=\"Sheet1\" sheetId=\"1\" r:id=\"rId1\"/></sheets>\n</workbook>", oe = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet1.xml\"/>\n <Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>\n</Relationships>", se = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">\n <numFmts count=\"1\"><numFmt numFmtId=\"164\" formatCode=\"yyyy-mm-dd\"/></numFmts>\n <fonts count=\"2\">\n <font><sz val=\"11\"/><name val=\"Calibri\"/></font>\n <font><sz val=\"11\"/><b/><name val=\"Calibri\"/></font>\n </fonts>\n <fills count=\"2\">\n <fill><patternFill patternType=\"none\"/></fill>\n <fill><patternFill patternType=\"gray125\"/></fill>\n </fills>\n <borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders>\n <cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>\n <cellXfs count=\"3\">\n <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>\n <xf numFmtId=\"164\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/>\n <xf numFmtId=\"0\" fontId=\"1\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\"/>\n </cellXfs>\n</styleSheet>";
|
|
1334
|
+
function ce(e, t) {
|
|
1335
|
+
let n = [], r = t.map((e, t) => `<c r="${F(0, t)}" t="str" s="2"><v>${N(e.header)}</v></c>`);
|
|
1294
1336
|
return n.push(`<row r="1">${r.join("")}</row>`), e.forEach((e, r) => {
|
|
1295
1337
|
let i = t.map((t, n) => {
|
|
1296
|
-
let i =
|
|
1338
|
+
let i = F(r + 1, n), a = e[t.key];
|
|
1297
1339
|
if (a == null) return `<c r="${i}" t="str"><v></v></c>`;
|
|
1298
1340
|
let o = t.type ?? "text";
|
|
1299
|
-
return o === "number" && typeof a == "number" ? `<c r="${i}"><v>${a}</v></c>` : (o === "date" || o === "datetime") && a instanceof Date ? `<c r="${i}" s="1"><v>${
|
|
1341
|
+
return o === "number" && typeof a == "number" ? `<c r="${i}"><v>${a}</v></c>` : (o === "date" || o === "datetime") && a instanceof Date ? `<c r="${i}" s="1"><v>${ne(a).toFixed(6)}</v></c>` : o === "boolean" ? `<c r="${i}" t="b"><v>${+!!a}</v></c>` : `<c r="${i}" t="str"><v>${N(String(a))}</v></c>`;
|
|
1300
1342
|
});
|
|
1301
1343
|
n.push(`<row r="${r + 2}">${i.join("")}</row>`);
|
|
1302
1344
|
}), `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
@@ -1304,55 +1346,55 @@ function se(e, t) {
|
|
|
1304
1346
|
<sheetData>${n.join("")}</sheetData>
|
|
1305
1347
|
</worksheet>`;
|
|
1306
1348
|
}
|
|
1307
|
-
function
|
|
1308
|
-
let n =
|
|
1309
|
-
return
|
|
1349
|
+
function le(e, t) {
|
|
1350
|
+
let n = ce(e, t);
|
|
1351
|
+
return te([
|
|
1310
1352
|
{
|
|
1311
1353
|
name: "[Content_Types].xml",
|
|
1312
|
-
data:
|
|
1354
|
+
data: j.encode(re)
|
|
1313
1355
|
},
|
|
1314
1356
|
{
|
|
1315
1357
|
name: "_rels/.rels",
|
|
1316
|
-
data:
|
|
1358
|
+
data: j.encode(ie)
|
|
1317
1359
|
},
|
|
1318
1360
|
{
|
|
1319
1361
|
name: "xl/workbook.xml",
|
|
1320
|
-
data:
|
|
1362
|
+
data: j.encode(ae)
|
|
1321
1363
|
},
|
|
1322
1364
|
{
|
|
1323
1365
|
name: "xl/_rels/workbook.xml.rels",
|
|
1324
|
-
data:
|
|
1366
|
+
data: j.encode(oe)
|
|
1325
1367
|
},
|
|
1326
1368
|
{
|
|
1327
1369
|
name: "xl/worksheets/sheet1.xml",
|
|
1328
|
-
data:
|
|
1370
|
+
data: j.encode(n)
|
|
1329
1371
|
},
|
|
1330
1372
|
{
|
|
1331
1373
|
name: "xl/styles.xml",
|
|
1332
|
-
data:
|
|
1374
|
+
data: j.encode(se)
|
|
1333
1375
|
}
|
|
1334
1376
|
]);
|
|
1335
1377
|
}
|
|
1336
1378
|
//#endregion
|
|
1337
1379
|
//#region src/export/export.ts
|
|
1338
|
-
function
|
|
1380
|
+
function I(e, t, n) {
|
|
1339
1381
|
switch (n) {
|
|
1340
|
-
case "csv": return
|
|
1341
|
-
case "tsv": return
|
|
1342
|
-
case "json": return
|
|
1343
|
-
case "xlsx": return
|
|
1382
|
+
case "csv": return R(e, t, ",");
|
|
1383
|
+
case "tsv": return R(e, t, " ");
|
|
1384
|
+
case "json": return B(e, t);
|
|
1385
|
+
case "xlsx": return le(e, t);
|
|
1344
1386
|
}
|
|
1345
1387
|
}
|
|
1346
|
-
function
|
|
1388
|
+
function L(e, t) {
|
|
1347
1389
|
return e == null ? "" : (t.type === "date" || t.type === "datetime") && e instanceof Date ? e.toISOString() : String(e);
|
|
1348
1390
|
}
|
|
1349
|
-
function
|
|
1350
|
-
return [t.map((e) =>
|
|
1391
|
+
function R(e, t, n) {
|
|
1392
|
+
return [t.map((e) => z(e.header, n)).join(n), ...e.map((e) => t.map((t) => z(L(e[t.key], t), n)).join(n))].join("\n");
|
|
1351
1393
|
}
|
|
1352
|
-
function
|
|
1394
|
+
function z(e, t) {
|
|
1353
1395
|
return e.includes(t) || e.includes("\"") || e.includes("\n") || e.includes("\r") ? "\"" + e.replace(/"/g, "\"\"") + "\"" : e;
|
|
1354
1396
|
}
|
|
1355
|
-
function
|
|
1397
|
+
function B(e, t) {
|
|
1356
1398
|
let n = e.map((e) => {
|
|
1357
1399
|
let n = {};
|
|
1358
1400
|
for (let r of t) {
|
|
@@ -1363,35 +1405,35 @@ function V(e, t) {
|
|
|
1363
1405
|
});
|
|
1364
1406
|
return JSON.stringify(n, null, 2);
|
|
1365
1407
|
}
|
|
1366
|
-
function
|
|
1408
|
+
function V(e, t, n) {
|
|
1367
1409
|
let r = new Blob([e], { type: n }), i = URL.createObjectURL(r), a = document.createElement("a");
|
|
1368
1410
|
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);
|
|
1369
1411
|
}
|
|
1370
|
-
var
|
|
1412
|
+
var H = {
|
|
1371
1413
|
csv: "text/csv;charset=utf-8",
|
|
1372
1414
|
tsv: "text/tab-separated-values;charset=utf-8",
|
|
1373
1415
|
json: "application/json;charset=utf-8",
|
|
1374
1416
|
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
1375
|
-
},
|
|
1417
|
+
}, U = {
|
|
1376
1418
|
csv: ".csv",
|
|
1377
1419
|
tsv: ".tsv",
|
|
1378
1420
|
json: ".json",
|
|
1379
1421
|
xlsx: ".xlsx"
|
|
1380
1422
|
};
|
|
1381
|
-
function le(e) {
|
|
1382
|
-
return U[e];
|
|
1383
|
-
}
|
|
1384
1423
|
function ue(e) {
|
|
1385
|
-
return
|
|
1424
|
+
return H[e];
|
|
1425
|
+
}
|
|
1426
|
+
function de(e) {
|
|
1427
|
+
return U[e];
|
|
1386
1428
|
}
|
|
1387
1429
|
//#endregion
|
|
1388
1430
|
//#region src/export/xlsx-reader.ts
|
|
1389
|
-
function
|
|
1431
|
+
function fe(e) {
|
|
1390
1432
|
for (let t = e.byteLength - 22; t >= 0; t--) if (e.getUint32(t, !0) === 101010256) return t;
|
|
1391
1433
|
throw Error("XLSX: End of central directory not found — not a valid ZIP file");
|
|
1392
1434
|
}
|
|
1393
|
-
function
|
|
1394
|
-
let t =
|
|
1435
|
+
function pe(e) {
|
|
1436
|
+
let t = fe(e), n = e.getUint32(t + 16, !0), r = e.getUint32(t + 12, !0), i = new TextDecoder("utf-8"), a = [], o = n, s = n + r;
|
|
1395
1437
|
for (; o < s && e.getUint32(o, !0) === 33639248;) {
|
|
1396
1438
|
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));
|
|
1397
1439
|
a.push({
|
|
@@ -1404,7 +1446,7 @@ function fe(e) {
|
|
|
1404
1446
|
}
|
|
1405
1447
|
return a;
|
|
1406
1448
|
}
|
|
1407
|
-
async function
|
|
1449
|
+
async function me(e) {
|
|
1408
1450
|
let t = new DecompressionStream("deflate-raw"), n = t.writable.getWriter(), r = t.readable.getReader();
|
|
1409
1451
|
n.write(e), n.close();
|
|
1410
1452
|
let i = [];
|
|
@@ -1417,40 +1459,40 @@ async function pe(e) {
|
|
|
1417
1459
|
for (let e of i) o.set(e, s), s += e.length;
|
|
1418
1460
|
return o;
|
|
1419
1461
|
}
|
|
1420
|
-
async function
|
|
1462
|
+
async function he(e, t) {
|
|
1421
1463
|
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);
|
|
1422
1464
|
if (t.compressionMethod === 0) return o.slice();
|
|
1423
|
-
if (t.compressionMethod === 8) return
|
|
1465
|
+
if (t.compressionMethod === 8) return me(o);
|
|
1424
1466
|
throw Error(`XLSX: Unsupported compression method ${t.compressionMethod}`);
|
|
1425
1467
|
}
|
|
1426
|
-
async function
|
|
1427
|
-
let t = new DataView(e), n =
|
|
1428
|
-
for (let e of n) e.compressedSize === 0 && e.name.endsWith("/") || r.set(e.name, await
|
|
1468
|
+
async function ge(e) {
|
|
1469
|
+
let t = new DataView(e), n = pe(t), r = /* @__PURE__ */ new Map();
|
|
1470
|
+
for (let e of n) e.compressedSize === 0 && e.name.endsWith("/") || r.set(e.name, await he(t, e));
|
|
1429
1471
|
return r;
|
|
1430
1472
|
}
|
|
1431
|
-
var
|
|
1432
|
-
function
|
|
1433
|
-
let t =
|
|
1473
|
+
var _e = new TextDecoder("utf-8");
|
|
1474
|
+
function W(e) {
|
|
1475
|
+
let t = _e.decode(e);
|
|
1434
1476
|
return new DOMParser().parseFromString(t, "application/xml");
|
|
1435
1477
|
}
|
|
1436
|
-
function
|
|
1478
|
+
function ve(e) {
|
|
1437
1479
|
if (!e) return [];
|
|
1438
|
-
let t =
|
|
1480
|
+
let t = W(e);
|
|
1439
1481
|
return Array.from(t.querySelectorAll("si")).map((e) => Array.from(e.querySelectorAll("t")).map((e) => e.textContent ?? "").join(""));
|
|
1440
1482
|
}
|
|
1441
|
-
function
|
|
1483
|
+
function ye(e) {
|
|
1442
1484
|
let t = (e - 1) * 864e5 + Date.UTC(1899, 11, 30);
|
|
1443
1485
|
return new Date(t).toISOString().slice(0, 10);
|
|
1444
1486
|
}
|
|
1445
|
-
function
|
|
1446
|
-
let n =
|
|
1487
|
+
function be(e, t) {
|
|
1488
|
+
let n = W(e).querySelectorAll("row");
|
|
1447
1489
|
if (n.length === 0) return [];
|
|
1448
1490
|
let r = 0, i = /* @__PURE__ */ new Map();
|
|
1449
1491
|
for (let e of n) {
|
|
1450
1492
|
let n = parseInt(e.getAttribute("r") ?? "1", 10) - 1, a = /* @__PURE__ */ new Map();
|
|
1451
1493
|
i.set(n, a);
|
|
1452
1494
|
for (let n of e.querySelectorAll("c")) {
|
|
1453
|
-
let e =
|
|
1495
|
+
let e = xe(n.getAttribute("r") ?? "");
|
|
1454
1496
|
e > r && (r = e);
|
|
1455
1497
|
let i = n.getAttribute("t") ?? "", o = n.getAttribute("s") ?? "", s = n.querySelector("v")?.textContent ?? "", c = n.querySelector("is > t")?.textContent ?? null, l;
|
|
1456
1498
|
if (i === "s") l = t[parseInt(s, 10)] ?? "";
|
|
@@ -1459,7 +1501,7 @@ function ye(e, t) {
|
|
|
1459
1501
|
else if (s === "") l = "";
|
|
1460
1502
|
else {
|
|
1461
1503
|
let e = parseFloat(s);
|
|
1462
|
-
l = !isNaN(e) && o !== "" && parseInt(o, 10) > 0 && e >= 1 && e <= 73050 ?
|
|
1504
|
+
l = !isNaN(e) && o !== "" && parseInt(o, 10) > 0 && e >= 1 && e <= 73050 ? ye(e) : s;
|
|
1463
1505
|
}
|
|
1464
1506
|
a.set(e, l);
|
|
1465
1507
|
}
|
|
@@ -1473,13 +1515,13 @@ function ye(e, t) {
|
|
|
1473
1515
|
}
|
|
1474
1516
|
return o;
|
|
1475
1517
|
}
|
|
1476
|
-
function
|
|
1518
|
+
function xe(e) {
|
|
1477
1519
|
let t = 0, n = 0;
|
|
1478
1520
|
for (; n < e.length && e[n] >= "A" && e[n] <= "Z";) t = t * 26 + (e.charCodeAt(n) - 64), n++;
|
|
1479
1521
|
return t - 1;
|
|
1480
1522
|
}
|
|
1481
|
-
async function
|
|
1482
|
-
let t = await
|
|
1523
|
+
async function G(e) {
|
|
1524
|
+
let t = await ge(e), n = ve(t.get("xl/sharedStrings.xml")), r = t.get("xl/worksheets/sheet1.xml");
|
|
1483
1525
|
if (!r) {
|
|
1484
1526
|
for (let [e, n] of t) if (e.toLowerCase().includes("worksheets/sheet")) {
|
|
1485
1527
|
r = n;
|
|
@@ -1487,7 +1529,7 @@ async function K(e) {
|
|
|
1487
1529
|
}
|
|
1488
1530
|
}
|
|
1489
1531
|
if (!r) throw Error("XLSX: No worksheet found");
|
|
1490
|
-
let i =
|
|
1532
|
+
let i = be(r, n);
|
|
1491
1533
|
return i.length === 0 ? {
|
|
1492
1534
|
headers: [],
|
|
1493
1535
|
rows: []
|
|
@@ -1498,7 +1540,7 @@ async function K(e) {
|
|
|
1498
1540
|
}
|
|
1499
1541
|
//#endregion
|
|
1500
1542
|
//#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorate.js
|
|
1501
|
-
function
|
|
1543
|
+
function K(e, t, n, r) {
|
|
1502
1544
|
var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
|
|
1503
1545
|
if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
|
|
1504
1546
|
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);
|
|
@@ -1506,14 +1548,14 @@ function q(e, t, n, r) {
|
|
|
1506
1548
|
}
|
|
1507
1549
|
//#endregion
|
|
1508
1550
|
//#region src/flex-table.ts
|
|
1509
|
-
var
|
|
1551
|
+
var q = {
|
|
1510
1552
|
eq: "=",
|
|
1511
1553
|
neq: "≠",
|
|
1512
1554
|
gt: ">",
|
|
1513
1555
|
lt: "<",
|
|
1514
1556
|
gte: "≥",
|
|
1515
1557
|
lte: "≤"
|
|
1516
|
-
},
|
|
1558
|
+
}, J = 120, Y = 40, X = 20, Z = 32, Q = 5, Se = 200, $ = class extends n {
|
|
1517
1559
|
constructor(...e) {
|
|
1518
1560
|
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.emptyMessage = "No data", this.noMatchingMessage = "No matching data", this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this.stylesheets = [], this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._baseStyleSheets = [], this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new x(), this._editing = new S(), this._rowSelection = new C(), this._undo = new E(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._lastCheckboxRowIndex = null, this._checkboxShiftPending = !1, 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._wasHeaderDrag = !1, this._wasRowDrag = !1, 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._warnedUnconstrained = !1, this._onCommentPopupOutsideClick = () => {
|
|
1519
1561
|
this._commitCommentPopup();
|
|
@@ -1880,12 +1922,12 @@ var J = {
|
|
|
1880
1922
|
if (!t) return "";
|
|
1881
1923
|
let n = this.visibleColumns.slice(t.startCol, t.endCol + 1), r = [];
|
|
1882
1924
|
for (let e = t.startRow; e <= t.endRow; e++) r.push(this.data[this._toDataIndex(e)]);
|
|
1883
|
-
return
|
|
1925
|
+
return I(r, n, e);
|
|
1884
1926
|
}
|
|
1885
|
-
return
|
|
1927
|
+
return I(this._sortedIndices.map((e) => this.data[e]), this.visibleColumns, e);
|
|
1886
1928
|
}
|
|
1887
1929
|
exportToFile(e, t) {
|
|
1888
|
-
|
|
1930
|
+
V(this.exportToString(e), t ?? `export${de(e)}`, ue(e));
|
|
1889
1931
|
}
|
|
1890
1932
|
setComment(e, t, n) {
|
|
1891
1933
|
let r = this.getComment(e, t);
|
|
@@ -1942,7 +1984,7 @@ var J = {
|
|
|
1942
1984
|
async importFromFile(e) {
|
|
1943
1985
|
let t = e.name.toLowerCase();
|
|
1944
1986
|
if (t.endsWith(".xlsx")) {
|
|
1945
|
-
let t = await
|
|
1987
|
+
let t = await G(await e.arrayBuffer());
|
|
1946
1988
|
this._applyImportedSheet(t);
|
|
1947
1989
|
} else if (t.endsWith(".csv") || t.endsWith(".tsv")) {
|
|
1948
1990
|
let t = O(await e.text());
|
|
@@ -2034,8 +2076,8 @@ var J = {
|
|
|
2034
2076
|
return this._columnWidths.get(e);
|
|
2035
2077
|
}
|
|
2036
2078
|
_getColWidth(e) {
|
|
2037
|
-
let t = this._columnWidths.get(e.key) ?? e.width ??
|
|
2038
|
-
return Math.max(t, e.minWidth ??
|
|
2079
|
+
let t = this._columnWidths.get(e.key) ?? e.width ?? J;
|
|
2080
|
+
return Math.max(t, e.minWidth ?? Y);
|
|
2039
2081
|
}
|
|
2040
2082
|
get headerHeight() {
|
|
2041
2083
|
return this.rowHeight + 8;
|
|
@@ -2115,7 +2157,7 @@ var J = {
|
|
|
2115
2157
|
(!(e.size <= 2 && !e.has("data") && !e.has("columns") && !e.has("_openFilterKey") && !e.has("_editingCell") && !e.has("_activeCell") && (e.has("_scrollTop") || e.has("_scrollLeft"))) || this._viewDirty) && (this._recomputeView(), this._viewDirty = !1), this._updateColOffsets(), this._selection.setDimensions(this._visibleRowCount, this.visibleColumns.length), this._rowSelection.setRowCount(this._visibleRowCount);
|
|
2116
2158
|
}
|
|
2117
2159
|
updated(e) {
|
|
2118
|
-
this._focusEditor(), this._adjustFilterDropdown(), e.has("stylesheets") && this._syncStylesheets();
|
|
2160
|
+
this._focusEditor(), this._adjustFilterDropdown(), e.has("_headerMenu") && this._keepMenuInView(".ft-header-menu"), e.has("_bodyContextMenu") && this._bodyContextMenu && (this._keepMenuInView(".ft-body-context-menu"), this.shadowRoot?.querySelector(".ft-body-context-menu [role=\"menuitem\"]")?.focus()), e.has("stylesheets") && this._syncStylesheets();
|
|
2119
2161
|
let t = String(this._visibleRowCount), n = String(this.visibleColumns.length);
|
|
2120
2162
|
if (this.getAttribute("aria-rowcount") !== t && this.setAttribute("aria-rowcount", t), this.getAttribute("aria-colcount") !== n && this.setAttribute("aria-colcount", n), this.loading ? this.getAttribute("aria-busy") !== "true" && this.setAttribute("aria-busy", "true") : this.hasAttribute("aria-busy") && this.removeAttribute("aria-busy"), e.has("_commentPopup") && this._commentPopup) {
|
|
2121
2163
|
let e = this.shadowRoot?.querySelector(".ft-comment-popup textarea");
|
|
@@ -2157,7 +2199,7 @@ var J = {
|
|
|
2157
2199
|
e !== this._viewportHeight && (this._viewportHeight = e), t !== this._viewportWidth && (this._viewportWidth = t), this._warnIfUnconstrained();
|
|
2158
2200
|
}
|
|
2159
2201
|
_warnIfUnconstrained() {
|
|
2160
|
-
this._warnedUnconstrained;
|
|
2202
|
+
this._warnedUnconstrained || process.env.NODE_ENV === "production" || this._visibleRowCount < Se || this.scrollHeight > this.clientHeight + 1 || (this._warnedUnconstrained = !0, console.warn(`[@iyulab/flex-table] ${this._visibleRowCount} rows but the host has no height constraint — it grew to fit every row, so virtual scrolling is off and all rows are rendered. Give the host a height (e.g. height: 400px, or flex: 1 1 auto with min-height: 0 inside a sized parent) so it becomes the scroll container.`));
|
|
2161
2203
|
}
|
|
2162
2204
|
_onScroll() {
|
|
2163
2205
|
this._scrollTop = this.scrollTop, this._scrollLeft = this.scrollLeft;
|
|
@@ -2481,17 +2523,21 @@ var J = {
|
|
|
2481
2523
|
if (!e.altKey || !this._activeCell || e.key !== "ArrowLeft" && e.key !== "ArrowRight") return !1;
|
|
2482
2524
|
e.preventDefault();
|
|
2483
2525
|
let n = t[this._activeCell.col];
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2526
|
+
return n && this._resizeColumnBy(n.key, e.key === "ArrowRight" ? X : -20), !0;
|
|
2527
|
+
}
|
|
2528
|
+
_resizeColumnBy(e, t) {
|
|
2529
|
+
let n = this.visibleColumns.findIndex((t) => t.key === e), r = this.visibleColumns[n];
|
|
2530
|
+
if (!r) return;
|
|
2531
|
+
let i = this._columnWidths.get(r.key) ?? r.width ?? J, a = r.minWidth ?? Y, o = Math.max(a, i + t);
|
|
2532
|
+
this._columnWidths.set(r.key, o), this.requestUpdate(), this.dispatchEvent(new CustomEvent("column-resize", {
|
|
2487
2533
|
detail: {
|
|
2488
|
-
key:
|
|
2534
|
+
key: r.key,
|
|
2489
2535
|
width: o,
|
|
2490
|
-
colIndex:
|
|
2536
|
+
colIndex: n
|
|
2491
2537
|
},
|
|
2492
2538
|
bubbles: !0,
|
|
2493
2539
|
composed: !0
|
|
2494
|
-
}))
|
|
2540
|
+
}));
|
|
2495
2541
|
}
|
|
2496
2542
|
_handleNavigation(e) {
|
|
2497
2543
|
switch (e.key) {
|
|
@@ -2827,34 +2873,39 @@ var J = {
|
|
|
2827
2873
|
<span>${e.header}</span>
|
|
2828
2874
|
${r ? i`<span class="ft-sort-indicator">${r.direction === "asc" ? "▲" : "▼"}</span>` : ""}
|
|
2829
2875
|
${o >= 0 ? i`<span class="ft-sort-order">${o + 1}</span>` : ""}
|
|
2830
|
-
${
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2876
|
+
<button class="ft-column-menu-btn ${s ? "ft-filter-active" : ""}"
|
|
2877
|
+
type="button"
|
|
2878
|
+
title="Column menu"
|
|
2879
|
+
aria-label=${`Column menu: ${e.header}`}
|
|
2880
|
+
aria-haspopup="menu"
|
|
2881
|
+
aria-expanded=${this._headerMenu?.key === e.key ? "true" : "false"}
|
|
2882
|
+
@mousedown=${(e) => e.stopPropagation()}
|
|
2883
|
+
@click=${(t) => this._onColumnMenuBtnClick(t, e)}>
|
|
2884
|
+
\u22EE
|
|
2885
|
+
</button>
|
|
2839
2886
|
<div class="ft-resize-handle"
|
|
2840
2887
|
@mousedown=${(e) => {
|
|
2841
2888
|
e.stopPropagation(), this._wasHeaderDrag = !0, this._onResizeStart(e, t);
|
|
2842
2889
|
}}
|
|
2843
|
-
@dblclick=${(
|
|
2890
|
+
@dblclick=${(t) => {
|
|
2891
|
+
t.preventDefault(), t.stopPropagation(), this._autoFitColumn(e.key);
|
|
2892
|
+
}}></div>
|
|
2844
2893
|
</div>
|
|
2845
2894
|
${this._openFilterKey === e.key ? this._renderFilterDropdown(e) : ""}
|
|
2846
2895
|
`;
|
|
2847
2896
|
}
|
|
2897
|
+
_hiddenNeighbors(e) {
|
|
2898
|
+
let t = this.columns, n = t.findIndex((t) => t.key === e.key), r = [];
|
|
2899
|
+
for (let e = n - 1; e >= 0 && t[e].hidden; e--) r.push(t[e]);
|
|
2900
|
+
for (let e = n + 1; e < t.length && t[e].hidden; e++) r.push(t[e]);
|
|
2901
|
+
return r;
|
|
2902
|
+
}
|
|
2848
2903
|
_onHeaderContextMenu(e, t) {
|
|
2849
|
-
e.preventDefault()
|
|
2850
|
-
let n = this.columns, r = n.findIndex((e) => e.key === t.key), i = [];
|
|
2851
|
-
for (let e = r - 1; e >= 0 && n[e].hidden; e--) i.push(n[e]);
|
|
2852
|
-
for (let e = r + 1; e < n.length && n[e].hidden; e++) i.push(n[e]);
|
|
2853
|
-
this._headerMenu = {
|
|
2904
|
+
e.preventDefault(), this._openFilterKey = null, this._headerMenu = {
|
|
2854
2905
|
key: t.key,
|
|
2855
2906
|
x: e.clientX,
|
|
2856
2907
|
y: e.clientY,
|
|
2857
|
-
hiddenNeighbors:
|
|
2908
|
+
hiddenNeighbors: this._hiddenNeighbors(t)
|
|
2858
2909
|
}, this.dispatchEvent(new CustomEvent("header-context-menu", {
|
|
2859
2910
|
detail: {
|
|
2860
2911
|
key: t.key,
|
|
@@ -2868,118 +2919,139 @@ var J = {
|
|
|
2868
2919
|
document.addEventListener("click", this._onDocumentClick, { once: !0 });
|
|
2869
2920
|
});
|
|
2870
2921
|
}
|
|
2922
|
+
_onColumnMenuBtnClick(e, t) {
|
|
2923
|
+
if (e.preventDefault(), e.stopPropagation(), this._headerMenu?.key === t.key) {
|
|
2924
|
+
this._headerMenu = null;
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
let n = e.currentTarget.getBoundingClientRect();
|
|
2928
|
+
this._openFilterKey = null, this._headerMenu = {
|
|
2929
|
+
key: t.key,
|
|
2930
|
+
x: n.left,
|
|
2931
|
+
y: n.bottom,
|
|
2932
|
+
hiddenNeighbors: this._hiddenNeighbors(t)
|
|
2933
|
+
}, requestAnimationFrame(() => {
|
|
2934
|
+
document.addEventListener("click", this._onDocumentClick, { once: !0 });
|
|
2935
|
+
}), this.updateComplete.then(() => this._menuItems()[0]?.focus());
|
|
2936
|
+
}
|
|
2937
|
+
_menuItems() {
|
|
2938
|
+
return Array.from(this.shadowRoot?.querySelectorAll(".ft-header-menu [role=\"menuitem\"]") ?? []);
|
|
2939
|
+
}
|
|
2940
|
+
_columnMenuButton(e) {
|
|
2941
|
+
let t = this.visibleColumns.findIndex((t) => t.key === e);
|
|
2942
|
+
return this.shadowRoot?.querySelector(`.ft-header-cell[data-col-index="${t}"] .ft-column-menu-btn`) ?? null;
|
|
2943
|
+
}
|
|
2944
|
+
_closeHeaderMenu(e) {
|
|
2945
|
+
let t = this._headerMenu?.key;
|
|
2946
|
+
this._headerMenu = null, e && t && this._columnMenuButton(t)?.focus();
|
|
2947
|
+
}
|
|
2948
|
+
_onHeaderMenuKeydown(e) {
|
|
2949
|
+
this._onMenuKeydown(e, ".ft-header-menu", (e) => this._closeHeaderMenu(e));
|
|
2950
|
+
}
|
|
2951
|
+
_onMenuKeydown(e, t, n) {
|
|
2952
|
+
let r = Array.from(this.shadowRoot?.querySelectorAll(`${t} [role="menuitem"]`) ?? []), i = r.indexOf(e.target), a = -1;
|
|
2953
|
+
switch (e.key) {
|
|
2954
|
+
case "ArrowDown":
|
|
2955
|
+
a = i < 0 ? 0 : (i + 1) % r.length;
|
|
2956
|
+
break;
|
|
2957
|
+
case "ArrowUp":
|
|
2958
|
+
a = i <= 0 ? r.length - 1 : i - 1;
|
|
2959
|
+
break;
|
|
2960
|
+
case "Home":
|
|
2961
|
+
a = 0;
|
|
2962
|
+
break;
|
|
2963
|
+
case "End":
|
|
2964
|
+
a = r.length - 1;
|
|
2965
|
+
break;
|
|
2966
|
+
case "Escape":
|
|
2967
|
+
e.preventDefault(), e.stopPropagation(), n(!0);
|
|
2968
|
+
return;
|
|
2969
|
+
case "Tab":
|
|
2970
|
+
n(!1);
|
|
2971
|
+
return;
|
|
2972
|
+
default: return;
|
|
2973
|
+
}
|
|
2974
|
+
e.preventDefault(), e.stopPropagation(), r[a]?.focus();
|
|
2975
|
+
}
|
|
2976
|
+
_openFilterFromMenu(e) {
|
|
2977
|
+
this._headerMenu = null, this._openFilterKey = e, requestAnimationFrame(() => {
|
|
2978
|
+
document.addEventListener("click", this._onDocumentClick, { once: !0 });
|
|
2979
|
+
}), this.updateComplete.then(() => {
|
|
2980
|
+
this.shadowRoot?.querySelector(".ft-filter-dropdown input, .ft-filter-dropdown select")?.focus();
|
|
2981
|
+
});
|
|
2982
|
+
}
|
|
2871
2983
|
_showHiddenBefore(e) {
|
|
2872
2984
|
let t = this.columns, n = t.findIndex((t) => t.key === e.key);
|
|
2873
2985
|
for (let e = n - 1; e >= 0 && t[e].hidden; e--) this._setColumnHidden(t[e].key, !1);
|
|
2874
2986
|
}
|
|
2875
2987
|
_renderHeaderContextMenu() {
|
|
2876
2988
|
if (!this._headerMenu) return a;
|
|
2877
|
-
let { key: e, x: t, y: n, hiddenNeighbors: r } = this._headerMenu;
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
${
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
`
|
|
2989
|
+
let { key: e, x: t, y: n, hiddenNeighbors: r } = this._headerMenu, o = this.columns.find((t) => t.key === e);
|
|
2990
|
+
if (!o) return a;
|
|
2991
|
+
let s = this._filters.some((t) => t.key === e), c = (e, t, n, r = !1) => i`
|
|
2992
|
+
<button type="button" role="menuitem" class="ft-header-menu-item" data-action=${e}
|
|
2993
|
+
@click=${(e) => {
|
|
2994
|
+
e.stopPropagation(), r || (this._headerMenu = null), n();
|
|
2995
|
+
}}>${t}</button>
|
|
2996
|
+
`;
|
|
2997
|
+
return i`
|
|
2998
|
+
<div class="ft-header-menu" role="menu" aria-label=${`${o.header} column`}
|
|
2999
|
+
style="position: fixed; left: ${t}px; top: ${n}px; z-index: 200;"
|
|
3000
|
+
@mousedown=${(e) => e.stopPropagation()}
|
|
3001
|
+
@keydown=${(e) => this._onHeaderMenuKeydown(e)}>
|
|
3002
|
+
${o.sortable === !1 ? a : i`
|
|
3003
|
+
${c("sort-asc", "Sort ascending", () => this._applySortFromMenu(e, "asc"))}
|
|
3004
|
+
${c("sort-desc", "Sort descending", () => this._applySortFromMenu(e, "desc"))}
|
|
3005
|
+
<div class="ft-header-menu-separator" role="separator"></div>
|
|
3006
|
+
`}
|
|
3007
|
+
${this.showFilters ? i`
|
|
3008
|
+
${c("filter", "Filter…", () => this._openFilterFromMenu(e))}
|
|
3009
|
+
${s ? c("clear-filter", "Clear filter", () => this._clearColumnFilter(e)) : a}
|
|
3010
|
+
<div class="ft-header-menu-separator" role="separator"></div>
|
|
3011
|
+
` : a}
|
|
3012
|
+
${c("hide", "Hide column", () => this._setColumnHidden(e, !0))}
|
|
3013
|
+
${r.map((e) => c("show", i`Show: ${e.header}`, () => this._setColumnHidden(e.key, !1)))}
|
|
3014
|
+
<div class="ft-header-menu-separator" role="separator"></div>
|
|
3015
|
+
${c("autofit", "Auto-fit width", () => this._autoFitColumn(e))}
|
|
3016
|
+
${c("wider", "Wider", () => this._resizeColumnBy(e, X), !0)}
|
|
3017
|
+
${c("narrower", "Narrower", () => this._resizeColumnBy(e, -20), !0)}
|
|
2895
3018
|
</div>
|
|
2896
|
-
|
|
3019
|
+
`;
|
|
2897
3020
|
}
|
|
2898
3021
|
_renderBodyContextMenu() {
|
|
2899
3022
|
if (!this._bodyContextMenu) return a;
|
|
2900
3023
|
let { colIndex: e, dataIndex: t, x: n, y: r } = this._bodyContextMenu, o = this.visibleColumns[e];
|
|
2901
3024
|
if (!o) return a;
|
|
2902
|
-
let s = this.data[t]?.[o.key], c = this._filters.some((e) => e.key === o.key), l = () => {
|
|
2903
|
-
this._bodyContextMenu = null;
|
|
2904
|
-
}, u =
|
|
3025
|
+
let s = this.data[t]?.[o.key], c = this._filters.some((e) => e.key === o.key), l = (e = !1) => {
|
|
3026
|
+
this._bodyContextMenu = null, e && this.focus();
|
|
3027
|
+
}, u = !!this.getComment(t, o.key), d = (e, t, n = !1) => i`
|
|
3028
|
+
<button type="button" role="menuitem"
|
|
3029
|
+
class="ft-context-menu-item ${n ? "ft-context-menu-danger" : ""}"
|
|
3030
|
+
@click=${() => {
|
|
3031
|
+
t(), l();
|
|
3032
|
+
}}>${e}</button>
|
|
3033
|
+
`, f = i`<div class="ft-context-menu-separator" role="separator"></div>`;
|
|
2905
3034
|
return i`
|
|
2906
|
-
<div class="ft-body-context-menu"
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
</div>
|
|
2927
|
-
<div class="ft-context-menu-item ft-context-menu-danger"
|
|
2928
|
-
@click=${() => {
|
|
2929
|
-
this.deleteRows([t]), l();
|
|
2930
|
-
}}>
|
|
2931
|
-
Delete row
|
|
2932
|
-
</div>
|
|
2933
|
-
<div class="ft-context-menu-separator"></div>
|
|
2934
|
-
<div class="ft-context-menu-item"
|
|
2935
|
-
@click=${() => {
|
|
2936
|
-
this._setColumnHidden(o.key, !0), l();
|
|
2937
|
-
}}>
|
|
2938
|
-
Hide column
|
|
2939
|
-
</div>
|
|
2940
|
-
<div class="ft-context-menu-separator"></div>
|
|
2941
|
-
<div class="ft-context-menu-item"
|
|
2942
|
-
@click=${() => {
|
|
2943
|
-
this._applySortFromMenu(o.key, "asc"), l();
|
|
2944
|
-
}}>
|
|
2945
|
-
Sort ascending ↑
|
|
2946
|
-
</div>
|
|
2947
|
-
<div class="ft-context-menu-item"
|
|
2948
|
-
@click=${() => {
|
|
2949
|
-
this._applySortFromMenu(o.key, "desc"), l();
|
|
2950
|
-
}}>
|
|
2951
|
-
Sort descending ↓
|
|
2952
|
-
</div>
|
|
2953
|
-
<div class="ft-context-menu-separator"></div>
|
|
2954
|
-
<div class="ft-context-menu-item"
|
|
2955
|
-
@click=${() => {
|
|
2956
|
-
this.setFilter(o.key, (e) => e === s), l();
|
|
2957
|
-
}}>
|
|
2958
|
-
Filter by this value
|
|
2959
|
-
</div>
|
|
2960
|
-
${c ? i`
|
|
2961
|
-
<div class="ft-context-menu-item"
|
|
2962
|
-
@click=${() => {
|
|
2963
|
-
this.removeFilter(o.key), l();
|
|
2964
|
-
}}>
|
|
2965
|
-
Clear filter
|
|
2966
|
-
</div>
|
|
2967
|
-
` : ""}
|
|
2968
|
-
<div class="ft-context-menu-separator"></div>
|
|
2969
|
-
<div class="ft-context-menu-item"
|
|
2970
|
-
@click=${() => {
|
|
2971
|
-
this._openCommentPopup(t, o.key, n, r), l();
|
|
2972
|
-
}}>
|
|
2973
|
-
${this.getComment(t, o.key) ? "Edit Comment" : "Add Comment"}
|
|
2974
|
-
</div>
|
|
2975
|
-
${this.getComment(t, o.key) ? i`
|
|
2976
|
-
<div class="ft-context-menu-item ft-context-menu-danger"
|
|
2977
|
-
@click=${() => {
|
|
2978
|
-
this.setComment(t, o.key, null), l();
|
|
2979
|
-
}}>
|
|
2980
|
-
Delete Comment
|
|
2981
|
-
</div>
|
|
2982
|
-
` : ""}
|
|
3035
|
+
<div class="ft-body-context-menu" role="menu" aria-label="Cell actions"
|
|
3036
|
+
style="position: fixed; left: ${n}px; top: ${r}px; z-index: 200;"
|
|
3037
|
+
@mousedown=${(e) => e.stopPropagation()}
|
|
3038
|
+
@keydown=${(e) => this._onMenuKeydown(e, ".ft-body-context-menu", l)}>
|
|
3039
|
+
${d("Copy", () => this._handleCopy(!1))}
|
|
3040
|
+
${f}
|
|
3041
|
+
${d("Insert row above", () => this.addRow(void 0, t))}
|
|
3042
|
+
${d("Insert row below", () => this.addRow(void 0, t + 1))}
|
|
3043
|
+
${d("Delete row", () => this.deleteRows([t]), !0)}
|
|
3044
|
+
${f}
|
|
3045
|
+
${d("Hide column", () => this._setColumnHidden(o.key, !0))}
|
|
3046
|
+
${f}
|
|
3047
|
+
${d("Sort ascending ↑", () => this._applySortFromMenu(o.key, "asc"))}
|
|
3048
|
+
${d("Sort descending ↓", () => this._applySortFromMenu(o.key, "desc"))}
|
|
3049
|
+
${f}
|
|
3050
|
+
${d("Filter by this value", () => this.setFilter(o.key, (e) => e === s))}
|
|
3051
|
+
${c ? d("Clear filter", () => this.removeFilter(o.key)) : a}
|
|
3052
|
+
${f}
|
|
3053
|
+
${d(u ? "Edit Comment" : "Add Comment", () => this._openCommentPopup(t, o.key, n, r))}
|
|
3054
|
+
${u ? d("Delete Comment", () => this.setComment(t, o.key, null), !0) : a}
|
|
2983
3055
|
</div>
|
|
2984
3056
|
`;
|
|
2985
3057
|
}
|
|
@@ -3022,15 +3094,22 @@ var J = {
|
|
|
3022
3094
|
</div>
|
|
3023
3095
|
`;
|
|
3024
3096
|
}
|
|
3025
|
-
_applySortFromMenu(e,
|
|
3026
|
-
this.columns.find((t) => t.key === e)
|
|
3097
|
+
_applySortFromMenu(e, t) {
|
|
3098
|
+
let n = this.columns.find((t) => t.key === e);
|
|
3099
|
+
n && n.sortable !== !1 && (this._sortCriteria = [{
|
|
3027
3100
|
key: e,
|
|
3028
|
-
direction:
|
|
3029
|
-
}], this.
|
|
3030
|
-
detail: {
|
|
3101
|
+
direction: t
|
|
3102
|
+
}], this.dataMode !== "server" && this._recomputeView(), this.requestUpdate(), this.dispatchEvent(new CustomEvent("sort-change", {
|
|
3103
|
+
detail: { criteria: [...this._sortCriteria] },
|
|
3031
3104
|
bubbles: !0,
|
|
3032
3105
|
composed: !0
|
|
3033
|
-
}))
|
|
3106
|
+
})));
|
|
3107
|
+
}
|
|
3108
|
+
_keepMenuInView(e) {
|
|
3109
|
+
let t = this.shadowRoot?.querySelector(e);
|
|
3110
|
+
if (!t) return;
|
|
3111
|
+
let n = t.getBoundingClientRect(), r = document.documentElement.clientWidth, i = document.documentElement.clientHeight;
|
|
3112
|
+
n.right > r - 4 && (t.style.left = `${Math.max(4, r - n.width - 4)}px`), n.bottom > i - 4 && (t.style.top = `${Math.max(4, i - n.height - 4)}px`);
|
|
3034
3113
|
}
|
|
3035
3114
|
_adjustFilterDropdown() {
|
|
3036
3115
|
if (!this._openFilterKey) return;
|
|
@@ -3040,13 +3119,6 @@ var J = {
|
|
|
3040
3119
|
let t = e.getBoundingClientRect(), n = window.innerHeight;
|
|
3041
3120
|
t.bottom > n && (e.style.top = "auto", e.style.bottom = "100%");
|
|
3042
3121
|
}
|
|
3043
|
-
_onFilterBtnClick(e, t) {
|
|
3044
|
-
e.preventDefault(), e.stopPropagation();
|
|
3045
|
-
let n = this._openFilterKey === t.key ? null : t.key;
|
|
3046
|
-
this._openFilterKey = n, n && requestAnimationFrame(() => {
|
|
3047
|
-
document.addEventListener("click", this._onDocumentClick, { once: !0 });
|
|
3048
|
-
});
|
|
3049
|
-
}
|
|
3050
3122
|
_renderFilterDropdown(e) {
|
|
3051
3123
|
let t = e.type ?? "text";
|
|
3052
3124
|
return i`
|
|
@@ -3229,7 +3301,7 @@ var J = {
|
|
|
3229
3301
|
@keydown=${(e) => {
|
|
3230
3302
|
e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
|
|
3231
3303
|
}}>
|
|
3232
|
-
${Object.keys(
|
|
3304
|
+
${Object.keys(q).map((e) => i`<option value=${e}>${q[e]}</option>`)}
|
|
3233
3305
|
</select>
|
|
3234
3306
|
<input class="ft-filter-input ft-num-cond-input" type="number" placeholder="Value"
|
|
3235
3307
|
.value=${n.value == null ? "" : String(n.value)}
|
|
@@ -3334,13 +3406,12 @@ var J = {
|
|
|
3334
3406
|
_clearColumnFilter(e) {
|
|
3335
3407
|
this.removeFilter(e), this._textFilterState.delete(e), this._numberFilterState.delete(e), this._dateFilterState.delete(e), this._emptyFilterState.delete(e), this._openFilterKey = null;
|
|
3336
3408
|
}
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
let n = this.visibleColumns[t];
|
|
3409
|
+
_autoFitColumn(e) {
|
|
3410
|
+
let t = this.visibleColumns.findIndex((t) => t.key === e), n = this.visibleColumns[t];
|
|
3340
3411
|
if (!n) return;
|
|
3341
3412
|
let r = this.shadowRoot?.querySelectorAll(`.ft-cell[data-col-index="${t}"]`), i = 0, a = this.shadowRoot?.querySelector(`.ft-header-cell[data-col-index="${t}"]`);
|
|
3342
3413
|
if (a && (i = Math.max(i, a.scrollWidth)), r) for (let e of r) i = Math.max(i, e.scrollWidth);
|
|
3343
|
-
let o = n.minWidth ??
|
|
3414
|
+
let o = n.minWidth ?? Y, s = Math.max(o, i + 8);
|
|
3344
3415
|
this._columnWidths.set(n.key, s), this.requestUpdate(), this.dispatchEvent(new CustomEvent("column-resize", {
|
|
3345
3416
|
detail: {
|
|
3346
3417
|
key: n.key,
|
|
@@ -3353,7 +3424,7 @@ var J = {
|
|
|
3353
3424
|
}
|
|
3354
3425
|
_onResizeStart(e, t) {
|
|
3355
3426
|
e.preventDefault(), e.stopPropagation();
|
|
3356
|
-
let n = this.visibleColumns[t], r = this._columnWidths.get(n.key) ?? n.width ??
|
|
3427
|
+
let n = this.visibleColumns[t], r = this._columnWidths.get(n.key) ?? n.width ?? J;
|
|
3357
3428
|
this._resizing = {
|
|
3358
3429
|
colIndex: t,
|
|
3359
3430
|
startX: e.clientX,
|
|
@@ -3361,13 +3432,13 @@ var J = {
|
|
|
3361
3432
|
};
|
|
3362
3433
|
let i = (e) => {
|
|
3363
3434
|
if (!this._resizing) return;
|
|
3364
|
-
let t = e.clientX - this._resizing.startX, r = n.minWidth ??
|
|
3435
|
+
let t = e.clientX - this._resizing.startX, r = n.minWidth ?? Y, i = Math.max(r, this._resizing.startWidth + t);
|
|
3365
3436
|
this._columnWidths.set(n.key, i), this.requestUpdate();
|
|
3366
3437
|
}, a = () => {
|
|
3367
3438
|
document.removeEventListener("mousemove", i), document.removeEventListener("mouseup", o), this._resizeCleanup = null;
|
|
3368
3439
|
}, o = () => {
|
|
3369
3440
|
if (a(), this._resizing) {
|
|
3370
|
-
let e = this._columnWidths.get(n.key) ??
|
|
3441
|
+
let e = this._columnWidths.get(n.key) ?? J;
|
|
3371
3442
|
this.dispatchEvent(new CustomEvent("column-resize", {
|
|
3372
3443
|
detail: {
|
|
3373
3444
|
key: n.key,
|
|
@@ -4032,60 +4103,60 @@ var J = {
|
|
|
4032
4103
|
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")}`;
|
|
4033
4104
|
}
|
|
4034
4105
|
};
|
|
4035
|
-
|
|
4106
|
+
K([s({ type: Array })], $.prototype, "columns", void 0), K([s({
|
|
4036
4107
|
type: Array,
|
|
4037
4108
|
hasChanged: () => !0
|
|
4038
|
-
})], $.prototype, "data", null),
|
|
4109
|
+
})], $.prototype, "data", null), K([s({
|
|
4039
4110
|
type: Boolean,
|
|
4040
4111
|
attribute: "clear-undo-on-data-change"
|
|
4041
|
-
})], $.prototype, "clearUndoOnDataChange", void 0),
|
|
4112
|
+
})], $.prototype, "clearUndoOnDataChange", void 0), K([s({
|
|
4042
4113
|
type: Boolean,
|
|
4043
4114
|
attribute: "clear-selection-on-data-change"
|
|
4044
|
-
})], $.prototype, "clearSelectionOnDataChange", void 0),
|
|
4115
|
+
})], $.prototype, "clearSelectionOnDataChange", void 0), K([s({
|
|
4045
4116
|
type: Number,
|
|
4046
4117
|
attribute: "row-height"
|
|
4047
|
-
})], $.prototype, "rowHeight", null),
|
|
4118
|
+
})], $.prototype, "rowHeight", null), K([s({
|
|
4048
4119
|
type: Boolean,
|
|
4049
4120
|
attribute: "show-row-numbers"
|
|
4050
|
-
})], $.prototype, "showRowNumbers", void 0),
|
|
4121
|
+
})], $.prototype, "showRowNumbers", void 0), K([s({
|
|
4051
4122
|
type: String,
|
|
4052
4123
|
reflect: !0
|
|
4053
|
-
})], $.prototype, "theme", void 0),
|
|
4124
|
+
})], $.prototype, "theme", void 0), K([s({
|
|
4054
4125
|
type: Number,
|
|
4055
4126
|
attribute: "max-rows"
|
|
4056
|
-
})], $.prototype, "maxRows", void 0),
|
|
4127
|
+
})], $.prototype, "maxRows", void 0), K([s({ type: Boolean })], $.prototype, "editable", void 0), K([s({
|
|
4057
4128
|
type: Boolean,
|
|
4058
4129
|
attribute: "show-filters"
|
|
4059
|
-
})], $.prototype, "showFilters", void 0),
|
|
4130
|
+
})], $.prototype, "showFilters", void 0), K([s({
|
|
4060
4131
|
type: Boolean,
|
|
4061
4132
|
attribute: "show-context-menu"
|
|
4062
|
-
})], $.prototype, "showContextMenu", void 0),
|
|
4133
|
+
})], $.prototype, "showContextMenu", void 0), K([s({
|
|
4063
4134
|
type: Number,
|
|
4064
4135
|
attribute: "frozen-rows"
|
|
4065
|
-
})], $.prototype, "frozenRows", void 0),
|
|
4136
|
+
})], $.prototype, "frozenRows", void 0), K([s({
|
|
4066
4137
|
type: String,
|
|
4067
4138
|
attribute: "empty-message"
|
|
4068
|
-
})], $.prototype, "emptyMessage", void 0),
|
|
4139
|
+
})], $.prototype, "emptyMessage", void 0), K([s({
|
|
4069
4140
|
type: String,
|
|
4070
4141
|
attribute: "no-matching-message"
|
|
4071
|
-
})], $.prototype, "noMatchingMessage", void 0),
|
|
4142
|
+
})], $.prototype, "noMatchingMessage", void 0), K([s({
|
|
4072
4143
|
type: Boolean,
|
|
4073
4144
|
reflect: !0
|
|
4074
|
-
})], $.prototype, "loading", void 0),
|
|
4145
|
+
})], $.prototype, "loading", void 0), K([s({
|
|
4075
4146
|
type: Boolean,
|
|
4076
4147
|
attribute: "import-enabled"
|
|
4077
|
-
})], $.prototype, "importEnabled", void 0),
|
|
4148
|
+
})], $.prototype, "importEnabled", void 0), K([s({ type: Boolean })], $.prototype, "selectable", void 0), K([s({
|
|
4078
4149
|
type: String,
|
|
4079
4150
|
attribute: "selection-mode"
|
|
4080
|
-
})], $.prototype, "selectionMode", null),
|
|
4151
|
+
})], $.prototype, "selectionMode", null), K([s({
|
|
4081
4152
|
type: String,
|
|
4082
4153
|
attribute: "data-mode"
|
|
4083
|
-
})], $.prototype, "dataMode", void 0),
|
|
4154
|
+
})], $.prototype, "dataMode", void 0), K([s({
|
|
4084
4155
|
type: Object,
|
|
4085
4156
|
attribute: "footer-data"
|
|
4086
|
-
})], $.prototype, "footerData", void 0),
|
|
4157
|
+
})], $.prototype, "footerData", void 0), K([s({ attribute: !1 })], $.prototype, "stylesheets", void 0), K([s({
|
|
4087
4158
|
type: Number,
|
|
4088
4159
|
attribute: "max-undo-size"
|
|
4089
|
-
})], $.prototype, "maxUndoSize", null),
|
|
4160
|
+
})], $.prototype, "maxUndoSize", null), K([c()], $.prototype, "_scrollTop", void 0), K([c()], $.prototype, "_scrollLeft", void 0), K([c()], $.prototype, "_viewportHeight", void 0), K([c()], $.prototype, "_viewportWidth", void 0), K([c()], $.prototype, "_activeCell", void 0), K([c()], $.prototype, "_editingCell", void 0), K([c()], $.prototype, "_sortCriteria", void 0), K([c()], $.prototype, "_openFilterKey", void 0), K([c()], $.prototype, "_rowSelectionVersion", void 0), K([c()], $.prototype, "_autocompleteState", void 0), K([c()], $.prototype, "_headerMenu", void 0), K([c()], $.prototype, "_bodyContextMenu", void 0), K([c()], $.prototype, "_commentPopup", void 0), K([c()], $.prototype, "_isDragOver", void 0), $ = K([o("flex-table")], $);
|
|
4090
4161
|
//#endregion
|
|
4091
|
-
export { m as a, C as i,
|
|
4162
|
+
export { m as a, C as i, I as n, E as r, $ as t };
|
package/dist/flex-table.d.ts
CHANGED
|
@@ -332,7 +332,7 @@ export declare class FlexTable extends LitElement {
|
|
|
332
332
|
private _measureViewport;
|
|
333
333
|
private _warnedUnconstrained;
|
|
334
334
|
/**
|
|
335
|
-
* 개발 모드 사용
|
|
335
|
+
* 개발 모드 사용 안내: 행이 많은데 호스트가 스크롤 컨테이너가 아니면 — 호스트가 내용
|
|
336
336
|
* 높이만큼 자라 스크롤할 것이 없으면 — 가상화가 꺼진 것이다. 오류가 없어 아무도 모른다.
|
|
337
337
|
* 한 번만, 개발 모드에서만. ResizeObserver 경로에서 불리지만 판정은 프로퍼티 둘을 읽는 것뿐이다.
|
|
338
338
|
*/
|
|
@@ -366,6 +366,8 @@ export declare class FlexTable extends LitElement {
|
|
|
366
366
|
private _onKeyDown;
|
|
367
367
|
private _handleCtrlKey;
|
|
368
368
|
private _handleAltKey;
|
|
369
|
+
/** Widen (positive) or narrow (negative) a visible column by `delta` px, never below its minimum. */
|
|
370
|
+
private _resizeColumnBy;
|
|
369
371
|
private _handleNavigation;
|
|
370
372
|
private _handleCopy;
|
|
371
373
|
private _handlePaste;
|
|
@@ -402,7 +404,26 @@ export declare class FlexTable extends LitElement {
|
|
|
402
404
|
*/
|
|
403
405
|
private _getPinnedRightLeft;
|
|
404
406
|
private _renderHeaderCell;
|
|
407
|
+
/** Hidden columns directly adjacent to `col` (before and after) — the ones its menu can show again. */
|
|
408
|
+
private _hiddenNeighbors;
|
|
405
409
|
private _onHeaderContextMenu;
|
|
410
|
+
/**
|
|
411
|
+
* The header's column menu button — the same menu a right-click opens, anchored under the button.
|
|
412
|
+
* It is the pointer-sized, keyboard-reachable way to every per-column action: filtering, hiding,
|
|
413
|
+
* and resizing without dragging the 6px handle (WCAG 2.2 SC 2.5.7 / 2.5.8).
|
|
414
|
+
*/
|
|
415
|
+
private _onColumnMenuBtnClick;
|
|
416
|
+
private _menuItems;
|
|
417
|
+
private _columnMenuButton;
|
|
418
|
+
private _closeHeaderMenu;
|
|
419
|
+
private _onHeaderMenuKeydown;
|
|
420
|
+
/**
|
|
421
|
+
* Menu keyboard pattern shared by the column menu and the cell context menu: arrows and Home/End
|
|
422
|
+
* move focus between the items (wrapping), Escape closes and returns focus, Tab closes.
|
|
423
|
+
*/
|
|
424
|
+
private _onMenuKeydown;
|
|
425
|
+
/** Open the filter dropdown for `key` from its column menu, focusing its first control. */
|
|
426
|
+
private _openFilterFromMenu;
|
|
406
427
|
private _showHiddenBefore;
|
|
407
428
|
private _renderHeaderContextMenu;
|
|
408
429
|
private _renderBodyContextMenu;
|
|
@@ -411,9 +432,15 @@ export declare class FlexTable extends LitElement {
|
|
|
411
432
|
private _commitCommentPopup;
|
|
412
433
|
private _cancelCommentPopup;
|
|
413
434
|
private _renderCommentPopup;
|
|
435
|
+
/**
|
|
436
|
+
* Sort by one column from a menu — the column menu (the keyboard path to sorting) and the cell
|
|
437
|
+
* context menu. Same contract as a header click: server mode only reports, and `sort-change`
|
|
438
|
+
* carries `{ criteria }`.
|
|
439
|
+
*/
|
|
414
440
|
private _applySortFromMenu;
|
|
441
|
+
/** Keep a fixed-position menu inside the viewport — a menu opened near the right or bottom edge would spill off-screen. */
|
|
442
|
+
private _keepMenuInView;
|
|
415
443
|
private _adjustFilterDropdown;
|
|
416
|
-
private _onFilterBtnClick;
|
|
417
444
|
private _renderFilterDropdown;
|
|
418
445
|
private _buildTextPredicate;
|
|
419
446
|
private _applyTextFilter;
|
|
@@ -439,7 +466,8 @@ export declare class FlexTable extends LitElement {
|
|
|
439
466
|
private _applyDateFilter;
|
|
440
467
|
private _renderBooleanFilter;
|
|
441
468
|
private _clearColumnFilter;
|
|
442
|
-
|
|
469
|
+
/** Size a column to its widest rendered content — the resize handle's double-click and the column menu. */
|
|
470
|
+
private _autoFitColumn;
|
|
443
471
|
private _onResizeStart;
|
|
444
472
|
private _onHeaderMouseDown;
|
|
445
473
|
private _startColumnGhost;
|
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-Y4GyDV0Y.js";
|
|
2
2
|
export { i as FlexTable, t as RowSelectionState, r as UndoStack, n as exportData, e as renderCell };
|
package/dist/react.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/flex-table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "A minimalist, input-centric data grid web component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/flex-table.js",
|
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
47
47
|
"test": "vitest run",
|
|
48
48
|
"test:watch": "vitest",
|
|
49
|
-
"test:coverage": "vitest run --coverage",
|
|
50
49
|
"lint": "eslint src/",
|
|
51
50
|
"lint:fix": "eslint src/ --fix"
|
|
52
51
|
},
|
|
@@ -91,16 +90,14 @@
|
|
|
91
90
|
"@types/react-dom": "^19.2.7",
|
|
92
91
|
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
|
93
92
|
"@typescript-eslint/parser": "^8.64.0",
|
|
94
|
-
"@vitest/browser-playwright": "^
|
|
93
|
+
"@vitest/browser-playwright": "^5.0.0",
|
|
95
94
|
"eslint": "^10.1.0",
|
|
96
95
|
"eslint-plugin-lit": "^2.3.1",
|
|
97
|
-
"jsdom": "^
|
|
98
|
-
"loupe": "^3.2.1",
|
|
96
|
+
"jsdom": "^30.0.1",
|
|
99
97
|
"react": "^19.2.7",
|
|
100
98
|
"react-dom": "^19.2.8",
|
|
101
|
-
"strip-literal": "^3.1.0",
|
|
102
99
|
"typescript": "^6.0.2",
|
|
103
100
|
"vite": "^8.1.4",
|
|
104
|
-
"vitest": "^
|
|
101
|
+
"vitest": "^5.0.0"
|
|
105
102
|
}
|
|
106
103
|
}
|