@veltra/sheet 2.0.0 → 2.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.
Files changed (203) hide show
  1. package/dist/index.d.ts +4 -25
  2. package/dist/index.js +3 -22
  3. package/dist/tools/builtin.js +279 -8
  4. package/dist/tools/context.d.ts +54 -11
  5. package/dist/tools/context.js +28 -5
  6. package/dist/tools/download.js +68 -0
  7. package/dist/tools/export.worker.d.ts +16 -0
  8. package/dist/tools/export.worker.js +29 -0
  9. package/dist/tools/registry.d.ts +7 -4
  10. package/dist/tools/registry.js +1 -3
  11. package/dist/types/index.d.ts +1 -0
  12. package/dist/types/sheet.d.ts +6 -5
  13. package/dist/vue/formula-bar.js +393 -0
  14. package/dist/vue/formula-suggest-list.js +66 -0
  15. package/dist/vue/import-file.js +140 -0
  16. package/dist/vue/insert-count-menu-item.js +68 -0
  17. package/dist/vue/insert-image.js +79 -0
  18. package/dist/vue/popup-helpers.js +73 -0
  19. package/dist/vue/popups/border-popup.js +75 -0
  20. package/dist/vue/popups/border-preset-glyph.js +106 -0
  21. package/dist/vue/popups/export-popup.js +51 -0
  22. package/dist/vue/popups/fill-color-popup.js +37 -0
  23. package/dist/vue/popups/find-popup.js +113 -0
  24. package/dist/vue/popups/font-color-popup.js +37 -0
  25. package/dist/vue/popups/font-size-popup.js +63 -0
  26. package/dist/vue/popups/import.worker.d.ts +21 -0
  27. package/dist/vue/popups/import.worker.js +41 -0
  28. package/dist/vue/popups/insert-image-popup.js +38 -0
  29. package/dist/vue/sheet-context-menu.js +200 -0
  30. package/dist/vue/sheet-tabs.js +162 -0
  31. package/dist/vue/sheet-toolbar.js +106 -0
  32. package/dist/vue/sheet.js +144 -138
  33. package/dist/vue/style.d.ts +11 -1
  34. package/dist/vue/style.js +11 -0
  35. package/dist/vue/style2.css +564 -3
  36. package/dist/vue/use-find-replace.js +161 -0
  37. package/dist/vue/use-formula-ref-select.js +37 -0
  38. package/dist/vue/use-formula-suggest.js +126 -0
  39. package/dist/vue/use-sheet-grid.js +180 -0
  40. package/dist/vue/use-sheet-state.js +122 -0
  41. package/dist/vue/use-sheet-tabs-bar.js +90 -0
  42. package/dist/vue/use-tool-groups.js +42 -0
  43. package/dist/vue/use-tool-popup.js +123 -0
  44. package/dist/vue/use-toolbar-scroll.js +72 -0
  45. package/package.json +12 -6
  46. package/src/env.d.ts +14 -0
  47. package/src/index.ts +203 -13
  48. package/src/tools/__test__/builtin.test.ts +208 -66
  49. package/src/tools/__test__/context.test.ts +62 -2
  50. package/src/tools/builtin.ts +350 -6
  51. package/src/tools/context.ts +89 -11
  52. package/src/tools/download.ts +82 -0
  53. package/src/tools/export.worker.ts +61 -0
  54. package/src/tools/registry.ts +16 -1
  55. package/src/types/index.ts +6 -0
  56. package/src/types/sheet.ts +5 -3
  57. package/src/vue/__test__/formula-bar.test.ts +455 -0
  58. package/src/vue/__test__/import-file.test.ts +183 -0
  59. package/src/vue/__test__/insert-count-menu-item.test.ts +123 -0
  60. package/src/vue/__test__/insert-image-popup.test.ts +89 -0
  61. package/src/vue/__test__/insert-image.test.ts +100 -0
  62. package/src/vue/__test__/sheet-component.test.ts +716 -27
  63. package/src/vue/__test__/sheet-context-menu.test.ts +206 -0
  64. package/src/vue/__test__/use-formula-ref-select.test.ts +35 -0
  65. package/src/vue/__test__/use-formula-suggest.test.ts +83 -0
  66. package/src/vue/formula-bar.vue +473 -0
  67. package/src/vue/formula-suggest-list.vue +64 -0
  68. package/src/vue/import-file.ts +173 -0
  69. package/src/vue/insert-count-menu-item.vue +69 -0
  70. package/src/vue/insert-image.ts +90 -0
  71. package/src/vue/parsing.ts +11 -0
  72. package/src/vue/popup-helpers.ts +50 -0
  73. package/src/vue/popups/border-popup.vue +93 -0
  74. package/src/vue/popups/border-preset-glyph.vue +111 -0
  75. package/src/vue/popups/export-popup.vue +52 -0
  76. package/src/vue/popups/fill-color-popup.vue +39 -0
  77. package/src/vue/popups/find-popup.vue +111 -0
  78. package/src/vue/popups/font-color-popup.vue +39 -0
  79. package/src/vue/popups/font-size-popup.vue +52 -0
  80. package/src/vue/popups/import.worker.ts +66 -0
  81. package/src/vue/popups/insert-image-popup.vue +39 -0
  82. package/src/vue/sheet-context-menu.ts +229 -0
  83. package/src/vue/sheet-tabs.vue +162 -0
  84. package/src/vue/sheet-toolbar.vue +91 -0
  85. package/src/vue/sheet.vue +163 -148
  86. package/src/vue/style.scss +646 -3
  87. package/src/vue/style.ts +12 -0
  88. package/src/vue/use-find-replace.ts +200 -0
  89. package/src/vue/use-formula-ref-select.ts +34 -0
  90. package/src/vue/use-formula-suggest.ts +150 -0
  91. package/src/vue/use-sheet-grid.ts +243 -0
  92. package/src/vue/use-sheet-state.ts +161 -0
  93. package/src/vue/use-sheet-tabs-bar.ts +115 -0
  94. package/src/vue/use-tool-groups.ts +61 -0
  95. package/src/vue/use-tool-popup.ts +150 -0
  96. package/src/vue/use-toolbar-scroll.ts +95 -0
  97. package/dist/core/address.d.ts +0 -60
  98. package/dist/core/address.js +0 -142
  99. package/dist/core/address.js.map +0 -1
  100. package/dist/core/cell-store.d.ts +0 -61
  101. package/dist/core/cell-store.js +0 -135
  102. package/dist/core/cell-store.js.map +0 -1
  103. package/dist/core/command/default-registry.d.ts +0 -10
  104. package/dist/core/command/default-registry.js +0 -18
  105. package/dist/core/command/default-registry.js.map +0 -1
  106. package/dist/core/command/history.d.ts +0 -64
  107. package/dist/core/command/history.js +0 -127
  108. package/dist/core/command/history.js.map +0 -1
  109. package/dist/core/command/merge-cells.d.ts +0 -21
  110. package/dist/core/command/merge-cells.js +0 -74
  111. package/dist/core/command/merge-cells.js.map +0 -1
  112. package/dist/core/command/registry.d.ts +0 -17
  113. package/dist/core/command/registry.js +0 -26
  114. package/dist/core/command/registry.js.map +0 -1
  115. package/dist/core/command/set-cell-formula.d.ts +0 -17
  116. package/dist/core/command/set-cell-formula.js +0 -29
  117. package/dist/core/command/set-cell-formula.js.map +0 -1
  118. package/dist/core/command/set-cell-value.d.ts +0 -22
  119. package/dist/core/command/set-cell-value.js +0 -35
  120. package/dist/core/command/set-cell-value.js.map +0 -1
  121. package/dist/core/command/types.d.ts +0 -68
  122. package/dist/core/events.js +0 -29
  123. package/dist/core/events.js.map +0 -1
  124. package/dist/core/formula/ast.d.ts +0 -58
  125. package/dist/core/formula/ast.js +0 -38
  126. package/dist/core/formula/ast.js.map +0 -1
  127. package/dist/core/formula/dependency-graph.d.ts +0 -70
  128. package/dist/core/formula/dependency-graph.js +0 -269
  129. package/dist/core/formula/dependency-graph.js.map +0 -1
  130. package/dist/core/formula/errors.d.ts +0 -25
  131. package/dist/core/formula/errors.js +0 -36
  132. package/dist/core/formula/errors.js.map +0 -1
  133. package/dist/core/formula/evaluator.d.ts +0 -41
  134. package/dist/core/formula/evaluator.js +0 -154
  135. package/dist/core/formula/evaluator.js.map +0 -1
  136. package/dist/core/formula/functions.d.ts +0 -30
  137. package/dist/core/formula/functions.js +0 -209
  138. package/dist/core/formula/functions.js.map +0 -1
  139. package/dist/core/formula/parser.d.ts +0 -7
  140. package/dist/core/formula/parser.js +0 -208
  141. package/dist/core/formula/parser.js.map +0 -1
  142. package/dist/core/formula/tokenizer.d.ts +0 -37
  143. package/dist/core/formula/tokenizer.js +0 -146
  144. package/dist/core/formula/tokenizer.js.map +0 -1
  145. package/dist/core/merge-manager.d.ts +0 -64
  146. package/dist/core/merge-manager.js +0 -111
  147. package/dist/core/merge-manager.js.map +0 -1
  148. package/dist/core/selection.d.ts +0 -31
  149. package/dist/core/selection.js +0 -62
  150. package/dist/core/selection.js.map +0 -1
  151. package/dist/core/sheet.d.ts +0 -105
  152. package/dist/core/sheet.js +0 -202
  153. package/dist/core/sheet.js.map +0 -1
  154. package/dist/core/workbook.d.ts +0 -43
  155. package/dist/core/workbook.js +0 -77
  156. package/dist/core/workbook.js.map +0 -1
  157. package/dist/grid/sheet-grid.d.ts +0 -59
  158. package/dist/grid/sheet-grid.js +0 -230
  159. package/dist/grid/sheet-grid.js.map +0 -1
  160. package/dist/tools/builtin.js.map +0 -1
  161. package/dist/tools/context.js.map +0 -1
  162. package/dist/tools/registry.js.map +0 -1
  163. package/dist/vue/index.d.ts +0 -2
  164. package/dist/vue/sheet.js.map +0 -1
  165. package/dist/vue/sheet.vue.d.ts +0 -24
  166. package/src/core/__test__/address.test.ts +0 -113
  167. package/src/core/__test__/cell-store.test.ts +0 -97
  168. package/src/core/__test__/command.test.ts +0 -377
  169. package/src/core/__test__/formula-adversarial.test.ts +0 -265
  170. package/src/core/__test__/formula-engine.test.ts +0 -470
  171. package/src/core/__test__/formula-functions.test.ts +0 -178
  172. package/src/core/__test__/formula-parser.test.ts +0 -160
  173. package/src/core/__test__/history.test.ts +0 -225
  174. package/src/core/__test__/merge-manager.test.ts +0 -106
  175. package/src/core/__test__/sheet.test.ts +0 -142
  176. package/src/core/address.ts +0 -166
  177. package/src/core/cell-store.ts +0 -173
  178. package/src/core/command/default-registry.ts +0 -15
  179. package/src/core/command/history.ts +0 -167
  180. package/src/core/command/index.ts +0 -7
  181. package/src/core/command/merge-cells.ts +0 -74
  182. package/src/core/command/registry.ts +0 -30
  183. package/src/core/command/set-cell-formula.ts +0 -29
  184. package/src/core/command/set-cell-value.ts +0 -38
  185. package/src/core/command/types.ts +0 -73
  186. package/src/core/events.ts +0 -37
  187. package/src/core/formula/ast.ts +0 -66
  188. package/src/core/formula/dependency-graph.ts +0 -329
  189. package/src/core/formula/errors.ts +0 -41
  190. package/src/core/formula/evaluator.ts +0 -219
  191. package/src/core/formula/functions.ts +0 -266
  192. package/src/core/formula/index.ts +0 -7
  193. package/src/core/formula/parser.ts +0 -198
  194. package/src/core/formula/tokenizer.ts +0 -160
  195. package/src/core/merge-manager.ts +0 -158
  196. package/src/core/selection.ts +0 -66
  197. package/src/core/sheet.ts +0 -257
  198. package/src/core/workbook.ts +0 -99
  199. package/src/grid/__test__/canvas-mock.ts +0 -49
  200. package/src/grid/__test__/setup.ts +0 -3
  201. package/src/grid/__test__/sheet-grid-formula.test.ts +0 -140
  202. package/src/grid/__test__/sheet-grid.test.ts +0 -177
  203. package/src/grid/sheet-grid.ts +0 -283
