@a3s-lab/office 0.2.2 → 0.4.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.
Files changed (277) hide show
  1. package/COLLABORATION_ROADMAP.md +426 -0
  2. package/README.md +649 -10
  3. package/dist/0~1403.js +1 -1
  4. package/dist/0~2805.js +1279 -0
  5. package/dist/{0~use-office-draft.js → 0~345.js} +30 -1
  6. package/dist/0~3539.js +1 -1
  7. package/dist/0~3557.js +1 -1
  8. package/dist/0~3635.js +1 -1
  9. package/dist/0~4595.js +123 -12
  10. package/dist/0~4980.js +154 -0
  11. package/dist/0~5050.js +124 -113
  12. package/dist/{0~9512.js → 0~6090.js} +540 -3
  13. package/dist/0~7048.js +92 -66
  14. package/dist/0~7614.js +2 -1
  15. package/dist/0~7754.js +5653 -0
  16. package/dist/0~9445.js +1 -1
  17. package/dist/0~document-editor.js +4992 -3323
  18. package/dist/0~markdown-editor.js +250 -44
  19. package/dist/0~pdf-viewer.js +1016 -24
  20. package/dist/0~presentation-editor.js +179 -8
  21. package/dist/0~spreadsheet-editor.js +3371 -578
  22. package/dist/0~work-docx-export.js +6801 -658
  23. package/dist/0~work-docx-import.js +1388 -795
  24. package/dist/0~work-office-diagnostics.js +524 -30
  25. package/dist/0~work-pptx-export.js +2 -1
  26. package/dist/0~work-pptx-import.js +1 -2
  27. package/dist/0~work-presentation-charts.js +1 -1
  28. package/dist/2591.js +197 -0
  29. package/dist/3818.js +1090 -0
  30. package/dist/4104.js +970 -60
  31. package/dist/432.js +171 -14
  32. package/dist/4476.js +2008 -0
  33. package/dist/4560.js +674 -0
  34. package/dist/4650.js +70 -0
  35. package/dist/{1134.js → 5184.js} +183 -1
  36. package/dist/6489.js +18 -18
  37. package/dist/7060.js +132 -0
  38. package/dist/8715.js +2 -136
  39. package/dist/8928.js +16778 -0
  40. package/dist/9787.js +209 -0
  41. package/dist/core.d.ts +29 -9
  42. package/dist/core.js +10 -2
  43. package/dist/index.js +13 -3
  44. package/dist/internal/collaboration/office-collaboration-flat-json.d.ts +13 -0
  45. package/dist/internal/collaboration/office-collaboration-json.d.ts +4 -0
  46. package/dist/internal/collaboration/office-collaboration-presence.d.ts +86 -0
  47. package/dist/internal/collaboration/office-collaboration-transport.d.ts +42 -0
  48. package/dist/internal/collaboration/office-collaboration.d.ts +85 -0
  49. package/dist/internal/collaboration/office-document-collaboration-bibliography.d.ts +7 -0
  50. package/dist/internal/collaboration/office-document-collaboration-claims.d.ts +9 -0
  51. package/dist/internal/collaboration/office-document-collaboration-comments.d.ts +7 -0
  52. package/dist/internal/collaboration/office-document-collaboration-sidecar-utils.d.ts +27 -0
  53. package/dist/internal/collaboration/office-document-collaboration-sidecars.d.ts +16 -0
  54. package/dist/internal/collaboration/office-document-collaboration.d.ts +45 -0
  55. package/dist/internal/collaboration/office-markdown-collaboration.d.ts +31 -0
  56. package/dist/internal/collaboration/office-pdf-collaboration-claims.d.ts +4 -0
  57. package/dist/internal/collaboration/office-pdf-collaboration-conflicts.d.ts +2 -0
  58. package/dist/internal/collaboration/office-pdf-collaboration-model.d.ts +26 -0
  59. package/dist/internal/collaboration/office-pdf-collaboration-records.d.ts +21 -0
  60. package/dist/internal/collaboration/office-pdf-collaboration-types.d.ts +91 -0
  61. package/dist/internal/collaboration/office-pdf-collaboration-validation.d.ts +4 -0
  62. package/dist/internal/collaboration/office-pdf-collaboration.d.ts +35 -0
  63. package/dist/internal/collaboration/office-presentation-collaboration-model.d.ts +25 -0
  64. package/dist/internal/collaboration/office-presentation-collaboration-records.d.ts +22 -0
  65. package/dist/internal/collaboration/office-presentation-collaboration-validation.d.ts +4 -0
  66. package/dist/internal/collaboration/office-presentation-collaboration.d.ts +32 -0
  67. package/dist/internal/collaboration/office-spreadsheet-collaboration-cells.d.ts +13 -0
  68. package/dist/internal/collaboration/office-spreadsheet-collaboration-claims.d.ts +4 -0
  69. package/dist/internal/collaboration/office-spreadsheet-collaboration-conflicts.d.ts +2 -0
  70. package/dist/internal/collaboration/office-spreadsheet-collaboration-flat-json.d.ts +10 -0
  71. package/dist/internal/collaboration/office-spreadsheet-collaboration-model.d.ts +31 -0
  72. package/dist/internal/collaboration/office-spreadsheet-collaboration-records.d.ts +17 -0
  73. package/dist/internal/collaboration/office-spreadsheet-collaboration-validation.d.ts +4 -0
  74. package/dist/internal/collaboration/office-spreadsheet-collaboration.d.ts +32 -0
  75. package/dist/internal/design-system/primitives/navigation/tabs.d.ts +3 -1
  76. package/dist/internal/features/work/components/work-document-page-border.d.ts +6 -0
  77. package/dist/internal/features/work/editors/document-command-catalog.d.ts +536 -0
  78. package/dist/internal/features/work/editors/document-editor-focus.d.ts +1 -1
  79. package/dist/internal/features/work/editors/document-editor-support.d.ts +7 -3
  80. package/dist/internal/features/work/editors/document-editor.d.ts +8 -2
  81. package/dist/internal/features/work/editors/document-external-content.d.ts +14 -0
  82. package/dist/internal/features/work/editors/document-format-clipboard.d.ts +6 -0
  83. package/dist/internal/features/work/editors/document-format-tools.d.ts +4 -0
  84. package/dist/internal/features/work/editors/document-layout-panel.d.ts +4 -1
  85. package/dist/internal/features/work/editors/document-page-chrome-wps-shortcuts.d.ts +2 -0
  86. package/dist/internal/features/work/editors/document-page-layout-ribbon.d.ts +14 -0
  87. package/dist/internal/features/work/editors/document-page-stack.d.ts +10 -1
  88. package/dist/internal/features/work/editors/document-page-thumbnail.d.ts +2 -0
  89. package/dist/internal/features/work/editors/document-pagination-run-coordinator.d.ts +26 -0
  90. package/dist/internal/features/work/editors/document-picture-properties-dialog-model.d.ts +20 -1
  91. package/dist/internal/features/work/editors/document-review-navigation.d.ts +9 -0
  92. package/dist/internal/features/work/editors/document-statistics-dialog.d.ts +7 -0
  93. package/dist/internal/features/work/editors/document-status-bar.d.ts +2 -1
  94. package/dist/internal/features/work/editors/document-table-properties-dialog-model.d.ts +4 -1
  95. package/dist/internal/features/work/editors/document-toolbar.d.ts +19 -42
  96. package/dist/internal/features/work/editors/document-wps-shortcuts.d.ts +12 -0
  97. package/dist/internal/features/work/editors/document-zoom.d.ts +17 -0
  98. package/dist/internal/features/work/editors/markdown-editor-focus.d.ts +1 -1
  99. package/dist/internal/features/work/editors/markdown-editor.d.ts +5 -1
  100. package/dist/internal/features/work/editors/markdown-toolbar.d.ts +2 -1
  101. package/dist/internal/features/work/editors/office-collaboration-participants.d.ts +3 -0
  102. package/dist/internal/features/work/editors/office-collaboration-presence-context.d.ts +17 -0
  103. package/dist/internal/features/work/editors/office-collaboration-presence-ui.d.ts +25 -0
  104. package/dist/internal/features/work/editors/office-editor-focus-handoff.d.ts +27 -0
  105. package/dist/internal/features/work/editors/pdf-annotation-controller.d.ts +5 -0
  106. package/dist/internal/features/work/editors/pdf-collaboration-presence.d.ts +3 -0
  107. package/dist/internal/features/work/editors/pdf-collaboration-projection.d.ts +10 -0
  108. package/dist/internal/features/work/editors/pdf-evidence-contract.d.ts +36 -0
  109. package/dist/internal/features/work/editors/pdf-evidence-overlay.d.ts +37 -0
  110. package/dist/internal/features/work/editors/pdf-toolbar.d.ts +2 -1
  111. package/dist/internal/features/work/editors/pdf-viewer-controller.d.ts +7 -1
  112. package/dist/internal/features/work/editors/pdf-viewer.d.ts +11 -1
  113. package/dist/internal/features/work/editors/presentation-collaboration-presence.d.ts +5 -0
  114. package/dist/internal/features/work/editors/presentation-editor-focus.d.ts +3 -2
  115. package/dist/internal/features/work/editors/presentation-editor-types.d.ts +3 -0
  116. package/dist/internal/features/work/editors/presentation-editor.d.ts +1 -1
  117. package/dist/internal/features/work/editors/spreadsheet-auto-filter.d.ts +9 -0
  118. package/dist/internal/features/work/editors/spreadsheet-cell-clear.d.ts +3 -0
  119. package/dist/internal/features/work/editors/spreadsheet-cell-merge.d.ts +12 -0
  120. package/dist/internal/features/work/editors/spreadsheet-clipboard-shortcuts.d.ts +3 -7
  121. package/dist/internal/features/work/editors/spreadsheet-collaboration-presence.d.ts +9 -0
  122. package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +464 -0
  123. package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +45 -9
  124. package/dist/internal/features/work/editors/spreadsheet-context-menu.d.ts +2 -0
  125. package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +9 -28
  126. package/dist/internal/features/work/editors/spreadsheet-editor.d.ts +8 -2
  127. package/dist/internal/features/work/editors/spreadsheet-format-painter.d.ts +27 -0
  128. package/dist/internal/features/work/editors/spreadsheet-freeze-panes.d.ts +14 -0
  129. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -2
  130. package/dist/internal/features/work/editors/use-document-pagination.d.ts +6 -1
  131. package/dist/internal/features/work/editors/use-document-review-conflicts.d.ts +28 -0
  132. package/dist/internal/features/work/editors/use-spreadsheet-auto-filter.d.ts +21 -0
  133. package/dist/internal/features/work/editors/use-spreadsheet-collaboration.d.ts +27 -0
  134. package/dist/internal/features/work/editors/use-spreadsheet-format-painter.d.ts +18 -0
  135. package/dist/internal/features/work/editors/work-office-chrome.d.ts +22 -3
  136. package/dist/internal/features/work/editors/work-office-ribbon-overflow.d.ts +2 -0
  137. package/dist/internal/features/work/work-document-bookmark-references.d.ts +37 -0
  138. package/dist/internal/features/work/work-document-bookmarks.d.ts +29 -0
  139. package/dist/internal/features/work/work-document-caption-nodes.d.ts +4 -1
  140. package/dist/internal/features/work/work-document-comments.d.ts +1 -1
  141. package/dist/internal/features/work/work-document-equations.d.ts +513 -0
  142. package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
  143. package/dist/internal/features/work/work-document-field-identity.d.ts +2 -0
  144. package/dist/internal/features/work/work-document-field-node.d.ts +2 -2
  145. package/dist/internal/features/work/work-document-fields.d.ts +7 -0
  146. package/dist/internal/features/work/work-document-image-identity.d.ts +30 -0
  147. package/dist/internal/features/work/work-document-image-layout.d.ts +46 -1
  148. package/dist/internal/features/work/work-document-image-wrap-contour.d.ts +16 -0
  149. package/dist/internal/features/work/work-document-incremental-layout.d.ts +2 -2
  150. package/dist/internal/features/work/work-document-model-codec.d.ts +4 -1
  151. package/dist/internal/features/work/work-document-note-integrity.d.ts +2 -0
  152. package/dist/internal/features/work/work-document-page-borders.d.ts +25 -0
  153. package/dist/internal/features/work/work-document-page-capture.d.ts +8 -0
  154. package/dist/internal/features/work/work-document-page-frames.d.ts +15 -0
  155. package/dist/internal/features/work/work-document-page-margins.d.ts +56 -0
  156. package/dist/internal/features/work/work-document-page-size.d.ts +47 -0
  157. package/dist/internal/features/work/work-document-pagination-measurement.d.ts +1 -2
  158. package/dist/internal/features/work/work-document-pagination-types.d.ts +7 -2
  159. package/dist/internal/features/work/work-document-pagination.d.ts +2 -2
  160. package/dist/internal/features/work/work-document-paragraph-borders.d.ts +32 -0
  161. package/dist/internal/features/work/work-document-paragraph-identity.d.ts +26 -0
  162. package/dist/internal/features/work/work-document-paragraph-shading.d.ts +18 -0
  163. package/dist/internal/features/work/work-document-review-conflicts.d.ts +35 -0
  164. package/dist/internal/features/work/work-document-section.d.ts +4 -0
  165. package/dist/internal/features/work/work-document-snapshot.d.ts +19 -0
  166. package/dist/internal/features/work/work-document-source.d.ts +23 -0
  167. package/dist/internal/features/work/work-document-table-column-widths.d.ts +7 -0
  168. package/dist/internal/features/work/work-document-table-property-changes.d.ts +3 -0
  169. package/dist/internal/features/work/work-document-table-row-identity.d.ts +15 -0
  170. package/dist/internal/features/work/work-document-table-sizing.d.ts +3 -0
  171. package/dist/internal/features/work/work-document-text-layout.d.ts +3 -1
  172. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +2 -0
  173. package/dist/internal/features/work/work-docx-bookmark-diagnostics.d.ts +3 -0
  174. package/dist/internal/features/work/work-docx-bookmark-import.d.ts +24 -0
  175. package/dist/internal/features/work/work-docx-bookmarks.d.ts +17 -0
  176. package/dist/internal/features/work/work-docx-comment-durable-id-preservation.d.ts +2 -0
  177. package/dist/internal/features/work/work-docx-comment-export.d.ts +3 -0
  178. package/dist/internal/features/work/work-docx-comment-metadata.d.ts +2 -0
  179. package/dist/internal/features/work/work-docx-drawing-extension-preservation.d.ts +4 -0
  180. package/dist/internal/features/work/work-docx-equation-diagnostics.d.ts +3 -0
  181. package/dist/internal/features/work/work-docx-equation-export.d.ts +14 -0
  182. package/dist/internal/features/work/work-docx-equation-import.d.ts +30 -0
  183. package/dist/internal/features/work/work-docx-equation-story.d.ts +9 -0
  184. package/dist/internal/features/work/work-docx-export-formatting.d.ts +3 -1
  185. package/dist/internal/features/work/work-docx-export-image.d.ts +5 -1
  186. package/dist/internal/features/work/work-docx-export.d.ts +1 -1
  187. package/dist/internal/features/work/work-docx-field-instructions.d.ts +8 -0
  188. package/dist/internal/features/work/work-docx-font-table-preservation.d.ts +3 -0
  189. package/dist/internal/features/work/work-docx-ignorable-extension-preservation.d.ts +15 -0
  190. package/dist/internal/features/work/work-docx-image-crop.d.ts +12 -0
  191. package/dist/internal/features/work/work-docx-image-identity.d.ts +17 -0
  192. package/dist/internal/features/work/work-docx-image-layer.d.ts +12 -0
  193. package/dist/internal/features/work/work-docx-image-layout-import.d.ts +14 -2
  194. package/dist/internal/features/work/work-docx-image-wrap.d.ts +15 -0
  195. package/dist/internal/features/work/work-docx-import.d.ts +13 -3
  196. package/dist/internal/features/work/work-docx-list-export.d.ts +18 -0
  197. package/dist/internal/features/work/work-docx-list-import.d.ts +14 -0
  198. package/dist/internal/features/work/work-docx-note-comment-content-control-block-preservation.d.ts +18 -0
  199. package/dist/internal/features/work/work-docx-note-comment-content-control-model.d.ts +47 -0
  200. package/dist/internal/features/work/work-docx-note-comment-content-control-preservation.d.ts +4 -0
  201. package/dist/internal/features/work/work-docx-note-comment-content-control-properties.d.ts +17 -0
  202. package/dist/internal/features/work/work-docx-note-comment-content-control-table-model.d.ts +17 -0
  203. package/dist/internal/features/work/work-docx-note-comment-content-control-xml.d.ts +14 -0
  204. package/dist/internal/features/work/work-docx-note-comment-extension-preservation.d.ts +2 -0
  205. package/dist/internal/features/work/work-docx-note-comment-hyperlink-content.d.ts +40 -0
  206. package/dist/internal/features/work/work-docx-note-comment-hyperlink-preservation.d.ts +5 -0
  207. package/dist/internal/features/work/work-docx-note-comment-hyperlink-relationships.d.ts +35 -0
  208. package/dist/internal/features/work/work-docx-note-comment-identity.d.ts +15 -0
  209. package/dist/internal/features/work/work-docx-note-comment-run-preservation.d.ts +4 -0
  210. package/dist/internal/features/work/work-docx-note-comment-run-properties.d.ts +2 -0
  211. package/dist/internal/features/work/work-docx-note-image-import.d.ts +12 -0
  212. package/dist/internal/features/work/work-docx-note-image-relationships.d.ts +1 -0
  213. package/dist/internal/features/work/work-docx-numbering-extension-preservation.d.ts +6 -0
  214. package/dist/internal/features/work/work-docx-numbering.d.ts +1 -0
  215. package/dist/internal/features/work/work-docx-page-borders-diagnostics.d.ts +3 -0
  216. package/dist/internal/features/work/work-docx-page-borders-import.d.ts +10 -0
  217. package/dist/internal/features/work/work-docx-page-margins-diagnostics.d.ts +3 -0
  218. package/dist/internal/features/work/work-docx-page-margins-import.d.ts +18 -0
  219. package/dist/internal/features/work/work-docx-page-size-diagnostics.d.ts +2 -0
  220. package/dist/internal/features/work/work-docx-page-size-import.d.ts +17 -0
  221. package/dist/internal/features/work/work-docx-paragraph-borders-diagnostics.d.ts +3 -0
  222. package/dist/internal/features/work/work-docx-paragraph-borders-export.d.ts +18 -0
  223. package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +24 -0
  224. package/dist/internal/features/work/work-docx-paragraph-default-collapsed.d.ts +19 -0
  225. package/dist/internal/features/work/work-docx-paragraph-extension-preservation.d.ts +2 -0
  226. package/dist/internal/features/work/work-docx-paragraph-identity-import.d.ts +10 -0
  227. package/dist/internal/features/work/work-docx-paragraph-identity.d.ts +18 -0
  228. package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +6 -1
  229. package/dist/internal/features/work/work-docx-paragraph-shading-diagnostics.d.ts +3 -0
  230. package/dist/internal/features/work/work-docx-paragraph-shading-export.d.ts +5 -0
  231. package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +18 -0
  232. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +3 -0
  233. package/dist/internal/features/work/work-docx-settings-preservation.d.ts +10 -0
  234. package/dist/internal/features/work/work-docx-settings-xml.d.ts +16 -0
  235. package/dist/internal/features/work/work-docx-style-extension-preservation.d.ts +2 -0
  236. package/dist/internal/features/work/work-docx-table-cell-export.d.ts +2 -1
  237. package/dist/internal/features/work/work-docx-table-cell-import.d.ts +3 -0
  238. package/dist/internal/features/work/work-docx-table-extension-preservation.d.ts +2 -0
  239. package/dist/internal/features/work/work-docx-table-row-import.d.ts +2 -0
  240. package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +1 -0
  241. package/dist/internal/features/work/work-docx-theme-reference.d.ts +23 -0
  242. package/dist/internal/features/work/work-docx-theme.d.ts +4 -1
  243. package/dist/internal/features/work/work-docx-twips.d.ts +12 -0
  244. package/dist/internal/features/work/work-file-contract.d.ts +1 -0
  245. package/dist/internal/features/work/work-file-io.d.ts +1 -1
  246. package/dist/internal/features/work/work-markdown-extensions.d.ts +4 -1
  247. package/dist/internal/features/work/work-ooxml-package-preservation.d.ts +12 -0
  248. package/dist/internal/features/work/work-ooxml-package-security.d.ts +6 -0
  249. package/dist/internal/features/work/work-ooxml-relationship-preservation.d.ts +10 -0
  250. package/dist/internal/features/work/work-ooxml-xml.d.ts +2 -0
  251. package/dist/internal/features/work/work-pdf-export.d.ts +10 -0
  252. package/dist/internal/features/work/work-repository.d.ts +2 -0
  253. package/dist/internal/features/work/work-types.d.ts +15 -2
  254. package/dist/internal/kernel/office-kernel-client-types.d.ts +1 -0
  255. package/dist/internal/kernel/office-kernel-protocol.d.ts +7 -0
  256. package/dist/internal/kernel/office-kernel-version.d.ts +1 -1
  257. package/dist/office-kernel.wasm +0 -0
  258. package/dist/office-kernel.worker.js +93 -41
  259. package/dist/react.d.ts +18 -12
  260. package/dist/react.js +1 -1
  261. package/dist/styles.css +1367 -16
  262. package/dist/vue.d.ts +48 -5
  263. package/dist/vue.js +42 -3
  264. package/dist/web-component.d.ts +24 -2
  265. package/dist/web-component.js +101 -2
  266. package/docs/latest/en/browser-editor-architecture.md +262 -47
  267. package/package.json +29 -4
  268. package/dist/0~152.js +0 -94
  269. package/dist/0~2582.js +0 -485
  270. package/dist/0~4750.js +0 -939
  271. package/dist/0~5861.js +0 -596
  272. package/dist/0~work-document-extensions.js +0 -3045
  273. package/dist/0~work-document-model-codec.js +0 -42
  274. package/dist/0~work-docx-bibliography.js +0 -347
  275. package/dist/0~work-docx-page-color.js +0 -42
  276. package/dist/2881.js +0 -1455
  277. package/dist/8728.js +0 -723
@@ -1,22 +1,22 @@
1
1
  import { __webpack_require__ } from "./0~rslib-runtime.js";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Workbook } from "@fortune-sheet/react";
