@canvas-components/utils 0.2.2 → 0.2.4

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,141 +1,159 @@
1
- # @canvas-components/utils
2
-
3
- `@canvas-components/utils` 提供表格包之间可复用的纯原生工具能力,覆盖日期时间、数据读取、剪贴板写入和导出文件。
4
-
5
- ## 功能作用
6
-
7
- - 写入文本、二维表格文本、JSON 到系统剪贴板
8
- - 通过 `dataIndex` 安全读取嵌套数据
9
- - 使用 Day.js 风格的常用 token 格式化日期时间,不依赖 Day.js 运行时
10
- - 复用数字、金额、百分比、科学计数法和自定义数字格式化
11
- - 复用数据路径比较、数值约束和矩形命中等纯函数
12
- - 把行列数据导出为 `csv`、`txt`、`xlsx`
13
- - 生成导出前的中间文本 / 记录结构
14
- - 对大批量导出做分片让步,减少主线程长时间阻塞
15
-
16
- ## 安装方法
17
-
18
- ```bash
19
- pnpm add @canvas-components/utils
20
- ```
21
-
22
- 最低环境要求:
23
-
24
- - `Node.js >= 18.12.0`
25
- - `pnpm >= 9.0.0`
26
-
27
- ## 使用方法
28
-
29
- ```ts
30
- import { copyRows, exportTableToXlsx } from "@canvas-components/utils";
31
-
32
- await copyRows([
33
- ["姓名", "年龄"],
34
- ["张三", 28],
35
- ["李四", 31],
36
- ]);
37
-
38
- await exportTableToXlsx({
39
- fileName: "users.xlsx",
40
- rows: [
41
- { name: "张三", age: 28 },
42
- { name: "李四", age: 31 },
43
- ],
44
- columns: [
45
- { key: "name", title: "姓名", value: (row) => row.name },
46
- { key: "age", title: "年龄", value: (row) => row.age },
47
- ],
48
- });
49
- ```
50
-
51
- ## 属性说明
52
-
53
- ### `ExportTableColumn<Row>`
54
-
55
- | 属性 | 类型 | 说明 |
56
- | ------- | -------------------------------------- | ------------------ |
57
- | `key` | `string` | 列唯一标识 |
58
- | `title` | `string` | 导出列标题 |
59
- | `value` | `(row: Row, index: number) => unknown` | 从行数据中取导出值 |
60
-
61
- ### `ClipboardCellValue`
62
-
63
- 支持的剪贴板单元格值类型:
64
-
65
- ```ts
66
- type ClipboardCellValue = string | number | boolean | null | undefined;
67
- ```
68
-
69
- ## 方法说明
70
-
71
- ### 剪贴板
72
-
73
- | 方法 | 签名 | 说明 |
74
- | ------------------------ | ------------------------------------------------- | ------------------------ |
75
- | `copyText` | `(text: string) => Promise<void>` | 写入纯文本 |
76
- | `copyRows` | `(rows: ClipboardCellValue[][]) => Promise<void>` | 把二维数据写入剪贴板 |
77
- | `copyJson` | `(value: unknown, space = 2) => Promise<void>` | 写入 JSON 文本 |
78
- | `formatRowsForClipboard` | `(rows) => string` | 把二维数据转成制表符文本 |
79
-
80
- ### 导出
81
-
82
- | 方法 | 签名 | 说明 |
83
- | ------------------------ | ------------------------------------------------------------ | --------------------------- |
84
- | `mapRowsToExportRecords` | `({ rows, columns }) => Record<string, unknown>[]` | 生成导出记录数组 |
85
- | `buildCsvContent` | `({ rows, columns }) => string` | 生成 CSV 文本 |
86
- | `serializeCsvRows` | `(rows, lineBreak?) => string` | 将二维数据序列化为 CSV 文本 |
87
- | `buildTxtContent` | `({ rows, columns }) => string` | 生成 TXT 文本 |
88
- | `downloadJsonFile` | `({ fileName, data, space? }) => Promise<void>` | 下载 JSON |
89
- | `exportTableToCsv` | `({ fileName, rows, columns }) => Promise<void>` | 导出 CSV |
90
- | `exportTableToTxt` | `({ fileName, rows, columns }) => Promise<void>` | 导出 TXT |
91
- | `exportTableToXlsx` | `({ fileName, rows, columns, sheetName? }) => Promise<void>` | 导出 XLSX |
92
-
93
- ### 数据读取
94
-
95
- | 方法 | 签名 | 说明 |
96
- | --------------------- | --------------------------------- | -------------------------------------------- |
97
- | `getValueByDataIndex` | `(source, dataIndex?) => unknown` | 通过字符串、数字或属性路径数组安全读取嵌套值 |
98
- | `stringifyDataIndex` | `(dataIndex) => string` | 将属性路径转换为稳定字符串 |
99
- | `isSameDataIndex` | `(left, right) => boolean` | 判断两个属性路径是否一致 |
100
-
101
- ### 日期时间
102
-
103
- 可从主入口或 `@canvas-components/utils/date` 子路径引入。
104
-
105
- | 方法 | 签名 | 说明 |
106
- | ------------------------- | --------------------------------------------- | ---------------------------------------------- |
107
- | `resolveDateValue` | `(value: unknown) => Date \| null` | 将日期、时间戳或日期字符串解析为有效日期 |
108
- | `isDateTimeFormatPattern` | `(pattern: string) => boolean` | 判断是否包含日期时间格式 token |
109
- | `formatDateTimeValue` | `(value, pattern, locale?) => string \| null` | 按格式字符串输出日期时间,默认使用中文区域设置 |
110
-
111
- 常用 token 包括 `YYYY`、`YY`、`M`、`MM`、`D`、`DD`、`ddd`、`dddd`、`H`、`HH`、`h`、`hh`、`m`、`mm`、`s`、`ss`、`SSS`、`A`。方括号中的内容按普通文本输出,例如 `YYYY年M月D日 [更新]`。
112
-
113
- ### 数字格式
114
-
115
- 可从主入口或 `@canvas-components/utils/number` 子路径引入。
116
-
117
- | 方法 | 签名 | 说明 |
118
- | --------------------------- | ------------------------------------ | -------------------------------------------- |
119
- | `parseNumberValue` | `(value: unknown) => number \| null` | 解析数字和带千分位的数字字符串 |
120
- | `formatNumberValue` | `(value, options?) => string` | 格式化数字、金额和千分位数字 |
121
- | `formatPercentValue` | `(value, decimalPlaces?) => string` | 将比例数值格式化为百分比 |
122
- | `formatScientificValue` | `(value, decimalPlaces?) => string` | 格式化科学计数法 |
123
- | `formatCustomNumberPattern` | `(value, pattern) => string` | 按常见表格数字 pattern 格式化 |
124
- | `formatChineseNumber` | `(value, style?) => string \| null` | 将数字转换为普通或金融大写中文数字 |
125
- | `parseChineseNumber` | `(value) => number \| null` | 将规范的普通或金融大写中文数字转为阿拉伯数字 |
126
-
127
- 金融大写使用规范数字字符,例如 `918000` 输出 `玖拾壹万捌仟`;不兼容形近字或错别字。
128
-
129
- ### 基础几何
130
-
131
- | 方法 | 签名 | 说明 |
132
- | --------------- | ------------------------------------- | ---------------------- |
133
- | `clamp` | `(value, minimum, maximum) => number` | 将数值限制在闭区间内 |
134
- | `isPointInRect` | `(x, y, rect) => boolean` | 判断坐标是否位于矩形内 |
135
-
136
- ## 适用场景
137
-
138
- - 表格导出
139
- - 系统复制功能
140
- - 大批量数据下载
141
- - 非 React 项目里的浏览器工具函数复用
1
+ # @canvas-components/utils
2
+
3
+ `@canvas-components/utils` 提供表格包之间可复用的纯原生工具能力,覆盖日期时间、数据读取、剪贴板写入和导出文件。
4
+
5
+ ## 功能作用
6
+
7
+ - 写入文本、二维表格文本、JSON 到系统剪贴板
8
+ - 通过 `dataIndex` 安全读取嵌套数据
9
+ - 使用 Day.js 风格的常用 token 格式化日期时间,不依赖 Day.js 运行时
10
+ - 复用数字、金额、百分比、科学计数法和自定义数字格式化
11
+ - 复用数据路径比较、数值约束和矩形命中等纯函数
12
+ - 把行列数据导出为 `csv`、`txt`、`xlsx`
13
+ - 生成导出前的中间文本 / 记录结构
14
+ - 对大批量导出做分片让步,减少主线程长时间阻塞
15
+
16
+ ## 安装方法
17
+
18
+ ```bash
19
+ pnpm add @canvas-components/utils
20
+ ```
21
+
22
+ 最低环境要求:
23
+
24
+ - `Node.js >= 18.12.0`
25
+ - `pnpm >= 9.0.0`
26
+
27
+ ## 使用方法
28
+
29
+ ```ts
30
+ import { copyRows, exportTableToXlsx } from "@canvas-components/utils";
31
+
32
+ await copyRows([
33
+ ["姓名", "年龄"],
34
+ ["张三", 28],
35
+ ["李四", 31],
36
+ ]);
37
+
38
+ await exportTableToXlsx({
39
+ fileName: "users.xlsx",
40
+ rows: [
41
+ { name: "张三", age: 28 },
42
+ { name: "李四", age: 31 },
43
+ ],
44
+ columns: [
45
+ { key: "name", title: "姓名", value: (row) => row.name },
46
+ { key: "age", title: "年龄", value: (row) => row.age },
47
+ ],
48
+ });
49
+ ```
50
+
51
+ ## 属性说明
52
+
53
+ ### XLSX 大数据导出
54
+
55
+ `exportTableToXlsx` 默认在数据达到 1 万行时尝试 Web Worker,工作表构建与 XLSX 编码在后台完成。业务 `value` 回调仍在主线程分批执行;每批确认后才发送下一批,避免一次性复制全部数据。SheetJS 随包内置,不依赖 CDN。
56
+
57
+ - `worker: "auto" | true | false`:默认 `"auto"`;`true` 始终尝试 Worker,`false` 使用主线程。
58
+ - `signal: AbortSignal`:取消导出,Promise 以 `AbortError` 拒绝;Worker 编码期间也可取消。
59
+ - `onProgress(progress)`:阶段为 `preparing`、`encoding`、`saving`、`done`,包含全局 `completedRows`、`totalRows`、实际是否使用 `worker`,以及从 1 开始的 `fileIndex`、`fileCount`。`done` 表示全部文件已交给保存流程,不代表浏览器确认文件已落盘。
60
+ - `footerRows: unknown[][]`:追加汇总等尾行,无需改造原始行对象。
61
+ - `maxRowsPerFile`:每份文件的数据行上限,默认且最多 100,000 行(尾行计入,表头不计入)。
62
+ - `maxCellsPerFile`:每份文件的单元格预算,默认且最多 2,000,020 个(含表头),保证 20 列以内可以按十万行分文件;例如 40 列时每份最多 49,999 行。两个选项只能收紧安全上限。
63
+ - `saveFile(blob, fileName, { fileIndex, fileCount })`:可接管文件保存;等待回调完成后才开始下一份。不要在回调中累积保存所有 Blob。
64
+
65
+ Worker 不可用或启动被 CSP 阻止时回退到主线程分批构建;主线程最终编码仍为同步操作,不能在该阶段即时取消。Worker 运行中失败直接报错,不重复执行业务回调。
66
+
67
+ 大数据自动拆成独立的 `文件名_001.xlsx`、`文件名_002.xlsx` 等文件;单文件保持原文件名。每份重复表头,汇总只在数据末尾追加,不重新按文件计算。只生成一份工作簿,结束后终止 Worker,默认下载撤销 Blob URL 后才开始下一份;不累积全部文件,也不合成大 ZIP。
68
+
69
+ 浏览器可能要求允许“下载多个文件”,脚本不能确认是否被下载策略拦截;也可以通过 `saveFile` 接管保存。取消或失败会停止后续文件,已下载文件不会撤回。单元格文本超过 32,767 字符、单文件累计文本超过 16M UTF-16 码元时会报错,不静默截断;长文本数据可降低 `maxRowsPerFile`。内存安全取决于现有数据源及浏览器状态,千万行、多列数据仍建议服务端导出。
70
+
71
+ ### `ExportTableColumn<Row>`
72
+
73
+ | 属性 | 类型 | 说明 |
74
+ | ------- | -------------------------------------- | ------------------ |
75
+ | `key` | `string` | 列唯一标识 |
76
+ | `title` | `string` | 导出列标题 |
77
+ | `value` | `(row: Row, index: number) => unknown` | 从行数据中取导出值 |
78
+
79
+ ### `ClipboardCellValue`
80
+
81
+ 支持的剪贴板单元格值类型:
82
+
83
+ ```ts
84
+ type ClipboardCellValue = string | number | boolean | null | undefined;
85
+ ```
86
+
87
+ ## 方法说明
88
+
89
+ ### 剪贴板
90
+
91
+ | 方法 | 签名 | 说明 |
92
+ | ------------------------ | ------------------------------------------------- | ------------------------ |
93
+ | `copyText` | `(text: string) => Promise<void>` | 写入纯文本 |
94
+ | `copyRows` | `(rows: ClipboardCellValue[][]) => Promise<void>` | 把二维数据写入剪贴板 |
95
+ | `copyJson` | `(value: unknown, space = 2) => Promise<void>` | 写入 JSON 文本 |
96
+ | `formatRowsForClipboard` | `(rows) => string` | 把二维数据转成制表符文本 |
97
+
98
+ ### 导出
99
+
100
+ | 方法 | 签名 | 说明 |
101
+ | ------------------------ | ------------------------------------------------------------ | --------------------------- |
102
+ | `mapRowsToExportRecords` | `({ rows, columns }) => Record<string, unknown>[]` | 生成导出记录数组 |
103
+ | `buildCsvContent` | `({ rows, columns }) => string` | 生成 CSV 文本 |
104
+ | `serializeCsvRows` | `(rows, lineBreak?) => string` | 将二维数据序列化为 CSV 文本 |
105
+ | `buildTxtContent` | `({ rows, columns }) => string` | 生成 TXT 文本 |
106
+ | `downloadJsonFile` | `({ fileName, data, space? }) => Promise<void>` | 下载 JSON |
107
+ | `exportTableToCsv` | `({ fileName, rows, columns }) => Promise<void>` | 导出 CSV |
108
+ | `exportTableToTxt` | `({ fileName, rows, columns }) => Promise<void>` | 导出 TXT |
109
+ | `exportTableToXlsx` | `({ fileName, rows, columns, sheetName? }) => Promise<void>` | 导出 XLSX |
110
+
111
+ ### 数据读取
112
+
113
+ | 方法 | 签名 | 说明 |
114
+ | --------------------- | --------------------------------- | -------------------------------------------- |
115
+ | `getValueByDataIndex` | `(source, dataIndex?) => unknown` | 通过字符串、数字或属性路径数组安全读取嵌套值 |
116
+ | `stringifyDataIndex` | `(dataIndex) => string` | 将属性路径转换为稳定字符串 |
117
+ | `isSameDataIndex` | `(left, right) => boolean` | 判断两个属性路径是否一致 |
118
+
119
+ ### 日期时间
120
+
121
+ 可从主入口或 `@canvas-components/utils/date` 子路径引入。
122
+
123
+ | 方法 | 签名 | 说明 |
124
+ | ------------------------- | --------------------------------------------- | ---------------------------------------------- |
125
+ | `resolveDateValue` | `(value: unknown) => Date \| null` | 将日期、时间戳或日期字符串解析为有效日期 |
126
+ | `isDateTimeFormatPattern` | `(pattern: string) => boolean` | 判断是否包含日期时间格式 token |
127
+ | `formatDateTimeValue` | `(value, pattern, locale?) => string \| null` | 按格式字符串输出日期时间,默认使用中文区域设置 |
128
+
129
+ 常用 token 包括 `YYYY`、`YY`、`M`、`MM`、`D`、`DD`、`ddd`、`dddd`、`H`、`HH`、`h`、`hh`、`m`、`mm`、`s`、`ss`、`SSS`、`A`。方括号中的内容按普通文本输出,例如 `YYYY年M月D日 [更新]`。
130
+
131
+ ### 数字格式
132
+
133
+ 可从主入口或 `@canvas-components/utils/number` 子路径引入。
134
+
135
+ | 方法 | 签名 | 说明 |
136
+ | --------------------------- | ------------------------------------ | -------------------------------------------- |
137
+ | `parseNumberValue` | `(value: unknown) => number \| null` | 解析数字和带千分位的数字字符串 |
138
+ | `formatNumberValue` | `(value, options?) => string` | 格式化数字、金额和千分位数字 |
139
+ | `formatPercentValue` | `(value, decimalPlaces?) => string` | 将比例数值格式化为百分比 |
140
+ | `formatScientificValue` | `(value, decimalPlaces?) => string` | 格式化科学计数法 |
141
+ | `formatCustomNumberPattern` | `(value, pattern) => string` | 按常见表格数字 pattern 格式化 |
142
+ | `formatChineseNumber` | `(value, style?) => string \| null` | 将数字转换为普通或金融大写中文数字 |
143
+ | `parseChineseNumber` | `(value) => number \| null` | 将规范的普通或金融大写中文数字转为阿拉伯数字 |
144
+
145
+ 金融大写使用规范数字字符,例如 `918000` 输出 `玖拾壹万捌仟`;不兼容形近字或错别字。
146
+
147
+ ### 基础几何
148
+
149
+ | 方法 | 签名 | 说明 |
150
+ | --------------- | ------------------------------------- | ---------------------- |
151
+ | `clamp` | `(value, minimum, maximum) => number` | 将数值限制在闭区间内 |
152
+ | `isPointInRect` | `(x, y, rect) => boolean` | 判断坐标是否位于矩形内 |
153
+
154
+ ## 适用场景
155
+
156
+ - 表格导出
157
+ - 系统复制功能
158
+ - 大批量数据下载
159
+ - 非 React 项目里的浏览器工具函数复用