package/src/vue/sheet.vue CHANGED
@@ -1,49 +1,99 @@
1
1
  <template>
2
- <div :class="cls.b">
3
- <div v-if="showToolbar" :class="cls.e('toolbar')">
4
- <template v-for="(group, groupIndex) in toolGroups" :key="group.name">
5
- <span v-if="groupIndex > 0" :class="cls.e('toolbar-divider')" />
6
- <button
7
- v-for="item in group.tools"
8
- :key="item.tool.id"
9
- type="button"
10
- :class="cls.e('tool')"
11
- :disabled="item.disabled"
12
- :title="item.tool.tooltip ?? item.tool.title"
13
- @click="handleToolClick(item.tool)"
14
- >
15
- <component :is="item.tool.icon" v-if="item.tool.icon" :class="cls.e('tool-icon')" />
16
- <span>{{ item.tool.title }}</span>
17
- </button>
18
- </template>
2
+ <div ref="rootRef" :class="cls.b">
3
+ <div :class="cls.e('toolbar-wrap')">
4
+ <u-sheet-toolbar v-if="showToolbar" :groups="toolGroups" @tool-click="handleToolClick" />
5
+
6
+ <!-- 弹层型工具面板(填充/边框/字体色/字号/查找/插入图片/导出):UDropdown(Teleport 到
7
+ #pop-container + floating-ui 定位:锚点跟随触发按钮、自动翻转/边界位移;
8
+ 触发元素滚动/窗口缩放时自动关闭)。面板交互走 SheetContext 命令入口。
9
+ 导入无弹层:点击直接系统文件选择(见 handleToolClick → pickAndImportFile) -->
10
+ <u-dropdown
11
+ ref="popupDropdownRef"
12
+ trigger="custom"
13
+ :visible="popupTool !== null"
14
+ width="auto"
15
+ @update:visible="handlePopupVisible"
16
+ >
17
+ <template #content>
18
+ <div v-if="popupTool" :class="cls.e('popup')" @click.stop>
19
+ <u-sheet-fill-color-popup v-if="popupTool.popup === 'fill-color'" :context="context" />
20
+ <u-sheet-border-popup v-else-if="popupTool.popup === 'border'" :context="context" />
21
+ <u-sheet-font-color-popup
22
+ v-else-if="popupTool.popup === 'font-color'"
23
+ :context="context"
24
+ />
25
+ <u-sheet-font-size-popup
26
+ v-else-if="popupTool.popup === 'font-size'"
27
+ :context="context"
28
+ />
29
+ <u-sheet-find-popup
30
+ v-else-if="popupTool.popup === 'find'"
31
+ :sheet="activeSheet"
32
+ :context="context"
33
+ @close="closePopup"
34
+ />
35
+ <u-sheet-insert-image-popup
36
+ v-else-if="popupTool.popup === 'insert-image'"
37
+ :context="context"
38
+ @close="closePopup"
39
+ />
40
+ <u-sheet-export-popup
41
+ v-else-if="popupTool.popup === 'export'"
42
+ :context="context"
43
+ @close="closePopup"
44
+ />
45
+ </div>
46
+ </template>
47
+ </u-dropdown>
19
48
  </div>