4
- import { AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, BarChart3, Bold, Bookmark, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Cloud, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, Grid3X3, Italic, KeyRound, Layers2, Merge, MoveHorizontal, MoveVertical, Palette, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Save, Scissors, Search, ShieldCheck, TableProperties, Trash2, Underline, Undo2, WrapText, X } from "lucide-react";
4
+ import { AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Grid3X3, Italic, KeyRound, Layers2, Link2Off, ListFilter, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, ShieldCheck, SortAsc, SortDesc, TableProperties, Trash2, Underline, Undo2, WrapText, X } from "lucide-react";
5
5
  import { Fragment as external_react_Fragment, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
- import { genarate, update as core_update } from "@fortune-sheet/core";
7
- import { withoutEditableRange, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, editableRangeRequiresCredentials, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSpreadsheetConditionalVisualOptions, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, effectiveSpreadsheetPageSetup, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, withEditableRange, spreadsheetConditionalComparisonNeedsUpperValue, isSpreadsheetConditionalComparisonOperator, drawSpreadsheetConditionalIcon, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, sheetProtectionAuthority, spreadsheetConditionalThresholdsEqual, editableRangeCellCount, unlockedCellCount } from "./8728.js";
8
- import { showToast } from "./6489.js";
9
- import { useOfficeTaskPaneEscape, OfficeCheckbox, CommittedOfficeNumberField, useOfficeDialog, useOfficeTaskPaneModal, isOfficeKernelSpreadsheetError, officeFontFamilies, CollectionState, office_kernel_spreadsheet_protocol_OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, OfficeNumberField, createOfficeKernelClient, handleOfficeTaskPaneKeyDown } from "./0~7048.js";
6
+ import { genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
7
+ import { withoutEditableRange, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, editableRangeRequiresCredentials, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSpreadsheetConditionalVisualOptions, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, effectiveSpreadsheetPageSetup, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, withEditableRange, spreadsheetConditionalComparisonNeedsUpperValue, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, isSpreadsheetConditionalComparisonOperator, drawSpreadsheetConditionalIcon, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, sheetProtectionAuthority, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, spreadsheetConditionalThresholdsEqual, editableRangeCellCount, unlockedCellCount } from "./4476.js";
8
+ import { useOfficeTaskPaneEscape, OfficeCheckbox, CommittedOfficeNumberField, useOfficeDialog, useOfficeTaskPaneModal, createOfficeKernelClient, officeFontFamilies, CollectionState, OfficeNumberField, handleOfficeTaskPaneKeyDown } from "./0~7048.js";
10
9
  import { isOfficeShortcutBlocked, OfficeColorPicker } from "./0~3635.js";
11
10
  import { createOfficeEditorExtension, useOfficeEditorRuntime, useOfficeEditorKeyboardShortcuts } from "./0~3557.js";
11
+ import { showToast } from "./6489.js";
12
12
  import { workSpreadsheetChartSupportsGrouping, createSpreadsheetChartFromSelection, normalizeWorkSpreadsheetDataLabelPosition, normalizeSpreadsheetPrintTitleRows, normalizeSpreadsheetPrintTitleColumns, formulaHasStructuredReference, normalizeWorkSpreadsheetErrorBars, defaultPivotValueCaption, workSpreadsheetChartSupportsBarSpacing, normalizeWorkSpreadsheetChartGrouping, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetScatterStyle, spreadsheetPivotAggregationLabel, workSpreadsheetChartAxisIsCategoryAxis, reconcileSpreadsheetChartPreviews, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetChartLegendPosition, spreadsheetChartCount, workSpreadsheetChartTypeLabel, normalizeWorkSpreadsheetTrendline, spreadsheetPivotOutputContains, effectiveSpreadsheetCalculationSettings, normalizeWorkSpreadsheetDataLabels, spreadsheetPivotIntersects, normalizeSpreadsheetPrintArea, spreadsheetCellAddress, formatSpreadsheetCellRanges, normalizeWorkSpreadsheetChartLayout, spreadsheetFormulaRangeForCell, workSpreadsheetChartAxisIsValueAxis, normalizeWorkSpreadsheetChartSmoothLines, spreadsheetPivotFilterValueKey, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetChartSeriesStyle, volatileSpreadsheetFormulaFunctions, normalizeWorkSpreadsheetBubbleSizeRepresents, workSpreadsheetChartSupportsTrendlines, normalizeWorkSpreadsheetChartLegendOverlay, parseSpreadsheetChartReference, workSpreadsheetChartGroupingIsStacked, normalizeWorkSpreadsheetChartGapWidth, workSpreadsheetChartSupportsSmoothLines, refreshSpreadsheetPivotTables, createSpreadsheetPivotFromSelection, spreadsheetPivotCount, reconcileSpreadsheetPivots, workSpreadsheetChartSupportsErrorBars, formulaHasExternalReference, isValidSpreadsheetDefinedName, spreadsheetPivotValidation, deleteSpreadsheetPivotTable, workSpreadsheetChartAxisDefaultLabelPosition, workSpreadsheetChartSupportsSeriesAnalysis, parseSpreadsheetCellRanges, spreadsheetPivotFields, spreadsheetPivotFilterItems, normalizeWorkSpreadsheetChartOverlap, spreadsheetFormulaRangeConflict, spreadsheetSheetsWithChartPreviews, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, workSpreadsheetChartSupportsAxes, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetRadarStyle } from "./8715.js";
13
13
  import { unsupportedSpreadsheetFormulaFunctions } from "./0~work-spreadsheet-formula-support.js";
14
14
  import { WorkspaceContextMenu, isWorkspaceContextMenuKeyboardEvent, WorkOfficeZoomControls, WorkOfficeStatusBar, WorkOfficeRibbon, workspaceContextMenuPosition, OfficeSelect, WorkOfficeRibbonGroup, WorkOfficePreviewBar, WorkOfficeRibbonButton } from "./0~4595.js";
15
- import { moveOfficeMenuFocus, stepOfficeZoom, Popover, button_Button, icon_button_IconButton, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom } from "./0~9512.js";
16
- import { useOfficeDraft } from "./0~use-office-draft.js";
15
+ import { moveOfficeMenuFocus, stepOfficeZoom, Popover, useOfficeRemoteParticipants, button_Button, officePresenceColor, useOfficePublishPresenceLocation, icon_button_IconButton, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom } from "./0~6090.js";
16
+ import { useOfficeDraft, InlineNotice } from "./0~345.js";
17
17
  import { spreadsheetAgentMenuItems, SpreadsheetChartSeriesStyleEditor, normalizeOptionalOfficeNumber, normalizeRequiredOfficeNumber, SpreadsheetErrorBarEditor, useOfficeHistory, spreadsheetAgentSelection, SpreadsheetTrendlineEditor, applySpreadsheetAgentProposalChanges } from "./0~3539.js";
18
- import { StateView } from "./432.js";
19
- import { createOfficeId as createWorkId } from "./1134.js";
18
+ import { useOfficeEditorFocusOrigin, useOfficeCollaborationLocationNavigator, StateView } from "./432.js";
19
+ import { OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, createOfficeId as createWorkId, isOfficeKernelSpreadsheetError } from "./5184.js";
20
20
  var spreadsheet_editor_namespaceObject = {};
21
21
  __webpack_require__.r(spreadsheet_editor_namespaceObject);
22
22
  __webpack_require__.d(spreadsheet_editor_namespaceObject, {
@@ -483,249 +483,6 @@ function decodeColumn(value) {
483
483
  for (const character of value.toUpperCase())result = 26 * result + character.charCodeAt(0) - 64;
484
484
  return result - 1;
485
485
  }
486
- const spreadsheetSystemClipboardTimeoutMs = 800;
487
- function spreadsheetCoreContextMenuItems({ can, clipboard = browserSpreadsheetClipboard, commands, selection }) {
488
- const copy = (cut)=>{
489
- copySpreadsheetSelection(clipboard, selection.clipboard, cut).then((copied)=>{
490
- if (copied && cut && !commands.clearSelectedCells()) showToast('选区已复制,但无法清除原内容。', 'error');
491
- });
492
- };
493
- return [
494
- {
495
- id: 'cut-cells',
496
- label: '剪切',
497
- icon: /*#__PURE__*/ jsx(Scissors, {
498
- size: 14
499
- }),
500
- shortcut: '⌘X',
501
- ariaKeyShortcut: 'Control+X Meta+X',
502
- disabled: !can.clearSelectedCells(),
503
- onSelect: ()=>copy(true)
504
- },
505
- {
506
- id: 'copy-cells',
507
- label: '复制',
508
- icon: /*#__PURE__*/ jsx(Copy, {
509
- size: 14
510
- }),
511
- shortcut: '⌘C',
512
- ariaKeyShortcut: 'Control+C Meta+C',
513
- onSelect: ()=>copy(false)
514
- },
515
- {
516
- id: 'paste-cells',
517
- label: '粘贴',
518
- icon: /*#__PURE__*/ jsx(ClipboardPaste, {
519
- size: 14
520
- }),
521
- shortcut: '⌘V',
522
- ariaKeyShortcut: 'Control+V Meta+V',
523
- disabled: !can.pasteCells([
524
- [
525
- ''
526
- ]
527
- ]),
528
- onSelect: ()=>void pasteSpreadsheetSelection(clipboard, commands.pasteCells)
529
- },
530
- {
531
- id: 'clear-cells',
532
- label: '清除内容',
533
- icon: /*#__PURE__*/ jsx(Eraser, {
534
- size: 14
535
- }),
536
- shortcut: 'Delete',
537
- ariaKeyShortcut: 'Delete',
538
- separatorBefore: true,
539
- disabled: !can.clearSelectedCells(),
540
- onSelect: ()=>{
541
- if (!commands.clearSelectedCells()) showToast(`无法清除选区 ${selection.reference}。`, 'error');
542
- }
543
- }
544
- ];
545
- }
546
- function spreadsheetStructureContextMenuItems({ axis, can, commands, onResize }) {
547
- const row = 'row' === axis;
548
- const subject = row ? '行' : '列';
549
- const before = row ? '上方' : '左侧';
550
- const after = row ? '下方' : '右侧';
551
- const defaultSize = row ? 24 : 96;
552
- return [
553
- ...spreadsheetSortContextMenuItems({
554
- can,
555
- commands,
556
- idSuffix: axis
557
- }),
558
- {
559
- id: `insert-${axis}-before`,
560
- label: `在${before}插入${subject}`,
561
- icon: /*#__PURE__*/ jsx(Plus, {
562
- size: 14
563
- }),
564
- separatorBefore: true,
565
- disabled: !can.insertSelectedStructure(axis, 'before'),
566
- onSelect: ()=>commands.insertSelectedStructure(axis, 'before')
567
- },
568
- {
569
- id: `insert-${axis}-after`,
570
- label: `在${after}插入${subject}`,
571
- icon: /*#__PURE__*/ jsx(Plus, {
572
- size: 14
573
- }),
574
- disabled: !can.insertSelectedStructure(axis, 'after'),
575
- onSelect: ()=>commands.insertSelectedStructure(axis, 'after')
576
- },
577
- {
578
- id: `delete-${axis}`,
579
- label: `删除所选${subject}`,
580
- icon: /*#__PURE__*/ jsx(Trash2, {
581
- size: 14
582
- }),
583
- danger: true,
584
- disabled: !can.deleteSelectedStructure(axis),
585
- onSelect: ()=>commands.deleteSelectedStructure(axis)
586
- },
587
- {
588
- id: `resize-${axis}`,
589
- label: row ? '行高…' : '列宽…',
590
- icon: row ? /*#__PURE__*/ jsx(MoveVertical, {
591
- size: 14
592
- }) : /*#__PURE__*/ jsx(MoveHorizontal, {
593
- size: 14
594
- }),
595
- disabled: !can.setSelectedStructureSize(axis, defaultSize),
596
- onSelect: ()=>onResize(axis)
597
- },
598
- {
599
- id: `hide-${axis}`,
600
- label: `隐藏所选${subject}`,
601
- icon: /*#__PURE__*/ jsx(EyeOff, {
602
- size: 14
603
- }),
604
- separatorBefore: true,
605
- disabled: !can.setSelectedStructureHidden(axis, true),
606
- onSelect: ()=>commands.setSelectedStructureHidden(axis, true)
607
- },
608
- {
609
- id: `show-${axis}`,
610
- label: `取消隐藏${subject}`,
611
- icon: /*#__PURE__*/ jsx(Eye, {
612
- size: 14
613
- }),
614
- disabled: !can.setSelectedStructureHidden(axis, false),
615
- onSelect: ()=>commands.setSelectedStructureHidden(axis, false)
616
- }
617
- ];
618
- }
619
- function spreadsheetSortContextMenuItems({ can, commands, idSuffix = 'selection', separatorBefore = false }) {
620
- return [
621
- {
622
- id: `sort-${idSuffix}-ascending`,
623
- label: '升序排列',
624
- icon: /*#__PURE__*/ jsx(ArrowUp, {
625
- size: 14
626
- }),
627
- separatorBefore,
628
- disabled: !can.sortSelectedCells('ascending'),
629
- onSelect: ()=>commands.sortSelectedCells('ascending')
630
- },
631
- {
632
- id: `sort-${idSuffix}-descending`,
633
- label: '降序排列',
634
- icon: /*#__PURE__*/ jsx(ArrowDown, {
635
- size: 14
636
- }),
637
- disabled: !can.sortSelectedCells('descending'),
638
- onSelect: ()=>commands.sortSelectedCells('descending')
639
- }
640
- ];
641
- }
642
- function parseSpreadsheetClipboardText(value) {
643
- if (!value) return [];
644
- const normalized = value.replace(/\r\n?/g, '\n').replace(/\n$/, '');
645
- if (!normalized) return [];
646
- const rows = normalized.split('\n').map((row)=>row.split('\t'));
647
- const width = Math.max(...rows.map((row)=>row.length));
648
- return rows.map((row)=>[
649
- ...row,
650
- ...Array.from({
651
- length: width - row.length
652
- }, ()=>'')
653
- ]);
654
- }
655
- const browserSpreadsheetClipboard = {
656
- readText: ()=>readSpreadsheetClipboardText("u" > typeof navigator && navigator.clipboard?.readText ? ()=>navigator.clipboard.readText() : void 0, readLocalClipboard, spreadsheetSystemClipboardTimeoutMs, canReadBrowserSystemClipboard),
657
- writeText: (value)=>writeSpreadsheetClipboardText(value, "u" > typeof navigator && navigator.clipboard?.writeText ? (next)=>navigator.clipboard.writeText(next) : void 0, writeLocalClipboard)
658
- };
659
- async function readSpreadsheetClipboardText(readSystem, readLocal, timeoutMs = spreadsheetSystemClipboardTimeoutMs, canReadSystem = ()=>true) {
660
- if (readSystem && await canReadSystem()) try {
661
- const value = await withSpreadsheetClipboardTimeout(readSystem(), timeoutMs);
662
- if (value) return value;
663
- } catch {}
664
- return readLocal();
665
- }
666
- async function canReadBrowserSystemClipboard() {
667
- if ("u" < typeof navigator || !navigator.permissions?.query) return true;
668
- try {
669
- const permission = await navigator.permissions.query({
670
- name: 'clipboard-read'
671
- });
672
- return 'granted' === permission.state;
673
- } catch {
674
- return true;
675
- }
676
- }
677
- async function writeSpreadsheetClipboardText(value, writeSystem, writeLocal, timeoutMs = spreadsheetSystemClipboardTimeoutMs) {
678
- writeLocal(value);
679
- if (!writeSystem) return;
680
- try {
681
- await withSpreadsheetClipboardTimeout(writeSystem(value), timeoutMs);
682
- } catch {}
683
- }
684
- async function copySpreadsheetSelection(clipboard, value, cut) {
685
- try {
686
- await clipboard.writeText(value);
687
- showToast(cut ? '选区已剪切' : '选区已复制', 'success');
688
- return true;
689
- } catch {
690
- showToast('无法访问剪贴板,请使用系统快捷键。', 'error');
691
- return false;
692
- }
693
- }
694
- async function pasteSpreadsheetSelection(clipboard, paste) {
695
- try {
696
- const values = parseSpreadsheetClipboardText(await clipboard.readText());
697
- if (!values.length) return void showToast('剪贴板中没有可粘贴的表格内容。', 'error');
698
- if (!paste(values)) showToast('当前选区无法粘贴这些内容。', 'error');
699
- } catch {
700
- showToast('无法读取剪贴板,请使用系统粘贴快捷键。', 'error');
701
- }
702
- }
703
- function readLocalClipboard() {
704
- try {
705
- return sessionStorage.getItem('a3s-office-spreadsheet-clipboard') ?? '';
706
- } catch {
707
- return '';
708
- }
709
- }
710
- function writeLocalClipboard(value) {
711
- try {
712
- sessionStorage.setItem('a3s-office-spreadsheet-clipboard', value);
713
- } catch {}
714
- }
715
- async function withSpreadsheetClipboardTimeout(request, timeoutMs) {
716
- let timeout;
717
- const expired = new Promise((_resolve, reject)=>{
718
- timeout = setTimeout(()=>reject(new Error('Spreadsheet clipboard request timed out.')), Math.max(0, timeoutMs));
719
- });
720
- try {
721
- return await Promise.race([
722
- request,
723
- expired
724
- ]);
725
- } finally{
726
- if (timeout) clearTimeout(timeout);
727
- }
728
- }
729
486
  const spreadsheetFontSizes = [
730
487
  9,
731
488
  10,
@@ -1021,38 +778,561 @@ function spreadsheetFormulaHistoryCellType(cellType) {
1021
778
  };
1022
779
  return Object.keys(normalized).length ? normalized : void 0;
1023
780
  }
1024
- function runSpreadsheetClipboardShortcut(event, options) {
1025
- const key = event.key.toLocaleLowerCase();
1026
- if (event.defaultPrevented || event.repeat || event.altKey || event.shiftKey || !(event.metaKey || event.ctrlKey) || !clipboardShortcutKeys.has(key) || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target)) return false;
1027
- const selectionText = 'v' === key ? null : options.readSelectionText();
1028
- if ('v' !== key && null === selectionText) return false;
781
+ function runSpreadsheetClipboardShortcut(event, canExecute, execute) {
782
+ if (event.defaultPrevented || event.repeat || event.altKey || event.shiftKey || !(event.metaKey || event.ctrlKey) || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute() || !execute()) return false;
1029
783
  event.preventDefault();
1030
784
  event.stopPropagation();
1031
- if ('v' === key) {
1032
- options.clipboard.readText().then((value)=>{
1033
- const cells = parseSpreadsheetClipboardText(value);
1034
- if (cells.length) options.pasteCells(cells);
1035
- }).catch(()=>void 0).finally(options.restoreFocus);
1036
- return true;
1037
- }
1038
- options.clipboard.writeText(selectionText ?? '').catch(()=>void 0);
1039
- if ('x' === key) options.clearSelectedCells();
1040
- options.restoreFocus();
1041
785
  return true;
1042
786
  }
1043
- const clipboardShortcutKeys = new Set([
1044
- 'c',
1045
- 'v',
1046
- 'x'
1047
- ]);
1048
- const spreadsheetKeyboardPageRows = 20;
1049
- const spreadsheetDefaultRows = 60;
1050
- const spreadsheetDefaultColumns = 26;
1051
- function moveSpreadsheetKeyboardSelection(sheet, selection, move, extend) {
1052
- const bounds = spreadsheetSheetBounds(sheet);
1053
- const current = normalizeSpreadsheetKeyboardSelection(selection, bounds);
1054
- let targetRow = current.rowFocus;
1055
- let targetColumn = current.columnFocus;
787
+ const spreadsheetCellFormatProperties = [
788
+ 'bg',
789
+ 'bl',
790
+ 'it',
791
+ 'ff',
792
+ 'fs',
793
+ 'fc',
794
+ 'ht',
795
+ 'vt',
796
+ 'tb',
797
+ 'cl',
798
+ 'un',
799
+ 'tr',
800
+ 'rt',
801
+ 'ct',
802
+ 'lo',
803
+ 'hi'
804
+ ];
805
+ function clearSpreadsheetSheetSelection(sheet, selection, mode) {
806
+ const range = normalizeClearRange(selection);
807
+ const data = sheet.data?.map((row, rowIndex)=>{
808
+ if (rowIndex < range.row[0] || rowIndex > range.row[1]) return row;
809
+ return row.map((cell, columnIndex)=>clearSpreadsheetCellAt(cell, rowIndex, columnIndex, range, mode));
810
+ });
811
+ const celldata = data ? void 0 : sheet.celldata?.flatMap((entry)=>{
812
+ const cell = clearSpreadsheetCellAt(entry.v, entry.r, entry.c, range, mode);
813
+ return cell ? [
814
+ {
815
+ ...entry,
816
+ v: cell
817
+ }
818
+ ] : [];
819
+ });
820
+ let next = {
821
+ ...sheet,
822
+ ...data ? {
823
+ data
824
+ } : {},
825
+ ...celldata ? {
826
+ celldata
827
+ } : {}
828
+ };
829
+ if (data && next.celldata) delete next.celldata;
830
+ if ('formats' === mode || 'all' === mode) next = clearSpreadsheetSheetFormats(next, range);
831
+ if ('hyperlinks' === mode || 'all' === mode) next = clearSpreadsheetSheetHyperlinks(next, range);
832
+ return next;
833
+ }
834
+ function clearSpreadsheetCellAt(cell, row, column, range, mode) {
835
+ if (!cell || !clearRangeContains(range, row, column)) return cell;
836
+ if ('all' === mode) return cell.mc ? {
837
+ mc: {
838
+ ...cell.mc
839
+ }
840
+ } : null;
841
+ const next = {
842
+ ...cell
843
+ };
844
+ if ('contents' === mode) clearSpreadsheetCellContents(next);
845
+ if ('formats' === mode) clearSpreadsheetCellFormats(next);
846
+ if ('comments' === mode) delete next.ps;
847
+ if ('hyperlinks' === mode) delete next.hl;
848
+ removeUndefinedCellProperties(next);
849
+ return Object.keys(next).length ? next : null;
850
+ }
851
+ function clearSpreadsheetCellContents(cell) {
852
+ delete cell.v;
853
+ delete cell.m;
854
+ delete cell.f;
855
+ delete cell.spl;
856
+ delete cell.qp;
857
+ if (cell.ct && 's' in cell.ct) {
858
+ const cellType = {
859
+ ...cell.ct
860
+ };
861
+ delete cellType.s;
862
+ cell.ct = Object.keys(cellType).length ? cellType : void 0;
863
+ }
864
+ }
865
+ function clearSpreadsheetCellFormats(cell) {
866
+ const inlineText = spreadsheetInlineCellText(cell);
867
+ for (const property of spreadsheetCellFormatProperties)delete cell[property];
868
+ if (void 0 !== cell.v) cell.m = spreadsheetGeneralDisplayValue(cell.v);
869
+ else if (!cell.f && null !== inlineText) {
870
+ cell.v = inlineText;
871
+ cell.m = inlineText;
872
+ }
873
+ }
874
+ function spreadsheetInlineCellText(cell) {
875
+ const segments = cell.ct?.s;
876
+ if (!Array.isArray(segments)) return null;
877
+ return segments.map((segment)=>{
878
+ if (!segment || 'object' != typeof segment) return '';
879
+ const value = segment.v;
880
+ return null == value ? '' : String(value);
881
+ }).join('');
882
+ }
883
+ function spreadsheetGeneralDisplayValue(value) {
884
+ if ('boolean' == typeof value) return value ? 'TRUE' : 'FALSE';
885
+ return void 0 === value ? '' : String(value);
886
+ }
887
+ function removeUndefinedCellProperties(cell) {
888
+ const record = cell;
889
+ for (const key of Object.keys(record))if (void 0 === record[key]) delete record[key];
890
+ }
891
+ function clearSpreadsheetSheetFormats(sheet, range) {
892
+ const config = sheet.config ? {
893
+ ...sheet.config
894
+ } : void 0;
895
+ if (config?.borderInfo) {
896
+ const borderInfo = clearSpreadsheetBorderFormats(config.borderInfo, range);
897
+ if (borderInfo.length) config.borderInfo = borderInfo;
898
+ else delete config.borderInfo;
899
+ }
900
+ const conditionalFormats = clearSpreadsheetConditionalFormats(sheet.luckysheet_conditionformat_save, range);
901
+ const alternateFormats = clearSpreadsheetAlternateFormats(sheet.luckysheet_alternateformat_save, range);
902
+ const next = {
903
+ ...sheet,
904
+ ...config ? {
905
+ config
906
+ } : {},
907
+ ...conditionalFormats.length ? {
908
+ luckysheet_conditionformat_save: conditionalFormats
909
+ } : {},
910
+ ...alternateFormats.length ? {
911
+ luckysheet_alternateformat_save: alternateFormats
912
+ } : {}
913
+ };
914
+ if (!conditionalFormats.length) delete next.luckysheet_conditionformat_save;
915
+ if (!alternateFormats.length) delete next.luckysheet_alternateformat_save;
916
+ return next;
917
+ }
918
+ function clearSpreadsheetBorderFormats(formats, range) {
919
+ return formats.flatMap((format)=>{
920
+ if (!isRecord(format)) return [
921
+ format
922
+ ];
923
+ if ('cell' === format.rangeType) {
924
+ const value = isRecord(format.value) ? format.value : null;
925
+ const row = finiteIndex(value?.row_index);
926
+ const column = finiteIndex(value?.col_index);
927
+ return null !== row && null !== column && clearRangeContains(range, row, column) ? [] : [
928
+ format
929
+ ];
930
+ }
931
+ if ('range' !== format.rangeType || !Array.isArray(format.range)) return [
932
+ format
933
+ ];
934
+ const remaining = format.range.flatMap((candidate)=>{
935
+ const parsed = parseClearRange(candidate);
936
+ if (!parsed) return [
937
+ candidate
938
+ ];
939
+ if ('border-slash' === format.borderType) return clearRangesIntersect(parsed, range) ? [] : [
940
+ parsed
941
+ ];
942
+ return subtractSpreadsheetRange(parsed, range);
943
+ });
944
+ return remaining.length ? [
945
+ {
946
+ ...format,
947
+ range: remaining
948
+ }
949
+ ] : [];
950
+ });
951
+ }
952
+ function clearSpreadsheetConditionalFormats(formats, range) {
953
+ if (!Array.isArray(formats)) return [];
954
+ return formats.flatMap((format)=>{
955
+ if (!isRecord(format) || !Array.isArray(format.cellrange)) return [
956
+ format
957
+ ];
958
+ const remaining = format.cellrange.flatMap((candidate)=>{
959
+ const parsed = parseClearRange(candidate);
960
+ return parsed ? subtractSpreadsheetRange(parsed, range) : [
961
+ candidate
962
+ ];
963
+ });
964
+ return remaining.length ? [
965
+ {
966
+ ...format,
967
+ cellrange: remaining
968
+ }
969
+ ] : [];
970
+ });
971
+ }
972
+ function clearSpreadsheetAlternateFormats(formats, range) {
973
+ if (!Array.isArray(formats)) return [];
974
+ return formats.flatMap((format)=>{
975
+ if (!isRecord(format)) return [
976
+ format
977
+ ];
978
+ const source = parseClearRange(format.cellrange);
979
+ if (!source) return [
980
+ format
981
+ ];
982
+ return subtractSpreadsheetRange(source, range).map((cellrange)=>({
983
+ ...format,
984
+ cellrange
985
+ }));
986
+ });
987
+ }
988
+ function clearSpreadsheetSheetHyperlinks(sheet, range) {
989
+ if (!sheet.hyperlink) return sheet;
990
+ const hyperlink = Object.fromEntries(Object.entries(sheet.hyperlink).filter(([key])=>{
991
+ const position = spreadsheetHyperlinkPosition(key);
992
+ return !position || !clearRangeContains(range, position.row, position.column);
993
+ }));
994
+ const next = {
995
+ ...sheet,
996
+ hyperlink
997
+ };
998
+ if (!Object.keys(hyperlink).length) delete next.hyperlink;
999
+ return next;
1000
+ }
1001
+ function spreadsheetHyperlinkPosition(value) {
1002
+ const match = /^(\d+)_(\d+)$/.exec(value);
1003
+ if (!match) return null;
1004
+ return {
1005
+ row: Number(match[1]),
1006
+ column: Number(match[2])
1007
+ };
1008
+ }
1009
+ function subtractSpreadsheetRange(source, removed) {
1010
+ const top = Math.max(source.row[0], removed.row[0]);
1011
+ const bottom = Math.min(source.row[1], removed.row[1]);
1012
+ const left = Math.max(source.column[0], removed.column[0]);
1013
+ const right = Math.min(source.column[1], removed.column[1]);
1014
+ if (top > bottom || left > right) return [
1015
+ source
1016
+ ];
1017
+ const remaining = [];
1018
+ if (source.row[0] < top) remaining.push({
1019
+ row: [
1020
+ source.row[0],
1021
+ top - 1
1022
+ ],
1023
+ column: [
1024
+ ...source.column
1025
+ ]
1026
+ });
1027
+ if (bottom < source.row[1]) remaining.push({
1028
+ row: [
1029
+ bottom + 1,
1030
+ source.row[1]
1031
+ ],
1032
+ column: [
1033
+ ...source.column
1034
+ ]
1035
+ });
1036
+ if (source.column[0] < left) remaining.push({
1037
+ row: [
1038
+ top,
1039
+ bottom
1040
+ ],
1041
+ column: [
1042
+ source.column[0],
1043
+ left - 1
1044
+ ]
1045
+ });
1046
+ if (right < source.column[1]) remaining.push({
1047
+ row: [
1048
+ top,
1049
+ bottom
1050
+ ],
1051
+ column: [
1052
+ right + 1,
1053
+ source.column[1]
1054
+ ]
1055
+ });
1056
+ return remaining;
1057
+ }
1058
+ function clearRangesIntersect(left, right) {
1059
+ return !(left.row[1] < right.row[0] || right.row[1] < left.row[0] || left.column[1] < right.column[0] || right.column[1] < left.column[0]);
1060
+ }
1061
+ function clearRangeContains(range, row, column) {
1062
+ return row >= range.row[0] && row <= range.row[1] && column >= range.column[0] && column <= range.column[1];
1063
+ }
1064
+ function normalizeClearRange(range) {
1065
+ return {
1066
+ row: normalizeClearAxis(range.row),
1067
+ column: normalizeClearAxis(range.column)
1068
+ };
1069
+ }
1070
+ function parseClearRange(value) {
1071
+ if (!isRecord(value)) return null;
1072
+ const row = parseClearAxis(value.row);
1073
+ const column = parseClearAxis(value.column);
1074
+ return row && column ? {
1075
+ row,
1076
+ column
1077
+ } : null;
1078
+ }
1079
+ function normalizeClearAxis(value) {
1080
+ const first = finiteIndex(value[0]) ?? 0;
1081
+ const second = finiteIndex(value[1]) ?? first;
1082
+ return [
1083
+ Math.min(first, second),
1084
+ Math.max(first, second)
1085
+ ];
1086
+ }
1087
+ function parseClearAxis(value) {
1088
+ if (!Array.isArray(value)) return null;
1089
+ const first = finiteIndex(value[0]);
1090
+ const second = finiteIndex(value[1]);
1091
+ if (null === first || null === second) return null;
1092
+ return [
1093
+ Math.min(first, second),
1094
+ Math.max(first, second)
1095
+ ];
1096
+ }
1097
+ function finiteIndex(value) {
1098
+ return 'number' == typeof value && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
1099
+ }
1100
+ function isRecord(value) {
1101
+ return Boolean(value && 'object' == typeof value && !Array.isArray(value));
1102
+ }
1103
+ function canApplySpreadsheetCellMerge(sheet, range, command) {
1104
+ if (!sheet) return false;
1105
+ const normalized = normalizeSpreadsheetCellMergeRange(range);
1106
+ if (!normalized) return false;
1107
+ const merged = spreadsheetMergesIntersectingRange(sheet, normalized);
1108
+ if ('unmerge-cells' === command || 'unmerge-and-fill' === command) return merged.length > 0;
1109
+ if (merged.length > 0) return false;
1110
+ const rowCount = normalized.row[1] - normalized.row[0] + 1;
1111
+ const columnCount = normalized.column[1] - normalized.column[0] + 1;
1112
+ if ('merge-across' === command) return columnCount > 1;
1113
+ return rowCount * columnCount > 1;
1114
+ }
1115
+ function spreadsheetCellMergeApiCalls(sheet, sheetId, range, command) {
1116
+ if (!canApplySpreadsheetCellMerge(sheet, range, command) || !sheet) return [];
1117
+ const normalized = normalizeSpreadsheetCellMergeRange(range);
1118
+ if (!normalized) return [];
1119
+ const options = {
1120
+ id: sheetId
1121
+ };
1122
+ if ('merge-and-center' === command) return [
1123
+ {
1124
+ name: 'mergeCells',
1125
+ args: [
1126
+ [
1127
+ normalized
1128
+ ],
1129
+ 'merge-all',
1130
+ options
1131
+ ]
1132
+ },
1133
+ {
1134
+ name: 'setCellFormatByRange',
1135
+ args: [
1136
+ 'ht',
1137
+ '0',
1138
+ normalized,
1139
+ options
1140
+ ]
1141
+ }
1142
+ ];
1143
+ if ('merge-cells' === command || 'merge-across' === command) return [
1144
+ {
1145
+ name: 'mergeCells',
1146
+ args: [
1147
+ [
1148
+ normalized
1149
+ ],
1150
+ 'merge-across' === command ? 'merge-horizontal' : 'merge-all',
1151
+ options
1152
+ ]
1153
+ }
1154
+ ];
1155
+ const merges = spreadsheetMergesIntersectingRange(sheet, normalized);
1156
+ const mergeRanges = merges.map(spreadsheetNativeMergeRange);
1157
+ const calls = [
1158
+ {
1159
+ name: 'cancelMerge',
1160
+ args: [
1161
+ mergeRanges,
1162
+ options
1163
+ ]
1164
+ }
1165
+ ];
1166
+ if ('unmerge-cells' === command) return calls;
1167
+ for (const merge of merges){
1168
+ const anchor = sheet.data?.[merge.r]?.[merge.c];
1169
+ if (!anchor) continue;
1170
+ const targetRange = spreadsheetNativeMergeRange(merge);
1171
+ calls.push({
1172
+ name: 'setCellValuesByRange',
1173
+ args: [
1174
+ Array.from({
1175
+ length: merge.rs
1176
+ }, ()=>Array.from({
1177
+ length: merge.cs
1178
+ }, ()=>cloneSpreadsheetCellWithoutMerge(anchor))),
1179
+ targetRange,
1180
+ null,
1181
+ options
1182
+ ]
1183
+ });
1184
+ }
1185
+ return calls;
1186
+ }
1187
+ function normalizeSpreadsheetCellMergeRange(range) {
1188
+ const row = spreadsheetCellMergeBoundary(range.row);
1189
+ const column = spreadsheetCellMergeBoundary(range.column);
1190
+ return row && column ? {
1191
+ row,
1192
+ column
1193
+ } : null;
1194
+ }
1195
+ function spreadsheetCellMergeBoundary(values) {
1196
+ if (!values.length || values.some((value)=>!Number.isInteger(value) || value < 0)) return null;
1197
+ return [
1198
+ Math.min(...values),
1199
+ Math.max(...values)
1200
+ ];
1201
+ }
1202
+ function spreadsheetMergesIntersectingRange(sheet, range) {
1203
+ return Object.values(sheet.config?.merge ?? {}).filter(isSpreadsheetNativeMerge).filter((merge)=>merge.r <= range.row[1] && merge.r + merge.rs - 1 >= range.row[0] && merge.c <= range.column[1] && merge.c + merge.cs - 1 >= range.column[0]).sort((left, right)=>left.r - right.r || left.c - right.c);
1204
+ }
1205
+ function isSpreadsheetNativeMerge(value) {
1206
+ if (!value || 'object' != typeof value) return false;
1207
+ const merge = value;
1208
+ return Number.isInteger(merge.r) && Number.isInteger(merge.c) && Number.isInteger(merge.rs) && Number.isInteger(merge.cs) && Number(merge.r) >= 0 && Number(merge.c) >= 0 && Number(merge.rs) > 0 && Number(merge.cs) > 0;
1209
+ }
1210
+ function spreadsheetNativeMergeRange(merge) {
1211
+ return {
1212
+ row: [
1213
+ merge.r,
1214
+ merge.r + merge.rs - 1
1215
+ ],
1216
+ column: [
1217
+ merge.c,
1218
+ merge.c + merge.cs - 1
1219
+ ]
1220
+ };
1221
+ }
1222
+ function cloneSpreadsheetCellWithoutMerge(cell) {
1223
+ const clone = structuredClone(cell);
1224
+ delete clone.mc;
1225
+ return clone;
1226
+ }
1227
+ function spreadsheetFreezePaneForSelection(selection) {
1228
+ const finite = finiteSpreadsheetSelection(selection);
1229
+ return spreadsheetFrozenPaneFromCounts(finite.row_focus ?? finite.row[0] ?? 0, finite.column_focus ?? finite.column[0] ?? 0);
1230
+ }
1231
+ function spreadsheetFreezePanesSelectionLabel(selection) {
1232
+ const boundary = spreadsheetFreezePaneForSelection(selection);
1233
+ if (!boundary) return '冻结至当前单元格';
1234
+ const { rows, columns } = spreadsheetFreezePaneBoundary(boundary);
1235
+ if (rows && columns) return `冻结至第 ${rows} 行、${indexToColumnChar(columns - 1)} 列`;
1236
+ if (rows) return `冻结至第 ${rows} 行`;
1237
+ return `冻结至 ${indexToColumnChar(columns - 1)} 列`;
1238
+ }
1239
+ function spreadsheetFreezePaneBoundary(frozen) {
1240
+ if (!frozen) return {
1241
+ rows: 0,
1242
+ columns: 0
1243
+ };
1244
+ const freezesRows = [
1245
+ 'row',
1246
+ 'both',
1247
+ 'rangeRow',
1248
+ 'rangeBoth'
1249
+ ].includes(frozen.type);
1250
+ const freezesColumns = [
1251
+ 'column',
1252
+ 'both',
1253
+ 'rangeColumn',
1254
+ 'rangeBoth'
1255
+ ].includes(frozen.type);
1256
+ return {
1257
+ rows: freezesRows ? Math.max(1, finiteFreezeIndex(frozen.range?.row_focus) + 1) : 0,
1258
+ columns: freezesColumns ? Math.max(1, finiteFreezeIndex(frozen.range?.column_focus) + 1) : 0
1259
+ };
1260
+ }
1261
+ function spreadsheetFreezePanesStatus(frozen) {
1262
+ const { rows, columns } = spreadsheetFreezePaneBoundary(frozen);
1263
+ if (rows && columns) return `已冻结前 ${rows} 行和前 ${columns} 列。`;
1264
+ if (rows) return `已冻结前 ${rows} 行。`;
1265
+ if (columns) return `已冻结前 ${columns} 列。`;
1266
+ return '';
1267
+ }
1268
+ function updateSpreadsheetFreezePanes(content, sheetId, preset, selection) {
1269
+ const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
1270
+ const sheet = content.sheets[sheetIndex];
1271
+ if (!sheet) return null;
1272
+ const nextFrozen = spreadsheetFrozenPaneForPreset(preset, selection);
1273
+ if ('none' !== preset && !nextFrozen) return null;
1274
+ if (!nextFrozen && !sheet.frozen) return null;
1275
+ if (nextFrozen && sameSpreadsheetFreezePane(sheet.frozen, nextFrozen)) return null;
1276
+ const savedSelection = finiteSpreadsheetSelection(selection);
1277
+ const nextSheet = nextFrozen ? {
1278
+ ...sheet,
1279
+ frozen: nextFrozen,
1280
+ luckysheet_select_save: [
1281
+ savedSelection
1282
+ ]
1283
+ } : spreadsheetSheetWithoutFreezePanes(sheet, savedSelection);
1284
+ const sheets = [
1285
+ ...content.sheets
1286
+ ];
1287
+ sheets[sheetIndex] = nextSheet;
1288
+ return {
1289
+ ...content,
1290
+ sheets
1291
+ };
1292
+ }
1293
+ function spreadsheetFrozenPaneForPreset(preset, selection) {
1294
+ if ('selection' === preset) return spreadsheetFreezePaneForSelection(selection);
1295
+ if ('topRow' === preset) return spreadsheetFrozenPaneFromCounts(1, 0);
1296
+ if ('firstColumn' === preset) return spreadsheetFrozenPaneFromCounts(0, 1);
1297
+ return null;
1298
+ }
1299
+ function spreadsheetFrozenPaneFromCounts(rows, columns) {
1300
+ const safeRows = finiteFreezeIndex(rows);
1301
+ const safeColumns = finiteFreezeIndex(columns);
1302
+ if (!safeRows && !safeColumns) return null;
1303
+ return {
1304
+ type: safeRows && safeColumns ? 'rangeBoth' : safeRows ? 'rangeRow' : 'rangeColumn',
1305
+ range: {
1306
+ row_focus: Math.max(0, safeRows - 1),
1307
+ column_focus: Math.max(0, safeColumns - 1)
1308
+ }
1309
+ };
1310
+ }
1311
+ function spreadsheetSheetWithoutFreezePanes(sheet, selection) {
1312
+ const { frozen: _frozen, ...remaining } = sheet;
1313
+ return {
1314
+ ...remaining,
1315
+ luckysheet_select_save: [
1316
+ selection
1317
+ ]
1318
+ };
1319
+ }
1320
+ function sameSpreadsheetFreezePane(current, next) {
1321
+ const currentBoundary = spreadsheetFreezePaneBoundary(current);
1322
+ const nextBoundary = spreadsheetFreezePaneBoundary(next);
1323
+ return currentBoundary.rows === nextBoundary.rows && currentBoundary.columns === nextBoundary.columns;
1324
+ }
1325
+ function finiteFreezeIndex(value) {
1326
+ return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
1327
+ }
1328
+ const spreadsheetKeyboardPageRows = 20;
1329
+ const spreadsheetDefaultRows = 60;
1330
+ const spreadsheetDefaultColumns = 26;
1331
+ function moveSpreadsheetKeyboardSelection(sheet, selection, move, extend) {
1332
+ const bounds = spreadsheetSheetBounds(sheet);
1333
+ const current = normalizeSpreadsheetKeyboardSelection(selection, bounds);
1334
+ let targetRow = current.rowFocus;
1335
+ let targetColumn = current.columnFocus;
1056
1336
  switch(move){
1057
1337
  case 'up':
1058
1338
  targetRow -= 1;
@@ -1468,6 +1748,53 @@ function createSpreadsheetEditorExtensions() {
1468
1748
  }
1469
1749
  })
1470
1750
  }),
1751
+ createOfficeEditorExtension({
1752
+ name: 'spreadsheetFormatPainter',
1753
+ addCommands: ()=>({
1754
+ activateFormatPainter: {
1755
+ canExecute: ({ formatPainter })=>formatPainter.canActivate,
1756
+ execute: ({ formatPainter }, mode)=>formatPainter.canActivate && formatPainter.activate(mode)
1757
+ },
1758
+ applyFormatPainter: {
1759
+ canExecute: ({ formatPainter })=>formatPainter.active,
1760
+ execute: ({ formatPainter }, target)=>formatPainter.active && formatPainter.applySelection(target)
1761
+ },
1762
+ cancelFormatPainter: {
1763
+ canExecute: ({ formatPainter })=>formatPainter.active,
1764
+ execute: ({ formatPainter })=>formatPainter.active && formatPainter.cancel()
1765
+ }
1766
+ })
1767
+ }),
1768
+ createOfficeEditorExtension({
1769
+ name: 'spreadsheetAutoFilter',
1770
+ addCommands: ()=>({
1771
+ openAutoFilterMenu: {
1772
+ canExecute: ({ autoFilter })=>autoFilter.canOpenMenu,
1773
+ execute: ({ autoFilter })=>autoFilter.canOpenMenu && autoFilter.openMenu()
1774
+ },
1775
+ toggleAutoFilter: {
1776
+ canExecute: ({ autoFilter })=>autoFilter.canToggle,
1777
+ execute: ({ autoFilter })=>autoFilter.canToggle && autoFilter.toggle()
1778
+ }
1779
+ })
1780
+ }),
1781
+ createOfficeEditorExtension({
1782
+ name: 'spreadsheetClipboard',
1783
+ addCommands: ()=>({
1784
+ copySelection: {
1785
+ canExecute: ({ clipboard })=>clipboard.canCopySelection,
1786
+ execute: ({ clipboard })=>clipboard.canCopySelection && clipboard.copySelection()
1787
+ },
1788
+ cutSelection: {
1789
+ canExecute: ({ clipboard })=>clipboard.canCutSelection,
1790
+ execute: ({ clipboard })=>clipboard.canCutSelection && clipboard.cutSelection()
1791
+ },
1792
+ pasteSelection: {
1793
+ canExecute: ({ clipboard })=>clipboard.canPasteSelection,
1794
+ execute: ({ clipboard })=>clipboard.canPasteSelection && clipboard.pasteSelection()
1795
+ }
1796
+ })
1797
+ }),
1471
1798
  createOfficeEditorExtension({
1472
1799
  name: 'spreadsheetHistory',
1473
1800
  addCommands: ()=>({
@@ -1533,17 +1860,25 @@ function createSpreadsheetEditorExtensions() {
1533
1860
  addKeyboardShortcuts: ()=>({
1534
1861
  'Mod-a': ({ can, commands }, event)=>selectSpreadsheetFormulaBarContents(event) || runSpreadsheetSelectionScopeShortcut(event, can.selectCellRange, commands.selectCellRange, 'all'),
1535
1862
  'Mod-b': ({ can, commands, context }, event)=>runSpreadsheetCellFormatShortcut(event, context, can.setCellFormat, commands.setCellFormat, 'bl'),
1863
+ 'Mod-c': ({ can, commands }, event)=>runSpreadsheetClipboardShortcut(event, can.copySelection, commands.copySelection),
1536
1864
  'Mod-i': ({ can, commands, context }, event)=>runSpreadsheetCellFormatShortcut(event, context, can.setCellFormat, commands.setCellFormat, 'it'),
1865
+ 'Mod-v': ({ can, commands }, event)=>runSpreadsheetClipboardShortcut(event, can.pasteSelection, commands.pasteSelection),
1866
+ 'Mod-x': ({ can, commands }, event)=>runSpreadsheetClipboardShortcut(event, can.cutSelection, commands.cutSelection),
1537
1867
  'Mod-u': ({ can, commands, context }, event)=>runSpreadsheetCellFormatShortcut(event, context, can.setCellFormat, commands.setCellFormat, 'un'),
1538
1868
  'Mod-z': ({ can, commands }, event)=>runSpreadsheetHistoryShortcut(event, can.undo, commands.undo),
1539
1869
  'Mod-Shift-z': ({ can, commands }, event)=>runSpreadsheetHistoryShortcut(event, can.redo, commands.redo),
1540
1870
  'Mod-y': ({ can, commands }, event)=>runSpreadsheetHistoryShortcut(event, can.redo, commands.redo),
1871
+ 'Control-m': ({ can, commands }, event)=>runSpreadsheetMergeShortcut(event, can.mergeSelectedCells, commands.mergeSelectedCells),
1872
+ 'Mod-Shift-l': ({ can, commands }, event)=>runSpreadsheetAutoFilterShortcut(event, can.toggleAutoFilter, commands.toggleAutoFilter),
1873
+ 'Alt-ArrowDown': ({ can, commands }, event)=>runSpreadsheetAutoFilterShortcut(event, can.openAutoFilterMenu, commands.openAutoFilterMenu),
1541
1874
  'Control-PageUp': ({ context, commands }, event)=>runSpreadsheetSheetNavigationShortcut(event, context, commands.activateSheet, -1),
1542
1875
  'Control-PageDown': ({ context, commands }, event)=>runSpreadsheetSheetNavigationShortcut(event, context, commands.activateSheet, 1),
1543
1876
  'Mod-PageUp': ({ context, commands }, event)=>runSpreadsheetSheetNavigationShortcut(event, context, commands.activateSheet, -1),
1544
1877
  'Mod-PageDown': ({ context, commands }, event)=>runSpreadsheetSheetNavigationShortcut(event, context, commands.activateSheet, 1),
1545
1878
  'Shift-F11': ({ can, commands }, event)=>runSpreadsheetAddSheetShortcut(event, can.addSheet, commands.addSheet),
1546
1879
  'Alt-Shift-F1': ({ can, commands }, event)=>runSpreadsheetAddSheetShortcut(event, can.addSheet, commands.addSheet),
1880
+ Escape: ({ can, commands }, event)=>runSpreadsheetFormatPainterEscape(event, can.cancelFormatPainter, commands.cancelFormatPainter),
1881
+ F9: ({ can, commands }, event)=>runSpreadsheetRecalculationShortcut(event, can.recalculateFormula, commands.recalculateFormula),
1547
1882
  Delete: ({ can, commands }, event)=>runSpreadsheetClearShortcut(event, can.clearSelectedCells, commands.clearSelectedCells),
1548
1883
  Backspace: ({ can, commands }, event)=>runSpreadsheetClearShortcut(event, can.clearSelectedCells, commands.clearSelectedCells),
1549
1884
  ArrowUp: ({ can, commands }, event)=>runSpreadsheetSelectionMoveShortcut(event, can.moveSelection, commands.moveSelection, 'up', false),
@@ -1612,7 +1947,7 @@ function createSpreadsheetEditorExtensions() {
1612
1947
  name: 'spreadsheetCellFormatting',
1613
1948
  addCommands: ()=>({
1614
1949
  clearSelectedCells: {
1615
- canExecute: canEditSelectedCells,
1950
+ canExecute: (context)=>canEditSelectedCells(context),
1616
1951
  execute: clearSelectedCells
1617
1952
  },
1618
1953
  pasteCells: {
@@ -1623,9 +1958,9 @@ function createSpreadsheetEditorExtensions() {
1623
1958
  canExecute: canEditSelectedCells,
1624
1959
  execute: formatCells
1625
1960
  },
1626
- toggleCellMerge: {
1627
- canExecute: canEditSelectedCells,
1628
- execute: toggleCellMerge
1961
+ mergeSelectedCells: {
1962
+ canExecute: canMergeSelectedCells,
1963
+ execute: mergeSelectedCells
1629
1964
  }
1630
1965
  })
1631
1966
  }),
@@ -1641,6 +1976,10 @@ function createSpreadsheetEditorExtensions() {
1641
1976
  createOfficeEditorExtension({
1642
1977
  name: 'spreadsheetView',
1643
1978
  addCommands: ()=>({
1979
+ setFreezePanes: {
1980
+ canExecute: canSetFreezePanes,
1981
+ execute: setFreezePanes
1982
+ },
1644
1983
  setGridLines: {
1645
1984
  canExecute: hasActiveSheet,
1646
1985
  execute: (context, visible)=>updateSpreadsheetSheet(context, 'gridLines', visible)
@@ -1798,26 +2137,37 @@ function spreadsheetStructureExtent(context, axis) {
1798
2137
  const dataWidth = Math.max(0, ...(sheet.data ?? []).map((row)=>row.length));
1799
2138
  return Math.max(sheet.column ?? 0, dataWidth, range[1] + 1);
1800
2139
  }
1801
- function clearSelectedCells(context) {
2140
+ function clearSelectedCells(context, mode = 'contents') {
1802
2141
  if (!context.workbook || !context.targetSheetId) return false;
1803
2142
  const range = liveRange(context);
1804
2143
  const calls = [];
1805
- for(let row = range.row[0]; row <= range.row[1]; row += 1)for(let column = range.column[0]; column <= range.column[1]; column += 1)calls.push({
1806
- name: 'clearCell',
1807
- args: [
1808
- row,
1809
- column,
1810
- {
1811
- id: context.targetSheetId
1812
- }
1813
- ]
1814
- });
1815
2144
  try {
2145
+ const sheet = context.workbook.getSheet({
2146
+ id: context.targetSheetId
2147
+ });
2148
+ if ('contents' === mode || 'all' === mode) for(let row = range.row[0]; row <= range.row[1]; row += 1)for(let column = range.column[0]; column <= range.column[1]; column += 1)calls.push({
2149
+ name: 'clearCell',
2150
+ args: [
2151
+ row,
2152
+ column,
2153
+ {
2154
+ id: context.targetSheetId
2155
+ }
2156
+ ]
2157
+ });
2158
+ calls.push({
2159
+ name: 'updateSheet',
2160
+ args: [
2161
+ [
2162
+ clearSpreadsheetSheetSelection(sheet, range, mode)
2163
+ ]
2164
+ ]
2165
+ });
1816
2166
  context.workbook.batchCallApis(calls);
1817
2167
  } catch {
1818
2168
  return false;
1819
2169
  }
1820
- syncSpreadsheetFormulaBar(context, '');
2170
+ if ('contents' === mode || 'all' === mode) syncSpreadsheetFormulaBar(context, '');
1821
2171
  return true;
1822
2172
  }
1823
2173
  function canNavigateSpreadsheetSelection(context) {
@@ -1916,18 +2266,20 @@ function isSpreadsheetCellTypeFormat(value) {
1916
2266
  const format = value;
1917
2267
  return Boolean('string' == typeof format?.fa && format.fa.trim() && 'string' == typeof format.t && format.t.trim());
1918
2268
  }
1919
- function toggleCellMerge(context, merged) {
2269
+ function canMergeSelectedCells(context, command) {
2270
+ if (!canEditSelectedCells(context)) return false;
2271
+ return canApplySpreadsheetCellMerge(context.content.sheets.find((sheet)=>sheet.id === context.targetSheetId), liveRange(context), command);
2272
+ }
2273
+ function mergeSelectedCells(context, command) {
1920
2274
  if (!context.workbook || !context.targetSheetId) return false;
1921
- const ranges = [
1922
- liveRange(context)
1923
- ];
1924
- if (merged) context.workbook.cancelMerge(ranges, {
1925
- id: context.targetSheetId
1926
- });
1927
- else context.workbook.mergeCells(ranges, 'merge-all', {
1928
- id: context.targetSheetId
1929
- });
1930
- return true;
2275
+ const calls = spreadsheetCellMergeApiCalls(context.content.sheets.find((sheet)=>sheet.id === context.targetSheetId), context.targetSheetId, liveRange(context), command);
2276
+ if (!calls.length) return false;
2277
+ try {
2278
+ context.workbook.batchCallApis(calls);
2279
+ return true;
2280
+ } catch {
2281
+ return false;
2282
+ }
1931
2283
  }
1932
2284
  function recalculateSpreadsheet(context, scope) {
1933
2285
  if (!context.calculation) return false;
@@ -1963,6 +2315,15 @@ function updateSpreadsheetSheet(context, property, value) {
1963
2315
  context.onChange(next);
1964
2316
  return true;
1965
2317
  }
2318
+ function canSetFreezePanes(context, preset) {
2319
+ return Boolean(context.editable && updateSpreadsheetFreezePanes(context.content, context.activeSheetId, preset, liveFreezePanesSelection(context)));
2320
+ }
2321
+ function setFreezePanes(context, preset) {
2322
+ return applySpreadsheetSheetChange(context, updateSpreadsheetFreezePanes(context.content, context.activeSheetId, preset, liveFreezePanesSelection(context)));
2323
+ }
2324
+ function liveFreezePanesSelection(context) {
2325
+ return finiteSpreadsheetSelection(context.workbook?.getSelection()?.at(-1) ?? context.fallbackRange);
2326
+ }
1966
2327
  function canEditSelectedCells(context) {
1967
2328
  return Boolean(context.editable && context.workbook && context.targetSheetId);
1968
2329
  }
@@ -1978,15 +2339,30 @@ function runSpreadsheetHistoryShortcut(event, canExecute, execute) {
1978
2339
  if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute()) return false;
1979
2340
  return execute();
1980
2341
  }
2342
+ function isSpreadsheetAutoFilterTextTarget(target) {
2343
+ return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target instanceof HTMLElement && (target.isContentEditable || Boolean(target.closest('[contenteditable="true"]')));
2344
+ }
1981
2345
  function runSpreadsheetCellFormatShortcut(event, context, canExecute, execute, attribute) {
1982
2346
  if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target)) return false;
1983
2347
  const value = 1 === Number(context.toolbarCell?.[attribute]) ? 0 : 1;
1984
2348
  return canExecute(attribute, value) && execute(attribute, value);
1985
2349
  }
2350
+ function runSpreadsheetMergeShortcut(event, canExecute, execute) {
2351
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute('merge-and-center')) return false;
2352
+ return execute('merge-and-center');
2353
+ }
1986
2354
  function runSpreadsheetClearShortcut(event, canExecute, execute) {
1987
2355
  if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute()) return false;
1988
2356
  return execute();
1989
2357
  }
2358
+ function runSpreadsheetAutoFilterShortcut(event, canExecute, execute) {
2359
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || isSpreadsheetAutoFilterTextTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute()) return false;
2360
+ return execute();
2361
+ }
2362
+ function runSpreadsheetFormatPainterEscape(event, canExecute, execute) {
2363
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute()) return false;
2364
+ return execute();
2365
+ }
1990
2366
  function runSpreadsheetSheetNavigationShortcut(event, context, activateSheet, direction) {
1991
2367
  if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target)) return false;
1992
2368
  const target = adjacentSpreadsheetSheetId(context.content, context.activeSheetId, direction);
@@ -1996,16 +2372,263 @@ function runSpreadsheetAddSheetShortcut(event, canExecute, execute) {
1996
2372
  if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute()) return false;
1997
2373
  return execute();
1998
2374
  }
2375
+ function runSpreadsheetRecalculationShortcut(event, canExecute, execute) {
2376
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute('workbook')) return false;
2377
+ return execute('workbook');
2378
+ }
1999
2379
  function liveRange(context) {
2000
2380
  const live = context.workbook?.getSelection()?.at(-1);
2001
2381
  return spreadsheetSingleRange(live ?? context.fallbackRange);
2002
2382
  }
2003
- function spreadsheetFormulaCount(content) {
2004
- return content.sheets.reduce((count, sheet)=>count + (sheet.data ?? []).reduce((sheetCount, row)=>sheetCount + row.reduce((rowCount, cell)=>rowCount + (cell?.f ? 1 : 0), 0), 0), 0);
2005
- }
2006
- function spreadsheetFormulaAnalysis(content) {
2007
- const scan = scanSpreadsheetFormulas(content);
2008
- return {
2383
+ const spreadsheetSystemClipboardTimeoutMs = 800;
2384
+ function spreadsheetCoreContextMenuItems({ can, clipboard = browserSpreadsheetClipboard, commands, selection }) {
2385
+ const copy = (cut)=>{
2386
+ copySpreadsheetSelection(clipboard, selection.clipboard, cut).then((copied)=>{
2387
+ if (copied && cut && !commands.clearSelectedCells()) showToast('选区已复制,但无法清除原内容。', 'error');
2388
+ });
2389
+ };
2390
+ return [
2391
+ {
2392
+ id: 'cut-cells',
2393
+ label: '剪切',
2394
+ icon: /*#__PURE__*/ jsx(Scissors, {
2395
+ size: 14
2396
+ }),
2397
+ shortcut: '⌘X',
2398
+ ariaKeyShortcut: 'Control+X Meta+X',
2399
+ disabled: !can.clearSelectedCells(),
2400
+ onSelect: ()=>copy(true)
2401
+ },
2402
+ {
2403
+ id: 'copy-cells',
2404
+ label: '复制',
2405
+ icon: /*#__PURE__*/ jsx(Copy, {
2406
+ size: 14
2407
+ }),
2408
+ shortcut: '⌘C',
2409
+ ariaKeyShortcut: 'Control+C Meta+C',
2410
+ onSelect: ()=>copy(false)
2411
+ },
2412
+ {
2413
+ id: 'paste-cells',
2414
+ label: '粘贴',
2415
+ icon: /*#__PURE__*/ jsx(ClipboardPaste, {
2416
+ size: 14
2417
+ }),
2418
+ shortcut: '⌘V',
2419
+ ariaKeyShortcut: 'Control+V Meta+V',
2420
+ disabled: !can.pasteCells([
2421
+ [
2422
+ ''
2423
+ ]
2424
+ ]),
2425
+ onSelect: ()=>void pasteSpreadsheetSelection(clipboard, commands.pasteCells)
2426
+ },
2427
+ {
2428
+ id: 'clear-cells',
2429
+ label: '清除内容',
2430
+ icon: /*#__PURE__*/ jsx(Eraser, {
2431
+ size: 14
2432
+ }),
2433
+ shortcut: 'Delete',
2434
+ ariaKeyShortcut: 'Delete',
2435
+ separatorBefore: true,
2436
+ disabled: !can.clearSelectedCells(),
2437
+ onSelect: ()=>{
2438
+ if (!commands.clearSelectedCells()) showToast(`无法清除选区 ${selection.reference}。`, 'error');
2439
+ }
2440
+ }
2441
+ ];
2442
+ }
2443
+ function spreadsheetStructureContextMenuItems({ axis, can, commands, onResize }) {
2444
+ const row = 'row' === axis;
2445
+ const subject = row ? '行' : '列';
2446
+ const before = row ? '上方' : '左侧';
2447
+ const after = row ? '下方' : '右侧';
2448
+ const defaultSize = row ? 24 : 96;
2449
+ return [
2450
+ ...spreadsheetSortContextMenuItems({
2451
+ can,
2452
+ commands,
2453
+ idSuffix: axis
2454
+ }),
2455
+ {
2456
+ id: `insert-${axis}-before`,
2457
+ label: `在${before}插入${subject}`,
2458
+ icon: /*#__PURE__*/ jsx(Plus, {
2459
+ size: 14
2460
+ }),
2461
+ separatorBefore: true,
2462
+ disabled: !can.insertSelectedStructure(axis, 'before'),
2463
+ onSelect: ()=>commands.insertSelectedStructure(axis, 'before')
2464
+ },
2465
+ {
2466
+ id: `insert-${axis}-after`,
2467
+ label: `在${after}插入${subject}`,
2468
+ icon: /*#__PURE__*/ jsx(Plus, {
2469
+ size: 14
2470
+ }),
2471
+ disabled: !can.insertSelectedStructure(axis, 'after'),
2472
+ onSelect: ()=>commands.insertSelectedStructure(axis, 'after')
2473
+ },
2474
+ {
2475
+ id: `delete-${axis}`,
2476
+ label: `删除所选${subject}`,
2477
+ icon: /*#__PURE__*/ jsx(Trash2, {
2478
+ size: 14
2479
+ }),
2480
+ danger: true,
2481
+ disabled: !can.deleteSelectedStructure(axis),
2482
+ onSelect: ()=>commands.deleteSelectedStructure(axis)
2483
+ },
2484
+ {
2485
+ id: `resize-${axis}`,
2486
+ label: row ? '行高…' : '列宽…',
2487
+ icon: row ? /*#__PURE__*/ jsx(MoveVertical, {
2488
+ size: 14
2489
+ }) : /*#__PURE__*/ jsx(MoveHorizontal, {
2490
+ size: 14
2491
+ }),
2492
+ disabled: !can.setSelectedStructureSize(axis, defaultSize),
2493
+ onSelect: ()=>onResize(axis)
2494
+ },
2495
+ {
2496
+ id: `hide-${axis}`,
2497
+ label: `隐藏所选${subject}`,
2498
+ icon: /*#__PURE__*/ jsx(EyeOff, {
2499
+ size: 14
2500
+ }),
2501
+ separatorBefore: true,
2502
+ disabled: !can.setSelectedStructureHidden(axis, true),
2503
+ onSelect: ()=>commands.setSelectedStructureHidden(axis, true)
2504
+ },
2505
+ {
2506
+ id: `show-${axis}`,
2507
+ label: `取消隐藏${subject}`,
2508
+ icon: /*#__PURE__*/ jsx(Eye, {
2509
+ size: 14
2510
+ }),
2511
+ disabled: !can.setSelectedStructureHidden(axis, false),
2512
+ onSelect: ()=>commands.setSelectedStructureHidden(axis, false)
2513
+ }
2514
+ ];
2515
+ }
2516
+ function spreadsheetSortContextMenuItems({ can, commands, idSuffix = 'selection', separatorBefore = false }) {
2517
+ return [
2518
+ {
2519
+ id: `sort-${idSuffix}-ascending`,
2520
+ label: '升序排列',
2521
+ icon: /*#__PURE__*/ jsx(ArrowUp, {
2522
+ size: 14
2523
+ }),
2524
+ separatorBefore,
2525
+ disabled: !can.sortSelectedCells('ascending'),
2526
+ onSelect: ()=>commands.sortSelectedCells('ascending')
2527
+ },
2528
+ {
2529
+ id: `sort-${idSuffix}-descending`,
2530
+ label: '降序排列',
2531
+ icon: /*#__PURE__*/ jsx(ArrowDown, {
2532
+ size: 14
2533
+ }),
2534
+ disabled: !can.sortSelectedCells('descending'),
2535
+ onSelect: ()=>commands.sortSelectedCells('descending')
2536
+ }
2537
+ ];
2538
+ }
2539
+ function parseSpreadsheetClipboardText(value) {
2540
+ if (!value) return [];
2541
+ const normalized = value.replace(/\r\n?/g, '\n').replace(/\n$/, '');
2542
+ if (!normalized) return [];
2543
+ const rows = normalized.split('\n').map((row)=>row.split('\t'));
2544
+ const width = Math.max(...rows.map((row)=>row.length));
2545
+ return rows.map((row)=>[
2546
+ ...row,
2547
+ ...Array.from({
2548
+ length: width - row.length
2549
+ }, ()=>'')
2550
+ ]);
2551
+ }
2552
+ const browserSpreadsheetClipboard = {
2553
+ readText: ()=>readSpreadsheetClipboardText("u" > typeof navigator && navigator.clipboard?.readText ? ()=>navigator.clipboard.readText() : void 0, readLocalClipboard, spreadsheetSystemClipboardTimeoutMs, canReadBrowserSystemClipboard),
2554
+ writeText: (value)=>writeSpreadsheetClipboardText(value, "u" > typeof navigator && navigator.clipboard?.writeText ? (next)=>navigator.clipboard.writeText(next) : void 0, writeLocalClipboard)
2555
+ };
2556
+ async function readSpreadsheetClipboardText(readSystem, readLocal, timeoutMs = spreadsheetSystemClipboardTimeoutMs, canReadSystem = ()=>true) {
2557
+ if (readSystem && await canReadSystem()) try {
2558
+ const value = await withSpreadsheetClipboardTimeout(readSystem(), timeoutMs);
2559
+ if (value) return value;
2560
+ } catch {}
2561
+ return readLocal();
2562
+ }
2563
+ async function canReadBrowserSystemClipboard() {
2564
+ if ("u" < typeof navigator || !navigator.permissions?.query) return true;
2565
+ try {
2566
+ const permission = await navigator.permissions.query({
2567
+ name: 'clipboard-read'
2568
+ });
2569
+ return 'granted' === permission.state;
2570
+ } catch {
2571
+ return true;
2572
+ }
2573
+ }
2574
+ async function writeSpreadsheetClipboardText(value, writeSystem, writeLocal, timeoutMs = spreadsheetSystemClipboardTimeoutMs) {
2575
+ writeLocal(value);
2576
+ if (!writeSystem) return;
2577
+ try {
2578
+ await withSpreadsheetClipboardTimeout(writeSystem(value), timeoutMs);
2579
+ } catch {}
2580
+ }
2581
+ async function copySpreadsheetSelection(clipboard, value, cut) {
2582
+ try {
2583
+ await clipboard.writeText(value);
2584
+ showToast(cut ? '选区已剪切' : '选区已复制', 'success');
2585
+ return true;
2586
+ } catch {
2587
+ showToast('无法访问剪贴板,请使用系统快捷键。', 'error');
2588
+ return false;
2589
+ }
2590
+ }
2591
+ async function pasteSpreadsheetSelection(clipboard, paste) {
2592
+ try {
2593
+ const values = parseSpreadsheetClipboardText(await clipboard.readText());
2594
+ if (!values.length) return void showToast('剪贴板中没有可粘贴的表格内容。', 'error');
2595
+ if (!paste(values)) showToast('当前选区无法粘贴这些内容。', 'error');
2596
+ } catch {
2597
+ showToast('无法读取剪贴板,请使用系统粘贴快捷键。', 'error');
2598
+ }
2599
+ }
2600
+ function readLocalClipboard() {
2601
+ try {
2602
+ return sessionStorage.getItem('a3s-office-spreadsheet-clipboard') ?? '';
2603
+ } catch {
2604
+ return '';
2605
+ }
2606
+ }
2607
+ function writeLocalClipboard(value) {
2608
+ try {
2609
+ sessionStorage.setItem('a3s-office-spreadsheet-clipboard', value);
2610
+ } catch {}
2611
+ }
2612
+ async function withSpreadsheetClipboardTimeout(request, timeoutMs) {
2613
+ let timeout;
2614
+ const expired = new Promise((_resolve, reject)=>{
2615
+ timeout = setTimeout(()=>reject(new Error('Spreadsheet clipboard request timed out.')), Math.max(0, timeoutMs));
2616
+ });
2617
+ try {
2618
+ return await Promise.race([
2619
+ request,
2620
+ expired
2621
+ ]);
2622
+ } finally{
2623
+ if (timeout) clearTimeout(timeout);
2624
+ }
2625
+ }
2626
+ function spreadsheetFormulaCount(content) {
2627
+ return content.sheets.reduce((count, sheet)=>count + (sheet.data ?? []).reduce((sheetCount, row)=>sheetCount + row.reduce((rowCount, cell)=>rowCount + (cell?.f ? 1 : 0), 0), 0), 0);
2628
+ }
2629
+ function spreadsheetFormulaAnalysis(content) {
2630
+ const scan = scanSpreadsheetFormulas(content);
2631
+ return {
2009
2632
  summary: scan.summary,
2010
2633
  diagnostics: createSpreadsheetFormulaDiagnostics(content, scan)
2011
2634
  };
@@ -2185,13 +2808,493 @@ function scanSpreadsheetFormulas(content) {
2185
2808
  function isFormulaError(cell) {
2186
2809
  return cell.ct?.t === 'e' || /^#(?:NULL!|DIV\/0!|VALUE!|REF!|NAME\?|NUM!|N\/A|GETTING_DATA)$/i.test(String(cell.m ?? ''));
2187
2810
  }
2188
- const COMPARISON_LABELS = {
2189
- greaterThan: '大于',
2190
- greaterThanOrEqual: '大于或等于',
2191
- lessThan: '小于',
2192
- lessThanOrEqual: '小于或等于',
2193
- equal: '等于',
2194
- notEqual: '不等于',
2811
+ const spreadsheetRibbonTabs = [
2812
+ {
2813
+ id: 'home',
2814
+ label: '开始'
2815
+ },
2816
+ {
2817
+ id: 'insert',
2818
+ label: '插入'
2819
+ },
2820
+ {
2821
+ id: 'pageLayout',
2822
+ label: '页面布局',
2823
+ compactLabel: '布局'
2824
+ },
2825
+ {
2826
+ id: 'formulas',
2827
+ label: '公式'
2828
+ },
2829
+ {
2830
+ id: 'data',
2831
+ label: '数据'
2832
+ },
2833
+ {
2834
+ id: 'review',
2835
+ label: '审阅'
2836
+ },
2837
+ {
2838
+ id: 'view',
2839
+ label: '视图'
2840
+ }
2841
+ ];
2842
+ const spreadsheetCommandCatalog = {
2843
+ undo: {
2844
+ id: 'history.undo',
2845
+ label: '撤销',
2846
+ location: {
2847
+ area: 'quickAccess'
2848
+ },
2849
+ shortcut: {
2850
+ label: 'Cmd/Ctrl+Z',
2851
+ aria: 'Control+Z Meta+Z',
2852
+ editor: [
2853
+ 'Mod-z'
2854
+ ]
2855
+ }
2856
+ },
2857
+ redo: {
2858
+ id: 'history.redo',
2859
+ label: '重做',
2860
+ location: {
2861
+ area: 'quickAccess'
2862
+ },
2863
+ shortcut: {
2864
+ label: 'Cmd/Ctrl+Shift+Z 或 Cmd/Ctrl+Y',
2865
+ aria: 'Control+Shift+Z Meta+Shift+Z Control+Y Meta+Y',
2866
+ editor: [
2867
+ 'Mod-Shift-z',
2868
+ 'Mod-y'
2869
+ ]
2870
+ }
2871
+ },
2872
+ paste: {
2873
+ id: 'clipboard.paste',
2874
+ label: '粘贴',
2875
+ location: {
2876
+ area: 'ribbon',
2877
+ tab: 'home',
2878
+ group: 'clipboard'
2879
+ },
2880
+ shortcut: {
2881
+ label: 'Cmd/Ctrl+V',
2882
+ aria: 'Control+V Meta+V',
2883
+ editor: [
2884
+ 'Mod-v'
2885
+ ]
2886
+ }
2887
+ },
2888
+ cut: {
2889
+ id: 'clipboard.cut',
2890
+ label: '剪切',
2891
+ location: {
2892
+ area: 'ribbon',
2893
+ tab: 'home',
2894
+ group: 'clipboard'
2895
+ },
2896
+ shortcut: {
2897
+ label: 'Cmd/Ctrl+X',
2898
+ aria: 'Control+X Meta+X',
2899
+ editor: [
2900
+ 'Mod-x'
2901
+ ]
2902
+ }
2903
+ },
2904
+ copy: {
2905
+ id: 'clipboard.copy',
2906
+ label: '复制',
2907
+ location: {
2908
+ area: 'ribbon',
2909
+ tab: 'home',
2910
+ group: 'clipboard'
2911
+ },
2912
+ shortcut: {
2913
+ label: 'Cmd/Ctrl+C',
2914
+ aria: 'Control+C Meta+C',
2915
+ editor: [
2916
+ 'Mod-c'
2917
+ ]
2918
+ }
2919
+ },
2920
+ formatPainter: {
2921
+ id: 'clipboard.formatPainter',
2922
+ label: '格式刷',
2923
+ location: {
2924
+ area: 'ribbon',
2925
+ tab: 'home',
2926
+ group: 'clipboard'
2927
+ }
2928
+ },
2929
+ bold: {
2930
+ id: 'font.bold',
2931
+ label: '加粗',
2932
+ location: {
2933
+ area: 'ribbon',
2934
+ tab: 'home',
2935
+ group: 'font'
2936
+ },
2937
+ shortcut: {
2938
+ label: 'Cmd/Ctrl+B',
2939
+ aria: 'Control+B Meta+B',
2940
+ editor: [
2941
+ 'Mod-b'
2942
+ ]
2943
+ }
2944
+ },
2945
+ italic: {
2946
+ id: 'font.italic',
2947
+ label: '斜体',
2948
+ location: {
2949
+ area: 'ribbon',
2950
+ tab: 'home',
2951
+ group: 'font'
2952
+ },
2953
+ shortcut: {
2954
+ label: 'Cmd/Ctrl+I',
2955
+ aria: 'Control+I Meta+I',
2956
+ editor: [
2957
+ 'Mod-i'
2958
+ ]
2959
+ }
2960
+ },
2961
+ underline: {
2962
+ id: 'font.underline',
2963
+ label: '下划线',
2964
+ location: {
2965
+ area: 'ribbon',
2966
+ tab: 'home',
2967
+ group: 'font'
2968
+ },
2969
+ shortcut: {
2970
+ label: 'Cmd/Ctrl+U',
2971
+ aria: 'Control+U Meta+U',
2972
+ editor: [
2973
+ 'Mod-u'
2974
+ ]
2975
+ }
2976
+ },
2977
+ mergeAndCenter: {
2978
+ id: 'alignment.mergeAndCenter',
2979
+ label: '合并居中',
2980
+ location: {
2981
+ area: 'ribbon',
2982
+ tab: 'home',
2983
+ group: 'alignment'
2984
+ },
2985
+ shortcut: {
2986
+ label: 'Ctrl+M',
2987
+ aria: 'Control+M',
2988
+ editor: [
2989
+ 'Control-m'
2990
+ ]
2991
+ }
2992
+ },
2993
+ mergeCells: {
2994
+ id: 'alignment.mergeCells',
2995
+ label: '合并单元格',
2996
+ location: {
2997
+ area: 'ribbon',
2998
+ tab: 'home',
2999
+ group: 'alignment'
3000
+ }
3001
+ },
3002
+ mergeAcross: {
3003
+ id: 'alignment.mergeAcross',
3004
+ label: '跨行合并',
3005
+ location: {
3006
+ area: 'ribbon',
3007
+ tab: 'home',
3008
+ group: 'alignment'
3009
+ }
3010
+ },
3011
+ unmergeCells: {
3012
+ id: 'alignment.unmergeCells',
3013
+ label: '取消合并单元格',
3014
+ location: {
3015
+ area: 'ribbon',
3016
+ tab: 'home',
3017
+ group: 'alignment'
3018
+ }
3019
+ },
3020
+ unmergeAndFill: {
3021
+ id: 'alignment.unmergeAndFill',
3022
+ label: '取消合并并填充',
3023
+ location: {
3024
+ area: 'ribbon',
3025
+ tab: 'home',
3026
+ group: 'alignment'
3027
+ }
3028
+ },
3029
+ conditionalFormatting: {
3030
+ id: 'styles.conditionalFormatting',
3031
+ label: '条件格式',
3032
+ location: {
3033
+ area: 'ribbon',
3034
+ tab: 'home',
3035
+ group: 'styles'
3036
+ }
3037
+ },
3038
+ insertRowsAbove: {
3039
+ id: 'cells.insertRowsAbove',
3040
+ label: '在上方插入行',
3041
+ location: {
3042
+ area: 'ribbon',
3043
+ tab: 'home',
3044
+ group: 'cells'
3045
+ }
3046
+ },
3047
+ insertRowsBelow: {
3048
+ id: 'cells.insertRowsBelow',
3049
+ label: '在下方插入行',
3050
+ location: {
3051
+ area: 'ribbon',
3052
+ tab: 'home',
3053
+ group: 'cells'
3054
+ }
3055
+ },
3056
+ insertColumnsLeft: {
3057
+ id: 'cells.insertColumnsLeft',
3058
+ label: '在左侧插入列',
3059
+ location: {
3060
+ area: 'ribbon',
3061
+ tab: 'home',
3062
+ group: 'cells'
3063
+ }
3064
+ },
3065
+ insertColumnsRight: {
3066
+ id: 'cells.insertColumnsRight',
3067
+ label: '在右侧插入列',
3068
+ location: {
3069
+ area: 'ribbon',
3070
+ tab: 'home',
3071
+ group: 'cells'
3072
+ }
3073
+ },
3074
+ deleteRows: {
3075
+ id: 'cells.deleteRows',
3076
+ label: '删除所选行',
3077
+ location: {
3078
+ area: 'ribbon',
3079
+ tab: 'home',
3080
+ group: 'cells'
3081
+ }
3082
+ },
3083
+ deleteColumns: {
3084
+ id: 'cells.deleteColumns',
3085
+ label: '删除所选列',
3086
+ location: {
3087
+ area: 'ribbon',
3088
+ tab: 'home',
3089
+ group: 'cells'
3090
+ }
3091
+ },
3092
+ clearAll: {
3093
+ id: 'editing.clearAll',
3094
+ label: '清除全部',
3095
+ location: {
3096
+ area: 'ribbon',
3097
+ tab: 'home',
3098
+ group: 'editing'
3099
+ }
3100
+ },
3101
+ clearFormats: {
3102
+ id: 'editing.clearFormats',
3103
+ label: '清除格式',
3104
+ location: {
3105
+ area: 'ribbon',
3106
+ tab: 'home',
3107
+ group: 'editing'
3108
+ }
3109
+ },
3110
+ clearContents: {
3111
+ id: 'editing.clearContents',
3112
+ label: '清除内容',
3113
+ location: {
3114
+ area: 'ribbon',
3115
+ tab: 'home',
3116
+ group: 'editing'
3117
+ },
3118
+ shortcut: {
3119
+ label: 'Delete/Backspace',
3120
+ aria: 'Delete Backspace',
3121
+ editor: [
3122
+ 'Delete',
3123
+ 'Backspace'
3124
+ ]
3125
+ }
3126
+ },
3127
+ clearComments: {
3128
+ id: 'editing.clearComments',
3129
+ label: '清除批注',
3130
+ location: {
3131
+ area: 'ribbon',
3132
+ tab: 'home',
3133
+ group: 'editing'
3134
+ }
3135
+ },
3136
+ clearHyperlinks: {
3137
+ id: 'editing.clearHyperlinks',
3138
+ label: '清除超链接',
3139
+ location: {
3140
+ area: 'ribbon',
3141
+ tab: 'home',
3142
+ group: 'editing'
3143
+ }
3144
+ },
3145
+ find: {
3146
+ id: 'editing.find',
3147
+ label: '查找',
3148
+ location: {
3149
+ area: 'ribbon',
3150
+ tab: 'home',
3151
+ group: 'editing'
3152
+ },
3153
+ shortcut: {
3154
+ label: 'Cmd/Ctrl+F',
3155
+ aria: 'Control+F Meta+F',
3156
+ editor: [
3157
+ 'Mod-f'
3158
+ ]
3159
+ }
3160
+ },
3161
+ insertChart: {
3162
+ id: 'insert.chart',
3163
+ label: '插入图表',
3164
+ location: {
3165
+ area: 'ribbon',
3166
+ tab: 'insert',
3167
+ group: 'charts'
3168
+ }
3169
+ },
3170
+ printSettings: {
3171
+ id: 'pageLayout.printSettings',
3172
+ label: '打印设置',
3173
+ location: {
3174
+ area: 'ribbon',
3175
+ tab: 'pageLayout',
3176
+ group: 'pageSetup'
3177
+ }
3178
+ },
3179
+ nameManager: {
3180
+ id: 'formulas.nameManager',
3181
+ label: '名称管理器',
3182
+ location: {
3183
+ area: 'ribbon',
3184
+ tab: 'formulas',
3185
+ group: 'definedNames'
3186
+ }
3187
+ },
3188
+ formulaManager: {
3189
+ id: 'formulas.manager',
3190
+ label: '公式与计算',
3191
+ location: {
3192
+ area: 'ribbon',
3193
+ tab: 'formulas',
3194
+ group: 'calculation'
3195
+ }
3196
+ },
3197
+ recalculateWorkbook: {
3198
+ id: 'formulas.recalculateWorkbook',
3199
+ label: '重新计算工作簿',
3200
+ location: {
3201
+ area: 'ribbon',
3202
+ tab: 'formulas',
3203
+ group: 'calculation'
3204
+ },
3205
+ shortcut: {
3206
+ label: 'F9',
3207
+ aria: 'F9',
3208
+ editor: [
3209
+ 'F9'
3210
+ ]
3211
+ }
3212
+ },
3213
+ sortAscending: {
3214
+ id: 'data.sortAscending',
3215
+ label: '升序',
3216
+ location: {
3217
+ area: 'ribbon',
3218
+ tab: 'data',
3219
+ group: 'sortAndFilter'
3220
+ }
3221
+ },
3222
+ sortDescending: {
3223
+ id: 'data.sortDescending',
3224
+ label: '降序',
3225
+ location: {
3226
+ area: 'ribbon',
3227
+ tab: 'data',
3228
+ group: 'sortAndFilter'
3229
+ }
3230
+ },
3231
+ autoFilter: {
3232
+ id: 'data.autoFilter',
3233
+ label: '自动筛选',
3234
+ location: {
3235
+ area: 'ribbon',
3236
+ tab: 'data',
3237
+ group: 'sortAndFilter'
3238
+ },
3239
+ shortcut: {
3240
+ label: 'Cmd/Ctrl+Shift+L',
3241
+ aria: 'Control+Shift+L Meta+Shift+L',
3242
+ editor: [
3243
+ 'Mod-Shift-l'
3244
+ ]
3245
+ },
3246
+ menuShortcut: {
3247
+ label: 'Alt+↓',
3248
+ aria: 'Alt+ArrowDown',
3249
+ editor: [
3250
+ 'Alt-ArrowDown'
3251
+ ]
3252
+ }
3253
+ },
3254
+ pivotTable: {
3255
+ id: 'data.pivotTable',
3256
+ label: '数据透视表',
3257
+ location: {
3258
+ area: 'ribbon',
3259
+ tab: 'data',
3260
+ group: 'analysis'
3261
+ }
3262
+ },
3263
+ protectSheet: {
3264
+ id: 'review.protectSheet',
3265
+ label: '工作表保护',
3266
+ location: {
3267
+ area: 'ribbon',
3268
+ tab: 'review',
3269
+ group: 'protection'
3270
+ }
3271
+ },
3272
+ gridLines: {
3273
+ id: 'view.gridLines',
3274
+ label: '网格线',
3275
+ location: {
3276
+ area: 'ribbon',
3277
+ tab: 'view',
3278
+ group: 'workbookViews'
3279
+ }
3280
+ },
3281
+ freezePanes: {
3282
+ id: 'view.freezePanes',
3283
+ label: '冻结窗格',
3284
+ location: {
3285
+ area: 'ribbon',
3286
+ tab: 'view',
3287
+ group: 'window'
3288
+ }
3289
+ }
3290
+ };
3291
+ const COMPARISON_LABELS = {
3292
+ greaterThan: '大于',
3293
+ greaterThanOrEqual: '大于或等于',
3294
+ lessThan: '小于',
3295
+ lessThanOrEqual: '小于或等于',
3296
+ equal: '等于',
3297
+ notEqual: '不等于',
2195
3298
  between: '介于',
2196
3299
  notBetween: '不介于'
2197
3300
  };
@@ -2589,34 +3692,6 @@ function adjustSpreadsheetNumberFormatSection(section, direction) {
2589
3692
  const replacement = `${match[1]}${nextDecimals ? `.${nextDecimals}` : ''}`;
2590
3693
  return `${section.slice(0, match.index)}${replacement}${section.slice(match.index + match[0].length)}`;
2591
3694
  }
2592
- function InlineNotice({ title, children, icon, actions, tone = 'info', role, className = '' }) {
2593
- return /*#__PURE__*/ jsxs("div", {
2594
- className: `ds-inline-notice ${tone}${className ? ` ${className}` : ''}`,
2595
- role: role,
2596
- children: [
2597
- icon && /*#__PURE__*/ jsx("span", {
2598
- className: "ds-inline-notice-icon",
2599
- "aria-hidden": "true",
2600
- children: icon
2601
- }),
2602
- /*#__PURE__*/ jsxs("div", {
2603
- className: "ds-inline-notice-copy",
2604
- children: [
2605
- title && /*#__PURE__*/ jsx("strong", {
2606
- children: title
2607
- }),
2608
- /*#__PURE__*/ jsx("div", {
2609
- children: children
2610
- })
2611
- ]
2612
- }),
2613
- actions && /*#__PURE__*/ jsx("div", {
2614
- className: "ds-inline-notice-actions",
2615
- children: actions
2616
- })
2617
- ]
2618
- });
2619
- }
2620
3695
  function parseSpreadsheetRowPageBreaks(value, maximumRow) {
2621
3696
  return parsePageBreakList(value, maximumRow, (token)=>{
2622
3697
  const match = /^\$?([1-9]\d*)$/.exec(token);
@@ -3394,37 +4469,6 @@ function validPageSetup(pageSetup) {
3394
4469
  const validInteger = (value, minimum, maximum)=>Number.isInteger(value) && value >= minimum && value <= maximum;
3395
4470
  return validInteger(pageSetup.scale, 10, 400) && validInteger(pageSetup.fitToWidth, 0, 32767) && validInteger(pageSetup.fitToHeight, 0, 32767) && validInteger(pageSetup.pageNumberStart, 1, 32767) && Object.values(pageSetup.margins).every((margin)=>Number.isFinite(margin) && margin >= 0 && margin <= 100);
3396
4471
  }
3397
- const spreadsheetRibbonTabs = [
3398
- {
3399
- id: 'home',
3400
- label: '开始'
3401
- },
3402
- {
3403
- id: 'insert',
3404
- label: '插入'
3405
- },
3406
- {
3407
- id: 'pageLayout',
3408
- label: '页面布局',
3409
- compactLabel: '布局'
3410
- },
3411
- {
3412
- id: 'formulas',
3413
- label: '公式'
3414
- },
3415
- {
3416
- id: 'data',
3417
- label: '数据'
3418
- },
3419
- {
3420
- id: 'review',
3421
- label: '审阅'
3422
- },
3423
- {
3424
- id: 'view',
3425
- label: '视图'
3426
- }
3427
- ];
3428
4472
  const spreadsheetNumberFormatOptions = [
3429
4473
  {
3430
4474
  value: 'general',
@@ -3444,7 +4488,19 @@ const spreadsheetNumberFormatOptions = [
3444
4488
  disabled: true
3445
4489
  }
3446
4490
  ];
3447
- function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileActions, findOpen, gridLinesVisible, multipleCellsSelected, panelId, onOpenFind, onTabChange, onTogglePanel, panel, toolbarCell }) {
4491
+ const defaultSpreadsheetFreezePanesSelection = {
4492
+ row: [
4493
+ 0,
4494
+ 0
4495
+ ],
4496
+ column: [
4497
+ 0,
4498
+ 0
4499
+ ],
4500
+ row_focus: 0,
4501
+ column_focus: 0
4502
+ };
4503
+ function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, commands, content, fileActions, findOpen, formatPainterMode = null, freezePanesActive = false, freezePanesSelection = defaultSpreadsheetFreezePanesSelection, gridLinesVisible, panelId, onOpenFind, onTabChange, onTogglePanel, panel, toolbarCell }) {
3448
4504
  const formulaCount = useMemo(()=>spreadsheetFormulaCount(content), [
3449
4505
  content
3450
4506
  ]);
@@ -3464,32 +4520,89 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3464
4520
  defaultTab: "home",
3465
4521
  activeTab: activeTab,
3466
4522
  onTabChange: onTabChange,
4523
+ adaptive: true,
4524
+ collapsible: true,
3467
4525
  fileActions: fileActions,
4526
+ quickAccessActions: [
4527
+ {
4528
+ id: spreadsheetCommandCatalog.undo.id,
4529
+ label: spreadsheetCommandCatalog.undo.label,
4530
+ icon: /*#__PURE__*/ jsx(Undo2, {
4531
+ size: 15
4532
+ }),
4533
+ shortcut: spreadsheetCommandCatalog.undo.shortcut.label,
4534
+ ariaKeyShortcuts: spreadsheetCommandCatalog.undo.shortcut.aria,
4535
+ disabled: !can.undo(),
4536
+ onSelect: ()=>{
4537
+ commands.undo();
4538
+ }
4539
+ },
4540
+ {
4541
+ id: spreadsheetCommandCatalog.redo.id,
4542
+ label: spreadsheetCommandCatalog.redo.label,
4543
+ icon: /*#__PURE__*/ jsx(Redo2, {
4544
+ size: 15
4545
+ }),
4546
+ shortcut: spreadsheetCommandCatalog.redo.shortcut.label,
4547
+ ariaKeyShortcuts: spreadsheetCommandCatalog.redo.shortcut.aria,
4548
+ disabled: !can.redo(),
4549
+ onSelect: ()=>{
4550
+ commands.redo();
4551
+ }
4552
+ }
4553
+ ],
3468
4554
  className: "work-spreadsheet-ribbon",
3469
4555
  toolbarClassName: "work-spreadsheet-ribbon-toolbar",
3470
4556
  panels: {
3471
4557
  home: /*#__PURE__*/ jsxs(Fragment, {
3472
4558
  children: [
3473
4559
  /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
3474
- label: "撤销与恢复",
4560
+ label: "剪贴板",
4561
+ priority: "high",
3475
4562
  children: [
3476
4563
  /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3477
- label: "撤销",
3478
- title: "撤销(Cmd/Ctrl+Z)",
3479
- "aria-keyshortcuts": "Control+Z Meta+Z",
3480
- disabled: !can.undo(),
3481
- onClick: commands.undo,
3482
- children: /*#__PURE__*/ jsx(Undo2, {
4564
+ label: spreadsheetCommandCatalog.paste.label,
4565
+ title: `${spreadsheetCommandCatalog.paste.label}(${spreadsheetCommandCatalog.paste.shortcut.label})`,
4566
+ "aria-keyshortcuts": spreadsheetCommandCatalog.paste.shortcut.aria,
4567
+ disabled: !can.pasteSelection(),
4568
+ onClick: commands.pasteSelection,
4569
+ children: /*#__PURE__*/ jsx(ClipboardPaste, {
4570
+ size: 19
4571
+ })
4572
+ }),
4573
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
4574
+ label: spreadsheetCommandCatalog.cut.label,
4575
+ title: `${spreadsheetCommandCatalog.cut.label}(${spreadsheetCommandCatalog.cut.shortcut.label})`,
4576
+ "aria-keyshortcuts": spreadsheetCommandCatalog.cut.shortcut.aria,
4577
+ disabled: !can.cutSelection(),
4578
+ onClick: commands.cutSelection,
4579
+ children: /*#__PURE__*/ jsx(Scissors, {
4580
+ size: 19
4581
+ })
4582
+ }),
4583
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
4584
+ label: spreadsheetCommandCatalog.copy.label,
4585
+ title: `${spreadsheetCommandCatalog.copy.label}(${spreadsheetCommandCatalog.copy.shortcut.label})`,
4586
+ "aria-keyshortcuts": spreadsheetCommandCatalog.copy.shortcut.aria,
4587
+ disabled: !can.copySelection(),
4588
+ onClick: commands.copySelection,
4589
+ children: /*#__PURE__*/ jsx(Copy, {
3483
4590
  size: 19
3484
4591
  })
3485
4592
  }),
3486
4593
  /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3487
- label: "重做",
3488
- title: "重做(Cmd/Ctrl+Shift+Z 或 Cmd/Ctrl+Y)",
3489
- "aria-keyshortcuts": "Control+Shift+Z Meta+Shift+Z Control+Y Meta+Y",
3490
- disabled: !can.redo(),
3491
- onClick: commands.redo,
3492
- children: /*#__PURE__*/ jsx(Redo2, {
4594
+ label: spreadsheetCommandCatalog.formatPainter.label,
4595
+ title: spreadsheetFormatPainterTitle(formatPainterMode),
4596
+ active: null !== formatPainterMode,
4597
+ badge: 'locked' === formatPainterMode ? '连续' : void 0,
4598
+ disabled: null === formatPainterMode ? !can.activateFormatPainter('once') : !can.cancelFormatPainter(),
4599
+ onClick: (event)=>{
4600
+ if (event.detail > 1) return;
4601
+ if (null === formatPainterMode) commands.activateFormatPainter('once');
4602
+ else commands.cancelFormatPainter();
4603
+ },
4604
+ onDoubleClick: ()=>commands.activateFormatPainter('locked'),
4605
+ children: /*#__PURE__*/ jsx(Paintbrush, {
3493
4606
  size: 19
3494
4607
  })
3495
4608
  })
@@ -3497,6 +4610,7 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3497
4610
  }),
3498
4611
  /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
3499
4612
  label: "字体",
4613
+ priority: "high",
3500
4614
  children: [
3501
4615
  /*#__PURE__*/ jsx(OfficeSelect, {
3502
4616
  className: "work-spreadsheet-font-family",
@@ -3514,9 +4628,9 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3514
4628
  onValueChange: (value)=>commands.setCellFormat('fs', Number(value))
3515
4629
  }),
3516
4630
  /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3517
- label: "加粗",
3518
- title: "加粗(Cmd/Ctrl+B)",
3519
- "aria-keyshortcuts": "Control+B Meta+B",
4631
+ label: spreadsheetCommandCatalog.bold.label,
4632
+ title: `${spreadsheetCommandCatalog.bold.label}(${spreadsheetCommandCatalog.bold.shortcut.label})`,
4633
+ "aria-keyshortcuts": spreadsheetCommandCatalog.bold.shortcut.aria,
3520
4634
  displayLabel: false,
3521
4635
  active: 1 === Number(toolbarCell?.bl),
3522
4636
  disabled: !can.setCellFormat('bl', 1 === Number(toolbarCell?.bl) ? 0 : 1),
@@ -3526,9 +4640,9 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3526
4640
  })
3527
4641
  }),
3528
4642
  /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3529
- label: "斜体",
3530
- title: "斜体(Cmd/Ctrl+I)",
3531
- "aria-keyshortcuts": "Control+I Meta+I",
4643
+ label: spreadsheetCommandCatalog.italic.label,
4644
+ title: `${spreadsheetCommandCatalog.italic.label}(${spreadsheetCommandCatalog.italic.shortcut.label})`,
4645
+ "aria-keyshortcuts": spreadsheetCommandCatalog.italic.shortcut.aria,
3532
4646
  displayLabel: false,
3533
4647
  active: 1 === Number(toolbarCell?.it),
3534
4648
  disabled: !can.setCellFormat('it', 1 === Number(toolbarCell?.it) ? 0 : 1),
@@ -3538,9 +4652,9 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3538
4652
  })
3539
4653
  }),
3540
4654
  /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3541
- label: "下划线",
3542
- title: "下划线(Cmd/Ctrl+U)",
3543
- "aria-keyshortcuts": "Control+U Meta+U",
4655
+ label: spreadsheetCommandCatalog.underline.label,
4656
+ title: `${spreadsheetCommandCatalog.underline.label}(${spreadsheetCommandCatalog.underline.shortcut.label})`,
4657
+ "aria-keyshortcuts": spreadsheetCommandCatalog.underline.shortcut.aria,
3544
4658
  displayLabel: false,
3545
4659
  active: 1 === Number(toolbarCell?.un),
3546
4660
  disabled: !can.setCellFormat('un', 1 === Number(toolbarCell?.un) ? 0 : 1),
@@ -3569,6 +4683,7 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3569
4683
  }),
3570
4684
  /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
3571
4685
  label: "对齐",
4686
+ priority: "high",
3572
4687
  children: [
3573
4688
  /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3574
4689
  label: "左对齐",
@@ -3639,11 +4754,16 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3639
4754
  children: /*#__PURE__*/ jsx(WrapText, {
3640
4755
  size: 15
3641
4756
  })
4757
+ }),
4758
+ /*#__PURE__*/ jsx(SpreadsheetMergeMenu, {
4759
+ can: can,
4760
+ commands: commands
3642
4761
  })
3643
4762
  ]
3644
4763
  }),
3645
4764
  /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
3646
4765
  label: "数字",
4766
+ priority: "high",
3647
4767
  children: [
3648
4768
  /*#__PURE__*/ jsx(OfficeSelect, {
3649
4769
  className: "work-spreadsheet-number-format",
@@ -3689,54 +4809,12 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3689
4809
  })
3690
4810
  ]
3691
4811
  }),
3692
- /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3693
- label: "单元格",
3694
- children: /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3695
- label: toolbarCell?.mc ? '取消合并' : '合并单元格',
3696
- disabled: !can.toggleCellMerge(Boolean(toolbarCell?.mc)) || !toolbarCell?.mc && !multipleCellsSelected,
3697
- onClick: ()=>commands.toggleCellMerge(Boolean(toolbarCell?.mc)),
3698
- children: /*#__PURE__*/ jsx(Merge, {
3699
- size: 19
3700
- })
3701
- })
3702
- }),
3703
- /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3704
- label: "编辑",
3705
- children: /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3706
- label: "查找",
3707
- title: "查找(Cmd/Ctrl+F)",
3708
- "aria-keyshortcuts": "Control+F Meta+F",
3709
- active: findOpen,
3710
- onClick: onOpenFind,
3711
- children: /*#__PURE__*/ jsx(Search, {
3712
- size: 19
3713
- })
3714
- })
3715
- })
3716
- ]
3717
- }),
3718
- insert: /*#__PURE__*/ jsxs(Fragment, {
3719
- children: [
3720
- /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3721
- label: "图表",
3722
- children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
3723
- controlsId: panelId,
3724
- panel: "charts",
3725
- label: "插入图表",
3726
- count: spreadsheetChartCount(content),
3727
- icon: /*#__PURE__*/ jsx(BarChart3, {
3728
- size: 19
3729
- }),
3730
- active: 'charts' === panel,
3731
- onToggle: onTogglePanel
3732
- })
3733
- }),
3734
4812
  /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3735
