editor-svg 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintrc +46 -0
  3. package/.prettierrc +8 -0
  4. package/AGENTS.md +186 -0
  5. package/CHANGELOG.md +2543 -0
  6. package/CLAUDE.md +110 -0
  7. package/LICENSE +21 -0
  8. package/README.md +110 -0
  9. package/cypress/e2e/control/checkbox.cy.ts +46 -0
  10. package/cypress/e2e/control/select.cy.ts +56 -0
  11. package/cypress/e2e/control/text.cy.ts +43 -0
  12. package/cypress/e2e/editor.cy.ts +67 -0
  13. package/cypress/e2e/menus/block.cy.ts +38 -0
  14. package/cypress/e2e/menus/checkbox.cy.ts +33 -0
  15. package/cypress/e2e/menus/codeblock.cy.ts +34 -0
  16. package/cypress/e2e/menus/date.cy.ts +28 -0
  17. package/cypress/e2e/menus/format.cy.ts +40 -0
  18. package/cypress/e2e/menus/hyperlink.cy.ts +39 -0
  19. package/cypress/e2e/menus/image.cy.ts +25 -0
  20. package/cypress/e2e/menus/latex.cy.ts +34 -0
  21. package/cypress/e2e/menus/pagebreak.cy.ts +21 -0
  22. package/cypress/e2e/menus/painter.cy.ts +53 -0
  23. package/cypress/e2e/menus/print.cy.ts +25 -0
  24. package/cypress/e2e/menus/row.cy.ts +103 -0
  25. package/cypress/e2e/menus/search.cy.ts +112 -0
  26. package/cypress/e2e/menus/separator.cy.ts +32 -0
  27. package/cypress/e2e/menus/table.cy.ts +25 -0
  28. package/cypress/e2e/menus/text.cy.ts +304 -0
  29. package/cypress/e2e/menus/title.cy.ts +43 -0
  30. package/cypress/e2e/menus/undoRedo.cy.ts +49 -0
  31. package/cypress/e2e/menus/watermark.cy.ts +64 -0
  32. package/cypress/fixtures/example.json +3 -0
  33. package/cypress/fixtures/test.png +0 -0
  34. package/cypress/global.d.ts +13 -0
  35. package/cypress/support/commands.ts +5 -0
  36. package/cypress/support/e2e.ts +1 -0
  37. package/cypress/tsconfig.json +21 -0
  38. package/cypress.config.ts +10 -0
  39. package/docs/.vitepress/config.ts +191 -0
  40. package/docs/.vitepress/theme/components/DeepWikiBadge.vue +21 -0
  41. package/docs/.vitepress/theme/components/ZreadBadge.vue +21 -0
  42. package/docs/.vitepress/theme/index.ts +27 -0
  43. package/docs/en/guide/api-common.md +49 -0
  44. package/docs/en/guide/api-instance.md +34 -0
  45. package/docs/en/guide/command-execute.md +1167 -0
  46. package/docs/en/guide/command-get.md +355 -0
  47. package/docs/en/guide/contextmenu-custom.md +44 -0
  48. package/docs/en/guide/contextmenu-internal.md +61 -0
  49. package/docs/en/guide/eventbus.md +260 -0
  50. package/docs/en/guide/i18n.md +112 -0
  51. package/docs/en/guide/listener.md +126 -0
  52. package/docs/en/guide/option.md +214 -0
  53. package/docs/en/guide/override.md +57 -0
  54. package/docs/en/guide/plugin-custom.md +25 -0
  55. package/docs/en/guide/plugin-internal.md +125 -0
  56. package/docs/en/guide/schema.md +237 -0
  57. package/docs/en/guide/shortcut-custom.md +22 -0
  58. package/docs/en/guide/shortcut-internal.md +189 -0
  59. package/docs/en/guide/start.md +97 -0
  60. package/docs/en/index.md +43 -0
  61. package/docs/guide/api-common.md +49 -0
  62. package/docs/guide/api-instance.md +34 -0
  63. package/docs/guide/command-execute.md +1157 -0
  64. package/docs/guide/command-get.md +353 -0
  65. package/docs/guide/contextmenu-custom.md +44 -0
  66. package/docs/guide/contextmenu-internal.md +61 -0
  67. package/docs/guide/eventbus.md +260 -0
  68. package/docs/guide/i18n.md +111 -0
  69. package/docs/guide/listener.md +126 -0
  70. package/docs/guide/option.md +214 -0
  71. package/docs/guide/override.md +57 -0
  72. package/docs/guide/plugin-custom.md +25 -0
  73. package/docs/guide/plugin-internal.md +125 -0
  74. package/docs/guide/schema.md +237 -0
  75. package/docs/guide/shortcut-custom.md +22 -0
  76. package/docs/guide/shortcut-internal.md +189 -0
  77. package/docs/guide/start.md +97 -0
  78. package/docs/index.md +43 -0
  79. package/docs/public/favicon.png +0 -0
  80. package/favicon.png +0 -0
  81. package/index.html +435 -0
  82. package/package.json +55 -0
  83. package/pnpm-lock.yaml +4113 -0
  84. package/scripts/release.js +41 -0
  85. package/scripts/verifyCommit.js +19 -0
  86. package/src/assets/images/alignment.svg +1 -0
  87. package/src/assets/images/arrow-left.svg +1 -0
  88. package/src/assets/images/arrow-right.svg +1 -0
  89. package/src/assets/images/block.svg +1 -0
  90. package/src/assets/images/bold.svg +1 -0
  91. package/src/assets/images/catalog.svg +1 -0
  92. package/src/assets/images/center.svg +1 -0
  93. package/src/assets/images/checkbox.svg +1 -0
  94. package/src/assets/images/close.svg +1 -0
  95. package/src/assets/images/codeblock.svg +1 -0
  96. package/src/assets/images/color.svg +1 -0
  97. package/src/assets/images/control.svg +1 -0
  98. package/src/assets/images/date.svg +1 -0
  99. package/src/assets/images/exit-fullscreen.svg +1 -0
  100. package/src/assets/images/format.svg +1 -0
  101. package/src/assets/images/highlight.svg +1 -0
  102. package/src/assets/images/hyperlink.svg +1 -0
  103. package/src/assets/images/image.svg +1 -0
  104. package/src/assets/images/italic.svg +1 -0
  105. package/src/assets/images/justify.svg +7 -0
  106. package/src/assets/images/latex.svg +1 -0
  107. package/src/assets/images/left.svg +1 -0
  108. package/src/assets/images/line-dash-dot-dot.svg +1 -0
  109. package/src/assets/images/line-dash-dot.svg +1 -0
  110. package/src/assets/images/line-dash-large-gap.svg +1 -0
  111. package/src/assets/images/line-dash-small-gap.svg +1 -0
  112. package/src/assets/images/line-dot.svg +1 -0
  113. package/src/assets/images/line-double.svg +1 -0
  114. package/src/assets/images/line-single.svg +1 -0
  115. package/src/assets/images/line-wavy.svg +1 -0
  116. package/src/assets/images/list.svg +1 -0
  117. package/src/assets/images/option.svg +1 -0
  118. package/src/assets/images/page-break.svg +1 -0
  119. package/src/assets/images/page-mode.svg +1 -0
  120. package/src/assets/images/page-scale-add.svg +1 -0
  121. package/src/assets/images/page-scale-minus.svg +1 -0
  122. package/src/assets/images/painter.svg +1 -0
  123. package/src/assets/images/paper-direction.svg +1 -0
  124. package/src/assets/images/paper-margin.svg +1 -0
  125. package/src/assets/images/paper-size.svg +1 -0
  126. package/src/assets/images/print.svg +1 -0
  127. package/src/assets/images/radio.svg +4 -0
  128. package/src/assets/images/redo.svg +1 -0
  129. package/src/assets/images/request-fullscreen.svg +1 -0
  130. package/src/assets/images/right.svg +1 -0
  131. package/src/assets/images/row-margin.svg +1 -0
  132. package/src/assets/images/search.svg +1 -0
  133. package/src/assets/images/separator.svg +1 -0
  134. package/src/assets/images/signature-undo.svg +1 -0
  135. package/src/assets/images/signature.svg +1 -0
  136. package/src/assets/images/size-add.svg +1 -0
  137. package/src/assets/images/size-minus.svg +1 -0
  138. package/src/assets/images/strikeout.svg +1 -0
  139. package/src/assets/images/subscript.svg +1 -0
  140. package/src/assets/images/superscript.svg +1 -0
  141. package/src/assets/images/table.svg +1 -0
  142. package/src/assets/images/title.svg +1 -0
  143. package/src/assets/images/trash.svg +1 -0
  144. package/src/assets/images/underline.svg +1 -0
  145. package/src/assets/images/undo.svg +1 -0
  146. package/src/assets/images/watermark.svg +1 -0
  147. package/src/assets/images/word-tool.svg +1 -0
  148. package/src/assets/snapshots/main_v0.2.1.png +0 -0
  149. package/src/assets/snapshots/main_v0.2.2.png +0 -0
  150. package/src/assets/snapshots/main_v0.3.0.png +0 -0
  151. package/src/assets/snapshots/main_v0.3.1.png +0 -0
  152. package/src/assets/snapshots/main_v0.5.0.png +0 -0
  153. package/src/assets/snapshots/main_v0.5.1.png +0 -0
  154. package/src/assets/snapshots/main_v0.6.0.png +0 -0
  155. package/src/assets/snapshots/main_v0.6.1.png +0 -0
  156. package/src/assets/snapshots/main_v0.7.0.png +0 -0
  157. package/src/assets/snapshots/main_v0.7.1.png +0 -0
  158. package/src/assets/snapshots/main_v0.7.2.png +0 -0
  159. package/src/assets/snapshots/main_v0.7.3.png +0 -0
  160. package/src/assets/snapshots/main_v0.7.4.png +0 -0
  161. package/src/assets/snapshots/main_v0.7.6.png +0 -0
  162. package/src/assets/snapshots/main_v0.7.7.png +0 -0
  163. package/src/assets/snapshots/main_v0.8.0.png +0 -0
  164. package/src/assets/snapshots/main_v0.8.5.png +0 -0
  165. package/src/assets/snapshots/main_v0.8.6.png +0 -0
  166. package/src/assets/snapshots/main_v0.8.7.png +0 -0
  167. package/src/assets/snapshots/main_v0.8.8.png +0 -0
  168. package/src/assets/snapshots/main_v0.9.0.png +0 -0
  169. package/src/assets/snapshots/main_v0.9.1.png +0 -0
  170. package/src/assets/snapshots/main_v0.9.2.png +0 -0
  171. package/src/assets/snapshots/main_v0.9.23.png +0 -0
  172. package/src/assets/snapshots/main_v0.9.28.png +0 -0
  173. package/src/assets/snapshots/main_v0.9.29.png +0 -0
  174. package/src/assets/snapshots/main_v0.9.3.png +0 -0
  175. package/src/assets/snapshots/main_v0.9.30.png +0 -0
  176. package/src/assets/snapshots/main_v0.9.32.png +0 -0
  177. package/src/assets/snapshots/main_v0.9.35.png +0 -0
  178. package/src/assets/snapshots/main_v0.9.4.png +0 -0
  179. package/src/assets/snapshots/main_v0.9.5.png +0 -0
  180. package/src/assets/snapshots/main_v0.9.6.png +0 -0
  181. package/src/assets/snapshots/main_v0.9.8.png +0 -0
  182. package/src/components/dialog/Dialog.ts +171 -0
  183. package/src/components/dialog/dialog.css +131 -0
  184. package/src/components/signature/Signature.ts +340 -0
  185. package/src/components/signature/signature.css +132 -0
  186. package/src/editor/assets/css/block/block.css +21 -0
  187. package/src/editor/assets/css/contextmenu/contextmenu.css +196 -0
  188. package/src/editor/assets/css/control/calculator.css +85 -0
  189. package/src/editor/assets/css/control/select.css +44 -0
  190. package/src/editor/assets/css/date/datePicker.css +233 -0
  191. package/src/editor/assets/css/hyperlink/hyperlink.css +26 -0
  192. package/src/editor/assets/css/index.css +78 -0
  193. package/src/editor/assets/css/previewer/previewer.css +122 -0
  194. package/src/editor/assets/css/resizer/resizer.css +74 -0
  195. package/src/editor/assets/css/table/table.css +155 -0
  196. package/src/editor/assets/css/zone/zone.css +61 -0
  197. package/src/editor/assets/images/close.svg +1 -0
  198. package/src/editor/assets/images/delete-col.svg +1 -0
  199. package/src/editor/assets/images/delete-row-col.svg +1 -0
  200. package/src/editor/assets/images/delete-row.svg +1 -0
  201. package/src/editor/assets/images/delete-table.svg +1 -0
  202. package/src/editor/assets/images/image-change.svg +1 -0
  203. package/src/editor/assets/images/image-download.svg +1 -0
  204. package/src/editor/assets/images/image-next.svg +1 -0
  205. package/src/editor/assets/images/image-pre.svg +1 -0
  206. package/src/editor/assets/images/image.svg +1 -0
  207. package/src/editor/assets/images/insert-bottom-row.svg +1 -0
  208. package/src/editor/assets/images/insert-left-col.svg +1 -0
  209. package/src/editor/assets/images/insert-right-col.svg +1 -0
  210. package/src/editor/assets/images/insert-row-col.svg +1 -0
  211. package/src/editor/assets/images/insert-top-row.svg +1 -0
  212. package/src/editor/assets/images/merge-cancel-cell.svg +1 -0
  213. package/src/editor/assets/images/merge-cell.svg +1 -0
  214. package/src/editor/assets/images/original-size.svg +1 -0
  215. package/src/editor/assets/images/print.svg +1 -0
  216. package/src/editor/assets/images/rotate.svg +1 -0
  217. package/src/editor/assets/images/submenu-dropdown.svg +1 -0
  218. package/src/editor/assets/images/table-border-all.svg +1 -0
  219. package/src/editor/assets/images/table-border-dash.svg +1 -0
  220. package/src/editor/assets/images/table-border-empty.svg +1 -0
  221. package/src/editor/assets/images/table-border-external.svg +1 -0
  222. package/src/editor/assets/images/table-border-internal.svg +1 -0
  223. package/src/editor/assets/images/table-border-td-back.svg +1 -0
  224. package/src/editor/assets/images/table-border-td-bottom.svg +1 -0
  225. package/src/editor/assets/images/table-border-td-forward.svg +1 -0
  226. package/src/editor/assets/images/table-border-td-left.svg +1 -0
  227. package/src/editor/assets/images/table-border-td-right.svg +1 -0
  228. package/src/editor/assets/images/table-border-td-top.svg +1 -0
  229. package/src/editor/assets/images/table-border-td.svg +1 -0
  230. package/src/editor/assets/images/vertical-align-bottom.svg +1 -0
  231. package/src/editor/assets/images/vertical-align-middle.svg +1 -0
  232. package/src/editor/assets/images/vertical-align-top.svg +1 -0
  233. package/src/editor/assets/images/vertical-align.svg +1 -0
  234. package/src/editor/assets/images/zoom-in.svg +1 -0
  235. package/src/editor/assets/images/zoom-out.svg +1 -0
  236. package/src/editor/core/actuator/Actuator.ts +21 -0
  237. package/src/editor/core/actuator/handlers/positionContextChange.ts +13 -0
  238. package/src/editor/core/command/Command.ts +312 -0
  239. package/src/editor/core/command/CommandAdapt.ts +2733 -0
  240. package/src/editor/core/contextmenu/ContextMenu.ts +363 -0
  241. package/src/editor/core/contextmenu/menus/controlMenus.ts +25 -0
  242. package/src/editor/core/contextmenu/menus/globalMenus.ts +66 -0
  243. package/src/editor/core/contextmenu/menus/hyperlinkMenus.ts +58 -0
  244. package/src/editor/core/contextmenu/menus/imageMenus.ts +134 -0
  245. package/src/editor/core/contextmenu/menus/tableMenus.ts +331 -0
  246. package/src/editor/core/cursor/Cursor.ts +248 -0
  247. package/src/editor/core/cursor/CursorAgent.ts +75 -0
  248. package/src/editor/core/draw/Draw.ts +2934 -0
  249. package/src/editor/core/draw/control/Control.ts +1767 -0
  250. package/src/editor/core/draw/control/checkbox/CheckboxControl.ts +154 -0
  251. package/src/editor/core/draw/control/date/DateControl.ts +363 -0
  252. package/src/editor/core/draw/control/interactive/ControlSearch.ts +214 -0
  253. package/src/editor/core/draw/control/number/Calculator.ts +183 -0
  254. package/src/editor/core/draw/control/number/NumberControl.ts +183 -0
  255. package/src/editor/core/draw/control/radio/RadioControl.ts +68 -0
  256. package/src/editor/core/draw/control/richtext/Border.ts +52 -0
  257. package/src/editor/core/draw/control/select/SelectControl.ts +567 -0
  258. package/src/editor/core/draw/control/text/TextControl.ts +280 -0
  259. package/src/editor/core/draw/frame/Background.ts +117 -0
  260. package/src/editor/core/draw/frame/Badge.ts +88 -0
  261. package/src/editor/core/draw/frame/Footer.ts +155 -0
  262. package/src/editor/core/draw/frame/Header.ts +158 -0
  263. package/src/editor/core/draw/frame/LineNumber.ts +43 -0
  264. package/src/editor/core/draw/frame/Margin.ts +53 -0
  265. package/src/editor/core/draw/frame/PageBorder.ts +47 -0
  266. package/src/editor/core/draw/frame/PageNumber.ts +88 -0
  267. package/src/editor/core/draw/frame/Placeholder.ts +114 -0
  268. package/src/editor/core/draw/frame/Watermark.ts +188 -0
  269. package/src/editor/core/draw/graffiti/Graffiti.ts +125 -0
  270. package/src/editor/core/draw/interactive/Area.ts +312 -0
  271. package/src/editor/core/draw/interactive/Group.ts +198 -0
  272. package/src/editor/core/draw/interactive/Search.ts +527 -0
  273. package/src/editor/core/draw/particle/CheckboxParticle.ts +111 -0
  274. package/src/editor/core/draw/particle/HyperlinkParticle.ts +86 -0
  275. package/src/editor/core/draw/particle/ImageParticle.ts +280 -0
  276. package/src/editor/core/draw/particle/LabelParticle.ts +79 -0
  277. package/src/editor/core/draw/particle/LineBreakParticle.ts +55 -0
  278. package/src/editor/core/draw/particle/ListParticle.ts +255 -0
  279. package/src/editor/core/draw/particle/PageBreakParticle.ts +54 -0
  280. package/src/editor/core/draw/particle/RadioParticle.ts +99 -0
  281. package/src/editor/core/draw/particle/SeparatorParticle.ts +37 -0
  282. package/src/editor/core/draw/particle/SubscriptParticle.ts +23 -0
  283. package/src/editor/core/draw/particle/SuperscriptParticle.ts +23 -0
  284. package/src/editor/core/draw/particle/TextParticle.ts +174 -0
  285. package/src/editor/core/draw/particle/WhiteSpaceParticle.ts +32 -0
  286. package/src/editor/core/draw/particle/block/BlockParticle.ts +76 -0
  287. package/src/editor/core/draw/particle/block/modules/BaseBlock.ts +280 -0
  288. package/src/editor/core/draw/particle/block/modules/IFrameBlock.ts +47 -0
  289. package/src/editor/core/draw/particle/block/modules/VideoBlock.ts +61 -0
  290. package/src/editor/core/draw/particle/date/DateParticle.ts +111 -0
  291. package/src/editor/core/draw/particle/date/DatePicker.ts +577 -0
  292. package/src/editor/core/draw/particle/latex/LaTexParticle.ts +43 -0
  293. package/src/editor/core/draw/particle/latex/utils/LaTexUtils.ts +1196 -0
  294. package/src/editor/core/draw/particle/latex/utils/hershey.ts +1632 -0
  295. package/src/editor/core/draw/particle/latex/utils/symbols.ts +318 -0
  296. package/src/editor/core/draw/particle/previewer/Previewer.ts +582 -0
  297. package/src/editor/core/draw/particle/table/TableOperate.ts +988 -0
  298. package/src/editor/core/draw/particle/table/TableParticle.ts +558 -0
  299. package/src/editor/core/draw/particle/table/TableTool.ts +551 -0
  300. package/src/editor/core/draw/richtext/AbstractRichText.ts +59 -0
  301. package/src/editor/core/draw/richtext/Highlight.ts +24 -0
  302. package/src/editor/core/draw/richtext/Strikeout.ts +28 -0
  303. package/src/editor/core/draw/richtext/Underline.ts +106 -0
  304. package/src/editor/core/event/CanvasEvent.ts +215 -0
  305. package/src/editor/core/event/GlobalEvent.ts +173 -0
  306. package/src/editor/core/event/eventbus/EventBus.ts +50 -0
  307. package/src/editor/core/event/handlers/click.ts +234 -0
  308. package/src/editor/core/event/handlers/composition.ts +45 -0
  309. package/src/editor/core/event/handlers/copy.ts +72 -0
  310. package/src/editor/core/event/handlers/cut.ts +47 -0
  311. package/src/editor/core/event/handlers/drag.ts +66 -0
  312. package/src/editor/core/event/handlers/drop.ts +28 -0
  313. package/src/editor/core/event/handlers/input.ts +129 -0
  314. package/src/editor/core/event/handlers/keydown/backspace.ts +161 -0
  315. package/src/editor/core/event/handlers/keydown/delete.ts +119 -0
  316. package/src/editor/core/event/handlers/keydown/end.ts +69 -0
  317. package/src/editor/core/event/handlers/keydown/enter.ts +126 -0
  318. package/src/editor/core/event/handlers/keydown/home.ts +69 -0
  319. package/src/editor/core/event/handlers/keydown/index.ts +85 -0
  320. package/src/editor/core/event/handlers/keydown/left.ts +162 -0
  321. package/src/editor/core/event/handlers/keydown/right.ts +178 -0
  322. package/src/editor/core/event/handlers/keydown/tab.ts +41 -0
  323. package/src/editor/core/event/handlers/keydown/updown.ts +342 -0
  324. package/src/editor/core/event/handlers/mousedown.ts +262 -0
  325. package/src/editor/core/event/handlers/mouseleave.ts +14 -0
  326. package/src/editor/core/event/handlers/mousemove.ts +133 -0
  327. package/src/editor/core/event/handlers/mouseup.ts +341 -0
  328. package/src/editor/core/event/handlers/paste.ts +231 -0
  329. package/src/editor/core/history/HistoryManager.ts +61 -0
  330. package/src/editor/core/i18n/I18n.ts +51 -0
  331. package/src/editor/core/i18n/lang/en.json +92 -0
  332. package/src/editor/core/i18n/lang/zh-CN.json +92 -0
  333. package/src/editor/core/listener/Listener.ts +41 -0
  334. package/src/editor/core/observer/ImageObserver.ts +19 -0
  335. package/src/editor/core/observer/MouseObserver.ts +56 -0
  336. package/src/editor/core/observer/ScrollObserver.ts +88 -0
  337. package/src/editor/core/observer/SelectionObserver.ts +143 -0
  338. package/src/editor/core/override/Override.ts +14 -0
  339. package/src/editor/core/plugin/Plugin.ts +17 -0
  340. package/src/editor/core/position/Position.ts +870 -0
  341. package/src/editor/core/range/RangeManager.ts +723 -0
  342. package/src/editor/core/register/Register.ts +28 -0
  343. package/src/editor/core/shortcut/Shortcut.ts +80 -0
  344. package/src/editor/core/shortcut/keys/listKeys.ts +22 -0
  345. package/src/editor/core/shortcut/keys/richtextKeys.ts +102 -0
  346. package/src/editor/core/shortcut/keys/titleKeys.ts +62 -0
  347. package/src/editor/core/worker/WorkerManager.ts +96 -0
  348. package/src/editor/core/worker/works/catalog.ts +189 -0
  349. package/src/editor/core/worker/works/group.ts +34 -0
  350. package/src/editor/core/worker/works/value.ts +32 -0
  351. package/src/editor/core/worker/works/wordCount.ts +132 -0
  352. package/src/editor/core/zone/Zone.ts +183 -0
  353. package/src/editor/core/zone/ZoneTip.ts +108 -0
  354. package/src/editor/dataset/constant/Background.ts +10 -0
  355. package/src/editor/dataset/constant/Badge.ts +6 -0
  356. package/src/editor/dataset/constant/Checkbox.ts +12 -0
  357. package/src/editor/dataset/constant/Common.ts +44 -0
  358. package/src/editor/dataset/constant/ContextMenu.ts +61 -0
  359. package/src/editor/dataset/constant/Control.ts +14 -0
  360. package/src/editor/dataset/constant/Cursor.ts +11 -0
  361. package/src/editor/dataset/constant/Editor.ts +19 -0
  362. package/src/editor/dataset/constant/Element.ts +173 -0
  363. package/src/editor/dataset/constant/Footer.ts +10 -0
  364. package/src/editor/dataset/constant/Graffiti.ts +6 -0
  365. package/src/editor/dataset/constant/Group.ts +10 -0
  366. package/src/editor/dataset/constant/Header.ts +10 -0
  367. package/src/editor/dataset/constant/ImgCaption.ts +8 -0
  368. package/src/editor/dataset/constant/Label.ts +8 -0
  369. package/src/editor/dataset/constant/LineBreak.ts +7 -0
  370. package/src/editor/dataset/constant/LineNumber.ts +11 -0
  371. package/src/editor/dataset/constant/List.ts +26 -0
  372. package/src/editor/dataset/constant/PageBorder.ts +8 -0
  373. package/src/editor/dataset/constant/PageBreak.ts +7 -0
  374. package/src/editor/dataset/constant/PageNumber.ts +22 -0
  375. package/src/editor/dataset/constant/Placeholder.ts +9 -0
  376. package/src/editor/dataset/constant/Radio.ts +12 -0
  377. package/src/editor/dataset/constant/Regular.ts +23 -0
  378. package/src/editor/dataset/constant/Separator.ts +6 -0
  379. package/src/editor/dataset/constant/Shortcut.ts +3 -0
  380. package/src/editor/dataset/constant/Table.ts +9 -0
  381. package/src/editor/dataset/constant/Title.ts +38 -0
  382. package/src/editor/dataset/constant/Watermark.ts +17 -0
  383. package/src/editor/dataset/constant/WhiteSpace.ts +7 -0
  384. package/src/editor/dataset/constant/Zone.ts +5 -0
  385. package/src/editor/dataset/enum/Area.ts +5 -0
  386. package/src/editor/dataset/enum/Background.ts +11 -0
  387. package/src/editor/dataset/enum/Block.ts +4 -0
  388. package/src/editor/dataset/enum/Common.ts +30 -0
  389. package/src/editor/dataset/enum/Control.ts +39 -0
  390. package/src/editor/dataset/enum/Editor.ts +51 -0
  391. package/src/editor/dataset/enum/Element.ts +21 -0
  392. package/src/editor/dataset/enum/ElementStyle.ts +12 -0
  393. package/src/editor/dataset/enum/Event.ts +5 -0
  394. package/src/editor/dataset/enum/KeyMap.ts +85 -0
  395. package/src/editor/dataset/enum/LineNumber.ts +4 -0
  396. package/src/editor/dataset/enum/List.ts +23 -0
  397. package/src/editor/dataset/enum/Observer.ts +6 -0
  398. package/src/editor/dataset/enum/Row.ts +7 -0
  399. package/src/editor/dataset/enum/Text.ts +13 -0
  400. package/src/editor/dataset/enum/Title.ts +8 -0
  401. package/src/editor/dataset/enum/VerticalAlign.ts +5 -0
  402. package/src/editor/dataset/enum/Watermark.ts +4 -0
  403. package/src/editor/dataset/enum/table/Table.ts +19 -0
  404. package/src/editor/dataset/enum/table/TableTool.ts +4 -0
  405. package/src/editor/index.ts +241 -0
  406. package/src/editor/interface/Area.ts +68 -0
  407. package/src/editor/interface/Background.ts +9 -0
  408. package/src/editor/interface/Badge.ts +17 -0
  409. package/src/editor/interface/Block.ts +18 -0
  410. package/src/editor/interface/Catalog.ts +11 -0
  411. package/src/editor/interface/Checkbox.ts +17 -0
  412. package/src/editor/interface/Command.ts +3 -0
  413. package/src/editor/interface/Common.ts +43 -0
  414. package/src/editor/interface/Control.ts +250 -0
  415. package/src/editor/interface/Cursor.ts +7 -0
  416. package/src/editor/interface/Draw.ts +86 -0
  417. package/src/editor/interface/Editor.ts +172 -0
  418. package/src/editor/interface/Element.ts +273 -0
  419. package/src/editor/interface/Event.ts +27 -0
  420. package/src/editor/interface/EventBus.ts +46 -0
  421. package/src/editor/interface/Footer.ts +9 -0
  422. package/src/editor/interface/Graffiti.ts +15 -0
  423. package/src/editor/interface/Group.ts +8 -0
  424. package/src/editor/interface/Header.ts +9 -0
  425. package/src/editor/interface/Label.ts +8 -0
  426. package/src/editor/interface/LineBreak.ts +5 -0
  427. package/src/editor/interface/LineNumber.ts +10 -0
  428. package/src/editor/interface/Listener.ts +88 -0
  429. package/src/editor/interface/Margin.ts +1 -0
  430. package/src/editor/interface/PageBorder.ts +8 -0
  431. package/src/editor/interface/PageBreak.ts +5 -0
  432. package/src/editor/interface/PageNumber.ts +16 -0
  433. package/src/editor/interface/Placeholder.ts +7 -0
  434. package/src/editor/interface/Plugin.ts +8 -0
  435. package/src/editor/interface/Position.ts +113 -0
  436. package/src/editor/interface/Previewer.ts +15 -0
  437. package/src/editor/interface/Radio.ts +17 -0
  438. package/src/editor/interface/Range.ts +61 -0
  439. package/src/editor/interface/Row.ts +25 -0
  440. package/src/editor/interface/Search.ts +36 -0
  441. package/src/editor/interface/Separator.ts +4 -0
  442. package/src/editor/interface/Text.ts +15 -0
  443. package/src/editor/interface/Title.ts +32 -0
  444. package/src/editor/interface/Watermark.ts +16 -0
  445. package/src/editor/interface/WhiteSpace.ts +5 -0
  446. package/src/editor/interface/Zone.ts +3 -0
  447. package/src/editor/interface/contextmenu/ContextMenu.ts +76 -0
  448. package/src/editor/interface/i18n/I18n.ts +7 -0
  449. package/src/editor/interface/shortcut/Shortcut.ts +14 -0
  450. package/src/editor/interface/table/Colgroup.ts +4 -0
  451. package/src/editor/interface/table/Table.ts +9 -0
  452. package/src/editor/interface/table/Td.ts +36 -0
  453. package/src/editor/interface/table/Tr.ts +11 -0
  454. package/src/editor/types/index.d.ts +5 -0
  455. package/src/editor/utils/clipboard.ts +94 -0
  456. package/src/editor/utils/element.ts +1877 -0
  457. package/src/editor/utils/hotkey.ts +5 -0
  458. package/src/editor/utils/index.ts +445 -0
  459. package/src/editor/utils/option.ts +253 -0
  460. package/src/editor/utils/paragraph.ts +28 -0
  461. package/src/editor/utils/print.ts +99 -0
  462. package/src/editor/utils/ua.ts +13 -0
  463. package/src/main.ts +2053 -0
  464. package/src/mock.ts +611 -0
  465. package/src/plugins/copy/index.ts +30 -0
  466. package/src/plugins/markdown/index.ts +118 -0
  467. package/src/style.css +1079 -0
  468. package/src/utils/index.ts +45 -0
  469. package/src/utils/prism.ts +89 -0
  470. package/src/vite-env.d.ts +1 -0
  471. package/tsconfig.json +25 -0
  472. package/vite.config.ts +46 -0