20
49
 
21
- <div ref="gridRef" :class="cls.e('grid')" />
22
-
23
- <div v-if="showTabs" :class="cls.e('tabs')">
24
- <button
25
- v-for="(sheet, index) in sheetList"
26
- :key="sheet.name"
27
- type="button"
28
- :class="[cls.e('tab'), bem.is('active', index === activeIndex)]"
29
- @click="handleTabClick(index)"
30
- >
31
- {{ sheet.name }}
32
- </button>
50
+ <u-formula-bar
51
+ v-if="showFormulaBar"
52
+ ref="formulaBarRef"
53
+ :sheet="activeSheet"
54
+ :context="context"
55
+ />
56
+
57
+ <div ref="gridRef" :class="cls.e('grid')">
58
+ <!-- 导入解析覆盖层(自绘,不动 desktop Loading 组件):遮罩 + 动画 + 文字
59
+ 同一层,动画在上、文字在下,不存在被遮罩掩盖的问题 -->
60
+ <div v-if="parsing" :class="cls.e('loading-mask')">
61
+ <div :class="cls.e('loading-spinner')"></div>
62
+ <div :class="cls.e('loading-text')">{{ parseText }}</div>
63
+ </div>
33
64
  </div>
65
+
66
+ <u-sheet-tabs
67
+ v-if="showTabs"
68
+ :workbook="workbook"
69
+ :sheet-list="sheetList"
70
+ :active-index="activeIndex"
71
+ />
34
72
  </div>
