@iyulab/flex-table 0.43.0 → 0.45.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 CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.45.0] - 2026-10-05
4
+
5
+ ### Added
6
+
7
+ - **Press targets follow `--u-target-size`** (the host's minimum target size from `@iyulab/components`).
8
+ When it is set, rows (and so the header row) are at least that tall — it wins over `row-height` and
9
+ `--ft-row-height`, because it is a floor — and the row-selection column is at least that wide. Column
10
+ menu buttons, menu items, filter fields and the find panel's fields and buttons follow too. Unset,
11
+ nothing changes. The column resize handle and the hidden-column marker keep their size: the column menu
12
+ does the same jobs (Wider · Narrower · Fit to content · Show …) with full-size targets.
13
+
14
+ ### Changed
15
+
16
+ - **The whole row-selection cell toggles the checkbox**, not just the 16px box (it is now a label). The box
17
+ itself looks the same.
18
+
19
+ ### Fixed
20
+
21
+ - **The column filter dropdown is no longer cut off by the table.** It was positioned inside the table's
22
+ scroll area, so in a short table its lower controls were hidden and could not be pressed. It now floats
23
+ like the column menu does, below the header (or above it when there is no room).
24
+
25
+ ## [0.44.0] - 2026-10-05
26
+
27
+ ### Added
28
+
29
+ - **`exportToBlob(format, options?)`** — the export as a `Blob` typed with the format's MIME type.
30
+ XLSX comes out DEFLATE-compressed (platform `CompressionStream`), a fraction of the stored size —
31
+ a 500-row sheet is under a quarter of it.
32
+
33
+ ### Changed
34
+
35
+ - **`exportToFile` returns `Promise<void>` and downloads a compressed XLSX.** It was synchronous and
36
+ wrote the workbook uncompressed (ZIP STORE). Calling it without `await` still works.
37
+ `exportToString` stays synchronous and still returns an uncompressed workbook for `'xlsx'` — use
38
+ `exportToBlob` when the bytes leave the browser.
39
+
3
40
  ## [0.43.0] - 2026-10-04
4
41
 
5
42
  ### Changed
package/README.md CHANGED
@@ -276,8 +276,9 @@ Default is `false`, matching `clear-undo-on-data-change`.
276
276
 
277
277
  | Method | Returns | Description |
278
278
  |--------|---------|-------------|
279
- | `exportToString(format, options?)` | `string \| Uint8Array` | Export to `'csv'` / `'tsv'` / `'json'` (a string) or `'xlsx'` (bytes). Pass `{ selectionOnly: true }` for selection range |
280
- | `exportToFile(format, filename?)` | `void` | Export and trigger browser file download |
279
+ | `exportToString(format, options?)` | `string \| Uint8Array` | Export to `'csv'` / `'tsv'` / `'json'` (a string) or `'xlsx'` (bytes, uncompressed). Pass `{ selectionOnly: true }` for selection range |
280
+ | `exportToBlob(format, options?)` | `Promise<Blob>` | The same export as a `Blob` of the format's MIME type — `'xlsx'` is DEFLATE-compressed |
281
+ | `exportToFile(format, filename?)` | `Promise<void>` | Export and trigger browser file download (`'xlsx'` compressed) |
281
282
 
282
283
  ## Events
283
284
 
@@ -5,9 +5,14 @@ export type ExportFormat = 'csv' | 'tsv' | 'json' | 'xlsx';
5
5
  * Returns string for text formats (csv/tsv/json) or Uint8Array for xlsx.
6
6
  */
7
7
  export declare function exportData(data: DataRow[], columns: ColumnDefinition[], format: ExportFormat): string | Uint8Array<ArrayBuffer>;
8
+ /**
9
+ * Export data as a `Blob` of the format's MIME type. XLSX is DEFLATE-compressed (unlike `exportData`,
10
+ * which stays synchronous and therefore stores the workbook uncompressed).
11
+ */
12
+ export declare function exportDataBlob(data: DataRow[], columns: ColumnDefinition[], format: ExportFormat): Promise<Blob>;
8
13
  /**
9
14
  * Trigger a file download in the browser.
10
15
  */
11
- export declare function downloadFile(content: string | Uint8Array<ArrayBuffer>, filename: string, mimeType: string): void;
16
+ export declare function downloadBlob(blob: Blob, filename: string): void;
12
17
  export declare function getExportMimeType(format: ExportFormat): string;
13
18
  export declare function getExportExtension(format: ExportFormat): string;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Minimal XLSX writer — no external dependencies.
3
- * Generates OOXML (.xlsx) files using ZIP STORE method (no compression).
3
+ * Generates OOXML (.xlsx) files — ZIP STORE (sync) or DEFLATE (async, platform CompressionStream).
4
4
  * Supports string, number, boolean, and date cell types.
5
5
  */
6
6
  import type { ColumnDefinition, DataRow } from '../models/types.js';
@@ -10,7 +10,14 @@ export interface ZipFile {
10
10
  }
11
11
  /** @internal 테스트가 실물에 가까운 통합 문서(스타일·1904 날짜 체계)를 조립하는 데 쓴다. */
12
12
  export declare function buildZip(files: ZipFile[]): Uint8Array<ArrayBuffer>;
13
+ /**
14
+ * DEFLATE 로 압축한 ZIP. 항목마다 압축본이 원본보다 작을 때만 DEFLATE 를 쓰고, 아니면 STORE 로 둔다.
15
+ * 압축은 플랫폼 `CompressionStream('deflate-raw')` 라 비동기다 — 읽기 쪽이 이미 `DecompressionStream` 을 쓴다.
16
+ */
17
+ export declare function buildZipDeflated(files: ZipFile[]): Promise<Uint8Array<ArrayBuffer>>;
13
18
  /**
14
19
  * Build an XLSX file as a Uint8Array.
15
20
  */
16
21
  export declare function buildXlsx(data: DataRow[], columns: ColumnDefinition[]): Uint8Array<ArrayBuffer>;
22
+ /** Build a DEFLATE-compressed XLSX file — the same workbook as `buildXlsx`, a fraction of the size. */
23
+ export declare function buildXlsxDeflated(data: DataRow[], columns: ColumnDefinition[]): Promise<Uint8Array<ArrayBuffer>>;
@@ -467,8 +467,9 @@ var y = r`
467
467
  display: inline-flex;
468
468
  align-items: center;
469
469
  justify-content: center;
470
- inline-size: 24px;
471
- block-size: 24px;
470
+ /* 호스트 하한(--u-target-size)이 있으면 그 값 — 미설정이면 24px. */
471
+ inline-size: max(24px, var(--u-target-size, 0px));
472
+ block-size: max(24px, var(--u-target-size, 0px));
472
473
  margin-left: auto;
473
474
  flex-shrink: 0;
474
475
  border-radius: 4px;
@@ -496,22 +497,23 @@ var y = r`
496
497
  opacity: 1;
497
498
  }