4813
  label: "样式",
3736
4814
  children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
3737
4815
  controlsId: panelId,
3738
4816
  panel: "conditional-formatting",
3739
- label: "条件格式",
4817
+ label: spreadsheetCommandCatalog.conditionalFormatting.label,
3740
4818
  count: managedConditionalFormatCount(content),
3741
4819
  icon: /*#__PURE__*/ jsx(Palette, {
3742
4820
  size: 19
@@ -3744,16 +4822,59 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3744
4822
  active: 'conditional-formatting' === panel,
3745
4823
  onToggle: onTogglePanel
3746
4824
  })
4825
+ }),
4826
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
4827
+ label: "单元格",
4828
+ children: /*#__PURE__*/ jsx(SpreadsheetRowsAndColumnsMenu, {
4829
+ can: can,
4830
+ commands: commands
4831
+ })
4832
+ }),
4833
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
4834
+ label: "编辑",
4835
+ priority: "low",
4836
+ children: [
4837
+ /*#__PURE__*/ jsx(SpreadsheetClearMenu, {
4838
+ can: can,
4839
+ commands: commands
4840
+ }),
4841
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
4842
+ label: spreadsheetCommandCatalog.find.label,
4843
+ title: `${spreadsheetCommandCatalog.find.label}(${spreadsheetCommandCatalog.find.shortcut.label})`,
4844
+ "aria-keyshortcuts": spreadsheetCommandCatalog.find.shortcut.aria,
4845
+ active: findOpen,
4846
+ onClick: onOpenFind,
4847
+ children: /*#__PURE__*/ jsx(Search, {
4848
+ size: 19
4849
+ })
4850
+ })
4851
+ ]
3747
4852
  })
