@canvas-components/utils 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,96 +1,96 @@
1
- # @canvas-components/utils
2
-
3
- `@canvas-components/utils` 提供和表格外围常见流程相关的浏览器工具能力,主要覆盖剪贴板写入和导出文件。
4
-
5
- ## 功能作用
6
-
7
- - 写入文本、二维表格文本、JSON 到系统剪贴板
8
- - 把行列数据导出为 `csv`、`txt`、`xlsx`
9
- - 生成导出前的中间文本 / 记录结构
10
- - 对大批量导出做分片让步,减少主线程长时间阻塞
11
-
12
- ## 安装方法
13
-
14
- ```bash
15
- pnpm add @canvas-components/utils
16
- ```
17
-
18
- 最低环境要求:
19
-
20
- - `Node.js >= 18.12.0`
21
- - `pnpm >= 9.0.0`
22
-
23
- ## 使用方法
24
-
25
- ```ts
26
- import {
27
- copyRows,
28
- exportTableToXlsx,
29
- } from "@canvas-components/utils";
30
-
31
- await copyRows([
32
- ["姓名", "年龄"],
33
- ["张三", 28],
34
- ["李四", 31],
35
- ]);
36
-
37
- await exportTableToXlsx({
38
- fileName: "users.xlsx",
39
- rows: [
40
- { name: "张三", age: 28 },
41
- { name: "李四", age: 31 },
42
- ],
43
- columns: [
44
- { key: "name", title: "姓名", value: (row) => row.name },
45
- { key: "age", title: "年龄", value: (row) => row.age },
46
- ],
47
- });
48
- ```
49
-
50
- ## 属性说明
51
-
52
- ### `ExportTableColumn<Row>`
53
-
54
- | 属性 | 类型 | 说明 |
55
- | --- | --- | --- |
56
- | `key` | `string` | 列唯一标识 |
57
- | `title` | `string` | 导出列标题 |
58
- | `value` | `(row: Row, index: number) => unknown` | 从行数据中取导出值 |
59
-
60
- ### `ClipboardCellValue`
61
-
62
- 支持的剪贴板单元格值类型:
63
-
64
- ```ts
65
- type ClipboardCellValue = string | number | boolean | null | undefined;
66
- ```
67
-
68
- ## 方法说明
69
-
70
- ### 剪贴板
71
-
72
- | 方法 | 签名 | 说明 |
73
- | --- | --- | --- |
74
- | `copyText` | `(text: string) => Promise<void>` | 写入纯文本 |
75
- | `copyRows` | `(rows: ClipboardCellValue[][]) => Promise<void>` | 把二维数据写入剪贴板 |
76
- | `copyJson` | `(value: unknown, space = 2) => Promise<void>` | 写入 JSON 文本 |
77
- | `formatRowsForClipboard` | `(rows) => string` | 把二维数据转成制表符文本 |
78
-
79
- ### 导出
80
-
81
- | 方法 | 签名 | 说明 |
82
- | --- | --- | --- |
83
- | `mapRowsToExportRecords` | `({ rows, columns }) => Record<string, unknown>[]` | 生成导出记录数组 |
84
- | `buildCsvContent` | `({ rows, columns }) => string` | 生成 CSV 文本 |
85
- | `buildTxtContent` | `({ rows, columns }) => string` | 生成 TXT 文本 |
86
- | `downloadJsonFile` | `({ fileName, data, space? }) => Promise<void>` | 下载 JSON |
87
- | `exportTableToCsv` | `({ fileName, rows, columns }) => Promise<void>` | 导出 CSV |
88
- | `exportTableToTxt` | `({ fileName, rows, columns }) => Promise<void>` | 导出 TXT |
89
- | `exportTableToXlsx` | `({ fileName, rows, columns, sheetName? }) => Promise<void>` | 导出 XLSX |
90
-
91
- ## 适用场景
92
-
93
- - 表格导出
94
- - 系统复制功能
95
- - 大批量数据下载
96
- - 非 React 项目里的浏览器工具函数复用
1
+ # @canvas-components/utils
2
+
3
+ `@canvas-components/utils` 提供和表格外围常见流程相关的浏览器工具能力,主要覆盖剪贴板写入和导出文件。
4
+
5
+ ## 功能作用
6
+
7
+ - 写入文本、二维表格文本、JSON 到系统剪贴板
8
+ - 把行列数据导出为 `csv`、`txt`、`xlsx`
9
+ - 生成导出前的中间文本 / 记录结构
10
+ - 对大批量导出做分片让步,减少主线程长时间阻塞
11
+
12
+ ## 安装方法
13
+
14
+ ```bash
15
+ pnpm add @canvas-components/utils
16
+ ```
17
+
18
+ 最低环境要求:
19
+
20
+ - `Node.js >= 18.12.0`
21
+ - `pnpm >= 9.0.0`
22
+
23
+ ## 使用方法
24
+
25
+ ```ts
26
+ import {
27
+ copyRows,
28
+ exportTableToXlsx,
29
+ } from "@canvas-components/utils";
30
+
31
+ await copyRows([
32
+ ["姓名", "年龄"],
33
+ ["张三", 28],
34
+ ["李四", 31],
35
+ ]);
36
+
37
+ await exportTableToXlsx({
38
+ fileName: "users.xlsx",
39
+ rows: [
40
+ { name: "张三", age: 28 },
41
+ { name: "李四", age: 31 },
42
+ ],
43
+ columns: [
44
+ { key: "name", title: "姓名", value: (row) => row.name },
45
+ { key: "age", title: "年龄", value: (row) => row.age },
46
+ ],
47
+ });
48
+ ```
49
+
50
+ ## 属性说明
51
+
52
+ ### `ExportTableColumn<Row>`
53
+
54
+ | 属性 | 类型 | 说明 |
55
+ | --- | --- | --- |
56
+ | `key` | `string` | 列唯一标识 |
57
+ | `title` | `string` | 导出列标题 |
58
+ | `value` | `(row: Row, index: number) => unknown` | 从行数据中取导出值 |
59
+
60
+ ### `ClipboardCellValue`
61
+
62
+ 支持的剪贴板单元格值类型:
63
+
64
+ ```ts
65
+ type ClipboardCellValue = string | number | boolean | null | undefined;
66
+ ```
67
+
68
+ ## 方法说明
69
+
70
+ ### 剪贴板
71
+
72
+ | 方法 | 签名 | 说明 |
73
+ | --- | --- | --- |
74
+ | `copyText` | `(text: string) => Promise<void>` | 写入纯文本 |
75
+ | `copyRows` | `(rows: ClipboardCellValue[][]) => Promise<void>` | 把二维数据写入剪贴板 |
76
+ | `copyJson` | `(value: unknown, space = 2) => Promise<void>` | 写入 JSON 文本 |
77
+ | `formatRowsForClipboard` | `(rows) => string` | 把二维数据转成制表符文本 |
78
+
79
+ ### 导出
80
+
81
+ | 方法 | 签名 | 说明 |
82
+ | --- | --- | --- |
83
+ | `mapRowsToExportRecords` | `({ rows, columns }) => Record<string, unknown>[]` | 生成导出记录数组 |
84
+ | `buildCsvContent` | `({ rows, columns }) => string` | 生成 CSV 文本 |
85
+ | `buildTxtContent` | `({ rows, columns }) => string` | 生成 TXT 文本 |
86
+ | `downloadJsonFile` | `({ fileName, data, space? }) => Promise<void>` | 下载 JSON |
87
+ | `exportTableToCsv` | `({ fileName, rows, columns }) => Promise<void>` | 导出 CSV |
88
+ | `exportTableToTxt` | `({ fileName, rows, columns }) => Promise<void>` | 导出 TXT |
89
+ | `exportTableToXlsx` | `({ fileName, rows, columns, sheetName? }) => Promise<void>` | 导出 XLSX |
90
+
91
+ ## 适用场景
92
+
93
+ - 表格导出
94
+ - 系统复制功能
95
+ - 大批量数据下载
96
+ - 非 React 项目里的浏览器工具函数复用
@@ -2,11 +2,39 @@
2
2
 
3
3
  // src/clipboard/index.ts
