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,1767 @@
1
+ import {
2
+ ControlComponent,
3
+ ControlState,
4
+ ControlType
5
+ } from '../../../dataset/enum/Control'
6
+ import { EditorMode, EditorZone } from '../../../dataset/enum/Editor'
7
+ import { ElementType } from '../../../dataset/enum/Element'
8
+ import { DeepRequired } from '../../../interface/Common'
9
+ import {
10
+ IControl,
11
+ IControlChangeOption,
12
+ IControlChangeResult,
13
+ IControlContentChangeResult,
14
+ IControlContext,
15
+ IControlHighlight,
16
+ IControlInitOption,
17
+ IControlInstance,
18
+ IControlOption,
19
+ IControlRuleOption,
20
+ IDestroyControlOption,
21
+ IGetControlValueOption,
22
+ IGetControlValueResult,
23
+ IInitNextControlOption,
24
+ INextControlContext,
25
+ IRepaintControlOption,
26
+ ISetControlExtensionOption,
27
+ ISetControlProperties,
28
+ ISetControlRowFlexOption,
29
+ ISetControlValueOption
30
+ } from '../../../interface/Control'
31
+ import { IEditorData, IEditorOption } from '../../../interface/Editor'
32
+ import { IElement, IElementPosition } from '../../../interface/Element'
33
+ import { EventBusMap } from '../../../interface/EventBus'
34
+ import { IRange } from '../../../interface/Range'
35
+ import {
36
+ deepClone,
37
+ isArray,
38
+ isString,
39
+ omitObject,
40
+ pickObject,
41
+ splitText
42
+ } from '../../../utils'
43
+ import {
44
+ formatElementContext,
45
+ formatElementList,
46
+ getNonHideElementIndex,
47
+ pickElementAttr,
48
+ zipElementList
49
+ } from '../../../utils/element'
50
+ import { EventBus } from '../../event/eventbus/EventBus'
51
+ import { Listener } from '../../listener/Listener'
52
+ import { RangeManager } from '../../range/RangeManager'
53
+ import { Draw } from '../Draw'
54
+ import { CheckboxControl } from './checkbox/CheckboxControl'
55
+ import { RadioControl } from './radio/RadioControl'
56
+ import { ControlSearch } from './interactive/ControlSearch'
57
+ import { ControlBorder } from './richtext/Border'
58
+ import { SelectControl } from './select/SelectControl'
59
+ import { TextControl } from './text/TextControl'
60
+ import { DateControl } from './date/DateControl'
61
+ import { NumberControl } from './number/NumberControl'
62
+ import { MoveDirection } from '../../../dataset/enum/Observer'
63
+ import {
64
+ CONTROL_CONTEXT_ATTR,
65
+ CONTROL_STYLE_ATTR,
66
+ LIST_CONTEXT_ATTR,
67
+ TITLE_CONTEXT_ATTR
68
+ } from '../../../dataset/constant/Element'
69
+ import { IRowElement } from '../../../interface/Row'
70
+ import { RowFlex } from '../../../dataset/enum/Row'
71
+ import { ZERO } from '../../../dataset/constant/Common'
72
+
73
+ interface IMoveCursorResult {
74
+ newIndex: number
75
+ newElement: IElement
76
+ }
77
+ export class Control {
78
+ private controlBorder: ControlBorder
79
+ private draw: Draw
80
+ private range: RangeManager
81
+ private listener: Listener
82
+ private eventBus: EventBus<EventBusMap>
83
+ private controlSearch: ControlSearch
84
+ private options: DeepRequired<IEditorOption>
85
+ private controlOptions: IControlOption
86
+ private activeControl: IControlInstance | null
87
+ private activeControlValue: IElement[]
88
+ private preElement: IElement | null
89
+
90
+ constructor(draw: Draw) {
91
+ this.controlBorder = new ControlBorder(draw)
92
+
93
+ this.draw = draw
94
+ this.range = draw.getRange()
95
+ this.listener = draw.getListener()
96
+ this.eventBus = draw.getEventBus()
97
+ this.controlSearch = new ControlSearch(this)
98
+
99
+ this.options = draw.getOptions()
100
+ this.controlOptions = this.options.control
101
+ this.activeControl = null
102
+ this.activeControlValue = []
103
+ this.preElement = null
104
+ }
105
+
106
+ // 搜索高亮匹配
107
+ public setHighlightList(payload: IControlHighlight[]) {
108
+ this.controlSearch.setHighlightList(payload)
109
+ }
110
+
111
+ public computeHighlightList() {
112
+ const highlightList = this.controlSearch.getHighlightList()
113
+ if (highlightList.length) {
114
+ this.controlSearch.computeHighlightList()
115
+ }
116
+ }
117
+
118
+ public renderHighlightList(ctx: CanvasRenderingContext2D, pageNo: number) {
119
+ const highlightMatchResult = this.controlSearch.getHighlightMatchResult()
120
+ if (highlightMatchResult.length) {
121
+ this.controlSearch.renderHighlightList(ctx, pageNo)
122
+ }
123
+ }
124
+
125
+ public getDraw(): Draw {
126
+ return this.draw
127
+ }
128
+
129
+ // 过滤控件辅助元素(前后缀、背景提示)
130
+ public filterAssistElement(elementList: IElement[]): IElement[] {
131
+ return elementList.filter((element, index) => {
132
+ if (element.type === ElementType.TABLE) {
133
+ const trList = element.trList!
134
+ for (let r = 0; r < trList.length; r++) {
135
+ const tr = trList[r]
136
+ for (let d = 0; d < tr.tdList.length; d++) {
137
+ const td = tr.tdList[d]
138
+ td.value = this.filterAssistElement(td.value)
139
+ }
140
+ }
141
+ }
142
+ if (!element.controlId) return true
143
+ if (element.control?.minWidth) {
144
+ if (
145
+ element.controlComponent === ControlComponent.PREFIX ||
146
+ element.controlComponent === ControlComponent.POSTFIX
147
+ ) {
148
+ element.value = ''
149
+ return true
150
+ }
151
+ } else {
152
+ // 控件存在值时无需过滤前后文本
153
+ if (
154
+ element.control?.preText &&
155
+ element.controlComponent === ControlComponent.PRE_TEXT
156
+ ) {
157
+ let isExistValue = false
158
+ let start = index + 1
159
+ while (start < elementList.length) {
160
+ const nextElement = elementList[start]
161
+ if (element.controlId !== nextElement.controlId) break
162
+ if (nextElement.controlComponent === ControlComponent.VALUE) {
163
+ isExistValue = true
164
+ break
165
+ }
166
+ start++
167
+ }
168
+ return isExistValue
169
+ }
170
+ if (
171
+ element.control?.postText &&
172
+ element.controlComponent === ControlComponent.POST_TEXT
173
+ ) {
174
+ let isExistValue = false
175
+ let start = index - 1
176
+ while (start < elementList.length) {
177
+ const preElement = elementList[start]
178
+ if (element.controlId !== preElement.controlId) break
179
+ if (preElement.controlComponent === ControlComponent.VALUE) {
180
+ isExistValue = true
181
+ break
182
+ }
183
+ start--
184
+ }
185
+ return isExistValue
186
+ }
187
+ }
188
+ return (
189
+ element.controlComponent !== ControlComponent.PREFIX &&
190
+ element.controlComponent !== ControlComponent.POSTFIX &&
191
+ element.controlComponent !== ControlComponent.PLACEHOLDER
192
+ )
193
+ })
194
+ }
195
+
196
+ // 是否属于控件可以捕获事件的选区
197
+ public getIsRangeCanCaptureEvent(): boolean {
198
+ if (!this.activeControl) return false
199
+ const { startIndex, endIndex } = this.getRange()
200
+ if (!~startIndex && !~endIndex) return false
201
+ const elementList = this.getElementList()
202
+ const startElement = elementList[startIndex]
203
+ // 闭合光标在后缀处
204
+ if (
205
+ startIndex === endIndex &&
206
+ startElement.controlComponent === ControlComponent.POSTFIX
207
+ ) {
208
+ return true
209
+ }
210
+ // 在控件内
211
+ const endElement = elementList[endIndex]
212
+ if (
213
+ startElement.controlId &&
214
+ startElement.controlId === endElement.controlId &&
215
+ endElement.controlComponent !== ControlComponent.POSTFIX
216
+ ) {
217
+ return true
218
+ }
219
+ return false
220
+ }
221
+
222
+ // 判断选区是否在后缀处
223
+ public getIsRangeInPostfix(): boolean {
224
+ if (!this.activeControl) return false
225
+ const { startIndex, endIndex } = this.getRange()
226
+ if (startIndex !== endIndex) return false
227
+ const elementList = this.getElementList()
228
+ const element = elementList[startIndex]
229
+ return element.controlComponent === ControlComponent.POSTFIX
230
+ }
231
+
232
+ // 判断选区是否在控件内
233
+ public getIsRangeWithinControl(): boolean {
234
+ const { startIndex, endIndex } = this.getRange()
235
+ if (!~startIndex && !~endIndex) return false
236
+ const elementList = this.getElementList()
237
+ const startElement = elementList[startIndex]
238
+ const endElement = elementList[endIndex]
239
+ if (
240
+ startElement?.controlId &&
241
+ startElement.controlId === endElement.controlId &&
242
+ endElement.controlComponent !== ControlComponent.POSTFIX
243
+ ) {
244
+ return true
245
+ }
246
+ return false
247
+ }
248
+
249
+ // 是否元素包含完整控件元素
250
+ public getIsElementListContainFullControl(elementList: IElement[]): boolean {
251
+ if (!elementList.some(element => element.controlId)) return false
252
+ let prefixCount = 0
253
+ let postfixCount = 0
254
+ for (let e = 0; e < elementList.length; e++) {
255
+ const element = elementList[e]
256
+ if (element.controlComponent === ControlComponent.PREFIX) {
257
+ prefixCount++
258
+ } else if (element.controlComponent === ControlComponent.POSTFIX) {
259
+ postfixCount++
260
+ }
261
+ }
262
+ if (!prefixCount || !postfixCount) return false
263
+ return prefixCount === postfixCount
264
+ }
265
+
266
+ public getIsDisabledControl(context: IControlContext = {}): boolean {
267
+ if (this.draw.isDesignMode() || !this.activeControl) return false
268
+ const { startIndex, endIndex } = context.range || this.range.getRange()
269
+ if (startIndex === endIndex && ~startIndex && ~endIndex) {
270
+ const elementList = context.elementList || this.getElementList()
271
+ const startElement = elementList[startIndex]
272
+ if (startElement.controlComponent === ControlComponent.POSTFIX) {
273
+ return false
274
+ }
275
+ }
276
+ return !!this.activeControl.getElement()?.control?.disabled
277
+ }
278
+
279
+ public getIsDisabledPasteControl(context: IControlContext = {}): boolean {
280
+ if (this.draw.isDesignMode() || !this.activeControl) return false
281
+ const { startIndex, endIndex } = context.range || this.range.getRange()
282
+ if (startIndex === endIndex && ~startIndex && ~endIndex) {
283
+ const elementList = context.elementList || this.getElementList()
284
+ const startElement = elementList[startIndex]
285
+ if (startElement.controlComponent === ControlComponent.POSTFIX) {
286
+ return false
287
+ }
288
+ }
289
+ return !!this.activeControl.getElement()?.control?.pasteDisabled
290
+ }
291
+
292
+ // 通过索引找到控件并判断控件是否存在值
293
+ public getIsExistValueByElementListIndex(
294
+ elementList: IElement[],
295
+ index: number
296
+ ): boolean {
297
+ const element = elementList[index]
298
+ // 是否是控件
299
+ if (!element.controlId) return false
300
+ // 单选框、复选框仅需验证控件值
301
+ if (
302
+ element.control?.type === ControlType.CHECKBOX ||
303
+ element.control?.type === ControlType.RADIO
304
+ ) {
305
+ return !!element.control?.code
306
+ }
307
+ // 其他控件需校验文本
308
+ if (element.controlComponent === ControlComponent.VALUE) {
309
+ return true
310
+ }
311
+ if (element.controlComponent === ControlComponent.PLACEHOLDER) {
312
+ return false
313
+ }
314
+ // 向后查找值元素
315
+ if (
316
+ element.controlComponent === ControlComponent.PREFIX ||
317
+ element.controlComponent === ControlComponent.PRE_TEXT
318
+ ) {
319
+ let i = index + 1
320
+ while (i < elementList.length) {
321
+ const nextElement = elementList[i]
322
+ if (nextElement.controlId !== element.controlId) {
323
+ return false
324
+ }
325
+ if (nextElement.controlComponent === ControlComponent.VALUE) {
326
+ return true
327
+ }
328
+ if (nextElement.controlComponent === ControlComponent.PLACEHOLDER) {
329
+ return false
330
+ }
331
+ i++
332
+ }
333
+ }
334
+ // 向前查找值元素
335
+ if (
336
+ element.controlComponent === ControlComponent.POSTFIX ||
337
+ element.controlComponent === ControlComponent.POST_TEXT
338
+ ) {
339
+ let i = index - 1
340
+ while (i >= 0) {
341
+ const preElement = elementList[i]
342
+ if (preElement.controlId !== element.controlId) {
343
+ return false
344
+ }
345
+ if (preElement.controlComponent === ControlComponent.VALUE) {
346
+ return true
347
+ }
348
+ if (preElement.controlComponent === ControlComponent.PLACEHOLDER) {
349
+ return false
350
+ }
351
+ i--
352
+ }
353
+ }
354
+ return false
355
+ }
356
+
357
+ public getControlHighlight(elementList: IElement[], index: number) {
358
+ return this.controlSearch.getControlHighlight(elementList, index)
359
+ }
360
+
361
+ public getContainer(): HTMLDivElement {
362
+ return this.draw.getContainer()
363
+ }
364
+
365
+ public getElementList(): IElement[] {
366
+ return this.draw.getElementList()
367
+ }
368
+
369
+ public getPosition(): IElementPosition | null {
370
+ const positionList = this.draw.getPosition().getPositionList()
371
+ const { endIndex } = this.range.getRange()
372
+ return positionList[endIndex] || null
373
+ }
374
+
375
+ public getPreY(): number {
376
+ const height = this.draw.getHeight()
377
+ const pageGap = this.draw.getPageGap()
378
+ const pageNo = this.getPosition()?.pageNo ?? this.draw.getPageNo()
379
+ return pageNo * (height + pageGap)
380
+ }
381
+
382
+ public getRange(): IRange {
383
+ return this.range.getRange()
384
+ }
385
+
386
+ public getValueRange(context: IControlContext = {}): IRange | null {
387
+ const elementList = context.elementList || this.getElementList()
388
+ const { startIndex } = context.range || this.getRange()
389
+ const startElement = elementList[startIndex]
390
+ // 向左查找
391
+ let preIndex = startIndex
392
+ while (preIndex > 0) {
393
+ const preElement = elementList[preIndex]
394
+ if (
395
+ preElement.controlId !== startElement.controlId ||
396
+ preElement.controlComponent === ControlComponent.PREFIX ||
397
+ preElement.controlComponent === ControlComponent.PRE_TEXT
398
+ ) {
399
+ break
400
+ }
401
+ preIndex--
402
+ }
403
+ // 向右查找
404
+ let nextIndex = startIndex + 1
405
+ while (nextIndex < elementList.length) {
406
+ const nextElement = elementList[nextIndex]
407
+ if (
408
+ nextElement.controlId !== startElement.controlId ||
409
+ nextElement.controlComponent === ControlComponent.POSTFIX ||
410
+ nextElement.controlComponent === ControlComponent.POST_TEXT
411
+ ) {
412
+ break
413
+ }
414
+ nextIndex++
415
+ }
416
+ if (preIndex === nextIndex) return null
417
+ return {
418
+ startIndex: preIndex,
419
+ endIndex: nextIndex - 1
420
+ }
421
+ }
422
+
423
+ public shrinkBoundary(context: IControlContext = {}) {
424
+ this.range.shrinkBoundary(context)
425
+ }
426
+
427
+ public getActiveControl(): IControlInstance | null {
428
+ return this.activeControl
429
+ }
430
+
431
+ public getControlElementList(context: IControlContext = {}): IElement[] {
432
+ const elementList = context.elementList || this.getElementList()
433
+ const { startIndex } = context.range || this.getRange()
434
+ const startElement = elementList[startIndex]
435
+ if (!startElement?.controlId) return []
436
+ const data: IElement[] = []
437
+ // 向左查找
438
+ let preIndex = startIndex
439
+ while (preIndex > 0) {
440
+ const preElement = elementList[preIndex]
441
+ if (preElement.controlId !== startElement.controlId) break
442
+ data.unshift(preElement)
443
+ preIndex--
444
+ }
445
+ // 向右查找
446
+ let nextIndex = startIndex + 1
447
+ while (nextIndex < elementList.length) {
448
+ const nextElement = elementList[nextIndex]
449
+ if (nextElement.controlId !== startElement.controlId) break
450
+ data.push(nextElement)
451
+ nextIndex++
452
+ }
453
+ return data
454
+ }
455
+
456
+ public updateActiveControlValue() {
457
+ if (this.activeControl) {
458
+ this.activeControlValue = this.getControlElementList()
459
+ }
460
+ }
461
+
462
+ public emitControlChange(state: ControlState) {
463
+ if (!this.activeControl) return
464
+ const isSubscribeControlChange = this.eventBus.isSubscribe('controlChange')
465
+ if (!this.listener.controlChange && !isSubscribeControlChange) return
466
+ let control: IControl
467
+ const value = this.activeControlValue
468
+ const activeElement = this.activeControl.getElement()
469
+ if (value?.length) {
470
+ control = zipElementList(value)[0].control!
471
+ } else {
472
+ control = pickElementAttr(deepClone(activeElement)).control!
473
+ control.value = []
474
+ }
475
+ const payload: IControlChangeResult = {
476
+ state,
477
+ control,
478
+ controlId: activeElement.controlId!
479
+ }
480
+ this.listener.controlChange?.(payload)
481
+ if (isSubscribeControlChange) {
482
+ this.eventBus.emit('controlChange', payload)
483
+ }
484
+ }
485
+
486
+ public initControl() {
487
+ const elementList = this.getElementList()
488
+ const range = this.getRange()
489
+ const element = elementList[range.startIndex]
490
+ // 判断控件是否已经激活
491
+ if (this.activeControl) {
492
+ // 弹窗类控件唤醒弹窗,后缀处移除弹窗
493
+ if (
494
+ this.activeControl instanceof SelectControl ||
495
+ this.activeControl instanceof DateControl ||
496
+ this.activeControl instanceof NumberControl
497
+ ) {
498
+ if (element.controlComponent === ControlComponent.POSTFIX) {
499
+ this.activeControl.destroy()
500
+ } else {
501
+ this.activeControl.awake()
502
+ }
503
+ }
504
+ // 相同控件元素
505
+ if (this.preElement?.controlId === element.controlId) {
506
+ // 当前元素在尾部:控件失活事件
507
+ if (element.controlComponent === ControlComponent.POSTFIX) {
508
+ this.emitControlChange(ControlState.INACTIVE)
509
+ } else if (
510
+ // 之前元素在尾部 && 当前不在尾部:控件激活事件
511
+ this.preElement?.controlComponent === ControlComponent.POSTFIX
512
+ ) {
513
+ this.emitControlChange(ControlState.ACTIVE)
514
+ }
515
+ }
516
+ // 更新缓存控件数据
517
+ const controlElement = this.activeControl.getElement()
518
+ if (element.controlId === controlElement.controlId) {
519
+ this.updateActiveControlValue()
520
+ this.preElement = element
521
+ return
522
+ }
523
+ }
524
+ // 销毁旧激活控件
525
+ this.destroyControl()
526
+ // 激活控件
527
+ const isReadonly = this.draw.isReadonly()
528
+ if (isReadonly) return
529
+ const control = element.control!
530
+ if (control.type === ControlType.TEXT) {
531
+ this.activeControl = new TextControl(element, this)
532
+ } else if (control.type === ControlType.SELECT) {
533
+ const selectControl = new SelectControl(element, this)
534
+ this.activeControl = selectControl
535
+ selectControl.awake()
536
+ } else if (control.type === ControlType.CHECKBOX) {
537
+ this.activeControl = new CheckboxControl(element, this)
538
+ } else if (control.type === ControlType.RADIO) {
539
+ this.activeControl = new RadioControl(element, this)
540
+ } else if (control.type === ControlType.DATE) {
541
+ const dateControl = new DateControl(element, this)
542
+ this.activeControl = dateControl
543
+ dateControl.awake()
544
+ } else if (control.type === ControlType.NUMBER) {
545
+ const numberControl = new NumberControl(element, this)
546
+ this.activeControl = numberControl
547
+ numberControl.awake()
548
+ }
549
+ // 缓存控件数据
550
+ this.updateActiveControlValue()
551
+ this.preElement = element
552
+ // 激活控件回调
553
+ if (element.controlComponent !== ControlComponent.POSTFIX) {
554
+ this.emitControlChange(ControlState.ACTIVE)
555
+ }
556
+ }
557
+
558
+ public destroyControl(options: IDestroyControlOption = {}) {
559
+ if (!this.activeControl) return
560
+ const { isEmitEvent = true } = options
561
+ if (
562
+ this.activeControl instanceof SelectControl ||
563
+ this.activeControl instanceof DateControl ||
564
+ this.activeControl instanceof NumberControl
565
+ ) {
566
+ this.activeControl.destroy()
567
+ }
568
+ // 销毁控件回调
569
+ if (
570
+ isEmitEvent &&
571
+ this.preElement?.controlComponent !== ControlComponent.POSTFIX
572
+ ) {
573
+ this.emitControlChange(ControlState.INACTIVE)
574
+ }
575
+ // 清空变量
576
+ this.preElement = null
577
+ this.activeControl = null
578
+ this.activeControlValue = []
579
+ }
580
+
581
+ public repaintControl(options: IRepaintControlOption = {}) {
582
+ const {
583
+ curIndex,
584
+ isCompute = true,
585
+ isSubmitHistory = true,
586
+ isSetCursor = true
587
+ } = options
588
+ // 重新渲染
589
+ if (curIndex === undefined) {
590
+ this.range.clearRange()
591
+ this.draw.render({
592
+ isCompute,
593
+ isSubmitHistory,
594
+ isSetCursor: false
595
+ })
596
+ } else {
597
+ this.range.setRange(curIndex, curIndex)
598
+ this.draw.render({
599
+ curIndex,
600
+ isCompute,
601
+ isSetCursor,
602
+ isSubmitHistory
603
+ })
604
+ }
605
+ }
606
+
607
+ public emitControlContentChange(options?: IControlChangeOption) {
608
+ const isSubscribeControlContentChange = this.eventBus.isSubscribe(
609
+ 'controlContentChange'
610
+ )
611
+ if (
612
+ !isSubscribeControlContentChange &&
613
+ !this.listener.controlContentChange
614
+ ) {
615
+ return
616
+ }
617
+ const controlElement =
618
+ options?.controlElement || this.activeControl?.getElement()
619
+ if (!controlElement) return
620
+ // 控件被删除不触发事件
621
+ const elementList = options?.context?.elementList || this.getElementList()
622
+ const { startIndex } = options?.context?.range || this.getRange()
623
+ if (!elementList[startIndex]?.controlId) return
624
+ // 格式化回调数据
625
+ const controlValue =
626
+ options?.controlValue || this.getControlElementList(options?.context)
627
+ let control: IControl
628
+ if (controlValue?.length) {
629
+ control = zipElementList(controlValue)[0].control!
630
+ } else {
631
+ control = controlElement.control!
632
+ control.value = []
633
+ }
634
+ if (!control) return
635
+ const payload: IControlContentChangeResult = {
636
+ control,
637
+ controlId: controlElement.controlId!
638
+ }
639
+ this.listener.controlContentChange?.(payload)
640
+ if (isSubscribeControlContentChange) {
641
+ this.eventBus.emit('controlContentChange', payload)
642
+ }
643
+ }
644
+
645
+ public reAwakeControl() {
646
+ if (!this.activeControl) return
647
+ const elementList = this.getElementList()
648
+ const range = this.getRange()
649
+ const element = elementList[range.startIndex]
650
+ this.activeControl.setElement(element)
651
+ if (
652
+ (this.activeControl instanceof DateControl ||
653
+ this.activeControl instanceof SelectControl ||
654
+ this.activeControl instanceof NumberControl) &&
655
+ this.activeControl.getIsPopup()
656
+ ) {
657
+ this.activeControl.destroy()
658
+ this.activeControl.awake()
659
+ }
660
+ }
661
+
662
+ public selectValue(): boolean {
663
+ const elementList = this.getElementList()
664
+ const { startIndex } = this.getRange()
665
+ const startElement = elementList[startIndex]
666
+ if (
667
+ !startElement?.controlId ||
668
+ (startElement.controlComponent !== ControlComponent.VALUE &&
669
+ elementList[startIndex + 1]?.controlComponent ===
670
+ ControlComponent.VALUE)
671
+ ) {
672
+ return false
673
+ }
674
+ // 向左查找
675
+ let preIndex = startIndex
676
+ while (preIndex > 0) {
677
+ const preElement = elementList[preIndex]
678
+ if (preElement.controlComponent !== ControlComponent.VALUE) break
679
+ preIndex--
680
+ }
681
+ // 向右查找
682
+ let nextIndex = startIndex + 1
683
+ while (nextIndex < elementList.length) {
684
+ const nextElement = elementList[nextIndex]
685
+ if (nextElement.controlComponent !== ControlComponent.VALUE) {
686
+ nextIndex--
687
+ break
688
+ }
689
+ nextIndex++
690
+ }
691
+ if (preIndex !== nextIndex) {
692
+ const range = this.range.getRange()
693
+ this.range.replaceRange({
694
+ ...range,
695
+ startIndex: preIndex,
696
+ endIndex: nextIndex
697
+ })
698
+ this.draw.render({
699
+ isCompute: false,
700
+ isSetCursor: false,
701
+ isSubmitHistory: false
702
+ })
703
+ return true
704
+ }
705
+ return false
706
+ }
707
+
708
+ public moveCursor(position: IControlInitOption): IMoveCursorResult {
709
+ const { index, trIndex, tdIndex, tdValueIndex } = position
710
+ let elementList = this.draw.getOriginalElementList()
711
+ let element: IElement
712
+ const newIndex = position.isTable ? tdValueIndex! : index
713
+ if (position.isTable) {
714
+ elementList = elementList[index!].trList![trIndex!].tdList[tdIndex!].value
715
+ element = elementList[tdValueIndex!]
716
+ } else {
717
+ element = elementList[index]
718
+ }
719
+ // 隐藏元素移动光标
720
+ if (element.hide || element.control?.hide || element.area?.hide) {
721
+ const nonHideIndex = getNonHideElementIndex(elementList, newIndex)
722
+ return {
723
+ newIndex: nonHideIndex,
724
+ newElement: elementList[nonHideIndex]
725
+ }
726
+ }
727
+ // 控件内移动光标
728
+ if (element.controlComponent === ControlComponent.VALUE) {
729
+ // VALUE-无需移动
730
+ return {
731
+ newIndex,
732
+ newElement: element
733
+ }
734
+ } else if (element.controlComponent === ControlComponent.POSTFIX) {
735
+ // POSTFIX-移动到最后一个后缀字符后
736
+ let startIndex = newIndex + 1
737
+ while (startIndex < elementList.length) {
738
+ const nextElement = elementList[startIndex]
739
+ if (nextElement.controlId !== element.controlId) {
740
+ return {
741
+ newIndex: startIndex - 1,
742
+ newElement: elementList[startIndex - 1]
743
+ }
744
+ }
745
+ // 全文最后一个元素时移动后缀尾部
746
+ if (startIndex === elementList.length - 1) {
747
+ return {
748
+ newIndex: startIndex,
749
+ newElement: elementList[startIndex]
750
+ }
751
+ }
752
+ startIndex++
753
+ }
754
+ } else if (
755
+ element.controlComponent === ControlComponent.PREFIX ||
756
+ element.controlComponent === ControlComponent.PRE_TEXT
757
+ ) {
758
+ // PREFIX或前文本-移动到最后一个前缀字符后
759
+ let startIndex = newIndex + 1
760
+ while (startIndex < elementList.length) {
761
+ const nextElement = elementList[startIndex]
762
+ if (
763
+ nextElement.controlId !== element.controlId ||
764
+ (nextElement.controlComponent !== ControlComponent.PREFIX &&
765
+ nextElement.controlComponent !== ControlComponent.PRE_TEXT)
766
+ ) {
767
+ return {
768
+ newIndex: startIndex - 1,
769
+ newElement: elementList[startIndex - 1]
770
+ }
771
+ }
772
+ startIndex++
773
+ }
774
+ } else if (
775
+ element.controlComponent === ControlComponent.PLACEHOLDER ||
776
+ element.controlComponent === ControlComponent.POST_TEXT
777
+ ) {
778
+ // PLACEHOLDER或后文本-移动到第一个前缀或内容后
779
+ let startIndex = newIndex - 1
780
+ while (startIndex > 0) {
781
+ const preElement = elementList[startIndex]
782
+ if (
783
+ preElement.controlId !== element.controlId ||
784
+ preElement.controlComponent === ControlComponent.VALUE ||
785
+ preElement.controlComponent === ControlComponent.PREFIX ||
786
+ preElement.controlComponent === ControlComponent.PRE_TEXT
787
+ ) {
788
+ return {
789
+ newIndex: startIndex,
790
+ newElement: elementList[startIndex]
791
+ }
792
+ }
793
+ startIndex--
794
+ }
795
+ }
796
+ return {
797
+ newIndex,
798
+ newElement: element
799
+ }
800
+ }
801
+
802
+ public removeControl(
803
+ startIndex: number,
804
+ context: IControlContext = {}
805
+ ): number | null {
806
+ const elementList = context.elementList || this.getElementList()
807
+ const startElement = elementList[startIndex]
808
+ // 设计模式 || 元素隐藏 => 不验证删除权限
809
+ if (
810
+ !this.draw.isDesignMode() &&
811
+ !startElement?.hide &&
812
+ !startElement?.control?.hide &&
813
+ !startElement?.area?.hide
814
+ ) {
815
+ const { deletable = true } = startElement.control!
816
+ if (!deletable) return null
817
+ // 表单模式控件删除权限验证
818
+ const mode = this.draw.getMode()
819
+ if (
820
+ mode === EditorMode.FORM &&
821
+ this.options.modeRule[mode].controlDeletableDisabled
822
+ ) {
823
+ return null
824
+ }
825
+ }
826
+ let leftIndex = -1
827
+ let rightIndex = -1
828
+ // 向左查找
829
+ let preIndex = startIndex
830
+ while (preIndex > 0) {
831
+ const preElement = elementList[preIndex]
832
+ if (preElement.controlId !== startElement.controlId) {
833
+ leftIndex = preIndex
834
+ break
835
+ }
836
+ preIndex--
837
+ }
838
+ // 向右查找
839
+ let nextIndex = startIndex + 1
840
+ while (nextIndex < elementList.length) {
841
+ const nextElement = elementList[nextIndex]
842
+ if (nextElement.controlId !== startElement.controlId) {
843
+ rightIndex = nextIndex - 1
844
+ break
845
+ }
846
+ nextIndex++
847
+ }
848
+ // 控件在最后
849
+ if (nextIndex === elementList.length) {
850
+ rightIndex = nextIndex - 1
851
+ }
852
+ if (!~leftIndex && !~rightIndex) return startIndex
853
+ leftIndex = ~leftIndex ? leftIndex : 0
854
+ // 删除元素
855
+ this.draw.spliceElementList(
856
+ elementList,
857
+ leftIndex + 1,
858
+ rightIndex - leftIndex
859
+ )
860
+ return leftIndex
861
+ }
862
+
863
+ public removePlaceholder(startIndex: number, context: IControlContext = {}) {
864
+ const elementList = context.elementList || this.getElementList()
865
+ const startElement = elementList[startIndex]
866
+ const nextElement = elementList[startIndex + 1]
867
+ if (
868
+ startElement.controlComponent === ControlComponent.PLACEHOLDER ||
869
+ nextElement.controlComponent === ControlComponent.PLACEHOLDER
870
+ ) {
871
+ let isHasSubmitHistory = false
872
+ let index = startIndex
873
+ while (index < elementList.length) {
874
+ const curElement = elementList[index]
875
+ if (curElement.controlId !== startElement.controlId) break
876
+ if (curElement.controlComponent === ControlComponent.PLACEHOLDER) {
877
+ // 删除占位符时替换前一个历史记录
878
+ if (!isHasSubmitHistory) {
879
+ isHasSubmitHistory = true
880
+ this.draw.getHistoryManager().popUndo()
881
+ this.draw.submitHistory(startIndex)
882
+ }
883
+ elementList.splice(index, 1)
884
+ } else {
885
+ index++
886
+ }
887
+ }
888
+ }
889
+ }
890
+
891
+ public addPlaceholder(startIndex: number, context: IControlContext = {}) {
892
+ const elementList = context.elementList || this.getElementList()
893
+ const startElement = elementList[startIndex]
894
+ const control = startElement.control!
895
+ if (!control.placeholder) return
896
+ const placeholderStrList = splitText(control.placeholder)
897
+ // 优先使用默认控件样式
898
+ const anchorElementStyleAttr = pickObject(startElement, CONTROL_STYLE_ATTR)
899
+ for (let p = 0; p < placeholderStrList.length; p++) {
900
+ const value = placeholderStrList[p]
901
+ const newElement: IElement = {
902
+ ...anchorElementStyleAttr,
903
+ value: value === '\n' ? ZERO : value,
904
+ controlId: startElement.controlId,
905
+ type: ElementType.CONTROL,
906
+ control: startElement.control,
907
+ controlComponent: ControlComponent.PLACEHOLDER,
908
+ color: this.controlOptions.placeholderColor
909
+ }
910
+ formatElementContext(elementList, [newElement], startIndex, {
911
+ editorOptions: this.options
912
+ })
913
+ this.draw.spliceElementList(elementList, startIndex + p + 1, 0, [
914
+ newElement
915
+ ])
916
+ }
917
+ }
918
+
919
+ public setValue(data: IElement[]): number {
920
+ if (!this.activeControl) {
921
+ throw new Error('active control is null')
922
+ }
923
+ return this.activeControl.setValue(data)
924
+ }
925
+
926
+ public setControlProperties(
927
+ properties: Partial<IControl>,
928
+ context: IControlContext = {}
929
+ ) {
930
+ const elementList = context.elementList || this.getElementList()
931
+ const { startIndex } = context.range || this.getRange()
932
+ const startElement = elementList[startIndex]
933
+ // 向左查找
934
+ let preIndex = startIndex
935
+ while (preIndex > 0) {
936
+ const preElement = elementList[preIndex]
937
+ if (preElement.controlId !== startElement.controlId) break
938
+ preElement.control = {
939
+ ...preElement.control!,
940
+ ...properties
941
+ }
942
+ preIndex--
943
+ }
944
+ // 向右查找
945
+ let nextIndex = startIndex + 1
946
+ while (nextIndex < elementList.length) {
947
+ const nextElement = elementList[nextIndex]
948
+ if (nextElement.controlId !== startElement.controlId) break
949
+ nextElement.control = {
950
+ ...nextElement.control!,
951
+ ...properties
952
+ }
953
+ nextIndex++
954
+ }
955
+ }
956
+
957
+ public keydown(evt: KeyboardEvent): number | null {
958
+ if (!this.activeControl) {
959
+ throw new Error('active control is null')
960
+ }
961
+ return this.activeControl.keydown(evt)
962
+ }
963
+
964
+ public cut(): number {
965
+ if (!this.activeControl) {
966
+ throw new Error('active control is null')
967
+ }
968
+ return this.activeControl.cut()
969
+ }
970
+
971
+ public getValueById(payload: IGetControlValueOption): IGetControlValueResult {
972
+ const { id, groupId, conceptId, areaId } = payload
973
+ const result: IGetControlValueResult = []
974
+ if (!id && !conceptId && !groupId) return result
975
+ const getValue = (elementList: IElement[], zone: EditorZone) => {
976
+ let i = 0
977
+ while (i < elementList.length) {
978
+ const element = elementList[i]
979
+ i++
980
+ // 表格下钻处理
981
+ if (element.type === ElementType.TABLE) {
982
+ const trList = element.trList!
983
+ for (let r = 0; r < trList.length; r++) {
984
+ const tr = trList[r]
985
+ for (let d = 0; d < tr.tdList.length; d++) {
986
+ const td = tr.tdList[d]
987
+ getValue(td.value, zone)
988
+ }
989
+ }
990
+ }
991
+ if (
992
+ !element.control ||
993
+ (groupId && element.control.groupId !== groupId) ||
994
+ (id && element.controlId !== id) ||
995
+ (conceptId && element.control.conceptId !== conceptId) ||
996
+ (areaId && element.areaId !== areaId)
997
+ ) {
998
+ continue
999
+ }
1000
+ const { type, code, valueSets } = element.control
1001
+ let j = i
1002
+ let textControlValue = ''
1003
+ const textControlElementList = []
1004
+ while (j < elementList.length) {
1005
+ const nextElement = elementList[j]
1006
+ if (nextElement.controlId !== element.controlId) break
1007
+ if (
1008
+ (type === ControlType.TEXT ||
1009
+ type === ControlType.DATE ||
1010
+ type === ControlType.NUMBER) &&
1011
+ nextElement.controlComponent === ControlComponent.VALUE
1012
+ ) {
1013
+ textControlValue += nextElement.value
1014
+ textControlElementList.push(
1015
+ omitObject(nextElement, CONTROL_CONTEXT_ATTR)
1016
+ )
1017
+ }
1018
+ j++
1019
+ }
1020
+ if (
1021
+ type === ControlType.TEXT ||
1022
+ type === ControlType.DATE ||
1023
+ type === ControlType.NUMBER
1024
+ ) {
1025
+ result.push({
1026
+ ...element.control,
1027
+ zone,
1028
+ value: textControlValue || null,
1029
+ innerText: textControlValue || null,
1030
+ elementList: zipElementList(textControlElementList)
1031
+ })
1032
+ } else if (
1033
+ type === ControlType.SELECT ||
1034
+ type === ControlType.CHECKBOX ||
1035
+ type === ControlType.RADIO
1036
+ ) {
1037
+ const innerText = code
1038
+ ?.split(',')
1039
+ .map(
1040
+ selectCode =>
1041
+ valueSets?.find(valueSet => valueSet.code === selectCode)?.value
1042
+ )
1043
+ .filter(Boolean)
1044
+ .join('')
1045
+ result.push({
1046
+ ...element.control,
1047
+ zone,
1048
+ value: code || null,
1049
+ innerText: innerText || null
1050
+ })
1051
+ }
1052
+ i = j
1053
+ }
1054
+ }
1055
+ const data = [
1056
+ {
1057
+ zone: EditorZone.HEADER,
1058
+ elementList: this.draw.getHeaderElementList()
1059
+ },
1060
+ {
1061
+ zone: EditorZone.MAIN,
1062
+ elementList: this.draw.getOriginalMainElementList()
1063
+ },
1064
+ {
1065
+ zone: EditorZone.FOOTER,
1066
+ elementList: this.draw.getFooterElementList()
1067
+ }
1068
+ ]
1069
+ for (const { zone, elementList } of data) {
1070
+ getValue(elementList, zone)
1071
+ }
1072
+ return result
1073
+ }
1074
+
1075
+ public setValueListById(payload: ISetControlValueOption[]) {
1076
+ if (!payload.length) return
1077
+ let isExistSet = false
1078
+ let isExistSubmitHistory = false
1079
+ // 设置值
1080
+ const setValue = (elementList: IElement[]) => {
1081
+ let i = 0
1082
+ while (i < elementList.length) {
1083
+ const element = elementList[i]
1084
+ i++
1085
+ // 表格下钻处理
1086
+ if (element.type === ElementType.TABLE) {
1087
+ const trList = element.trList!
1088
+ for (let r = 0; r < trList.length; r++) {
1089
+ const tr = trList[r]
1090
+ for (let d = 0; d < tr.tdList.length; d++) {
1091
+ const td = tr.tdList[d]
1092
+ setValue(td.value)
1093
+ }
1094
+ }
1095
+ }
1096
+ if (!element.control) continue
1097
+ // 获取设置值优先id、conceptId、areaId并于groupId组合设置
1098
+ const payloadItem = payload.find(
1099
+ p =>
1100
+ (!p.groupId || p.groupId === element.control?.groupId) &&
1101
+ ((p.id && element.controlId === p.id) ||
1102
+ (p.conceptId && element.control!.conceptId === p.conceptId) ||
1103
+ (p.areaId && element.areaId === p.areaId))
1104
+ )
1105
+ if (!payloadItem) continue
1106
+ const { value, isSubmitHistory = true } = payloadItem
1107
+ // 只要存在一次保存历史均记录
1108
+ isExistSet = true
1109
+ if (isSubmitHistory) {
1110
+ isExistSubmitHistory = true
1111
+ }
1112
+ const { type } = element.control!
1113
+ // 当前控件结束索引
1114
+ let currentEndIndex = i
1115
+ while (currentEndIndex < elementList.length) {
1116
+ const nextElement = elementList[currentEndIndex]
1117
+ if (nextElement.controlId !== element.controlId) break
1118
+ currentEndIndex++
1119
+ }
1120
+ // 模拟光标选区上下文
1121
+ const fakeRange = {
1122
+ startIndex: i - 1,
1123
+ endIndex: currentEndIndex - 2
1124
+ }
1125
+ const controlContext: IControlContext = {
1126
+ range: fakeRange,
1127
+ elementList
1128
+ }
1129
+ const controlRule: IControlRuleOption = {
1130
+ isIgnoreDisabledRule: true,
1131
+ isIgnoreDeletedRule: true
1132
+ }
1133
+ if (type === ControlType.TEXT) {
1134
+ const formatValue = Array.isArray(value)
1135
+ ? value
1136
+ : value
1137
+ ? [{ value }]
1138
+ : []
1139
+ if (formatValue.length) {
1140
+ formatElementList(formatValue, {
1141
+ isHandleFirstElement: false,
1142
+ editorOptions: this.options
1143
+ })
1144
+ }
1145
+ const text = new TextControl(element, this)
1146
+ this.activeControl = text
1147
+ if (formatValue.length) {
1148
+ text.setValue(formatValue, controlContext, controlRule)
1149
+ } else {
1150
+ text.clearValue(controlContext, controlRule)
1151
+ }
1152
+ } else if (type === ControlType.SELECT) {
1153
+ if (Array.isArray(value)) continue
1154
+ const select = new SelectControl(element, this)
1155
+ this.activeControl = select
1156
+ if (value) {
1157
+ select.setSelect(value, controlContext, controlRule)
1158
+ } else {
1159
+ select.clearSelect(controlContext, controlRule)
1160
+ }
1161
+ } else if (type === ControlType.CHECKBOX) {
1162
+ if (Array.isArray(value)) continue
1163
+ const checkbox = new CheckboxControl(element, this)
1164
+ this.activeControl = checkbox
1165
+ const codes = value ? value.split(',') : []
1166
+ checkbox.setSelect(codes, controlContext, controlRule)
1167
+ } else if (type === ControlType.RADIO) {
1168
+ if (Array.isArray(value)) continue
1169
+ const radio = new RadioControl(element, this)
1170
+ this.activeControl = radio
1171
+ const codes = value ? [value] : []
1172
+ radio.setSelect(codes, controlContext, controlRule)
1173
+ } else if (type === ControlType.DATE) {
1174
+ const date = new DateControl(element, this)
1175
+ this.activeControl = date
1176
+ if (isArray(value)) {
1177
+ if (value.length) {
1178
+ formatElementList(value, {
1179
+ isHandleFirstElement: false,
1180
+ editorOptions: this.options
1181
+ })
1182
+ }
1183
+ date.setValue(value, controlContext, controlRule)
1184
+ } else if (isString(value)) {
1185
+ date.setSelect(value, controlContext, controlRule)
1186
+ } else {
1187
+ date.clearSelect(controlContext, controlRule)
1188
+ }
1189
+ } else if (type === ControlType.NUMBER) {
1190
+ const formatValue = Array.isArray(value)
1191
+ ? value
1192
+ : value
1193
+ ? [{ value }]
1194
+ : []
1195
+ if (formatValue.length) {
1196
+ formatElementList(formatValue, {
1197
+ isHandleFirstElement: false,
1198
+ editorOptions: this.options
1199
+ })
1200
+ }
1201
+ const text = new NumberControl(element, this)
1202
+ this.activeControl = text
1203
+ if (formatValue.length) {
1204
+ text.setValue(formatValue, controlContext, controlRule)
1205
+ } else {
1206
+ text.clearValue(controlContext, controlRule)
1207
+ }
1208
+ }
1209
+ // 控件值变更事件
1210
+ this.emitControlContentChange({
1211
+ context: controlContext
1212
+ })
1213
+ // 模拟控件激活后销毁
1214
+ this.activeControl = null
1215
+ // 修改后控件结束索引
1216
+ let newEndIndex = i
1217
+ while (newEndIndex < elementList.length) {
1218
+ const nextElement = elementList[newEndIndex]
1219
+ if (nextElement.controlId !== element.controlId) break
1220
+ newEndIndex++
1221
+ }
1222
+ i = newEndIndex
1223
+ }
1224
+ }
1225
+ // 销毁旧控件
1226
+ this.destroyControl({
1227
+ isEmitEvent: false
1228
+ })
1229
+ // 页眉、内容区、页脚同时处理
1230
+ const data = [
1231
+ this.draw.getHeaderElementList(),
1232
+ this.draw.getOriginalMainElementList(),
1233
+ this.draw.getFooterElementList()
1234
+ ]
1235
+ for (const elementList of data) {
1236
+ setValue(elementList)
1237
+ }
1238
+ if (isExistSet) {
1239
+ // 不保存历史时需清空之前记录,避免还原
1240
+ if (!isExistSubmitHistory) {
1241
+ this.draw.getHistoryManager().recovery()
1242
+ }
1243
+ this.draw.render({
1244
+ isSubmitHistory: isExistSubmitHistory,
1245
+ isSetCursor: false
1246
+ })
1247
+ }
1248
+ }
1249
+
1250
+ public setExtensionListById(payload: ISetControlExtensionOption[]) {
1251
+ if (!payload.length) return
1252
+ const setExtension = (elementList: IElement[]) => {
1253
+ let i = 0
1254
+ while (i < elementList.length) {
1255
+ const element = elementList[i]
1256
+ i++
1257
+ // 表格下钻处理
1258
+ if (element.type === ElementType.TABLE) {
1259
+ const trList = element.trList!
1260
+ for (let r = 0; r < trList.length; r++) {
1261
+ const tr = trList[r]
1262
+ for (let d = 0; d < tr.tdList.length; d++) {
1263
+ const td = tr.tdList[d]
1264
+ setExtension(td.value)
1265
+ }
1266
+ }
1267
+ }
1268
+ if (!element.control) continue
1269
+ // 获取设置值优先id、conceptId、areaId并于groupId组合设置
1270
+ const payloadItem = payload.find(
1271
+ p =>
1272
+ (!p.groupId || p.groupId === element.control?.groupId) &&
1273
+ ((p.id && element.controlId === p.id) ||
1274
+ (p.conceptId && element.control!.conceptId === p.conceptId) ||
1275
+ (p.areaId && element.areaId === p.areaId))
1276
+ )
1277
+ if (!payloadItem) continue
1278
+ const { extension } = payloadItem
1279
+ // 设置值
1280
+ this.setControlProperties(
1281
+ {
1282
+ extension
1283
+ },
1284
+ {
1285
+ elementList,
1286
+ range: { startIndex: i, endIndex: i }
1287
+ }
1288
+ )
1289
+ // 修改后控件结束索引
1290
+ let newEndIndex = i
1291
+ while (newEndIndex < elementList.length) {
1292
+ const nextElement = elementList[newEndIndex]
1293
+ if (nextElement.controlId !== element.controlId) break
1294
+ newEndIndex++
1295
+ }
1296
+ i = newEndIndex
1297
+ }
1298
+ }
1299
+ const data = [
1300
+ this.draw.getHeaderElementList(),
1301
+ this.draw.getOriginalMainElementList(),
1302
+ this.draw.getFooterElementList()
1303
+ ]
1304
+ for (const elementList of data) {
1305
+ setExtension(elementList)
1306
+ }
1307
+ }
1308
+
1309
+ public setPropertiesListById(payload: ISetControlProperties[]) {
1310
+ if (!payload.length) return
1311
+ let isExistUpdate = false
1312
+ let isExistSubmitHistory = false
1313
+ const setProperties = (elementList: IElement[]) => {
1314
+ let i = 0
1315
+ while (i < elementList.length) {
1316
+ const element = elementList[i]
1317
+ i++
1318
+ if (element.type === ElementType.TABLE) {
1319
+ const trList = element.trList!
1320
+ for (let r = 0; r < trList.length; r++) {
1321
+ const tr = trList[r]
1322
+ for (let d = 0; d < tr.tdList.length; d++) {
1323
+ const td = tr.tdList[d]
1324
+ setProperties(td.value)
1325
+ }
1326
+ }
1327
+ }
1328
+ if (!element.control) continue
1329
+ // 获取设置值优先id、conceptId、areaId并于groupId组合设置
1330
+ const payloadItem = payload.find(
1331
+ p =>
1332
+ (!p.groupId || p.groupId === element.control?.groupId) &&
1333
+ ((p.id && element.controlId === p.id) ||
1334
+ (p.conceptId && element.control!.conceptId === p.conceptId) ||
1335
+ (p.areaId && element.areaId === p.areaId))
1336
+ )
1337
+ if (!payloadItem) continue
1338
+ const { properties, isSubmitHistory = true } = payloadItem
1339
+ isExistUpdate = true
1340
+ if (isSubmitHistory) {
1341
+ isExistSubmitHistory = true
1342
+ }
1343
+ // 设置属性
1344
+ this.setControlProperties(
1345
+ {
1346
+ ...element.control,
1347
+ ...properties,
1348
+ value: element.control.value
1349
+ },
1350
+ {
1351
+ elementList,
1352
+ range: { startIndex: i, endIndex: i }
1353
+ }
1354
+ )
1355
+ // 控件默认样式
1356
+ CONTROL_STYLE_ATTR.forEach(key => {
1357
+ const controlStyleProperty = properties[key]
1358
+ if (controlStyleProperty) {
1359
+ Reflect.set(element, key, controlStyleProperty)
1360
+ }
1361
+ })
1362
+ // 修改后控件结束索引
1363
+ let newEndIndex = i
1364
+ while (newEndIndex < elementList.length) {
1365
+ const nextElement = elementList[newEndIndex]
1366
+ if (nextElement.controlId !== element.controlId) break
1367
+ newEndIndex++
1368
+ }
1369
+ i = newEndIndex
1370
+ }
1371
+ }
1372
+ // 页眉页脚正文启动搜索
1373
+ const pageComponentData: IEditorData = {
1374
+ header: this.draw.getHeaderElementList(),
1375
+ main: this.draw.getOriginalMainElementList(),
1376
+ footer: this.draw.getFooterElementList()
1377
+ }
1378
+ for (const key in pageComponentData) {
1379
+ const elementList =
1380
+ pageComponentData[<keyof Omit<IEditorData, 'graffiti'>>key]!
1381
+ setProperties(elementList)
1382
+ }
1383
+ if (!isExistUpdate) return
1384
+ // 强制更新
1385
+ for (const key in pageComponentData) {
1386
+ const pageComponentKey = <keyof Omit<IEditorData, 'graffiti'>>key
1387
+ const elementList = zipElementList(pageComponentData[pageComponentKey]!, {
1388
+ isClassifyArea: true,
1389
+ extraPickAttrs: ['id']
1390
+ })
1391
+ pageComponentData[pageComponentKey] = elementList
1392
+ formatElementList(elementList, {
1393
+ editorOptions: this.options,
1394
+ isForceCompensation: true
1395
+ })
1396
+ }
1397
+ this.draw.setEditorData(pageComponentData)
1398
+ // 不保存历史时需清空之前记录,避免还原
1399
+ if (!isExistSubmitHistory) {
1400
+ this.draw.getHistoryManager().recovery()
1401
+ }
1402
+ this.draw.render({
1403
+ isSubmitHistory: isExistSubmitHistory,
1404
+ isSetCursor: false
1405
+ })
1406
+ }
1407
+
1408
+ public getList(): IElement[] {
1409
+ const controlElementList: IElement[] = []
1410
+ function getControlElementList(elementList: IElement[]) {
1411
+ for (let e = 0; e < elementList.length; e++) {
1412
+ const element = elementList[e]
1413
+ if (element.type === ElementType.TABLE) {
1414
+ const trList = element.trList!
1415
+ for (let r = 0; r < trList.length; r++) {
1416
+ const tr = trList[r]
1417
+ for (let d = 0; d < tr.tdList.length; d++) {
1418
+ const td = tr.tdList[d]
1419
+ const tdElement = td.value
1420
+ getControlElementList(tdElement)
1421
+ }
1422
+ }
1423
+ }
1424
+ if (element.controlId) {
1425
+ // 移除控件所在标题及列表上下文信息
1426
+ const controlElement = omitObject(element, [
1427
+ ...TITLE_CONTEXT_ATTR,
1428
+ ...LIST_CONTEXT_ATTR
1429
+ ])
1430
+ controlElementList.push(controlElement)
1431
+ }
1432
+ }
1433
+ }
1434
+ const data = [
1435
+ this.draw.getHeader().getElementList(),
1436
+ this.draw.getOriginalMainElementList(),
1437
+ this.draw.getFooter().getElementList()
1438
+ ]
1439
+ for (const elementList of data) {
1440
+ getControlElementList(elementList)
1441
+ }
1442
+ return zipElementList(controlElementList, {
1443
+ extraPickAttrs: ['controlId']
1444
+ })
1445
+ }
1446
+
1447
+ public recordBorderInfo(x: number, y: number, width: number, height: number) {
1448
+ this.controlBorder.recordBorderInfo(x, y, width, height)
1449
+ }
1450
+
1451
+ public drawBorder(ctx: CanvasRenderingContext2D) {
1452
+ this.controlBorder.render(ctx)
1453
+ }
1454
+
1455
+ public getPreControlContext(): INextControlContext | null {
1456
+ if (!this.activeControl) return null
1457
+ const position = this.draw.getPosition()
1458
+ const positionContext = position.getPositionContext()
1459
+ if (!positionContext) return null
1460
+ const controlElement = this.activeControl.getElement()
1461
+ // 获取上一个控件上下文本信息
1462
+ function getPreContext(
1463
+ elementList: IElement[],
1464
+ start: number
1465
+ ): INextControlContext | null {
1466
+ for (let e = start; e > 0; e--) {
1467
+ const element = elementList[e]
1468
+ // 表格元素
1469
+ if (element.type === ElementType.TABLE) {
1470
+ const trList = element.trList || []
1471
+ for (let r = trList.length - 1; r >= 0; r--) {
1472
+ const tr = trList[r]
1473
+ const tdList = tr.tdList
1474
+ for (let d = tdList.length - 1; d >= 0; d--) {
1475
+ const td = tdList[d]
1476
+ const context = getPreContext(td.value, td.value.length - 1)
1477
+ if (context) {
1478
+ return {
1479
+ positionContext: {
1480
+ isTable: true,
1481
+ index: e,
1482
+ trIndex: r,
1483
+ tdIndex: d,
1484
+ tdId: td.id,
1485
+ trId: tr.id,
1486
+ tableId: element.id
1487
+ },
1488
+ nextIndex: context.nextIndex
1489
+ }
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1494
+ if (
1495
+ !element.controlId ||
1496
+ element.controlId === controlElement.controlId
1497
+ ) {
1498
+ continue
1499
+ }
1500
+ // 找到尾部第一个非占位符元素
1501
+ let nextIndex = e
1502
+ while (nextIndex > 0) {
1503
+ const nextElement = elementList[nextIndex]
1504
+ if (
1505
+ nextElement.controlComponent === ControlComponent.VALUE ||
1506
+ nextElement.controlComponent === ControlComponent.PREFIX ||
1507
+ nextElement.controlComponent === ControlComponent.PRE_TEXT
1508
+ ) {
1509
+ break
1510
+ }
1511
+ nextIndex--
1512
+ }
1513
+ return {
1514
+ positionContext: {
1515
+ isTable: false
1516
+ },
1517
+ nextIndex
1518
+ }
1519
+ }
1520
+ return null
1521
+ }
1522
+ // 当前上下文控件信息
1523
+ const { startIndex } = this.range.getRange()
1524
+ const elementList = this.getElementList()
1525
+ const context = getPreContext(elementList, startIndex)
1526
+ if (context) {
1527
+ return {
1528
+ positionContext: positionContext.isTable
1529
+ ? positionContext
1530
+ : context.positionContext,
1531
+ nextIndex: context.nextIndex
1532
+ }
1533
+ }
1534
+ // 控件在单元内时继续循环
1535
+ if (controlElement.tableId) {
1536
+ const originalElementList = this.draw.getOriginalElementList()
1537
+ const { index, trIndex, tdIndex } = positionContext
1538
+ const trList = originalElementList[index!].trList!
1539
+ for (let r = trIndex!; r >= 0; r--) {
1540
+ const tr = trList[r]
1541
+ const tdList = tr.tdList
1542
+ for (let d = tdList.length - 1; d >= 0; d--) {
1543
+ if (trIndex === r && d >= tdIndex!) continue
1544
+ const td = tdList[d]
1545
+ const context = getPreContext(td.value, td.value.length - 1)
1546
+ if (context) {
1547
+ return {
1548
+ positionContext: {
1549
+ isTable: true,
1550
+ index: positionContext.index,
1551
+ trIndex: r,
1552
+ tdIndex: d,
1553
+ tdId: td.id,
1554
+ trId: tr.id,
1555
+ tableId: controlElement.tableId
1556
+ },
1557
+ nextIndex: context.nextIndex
1558
+ }
1559
+ }
1560
+ }
1561
+ }
1562
+ // 跳出表格继续循环
1563
+ const context = getPreContext(originalElementList, index! - 1)
1564
+ if (context) {
1565
+ return {
1566
+ positionContext: {
1567
+ isTable: false
1568
+ },
1569
+ nextIndex: context.nextIndex
1570
+ }
1571
+ }
1572
+ }
1573
+ return null
1574
+ }
1575
+
1576
+ public getNextControlContext(): INextControlContext | null {
1577
+ if (!this.activeControl) return null
1578
+ const position = this.draw.getPosition()
1579
+ const positionContext = position.getPositionContext()
1580
+ if (!positionContext) return null
1581
+ const controlElement = this.activeControl.getElement()
1582
+ // 获取下一个控件上下文本信息
1583
+ function getNextContext(
1584
+ elementList: IElement[],
1585
+ start: number
1586
+ ): INextControlContext | null {
1587
+ for (let e = start; e < elementList.length; e++) {
1588
+ const element = elementList[e]
1589
+ // 表格元素
1590
+ if (element.type === ElementType.TABLE) {
1591
+ const trList = element.trList || []
1592
+ for (let r = 0; r < trList.length; r++) {
1593
+ const tr = trList[r]
1594
+ const tdList = tr.tdList
1595
+ for (let d = 0; d < tdList.length; d++) {
1596
+ const td = tdList[d]
1597
+ const context = getNextContext(td.value!, 0)
1598
+ if (context) {
1599
+ return {
1600
+ positionContext: {
1601
+ isTable: true,
1602
+ index: e,
1603
+ trIndex: r,
1604
+ tdIndex: d,
1605
+ tdId: td.id,
1606
+ trId: tr.id,
1607
+ tableId: element.id
1608
+ },
1609
+ nextIndex: context.nextIndex
1610
+ }
1611
+ }
1612
+ }
1613
+ }
1614
+ }
1615
+ if (
1616
+ !element.controlId ||
1617
+ element.controlId === controlElement.controlId ||
1618
+ elementList[e + 1]?.controlComponent === ControlComponent.PREFIX ||
1619
+ elementList[e + 1]?.controlComponent === ControlComponent.PRE_TEXT
1620
+ ) {
1621
+ continue
1622
+ }
1623
+ return {
1624
+ positionContext: {
1625
+ isTable: false
1626
+ },
1627
+ nextIndex: e
1628
+ }
1629
+ }
1630
+ return null
1631
+ }
1632
+ // 当前上下文控件信息
1633
+ const { endIndex } = this.range.getRange()
1634
+ const elementList = this.getElementList()
1635
+ const context = getNextContext(elementList, endIndex)
1636
+ if (context) {
1637
+ return {
1638
+ positionContext: positionContext.isTable
1639
+ ? positionContext
1640
+ : context.positionContext,
1641
+ nextIndex: context.nextIndex
1642
+ }
1643
+ }
1644
+ // 控件在单元内时继续循环
1645
+ if (controlElement.tableId) {
1646
+ const originalElementList = this.draw.getOriginalElementList()
1647
+ const { index, trIndex, tdIndex } = positionContext
1648
+ const trList = originalElementList[index!].trList!
1649
+ for (let r = trIndex!; r < trList.length; r++) {
1650
+ const tr = trList[r]
1651
+ const tdList = tr.tdList
1652
+ for (let d = 0; d < tdList.length; d++) {
1653
+ if (trIndex === r && d <= tdIndex!) continue
1654
+ const td = tdList[d]
1655
+ const context = getNextContext(td.value, 0)
1656
+ if (context) {
1657
+ return {
1658
+ positionContext: {
1659
+ isTable: true,
1660
+ index: positionContext.index,
1661
+ trIndex: r,
1662
+ tdIndex: d,
1663
+ tdId: td.id,
1664
+ trId: tr.id,
1665
+ tableId: controlElement.tableId
1666
+ },
1667
+ nextIndex: context.nextIndex
1668
+ }
1669
+ }
1670
+ }
1671
+ }
1672
+ // 跳出表格继续循环
1673
+ const context = getNextContext(originalElementList, index! + 1)
1674
+ if (context) {
1675
+ return {
1676
+ positionContext: {
1677
+ isTable: false
1678
+ },
1679
+ nextIndex: context.nextIndex
1680
+ }
1681
+ }
1682
+ }
1683
+ return null
1684
+ }
1685
+
1686
+ public initNextControl(option: IInitNextControlOption = {}) {
1687
+ const { direction = MoveDirection.DOWN } = option
1688
+ let context: INextControlContext | null = null
1689
+ if (direction === MoveDirection.UP) {
1690
+ context = this.getPreControlContext()
1691
+ } else {
1692
+ context = this.getNextControlContext()
1693
+ }
1694
+ if (!context) return
1695
+ const { nextIndex, positionContext } = context
1696
+ const position = this.draw.getPosition()
1697
+ // 设置上下文
1698
+ position.setPositionContext(positionContext)
1699
+ this.draw.getRange().replaceRange({
1700
+ startIndex: nextIndex,
1701
+ endIndex: nextIndex
1702
+ })
1703
+ // 重新渲染并定位
1704
+ this.draw.render({
1705
+ curIndex: nextIndex,
1706
+ isCompute: false,
1707
+ isSetCursor: true,
1708
+ isSubmitHistory: false
1709
+ })
1710
+ }
1711
+
1712
+ public setMinWidthControlInfo(option: ISetControlRowFlexOption) {
1713
+ const { row, rowElement, controlRealWidth, availableWidth } = option
1714
+ if (!rowElement.control?.minWidth) return
1715
+ const { scale } = this.options
1716
+ const controlMinWidth = rowElement.control.minWidth * scale
1717
+ // 设置首字符偏移量:如果控件内设置对齐方式&&存在设置最小宽度
1718
+ let controlFirstElement: IRowElement | null = null
1719
+ if (
1720
+ rowElement.control?.minWidth &&
1721
+ (rowElement.control?.rowFlex === RowFlex.CENTER ||
1722
+ rowElement.control?.rowFlex === RowFlex.RIGHT)
1723
+ ) {
1724
+ // 计算当前控件内容宽度是否超出最小宽度设置
1725
+ let controlContentWidth = rowElement.metrics.width
1726
+ let controlElementIndex = row.elementList.length - 1
1727
+ while (controlElementIndex >= 0) {
1728
+ const controlRowElement = row.elementList[controlElementIndex]
1729
+ controlContentWidth += controlRowElement.metrics.width
1730
+ // 找到首字符结束循环
1731
+ if (
1732
+ row.elementList[controlElementIndex - 1]?.controlComponent ===
1733
+ ControlComponent.PREFIX
1734
+ ) {
1735
+ controlFirstElement = controlRowElement
1736
+ break
1737
+ }
1738
+ controlElementIndex--
1739
+ }
1740
+ // 计算首字符偏移量
1741
+ if (controlFirstElement) {
1742
+ if (controlContentWidth < controlMinWidth) {
1743
+ if (rowElement.control.rowFlex === RowFlex.CENTER) {
1744
+ controlFirstElement.left =
1745
+ (controlMinWidth - controlContentWidth) / 2
1746
+ } else if (rowElement.control.rowFlex === RowFlex.RIGHT) {
1747
+ // 最小宽度 - 实际宽度 - 后缀元素宽度
1748
+ controlFirstElement.left =
1749
+ controlMinWidth - controlContentWidth - rowElement.metrics.width
1750
+ }
1751
+ }
1752
+ }
1753
+ }
1754
+ // 设置后缀偏移量:消费小于实际最小宽度
1755
+ const extraWidth = controlMinWidth - controlRealWidth
1756
+ if (extraWidth > 0) {
1757
+ const controlFirstElementLeft = controlFirstElement?.left || 0
1758
+ // 超出行宽时截断
1759
+ const rowRemainingWidth =
1760
+ availableWidth - row.width - rowElement.metrics.width
1761
+ const left = Math.min(rowRemainingWidth, extraWidth)
1762
+ // 后缀偏移量需减去首字符的偏移量,避免重复偏移
1763
+ rowElement.left = left - controlFirstElementLeft
1764
+ row.width += left - controlFirstElementLeft
1765
+ }
1766
+ }
1767
+ }