@canvas-components/react-xlsx-table
@canvas-components/react-xlsx-table 是 @canvas-components/xlsx-table 的 React 适配层,提供:
- 表格宿主挂载与 options 同步
- 公式栏
XlsxFormulaBar - 公式栏自动补全(函数名 + 命名区域,
showFormulaAutocomplete,默认开启) - 类飞书顶部工具栏
XlsxToolbar(职责分组、图标与名称、容器宽度折叠,默认开启) - 单元格编辑浮层
XlsxEditorOverlay(portal 到 canvas 宿主) - 顶部工作表标签栏
XlsxSheetTabs(showSheetTabs,默认开启) - 命名区域管理器
XlsxNamedRangeManager(showNamedRangeManager,默认关闭) - 公式依赖图调试面板
XlsxFormulaDependencyDebugPanel(showFormulaDependencyDebugger,默认关闭)
破坏性数据契约变更
只接收 SheetJS WorkBook / WorkSheet / CellObject,工作表名称为标识,地址使用 A1。旧逻辑 JSON、旧存档及旧协同载荷明确拒绝,不提供映射、类型别名或自动迁移。业务单元格直接读写 t/v/f/F/w/z/s/l/c,f 不带前导等号。
SheetJS 0.18.5 不保证复杂文件无损。普通保存默认阻止有损/未知内容;界面列出摘要后,用户明确确认才允许降级。失效公式缓存和损坏结构始终禁止保存。含未知/外部/数组公式的工作簿保留缓存并阻止值、公式与结构修改。
安装
pnpm add @canvas-components/react-xlsx-table @canvas-components/xlsx-table
使用
import { useState } from "react";
import type { WorkBook } from "xlsx";
import { ReactXlsxTable } from "@canvas-components/react-xlsx-table";
export default function Demo() {
const [workbook, setWorkbook] = useState<WorkBook>({
SheetNames: ["采购"],
Sheets: {
采购: {
"!ref": "A1:B100",
A1: { t: "s", v: "商品" },
B1: { t: "n", v: 20 },
},
},
});
return (
<ReactXlsxTable
workbook={workbook}
onWorkbookChange={setWorkbook}
style={{ height: 480 }}
/>
);
}
能力说明
| 能力 | 操作 |
|---|---|
| 选区 | 拖拽 / Shift 扩展 / Cmd·Ctrl+点击多选 / 行头列头 / Cmd·Ctrl+A |
| 编辑 | 双击、Enter、F2、直接输入 |
| 公式栏 | 同步活动格,Enter 提交 |
| 公式补全 | 公式栏输入 =SU / =Sal 时提示函数与命名区域 |
| 顶部工具栏 | SVG 图标按钮;加粗、颜色、填充、对齐、金额/百分比、冻结、公式、行列增删 |
| 公式 | =A1+1 / =SUM / IF / 文本函数 / Sheet1!A1 跨表 |
| 依赖图调试 | showFormulaDependencyDebugger 查看公式节点、单格边、大区间边 |
| 命名区域 | showNamedRangeManager 管理 Sales / TaxRate 等 workbook 级名称 |
| 复制粘贴 | Cmd/Ctrl+C/V/X,Delete 清空 |
| 填充柄 | 拖选区右下角小方块 |
| 序列填充 | 数字/日期拖拽按等差扩展;文本仍 copy |
| 列宽/行高 | 拖列头右边界 / 行头下边界 |
| 撤销重做 | Cmd/Ctrl+Z / Shift+Z / Y |
| 多人协同 | collaboration.adapter + presence 远端选区 |
| 多 Sheet | 顶部标签切换 / 新增 / 内联重命名 / 复制 / 左右移动 / 删除 |
顶部工具栏
ReactXlsxTable 默认展示顶部工具栏。顶部区域按 Sheet 标签栏 -> 命令工具栏 -> 公式栏 -> 表格画布 排列。命令工具栏参考在线表格的高密度布局,按主菜单、历史、快速格式、结构、字体、边框与对齐、数字格式、数据、公式与工具分组;容器宽度不足时根据工具栏自身宽度把低优先级分组折叠到“更多”,不依赖页面或 body 宽度。
工具栏会读取当前活动格的样式与单元格类型,自动高亮加粗、斜体、下划线、删除线、对齐、冻结等按钮,并回显字号、数字格式、文字颜色和填充色。
已内置真实命令:
- 撤销 / 重做
- 设置选区字号、加粗、斜体、下划线
- 设置选区文字颜色、填充色
- 清除全部格式、清除文字颜色、清除填充色
- 设置选区左 / 中 / 右对齐
- 设置选区常规 / 数字 / 金额 / 百分比类型
- 冻结到当前活动格,或在已冻结时取消冻结
- 向活动格插入
=SUM()/=AVERAGE()/=COUNT()公式模板 - 在活动格上方 / 下方插入行,在左侧 / 右侧插入列
- 清空选区内容,删除选中行 / 列
- 打开当前活动列的筛选和排序入口
可以通过 toolbar 配置密度、隐藏命令、强制禁用命令,以及显式启用由 onToolbarCommand 承接的扩展命令:
<ReactXlsxTable
workbook={workbook}
toolbar={{
density: "comfortable",
hiddenCommands: ["multidimensional-table"],
enabledCommands: ["comment", "ai-formula"],
}}
onToolbarCommand={(command) => {
if (command === "comment") {
openCommentPanel();
}
if (command === "ai-formula") {
openAiFormulaPanel();
}
}}
/>
toolbar={false} 可隐藏工具栏。缺少核心能力且未通过 enabledCommands 显式接管的命令会保持 disabled,不会出现可点击但无行为的入口。
顶部 Sheet 标签栏
ReactXlsxTable 默认把 sheet 标签栏放在顶部。标签栏支持:
- 点击切换 sheet
+新增 sheet- 双击 sheet 名称进入内联重命名
- 右键或点击 sheet 更多按钮打开操作菜单
- 复制工作表
- 左移 / 右移工作表
- 删除工作表(至少保留一张)
- 鼠标滚轮横向浏览较多 sheet,隐藏滚动条,避免页面出现横向滚动条
本地内置小数位调整、跨表查找替换、菜单复制/剪切/粘贴、文档信息、全屏、快捷键帮助、使用说明、原生链接与备注。小数位只修改数字格式 z,不舍入原值;多表替换一次撤销。浏览器剪贴板权限失败时提供错误提示,读取失败可手动粘贴 TSV。所有禁用入口通过 Tooltip 解释原因。条件格式、数据验证、图片和图表等尚未实现的能力继续禁用。
Ref API
ref.getSelection();
ref.getWorkbook();
ref.getFormulaDependencyDebugSnapshot();
ref.getNamedRanges();
ref.upsertNamedRange(named);
ref.removeNamedRange(name);
ref.getFormulaBarState();
ref.getActiveSheetId();
ref.setActiveSheet(sheetId);
ref.addSheet(options?);
ref.duplicateSheet(sheetId, { name?, activate? });
ref.moveSheet(sheetId, targetIndex);
ref.renameSheet(sheetId, name);
ref.removeSheet(sheetId);
ref.startEditing();
ref.commitFormulaBarText(text);
ref.undo();
ref.redo();
ref.getHistoryState();
ref.getRemoteSelections();
ref.getPresenceRecord();
ref.acknowledgePatches(patchIds);
ref.hideRows(rowIndexes);
ref.hideColumns(columnIndexes);
ref.clearSelectionValues();
ref.setSelectionCellStyle(stylePatch);
ref.toggleSelectionCellStyle(activeStyle, inactiveStyle);
ref.clearSelectionCellStyle();
ref.clearSelectionCellStyleKeys(keys);
ref.setSelectionCellType(type);
ref.setFrozenPane(frozen);
ref.freezeToSelection();
ref.clearFreeze();
ref.insertFormulaTemplate("=SUM()");
ref.insertRows(rowIndex, count?);
ref.insertColumns(columnIndex, count?);
ref.deleteRows(rowIndex, count?);
ref.deleteColumns(columnIndex, count?);
文件、事务和视图边界
parseXlsx/writeXlsx位于@canvas-components/xlsx-table/file,输入输出直接使用 SheetJS 原生对象。- Core 的
getDocumentStamp()与commitNativeCellTransaction({stamp,changes:[{sheetName,address,after}]})支持多表原子提交。文档替换重置历史和 session;迟到 revision 拒绝。一次编辑只增加一个撤销条目。 - React ref 提供
openXlsxFile(file)、exportWorkbookXlsx(fileName?)、cancelFileTask()。菜单“完整工作簿 (.xlsx)”保存所有表与被筛选行;CSV/TXT/JSON 属展示值导出。 - React
fileWorkerFactory可注入模块 Worker。默认动态加载@canvas-components/xlsx-table/file/worker-loader(浏览器 ESM);CJS 主入口不会直接求值import.meta.url。worker 是自包含产物,取消会 terminate。 - 筛选条件、冻结 pane、排序指示器留在运行时;手动隐藏使用
!rows/!cols。排序移动原生 cell,格式、链接和备注随单元格保留。当前 writer 不能完整写回字体、填充、边框、冻结和规则;请阅读 能力矩阵。 - 协同线协议为
sheetjs-v1,原生快照与 A1 变更支持可逆日期/二进制编码。原子批次失败不部分提交,重复 ID 不重复记账。结构快照要求基版本一致,不宣称任意并发结构 OT。 - React 表单复用 react-components;唯一直接原生表单是隐藏文件选择 input。核心包不依赖 React。
真实 Excel/WPS/LibreOffice 往返互通尚未执行,不宣称兼容任意 XLSX。
本地动作与服务接口
<ReactXlsxTable
workbook={workbook}
features={{ "find-replace": true, fullscreen: true }}
commandHandlers={{
feedback: async ({ signal, isCurrent }) => {
// 接入应用自己的反馈界面;异步返回后通过 isCurrent() 检查实例所有权。
await openFeedback({ signal });
if (!isCurrent()) return;
},
}}
documentInfoProvider={async ({ signal }) => {
return loadActualDocumentMetadata({ signal });
}}
/>
commandHandlers 可接管本地动作,或接入 ai-formula、comment、history、feedback 等真实服务;缺失服务时禁用,不生成虚构结果。上下文包含原生工作簿快照、选区、文档 stamp、AbortSignal 与 isCurrent()。关闭面板、切换文档和卸载会使请求失效。documentInfoProvider 的所有者、创建/修改时间和权限与文件 Props 分开显示,缺失项显示“未提供”。features[action]=false 显式禁用动作;handler 或旧 enabledCommands + onToolbarCommand 可以接管执行,但不能绕过显式禁用和只读。
ref 新增 executeCommand(action)、adjustSelectionDecimals(delta)、search(query)、navigateSearchMatch(snapshot,index)、replaceSearchMatches(snapshot,replacement,indices?)、copySelectionText()、pasteText(text)。搜索结果带文档 stamp;内容变化后拒绝旧结果,跨表导航本身不会推进内容版本。只读模式仍可查找、复制和查看信息,写操作统一拒绝。原生链接和静态备注分别写入 l/c,与服务端评论线程分开处理。
统一命令与复杂文件安全(A0)
XlsxCommandId 统一覆盖顶部、主菜单和本地/宿主动作。features、commandHandlers 与工具栏 hidden/disabled/enabled 配置共用这些 ID;主菜单按显式 ID 接线,文字调整不会改变动作身份。ref 的 getCommandState(id) 与 executeCommand(id) 使用同源状态;没有无参执行器的原生配置动作返回 false,不打开空面板。
显式 features[id]=false、hidden/disabled 和只读优先于 handler;handler 只接管执行。原有 enabledCommands + onToolbarCommand 保留作为 API 桥。快捷键和剪贴板事件同样遵守禁用;输入框自身的输入/复制操作不被表格截获。
A0 对包含数据验证、条件格式、绘图、未知扩展/部件或不安全公式依赖的文件,先禁用结构变更与排序/重排,并在 core 操作入口二次检查;普通原生文件继续可用。该门禁独立读取 bookFiles,不依赖 WeakMap 诊断是否仍在。复制、信息、查找与独立单元格编辑不因此整体锁定。规则/图形保存和引用更新仍待 B0,不能通过“允许有损保存”绕过编辑前门禁。
右键菜单与工作表标签管理使用相同命令状态。由右键触发宿主 handler 时,上下文的 invocation 包含 source: "context-menu" 与原始 target,可保留目标行列/范围;未接管时继续执行携带原参数的 native callback。
A1 本地视图与基础编辑
查看菜单的“缩放/显示设置”支持 50%–200%、10% 步进、100% 与适应选区,以及网格线、标题、公式显示。绘制、指针、冻结与编辑框统一逻辑坐标;视图不写工作簿,也不进入撤销或内容 revision。只读仍可调整视图。初始/显式受控配置为 view,变更通过 onViewChange;同一配置的普通 React 回传不覆盖实例调整。
数字格式下拉的“更多格式”和格式菜单可设置标准数字格式代码;预览与写入复用当前 SSF,确认只改原生 z,不舍入 v、不改变公式。格式菜单另提供逻辑像素行高/列宽和稀疏内容自动适应;合并/隐藏目标或 A0 不安全文件明确拒绝。
编辑菜单“选择性粘贴”使用同实例复制快照,支持仅值、仅公式、转置、跳过空白。先在当前实例复制,或者调用 captureSelectionClipboard();切换文档使快照失效。未知公式缓存、数组/合并、隐藏目标、多范围、边界不足整批拒绝,确认一次提交/一次撤销。“仅格式”仍等待 B0,不用私有格式模型替代。
ref 增加 getViewState/setViewState/setZoom/zoomToSelection、setSelectionNumberFormat、setRowHeights/setColumnWidths/autoFitRows/autoFitColumns、captureSelectionClipboard/getSelectionClipboard/pasteSpecial。核心包提供相同原生 API,独立于 React。
数据清洗与打印预览
工具栏“数据清洗”提供预检、变更样本、错误及覆盖确认;确认后可一次撤销。浏览器使用独立可取消 Worker,可通过 cleaningWorkerFactory 注入;无 Worker 时只允许最多 50,000 格的小范围回退。切换设置、关闭面板或换文档会取消旧预检,文档/选区变化后须重新预览。
主菜单“打印”提供完整范围或单一选区的逐页预览,点击面板“打印”才调用浏览器打印。只读可打印;permissions={{ print: false }} 明确禁用,宿主 handler 也不能绕过。features.print=false 及工具栏禁用设置继续生效。预览和最终打印复用相同 HTML,浏览器决定最终打印机设置;已有原生源包可单独保存基本页面设置及受支持的打印范围/标题,预览不自动保存。ref 提供 previewCleaning、applyCleaning 和 createPrintPlan。
B0.2 原生部件接口
ref新增 getDocumentStamp()、getNativePartsState() 和 commitNativeTransaction(),直接复用纯原生core的cells+parts原子提交,不在React维护第二份工作簿。源包初始化和大范围预检可分别使用core file/ooxml 的 createXlsxPackageParts 与 XlsxNativePartsClient。受控回传应保留原生files和typed array;换文档会更换session,旧预检不能提交。
尚未开放额外工具栏持久化动作;协同parts须明确v2握手,旧导出仍提示未合成的部件路径,完整文件合成留待B0.3。
打印面板在源文件已有单一范围时默认显示“已保存范围”,可选择实际内容或当前工作表单一选区。“保存页面设置”只提交编辑过的基本字段;“保存打印范围与标题”单独提交范围和重复首行,支持一次撤销/重做。所有控件复用react-components;Tooltip仅用于工具栏。打印面板及其他位置使用普通控件,保留保存失败的错误反馈。切换目标工作表后需要在目标表重新选区,不能把另一张表的选区默默应用过去。
Tooltip 只在工具栏及其菜单/弹层的作用域内显示,包含禁用原因。公式栏、工作表标签、右键菜单及配置面板不显示 Tooltip;控件布局、事件、ref 和可访问名称继续保留。
无源程序工作簿在打印面板提供“启用原生保存”,由核心生成并注册完整标准源快照,再独立保存页面/打印范围草稿;预览不自动启用或保存。只读、协同或不能保留的输入保持禁用/拒绝。本地启用一次撤销,当前默认文件writer仍legacy,显式native写出及后续默认模式收口见核心说明。