@canvas-components/utils 0.1.5 → 0.2.1

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,114 @@
1
- # @canvas-components/utils
2
-
3
- `@canvas-components/utils` 提供和表格外围常见流程相关的浏览器工具能力,主要覆盖剪贴板写入和导出文件。
4
-
5
- ## 功能作用
6
-
1
+ # @canvas-components/utils
2
+
3
+ `@canvas-components/utils` 提供表格包之间可复用的纯原生工具能力,覆盖数据读取、剪贴板写入和导出文件。
4
+
5
+ ## 功能作用
6
+
7
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
- ## 方法说明
8
+ - 通过 `dataIndex` 安全读取嵌套数据
9
+ - 复用数据路径比较、数值约束和矩形命中等纯函数
10
+ - 把行列数据导出为 `csv`、`txt`、`xlsx`
11
+ - 生成导出前的中间文本 / 记录结构
12
+ - 对大批量导出做分片让步,减少主线程长时间阻塞
13
+
14
+ ## 安装方法
15
+
16
+ ```bash
17
+ pnpm add @canvas-components/utils
18
+ ```
19
+
20
+ 最低环境要求:
21
+
22
+ - `Node.js >= 18.12.0`
23
+ - `pnpm >= 9.0.0`
24
+
25
+ ## 使用方法
26
+
27
+ ```ts
28
+ import {
29
+ copyRows,
30
+ exportTableToXlsx,
31
+ } from "@canvas-components/utils";
32
+
33
+ await copyRows([
34
+ ["姓名", "年龄"],
35
+ ["张三", 28],
36
+ ["李四", 31],
37
+ ]);
38
+
39
+ await exportTableToXlsx({
40
+ fileName: "users.xlsx",
41
+ rows: [
42
+ { name: "张三", age: 28 },
43
+ { name: "李四", age: 31 },
44
+ ],
45
+ columns: [
46
+ { key: "name", title: "姓名", value: (row) => row.name },
47
+ { key: "age", title: "年龄", value: (row) => row.age },
48
+ ],
49
+ });
50
+ ```
51
+
52
+ ## 属性说明
53
+
54
+ ### `ExportTableColumn<Row>`
55
+
56
+ | 属性 | 类型 | 说明 |
57
+ | --- | --- | --- |
58
+ | `key` | `string` | 列唯一标识 |
59
+ | `title` | `string` | 导出列标题 |
60
+ | `value` | `(row: Row, index: number) => unknown` | 从行数据中取导出值 |
61
+
62
+ ### `ClipboardCellValue`
63
+
64
+ 支持的剪贴板单元格值类型:
65
+
66
+ ```ts
67
+ type ClipboardCellValue = string | number | boolean | null | undefined;
68
+ ```
69
+
70
+ ## 方法说明
71
+
72
+ ### 剪贴板
73
+
74
+ | 方法 | 签名 | 说明 |
75
+ | --- | --- | --- |
76
+ | `copyText` | `(text: string) => Promise<void>` | 写入纯文本 |
77
+ | `copyRows` | `(rows: ClipboardCellValue[][]) => Promise<void>` | 把二维数据写入剪贴板 |
78
+ | `copyJson` | `(value: unknown, space = 2) => Promise<void>` | 写入 JSON 文本 |
79
+ | `formatRowsForClipboard` | `(rows) => string` | 把二维数据转成制表符文本 |
80
+
81
+ ### 导出
82
+
83
+ | 方法 | 签名 | 说明 |
84
+ | --- | --- | --- |
85
+ | `mapRowsToExportRecords` | `({ rows, columns }) => Record<string, unknown>[]` | 生成导出记录数组 |
86
+ | `buildCsvContent` | `({ rows, columns }) => string` | 生成 CSV 文本 |
87
+ | `serializeCsvRows` | `(rows, lineBreak?) => string` | 将二维数据序列化为 CSV 文本 |
88
+ | `buildTxtContent` | `({ rows, columns }) => string` | 生成 TXT 文本 |
89
+ | `downloadJsonFile` | `({ fileName, data, space? }) => Promise<void>` | 下载 JSON |
90
+ | `exportTableToCsv` | `({ fileName, rows, columns }) => Promise<void>` | 导出 CSV |
91
+ | `exportTableToTxt` | `({ fileName, rows, columns }) => Promise<void>` | 导出 TXT |
92
+ | `exportTableToXlsx` | `({ fileName, rows, columns, sheetName? }) => Promise<void>` | 导出 XLSX |
69
93
 