@@ -0,0 +1,2733 @@
1
+ import { NBSP, WRAP, ZERO } from '../../dataset/constant/Common'
2
+ import {
3
+ AREA_CONTEXT_ATTR,
4
+ EDITOR_ELEMENT_STYLE_ATTR,
5
+ EDITOR_ROW_ATTR,
6
+ LIST_CONTEXT_ATTR,
7
+ TABLE_CONTEXT_ATTR
8
+ } from '../../dataset/constant/Element'
9
+ import {
10
+ titleOrderNumberMapping,
11
+ titleSizeMapping
12
+ } from '../../dataset/constant/Title'
13
+ import { defaultWatermarkOption } from '../../dataset/constant/Watermark'
14
+ import { ImageDisplay, LocationPosition } from '../../dataset/enum/Common'
15
+ import { ControlComponent } from '../../dataset/enum/Control'
16
+ import {
17
+ EditorMode,
18
+ EditorZone,
19
+ PageMode,
20
+ PaperDirection
21
+ } from '../../dataset/enum/Editor'
22
+ import { ElementType } from '../../dataset/enum/Element'
23
+ import { ElementStyleKey } from '../../dataset/enum/ElementStyle'
24
+ import { ListStyle, ListType } from '../../dataset/enum/List'
25
+ import { MoveDirection } from '../../dataset/enum/Observer'
26
+ import { RowFlex } from '../../dataset/enum/Row'
27
+ import { TableBorder, TdBorder, TdSlash } from '../../dataset/enum/table/Table'
28
+ import { TitleLevel } from '../../dataset/enum/Title'
29
+ import { VerticalAlign } from '../../dataset/enum/VerticalAlign'
30
+ import { ICatalog } from '../../interface/Catalog'
31
+ import { DeepRequired } from '../../interface/Common'
32
+ import {
33
+ IGetControlValueOption,
34
+ IGetControlValueResult,
35
+ ILocationControlOption,
36
+ IRemoveControlOption,
37
+ ISetControlExtensionOption,
38
+ ISetControlHighlightOption,
39
+ ISetControlProperties,
40
+ ISetControlValueOption
41
+ } from '../../interface/Control'
42
+ import {
43
+ IAppendElementListOption,
44
+ IDrawImagePayload,
45
+ IDrawOption,
46
+ IForceUpdateOption,
47
+ IGetImageOption,
48
+ IGetValueOption,
49
+ IPainterOption
50
+ } from '../../interface/Draw'
51
+ import {
52
+ IEditorData,
53
+ IEditorHTML,
54
+ IEditorOption,
55
+ IEditorResult,
56
+ IEditorText,
57
+ IFocusOption,
58
+ ISetValueOption,
59
+ IUpdateOption
60
+ } from '../../interface/Editor'
61
+ import {
62
+ IDeleteElementByIdOption,
63
+ IElement,
64
+ IElementPosition,
65
+ IElementStyle,
66
+ IGetElementByIdOption,
67
+ IImageCaption,
68
+ IImageCrop,
69
+ IInsertElementListOption,
70
+ IUpdateElementByIdOption
71
+ } from '../../interface/Element'
72
+ import {
73
+ ICopyOption,
74
+ IPasteOption,
75
+ IPositionContextByEventOption,
76
+ IPositionContextByEventResult,
77
+ ITableInfoByEvent
78
+ } from '../../interface/Event'
79
+ import { IMargin } from '../../interface/Margin'
80
+ import { ILocationPosition, IPositionContext } from '../../interface/Position'
81
+ import { IRange, RangeContext, RangeRect } from '../../interface/Range'
82
+ import {
83
+ IReplaceOption,
84
+ ISearchOption,
85
+ ISearchResultContext
86
+ } from '../../interface/Search'
87
+ import { ITextDecoration } from '../../interface/Text'
88
+ import {
89
+ IGetTitleValueOption,
90
+ IGetTitleValueResult
91
+ } from '../../interface/Title'
92
+ import { IWatermark } from '../../interface/Watermark'
93
+ import {
94
+ cloneProperty,
95
+ deepClone,
96
+ downloadFile,
97
+ getUUID,
98
+ isNumber,
99
+ isObjectEqual
100
+ } from '../../utils'
101
+ import { getParagraphNo } from '../../utils/paragraph'
102
+ import {
103
+ createDomFromElementList,
104
+ formatElementContext,
105
+ formatElementList,
106
+ isTextLikeElement,
107
+ pickElementAttr,
108
+ getElementListByHTML,
109
+ getTextFromElementList,
110
+ zipElementList,
111
+ getAnchorElement,
112
+ pickSurroundElementList
113
+ } from '../../utils/element'
114
+ import { mergeOption } from '../../utils/option'
115
+ import { printImageBase64 } from '../../utils/print'
116
+ import { Control } from '../draw/control/Control'
117
+ import { Draw } from '../draw/Draw'
118
+ import { INavigateInfo, Search } from '../draw/interactive/Search'
119
+ import { TableOperate } from '../draw/particle/table/TableOperate'
120
+ import { CanvasEvent } from '../event/CanvasEvent'
121
+ import { pasteByApi } from '../event/handlers/paste'
122
+ import { HistoryManager } from '../history/HistoryManager'
123
+ import { I18n } from '../i18n/I18n'
124
+ import { Position } from '../position/Position'
125
+ import { RangeManager } from '../range/RangeManager'
126
+ import { WorkerManager } from '../worker/WorkerManager'
127
+ import { Zone } from '../zone/Zone'
128
+ import {
129
+ IGetAreaValueOption,
130
+ IGetAreaValueResult,
131
+ IInsertAreaOption,
132
+ ILocationAreaOption,
133
+ ISetAreaPropertiesOption,
134
+ ISetAreaValueOption
135
+ } from '../../interface/Area'
136
+ import { IAreaBadge, IBadge } from '../../interface/Badge'
137
+ import { IRichtextOption } from '../../interface/Command'
138
+ import { WatermarkType } from '../../dataset/enum/Watermark'
139
+
140
+ export class CommandAdapt {
141
+ private draw: Draw
142
+ private range: RangeManager
143
+ private position: Position
144
+ private historyManager: HistoryManager
145
+ private canvasEvent: CanvasEvent
146
+ private options: DeepRequired<IEditorOption>
147
+ private control: Control
148
+ private workerManager: WorkerManager
149
+ private searchManager: Search
150
+ private i18n: I18n
151
+ private zone: Zone
152
+ private tableOperate: TableOperate
153
+
154
+ constructor(draw: Draw) {
155
+ this.draw = draw
156
+ this.range = draw.getRange()
157
+ this.position = draw.getPosition()
158
+ this.historyManager = draw.getHistoryManager()
159
+ this.canvasEvent = draw.getCanvasEvent()
160
+ this.options = draw.getOptions()
161
+ this.control = draw.getControl()
162
+ this.workerManager = draw.getWorkerManager()
163
+ this.searchManager = draw.getSearch()
164
+ this.i18n = draw.getI18n()
165
+ this.zone = draw.getZone()
166
+ this.tableOperate = draw.getTableOperate()
167
+ }
168
+
169
+ public mode(payload: EditorMode) {
170
+ this.draw.setMode(payload)
171
+ }
172
+
173
+ public async cut() {
174
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
175
+ if (isDisabled) return
176
+ await this.canvasEvent.cut()
177
+ }
178
+
179
+ public async copy(payload?: ICopyOption) {
180
+ await this.canvasEvent.copy(payload)
181
+ }
182
+
183
+ public paste(payload?: IPasteOption) {
184
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
185
+ if (isDisabled) return
186
+ pasteByApi(this.canvasEvent, payload)
187
+ }
188
+
189
+ public selectAll() {
190
+ this.canvasEvent.selectAll()
191
+ }
192
+
193
+ public backspace() {
194
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
195
+ if (isDisabled) return
196
+ const elementList = this.draw.getElementList()
197
+ const { startIndex, endIndex } = this.range.getRange()
198
+ const isCollapsed = startIndex === endIndex
199
+ // 首字符禁止删除
200
+ if (
201
+ isCollapsed &&
202
+ elementList[startIndex].value === ZERO &&
203
+ startIndex === 0
204
+ ) {
205
+ return
206
+ }
207
+ if (!isCollapsed) {
208
+ this.draw.spliceElementList(
209
+ elementList,
210
+ startIndex + 1,
211
+ endIndex - startIndex
212
+ )
213
+ } else {
214
+ this.draw.spliceElementList(elementList, startIndex, 1)
215
+ }
216
+ const curIndex = isCollapsed ? startIndex - 1 : startIndex
217
+ this.range.setRange(curIndex, curIndex)
218
+ this.draw.render({ curIndex })
219
+ }
220
+
221
+ public setRange(
222
+ startIndex: number,
223
+ endIndex: number,
224
+ tableId?: string,
225
+ startTdIndex?: number,
226
+ endTdIndex?: number,
227
+ startTrIndex?: number,
228
+ endTrIndex?: number
229
+ ) {
230
+ if (startIndex < 0 || endIndex < 0 || endIndex < startIndex) return
231
+ this.range.setRange(
232
+ startIndex,
233
+ endIndex,
234
+ tableId,
235
+ startTdIndex,
236
+ endTdIndex,
237
+ startTrIndex,
238
+ endTrIndex
239
+ )
240
+ const isCollapsed = startIndex === endIndex
241
+ this.draw.render({
242
+ curIndex: isCollapsed ? startIndex : undefined,
243
+ isCompute: false,
244
+ isSubmitHistory: false,
245
+ isSetCursor: isCollapsed
246
+ })
247
+ }
248
+
249
+ public replaceRange(range: IRange) {
250
+ this.setRange(
251
+ range.startIndex,
252
+ range.endIndex,
253
+ range.tableId,
254
+ range.startTdIndex,
255
+ range.endTdIndex,
256
+ range.startTrIndex,
257
+ range.endTrIndex
258
+ )
259
+ }
260
+
261
+ public setPositionContext(range: IRange) {
262
+ const { tableId, startTrIndex, startTdIndex } = range
263
+ const elementList = this.draw.getOriginalElementList()
264
+ if (tableId) {
265
+ const tableElementIndex = elementList.findIndex(el => el.id === tableId)
266
+ if (!~tableElementIndex) return
267
+ const tableElement = elementList[tableElementIndex]
268
+ const tr = tableElement.trList![startTrIndex!]
269
+ const td = tr.tdList[startTdIndex!]
270
+ this.position.setPositionContext({
271
+ isTable: true,
272
+ index: tableElementIndex,
273
+ trIndex: startTrIndex,
274
+ tdIndex: startTdIndex,
275
+ tdId: td.id,
276
+ trId: tr.id,
277
+ tableId
278
+ })
279
+ } else {
280
+ this.position.setPositionContext({
281
+ isTable: false
282
+ })
283
+ }
284
+ }
285
+
286
+ public forceUpdate(options?: IForceUpdateOption) {
287
+ const { isSubmitHistory = false } = options || {}
288
+ this.range.clearRange()
289
+ this.draw.render({
290
+ isSubmitHistory,
291
+ isSetCursor: false
292
+ })
293
+ }
294
+
295
+ public blur() {
296
+ this.range.clearRange()
297
+ this.draw.getCursor().recoveryCursor()
298
+ }
299
+
300
+ public hideCursor() {
301
+ this.draw.getCursor().recoveryCursor()
302
+ }
303
+
304
+ public undo() {
305
+ const isReadonly = this.draw.isReadonly()
306
+ if (isReadonly) return
307
+ this.historyManager.undo()
308
+ }
309
+
310
+ public redo() {
311
+ const isReadonly = this.draw.isReadonly()
312
+ if (isReadonly) return
313
+ this.historyManager.redo()
314
+ }
315
+
316
+ public painter(options: IPainterOption) {
317
+ // 如果单击且已经有样式设置则取消设置
318
+ if (!options.isDblclick && this.draw.getPainterStyle()) {
319
+ this.canvasEvent.clearPainterStyle()
320
+ return
321
+ }
322
+ const selection = this.range.getSelection()
323
+ if (!selection) return
324
+ const painterStyle: IElementStyle = {}
325
+ selection.forEach(s => {
326
+ const painterStyleKeys = EDITOR_ELEMENT_STYLE_ATTR
327
+ painterStyleKeys.forEach(p => {
328
+ const key = p as keyof typeof ElementStyleKey
329
+ if (painterStyle[key] === undefined) {
330
+ Reflect.set(painterStyle, key, s[key])
331
+ }
332
+ })
333
+ })
334
+ this.draw.setPainterStyle(painterStyle, options)
335
+ }
336
+
337
+ public applyPainterStyle() {
338
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
339
+ if (isDisabled) return
340
+ this.canvasEvent.applyPainterStyle()
341
+ }
342
+
343
+ public format(options?: IRichtextOption) {
344
+ const { isIgnoreDisabledRule = false } = options || {}
345
+ const isDisabled =
346
+ !isIgnoreDisabledRule &&
347
+ (this.draw.isReadonly() || this.draw.isDisabled())
348
+ if (isDisabled) return
349
+ const selection = this.range.getSelectionElementList()
350
+ // 选区设置或设置换行处样式
351
+ let renderOption: IDrawOption = {}
352
+ let changeElementList: IElement[] = []
353
+ if (selection?.length) {
354
+ changeElementList = selection
355
+ renderOption = { isSetCursor: false }
356
+ } else {
357
+ const { endIndex } = this.range.getRange()
358
+ const elementList = this.draw.getElementList()
359
+ const enterElement = elementList[endIndex]
360
+ if (enterElement?.value === ZERO) {
361
+ changeElementList.push(enterElement)
362
+ renderOption = { curIndex: endIndex }
363
+ }
364
+ }
365
+ if (!changeElementList.length) return
366
+ changeElementList.forEach(el => {
367
+ EDITOR_ELEMENT_STYLE_ATTR.forEach(attr => {
368
+ delete el[attr]
369
+ })
370
+ })
371
+ this.draw.render(renderOption)
372
+ }
373
+
374
+ public font(payload: string, options?: IRichtextOption) {
375
+ const { isIgnoreDisabledRule = false } = options || {}
376
+ const isDisabled =
377
+ !isIgnoreDisabledRule &&
378
+ (this.draw.isReadonly() || this.draw.isDisabled())
379
+ if (isDisabled) return
380
+ const selection = this.range.getSelectionElementList()
381
+ if (selection?.length) {
382
+ selection.forEach(el => {
383
+ el.font = payload
384
+ })
385
+ this.draw.render({ isSetCursor: false })
386
+ } else {
387
+ let isSubmitHistory = true
388
+ const { endIndex } = this.range.getRange()
389
+ const elementList = this.draw.getElementList()
390
+ const enterElement = elementList[endIndex]
391
+ this.range.setDefaultStyle({
392
+ font: payload
393
+ })
394
+ if (enterElement?.value === ZERO) {
395
+ enterElement.font = payload
396
+ } else {
397
+ isSubmitHistory = false
398
+ }
399
+ this.draw.render({
400
+ isSubmitHistory,
401
+ curIndex: endIndex,
402
+ isCompute: false
403
+ })
404
+ }
405
+ }
406
+
407
+ public size(payload: number, options?: IRichtextOption) {
408
+ const { isIgnoreDisabledRule = false } = options || {}
409
+ const isDisabled =
410
+ !isIgnoreDisabledRule &&
411
+ (this.draw.isReadonly() || this.draw.isDisabled())
412
+ if (isDisabled) return
413
+ const { minSize, maxSize, defaultSize } = this.options
414
+ if (payload < minSize || payload > maxSize) return
415
+ // 选区设置或设置换行处样式
416
+ let renderOption: IDrawOption = {}
417
+ let changeElementList: IElement[] = []
418
+ const selection = this.range.getTextLikeSelectionElementList()
419
+ if (selection?.length) {
420
+ changeElementList = selection
421
+ renderOption = { isSetCursor: false }
422
+ } else {
423
+ const { endIndex } = this.range.getRange()
424
+ const elementList = this.draw.getElementList()
425
+ const enterElement = elementList[endIndex]
426
+ this.range.setDefaultStyle({
427
+ size: payload
428
+ })
429
+ if (enterElement?.value === ZERO) {
430
+ changeElementList.push(enterElement)
431
+ renderOption = { curIndex: endIndex }
432
+ } else {
433
+ this.draw.render({
434
+ curIndex: endIndex,
435
+ isCompute: false,
436
+ isSubmitHistory: false
437
+ })
438
+ }
439
+ }
440
+ if (!changeElementList.length) return
441
+ let isExistUpdate = false
442
+ changeElementList.forEach(el => {
443
+ if (
444
+ (!el.size && payload === defaultSize) ||
445
+ (el.size && el.size === payload)
446
+ ) {
447
+ return
448
+ }
449
+ el.size = payload
450
+ isExistUpdate = true
451
+ })
452
+ if (isExistUpdate) {
453
+ this.draw.render(renderOption)
454
+ }
455
+ }
456
+
457
+ public sizeAdd(options?: IRichtextOption) {
458
+ const { isIgnoreDisabledRule = false } = options || {}
459
+ const isDisabled =
460
+ !isIgnoreDisabledRule &&
461
+ (this.draw.isReadonly() || this.draw.isDisabled())
462
+ if (isDisabled) return
463
+ const { defaultSize, maxSize } = this.options
464
+ const selection = this.range.getTextLikeSelectionElementList()
465
+ // 选区设置或设置换行处样式
466
+ let renderOption: IDrawOption = {}
467
+ let changeElementList: IElement[] = []
468
+ if (selection?.length) {
469
+ changeElementList = selection
470
+ renderOption = { isSetCursor: false }
471
+ } else {
472
+ const { endIndex } = this.range.getRange()
473
+ const elementList = this.draw.getElementList()
474
+ const enterElement = elementList[endIndex]
475
+ // 设置默认样式
476
+ const style = this.range.getDefaultStyle()
477
+ const anchorSize = style?.size || enterElement.size || defaultSize
478
+ this.range.setDefaultStyle({
479
+ size: anchorSize + 2 > maxSize ? maxSize : anchorSize + 2
480
+ })
481
+ if (enterElement?.value === ZERO) {
482
+ changeElementList.push(enterElement)
483
+ renderOption = { curIndex: endIndex }
484
+ } else {
485
+ this.draw.render({
486
+ curIndex: endIndex,
487
+ isCompute: false,
488
+ isSubmitHistory: false
489
+ })
490
+ }
491
+ }
492
+ if (!changeElementList.length) return
493
+ let isExistUpdate = false
494
+ changeElementList.forEach(el => {
495
+ if (!el.size) {
496
+ el.size = defaultSize
497
+ }
498
+ if (el.size >= maxSize) return
499
+ if (el.size + 2 > maxSize) {
500
+ el.size = maxSize
501
+ } else {
502
+ el.size += 2
503
+ }
504
+ isExistUpdate = true
505
+ })
506
+ if (isExistUpdate) {
507
+ this.draw.render(renderOption)
508
+ }
509
+ }
510
+
511
+ public sizeMinus(options?: IRichtextOption) {
512
+ const { isIgnoreDisabledRule = false } = options || {}
513
+ const isDisabled =
514
+ !isIgnoreDisabledRule &&
515
+ (this.draw.isReadonly() || this.draw.isDisabled())
516
+ if (isDisabled) return
517
+ const { defaultSize, minSize } = this.options
518
+ const selection = this.range.getTextLikeSelectionElementList()
519
+ // 选区设置或设置换行处样式
520
+ let renderOption: IDrawOption = {}
521
+ let changeElementList: IElement[] = []
522
+ if (selection?.length) {
523
+ changeElementList = selection
524
+ renderOption = { isSetCursor: false }
525
+ } else {
526
+ const { endIndex } = this.range.getRange()
527
+ const elementList = this.draw.getElementList()
528
+ const enterElement = elementList[endIndex]
529
+ const style = this.range.getDefaultStyle()
530
+ const anchorSize = style?.size || enterElement.size || defaultSize
531
+ this.range.setDefaultStyle({
532
+ size: anchorSize - 2 < minSize ? minSize : anchorSize - 2
533
+ })
534
+ if (enterElement?.value === ZERO) {
535
+ changeElementList.push(enterElement)
536
+ renderOption = { curIndex: endIndex }
537
+ } else {
538
+ this.draw.render({
539
+ curIndex: endIndex,
540
+ isCompute: false,
541
+ isSubmitHistory: false
542
+ })
543
+ }
544
+ }
545
+ if (!changeElementList.length) return
546
+ let isExistUpdate = false
547
+ changeElementList.forEach(el => {
548
+ if (!el.size) {
549
+ el.size = defaultSize
550
+ }
551
+ if (el.size <= minSize) return
552
+ if (el.size - 2 < minSize) {
553
+ el.size = minSize
554
+ } else {
555
+ el.size -= 2
556
+ }
557
+ isExistUpdate = true
558
+ })
559
+ if (isExistUpdate) {
560
+ this.draw.render(renderOption)
561
+ }
562
+ }
563
+
564
+ public bold(options?: IRichtextOption) {
565
+ const { isIgnoreDisabledRule = false } = options || {}
566
+ const isDisabled =
567
+ !isIgnoreDisabledRule &&
568
+ (this.draw.isReadonly() || this.draw.isDisabled())
569
+ if (isDisabled) return
570
+ const selection = this.range.getSelectionElementList()
571
+ if (selection?.length) {
572
+ const noBoldIndex = selection.findIndex(s => !s.bold)
573
+ selection.forEach(el => {
574
+ el.bold = !!~noBoldIndex
575
+ })
576
+ this.draw.render({ isSetCursor: false })
577
+ } else {
578
+ let isSubmitHistory = true
579
+ const { endIndex } = this.range.getRange()
580
+ const elementList = this.draw.getElementList()
581
+ const enterElement = elementList[endIndex]
582
+ this.range.setDefaultStyle({
583
+ bold: enterElement.bold ? false : !this.range.getDefaultStyle()?.bold
584
+ })
585
+ if (enterElement?.value === ZERO) {
586
+ enterElement.bold = !enterElement.bold
587
+ } else {
588
+ isSubmitHistory = false
589
+ }
590
+ this.draw.render({
591
+ isSubmitHistory,
592
+ curIndex: endIndex,
593
+ isCompute: false
594
+ })
595
+ }
596
+ }
597
+
598
+ public italic(options?: IRichtextOption) {
599
+ const { isIgnoreDisabledRule = false } = options || {}
600
+ const isDisabled =
601
+ !isIgnoreDisabledRule &&
602
+ (this.draw.isReadonly() || this.draw.isDisabled())
603
+ if (isDisabled) return
604
+ const selection = this.range.getSelectionElementList()
605
+ if (selection?.length) {
606
+ const noItalicIndex = selection.findIndex(s => !s.italic)
607
+ selection.forEach(el => {
608
+ el.italic = !!~noItalicIndex
609
+ })
610
+ this.draw.render({ isSetCursor: false })
611
+ } else {
612
+ let isSubmitHistory = true
613
+ const { endIndex } = this.range.getRange()
614
+ const elementList = this.draw.getElementList()
615
+ const enterElement = elementList[endIndex]
616
+ this.range.setDefaultStyle({
617
+ italic: enterElement.italic
618
+ ? false
619
+ : !this.range.getDefaultStyle()?.italic
620
+ })
621
+ if (enterElement?.value === ZERO) {
622
+ enterElement.italic = !enterElement.italic
623
+ } else {
624
+ isSubmitHistory = false
625
+ }
626
+ this.draw.render({
627
+ isSubmitHistory,
628
+ curIndex: endIndex,
629
+ isCompute: false
630
+ })
631
+ }
632
+ }
633
+
634
+ public underline(
635
+ textDecoration?: ITextDecoration,
636
+ options?: IRichtextOption
637
+ ) {
638
+ const { isIgnoreDisabledRule = false } = options || {}
639
+ const isDisabled =
640
+ !isIgnoreDisabledRule &&
641
+ (this.draw.isReadonly() || this.draw.isDisabled())
642
+ if (isDisabled) return
643
+ const selection = this.range.getSelectionElementList()
644
+ if (selection?.length) {
645
+ // 没有设置下划线、当前与之前有一个设置不存在、文本装饰不一致时重设下划线
646
+ const isSetUnderline = selection.some(
647
+ s =>
648
+ !s.underline ||
649
+ (!textDecoration && s.textDecoration) ||
650
+ (textDecoration && !s.textDecoration) ||
651
+ (textDecoration &&
652
+ s.textDecoration &&
653
+ !isObjectEqual(s.textDecoration, textDecoration))
654
+ )
655
+ selection.forEach(el => {
656
+ el.underline = isSetUnderline
657
+ if (isSetUnderline && textDecoration) {
658
+ el.textDecoration = textDecoration
659
+ } else {
660
+ delete el.textDecoration
661
+ }
662
+ })
663
+ this.draw.render({
664
+ isSetCursor: false,
665
+ isCompute: false
666
+ })
667
+ } else {
668
+ let isSubmitHistory = true
669
+ const { endIndex } = this.range.getRange()
670
+ const elementList = this.draw.getElementList()
671
+ const enterElement = elementList[endIndex]
672
+ this.range.setDefaultStyle({
673
+ underline: enterElement?.underline
674
+ ? false
675
+ : !this.range.getDefaultStyle()?.underline
676
+ })
677
+ if (enterElement?.value === ZERO) {
678
+ enterElement.underline = !enterElement.underline
679
+ } else {
680
+ isSubmitHistory = false
681
+ }
682
+ this.draw.render({
683
+ isSubmitHistory,
684
+ curIndex: endIndex,
685
+ isCompute: false
686
+ })
687
+ }
688
+ }
689
+
690
+ public strikeout(options?: IRichtextOption) {
691
+ const { isIgnoreDisabledRule = false } = options || {}
692
+ const isDisabled =
693
+ !isIgnoreDisabledRule &&
694
+ (this.draw.isReadonly() || this.draw.isDisabled())
695
+ if (isDisabled) return
696
+ const selection = this.range.getSelectionElementList()
697
+ if (selection?.length) {
698
+ const noStrikeoutIndex = selection.findIndex(s => !s.strikeout)
699
+ selection.forEach(el => {
700
+ el.strikeout = !!~noStrikeoutIndex
701
+ })
702
+ this.draw.render({
703
+ isSetCursor: false,
704
+ isCompute: false
705
+ })
706
+ } else {
707
+ let isSubmitHistory = true
708
+ const { endIndex } = this.range.getRange()
709
+ const elementList = this.draw.getElementList()
710
+ const enterElement = elementList[endIndex]
711
+ this.range.setDefaultStyle({
712
+ strikeout: enterElement.strikeout
713
+ ? false
714
+ : !this.range.getDefaultStyle()?.strikeout
715
+ })
716
+ if (enterElement?.value === ZERO) {
717
+ enterElement.strikeout = !enterElement.strikeout
718
+ } else {
719
+ isSubmitHistory = false
720
+ }
721
+ this.draw.render({
722
+ isSubmitHistory,
723
+ curIndex: endIndex,
724
+ isCompute: false
725
+ })
726
+ }
727
+ }
728
+
729
+ public superscript(options?: IRichtextOption) {
730
+ const { isIgnoreDisabledRule = false } = options || {}
731
+ const isDisabled =
732
+ !isIgnoreDisabledRule &&
733
+ (this.draw.isReadonly() || this.draw.isDisabled())
734
+ if (isDisabled) return
735
+ const selection = this.range.getSelectionElementList()
736
+ if (!selection) return
737
+ const superscriptIndex = selection.findIndex(
738
+ s => s.type === ElementType.SUPERSCRIPT
739
+ )
740
+ selection.forEach(el => {
741
+ // 取消上标
742
+ if (~superscriptIndex) {
743
+ if (el.type === ElementType.SUPERSCRIPT) {
744
+ el.type = ElementType.TEXT
745
+ delete el.actualSize
746
+ }
747
+ } else {
748
+ // 设置上标
749
+ if (
750
+ !el.type ||
751
+ el.type === ElementType.TEXT ||
752
+ el.type === ElementType.SUBSCRIPT
753
+ ) {
754
+ el.type = ElementType.SUPERSCRIPT
755
+ }
756
+ }
757
+ })
758
+ this.draw.render({ isSetCursor: false })
759
+ }
760
+
761
+ public subscript(options?: IRichtextOption) {
762
+ const { isIgnoreDisabledRule = false } = options || {}
763
+ const isDisabled =
764
+ !isIgnoreDisabledRule &&
765
+ (this.draw.isReadonly() || this.draw.isDisabled())
766
+ if (isDisabled) return
767
+ const selection = this.range.getSelectionElementList()
768
+ if (!selection) return
769
+ const subscriptIndex = selection.findIndex(
770
+ s => s.type === ElementType.SUBSCRIPT
771
+ )
772
+ selection.forEach(el => {
773
+ // 取消下标
774
+ if (~subscriptIndex) {
775
+ if (el.type === ElementType.SUBSCRIPT) {
776
+ el.type = ElementType.TEXT
777
+ delete el.actualSize
778
+ }
779
+ } else {
780
+ // 设置下标
781
+ if (
782
+ !el.type ||
783
+ el.type === ElementType.TEXT ||
784
+ el.type === ElementType.SUPERSCRIPT
785
+ ) {
786
+ el.type = ElementType.SUBSCRIPT
787
+ }
788
+ }
789
+ })
790
+ this.draw.render({ isSetCursor: false })
791
+ }
792
+
793
+ public color(payload: string | null, options?: IRichtextOption) {
794
+ const { isIgnoreDisabledRule = false } = options || {}
795
+ const isDisabled =
796
+ !isIgnoreDisabledRule &&
797
+ (this.draw.isReadonly() || this.draw.isDisabled())
798
+ if (isDisabled) return
799
+ const selection = this.range.getSelectionElementList()
800
+ if (selection?.length) {
801
+ selection.forEach(el => {
802
+ if (payload) {
803
+ el.color = payload
804
+ } else {
805
+ delete el.color
806
+ }
807
+ })
808
+ this.draw.render({
809
+ isSetCursor: false,
810
+ isCompute: false
811
+ })
812
+ } else {
813
+ let isSubmitHistory = true
814
+ const { endIndex } = this.range.getRange()
815
+ const elementList = this.draw.getElementList()
816
+ const enterElement = elementList[endIndex]
817
+ this.range.setDefaultStyle({
818
+ color: payload || undefined
819
+ })
820
+ if (enterElement?.value === ZERO) {
821
+ if (payload) {
822
+ enterElement.color = payload
823
+ } else {
824
+ delete enterElement.color
825
+ }
826
+ } else {
827
+ isSubmitHistory = false
828
+ }
829
+ this.draw.render({
830
+ isSubmitHistory,
831
+ curIndex: endIndex,
832
+ isCompute: false
833
+ })
834
+ }
835
+ }
836
+
837
+ public highlight(payload: string | null, options?: IRichtextOption) {
838
+ const { isIgnoreDisabledRule = false } = options || {}
839
+ const isDisabled =
840
+ !isIgnoreDisabledRule &&
841
+ (this.draw.isReadonly() || this.draw.isDisabled())
842
+ if (isDisabled) return
843
+ const selection = this.range.getSelectionElementList()
844
+ if (selection?.length) {
845
+ selection.forEach(el => {
846
+ if (payload) {
847
+ el.highlight = payload
848
+ } else {
849
+ delete el.highlight
850
+ }
851
+ })
852
+ this.draw.render({
853
+ isSetCursor: false,
854
+ isCompute: false
855
+ })
856
+ } else {
857
+ let isSubmitHistory = true
858
+ const { endIndex } = this.range.getRange()
859
+ const elementList = this.draw.getElementList()
860
+ const enterElement = elementList[endIndex]
861
+ this.range.setDefaultStyle({
862
+ highlight: payload || undefined
863
+ })
864
+ if (enterElement?.value === ZERO) {
865
+ if (payload) {
866
+ enterElement.highlight = payload
867
+ } else {
868
+ delete enterElement.highlight
869
+ }
870
+ } else {
871
+ isSubmitHistory = false
872
+ }
873
+ this.draw.render({
874
+ isSubmitHistory,
875
+ curIndex: endIndex,
876
+ isCompute: false
877
+ })
878
+ }
879
+ }
880
+
881
+ public title(payload: TitleLevel | null) {
882
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
883
+ if (isDisabled) return
884
+ const { startIndex, endIndex } = this.range.getRange()
885
+ if (!~startIndex && !~endIndex) return
886
+ const elementList = this.draw.getElementList()
887
+ // 需要改变的元素列表
888
+ const changeElementList =
889
+ startIndex === endIndex
890
+ ? this.range.getRangeParagraphElementList()
891
+ : elementList.slice(startIndex + 1, endIndex + 1)
892
+ if (!changeElementList || !changeElementList.length) return
893
+ // 设置值
894
+ const titleId = getUUID()
895
+ const titleOptions = this.draw.getOptions().title
896
+ changeElementList.forEach(el => {
897
+ if (!el.type && el.value === ZERO) return
898
+ if (payload) {
899
+ el.level = payload
900
+ el.titleId = titleId
901
+ if (isTextLikeElement(el)) {
902
+ el.size = titleOptions[titleSizeMapping[payload]]
903
+ el.bold = true
904
+ }
905
+ } else {
906
+ if (el.titleId) {
907
+ delete el.titleId
908
+ delete el.title
909
+ delete el.level
910
+ delete el.size
911
+ delete el.bold
912
+ }
913
+ }
914
+ })
915
+ // 光标定位
916
+ const isSetCursor = startIndex === endIndex
917
+ const curIndex = isSetCursor ? endIndex : startIndex
918
+ this.draw.render({ curIndex, isSetCursor })
919
+ }
920
+
921
+ public list(listType: ListType | null, listStyle?: ListStyle) {
922
+ const isReadonly = this.draw.isReadonly()
923
+ if (isReadonly) return
924
+ this.draw.getListParticle().setList(listType, listStyle)
925
+ }
926
+
927
+ public rowFlex(payload: RowFlex) {
928
+ const isReadonly = this.draw.isReadonly()
929
+ if (isReadonly) return
930
+ const { startIndex, endIndex } = this.range.getRange()
931
+ if (!~startIndex && !~endIndex) return
932
+ const rowElementList = this.range.getRangeRowElementList()
933
+ if (!rowElementList) return
934
+ rowElementList.forEach(element => {
935
+ element.rowFlex = payload
936
+ })
937
+ // 光标定位
938
+ const isSetCursor = startIndex === endIndex
939
+ const curIndex = isSetCursor ? endIndex : startIndex
940
+ this.draw.render({ curIndex, isSetCursor })
941
+ }
942
+
943
+ public rowMargin(payload: number) {
944
+ const isReadonly = this.draw.isReadonly()
945
+ if (isReadonly) return
946
+ const { startIndex, endIndex } = this.range.getRange()
947
+ if (!~startIndex && !~endIndex) return
948
+ const rowElementList = this.range.getRangeRowElementList()
949
+ if (!rowElementList) return
950
+ rowElementList.forEach(element => {
951
+ element.rowMargin = payload
952
+ })
953
+ // 光标定位
954
+ const isSetCursor = startIndex === endIndex
955
+ const curIndex = isSetCursor ? endIndex : startIndex
956
+ this.draw.render({ curIndex, isSetCursor })
957
+ }
958
+
959
+ public insertTable(row: number, col: number) {
960
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
961
+ if (isDisabled) return
962
+ const activeControl = this.control.getActiveControl()
963
+ if (activeControl) return
964
+ this.tableOperate.insertTable(row, col)
965
+ }
966
+
967
+ public insertTableTopRow() {
968
+ const isReadonly = this.draw.isReadonly()
969
+ if (isReadonly) return
970
+ this.tableOperate.insertTableTopRow()
971
+ }
972
+
973
+ public insertTableBottomRow() {
974
+ const isReadonly = this.draw.isReadonly()
975
+ if (isReadonly) return
976
+ this.tableOperate.insertTableBottomRow()
977
+ }
978
+
979
+ public insertTableLeftCol() {
980
+ const isReadonly = this.draw.isReadonly()
981
+ if (isReadonly) return
982
+ this.tableOperate.insertTableLeftCol()
983
+ }
984
+
985
+ public insertTableRightCol() {
986
+ const isReadonly = this.draw.isReadonly()
987
+ if (isReadonly) return
988
+ this.tableOperate.insertTableRightCol()
989
+ }
990
+
991
+ public deleteTableRow() {
992
+ const isReadonly = this.draw.isReadonly()
993
+ if (isReadonly) return
994
+ this.tableOperate.deleteTableRow()
995
+ }
996
+
997
+ public deleteTableCol() {
998
+ const isReadonly = this.draw.isReadonly()
999
+ if (isReadonly) return
1000
+ this.tableOperate.deleteTableCol()
1001
+ }
1002
+
1003
+ public deleteTable() {
1004
+ const isReadonly = this.draw.isReadonly()
1005
+ if (isReadonly) return
1006
+ this.tableOperate.deleteTable()
1007
+ }
1008
+
1009
+ public mergeTableCell() {
1010
+ const isReadonly = this.draw.isReadonly()
1011
+ if (isReadonly) return
1012
+ this.tableOperate.mergeTableCell()
1013
+ }
1014
+
1015
+ public cancelMergeTableCell() {
1016
+ const isReadonly = this.draw.isReadonly()
1017
+ if (isReadonly) return
1018
+ this.tableOperate.cancelMergeTableCell()
1019
+ }
1020
+
1021
+ public splitVerticalTableCell() {
1022
+ const isReadonly = this.draw.isReadonly()
1023
+ if (isReadonly) return
1024
+ this.tableOperate.splitVerticalTableCell()
1025
+ }
1026
+
1027
+ public splitHorizontalTableCell() {
1028
+ const isReadonly = this.draw.isReadonly()
1029
+ if (isReadonly) return
1030
+ this.tableOperate.splitHorizontalTableCell()
1031
+ }
1032
+
1033
+ public tableTdVerticalAlign(payload: VerticalAlign) {
1034
+ const isReadonly = this.draw.isReadonly()
1035
+ if (isReadonly) return
1036
+ this.tableOperate.tableTdVerticalAlign(payload)
1037
+ }
1038
+
1039
+ public tableBorderType(payload: TableBorder) {
1040
+ const isReadonly = this.draw.isReadonly()
1041
+ if (isReadonly) return
1042
+ this.tableOperate.tableBorderType(payload)
1043
+ }
1044
+
1045
+ public tableBorderColor(payload: string) {
1046
+ const isReadonly = this.draw.isReadonly()
1047
+ if (isReadonly) return
1048
+ this.tableOperate.tableBorderColor(payload)
1049
+ }
1050
+
1051
+ public tableTdBorderType(payload: TdBorder) {
1052
+ const isReadonly = this.draw.isReadonly()
1053
+ if (isReadonly) return
1054
+ this.tableOperate.tableTdBorderType(payload)
1055
+ }
1056
+
1057
+ public tableTdSlashType(payload: TdSlash) {
1058
+ const isReadonly = this.draw.isReadonly()
1059
+ if (isReadonly) return
1060
+ this.tableOperate.tableTdSlashType(payload)
1061
+ }
1062
+
1063
+ public tableTdBackgroundColor(payload: string) {
1064
+ const isReadonly = this.draw.isReadonly()
1065
+ if (isReadonly) return
1066
+ this.tableOperate.tableTdBackgroundColor(payload)
1067
+ }
1068
+
1069
+ public tableSelectAll() {
1070
+ this.tableOperate.tableSelectAll()
1071
+ }
1072
+
1073
+ public hyperlink(
1074
+ payload: Pick<IElement, 'valueList' | 'hyperlinkId' | 'url'>
1075
+ ) {
1076
+ const { valueList, url, hyperlinkId } = payload
1077
+ if (!url || !valueList?.length) return
1078
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1079
+ if (isDisabled) return
1080
+ const activeControl = this.control.getActiveControl()
1081
+ if (activeControl) return
1082
+ const { startIndex, endIndex } = this.range.getRange()
1083
+ if (!~startIndex && !~endIndex) return
1084
+ this.insertElementList([
1085
+ {
1086
+ type: ElementType.HYPERLINK,
1087
+ value: '',
1088
+ valueList,
1089
+ url,
1090
+ hyperlinkId: hyperlinkId || getUUID()
1091
+ }
1092
+ ])
1093
+ }
1094
+
1095
+ public getHyperlinkRange(): [number, number] | null {
1096
+ let leftIndex = -1
1097
+ let rightIndex = -1
1098
+ const { startIndex, endIndex } = this.range.getRange()
1099
+ if (!~startIndex && !~endIndex) return null
1100
+ const elementList = this.draw.getElementList()
1101
+ const startElement = elementList[startIndex]
1102
+ if (startElement.type !== ElementType.HYPERLINK) return null
1103
+ // 向左查找
1104
+ let preIndex = startIndex
1105
+ while (preIndex > 0) {
1106
+ const preElement = elementList[preIndex]
1107
+ if (preElement.hyperlinkId !== startElement.hyperlinkId) {
1108
+ leftIndex = preIndex + 1
1109
+ break
1110
+ }
1111
+ preIndex--
1112
+ }
1113
+ // 向右查找
1114
+ let nextIndex = startIndex + 1
1115
+ while (nextIndex < elementList.length) {
1116
+ const nextElement = elementList[nextIndex]
1117
+ if (nextElement.hyperlinkId !== startElement.hyperlinkId) {
1118
+ rightIndex = nextIndex - 1
1119
+ break
1120
+ }
1121
+ nextIndex++
1122
+ }
1123
+ // 控件在最后
1124
+ if (nextIndex === elementList.length) {
1125
+ rightIndex = nextIndex - 1
1126
+ }
1127
+ if (!~leftIndex || !~rightIndex) return null
1128
+ return [leftIndex, rightIndex]
1129
+ }
1130
+
1131
+ public deleteHyperlink() {
1132
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1133
+ if (isDisabled) return
1134
+ // 获取超链接索引
1135
+ const hyperRange = this.getHyperlinkRange()
1136
+ if (!hyperRange) return
1137
+ const elementList = this.draw.getElementList()
1138
+ const [leftIndex, rightIndex] = hyperRange
1139
+ // 删除元素
1140
+ this.draw.spliceElementList(
1141
+ elementList,
1142
+ leftIndex,
1143
+ rightIndex - leftIndex + 1
1144
+ )
1145
+ this.draw.getHyperlinkParticle().clearHyperlinkPopup()
1146
+ // 重置画布
1147
+ const newIndex = leftIndex - 1
1148
+ this.range.setRange(newIndex, newIndex)
1149
+ this.draw.render({
1150
+ curIndex: newIndex
1151
+ })
1152
+ }
1153
+
1154
+ public cancelHyperlink() {
1155
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1156
+ if (isDisabled) return
1157
+ // 获取超链接索引
1158
+ const hyperRange = this.getHyperlinkRange()
1159
+ if (!hyperRange) return
1160
+ const elementList = this.draw.getElementList()
1161
+ const [leftIndex, rightIndex] = hyperRange
1162
+ // 删除属性
1163
+ for (let i = leftIndex; i <= rightIndex; i++) {
1164
+ const element = elementList[i]
1165
+ delete element.type
1166
+ delete element.url
1167
+ delete element.hyperlinkId
1168
+ delete element.underline
1169
+ }
1170
+ this.draw.getHyperlinkParticle().clearHyperlinkPopup()
1171
+ // 重置画布
1172
+ const { endIndex } = this.range.getRange()
1173
+ this.draw.render({
1174
+ curIndex: endIndex,
1175
+ isCompute: false
1176
+ })
1177
+ }
1178
+
1179
+ public editHyperlink(payload: string) {
1180
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1181
+ if (isDisabled) return
1182
+ // 获取超链接索引
1183
+ const hyperRange = this.getHyperlinkRange()
1184
+ if (!hyperRange) return
1185
+ const elementList = this.draw.getElementList()
1186
+ const [leftIndex, rightIndex] = hyperRange
1187
+ // 替换url
1188
+ for (let i = leftIndex; i <= rightIndex; i++) {
1189
+ const element = elementList[i]
1190
+ element.url = payload
1191
+ }
1192
+ this.draw.getHyperlinkParticle().clearHyperlinkPopup()
1193
+ // 重置画布
1194
+ const { endIndex } = this.range.getRange()
1195
+ this.draw.render({
1196
+ curIndex: endIndex,
1197
+ isCompute: false
1198
+ })
1199
+ }
1200
+
1201
+ public separator(
1202
+ dashArray: number[],
1203
+ option?: { lineWidth?: number; color?: string }
1204
+ ) {
1205
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1206
+ if (isDisabled) return
1207
+ const activeControl = this.control.getActiveControl()
1208
+ if (activeControl) return
1209
+ const { startIndex, endIndex } = this.range.getRange()
1210
+ if (!~startIndex && !~endIndex) return
1211
+ const elementList = this.draw.getElementList()
1212
+ let curIndex = -1
1213
+ // 光标存在分割线,则判断为修改线段逻辑
1214
+ const endElement = elementList[endIndex + 1]
1215
+ if (endElement && endElement.type === ElementType.SEPARATOR) {
1216
+ if (
1217
+ endElement.dashArray &&
1218
+ endElement.dashArray.join() === dashArray.join()
1219
+ ) {
1220
+ return
1221
+ }
1222
+ curIndex = endIndex
1223
+ Object.assign(endElement, {
1224
+ dashArray,
1225
+ ...option
1226
+ })
1227
+ } else {
1228
+ const newElement: IElement = {
1229
+ value: WRAP,
1230
+ type: ElementType.SEPARATOR,
1231
+ dashArray,
1232
+ ...option
1233
+ }
1234
+ // 从行头增加分割线
1235
+ formatElementContext(elementList, [newElement], startIndex, {
1236
+ editorOptions: this.options
1237
+ })
1238
+ if (startIndex !== 0 && elementList[startIndex].value === ZERO) {
1239
+ this.draw.spliceElementList(elementList, startIndex, 1, [newElement])
1240
+ curIndex = startIndex - 1
1241
+ } else {
1242
+ this.draw.spliceElementList(elementList, startIndex + 1, 0, [
1243
+ newElement
1244
+ ])
1245
+ curIndex = startIndex
1246
+ }
1247
+ }
1248
+ this.range.setRange(curIndex, curIndex)
1249
+ this.draw.render({ curIndex })
1250
+ }
1251
+
1252
+ public pageBreak() {
1253
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1254
+ if (isDisabled) return
1255
+ const activeControl = this.control.getActiveControl()
1256
+ if (activeControl) return
1257
+ this.insertElementList([
1258
+ {
1259
+ type: ElementType.PAGE_BREAK,
1260
+ value: WRAP
1261
+ }
1262
+ ])
1263
+ }
1264
+
1265
+ public addWatermark(payload: IWatermark) {
1266
+ const isReadonly = this.draw.isReadonly()
1267
+ if (isReadonly) return
1268
+ const options = this.draw.getOptions()
1269
+ const { color, size, opacity, font, gap } = defaultWatermarkOption
1270
+ options.watermark.data = payload.data
1271
+ options.watermark.type = payload.type || WatermarkType.TEXT
1272
+ if (payload.width) {
1273
+ options.watermark.width = payload.width
1274
+ }
1275
+ if (payload.height) {
1276
+ options.watermark.height = payload.height
1277
+ }
1278
+ options.watermark.color = payload.color || color
1279
+ options.watermark.opacity = payload.opacity || opacity
1280
+ options.watermark.size = payload.size || size
1281
+ options.watermark.font = payload.font || font
1282
+ options.watermark.repeat = !!payload.repeat
1283
+ if (payload.numberType) {
1284
+ options.watermark.numberType = payload.numberType
1285
+ }
1286
+ options.watermark.gap = payload.gap || gap
1287
+ this.draw.render({
1288
+ isSetCursor: false,
1289
+ isSubmitHistory: false,
1290
+ isCompute: false
1291
+ })
1292
+ }
1293
+
1294
+ public deleteWatermark() {
1295
+ const isReadonly = this.draw.isReadonly()
1296
+ if (isReadonly) return
1297
+ const options = this.draw.getOptions()
1298
+ if (options.watermark && options.watermark.data) {
1299
+ options.watermark = { ...defaultWatermarkOption }
1300
+ this.draw.render({
1301
+ isSetCursor: false,
1302
+ isSubmitHistory: false,
1303
+ isCompute: false
1304
+ })
1305
+ }
1306
+ }
1307
+
1308
+ public image(payload: IDrawImagePayload): string | null {
1309
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1310
+ if (isDisabled) return null
1311
+ const { startIndex, endIndex } = this.range.getRange()
1312
+ if (!~startIndex && !~endIndex) return null
1313
+ const imageId = payload.id || getUUID()
1314
+ this.insertElementList([
1315
+ {
1316
+ ...payload,
1317
+ id: imageId,
1318
+ type: ElementType.IMAGE
1319
+ }
1320
+ ])
1321
+ return imageId
1322
+ }
1323
+
1324
+ public search(payload: string | null, options?: ISearchOption) {
1325
+ this.searchManager.setSearchKeyword(payload, options)
1326
+ this.draw.render({
1327
+ isSubmitHistory: false
1328
+ })
1329
+ }
1330
+
1331
+ public searchNavigatePre() {
1332
+ const index = this.searchManager.searchNavigatePre()
1333
+ if (index === null) return
1334
+ this.draw.render({
1335
+ isSetCursor: false,
1336
+ isSubmitHistory: false,
1337
+ isCompute: false,
1338
+ isLazy: false
1339
+ })
1340
+ }
1341
+
1342
+ public searchNavigateNext() {
1343
+ const index = this.searchManager.searchNavigateNext()
1344
+ if (index === null) return
1345
+ this.draw.render({
1346
+ isSetCursor: false,
1347
+ isSubmitHistory: false,
1348
+ isCompute: false,
1349
+ isLazy: false
1350
+ })
1351
+ }
1352
+
1353
+ public getSearchNavigateInfo(): null | INavigateInfo {
1354
+ return this.searchManager.getSearchNavigateInfo()
1355
+ }
1356
+
1357
+ public replace(payload: string, option?: IReplaceOption) {
1358
+ this.draw.getSearch().replace(payload, option)
1359
+ }
1360
+
1361
+ public async print() {
1362
+ const { scale, printPixelRatio, paperDirection, width, height } =
1363
+ this.options
1364
+ if (scale !== 1) {
1365
+ this.draw.setPageScale(1)
1366
+ }
1367
+ const base64List = await this.draw.getDataURL({
1368
+ pixelRatio: printPixelRatio,
1369
+ mode: EditorMode.PRINT
1370
+ })
1371
+ printImageBase64(base64List, {
1372
+ width,
1373
+ height,
1374
+ direction: paperDirection
1375
+ })
1376
+ if (scale !== 1) {
1377
+ this.draw.setPageScale(scale)
1378
+ }
1379
+ }
1380
+
1381
+ public replaceImageElement(payload: string) {
1382
+ const { startIndex } = this.range.getRange()
1383
+ const elementList = this.draw.getElementList()
1384
+ const element = elementList[startIndex]
1385
+ if (!element || element.type !== ElementType.IMAGE) return
1386
+ element.value = payload
1387
+ this.draw.render({
1388
+ isSetCursor: false
1389
+ })
1390
+ }
1391
+
1392
+ public saveAsImageElement() {
1393
+ const { startIndex } = this.range.getRange()
1394
+ const elementList = this.draw.getElementList()
1395
+ const element = elementList[startIndex]
1396
+ if (!element || element.type !== ElementType.IMAGE) return
1397
+ downloadFile(element.value, `${element.id!}.png`)
1398
+ }
1399
+
1400
+ public setImageCrop(crop: IImageCrop) {
1401
+ const { startIndex } = this.range.getRange()
1402
+ const elementList = this.draw.getElementList()
1403
+ const element = elementList[startIndex]
1404
+ if (!element || element.type !== ElementType.IMAGE) return
1405
+ element.imgCrop = crop
1406
+ this.draw.render({
1407
+ isSetCursor: false,
1408
+ isCompute: false
1409
+ })
1410
+ }
1411
+
1412
+ public setImageCaption(imgCaption: IImageCaption) {
1413
+ const { startIndex } = this.range.getRange()
1414
+ const elementList = this.draw.getElementList()
1415
+ const element = elementList[startIndex]
1416
+ if (element?.type !== ElementType.IMAGE) return
1417
+ element.imgCaption = imgCaption
1418
+ this.draw.render({
1419
+ isSetCursor: false
1420
+ })
1421
+ }
1422
+
1423
+ public changeImageDisplay(element: IElement, display: ImageDisplay) {
1424
+ if (element.imgDisplay === display) return
1425
+ element.imgDisplay = display
1426
+ const { startIndex, endIndex } = this.range.getRange()
1427
+ if (
1428
+ display === ImageDisplay.SURROUND ||
1429
+ display === ImageDisplay.FLOAT_TOP ||
1430
+ display === ImageDisplay.FLOAT_BOTTOM
1431
+ ) {
1432
+ const positionList = this.position.getPositionList()
1433
+ const {
1434
+ pageNo,
1435
+ coordinate: { leftTop }
1436
+ } = positionList[startIndex]
1437
+ element.imgFloatPosition = {
1438
+ pageNo,
1439
+ x: leftTop[0],
1440
+ y: leftTop[1]
1441
+ }
1442
+ } else {
1443
+ delete element.imgFloatPosition
1444
+ }
1445
+ this.draw.getPreviewer().clearResizer()
1446
+ this.draw.render({
1447
+ isSetCursor: true,
1448
+ curIndex: endIndex
1449
+ })
1450
+ }
1451
+
1452
+ public getImage(payload?: IGetImageOption): Promise<string[]> {
1453
+ return this.draw.getDataURL(payload)
1454
+ }
1455
+
1456
+ public getOptions(): DeepRequired<IEditorOption> {
1457
+ return this.options
1458
+ }
1459
+
1460
+ public getValue(options?: IGetValueOption): IEditorResult {
1461
+ return this.draw.getValue(options)
1462
+ }
1463
+
1464
+ public getValueAsync(options?: IGetValueOption): Promise<IEditorResult> {
1465
+ return this.draw.getWorkerManager().getValue(options)
1466
+ }
1467
+
1468
+ public getAreaValue(
1469
+ options?: IGetAreaValueOption
1470
+ ): IGetAreaValueResult | null {
1471
+ return this.draw.getArea().getAreaValue(options)
1472
+ }
1473
+
1474
+ public getHTML(): IEditorHTML {
1475
+ const options = this.options
1476
+ const headerElementList = this.draw.getHeaderElementList()
1477
+ const mainElementList = this.draw.getOriginalMainElementList()
1478
+ const footerElementList = this.draw.getFooterElementList()
1479
+ return {
1480
+ header: createDomFromElementList(headerElementList, options).innerHTML,
1481
+ main: createDomFromElementList(mainElementList, options).innerHTML,
1482
+ footer: createDomFromElementList(footerElementList, options).innerHTML
1483
+ }
1484
+ }
1485
+
1486
+ public getText(): IEditorText {
1487
+ const headerElementList = this.draw.getHeaderElementList()
1488
+ const mainElementList = this.draw.getOriginalMainElementList()
1489
+ const footerElementList = this.draw.getFooterElementList()
1490
+ return {
1491
+ header: getTextFromElementList(headerElementList),
1492
+ main: getTextFromElementList(mainElementList),
1493
+ footer: getTextFromElementList(footerElementList)
1494
+ }
1495
+ }
1496
+
1497
+ public getWordCount(): Promise<number> {
1498
+ return this.workerManager.getWordCount()
1499
+ }
1500
+
1501
+ public getCursorPosition(): IElementPosition | null {
1502
+ return this.position.getCursorPosition()
1503
+ }
1504
+
1505
+ public getRemainingContentHeight(): number {
1506
+ if (!this.draw.getIsPagingMode()) return 0
1507
+ const pageRowList = this.draw.getPageRowList()
1508
+ const lastPageIndex = pageRowList.length - 1
1509
+ const rowList = pageRowList[lastPageIndex] || []
1510
+ const usedHeight = rowList.reduce(
1511
+ (pre, cur) => pre + cur.height + (cur.offsetY || 0),
1512
+ 0
1513
+ )
1514
+ const height = this.draw.getHeight()
1515
+ const mainOuterHeight = this.draw.getMainOuterHeight()
1516
+ const remaining = height - (mainOuterHeight + usedHeight)
1517
+ return remaining > 0 ? remaining : 0
1518
+ }
1519
+
1520
+ public computeElementListHeight(elementList: IElement[]): number {
1521
+ if (!elementList.length) return 0
1522
+ const innerWidth = this.draw.getInnerWidth()
1523
+ if (innerWidth <= 0) return 0
1524
+ const targetElementList = deepClone(elementList)
1525
+ formatElementList(targetElementList, {
1526
+ isHandleFirstElement: false,
1527
+ editorOptions: this.options
1528
+ })
1529
+ const surroundElementList = pickSurroundElementList(targetElementList)
1530
+ const rowList = this.draw.computeRowList({
1531
+ innerWidth,
1532
+ elementList: targetElementList,
1533
+ surroundElementList
1534
+ })
1535
+ return rowList.reduce(
1536
+ (pre, cur) => pre + cur.height + (cur.offsetY || 0),
1537
+ 0
1538
+ )
1539
+ }
1540
+
1541
+ public getRange(): IRange {
1542
+ return deepClone(this.range.getRange())
1543
+ }
1544
+
1545
+ public getRangeText(): string {
1546
+ return this.range.toString()
1547
+ }
1548
+
1549
+ public getRangeContext(): RangeContext | null {
1550
+ const range = this.range.getRange()
1551
+ const { startIndex, endIndex } = range
1552
+ if (!~startIndex && !~endIndex) return null
1553
+ // 选区信息
1554
+ const isCollapsed = startIndex === endIndex
1555
+ const selectionText = this.range.toString()
1556
+ const selectionElementList = zipElementList(
1557
+ this.range.getSelectionElementList() || []
1558
+ )
1559
+ // 元素信息
1560
+ const elementList = this.draw.getElementList()
1561
+ const startElement = pickElementAttr(
1562
+ elementList[isCollapsed ? startIndex : startIndex + 1],
1563
+ {
1564
+ extraPickAttrs: ['id', 'controlComponent']
1565
+ }
1566
+ )
1567
+ const endElement = pickElementAttr(elementList[endIndex], {
1568
+ extraPickAttrs: ['id', 'controlComponent']
1569
+ })
1570
+ // 页码信息、行信息
1571
+ const rowList = this.draw.getRowList()
1572
+ const positionList = this.position.getPositionList()
1573
+ const startPosition = positionList[startIndex]
1574
+ const endPosition = positionList[endIndex]
1575
+ const startPageNo = startPosition.pageNo
1576
+ const endPageNo = endPosition.pageNo
1577
+ const startRowNo = startPosition.rowIndex
1578
+ const endRowNo = endPosition.rowIndex
1579
+ // 列信息
1580
+ const startRow = rowList[startRowNo]
1581
+ const endRow = rowList[endRowNo]
1582
+ let startColNo = 0
1583
+ let endColNo = 0
1584
+ // 以光标显示位置为准
1585
+ if (!this.draw.getCursor().getHitLineStartIndex()) {
1586
+ // 换行符不计算列数量
1587
+ startColNo =
1588
+ startRow.elementList[0]?.value === ZERO
1589
+ ? startPosition.index! - startRow.startIndex
1590
+ : startPosition.index! - startRow.startIndex + 1
1591
+ }
1592
+ // 光标闭合时列位置相同
1593
+ if (startPosition === endPosition) {
1594
+ endColNo = startColNo
1595
+ } else {
1596
+ endColNo =
1597
+ endRow.elementList[0]?.value === ZERO
1598
+ ? endPosition.index! - endRow.startIndex
1599
+ : endPosition.index! - endRow.startIndex + 1
1600
+ }
1601
+
1602
+ // 坐标信息(相对编辑器书写区)
1603
+ const rangeRects: RangeRect[] = []
1604
+ const height = this.draw.getOriginalHeight()
1605
+ const pageGap = this.draw.getOriginalPageGap()
1606
+ const selectionPositionList = this.position.getSelectionPositionList()
1607
+ if (selectionPositionList) {
1608
+ // 起始信息及x坐标
1609
+ let currentRowNo: number | null = null
1610
+ let currentX = 0
1611
+ let rangeRect: RangeRect | null = null
1612
+ for (let p = 0; p < selectionPositionList.length; p++) {
1613
+ const {
1614
+ rowNo,
1615
+ pageNo,
1616
+ coordinate: { leftTop, rightTop },
1617
+ lineHeight
1618
+ } = selectionPositionList[p]
1619
+ // 起始行变化追加选区信息
1620
+ if (currentRowNo === null || currentRowNo !== rowNo) {
1621
+ if (rangeRect) {
1622
+ rangeRects.push(rangeRect)
1623
+ }
1624
+ rangeRect = {
1625
+ x: leftTop[0],
1626
+ y: leftTop[1] + pageNo * (height + pageGap),
1627
+ width: rightTop[0] - leftTop[0],
1628
+ height: lineHeight
1629
+ }
1630
+ currentRowNo = rowNo
1631
+ currentX = leftTop[0]
1632
+ } else {
1633
+ rangeRect!.width = rightTop[0] - currentX
1634
+ }
1635
+ // 最后一个元素结束追加选区信息
1636
+ if (p === selectionPositionList.length - 1 && rangeRect) {
1637
+ rangeRects.push(rangeRect)
1638
+ }
1639
+ }
1640
+ } else {
1641
+ const positionList = this.position.getPositionList()
1642
+ const position = positionList[endIndex]
1643
+ const {
1644
+ coordinate: { rightTop },
1645
+ pageNo,
1646
+ lineHeight
1647
+ } = position
1648
+ rangeRects.push({
1649
+ x: rightTop[0],
1650
+ y: rightTop[1] + pageNo * (height + pageGap),
1651
+ width: 0,
1652
+ height: lineHeight
1653
+ })
1654
+ }
1655
+ // 区域信息
1656
+ const zone = this.draw.getZone().getZone()
1657
+ // 表格信息
1658
+ const { isTable, trIndex, tdIndex, index } =
1659
+ this.position.getPositionContext()
1660
+ let tableElement: IElement | null = null
1661
+ if (isTable) {
1662
+ const originalElementList = this.draw.getOriginalElementList()
1663
+ const originTableElement = originalElementList[index!] || null
1664
+ if (originTableElement) {
1665
+ tableElement = zipElementList([originTableElement])[0]
1666
+ }
1667
+ }
1668
+ // 标题信息
1669
+ let titleId: string | null = null
1670
+ let titleStartPageNo: number | null = null
1671
+ let start = startIndex - 1
1672
+ while (start > 0) {
1673
+ const curElement = elementList[start]
1674
+ const preElement = elementList[start - 1]
1675
+ if (curElement.titleId && curElement.titleId !== preElement?.titleId) {
1676
+ titleId = curElement.titleId
1677
+ titleStartPageNo = positionList[start].pageNo
1678
+ break
1679
+ }
1680
+ start--
1681
+ }
1682
+ // 段落索引
1683
+ const startParagraphNo = getParagraphNo(elementList, startIndex)
1684
+ const endParagraphNo =
1685
+ startIndex === endIndex
1686
+ ? startParagraphNo
1687
+ : getParagraphNo(elementList, endIndex)
1688
+ return deepClone<RangeContext>({
1689
+ isCollapsed,
1690
+ startElement,
1691
+ endElement,
1692
+ startPageNo,
1693
+ endPageNo,
1694
+ startRowNo,
1695
+ endRowNo,
1696
+ startColNo,
1697
+ endColNo,
1698
+ rangeRects,
1699
+ zone,
1700
+ isTable,
1701
+ trIndex: trIndex ?? null,
1702
+ tdIndex: tdIndex ?? null,
1703
+ tableElement,
1704
+ selectionText,
1705
+ selectionElementList,
1706
+ titleId,
1707
+ titleStartPageNo,
1708
+ startParagraphNo,
1709
+ endParagraphNo
1710
+ })
1711
+ }
1712
+
1713
+ public getRangeRow(): IElement[] | null {
1714
+ const rowElementList = this.range.getRangeRowElementList()
1715
+ return rowElementList ? zipElementList(rowElementList) : null
1716
+ }
1717
+
1718
+ public getRangeParagraph(): IElement[] | null {
1719
+ const paragraphElementList = this.range.getRangeParagraphElementList()
1720
+ return paragraphElementList ? zipElementList(paragraphElementList) : null
1721
+ }
1722
+
1723
+ public getKeywordRangeList(payload: string): IRange[] {
1724
+ return this.range.getKeywordRangeList(payload)
1725
+ }
1726
+
1727
+ public getKeywordContext(payload: string): ISearchResultContext[] | null {
1728
+ const rangeList = this.getKeywordRangeList(payload)
1729
+ if (!rangeList.length) return null
1730
+ const searchResultContextList: ISearchResultContext[] = []
1731
+ const positionList = this.position.getOriginalMainPositionList()
1732
+ const elementList = this.draw.getOriginalMainElementList()
1733
+ for (let r = 0; r < rangeList.length; r++) {
1734
+ const range = rangeList[r]
1735
+ const { startIndex, endIndex, tableId, startTrIndex, startTdIndex } =
1736
+ range
1737
+ let keywordPositionList: IElementPosition[] = positionList
1738
+ if (range.tableId) {
1739
+ const tableElement = elementList.find(el => el.id === tableId)
1740
+ if (tableElement) {
1741
+ keywordPositionList =
1742
+ tableElement.trList?.[startTrIndex!]?.tdList?.[startTdIndex!]
1743
+ ?.positionList || []
1744
+ }
1745
+ }
1746
+ // 获取关键词始末位置
1747
+ const startPosition = deepClone(keywordPositionList[startIndex])
1748
+ const endPosition = deepClone(keywordPositionList[endIndex])
1749
+ searchResultContextList.push({
1750
+ range,
1751
+ startPosition,
1752
+ endPosition
1753
+ })
1754
+ }
1755
+ return searchResultContextList
1756
+ }
1757
+
1758
+ public pageMode(payload: PageMode) {
1759
+ this.draw.setPageMode(payload)
1760
+ }
1761
+
1762
+ public pageScale(scale: number) {
1763
+ if (scale === this.options.scale) return
1764
+ this.draw.setPageScale(scale)
1765
+ }
1766
+
1767
+ public pageScaleRecovery() {
1768
+ const { scale } = this.options
1769
+ if (scale !== 1) {
1770
+ this.draw.setPageScale(1)
1771
+ }
1772
+ }
1773
+
1774
+ public pageScaleMinus() {
1775
+ const { scale } = this.options
1776
+ const nextScale = scale * 10 - 1
1777
+ if (nextScale >= 5) {
1778
+ this.draw.setPageScale(nextScale / 10)
1779
+ }
1780
+ }
1781
+
1782
+ public pageScaleAdd() {
1783
+ const { scale } = this.options
1784
+ const nextScale = scale * 10 + 1
1785
+ if (nextScale <= 30) {
1786
+ this.draw.setPageScale(nextScale / 10)
1787
+ }
1788
+ }
1789
+
1790
+ public paperSize(width: number, height: number) {
1791
+ this.draw.setPaperSize(width, height)
1792
+ }
1793
+
1794
+ public paperDirection(payload: PaperDirection) {
1795
+ this.draw.setPaperDirection(payload)
1796
+ }
1797
+
1798
+ public getPaperMargin(): number[] {
1799
+ return this.options.margins
1800
+ }
1801
+
1802
+ public setPaperMargin(payload: IMargin) {
1803
+ return this.draw.setPaperMargin(payload)
1804
+ }
1805
+
1806
+ public setMainBadge(payload: IBadge | null) {
1807
+ this.draw.getBadge().setMainBadge(payload)
1808
+ this.draw.render({
1809
+ isCompute: false,
1810
+ isSubmitHistory: false
1811
+ })
1812
+ }
1813
+
1814
+ public setAreaBadge(payload: IAreaBadge[]) {
1815
+ this.draw.getBadge().setAreaBadgeMap(payload)
1816
+ this.draw.render({
1817
+ isCompute: false,
1818
+ isSubmitHistory: false
1819
+ })
1820
+ }
1821
+
1822
+ public insertElementList(
1823
+ payload: IElement[],
1824
+ options: IInsertElementListOption = {}
1825
+ ) {
1826
+ if (!payload.length) return
1827
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
1828
+ if (isDisabled) return
1829
+ const { isReplace = true, ignoreContextKeys } = options
1830
+ // 如果配置不替换时,需收缩选区至末尾
1831
+ if (!isReplace) {
1832
+ this.range.shrinkRange()
1833
+ }
1834
+ const cloneElementList = deepClone(payload)
1835
+ // 格式化上下文信息
1836
+ const { startIndex } = this.range.getRange()
1837
+ const elementList = this.draw.getElementList()
1838
+ formatElementContext(elementList, cloneElementList, startIndex, {
1839
+ ignoreContextKeys,
1840
+ isBreakWhenWrap: true,
1841
+ editorOptions: this.options
1842
+ })
1843
+ this.draw.insertElementList(cloneElementList, options)
1844
+ }
1845
+
1846
+ public appendElementList(
1847
+ elementList: IElement[],
1848
+ options?: IAppendElementListOption
1849
+ ) {
1850
+ if (!elementList.length) return
1851
+ const isReadonly = this.draw.isReadonly()
1852
+ if (isReadonly) return
1853
+ this.draw.appendElementList(deepClone(elementList), options)
1854
+ }
1855
+
1856
+ public updateElementById(payload: IUpdateElementByIdOption) {
1857
+ const { id, conceptId } = payload
1858
+ if (!id && !conceptId) return
1859
+ const updateElementInfoList: {
1860
+ elementList: IElement[]
1861
+ index: number
1862
+ }[] = []
1863
+ function getElementInfoById(elementList: IElement[]) {
1864
+ let i = 0
1865
+ while (i < elementList.length) {
1866
+ const element = elementList[i]
1867
+ i++
1868
+ if (element.type === ElementType.TABLE) {
1869
+ const trList = element.trList!
1870
+ for (let r = 0; r < trList.length; r++) {
1871
+ const tr = trList[r]
1872
+ for (let d = 0; d < tr.tdList.length; d++) {
1873
+ const td = tr.tdList[d]
1874
+ getElementInfoById(td.value)
1875
+ }
1876
+ }
1877
+ }
1878
+ if (
1879
+ (id && element.id === id) ||
1880
+ (conceptId && element.conceptId === conceptId)
1881
+ ) {
1882
+ updateElementInfoList.push({
1883
+ elementList,
1884
+ index: i - 1
1885
+ })
1886
+ }
1887
+ }
1888
+ }
1889
+ // 优先正文再页眉页脚
1890
+ const data = [
1891
+ this.draw.getOriginalMainElementList(),
1892
+ this.draw.getHeaderElementList(),
1893
+ this.draw.getFooterElementList()
1894
+ ]
1895
+ for (const elementList of data) {
1896
+ getElementInfoById(elementList)
1897
+ }
1898
+ // 更新内容
1899
+ if (!updateElementInfoList.length) return
1900
+ for (let i = 0; i < updateElementInfoList.length; i++) {
1901
+ const { elementList, index } = updateElementInfoList[i]
1902
+ // 重新格式化元素
1903
+ const oldElement = elementList[index]
1904
+ const newElement = zipElementList(
1905
+ [
1906
+ {
1907
+ ...oldElement,
1908
+ ...payload.properties
1909
+ }
1910
+ ],
1911
+ {
1912
+ extraPickAttrs: ['id']
1913
+ }
1914
+ )
1915
+ // 区域上下文提取
1916
+ cloneProperty<IElement>(AREA_CONTEXT_ATTR, oldElement, newElement[0])
1917
+ formatElementList(newElement, {
1918
+ isHandleFirstElement: false,
1919
+ editorOptions: this.options
1920
+ })
1921
+ elementList[index] = newElement[0]
1922
+ }
1923
+ this.draw.render({
1924
+ isSetCursor: false
1925
+ })
1926
+ }
1927
+
1928
+ public deleteElementById(payload: IDeleteElementByIdOption) {
1929
+ const { id, conceptId } = payload
1930
+ if (!id && !conceptId) return
1931
+ let isExistDelete = false
1932
+ function deleteElement(elementList: IElement[]) {
1933
+ let i = 0
1934
+ while (i < elementList.length) {
1935
+ const element = elementList[i]
1936
+ if (element.type === ElementType.TABLE) {
1937
+ const trList = element.trList!
1938
+ for (let r = 0; r < trList.length; r++) {
1939
+ const tr = trList[r]
1940
+ for (let d = 0; d < tr.tdList.length; d++) {
1941
+ const td = tr.tdList[d]
1942
+ deleteElement(td.value)
1943
+ }
1944
+ }
1945
+ }
1946
+ if (
1947
+ (id && element.id === id) ||
1948
+ (conceptId && element.conceptId === conceptId)
1949
+ ) {
1950
+ isExistDelete = true
1951
+ elementList.splice(i, 1)
1952
+ i--
1953
+ }
1954
+ i++
1955
+ }
1956
+ }
1957
+ // 优先正文再页眉页脚
1958
+ const data = [
1959
+ this.draw.getOriginalMainElementList(),
1960
+ this.draw.getHeaderElementList(),
1961
+ this.draw.getFooterElementList()
1962
+ ]
1963
+ for (const elementList of data) {
1964
+ deleteElement(elementList)
1965
+ }
1966
+ if (!isExistDelete) return
1967
+ this.draw.render({
1968
+ isSetCursor: false
1969
+ })
1970
+ }
1971
+
1972
+ public getElementById(payload: IGetElementByIdOption): IElement[] {
1973
+ const { id, conceptId } = payload
1974
+ const result: IElement[] = []
1975
+ if (!id && !conceptId) return result
1976
+ const getElement = (elementList: IElement[]) => {
1977
+ let i = 0
1978
+ while (i < elementList.length) {
1979
+ const element = elementList[i]
1980
+ i++
1981
+ if (element.type === ElementType.TABLE) {
1982
+ const trList = element.trList!
1983
+ for (let r = 0; r < trList.length; r++) {
1984
+ const tr = trList[r]
1985
+ for (let d = 0; d < tr.tdList.length; d++) {
1986
+ const td = tr.tdList[d]
1987
+ getElement(td.value)
1988
+ }
1989
+ }
1990
+ }
1991
+ if (
1992
+ (id && element.id !== id) ||
1993
+ (conceptId && element.conceptId !== conceptId)
1994
+ ) {
1995
+ continue
1996
+ }
1997
+ result.push(element)
1998
+ }
1999
+ }
2000
+ const data = [
2001
+ this.draw.getHeaderElementList(),
2002
+ this.draw.getOriginalMainElementList(),
2003
+ this.draw.getFooterElementList()
2004
+ ]
2005
+ for (const elementList of data) {
2006
+ getElement(elementList)
2007
+ }
2008
+ return zipElementList(result, {
2009
+ extraPickAttrs: ['id']
2010
+ })
2011
+ }
2012
+
2013
+ public setValue(payload: Partial<IEditorData>, options?: ISetValueOption) {
2014
+ this.draw.setValue(payload, options)
2015
+ }
2016
+
2017
+ public removeControl(payload?: IRemoveControlOption) {
2018
+ if (payload?.id || payload?.conceptId) {
2019
+ const { id, conceptId } = payload
2020
+ let isExistRemove = false
2021
+ const remove = (elementList: IElement[]) => {
2022
+ let i = elementList.length - 1
2023
+ while (i >= 0) {
2024
+ const element = elementList[i]
2025
+ if (element.type === ElementType.TABLE) {
2026
+ const trList = element.trList!
2027
+ for (let r = 0; r < trList.length; r++) {
2028
+ const tr = trList[r]
2029
+ for (let d = 0; d < tr.tdList.length; d++) {
2030
+ const td = tr.tdList[d]
2031
+ remove(td.value)
2032
+ }
2033
+ }
2034
+ }
2035
+ i--
2036
+ if (
2037
+ !element.control ||
2038
+ (id && element.controlId !== id) ||
2039
+ (conceptId && element.control.conceptId !== conceptId)
2040
+ ) {
2041
+ continue
2042
+ }
2043
+ isExistRemove = true
2044
+ elementList.splice(i + 1, 1)
2045
+ }
2046
+ }
2047
+ const data = [
2048
+ this.draw.getHeaderElementList(),
2049
+ this.draw.getOriginalMainElementList(),
2050
+ this.draw.getFooterElementList()
2051
+ ]
2052
+ for (const elementList of data) {
2053
+ remove(elementList)
2054
+ }
2055
+ if (isExistRemove) {
2056
+ this.draw.render({
2057
+ isSetCursor: false
2058
+ })
2059
+ }
2060
+ } else {
2061
+ const { startIndex, endIndex } = this.range.getRange()
2062
+ if (startIndex !== endIndex) return
2063
+ const elementList = this.draw.getElementList()
2064
+ const element = elementList[startIndex]
2065
+ if (!element.controlId) return
2066
+ // 删除控件
2067
+ const control = this.draw.getControl()
2068
+ const newIndex = control.removeControl(startIndex)
2069
+ if (newIndex === null) return
2070
+ // 重新渲染
2071
+ this.range.setRange(newIndex, newIndex)
2072
+ this.draw.render({
2073
+ curIndex: newIndex
2074
+ })
2075
+ }
2076
+ }
2077
+
2078
+ public translate(path: string): string {
2079
+ return this.i18n.t(path)
2080
+ }
2081
+
2082
+ public setLocale(payload: string) {
2083
+ this.i18n.setLocale(payload)
2084
+ }
2085
+
2086
+ public getLocale(): string {
2087
+ return this.i18n.getLocale()
2088
+ }
2089
+
2090
+ public getCatalog(): Promise<ICatalog | null> {
2091
+ return this.workerManager.getCatalog()
2092
+ }
2093
+
2094
+ public locationCatalog(titleId: string) {
2095
+ const elementList = this.draw.getOriginalElementList()
2096
+
2097
+ function getPosition(
2098
+ elementList: IElement[],
2099
+ titleId: string
2100
+ ): (IRange & IPositionContext) | null {
2101
+ for (let e = 0; e < elementList.length; e++) {
2102
+ const element = elementList[e]
2103
+ if (element.type === ElementType.TABLE) {
2104
+ const trList = element.trList!
2105
+ for (let r = 0; r < trList.length; r++) {
2106
+ const tr = trList[r]
2107
+ for (let d = 0; d < tr.tdList.length; d++) {
2108
+ const td = tr.tdList[d]
2109
+ const range = getPosition(td.value, titleId)
2110
+ if (range) {
2111
+ return {
2112
+ ...range,
2113
+ isTable: true,
2114
+ index: e,
2115
+ trIndex: r,
2116
+ tdIndex: d,
2117
+ tdId: td.id,
2118
+ trId: tr.id,
2119
+ tableId: element.id
2120
+ }
2121
+ }
2122
+ }
2123
+ }
2124
+ }
2125
+ // 找到标题末尾
2126
+ if (element.titleId === titleId) {
2127
+ let newIndex = e
2128
+ while (newIndex < elementList.length) {
2129
+ if (elementList[newIndex + 1]?.titleId !== titleId) {
2130
+ return {
2131
+ isTable: false,
2132
+ startIndex: newIndex,
2133
+ endIndex: newIndex
2134
+ }
2135
+ }
2136
+ newIndex++
2137
+ }
2138
+ }
2139
+ }
2140
+ return null
2141
+ }
2142
+
2143
+ const context = getPosition(elementList, titleId)
2144
+ if (!context) return
2145
+ const {
2146
+ isTable,
2147
+ index,
2148
+ startTdIndex,
2149
+ endTdIndex,
2150
+ startTrIndex,
2151
+ endTrIndex,
2152
+ trIndex,
2153
+ tdIndex,
2154
+ tdId,
2155
+ trId,
2156
+ tableId,
2157
+ endIndex
2158
+ } = context
2159
+ this.position.setPositionContext({
2160
+ isTable,
2161
+ index,
2162
+ trIndex,
2163
+ tdIndex,
2164
+ tdId,
2165
+ trId,
2166
+ tableId
2167
+ })
2168
+ this.range.setRange(
2169
+ endIndex,
2170
+ endIndex,
2171
+ tableId,
2172
+ startTdIndex,
2173
+ endTdIndex,
2174
+ startTrIndex,
2175
+ endTrIndex
2176
+ )
2177
+ this.draw.render({
2178
+ curIndex: endIndex,
2179
+ isCompute: false,
2180
+ isSubmitHistory: false
2181
+ })
2182
+ }
2183
+
2184
+ public wordTool() {
2185
+ const elementList = this.draw.getMainElementList()
2186
+ let isApply = false
2187
+ for (let i = 0; i < elementList.length; i++) {
2188
+ const element = elementList[i]
2189
+ // 删除空行、行首空格
2190
+ if (element.value === ZERO) {
2191
+ while (i + 1 < elementList.length) {
2192
+ const nextElement = elementList[i + 1]
2193
+ if (nextElement.value !== ZERO && nextElement.value !== NBSP) break
2194
+ elementList.splice(i + 1, 1)
2195
+ isApply = true
2196
+ }
2197
+ }
2198
+ }
2199
+ if (!isApply) {
2200
+ // 避免输入框光标丢失
2201
+ const isCollapsed = this.range.getIsCollapsed()
2202
+ this.draw.getCursor().drawCursor({
2203
+ isShow: isCollapsed
2204
+ })
2205
+ } else {
2206
+ this.draw.render({
2207
+ isSetCursor: false
2208
+ })
2209
+ }
2210
+ }
2211
+
2212
+ public setHTML(payload: Partial<IEditorHTML>) {
2213
+ const { header, main, footer } = payload
2214
+ const innerWidth = this.draw.getOriginalInnerWidth()
2215
+ // 不设置值时数据为undefined,避免覆盖当前数据
2216
+ const getElementList = (htmlText?: string) =>
2217
+ htmlText !== undefined
2218
+ ? getElementListByHTML(htmlText, {
2219
+ innerWidth
2220
+ })
2221
+ : undefined
2222
+ this.setValue({
2223
+ header: getElementList(header),
2224
+ main: getElementList(main),
2225
+ footer: getElementList(footer)
2226
+ })
2227
+ }
2228
+
2229
+ public setGroup(): string | null {
2230
+ const isReadonly = this.draw.isReadonly()
2231
+ if (isReadonly) return null
2232
+ return this.draw.getGroup().setGroup()
2233
+ }
2234
+
2235
+ public deleteGroup(groupId: string) {
2236
+ const isReadonly = this.draw.isReadonly()
2237
+ if (isReadonly) return
2238
+ this.draw.getGroup().deleteGroup(groupId)
2239
+ }
2240
+
2241
+ public getGroupIds(): Promise<string[]> {
2242
+ return this.draw.getWorkerManager().getGroupIds()
2243
+ }
2244
+
2245
+ public locationGroup(groupId: string) {
2246
+ const elementList = this.draw.getOriginalMainElementList()
2247
+ const context = this.draw
2248
+ .getGroup()
2249
+ .getContextByGroupId(elementList, groupId)
2250
+ if (!context) return
2251
+ const { isTable, index, trIndex, tdIndex, tdId, trId, tableId, endIndex } =
2252
+ context
2253
+ this.position.setPositionContext({
2254
+ isTable,
2255
+ index,
2256
+ trIndex,
2257
+ tdIndex,
2258
+ tdId,
2259
+ trId,
2260
+ tableId
2261
+ })
2262
+ this.range.setRange(endIndex, endIndex)
2263
+ this.draw.render({
2264
+ curIndex: endIndex,
2265
+ isCompute: false,
2266
+ isSubmitHistory: false
2267
+ })
2268
+ }
2269
+
2270
+ public setZone(zone: EditorZone) {
2271
+ this.draw.getZone().setZone(zone)
2272
+ }
2273
+
2274
+ public getControlValue(
2275
+ payload: IGetControlValueOption
2276
+ ): IGetControlValueResult | null {
2277
+ return this.draw.getControl().getValueById(payload)
2278
+ }
2279
+
2280
+ public setControlValue(payload: ISetControlValueOption) {
2281
+ this.draw.getControl().setValueListById([payload])
2282
+ }
2283
+
2284
+ public setControlValueList(payload: ISetControlValueOption[]) {
2285
+ this.draw.getControl().setValueListById(payload)
2286
+ }
2287
+
2288
+ public setControlExtension(payload: ISetControlExtensionOption) {
2289
+ this.draw.getControl().setExtensionListById([payload])
2290
+ }
2291
+
2292
+ public setControlExtensionList(payload: ISetControlExtensionOption[]) {
2293
+ this.draw.getControl().setExtensionListById(payload)
2294
+ }
2295
+
2296
+ public setControlProperties(payload: ISetControlProperties) {
2297
+ this.draw.getControl().setPropertiesListById([payload])
2298
+ }
2299
+
2300
+ public setControlPropertiesList(payload: ISetControlProperties[]) {
2301
+ this.draw.getControl().setPropertiesListById(payload)
2302
+ }
2303
+
2304
+ public setControlHighlight(payload: ISetControlHighlightOption) {
2305
+ this.draw.getControl().setHighlightList(payload)
2306
+ this.draw.render({
2307
+ isSubmitHistory: false
2308
+ })
2309
+ }
2310
+
2311
+ public updateOptions(payload: IUpdateOption) {
2312
+ const newOption = mergeOption(payload)
2313
+ Object.entries(newOption).forEach(([key, value]) => {
2314
+ Reflect.set(this.options, key, value)
2315
+ })
2316
+ this.forceUpdate()
2317
+ }
2318
+
2319
+ public getControlList(): IElement[] {
2320
+ return this.draw.getControl().getList()
2321
+ }
2322
+
2323
+ public locationControl(controlId: string, options?: ILocationControlOption) {
2324
+ function location(
2325
+ elementList: IElement[],
2326
+ zone: EditorZone
2327
+ ): ILocationPosition | null {
2328
+ let i = 0
2329
+ while (i < elementList.length) {
2330
+ const element = elementList[i]
2331
+ i++
2332
+ if (element.type === ElementType.TABLE) {
2333
+ const trList = element.trList!
2334
+ for (let r = 0; r < trList.length; r++) {
2335
+ const tr = trList[r]
2336
+ for (let d = 0; d < tr.tdList.length; d++) {
2337
+ const td = tr.tdList[d]
2338
+ const locationContext = location(td.value, zone)
2339
+ if (locationContext) {
2340
+ return {
2341
+ ...locationContext,
2342
+ positionContext: {
2343
+ isTable: true,
2344
+ index: i - 1,
2345
+ trIndex: r,
2346
+ tdIndex: d,
2347
+ tdId: element.tdId,
2348
+ trId: element.trId,
2349
+ tableId: element.tableId
2350
+ }
2351
+ }
2352
+ }
2353
+ }
2354
+ }
2355
+ }
2356
+ if (element?.controlId !== controlId) continue
2357
+ let curIndex = i - 1
2358
+ if (options?.position === LocationPosition.OUTER_AFTER) {
2359
+ // 控件外面最后
2360
+ if (
2361
+ !(
2362
+ element.controlComponent === ControlComponent.POSTFIX &&
2363
+ elementList[i + 1]?.controlComponent !==
2364
+ ControlComponent.POST_TEXT
2365
+ )
2366
+ ) {
2367
+ continue
2368
+ }
2369
+ } else if (options?.position === LocationPosition.OUTER_BEFORE) {
2370
+ // 控件外面最前
2371
+ curIndex -= 1
2372
+ } else if (options?.position === LocationPosition.AFTER) {
2373
+ // 控件内部最后
2374
+ curIndex -= 1
2375
+ if (
2376
+ element.controlComponent !== ControlComponent.PLACEHOLDER &&
2377
+ element.controlComponent !== ControlComponent.POSTFIX &&
2378
+ element.controlComponent !== ControlComponent.POST_TEXT
2379
+ ) {
2380
+ continue
2381
+ }
2382
+ } else {
2383
+ // 控件内部最前(默认)
2384
+ if (
2385
+ (element.controlComponent !== ControlComponent.PREFIX &&
2386
+ element.controlComponent !== ControlComponent.PRE_TEXT) ||
2387
+ elementList[i]?.controlComponent === ControlComponent.PREFIX ||
2388
+ elementList[i]?.controlComponent === ControlComponent.PRE_TEXT
2389
+ ) {
2390
+ continue
2391
+ }
2392
+ }
2393
+ return {
2394
+ zone,
2395
+ range: {
2396
+ startIndex: curIndex,
2397
+ endIndex: curIndex
2398
+ },
2399
+ positionContext: {
2400
+ isTable: false
2401
+ }
2402
+ }
2403
+ }
2404
+ return null
2405
+ }
2406
+ const data = [
2407
+ {
2408
+ zone: EditorZone.HEADER,
2409
+ elementList: this.draw.getHeaderElementList()
2410
+ },
2411
+ {
2412
+ zone: EditorZone.MAIN,
2413
+ elementList: this.draw.getOriginalMainElementList()
2414
+ },
2415
+ {
2416
+ zone: EditorZone.FOOTER,
2417
+ elementList: this.draw.getFooterElementList()
2418
+ }
2419
+ ]
2420
+ for (const context of data) {
2421
+ const locationContext = location(context.elementList, context.zone)
2422
+ if (locationContext) {
2423
+ // 设置区域、上下文、光标信息
2424
+ this.setZone(locationContext.zone)
2425
+ this.position.setPositionContext(locationContext.positionContext)
2426
+ this.range.replaceRange(locationContext.range)
2427
+ this.draw.render({
2428
+ curIndex: locationContext.range.startIndex,
2429
+ isCompute: false,
2430
+ isSubmitHistory: false
2431
+ })
2432
+ break
2433
+ }
2434
+ }
2435
+ }
2436
+
2437
+ public insertControl(payload: IElement) {
2438
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
2439
+ if (isDisabled) return
2440
+ const cloneElement = deepClone(payload)
2441
+ // 格式化上下文信息
2442
+ const { startIndex } = this.range.getRange()
2443
+ const elementList = this.draw.getElementList()
2444
+ const copyElement = getAnchorElement(elementList, startIndex)
2445
+ if (!copyElement) return
2446
+ const cloneAttr = [
2447
+ ...TABLE_CONTEXT_ATTR,
2448
+ ...EDITOR_ROW_ATTR,
2449
+ ...LIST_CONTEXT_ATTR,
2450
+ ...AREA_CONTEXT_ATTR
2451
+ ]
2452
+ cloneProperty<IElement>(cloneAttr, copyElement, cloneElement)
2453
+ // 插入控件
2454
+ this.draw.insertElementList([cloneElement])
2455
+ }
2456
+
2457
+ public jumpControl(payload?: { direction?: MoveDirection }) {
2458
+ this.draw.getControl().initNextControl({
2459
+ direction: payload?.direction
2460
+ })
2461
+ }
2462
+
2463
+ public getContainer(): HTMLDivElement {
2464
+ return this.draw.getContainer()
2465
+ }
2466
+
2467
+ public getTitleValue(
2468
+ payload: IGetTitleValueOption
2469
+ ): IGetTitleValueResult | null {
2470
+ const { conceptId } = payload
2471
+ const result: IGetTitleValueResult = []
2472
+ const getValue = (elementList: IElement[], zone: EditorZone) => {
2473
+ let i = 0
2474
+ while (i < elementList.length) {
2475
+ const element = elementList[i]
2476
+ i++
2477
+ if (element.type === ElementType.TABLE) {
2478
+ const trList = element.trList!
2479
+ for (let r = 0; r < trList.length; r++) {
2480
+ const tr = trList[r]
2481
+ for (let d = 0; d < tr.tdList.length; d++) {
2482
+ const td = tr.tdList[d]
2483
+ getValue(td.value, zone)
2484
+ }
2485
+ }
2486
+ }
2487
+ if (element?.title?.conceptId !== conceptId) continue
2488
+ // 先查找到标题,后循环至同级或上级标题处停止
2489
+ const valueList: IElement[] = []
2490
+ let j = i
2491
+ while (j < elementList.length) {
2492
+ const nextElement = elementList[j]
2493
+ j++
2494
+ if (element.titleId === nextElement.titleId) continue
2495
+ if (
2496
+ nextElement.level &&
2497
+ titleOrderNumberMapping[nextElement.level] <=
2498
+ titleOrderNumberMapping[element.level!]
2499
+ ) {
2500
+ break
2501
+ }
2502
+ valueList.push(nextElement)
2503
+ }
2504
+ result.push({
2505
+ ...element.title!,
2506
+ value: getTextFromElementList(valueList),
2507
+ elementList: zipElementList(valueList),
2508
+ zone
2509
+ })
2510
+ i = j
2511
+ }
2512
+ }
2513
+ const data = [
2514
+ {
2515
+ zone: EditorZone.HEADER,
2516
+ elementList: this.draw.getHeaderElementList()
2517
+ },
2518
+ {
2519
+ zone: EditorZone.MAIN,
2520
+ elementList: this.draw.getOriginalMainElementList()
2521
+ },
2522
+ {
2523
+ zone: EditorZone.FOOTER,
2524
+ elementList: this.draw.getFooterElementList()
2525
+ }
2526
+ ]
2527
+ for (const { zone, elementList } of data) {
2528
+ getValue(elementList, zone)
2529
+ }
2530
+ return result
2531
+ }
2532
+
2533
+ public getPositionContextByEvent(
2534
+ evt: MouseEvent,
2535
+ options: IPositionContextByEventOption = {}
2536
+ ): IPositionContextByEventResult | null {
2537
+ const pageIndex = (<HTMLElement>evt.target)?.dataset.index
2538
+ if (!pageIndex) return null
2539
+ const { isMustDirectHit = true } = options
2540
+ const pageNo = Number(pageIndex)
2541
+ const positionContext = this.position.getPositionByXY({
2542
+ x: evt.offsetX,
2543
+ y: evt.offsetY,
2544
+ pageNo
2545
+ })
2546
+ const {
2547
+ isDirectHit,
2548
+ isTable,
2549
+ index,
2550
+ trIndex,
2551
+ tdIndex,
2552
+ tdValueIndex,
2553
+ zone
2554
+ } = positionContext
2555
+ // 非直接命中或选区不一致时返回空值
2556
+ if (
2557
+ (isMustDirectHit && !isDirectHit) ||
2558
+ (zone && zone !== this.zone.getZone())
2559
+ ) {
2560
+ return null
2561
+ }
2562
+ // 命中元素信息
2563
+ let tableInfo: ITableInfoByEvent | null = null
2564
+ let element: IElement | null = null
2565
+ const elementList = this.draw.getOriginalElementList()
2566
+ let position: IElementPosition | null = null
2567
+ const positionList = this.position.getOriginalPositionList()
2568
+ if (isTable) {
2569
+ const td = elementList[index!].trList?.[trIndex!].tdList[tdIndex!]
2570
+ element = td?.value[tdValueIndex!] || null
2571
+ position = td?.positionList?.[tdValueIndex!] || null
2572
+ tableInfo = {
2573
+ element: elementList[index!],
2574
+ trIndex: trIndex!,
2575
+ tdIndex: tdIndex!
2576
+ }
2577
+ } else {
2578
+ element = elementList[index] || null
2579
+ position = positionList[index] || null
2580
+ }
2581
+ // 元素包围信息
2582
+ let rangeRect: RangeRect | null = null
2583
+ if (position) {
2584
+ const {
2585
+ pageNo,
2586
+ coordinate: { leftTop, rightTop },
2587
+ lineHeight
2588
+ } = position
2589
+ const height = this.draw.getOriginalHeight()
2590
+ const pageGap = this.draw.getOriginalPageGap()
2591
+ rangeRect = {
2592
+ x: leftTop[0],
2593
+ y: leftTop[1] + pageNo * (height + pageGap),
2594
+ width: rightTop[0] - leftTop[0],
2595
+ height: lineHeight
2596
+ }
2597
+ }
2598
+ return {
2599
+ pageNo,
2600
+ element,
2601
+ rangeRect,
2602
+ tableInfo
2603
+ }
2604
+ }
2605
+
2606
+ public insertTitle(payload: IElement) {
2607
+ const isDisabled = this.draw.isReadonly() || this.draw.isDisabled()
2608
+ if (isDisabled) return
2609
+ const cloneElement = deepClone(payload)
2610
+ // 格式化上下文信息
2611
+ const { startIndex } = this.range.getRange()
2612
+ const elementList = this.draw.getElementList()
2613
+ const copyElement = getAnchorElement(elementList, startIndex)
2614
+ if (!copyElement) return
2615
+ const cloneAttr = [
2616
+ ...TABLE_CONTEXT_ATTR,
2617
+ ...EDITOR_ROW_ATTR,
2618
+ ...LIST_CONTEXT_ATTR,
2619
+ ...AREA_CONTEXT_ATTR
2620
+ ]
2621
+ cloneElement.valueList?.forEach(valueItem => {
2622
+ cloneProperty<IElement>(cloneAttr, copyElement, valueItem)
2623
+ })
2624
+ // 插入标题
2625
+ this.draw.insertElementList([cloneElement])
2626
+ }
2627
+
2628
+ public focus(payload?: IFocusOption) {
2629
+ const {
2630
+ position = LocationPosition.AFTER,
2631
+ isMoveCursorToVisible = true,
2632
+ rowNo,
2633
+ range
2634
+ } = payload || {}
2635
+ let curIndex = -1
2636
+ if (range) {
2637
+ // 根据选区定位
2638
+ this.range.replaceRange(range)
2639
+ curIndex =
2640
+ position === LocationPosition.BEFORE ? range.startIndex : range.endIndex
2641
+ } else if (isNumber(rowNo)) {
2642
+ // 根据行号定位
2643
+ const rowList = this.draw.getOriginalRowList()
2644
+ curIndex =
2645
+ position === LocationPosition.BEFORE
2646
+ ? rowList[rowNo]?.startIndex
2647
+ : rowList[rowNo + 1]?.startIndex - 1
2648
+ if (!isNumber(curIndex)) return
2649
+ this.range.setRange(curIndex, curIndex)
2650
+ } else {
2651
+ // 默认文档首尾
2652
+ curIndex =
2653
+ position === LocationPosition.BEFORE
2654
+ ? 0
2655
+ : this.draw.getOriginalMainElementList().length - 1
2656
+ this.range.setRange(curIndex, curIndex)
2657
+ }
2658
+ // 光标存在且闭合时定位
2659
+ const renderParams: IDrawOption = {
2660
+ isCompute: false,
2661
+ isSetCursor: false,
2662
+ isSubmitHistory: false
2663
+ }
2664
+ if (isMoveCursorToVisible && ~curIndex && this.range.getIsCollapsed()) {
2665
+ renderParams.curIndex = curIndex
2666
+ renderParams.isSetCursor = true
2667
+ }
2668
+ this.draw.render(renderParams)
2669
+ }
2670
+
2671
+ public insertArea(payload: IInsertAreaOption) {
2672
+ return this.draw.getArea().insertArea(payload)
2673
+ }
2674
+
2675
+ public setAreaValue(payload: ISetAreaValueOption) {
2676
+ return this.draw.getArea().setAreaValue(payload)
2677
+ }
2678
+
2679
+ public setAreaProperties(payload: ISetAreaPropertiesOption) {
2680
+ this.draw.getArea().setAreaProperties(payload)
2681
+ }
2682
+
2683
+ public locationArea(areaId: string, options?: ILocationAreaOption) {
2684
+ // 区域在最后时,如果后面没有元素是否追加换行符
2685
+ if (
2686
+ options?.isAppendLastLineBreak &&
2687
+ options?.position === LocationPosition.OUTER_AFTER
2688
+ ) {
2689
+ const elementList = this.draw.getOriginalMainElementList()
2690
+ if (elementList[elementList.length - 1].areaId === areaId) {
2691
+ this.draw.appendElementList(
2692
+ [
2693
+ {
2694
+ value: ZERO
2695
+ }
2696
+ ],
2697
+ {
2698
+ isSubmitHistory: false
2699
+ }
2700
+ )
2701
+ }
2702
+ }
2703
+ // 获取区域位置
2704
+ const context = this.draw.getArea().getContextByAreaId(areaId, options)
2705
+ if (!context) return
2706
+ const {
2707
+ range: { endIndex }
2708
+ } = context
2709
+ this.position.setPositionContext({
2710
+ isTable: false
2711
+ })
2712
+ this.range.setRange(endIndex, endIndex)
2713
+ this.draw.render({
2714
+ curIndex: endIndex,
2715
+ isSetCursor: true,
2716
+ isCompute: false,
2717
+ isSubmitHistory: false
2718
+ })
2719
+ }
2720
+
2721
+ // 清空涂鸦信息
2722
+ public clearGraffiti() {
2723
+ this.draw.getGraffiti().clear()
2724
+ // 涂鸦模式下重新渲染
2725
+ if (this.draw.isGraffitiMode()) {
2726
+ this.draw.render({
2727
+ isCompute: false,
2728
+ isSetCursor: false,
2729
+ isSubmitHistory: false
2730
+ })
2731
+ }
2732
+ }
2733
+ }