@veltra/sheet 2.8.1 → 3.0.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/dist/components/sheet/formula-bar.js +1 -1
- package/dist/components/sheet/import-file.js +1 -1
- package/dist/components/sheet/insert-image.js +1 -1
- package/dist/components/sheet/popup-helpers.js +1 -1
- package/dist/components/sheet/popups/border-popup.js +1 -2
- package/dist/components/sheet/popups/functions-popup.js +3 -3
- package/dist/components/sheet/popups/import.worker.d.ts +1 -1
- package/dist/components/sheet/popups/import.worker.js +1 -1
- package/dist/components/sheet/sheet-context-menu.js +2 -2
- package/dist/components/sheet/sheet.js +12 -2
- package/dist/components/sheet/use-find-replace.js +1 -2
- package/dist/components/sheet/use-formula-suggest.js +5 -4
- package/dist/components/sheet/use-sheet-grid.js +4 -2
- package/dist/components/sheet/use-sheet-state.js +1 -1
- package/dist/components/sheet/use-tool-popup.js +13 -2
- package/dist/index.d.ts +2 -2
- package/dist/tools/builtin.js +1 -1
- package/dist/tools/context.d.ts +1 -11
- package/dist/tools/download.js +3 -3
- package/dist/tools/export.worker.d.ts +1 -1
- package/dist/tools/export.worker.js +1 -1
- package/dist/tools/registry.js +1 -1
- package/dist/types/sheet.d.ts +15 -7
- package/package.json +2 -3
- package/src/components/sheet/__test__/canvas-mock.ts +49 -0
- package/src/components/sheet/__test__/find-shortcut-anchor.test.ts +117 -0
- package/src/components/sheet/__test__/formula-bar.test.ts +11 -9
- package/src/components/sheet/__test__/formula-capability.test.ts +160 -0
- package/src/components/sheet/__test__/functions-popup.test.ts +7 -7
- package/src/components/sheet/__test__/grid-setup.ts +3 -0
- package/src/components/sheet/__test__/grid-test-utils.ts +47 -0
- package/src/components/sheet/__test__/import-file.test.ts +11 -7
- package/src/components/sheet/__test__/insert-image-popup.test.ts +1 -1
- package/src/components/sheet/__test__/insert-image.test.ts +1 -1
- package/src/components/sheet/__test__/sheet-component.test.ts +9 -19
- package/src/components/sheet/__test__/sheet-context-menu.test.ts +2 -2
- package/src/components/sheet/__test__/sheet-grow-on-scroll.test.ts +105 -0
- package/src/components/sheet/__test__/sheet-scrollbar.test.ts +137 -0
- package/src/components/sheet/__test__/use-formula-suggest.test.ts +1 -1
- package/src/components/sheet/formula-bar.vue +4 -4
- package/src/components/sheet/import-file.ts +3 -7
- package/src/components/sheet/insert-image.ts +2 -2
- package/src/components/sheet/popup-helpers.ts +3 -3
- package/src/components/sheet/popups/border-popup.vue +4 -4
- package/src/components/sheet/popups/export-popup.vue +1 -1
- package/src/components/sheet/popups/find-popup.vue +1 -1
- package/src/components/sheet/popups/functions-popup.vue +3 -9
- package/src/components/sheet/popups/import.worker.ts +3 -3
- package/src/components/sheet/sheet-context-menu.ts +4 -4
- package/src/components/sheet/sheet-tabs.vue +2 -2
- package/src/components/sheet/sheet.vue +12 -3
- package/src/components/sheet/use-find-replace.ts +3 -7
- package/src/components/sheet/use-formula-suggest.ts +6 -8
- package/src/components/sheet/use-sheet-grid.ts +6 -4
- package/src/components/sheet/use-sheet-state.ts +2 -2
- package/src/components/sheet/use-tool-popup.ts +22 -2
- package/src/index.ts +3 -1
- package/src/tools/__test__/apply-style.test.ts +2 -2
- package/src/tools/__test__/builtin.test.ts +4 -4
- package/src/tools/__test__/context.test.ts +2 -2
- package/src/tools/apply-style.ts +3 -3
- package/src/tools/builtin.ts +2 -2
- package/src/tools/context.ts +11 -11
- package/src/tools/download.ts +4 -4
- package/src/tools/export.worker.ts +4 -7
- package/src/tools/registry.ts +1 -1
- package/src/types/sheet.ts +19 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { message } from '@veltra/desktop'
|
|
2
|
-
import type { SheetImageAnchor, SheetImageType } from '
|
|
2
|
+
import type { SheetImageAnchor, SheetImageType } from 'infinitable/sheet'
|
|
3
3
|
|
|
4
4
|
import type { SheetContext } from '../../tools/context'
|
|
5
5
|
|
|
@@ -24,7 +24,7 @@ const EXT_TO_TYPE: Record<string, SheetImageType> = {
|
|
|
24
24
|
webp: 'webp'
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** MIME →
|
|
27
|
+
/** MIME → 引擎图片类型(SheetImageType);MIME 为空时回落扩展名 */
|
|
28
28
|
export function resolveImageType(file: File): SheetImageType | undefined {
|
|
29
29
|
const mime = file.type.trim().toLowerCase()
|
|
30
30
|
if (mime && MIME_TO_TYPE[mime]) return MIME_TO_TYPE[mime]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CellRange } from '
|
|
2
|
-
import type { BorderPreset } from '
|
|
3
|
-
import { BORDER_STYLE_WIDTH, type BorderLineStyle } from '
|
|
1
|
+
import type { CellRange } from 'infinitable/sheet'
|
|
2
|
+
import type { BorderPreset } from 'infinitable/sheet'
|
|
3
|
+
import { BORDER_STYLE_WIDTH, type BorderLineStyle } from 'infinitable/sheet'
|
|
4
4
|
|
|
5
5
|
import type { SheetContext } from '../../tools/context'
|
|
6
6
|
|
|
@@ -35,15 +35,15 @@
|
|
|
35
35
|
|
|
36
36
|
<script lang="ts" setup>
|
|
37
37
|
import { UPalette } from '@veltra/desktop'
|
|
38
|
-
import
|
|
39
|
-
import {
|
|
38
|
+
import { bem } from '@veltra/utils'
|
|
39
|
+
import type { SetCellStyleItem } from 'infinitable/sheet'
|
|
40
|
+
import { buildBorderPresetItems } from 'infinitable/sheet'
|
|
40
41
|
import {
|
|
41
42
|
BORDER_STYLE_WIDTH,
|
|
42
43
|
type BorderEdge,
|
|
43
44
|
type BorderLineStyle,
|
|
44
45
|
type CellStylePatch
|
|
45
|
-
} from '
|
|
46
|
-
import { bem } from '@veltra/utils'
|
|
46
|
+
} from 'infinitable/sheet'
|
|
47
47
|
import { ref } from 'vue'
|
|
48
48
|
|
|
49
49
|
import type { SheetContext } from '../../../tools/context'
|
|
@@ -30,7 +30,7 @@ const cls = bem('sheet')
|
|
|
30
30
|
function exportXlsx(): void {
|
|
31
31
|
emit('close')
|
|
32
32
|
// 大工作簿序列化在 worker 中进行(数秒):给常驻反馈,失败(如 sheet 名含
|
|
33
|
-
// Excel
|
|
33
|
+
// Excel 非法字符,引擎写入校验抛 InvalidArgumentError)明确报错,
|
|
34
34
|
// 绝不静默失败
|
|
35
35
|
const loading = message({ message: '正在导出…', duration: 0 })
|
|
36
36
|
exportWorkbookFile(props.context)
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
|
|
70
70
|
<script lang="ts" setup>
|
|
71
71
|
import { UInput } from '@veltra/desktop'
|
|
72
|
-
import type { Sheet } from '@veltra/sheet-core/core/sheet.js'
|
|
73
72
|
import { bem } from '@veltra/utils'
|
|
73
|
+
import type { Sheet } from 'infinitable/sheet'
|
|
74
74
|
|
|
75
75
|
import type { SheetContext } from '../../../tools/context'
|
|
76
76
|
import { useFindReplace } from '../use-find-replace'
|
|
@@ -28,9 +28,7 @@
|
|
|
28
28
|
@click="emit('select', fn.name)"
|
|
29
29
|
@mouseenter="activeIndex = index"
|
|
30
30
|
>
|
|
31
|
-
<span :class="cls.e('functions-signature')">{{
|
|
32
|
-
formatFunctionSignature(fn.name, fn.params)
|
|
33
|
-
}}</span>
|
|
31
|
+
<span :class="cls.e('functions-signature')">{{ fn.signature }}</span>
|
|
34
32
|
<span v-if="fn.description" :class="cls.e('functions-description')">{{
|
|
35
33
|
fn.description
|
|
36
34
|
}}</span>
|
|
@@ -44,16 +42,12 @@
|
|
|
44
42
|
|
|
45
43
|
<script lang="ts" setup>
|
|
46
44
|
import { UInput, UScroll } from '@veltra/desktop'
|
|
47
|
-
import {
|
|
48
|
-
listFormulaFunctions,
|
|
49
|
-
type FormulaFunctionMeta
|
|
50
|
-
} from '@veltra/sheet-core/core/formula/functions.js'
|
|
51
45
|
import { bem } from '@veltra/utils'
|
|
46
|
+
import { listFormulaFunctions, type FormulaFunctionInfo } from 'infinitable'
|
|
52
47
|
import { computed, onMounted, ref, useTemplateRef, watch } from 'vue'
|
|
53
48
|
|
|
54
49
|
import {
|
|
55
50
|
COMMON_FORMULA_NAMES,
|
|
56
|
-
formatFunctionSignature,
|
|
57
51
|
FUNCTION_POPUP_CATEGORIES,
|
|
58
52
|
moveSuggestIndex,
|
|
59
53
|
type FunctionPopupCategory
|
|
@@ -68,7 +62,7 @@ defineOptions({ name: 'USheetFunctionsPopup' })
|
|
|
68
62
|
* 弹框随 v-if 挂载 / 销毁,每次打开都是全新状态(关键词清空、回到「常用」)。
|
|
69
63
|
*/
|
|
70
64
|
|
|
71
|
-
type FunctionItem =
|
|
65
|
+
type FunctionItem = FormulaFunctionInfo
|
|
72
66
|
|
|
73
67
|
const emit = defineEmits<{ select: [name: string] }>()
|
|
74
68
|
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
* - { type: 'done', ok: true, sheets: [{ name, snapshot }], activeIndex }(快照为
|
|
9
9
|
* 纯数据,可结构化克隆);失败 { type: 'done', ok: false, error }。
|
|
10
10
|
*
|
|
11
|
-
* 解析与构建经 sheet
|
|
11
|
+
* 解析与构建经 infinitable/sheet importXlsx 完成(hucre 已内联进引擎统一包);
|
|
12
12
|
* 名字唯一化与活动表对齐在 worker 内完成,主线程直接把快照替换进目标
|
|
13
13
|
* (无 undo 历史——替换语义由确认后的 replaceWorkbookWithSnapshots 负责)。
|
|
14
14
|
*
|
|
15
15
|
* 注意:必须用**运行时动态 import**——worker 顶层静态 import 在 vite dev 的
|
|
16
16
|
* worker 上下文中会因模块图加载顺序导致 `Workbook is not defined`(实测)。
|
|
17
17
|
*/
|
|
18
|
-
import type { SheetSnapshot } from '
|
|
18
|
+
import type { SheetSnapshot } from 'infinitable/sheet'
|
|
19
19
|
|
|
20
20
|
export type ImportWorkerResponse =
|
|
21
21
|
| { type: 'progress'; done: number; total: number }
|
|
@@ -31,7 +31,7 @@ self.onmessage = (e: MessageEvent<{ buffer: ArrayBuffer }>): void => {
|
|
|
31
31
|
const { buffer } = e.data
|
|
32
32
|
void (async () => {
|
|
33
33
|
try {
|
|
34
|
-
const { importXlsx } = await import('
|
|
34
|
+
const { importXlsx } = await import('infinitable/sheet')
|
|
35
35
|
// 分片构建:按 10% 粒度回报进度(避免 196 条/秒的消息风暴与文字跳变过快;
|
|
36
36
|
// 模型构建段约 1s,10 次更新肉眼可见数字推进)
|
|
37
37
|
let lastPercent = -1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ContextmenuItem } from '@veltra/desktop'
|
|
2
|
-
import { createRange, type CellAddress, type CellRange } from '
|
|
3
|
-
import type { NumFmt } from '
|
|
2
|
+
import { createRange, type CellAddress, type CellRange } from 'infinitable/sheet'
|
|
3
|
+
import type { NumFmt } from 'infinitable/sheet'
|
|
4
4
|
import { defineComponent, h } from 'vue'
|
|
5
5
|
|
|
6
6
|
import type { SheetContext } from '../../tools/context'
|
|
@@ -20,7 +20,7 @@ export const DECIMAL_PLACES_MAX = 10
|
|
|
20
20
|
/** 行高/列宽数值项输入上限(下限复用门面 MIN_ROW_COL_SIZE,对齐引擎 resize 最小值) */
|
|
21
21
|
export const AXIS_SIZE_MAX = 1000
|
|
22
22
|
|
|
23
|
-
/**
|
|
23
|
+
/** 数值项默认值回落:未设置自定义尺寸时展示引擎默认行高/列宽(对齐引擎 grid-theme) */
|
|
24
24
|
export const FALLBACK_ROW_HEIGHT = 28
|
|
25
25
|
export const FALLBACK_COL_WIDTH = 80
|
|
26
26
|
|
|
@@ -247,7 +247,7 @@ export function buildColHeaderMenus(
|
|
|
247
247
|
|
|
248
248
|
/**
|
|
249
249
|
* 对当前选区应用 numFmt(经 applyStyle → 命令系统,天然可 undo/redo)。
|
|
250
|
-
*
|
|
250
|
+
* 仅影响显示,单元格恒存原始值(引擎 numFmt 显示约定)。
|
|
251
251
|
*/
|
|
252
252
|
export function applyNumFmtToSelection(ctx: SheetContext, numFmt: NumFmt): void {
|
|
253
253
|
for (const range of ctx.getSelection().ranges) ctx.applyStyle(range, { numFmt })
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
<script lang="ts" setup>
|
|
55
55
|
import { contextmenu, message, messageConfirm, UIcon } from '@veltra/desktop'
|
|
56
56
|
import { ArrowLeft, ArrowRight } from '@veltra/icons/normal'
|
|
57
|
-
import type { Sheet } from '@veltra/sheet-core/core/sheet.js'
|
|
58
|
-
import type { Workbook } from '@veltra/sheet-core/core/workbook.js'
|
|
59
57
|
import { bem } from '@veltra/utils'
|
|
58
|
+
import type { Sheet } from 'infinitable/sheet'
|
|
59
|
+
import type { Workbook } from 'infinitable/sheet'
|
|
60
60
|
import { nextTick, ref, toRef, useTemplateRef } from 'vue'
|
|
61
61
|
|
|
62
62
|
import { useSheetTabsBar } from './use-sheet-tabs-bar'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="rootRef" :class="cls.b">
|
|
3
|
-
<div :class="cls.e('toolbar-wrap')">
|
|
3
|
+
<div ref="toolbarWrapRef" :class="cls.e('toolbar-wrap')">
|
|
4
4
|
<u-sheet-toolbar v-if="showToolbar" :groups="toolGroups" @tool-click="handleToolClick" />
|
|
5
5
|
|
|
6
6
|
<!-- 弹层型工具面板(填充/边框/字体色/字号/查找/函数/插入图片/导出):UDropdown(Teleport 到
|
|
@@ -113,7 +113,13 @@ const props = withDefaults(defineProps<SheetProps>(), {
|
|
|
113
113
|
showColHeader: true,
|
|
114
114
|
readonly: false,
|
|
115
115
|
colResize: false,
|
|
116
|
-
|
|
116
|
+
// 缺省原生档:引擎在 grid 容器内装配真实 DOM 滚动容器(原生滚动条独立
|
|
117
|
+
// gutter,不遮挡单元格),画布悬浮滚动条不再绘制。对象缺省被所有实例共享,
|
|
118
|
+
// 只读透传不改动
|
|
119
|
+
scrollbar: { mode: 'native' },
|
|
120
|
+
// 显式对齐引擎当前缺省(true 近边增长):boolean prop 不写 default 会被
|
|
121
|
+
// Vue 铸成 false,把引擎缺省翻成固定尺寸网格
|
|
122
|
+
growOnScroll: true
|
|
117
123
|
})
|
|
118
124
|
|
|
119
125
|
const emit = defineEmits<SheetEmits>()
|
|
@@ -123,6 +129,9 @@ const cls = bem('sheet')
|
|
|
123
129
|
/** 根容器 ref:Ctrl/Cmd+F 判定焦点是否在本实例内(#6,避免劫持浏览器查找) */
|
|
124
130
|
const rootRef = useTemplateRef<HTMLElement>('rootRef')
|
|
125
131
|
|
|
132
|
+
/** 工具栏容器 ref:工具栏隐藏(showToolbar=false)时查找弹层的回退锚点(0 高贴 sheet 顶) */
|
|
133
|
+
const toolbarWrapRef = useTemplateRef<HTMLElement>('toolbarWrapRef')
|
|
134
|
+
|
|
126
135
|
// ─── 状态源(workbook / sheet 列表 / 活动 sheet / 工具上下文)────────
|
|
127
136
|
// hooks 引用的 closePopup / rebuildGrid 由下方组合提供:箭头函数在事件触发时才
|
|
128
137
|
// 求值(彼时已完成全部 setup),组合声明顺序不构成循环依赖。
|
|
@@ -144,7 +153,7 @@ const {
|
|
|
144
153
|
popupAnchor,
|
|
145
154
|
closePopup,
|
|
146
155
|
handleToolClick: openOrRunTool
|
|
147
|
-
} = useToolPopup(context, rootRef)
|
|
156
|
+
} = useToolPopup(context, rootRef, () => toolbarWrapRef.value ?? null)
|
|
148
157
|
// xlsx 解析中(worker):grid 容器显示自绘覆盖层(import-file 写入 parsing /
|
|
149
158
|
// parseProgress;遮罩 + 动画 + 文字同层——动画在上、文字在下)
|
|
150
159
|
const parsing = ref(false)
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { debounce } from '@cat-kit/core'
|
|
2
|
-
import {
|
|
3
|
-
inferCellType,
|
|
4
|
-
normalizeInputValue,
|
|
5
|
-
type CellData
|
|
6
|
-
} from '@veltra/sheet-core/core/cell-store.js'
|
|
2
|
+
import { inferCellType, normalizeInputValue, type CellData } from 'infinitable/sheet'
|
|
7
3
|
import {
|
|
8
4
|
findAll,
|
|
9
5
|
findNextFrom,
|
|
10
6
|
findPrevFrom,
|
|
11
7
|
type FindMatch,
|
|
12
8
|
type FindOptions
|
|
13
|
-
} from '
|
|
14
|
-
import type { Sheet } from '
|
|
9
|
+
} from 'infinitable/sheet'
|
|
10
|
+
import type { Sheet } from 'infinitable/sheet'
|
|
15
11
|
import { computed, ref, shallowRef, watch } from 'vue'
|
|
16
12
|
|
|
17
13
|
import type { SheetContext } from '../../tools/context'
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
listFormulaFunctions,
|
|
3
|
-
type FormulaFunctionMeta
|
|
4
|
-
} from '@veltra/sheet-core/core/formula/functions.js'
|
|
1
|
+
import { listFormulaFunctions, type FormulaFunctionInfo } from 'infinitable'
|
|
5
2
|
|
|
6
3
|
/** 补全候选上限 */
|
|
7
4
|
export const FORMULA_SUGGEST_LIMIT = 10
|
|
@@ -26,7 +23,7 @@ export const COMMON_FORMULA_NAMES = [
|
|
|
26
23
|
/**
|
|
27
24
|
* 函数弹框分类导航固定集合(顺序即展示顺序)。
|
|
28
25
|
* 「常用」「全部」是导航概念(非注册表分类):常用 = COMMON_FORMULA_NAMES 固定清单,
|
|
29
|
-
* 全部 =
|
|
26
|
+
* 全部 = 全部已注册函数;其余与公式注册表 `FormulaFunctionCategory` 对应。
|
|
30
27
|
*/
|
|
31
28
|
export const FUNCTION_POPUP_CATEGORIES = [
|
|
32
29
|
'常用',
|
|
@@ -131,12 +128,13 @@ export function filterFormulaSuggestions(prefix: string): FormulaSuggestItem[] {
|
|
|
131
128
|
return items
|
|
132
129
|
}
|
|
133
130
|
|
|
134
|
-
function toSuggestItem(fn:
|
|
131
|
+
function toSuggestItem(fn: FormulaFunctionInfo): FormulaSuggestItem {
|
|
132
|
+
const params = fn.params.map((p) => p.name)
|
|
135
133
|
return {
|
|
136
134
|
name: fn.name,
|
|
137
|
-
params
|
|
135
|
+
params,
|
|
138
136
|
description: fn.description,
|
|
139
|
-
signature: formatFunctionSignature(fn.name,
|
|
137
|
+
signature: formatFunctionSignature(fn.name, params)
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
140
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { contextmenu } from '@veltra/desktop'
|
|
2
|
-
import type { CellAddress, CellRange } from '
|
|
3
|
-
import type { Sheet } from '
|
|
4
|
-
import { SheetGrid, type SheetGridContextMenuInfo } from '
|
|
2
|
+
import type { CellAddress, CellRange } from 'infinitable/sheet'
|
|
3
|
+
import type { Sheet } from 'infinitable/sheet'
|
|
4
|
+
import { SheetGrid, type SheetGridContextMenuInfo } from 'infinitable/sheet'
|
|
5
5
|
import { onBeforeUnmount, onMounted, watch } from 'vue'
|
|
6
6
|
|
|
7
7
|
import type { SheetContext } from '../../tools/context'
|
|
@@ -132,6 +132,7 @@ export function useSheetGrid(options: UseSheetGridOptions) {
|
|
|
132
132
|
editors: props.editors,
|
|
133
133
|
colResize: props.colResize,
|
|
134
134
|
scrollbar: props.scrollbar,
|
|
135
|
+
growOnScroll: props.growOnScroll,
|
|
135
136
|
onContextMenu: handleContextMenu,
|
|
136
137
|
onEditStart: (addr) => formulaBarRef.value?.mirrorGridEdit(addr),
|
|
137
138
|
onEditEnd: (addr) => formulaBarRef.value?.exitMirror(addr),
|
|
@@ -275,7 +276,8 @@ export function useSheetGrid(options: UseSheetGridOptions) {
|
|
|
275
276
|
props.header,
|
|
276
277
|
props.editors,
|
|
277
278
|
props.colResize,
|
|
278
|
-
props.scrollbar
|
|
279
|
+
props.scrollbar,
|
|
280
|
+
props.growOnScroll
|
|
279
281
|
] as const,
|
|
280
282
|
([nextRows, nextCols]) => {
|
|
281
283
|
const sheet = getActiveSheet()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Sheet } from '
|
|
2
|
-
import { Workbook } from '
|
|
1
|
+
import type { Sheet } from 'infinitable/sheet'
|
|
2
|
+
import { Workbook } from 'infinitable/sheet'
|
|
3
3
|
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
|
4
4
|
|
|
5
5
|
import { createSheetContext } from '../../tools/context'
|
|
@@ -31,7 +31,15 @@ function joinsTransaction(tool: SheetTool): boolean {
|
|
|
31
31
|
* 函数面板选中只进入公式栏编辑态(无直接模型写入),与查找 / 导出一样不参与事务。
|
|
32
32
|
* 导入在 sheet.vue 特殊处理(直接系统文件选择),不经弹层。
|
|
33
33
|
*/
|
|
34
|
-
export function useToolPopup(
|
|
34
|
+
export function useToolPopup(
|
|
35
|
+
context: SheetContext,
|
|
36
|
+
rootEl: ElRef,
|
|
37
|
+
/**
|
|
38
|
+
* 查找弹层回退锚点(工具栏隐藏时 find 按钮不可达,取 sheet 顶部容器:
|
|
39
|
+
* 0 高贴 sheet 顶,bottom-start 定位下弹层自 sheet 左上角展开,落在 sheet 视口内)
|
|
40
|
+
*/
|
|
41
|
+
getFallbackAnchor?: () => HTMLElement | null | undefined
|
|
42
|
+
) {
|
|
35
43
|
/** 当前打开的弹层工具(null = 未打开) */
|
|
36
44
|
const popupTool = shallowRef<SheetTool | null>(null)
|
|
37
45
|
/** 触发按钮元素(打开时的 currentTarget;用于面板 left 对齐按钮) */
|
|
@@ -104,6 +112,18 @@ export function useToolPopup(context: SheetContext, rootEl: ElRef) {
|
|
|
104
112
|
closePopup()
|
|
105
113
|
}
|
|
106
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Ctrl/Cmd+F 的锚点解析:优先工具栏「查找与替换」按钮(与按钮入口完全同锚点,
|
|
117
|
+
* 弹层同样锚定按钮下方);按钮不可达(showToolbar 隐藏)时回退 sheet 顶部容器。
|
|
118
|
+
*/
|
|
119
|
+
function resolveFindAnchor(): HTMLElement | null {
|
|
120
|
+
const root = rootEl.value
|
|
121
|
+
if (!root) return null
|
|
122
|
+
const button = root.querySelector<HTMLElement>('[data-tool-id="find"]')
|
|
123
|
+
if (button) return button
|
|
124
|
+
return getFallbackAnchor?.() ?? null
|
|
125
|
+
}
|
|
126
|
+
|
|
107
127
|
/** Ctrl/Cmd+F 打开 / 关闭查找条(与工具按钮同一 toggle 逻辑) */
|
|
108
128
|
function onGlobalKeydown(event: KeyboardEvent): void {
|
|
109
129
|
if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== 'f') return
|
|
@@ -131,7 +151,7 @@ export function useToolPopup(context: SheetContext, rootEl: ElRef) {
|
|
|
131
151
|
closePopup()
|
|
132
152
|
return
|
|
133
153
|
}
|
|
134
|
-
scheduleOpen(findTool)
|
|
154
|
+
scheduleOpen(findTool, resolveFindAnchor())
|
|
135
155
|
}
|
|
136
156
|
|
|
137
157
|
onMounted(() => {
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import './tools/builtin'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// SheetContextOptions:createSheetContext(公共导出)的第三参类型——导出函数
|
|
4
|
+
// 签名引用的稳定类型须随函数可达(无头场景宿主需引用该类型),不属引擎符号
|
|
5
|
+
export { createSheetContext, type SheetContext, type SheetContextOptions } from './tools/context'
|
|
4
6
|
|
|
5
7
|
export {
|
|
6
8
|
defaultToolRegistry,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createRange } from '
|
|
2
|
-
import { Sheet } from '
|
|
1
|
+
import { createRange } from 'infinitable/sheet'
|
|
2
|
+
import { Sheet } from 'infinitable/sheet'
|
|
3
3
|
import { describe, expect, it } from 'vite-plus/test'
|
|
4
4
|
|
|
5
5
|
import { classifySelectionStyleTarget, type SelectionStyleTarget } from '../apply-style'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createRange } from '
|
|
1
|
+
import { createRange } from 'infinitable/sheet'
|
|
2
2
|
|
|
3
3
|
import '../builtin'
|
|
4
|
-
import { Sheet } from '
|
|
5
|
-
import { Workbook } from '
|
|
4
|
+
import { Sheet } from 'infinitable/sheet'
|
|
5
|
+
import { Workbook } from 'infinitable/sheet'
|
|
6
6
|
import { describe, expect, it, vi } from 'vite-plus/test'
|
|
7
7
|
|
|
8
8
|
import { createSheetContext } from '../context'
|
|
@@ -122,7 +122,7 @@ describe('内置工具(dogfood 扩展机制)', () => {
|
|
|
122
122
|
}
|
|
123
123
|
})
|
|
124
124
|
|
|
125
|
-
it('导出 xlsx:sheet 名含 Excel 非法字符时 reject
|
|
125
|
+
it('导出 xlsx:sheet 名含 Excel 非法字符时 reject(引擎写入校验,错误传播给调用方)', async () => {
|
|
126
126
|
const workbook = new Workbook()
|
|
127
127
|
workbook.renameSheet('Sheet1', 'a:b')
|
|
128
128
|
const ctx = createSheetContext(workbook.activeSheet, workbook)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createRange } from '
|
|
2
|
-
import { Sheet } from '
|
|
1
|
+
import { createRange } from 'infinitable/sheet'
|
|
2
|
+
import { Sheet } from 'infinitable/sheet'
|
|
3
3
|
import { describe, expect, it, vi } from 'vite-plus/test'
|
|
4
4
|
|
|
5
5
|
import { createSheetContext, MIN_ROW_COL_SIZE } from '../context'
|
package/src/tools/apply-style.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CellRange } from '
|
|
2
|
-
import type { SetAxisStyleItem } from '
|
|
3
|
-
import type { CellStylePatch } from '
|
|
1
|
+
import type { CellRange } from 'infinitable/sheet'
|
|
2
|
+
import type { SetAxisStyleItem } from 'infinitable/sheet'
|
|
3
|
+
import type { CellStylePatch } from 'infinitable/sheet'
|
|
4
4
|
|
|
5
5
|
/** 样式写入目标:整行 / 整列默认样式,或逐格 */
|
|
6
6
|
export type SelectionStyleTarget = 'row' | 'col' | 'cell'
|
package/src/tools/builtin.ts
CHANGED
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
VerticalAlignRight,
|
|
25
25
|
Wrap
|
|
26
26
|
} from '@veltra/icons/normal'
|
|
27
|
-
import { rangesEqual } from '
|
|
28
|
-
import type { HorizontalAlign, VerticalAlign } from '
|
|
27
|
+
import { rangesEqual } from 'infinitable/sheet'
|
|
28
|
+
import type { HorizontalAlign, VerticalAlign } from 'infinitable/sheet'
|
|
29
29
|
|
|
30
30
|
import type { SheetContext } from './context'
|
|
31
31
|
import { registerTool } from './registry'
|
package/src/tools/context.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type { CellAddress, CellRange } from '
|
|
2
|
-
import type { CellData, CellValue } from '
|
|
3
|
-
import type { HistoryState } from '
|
|
4
|
-
import type { ImageUpdateFields } from '
|
|
5
|
-
import type { SetCellValueItem } from '
|
|
6
|
-
import type { ImageInput, SheetImage } from '
|
|
7
|
-
import type { CellInfo } from '
|
|
8
|
-
import type { SelectionState } from '
|
|
9
|
-
import type { FrozenState, Sheet } from '
|
|
10
|
-
import type { CellStyle, CellStylePatch } from '
|
|
11
|
-
import type { Workbook } from '
|
|
1
|
+
import type { CellAddress, CellRange } from 'infinitable/sheet'
|
|
2
|
+
import type { CellData, CellValue } from 'infinitable/sheet'
|
|
3
|
+
import type { HistoryState } from 'infinitable/sheet'
|
|
4
|
+
import type { ImageUpdateFields } from 'infinitable/sheet'
|
|
5
|
+
import type { SetCellValueItem } from 'infinitable/sheet'
|
|
6
|
+
import type { ImageInput, SheetImage } from 'infinitable/sheet'
|
|
7
|
+
import type { CellInfo } from 'infinitable/sheet'
|
|
8
|
+
import type { SelectionState } from 'infinitable/sheet'
|
|
9
|
+
import type { FrozenState, Sheet } from 'infinitable/sheet'
|
|
10
|
+
import type { CellStyle, CellStylePatch } from 'infinitable/sheet'
|
|
11
|
+
import type { Workbook } from 'infinitable/sheet'
|
|
12
12
|
|
|
13
13
|
import { axisStyleItemsForRange, classifySelectionStyleTarget } from './apply-style'
|
|
14
14
|
|
package/src/tools/download.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { saveBlob } from '@cat-kit/fe'
|
|
2
|
-
import { exportSheetCsv, exportWorkbookXlsx } from '
|
|
3
|
-
import type { Workbook } from '
|
|
2
|
+
import { exportSheetCsv, exportWorkbookXlsx } from 'infinitable/sheet'
|
|
3
|
+
import type { Workbook } from 'infinitable/sheet'
|
|
4
4
|
|
|
5
5
|
import type { SheetContext } from './context'
|
|
6
6
|
import type { ExportWorkerPayload, ExportWorkerResponse } from './export.worker'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* 大工作簿的 XLSX
|
|
9
|
+
* 大工作簿的 XLSX 导出(引擎序列化 + ZIP 压缩为秒级同步重活)移入 Web Worker:
|
|
10
10
|
* 主线程只采集快照(结构化克隆友好的纯数据,行高随 SheetSnapshot.rowHeights
|
|
11
11
|
* 携带),序列化与压缩在 worker 线程进行。worker 不可用(极端环境 / 打包产物
|
|
12
12
|
* 缺文件)回退主线程导出——与导入侧 parseXlsxAsync 同一模式。
|
|
@@ -55,7 +55,7 @@ async function exportWorkbookXlsxAsync(workbook: Workbook): Promise<Uint8Array>
|
|
|
55
55
|
/**
|
|
56
56
|
* 导出当前工作簿为 .xlsx(无 workbook 时空操作)。
|
|
57
57
|
* 下载动作复用 @cat-kit/fe 的 saveBlob(#28:不再自研 Object URL + a.click)。
|
|
58
|
-
*
|
|
58
|
+
* 导出侧引擎的 writeXlsx 会校验 sheet 名(Excel 非法字符 / 超长 / 保留名)并抛
|
|
59
59
|
* InvalidArgumentError——失败经返回的 Promise 传播,调用方(导出面板)负责提示。
|
|
60
60
|
*/
|
|
61
61
|
export async function exportWorkbookFile(ctx: SheetContext): Promise<void> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="webworker" />
|
|
2
2
|
/**
|
|
3
|
-
* XLSX 导出 Worker:sheet
|
|
3
|
+
* XLSX 导出 Worker:infinitable/sheet exportWorkbookXlsx(模型序列化 + ZIP 压缩,
|
|
4
4
|
* 大工作簿为秒级同步重活)移到独立线程,主线程保持空闲。
|
|
5
5
|
*
|
|
6
6
|
* 协议:主线程 postMessage({ sheets: [{ name, snapshot }], activeIndex })
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 静态 import 在 vite dev 的 worker 上下文中会因模块图加载顺序导致
|
|
13
13
|
* `Workbook is not defined`(实测)。
|
|
14
14
|
*/
|
|
15
|
-
import type { SheetSnapshot } from '
|
|
15
|
+
import type { SheetSnapshot } from 'infinitable/sheet'
|
|
16
16
|
|
|
17
17
|
export interface ExportWorkerPayload {
|
|
18
18
|
sheets: { name: string; snapshot: SheetSnapshot }[]
|
|
@@ -26,17 +26,14 @@ export interface ExportWorkerResponse {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
self.onmessage = (e: MessageEvent<ExportWorkerPayload>): void => {
|
|
29
|
-
void Promise.all([
|
|
30
|
-
import('@veltra/sheet-core/core/io/export.js'),
|
|
31
|
-
import('@veltra/sheet-core/core/workbook.js')
|
|
32
|
-
])
|
|
29
|
+
void Promise.all([import('infinitable/sheet'), import('infinitable/sheet')])
|
|
33
30
|
.then(([{ exportWorkbookXlsx }, { Workbook }]) => {
|
|
34
31
|
const wb = new Workbook()
|
|
35
32
|
const sheets = e.data.sheets
|
|
36
33
|
for (let i = 0; i < sheets.length; i++) {
|
|
37
34
|
const { name, snapshot } = sheets[i]!
|
|
38
35
|
// 首个 sheet 复用 Workbook 自带的默认表:先改名对齐(空名/重名保持原名,
|
|
39
|
-
//
|
|
36
|
+
// 导出侧引擎的 writeXlsx 会校验表名合法性)
|
|
40
37
|
const sheet = i === 0 ? wb.activeSheet : wb.addSheet(name)
|
|
41
38
|
if (i === 0 && sheet.name !== name) wb.renameSheet(sheet.name, name)
|
|
42
39
|
sheet.restore(snapshot)
|
package/src/tools/registry.ts
CHANGED
package/src/types/sheet.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { DeconstructValue } from '@veltra/utils'
|
|
2
|
+
import type { Sheet } from 'infinitable/sheet'
|
|
3
|
+
import type { Workbook } from 'infinitable/sheet'
|
|
3
4
|
import type {
|
|
4
5
|
ResolveCellRenderer,
|
|
5
6
|
ResolveCellStyleHook,
|
|
6
7
|
ResolveDisplayValue,
|
|
8
|
+
ScrollbarOptions,
|
|
7
9
|
SheetGrid,
|
|
8
10
|
SheetGridEditorsOptions,
|
|
9
11
|
SheetGridHeaderOptions
|
|
10
|
-
} from '
|
|
11
|
-
import type { DeconstructValue } from '@veltra/utils'
|
|
12
|
+
} from 'infinitable/sheet'
|
|
12
13
|
import type { ComputedRef } from 'vue'
|
|
13
14
|
|
|
14
15
|
import type { SheetContext } from '../tools/context'
|
|
@@ -31,7 +32,7 @@ export interface SheetProps {
|
|
|
31
32
|
resolveCellStyle?: ResolveCellStyleHook
|
|
32
33
|
/**
|
|
33
34
|
* 动态单元格渲染(ADR-0004):视口布局时按格自定义渲染形态(返回引擎
|
|
34
|
-
* `CellRenderer`,见
|
|
35
|
+
* `CellRenderer`,见 `infinitable/sheet`),返回 undefined 回落默认渲染;
|
|
35
36
|
* 不写模型、不进快照
|
|
36
37
|
*/
|
|
37
38
|
resolveCellRenderer?: ResolveCellRenderer
|
|
@@ -63,10 +64,20 @@ export interface SheetProps {
|
|
|
63
64
|
*/
|
|
64
65
|
colResize?: boolean
|
|
65
66
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
67
|
+
* 滚动条(透传 SheetGrid → 引擎内建,构造期选项):false 整体关闭(不显示任何
|
|
68
|
+
* 滚动条);缺省原生档 `{ mode: 'native' }`——浏览器原生滚动条在独立 gutter
|
|
69
|
+
* 渲染、不遮挡最底行/最右列;true 与对象形态按引擎语义透传(对象可配
|
|
70
|
+
* `mode: 'canvas'` 回画布悬浮滚动条,及 visibility / hideDelay / reserve 等
|
|
71
|
+
* canvas 档显示策略)。对象形态引用更替触发网格重建——沿用 header / editors
|
|
72
|
+
* 的稳定引用约定,宿主勿在模板内联对象字面量
|
|
73
|
+
*/
|
|
74
|
+
scrollbar?: boolean | ScrollbarOptions
|
|
75
|
+
/**
|
|
76
|
+
* 滚动近端动态增长(透传 SheetGrid,构造期选项):滚动缓冲末端触到当前行列数
|
|
77
|
+
* 时模型与引擎同步扩容(引擎缺省 true);false 为固定尺寸网格——内容定尺寸的
|
|
78
|
+
* 报表 / 表单宿主置 false 后不再滚入空白增长区。变化触发网格重建
|
|
68
79
|
*/
|
|
69
|
-
|
|
80
|
+
growOnScroll?: boolean
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
export interface SheetEmits {
|