4
4
  async function copyText(text) {
5
- if (navigator.clipboard?.writeText) {
6
- await navigator.clipboard.writeText(text);
7
- return;
5
+ try {
6
+ if (navigator.clipboard?.writeText) {
7
+ await navigator.clipboard.writeText(text);
8
+ return;
9
+ }
10
+ } catch {
11
+ }
12
+ copyTextSync(text);
13
+ }
14
+ function copyTextSync(text, ownerDocument = document) {
15
+ const activeElement = ownerDocument.activeElement;
16
+ const textarea = ownerDocument.createElement("textarea");
17
+ textarea.value = text;
18
+ textarea.setAttribute("readonly", "true");
19
+ textarea.style.position = "fixed";
20
+ textarea.style.left = "-9999px";
21
+ textarea.style.top = "0";
22
+ textarea.style.opacity = "0";
23
+ ownerDocument.body.appendChild(textarea);
24
+ textarea.focus({ preventScroll: true });
25
+ textarea.select();
26
+ textarea.setSelectionRange(0, textarea.value.length);
27
+ let copied = false;
28
+ try {
29
+ copied = ownerDocument.execCommand?.("copy") ?? false;
30
+ } finally {
31
+ textarea.remove();
32
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;
33
+ if (HTMLElementConstructor && activeElement instanceof HTMLElementConstructor) {
34
+ activeElement.focus({ preventScroll: true });
35
+ }
8
36
  }
9
- copyTextByTextarea(text);
37
+ return copied;
10
38
  }
11
39
  async function copyRows(rows) {
12
40
  await copyText(formatRowsForClipboard(rows));
@@ -23,27 +51,11 @@ function formatClipboardCell(value) {
23
51
  }
24
52
  return String(value);
25
53
  }
26
- function copyTextByTextarea(text) {
27
- const textarea = document.createElement("textarea");
28
- textarea.value = text;
29
- textarea.setAttribute("readonly", "true");
30
- textarea.style.position = "fixed";
31
- textarea.style.left = "-9999px";
32
- textarea.style.top = "0";
33
- textarea.style.opacity = "0";
34
- document.body.appendChild(textarea);
35
- textarea.select();
36
- textarea.setSelectionRange(0, textarea.value.length);
37
- try {
38
- document.execCommand("copy");
39
- } finally {
40
- textarea.remove();
41
- }
42
- }
43
54
 
44
55
  exports.copyJson = copyJson;
45
56
  exports.copyRows = copyRows;
46
57
  exports.copyText = copyText;
58
+ exports.copyTextSync = copyTextSync;
47
59
  exports.formatRowsForClipboard = formatRowsForClipboard;
48
60
  //# sourceMappingURL=index.cjs.map
49
61
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/clipboard/index.ts"],"names":[],"mappings":";;;AAUA,eAAsB,SAAS,IAAA,EAAc;AAC3C,EAAA,IAAI,SAAA,CAAU,WAAW,SAAA,EAAW;AAClC,IAAA,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAU,IAAI,CAAA;AACxC,IAAA;AAAA,EACF;AAEA,EAAA,kBAAA,CAAmB,IAAI,CAAA;AACzB;AAKA,eAAsB,SAAS,IAAA,EAA8B;AAC3D,EAAA,MAAM,QAAA,CAAS,sBAAA,CAAuB,IAAI,CAAC,CAAA;AAC7C;AAKA,eAAsB,QAAA,CAAS,KAAA,EAAgB,KAAA,GAAQ,CAAA,EAAG;AACxD,EAAA,MAAM,SAAS,IAAA,CAAK,SAAA,CAAU,OAAO,IAAA,EAAM,KAAK,KAAK,EAAE,CAAA;AACzD;AAKO,SAAS,uBAAuB,IAAA,EAA8B;AACnE,EAAA,OAAO,IAAA,CACJ,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAA,CAAI,GAAA,CAAI,mBAAmB,CAAA,CAAE,IAAA,CAAK,GAAI,CAAC,CAAA,CACpD,KAAK,IAAI,CAAA;AACd;AAEA,SAAS,oBAAoB,KAAA,EAA2B;AACtD,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAO,KAAK,CAAA;AACrB;AAEA,SAAS,mBAAmB,IAAA,EAAc;AACxC,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,aAAA,CAAc,UAAU,CAAA;AAClD,EAAA,QAAA,CAAS,KAAA,GAAQ,IAAA;AACjB,EAAA,QAAA,CAAS,YAAA,CAAa,YAAY,MAAM,CAAA;AACxC,EAAA,QAAA,CAAS,MAAM,QAAA,GAAW,OAAA;AAC1B,EAAA,QAAA,CAAS,MAAM,IAAA,GAAO,SAAA;AACtB,EAAA,QAAA,CAAS,MAAM,GAAA,GAAM,GAAA;AACrB,EAAA,QAAA,CAAS,MAAM,OAAA,GAAU,GAAA;AAEzB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,QAAQ,CAAA;AAClC,EAAA,QAAA,CAAS,MAAA,EAAO;AAChB,EAAA,QAAA,CAAS,iBAAA,CAAkB,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AAEnD,EAAA,IAAI;AACF,IAAA,QAAA,CAAS,YAAY,MAAM,CAAA;AAAA,EAC7B,CAAA,SAAE;AACA,IAAA,QAAA,CAAS,MAAA,EAAO;AAAA,EAClB;AACF","file":"index.cjs","sourcesContent":["export type ClipboardCellValue =\r\n | string\r\n | number\r\n | boolean\r\n | null\r\n | undefined;\r\n\r\n/**\r\n * 写入纯文本到系统剪贴板。\r\n */\r\nexport async function copyText(text: string) {\r\n if (navigator.clipboard?.writeText) {\r\n await navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n copyTextByTextarea(text);\r\n}\r\n\r\n/**\r\n * 写入二维表格文本到系统剪贴板。\r\n */\r\nexport async function copyRows(rows: ClipboardCellValue[][]) {\r\n await copyText(formatRowsForClipboard(rows));\r\n}\r\n\r\n/**\r\n * 写入 JSON 文本到系统剪贴板。\r\n */\r\nexport async function copyJson(value: unknown, space = 2) {\r\n await copyText(JSON.stringify(value, null, space) ?? \"\");\r\n}\r\n\r\n/**\r\n * 格式化二维数据为剪贴板友好的文本。\r\n */\r\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\r\n return rows\r\n .map((row) => row.map(formatClipboardCell).join(\"\\t\"))\r\n .join(\"\\n\");\r\n}\r\n\r\nfunction formatClipboardCell(value: ClipboardCellValue) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction copyTextByTextarea(text: string) {\r\n const textarea = document.createElement(\"textarea\");\r\n textarea.value = text;\r\n textarea.setAttribute(\"readonly\", \"true\");\r\n textarea.style.position = \"fixed\";\r\n textarea.style.left = \"-9999px\";\r\n textarea.style.top = \"0\";\r\n textarea.style.opacity = \"0\";\r\n\r\n document.body.appendChild(textarea);\r\n textarea.select();\r\n textarea.setSelectionRange(0, textarea.value.length);\r\n\r\n try {\r\n document.execCommand(\"copy\");\r\n } finally {\r\n textarea.remove();\r\n }\r\n}\r\n"]}
1
+ {"version":3,"sources":["../../src/clipboard/index.ts"],"names":[],"mappings":";;;AAKA,eAAsB,SAAS,IAAA,EAAc;AAC3C,EAAA,IAAI;AACF,IAAA,IAAI,SAAA,CAAU,WAAW,SAAA,EAAW;AAClC,MAAA,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAU,IAAI,CAAA;AACxC,MAAA;AAAA,IACF;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,YAAA,CAAa,IAAI,CAAA;AACnB;AAGO,SAAS,YAAA,CAAa,IAAA,EAAc,aAAA,GAAgB,QAAA,EAAU;AACnE,EAAA,MAAM,gBAAgB,aAAA,CAAc,aAAA;AACpC,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,aAAA,CAAc,UAAU,CAAA;AACvD,EAAA,QAAA,CAAS,KAAA,GAAQ,IAAA;AACjB,EAAA,QAAA,CAAS,YAAA,CAAa,YAAY,MAAM,CAAA;AACxC,EAAA,QAAA,CAAS,MAAM,QAAA,GAAW,OAAA;AAC1B,EAAA,QAAA,CAAS,MAAM,IAAA,GAAO,SAAA;AACtB,EAAA,QAAA,CAAS,MAAM,GAAA,GAAM,GAAA;AACrB,EAAA,QAAA,CAAS,MAAM,OAAA,GAAU,GAAA;AAEzB,EAAA,aAAA,CAAc,IAAA,CAAK,YAAY,QAAQ,CAAA;AACvC,EAAA,QAAA,CAAS,KAAA,CAAM,EAAE,aAAA,EAAe,IAAA,EAAM,CAAA;AACtC,EAAA,QAAA,CAAS,MAAA,EAAO;AAChB,EAAA,QAAA,CAAS,iBAAA,CAAkB,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AAEnD,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,aAAA,CAAc,WAAA,GAAc,MAAM,CAAA,IAAK,KAAA;AAAA,EAClD,CAAA,SAAE;AACA,IAAA,QAAA,CAAS,MAAA,EAAO;AAChB,IAAA,MAAM,sBAAA,GAAyB,cAAc,WAAA,EAAa,WAAA;AAC1D,IAAA,IACE,sBAAA,IACA,yBAAyB,sBAAA,EACzB;AACA,MAAA,aAAA,CAAc,KAAA,CAAM,EAAE,aAAA,EAAe,IAAA,EAAM,CAAA;AAAA,IAC7C;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAKA,eAAsB,SAAS,IAAA,EAA8B;AAC3D,EAAA,MAAM,QAAA,CAAS,sBAAA,CAAuB,IAAI,CAAC,CAAA;AAC7C;AAKA,eAAsB,QAAA,CAAS,KAAA,EAAgB,KAAA,GAAQ,CAAA,EAAG;AACxD,EAAA,MAAM,SAAS,IAAA,CAAK,SAAA,CAAU,OAAO,IAAA,EAAM,KAAK,KAAK,EAAE,CAAA;AACzD;AAKO,SAAS,uBAAuB,IAAA,EAA8B;AACnE,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAA,CAAI,GAAA,CAAI,mBAAmB,CAAA,CAAE,IAAA,CAAK,GAAI,CAAC,CAAA,CAAE,KAAK,IAAI,CAAA;AAC7E;AAEA,SAAS,oBAAoB,KAAA,EAA2B;AACtD,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAO,KAAK,CAAA;AACrB","file":"index.cjs","sourcesContent":["export type ClipboardCellValue = string | number | boolean | null | undefined;\n\n/**\n * 写入纯文本到系统剪贴板。\n */\nexport async function copyText(text: string) {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return;\n }\n } catch {\n // 非安全上下文或权限拒绝时回退到同步 DOM 复制。\n }\n\n copyTextSync(text);\n}\n\n/** 在当前用户激活事件中同步复制纯文本。 */\nexport function copyTextSync(text: string, ownerDocument = document) {\n const activeElement = ownerDocument.activeElement;\n const textarea = ownerDocument.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"true\");\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n textarea.style.top = \"0\";\n textarea.style.opacity = \"0\";\n\n ownerDocument.body.appendChild(textarea);\n textarea.focus({ preventScroll: true });\n textarea.select();\n textarea.setSelectionRange(0, textarea.value.length);\n\n let copied = false;\n try {\n copied = ownerDocument.execCommand?.(\"copy\") ?? false;\n } finally {\n textarea.remove();\n const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;\n if (\n HTMLElementConstructor &&\n activeElement instanceof HTMLElementConstructor\n ) {\n activeElement.focus({ preventScroll: true });\n }\n }\n return copied;\n}\n\n/**\n * 写入二维表格文本到系统剪贴板。\n */\nexport async function copyRows(rows: ClipboardCellValue[][]) {\n await copyText(formatRowsForClipboard(rows));\n}\n\n/**\n * 写入 JSON 文本到系统剪贴板。\n */\nexport async function copyJson(value: unknown, space = 2) {\n await copyText(JSON.stringify(value, null, space) ?? \"\");\n}\n\n/**\n * 格式化二维数据为剪贴板友好的文本。\n */\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\n return rows.map((row) => row.map(formatClipboardCell).join(\"\\t\")).join(\"\\n\");\n}\n\nfunction formatClipboardCell(value: ClipboardCellValue) {\n if (value == null) {\n return \"\";\n }\n\n return String(value);\n}\n"]}
@@ -3,6 +3,8 @@ type ClipboardCellValue = string | number | boolean | null | undefined;
3
3
  * 写入纯文本到系统剪贴板。
4
4
  */
5
5
  declare function copyText(text: string): Promise<void>;
6
+ /** 在当前用户激活事件中同步复制纯文本。 */
7
+ declare function copyTextSync(text: string, ownerDocument?: Document): boolean;
6
8
  /**
7
9
  * 写入二维表格文本到系统剪贴板。
8
10
  */
@@ -16,4 +18,4 @@ declare function copyJson(value: unknown, space?: number): Promise<void>;
16
18
  */
17
19
  declare function formatRowsForClipboard(rows: ClipboardCellValue[][]): string;
18
20
 
19
- export { type ClipboardCellValue, copyJson, copyRows, copyText, formatRowsForClipboard };
21
+ export { type ClipboardCellValue, copyJson, copyRows, copyText, copyTextSync, formatRowsForClipboard };
@@ -3,6 +3,8 @@ type ClipboardCellValue = string | number | boolean | null | undefined;
3
3
  * 写入纯文本到系统剪贴板。
4
4
  */
5
5
  declare function copyText(text: string): Promise<void>;
6
+ /** 在当前用户激活事件中同步复制纯文本。 */
7
+ declare function copyTextSync(text: string, ownerDocument?: Document): boolean;
6
8
  /**
7
9
  * 写入二维表格文本到系统剪贴板。
8
10
  */
@@ -16,4 +18,4 @@ declare function copyJson(value: unknown, space?: number): Promise<void>;
16
18
  */
17
19
  declare function formatRowsForClipboard(rows: ClipboardCellValue[][]): string;
18
20
 
19
- export { type ClipboardCellValue, copyJson, copyRows, copyText, formatRowsForClipboard };
21
+ export { type ClipboardCellValue, copyJson, copyRows, copyText, copyTextSync, formatRowsForClipboard };
@@ -1,10 +1,38 @@
1
1
  // src/clipboard/index.ts
2
2
  async function copyText(text) {
3
- if (navigator.clipboard?.writeText) {
4
- await navigator.clipboard.writeText(text);
5
- return;
3
+ try {
4
+ if (navigator.clipboard?.writeText) {
5
+ await navigator.clipboard.writeText(text);
6
+ return;
7
+ }
8
+ } catch {
9
+ }
10
+ copyTextSync(text);
11
+ }
12
+ function copyTextSync(text, ownerDocument = document) {
13
+ const activeElement = ownerDocument.activeElement;
14
+ const textarea = ownerDocument.createElement("textarea");
15
+ textarea.value = text;
16
+ textarea.setAttribute("readonly", "true");
17
+ textarea.style.position = "fixed";
18
+ textarea.style.left = "-9999px";
19
+ textarea.style.top = "0";
20
+ textarea.style.opacity = "0";
21
+ ownerDocument.body.appendChild(textarea);
22
+ textarea.focus({ preventScroll: true });
23
+ textarea.select();
24
+ textarea.setSelectionRange(0, textarea.value.length);
25
+ let copied = false;
26
+ try {
27
+ copied = ownerDocument.execCommand?.("copy") ?? false;
28
+ } finally {
29
+ textarea.remove();
30
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;
31
+ if (HTMLElementConstructor && activeElement instanceof HTMLElementConstructor) {
32
+ activeElement.focus({ preventScroll: true });
33
+ }
6
34
  }
7
- copyTextByTextarea(text);
35
+ return copied;
8
36
  }
9
37
  async function copyRows(rows) {
10
38
  await copyText(formatRowsForClipboard(rows));
@@ -21,24 +49,7 @@ function formatClipboardCell(value) {
21
49
  }
22
50
  return String(value);
23
51
  }
24
- function copyTextByTextarea(text) {
25
- const textarea = document.createElement("textarea");
26
- textarea.value = text;
27
- textarea.setAttribute("readonly", "true");
28
- textarea.style.position = "fixed";
29
- textarea.style.left = "-9999px";
30
- textarea.style.top = "0";
31
- textarea.style.opacity = "0";
32
- document.body.appendChild(textarea);
33
- textarea.select();
34
- textarea.setSelectionRange(0, textarea.value.length);
35
- try {
36
- document.execCommand("copy");
37
- } finally {
38
- textarea.remove();
39
- }
40
- }
41
52
 
42
- export { copyJson, copyRows, copyText, formatRowsForClipboard };
53
+ export { copyJson, copyRows, copyText, copyTextSync, formatRowsForClipboard };
43
54
  //# sourceMappingURL=index.js.map
44
55
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/clipboard/index.ts"],"names":[],"mappings":";AAUA,eAAsB,SAAS,IAAA,EAAc;AAC3C,EAAA,IAAI,SAAA,CAAU,WAAW,SAAA,EAAW;AAClC,IAAA,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAU,IAAI,CAAA;AACxC,IAAA;AAAA,EACF;AAEA,EAAA,kBAAA,CAAmB,IAAI,CAAA;AACzB;AAKA,eAAsB,SAAS,IAAA,EAA8B;AAC3D,EAAA,MAAM,QAAA,CAAS,sBAAA,CAAuB,IAAI,CAAC,CAAA;AAC7C;AAKA,eAAsB,QAAA,CAAS,KAAA,EAAgB,KAAA,GAAQ,CAAA,EAAG;AACxD,EAAA,MAAM,SAAS,IAAA,CAAK,SAAA,CAAU,OAAO,IAAA,EAAM,KAAK,KAAK,EAAE,CAAA;AACzD;AAKO,SAAS,uBAAuB,IAAA,EAA8B;AACnE,EAAA,OAAO,IAAA,CACJ,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAA,CAAI,GAAA,CAAI,mBAAmB,CAAA,CAAE,IAAA,CAAK,GAAI,CAAC,CAAA,CACpD,KAAK,IAAI,CAAA;AACd;AAEA,SAAS,oBAAoB,KAAA,EAA2B;AACtD,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAO,KAAK,CAAA;AACrB;AAEA,SAAS,mBAAmB,IAAA,EAAc;AACxC,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,aAAA,CAAc,UAAU,CAAA;AAClD,EAAA,QAAA,CAAS,KAAA,GAAQ,IAAA;AACjB,EAAA,QAAA,CAAS,YAAA,CAAa,YAAY,MAAM,CAAA;AACxC,EAAA,QAAA,CAAS,MAAM,QAAA,GAAW,OAAA;AAC1B,EAAA,QAAA,CAAS,MAAM,IAAA,GAAO,SAAA;AACtB,EAAA,QAAA,CAAS,MAAM,GAAA,GAAM,GAAA;AACrB,EAAA,QAAA,CAAS,MAAM,OAAA,GAAU,GAAA;AAEzB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,QAAQ,CAAA;AAClC,EAAA,QAAA,CAAS,MAAA,EAAO;AAChB,EAAA,QAAA,CAAS,iBAAA,CAAkB,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AAEnD,EAAA,IAAI;AACF,IAAA,QAAA,CAAS,YAAY,MAAM,CAAA;AAAA,EAC7B,CAAA,SAAE;AACA,IAAA,QAAA,CAAS,MAAA,EAAO;AAAA,EAClB;AACF","file":"index.js","sourcesContent":["export type ClipboardCellValue =\r\n | string\r\n | number\r\n | boolean\r\n | null\r\n | undefined;\r\n\r\n/**\r\n * 写入纯文本到系统剪贴板。\r\n */\r\nexport async function copyText(text: string) {\r\n if (navigator.clipboard?.writeText) {\r\n await navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n copyTextByTextarea(text);\r\n}\r\n\r\n/**\r\n * 写入二维表格文本到系统剪贴板。\r\n */\r\nexport async function copyRows(rows: ClipboardCellValue[][]) {\r\n await copyText(formatRowsForClipboard(rows));\r\n}\r\n\r\n/**\r\n * 写入 JSON 文本到系统剪贴板。\r\n */\r\nexport async function copyJson(value: unknown, space = 2) {\r\n await copyText(JSON.stringify(value, null, space) ?? \"\");\r\n}\r\n\r\n/**\r\n * 格式化二维数据为剪贴板友好的文本。\r\n */\r\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\r\n return rows\r\n .map((row) => row.map(formatClipboardCell).join(\"\\t\"))\r\n .join(\"\\n\");\r\n}\r\n\r\nfunction formatClipboardCell(value: ClipboardCellValue) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction copyTextByTextarea(text: string) {\r\n const textarea = document.createElement(\"textarea\");\r\n textarea.value = text;\r\n textarea.setAttribute(\"readonly\", \"true\");\r\n textarea.style.position = \"fixed\";\r\n textarea.style.left = \"-9999px\";\r\n textarea.style.top = \"0\";\r\n textarea.style.opacity = \"0\";\r\n\r\n document.body.appendChild(textarea);\r\n textarea.select();\r\n textarea.setSelectionRange(0, textarea.value.length);\r\n\r\n try {\r\n document.execCommand(\"copy\");\r\n } finally {\r\n textarea.remove();\r\n }\r\n}\r\n"]}
1
+ {"version":3,"sources":["../../src/clipboard/index.ts"],"names":[],"mappings":";AAKA,eAAsB,SAAS,IAAA,EAAc;AAC3C,EAAA,IAAI;AACF,IAAA,IAAI,SAAA,CAAU,WAAW,SAAA,EAAW;AAClC,MAAA,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAU,IAAI,CAAA;AACxC,MAAA;AAAA,IACF;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,YAAA,CAAa,IAAI,CAAA;AACnB;AAGO,SAAS,YAAA,CAAa,IAAA,EAAc,aAAA,GAAgB,QAAA,EAAU;AACnE,EAAA,MAAM,gBAAgB,aAAA,CAAc,aAAA;AACpC,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,aAAA,CAAc,UAAU,CAAA;AACvD,EAAA,QAAA,CAAS,KAAA,GAAQ,IAAA;AACjB,EAAA,QAAA,CAAS,YAAA,CAAa,YAAY,MAAM,CAAA;AACxC,EAAA,QAAA,CAAS,MAAM,QAAA,GAAW,OAAA;AAC1B,EAAA,QAAA,CAAS,MAAM,IAAA,GAAO,SAAA;AACtB,EAAA,QAAA,CAAS,MAAM,GAAA,GAAM,GAAA;AACrB,EAAA,QAAA,CAAS,MAAM,OAAA,GAAU,GAAA;AAEzB,EAAA,aAAA,CAAc,IAAA,CAAK,YAAY,QAAQ,CAAA;AACvC,EAAA,QAAA,CAAS,KAAA,CAAM,EAAE,aAAA,EAAe,IAAA,EAAM,CAAA;AACtC,EAAA,QAAA,CAAS,MAAA,EAAO;AAChB,EAAA,QAAA,CAAS,iBAAA,CAAkB,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AAEnD,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,aAAA,CAAc,WAAA,GAAc,MAAM,CAAA,IAAK,KAAA;AAAA,EAClD,CAAA,SAAE;AACA,IAAA,QAAA,CAAS,MAAA,EAAO;AAChB,IAAA,MAAM,sBAAA,GAAyB,cAAc,WAAA,EAAa,WAAA;AAC1D,IAAA,IACE,sBAAA,IACA,yBAAyB,sBAAA,EACzB;AACA,MAAA,aAAA,CAAc,KAAA,CAAM,EAAE,aAAA,EAAe,IAAA,EAAM,CAAA;AAAA,IAC7C;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAKA,eAAsB,SAAS,IAAA,EAA8B;AAC3D,EAAA,MAAM,QAAA,CAAS,sBAAA,CAAuB,IAAI,CAAC,CAAA;AAC7C;AAKA,eAAsB,QAAA,CAAS,KAAA,EAAgB,KAAA,GAAQ,CAAA,EAAG;AACxD,EAAA,MAAM,SAAS,IAAA,CAAK,SAAA,CAAU,OAAO,IAAA,EAAM,KAAK,KAAK,EAAE,CAAA;AACzD;AAKO,SAAS,uBAAuB,IAAA,EAA8B;AACnE,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAA,CAAI,GAAA,CAAI,mBAAmB,CAAA,CAAE,IAAA,CAAK,GAAI,CAAC,CAAA,CAAE,KAAK,IAAI,CAAA;AAC7E;AAEA,SAAS,oBAAoB,KAAA,EAA2B;AACtD,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAO,KAAK,CAAA;AACrB","file":"index.js","sourcesContent":["export type ClipboardCellValue = string | number | boolean | null | undefined;\n\n/**\n * 写入纯文本到系统剪贴板。\n */\nexport async function copyText(text: string) {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return;\n }\n } catch {\n // 非安全上下文或权限拒绝时回退到同步 DOM 复制。\n }\n\n copyTextSync(text);\n}\n\n/** 在当前用户激活事件中同步复制纯文本。 */\nexport function copyTextSync(text: string, ownerDocument = document) {\n const activeElement = ownerDocument.activeElement;\n const textarea = ownerDocument.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"true\");\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n textarea.style.top = \"0\";\n textarea.style.opacity = \"0\";\n\n ownerDocument.body.appendChild(textarea);\n textarea.focus({ preventScroll: true });\n textarea.select();\n textarea.setSelectionRange(0, textarea.value.length);\n\n let copied = false;\n try {\n copied = ownerDocument.execCommand?.(\"copy\") ?? false;\n } finally {\n textarea.remove();\n const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;\n if (\n HTMLElementConstructor &&\n activeElement instanceof HTMLElementConstructor\n ) {\n activeElement.focus({ preventScroll: true });\n }\n }\n return copied;\n}\n\n/**\n * 写入二维表格文本到系统剪贴板。\n */\nexport async function copyRows(rows: ClipboardCellValue[][]) {\n await copyText(formatRowsForClipboard(rows));\n}\n\n/**\n * 写入 JSON 文本到系统剪贴板。\n */\nexport async function copyJson(value: unknown, space = 2) {\n await copyText(JSON.stringify(value, null, space) ?? \"\");\n}\n\n/**\n * 格式化二维数据为剪贴板友好的文本。\n */\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\n return rows.map((row) => row.map(formatClipboardCell).join(\"\\t\")).join(\"\\n\");\n}\n\nfunction formatClipboardCell(value: ClipboardCellValue) {\n if (value == null) {\n return \"\";\n }\n\n return String(value);\n}\n"]}
package/dist/index.cjs CHANGED
@@ -24,11 +24,39 @@ var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
24
24
 
25
25
  // src/clipboard/index.ts
26
26
  async function copyText(text) {
27
- if (navigator.clipboard?.writeText) {
28
- await navigator.clipboard.writeText(text);
29
- return;
27
+ try {
28
+ if (navigator.clipboard?.writeText) {
29
+ await navigator.clipboard.writeText(text);
30
+ return;
31
+ }
32
+ } catch {
33
+ }
34
+ copyTextSync(text);
35
+ }
36
+ function copyTextSync(text, ownerDocument = document) {
37
+ const activeElement = ownerDocument.activeElement;
38
+ const textarea = ownerDocument.createElement("textarea");
39
+ textarea.value = text;
40
+ textarea.setAttribute("readonly", "true");
41
+ textarea.style.position = "fixed";
42
+ textarea.style.left = "-9999px";
43
+ textarea.style.top = "0";
44
+ textarea.style.opacity = "0";
45
+ ownerDocument.body.appendChild(textarea);
46
+ textarea.focus({ preventScroll: true });
47
+ textarea.select();
48
+ textarea.setSelectionRange(0, textarea.value.length);
49
+ let copied = false;
50
+ try {
51
+ copied = ownerDocument.execCommand?.("copy") ?? false;
52
+ } finally {
53
+ textarea.remove();
54
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;
55
+ if (HTMLElementConstructor && activeElement instanceof HTMLElementConstructor) {
56
+ activeElement.focus({ preventScroll: true });
57
+ }
30
58
  }
31
- copyTextByTextarea(text);
59
+ return copied;
32
60
  }
33
61
  async function copyRows(rows) {
34
62
  await copyText(formatRowsForClipboard(rows));
@@ -45,23 +73,6 @@ function formatClipboardCell(value) {
45
73
  }
46
74
  return String(value);
47
75
  }
48
- function copyTextByTextarea(text) {
49
- const textarea = document.createElement("textarea");
50
- textarea.value = text;
51
- textarea.setAttribute("readonly", "true");
52
- textarea.style.position = "fixed";
53
- textarea.style.left = "-9999px";
54
- textarea.style.top = "0";
55
- textarea.style.opacity = "0";
56
- document.body.appendChild(textarea);
57
- textarea.select();
58
- textarea.setSelectionRange(0, textarea.value.length);
59
- try {
60
- document.execCommand("copy");
61
- } finally {
62
- textarea.remove();
63
- }
64
- }
65
76
  var EXPORT_CHUNK_SIZE = 2e3;
66
77
  function mapRowsToExportRecords(params) {
67
78
  const { rows, columns } = params;
@@ -253,6 +264,7 @@ exports.buildTxtContent = buildTxtContent;
253
264
  exports.copyJson = copyJson;
254
265
  exports.copyRows = copyRows;
255
266
  exports.copyText = copyText;
267
+ exports.copyTextSync = copyTextSync;
256
268
  exports.downloadJsonFile = downloadJsonFile;
257
269
  exports.exportTableToCsv = exportTableToCsv;
258
270
  exports.exportTableToTxt = exportTableToTxt;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/clipboard/index.ts","../src/export/index.ts"],"names":["XLSX"],"mappings":"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","file":"index.cjs","sourcesContent":["export type ClipboardCellValue =\r\n | string\r\n | number\r\n | boolean\r\n | null\r\n | undefined;\r\n\r\n/**\r\n * 写入纯文本到系统剪贴板。\r\n */\r\nexport async function copyText(text: string) {\r\n if (navigator.clipboard?.writeText) {\r\n await navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n copyTextByTextarea(text);\r\n}\r\n\r\n/**\r\n * 写入二维表格文本到系统剪贴板。\r\n */\r\nexport async function copyRows(rows: ClipboardCellValue[][]) {\r\n await copyText(formatRowsForClipboard(rows));\r\n}\r\n\r\n/**\r\n * 写入 JSON 文本到系统剪贴板。\r\n */\r\nexport async function copyJson(value: unknown, space = 2) {\r\n await copyText(JSON.stringify(value, null, space) ?? \"\");\r\n}\r\n\r\n/**\r\n * 格式化二维数据为剪贴板友好的文本。\r\n */\r\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\r\n return rows\r\n .map((row) => row.map(formatClipboardCell).join(\"\\t\"))\r\n .join(\"\\n\");\r\n}\r\n\r\nfunction formatClipboardCell(value: ClipboardCellValue) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction copyTextByTextarea(text: string) {\r\n const textarea = document.createElement(\"textarea\");\r\n textarea.value = text;\r\n textarea.setAttribute(\"readonly\", \"true\");\r\n textarea.style.position = \"fixed\";\r\n textarea.style.left = \"-9999px\";\r\n textarea.style.top = \"0\";\r\n textarea.style.opacity = \"0\";\r\n\r\n document.body.appendChild(textarea);\r\n textarea.select();\r\n textarea.setSelectionRange(0, textarea.value.length);\r\n\r\n try {\r\n document.execCommand(\"copy\");\r\n } finally {\r\n textarea.remove();\r\n }\r\n}\r\n","import * as XLSX from \"xlsx\";\r\n\r\nconst EXPORT_CHUNK_SIZE = 2_000;\r\n\r\nexport interface ExportTableColumn<Row> {\r\n key: string;\r\n title: string;\r\n value: (row: Row, index: number) => unknown;\r\n}\r\n\r\n/**\r\n * 将表格行映射为适合导出的对象数组。\r\n */\r\nexport function mapRowsToExportRecords<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n return rows.map((row, rowIndex) =>\r\n columns.reduce<Record<string, unknown>>((record, column) => {\r\n record[column.title] = column.value(row, rowIndex);\r\n return record;\r\n }, {}),\r\n );\r\n}\r\n\r\n/**\r\n * 生成 CSV 文本内容。\r\n */\r\nexport function buildCsvContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => escapeCsvCell(column.value(row, rowIndex)))\r\n .join(\",\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 生成 TXT 文本内容。\r\n */\r\nexport function buildTxtContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => normalizeTextCell(column.value(row, rowIndex)))\r\n .join(\"\\t\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 下载 JSON 文件。\r\n */\r\nexport async function downloadJsonFile(params: {\r\n fileName: string;\r\n data: unknown;\r\n space?: number;\r\n}) {\r\n const { data, fileName, space = 2 } = params;\r\n const content = await buildJsonContentAsync({ data, space });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"json\"),\r\n content,\r\n type: \"application/json;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 CSV 文件。\r\n */\r\nexport async function exportTableToCsv<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildCsvContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"csv\"),\r\n content,\r\n type: \"text/csv;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 TXT 文件。\r\n */\r\nexport async function exportTableToTxt<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildTxtContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"txt\"),\r\n content,\r\n type: \"text/plain;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 XLSX 文件。\r\n */\r\nexport async function exportTableToXlsx<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n sheetName?: string;\r\n}) {\r\n const {\r\n fileName,\r\n rows,\r\n columns,\r\n sheetName = \"Sheet1\",\r\n } = params;\r\n const worksheet = await buildXlsxWorksheet({ rows, columns });\r\n const workbook = XLSX.utils.book_new();\r\n XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);\r\n await yieldToMainThread();\r\n XLSX.writeFile(workbook, ensureExtension(fileName, \"xlsx\"));\r\n}\r\n\r\n/**\r\n * 构建适合大数据量导出的 XLSX 工作表。\r\n */\r\nasync function buildXlsxWorksheet<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => column.title);\r\n const denseOptions = { dense: true } as unknown as XLSX.AOA2SheetOpts;\r\n const appendOptions = {\r\n origin: -1,\r\n dense: true,\r\n } as unknown as XLSX.SheetAOAOpts;\r\n const worksheet = XLSX.utils.aoa_to_sheet([header], denseOptions);\r\n const chunkSize = EXPORT_CHUNK_SIZE;\r\n\r\n for (let start = 0; start < rows.length; start += chunkSize) {\r\n const chunk = rows.slice(start, start + chunkSize).map((row, rowIndex) =>\r\n columns.map((column) => normalizeXlsxCell(column.value(row, start + rowIndex))),\r\n );\r\n\r\n XLSX.utils.sheet_add_aoa(worksheet, chunk, appendOptions);\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return worksheet;\r\n}\r\n\r\nasync function buildJsonContentAsync(params: {\r\n data: unknown;\r\n space: number;\r\n}) {\r\n const { data, space } = params;\r\n if (!Array.isArray(data)) {\r\n return JSON.stringify(data, null, space) ?? \"\";\r\n }\r\n\r\n if (data.length === 0) {\r\n return \"[]\";\r\n }\r\n\r\n const lineBreak = space > 0 ? \"\\n\" : \"\";\r\n const indent = space > 0 ? \" \".repeat(space) : \"\";\r\n const parts = [\"[\", lineBreak];\r\n\r\n for (let start = 0; start < data.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = data.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let index = 0; index < chunk.length; index += 1) {\r\n const itemIndex = start + index;\r\n const prefix = itemIndex === 0 ? \"\" : `,${lineBreak}`;\r\n const content = JSON.stringify(chunk[index], null, space) ?? \"null\";\r\n parts.push(prefix, indent, content);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n parts.push(lineBreak, \"]\");\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildCsvContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => escapeCsvCell(column.value(row, start + rowIndex)))\r\n .join(\",\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildTxtContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => normalizeTextCell(column.value(row, start + rowIndex)))\r\n .join(\"\\t\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nfunction escapeCsvCell(value: unknown) {\r\n const normalized = value == null ? \"\" : String(value);\r\n if (\r\n normalized.includes(\",\") ||\r\n normalized.includes(\"\\\"\") ||\r\n normalized.includes(\"\\n\")\r\n ) {\r\n return `\"${normalized.split(\"\\\"\").join(\"\\\"\\\"\")}\"`;\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction normalizeTextCell(value: unknown) {\r\n return value == null ? \"\" : String(value);\r\n}\r\n\r\nfunction normalizeXlsxCell(value: unknown) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n if (\r\n typeof value === \"string\" ||\r\n typeof value === \"number\" ||\r\n typeof value === \"boolean\"\r\n ) {\r\n return value;\r\n }\r\n\r\n if (value instanceof Date) {\r\n return value;\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction ensureExtension(fileName: string, extension: string) {\r\n return fileName.toLowerCase().endsWith(`.${extension}`)\r\n ? fileName\r\n : `${fileName}.${extension}`;\r\n}\r\n\r\nfunction yieldToMainThread() {\r\n return new Promise<void>((resolve) => {\r\n if (typeof window !== \"undefined\" && typeof window.requestAnimationFrame === \"function\") {\r\n window.requestAnimationFrame(() => {\r\n resolve();\r\n });\r\n return;\r\n }\r\n\r\n setTimeout(resolve, 0);\r\n });\r\n}\r\n\r\nfunction downloadTextFile(params: {\r\n fileName: string;\r\n content: string;\r\n type: string;\r\n}) {\r\n const { fileName, content, type } = params;\r\n const blob = new Blob([content], { type });\r\n const url = URL.createObjectURL(blob);\r\n const anchor = document.createElement(\"a\");\r\n anchor.href = url;\r\n anchor.download = fileName;\r\n anchor.click();\r\n URL.revokeObjectURL(url);\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/clipboard/index.ts","../src/export/index.ts"],"names":["XLSX"],"mappings":"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","file":"index.cjs","sourcesContent":["export type ClipboardCellValue = string | number | boolean | null | undefined;\n\n/**\n * 写入纯文本到系统剪贴板。\n */\nexport async function copyText(text: string) {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return;\n }\n } catch {\n // 非安全上下文或权限拒绝时回退到同步 DOM 复制。\n }\n\n copyTextSync(text);\n}\n\n/** 在当前用户激活事件中同步复制纯文本。 */\nexport function copyTextSync(text: string, ownerDocument = document) {\n const activeElement = ownerDocument.activeElement;\n const textarea = ownerDocument.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"true\");\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n textarea.style.top = \"0\";\n textarea.style.opacity = \"0\";\n\n ownerDocument.body.appendChild(textarea);\n textarea.focus({ preventScroll: true });\n textarea.select();\n textarea.setSelectionRange(0, textarea.value.length);\n\n let copied = false;\n try {\n copied = ownerDocument.execCommand?.(\"copy\") ?? false;\n } finally {\n textarea.remove();\n const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;\n if (\n HTMLElementConstructor &&\n activeElement instanceof HTMLElementConstructor\n ) {\n activeElement.focus({ preventScroll: true });\n }\n }\n return copied;\n}\n\n/**\n * 写入二维表格文本到系统剪贴板。\n */\nexport async function copyRows(rows: ClipboardCellValue[][]) {\n await copyText(formatRowsForClipboard(rows));\n}\n\n/**\n * 写入 JSON 文本到系统剪贴板。\n */\nexport async function copyJson(value: unknown, space = 2) {\n await copyText(JSON.stringify(value, null, space) ?? \"\");\n}\n\n/**\n * 格式化二维数据为剪贴板友好的文本。\n */\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\n return rows.map((row) => row.map(formatClipboardCell).join(\"\\t\")).join(\"\\n\");\n}\n\nfunction formatClipboardCell(value: ClipboardCellValue) {\n if (value == null) {\n return \"\";\n }\n\n return String(value);\n}\n","import * as XLSX from \"xlsx\";\r\n\r\nconst EXPORT_CHUNK_SIZE = 2_000;\r\n\r\nexport interface ExportTableColumn<Row> {\r\n key: string;\r\n title: string;\r\n value: (row: Row, index: number) => unknown;\r\n}\r\n\r\n/**\r\n * 将表格行映射为适合导出的对象数组。\r\n */\r\nexport function mapRowsToExportRecords<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n return rows.map((row, rowIndex) =>\r\n columns.reduce<Record<string, unknown>>((record, column) => {\r\n record[column.title] = column.value(row, rowIndex);\r\n return record;\r\n }, {}),\r\n );\r\n}\r\n\r\n/**\r\n * 生成 CSV 文本内容。\r\n */\r\nexport function buildCsvContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => escapeCsvCell(column.value(row, rowIndex)))\r\n .join(\",\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 生成 TXT 文本内容。\r\n */\r\nexport function buildTxtContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => normalizeTextCell(column.value(row, rowIndex)))\r\n .join(\"\\t\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 下载 JSON 文件。\r\n */\r\nexport async function downloadJsonFile(params: {\r\n fileName: string;\r\n data: unknown;\r\n space?: number;\r\n}) {\r\n const { data, fileName, space = 2 } = params;\r\n const content = await buildJsonContentAsync({ data, space });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"json\"),\r\n content,\r\n type: \"application/json;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 CSV 文件。\r\n */\r\nexport async function exportTableToCsv<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildCsvContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"csv\"),\r\n content,\r\n type: \"text/csv;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 TXT 文件。\r\n */\r\nexport async function exportTableToTxt<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildTxtContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"txt\"),\r\n content,\r\n type: \"text/plain;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 XLSX 文件。\r\n */\r\nexport async function exportTableToXlsx<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n sheetName?: string;\r\n}) {\r\n const {\r\n fileName,\r\n rows,\r\n columns,\r\n sheetName = \"Sheet1\",\r\n } = params;\r\n const worksheet = await buildXlsxWorksheet({ rows, columns });\r\n const workbook = XLSX.utils.book_new();\r\n XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);\r\n await yieldToMainThread();\r\n XLSX.writeFile(workbook, ensureExtension(fileName, \"xlsx\"));\r\n}\r\n\r\n/**\r\n * 构建适合大数据量导出的 XLSX 工作表。\r\n */\r\nasync function buildXlsxWorksheet<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => column.title);\r\n const denseOptions = { dense: true } as unknown as XLSX.AOA2SheetOpts;\r\n const appendOptions = {\r\n origin: -1,\r\n dense: true,\r\n } as unknown as XLSX.SheetAOAOpts;\r\n const worksheet = XLSX.utils.aoa_to_sheet([header], denseOptions);\r\n const chunkSize = EXPORT_CHUNK_SIZE;\r\n\r\n for (let start = 0; start < rows.length; start += chunkSize) {\r\n const chunk = rows.slice(start, start + chunkSize).map((row, rowIndex) =>\r\n columns.map((column) => normalizeXlsxCell(column.value(row, start + rowIndex))),\r\n );\r\n\r\n XLSX.utils.sheet_add_aoa(worksheet, chunk, appendOptions);\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return worksheet;\r\n}\r\n\r\nasync function buildJsonContentAsync(params: {\r\n data: unknown;\r\n space: number;\r\n}) {\r\n const { data, space } = params;\r\n if (!Array.isArray(data)) {\r\n return JSON.stringify(data, null, space) ?? \"\";\r\n }\r\n\r\n if (data.length === 0) {\r\n return \"[]\";\r\n }\r\n\r\n const lineBreak = space > 0 ? \"\\n\" : \"\";\r\n const indent = space > 0 ? \" \".repeat(space) : \"\";\r\n const parts = [\"[\", lineBreak];\r\n\r\n for (let start = 0; start < data.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = data.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let index = 0; index < chunk.length; index += 1) {\r\n const itemIndex = start + index;\r\n const prefix = itemIndex === 0 ? \"\" : `,${lineBreak}`;\r\n const content = JSON.stringify(chunk[index], null, space) ?? \"null\";\r\n parts.push(prefix, indent, content);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n parts.push(lineBreak, \"]\");\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildCsvContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => escapeCsvCell(column.value(row, start + rowIndex)))\r\n .join(\",\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildTxtContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => normalizeTextCell(column.value(row, start + rowIndex)))\r\n .join(\"\\t\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nfunction escapeCsvCell(value: unknown) {\r\n const normalized = value == null ? \"\" : String(value);\r\n if (\r\n normalized.includes(\",\") ||\r\n normalized.includes(\"\\\"\") ||\r\n normalized.includes(\"\\n\")\r\n ) {\r\n return `\"${normalized.split(\"\\\"\").join(\"\\\"\\\"\")}\"`;\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction normalizeTextCell(value: unknown) {\r\n return value == null ? \"\" : String(value);\r\n}\r\n\r\nfunction normalizeXlsxCell(value: unknown) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n if (\r\n typeof value === \"string\" ||\r\n typeof value === \"number\" ||\r\n typeof value === \"boolean\"\r\n ) {\r\n return value;\r\n }\r\n\r\n if (value instanceof Date) {\r\n return value;\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction ensureExtension(fileName: string, extension: string) {\r\n return fileName.toLowerCase().endsWith(`.${extension}`)\r\n ? fileName\r\n : `${fileName}.${extension}`;\r\n}\r\n\r\nfunction yieldToMainThread() {\r\n return new Promise<void>((resolve) => {\r\n if (typeof window !== \"undefined\" && typeof window.requestAnimationFrame === \"function\") {\r\n window.requestAnimationFrame(() => {\r\n resolve();\r\n });\r\n return;\r\n }\r\n\r\n setTimeout(resolve, 0);\r\n });\r\n}\r\n\r\nfunction downloadTextFile(params: {\r\n fileName: string;\r\n content: string;\r\n type: string;\r\n}) {\r\n const { fileName, content, type } = params;\r\n const blob = new Blob([content], { type });\r\n const url = URL.createObjectURL(blob);\r\n const anchor = document.createElement(\"a\");\r\n anchor.href = url;\r\n anchor.download = fileName;\r\n anchor.click();\r\n URL.revokeObjectURL(url);\r\n}\r\n"]}
package/dist/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- export { ClipboardCellValue, copyJson, copyRows, copyText, formatRowsForClipboard } from './clipboard/index.cjs';
1
+ export { ClipboardCellValue, copyJson, copyRows, copyText, copyTextSync, formatRowsForClipboard } from './clipboard/index.cjs';
2
2
  export { ExportTableColumn, buildCsvContent, buildTxtContent, downloadJsonFile, exportTableToCsv, exportTableToTxt, exportTableToXlsx, mapRowsToExportRecords } from './export/index.cjs';
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { ClipboardCellValue, copyJson, copyRows, copyText, formatRowsForClipboard } from './clipboard/index.js';
1
+ export { ClipboardCellValue, copyJson, copyRows, copyText, copyTextSync, formatRowsForClipboard } from './clipboard/index.js';
2
2
  export { ExportTableColumn, buildCsvContent, buildTxtContent, downloadJsonFile, exportTableToCsv, exportTableToTxt, exportTableToXlsx, mapRowsToExportRecords } from './export/index.js';
package/dist/index.js CHANGED
@@ -2,11 +2,39 @@ import * as XLSX from 'xlsx';
2
2
 
3
3
  // src/clipboard/index.ts
4
4
  async function copyText(text) {
5
- if (navigator.clipboard?.writeText) {
6
- await navigator.clipboard.writeText(text);
7
- return;
5
+ try {
6
+ if (navigator.clipboard?.writeText) {
7
+ await navigator.clipboard.writeText(text);
8
+ return;
9
+ }
10
+ } catch {
11
+ }
12
+ copyTextSync(text);
13
+ }
14
+ function copyTextSync(text, ownerDocument = document) {
15
+ const activeElement = ownerDocument.activeElement;
16
+ const textarea = ownerDocument.createElement("textarea");
17
+ textarea.value = text;
18
+ textarea.setAttribute("readonly", "true");
19
+ textarea.style.position = "fixed";
20
+ textarea.style.left = "-9999px";
21
+ textarea.style.top = "0";
22
+ textarea.style.opacity = "0";
23
+ ownerDocument.body.appendChild(textarea);
24
+ textarea.focus({ preventScroll: true });
25
+ textarea.select();
26
+ textarea.setSelectionRange(0, textarea.value.length);
27
+ let copied = false;
28
+ try {
29
+ copied = ownerDocument.execCommand?.("copy") ?? false;
30
+ } finally {
31
+ textarea.remove();
32
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;
33
+ if (HTMLElementConstructor && activeElement instanceof HTMLElementConstructor) {
34
+ activeElement.focus({ preventScroll: true });
35
+ }
8
36
  }
9
- copyTextByTextarea(text);
37
+ return copied;
10
38
  }
11
39
  async function copyRows(rows) {
12
40
  await copyText(formatRowsForClipboard(rows));
@@ -23,23 +51,6 @@ function formatClipboardCell(value) {
23
51
  }
24
52
  return String(value);
25
53
  }
26
- function copyTextByTextarea(text) {
27
- const textarea = document.createElement("textarea");
28
- textarea.value = text;
29
- textarea.setAttribute("readonly", "true");
30
- textarea.style.position = "fixed";
31
- textarea.style.left = "-9999px";
32
- textarea.style.top = "0";
33
- textarea.style.opacity = "0";
34
- document.body.appendChild(textarea);
35
- textarea.select();
36
- textarea.setSelectionRange(0, textarea.value.length);
37
- try {
38
- document.execCommand("copy");
39
- } finally {
40
- textarea.remove();
41
- }
42
- }
43
54
  var EXPORT_CHUNK_SIZE = 2e3;
44
55
  function mapRowsToExportRecords(params) {
45
56
  const { rows, columns } = params;
@@ -226,6 +237,6 @@ function downloadTextFile(params) {
226
237
  URL.revokeObjectURL(url);
227
238
  }
228
239
 
229
- export { buildCsvContent, buildTxtContent, copyJson, copyRows, copyText, downloadJsonFile, exportTableToCsv, exportTableToTxt, exportTableToXlsx, formatRowsForClipboard, mapRowsToExportRecords };
240
+ export { buildCsvContent, buildTxtContent, copyJson, copyRows, copyText, copyTextSync, downloadJsonFile, exportTableToCsv, exportTableToTxt, exportTableToXlsx, formatRowsForClipboard, mapRowsToExportRecords };
230
241
  //# sourceMappingURL=index.js.map
231
242
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/clipboard/index.ts","../src/export/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export type ClipboardCellValue =\r\n | string\r\n | number\r\n | boolean\r\n | null\r\n | undefined;\r\n\r\n/**\r\n * 写入纯文本到系统剪贴板。\r\n */\r\nexport async function copyText(text: string) {\r\n if (navigator.clipboard?.writeText) {\r\n await navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n copyTextByTextarea(text);\r\n}\r\n\r\n/**\r\n * 写入二维表格文本到系统剪贴板。\r\n */\r\nexport async function copyRows(rows: ClipboardCellValue[][]) {\r\n await copyText(formatRowsForClipboard(rows));\r\n}\r\n\r\n/**\r\n * 写入 JSON 文本到系统剪贴板。\r\n */\r\nexport async function copyJson(value: unknown, space = 2) {\r\n await copyText(JSON.stringify(value, null, space) ?? \"\");\r\n}\r\n\r\n/**\r\n * 格式化二维数据为剪贴板友好的文本。\r\n */\r\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\r\n return rows\r\n .map((row) => row.map(formatClipboardCell).join(\"\\t\"))\r\n .join(\"\\n\");\r\n}\r\n\r\nfunction formatClipboardCell(value: ClipboardCellValue) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction copyTextByTextarea(text: string) {\r\n const textarea = document.createElement(\"textarea\");\r\n textarea.value = text;\r\n textarea.setAttribute(\"readonly\", \"true\");\r\n textarea.style.position = \"fixed\";\r\n textarea.style.left = \"-9999px\";\r\n textarea.style.top = \"0\";\r\n textarea.style.opacity = \"0\";\r\n\r\n document.body.appendChild(textarea);\r\n textarea.select();\r\n textarea.setSelectionRange(0, textarea.value.length);\r\n\r\n try {\r\n document.execCommand(\"copy\");\r\n } finally {\r\n textarea.remove();\r\n }\r\n}\r\n","import * as XLSX from \"xlsx\";\r\n\r\nconst EXPORT_CHUNK_SIZE = 2_000;\r\n\r\nexport interface ExportTableColumn<Row> {\r\n key: string;\r\n title: string;\r\n value: (row: Row, index: number) => unknown;\r\n}\r\n\r\n/**\r\n * 将表格行映射为适合导出的对象数组。\r\n */\r\nexport function mapRowsToExportRecords<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n return rows.map((row, rowIndex) =>\r\n columns.reduce<Record<string, unknown>>((record, column) => {\r\n record[column.title] = column.value(row, rowIndex);\r\n return record;\r\n }, {}),\r\n );\r\n}\r\n\r\n/**\r\n * 生成 CSV 文本内容。\r\n */\r\nexport function buildCsvContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => escapeCsvCell(column.value(row, rowIndex)))\r\n .join(\",\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 生成 TXT 文本内容。\r\n */\r\nexport function buildTxtContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => normalizeTextCell(column.value(row, rowIndex)))\r\n .join(\"\\t\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 下载 JSON 文件。\r\n */\r\nexport async function downloadJsonFile(params: {\r\n fileName: string;\r\n data: unknown;\r\n space?: number;\r\n}) {\r\n const { data, fileName, space = 2 } = params;\r\n const content = await buildJsonContentAsync({ data, space });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"json\"),\r\n content,\r\n type: \"application/json;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 CSV 文件。\r\n */\r\nexport async function exportTableToCsv<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildCsvContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"csv\"),\r\n content,\r\n type: \"text/csv;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 TXT 文件。\r\n */\r\nexport async function exportTableToTxt<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildTxtContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"txt\"),\r\n content,\r\n type: \"text/plain;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 XLSX 文件。\r\n */\r\nexport async function exportTableToXlsx<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n sheetName?: string;\r\n}) {\r\n const {\r\n fileName,\r\n rows,\r\n columns,\r\n sheetName = \"Sheet1\",\r\n } = params;\r\n const worksheet = await buildXlsxWorksheet({ rows, columns });\r\n const workbook = XLSX.utils.book_new();\r\n XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);\r\n await yieldToMainThread();\r\n XLSX.writeFile(workbook, ensureExtension(fileName, \"xlsx\"));\r\n}\r\n\r\n/**\r\n * 构建适合大数据量导出的 XLSX 工作表。\r\n */\r\nasync function buildXlsxWorksheet<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => column.title);\r\n const denseOptions = { dense: true } as unknown as XLSX.AOA2SheetOpts;\r\n const appendOptions = {\r\n origin: -1,\r\n dense: true,\r\n } as unknown as XLSX.SheetAOAOpts;\r\n const worksheet = XLSX.utils.aoa_to_sheet([header], denseOptions);\r\n const chunkSize = EXPORT_CHUNK_SIZE;\r\n\r\n for (let start = 0; start < rows.length; start += chunkSize) {\r\n const chunk = rows.slice(start, start + chunkSize).map((row, rowIndex) =>\r\n columns.map((column) => normalizeXlsxCell(column.value(row, start + rowIndex))),\r\n );\r\n\r\n XLSX.utils.sheet_add_aoa(worksheet, chunk, appendOptions);\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return worksheet;\r\n}\r\n\r\nasync function buildJsonContentAsync(params: {\r\n data: unknown;\r\n space: number;\r\n}) {\r\n const { data, space } = params;\r\n if (!Array.isArray(data)) {\r\n return JSON.stringify(data, null, space) ?? \"\";\r\n }\r\n\r\n if (data.length === 0) {\r\n return \"[]\";\r\n }\r\n\r\n const lineBreak = space > 0 ? \"\\n\" : \"\";\r\n const indent = space > 0 ? \" \".repeat(space) : \"\";\r\n const parts = [\"[\", lineBreak];\r\n\r\n for (let start = 0; start < data.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = data.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let index = 0; index < chunk.length; index += 1) {\r\n const itemIndex = start + index;\r\n const prefix = itemIndex === 0 ? \"\" : `,${lineBreak}`;\r\n const content = JSON.stringify(chunk[index], null, space) ?? \"null\";\r\n parts.push(prefix, indent, content);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n parts.push(lineBreak, \"]\");\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildCsvContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => escapeCsvCell(column.value(row, start + rowIndex)))\r\n .join(\",\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildTxtContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => normalizeTextCell(column.value(row, start + rowIndex)))\r\n .join(\"\\t\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nfunction escapeCsvCell(value: unknown) {\r\n const normalized = value == null ? \"\" : String(value);\r\n if (\r\n normalized.includes(\",\") ||\r\n normalized.includes(\"\\\"\") ||\r\n normalized.includes(\"\\n\")\r\n ) {\r\n return `\"${normalized.split(\"\\\"\").join(\"\\\"\\\"\")}\"`;\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction normalizeTextCell(value: unknown) {\r\n return value == null ? \"\" : String(value);\r\n}\r\n\r\nfunction normalizeXlsxCell(value: unknown) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n if (\r\n typeof value === \"string\" ||\r\n typeof value === \"number\" ||\r\n typeof value === \"boolean\"\r\n ) {\r\n return value;\r\n }\r\n\r\n if (value instanceof Date) {\r\n return value;\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction ensureExtension(fileName: string, extension: string) {\r\n return fileName.toLowerCase().endsWith(`.${extension}`)\r\n ? fileName\r\n : `${fileName}.${extension}`;\r\n}\r\n\r\nfunction yieldToMainThread() {\r\n return new Promise<void>((resolve) => {\r\n if (typeof window !== \"undefined\" && typeof window.requestAnimationFrame === \"function\") {\r\n window.requestAnimationFrame(() => {\r\n resolve();\r\n });\r\n return;\r\n }\r\n\r\n setTimeout(resolve, 0);\r\n });\r\n}\r\n\r\nfunction downloadTextFile(params: {\r\n fileName: string;\r\n content: string;\r\n type: string;\r\n}) {\r\n const { fileName, content, type } = params;\r\n const blob = new Blob([content], { type });\r\n const url = URL.createObjectURL(blob);\r\n const anchor = document.createElement(\"a\");\r\n anchor.href = url;\r\n anchor.download = fileName;\r\n anchor.click();\r\n URL.revokeObjectURL(url);\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/clipboard/index.ts","../src/export/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export type ClipboardCellValue = string | number | boolean | null | undefined;\n\n/**\n * 写入纯文本到系统剪贴板。\n */\nexport async function copyText(text: string) {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return;\n }\n } catch {\n // 非安全上下文或权限拒绝时回退到同步 DOM 复制。\n }\n\n copyTextSync(text);\n}\n\n/** 在当前用户激活事件中同步复制纯文本。 */\nexport function copyTextSync(text: string, ownerDocument = document) {\n const activeElement = ownerDocument.activeElement;\n const textarea = ownerDocument.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"true\");\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n textarea.style.top = \"0\";\n textarea.style.opacity = \"0\";\n\n ownerDocument.body.appendChild(textarea);\n textarea.focus({ preventScroll: true });\n textarea.select();\n textarea.setSelectionRange(0, textarea.value.length);\n\n let copied = false;\n try {\n copied = ownerDocument.execCommand?.(\"copy\") ?? false;\n } finally {\n textarea.remove();\n const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;\n if (\n HTMLElementConstructor &&\n activeElement instanceof HTMLElementConstructor\n ) {\n activeElement.focus({ preventScroll: true });\n }\n }\n return copied;\n}\n\n/**\n * 写入二维表格文本到系统剪贴板。\n */\nexport async function copyRows(rows: ClipboardCellValue[][]) {\n await copyText(formatRowsForClipboard(rows));\n}\n\n/**\n * 写入 JSON 文本到系统剪贴板。\n */\nexport async function copyJson(value: unknown, space = 2) {\n await copyText(JSON.stringify(value, null, space) ?? \"\");\n}\n\n/**\n * 格式化二维数据为剪贴板友好的文本。\n */\nexport function formatRowsForClipboard(rows: ClipboardCellValue[][]) {\n return rows.map((row) => row.map(formatClipboardCell).join(\"\\t\")).join(\"\\n\");\n}\n\nfunction formatClipboardCell(value: ClipboardCellValue) {\n if (value == null) {\n return \"\";\n }\n\n return String(value);\n}\n","import * as XLSX from \"xlsx\";\r\n\r\nconst EXPORT_CHUNK_SIZE = 2_000;\r\n\r\nexport interface ExportTableColumn<Row> {\r\n key: string;\r\n title: string;\r\n value: (row: Row, index: number) => unknown;\r\n}\r\n\r\n/**\r\n * 将表格行映射为适合导出的对象数组。\r\n */\r\nexport function mapRowsToExportRecords<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n return rows.map((row, rowIndex) =>\r\n columns.reduce<Record<string, unknown>>((record, column) => {\r\n record[column.title] = column.value(row, rowIndex);\r\n return record;\r\n }, {}),\r\n );\r\n}\r\n\r\n/**\r\n * 生成 CSV 文本内容。\r\n */\r\nexport function buildCsvContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => escapeCsvCell(column.value(row, rowIndex)))\r\n .join(\",\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 生成 TXT 文本内容。\r\n */\r\nexport function buildTxtContent<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const body = rows.map((row, rowIndex) =>\r\n columns\r\n .map((column) => normalizeTextCell(column.value(row, rowIndex)))\r\n .join(\"\\t\"),\r\n );\r\n\r\n return [header, ...body].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 下载 JSON 文件。\r\n */\r\nexport async function downloadJsonFile(params: {\r\n fileName: string;\r\n data: unknown;\r\n space?: number;\r\n}) {\r\n const { data, fileName, space = 2 } = params;\r\n const content = await buildJsonContentAsync({ data, space });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"json\"),\r\n content,\r\n type: \"application/json;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 CSV 文件。\r\n */\r\nexport async function exportTableToCsv<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildCsvContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"csv\"),\r\n content,\r\n type: \"text/csv;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 TXT 文件。\r\n */\r\nexport async function exportTableToTxt<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { fileName, rows, columns } = params;\r\n const content = await buildTxtContentAsync({ rows, columns });\r\n downloadTextFile({\r\n fileName: ensureExtension(fileName, \"txt\"),\r\n content,\r\n type: \"text/plain;charset=utf-8\",\r\n });\r\n}\r\n\r\n/**\r\n * 导出表格 XLSX 文件。\r\n */\r\nexport async function exportTableToXlsx<Row>(params: {\r\n fileName: string;\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n sheetName?: string;\r\n}) {\r\n const {\r\n fileName,\r\n rows,\r\n columns,\r\n sheetName = \"Sheet1\",\r\n } = params;\r\n const worksheet = await buildXlsxWorksheet({ rows, columns });\r\n const workbook = XLSX.utils.book_new();\r\n XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);\r\n await yieldToMainThread();\r\n XLSX.writeFile(workbook, ensureExtension(fileName, \"xlsx\"));\r\n}\r\n\r\n/**\r\n * 构建适合大数据量导出的 XLSX 工作表。\r\n */\r\nasync function buildXlsxWorksheet<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => column.title);\r\n const denseOptions = { dense: true } as unknown as XLSX.AOA2SheetOpts;\r\n const appendOptions = {\r\n origin: -1,\r\n dense: true,\r\n } as unknown as XLSX.SheetAOAOpts;\r\n const worksheet = XLSX.utils.aoa_to_sheet([header], denseOptions);\r\n const chunkSize = EXPORT_CHUNK_SIZE;\r\n\r\n for (let start = 0; start < rows.length; start += chunkSize) {\r\n const chunk = rows.slice(start, start + chunkSize).map((row, rowIndex) =>\r\n columns.map((column) => normalizeXlsxCell(column.value(row, start + rowIndex))),\r\n );\r\n\r\n XLSX.utils.sheet_add_aoa(worksheet, chunk, appendOptions);\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return worksheet;\r\n}\r\n\r\nasync function buildJsonContentAsync(params: {\r\n data: unknown;\r\n space: number;\r\n}) {\r\n const { data, space } = params;\r\n if (!Array.isArray(data)) {\r\n return JSON.stringify(data, null, space) ?? \"\";\r\n }\r\n\r\n if (data.length === 0) {\r\n return \"[]\";\r\n }\r\n\r\n const lineBreak = space > 0 ? \"\\n\" : \"\";\r\n const indent = space > 0 ? \" \".repeat(space) : \"\";\r\n const parts = [\"[\", lineBreak];\r\n\r\n for (let start = 0; start < data.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = data.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let index = 0; index < chunk.length; index += 1) {\r\n const itemIndex = start + index;\r\n const prefix = itemIndex === 0 ? \"\" : `,${lineBreak}`;\r\n const content = JSON.stringify(chunk[index], null, space) ?? \"null\";\r\n parts.push(prefix, indent, content);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n parts.push(lineBreak, \"]\");\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildCsvContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => escapeCsvCell(column.title)).join(\",\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => escapeCsvCell(column.value(row, start + rowIndex)))\r\n .join(\",\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nasync function buildTxtContentAsync<Row>(params: {\r\n rows: Row[];\r\n columns: ExportTableColumn<Row>[];\r\n}) {\r\n const { rows, columns } = params;\r\n const header = columns.map((column) => normalizeTextCell(column.title)).join(\"\\t\");\r\n const parts = [header];\r\n\r\n for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {\r\n const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);\r\n for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {\r\n const row = chunk[rowIndex];\r\n const line = columns\r\n .map((column) => normalizeTextCell(column.value(row, start + rowIndex)))\r\n .join(\"\\t\");\r\n parts.push(\"\\n\", line);\r\n }\r\n\r\n await yieldToMainThread();\r\n }\r\n\r\n return parts.join(\"\");\r\n}\r\n\r\nfunction escapeCsvCell(value: unknown) {\r\n const normalized = value == null ? \"\" : String(value);\r\n if (\r\n normalized.includes(\",\") ||\r\n normalized.includes(\"\\\"\") ||\r\n normalized.includes(\"\\n\")\r\n ) {\r\n return `\"${normalized.split(\"\\\"\").join(\"\\\"\\\"\")}\"`;\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction normalizeTextCell(value: unknown) {\r\n return value == null ? \"\" : String(value);\r\n}\r\n\r\nfunction normalizeXlsxCell(value: unknown) {\r\n if (value == null) {\r\n return \"\";\r\n }\r\n\r\n if (\r\n typeof value === \"string\" ||\r\n typeof value === \"number\" ||\r\n typeof value === \"boolean\"\r\n ) {\r\n return value;\r\n }\r\n\r\n if (value instanceof Date) {\r\n return value;\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\nfunction ensureExtension(fileName: string, extension: string) {\r\n return fileName.toLowerCase().endsWith(`.${extension}`)\r\n ? fileName\r\n : `${fileName}.${extension}`;\r\n}\r\n\r\nfunction yieldToMainThread() {\r\n return new Promise<void>((resolve) => {\r\n if (typeof window !== \"undefined\" && typeof window.requestAnimationFrame === \"function\") {\r\n window.requestAnimationFrame(() => {\r\n resolve();\r\n });\r\n return;\r\n }\r\n\r\n setTimeout(resolve, 0);\r\n });\r\n}\r\n\r\nfunction downloadTextFile(params: {\r\n fileName: string;\r\n content: string;\r\n type: string;\r\n}) {\r\n const { fileName, content, type } = params;\r\n const blob = new Blob([content], { type });\r\n const url = URL.createObjectURL(blob);\r\n const anchor = document.createElement(\"a\");\r\n anchor.href = url;\r\n anchor.download = fileName;\r\n anchor.click();\r\n URL.revokeObjectURL(url);\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canvas-components/utils",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "Clipboard and export utilities for canvas-components.",
5
5
  "license": "MIT",
6
6
  "type": "module",