35
73
  </template>
36
74
 
37
75
  <script lang="ts" setup>
76
+ import { UDropdown } from '@veltra/desktop'
38
77
  import { bem } from '@veltra/utils'
39
- import { computed, onBeforeUnmount, onMounted, ref, shallowRef, useTemplateRef, watch } from 'vue'
78
+ import { computed, ref, useTemplateRef, watch } from 'vue'
40
79
 
41
- import type { Sheet } from '../core/sheet'
42
- import { Workbook } from '../core/workbook'
43
- import { SheetGrid } from '../grid/sheet-grid'
44
- import { createSheetContext } from '../tools/context'
45
- import { defaultToolRegistry, type SheetTool } from '../tools/registry'
80
+ import type { SheetTool } from '../tools/registry'
46
81
  import type { SheetEmits, SheetProps, _SheetExposed } from '../types'
82
+ import UFormulaBar from './formula-bar.vue'
83
+ import { pickAndImportFile } from './import-file'
84
+ import USheetBorderPopup from './popups/border-popup.vue'
85
+ import USheetExportPopup from './popups/export-popup.vue'
86
+ import USheetFillColorPopup from './popups/fill-color-popup.vue'
87
+ import USheetFindPopup from './popups/find-popup.vue'
88
+ import USheetFontColorPopup from './popups/font-color-popup.vue'
89
+ import USheetFontSizePopup from './popups/font-size-popup.vue'
90
+ import USheetInsertImagePopup from './popups/insert-image-popup.vue'
91
+ import USheetTabs from './sheet-tabs.vue'
92
+ import USheetToolbar from './sheet-toolbar.vue'
93
+ import { useSheetGrid } from './use-sheet-grid'
94
+ import { useSheetState } from './use-sheet-state'
95
+ import { useToolGroups } from './use-tool-groups'
96
+ import { useToolPopup } from './use-tool-popup'
47
97
 
