@4399ywkf/editor 0.4.4 → 0.5.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 +84 -0
- package/dist/SheetView-DSADIZNS.js +3 -0
- package/dist/{SheetView-2EID6B2I.js.map → SheetView-DSADIZNS.js.map} +1 -1
- package/dist/chunk-L6ZR5HHW.js +84 -0
- package/dist/chunk-L6ZR5HHW.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/pdf.d.ts +1 -1
- package/dist/registry-BBL8yeIT.d.ts +148 -0
- package/dist/sheet.d.ts +8 -4
- package/dist/sheet.js +1 -1
- package/dist/xlsx.d.ts +273 -0
- package/dist/xlsx.js +613 -0
- package/dist/xlsx.js.map +1 -0
- package/package.json +6 -2
- package/dist/SheetView-2EID6B2I.js +0 -3
- package/dist/chunk-RJUDOUEZ.js +0 -56
- package/dist/chunk-RJUDOUEZ.js.map +0 -1
- package/dist/registry-CvGg65s9.d.ts +0 -68
package/README.md
CHANGED
|
@@ -36,11 +36,95 @@ Univer sheets 核心 gzip 后 871KB;无条件打进主包会直接毁掉阅读
|
|
|
36
36
|
```ts
|
|
37
37
|
import { NotionEditor, ArticleViewer } from "@4399ywkf/editor/doc"
|
|
38
38
|
import { ImportDocx, ExportDocx } from "@4399ywkf/editor/docx"
|
|
39
|
+
import { downloadXlsx } from "@4399ywkf/editor/xlsx"
|
|
39
40
|
import { SheetView } from "@4399ywkf/editor/sheet"
|
|
40
41
|
import { PdfView } from "@4399ywkf/editor/pdf"
|
|
41
42
|
import "@4399ywkf/editor/styles"
|
|
42
43
|
```
|
|
43
44
|
|
|
45
|
+
## 表格通道配置
|
|
46
|
+
|
|
47
|
+
Univer 的 UI 是 `univer-` 前缀的原子类,没有稳定选择器 —— **别用 CSS 硬藏工具栏**。
|
|
48
|
+
开关走 `sheet` 这个 prop,直接透传到 Univer preset:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<DocumentView
|
|
52
|
+
format="sheet"
|
|
53
|
+
content={snapshot}
|
|
54
|
+
readOnly
|
|
55
|
+
sheet={{
|
|
56
|
+
findReplace: true, // Ctrl+F 查找替换
|
|
57
|
+
ui: { footer: true, formulaBar: false }, // 底栏保留(多表切换靠它),公式栏收掉
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`ui` 可用:`header` / `toolbar` / `ribbonType` / `footer` / `formulaBar` / `contextMenu` /
|
|
63
|
+
`statusBarStatistic` / `menu`。
|
|
64
|
+
|
|
65
|
+
- **`readOnly` 时 `toolbar` 默认为 `false`** —— 整条 ribbon 在只读预览里全是死按钮,
|
|
66
|
+
窄面板里还挤。要它回来就显式传 `ui: { toolbar: true }`。
|
|
67
|
+
- `footer` 别随手关:多工作表时它是唯一的切表入口,关掉用户就只看得见第一张表。
|
|
68
|
+
- `findReplace` 不需要额外装包:`preset-sheets-find-replace` 是 `@univerjs/presets`
|
|
69
|
+
自己的 dependency,走子路径动态 import,没开就完全不下载。
|
|
70
|
+
|
|
71
|
+
其它能力(筛选、排序、条件格式、图片……)自己 import preset 传进来。
|
|
72
|
+
`createUniver` **不会**自动收集 preset 自带的 locales,得配套传 `locales`,
|
|
73
|
+
否则菜单会显示成原始 i18n key:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { UniverSheetsFilterPreset } from "@univerjs/presets/preset-sheets-filter"
|
|
77
|
+
import zhCN from "@univerjs/presets/preset-sheets-filter/locales/zh-CN"
|
|
78
|
+
|
|
79
|
+
<DocumentView format="sheet" content={snapshot}
|
|
80
|
+
sheet={{ presets: [UniverSheetsFilterPreset()], locales: [zhCN] }} />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`sheet` 里的配置**只在引擎创建时读一次**,挂载后改不生效也不会重建
|
|
84
|
+
(重建 = 重下引擎 + 丢掉选区和滚动位置,而宿主几乎总是传内联字面量)。要换配置请换 `key`。
|
|
85
|
+
|
|
86
|
+
## xlsx 导出
|
|
87
|
+
|
|
88
|
+
和 docx 一样**在本地生成,文档不出内网**;不引 SheetJS,只多用已有的 fflate。
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { downloadXlsx, exportXlsx } from "@4399ywkf/editor/xlsx"
|
|
92
|
+
|
|
93
|
+
const api = useRef<unknown>(null)
|
|
94
|
+
<DocumentView format="sheet" content={snapshot} onReady={(a) => (api.current = a)} />
|
|
95
|
+
|
|
96
|
+
// univerAPI 或快照都收
|
|
97
|
+
const { warnings } = downloadXlsx(api.current, "年度报表.xlsx")
|
|
98
|
+
if (warnings.length) toast(warnings.join("\n")) // ← 别吞,见下
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
服务端 / 定时任务只有 JSON 也能导 —— 整条链路不碰 DOM,不用起 Univer:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { xlsxFromSnapshot } from "@4399ywkf/editor/xlsx"
|
|
105
|
+
const { bytes, warnings, stats } = xlsxFromSnapshot(snapshot)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
保真范围:
|
|
109
|
+
|
|
110
|
+
| | 状态 |
|
|
111
|
+
|---|---|
|
|
112
|
+
| 值与类型、共享字符串、强制文本(`quotePrefix`) | ✅ |
|
|
113
|
+
| 公式 + 缓存值,填充公式还原成 xlsx 的共享公式 | ✅ |
|
|
114
|
+
| 数字格式、字体、颜色、填充、边框、对齐、换行、旋转 | ✅ |
|
|
115
|
+
| 合并单元格、行高列宽、隐藏行列、冻结窗格 | ✅ |
|
|
116
|
+
| 多工作表、工作表隐藏、标签颜色、网格线开关 | ✅ |
|
|
117
|
+
| 富文本单元格 | ~ 压平成纯文本 |
|
|
118
|
+
| 图片、图表、透视表、条件格式、数据校验、筛选、批注、超链接 | ✕ |
|
|
119
|
+
|
|
120
|
+
最后一行是 Univer 社区版的结构性限制:这些数据不在 `cellData` 里,而是各插件自己
|
|
121
|
+
序列化进 `resources` 的私有结构。**所以 `warnings` 必须展示给用户** —— 导出时它会
|
|
122
|
+
逐条点名丢了什么(「条件格式未随 xlsx 导出」),打开文件才发现要糟得多。
|
|
123
|
+
|
|
124
|
+
`cachedFormulaValues`(默认 true)决定公式格写不写 Univer 算好的值。关掉则只留公式、
|
|
125
|
+
由 Excel 打开时重算 —— 这时会自动加上 `fullCalcOnLoad`,否则不重算的消费方
|
|
126
|
+
(网页预览、pandas)会看到一片空白。
|
|
127
|
+
|
|
44
128
|
## docx 导入导出
|
|
45
129
|
|
|
46
130
|
API 形态对齐 `@tiptap-pro/extension-import-docx` / `-export-docx`,但**在本地解析**,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"SheetView-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"SheetView-DSADIZNS.js"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/views/SheetView.tsx
|
|
5
|
+
function SheetView({
|
|
6
|
+
content,
|
|
7
|
+
readOnly,
|
|
8
|
+
className,
|
|
9
|
+
sheet,
|
|
10
|
+
onReady,
|
|
11
|
+
onError
|
|
12
|
+
}) {
|
|
13
|
+
const hostRef = useRef(null);
|
|
14
|
+
const disposeRef = useRef(null);
|
|
15
|
+
const latest = useRef({ sheet, onReady, onError });
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
latest.current = { sheet, onReady, onError };
|
|
18
|
+
});
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
let cancelled = false;
|
|
21
|
+
const host = hostRef.current;
|
|
22
|
+
if (!host) return;
|
|
23
|
+
(async () => {
|
|
24
|
+
const { sheet: opts, onReady: ready, onError: fail } = latest.current;
|
|
25
|
+
try {
|
|
26
|
+
const [{ createUniver, LocaleType, merge }, sheetsCore, zhCN] = await Promise.all([
|
|
27
|
+
import('@univerjs/presets'),
|
|
28
|
+
import('@univerjs/presets/preset-sheets-core'),
|
|
29
|
+
import('@univerjs/presets/preset-sheets-core/locales/zh-CN')
|
|
30
|
+
]);
|
|
31
|
+
if (cancelled) return;
|
|
32
|
+
const ui = { ...readOnly ? { toolbar: false } : null, ...opts?.ui };
|
|
33
|
+
const presets = [
|
|
34
|
+
sheetsCore.UniverSheetsCorePreset({ container: host, ...ui })
|
|
35
|
+
];
|
|
36
|
+
const locales = [zhCN.default];
|
|
37
|
+
if (opts?.findReplace) {
|
|
38
|
+
const [findReplace, findReplaceZhCN] = await Promise.all([
|
|
39
|
+
import('@univerjs/presets/preset-sheets-find-replace'),
|
|
40
|
+
import('@univerjs/presets/preset-sheets-find-replace/locales/zh-CN')
|
|
41
|
+
]);
|
|
42
|
+
if (cancelled) return;
|
|
43
|
+
presets.push(
|
|
44
|
+
findReplace.UniverSheetsFindReplacePreset()
|
|
45
|
+
);
|
|
46
|
+
locales.push(findReplaceZhCN.default);
|
|
47
|
+
}
|
|
48
|
+
if (opts?.presets?.length) presets.push(...opts.presets);
|
|
49
|
+
if (opts?.locales?.length) locales.push(...opts.locales);
|
|
50
|
+
const { univerAPI, univer } = createUniver({
|
|
51
|
+
locale: LocaleType.ZH_CN,
|
|
52
|
+
// createUniver 不会自动收集 preset 自带的 locales,得在这里一起合进来,
|
|
53
|
+
// 否则新加的 preset 菜单会显示成原始 i18n key
|
|
54
|
+
locales: { [LocaleType.ZH_CN]: merge({}, ...locales) },
|
|
55
|
+
presets
|
|
56
|
+
});
|
|
57
|
+
const dispose = () => univer.dispose();
|
|
58
|
+
if (cancelled) return dispose();
|
|
59
|
+
if (content) univerAPI.createWorkbook(content);
|
|
60
|
+
else univerAPI.createWorkbook({});
|
|
61
|
+
if (readOnly) univerAPI.getActiveWorkbook?.()?.setEditable?.(false);
|
|
62
|
+
disposeRef.current = dispose;
|
|
63
|
+
ready?.(univerAPI);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
66
|
+
fail?.(
|
|
67
|
+
new Error(
|
|
68
|
+
`表格引擎加载失败:${err.message}。请确认已安装可选依赖 @univerjs/presets(pnpm add @univerjs/presets)`
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
})();
|
|
73
|
+
return () => {
|
|
74
|
+
cancelled = true;
|
|
75
|
+
disposeRef.current?.();
|
|
76
|
+
disposeRef.current = null;
|
|
77
|
+
};
|
|
78
|
+
}, [content, readOnly]);
|
|
79
|
+
return /* @__PURE__ */ jsx("div", { ref: hostRef, className, style: { width: "100%", height: "100%" } });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { SheetView };
|
|
83
|
+
//# sourceMappingURL=chunk-L6ZR5HHW.js.map
|
|
84
|
+
//# sourceMappingURL=chunk-L6ZR5HHW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/views/SheetView.tsx"],"names":[],"mappings":"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","file":"chunk-L6ZR5HHW.js","sourcesContent":["import { useEffect, useRef } from \"react\"\nimport type { DocumentViewProps } from \"../registry\"\nimport type { SheetUiOptions } from \"../sheet-options\"\n\n/**\n * sheet 通道:Univer 适配。\n *\n * 只用 Apache-2.0 的 OSS 核心。**不碰** `@univerjs-pro/*` 与 `univer-cli`:\n * 前者要自建 Univer Server 且需商业授权,后者 63MB、license 字段为 None、\n * 90/91 个 JS 被混淆、还会向 univer.ai 上报遥测 —— 不能进生产链路。\n *\n * xlsx **导出**用本包的 `./xlsx`:直接读 Univer 快照拼 OOXML,不引 SheetJS。\n * **导入**仍在宿主侧自理(SheetJS 或后端解析),本包不做 xlsx → 快照那一向。\n *\n * 配置见 `SheetOptions`:UI 开关直接透传给 preset(Univer 的 class 是\n * `univer-` 前缀的原子类,没有稳定选择器,CSS 硬藏靠不住),额外能力靠追加 preset。\n */\nexport default function SheetView({\n content,\n readOnly,\n className,\n sheet,\n onReady,\n onError,\n}: DocumentViewProps) {\n const hostRef = useRef<HTMLDivElement>(null)\n const disposeRef = useRef<(() => void) | null>(null)\n\n // 这几项都是「创建时读一次」的,且宿主多半传内联字面量/箭头函数。\n // 放进依赖数组 = 父组件每渲染一次就重建一次引擎(重下 871KB + 丢选区滚动位置),\n // 所以走 ref:取最新值,但不参与依赖比较。\n const latest = useRef({ sheet, onReady, onError })\n useEffect(() => {\n latest.current = { sheet, onReady, onError }\n })\n\n useEffect(() => {\n let cancelled = false\n const host = hostRef.current\n if (!host) return\n\n ;(async () => {\n const { sheet: opts, onReady: ready, onError: fail } = latest.current\n try {\n // 动态 import:871KB gzip 的引擎只在真的打开表格时才下载\n const [{ createUniver, LocaleType, merge }, sheetsCore, zhCN] = await Promise.all([\n import(\"@univerjs/presets\"),\n import(\"@univerjs/presets/preset-sheets-core\"),\n import(\"@univerjs/presets/preset-sheets-core/locales/zh-CN\"),\n ])\n if (cancelled) return\n\n // 只读时默认摘掉工具栏:整条 ribbon 全是死按钮,窄面板里还挤。\n // 宿主显式传了 toolbar 就以宿主为准。\n const ui: SheetUiOptions = { ...(readOnly ? { toolbar: false } : null), ...opts?.ui }\n\n // 可选 peer:@univerjs/presets 未安装时这里的类型解析不到,\n // 统一用 never 过桥,运行时由外层 try/catch 兜底并给出可读提示\n const presets: unknown[] = [\n (\n sheetsCore as unknown as { UniverSheetsCorePreset: (o: object) => never }\n ).UniverSheetsCorePreset({ container: host, ...ui }),\n ]\n const locales: unknown[] = [(zhCN as unknown as { default: object }).default]\n\n if (opts?.findReplace) {\n // preset-sheets-find-replace 是 @univerjs/presets 自己的 dependency,\n // 走子路径不需要宿主再装包;没开就完全不下载。\n const [findReplace, findReplaceZhCN] = await Promise.all([\n import(\"@univerjs/presets/preset-sheets-find-replace\"),\n import(\"@univerjs/presets/preset-sheets-find-replace/locales/zh-CN\"),\n ])\n if (cancelled) return\n presets.push(\n (\n findReplace as unknown as { UniverSheetsFindReplacePreset: () => never }\n ).UniverSheetsFindReplacePreset(),\n )\n locales.push((findReplaceZhCN as unknown as { default: object }).default)\n }\n\n if (opts?.presets?.length) presets.push(...opts.presets)\n if (opts?.locales?.length) locales.push(...opts.locales)\n\n const { univerAPI, univer } = createUniver({\n locale: LocaleType.ZH_CN,\n // createUniver 不会自动收集 preset 自带的 locales,得在这里一起合进来,\n // 否则新加的 preset 菜单会显示成原始 i18n key\n locales: { [LocaleType.ZH_CN]: merge({}, ...(locales as object[])) },\n presets: presets as never,\n })\n\n const dispose = () => (univer as unknown as { dispose: () => void }).dispose()\n // 卸载可能发生在上面任一个 await 期间:那时 cleanup 拿到的 disposeRef 还是 null,\n // 不在这儿自己收掉,引擎就挂在一个已脱离文档的 host 上泄漏了\n if (cancelled) return dispose()\n\n if (content) univerAPI.createWorkbook(content as never)\n else univerAPI.createWorkbook({})\n if (readOnly) univerAPI.getActiveWorkbook?.()?.setEditable?.(false)\n\n disposeRef.current = dispose\n ready?.(univerAPI)\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e))\n fail?.(\n new Error(\n `表格引擎加载失败:${err.message}。` +\n `请确认已安装可选依赖 @univerjs/presets(pnpm add @univerjs/presets)`,\n ),\n )\n }\n })()\n\n return () => {\n cancelled = true\n disposeRef.current?.()\n disposeRef.current = null\n }\n }, [content, readOnly])\n\n return <div ref={hostRef} className={className} style={{ width: \"100%\", height: \"100%\" }} />\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { D as DocumentViewProps, a as DocumentFormat } from './registry-CvGg65s9.js';
|
|
2
|
-
export { F as FormatAdapter, b as FormatCapabilities, d as detectFormat, g as getFormat, l as listFormats, r as registerFormat } from './registry-CvGg65s9.js';
|
|
3
1
|
import * as react from 'react';
|
|
4
2
|
import { ReactNode } from 'react';
|
|
3
|
+
import { D as DocumentViewProps, a as DocumentFormat } from './registry-BBL8yeIT.js';
|
|
4
|
+
export { F as FormatAdapter, b as FormatCapabilities, S as SheetOptions, c as SheetUiOptions, d as detectFormat, g as getFormat, l as listFormats, r as registerFormat } from './registry-BBL8yeIT.js';
|
|
5
5
|
|
|
6
6
|
interface DocumentViewComponentProps extends DocumentViewProps {
|
|
7
7
|
/** 文档格式。不传则尝试从 source 的扩展名推断 */
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useMemo, lazy, Suspense } from 'react';
|
|
2
2
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
|
+
// src/DocumentView.tsx
|
|
5
|
+
|
|
4
6
|
// src/registry.ts
|
|
5
7
|
var registry = /* @__PURE__ */ new Map();
|
|
6
8
|
function registerFormat(adapter) {
|
|
@@ -43,7 +45,7 @@ registerFormat({
|
|
|
43
45
|
importExtensions: [".xlsx", ".csv"],
|
|
44
46
|
exportFormats: ["xlsx", "csv"]
|
|
45
47
|
},
|
|
46
|
-
load: () => import('./SheetView-
|
|
48
|
+
load: () => import('./SheetView-DSADIZNS.js')
|
|
47
49
|
});
|
|
48
50
|
registerFormat({
|
|
49
51
|
format: "pdf",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/registry.ts","../src/DocumentView.tsx","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 格式注册表 —— 整个包的骨架。\n *\n * 设计取自腾讯 WorkBuddy 的三通道结构:**按「文档住在哪 / 是什么」分通道,\n * 每个通道一个独立引擎,上面加一层路由**。它没有试图用一套编辑器覆盖全部格式,\n * 我们也不该。\n *\n * 三条硬约束:\n *\n * 1. **引擎必须懒加载。** Univer sheets 核心 gzip 后 871KB、pdf.js 也有几百 KB。\n * 塞进主包会毁掉阅读侧的首屏 —— 只有用户真的打开一个表格 / PDF 时才该下载。\n *\n * 2. **能力要可申报,不能靠猜。** `capabilities` 明确说清每种格式能不能编辑、\n * 能不能被 AI 结构化操作。上层据此决定给什么 UI,而不是先渲染再报错。\n *\n * 3. **宿主可注册自有引擎。** 内置覆盖不了的格式(如 pptx)留给宿主接,\n * 而不是我们假装支持。\n */\nimport type { ComponentType } from \"react\"\n\nexport type DocumentFormat = \"doc\" | \"sheet\" | \"pdf\" | \"pptx\" | (string & {})\n\nexport interface FormatCapabilities {\n /** 能否编辑。false 表示只读预览 */\n editable: boolean\n /** 能否被 AI 通过结构化工具面操作(读结构、按 id 定位、增删改) */\n aiOperable: boolean\n /** 能否协同编辑 */\n collaborative: boolean\n /** 支持导入的扩展名 */\n importExtensions: string[]\n /** 支持导出的格式 */\n exportFormats: string[]\n}\n\nexport interface FormatAdapter {\n format: DocumentFormat\n /** 展示名,给 UI 用 */\n label: string\n capabilities: FormatCapabilities\n /** 该格式认领哪些扩展名(小写、带点) */\n extensions: string[]\n /**\n * 懒加载视图组件。**必须是动态 import**,否则引擎会被打进主包。\n */\n load: () => Promise<{ default: ComponentType<DocumentViewProps> }>\n}\n\nexport interface DocumentViewProps {\n /** 文档来源:URL / File / 已有内容 */\n source?: string | Blob | File\n /** 已有内容(doc 走 tiptap JSON 或 HTML;sheet 走 Univer snapshot) */\n content?: unknown\n readOnly?: boolean\n /** 协同房间号,仅 collaborative 为 true 的格式有意义 */\n room?: string\n className?: string\n /** 引擎就绪回调,拿到底层实例(tiptap Editor / Univer API / PDFDocumentProxy) */\n onReady?: (instance: unknown) => void\n onError?: (error: Error) => void\n}\n\nconst registry = new Map<DocumentFormat, FormatAdapter>()\n\nexport function registerFormat(adapter: FormatAdapter): void {\n registry.set(adapter.format, adapter)\n}\n\nexport function getFormat(format: DocumentFormat): FormatAdapter | undefined {\n return registry.get(format)\n}\n\nexport function listFormats(): FormatAdapter[] {\n return [...registry.values()]\n}\n\n/** 按扩展名判定格式。传文件名、路径或裸扩展名都行。 */\nexport function detectFormat(nameOrExt: string): DocumentFormat | undefined {\n const m = /\\.([a-z0-9]+)$/i.exec(nameOrExt.trim())\n const ext = `.${(m ? m[1] : nameOrExt.replace(/^\\./, \"\")).toLowerCase()}`\n for (const a of registry.values()) if (a.extensions.includes(ext)) return a.format\n return undefined\n}\n\n/* --------------------------- 内置格式 --------------------------- */\n\nregisterFormat({\n format: \"doc\",\n label: \"文档\",\n extensions: [\".doc\", \".docx\", \".dot\", \".dotx\", \".docm\", \".dotm\", \".wps\", \".wpt\", \".md\", \".html\"],\n capabilities: {\n editable: true,\n aiOperable: true,\n collaborative: true,\n importExtensions: [\".docx\", \".md\", \".html\"],\n exportFormats: [\"docx\", \"html\", \"json\", \"markdown\"],\n },\n load: () => import(\"./views/DocView\"),\n})\n\nregisterFormat({\n format: \"sheet\",\n label: \"表格\",\n extensions: [\".xls\", \".xlsx\", \".xlt\", \".xltx\", \".xlsm\", \".xltm\", \".csv\", \".tsv\"],\n capabilities: {\n editable: true,\n aiOperable: true,\n collaborative: false, // Univer 协同属于 Pro,OSS 核心不含\n importExtensions: [\".xlsx\", \".csv\"],\n exportFormats: [\"xlsx\", \"csv\"],\n },\n load: () => import(\"./views/SheetView\"),\n})\n\nregisterFormat({\n format: \"pdf\",\n label: \"PDF\",\n extensions: [\".pdf\"],\n capabilities: {\n editable: false, // 只读预览。PDF 编辑是 OOXML 引擎那一档的事\n aiOperable: false,\n collaborative: false,\n importExtensions: [\".pdf\"],\n exportFormats: [],\n },\n load: () => import(\"./views/PdfView\"),\n})\n\n/**\n * pptx 刻意**不内置**。\n *\n * 调研结论:Univer Slides 官方文档原文写着「请勿用于生产环境」,\n * `@univerjs/slides` 只有 0.17MB 骨架;开源世界里没有第二个可用的\n * 前端 pptx 编辑/渲染引擎。真要做只有 OnlyOffice / Collabora 那一档\n * (AGPL,需法务口径 + 独立部署)。\n *\n * 与其假装支持,不如把口子留给宿主:\n *\n * ```ts\n * registerFormat({\n * format: \"pptx\",\n * label: \"演示文稿\",\n * extensions: [\".ppt\", \".pptx\"],\n * capabilities: { editable: false, aiOperable: false, collaborative: false,\n * importExtensions: [], exportFormats: [] },\n * load: () => import(\"./MyOnlyOfficeSlideView\"),\n * })\n * ```\n */\n","import { Suspense, lazy, useMemo, type ComponentType, type ReactNode } from \"react\"\nimport { detectFormat, getFormat, type DocumentFormat, type DocumentViewProps } from \"./registry\"\n\nexport interface DocumentViewComponentProps extends DocumentViewProps {\n /** 文档格式。不传则尝试从 source 的扩展名推断 */\n format?: DocumentFormat\n /** 引擎加载中的占位 */\n fallback?: ReactNode\n /** 格式不支持时的兜底渲染。不传则渲染一段说明文字 */\n unsupported?: (format: string) => ReactNode\n}\n\n/**\n * 按格式路由到对应引擎,引擎按需下载。\n *\n * 为什么用 registry + lazy 而不是 if/else 直接 import:\n * 直接 import 会让 Univer(871KB gzip)和 pdf.js 无条件进主包,\n * 哪怕用户从头到尾只看文章。这在阅读侧是不可接受的 ——\n * 本仓刚做完「服务端直出 588KB」的优化,一个表格引擎就能全毁掉。\n */\nexport function DocumentView({\n format,\n source,\n fallback,\n unsupported,\n ...rest\n}: DocumentViewComponentProps) {\n const resolved = useMemo<DocumentFormat | undefined>(() => {\n if (format) return format\n const name = typeof source === \"string\" ? source : (source as File | undefined)?.name\n if (!name) return undefined\n return detectFormat(name)\n }, [format, source])\n\n const adapter = resolved ? getFormat(resolved) : undefined\n\n const View = useMemo<ComponentType<DocumentViewProps> | null>(() => {\n if (!adapter) return null\n return lazy(adapter.load)\n }, [adapter])\n\n if (!adapter || !View) {\n return (\n <>\n {unsupported?.(String(resolved ?? \"unknown\")) ?? (\n <div data-editor-unsupported={String(resolved ?? \"unknown\")}>\n 暂不支持该格式{resolved ? `(${resolved})` : \"\"}。\n 可通过 registerFormat() 注册自有引擎。\n </div>\n )}\n </>\n )\n }\n\n return (\n <Suspense fallback={fallback ?? <div data-editor-loading={adapter.format} />}>\n <View source={source} {...rest} />\n </Suspense>\n )\n}\n","/**\n * @4399ywkf/editor —— 多格式文档编辑器\n *\n * 主入口只放**格式路由与能力申报**,具体引擎全部懒加载。\n * 直接 import 这里不会把 tiptap / Univer / pdf.js 打进你的主包。\n *\n * ```tsx\n * import { DocumentView, detectFormat } from \"@4399ywkf/editor\"\n * import \"@4399ywkf/editor/styles\"\n *\n * <DocumentView format={detectFormat(\"年度报告.docx\")} content={html} />\n * ```\n *\n * 需要直接用某个引擎时走子路径,能拿到完整 API:\n *\n * ```ts\n * import { NotionEditor } from \"@4399ywkf/editor/doc\"\n * import { ImportDocx, ExportDocx } from \"@4399ywkf/editor/docx\"\n * ```\n */\nexport {\n detectFormat,\n getFormat,\n listFormats,\n registerFormat,\n type DocumentFormat,\n type DocumentViewProps,\n type FormatAdapter,\n type FormatCapabilities,\n} from \"./registry\"\n\nexport { DocumentView, type DocumentViewComponentProps } from \"./DocumentView\"\n\nexport const VERSION = \"0.1.0\"\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/registry.ts","../src/DocumentView.tsx","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 格式注册表 —— 整个包的骨架。\n *\n * 设计取自腾讯 WorkBuddy 的三通道结构:**按「文档住在哪 / 是什么」分通道,\n * 每个通道一个独立引擎,上面加一层路由**。它没有试图用一套编辑器覆盖全部格式,\n * 我们也不该。\n *\n * 三条硬约束:\n *\n * 1. **引擎必须懒加载。** Univer sheets 核心 gzip 后 871KB、pdf.js 也有几百 KB。\n * 塞进主包会毁掉阅读侧的首屏 —— 只有用户真的打开一个表格 / PDF 时才该下载。\n *\n * 2. **能力要可申报,不能靠猜。** `capabilities` 明确说清每种格式能不能编辑、\n * 能不能被 AI 结构化操作。上层据此决定给什么 UI,而不是先渲染再报错。\n *\n * 3. **宿主可注册自有引擎。** 内置覆盖不了的格式(如 pptx)留给宿主接,\n * 而不是我们假装支持。\n */\nimport type { ComponentType } from \"react\"\nimport type { SheetOptions } from \"./sheet-options\"\n\nexport type DocumentFormat = \"doc\" | \"sheet\" | \"pdf\" | \"pptx\" | (string & {})\n\nexport interface FormatCapabilities {\n /** 能否编辑。false 表示只读预览 */\n editable: boolean\n /** 能否被 AI 通过结构化工具面操作(读结构、按 id 定位、增删改) */\n aiOperable: boolean\n /** 能否协同编辑 */\n collaborative: boolean\n /** 支持导入的扩展名 */\n importExtensions: string[]\n /** 支持导出的格式 */\n exportFormats: string[]\n}\n\nexport interface FormatAdapter {\n format: DocumentFormat\n /** 展示名,给 UI 用 */\n label: string\n capabilities: FormatCapabilities\n /** 该格式认领哪些扩展名(小写、带点) */\n extensions: string[]\n /**\n * 懒加载视图组件。**必须是动态 import**,否则引擎会被打进主包。\n */\n load: () => Promise<{ default: ComponentType<DocumentViewProps> }>\n}\n\nexport interface DocumentViewProps {\n /** 文档来源:URL / File / 已有内容 */\n source?: string | Blob | File\n /** 已有内容(doc 走 tiptap JSON 或 HTML;sheet 走 Univer snapshot) */\n content?: unknown\n readOnly?: boolean\n /** 协同房间号,仅 collaborative 为 true 的格式有意义 */\n room?: string\n className?: string\n /**\n * sheet 通道专属配置(Univer preset 与 UI 开关),其它格式忽略。\n *\n * 放在这里而不是只给 `SheetView`,是因为宿主通常只见得到 `DocumentView`\n * 这一层路由 —— 配置进不来的话,只读预览就只能顶着整条编辑工具条。\n */\n sheet?: SheetOptions\n /** 引擎就绪回调,拿到底层实例(tiptap Editor / Univer API / PDFDocumentProxy) */\n onReady?: (instance: unknown) => void\n onError?: (error: Error) => void\n}\n\nconst registry = new Map<DocumentFormat, FormatAdapter>()\n\nexport function registerFormat(adapter: FormatAdapter): void {\n registry.set(adapter.format, adapter)\n}\n\nexport function getFormat(format: DocumentFormat): FormatAdapter | undefined {\n return registry.get(format)\n}\n\nexport function listFormats(): FormatAdapter[] {\n return [...registry.values()]\n}\n\n/** 按扩展名判定格式。传文件名、路径或裸扩展名都行。 */\nexport function detectFormat(nameOrExt: string): DocumentFormat | undefined {\n const m = /\\.([a-z0-9]+)$/i.exec(nameOrExt.trim())\n const ext = `.${(m ? m[1] : nameOrExt.replace(/^\\./, \"\")).toLowerCase()}`\n for (const a of registry.values()) if (a.extensions.includes(ext)) return a.format\n return undefined\n}\n\n/* --------------------------- 内置格式 --------------------------- */\n\nregisterFormat({\n format: \"doc\",\n label: \"文档\",\n extensions: [\".doc\", \".docx\", \".dot\", \".dotx\", \".docm\", \".dotm\", \".wps\", \".wpt\", \".md\", \".html\"],\n capabilities: {\n editable: true,\n aiOperable: true,\n collaborative: true,\n importExtensions: [\".docx\", \".md\", \".html\"],\n exportFormats: [\"docx\", \"html\", \"json\", \"markdown\"],\n },\n load: () => import(\"./views/DocView\"),\n})\n\nregisterFormat({\n format: \"sheet\",\n label: \"表格\",\n extensions: [\".xls\", \".xlsx\", \".xlt\", \".xltx\", \".xlsm\", \".xltm\", \".csv\", \".tsv\"],\n capabilities: {\n editable: true,\n aiOperable: true,\n collaborative: false, // Univer 协同属于 Pro,OSS 核心不含\n importExtensions: [\".xlsx\", \".csv\"],\n exportFormats: [\"xlsx\", \"csv\"],\n },\n load: () => import(\"./views/SheetView\"),\n})\n\nregisterFormat({\n format: \"pdf\",\n label: \"PDF\",\n extensions: [\".pdf\"],\n capabilities: {\n editable: false, // 只读预览。PDF 编辑是 OOXML 引擎那一档的事\n aiOperable: false,\n collaborative: false,\n importExtensions: [\".pdf\"],\n exportFormats: [],\n },\n load: () => import(\"./views/PdfView\"),\n})\n\n/**\n * pptx 刻意**不内置**。\n *\n * 调研结论:Univer Slides 官方文档原文写着「请勿用于生产环境」,\n * `@univerjs/slides` 只有 0.17MB 骨架;开源世界里没有第二个可用的\n * 前端 pptx 编辑/渲染引擎。真要做只有 OnlyOffice / Collabora 那一档\n * (AGPL,需法务口径 + 独立部署)。\n *\n * 与其假装支持,不如把口子留给宿主:\n *\n * ```ts\n * registerFormat({\n * format: \"pptx\",\n * label: \"演示文稿\",\n * extensions: [\".ppt\", \".pptx\"],\n * capabilities: { editable: false, aiOperable: false, collaborative: false,\n * importExtensions: [], exportFormats: [] },\n * load: () => import(\"./MyOnlyOfficeSlideView\"),\n * })\n * ```\n */\n","import { Suspense, lazy, useMemo, type ComponentType, type ReactNode } from \"react\"\nimport { detectFormat, getFormat, type DocumentFormat, type DocumentViewProps } from \"./registry\"\n\nexport interface DocumentViewComponentProps extends DocumentViewProps {\n /** 文档格式。不传则尝试从 source 的扩展名推断 */\n format?: DocumentFormat\n /** 引擎加载中的占位 */\n fallback?: ReactNode\n /** 格式不支持时的兜底渲染。不传则渲染一段说明文字 */\n unsupported?: (format: string) => ReactNode\n}\n\n/**\n * 按格式路由到对应引擎,引擎按需下载。\n *\n * 为什么用 registry + lazy 而不是 if/else 直接 import:\n * 直接 import 会让 Univer(871KB gzip)和 pdf.js 无条件进主包,\n * 哪怕用户从头到尾只看文章。这在阅读侧是不可接受的 ——\n * 本仓刚做完「服务端直出 588KB」的优化,一个表格引擎就能全毁掉。\n */\nexport function DocumentView({\n format,\n source,\n fallback,\n unsupported,\n ...rest\n}: DocumentViewComponentProps) {\n const resolved = useMemo<DocumentFormat | undefined>(() => {\n if (format) return format\n const name = typeof source === \"string\" ? source : (source as File | undefined)?.name\n if (!name) return undefined\n return detectFormat(name)\n }, [format, source])\n\n const adapter = resolved ? getFormat(resolved) : undefined\n\n const View = useMemo<ComponentType<DocumentViewProps> | null>(() => {\n if (!adapter) return null\n return lazy(adapter.load)\n }, [adapter])\n\n if (!adapter || !View) {\n return (\n <>\n {unsupported?.(String(resolved ?? \"unknown\")) ?? (\n <div data-editor-unsupported={String(resolved ?? \"unknown\")}>\n 暂不支持该格式{resolved ? `(${resolved})` : \"\"}。\n 可通过 registerFormat() 注册自有引擎。\n </div>\n )}\n </>\n )\n }\n\n return (\n <Suspense fallback={fallback ?? <div data-editor-loading={adapter.format} />}>\n <View source={source} {...rest} />\n </Suspense>\n )\n}\n","/**\n * @4399ywkf/editor —— 多格式文档编辑器\n *\n * 主入口只放**格式路由与能力申报**,具体引擎全部懒加载。\n * 直接 import 这里不会把 tiptap / Univer / pdf.js 打进你的主包。\n *\n * ```tsx\n * import { DocumentView, detectFormat } from \"@4399ywkf/editor\"\n * import \"@4399ywkf/editor/styles\"\n *\n * <DocumentView format={detectFormat(\"年度报告.docx\")} content={html} />\n * ```\n *\n * 需要直接用某个引擎时走子路径,能拿到完整 API:\n *\n * ```ts\n * import { NotionEditor } from \"@4399ywkf/editor/doc\"\n * import { ImportDocx, ExportDocx } from \"@4399ywkf/editor/docx\"\n * ```\n */\n\nexport { DocumentView, type DocumentViewComponentProps } from \"./DocumentView\"\nexport {\n type DocumentFormat,\n type DocumentViewProps,\n detectFormat,\n type FormatAdapter,\n type FormatCapabilities,\n getFormat,\n listFormats,\n registerFormat,\n} from \"./registry\"\n\nexport type { SheetOptions, SheetUiOptions } from \"./sheet-options\"\n\nexport const VERSION = \"0.1.0\"\n"]}
|
package/dist/pdf.d.ts
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* sheet 通道的初始化配置。
|
|
5
|
+
*
|
|
6
|
+
* 单独一个文件是为了让 `registry.ts` 能只 `import type` 拿到它 ——
|
|
7
|
+
* 类型在构建期就被擦掉,不会把 `views/SheetView` 从懒加载里拽出来。
|
|
8
|
+
*
|
|
9
|
+
* **这些值只在 Univer 实例创建时读一次。** 挂载后再改不会热更新,
|
|
10
|
+
* 也不会触发重建(重建一次 = 重下 871KB 引擎 + 丢掉全部选区/滚动位置,
|
|
11
|
+
* 而宿主几乎总是传内联字面量,一进依赖数组就是每次渲染重建)。
|
|
12
|
+
* 要换配置请换 `key`。
|
|
13
|
+
*/
|
|
14
|
+
/** 对应 Univer 的 `IWorkbenchOptions` / `IUniverSheetsUIConfig`,只透传只读预览会用到的那些。 */
|
|
15
|
+
interface SheetUiOptions {
|
|
16
|
+
/** 顶部区域总开关(关掉连带工具栏一起没)。默认 true */
|
|
17
|
+
header?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* 工具栏(功能区)。
|
|
20
|
+
* **`readOnly` 时默认 false** —— 整条 ribbon 在只读预览里全是死按钮,
|
|
21
|
+
* 在窄面板里还特别挤。显式传值一律以宿主为准。
|
|
22
|
+
*/
|
|
23
|
+
toolbar?: boolean;
|
|
24
|
+
/** 功能区形态:`collapsed` 收起 / `simple` 精简 / `classic` 完整 */
|
|
25
|
+
ribbonType?: "collapsed" | "simple" | "classic";
|
|
26
|
+
/**
|
|
27
|
+
* 底栏(工作表标签 + 状态栏)。默认 true。
|
|
28
|
+
* 多工作表时它是唯一的切表入口 —— 只读预览也别关,否则用户只能看见第一张表。
|
|
29
|
+
*/
|
|
30
|
+
footer?: boolean;
|
|
31
|
+
/** 编辑栏 / 公式栏。默认 true */
|
|
32
|
+
formulaBar?: boolean;
|
|
33
|
+
/** 右键菜单。默认 true(只读下仍保留复制) */
|
|
34
|
+
contextMenu?: boolean;
|
|
35
|
+
/** 底部状态栏的求和 / 计数统计。默认 true */
|
|
36
|
+
statusBarStatistic?: boolean;
|
|
37
|
+
/** 逐个菜单项的显隐与禁用,key 是 Univer 的命令 id。透传给 `IUniverUIConfig["menu"]` */
|
|
38
|
+
menu?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
interface SheetOptions {
|
|
41
|
+
/** UI 开关,直接透传给 `UniverSheetsCorePreset` */
|
|
42
|
+
ui?: SheetUiOptions;
|
|
43
|
+
/**
|
|
44
|
+
* 内置查找替换(Ctrl+F)。
|
|
45
|
+
*
|
|
46
|
+
* 不需要宿主额外装包:`preset-sheets-find-replace` 是 `@univerjs/presets`
|
|
47
|
+
* 自己的 dependency,这里走子路径动态 import,只在开了的时候才下载。
|
|
48
|
+
*/
|
|
49
|
+
findReplace?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* 追加任意其它 Univer preset(筛选、排序、条件格式、图片……)。
|
|
52
|
+
*
|
|
53
|
+
* 宿主自己 import 工厂函数、自己调用,把返回值传进来 —— 本包不代劳,
|
|
54
|
+
* 因为每个 preset 都是一笔独立体积,该由用它的人显式付出。
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { UniverSheetsFilterPreset } from "@univerjs/presets/preset-sheets-filter"
|
|
58
|
+
* import zhCN from "@univerjs/presets/preset-sheets-filter/locales/zh-CN"
|
|
59
|
+
*
|
|
60
|
+
* <DocumentView format="sheet" content={snapshot}
|
|
61
|
+
* sheet={{ presets: [UniverSheetsFilterPreset()], locales: [zhCN] }} />
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* 类型是 `unknown[]`:`@univerjs/presets` 是可选 peer,没装的项目里
|
|
65
|
+
* `IPreset` 根本解析不到,写死类型会让这些人 typecheck 直接红。
|
|
66
|
+
*/
|
|
67
|
+
presets?: unknown[];
|
|
68
|
+
/**
|
|
69
|
+
* 额外的 locale 包,与内置 zh-CN 深合并。
|
|
70
|
+
* `createUniver` **不会**自动收集 preset 自带的 locales —— 加了 preset 就得配套加这个,
|
|
71
|
+
* 否则菜单上会显示成 `sheets-filter.command.title` 这样的原始 key。
|
|
72
|
+
*/
|
|
73
|
+
locales?: unknown[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 格式注册表 —— 整个包的骨架。
|
|
78
|
+
*
|
|
79
|
+
* 设计取自腾讯 WorkBuddy 的三通道结构:**按「文档住在哪 / 是什么」分通道,
|
|
80
|
+
* 每个通道一个独立引擎,上面加一层路由**。它没有试图用一套编辑器覆盖全部格式,
|
|
81
|
+
* 我们也不该。
|
|
82
|
+
*
|
|
83
|
+
* 三条硬约束:
|
|
84
|
+
*
|
|
85
|
+
* 1. **引擎必须懒加载。** Univer sheets 核心 gzip 后 871KB、pdf.js 也有几百 KB。
|
|
86
|
+
* 塞进主包会毁掉阅读侧的首屏 —— 只有用户真的打开一个表格 / PDF 时才该下载。
|
|
87
|
+
*
|
|
88
|
+
* 2. **能力要可申报,不能靠猜。** `capabilities` 明确说清每种格式能不能编辑、
|
|
89
|
+
* 能不能被 AI 结构化操作。上层据此决定给什么 UI,而不是先渲染再报错。
|
|
90
|
+
*
|
|
91
|
+
* 3. **宿主可注册自有引擎。** 内置覆盖不了的格式(如 pptx)留给宿主接,
|
|
92
|
+
* 而不是我们假装支持。
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
type DocumentFormat = "doc" | "sheet" | "pdf" | "pptx" | (string & {});
|
|
96
|
+
interface FormatCapabilities {
|
|
97
|
+
/** 能否编辑。false 表示只读预览 */
|
|
98
|
+
editable: boolean;
|
|
99
|
+
/** 能否被 AI 通过结构化工具面操作(读结构、按 id 定位、增删改) */
|
|
100
|
+
aiOperable: boolean;
|
|
101
|
+
/** 能否协同编辑 */
|
|
102
|
+
collaborative: boolean;
|
|
103
|
+
/** 支持导入的扩展名 */
|
|
104
|
+
importExtensions: string[];
|
|
105
|
+
/** 支持导出的格式 */
|
|
106
|
+
exportFormats: string[];
|
|
107
|
+
}
|
|
108
|
+
interface FormatAdapter {
|
|
109
|
+
format: DocumentFormat;
|
|
110
|
+
/** 展示名,给 UI 用 */
|
|
111
|
+
label: string;
|
|
112
|
+
capabilities: FormatCapabilities;
|
|
113
|
+
/** 该格式认领哪些扩展名(小写、带点) */
|
|
114
|
+
extensions: string[];
|
|
115
|
+
/**
|
|
116
|
+
* 懒加载视图组件。**必须是动态 import**,否则引擎会被打进主包。
|
|
117
|
+
*/
|
|
118
|
+
load: () => Promise<{
|
|
119
|
+
default: ComponentType<DocumentViewProps>;
|
|
120
|
+
}>;
|
|
121
|
+
}
|
|
122
|
+
interface DocumentViewProps {
|
|
123
|
+
/** 文档来源:URL / File / 已有内容 */
|
|
124
|
+
source?: string | Blob | File;
|
|
125
|
+
/** 已有内容(doc 走 tiptap JSON 或 HTML;sheet 走 Univer snapshot) */
|
|
126
|
+
content?: unknown;
|
|
127
|
+
readOnly?: boolean;
|
|
128
|
+
/** 协同房间号,仅 collaborative 为 true 的格式有意义 */
|
|
129
|
+
room?: string;
|
|
130
|
+
className?: string;
|
|
131
|
+
/**
|
|
132
|
+
* sheet 通道专属配置(Univer preset 与 UI 开关),其它格式忽略。
|
|
133
|
+
*
|
|
134
|
+
* 放在这里而不是只给 `SheetView`,是因为宿主通常只见得到 `DocumentView`
|
|
135
|
+
* 这一层路由 —— 配置进不来的话,只读预览就只能顶着整条编辑工具条。
|
|
136
|
+
*/
|
|
137
|
+
sheet?: SheetOptions;
|
|
138
|
+
/** 引擎就绪回调,拿到底层实例(tiptap Editor / Univer API / PDFDocumentProxy) */
|
|
139
|
+
onReady?: (instance: unknown) => void;
|
|
140
|
+
onError?: (error: Error) => void;
|
|
141
|
+
}
|
|
142
|
+
declare function registerFormat(adapter: FormatAdapter): void;
|
|
143
|
+
declare function getFormat(format: DocumentFormat): FormatAdapter | undefined;
|
|
144
|
+
declare function listFormats(): FormatAdapter[];
|
|
145
|
+
/** 按扩展名判定格式。传文件名、路径或裸扩展名都行。 */
|
|
146
|
+
declare function detectFormat(nameOrExt: string): DocumentFormat | undefined;
|
|
147
|
+
|
|
148
|
+
export { type DocumentViewProps as D, type FormatAdapter as F, type SheetOptions as S, type DocumentFormat as a, type FormatCapabilities as b, type SheetUiOptions as c, detectFormat as d, getFormat as g, listFormats as l, registerFormat as r };
|
package/dist/sheet.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { D as DocumentViewProps } from './registry-BBL8yeIT.js';
|
|
2
|
+
export { S as SheetOptions, c as SheetUiOptions } from './registry-BBL8yeIT.js';
|
|
1
3
|
import * as react from 'react';
|
|
2
|
-
import { D as DocumentViewProps } from './registry-CvGg65s9.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* sheet 通道:Univer 适配。
|
|
@@ -8,9 +9,12 @@ import { D as DocumentViewProps } from './registry-CvGg65s9.js';
|
|
|
8
9
|
* 前者要自建 Univer Server 且需商业授权,后者 63MB、license 字段为 None、
|
|
9
10
|
* 90/91 个 JS 被混淆、还会向 univer.ai 上报遥测 —— 不能进生产链路。
|
|
10
11
|
*
|
|
11
|
-
* xlsx
|
|
12
|
-
*
|
|
12
|
+
* xlsx **导出**用本包的 `./xlsx`:直接读 Univer 快照拼 OOXML,不引 SheetJS。
|
|
13
|
+
* **导入**仍在宿主侧自理(SheetJS 或后端解析),本包不做 xlsx → 快照那一向。
|
|
14
|
+
*
|
|
15
|
+
* 配置见 `SheetOptions`:UI 开关直接透传给 preset(Univer 的 class 是
|
|
16
|
+
* `univer-` 前缀的原子类,没有稳定选择器,CSS 硬藏靠不住),额外能力靠追加 preset。
|
|
13
17
|
*/
|
|
14
|
-
declare function SheetView({ content, readOnly, className, onReady, onError }: DocumentViewProps): react.JSX.Element;
|
|
18
|
+
declare function SheetView({ content, readOnly, className, sheet, onReady, onError, }: DocumentViewProps): react.JSX.Element;
|
|
15
19
|
|
|
16
20
|
export { DocumentViewProps, SheetView };
|
package/dist/sheet.js
CHANGED