70
- ### 剪贴板
94
+ ### 数据读取
71
95
 
72
96
  | 方法 | 签名 | 说明 |
73
97
  | --- | --- | --- |
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` | 把二维数据转成制表符文本 |
98
+ | `getValueByDataIndex` | `(source, dataIndex?) => unknown` | 通过字符串、数字或属性路径数组安全读取嵌套值 |
99
+ | `stringifyDataIndex` | `(dataIndex) => string` | 将属性路径转换为稳定字符串 |
100
+ | `isSameDataIndex` | `(left, right) => boolean` | 判断两个属性路径是否一致 |
78
101
 
79
- ### 导出
102
+ ### 基础几何
80
103
 
81
104
  | 方法 | 签名 | 说明 |
82
105
  | --- | --- | --- |
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 项目里的浏览器工具函数复用
106
+ | `clamp` | `(value, minimum, maximum) => number` | 将数值限制在闭区间内 |
107
+ | `isPointInRect` | `(x, y, rect) => boolean` | 判断坐标是否位于矩形内 |
108
+
109
+ ## 适用场景
110
+
111
+ - 表格导出
112
+ - 系统复制功能
113
+ - 大批量数据下载
114
+ - 非 React 项目里的浏览器工具函数复用
@@ -1,12 +1,43 @@
1
1
  'use strict';
2
2
 
3
3
  // src/clipboard/index.ts
4
- async function copyText(text) {
5
- if (navigator.clipboard?.writeText) {
6
- await navigator.clipboard.writeText(text);
7
- return;
4
+ async function copyText(text, ownerDocument = document) {
5
+ const clipboard = ownerDocument.defaultView?.navigator.clipboard;
6
+ try {
7
+ if (clipboard?.writeText) {
8
+ await clipboard.writeText(text);
9
+ return true;
10
+ }
11
+ } catch {
12
+ }
13
+ return copyTextSync(text, ownerDocument);
14
+ }
15
+ function copyTextSync(text, ownerDocument = document) {
16
+ const activeElement = ownerDocument.activeElement;
17
+ const textarea = ownerDocument.createElement("textarea");
18
+ textarea.value = text;
19
+ textarea.setAttribute("readonly", "true");
20
+ textarea.style.position = "fixed";
21
+ textarea.style.left = "-9999px";
22
+ textarea.style.top = "0";
23
+ textarea.style.opacity = "0";
24
+ ownerDocument.body.appendChild(textarea);
25
+ textarea.focus({ preventScroll: true });
26
+ textarea.select();
27
+ textarea.setSelectionRange(0, textarea.value.length);
28
+ let copied = false;
29
+ try {
30
+ copied = ownerDocument.execCommand?.("copy") ?? false;
31
+ } catch {
32
+ copied = false;
33
+ } finally {
34
+ textarea.remove();
35
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;
36
+ if (HTMLElementConstructor && activeElement instanceof HTMLElementConstructor) {
37
+ activeElement.focus({ preventScroll: true });
38
+ }
8
39
  }
9
- copyTextByTextarea(text);
40
+ return copied;
10
41
  }
11
42
  async function copyRows(rows) {
12
43
  await copyText(formatRowsForClipboard(rows));
@@ -23,27 +54,11 @@ function formatClipboardCell(value) {
23
54
  }
24
55
  return String(value);
25
56
  }
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
57
 
44
58
  exports.copyJson = copyJson;
45
59
  exports.copyRows = copyRows;
46
60
  exports.copyText = copyText;
61
+ exports.copyTextSync = copyTextSync;
47
62
  exports.formatRowsForClipboard = formatRowsForClipboard;
48
63
  //# sourceMappingURL=index.cjs.map
49
64
  //# 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,QAAA,CAAS,IAAA,EAAc,aAAA,GAAgB,QAAA,EAAU;AACrE,EAAA,MAAM,SAAA,GAAY,aAAA,CAAc,WAAA,EAAa,SAAA,CAAU,SAAA;AACvD,EAAA,IAAI;AACF,IAAA,IAAI,WAAW,SAAA,EAAW;AACxB,MAAA,MAAM,SAAA,CAAU,UAAU,IAAI,CAAA;AAC9B,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,OAAO,YAAA,CAAa,MAAM,aAAa,CAAA;AACzC;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,CAAA,MAAQ;AACN,IAAA,MAAA,GAAS,KAAA;AAAA,EACX,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, ownerDocument = document) {\n const clipboard = ownerDocument.defaultView?.navigator.clipboard;\n try {\n if (clipboard?.writeText) {\n await clipboard.writeText(text);\n return true;\n }\n } catch {\n // 非安全上下文或权限拒绝时回退到同步 DOM 复制。\n }\n\n return copyTextSync(text, ownerDocument);\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 } catch {\n copied = 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"]}
@@ -2,7 +2,9 @@ type ClipboardCellValue = string | number | boolean | null | undefined;
2
2
  /**
3
3
  * 写入纯文本到系统剪贴板。
4
4
  */
5
- declare function copyText(text: string): Promise<void>;
5
+ declare function copyText(text: string, ownerDocument?: Document): Promise<boolean>;
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 };
@@ -2,7 +2,9 @@ type ClipboardCellValue = string | number | boolean | null | undefined;
2
2
  /**
3
3
  * 写入纯文本到系统剪贴板。
4
4
  */
5
- declare function copyText(text: string): Promise<void>;
5
+ declare function copyText(text: string, ownerDocument?: Document): Promise<boolean>;
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,41 @@
1
1
  // src/clipboard/index.ts
2
- async function copyText(text) {
3
- if (navigator.clipboard?.writeText) {
4
- await navigator.clipboard.writeText(text);
5
- return;
2
+ async function copyText(text, ownerDocument = document) {
3
+ const clipboard = ownerDocument.defaultView?.navigator.clipboard;
4
+ try {
5
+ if (clipboard?.writeText) {
6
+ await clipboard.writeText(text);
7
+ return true;
8
+ }
9
+ } catch {
10
+ }
11
+ return copyTextSync(text, ownerDocument);
12
+ }
13
+ function copyTextSync(text, ownerDocument = document) {
14
+ const activeElement = ownerDocument.activeElement;
15
+ const textarea = ownerDocument.createElement("textarea");
16
+ textarea.value = text;
17
+ textarea.setAttribute("readonly", "true");
18
+ textarea.style.position = "fixed";
19
+ textarea.style.left = "-9999px";
20
+ textarea.style.top = "0";
21
+ textarea.style.opacity = "0";
22
+ ownerDocument.body.appendChild(textarea);
23
+ textarea.focus({ preventScroll: true });
24
+ textarea.select();
25
+ textarea.setSelectionRange(0, textarea.value.length);
26
+ let copied = false;
27
+ try {
28
+ copied = ownerDocument.execCommand?.("copy") ?? false;
29
+ } catch {
30
+ copied = false;
31
+ } finally {
32
+ textarea.remove();
33
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement;
34
+ if (HTMLElementConstructor && activeElement instanceof HTMLElementConstructor) {
35
+ activeElement.focus({ preventScroll: true });
36
+ }
6
37
  }
7
- copyTextByTextarea(text);
38
+ return copied;
8
39
  }
9
40
  async function copyRows(rows) {
10
41
  await copyText(formatRowsForClipboard(rows));
@@ -21,24 +52,7 @@ function formatClipboardCell(value) {
21
52
  }
22
53
  return String(value);
23
54
  }
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
55
 
42
- export { copyJson, copyRows, copyText, formatRowsForClipboard };
56
+ export { copyJson, copyRows, copyText, copyTextSync, formatRowsForClipboard };
43
57
  //# sourceMappingURL=index.js.map
44
58
  //# 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,QAAA,CAAS,IAAA,EAAc,aAAA,GAAgB,QAAA,EAAU;AACrE,EAAA,MAAM,SAAA,GAAY,aAAA,CAAc,WAAA,EAAa,SAAA,CAAU,SAAA;AACvD,EAAA,IAAI;AACF,IAAA,IAAI,WAAW,SAAA,EAAW;AACxB,MAAA,MAAM,SAAA,CAAU,UAAU,IAAI,CAAA;AAC9B,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,OAAO,YAAA,CAAa,MAAM,aAAa,CAAA;AACzC;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,CAAA,MAAQ;AACN,IAAA,MAAA,GAAS,KAAA;AAAA,EACX,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, ownerDocument = document) {\n const clipboard = ownerDocument.defaultView?.navigator.clipboard;\n try {\n if (clipboard?.writeText) {\n await clipboard.writeText(text);\n return true;\n }\n } catch {\n // 非安全上下文或权限拒绝时回退到同步 DOM 复制。\n }\n\n return copyTextSync(text, ownerDocument);\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 } catch {\n copied = 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"]}
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ // src/data/index.ts
4
+ function stringifyDataIndex(dataIndex) {
5
+ return (Array.isArray(dataIndex) ? dataIndex : [dataIndex]).map((item) => String(item)).join(".");
6
+ }
7
+ function isSameDataIndex(left, right) {
8
+ const leftPath = Array.isArray(left) ? left : [left];
9
+ const rightPath = Array.isArray(right) ? right : [right];
10
+ return leftPath.length === rightPath.length && leftPath.every((key, index) => key === rightPath[index]);
11
+ }
12
+ function getValueByDataIndex(source, dataIndex) {
13
+ if (dataIndex == null) {
14
+ return void 0;
15
+ }
16
+ const path = Array.isArray(dataIndex) ? dataIndex : [dataIndex];
17
+ let current = source;
18
+ for (const key of path) {
19
+ if (current == null || typeof current !== "object" && typeof current !== "function") {
20
+ return void 0;
21
+ }
22
+ current = current[key];
23
+ }
24
+ return current;
25
+ }
26
+
27
+ exports.getValueByDataIndex = getValueByDataIndex;
28
+ exports.isSameDataIndex = isSameDataIndex;
29
+ exports.stringifyDataIndex = stringifyDataIndex;
30
+ //# sourceMappingURL=index.cjs.map
31
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data/index.ts"],"names":[],"mappings":";;;AAQO,SAAS,mBAAmB,SAAA,EAA2B;AAC5D,EAAA,OAAA,CAAQ,MAAM,OAAA,CAAQ,SAAS,CAAA,GAAI,SAAA,GAAY,CAAC,SAAS,CAAA,EACtD,GAAA,CAAI,CAAC,SAAS,MAAA,CAAO,IAAI,CAAC,CAAA,CAC1B,KAAK,GAAG,CAAA;AACb;AAKO,SAAS,eAAA,CAAgB,MAAsB,KAAA,EAAuB;AAC3E,EAAA,MAAM,WAAW,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA,GAAO,CAAC,IAAI,CAAA;AACnD,EAAA,MAAM,YAAY,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AACvD,EAAA,OACE,QAAA,CAAS,MAAA,KAAW,SAAA,CAAU,MAAA,IAC9B,QAAA,CAAS,KAAA,CAAM,CAAC,GAAA,EAAK,KAAA,KAAU,GAAA,KAAQ,SAAA,CAAU,KAAK,CAAC,CAAA;AAE3D;AASO,SAAS,mBAAA,CACd,QACA,SAAA,EACA;AACA,EAAA,IAAI,aAAa,IAAA,EAAM;AACrB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAO,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA,GAAI,SAAA,GAAY,CAAC,SAAS,CAAA;AAC9D,EAAA,IAAI,OAAA,GAAmB,MAAA;AAEvB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IACE,WAAW,IAAA,IACV,OAAO,YAAY,QAAA,IAAY,OAAO,YAAY,UAAA,EACnD;AACA,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAW,QAA6C,GAAG,CAAA;AAAA,EAC7D;AAEA,EAAA,OAAO,OAAA;AACT","file":"index.cjs","sourcesContent":["/**\n * 支持对象属性路径的数据索引。\n */\nexport type ValueDataIndex = string | number | Array<string | number>;\n\n/**\n * 将 dataIndex 转换为稳定字符串。\n */\nexport function stringifyDataIndex(dataIndex: ValueDataIndex) {\n return (Array.isArray(dataIndex) ? dataIndex : [dataIndex])\n .map((item) => String(item))\n .join(\".\");\n}\n\n/**\n * 判断两个 dataIndex 是否指向相同属性路径。\n */\nexport function isSameDataIndex(left: ValueDataIndex, right: ValueDataIndex) {\n const leftPath = Array.isArray(left) ? left : [left];\n const rightPath = Array.isArray(right) ? right : [right];\n return (\n leftPath.length === rightPath.length &&\n leftPath.every((key, index) => key === rightPath[index])\n );\n}\n\n/**\n * 通过 dataIndex 安全读取值。\n *\n * @param source 数据记录。\n * @param dataIndex 属性路径。\n * @returns 读取结果。\n */\nexport function getValueByDataIndex(\n source: unknown,\n dataIndex?: ValueDataIndex,\n) {\n if (dataIndex == null) {\n return undefined;\n }\n\n const path = Array.isArray(dataIndex) ? dataIndex : [dataIndex];\n let current: unknown = source;\n\n for (const key of path) {\n if (\n current == null ||\n (typeof current !== \"object\" && typeof current !== \"function\")\n ) {\n return undefined;\n }\n current = (current as Record<string | number, unknown>)[key];\n }\n\n return current;\n}\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 支持对象属性路径的数据索引。
3
+ */
4
+ type ValueDataIndex = string | number | Array<string | number>;
5
+ /**
6
+ * 将 dataIndex 转换为稳定字符串。
7
+ */
8
+ declare function stringifyDataIndex(dataIndex: ValueDataIndex): string;
9
+ /**
10
+ * 判断两个 dataIndex 是否指向相同属性路径。
11
+ */
12
+ declare function isSameDataIndex(left: ValueDataIndex, right: ValueDataIndex): boolean;
13
+ /**
14
+ * 通过 dataIndex 安全读取值。
15
+ *
16
+ * @param source 数据记录。
17
+ * @param dataIndex 属性路径。
18
+ * @returns 读取结果。
19
+ */
20
+ declare function getValueByDataIndex(source: unknown, dataIndex?: ValueDataIndex): unknown;
21
+
22
+ export { type ValueDataIndex, getValueByDataIndex, isSameDataIndex, stringifyDataIndex };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 支持对象属性路径的数据索引。
3
+ */
4
+ type ValueDataIndex = string | number | Array<string | number>;
5
+ /**
6
+ * 将 dataIndex 转换为稳定字符串。
7
+ */
8
+ declare function stringifyDataIndex(dataIndex: ValueDataIndex): string;
9
+ /**
10
+ * 判断两个 dataIndex 是否指向相同属性路径。
11
+ */
12
+ declare function isSameDataIndex(left: ValueDataIndex, right: ValueDataIndex): boolean;
13
+ /**
14
+ * 通过 dataIndex 安全读取值。
15
+ *
16
+ * @param source 数据记录。
17
+ * @param dataIndex 属性路径。
18
+ * @returns 读取结果。
19
+ */
20
+ declare function getValueByDataIndex(source: unknown, dataIndex?: ValueDataIndex): unknown;
21
+
22
+ export { type ValueDataIndex, getValueByDataIndex, isSameDataIndex, stringifyDataIndex };
@@ -0,0 +1,27 @@
1
+ // src/data/index.ts
2
+ function stringifyDataIndex(dataIndex) {
3
+ return (Array.isArray(dataIndex) ? dataIndex : [dataIndex]).map((item) => String(item)).join(".");
4
+ }
5
+ function isSameDataIndex(left, right) {
6
+ const leftPath = Array.isArray(left) ? left : [left];
7
+ const rightPath = Array.isArray(right) ? right : [right];
8
+ return leftPath.length === rightPath.length && leftPath.every((key, index) => key === rightPath[index]);
9
+ }
10
+ function getValueByDataIndex(source, dataIndex) {
11
+ if (dataIndex == null) {
12
+ return void 0;
13
+ }
14
+ const path = Array.isArray(dataIndex) ? dataIndex : [dataIndex];
15
+ let current = source;
16
+ for (const key of path) {
17
+ if (current == null || typeof current !== "object" && typeof current !== "function") {
18
+ return void 0;
19
+ }
20
+ current = current[key];
21
+ }
22
+ return current;
23
+ }
24
+
25
+ export { getValueByDataIndex, isSameDataIndex, stringifyDataIndex };
26
+ //# sourceMappingURL=index.js.map
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data/index.ts"],"names":[],"mappings":";AAQO,SAAS,mBAAmB,SAAA,EAA2B;AAC5D,EAAA,OAAA,CAAQ,MAAM,OAAA,CAAQ,SAAS,CAAA,GAAI,SAAA,GAAY,CAAC,SAAS,CAAA,EACtD,GAAA,CAAI,CAAC,SAAS,MAAA,CAAO,IAAI,CAAC,CAAA,CAC1B,KAAK,GAAG,CAAA;AACb;AAKO,SAAS,eAAA,CAAgB,MAAsB,KAAA,EAAuB;AAC3E,EAAA,MAAM,WAAW,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA,GAAO,CAAC,IAAI,CAAA;AACnD,EAAA,MAAM,YAAY,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AACvD,EAAA,OACE,QAAA,CAAS,MAAA,KAAW,SAAA,CAAU,MAAA,IAC9B,QAAA,CAAS,KAAA,CAAM,CAAC,GAAA,EAAK,KAAA,KAAU,GAAA,KAAQ,SAAA,CAAU,KAAK,CAAC,CAAA;AAE3D;AASO,SAAS,mBAAA,CACd,QACA,SAAA,EACA;AACA,EAAA,IAAI,aAAa,IAAA,EAAM;AACrB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAO,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA,GAAI,SAAA,GAAY,CAAC,SAAS,CAAA;AAC9D,EAAA,IAAI,OAAA,GAAmB,MAAA;AAEvB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IACE,WAAW,IAAA,IACV,OAAO,YAAY,QAAA,IAAY,OAAO,YAAY,UAAA,EACnD;AACA,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAW,QAA6C,GAAG,CAAA;AAAA,EAC7D;AAEA,EAAA,OAAO,OAAA;AACT","file":"index.js","sourcesContent":["/**\n * 支持对象属性路径的数据索引。\n */\nexport type ValueDataIndex = string | number | Array<string | number>;\n\n/**\n * 将 dataIndex 转换为稳定字符串。\n */\nexport function stringifyDataIndex(dataIndex: ValueDataIndex) {\n return (Array.isArray(dataIndex) ? dataIndex : [dataIndex])\n .map((item) => String(item))\n .join(\".\");\n}\n\n/**\n * 判断两个 dataIndex 是否指向相同属性路径。\n */\nexport function isSameDataIndex(left: ValueDataIndex, right: ValueDataIndex) {\n const leftPath = Array.isArray(left) ? left : [left];\n const rightPath = Array.isArray(right) ? right : [right];\n return (\n leftPath.length === rightPath.length &&\n leftPath.every((key, index) => key === rightPath[index])\n );\n}\n\n/**\n * 通过 dataIndex 安全读取值。\n *\n * @param source 数据记录。\n * @param dataIndex 属性路径。\n * @returns 读取结果。\n */\nexport function getValueByDataIndex(\n source: unknown,\n dataIndex?: ValueDataIndex,\n) {\n if (dataIndex == null) {\n return undefined;\n }\n\n const path = Array.isArray(dataIndex) ? dataIndex : [dataIndex];\n let current: unknown = source;\n\n for (const key of path) {\n if (\n current == null ||\n (typeof current !== \"object\" && typeof current !== \"function\")\n ) {\n return undefined;\n }\n current = (current as Record<string | number, unknown>)[key];\n }\n\n return current;\n}\n"]}
@@ -35,11 +35,15 @@ function mapRowsToExportRecords(params) {
35
35
  }
36
36
  function buildCsvContent(params) {
37
37
  const { rows, columns } = params;
38
- const header = columns.map((column) => escapeCsvCell(column.title)).join(",");
39
- const body = rows.map(
40
- (row, rowIndex) => columns.map((column) => escapeCsvCell(column.value(row, rowIndex))).join(",")
41
- );
42
- return [header, ...body].join("\n");
38
+ return serializeCsvRows([
39
+ columns.map((column) => column.title),
40
+ ...rows.map(
41
+ (row, rowIndex) => columns.map((column) => column.value(row, rowIndex))
42
+ )
43
+ ]);
44
+ }
45
+ function serializeCsvRows(rows, lineBreak = "\n") {
46
+ return rows.map((row) => row.map((value) => escapeCsvCell(value)).join(",")).join(lineBreak);
43
47
  }
44
48
  function buildTxtContent(params) {
45
49
  const { rows, columns } = params;
@@ -77,12 +81,7 @@ async function exportTableToTxt(params) {
77
81
  });
78
82
  }
79
83
  async function exportTableToXlsx(params) {
80
- const {
81
- fileName,
82
- rows,
83
- columns,
84
- sheetName = "Sheet1"
85
- } = params;
84
+ const { fileName, rows, columns, sheetName = "Sheet1" } = params;
86
85
  const worksheet = await buildXlsxWorksheet({ rows, columns });
87
86
  const workbook = XLSX__namespace.utils.book_new();
88
87
  XLSX__namespace.utils.book_append_sheet(workbook, worksheet, sheetName);
@@ -101,7 +100,9 @@ async function buildXlsxWorksheet(params) {
101
100
  const chunkSize = EXPORT_CHUNK_SIZE;
102
101
  for (let start = 0; start < rows.length; start += chunkSize) {
103
102
  const chunk = rows.slice(start, start + chunkSize).map(
104
- (row, rowIndex) => columns.map((column) => normalizeXlsxCell(column.value(row, start + rowIndex)))
103
+ (row, rowIndex) => columns.map(
104
+ (column) => normalizeXlsxCell(column.value(row, start + rowIndex))
105
+ )
105
106
  );
106
107
  XLSX__namespace.utils.sheet_add_aoa(worksheet, chunk, appendOptions);
107
108
  await yieldToMainThread();
@@ -216,5 +217,6 @@ exports.exportTableToCsv = exportTableToCsv;
216
217
  exports.exportTableToTxt = exportTableToTxt;
217
218
  exports.exportTableToXlsx = exportTableToXlsx;
218
219
  exports.mapRowsToExportRecords = mapRowsToExportRecords;
220
+ exports.serializeCsvRows = serializeCsvRows;
219
221
  //# sourceMappingURL=index.cjs.map
220
222
  //# sourceMappingURL=index.cjs.map