48
98
  defineOptions({ name: 'USheet' })
49
99
 
@@ -51,6 +101,7 @@ const props = withDefaults(defineProps<SheetProps>(), {
51
101
  rows: 100,
52
102
  cols: 26,
53
103
  showToolbar: true,
104
+ showFormulaBar: true,
54
105
  showTabs: true
55
106
  })
56
107
 
@@ -58,137 +109,101 @@ const emit = defineEmits<SheetEmits>()
58
109
 
59
110
  const cls = bem('sheet')
60
111
 
61
- /** 工作簿:外部传入或内部自建(单 sheet) */
62
- const internalWorkbook = new Workbook()
63
- const workbook = computed(() => props.workbook ?? internalWorkbook)
64
-
65
- /** 激活 sheet 索引(镜像 workbook.activeSheetIndex,经事件同步) */
66
- const activeIndex = ref(workbook.value.activeSheetIndex)
67
- /** sheet 列表(tabs 渲染;由 sheets-change 同步) */
68
- const sheetList = shallowRef<Sheet[]>(workbook.value.getSheets())
69
- const activeSheet = computed(() => sheetList.value[activeIndex.value] ?? workbook.value.activeSheet)
70
-
71
- /** 工具上下文:动态解析活动 sheet,tab 切换后自动指向当前 sheet */
72
- const context = createSheetContext(() => activeSheet.value)
73
-
74
- const gridRef = useTemplateRef<HTMLElement>('gridRef')
75
- let grid: SheetGrid | undefined
76
-
77
- // ─── 工具栏状态刷新 ─────────────────────────────────────────
78
- // 工具 visible/disabled 是 (ctx) => boolean 纯函数,状态源变化时 bump 触发重算
79
-
80
- const stateTick = ref(0)
81
- const bump = (): void => {
82
- stateTick.value++
83
- }
84
-
85
- let disposeSheetEvents: (() => void)[] = []
86
- /** 订阅活动 sheet 的状态源(tab 切换 / 工作簿变更时重绑) */
87
- function bindSheetEvents(sheet: Sheet): void {
88
- for (const dispose of disposeSheetEvents) dispose()
89
- disposeSheetEvents = [
90
- sheet.on('selection-change', bump),
91
- sheet.on('history-change', bump),
92
- sheet.on('cell-change', bump),
93
- sheet.on('merge-change', bump)
94
- ]
95
- }
112
+ /** 根容器 ref:Ctrl/Cmd+F 判定焦点是否在本实例内(#6,避免劫持浏览器查找) */
113
+ const rootRef = useTemplateRef<HTMLElement>('rootRef')
114
+
115
+ // ─── 状态源(workbook / sheet 列表 / 活动 sheet / 工具上下文)────────
116
+ // hooks 引用的 closePopup / rebuildGrid 由下方组合提供:箭头函数在事件触发时才
117
+ // 求值(彼时已完成全部 setup),组合声明顺序不构成循环依赖。
118
+
119
+ const { workbook, activeIndex, sheetList, activeSheet, context, stateTick, syncFromWorkbook } =
120
+ useSheetState(props, emit, {
121
+ onBeforeSheetChange: () => closePopup(),
122
+ rebuildGrid: () => rebuildGrid(),
123
+ activateGrid: () => activateGrid(),
124
+ pruneCache: (sheets) => pruneCache(sheets)
125
+ })
126
+
127
+ // ─── 弹层型工具编排(打开 / 关闭 / 面板事务)─────────────────────
128
+
129
+ const {
130
+ popupTool,
131
+ popupAnchor,
132
+ closePopup,
133
+ handleToolClick: openOrRunTool
134
+ } = useToolPopup(context, rootRef)
135
+ // xlsx 解析中(worker):grid 容器显示自绘覆盖层(import-file 写入 parsing /
136
+ // parseProgress;遮罩 + 动画 + 文字同层——动画在上、文字在下)
137
+ const parsing = ref(false)
138
+ const parseProgress = ref({ done: 0, total: 0 })
139
+ const parseText = computed(() =>
140
+ parseProgress.value.total > 0
141
+ ? `正在解析… ${parseProgress.value.done}/${parseProgress.value.total}`
142
+ : '正在读取文件结构…'
143
+ )
96
144
 