498
499
 
500
+ /* 표 밖에 띄운다 — 자리는 머리 칸에 맞춰 스크립트가 정한다(_adjustFilterDropdown). 표의 스크롤 영역에 잘리지 않는다. */
499
501
  .ft-filter-dropdown {
500
- position: absolute;
501
- top: 100%;
502
- left: 0;
502
+ position: fixed;
503
503
  min-width: 200px;
504
504
  background: var(--ft-bg);
505
505
  border: 1px solid var(--ft-border-color);
506
506
  border-radius: 4px;
507
507
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
508
508
  padding: 8px;
509
- z-index: 10;
509
+ z-index: var(--u-layer-floating, 1000);
510
510
  box-sizing: border-box;
511
511
  }
512
512
 
513
513
  .ft-filter-input {
514
514
  width: 100%;
515
+ /* 호스트 하한(--u-target-size, 미설정 = 0) — 아래 필터·찾기 패널의 입력·버튼도 같다. */
516
+ min-height: var(--u-target-size, 0px);
515
517
  padding: 4px 8px;
516
518
  font: inherit;
517
519
  font-size: 13px;
@@ -547,6 +549,12 @@ var y = r`
547
549
  color: var(--ft-sort-indicator-color);
548
550
  padding: 2px 8px;
549
551
  border-radius: 3px;
552
+ box-sizing: border-box;
553
+ display: inline-flex;
554
+ align-items: center;
555
+ justify-content: center;
556
+ min-height: var(--u-target-size, 0px);
557
+ min-width: var(--u-target-size, 0px);
550
558
  }
551
559
 
552
560
  .ft-filter-clear:hover {
@@ -562,6 +570,9 @@ var y = r`
562
570
 
563
571
  .ft-filter-mode-select {
564
572
  flex: 1;
573
+ box-sizing: border-box;
574
+ min-height: var(--u-target-size, 0px);
575
+ min-width: var(--u-target-size, 0px);
565
576
  padding: 3px 6px;
566
577
  font: inherit;
567
578
  font-size: 12px;
@@ -616,7 +627,7 @@ var y = r`
616
627
  .ft-checkbox-header {
617
628
  background: var(--ft-header-bg);
618
629
  border-bottom: 2px solid var(--ft-border-color);
619
- }
630
+ }\n .ft-checkbox-header .ft-checkbox-hit {\n margin-block-end: -2px;\n }
620
631
 
621
632
  .ft-checkbox-cell {
622
633
  border-bottom: 1px solid var(--ft-border-color);
@@ -630,6 +641,19 @@ var y = r`
630
641
  .ft-row-selected .ft-cell { background: var(--ft-selection-bg) !important; }
631
642
  .ft-row-selected .ft-row-num { background: var(--ft-selection-bg); }
632
643
 
644
+ /* 누르는 자리는 칸 전체다(라벨이 입력을 토글한다) — 체크 상자는 16px 그대로. 칸의 좌우 여백(4px)까지 덮는다. */
645
+ .ft-checkbox-hit {
646
+ flex: 1;
647
+ align-self: stretch;
648
+ margin-inline: -4px;
649
+ /* 칸 아래 경계선(본문 1px · 머리 2px)까지 — 행 높이가 곧 누르는 높이가 되게. */
650
+ margin-block-end: -1px;
651
+ display: flex;
652
+ align-items: center;
653
+ justify-content: center;
654
+ cursor: pointer;
655
+ }
656
+
633
657
  .ft-checkbox-header input[type="checkbox"],
634
658
  .ft-checkbox-cell input[type="checkbox"] {
635
659
  width: 16px;
@@ -679,7 +703,7 @@ var y = r`
679
703
  all: unset;
680
704
  box-sizing: border-box;
681
705
  display: block;
682
- min-block-size: 28px;
706
+ min-block-size: max(28px, var(--u-target-size, 0px));
683
707
  padding: 6px 12px;
684
708
  cursor: pointer;
685
709
  font-size: var(--ft-font-size, 14px);
@@ -743,7 +767,7 @@ var y = r`
743
767
  box-sizing: border-box;
744
768
  display: block;
745
769
  inline-size: 100%;
746
- min-block-size: 28px;
770
+ min-block-size: max(28px, var(--u-target-size, 0px));
747
771
  padding: 6px 14px;
748
772
  cursor: pointer;
749
773
  font-size: var(--ft-font-size, 14px);
@@ -859,6 +883,8 @@ var y = r`
859
883
  .ft-find-input, .ft-find-replace-input {
860
884
  flex: 1;
861
885
  min-width: 120px;
886
+ /* content-box 다(최소 폭 120px 의 뜻을 바꾸지 않으려고) — 여백 3px ×2 와 테두리 1px ×2 를 뺀다. */
887
+ min-height: max(0px, calc(var(--u-target-size, 0px) - 8px));
862
888
  padding: 3px 6px;
863
889
  border: 1px solid var(--ft-border-color);
864
890
  border-radius: 3px;
@@ -881,6 +907,9 @@ var y = r`
881
907
  }
882
908
 
883
909
  .ft-find-panel button {
910
+ box-sizing: border-box;
911
+ min-height: var(--u-target-size, 0px);
912
+ min-width: var(--u-target-size, 0px);
884
913
  padding: 2px 8px;
885
914
  border: 1px solid var(--ft-border-color);
886
915
  border-radius: 3px;
@@ -902,6 +931,8 @@ var y = r`
902
931
  .ft-find-panel label {
903
932
  display: flex;
904
933
  align-items: center;
934
+ min-height: var(--u-target-size, 0px);
935
+ min-width: var(--u-target-size, 0px);
905
936
  gap: 2px;
906
937
  font-size: 12px;
907
938
  cursor: pointer;
@@ -1442,49 +1473,83 @@ function de(e) {
1442
1473
  }
1443
1474
  var F = new TextEncoder();
1444
1475
  function fe(e) {
1476
+ return I(e.map((e) => ({
1477
+ ...e,
1478
+ body: e.data,
1479
+ method: 0
1480
+ })));
1481
+ }
1482
+ async function pe(e) {
1483
+ let t = [];
1484
+ for (let n of e) {
1485
+ let e = await me(n.data);
1486
+ t.push(e.length < n.data.length ? {
1487
+ ...n,
1488
+ body: e,
1489
+ method: 8
1490
+ } : {
1491
+ ...n,
1492
+ body: n.data,
1493
+ method: 0
1494
+ });
1495
+ }
1496
+ return I(t);
1497
+ }
1498
+ async function me(e) {
1499
+ let t = new CompressionStream("deflate-raw"), n = t.writable.getWriter(), r = t.readable.getReader();
1500
+ n.write(e), n.close();
1501
+ let i = [];
1502
+ for (;;) {
1503
+ let { done: e, value: t } = await r.read();
1504
+ if (e) break;
1505
+ i.push(t);
1506
+ }
1507
+ return L(i);
1508
+ }
1509
+ function I(e) {
1445
1510
  let t = [], n = [], r = 0;
1446
1511
  for (let i of e) {
1447
- let e = F.encode(i.name), a = de(i.data), o = i.data.length, s = new ArrayBuffer(30 + e.length), c = new DataView(s);
1448
- 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);
1449
- let l = new ArrayBuffer(46 + e.length), u = new DataView(l);
1450
- 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;
1512
+ let e = F.encode(i.name), a = de(i.data), o = i.data.length, s = i.body.length, c = i.method === 8 ? 20 : 10, l = new ArrayBuffer(30 + e.length), u = new DataView(l);
1513
+ u.setUint32(0, 67324752, !0), u.setUint16(4, c, !0), u.setUint16(6, 0, !0), u.setUint16(8, i.method, !0), u.setUint16(10, 0, !0), u.setUint16(12, 33, !0), u.setUint32(14, a, !0), u.setUint32(18, s, !0), u.setUint32(22, o, !0), u.setUint16(26, e.length, !0), u.setUint16(28, 0, !0), new Uint8Array(l, 30).set(e), t.push(new Uint8Array(l)), t.push(i.body);
1514
+ let d = new ArrayBuffer(46 + e.length), f = new DataView(d);
1515
+ f.setUint32(0, 33639248, !0), f.setUint16(4, 20, !0), f.setUint16(6, c, !0), f.setUint16(8, 0, !0), f.setUint16(10, i.method, !0), f.setUint16(12, 0, !0), f.setUint16(14, 33, !0), f.setUint32(16, a, !0), f.setUint32(20, s, !0), f.setUint32(24, o, !0), f.setUint16(28, e.length, !0), f.setUint16(30, 0, !0), f.setUint16(32, 0, !0), f.setUint16(34, 0, !0), f.setUint16(36, 0, !0), f.setUint32(38, 0, !0), f.setUint32(42, r, !0), new Uint8Array(d, 46).set(e), n.push(new Uint8Array(d)), r += 30 + e.length + s;
1451
1516
  }
1452
- let i = I(n), a = /* @__PURE__ */ new ArrayBuffer(22), o = new DataView(a);
1453
- 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), I([
1517
+ let i = L(n), a = /* @__PURE__ */ new ArrayBuffer(22), o = new DataView(a);
1518
+ 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), L([
1454
1519
  ...t,
1455
1520
  i,
1456
1521
  new Uint8Array(a)
1457
1522
  ]);
1458
1523
  }
1459
- function I(e) {
1524
+ function L(e) {
1460
1525
  let t = e.reduce((e, t) => e + t.length, 0), n = new Uint8Array(t), r = 0;
1461
1526
  for (let t of e) n.set(t, r), r += t.length;
1462
1527
  return n;
1463
1528
  }
1464
- function L(e) {
1529
+ function R(e) {
1465
1530
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1466
1531
  }
1467
- function pe(e) {
1532
+ function he(e) {
1468
1533
  let t = "";
1469
1534
  for (e++; e > 0;) t = String.fromCharCode(64 + (e % 26 || 26)) + t, e = Math.floor((e - 1) / 26);
1470
1535
  return t;
1471
1536
  }
1472
- function R(e, t) {
1473
- return pe(t) + (e + 1);
1537
+ function z(e, t) {
1538
+ return he(t) + (e + 1);
1474
1539
  }
1475
- function me(e) {
1540
+ function ge(e) {
1476
1541
  let t = Date.UTC(1899, 11, 30);
1477
1542
  return (e.getTime() - t) / 864e5;
1478
1543
  }
1479
- var z = "<?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>", he = "<?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>", ge = "<?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>", _e = "<?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>", ve = "<?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>";
1480
- function ye(e, t) {
1481
- let n = [], r = t.map((e, t) => `<c r="${R(0, t)}" t="str" s="2"><v>${L(e.label)}</v></c>`);
1544
+ var _e = "<?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>", ve = "<?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>", ye = "<?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>", be = "<?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>", xe = "<?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>";
1545
+ function Se(e, t) {
1546
+ let n = [], r = t.map((e, t) => `<c r="${z(0, t)}" t="str" s="2"><v>${R(e.label)}</v></c>`);
1482
1547
  return n.push(`<row r="1">${r.join("")}</row>`), e.forEach((e, r) => {
1483
1548
  let i = t.map((t, n) => {
1484
- let i = R(r + 1, n), a = e[t.key];
1549
+ let i = z(r + 1, n), a = e[t.key];
1485
1550
  if (a == null) return `<c r="${i}" t="str"><v></v></c>`;
1486
1551
  let o = t.type ?? "text";
1487
- 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>${me(a).toFixed(6)}</v></c>` : o === "boolean" ? `<c r="${i}" t="b"><v>${+!!a}</v></c>` : `<c r="${i}" t="str"><v>${L(String(a))}</v></c>`;
1552
+ 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>${ge(a).toFixed(6)}</v></c>` : o === "boolean" ? `<c r="${i}" t="b"><v>${+!!a}</v></c>` : `<c r="${i}" t="str"><v>${R(String(a))}</v></c>`;
1488
1553
  });
1489
1554
  n.push(`<row r="${r + 2}">${i.join("")}</row>`);
1490
1555
  }), `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@@ -1492,24 +1557,30 @@ function ye(e, t) {
1492
1557
  <sheetData>${n.join("")}</sheetData>
1493
1558
  </worksheet>`;
1494
1559
  }
1495
- function be(e, t) {
1496
- let n = ye(e, t);
1497
- return fe([
1560
+ function Ce(e, t) {
1561
+ return fe(B(e, t));
1562
+ }
1563
+ function we(e, t) {
1564
+ return pe(B(e, t));
1565
+ }
1566
+ function B(e, t) {
1567
+ let n = Se(e, t);
1568
+ return [
1498
1569
  {
1499
1570
  name: "[Content_Types].xml",
1500
- data: F.encode(z)
1571
+ data: F.encode(_e)
1501
1572
  },
1502
1573
  {
1503
1574
  name: "_rels/.rels",
1504
- data: F.encode(he)
1575
+ data: F.encode(ve)
1505
1576
  },
1506
1577
  {
1507
1578
  name: "xl/workbook.xml",
1508
- data: F.encode(ge)
1579
+ data: F.encode(ye)
1509
1580
  },
1510
1581
  {
1511
1582
  name: "xl/_rels/workbook.xml.rels",
1512
- data: F.encode(_e)
1583
+ data: F.encode(be)
1513
1584
  },
1514
1585
  {
1515
1586
  name: "xl/worksheets/sheet1.xml",
@@ -1517,30 +1588,34 @@ function be(e, t) {
1517
1588
  },
1518
1589
  {
1519
1590
  name: "xl/styles.xml",
1520
- data: F.encode(ve)
1591
+ data: F.encode(xe)
1521
1592
  }
1522
- ]);
1593
+ ];
1523
1594
  }
1524
1595
  //#endregion
1525
1596
  //#region src/export/export.ts
1526
- function B(e, t, n) {
1597
+ function V(e, t, n) {
1527
1598
  switch (n) {
1528
- case "csv": return V(e, t, ",");
1529
- case "tsv": return V(e, t, " ");
1530
- case "json": return Se(e, t);
1531
- case "xlsx": return be(e, t);
1599
+ case "csv": return H(e, t, ",");
1600
+ case "tsv": return H(e, t, " ");
1601
+ case "json": return De(e, t);
1602
+ case "xlsx": return Ce(e, t);
1532
1603
  }
1533
1604
  }
1534
- function xe(e, t) {
1605
+ async function Te(e, t, n) {
1606
+ let r = n === "xlsx" ? await we(e, t) : V(e, t, n);
1607
+ return new Blob([r], { type: je(n) });
1608
+ }
1609
+ function Ee(e, t) {
1535
1610
  return e == null ? "" : (t.type === "date" || t.type === "datetime") && e instanceof Date ? e.toISOString() : String(e);
1536
1611
  }
1537
- function V(e, t, n) {
1538
- return [t.map((e) => H(e.label, n)).join(n), ...e.map((e) => t.map((t) => H(xe(e[t.key], t), n)).join(n))].join("\n");
1612
+ function H(e, t, n) {
1613
+ return [t.map((e) => U(e.label, n)).join(n), ...e.map((e) => t.map((t) => U(Ee(e[t.key], t), n)).join(n))].join("\n");
1539
1614
  }
1540
- function H(e, t) {
1615
+ function U(e, t) {
1541
1616
  return e.includes(t) || e.includes("\"") || e.includes("\n") || e.includes("\r") ? "\"" + e.replace(/"/g, "\"\"") + "\"" : e;
1542
1617
  }
1543
- function Se(e, t) {
1618
+ function De(e, t) {
1544
1619
  let n = e.map((e) => {
1545
1620
  let n = {};
1546
1621
  for (let r of t) {
@@ -1551,35 +1626,35 @@ function Se(e, t) {
1551
1626
  });
1552
1627
  return JSON.stringify(n, null, 2);
1553
1628
  }
1554
- function Ce(e, t, n) {
1555
- let r = new Blob([e], { type: n }), i = URL.createObjectURL(r), a = document.createElement("a");
1556
- 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);
1629
+ function Oe(e, t) {
1630
+ let n = URL.createObjectURL(e), r = document.createElement("a");
1631
+ r.href = n, r.download = t, r.style.display = "none", document.body.appendChild(r), r.click(), document.body.removeChild(r), setTimeout(() => URL.revokeObjectURL(n), 100);
1557
1632
  }
1558
- var U = {
1633
+ var ke = {
1559
1634
  csv: "text/csv;charset=utf-8",
1560
1635
  tsv: "text/tab-separated-values;charset=utf-8",
1561
1636
  json: "application/json;charset=utf-8",
1562
1637
  xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1563
- }, we = {
1638
+ }, Ae = {
1564
1639
  csv: ".csv",
1565
1640
  tsv: ".tsv",
1566
1641
  json: ".json",
1567
1642
  xlsx: ".xlsx"
1568
1643
  };
1569
- function Te(e) {
1570
- return U[e];
1644
+ function je(e) {
1645
+ return ke[e];
1571
1646
  }
1572
- function Ee(e) {
1573
- return we[e];
1647
+ function Me(e) {
1648
+ return Ae[e];
1574
1649
  }
1575
1650
  //#endregion
1576
1651
  //#region src/export/xlsx-reader.ts
1577
- function De(e) {
1652
+ function Ne(e) {
1578
1653
  for (let t = e.byteLength - 22; t >= 0; t--) if (e.getUint32(t, !0) === 101010256) return t;
1579
1654
  throw Error("XLSX: End of central directory not found — not a valid ZIP file");
1580
1655
  }
1581
- function Oe(e) {
1582
- 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;
1656
+ function Pe(e) {
1657
+ let t = Ne(e), n = e.getUint32(t + 16, !0), r = e.getUint32(t + 12, !0), i = new TextDecoder("utf-8"), a = [], o = n, s = n + r;
1583
1658
  for (; o < s && e.getUint32(o, !0) === 33639248;) {
1584
1659
  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));
1585
1660
  a.push({
@@ -1592,7 +1667,7 @@ function Oe(e) {
1592
1667
  }
1593
1668
  return a;
1594
1669
  }
1595
- async function ke(e) {
1670
+ async function Fe(e) {
1596
1671
  let t = new DecompressionStream("deflate-raw"), n = t.writable.getWriter(), r = t.readable.getReader();
1597
1672
  n.write(e), n.close();
1598
1673
  let i = [];
@@ -1605,28 +1680,28 @@ async function ke(e) {
1605
1680
  for (let e of i) o.set(e, s), s += e.length;
1606
1681
  return o;
1607
1682
  }
1608
- async function Ae(e, t) {
1683
+ async function Ie(e, t) {
1609
1684
  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);
1610
1685
  if (t.compressionMethod === 0) return o.slice();
1611
- if (t.compressionMethod === 8) return ke(o);
1686
+ if (t.compressionMethod === 8) return Fe(o);
1612
1687
  throw Error(`XLSX: Unsupported compression method ${t.compressionMethod}`);
1613
1688
  }
1614
- async function je(e) {
1615
- let t = new DataView(e), n = Oe(t), r = /* @__PURE__ */ new Map();
1616
- for (let e of n) e.compressedSize === 0 && e.name.endsWith("/") || r.set(e.name, await Ae(t, e));
1689
+ async function Le(e) {
1690
+ let t = new DataView(e), n = Pe(t), r = /* @__PURE__ */ new Map();
1691
+ for (let e of n) e.compressedSize === 0 && e.name.endsWith("/") || r.set(e.name, await Ie(t, e));
1617
1692
  return r;
1618
1693
  }
1619
- var Me = new TextDecoder("utf-8");
1694
+ var Re = new TextDecoder("utf-8");
1620
1695
  function W(e) {
1621
- let t = Me.decode(e);
1696
+ let t = Re.decode(e);
1622
1697
  return new DOMParser().parseFromString(t, "application/xml");
1623
1698
  }
1624
- function Ne(e) {
1699
+ function ze(e) {
1625
1700
  if (!e) return [];
1626
1701
  let t = W(e);
1627
1702
  return Array.from(t.querySelectorAll("si")).map((e) => Array.from(e.querySelectorAll("t")).map((e) => e.textContent ?? "").join(""));
1628
1703
  }
1629
- var Pe = /* @__PURE__ */ new Set([
1704
+ var Be = /* @__PURE__ */ new Set([
1630
1705
  14,
1631
1706
  15,
1632
1707
  16,
@@ -1646,29 +1721,29 @@ var Pe = /* @__PURE__ */ new Set([
1646
1721
  57,
1647
1722
  58
1648
1723
  ]);
1649
- function Fe(e) {
1724
+ function Ve(e) {
1650
1725
  let t = e.replace(/"[^"]*"/g, "").replace(/\\./g, "").replace(/\[[^\]]*\]/g, "").split(";")[0];
1651
1726
  return /[dy]/i.test(t);
1652
1727
  }
1653
- function Ie(e) {
1728
+ function He(e) {
1654
1729
  let t = /* @__PURE__ */ new Set();
1655
1730
  if (!e) return t;
1656
1731
  let n = W(e), r = /* @__PURE__ */ new Map();
1657
1732
  for (let e of Array.from(n.querySelectorAll("numFmts > numFmt"))) r.set(parseInt(e.getAttribute("numFmtId") ?? "", 10), e.getAttribute("formatCode") ?? "");
1658
1733
  return Array.from(n.querySelectorAll("cellXfs > xf")).forEach((e, n) => {
1659
1734
  let i = parseInt(e.getAttribute("numFmtId") ?? "0", 10), a = r.get(i);
1660
- (a === void 0 ? Pe.has(i) : Fe(a)) && t.add(n);
1735
+ (a === void 0 ? Be.has(i) : Ve(a)) && t.add(n);
1661
1736
  }), t;
1662
1737
  }
1663
- function Le(e) {
1738
+ function Ue(e) {
1664
1739
  if (!e) return !1;
1665
1740
  let t = W(e).querySelector("workbookPr")?.getAttribute("date1904");
1666
1741
  return t === "1" || t === "true";
1667
1742
  }
1668
- function Re(e, t) {
1743
+ function We(e, t) {
1669
1744
  return new Date((t ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30)) + Math.floor(e) * 864e5).toISOString().slice(0, 10);
1670
1745
  }
1671
- function ze(e, t, n, r) {
1746
+ function Ge(e, t, n, r) {
1672
1747
  let i = W(e).querySelectorAll("row");
1673
1748
  if (i.length === 0) return [];
1674
1749
  let a = 0, o = /* @__PURE__ */ new Map();
@@ -1676,7 +1751,7 @@ function ze(e, t, n, r) {
1676
1751
  let i = parseInt(e.getAttribute("r") ?? "1", 10) - 1, s = /* @__PURE__ */ new Map();
1677
1752
  o.set(i, s);
1678
1753
  for (let i of e.querySelectorAll("c")) {
1679
- let e = Be(i.getAttribute("r") ?? "");
1754
+ let e = Ke(i.getAttribute("r") ?? "");
1680
1755
  e > a && (a = e);
1681
1756
  let o = i.getAttribute("t") ?? "", c = i.getAttribute("s") ?? "", l = i.querySelector("v")?.textContent ?? "", u = i.querySelector("is > t")?.textContent ?? null, d;
1682
1757
  if (o === "s") d = t[parseInt(l, 10)] ?? "";
@@ -1685,7 +1760,7 @@ function ze(e, t, n, r) {
1685
1760
  else if (l === "") d = "";
1686
1761
  else {
1687
1762
  let e = parseFloat(l);
1688
- d = c !== "" && n.has(parseInt(c, 10)) && !isNaN(e) && e >= 0 ? Re(e, r) : l;
1763
+ d = c !== "" && n.has(parseInt(c, 10)) && !isNaN(e) && e >= 0 ? We(e, r) : l;
1689
1764
  }
1690
1765
  s.set(e, d);
1691
1766
  }
@@ -1699,13 +1774,13 @@ function ze(e, t, n, r) {
1699
1774
  }
1700
1775
  return c;
1701
1776
  }
1702
- function Be(e) {
1777
+ function Ke(e) {
1703
1778
  let t = 0, n = 0;
1704
1779
  for (; n < e.length && e[n] >= "A" && e[n] <= "Z";) t = t * 26 + (e.charCodeAt(n) - 64), n++;
1705
1780
  return t - 1;
1706
1781
  }
1707
- async function Ve(e) {
1708
- let t = await je(e), n = Ne(t.get("xl/sharedStrings.xml")), r = t.get("xl/worksheets/sheet1.xml");
1782
+ async function qe(e) {
1783
+ let t = await Le(e), n = ze(t.get("xl/sharedStrings.xml")), r = t.get("xl/worksheets/sheet1.xml");
1709
1784
  if (!r) {
1710
1785
  for (let [e, n] of t) if (e.toLowerCase().includes("worksheets/sheet")) {
1711
1786
  r = n;
@@ -1713,7 +1788,7 @@ async function Ve(e) {
1713
1788
  }
1714
1789
  }
1715
1790
  if (!r) throw Error("XLSX: No worksheet found");
1716
- let i = Ie(t.get("xl/styles.xml")), a = Le(t.get("xl/workbook.xml")), o = ze(r, n, i, a);
1791
+ let i = He(t.get("xl/styles.xml")), a = Ue(t.get("xl/workbook.xml")), o = Ge(r, n, i, a);
1717
1792
  return o.length === 0 ? {
1718
1793
  headers: [],
1719
1794
  rows: []
@@ -1744,9 +1819,9 @@ var q = {
1744
1819
  lt: "<",
1745
1820
  gte: "≥",
1746
1821
  lte: "≤"
1747
- }, J = 120, Y = 40, X = 20, Z = 32, Q = 5, He = 200, $ = class extends n {
1822
+ }, J = 120, Y = 40, X = 20, Z = 32, Q = 5, Je = 200, $ = class extends n {
1748
1823
  constructor(...e) {
1749
- 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 re(), this._editing = new ie(), this._rowSelection = new j(), this._undo = new M(), 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 = () => {
1824
+ super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = Z, this._targetFloor = 0, 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 re(), this._editing = new ie(), this._rowSelection = new j(), this._undo = new M(), 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 = () => {
1750
1825
  this._commitCommentPopup();
1751
1826
  }, 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();
1752
1827
  }
@@ -1761,7 +1836,8 @@ var q = {
1761
1836
  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);
1762
1837
  }
1763
1838
  get rowHeight() {
1764
- return this._rowHeightExplicit ?? this._rowHeightFromCss ?? Z;
1839
+ let e = this._rowHeightExplicit ?? this._rowHeightFromCss ?? Z;
1840
+ return Math.max(e, this._targetFloor);
1765
1841
  }
1766
1842
  set rowHeight(e) {
1767
1843
  let t = this.rowHeight;
@@ -1782,9 +1858,12 @@ var q = {
1782
1858
  get visibleColumns() {
1783
1859
  return this.columns.filter((e) => !e.hidden);
1784
1860
  }
1861
+ get _checkboxColWidth() {
1862
+ return Math.max(36, this._targetFloor);
1863
+ }
1785
1864
  get _prefixWidth() {
1786
1865
  let e = 0;
1787
- return this.selectable && (e += 36), this.showRowNumbers && (e += 48), e;
1866
+ return this.selectable && (e += this._checkboxColWidth), this.showRowNumbers && (e += 48), e;
1788
1867
  }
1789
1868
  get canUndo() {
1790
1869
  return this._undo.canUndo;
@@ -2106,17 +2185,34 @@ var q = {
2106
2185
  return e;
2107
2186
  }
2108
2187
  exportToString(e, t) {
2109
- if (t?.selectionOnly) {
2110
- let t = this._selection.getEffectiveRange();
2111
- if (!t) return "";
2112
- let n = this.visibleColumns.slice(t.startCol, t.endCol + 1), r = [];
2113
- for (let e = t.startRow; e <= t.endRow; e++) r.push(this.data[this._toDataIndex(e)]);
2114
- return B(r, n, e);
2115
- }
2116
- return B(this._sortedIndices.map((e) => this.data[e]), this.visibleColumns, e);
2188
+ let n = this._exportSlice(t);
2189
+ return n ? V(n.rows, n.cols, e) : "";
2117
2190
  }
2118
- exportToFile(e, t) {
2119
- Ce(this.exportToString(e), t ?? `export${Ee(e)}`, Te(e));
2191
+ async exportToBlob(e, t) {
2192
+ let n = this._exportSlice(t) ?? {
2193
+ rows: [],
2194
+ cols: []
2195
+ };
2196
+ return Te(n.rows, n.cols, e);
2197
+ }
2198
+ async exportToFile(e, t) {
2199
+ Oe(await this.exportToBlob(e), t ?? `export${Me(e)}`);
2200
+ }
2201
+ _exportSlice(e) {
2202
+ if (e?.selectionOnly) {
2203
+ let e = this._selection.getEffectiveRange();
2204
+ if (!e) return null;
2205
+ let t = this.visibleColumns.slice(e.startCol, e.endCol + 1), n = [];
2206
+ for (let t = e.startRow; t <= e.endRow; t++) n.push(this.data[this._toDataIndex(t)]);
2207
+ return {
2208
+ rows: n,
2209
+ cols: t
2210
+ };
2211
+ }
2212
+ return {
2213
+ rows: this._sortedIndices.map((e) => this.data[e]),
2214
+ cols: this.visibleColumns
2215
+ };
2120
2216
  }
2121
2217
  setComment(e, t, n) {
2122
2218
  let r = this.getComment(e, t);
@@ -2173,7 +2269,7 @@ var q = {
2173
2269
  async importFromFile(e) {
2174
2270
  let t = e.name.toLowerCase();
2175
2271
  if (t.endsWith(".xlsx")) {
2176
- let t = await Ve(await e.arrayBuffer());
2272
+ let t = await qe(await e.arrayBuffer());
2177
2273
  this._applyImportedSheet(t);
2178
2274
  } else if (t.endsWith(".csv") || t.endsWith(".tsv")) {
2179
2275
  let t = await e.text(), n = g(t);
@@ -2311,12 +2407,11 @@ var q = {
2311
2407
  this.shadowRoot && Array.isArray(this.shadowRoot.adoptedStyleSheets) && (this.shadowRoot.adoptedStyleSheets = [...this._baseStyleSheets, ...this.stylesheets]);
2312
2408
  }
2313
2409
  _readDensityTokens() {
2314
- let e = getComputedStyle(this).getPropertyValue("--ft-row-height").trim();
2315
- if (!e) return;
2316
- let t = parseFloat(e);
2317
- if (!Number.isFinite(t) || t <= 0 || !/px\s*$/.test(e) || t === this._rowHeightFromCss) return;
2318
- let n = this.rowHeight;
2319
- this._rowHeightFromCss = t, this._rowHeightExplicit === void 0 && this.requestUpdate("rowHeight", n);
2410
+ let e = getComputedStyle(this), t = (t) => {
2411
+ let n = e.getPropertyValue(t).trim(), r = parseFloat(n);
2412
+ return n && Number.isFinite(r) && r > 0 && /px\s*$/.test(n) ? r : void 0;
2413
+ }, n = this.rowHeight, r = t("--ft-row-height");
2414
+ r !== void 0 && (this._rowHeightFromCss = r), this._targetFloor = t("--u-target-size") ?? 0, this.rowHeight !== n && this.requestUpdate("rowHeight", n);
2320
2415
  }
2321
2416
  _updateColOffsets() {
2322
2417
  let e = this.visibleColumns, t = [], n = this._prefixWidth;
@@ -2396,7 +2491,7 @@ var q = {
2396
2491
  e !== this._viewportHeight && (this._viewportHeight = e), t !== this._viewportWidth && (this._viewportWidth = t), this._warnIfUnconstrained();
2397
2492
  }
2398
2493
  _warnIfUnconstrained() {
2399
- this._warnedUnconstrained || process.env.NODE_ENV === "production" || this._visibleRowCount < He || 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.`));
2494
+ this._warnedUnconstrained || process.env.NODE_ENV === "production" || this._visibleRowCount < Je || 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.`));
2400
2495
  }
2401
2496
  _onScroll() {
2402
2497
  this._scrollTop = this.scrollTop, this._scrollLeft = this.scrollLeft;
@@ -3041,7 +3136,7 @@ var q = {
3041
3136
  }
3042
3137
  _getPinnedLeft(e) {
3043
3138
  let t = this.visibleColumns, n = 0;
3044
- this.selectable && (n += 36), this.showRowNumbers && (n += 48);
3139
+ this.selectable && (n += this._checkboxColWidth), this.showRowNumbers && (n += 48);
3045
3140
  for (let r = 0; r < e; r++) t[r].pinned === "left" && (n += this._getColWidth(t[r]));
3046
3141
  return n;
3047
3142
  }
@@ -3328,11 +3423,12 @@ var q = {
3328
3423
  }
3329
3424
  _adjustFilterDropdown() {
3330
3425
  if (!this._openFilterKey) return;
3331
- let e = this.shadowRoot?.querySelector(".ft-filter-dropdown");
3332
- if (!e) return;
3333
- e.style.top = "", e.style.bottom = "";
3334
- let t = e.getBoundingClientRect(), n = window.innerHeight;
3335
- t.bottom > n && (e.style.top = "auto", e.style.bottom = "100%");
3426
+ let e = this.shadowRoot?.querySelector(".ft-filter-dropdown"), t = e?.previousElementSibling;
3427
+ if (!e || !t) return;
3428
+ let n = t.getBoundingClientRect(), r = e.getBoundingClientRect(), i = document.documentElement.clientWidth, a = document.documentElement.clientHeight, o = n.bottom;
3429
+ o + r.height > a - 4 && (o = n.top - r.height >= 4 ? n.top - r.height : Math.max(4, a - r.height - 4));
3430
+ let s = Math.max(4, Math.min(n.left, i - r.width - 4));
3431
+ e.style.left = `${s}px`, e.style.top = `${o}px`;
3336
3432
  }
3337
3433
  _renderFilterDropdown(e) {
3338
3434
  let t = e.type ?? "text";
@@ -4071,15 +4167,16 @@ var q = {
4071
4167
  let r = this.headerHeight, o = this._totalRowWidth, { start: s, end: c } = this.visibleColRange, l = [];
4072
4168
  for (let t = 0; t < e.length; t++) (e[t].pinned === "left" || e[t].pinned === "right") && (t < s || t >= c) && l.push(t);
4073
4169
  let u = 0, d = this._scrollLeft, f = this.selectable ? i`<div class="ft-checkbox-header"
4074
- style="position: absolute; top: 0; left: ${d + u}px; width: 36px; height: ${r}px; z-index: 4;">
4170
+ style="position: absolute; top: 0; left: ${d + u}px; width: ${this._checkboxColWidth}px; height: ${r}px; z-index: 4;">
4075
4171
  ${this._rowSelection.mode === "multi" ? i`
4076
- <input type="checkbox"
4172
+ <!-- 칸 전체가 누르는 자리다(라벨) — 체크 상자는 16px 그대로. -->
4173
+ <label class="ft-checkbox-hit"><input type="checkbox"
4077
4174
  .checked=${this._rowSelection.isAllSelected}
4078
4175
  .indeterminate=${this._rowSelection.isSomeSelected}
4079
- @change=${this._onSelectAllChange}>
4176
+ @change=${this._onSelectAllChange}></label>
4080
4177
  ` : ""}
4081
4178
  </div>` : "";
4082
- this.selectable && (u += 36);
4179
+ this.selectable && (u += this._checkboxColWidth);
4083
4180
  let p = this.showRowNumbers ? i`<div class="ft-row-num-header"
4084
4181
  style="position: absolute; top: 0; left: ${d + u}px; width: 48px; height: ${r}px; z-index: 4;">#</div>` : "", m = [];
4085
4182
  for (let t of l) m.push(this._renderHeaderCell(e[t], t));
@@ -4144,8 +4241,8 @@ var q = {
4144
4241
  _renderFooter(e, t, n) {
4145
4242
  if (!this.footerData) return "";
4146
4243
  let r = this.visibleColumns, a = this.rowHeight, o = this._totalRowWidth, s = this._scrollLeft, c = 0, l = this.selectable ? i`<div class="ft-footer-cell ft-checkbox-cell"
4147
- style="position: absolute; top: 0; left: ${s + c}px; width: 36px; height: ${a}px; z-index: 2;"></div>` : "";
4148
- this.selectable && (c += 36);
4244
+ style="position: absolute; top: 0; left: ${s + c}px; width: ${this._checkboxColWidth}px; height: ${a}px; z-index: 2;"></div>` : "";
4245
+ this.selectable && (c += this._checkboxColWidth);
4149
4246
  let u = this.showRowNumbers ? i`<div class="ft-footer-cell ft-row-num"
4150
4247
  style="position: absolute; top: 0; left: ${s + c}px; width: 48px; height: ${a}px; z-index: 2;"></div>` : "", d = [];
4151
4248
  for (let e of n) {
@@ -4172,14 +4269,14 @@ var q = {
4172
4269
  _renderRow(e, t, n, r, a) {
4173
4270
  let o = this.visibleColumns, s = this._toDataIndex(e), c = this.data[s], l = a ?? e * this.rowHeight, u = this.rowHeight, d = this._totalRowWidth, f = e % 2 == 0 ? "ft-row-even" : "ft-row-odd", p = this.selectable && this._rowSelection.isSelected(e), m = this._scrollLeft, h = 0, g = this.selectable ? i`
4174
4271
  <div class="ft-checkbox-cell"
4175
- style="position: absolute; top: 0; left: ${m + h}px; width: 36px; height: ${u}px; z-index: 2;">
4176
- <input type="checkbox"
4272
+ style="position: absolute; top: 0; left: ${m + h}px; width: ${this._checkboxColWidth}px; height: ${u}px; z-index: 2;">
4273
+ <label class="ft-checkbox-hit"><input type="checkbox"
4177
4274
  .checked=${p}
4178
4275
  @click=${(e) => this._onRowCheckboxClick(e)}
4179
- @change=${(t) => this._onRowCheckboxChange(t, e)}>
4276
+ @change=${(t) => this._onRowCheckboxChange(t, e)}></label>
4180
4277
  </div>
4181
4278
  ` : "";
4182
- this.selectable && (h += 36);
4279
+ this.selectable && (h += this._checkboxColWidth);
4183
4280
  let _ = this._rowDrag?.active && this._rowDrag.rowIndex === e, v = this.showRowNumbers ? i`
4184
4281
  <div class="ft-row-num ${_ ? "ft-col-dragging" : ""}"
4185
4282
  style="position: absolute; top: 0; left: ${m + h}px; width: 48px; height: ${u}px; z-index: 2; cursor: grab;"
@@ -4394,4 +4491,4 @@ K([s({ type: Array })], $.prototype, "columns", void 0), K([s({
4394
4491
  attribute: "max-undo-size"
4395
4492
  })], $.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")], $);
4396
4493
  //#endregion
4397
- export { j as a, M as i, G as n, k as o, B as r, w as s, $ as t };
4494
+ export { j as a, M as i, G as n, k as o, V as r, w as s, $ as t };
@@ -52,6 +52,8 @@ export declare class FlexTable extends LitElement {
52
52
  private _rowHeightExplicit?;
53
53
  /** `--ft-row-height` 판독 결과. 판독 전·판독 실패 시 기본값. */
54
54
  private _rowHeightFromCss;
55
+ /** 호스트 하한 `--u-target-size`(px) 판독 결과 — 행(그러므로 머리행)과 선택 열 폭의 하한. 미설정이면 0. */
56
+ private _targetFloor;
55
57
  showRowNumbers: boolean;
56
58
  theme: 'light' | 'dark' | undefined;
57
59
  maxRows: number;
@@ -147,6 +149,8 @@ export declare class FlexTable extends LitElement {
147
149
  private _comments;
148
150
  private _isDragOver;
149
151
  get visibleColumns(): ColumnDefinition[];
152
+ /** 행 선택 열의 폭 — 36px, 호스트 하한(`--u-target-size`)이 더 크면 그 값. */
153
+ private get _checkboxColWidth();
150
154
  private get _prefixWidth();
151
155
  /** Whether an undo operation is available. */
152
156
  get canUndo(): boolean;
@@ -251,16 +255,27 @@ export declare class FlexTable extends LitElement {
251
255
  }>): void;
252
256
  private _createEmptyRow;
253
257
  /**
254
- * Export table data to string in the specified format.
258
+ * Export table data to string in the specified format. Synchronous — XLSX comes back as an
259
+ * uncompressed workbook; use `exportToBlob` for a compressed one.
255
260
  * @param options.selectionOnly - Export only the currently selected range
256
261
  */
257
262
  exportToString(format: ExportFormat, options?: {
258
263
  selectionOnly?: boolean;
259
264
  }): string | Uint8Array<ArrayBuffer>;
260
265
  /**
261
- * Export table data and trigger file download.
266
+ * Export table data as a `Blob` of the format's MIME type. XLSX is DEFLATE-compressed.
267
+ * @param options.selectionOnly - Export only the currently selected range
268
+ */
269
+ exportToBlob(format: ExportFormat, options?: {
270
+ selectionOnly?: boolean;
271
+ }): Promise<Blob>;
272
+ /**
273
+ * Export table data and trigger file download. XLSX is DEFLATE-compressed; the promise settles
274
+ * once the download has been handed to the browser.
262
275
  */
263
- exportToFile(format: ExportFormat, filename?: string): void;
276
+ exportToFile(format: ExportFormat, filename?: string): Promise<void>;
277
+ /** 내보낼 행·열 — 선택만이면 선택 범위(없으면 `null`), 아니면 정렬 순서의 전체 행 · 보이는 열. */
278
+ private _exportSlice;
264
279
  /**
265
280
  * Set a comment on the cell at the given data index and column key.
266
281
  * Pass an empty string or null to remove the comment.
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-BdnkC234.js";
1
+ import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-Bv41ls8b.js";
2
2
  export { o as FlexTable, e as RowSelectionState, t as UndoStack, n as effectiveAlign, i as exportData, r as flexTableLocale, a as renderCell };
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./flex-table-BdnkC234.js";
1
+ import { t as e } from "./flex-table-Bv41ls8b.js";
2
2
  import { i as t, t as n } from "./query-Dw4iO0ir.js";
3
3
  import { t as r } from "./view-DiLr-TdY.js";
4
4
  import i, { useCallback as a, useEffect as o, useMemo as s, useRef as c, useState as l } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/flex-table",
3
- "version": "0.43.0",
3
+ "version": "0.45.0",
4
4
  "description": "A minimalist, input-centric data grid web component",
5
5
  "type": "module",
6
6
  "main": "./dist/flex-table.js",
@@ -127,8 +127,9 @@ Enter/Escape and blur-to-commit behavior.
127
127
  | `setComment(dataIndex, colKey, text)` | `null` or `''` removes; undoable |
128
128
  | `getComment(dataIndex, colKey)` / `clearComments()` | |
129
129
  | `importFromFile(file)` | `Promise<void>`; `.xlsx`, `.csv`, `.tsv`; fires `data-import` |
130
- | `exportToString(format, { selectionOnly? })` | `string \| Uint8Array`; format `'csv' \| 'tsv' \| 'json' \| 'xlsx'` (xlsx returns bytes) |
131
- | `exportToFile(format, filename?)` | Triggers a download |
130
+ | `exportToString(format, { selectionOnly? })` | `string \| Uint8Array`; format `'csv' \| 'tsv' \| 'json' \| 'xlsx'` (xlsx returns uncompressed bytes — synchronous) |
131
+ | `exportToBlob(format, { selectionOnly? })` | `Promise<Blob>` typed with the format's MIME; xlsx is DEFLATE-compressed |
132
+ | `exportToFile(format, filename?)` | `Promise<void>` — triggers a download (xlsx compressed) |
132
133
 
133
134
  ## Events
134
135