3748
4853
  ]
3749
4854
  }),
3750
- pageLayout: /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3751
- label: "页面设置",
4855
+ insert: /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
4856
+ label: "图表",
4857
+ priority: "high",
3752
4858
  children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
3753
4859
  controlsId: panelId,
3754
- panel: "print-area",
3755
- label: "打印设置",
3756
- count: spreadsheetPrintSettingCount(content),
4860
+ panel: "charts",
4861
+ label: spreadsheetCommandCatalog.insertChart.label,
4862
+ count: spreadsheetChartCount(content),
4863
+ icon: /*#__PURE__*/ jsx(BarChart3, {
4864
+ size: 19
4865
+ }),
4866
+ active: 'charts' === panel,
4867
+ onToggle: onTogglePanel
4868
+ })
4869
+ }),
4870
+ pageLayout: /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
4871
+ label: "页面设置",
4872
+ priority: "high",
4873
+ children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
4874
+ controlsId: panelId,
4875
+ panel: "print-area",
4876
+ label: spreadsheetCommandCatalog.printSettings.label,
4877
+ count: spreadsheetPrintSettingCount(content),
3757
4878
  icon: /*#__PURE__*/ jsx(Printer, {
3758
4879
  size: 19
3759
4880
  }),
@@ -3765,10 +4886,11 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3765
4886
  children: [
3766
4887
  /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3767
4888
  label: "定义的名称",
4889
+ priority: "high",
3768
4890
  children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
3769
4891
  controlsId: panelId,
3770
4892
  panel: "names",
3771
- label: "名称管理器",
4893
+ label: spreadsheetCommandCatalog.nameManager.label,
3772
4894
  count: content.namedRanges?.length ?? 0,
3773
4895
  icon: /*#__PURE__*/ jsx(Bookmark, {
3774
4896
  size: 19
@@ -3777,42 +4899,93 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3777
4899
  onToggle: onTogglePanel
3778
4900
  })
3779
4901
  }),