97
- let disposeWorkbookEvents: (() => void)[] = []
98
- function bindWorkbookEvents(wb: Workbook): void {
99
- for (const dispose of disposeWorkbookEvents) dispose()
100
- disposeWorkbookEvents = [
101
- wb.on('sheets-change', ({ sheets }) => {
102
- sheetList.value = sheets
103
- bump()
104
- }),
105
- wb.on('active-sheet-change', ({ sheet, index }) => {
106
- activeIndex.value = index
107
- bindSheetEvents(sheet)
108
- rebuildGrid()
109
- bump()
110
- emit('active-sheet-change', { sheet, index })
145
+ /** 导入无弹层:直接系统文件选择;其余工具走 useToolPopup */
146
+ function handleToolClick(tool: SheetTool, event?: MouseEvent): void {
147
+ if (tool.id === 'import') {
148
+ if (tool.disabled?.(context)) return
149
+ pickAndImportFile({
150
+ workbook: workbook.value,
151
+ activeSheet: activeSheet.value,
152
+ parsing,
153
+ parseProgress,
154
+ onCsvImported: () => rebuildGrid(),
155
+ onWorkbookReplaced: () => syncFromWorkbook()
111
156
  })
112
- ]
157
+ return
158
+ }
159
+ openOrRunTool(tool, event)
113
160
  }
114
161
 
115
- const offRegistryChange = defaultToolRegistry.onChange(bump)
116
-
117
- // ─── 工具栏 ────────────────────────────────────────────────
118
-
119
- const toolGroups = computed(() => {
120
- void stateTick.value // 依赖状态源:选区 / 历史 / 单元格 / 合并 / 注册表
121
- return defaultToolRegistry
122
- .getGroups()
123
- .map((group) => ({
124
- name: group.name,
125
- tools: group.tools
126
- .map((tool) => ({
127
- tool,
128
- visible: tool.visible?.(context) ?? true,
129
- disabled: tool.disabled?.(context) ?? false
130
- }))
131
- .filter((item) => item.visible)
132
- }))
133
- .filter((group) => group.tools.length > 0)
162
+ // ─── UDropdown 面板:锚点跟随触发按钮(floating-ui 定位)──────────
163
+
164
+ const popupDropdownRef = useTemplateRef<InstanceType<typeof UDropdown>>('popupDropdownRef')
165
+
166
+ // popupTool 变化 → 打开(传触发按钮作锚点)/ 关闭。
167
+ // 必须同步调用 open():usePop 在 content 挂载时执行定位与滚动监听,
168
+ // 若等 nextTick 后再设 customTriggerRef,usePop 首次 update 时锚点缺失 → 面板不定位。
169
+ watch(popupTool, () => {
170
+ const dropdown = popupDropdownRef.value
171
+ if (!dropdown) return
172
+ if (popupTool.value) {
173
+ // 触发元素移动(工具栏滚动 / 窗口缩放)由 usePop 监听 → UDropdown 自动关闭
174
+ dropdown.open({ trigger: popupAnchor.value ?? undefined })
175
+ } else {
176
+ dropdown.close()
177
+ }
134
178
  })
135
179
 
136
- function handleToolClick(tool: SheetTool): void {
137
- if (tool.disabled?.(context)) return
138
- tool.onClick(context)
139
- }
140
-
141
- // ─── sheet tabs ─────────────────────────────────────────────
142
-
143
- function handleTabClick(index: number): void {
144
- if (index === activeIndex.value) return
145
- const sheet = sheetList.value[index]
146
- if (sheet) workbook.value.activateSheet(sheet.name)
180
+ /** UDropdown 内部关闭(滚动 / resize / transition 结束)→ 同步状态并提交面板事务 */
181
+ function handlePopupVisible(visible: boolean): void {
182
+ if (!visible) closePopup()
147
183
  }
148
184
 
149
- // ─── grid 生命周期 ──────────────────────────────────────────
185
+ // ─── 工具栏分组视图模型 ────────────────────────────────────────
150
186
 
151
- function rebuildGrid(): void {
152
- const container = gridRef.value
153
- if (!container) return
154
- grid?.release()
155
- grid = new SheetGrid({ container, sheet: activeSheet.value, rows: props.rows, cols: props.cols })
156
- }
187
+ const toolGroups = useToolGroups(context, stateTick)
157
188
 
158
- onMounted(() => {
159
- bindWorkbookEvents(workbook.value)
160
- bindSheetEvents(activeSheet.value)
161
- rebuildGrid()
162
- })
189
+ // ─── grid 生命周期(重建 / 右键菜单 / 公式栏编辑镜像)────────────────
163
190
 
164
- watch(() => [props.rows, props.cols], rebuildGrid)
165
-
166
- watch(
167
- () => props.workbook,
168
- (wb, prev) => {
169
- if (wb === prev) return
170
- activeIndex.value = workbook.value.activeSheetIndex
171
- sheetList.value = workbook.value.getSheets()
172
- bindWorkbookEvents(workbook.value)
173
- bindSheetEvents(activeSheet.value)
174
- rebuildGrid()
175
- bump()
176
- }
177
- )
191
+ const gridRef = useTemplateRef<HTMLElement>('gridRef')
192
+ const formulaBarRef = useTemplateRef<InstanceType<typeof UFormulaBar>>('formulaBarRef')
178
193
 
179
- onBeforeUnmount(() => {
180
- offRegistryChange()
181
- for (const dispose of disposeWorkbookEvents) dispose()
182
- for (const dispose of disposeSheetEvents) dispose()
183
- grid?.release()
184
- grid = undefined
194
+ const { rebuildGrid, activateGrid, pruneCache, getGrid } = useSheetGrid({
195
+ props,
196
+ gridRef,
197
+ getActiveSheet: () => activeSheet.value,
198
+ context,
199
+ formulaBarRef
185
200
  })
186
201
 
187
202
  const exposed: _SheetExposed = {
188
203
  workbook,
189
204
  getActiveSheet: () => activeSheet.value,
190
205
  getContext: () => context,
191
- getGrid: () => grid
206
+ getGrid
192
207
  }
193
208
 
194
209
  defineExpose(exposed)