@canvas-components/utils 0.1.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/dist/index.js ADDED
@@ -0,0 +1,231 @@
1
+ import * as XLSX from 'xlsx';
2
+
3
+ // src/clipboard/index.ts
4
+ async function copyText(text) {
5
+ if (navigator.clipboard?.writeText) {
6
+ await navigator.clipboard.writeText(text);
7
+ return;
8
+ }
9
+ copyTextByTextarea(text);
10
+ }
11
+ async function copyRows(rows) {
12
+ await copyText(formatRowsForClipboard(rows));
13
+ }
14
+ async function copyJson(value, space = 2) {
15
+ await copyText(JSON.stringify(value, null, space) ?? "");
16
+ }
17
+ function formatRowsForClipboard(rows) {
18
+ return rows.map((row) => row.map(formatClipboardCell).join(" ")).join("\n");
19
+ }
20
+ function formatClipboardCell(value) {
21
+ if (value == null) {
22
+ return "";
23
+ }
24
+ return String(value);
25
+ }
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
+ var EXPORT_CHUNK_SIZE = 2e3;
44
+ function mapRowsToExportRecords(params) {
45
+ const { rows, columns } = params;
46
+ return rows.map(
47
+ (row, rowIndex) => columns.reduce((record, column) => {
48
+ record[column.title] = column.value(row, rowIndex);
49
+ return record;
50
+ }, {})
51
+ );
52
+ }
53
+ function buildCsvContent(params) {
54
+ const { rows, columns } = params;
55
+ const header = columns.map((column) => escapeCsvCell(column.title)).join(",");
56
+ const body = rows.map(
57
+ (row, rowIndex) => columns.map((column) => escapeCsvCell(column.value(row, rowIndex))).join(",")
58
+ );
59
+ return [header, ...body].join("\n");
60
+ }
61
+ function buildTxtContent(params) {
62
+ const { rows, columns } = params;
63
+ const header = columns.map((column) => normalizeTextCell(column.title)).join(" ");
64
+ const body = rows.map(
65
+ (row, rowIndex) => columns.map((column) => normalizeTextCell(column.value(row, rowIndex))).join(" ")
66
+ );
67
+ return [header, ...body].join("\n");
68
+ }
69
+ async function downloadJsonFile(params) {
70
+ const { data, fileName, space = 2 } = params;
71
+ const content = await buildJsonContentAsync({ data, space });
72
+ downloadTextFile({
73
+ fileName: ensureExtension(fileName, "json"),
74
+ content,
75
+ type: "application/json;charset=utf-8"
76
+ });
77
+ }
78
+ async function exportTableToCsv(params) {
79
+ const { fileName, rows, columns } = params;
80
+ const content = await buildCsvContentAsync({ rows, columns });
81
+ downloadTextFile({
82
+ fileName: ensureExtension(fileName, "csv"),
83
+ content,
84
+ type: "text/csv;charset=utf-8"
85
+ });
86
+ }
87
+ async function exportTableToTxt(params) {
88
+ const { fileName, rows, columns } = params;
89
+ const content = await buildTxtContentAsync({ rows, columns });
90
+ downloadTextFile({
91
+ fileName: ensureExtension(fileName, "txt"),
92
+ content,
93
+ type: "text/plain;charset=utf-8"
94
+ });
95
+ }
96
+ async function exportTableToXlsx(params) {
97
+ const {
98
+ fileName,
99
+ rows,
100
+ columns,
101
+ sheetName = "Sheet1"
102
+ } = params;
103
+ const worksheet = await buildXlsxWorksheet({ rows, columns });
104
+ const workbook = XLSX.utils.book_new();
105
+ XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
106
+ await yieldToMainThread();
107
+ XLSX.writeFile(workbook, ensureExtension(fileName, "xlsx"));
108
+ }
109
+ async function buildXlsxWorksheet(params) {
110
+ const { rows, columns } = params;
111
+ const header = columns.map((column) => column.title);
112
+ const denseOptions = { dense: true };
113
+ const appendOptions = {
114
+ origin: -1,
115
+ dense: true
116
+ };
117
+ const worksheet = XLSX.utils.aoa_to_sheet([header], denseOptions);
118
+ const chunkSize = EXPORT_CHUNK_SIZE;
119
+ for (let start = 0; start < rows.length; start += chunkSize) {
120
+ const chunk = rows.slice(start, start + chunkSize).map(
121
+ (row, rowIndex) => columns.map((column) => normalizeXlsxCell(column.value(row, start + rowIndex)))
122
+ );
123
+ XLSX.utils.sheet_add_aoa(worksheet, chunk, appendOptions);
124
+ await yieldToMainThread();
125
+ }
126
+ return worksheet;
127
+ }
128
+ async function buildJsonContentAsync(params) {
129
+ const { data, space } = params;
130
+ if (!Array.isArray(data)) {
131
+ return JSON.stringify(data, null, space) ?? "";
132
+ }
133
+ if (data.length === 0) {
134
+ return "[]";
135
+ }
136
+ const lineBreak = space > 0 ? "\n" : "";
137
+ const indent = space > 0 ? " ".repeat(space) : "";
138
+ const parts = ["[", lineBreak];
139
+ for (let start = 0; start < data.length; start += EXPORT_CHUNK_SIZE) {
140
+ const chunk = data.slice(start, start + EXPORT_CHUNK_SIZE);
141
+ for (let index = 0; index < chunk.length; index += 1) {
142
+ const itemIndex = start + index;
143
+ const prefix = itemIndex === 0 ? "" : `,${lineBreak}`;
144
+ const content = JSON.stringify(chunk[index], null, space) ?? "null";
145
+ parts.push(prefix, indent, content);
146
+ }
147
+ await yieldToMainThread();
148
+ }
149
+ parts.push(lineBreak, "]");
150
+ return parts.join("");
151
+ }
152
+ async function buildCsvContentAsync(params) {
153
+ const { rows, columns } = params;
154
+ const header = columns.map((column) => escapeCsvCell(column.title)).join(",");
155
+ const parts = [header];
156
+ for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {
157
+ const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);
158
+ for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {
159
+ const row = chunk[rowIndex];
160
+ const line = columns.map((column) => escapeCsvCell(column.value(row, start + rowIndex))).join(",");
161
+ parts.push("\n", line);
162
+ }
163
+ await yieldToMainThread();
164
+ }
165
+ return parts.join("");
166
+ }
167
+ async function buildTxtContentAsync(params) {
168
+ const { rows, columns } = params;
169
+ const header = columns.map((column) => normalizeTextCell(column.title)).join(" ");
170
+ const parts = [header];
171
+ for (let start = 0; start < rows.length; start += EXPORT_CHUNK_SIZE) {
172
+ const chunk = rows.slice(start, start + EXPORT_CHUNK_SIZE);
173
+ for (let rowIndex = 0; rowIndex < chunk.length; rowIndex += 1) {
174
+ const row = chunk[rowIndex];
175
+ const line = columns.map((column) => normalizeTextCell(column.value(row, start + rowIndex))).join(" ");
176
+ parts.push("\n", line);
177
+ }
178
+ await yieldToMainThread();
179
+ }
180
+ return parts.join("");
181
+ }
182
+ function escapeCsvCell(value) {
183
+ const normalized = value == null ? "" : String(value);
184
+ if (normalized.includes(",") || normalized.includes('"') || normalized.includes("\n")) {
185
+ return `"${normalized.split('"').join('""')}"`;
186
+ }
187
+ return normalized;
188
+ }
189
+ function normalizeTextCell(value) {
190
+ return value == null ? "" : String(value);
191
+ }
192
+ function normalizeXlsxCell(value) {
193
+ if (value == null) {
194
+ return "";
195
+ }
196
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
197
+ return value;
198
+ }
199
+ if (value instanceof Date) {
200
+ return value;
201
+ }
202
+ return String(value);
203
+ }
204
+ function ensureExtension(fileName, extension) {
205
+ return fileName.toLowerCase().endsWith(`.${extension}`) ? fileName : `${fileName}.${extension}`;
206
+ }
207
+ function yieldToMainThread() {
208
+ return new Promise((resolve) => {
209
+ if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
210
+ window.requestAnimationFrame(() => {
211
+ resolve();
212
+ });
213
+ return;
214
+ }
215
+ setTimeout(resolve, 0);
216
+ });
217
+ }
218
+ function downloadTextFile(params) {
219
+ const { fileName, content, type } = params;
220
+ const blob = new Blob([content], { type });
221
+ const url = URL.createObjectURL(blob);
222
+ const anchor = document.createElement("a");
223
+ anchor.href = url;
224
+ anchor.download = fileName;
225
+ anchor.click();
226
+ URL.revokeObjectURL(url);
227
+ }
228
+
229
+ export { buildCsvContent, buildTxtContent, copyJson, copyRows, copyText, downloadJsonFile, exportTableToCsv, exportTableToTxt, exportTableToXlsx, formatRowsForClipboard, mapRowsToExportRecords };
230
+ //# sourceMappingURL=index.js.map
231
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@canvas-components/utils",
3
+ "version": "0.1.1",
4
+ "description": "Clipboard and export utilities for canvas-components.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js",
18
+ "require": "./dist/index.cjs"
19
+ },
20
+ "./clipboard": {
21
+ "types": "./dist/clipboard/index.d.ts",
22
+ "import": "./dist/clipboard/index.js",
23
+ "require": "./dist/clipboard/index.cjs"
24
+ },
25
+ "./export": {
26
+ "types": "./dist/export/index.d.ts",
27
+ "import": "./dist/export/index.js",
28
+ "require": "./dist/export/index.cjs"
29
+ }
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "dependencies": {
35
+ "xlsx": "^0.18.5"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup --config tsup.config.ts"
39
+ }
40
+ }