3780
- /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
4902
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
3781
4903
  label: "计算",
4904
+ priority: "high",
4905
+ children: [
4906
+ /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
4907
+ controlsId: panelId,
4908
+ panel: "formulas",
4909
+ label: spreadsheetCommandCatalog.formulaManager.label,
4910
+ count: formulaCount,
4911
+ icon: /*#__PURE__*/ jsx(Calculator, {
4912
+ size: 19
4913
+ }),
4914
+ active: 'formulas' === panel,
4915
+ onToggle: onTogglePanel
4916
+ }),
4917
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
4918
+ label: spreadsheetCommandCatalog.recalculateWorkbook.label,
4919
+ title: `${spreadsheetCommandCatalog.recalculateWorkbook.label}(${spreadsheetCommandCatalog.recalculateWorkbook.shortcut.label})`,
4920
+ "aria-keyshortcuts": spreadsheetCommandCatalog.recalculateWorkbook.shortcut.aria,
4921
+ disabled: !can.recalculateFormula('workbook'),
4922
+ onClick: ()=>commands.recalculateFormula('workbook'),
4923
+ children: /*#__PURE__*/ jsx(RefreshCw, {
4924
+ size: 19
4925
+ })
4926
+ })
4927
+ ]
4928
+ })
4929
+ ]
4930
+ }),
4931
+ data: /*#__PURE__*/ jsxs(Fragment, {
4932
+ children: [
4933
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
4934
+ label: "排序和筛选",
4935
+ priority: "high",
4936
+ children: [
4937
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
4938
+ label: spreadsheetCommandCatalog.sortAscending.label,
4939
+ disabled: !can.sortSelectedCells('ascending'),
4940
+ onClick: ()=>commands.sortSelectedCells('ascending'),
4941
+ children: /*#__PURE__*/ jsx(SortAsc, {
4942
+ size: 19
4943
+ })
4944
+ }),
4945
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
4946
+ label: spreadsheetCommandCatalog.sortDescending.label,
4947
+ disabled: !can.sortSelectedCells('descending'),
4948
+ onClick: ()=>commands.sortSelectedCells('descending'),
4949
+ children: /*#__PURE__*/ jsx(SortDesc, {
4950
+ size: 19
4951
+ })
4952
+ }),
4953
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
4954
+ label: spreadsheetCommandCatalog.autoFilter.label,
4955
+ title: `${spreadsheetCommandCatalog.autoFilter.label}(${spreadsheetCommandCatalog.autoFilter.shortcut.label});表头菜单(${spreadsheetCommandCatalog.autoFilter.menuShortcut.label})`,
4956
+ "aria-keyshortcuts": spreadsheetCommandCatalog.autoFilter.shortcut.aria,
4957
+ active: autoFilterActive,
4958
+ disabled: !can.toggleAutoFilter(),
4959
+ onClick: ()=>commands.toggleAutoFilter(),
4960
+ children: /*#__PURE__*/ jsx(ListFilter, {
4961
+ size: 19
4962
+ })
4963
+ })
4964
+ ]
4965
+ }),
4966
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
4967
+ label: "分析",
3782
4968
  children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
3783
4969
  controlsId: panelId,
3784
- panel: "formulas",
3785
- label: "公式与计算",
3786
- count: formulaCount,
3787
- icon: /*#__PURE__*/ jsx(Calculator, {
4970
+ panel: "pivots",
4971
+ label: spreadsheetCommandCatalog.pivotTable.label,
4972
+ count: pivotCount,
4973
+ icon: /*#__PURE__*/ jsx(TableProperties, {
3788
4974
  size: 19
3789
4975
  }),
3790
- active: 'formulas' === panel,
4976
+ active: 'pivots' === panel,
3791
4977
  onToggle: onTogglePanel
3792
4978
  })
3793
4979
  })
3794
4980
  ]
3795
4981
  }),
3796
- data: /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3797
- label: "分析",
3798
- children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
3799
- controlsId: panelId,
3800
- panel: "pivots",
3801
- label: "数据透视表",
3802
- count: pivotCount,
3803
- icon: /*#__PURE__*/ jsx(TableProperties, {
3804
- size: 19
3805
- }),
3806
- active: 'pivots' === panel,
3807
- onToggle: onTogglePanel
3808
- })
3809
- }),
3810
4982
  review: /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3811
4983
  label: "保护",
4984
+ priority: "high",
3812
4985
  children: /*#__PURE__*/ jsx(SpreadsheetRibbonTool, {
3813
4986
  controlsId: panelId,
3814
4987
  panel: "protection",
3815
- label: "工作表保护",
4988
+ label: spreadsheetCommandCatalog.protectSheet.label,
3816
4989
  count: protectedSheetCount(content.sheets),
3817
4990
  icon: /*#__PURE__*/ jsx(ShieldCheck, {
3818
4991
  size: 19
@@ -3821,22 +4994,422 @@ function SpreadsheetEditorRibbon({ activeTab, can, commands, content, fileAction
3821
4994
  onToggle: onTogglePanel
3822
4995
  })
3823
4996
  }),
3824
- view: /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
3825
- label: "工作簿视图",
3826
- children: /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
3827
- label: gridLinesVisible ? '隐藏网格线' : '显示网格线',
3828
- visibleLabel: "网格线",
3829
- active: gridLinesVisible,
3830
- disabled: !can.setGridLines(!gridLinesVisible),
3831
- onClick: ()=>commands.setGridLines(!gridLinesVisible),
3832
- children: /*#__PURE__*/ jsx(Grid3X3, {
3833
- size: 19
4997
+ view: /*#__PURE__*/ jsxs(Fragment, {
4998
+ children: [
4999
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
5000
+ label: "工作簿视图",
5001
+ priority: "high",
5002
+ children: /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
5003
+ label: gridLinesVisible ? '隐藏网格线' : '显示网格线',
5004
+ visibleLabel: spreadsheetCommandCatalog.gridLines.label,
5005
+ active: gridLinesVisible,
5006
+ disabled: !can.setGridLines(!gridLinesVisible),
5007
+ onClick: ()=>commands.setGridLines(!gridLinesVisible),
5008
+ children: /*#__PURE__*/ jsx(Grid3X3, {
5009
+ size: 19
5010
+ })
5011
+ })
5012
+ }),
5013
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
5014
+ label: "窗口",
5015
+ priority: "high",
5016
+ children: /*#__PURE__*/ jsx(SpreadsheetFreezePanesMenu, {
5017
+ active: freezePanesActive,
5018
+ can: can,
5019
+ commands: commands,
5020
+ selection: freezePanesSelection
5021
+ })
3834
5022
  })
5023
+ ]
5024
+ })
5025
+ }
5026
+ });
5027
+ }
5028
+ function SpreadsheetMergeMenu({ can, commands }) {
5029
+ const items = [
5030
+ {
5031
+ command: 'merge-and-center',
5032
+ id: spreadsheetCommandCatalog.mergeAndCenter.id,
5033
+ label: spreadsheetCommandCatalog.mergeAndCenter.label,
5034
+ icon: /*#__PURE__*/ jsx(Merge, {
5035
+ size: 16
5036
+ })
5037
+ },
5038
+ {
5039
+ command: 'merge-cells',
5040
+ id: spreadsheetCommandCatalog.mergeCells.id,
5041
+ label: spreadsheetCommandCatalog.mergeCells.label,
5042
+ icon: /*#__PURE__*/ jsx(Grid3X3, {
5043
+ size: 16
5044
+ })
5045
+ },
5046
+ {
5047
+ command: 'merge-across',
5048
+ id: spreadsheetCommandCatalog.mergeAcross.id,
5049
+ label: spreadsheetCommandCatalog.mergeAcross.label,
5050
+ icon: /*#__PURE__*/ jsx(Rows3, {
5051
+ size: 16
5052
+ })
5053
+ },
5054
+ {
5055
+ command: 'unmerge-cells',
5056
+ id: spreadsheetCommandCatalog.unmergeCells.id,
5057
+ label: spreadsheetCommandCatalog.unmergeCells.label,
5058
+ icon: /*#__PURE__*/ jsx(X, {
5059
+ size: 16
5060
+ })
5061
+ },
5062
+ {
5063
+ command: 'unmerge-and-fill',
5064
+ id: spreadsheetCommandCatalog.unmergeAndFill.id,
5065
+ label: spreadsheetCommandCatalog.unmergeAndFill.label,
5066
+ icon: /*#__PURE__*/ jsx(Grid3X3, {
5067
+ size: 16
5068
+ })
5069
+ }
5070
+ ];
5071
+ const primaryDisabled = !can.mergeSelectedCells('merge-and-center');
5072
+ const menuDisabled = items.every(({ command })=>false === can.mergeSelectedCells(command));
5073
+ return /*#__PURE__*/ jsx(Popover, {
5074
+ label: "更多合并方式",
5075
+ panelLabel: "合并选项",
5076
+ panelRole: "menu",
5077
+ portal: true,
5078
+ className: "work-spreadsheet-ribbon-split-root",
5079
+ panelClassName: "work-office-context-menu work-spreadsheet-ribbon-menu",
5080
+ disabled: menuDisabled,
5081
+ focusFirstOnOpen: true,
5082
+ onPanelKeyDown: moveOfficeMenuFocus,
5083
+ trigger: (triggerProps, { open })=>/*#__PURE__*/ jsxs(Fragment, {
5084
+ children: [
5085
+ /*#__PURE__*/ jsxs("button", {
5086
+ type: "button",
5087
+ className: "with-label work-spreadsheet-ribbon-split-primary",
5088
+ "aria-label": spreadsheetCommandCatalog.mergeAndCenter.label,
5089
+ "aria-keyshortcuts": spreadsheetCommandCatalog.mergeAndCenter.shortcut.aria,
5090
+ title: `${spreadsheetCommandCatalog.mergeAndCenter.label}(${spreadsheetCommandCatalog.mergeAndCenter.shortcut.label})`,
5091
+ disabled: primaryDisabled,
5092
+ onClick: ()=>commands.mergeSelectedCells('merge-and-center'),
5093
+ children: [
5094
+ /*#__PURE__*/ jsx(Merge, {
5095
+ size: 19
5096
+ }),
5097
+ /*#__PURE__*/ jsx("span", {
5098
+ children: spreadsheetCommandCatalog.mergeAndCenter.label
5099
+ })
5100
+ ]
5101
+ }),
5102
+ /*#__PURE__*/ jsx("button", {
5103
+ ...triggerProps,
5104
+ className: `work-spreadsheet-ribbon-split-disclosure${open ? ' active' : ''}`,
5105
+ title: "更多合并方式",
5106
+ children: /*#__PURE__*/ jsx(ChevronDown, {
5107
+ size: 13,
5108
+ "aria-hidden": "true"
5109
+ })
5110
+ })
5111
+ ]
5112
+ }),
5113
+ children: (close)=>items.map(({ command, id, label, icon })=>/*#__PURE__*/ jsxs("button", {
5114
+ type: "button",
5115
+ role: "menuitem",
5116
+ tabIndex: -1,
5117
+ "aria-keyshortcuts": 'merge-and-center' === command ? spreadsheetCommandCatalog.mergeAndCenter.shortcut.aria : void 0,
5118
+ disabled: !can.mergeSelectedCells(command),
5119
+ onClick: ()=>{
5120
+ close();
5121
+ commands.mergeSelectedCells(command);
5122
+ },
5123
+ children: [
5124
+ /*#__PURE__*/ jsx("span", {
5125
+ className: "work-spreadsheet-ribbon-menu-item-icon",
5126
+ children: icon
5127
+ }),
5128
+ /*#__PURE__*/ jsx("span", {
5129
+ children: label
5130
+ })
5131
+ ]
5132
+ }, id))
5133
+ });
5134
+ }
5135
+ function SpreadsheetFreezePanesMenu({ active, can, commands, selection }) {
5136
+ const presets = [
5137
+ ...active ? [
5138
+ {
5139
+ preset: 'none',
5140
+ label: '取消冻结窗格',
5141
+ icon: /*#__PURE__*/ jsx(X, {
5142
+ size: 16
3835
5143
  })
5144
+ }
5145
+ ] : [],
5146
+ {
5147
+ preset: 'selection',
5148
+ label: spreadsheetFreezePanesSelectionLabel(selection),
5149
+ icon: /*#__PURE__*/ jsx(PanelsTopLeft, {
5150
+ size: 16
5151
+ })
5152
+ },
5153
+ {
5154
+ preset: 'topRow',
5155
+ label: '冻结首行',
5156
+ icon: /*#__PURE__*/ jsx(PanelTop, {
5157
+ size: 16
5158
+ })
5159
+ },
5160
+ {
5161
+ preset: 'firstColumn',
5162
+ label: '冻结首列',
5163
+ icon: /*#__PURE__*/ jsx(PanelLeft, {
5164
+ size: 16
5165
+ })
5166
+ }
5167
+ ];
5168
+ const disabled = !presets.some(({ preset })=>can.setFreezePanes(preset));
5169
+ return /*#__PURE__*/ jsx(Popover, {
5170
+ label: spreadsheetCommandCatalog.freezePanes.label,
5171
+ panelLabel: "冻结窗格选项",
5172
+ panelRole: "menu",
5173
+ portal: true,
5174
+ className: "work-spreadsheet-ribbon-menu-root",
5175
+ panelClassName: "work-office-context-menu work-spreadsheet-ribbon-menu",
5176
+ disabled: disabled,
5177
+ focusFirstOnOpen: true,
5178
+ onPanelKeyDown: moveOfficeMenuFocus,
5179
+ trigger: (triggerProps, { open })=>/*#__PURE__*/ jsxs("button", {
5180
+ ...triggerProps,
5181
+ className: `with-label work-spreadsheet-ribbon-menu-trigger${active || open ? ' active' : ''}`,
5182
+ "aria-pressed": active,
5183
+ title: active ? '冻结窗格(已启用)' : '冻结窗格',
5184
+ children: [
5185
+ /*#__PURE__*/ jsx(PanelsTopLeft, {
5186
+ size: 19
5187
+ }),
5188
+ /*#__PURE__*/ jsx("span", {
5189
+ children: spreadsheetCommandCatalog.freezePanes.label
5190
+ })
5191
+ ]
5192
+ }),
5193
+ children: (close)=>presets.map(({ preset, label, icon })=>/*#__PURE__*/ jsxs("button", {
5194
+ type: "button",
5195
+ role: "menuitem",
5196
+ tabIndex: -1,
5197
+ disabled: !can.setFreezePanes(preset),
5198
+ onClick: ()=>{
5199
+ close();
5200
+ commands.setFreezePanes(preset);
5201
+ },
5202
+ children: [
5203
+ /*#__PURE__*/ jsx("span", {
5204
+ className: "work-spreadsheet-ribbon-menu-item-icon",
5205
+ children: icon
5206
+ }),
5207
+ /*#__PURE__*/ jsx("span", {
5208
+ children: label
5209
+ })
5210
+ ]
5211
+ }, preset))
5212
+ });
5213
+ }
5214
+ function SpreadsheetRowsAndColumnsMenu({ can, commands }) {
5215
+ const items = [
5216
+ {
5217
+ id: spreadsheetCommandCatalog.insertRowsAbove.id,
5218
+ label: spreadsheetCommandCatalog.insertRowsAbove.label,
5219
+ icon: /*#__PURE__*/ jsx(BetweenHorizontalStart, {
5220
+ size: 16
5221
+ }),
5222
+ disabled: !can.insertSelectedStructure('row', 'before'),
5223
+ execute: ()=>commands.insertSelectedStructure('row', 'before')
5224
+ },
5225
+ {
5226
+ id: spreadsheetCommandCatalog.insertRowsBelow.id,
5227
+ label: spreadsheetCommandCatalog.insertRowsBelow.label,
5228
+ icon: /*#__PURE__*/ jsx(BetweenHorizontalEnd, {
5229
+ size: 16
5230
+ }),
5231
+ disabled: !can.insertSelectedStructure('row', 'after'),
5232
+ execute: ()=>commands.insertSelectedStructure('row', 'after')
5233
+ },
5234
+ {
5235
+ id: spreadsheetCommandCatalog.insertColumnsLeft.id,
5236
+ label: spreadsheetCommandCatalog.insertColumnsLeft.label,
5237
+ icon: /*#__PURE__*/ jsx(BetweenVerticalStart, {
5238
+ size: 16
5239
+ }),
5240
+ disabled: !can.insertSelectedStructure('column', 'before'),
5241
+ execute: ()=>commands.insertSelectedStructure('column', 'before')
5242
+ },
5243
+ {
5244
+ id: spreadsheetCommandCatalog.insertColumnsRight.id,
5245
+ label: spreadsheetCommandCatalog.insertColumnsRight.label,
5246
+ icon: /*#__PURE__*/ jsx(BetweenVerticalEnd, {
5247
+ size: 16
5248
+ }),
5249
+ disabled: !can.insertSelectedStructure('column', 'after'),
5250
+ execute: ()=>commands.insertSelectedStructure('column', 'after')
5251
+ },
5252
+ {
5253
+ id: spreadsheetCommandCatalog.deleteRows.id,
5254
+ label: spreadsheetCommandCatalog.deleteRows.label,
5255
+ icon: /*#__PURE__*/ jsx(Rows3, {
5256
+ size: 16
5257
+ }),
5258
+ danger: true,
5259
+ disabled: !can.deleteSelectedStructure('row'),
5260
+ execute: ()=>commands.deleteSelectedStructure('row')
5261
+ },
5262
+ {
5263
+ id: spreadsheetCommandCatalog.deleteColumns.id,
5264
+ label: spreadsheetCommandCatalog.deleteColumns.label,
5265
+ icon: /*#__PURE__*/ jsx(Columns3, {
5266
+ size: 16
5267
+ }),
5268
+ danger: true,
5269
+ disabled: !can.deleteSelectedStructure('column'),
5270
+ execute: ()=>commands.deleteSelectedStructure('column')
5271
+ }
5272
+ ];
5273
+ const disabled = items.every((item)=>item.disabled);
5274
+ return /*#__PURE__*/ jsx(Popover, {
5275
+ label: "行和列",
5276
+ panelLabel: "行和列选项",
5277
+ panelRole: "menu",
5278
+ portal: true,
5279
+ className: "work-spreadsheet-ribbon-menu-root",
5280
+ panelClassName: "work-office-context-menu work-spreadsheet-ribbon-menu",
5281
+ disabled: disabled,
5282
+ focusFirstOnOpen: true,
5283
+ onPanelKeyDown: moveOfficeMenuFocus,
5284
+ trigger: (triggerProps, { open })=>/*#__PURE__*/ jsxs("button", {
5285
+ ...triggerProps,
5286
+ className: `with-label work-spreadsheet-ribbon-menu-trigger${open ? ' active' : ''}`,
5287
+ title: "行和列",
5288
+ children: [
5289
+ /*#__PURE__*/ jsx(Rows3, {
5290
+ size: 19
5291
+ }),
5292
+ /*#__PURE__*/ jsx("span", {
5293
+ children: "行和列"
5294
+ })
5295
+ ]
5296
+ }),
5297
+ children: (close)=>items.map(({ id, label, icon, danger, disabled, execute })=>/*#__PURE__*/ jsxs("button", {
5298
+ type: "button",
5299
+ role: "menuitem",
5300
+ tabIndex: -1,
5301
+ className: danger ? 'danger' : void 0,
5302
+ disabled: disabled,
5303
+ onClick: ()=>{
5304
+ close();
5305
+ execute();
5306
+ },
5307
+ children: [
5308
+ /*#__PURE__*/ jsx("span", {
5309
+ className: "work-spreadsheet-ribbon-menu-item-icon",
5310
+ children: icon
5311
+ }),
5312
+ /*#__PURE__*/ jsx("span", {
5313
+ children: label
5314
+ })
5315
+ ]
5316
+ }, id))
5317
+ });
5318
+ }
5319
+ function SpreadsheetClearMenu({ can, commands }) {
5320
+ const items = [
5321
+ {
5322
+ mode: 'all',
5323
+ id: spreadsheetCommandCatalog.clearAll.id,
5324
+ label: spreadsheetCommandCatalog.clearAll.label,
5325
+ icon: /*#__PURE__*/ jsx(Trash2, {
5326
+ size: 16
5327
+ })
5328
+ },
5329
+ {
5330
+ mode: 'formats',
5331
+ id: spreadsheetCommandCatalog.clearFormats.id,
5332
+ label: spreadsheetCommandCatalog.clearFormats.label,
5333
+ icon: /*#__PURE__*/ jsx(Paintbrush, {
5334
+ size: 16
5335
+ })
5336
+ },
5337
+ {
5338
+ mode: 'contents',
5339
+ id: spreadsheetCommandCatalog.clearContents.id,
5340
+ label: spreadsheetCommandCatalog.clearContents.label,
5341
+ icon: /*#__PURE__*/ jsx(FileX2, {
5342
+ size: 16
5343
+ })
5344
+ },
5345
+ {
5346
+ mode: 'comments',
5347
+ id: spreadsheetCommandCatalog.clearComments.id,
5348
+ label: spreadsheetCommandCatalog.clearComments.label,
5349
+ icon: /*#__PURE__*/ jsx(MessageSquareX, {
5350
+ size: 16
5351
+ })
5352
+ },
5353
+ {
5354
+ mode: 'hyperlinks',
5355
+ id: spreadsheetCommandCatalog.clearHyperlinks.id,
5356
+ label: spreadsheetCommandCatalog.clearHyperlinks.label,
5357
+ icon: /*#__PURE__*/ jsx(Link2Off, {
5358
+ size: 16
3836
5359
  })
3837
5360
  }
5361
+ ];
5362
+ const disabled = items.every(({ mode })=>!can.clearSelectedCells(mode));
5363
+ return /*#__PURE__*/ jsx(Popover, {
5364
+ label: "清除",
5365
+ panelLabel: "清除选项",
5366
+ panelRole: "menu",
5367
+ portal: true,
5368
+ className: "work-spreadsheet-ribbon-menu-root",
5369
+ panelClassName: "work-office-context-menu work-spreadsheet-ribbon-menu",
5370
+ disabled: disabled,
5371
+ focusFirstOnOpen: true,
5372
+ onPanelKeyDown: moveOfficeMenuFocus,
5373
+ trigger: (triggerProps, { open })=>/*#__PURE__*/ jsxs("button", {
5374
+ ...triggerProps,
5375
+ className: `with-label work-spreadsheet-ribbon-menu-trigger${open ? ' active' : ''}`,
5376
+ title: "清除",
5377
+ children: [
5378
+ /*#__PURE__*/ jsx(Eraser, {
5379
+ size: 19
5380
+ }),
5381
+ /*#__PURE__*/ jsx("span", {
5382
+ children: "清除"
5383
+ })
5384
+ ]
5385
+ }),
5386
+ children: (close)=>items.map(({ mode, id, label, icon })=>/*#__PURE__*/ jsxs("button", {
5387
+ type: "button",
5388
+ role: "menuitem",
5389
+ tabIndex: -1,
5390
+ "aria-keyshortcuts": 'contents' === mode ? spreadsheetCommandCatalog.clearContents.shortcut.aria : void 0,
5391
+ disabled: !can.clearSelectedCells(mode),
5392
+ onClick: ()=>{
5393
+ close();
5394
+ commands.clearSelectedCells(mode);
5395
+ },
5396
+ children: [
5397
+ /*#__PURE__*/ jsx("span", {
5398
+ className: "work-spreadsheet-ribbon-menu-item-icon",
5399
+ children: icon
5400
+ }),
5401
+ /*#__PURE__*/ jsx("span", {
5402
+ children: label
5403
+ })
5404
+ ]
5405
+ }, id))
3838
5406
  });
3839
5407
  }
5408
+ function spreadsheetFormatPainterTitle(mode) {
5409
+ if ('locked' === mode) return '格式刷已锁定(再次点击或按 Escape 退出)';
5410
+ if ('once' === mode) return '格式刷已开启(选择目标区域,按 Escape 退出)';
5411
+ return '格式刷(单击应用一次,双击锁定连续应用)';
5412
+ }
3840
5413
  function SpreadsheetRibbonTool({ active, count, controlsId, icon, label, panel, onToggle }) {
3841
5414
  const hasCount = count > 0;
3842
5415
  return /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
@@ -8746,7 +10319,7 @@ function projectSpreadsheetKernelWorkbookOperations(previous, sheets, operations
8746
10319
  if ('data' !== operation.path[0]) continue;
8747
10320
  const row = operation.path[1];
8748
10321
  const column = operation.path[2];
8749
- if (!operation.id || !sheetsById.has(operation.id) || !isSpreadsheetCoordinateIndex(row, office_kernel_spreadsheet_protocol_OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) || !isSpreadsheetCoordinateIndex(column, 16384)) return null;
10322
+ if (!operation.id || !sheetsById.has(operation.id) || !isSpreadsheetCoordinateIndex(row, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) || !isSpreadsheetCoordinateIndex(column, 16384)) return null;
8750
10323
  const coordinate = {
8751
10324
  sheetId: operation.id,
8752
10325
  row,
@@ -8847,7 +10420,7 @@ function spreadsheetOperationsMayChangeCalculation(operations, workbook) {
8847
10420
  if (!workbook) return operation.path.length <= 3 || isInputCellPath(operation);
8848
10421
  const row = operation.path[1];
8849
10422
  const column = operation.path[2];
8850
- if (!operation.id || !isSpreadsheetCoordinateIndex(row, office_kernel_spreadsheet_protocol_OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) || !isSpreadsheetCoordinateIndex(column, 16384)) return true;
10423
+ if (!operation.id || !isSpreadsheetCoordinateIndex(row, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) || !isSpreadsheetCoordinateIndex(column, 16384)) return true;
8851
10424
  const previous = workbook.sourceState.sourceCells.get(sourceCellKey(operation.id, row, column));
8852
10425
  if (operation.path.length <= 3) {
8853
10426
  const current = spreadsheetInputCell('remove' === operation.op ? null : operation.value, row, column);
@@ -9108,7 +10681,7 @@ function sparseSpreadsheetCells(sheet, maximumCells) {
9108
10681
  return true;
9109
10682
  };
9110
10683
  if (sheet.data) {
9111
- if (sheet.data.length > office_kernel_spreadsheet_protocol_OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) return null;
10684
+ if (sheet.data.length > OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) return null;
9112
10685
  for(let row = 0; row < sheet.data.length; row += 1){
9113
10686
  const values = sheet.data[row];
9114
10687
  if (values) {
@@ -9121,7 +10694,7 @@ function sparseSpreadsheetCells(sheet, maximumCells) {
9121
10694
  }
9122
10695
  function spreadsheetInputCell(cell, row, column) {
9123
10696
  if (!cell || !cell.f && void 0 === cell.v && void 0 === cell.m) return null;
9124
- if (!Number.isSafeInteger(row) || row < 0 || row >= office_kernel_spreadsheet_protocol_OFFICE_KERNEL_SPREADSHEET_MAX_ROWS || !Number.isSafeInteger(column) || column < 0 || column >= 16384 || cell.f && Array.from(cell.f).length > SPREADSHEET_KERNEL_MAX_FORMULA_CHARACTERS) return;
10697
+ if (!Number.isSafeInteger(row) || row < 0 || row >= OFFICE_KERNEL_SPREADSHEET_MAX_ROWS || !Number.isSafeInteger(column) || column < 0 || column >= 16384 || cell.f && Array.from(cell.f).length > SPREADSHEET_KERNEL_MAX_FORMULA_CHARACTERS) return;
9125
10698
  const value = spreadsheetKernelValue(cell);
9126
10699
  if (!value) return;
9127
10700
  return {
@@ -9509,80 +11082,917 @@ function useSpreadsheetCalculation({ content, kernelWasmUrl, workbookRef }) {
9509
11082
  clearTimer(automaticTimerRef);
9510
11083
  clearTimer(fallbackTimerRef);
9511
11084
  }, []);
9512
- useEffect(()=>{
9513
- if (acceptedContentRef.current === content) {
9514
- acceptedContentRef.current = null;
9515
- return;
11085
+ useEffect(()=>{
11086
+ if (acceptedContentRef.current === content) {
11087
+ acceptedContentRef.current = null;
11088
+ return;
11089
+ }
11090
+ acceptedContentRef.current = null;
11091
+ resultOperationsRef.current = [];
11092
+ pendingOperationCancellationRef.current = false;
11093
+ synchronizeSnapshot(content, createSpreadsheetKernelWorkbook(content));
11094
+ }, [
11095
+ content,
11096
+ kernelWasmUrl,
11097
+ synchronizeSnapshot
11098
+ ]);
11099
+ const recalculate = useCallback((command)=>{
11100
+ clearTimer(automaticTimerRef);
11101
+ let snapshot = snapshotRef.current;
11102
+ if (!snapshot) {
11103
+ snapshot = createSpreadsheetKernelWorkbook(latestContentRef.current);
11104
+ snapshotRef.current = snapshot;
11105
+ }
11106
+ if (!snapshot) return void fallbackWithFortune(workbookRef.current, command);
11107
+ runCalculation(command, snapshot, documentRevisionRef.current);
11108
+ }, [
11109
+ runCalculation,
11110
+ workbookRef
11111
+ ]);
11112
+ const hasPendingResultPatches = useCallback(()=>resultOperationsRef.current.length > 0, []);
11113
+ return useMemo(()=>({
11114
+ hasPendingResultPatches,
11115
+ notifyWorkbookOperations,
11116
+ recalculate,
11117
+ synchronizeWorkbook
11118
+ }), [
11119
+ hasPendingResultPatches,
11120
+ notifyWorkbookOperations,
11121
+ recalculate,
11122
+ synchronizeWorkbook
11123
+ ]);
11124
+ }
11125
+ function scheduleIssueFallback(workbookRef, issues, active, revisionRef, timerRef) {
11126
+ if (!issues.length) return;
11127
+ clearTimer(timerRef);
11128
+ timerRef.current = setTimeout(()=>{
11129
+ timerRef.current = null;
11130
+ if (revisionRef.current !== active.revision || active.controller.signal.aborted) return;
11131
+ const workbook = workbookRef.current;
11132
+ if (!workbook) return;
11133
+ for (const issue of issues)workbook.calculateFormula(issue.cell.sheetId, {
11134
+ row: [
11135
+ issue.cell.row,
11136
+ issue.cell.row
11137
+ ],
11138
+ column: [
11139
+ issue.cell.column,
11140
+ issue.cell.column
11141
+ ]
11142
+ });
11143
+ }, 0);
11144
+ }
11145
+ function fallbackWithFortune(workbook, command) {
11146
+ if (!workbook) return;
11147
+ if ('workbook' === command.scope) workbook.calculateFormula();
11148
+ else workbook.calculateFormula(command.sheetId, command.range);
11149
+ }
11150
+ function clearTimer(timerRef) {
11151
+ if (null !== timerRef.current) clearTimeout(timerRef.current);
11152
+ timerRef.current = null;
11153
+ }
11154
+ function isCurrentCalculation(current, expected) {
11155
+ return current?.revision === expected.revision && current.documentRevision === expected.documentRevision && !expected.controller.signal.aborted;
11156
+ }
11157
+ function isAbortError(error) {
11158
+ return error instanceof DOMException && 'AbortError' === error.name;
11159
+ }
11160
+ function spreadsheetAutoFilterRange(sheet, selection) {
11161
+ if (sheet.isPivotTable || sheet.pivotTables?.length) return null;
11162
+ const normalized = normalizeAutoFilterSelection(selection);
11163
+ const range = normalized.row[0] !== normalized.row[1] ? normalized : spreadsheetCurrentRegion(sheet, normalized);
11164
+ if (!range || !validSpreadsheetAutoFilterRange(sheet, range)) return null;
11165
+ return range;
11166
+ }
11167
+ function spreadsheetAutoFilterHeaderColumn(sheet, selection) {
11168
+ const range = normalizedFilterSelection(sheet?.filter_select);
11169
+ if (!sheet || !range) return null;
11170
+ const row = spreadsheet_auto_filter_finiteIndex(selection.row_focus, selection.row[0]);
11171
+ const column = spreadsheet_auto_filter_finiteIndex(selection.column_focus, selection.column[0]);
11172
+ if (row !== range.row[0] || column < range.column[0] || column > range.column[1]) return null;
11173
+ return column;
11174
+ }
11175
+ function toggleSpreadsheetAutoFilter(content, sheetId, selection) {
11176
+ const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
11177
+ const sheet = content.sheets[sheetIndex];
11178
+ if (!sheet) return null;
11179
+ const nextSheet = sheet.filter_select ? spreadsheetSheetWithoutAutoFilter(sheet) : spreadsheetSheetWithAutoFilter(sheet, selection);
11180
+ if (!nextSheet) return null;
11181
+ const sheets = [
11182
+ ...content.sheets
11183
+ ];
11184
+ sheets[sheetIndex] = {
11185
+ ...nextSheet,
11186
+ luckysheet_select_save: [
11187
+ finiteSpreadsheetSelection(selection)
11188
+ ]
11189
+ };
11190
+ return {
11191
+ ...content,
11192
+ sheets
11193
+ };
11194
+ }
11195
+ function spreadsheetSheetWithAutoFilter(sheet, selection) {
11196
+ const range = spreadsheetAutoFilterRange(sheet, selection);
11197
+ if (!range) return null;
11198
+ return {
11199
+ ...sheet,
11200
+ filter: {},
11201
+ filter_select: {
11202
+ row: [
11203
+ ...range.row
11204
+ ],
11205
+ column: [
11206
+ ...range.column
11207
+ ]
11208
+ }
11209
+ };
11210
+ }
11211
+ function spreadsheetSheetWithoutAutoFilter(sheet) {
11212
+ const hiddenByFilter = spreadsheetFilterHiddenRows(sheet.filter);
11213
+ const rowhidden = {
11214
+ ...sheet.config?.rowhidden
11215
+ };
11216
+ for (const row of hiddenByFilter)delete rowhidden[row];
11217
+ const config = sheet.config ? {
11218
+ ...sheet.config,
11219
+ rowhidden
11220
+ } : void 0;
11221
+ const { filter: _filter, filter_select: _selection, ...remaining } = sheet;
11222
+ return {
11223
+ ...remaining,
11224
+ config
11225
+ };
11226
+ }
11227
+ function spreadsheetFilterHiddenRows(filter) {
11228
+ const rows = new Set();
11229
+ for (const value of Object.values(filter ?? {})){
11230
+ if (!value || 'object' != typeof value) continue;
11231
+ const rowhidden = value.rowhidden;
11232
+ if (rowhidden && 'object' == typeof rowhidden) for (const row of Object.keys(rowhidden))rows.add(row);
11233
+ }
11234
+ return rows;
11235
+ }
11236
+ function spreadsheetCurrentRegion(sheet, selection) {
11237
+ const cells = spreadsheetCellReader(sheet);
11238
+ const bounds = spreadsheetUsedBounds(sheet);
11239
+ let startRow = selection.row[0];
11240
+ let endRow = selection.row[1];
11241
+ let startColumn = selection.column[0];
11242
+ let endColumn = selection.column[1];
11243
+ if (startRow > bounds.lastRow || startColumn > bounds.lastColumn || !spreadsheet_auto_filter_spreadsheetCellHasContent(cells(startRow, startColumn))) return null;
11244
+ let changed = true;
11245
+ while(changed){
11246
+ changed = false;
11247
+ while(startColumn > 0 && spreadsheetColumnHasContent(cells, startColumn - 1, startRow, endRow)){
11248
+ startColumn -= 1;
11249
+ changed = true;
11250
+ }
11251
+ while(endColumn < bounds.lastColumn && spreadsheetColumnHasContent(cells, endColumn + 1, startRow, endRow)){
11252
+ endColumn += 1;
11253
+ changed = true;
11254
+ }
11255
+ while(startRow > 0 && spreadsheetRowHasContent(cells, startRow - 1, startColumn, endColumn)){
11256
+ startRow -= 1;
11257
+ changed = true;
11258
+ }
11259
+ while(endRow < bounds.lastRow && spreadsheetRowHasContent(cells, endRow + 1, startColumn, endColumn)){
11260
+ endRow += 1;
11261
+ changed = true;
11262
+ }
11263
+ }
11264
+ return {
11265
+ row: [
11266
+ startRow,
11267
+ endRow
11268
+ ],
11269
+ column: [
11270
+ startColumn,
11271
+ endColumn
11272
+ ]
11273
+ };
11274
+ }
11275
+ function validSpreadsheetAutoFilterRange(sheet, range) {
11276
+ if (range.row[1] <= range.row[0]) return false;
11277
+ const cells = spreadsheetCellReader(sheet);
11278
+ if (!spreadsheetRowHasContent(cells, range.row[0], range.column[0], range.column[1])) return false;
11279
+ let dataFound = false;
11280
+ for(let row = range.row[0] + 1; row <= range.row[1]; row += 1)if (spreadsheetRowHasContent(cells, row, range.column[0], range.column[1])) {
11281
+ dataFound = true;
11282
+ break;
11283
+ }
11284
+ return dataFound && !spreadsheetRangeIntersectsMerge(sheet, range);
11285
+ }
11286
+ function spreadsheetRangeIntersectsMerge(sheet, range) {
11287
+ return Object.values(sheet.config?.merge ?? {}).some((merge)=>{
11288
+ const endRow = merge.r + Math.max(merge.rs, 1) - 1;
11289
+ const endColumn = merge.c + Math.max(merge.cs, 1) - 1;
11290
+ return merge.r <= range.row[1] && endRow >= range.row[0] && merge.c <= range.column[1] && endColumn >= range.column[0];
11291
+ });
11292
+ }
11293
+ function spreadsheetRowHasContent(cellAt, row, startColumn, endColumn) {
11294
+ for(let column = startColumn; column <= endColumn; column += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
11295
+ return false;
11296
+ }
11297
+ function spreadsheetColumnHasContent(cellAt, column, startRow, endRow) {
11298
+ for(let row = startRow; row <= endRow; row += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
11299
+ return false;
11300
+ }
11301
+ function spreadsheet_auto_filter_spreadsheetCellHasContent(cell) {
11302
+ if (!cell) return false;
11303
+ return [
11304
+ cell.v,
11305
+ cell.m,
11306
+ cell.f
11307
+ ].some((value)=>null != value && '' !== value);
11308
+ }
11309
+ function spreadsheetCellReader(sheet) {
11310
+ if (sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
11311
+ const cells = new Map((sheet.celldata ?? []).map((cell)=>[
11312
+ `${cell.r}:${cell.c}`,
11313
+ cell.v
11314
+ ]));
11315
+ return (row, column)=>cells.get(`${row}:${column}`) ?? null;
11316
+ }
11317
+ function spreadsheetUsedBounds(sheet) {
11318
+ let lastRow = Math.max((sheet.data?.length ?? 1) - 1, 0);
11319
+ let lastColumn = Math.max(...sheet.data?.map((row)=>Math.max((row?.length ?? 1) - 1, 0)) ?? [
11320
+ 0
11321
+ ]);
11322
+ for (const cell of sheet.celldata ?? []){
11323
+ lastRow = Math.max(lastRow, cell.r);
11324
+ lastColumn = Math.max(lastColumn, cell.c);
11325
+ }
11326
+ return {
11327
+ lastColumn,
11328
+ lastRow
11329
+ };
11330
+ }
11331
+ function normalizeAutoFilterSelection(selection) {
11332
+ const startRow = spreadsheet_auto_filter_finiteIndex(selection.row[0], 0);
11333
+ const endRow = spreadsheet_auto_filter_finiteIndex(selection.row[1], startRow);
11334
+ const startColumn = spreadsheet_auto_filter_finiteIndex(selection.column[0], 0);
11335
+ const endColumn = spreadsheet_auto_filter_finiteIndex(selection.column[1], startColumn);
11336
+ return {
11337
+ row: [
11338
+ Math.min(startRow, endRow),
11339
+ Math.max(startRow, endRow)
11340
+ ],
11341
+ column: [
11342
+ Math.min(startColumn, endColumn),
11343
+ Math.max(startColumn, endColumn)
11344
+ ]
11345
+ };
11346
+ }
11347
+ function normalizedFilterSelection(selection) {
11348
+ if (!selection?.row?.length || !selection.column?.length) return null;
11349
+ return normalizeAutoFilterSelection(selection);
11350
+ }
11351
+ function spreadsheet_auto_filter_finiteIndex(value, fallback) {
11352
+ return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
11353
+ }
11354
+ function useSpreadsheetAutoFilter({ canvasRef, content, editable, mountRevision, onChange, selection, sheetId, workbook = null }) {
11355
+ const contentRef = useRef(content);
11356
+ const editableRef = useRef(editable);
11357
+ const onChangeRef = useRef(onChange);
11358
+ const selectionRef = useRef(selection);
11359
+ const sheetIdRef = useRef(sheetId);
11360
+ const workbookRef = useRef(workbook);
11361
+ const keyboardInvokerRef = useRef(null);
11362
+ const restoreInvokerFocusRef = useRef(false);
11363
+ const menuWasOpenRef = useRef(false);
11364
+ contentRef.current = content;
11365
+ editableRef.current = editable;
11366
+ onChangeRef.current = onChange;
11367
+ selectionRef.current = selection;
11368
+ sheetIdRef.current = sheetId;
11369
+ workbookRef.current = workbook;
11370
+ const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
11371
+ const active = Boolean(sheet?.filter_select);
11372
+ const canToggle = Boolean(editable && sheet && (active || spreadsheetAutoFilterRange(sheet, selection)));
11373
+ const canOpenMenu = Boolean(editable && sheet?.filter_select);
11374
+ const toggle = useCallback(()=>{
11375
+ if (!editableRef.current) return false;
11376
+ const next = toggleSpreadsheetAutoFilter(contentRef.current, sheetIdRef.current, currentSpreadsheetAutoFilterSelection(selectionRef.current, workbookRef.current));
11377
+ if (!next) return false;
11378
+ contentRef.current = next;
11379
+ onChangeRef.current(next);
11380
+ return true;
11381
+ }, []);
11382
+ const openMenu = useCallback(()=>{
11383
+ if (!editableRef.current) return false;
11384
+ const container = canvasRef.current;
11385
+ const activeSheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetIdRef.current);
11386
+ const column = spreadsheetAutoFilterHeaderColumn(activeSheet, currentSpreadsheetAutoFilterSelection(selectionRef.current, workbookRef.current));
11387
+ const startColumn = activeSheet?.filter_select?.column?.[0];
11388
+ if (!container || null === column || !Number.isFinite(startColumn)) return false;
11389
+ enhanceSpreadsheetAutoFilterSurface(container, activeSheet, keyboardInvokerRef.current);
11390
+ const triggers = [
11391
+ ...container.querySelectorAll('.luckysheet-filter-options')
11392
+ ];
11393
+ const trigger = triggers[column - Number(startColumn)];
11394
+ if (!trigger) return false;
11395
+ keyboardInvokerRef.current = trigger;
11396
+ restoreInvokerFocusRef.current = true;
11397
+ trigger.click();
11398
+ focusSpreadsheetAutoFilterMenu(container);
11399
+ return true;
11400
+ }, [
11401
+ canvasRef
11402
+ ]);
11403
+ const reserveAltKey = useCallback(()=>{
11404
+ if (!editableRef.current) return false;
11405
+ const activeSheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetIdRef.current);
11406
+ return null !== spreadsheetAutoFilterHeaderColumn(activeSheet, currentSpreadsheetAutoFilterSelection(selectionRef.current, workbookRef.current));
11407
+ }, []);
11408
+ useEffect(()=>{
11409
+ const container = canvasRef.current;
11410
+ if (!container || !editable) return;
11411
+ const enhance = ()=>{
11412
+ const activeSheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetIdRef.current);
11413
+ const menu = enhanceSpreadsheetAutoFilterSurface(container, activeSheet, keyboardInvokerRef.current);
11414
+ const menuOpen = Boolean(menu);
11415
+ if (menuWasOpenRef.current && !menuOpen && restoreInvokerFocusRef.current) {
11416
+ const invoker = keyboardInvokerRef.current;
11417
+ requestAnimationFrame(()=>{
11418
+ if (invoker?.isConnected) invoker.focus({
11419
+ preventScroll: true
11420
+ });
11421
+ });
11422
+ restoreInvokerFocusRef.current = false;
11423
+ }
11424
+ menuWasOpenRef.current = menuOpen;
11425
+ };
11426
+ const handlePointerDown = (event)=>{
11427
+ const trigger = spreadsheetAutoFilterTrigger(event.target);
11428
+ if (!trigger) return;
11429
+ keyboardInvokerRef.current = trigger;
11430
+ restoreInvokerFocusRef.current = false;
11431
+ };
11432
+ const handleKeyDown = (event)=>{
11433
+ const trigger = spreadsheetAutoFilterTrigger(event.target);
11434
+ if (trigger && ('Enter' === event.key || ' ' === event.key)) {
11435
+ event.preventDefault();
11436
+ event.stopPropagation();
11437
+ keyboardInvokerRef.current = trigger;
11438
+ restoreInvokerFocusRef.current = true;
11439
+ trigger.click();
11440
+ focusSpreadsheetAutoFilterMenu(container);
11441
+ return;
11442
+ }
11443
+ const menu = spreadsheetAutoFilterMenu(event.target);
11444
+ if (!menu) return;
11445
+ if ('Escape' === event.key) {
11446
+ event.preventDefault();
11447
+ event.stopPropagation();
11448
+ menu.querySelector('.button-default')?.click();
11449
+ return;
11450
+ }
11451
+ const target = event.target;
11452
+ if (('Enter' === event.key || ' ' === event.key) && target instanceof HTMLElement && !(target instanceof HTMLInputElement) && target.tabIndex >= 0) {
11453
+ event.preventDefault();
11454
+ event.stopPropagation();
11455
+ target.click();
11456
+ return;
11457
+ }
11458
+ if (('ArrowDown' === event.key || 'ArrowUp' === event.key) && !(target instanceof HTMLInputElement && 'checkbox' !== target.type)) {
11459
+ const focusable = spreadsheetAutoFilterMenuFocusable(menu);
11460
+ const current = focusable.indexOf(document.activeElement);
11461
+ if (!focusable.length) return;
11462
+ event.preventDefault();
11463
+ event.stopPropagation();
11464
+ const offset = 'ArrowDown' === event.key ? 1 : -1;
11465
+ const next = (Math.max(current, 0) + offset + focusable.length) % focusable.length;
11466
+ focusable[next]?.focus({
11467
+ preventScroll: true
11468
+ });
11469
+ }
11470
+ };
11471
+ enhance();
11472
+ container.addEventListener('pointerdown', handlePointerDown, true);
11473
+ container.addEventListener('keydown', handleKeyDown, true);
11474
+ if ("u" < typeof MutationObserver) return ()=>{
11475
+ container.removeEventListener('pointerdown', handlePointerDown, true);
11476
+ container.removeEventListener('keydown', handleKeyDown, true);
11477
+ };
11478
+ const observer = new MutationObserver(enhance);
11479
+ observer.observe(container, {
11480
+ childList: true,
11481
+ subtree: true
11482
+ });
11483
+ return ()=>{
11484
+ observer.disconnect();
11485
+ container.removeEventListener('pointerdown', handlePointerDown, true);
11486
+ container.removeEventListener('keydown', handleKeyDown, true);
11487
+ };
11488
+ }, [
11489
+ canvasRef,
11490
+ editable,
11491
+ mountRevision
11492
+ ]);
11493
+ const commandPort = useMemo(()=>({
11494
+ active,
11495
+ canOpenMenu,
11496
+ canToggle,
11497
+ openMenu,
11498
+ toggle
11499
+ }), [
11500
+ active,
11501
+ canOpenMenu,
11502
+ canToggle,
11503
+ openMenu,
11504
+ toggle
11505
+ ]);
11506
+ return {
11507
+ active,
11508
+ commandPort,
11509
+ reserveAltKey,
11510
+ status: active ? '自动筛选已开启;在表头按 Alt+向下箭头打开筛选菜单。' : ''
11511
+ };
11512
+ }
11513
+ function currentSpreadsheetAutoFilterSelection(fallback, workbook) {
11514
+ const live = workbook?.getSelection()?.at(-1);
11515
+ if (!live) return fallback;
11516
+ return {
11517
+ ...fallback,
11518
+ ...live,
11519
+ row: [
11520
+ ...live.row
11521
+ ],
11522
+ column: [
11523
+ ...live.column
11524
+ ]
11525
+ };
11526
+ }
11527
+ function enhanceSpreadsheetAutoFilterSurface(container, sheet, invoker) {
11528
+ const range = sheet?.filter_select;
11529
+ const startRow = range?.row?.[0];
11530
+ const startColumn = range?.column?.[0];
11531
+ const triggers = [
11532
+ ...container.querySelectorAll('.luckysheet-filter-options')
11533
+ ];
11534
+ for (const [index, trigger] of triggers.entries()){
11535
+ const column = Number(startColumn) + index;
11536
+ if (!Number.isFinite(column) || !Number.isFinite(startRow)) continue;
11537
+ const label = spreadsheetAutoFilterColumnLabel(sheet, Number(startRow), column);
11538
+ trigger.dataset.filterColumn = String(column);
11539
+ trigger.dataset.officeShortcuts = 'ignore';
11540
+ trigger.setAttribute('role', 'button');
11541
+ trigger.setAttribute('aria-label', `${label} 筛选`);
11542
+ trigger.setAttribute('aria-haspopup', 'dialog');
11543
+ trigger.setAttribute('aria-expanded', 'false');
11544
+ trigger.setAttribute('title', `${label} 筛选(Alt+↓)`);
11545
+ }
11546
+ const menu = container.querySelector('.fortune-filter-menu');
11547
+ if (!menu) return null;
11548
+ const trigger = invoker ?? triggers.find((candidate)=>candidate.matches(':focus'));
11549
+ const triggerLabel = trigger?.getAttribute('aria-label')?.replace(/\s*筛选$/, '');
11550
+ const label = triggerLabel || '列';
11551
+ menu.setAttribute('role', 'dialog');
11552
+ menu.dataset.officeShortcuts = 'ignore';
11553
+ menu.setAttribute('aria-label', `${label} 筛选`);
11554
+ menu.setAttribute('aria-modal', 'false');
11555
+ trigger?.setAttribute('aria-expanded', 'true');
11556
+ const search = menu.querySelector('.filtermenu-input-container input, input:not([type="checkbox"])');
11557
+ search?.setAttribute('aria-label', '搜索筛选值');
11558
+ for (const checkbox of menu.querySelectorAll('input[type="checkbox"]')){
11559
+ const item = checkbox.closest('.select-item');
11560
+ const itemLabel = item ? [
11561
+ ...item.children
11562
+ ].filter((child)=>child !== checkbox && !child.classList.contains('count') && !child.classList.contains('filter-caret')).map((child)=>child.textContent?.trim()).find(Boolean) : void 0;
11563
+ checkbox.setAttribute('aria-label', `显示 ${itemLabel || '筛选值'}`);
11564
+ }
11565
+ for (const action of menu.querySelectorAll('.luckysheet-cols-menuitem, .fortune-byvalue-btn, .button-basic')){
11566
+ action.setAttribute('role', 'button');
11567
+ const actionLabel = action.textContent?.replace(/\s+/g, ' ').trim();
11568
+ if (actionLabel) action.setAttribute('aria-label', actionLabel);
11569
+ }
11570
+ return menu;
11571
+ }
11572
+ function focusSpreadsheetAutoFilterMenu(container) {
11573
+ requestAnimationFrame(()=>{
11574
+ const menu = container.querySelector('.fortune-filter-menu');
11575
+ if (!menu) return;
11576
+ spreadsheetAutoFilterMenuFocusable(menu)[0]?.focus({
11577
+ preventScroll: true
11578
+ });
11579
+ });
11580
+ }
11581
+ function spreadsheetAutoFilterMenuFocusable(menu) {
11582
+ return [
11583
+ ...menu.querySelectorAll('.luckysheet-cols-menuitem, .fortune-byvalue-btn, .select-item[tabindex], input:not([disabled]), .button-basic')
11584
+ ].filter((element)=>element.tabIndex >= 0 && !element.hidden);
11585
+ }
11586
+ function spreadsheetAutoFilterTrigger(target) {
11587
+ return target instanceof Element ? target.closest('.luckysheet-filter-options') : null;
11588
+ }
11589
+ function spreadsheetAutoFilterMenu(target) {
11590
+ return target instanceof Element ? target.closest('.fortune-filter-menu') : null;
11591
+ }
11592
+ function spreadsheetAutoFilterColumnLabel(sheet, row, column) {
11593
+ const cell = sheet?.data ? sheet.data[row]?.[column] : sheet?.celldata?.find((candidate)=>candidate.r === row && candidate.c === column)?.v;
11594
+ const value = cell?.m ?? cell?.v;
11595
+ const label = null == value ? '' : String(value).trim();
11596
+ return label || `列 ${spreadsheetColumnName(column)}`;
11597
+ }
11598
+ function spreadsheetColumnName(column) {
11599
+ let value = Math.max(0, Math.floor(column)) + 1;
11600
+ let name = '';
11601
+ while(value > 0){
11602
+ value -= 1;
11603
+ name = String.fromCharCode(65 + value % 26) + name;
11604
+ value = Math.floor(value / 26);
11605
+ }
11606
+ return name;
11607
+ }
11608
+ const spreadsheetCellFormatAttributes = [
11609
+ 'bl',
11610
+ 'it',
11611
+ 'ff',
11612
+ 'fs',
11613
+ 'fc',
11614
+ 'ht',
11615
+ 'vt',
11616
+ 'tb',
11617
+ 'cl',
11618
+ 'un',
11619
+ 'tr',
11620
+ 'ct',
11621
+ 'bg'
11622
+ ];
11623
+ function captureSpreadsheetFormatPattern(cells) {
11624
+ const rowCount = cells.length;
11625
+ const columnCount = cells[0]?.length ?? 0;
11626
+ if (rowCount < 1 || columnCount < 1 || cells.some((row)=>row.length !== columnCount)) return null;
11627
+ return {
11628
+ cells: cells.map((row)=>row.map((cell)=>spreadsheetCellFormatSnapshot(cell))),
11629
+ columnCount,
11630
+ rowCount
11631
+ };
11632
+ }
11633
+ function spreadsheetFormatPainterTargetRange(target, pattern, bounds) {
11634
+ if (pattern.rowCount < 1 || pattern.columnCount < 1 || bounds.rowCount < 1 || bounds.columnCount < 1) return null;
11635
+ const normalized = normalizeSpreadsheetFormatPainterRange(target);
11636
+ if (!normalized) return null;
11637
+ const singleCell = normalized.row[0] === normalized.row[1] && normalized.column[0] === normalized.column[1];
11638
+ const startRow = clampIndex(normalized.row[0], bounds.rowCount);
11639
+ const startColumn = clampIndex(normalized.column[0], bounds.columnCount);
11640
+ const requestedEndRow = singleCell ? startRow + pattern.rowCount - 1 : normalized.row[1];
11641
+ const requestedEndColumn = singleCell ? startColumn + pattern.columnCount - 1 : normalized.column[1];
11642
+ return {
11643
+ row: [
11644
+ startRow,
11645
+ clampIndex(requestedEndRow, bounds.rowCount)
11646
+ ],
11647
+ column: [
11648
+ startColumn,
11649
+ clampIndex(requestedEndColumn, bounds.columnCount)
11650
+ ]
11651
+ };
11652
+ }
11653
+ function spreadsheetFormatPainterCellCount(range) {
11654
+ const normalized = normalizeSpreadsheetFormatPainterRange(range);
11655
+ if (!normalized) return 0;
11656
+ return (normalized.row[1] - normalized.row[0] + 1) * (normalized.column[1] - normalized.column[0] + 1);
11657
+ }
11658
+ function spreadsheetFormatPainterBatches(pattern, target) {
11659
+ const normalized = normalizeSpreadsheetFormatPainterRange(target);
11660
+ if (!normalized || pattern.rowCount < 1 || pattern.columnCount < 1) return [];
11661
+ const batches = [];
11662
+ for (const attribute of spreadsheetCellFormatAttributes){
11663
+ const rectangles = spreadsheetFormatRectangles(pattern, normalized, attribute);
11664
+ const groups = new Map();
11665
+ for (const rectangle of rectangles){
11666
+ const key = spreadsheetFormatValueKey(rectangle.value);
11667
+ const group = groups.get(key);
11668
+ if (group) group.ranges.push(rectangle.range);
11669
+ else groups.set(key, {
11670
+ ranges: [
11671
+ rectangle.range
11672
+ ],
11673
+ value: cloneSpreadsheetFormatValue(rectangle.value)
11674
+ });
11675
+ }
11676
+ for (const group of groups.values())batches.push({
11677
+ attribute,
11678
+ ranges: group.ranges,
11679
+ value: group.value
11680
+ });
11681
+ }
11682
+ return batches;
11683
+ }
11684
+ function spreadsheetCellFormatSnapshot(cell) {
11685
+ const snapshot = {};
11686
+ for (const attribute of spreadsheetCellFormatAttributes)snapshot[attribute] = 'ct' === attribute ? spreadsheetNumberFormatSnapshot(cell?.ct) : cloneSpreadsheetFormatValue(cell?.[attribute]);
11687
+ return snapshot;
11688
+ }
11689
+ function spreadsheetNumberFormatSnapshot(value) {
11690
+ const format = value?.fa?.trim();
11691
+ const type = value?.t?.trim();
11692
+ return {
11693
+ fa: format || 'General',
11694
+ t: type || 'g'
11695
+ };
11696
+ }
11697
+ function spreadsheetFormatRectangles(pattern, target, attribute) {
11698
+ const rectangles = [];
11699
+ let previous = new Map();
11700
+ for(let row = target.row[0]; row <= target.row[1]; row += 1){
11701
+ const runs = [];
11702
+ let startColumn = target.column[0];
11703
+ let value = spreadsheetPatternValue(pattern, target, row, startColumn, attribute);
11704
+ let key = spreadsheetFormatValueKey(value);
11705
+ for(let column = target.column[0] + 1; column <= target.column[1] + 1; column += 1){
11706
+ const nextValue = column <= target.column[1] ? spreadsheetPatternValue(pattern, target, row, column, attribute) : void 0;
11707
+ const nextKey = column <= target.column[1] ? spreadsheetFormatValueKey(nextValue) : '__end__';
11708
+ if (nextKey !== key) {
11709
+ runs.push({
11710
+ endColumn: column - 1,
11711
+ key,
11712
+ startColumn,
11713
+ value
11714
+ });
11715
+ startColumn = column;
11716
+ value = nextValue;
11717
+ key = nextKey;
11718
+ }
11719
+ }
11720
+ const current = new Map();
11721
+ for (const run of runs){
11722
+ const signature = `${run.startColumn}:${run.endColumn}:${run.key}`;
11723
+ const continuing = previous.get(signature);
11724
+ if (continuing) {
11725
+ continuing.range.row[1] = row;
11726
+ current.set(signature, continuing);
11727
+ continue;
11728
+ }
11729
+ const rectangle = {
11730
+ range: {
11731
+ row: [
11732
+ row,
11733
+ row
11734
+ ],
11735
+ column: [
11736
+ run.startColumn,
11737
+ run.endColumn
11738
+ ]
11739
+ },
11740
+ value: run.value
11741
+ };
11742
+ rectangles.push(rectangle);
11743
+ current.set(signature, rectangle);
11744
+ }
11745
+ previous = current;
11746
+ }
11747
+ return rectangles;
11748
+ }
11749
+ function spreadsheetPatternValue(pattern, target, row, column, attribute) {
11750
+ const sourceRow = (row - target.row[0]) % pattern.rowCount;
11751
+ const sourceColumn = (column - target.column[0]) % pattern.columnCount;
11752
+ return pattern.cells[sourceRow]?.[sourceColumn]?.[attribute];
11753
+ }
11754
+ function normalizeSpreadsheetFormatPainterRange(range) {
11755
+ const values = [
11756
+ ...range.row,
11757
+ ...range.column
11758
+ ];
11759
+ if (4 !== values.length || values.some((value)=>!Number.isFinite(value))) return null;
11760
+ return {
11761
+ row: [
11762
+ Math.max(0, Math.floor(Math.min(range.row[0], range.row[1]))),
11763
+ Math.max(0, Math.floor(Math.max(range.row[0], range.row[1])))
11764
+ ],
11765
+ column: [
11766
+ Math.max(0, Math.floor(Math.min(range.column[0], range.column[1]))),
11767
+ Math.max(0, Math.floor(Math.max(range.column[0], range.column[1])))
11768
+ ]
11769
+ };
11770
+ }
11771
+ function clampIndex(value, length) {
11772
+ return Math.max(0, Math.min(Math.floor(value), Math.floor(length) - 1));
11773
+ }
11774
+ function spreadsheetFormatValueKey(value) {
11775
+ if (void 0 === value) return 'undefined';
11776
+ if ('number' == typeof value && Number.isNaN(value)) return 'number:NaN';
11777
+ return `${typeof value}:${JSON.stringify(value)}`;
11778
+ }
11779
+ function cloneSpreadsheetFormatValue(value) {
11780
+ if (!value || 'object' != typeof value) return value;
11781
+ if (Array.isArray(value)) return value.map((item)=>cloneSpreadsheetFormatValue(item));
11782
+ return Object.fromEntries(Object.entries(value).map(([key, item])=>[
11783
+ key,
11784
+ cloneSpreadsheetFormatValue(item)
11785
+ ]));
11786
+ }
11787
+ const spreadsheetFormatPainterMaximumCells = 50000;
11788
+ function useSpreadsheetFormatPainter({ content, editable, onError, sourceRange, sourceSheetId, workbook }) {
11789
+ const contentRef = useRef(content);
11790
+ const editableRef = useRef(editable);
11791
+ const onErrorRef = useRef(onError);
11792
+ const sessionRef = useRef(null);
11793
+ const sourceRef = useRef({
11794
+ range: copySpreadsheetFormatPainterRange(sourceRange),
11795
+ sheetId: sourceSheetId
11796
+ });
11797
+ const workbookRef = useRef(workbook);
11798
+ const [mode, setMode] = useState(null);
11799
+ contentRef.current = content;
11800
+ editableRef.current = editable;
11801
+ onErrorRef.current = onError;
11802
+ sourceRef.current = {
11803
+ range: copySpreadsheetFormatPainterRange(sourceRange),
11804
+ sheetId: sourceSheetId
11805
+ };
11806
+ workbookRef.current = workbook;
11807
+ const clearSession = useCallback(()=>{
11808
+ sessionRef.current = null;
11809
+ setMode(null);
11810
+ }, []);
11811
+ useEffect(()=>{
11812
+ if (!editable || !workbook) clearSession();
11813
+ }, [
11814
+ clearSession,
11815
+ editable,
11816
+ workbook
11817
+ ]);
11818
+ const activate = useCallback((nextMode)=>{
11819
+ const activeWorkbook = workbookRef.current;
11820
+ const source = sourceRef.current;
11821
+ if (!editableRef.current || !activeWorkbook || !source.sheetId) return false;
11822
+ if (spreadsheetFormatPainterCellCount(source.range) > spreadsheetFormatPainterMaximumCells) {
11823
+ onErrorRef.current(`格式刷一次最多复制 ${spreadsheetFormatPainterMaximumCells.toLocaleString()} 个单元格,请缩小源区域后重试。`);
11824
+ return false;
11825
+ }
11826
+ try {
11827
+ const pattern = captureSpreadsheetFormatPattern(activeWorkbook.getCellsByRange(source.range, {
11828
+ id: source.sheetId
11829
+ }));
11830
+ if (!pattern || !spreadsheetFormatPatternMatchesRange(pattern, source.range)) {
11831
+ onErrorRef.current('无法读取完整的源格式,请重新选择后重试。');
11832
+ return false;
11833
+ }
11834
+ sessionRef.current = {
11835
+ mode: nextMode,
11836
+ pattern
11837
+ };
11838
+ setMode(nextMode);
11839
+ return true;
11840
+ } catch {
11841
+ onErrorRef.current('无法读取源格式,请重新选择后重试。');
11842
+ return false;
11843
+ }
11844
+ }, []);
11845
+ const applySelection = useCallback((target)=>{
11846
+ const activeWorkbook = workbookRef.current;
11847
+ const session = sessionRef.current;
11848
+ const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === target.sheetId);
11849
+ if (!editableRef.current || !activeWorkbook || !session || !sheet) return false;
11850
+ const rawTarget = spreadsheetSelectionRange(target);
11851
+ const targetRange = spreadsheetFormatPainterTargetRange(rawTarget, session.pattern, spreadsheetFormatPainterSheetBounds(sheet, rawTarget, session.pattern));
11852
+ if (!targetRange) return false;
11853
+ const targetKey = spreadsheetFormatPainterTargetKey(target.sheetId, targetRange);
11854
+ if (session.lastAppliedTargetKey === targetKey) return false;
11855
+ if (spreadsheetFormatPainterCellCount(targetRange) > spreadsheetFormatPainterMaximumCells) {
11856
+ onErrorRef.current(`格式刷一次最多应用到 ${spreadsheetFormatPainterMaximumCells.toLocaleString()} 个单元格,请缩小目标区域后重试。`);
11857
+ return false;
9516
11858
  }
9517
- acceptedContentRef.current = null;
9518
- resultOperationsRef.current = [];
9519
- pendingOperationCancellationRef.current = false;
9520
- synchronizeSnapshot(content, createSpreadsheetKernelWorkbook(content));
11859
+ const batches = spreadsheetFormatPainterBatches(session.pattern, targetRange);
11860
+ if (!batches.length) return false;
11861
+ session.lastAppliedTargetKey = targetKey;
11862
+ try {
11863
+ activeWorkbook.batchCallApis(batches.map((batch)=>({
11864
+ name: 'setCellFormatByRange',
11865
+ args: [
11866
+ batch.attribute,
11867
+ batch.value,
11868
+ batch.ranges,
11869
+ {
11870
+ id: target.sheetId
11871
+ }
11872
+ ]
11873
+ })));
11874
+ } catch {
11875
+ session.lastAppliedTargetKey = void 0;
11876
+ onErrorRef.current('无法应用格式,请缩小目标区域或重试。');
11877
+ return false;
11878
+ }
11879
+ if ('once' === session.mode) clearSession();
11880
+ return true;
9521
11881
  }, [
9522
- content,
9523
- kernelWasmUrl,
9524
- synchronizeSnapshot
11882
+ clearSession
9525
11883
  ]);
9526
- const recalculate = useCallback((command)=>{
9527
- clearTimer(automaticTimerRef);
9528
- let snapshot = snapshotRef.current;
9529
- if (!snapshot) {
9530
- snapshot = createSpreadsheetKernelWorkbook(latestContentRef.current);
9531
- snapshotRef.current = snapshot;
9532
- }
9533
- if (!snapshot) return void fallbackWithFortune(workbookRef.current, command);
9534
- runCalculation(command, snapshot, documentRevisionRef.current);
11884
+ const cancel = useCallback(()=>{
11885
+ if (!sessionRef.current) return false;
11886
+ clearSession();
11887
+ return true;
9535
11888
  }, [
9536
- runCalculation,
9537
- workbookRef
11889
+ clearSession
9538
11890
  ]);
9539
- const hasPendingResultPatches = useCallback(()=>resultOperationsRef.current.length > 0, []);
9540
- return useMemo(()=>({
9541
- hasPendingResultPatches,
9542
- notifyWorkbookOperations,
9543
- recalculate,
9544
- synchronizeWorkbook
11891
+ const commandPort = useMemo(()=>({
11892
+ active: null !== mode,
11893
+ activate,
11894
+ applySelection,
11895
+ canActivate: editable && Boolean(workbook && sourceSheetId),
11896
+ cancel,
11897
+ mode
9545
11898
  }), [
9546
- hasPendingResultPatches,
9547
- notifyWorkbookOperations,
9548
- recalculate,
9549
- synchronizeWorkbook
11899
+ activate,
11900
+ applySelection,
11901
+ cancel,
11902
+ editable,
11903
+ mode,
11904
+ sourceSheetId,
11905
+ workbook
9550
11906
  ]);
11907
+ return {
11908
+ commandPort,
11909
+ mode
11910
+ };
9551
11911
  }
9552
- function scheduleIssueFallback(workbookRef, issues, active, revisionRef, timerRef) {
9553
- if (!issues.length) return;
9554
- clearTimer(timerRef);
9555
- timerRef.current = setTimeout(()=>{
9556
- timerRef.current = null;
9557
- if (revisionRef.current !== active.revision || active.controller.signal.aborted) return;
9558
- const workbook = workbookRef.current;
9559
- if (!workbook) return;
9560
- for (const issue of issues)workbook.calculateFormula(issue.cell.sheetId, {
9561
- row: [
9562
- issue.cell.row,
9563
- issue.cell.row
9564
- ],
9565
- column: [
9566
- issue.cell.column,
9567
- issue.cell.column
9568
- ]
9569
- });
9570
- }, 0);
11912
+ function spreadsheetFormatPatternMatchesRange(pattern, range) {
11913
+ return pattern.rowCount === Math.abs(range.row[1] - range.row[0]) + 1 && pattern.columnCount === Math.abs(range.column[1] - range.column[0]) + 1;
9571
11914
  }
9572
- function fallbackWithFortune(workbook, command) {
9573
- if (!workbook) return;
9574
- if ('workbook' === command.scope) workbook.calculateFormula();
9575
- else workbook.calculateFormula(command.sheetId, command.range);
11915
+ function spreadsheetSelectionRange(target) {
11916
+ return {
11917
+ row: [
11918
+ ...target.selection.row
11919
+ ],
11920
+ column: [
11921
+ ...target.selection.column
11922
+ ]
11923
+ };
9576
11924
  }
9577
- function clearTimer(timerRef) {
9578
- if (null !== timerRef.current) clearTimeout(timerRef.current);
9579
- timerRef.current = null;
11925
+ function copySpreadsheetFormatPainterRange(range) {
11926
+ return {
11927
+ row: [
11928
+ ...range.row
11929
+ ],
11930
+ column: [
11931
+ ...range.column
11932
+ ]
11933
+ };
9580
11934
  }
9581
- function isCurrentCalculation(current, expected) {
9582
- return current?.revision === expected.revision && current.documentRevision === expected.documentRevision && !expected.controller.signal.aborted;
11935
+ function spreadsheetFormatPainterTargetKey(sheetId, range) {
11936
+ return `${sheetId}:${range.row[0]}:${range.row[1]}:${range.column[0]}:${range.column[1]}`;
9583
11937
  }
9584
- function isAbortError(error) {
9585
- return error instanceof DOMException && 'AbortError' === error.name;
11938
+ function spreadsheetFormatPainterSheetBounds(sheet, target, pattern) {
11939
+ const requestedRowCount = Math.max(target.row[0], target.row[1]) + pattern.rowCount;
11940
+ const requestedColumnCount = Math.max(target.column[0], target.column[1]) + pattern.columnCount;
11941
+ return {
11942
+ rowCount: positiveInteger(sheet.row) ?? Math.max(sheet.data?.length ?? 0, requestedRowCount),
11943
+ columnCount: positiveInteger(sheet.column) ?? Math.max(...sheet.data?.map((row)=>row?.length ?? 0) ?? [
11944
+ 0
11945
+ ], requestedColumnCount)
11946
+ };
11947
+ }
11948
+ function positiveInteger(value) {
11949
+ return 'number' == typeof value && Number.isFinite(value) && value > 0 ? Math.floor(value) : null;
11950
+ }
11951
+ function useSpreadsheetCollaborationPresenceProjection({ content, workbook }) {
11952
+ const participants = useOfficeRemoteParticipants();
11953
+ useEffect(()=>{
11954
+ if (!workbook) return;
11955
+ const projected = spreadsheetPresenceProjection(content, participants);
11956
+ if (projected.length) workbook.addPresences(projected);
11957
+ return ()=>{
11958
+ if (!projected.length) return;
11959
+ workbook.removePresences(projected.map(({ userId, username })=>({
11960
+ userId,
11961
+ username
11962
+ })));
11963
+ };
11964
+ }, [
11965
+ content,
11966
+ participants,
11967
+ workbook
11968
+ ]);
11969
+ }
11970
+ function spreadsheetPresenceProjection(content, participants) {
11971
+ return participants.flatMap((participant)=>{
11972
+ const location = participant.location;
11973
+ if (location?.kind !== 'spreadsheet') return [];
11974
+ const sheet = content.sheets.find((candidate)=>candidate.id === location.sheetId && 1 !== candidate.hide);
11975
+ if (!sheet) return [];
11976
+ const selection = location.activeCell ?? {
11977
+ row: location.ranges[0]?.startRow ?? -1,
11978
+ column: location.ranges[0]?.startColumn ?? -1
11979
+ };
11980
+ const rowCount = Math.max(sheet.row ?? 60, sheet.data?.length ?? 0);
11981
+ const columnCount = Math.max(sheet.column ?? 26, sheet.data?.reduce((maximum, row)=>Math.max(maximum, row?.length ?? 0), 0) ?? 0);
11982
+ if (selection.row < 0 || selection.column < 0 || selection.row >= rowCount || selection.column >= columnCount) return [];
11983
+ return [
11984
+ {
11985
+ sheetId: location.sheetId,
11986
+ username: participant.actor.name,
11987
+ userId: `a3s-office:${participant.presenceId}`,
11988
+ color: officePresenceColor(participant),
11989
+ selection: {
11990
+ r: selection.row,
11991
+ c: selection.column
11992
+ }
11993
+ }
11994
+ ];
11995
+ });
9586
11996
  }
9587
11997
  const MAXIMUM_BUFFERED_SPREADSHEET_OPERATIONS = 10001;
9588
11998
  function useSpreadsheetWorkbookSync(content) {
@@ -9621,10 +12031,173 @@ function useSpreadsheetWorkbookSync(content) {
9621
12031
  takeOperations
9622
12032
  };
9623
12033
  }
12034
+ function useSpreadsheetCollaboration({ initialContent, onChange, session }) {
12035
+ const sessionRef = useRef(session);
12036
+ if (sessionRef.current !== session) throw new Error('SpreadsheetEditor collaboration sessions cannot be replaced while mounted. Remount the editor for another shared workbook.');
12037
+ const viewStateRef = useRef(spreadsheetCollaborationViewState(initialContent));
12038
+ const [content, setContent] = useState(()=>spreadsheetContentWithCollaborationViewState(readWorkOfficeSpreadsheetCollaboration(session), viewStateRef.current));
12039
+ const contentRef = useRef(content);
12040
+ const bindingRef = useRef(void 0);
12041
+ const [historyRevision, refreshHistory] = useState(0);
12042
+ const onChangeRef = useRef(onChange);
12043
+ onChangeRef.current = onChange;
12044
+ contentRef.current = content;
12045
+ useEffect(()=>{
12046
+ const binding = createWorkOfficeSpreadsheetCollaborationBinding(session);
12047
+ bindingRef.current = binding;
12048
+ const project = (sharedContent)=>{
12049
+ const projected = spreadsheetContentWithCollaborationViewState(sharedContent, viewStateRef.current);
12050
+ contentRef.current = projected;
12051
+ setContent(projected);
12052
+ return projected;
12053
+ };
12054
+ const unsubscribeContent = binding.subscribe(({ content: next })=>{
12055
+ onChangeRef.current(project(next));
12056
+ });
12057
+ const unsubscribeError = binding.subscribeError((error)=>{
12058
+ queueMicrotask(()=>{
12059
+ throw error;
12060
+ });
12061
+ });
12062
+ const unsubscribeHistory = binding.subscribeHistory(()=>refreshHistory((value)=>value + 1));
12063
+ project(binding.content());
12064
+ return ()=>{
12065
+ unsubscribeContent();
12066
+ unsubscribeError();
12067
+ unsubscribeHistory();
12068
+ if (bindingRef.current === binding) bindingRef.current = void 0;
12069
+ binding.destroy();
12070
+ };
12071
+ }, [
12072
+ session
12073
+ ]);
12074
+ const commit = useCallback((next)=>{
12075
+ updateSpreadsheetCollaborationViewState(viewStateRef.current, next);
12076
+ const previous = contentRef.current;
12077
+ const binding = bindingRef.current;
12078
+ if (binding?.replace(previous, next)) {
12079
+ contentRef.current = spreadsheetContentWithCollaborationViewState(binding.content(), viewStateRef.current);
12080
+ return;
12081
+ }
12082
+ const projected = spreadsheetContentWithCollaborationViewState(binding?.content() ?? previous, viewStateRef.current);
12083
+ contentRef.current = projected;
12084
+ setContent(projected);
12085
+ }, []);
12086
+ const view = useMemo(()=>({
12087
+ activateSheet: (sheetId)=>{
12088
+ viewStateRef.current.activeSheetId = sheetId;
12089
+ },
12090
+ select: (sheetId, selection)=>{
12091
+ viewStateRef.current.selections.set(sheetId, finiteSpreadsheetSelection(selection));
12092
+ },
12093
+ setZoom: (sheetId, zoomRatio)=>{
12094
+ viewStateRef.current.zoomBySheet.set(sheetId, zoomRatio);
12095
+ }
12096
+ }), []);
12097
+ const history = useMemo(()=>({
12098
+ canRedo: bindingRef.current?.canRedo() ?? false,
12099
+ canUndo: bindingRef.current?.canUndo() ?? false,
12100
+ redo: ()=>bindingRef.current?.redo() ?? false,
12101
+ undo: ()=>bindingRef.current?.undo() ?? false
12102
+ }), [
12103
+ historyRevision
12104
+ ]);
12105
+ return {
12106
+ content,
12107
+ history,
12108
+ onChange: commit,
12109
+ readOnly: 'edit' !== session.mode,
12110
+ view
12111
+ };
12112
+ }
12113
+ function spreadsheetCollaborationViewState(content) {
12114
+ const selections = new Map();
12115
+ const zoomBySheet = new Map();
12116
+ for (const sheet of content.sheets){
12117
+ if (!sheet.id) continue;
12118
+ const selection = sheet.luckysheet_select_save?.at(-1);
12119
+ if (selection) selections.set(sheet.id, finiteSpreadsheetSelection(selection));
12120
+ if (void 0 !== sheet.zoomRatio) zoomBySheet.set(sheet.id, sheet.zoomRatio);
12121
+ }
12122
+ return {
12123
+ activeSheetId: content.sheets.find((sheet)=>1 === sheet.status)?.id ?? content.sheets.find((sheet)=>1 !== sheet.hide)?.id,
12124
+ selections,
12125
+ zoomBySheet
12126
+ };
12127
+ }
12128
+ function updateSpreadsheetCollaborationViewState(view, content) {
12129
+ pruneSpreadsheetCollaborationViewState(view, content);
12130
+ const active = content.sheets.find((sheet)=>1 === sheet.status);
12131
+ if (active?.id) view.activeSheetId = active.id;
12132
+ for (const sheet of content.sheets){
12133
+ if (!sheet.id) continue;
12134
+ const selection = sheet.luckysheet_select_save?.at(-1);
12135
+ if (selection) view.selections.set(sheet.id, finiteSpreadsheetSelection(selection));
12136
+ if (void 0 !== sheet.zoomRatio) view.zoomBySheet.set(sheet.id, sheet.zoomRatio);
12137
+ }
12138
+ }
12139
+ function spreadsheetContentWithCollaborationViewState(content, view) {
12140
+ pruneSpreadsheetCollaborationViewState(view, content);
12141
+ const visibleIds = new Set(content.sheets.filter((sheet)=>1 !== sheet.hide && sheet.id).map((sheet)=>sheet.id));
12142
+ const activeSheetId = (view.activeSheetId && visibleIds.has(view.activeSheetId) ? view.activeSheetId : content.sheets.find((sheet)=>1 !== sheet.hide)?.id) ?? content.sheets[0]?.id;
12143
+ if (activeSheetId) view.activeSheetId = activeSheetId;
12144
+ return {
12145
+ ...content,
12146
+ sheets: content.sheets.map((sheet)=>{
12147
+ const id = sheet.id;
12148
+ const selection = id ? view.selections.get(id) : void 0;
12149
+ const zoomRatio = id ? view.zoomBySheet.get(id) : void 0;
12150
+ return {
12151
+ ...sheet,
12152
+ status: id === activeSheetId ? 1 : 0,
12153
+ ...selection ? {
12154
+ luckysheet_select_save: [
12155
+ selection
12156
+ ]
12157
+ } : {},
12158
+ ...void 0 === zoomRatio ? {} : {
12159
+ zoomRatio
12160
+ }
12161
+ };
12162
+ })
12163
+ };
12164
+ }
12165
+ function pruneSpreadsheetCollaborationViewState(view, content) {
12166
+ const sheetIds = new Set(content.sheets.flatMap((sheet)=>sheet.id ? [
12167
+ sheet.id
12168
+ ] : []));
12169
+ for (const sheetId of view.selections.keys())if (!sheetIds.has(sheetId)) view.selections.delete(sheetId);
12170
+ for (const sheetId of view.zoomBySheet.keys())if (!sheetIds.has(sheetId)) view.zoomBySheet.delete(sheetId);
12171
+ }
9624
12172
  const spreadsheetFocusRetryFrames = 12;
9625
12173
  const spreadsheetFocusObservationMs = 5000;
9626
12174
  const spreadsheetFocusCleanups = new WeakMap();
9627
- function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已自动保存', fileActions, onChange, onAgentRequest }) {
12175
+ function SpreadsheetEditor(props) {
12176
+ if (props.collaboration) return /*#__PURE__*/ jsx(CollaborativeSpreadsheetEditor, {
12177
+ ...props,
12178
+ collaboration: props.collaboration
12179
+ });
12180
+ return /*#__PURE__*/ jsx(SpreadsheetEditorSurface, {
12181
+ ...props
12182
+ });
12183
+ }
12184
+ function CollaborativeSpreadsheetEditor(props) {
12185
+ const { collaboration } = props;
12186
+ const shared = useSpreadsheetCollaboration({
12187
+ initialContent: props.content,
12188
+ onChange: props.onChange,
12189
+ session: collaboration
12190
+ });
12191
+ return /*#__PURE__*/ jsx(SpreadsheetEditorSurface, {
12192
+ ...props,
12193
+ content: shared.content,
12194
+ collaborationHistory: shared.history,
12195
+ collaborationView: shared.view,
12196
+ onChange: shared.onChange,
12197
+ preview: props.preview || shared.readOnly
12198
+ });
12199
+ }
12200
+ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHistory, collaborationView, kernelWasmUrl, preview, saveStatus = '已自动保存', fileActions, onChange, onAgentRequest }) {
9628
12201
  const materializedContent = useMemo(()=>refreshSpreadsheetPivotTables(content), [
9629
12202
  content
9630
12203
  ]);
@@ -9633,6 +12206,7 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9633
12206
  const previewRef = useRef(preview);
9634
12207
  const spreadsheetRootRef = useRef(null);
9635
12208
  const spreadsheetCanvasRef = useRef(null);
12209
+ const editorFocusOrigin = useOfficeEditorFocusOrigin();
9636
12210
  const workbookRef = useRef(null);
9637
12211
  const spreadsheetZoomRef = useRef(100);
9638
12212
  const [workbookInstance, setWorkbookInstance] = useState(null);
@@ -9651,6 +12225,7 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9651
12225
  const [panel, setPanel] = useState(null);
9652
12226
  const panelTriggerRef = useRef(null);
9653
12227
  const [selectionState, setSelectionState] = useState(null);
12228
+ const formatPainterSelectionHandlerRef = useRef((_sheetId, _selection)=>void 0);
9654
12229
  const [contextMenu, setContextMenu] = useState(null);
9655
12230
  const officeDialog = useOfficeDialog();
9656
12231
  const [findOpen, setFindOpen] = useState(false);
@@ -9663,14 +12238,15 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9663
12238
  const focusedSheetIdRef = useRef(null);
9664
12239
  contentRef.current = materializedContent;
9665
12240
  previewRef.current = preview;
9666
- const history = useOfficeHistory({
12241
+ const controlledHistory = useOfficeHistory({
9667
12242
  content,
9668
12243
  onChange: (next)=>{
9669
12244
  const liveSelection = workbookRef.current?.getSelection()?.at(-1);
9670
12245
  onChange(spreadsheetContentWithSelection(next, selectionState?.sheetId ?? activeSheetIdRef.current, liveSelection ?? selectionState?.selection));
9671
12246
  },
9672
- sameValue: sameSpreadsheetHistoryContent
12247
+ sameValue: collaborationHistory ? ignoreSpreadsheetControlledHistory : sameSpreadsheetHistoryContent
9673
12248
  });
12249
+ const history = collaborationHistory ?? controlledHistory;
9674
12250
  const conditionalStylesBySheet = useMemo(()=>new Map(materializedContent.sheets.flatMap((sheet)=>sheet.id ? [
9675
12251
  [
9676
12252
  sheet.id,
@@ -9691,12 +12267,23 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9691
12267
  activeSheetId
9692
12268
  ]);
9693
12269
  useEffect(()=>{
9694
- if (!activeSheetId || focusedSheetIdRef.current === activeSheetId) return;
12270
+ if (preview || !activeSheetId || focusedSheetIdRef.current === activeSheetId) return;
12271
+ const initialFocus = null === focusedSheetIdRef.current;
12272
+ if (initialFocus && !autoFocus) {
12273
+ focusedSheetIdRef.current = activeSheetId;
12274
+ return;
12275
+ }
9695
12276
  focusedSheetIdRef.current = activeSheetId;
9696
- const frame = requestAnimationFrame(()=>focusSpreadsheetGrid(spreadsheetCanvasRef.current));
12277
+ const frame = requestAnimationFrame(()=>focusSpreadsheetGrid(spreadsheetCanvasRef.current, {
12278
+ focusOrigin: initialFocus ? editorFocusOrigin : document.activeElement,
12279
+ forceInitial: !initialFocus
12280
+ }));
9697
12281
  return ()=>cancelAnimationFrame(frame);
9698
12282
  }, [
9699
- activeSheetId
12283
+ activeSheetId,
12284
+ autoFocus,
12285
+ editorFocusOrigin,
12286
+ preview
9700
12287
  ]);
9701
12288
  useEffect(()=>{
9702
12289
  const container = spreadsheetCanvasRef.current;
@@ -9742,14 +12329,17 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9742
12329
  const workbookHooks = useMemo(()=>({
9743
12330
  afterActivateSheet: (id)=>{
9744
12331
  activeSheetIdRef.current = id;
12332
+ collaborationView?.activateSheet(id);
9745
12333
  if (previewRef.current) setPreviewActiveSheetId(id);
9746
12334
  setSelectionState(null);
9747
12335
  },
9748
12336
  afterSelectionChange: (sheetId, selection)=>{
12337
+ collaborationView?.select(sheetId, selection);
9749
12338
  setSelectionState({
9750
12339
  sheetId,
9751
12340
  selection
9752
12341
  });
12342
+ formatPainterSelectionHandlerRef.current(sheetId, selection);
9753
12343
  },
9754
12344
  beforeUpdateCell: (row, column)=>{
9755
12345
  const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === activeSheetIdRef.current);
@@ -9782,6 +12372,7 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9782
12372
  if (cell?.ps) drawSpreadsheetCommentMarker(context, cellInfo);
9783
12373
  }
9784
12374
  }), [
12375
+ collaborationView,
9785
12376
  conditionalStylesBySheet
9786
12377
  ]);
9787
12378
  const conditionalFormatKey = content.sheets.map((sheet)=>`${sheet.id}:${JSON.stringify(sheet.luckysheet_conditionformat_save ?? [])}`).join('|');
@@ -9842,15 +12433,93 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9842
12433
  const toolbarSheetId = selectionState?.sheetId ?? activeSheetId;
9843
12434
  const toolbarSheet = workbookSheets.find((sheet)=>sheet.id === toolbarSheetId);
9844
12435
  const toolbarSelection = finiteSpreadsheetSelection(selectionState?.selection ?? toolbarSheet?.luckysheet_select_save?.at(-1));
12436
+ const spreadsheetPresenceLocation = useMemo(()=>{
12437
+ if (!toolbarSheetId) return null;
12438
+ const liveSelections = workbookRef.current?.getSelection();
12439
+ const ranges = (liveSelections?.length ? liveSelections : [
12440
+ toolbarSelection
12441
+ ]).slice(0, 64).map((selection)=>({
12442
+ startRow: Math.min(selection.row[0] ?? 0, selection.row[1] ?? 0),
12443
+ startColumn: Math.min(selection.column[0] ?? 0, selection.column[1] ?? 0),
12444
+ endRow: Math.max(selection.row[0] ?? 0, selection.row[1] ?? 0),
12445
+ endColumn: Math.max(selection.column[0] ?? 0, selection.column[1] ?? 0)
12446
+ }));
12447
+ const activeCell = {
12448
+ row: toolbarSelection.row_focus ?? toolbarSelection.row[0] ?? 0,
12449
+ column: toolbarSelection.column_focus ?? toolbarSelection.column[0] ?? 0
12450
+ };
12451
+ return {
12452
+ kind: 'spreadsheet',
12453
+ sheetId: toolbarSheetId,
12454
+ ranges,
12455
+ activeCell
12456
+ };
12457
+ }, [
12458
+ selectionState,
12459
+ toolbarSelection,
12460
+ toolbarSheetId
12461
+ ]);
12462
+ useOfficePublishPresenceLocation(spreadsheetPresenceLocation);
12463
+ useSpreadsheetCollaborationPresenceProjection({
12464
+ content: materializedContent,
12465
+ workbook: workbookInstance
12466
+ });
9845
12467
  const toolbarCell = spreadsheetCellAt(toolbarSheet, toolbarSelection.row_focus ?? toolbarSelection.row[0], toolbarSelection.column_focus ?? toolbarSelection.column[0]);
9846
12468
  const selectedRange = spreadsheetSingleRange(toolbarSelection);
9847
12469
  const multipleCellsSelected = selectedRange.row[0] !== selectedRange.row[1] || selectedRange.column[0] !== selectedRange.column[1];
9848
12470
  const selectionSummary = multipleCellsSelected ? spreadsheetSelectionSummary(toolbarSheet, toolbarSelection) : null;
12471
+ const { commandPort: formatPainter, mode: formatPainterMode } = useSpreadsheetFormatPainter({
12472
+ content: materializedContent,
12473
+ editable: !preview,
12474
+ onError: (message)=>showToast(message, 'error'),
12475
+ sourceRange: selectedRange,
12476
+ sourceSheetId: toolbarSheetId,
12477
+ workbook: workbookInstance
12478
+ });
12479
+ const { active: autoFilterActive, commandPort: autoFilter, reserveAltKey: reserveAutoFilterAltKey, status: autoFilterStatus } = useSpreadsheetAutoFilter({
12480
+ canvasRef: spreadsheetCanvasRef,
12481
+ content: materializedContent,
12482
+ editable: !preview,
12483
+ mountRevision: workbookMountRevision,
12484
+ onChange: (next)=>{
12485
+ contentRef.current = next;
12486
+ onChange(next);
12487
+ },
12488
+ selection: toolbarSelection,
12489
+ sheetId: toolbarSheetId,
12490
+ workbook: workbookInstance
12491
+ });
12492
+ const currentClipboardSelection = ()=>{
12493
+ if (previewRef.current) return null;
12494
+ const sheetId = selectionState?.sheetId ?? activeSheetIdRef.current;
12495
+ const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetId);
12496
+ const selection = selectionState?.selection ?? sheet?.luckysheet_select_save?.at(-1);
12497
+ if (!selection) return null;
12498
+ const range = spreadsheetSingleRange(selection);
12499
+ const maximumCells = (range.row[1] - range.row[0] + 1) * (range.column[1] - range.column[0] + 1);
12500
+ return spreadsheetAgentSelection(contentRef.current, sheetId, selection, maximumCells);
12501
+ };
12502
+ const runSpreadsheetClipboardCopy = (cut)=>{
12503
+ const selection = currentClipboardSelection();
12504
+ const commands = spreadsheetCommandsRef.current;
12505
+ if (!selection || cut && !commands) return false;
12506
+ copySpreadsheetSelection(browserSpreadsheetClipboard, selection.clipboard, cut).then((copied)=>{
12507
+ if (copied && cut && !commands?.clearSelectedCells()) showToast('选区已复制,但无法清除原内容。', 'error');
12508
+ });
12509
+ return true;
12510
+ };
12511
+ const runSpreadsheetClipboardPaste = ()=>{
12512
+ const commands = spreadsheetCommandsRef.current;
12513
+ if (!commands) return false;
12514
+ pasteSpreadsheetSelection(browserSpreadsheetClipboard, commands.pasteCells);
12515
+ return true;
12516
+ };
9849
12517
  const activateReadOnlySpreadsheetSheet = useCallback((sheetId)=>{
9850
12518
  if (!previewRef.current) return false;
9851
12519
  const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetId && 1 !== candidate.hide);
9852
12520
  if (!sheet) return false;
9853
12521
  activeSheetIdRef.current = sheetId;
12522
+ collaborationView?.activateSheet(sheetId);
9854
12523
  setSelectionState(null);
9855
12524
  setPreviewActiveSheetId(sheetId);
9856
12525
  try {
@@ -9861,11 +12530,22 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9861
12530
  return false;
9862
12531
  }
9863
12532
  return true;
9864
- }, []);
12533
+ }, [
12534
+ collaborationView
12535
+ ]);
9865
12536
  const spreadsheetExtensions = useMemo(createSpreadsheetEditorExtensions, []);
9866
12537
  const spreadsheetEditor = useOfficeEditorRuntime({
9867
12538
  activeSheetId,
12539
+ autoFilter,
9868
12540
  calculation,
12541
+ clipboard: {
12542
+ canCopySelection: !preview && Boolean(toolbarSheet),
12543
+ canCutSelection: !preview && Boolean(toolbarSheet && workbookInstance),
12544
+ canPasteSelection: !preview && Boolean(toolbarSheet && workbookInstance),
12545
+ copySelection: ()=>runSpreadsheetClipboardCopy(false),
12546
+ cutSelection: ()=>runSpreadsheetClipboardCopy(true),
12547
+ pasteSelection: runSpreadsheetClipboardPaste
12548
+ },
9869
12549
  content: contentRef.current,
9870
12550
  editable: !preview,
9871
12551
  fallbackRange: selectedRange,
@@ -9875,6 +12555,7 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9875
12555
  if (formulaBar) formulaBar.textContent = null == value ? '' : String(value);
9876
12556
  }
9877
12557
  },
12558
+ formatPainter,
9878
12559
  history,
9879
12560
  onChange: (next)=>{
9880
12561
  contentRef.current = next;
@@ -9890,7 +12571,59 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9890
12571
  }, spreadsheetExtensions);
9891
12572
  const spreadsheetCommands = spreadsheetEditor.commands;
9892
12573
  spreadsheetCommandsRef.current = spreadsheetCommands;
12574
+ formatPainterSelectionHandlerRef.current = (sheetId, selection)=>{
12575
+ spreadsheetCommandsRef.current?.applyFormatPainter({
12576
+ sheetId,
12577
+ selection
12578
+ });
12579
+ };
9893
12580
  const spreadsheetCan = spreadsheetEditor.can();
12581
+ const navigateToSpreadsheetParticipant = useCallback((participant)=>{
12582
+ const location = participant.location;
12583
+ const workbook = workbookRef.current;
12584
+ if (!workbook || location?.kind !== 'spreadsheet') return false;
12585
+ const sheet = materializedContent.sheets.find((candidate)=>candidate.id === location.sheetId && 1 !== candidate.hide);
12586
+ if (!sheet) return false;
12587
+ const ranges = location.ranges.filter((range)=>spreadsheetPresenceRangeExists(sheet, range)).map((range)=>({
12588
+ row: [
12589
+ range.startRow,
12590
+ range.endRow
12591
+ ],
12592
+ column: [
12593
+ range.startColumn,
12594
+ range.endColumn
12595
+ ]
12596
+ }));
12597
+ if (!ranges.length) return false;
12598
+ const activeCell = location.activeCell ?? {
12599
+ row: ranges[0]?.row[0] ?? 0,
12600
+ column: ranges[0]?.column[0] ?? 0
12601
+ };
12602
+ if (!spreadsheetPresenceCellExists(sheet, activeCell)) return false;
12603
+ try {
12604
+ activeSheetIdRef.current = location.sheetId;
12605
+ collaborationView?.activateSheet(location.sheetId);
12606
+ if (previewRef.current) setPreviewActiveSheetId(location.sheetId);
12607
+ workbook.activateSheet({
12608
+ id: location.sheetId
12609
+ });
12610
+ workbook.setSelection(ranges, {
12611
+ id: location.sheetId
12612
+ });
12613
+ workbook.scroll({
12614
+ targetRow: activeCell.row,
12615
+ targetColumn: activeCell.column
12616
+ });
12617
+ } catch {
12618
+ return false;
12619
+ }
12620
+ requestAnimationFrame(()=>focusSpreadsheetGrid(spreadsheetCanvasRef.current));
12621
+ return true;
12622
+ }, [
12623
+ collaborationView,
12624
+ materializedContent.sheets
12625
+ ]);
12626
+ useOfficeCollaborationLocationNavigator(navigateToSpreadsheetParticipant);
9894
12627
  const restoreSpreadsheetGridFocus = useCallback(()=>focusSpreadsheetGrid(spreadsheetCanvasRef.current), []);
9895
12628
  const openSpreadsheetFind = useCallback(()=>{
9896
12629
  setFindOpen(true);
@@ -9938,6 +12671,8 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9938
12671
  const next = stepOfficeZoom(current, direction);
9939
12672
  if (next === current) return;
9940
12673
  spreadsheetZoomRef.current = next;
12674
+ const sheetId = activeSheetIdRef.current;
12675
+ if (sheetId) collaborationView?.setZoom(sheetId, next / 100);
9941
12676
  if (previewRef.current) setPreviewZoom(next);
9942
12677
  else spreadsheetCommandsRef.current?.setZoom(next);
9943
12678
  };
@@ -9949,17 +12684,12 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9949
12684
  const handleSpreadsheetEditingEscape = (event)=>{
9950
12685
  if ('Escape' === event.key && isSpreadsheetCellEditingTarget(event.target)) requestAnimationFrame(()=>focusSpreadsheetGrid(spreadsheetCanvasRef.current));
9951
12686
  };
9952
- const currentClipboardSelection = ()=>{
9953
- if (previewRef.current) return null;
9954
- const sheetId = selectionState?.sheetId ?? activeSheetIdRef.current;
9955
- const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetId);
9956
- const selection = selectionState?.selection ?? sheet?.luckysheet_select_save?.at(-1);
9957
- if (!selection) return null;
9958
- const range = spreadsheetSingleRange(selection);
9959
- const maximumCells = (range.row[1] - range.row[0] + 1) * (range.column[1] - range.column[0] + 1);
9960
- return spreadsheetAgentSelection(contentRef.current, sheetId, selection, maximumCells);
9961
- };
9962
12687
  const handleSpreadsheetKeyDownCapture = (event)=>{
12688
+ if ('Alt' === event.key && !event.repeat && event.target instanceof Element && event.target.closest('.fortune-sheet-overlay') && reserveAutoFilterAltKey()) {
12689
+ event.preventDefault();
12690
+ event.stopPropagation();
12691
+ return;
12692
+ }
9963
12693
  if ((event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey && 'f' === event.key.toLocaleLowerCase()) {
9964
12694
  event.preventDefault();
9965
12695
  event.stopPropagation();
@@ -9967,14 +12697,6 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
9967
12697
  return;
9968
12698
  }
9969
12699
  handleSpreadsheetEditingEscape(event);
9970
- const handled = runSpreadsheetClipboardShortcut(event.nativeEvent, {
9971
- clipboard: browserSpreadsheetClipboard,
9972
- clearSelectedCells: spreadsheetCommands.clearSelectedCells,
9973
- pasteCells: spreadsheetCommands.pasteCells,
9974
- readSelectionText: ()=>currentClipboardSelection()?.clipboard ?? null,
9975
- restoreFocus: restoreSpreadsheetGridFocus
9976
- });
9977
- if (handled) event.stopPropagation();
9978
12700
  };
9979
12701
  const handleSpreadsheetCopy = (event, cut)=>{
9980
12702
  if (isSpreadsheetNativeTextUndoTarget(event.target)) return;
@@ -10051,6 +12773,9 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
10051
12773
  return /*#__PURE__*/ jsxs("section", {
10052
12774
  ref: spreadsheetRootRef,
10053
12775
  className: `work-spreadsheet-editor ${preview ? 'preview' : ''}`,
12776
+ "data-auto-filter": autoFilterActive ? 'active' : void 0,
12777
+ "data-format-painter": formatPainterMode ?? void 0,
12778
+ "data-freeze-panes": toolbarSheet?.frozen ? 'active' : void 0,
10054
12779
  "aria-label": "表格工作区",
10055
12780
  onKeyDownCapture: handleSpreadsheetKeyDownCapture,
10056
12781
  onCopyCapture: (event)=>handleSpreadsheetCopy(event, false),
@@ -10066,13 +12791,16 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
10066
12791
  }),
10067
12792
  !preview && /*#__PURE__*/ jsx(SpreadsheetEditorRibbon, {
10068
12793
  activeTab: ribbonTab,
12794
+ autoFilterActive: autoFilterActive,
10069
12795
  can: spreadsheetCan,
10070
12796
  commands: spreadsheetRibbonCommands,
10071
12797
  content: content,
10072
12798
  fileActions: fileActions,
10073
12799
  findOpen: findOpen,
12800
+ formatPainterMode: formatPainterMode,
12801
+ freezePanesActive: Boolean(toolbarSheet?.frozen),
12802
+ freezePanesSelection: toolbarSelection,
10074
12803
  gridLinesVisible: gridLinesVisible,
10075
- multipleCellsSelected: multipleCellsSelected,
10076
12804
  panelId: panelId,
10077
12805
  onOpenFind: openSpreadsheetFind,
10078
12806
  onTabChange: (tab)=>{
@@ -10087,6 +12815,16 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
10087
12815
  panel: panel,
10088
12816
  toolbarCell: toolbarCell
10089
12817
  }),
12818
+ /*#__PURE__*/ jsx("output", {
12819
+ className: "sr-only",
12820
+ "aria-live": "polite",
12821
+ "aria-atomic": "true",
12822
+ children: [
12823
+ spreadsheetFormatPainterStatus(formatPainterMode),
12824
+ autoFilterStatus,
12825
+ spreadsheetFreezePanesStatus(toolbarSheet?.frozen)
12826
+ ].filter(Boolean).join(' ')
12827
+ }),
10090
12828
  /*#__PURE__*/ jsxs("div", {
10091
12829
  className: "work-spreadsheet-workspace",
10092
12830
  children: [
@@ -10184,6 +12922,8 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
10184
12922
  outputLabel: "表格缩放比例",
10185
12923
  sliderLabel: "表格缩放",
10186
12924
  onChange: (nextZoom)=>{
12925
+ const sheetId = activeSheetIdRef.current;
12926
+ if (sheetId) collaborationView?.setZoom(sheetId, nextZoom / 100);
10187
12927
  if (preview) setPreviewZoom(nextZoom);
10188
12928
  else spreadsheetCommands.setZoom(nextZoom);
10189
12929
  }
@@ -10255,24 +12995,36 @@ function SpreadsheetEditor({ content, kernelWasmUrl, preview, saveStatus = '已
10255
12995
  ]
10256
12996
  });
10257
12997
  }
10258
- function focusSpreadsheetGrid(container) {
12998
+ function focusSpreadsheetGrid(container, { focusOrigin = document.activeElement, forceInitial = true } = {}) {
10259
12999
  if (!container) return;
10260
13000
  spreadsheetFocusCleanups.get(container)?.();
10261
13001
  let remainingFrames = spreadsheetFocusRetryFrames;
10262
13002
  let lastFocusedTarget = null;
10263
- const commandTrigger = document.activeElement;
13003
+ const commandTrigger = focusOrigin;
10264
13004
  const initialFocusTarget = spreadsheetGridFocusTarget(container);
10265
13005
  const initialFocusTargetReady = spreadsheetGridFocusTargetReady(initialFocusTarget);
10266
13006
  let focusObserver = null;
10267
13007
  let focusObservationTimeout = null;
10268
13008
  let focusOutHandler = null;
13009
+ let pointerDownHandler = null;
13010
+ let tabKeyHandler = null;
13011
+ let focusRestorationStopped = false;
10269
13012
  const stopObservingFocusTarget = ()=>{
13013
+ focusRestorationStopped = true;
10270
13014
  focusObserver?.disconnect();
10271
13015
  focusObserver = null;
10272
13016
  if (focusOutHandler) {
10273
13017
  container.removeEventListener('focusout', focusOutHandler);
10274
13018
  focusOutHandler = null;
10275
13019
  }
13020
+ if (pointerDownHandler) {
13021
+ document.removeEventListener('pointerdown', pointerDownHandler, true);
13022
+ pointerDownHandler = null;
13023
+ }
13024
+ if (tabKeyHandler) {
13025
+ document.removeEventListener('keydown', tabKeyHandler, true);
13026
+ tabKeyHandler = null;
13027
+ }
10276
13028
  if (null !== focusObservationTimeout) {
10277
13029
  window.clearTimeout(focusObservationTimeout);
10278
13030
  focusObservationTimeout = null;
@@ -10280,6 +13032,7 @@ function focusSpreadsheetGrid(container) {
10280
13032
  if (spreadsheetFocusCleanups.get(container) === stopObservingFocusTarget) spreadsheetFocusCleanups.delete(container);
10281
13033
  };
10282
13034
  const restoreFocus = (force)=>{
13035
+ if (focusRestorationStopped) return;
10283
13036
  const activeElement = document.activeElement;
10284
13037
  if (!force && isSpreadsheetCellEditingTarget(activeElement)) return void stopObservingFocusTarget();
10285
13038
  if (!force && activeElement !== document.body && activeElement !== document.documentElement && activeElement !== commandTrigger && activeElement !== lastFocusedTarget) return void stopObservingFocusTarget();
@@ -10299,6 +13052,14 @@ function focusSpreadsheetGrid(container) {
10299
13052
  requestAnimationFrame(()=>restoreFocus(false));
10300
13053
  };
10301
13054
  container.addEventListener('focusout', focusOutHandler);
13055
+ pointerDownHandler = (event)=>{
13056
+ if (event.target instanceof Node && !container.contains(event.target)) stopObservingFocusTarget();
13057
+ };
13058
+ tabKeyHandler = (event)=>{
13059
+ if ('Tab' === event.key) stopObservingFocusTarget();
13060
+ };
13061
+ document.addEventListener('pointerdown', pointerDownHandler, true);
13062
+ document.addEventListener('keydown', tabKeyHandler, true);
10302
13063
  if ("u" > typeof MutationObserver) {
10303
13064
  focusObserver = new MutationObserver(()=>{
10304
13065
  const focusTarget = spreadsheetGridFocusTarget(container);
@@ -10319,7 +13080,7 @@ function focusSpreadsheetGrid(container) {
10319
13080
  }
10320
13081
  focusObservationTimeout = window.setTimeout(stopObservingFocusTarget, spreadsheetFocusObservationMs);
10321
13082
  spreadsheetFocusCleanups.set(container, stopObservingFocusTarget);
10322
- restoreFocus(true);
13083
+ restoreFocus(forceInitial);
10323
13084
  }
10324
13085
  function spreadsheetCommandsWithGridFocus(commands, restoreFocus) {
10325
13086
  const afterSuccessfulCommand = (command)=>(...args)=>{
@@ -10329,13 +13090,31 @@ function spreadsheetCommandsWithGridFocus(commands, restoreFocus) {
10329
13090
  };
10330
13091
  return {
10331
13092
  ...commands,
13093
+ activateFormatPainter: afterSuccessfulCommand(commands.activateFormatPainter),
13094
+ cancelFormatPainter: afterSuccessfulCommand(commands.cancelFormatPainter),
13095
+ clearSelectedCells: afterSuccessfulCommand(commands.clearSelectedCells),
13096
+ copySelection: afterSuccessfulCommand(commands.copySelection),
13097
+ cutSelection: afterSuccessfulCommand(commands.cutSelection),
13098
+ deleteSelectedStructure: afterSuccessfulCommand(commands.deleteSelectedStructure),
13099
+ insertSelectedStructure: afterSuccessfulCommand(commands.insertSelectedStructure),
13100
+ mergeSelectedCells: afterSuccessfulCommand(commands.mergeSelectedCells),
13101
+ pasteSelection: afterSuccessfulCommand(commands.pasteSelection),
10332
13102
  redo: afterSuccessfulCommand(commands.redo),
10333
13103
  setCellFormat: afterSuccessfulCommand(commands.setCellFormat),
13104
+ setFreezePanes: afterSuccessfulCommand(commands.setFreezePanes),
10334
13105
  setGridLines: afterSuccessfulCommand(commands.setGridLines),
10335
- toggleCellMerge: afterSuccessfulCommand(commands.toggleCellMerge),
13106
+ toggleAutoFilter: afterSuccessfulCommand(commands.toggleAutoFilter),
10336
13107
  undo: afterSuccessfulCommand(commands.undo)
10337
13108
  };
10338
13109
  }
13110
+ function ignoreSpreadsheetControlledHistory() {
13111
+ return true;
13112
+ }
13113
+ function spreadsheetFormatPainterStatus(mode) {
13114
+ if ('locked' === mode) return '格式刷已锁定,可连续选择目标区域;再次点击格式刷或按 Escape 退出。';
13115
+ if ('once' === mode) return '格式刷已开启,请选择一个目标区域;按 Escape 退出。';
13116
+ return '';
13117
+ }
10339
13118
  function spreadsheetGridFocusTarget(container) {
10340
13119
  return container?.querySelector('.fortune-sheet-overlay') ?? container?.querySelector('.fortune-cell-area') ?? null;
10341
13120
  }
@@ -10390,6 +13169,20 @@ function spreadsheetGridFocusTargetReady(target) {
10390
13169
  const bounds = target.getBoundingClientRect();
10391
13170
  return bounds.width > 0 && bounds.height > 0;
10392
13171
  }
13172
+ function spreadsheetPresenceRangeExists(sheet, range) {
13173
+ const { columns, rows } = spreadsheetPresenceSheetSize(sheet);
13174
+ return range.startRow >= 0 && range.startColumn >= 0 && range.endRow < rows && range.endColumn < columns;
13175
+ }
13176
+ function spreadsheetPresenceCellExists(sheet, cell) {
13177
+ const { columns, rows } = spreadsheetPresenceSheetSize(sheet);
13178
+ return cell.row >= 0 && cell.column >= 0 && cell.row < rows && cell.column < columns;
13179
+ }
13180
+ function spreadsheetPresenceSheetSize(sheet) {
13181
+ return {
13182
+ rows: Math.max(sheet.row ?? 60, sheet.data?.length ?? 0),
13183
+ columns: Math.max(sheet.column ?? 26, sheet.data?.reduce((maximum, row)=>Math.max(maximum, row?.length ?? 0), 0) ?? 0)
13184
+ };
13185
+ }
10393
13186
  function spreadsheetSelectionSummaryText(summary) {
10394
13187
  const parts = [
10395
13188
  `计数 ${summary.nonEmptyCount}`