@tmagic/editor 1.8.0-manmanyu.9 → 1.8.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 (279) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +22 -13
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +10 -5
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +7 -6
  4. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +21 -122
  5. package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +21 -5
  6. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +62 -16
  7. package/dist/es/components/Icon.vue_vue_type_script_setup_true_lang.js +1 -1
  8. package/dist/es/components/Resizer.vue_vue_type_script_setup_true_lang.js +3 -2
  9. package/dist/es/components/ScrollBar.js +0 -1
  10. package/dist/es/components/ScrollBar.vue_vue_type_script_setup_true_lang.js +5 -4
  11. package/dist/es/components/ScrollViewer.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/es/components/SearchInput.vue_vue_type_script_setup_true_lang.js +1 -1
  13. package/dist/es/components/SplitView.vue_vue_type_script_setup_true_lang.js +1 -1
  14. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +1 -1
  17. package/dist/es/components/ViewForm.js +5 -0
  18. package/dist/es/components/ViewForm.vue_vue_type_script_setup_true_lang.js +56 -0
  19. package/dist/es/editorProps.js +7 -1
  20. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +1 -1
  22. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +30 -113
  23. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +2 -2
  24. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +7 -19
  25. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +7 -6
  26. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +4 -2
  27. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +12 -4
  28. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +9 -20
  29. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +2 -2
  30. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +5 -3
  31. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +9 -4
  32. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +2 -2
  33. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +10 -110
  34. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +52 -363
  35. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +1 -1
  36. package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  37. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +3 -6
  38. package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +1 -1
  39. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +4 -35
  40. package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +3 -3
  41. package/dist/es/fields/StyleSetter/configs.js +663 -0
  42. package/dist/es/fields/StyleSetter/icons/align-items/Center.js +1 -1
  43. package/dist/es/fields/StyleSetter/icons/align-items/FlexEnd.js +1 -1
  44. package/dist/es/fields/StyleSetter/icons/align-items/FlexStart.js +1 -1
  45. package/dist/es/fields/StyleSetter/icons/align-items/SpaceAround.js +1 -1
  46. package/dist/es/fields/StyleSetter/icons/align-items/SpaceBetween.js +1 -1
  47. package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +1 -1
  48. package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +1 -1
  49. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +1 -1
  50. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +1 -1
  51. package/dist/es/fields/StyleSetter/icons/display/Block.js +1 -1
  52. package/dist/es/fields/StyleSetter/icons/display/Flex.js +1 -1
  53. package/dist/es/fields/StyleSetter/icons/display/Inline.js +1 -1
  54. package/dist/es/fields/StyleSetter/icons/display/InlineBlock.js +1 -1
  55. package/dist/es/fields/StyleSetter/icons/display/None.js +1 -1
  56. package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +1 -1
  57. package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +1 -1
  58. package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +1 -1
  59. package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +1 -1
  60. package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +1 -1
  61. package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +1 -1
  62. package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +1 -1
  63. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +1 -1
  64. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +1 -1
  65. package/dist/es/fields/StyleSetter/icons/text-align/Center.js +1 -1
  66. package/dist/es/fields/StyleSetter/icons/text-align/Left.js +1 -1
  67. package/dist/es/fields/StyleSetter/icons/text-align/Right.js +1 -1
  68. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +29 -106
  69. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +8 -11
  70. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +30 -89
  71. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +29 -297
  72. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +29 -82
  73. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +8 -24
  74. package/dist/es/fields/UISelect.js +0 -1
  75. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +1 -1
  76. package/dist/es/fields/configs/codeSelect.js +98 -0
  77. package/dist/es/fields/configs/displayConds.js +158 -0
  78. package/dist/es/fields/configs/eventSelect.js +225 -0
  79. package/dist/es/fields/configs/stickyAddButton.js +21 -0
  80. package/dist/es/fields/headless-validation.js +139 -0
  81. package/dist/es/headless.js +2 -0
  82. package/dist/es/hooks/use-code-block-edit.js +4 -2
  83. package/dist/es/hooks/use-compare-form.js +101 -0
  84. package/dist/es/hooks/use-form-context.js +29 -0
  85. package/dist/es/hooks/use-stage.js +1 -0
  86. package/dist/es/icons/CenterIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  87. package/dist/es/icons/CodeIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  88. package/dist/es/icons/DatasourceIcon.js +1 -1
  89. package/dist/es/icons/FolderMinusIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  90. package/dist/es/index.js +16 -8
  91. package/dist/es/initService.js +52 -16
  92. package/dist/es/layouts/AddPageBox.vue_vue_type_script_setup_true_lang.js +1 -1
  93. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +26 -10
  94. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +11 -8
  95. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  96. package/dist/es/layouts/NavMenuColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  97. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +1 -1
  98. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +8 -5
  99. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +1 -1
  100. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +26 -10
  101. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +11 -13
  102. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +1 -1
  103. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +1 -1
  104. package/dist/es/layouts/history-list/composables.js +1 -1
  105. package/dist/es/layouts/history-list/useHistoryRevert.js +38 -33
  106. package/dist/es/layouts/page-bar/AddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  107. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +1 -1
  108. package/dist/es/layouts/page-bar/PageBarScrollContainer.vue_vue_type_script_setup_true_lang.js +1 -1
  109. package/dist/es/layouts/page-bar/PageList.vue_vue_type_script_setup_true_lang.js +1 -1
  110. package/dist/es/layouts/page-bar/Search.vue_vue_type_script_setup_true_lang.js +2 -2
  111. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +50 -20
  112. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +53 -35
  113. package/dist/es/layouts/props-panel/use-style-panel.js +3 -2
  114. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +3 -3
  115. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +21 -36
  116. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +1 -1
  117. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +4 -4
  118. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +19 -4
  119. package/dist/es/layouts/sidebar/data-source/DataSourceAddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  120. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +9 -4
  121. package/dist/es/layouts/sidebar/data-source/DataSourceList.vue_vue_type_script_setup_true_lang.js +1 -1
  122. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +4 -4
  123. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +19 -4
  124. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +3 -3
  125. package/dist/es/layouts/sidebar/layer/LayerNodeContent.js +5 -0
  126. package/dist/es/layouts/sidebar/layer/LayerNodeContent.vue_vue_type_script_setup_true_lang.js +46 -0
  127. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +1 -1
  128. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +6 -4
  129. package/dist/es/layouts/sidebar/layer/use-click.js +3 -3
  130. package/dist/es/layouts/sidebar/layer/use-drag.js +11 -10
  131. package/dist/es/layouts/sidebar/layer/use-node-status.js +2 -2
  132. package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +1 -1
  133. package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +1 -1
  134. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +9 -3
  135. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +55 -9
  136. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -1
  137. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +8 -8
  138. package/dist/es/plugin.js +35 -23
  139. package/dist/es/services/codeBlock.js +8 -3
  140. package/dist/es/services/dataSource.js +3 -0
  141. package/dist/es/services/dep.js +162 -41
  142. package/dist/es/services/editor.js +344 -113
  143. package/dist/es/services/events.js +4 -4
  144. package/dist/es/services/history.js +17 -1
  145. package/dist/es/services/keybinding.js +3 -3
  146. package/dist/es/services/props.js +2 -1
  147. package/dist/es/services/storage.js +2 -2
  148. package/dist/es/services/ui.js +7 -2
  149. package/dist/es/style.css +277 -114
  150. package/dist/es/utils/code-block.js +14 -7
  151. package/dist/es/utils/config.js +4 -1
  152. package/dist/es/utils/const.js +2 -2
  153. package/dist/es/utils/content-menu.js +2 -2
  154. package/dist/es/utils/data-source/index.js +34 -12
  155. package/dist/es/utils/dep/collect-worker-client.js +154 -0
  156. package/dist/es/utils/dep/idle-task.js +70 -22
  157. package/dist/es/utils/dep/worker.js +1 -1
  158. package/dist/es/utils/editor.js +4 -2
  159. package/dist/es/utils/event.js +135 -0
  160. package/dist/es/utils/monaco-editor.js +22 -6
  161. package/dist/es/utils/operator.js +2 -2
  162. package/dist/es/utils/props.js +50 -106
  163. package/dist/es/utils/type-match-rules.js +356 -0
  164. package/dist/style.css +277 -114
  165. package/dist/themes/magic-admin.css +978 -175
  166. package/dist/tmagic-editor-headless.umd.cjs +10880 -0
  167. package/dist/tmagic-editor.umd.cjs +12695 -10896
  168. package/package.json +18 -13
  169. package/src/Editor.vue +23 -6
  170. package/src/components/CodeBlockEditor.vue +3 -0
  171. package/src/components/CodeParams.vue +10 -4
  172. package/src/components/CompareForm.vue +20 -190
  173. package/src/components/ContentMenu.vue +34 -6
  174. package/src/components/FloatingBox.vue +71 -7
  175. package/src/components/ScrollBar.vue +0 -67
  176. package/src/components/ViewForm.vue +54 -0
  177. package/src/editorProps.ts +23 -10
  178. package/src/fields/Code.vue +1 -1
  179. package/src/fields/CodeSelect.vue +20 -113
  180. package/src/fields/CodeSelectCol.vue +1 -1
  181. package/src/fields/CondOpSelect.vue +5 -26
  182. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +2 -2
  183. package/src/fields/DataSourceFieldSelect/Index.vue +4 -1
  184. package/src/fields/DataSourceFields.vue +5 -1
  185. package/src/fields/DataSourceInput.vue +10 -28
  186. package/src/fields/DataSourceMethodSelect.vue +1 -1
  187. package/src/fields/DataSourceMethods.vue +6 -2
  188. package/src/fields/DataSourceMocks.vue +3 -0
  189. package/src/fields/DataSourceSelect.vue +1 -1
  190. package/src/fields/DisplayConds.vue +17 -139
  191. package/src/fields/EventSelect.vue +29 -416
  192. package/src/fields/KeyValue.vue +1 -1
  193. package/src/fields/StyleSetter/Index.vue +2 -8
  194. package/src/fields/StyleSetter/components/Border.vue +4 -38
  195. package/src/fields/StyleSetter/components/Box.vue +1 -1
  196. package/src/fields/StyleSetter/components/Position.vue +1 -1
  197. package/src/fields/StyleSetter/configs.ts +690 -0
  198. package/src/fields/StyleSetter/pro/Background.vue +9 -64
  199. package/src/fields/StyleSetter/pro/Border.vue +5 -16
  200. package/src/fields/StyleSetter/pro/Font.vue +11 -71
  201. package/src/fields/StyleSetter/pro/Layout.vue +10 -230
  202. package/src/fields/StyleSetter/pro/Position.vue +11 -82
  203. package/src/fields/StyleSetter/pro/Transform.vue +6 -34
  204. package/src/fields/UISelect.vue +0 -12
  205. package/src/fields/configs/codeSelect.ts +127 -0
  206. package/src/fields/configs/displayConds.ts +184 -0
  207. package/src/fields/configs/eventSelect.ts +309 -0
  208. package/src/fields/configs/stickyAddButton.ts +32 -0
  209. package/src/fields/headless-validation.ts +177 -0
  210. package/src/headless.ts +33 -0
  211. package/src/hooks/index.ts +1 -0
  212. package/src/hooks/use-compare-form.ts +166 -0
  213. package/src/hooks/use-form-context.ts +56 -0
  214. package/src/hooks/use-stage.ts +1 -0
  215. package/src/index.ts +9 -0
  216. package/src/initService.ts +73 -18
  217. package/src/layouts/CodeEditor.vue +32 -6
  218. package/src/layouts/Framework.vue +7 -6
  219. package/src/layouts/history-list/BucketTab.vue +2 -2
  220. package/src/layouts/history-list/HistoryDiffDialog.vue +33 -15
  221. package/src/layouts/history-list/HistoryListPanel.vue +16 -18
  222. package/src/layouts/history-list/useHistoryRevert.ts +57 -41
  223. package/src/layouts/props-panel/FormPanel.vue +75 -20
  224. package/src/layouts/props-panel/PropsPanel.vue +81 -30
  225. package/src/layouts/props-panel/use-style-panel.ts +4 -3
  226. package/src/layouts/sidebar/ComponentListPanel.vue +6 -2
  227. package/src/layouts/sidebar/Sidebar.vue +5 -3
  228. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +5 -2
  229. package/src/layouts/sidebar/code-block/useContentMenu.ts +14 -2
  230. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +3 -0
  231. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +3 -3
  232. package/src/layouts/sidebar/data-source/useContentMenu.ts +14 -2
  233. package/src/layouts/sidebar/layer/LayerMenu.vue +2 -2
  234. package/src/layouts/sidebar/layer/LayerNodeContent.vue +57 -0
  235. package/src/layouts/sidebar/layer/LayerPanel.vue +5 -2
  236. package/src/layouts/sidebar/layer/use-click.ts +3 -3
  237. package/src/layouts/sidebar/layer/use-node-status.ts +2 -2
  238. package/src/layouts/workspace/viewer/NodeListMenu.vue +4 -1
  239. package/src/layouts/workspace/viewer/Stage.vue +80 -10
  240. package/src/layouts/workspace/viewer/ViewerMenu.vue +7 -7
  241. package/src/plugin.ts +35 -25
  242. package/src/services/codeBlock.ts +4 -0
  243. package/src/services/dataSource.ts +3 -0
  244. package/src/services/dep.ts +292 -47
  245. package/src/services/editor.ts +383 -120
  246. package/src/services/events.ts +8 -7
  247. package/src/services/history.ts +22 -1
  248. package/src/services/keybinding.ts +3 -3
  249. package/src/services/ui.ts +26 -5
  250. package/src/theme/code-block.scss +0 -37
  251. package/src/theme/display-conds.scss +4 -0
  252. package/src/theme/event.scss +25 -33
  253. package/src/theme/floating-box.scss +9 -1
  254. package/src/theme/framework.scss +4 -1
  255. package/src/theme/layer-node-content.scss +14 -0
  256. package/src/theme/nav-menu.scss +5 -1
  257. package/src/theme/props-panel.scss +17 -3
  258. package/src/theme/scroll-bar.scss +64 -0
  259. package/src/theme/theme.scss +3 -0
  260. package/src/theme/themes/magic-admin/index.scss +109 -9
  261. package/src/theme/ui-select.scss +9 -0
  262. package/src/type.ts +216 -6
  263. package/src/utils/code-block.ts +1 -0
  264. package/src/utils/config.ts +9 -1
  265. package/src/utils/const.ts +3 -3
  266. package/src/utils/content-menu.ts +2 -2
  267. package/src/utils/data-source/index.ts +31 -12
  268. package/src/utils/dep/collect-worker-client.ts +215 -0
  269. package/src/utils/dep/idle-task.ts +104 -35
  270. package/src/utils/dep/worker.ts +113 -9
  271. package/src/utils/event.ts +237 -0
  272. package/src/utils/index.ts +2 -0
  273. package/src/utils/monaco-editor.ts +27 -0
  274. package/src/utils/operator.ts +2 -2
  275. package/src/utils/props.ts +61 -122
  276. package/src/utils/type-match-rules.ts +697 -0
  277. package/types/headless.d.ts +17 -0
  278. package/types/index.d.ts +1199 -724
  279. package/src/fields/StyleSetter/icons/align-items/index copy.ts +0 -5
@@ -7,11 +7,13 @@ import storage_default, { Protocol } from "./storage.js";
7
7
  import { COPY_STORAGE_KEY, calcAlignCenterStyle, calcLayerTargetIndex, calcMoveStyle, classifyDragSources, collectRelatedNodes, describeStepForRevert, editorNodeMergeCustomizer, fixNodePosition, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, moveItemsInContainer, resolveSelectedNode, setChildrenLayout, setLayout, toggleFixedPosition } from "../utils/editor.js";
8
8
  import { beforePaste, getAddParent } from "../utils/operator.js";
9
9
  import { isFixed } from "@tmagic/stage";
10
- import { getNodeInfo, getNodePath, getValueByKeyPath, guid, isPage, isPageFragment, setValueByKeyPath } from "@tmagic/utils";
10
+ import { getNodeInfo, getNodePath, getValueByKeyPath, guid, isPage, isPageFragment, isPageOrFragment, setValueByKeyPath, traverseNode } from "@tmagic/utils";
11
11
  import { NodeType } from "@tmagic/core";
12
- import { reactive, toRaw } from "vue";
12
+ import { nextTick, reactive, toRaw } from "vue";
13
13
  import { cloneDeep as cloneDeep$1, isEmpty, isEqual, isObject, mergeWith, uniq } from "lodash-es";
14
14
  //#region packages/editor/src/services/editor.ts
15
+ /** 历史插回时把记录的下标收敛到 [0, length],越界(含未记录)一律追加到末尾 */
16
+ var clampIndex = (index, length) => typeof index === "number" && index >= 0 && index <= length ? index : length;
15
17
  /**
16
18
  * 把「变更前后节点快照」列表归一成 update 类型的 {@link StepDiffItem} 列表,供 {@link StepValue.diff} 使用。
17
19
  * `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
@@ -33,6 +35,7 @@ var Editor = class extends BaseService {
33
35
  stageLoading: true,
34
36
  highlightNode: null,
35
37
  modifiedNodeIds: /* @__PURE__ */ new Map(),
38
+ invalidNodeIds: /* @__PURE__ */ new Map(),
36
39
  pageLength: 0,
37
40
  pageFragmentLength: 0,
38
41
  disabledMultiSelect: false,
@@ -40,11 +43,21 @@ var Editor = class extends BaseService {
40
43
  });
41
44
  selectionBeforeOp = null;
42
45
  /**
46
+ * 操作前的节点校验错误快照,与 selectionBeforeOp 同时在 captureSelectionBeforeOp 中捕获,
47
+ * 供 pushOpHistory 写入 step.extra.invalidNodeIdsBefore,用于撤销时还原到「操作前」的错误状态。
48
+ */
49
+ invalidNodeIdsBeforeOp = null;
50
+ /**
43
51
  * 最近一次 pushOpHistory 写入的历史记录 uuid。
44
52
  * 供 *AndGetHistoryId 系列方法在调用普通操作后取回本次产生的历史记录 id;
45
53
  * 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
46
54
  */
47
55
  lastPushedHistoryId = null;
56
+ /**
57
+ * 上一次 doAdd 的插入记忆:nodeId 为插入的节点,selectedId 为当时的选中节点。
58
+ * 仅在选中节点未变化时复用(连续 add / doNotSelect / 批量粘贴),选中变化后自动失效,无需手动清理。
59
+ */
60
+ lastAdded = null;
48
61
  constructor() {
49
62
  super(canUsePluginMethods.async.map((methodName) => ({
50
63
  name: methodName,
@@ -57,7 +70,7 @@ var Editor = class extends BaseService {
57
70
  }
58
71
  /**
59
72
  * 设置当前指点节点配置
60
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
73
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'invalidNodeIds' | 'pageLength' | 'pageFragmentLength
61
74
  * @param value MNode
62
75
  * @param options.historySource 设置 root 时,本次变更写入历史记录的「操作来源」(仅 name === 'root' 时生效)
63
76
  */
@@ -85,11 +98,12 @@ var Editor = class extends BaseService {
85
98
  this.state.stageLoading = false;
86
99
  }
87
100
  this.emit("root-change", value, preValue, options);
101
+ this.pruneInvalidNodeIds();
88
102
  }
89
103
  }
90
104
  /**
91
105
  * 获取当前指点节点配置
92
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
106
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'invalidNodeIds' | 'pageLength' | 'pageFragmentLength'
93
107
  * @returns MNode
94
108
  */
95
109
  get(name) {
@@ -152,7 +166,7 @@ var Editor = class extends BaseService {
152
166
  isOnDifferentPage(node) {
153
167
  const currentPageId = this.get("page")?.id;
154
168
  if (currentPageId === void 0 || currentPageId === null) return false;
155
- if (isPage(node) || isPageFragment(node)) return `${node.id}` !== `${currentPageId}`;
169
+ if (isPageOrFragment(node)) return `${node.id}` !== `${currentPageId}`;
156
170
  const nodePage = this.getNodeInfo(node.id, false).page;
157
171
  if (!nodePage) return false;
158
172
  return `${nodePage.id}` !== `${currentPageId}`;
@@ -239,24 +253,29 @@ var Editor = class extends BaseService {
239
253
  this.set("stage", null);
240
254
  this.set("highlightNode", null);
241
255
  }
242
- async doAdd(node, parent) {
256
+ async doAdd(node, parent, _options = {}) {
243
257
  const root = this.get("root");
244
258
  if (!root) throw new Error("root为空");
245
259
  const curNode = this.get("node");
246
260
  const stage = this.get("stage");
247
261
  if (!curNode) throw new Error("当前选中节点为空");
248
- if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !(isPage(node) || isPageFragment(node))) throw new Error("app下不能添加组件");
249
- if (parent.id !== curNode.id && !(isPage(node) || isPageFragment(node))) {
250
- const index = parent.items.indexOf(curNode);
251
- parent.items?.splice(index + 1, 0, node);
252
- } else parent.items?.push(node);
262
+ if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !isPageOrFragment(node)) throw new Error("app下不能添加组件");
263
+ const anchorId = this.lastAdded?.selectedId === curNode.id ? this.lastAdded.nodeId : curNode.id;
264
+ const anchorIndex = isPageOrFragment(node) ? -1 : Math.max(getNodeIndex(anchorId, parent), getNodeIndex(curNode.id, parent));
265
+ const insertIndex = anchorIndex < 0 ? parent.items.length : anchorIndex + 1;
266
+ parent.items.splice(insertIndex, 0, node);
267
+ this.lastAdded = {
268
+ selectedId: curNode.id,
269
+ nodeId: node.id
270
+ };
253
271
  const layout = await this.getLayout(toRaw(parent), node);
254
272
  node.style = getInitPositionStyle(node.style, layout);
255
273
  await stage?.add({
256
274
  config: cloneDeep$1(node),
257
275
  parent: cloneDeep$1(parent),
258
276
  parentId: parent.id,
259
- root: cloneDeep$1(root)
277
+ root: cloneDeep$1(root),
278
+ index: insertIndex
260
279
  });
261
280
  const newStyle = fixNodePosition(node, parent, stage);
262
281
  if (newStyle && (newStyle.top !== node.style.top || newStyle.left !== node.style.left)) {
@@ -274,14 +293,12 @@ var Editor = class extends BaseService {
274
293
  * 向指点容器添加组件节点
275
294
  * @param addConfig 将要添加的组件节点配置
276
295
  * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
277
- * @param options 可选配置
278
- * @param options.doNotSelect 添加后是否不更新当前选中节点(默认 false,添加后会选中新增的节点)
279
- * @param options.doNotSwitchPage 添加后是否不切换当前页面(默认 false;新增页面 / 跨页新增时为 true 会跳过会引发页面切换的选中操作)
280
- * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
296
+ * @param options 可选配置,见 {@link DslOpOptions}
281
297
  * @returns 添加后的节点
282
298
  */
283
- async add(addNode, parent, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
299
+ async add(addNode, parent, options = {}) {
284
300
  this.captureSelectionBeforeOp();
301
+ const { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = options;
285
302
  const stage = this.get("stage");
286
303
  const addNodes = [];
287
304
  if (!Array.isArray(addNode)) {
@@ -289,13 +306,13 @@ var Editor = class extends BaseService {
289
306
  if (!type) throw new Error("组件类型不能为空");
290
307
  addNodes.push({ ...toRaw(await props_default.getPropsValue(type, config)) });
291
308
  } else addNodes.push(...addNode);
292
- const newNodes = await Promise.all(addNodes.map((node) => {
309
+ const newNodes = [];
310
+ for (const node of addNodes) {
293
311
  const root = this.get("root");
294
- if ((isPage(node) || isPageFragment(node)) && root) return this.doAdd(node, root);
295
- const parentNode = parent ?? getAddParent(node);
312
+ const parentNode = isPageOrFragment(node) && root ? root : parent ?? getAddParent(node);
296
313
  if (!parentNode) throw new Error("未找到父元素");
297
- return this.doAdd(node, parentNode);
298
- }));
314
+ newNodes.push(await this.doAdd(node, parentNode, options));
315
+ }
299
316
  if (newNodes.length > 1) {
300
317
  const wouldSwitchPage = newNodes.some((n) => this.isOnDifferentPage(n));
301
318
  if (!doNotSelect && !(doNotSwitchPage && wouldSwitchPage)) {
@@ -311,7 +328,7 @@ var Editor = class extends BaseService {
311
328
  else if (isPageFragment(newNodes[0])) this.state.pageFragmentLength += 1;
312
329
  else if (!skipSelect) stage?.select(newNodes[0].id);
313
330
  }
314
- if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
331
+ if (!isPageOrFragment(newNodes[0])) {
315
332
  const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
316
333
  if (!doNotPushHistory) {
317
334
  const parentId = (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id;
@@ -335,6 +352,20 @@ var Editor = class extends BaseService {
335
352
  } else this.selectionBeforeOp = null;
336
353
  }
337
354
  this.emit("add", newNodes);
355
+ this.emit("change", {
356
+ type: "add",
357
+ data: newNodes.map((node) => ({
358
+ node,
359
+ page: this.getPageOfNode(node.id)
360
+ })),
361
+ historySource,
362
+ doNotPushHistory
363
+ });
364
+ const addedPages = newNodes.filter((node) => isPageOrFragment(node));
365
+ if (addedPages.length) history_default.notifyPageStructureChange({
366
+ add: addedPages,
367
+ remove: []
368
+ });
338
369
  return Array.isArray(addNode) ? newNodes : newNodes[0];
339
370
  }
340
371
  async doRemove(node, { doNotSelect = false, doNotSwitchPage = false } = {}) {
@@ -344,13 +375,10 @@ var Editor = class extends BaseService {
344
375
  if (!parent || !curNode) throw new Error("找不要删除的节点");
345
376
  const index = getNodeIndex(curNode.id, parent);
346
377
  if (typeof index !== "number" || index === -1) throw new Error("找不要删除的节点");
347
- parent.items?.splice(index, 1);
348
378
  const stage = this.get("stage");
349
- stage?.remove({
350
- id: node.id,
351
- parentId: parent.id,
352
- root: cloneDeep$1(root)
353
- });
379
+ const currentPage = this.get("page");
380
+ const isDeletingCurrentPage = isPageOrFragment(node) && !!currentPage && `${currentPage.id}` === `${node.id}`;
381
+ parent.items?.splice(index, 1);
354
382
  const selectedNodes = this.get("nodes");
355
383
  const removedSelectedIndex = selectedNodes.findIndex((n) => `${n.id}` === `${node.id}`);
356
384
  if (removedSelectedIndex !== -1) {
@@ -358,24 +386,29 @@ var Editor = class extends BaseService {
358
386
  nextSelected.splice(removedSelectedIndex, 1);
359
387
  this.set("nodes", nextSelected);
360
388
  }
361
- if (isPage(node) || isPageFragment(node)) {
362
- const currentPage = this.get("page");
363
- if (currentPage && `${currentPage.id}` === `${node.id}`) this.set("page", null);
364
- }
365
- const selectDefault = async (pages) => {
366
- if (pages[0]) {
367
- await this.select(pages[0]);
368
- stage?.select(pages[0].id);
369
- } else this.selectRoot();
389
+ const removeData = {
390
+ id: node.id,
391
+ parentId: parent.id,
392
+ root: cloneDeep$1(root)
370
393
  };
371
394
  const rootItems = root.items || [];
372
- if (isPage(node)) {
373
- this.state.pageLength -= 1;
374
- if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
375
- } else if (isPageFragment(node)) {
376
- this.state.pageFragmentLength -= 1;
377
- if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
395
+ const shouldReselect = isDeletingCurrentPage && !doNotSelect && !doNotSwitchPage;
396
+ const shouldSwitchPage = shouldReselect && rootItems.length > 0;
397
+ if (isPageOrFragment(node)) {
398
+ if (isPage(node)) this.state.pageLength -= 1;
399
+ else this.state.pageFragmentLength -= 1;
400
+ if (shouldSwitchPage) {
401
+ await this.select(rootItems[0]);
402
+ stage?.select(rootItems[0].id);
403
+ await nextTick();
404
+ stage?.remove(removeData);
405
+ } else {
406
+ stage?.remove(removeData);
407
+ if (shouldReselect) this.selectRoot();
408
+ else if (isDeletingCurrentPage) this.set("page", null);
409
+ }
378
410
  } else {
411
+ stage?.remove(removeData);
379
412
  if (!doNotSelect) {
380
413
  await this.select(parent);
381
414
  stage?.select(parent.id);
@@ -395,12 +428,17 @@ var Editor = class extends BaseService {
395
428
  * @param options.doNotSwitchPage 删除后是否不切换当前页面(默认 false;删除页面 / 页面片段时为 true 会跳过自动切换到首个剩余页面)
396
429
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
397
430
  */
398
- async remove(nodeOrNodeList, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
431
+ async remove(nodeOrNodeList, options = {}) {
432
+ const { doNotPushHistory = false, historyDescription, historySource } = options;
399
433
  this.captureSelectionBeforeOp();
400
434
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
435
+ const changeItems = nodes.map((node) => ({
436
+ node,
437
+ page: this.getPageOfNode(node.id)
438
+ }));
401
439
  const removedItems = [];
402
440
  let pageForOp = null;
403
- if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) for (const n of nodes) {
441
+ if (!isPageOrFragment(nodes[0])) for (const n of nodes) {
404
442
  const { parent, node: curNode, page } = this.getNodeInfo(n.id, false);
405
443
  if (parent && curNode) {
406
444
  if (!pageForOp && page) pageForOp = {
@@ -415,27 +453,39 @@ var Editor = class extends BaseService {
415
453
  });
416
454
  }
417
455
  }
418
- await Promise.all(nodes.map((node) => this.doRemove(node, {
419
- doNotSelect,
420
- doNotSwitchPage
421
- })));
422
- if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
423
- diff: removedItems,
424
- pageData: pageForOp,
425
- historyDescription,
426
- source: historySource
427
- });
428
- else this.selectionBeforeOp = null;
456
+ await Promise.all(nodes.map((node) => this.doRemove(node, options)));
457
+ this.removeInvalidNodesBySubtree(nodes);
458
+ if (removedItems.length > 0 && pageForOp) {
459
+ if (!doNotPushHistory) this.pushOpHistory("remove", {
460
+ diff: removedItems,
461
+ pageData: pageForOp,
462
+ historyDescription,
463
+ source: historySource
464
+ });
465
+ else this.selectionBeforeOp = null;
466
+ }
429
467
  this.emit("remove", nodes);
468
+ this.emit("change", {
469
+ type: "remove",
470
+ data: changeItems,
471
+ historySource,
472
+ doNotPushHistory
473
+ });
474
+ const removedPages = nodes.filter((node) => isPageOrFragment(node));
475
+ if (removedPages.length) history_default.notifyPageStructureChange({
476
+ add: [],
477
+ remove: removedPages
478
+ });
430
479
  }
431
- async doUpdate(config, { changeRecords = [], historySource } = {}) {
480
+ async doUpdate(config, data = {}) {
481
+ const { changeRecords = [], historySource, replace = false } = data;
432
482
  if (!this.get("root")) throw new Error("root为空");
433
483
  if (!config?.id) throw new Error("没有配置或者配置缺少id值");
434
484
  const info = this.getNodeInfo(config.id, false);
435
485
  if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
436
486
  const node = toRaw(info.node);
437
- let newConfig = await toggleFixedPosition(toRaw(config), node, info.path, this.getLayout);
438
- newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
487
+ let newConfig = replace ? cloneDeep$1(toRaw(config)) : await toggleFixedPosition(toRaw(config), node, info.path, this.getLayout);
488
+ if (!replace) newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
439
489
  if (!newConfig.type) throw new Error("配置缺少type值");
440
490
  if (newConfig.type === NodeType.ROOT) {
441
491
  this.set("root", newConfig, { historySource });
@@ -450,9 +500,11 @@ var Editor = class extends BaseService {
450
500
  const parentNodeItems = parent.items;
451
501
  const index = getNodeIndex(newConfig.id, parent);
452
502
  if (!parentNodeItems || typeof index === "undefined" || index === -1) throw new Error("更新的节点未找到");
453
- const newLayout = await this.getLayout(newConfig);
454
- const layout = await this.getLayout(node);
455
- if (Array.isArray(newConfig.items) && newLayout !== layout) newConfig = setChildrenLayout(newConfig, newLayout);
503
+ if (!replace) {
504
+ const newLayout = await this.getLayout(newConfig);
505
+ const layout = await this.getLayout(node);
506
+ if (Array.isArray(newConfig.items) && newLayout !== layout) newConfig = setChildrenLayout(newConfig, newLayout);
507
+ }
456
508
  parentNodeItems[index] = newConfig;
457
509
  const selectedNodes = this.get("nodes");
458
510
  const targetIndex = selectedNodes.findIndex((nodeItem) => `${nodeItem.id}` === `${newConfig.id}`);
@@ -460,7 +512,7 @@ var Editor = class extends BaseService {
460
512
  selectedNodes.splice(targetIndex, 1, newConfig);
461
513
  this.set("nodes", [...selectedNodes]);
462
514
  }
463
- if (isPage(newConfig) || isPageFragment(newConfig)) {
515
+ if (isPageOrFragment(newConfig)) {
464
516
  const currentPage = this.get("page");
465
517
  if (currentPage && `${currentPage.id}` === `${newConfig.id}`) this.set("page", newConfig);
466
518
  }
@@ -475,43 +527,51 @@ var Editor = class extends BaseService {
475
527
  * 更新节点
476
528
  * update后会触发依赖收集,收集完后会掉stage.update方法
477
529
  * @param config 新的节点配置,配置中需要有id信息
478
- * @param data 额外数据
479
- * @param data.changeRecords 单节点 form 端变更记录(多节点场景下被忽略,使用 changeRecordList)
480
- * @param data.changeRecordList 多节点 form 端变更记录列表,按 config 数组同序对应每个节点;优先级高于 changeRecords
481
- * @param data.doNotPushHistory 是否不写入历史记录(默认 false)
482
- * @param data.historyDescription 入栈时附带的人类可读描述,用于历史面板展示(不影响 undo/redo 行为)
530
+ * @param data 额外数据,见 {@link UpdateOptions}
483
531
  * @returns 更新后的节点配置
484
532
  */
485
533
  async update(config, data = {}) {
486
534
  this.captureSelectionBeforeOp();
487
- const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource } = data;
535
+ const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource, invalidInfo } = data;
488
536
  const nodes = Array.isArray(config) ? config : [config];
489
537
  const updateData = await Promise.all(nodes.map((node, index) => {
490
538
  const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
491
539
  return this.doUpdate(node, {
492
- changeRecords: recordsForNode,
493
- historySource
540
+ ...data,
541
+ changeRecords: recordsForNode
494
542
  });
495
543
  }));
544
+ this.applyInvalidInfo(config, invalidInfo);
496
545
  if (updateData[0].oldNode?.type !== NodeType.ROOT) {
497
- if (this.get("nodes").length) if (!doNotPushHistory) {
498
- const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
499
- this.pushOpHistory("update", {
500
- diff: buildUpdateDiff(updateData.map((d) => ({
501
- oldNode: cloneDeep$1(d.oldNode),
502
- newNode: cloneDeep$1(d.newNode),
503
- changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
504
- }))),
505
- pageData: {
506
- name: pageForOp?.name || "",
507
- id: pageForOp.id
508
- },
509
- historyDescription,
510
- source: historySource
511
- });
512
- } else this.selectionBeforeOp = null;
546
+ if (this.get("nodes").length) {
547
+ if (!doNotPushHistory) {
548
+ const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
549
+ this.pushOpHistory("update", {
550
+ diff: buildUpdateDiff(updateData.map((d) => ({
551
+ oldNode: cloneDeep$1(d.oldNode),
552
+ newNode: cloneDeep$1(d.newNode),
553
+ changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
554
+ }))),
555
+ pageData: {
556
+ name: pageForOp?.name || "",
557
+ id: pageForOp.id
558
+ },
559
+ historyDescription,
560
+ source: historySource
561
+ });
562
+ } else this.selectionBeforeOp = null;
563
+ }
513
564
  }
514
565
  this.emit("update", updateData);
566
+ this.emit("change", {
567
+ type: "update",
568
+ data: updateData.map((node) => ({
569
+ node,
570
+ page: this.getPageOfNode(node.newNode.id)
571
+ })),
572
+ historySource,
573
+ doNotPushHistory
574
+ });
515
575
  return Array.isArray(config) ? updateData.map((item) => item.newNode) : updateData[0].newNode;
516
576
  }
517
577
  /**
@@ -628,8 +688,10 @@ var Editor = class extends BaseService {
628
688
  historyDescription,
629
689
  historySource
630
690
  });
631
- if (!doNotSelect) if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
632
- else await stage?.select(newNodes[0].id);
691
+ if (!doNotSelect) {
692
+ if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
693
+ else await stage?.select(newNodes[0].id);
694
+ }
633
695
  return newNode;
634
696
  }
635
697
  /**
@@ -677,6 +739,16 @@ var Editor = class extends BaseService {
677
739
  });
678
740
  } else this.selectionBeforeOp = null;
679
741
  this.emit("move-layer", offset);
742
+ this.emit("change", {
743
+ type: "move-layer",
744
+ data: [{
745
+ node,
746
+ page: this.getPageOfNode(node.id)
747
+ }],
748
+ offset,
749
+ historySource,
750
+ doNotPushHistory
751
+ });
680
752
  }
681
753
  /**
682
754
  * 移动一个或多个节点到指定容器中。
@@ -694,7 +766,7 @@ var Editor = class extends BaseService {
694
766
  */
695
767
  async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
696
768
  const isBatch = Array.isArray(config);
697
- const configs = (isBatch ? config : [config]).filter((item) => !(isPage(item) || isPageFragment(item)));
769
+ const configs = (isBatch ? config : [config]).filter((item) => !isPageOrFragment(item));
698
770
  if (configs.length === 0) throw new Error("没有可移动的节点");
699
771
  this.captureSelectionBeforeOp();
700
772
  const target = this.getNodeById(targetId, false);
@@ -806,6 +878,17 @@ var Editor = class extends BaseService {
806
878
  configs,
807
879
  targetParent
808
880
  });
881
+ this.emit("change", {
882
+ type: "drag-to",
883
+ data: configs.map((node) => ({
884
+ node,
885
+ page: this.getPageOfNode(node.id)
886
+ })),
887
+ targetIndex,
888
+ targetParent,
889
+ historySource,
890
+ doNotPushHistory
891
+ });
809
892
  }
810
893
  /**
811
894
  * 下列 *AndGetHistoryId 方法与对应的普通操作(add / remove / update ...)行为完全一致,
@@ -1039,6 +1122,7 @@ var Editor = class extends BaseService {
1039
1122
  this.set("stage", null);
1040
1123
  this.set("highlightNode", null);
1041
1124
  this.set("modifiedNodeIds", /* @__PURE__ */ new Map());
1125
+ this.set("invalidNodeIds", /* @__PURE__ */ new Map());
1042
1126
  this.set("pageLength", 0);
1043
1127
  }
1044
1128
  destroy() {
@@ -1049,6 +1133,51 @@ var Editor = class extends BaseService {
1049
1133
  resetModifiedNodeId() {
1050
1134
  this.get("modifiedNodeIds").clear();
1051
1135
  }
1136
+ /**
1137
+ * 记录(或覆盖)某个节点在指定来源(属性表单 / 样式表单)上的校验错误信息。
1138
+ * @param id 节点 id
1139
+ * @param source 错误来源:'props'(属性表单)| 'style'(样式表单)
1140
+ * @param message 错误文案(可能是包含 <br> 的 HTML)
1141
+ */
1142
+ setInvalidNode(id, source, message) {
1143
+ const map = this.get("invalidNodeIds");
1144
+ const info = { ...map.get(id) || {} };
1145
+ info[source] = message;
1146
+ map.set(id, info);
1147
+ this.emit("invalid-node-change", map);
1148
+ }
1149
+ /**
1150
+ * 删除节点的校验错误记录。
1151
+ * @param id 节点 id
1152
+ * @param source 指定来源则仅删除该来源;不传则删除该节点全部来源的错误
1153
+ */
1154
+ deleteInvalidNode(id, source) {
1155
+ const map = this.get("invalidNodeIds");
1156
+ if (!map.has(id)) return;
1157
+ if (!source) map.delete(id);
1158
+ else {
1159
+ const info = { ...map.get(id) || {} };
1160
+ delete info[source];
1161
+ if (info.props || info.style) map.set(id, info);
1162
+ else map.delete(id);
1163
+ }
1164
+ this.emit("invalid-node-change", map);
1165
+ }
1166
+ /** 获取当前存在校验错误的节点错误 Map(key 为节点 id) */
1167
+ getInvalidNodeIds() {
1168
+ return this.get("invalidNodeIds");
1169
+ }
1170
+ /** 获取指定节点的校验错误信息 */
1171
+ getInvalidNodeInfo(id) {
1172
+ return this.get("invalidNodeIds").get(id);
1173
+ }
1174
+ /** 清空全部校验错误记录 */
1175
+ resetInvalidNodeId() {
1176
+ const map = this.get("invalidNodeIds");
1177
+ if (map.size === 0) return;
1178
+ map.clear();
1179
+ this.emit("invalid-node-change", map);
1180
+ }
1052
1181
  usePlugin(options) {
1053
1182
  super.usePlugin(options);
1054
1183
  }
@@ -1061,12 +1190,63 @@ var Editor = class extends BaseService {
1061
1190
  emit(eventName, ...args) {
1062
1191
  return super.emit(eventName, ...args);
1063
1192
  }
1193
+ /**
1194
+ * 应用一次属性面板提交携带的校验错误信息(在写入历史记录之前调用,使历史快照与本次变更对齐)。
1195
+ * - invalidInfo 为空(调用方未携带):认为本次更新对应的节点已无校验错误,清除其全部来源的错误;
1196
+ * - error 非空则记录错误,为空则清除对应来源的错误。
1197
+ */
1198
+ applyInvalidInfo(config, invalidInfo) {
1199
+ if (!invalidInfo) {
1200
+ (Array.isArray(config) ? config : [config]).map((node) => node.id).filter((id) => id !== void 0).forEach((id) => this.deleteInvalidNode(id));
1201
+ return;
1202
+ }
1203
+ const { id, source, error } = invalidInfo;
1204
+ if (error) this.setInvalidNode(id, source, error);
1205
+ else this.deleteInvalidNode(id, source);
1206
+ }
1207
+ /** 删除被移除节点(含其子树)的校验错误记录,避免残留误报 */
1208
+ removeInvalidNodesBySubtree(nodes) {
1209
+ const map = this.get("invalidNodeIds");
1210
+ if (map.size === 0) return;
1211
+ let changed = false;
1212
+ nodes.forEach((node) => {
1213
+ traverseNode(node, (n) => {
1214
+ if (n.id !== void 0 && map.delete(n.id)) changed = true;
1215
+ });
1216
+ });
1217
+ if (changed) this.emit("invalid-node-change", map);
1218
+ }
1219
+ /** 清理不在当前 DSL 中的失效节点错误记录(用于整体替换 root 后) */
1220
+ pruneInvalidNodeIds() {
1221
+ const map = this.get("invalidNodeIds");
1222
+ if (map.size === 0) return;
1223
+ let changed = false;
1224
+ for (const id of [...map.keys()]) if (!this.getNodeById(id, false)) {
1225
+ map.delete(id);
1226
+ changed = true;
1227
+ }
1228
+ if (changed) this.emit("invalid-node-change", map);
1229
+ }
1064
1230
  addModifiedNodeId(id) {
1065
1231
  this.get("modifiedNodeIds").set(id, id);
1066
1232
  }
1233
+ /**
1234
+ * 获取指定节点所属的页面 / 页面片:
1235
+ * - 普通节点返回其所在的 page;
1236
+ * - 节点本身就是 page / pageFragment 时返回它自己;
1237
+ * - 找不到时返回 null。
1238
+ * 供 `change` 事件携带「变更节点对应的 page」(而非编辑器当前选中页)。
1239
+ */
1240
+ getPageOfNode(id) {
1241
+ const { node, page } = this.getNodeInfo(id, false);
1242
+ if (page) return page;
1243
+ if (node && isPageOrFragment(node)) return node;
1244
+ return null;
1245
+ }
1067
1246
  captureSelectionBeforeOp() {
1068
1247
  if (this.selectionBeforeOp) return;
1069
1248
  this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
1249
+ this.invalidNodeIdsBeforeOp = new Map(this.get("invalidNodeIds"));
1070
1250
  }
1071
1251
  /**
1072
1252
  * 比较「上一次 root」与「新 root」的页面 / 页面片,按页面粒度把整体替换拆成历史记录:
@@ -1083,24 +1263,35 @@ var Editor = class extends BaseService {
1083
1263
  const prevMap = new Map(prevPages.map((p) => [`${p.id}`, p]));
1084
1264
  const nextMap = new Map(nextPages.map((p) => [`${p.id}`, p]));
1085
1265
  const indexInItems = (root, id) => (root.items ?? []).findIndex((item) => `${item.id}` === `${id}`);
1266
+ const addedPages = [];
1267
+ const removedPages = [];
1086
1268
  nextPages.forEach((nextPage) => {
1087
1269
  const prevPage = prevMap.get(`${nextPage.id}`);
1088
- if (!prevPage) this.pushPageDiffStep("add", nextPage, {
1089
- newSchema: cloneDeep$1(toRaw(nextPage)),
1090
- parentId: nextRoot.id,
1091
- index: indexInItems(nextRoot, nextPage.id)
1092
- }, source);
1093
- else if (!isEqual(toRaw(prevPage), toRaw(nextPage))) this.pushPageDiffStep("update", nextPage, {
1270
+ if (!prevPage) {
1271
+ this.pushPageDiffStep("add", nextPage, {
1272
+ newSchema: cloneDeep$1(toRaw(nextPage)),
1273
+ parentId: nextRoot.id,
1274
+ index: indexInItems(nextRoot, nextPage.id)
1275
+ }, source);
1276
+ addedPages.push(nextPage);
1277
+ } else if (!isEqual(toRaw(prevPage), toRaw(nextPage))) this.pushPageDiffStep("update", nextPage, {
1094
1278
  oldSchema: cloneDeep$1(toRaw(prevPage)),
1095
1279
  newSchema: cloneDeep$1(toRaw(nextPage))
1096
1280
  }, source);
1097
1281
  });
1098
1282
  prevPages.forEach((prevPage) => {
1099
- if (!nextMap.has(`${prevPage.id}`)) this.pushPageDiffStep("remove", prevPage, {
1100
- oldSchema: cloneDeep$1(toRaw(prevPage)),
1101
- parentId: preRoot.id,
1102
- index: indexInItems(preRoot, prevPage.id)
1103
- }, source);
1283
+ if (!nextMap.has(`${prevPage.id}`)) {
1284
+ this.pushPageDiffStep("remove", prevPage, {
1285
+ oldSchema: cloneDeep$1(toRaw(prevPage)),
1286
+ parentId: preRoot.id,
1287
+ index: indexInItems(preRoot, prevPage.id)
1288
+ }, source);
1289
+ removedPages.push(prevPage);
1290
+ }
1291
+ });
1292
+ if (addedPages.length || removedPages.length) history_default.notifyPageStructureChange({
1293
+ add: addedPages,
1294
+ remove: removedPages
1104
1295
  });
1105
1296
  }
1106
1297
  /**
@@ -1139,7 +1330,9 @@ var Editor = class extends BaseService {
1139
1330
  extra: {
1140
1331
  selectedBefore: this.selectionBeforeOp ?? [],
1141
1332
  selectedAfter: this.get("nodes").map((n) => n.id),
1142
- modifiedNodeIds: new Map(this.get("modifiedNodeIds"))
1333
+ modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
1334
+ invalidNodeIdsBefore: new Map(this.invalidNodeIdsBeforeOp ?? this.get("invalidNodeIds")),
1335
+ invalidNodeIdsAfter: new Map(this.get("invalidNodeIds"))
1143
1336
  },
1144
1337
  diff
1145
1338
  };
@@ -1148,15 +1341,18 @@ var Editor = class extends BaseService {
1148
1341
  const historyId = history_default.push("page", step, pageData.id) ? step.uuid : null;
1149
1342
  this.lastPushedHistoryId = historyId;
1150
1343
  this.selectionBeforeOp = null;
1344
+ this.invalidNodeIdsBeforeOp = null;
1151
1345
  return historyId;
1152
1346
  }
1153
1347
  /**
1154
1348
  * 应用历史操作(撤销 / 重做)
1155
1349
  *
1156
- * 所有 DSL 修改都走 `editor.add / remove / update`,并通过 `doNotPushHistory` 阻止再次入栈、
1157
- * `doNotSelect / doNotSwitchPage` 让选区由方法末尾的统一逻辑兜底。
1350
+ * 删除 / 更新类修改走 `editor.remove / update`,并通过 `doNotPushHistory` 阻止再次入栈、
1351
+ * `doNotSelect / doNotSwitchPage` 让选区由方法末尾的统一逻辑兜底;
1352
+ * 「重新插回节点」(撤销 remove / 重做 add)需按 step 记录的 parentId / index 精确还原,
1353
+ * 不走 this.add,由手工 splice + stage.add 完成后补发等价的 add / change 事件(见 emitHistoryInsertEvents)。
1158
1354
  *
1159
- * 注意:这些公开方法会发出 add / remove / update 事件,业务侧若需要区分"用户操作"与"撤销重做触发",
1355
+ * 注意:这些路径都会发出 add / remove / update / change 事件,业务侧若需要区分"用户操作"与"撤销重做触发",
1160
1356
  * 请监听 `history-change` 事件配合判断。
1161
1357
  *
1162
1358
  * @param step 操作记录
@@ -1170,7 +1366,8 @@ var Editor = class extends BaseService {
1170
1366
  const commonOpts = {
1171
1367
  doNotSelect: true,
1172
1368
  doNotSwitchPage: true,
1173
- doNotPushHistory: true
1369
+ doNotPushHistory: true,
1370
+ historySource: step.source
1174
1371
  };
1175
1372
  switch (step.opType) {
1176
1373
  case "add": {
@@ -1182,20 +1379,25 @@ var Editor = class extends BaseService {
1182
1379
  }
1183
1380
  else {
1184
1381
  const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1382
+ const addedNodes = [];
1185
1383
  for (const { newSchema, parentId, index } of sorted) {
1186
1384
  if (!newSchema || parentId === void 0) continue;
1187
1385
  const parent = this.getNodeById(parentId, false);
1188
1386
  if (parent?.items) {
1189
- if (typeof index === "number" && index >= 0 && index < parent.items.length) parent.items.splice(index, 0, cloneDeep$1(newSchema));
1190
- else parent.items.push(cloneDeep$1(newSchema));
1387
+ const addedNode = cloneDeep$1(newSchema);
1388
+ const insertIndex = clampIndex(index, parent.items.length);
1389
+ parent.items.splice(insertIndex, 0, addedNode);
1390
+ addedNodes.push(addedNode);
1191
1391
  await stage?.add({
1192
1392
  config: cloneDeep$1(newSchema),
1193
1393
  parent: cloneDeep$1(parent),
1194
1394
  parentId: parent.id,
1195
- root: cloneDeep$1(root)
1395
+ root: cloneDeep$1(root),
1396
+ index: insertIndex
1196
1397
  });
1197
1398
  }
1198
1399
  }
1400
+ this.emitHistoryInsertEvents(addedNodes, step.source);
1199
1401
  }
1200
1402
  break;
1201
1403
  }
@@ -1203,19 +1405,25 @@ var Editor = class extends BaseService {
1203
1405
  const items = step.diff ?? [];
1204
1406
  if (reverse) {
1205
1407
  const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1408
+ const addedNodes = [];
1206
1409
  for (const { oldSchema, parentId, index } of sorted) {
1207
1410
  if (!oldSchema || parentId === void 0) continue;
1208
1411
  const parent = this.getNodeById(parentId, false);
1209
1412
  if (parent?.items) {
1210
- parent.items.splice(index ?? parent.items.length, 0, cloneDeep$1(oldSchema));
1413
+ const addedNode = cloneDeep$1(oldSchema);
1414
+ const insertIndex = clampIndex(index, parent.items.length);
1415
+ parent.items.splice(insertIndex, 0, addedNode);
1416
+ addedNodes.push(addedNode);
1211
1417
  await stage?.add({
1212
1418
  config: cloneDeep$1(oldSchema),
1213
1419
  parent: cloneDeep$1(parent),
1214
1420
  parentId,
1215
- root: cloneDeep$1(root)
1421
+ root: cloneDeep$1(root),
1422
+ index: insertIndex
1216
1423
  });
1217
1424
  }
1218
1425
  }
1426
+ this.emitHistoryInsertEvents(addedNodes, step.source);
1219
1427
  } else for (const { oldSchema } of items) {
1220
1428
  if (!oldSchema) continue;
1221
1429
  const existing = this.getNodeById(oldSchema.id, false);
@@ -1242,11 +1450,17 @@ var Editor = class extends BaseService {
1242
1450
  }
1243
1451
  return cloneDeep$1(reverse ? oldNode : newNode);
1244
1452
  });
1245
- if (configs.length) await this.update(configs, { doNotPushHistory: true });
1453
+ if (configs.length) await this.update(configs, {
1454
+ doNotPushHistory: true,
1455
+ historySource: step.source
1456
+ });
1246
1457
  break;
1247
1458
  }
1248
1459
  }
1249
1460
  this.set("modifiedNodeIds", step.extra?.modifiedNodeIds ?? /* @__PURE__ */ new Map());
1461
+ const invalidToRestore = (reverse ? step.extra?.invalidNodeIdsBefore : step.extra?.invalidNodeIdsAfter) ?? /* @__PURE__ */ new Map();
1462
+ this.set("invalidNodeIds", new Map(invalidToRestore));
1463
+ this.emit("invalid-node-change", this.get("invalidNodeIds"));
1250
1464
  const page = toRaw(this.get("page"));
1251
1465
  if (page) {
1252
1466
  const selectIds = (reverse ? step.extra?.selectedBefore : step.extra?.selectedAfter) ?? [];
@@ -1263,6 +1477,23 @@ var Editor = class extends BaseService {
1263
1477
  selectedConfigExceptionHandler(config) {
1264
1478
  return resolveSelectedNode(config, (id) => this.getNodeInfo(id), this.state.root?.id);
1265
1479
  }
1480
+ /**
1481
+ * 撤销 remove / 重做 add 通过手工 splice 按 step 记录的 parentId / index 精确还原节点(不走 this.add,原因见调用处注释),
1482
+ * 这里补齐与 this.add 等价的 add / change 事件,保证撤销/重做路径的事件行为与正向操作一致。
1483
+ */
1484
+ emitHistoryInsertEvents(nodes, source) {
1485
+ if (!nodes.length) return;
1486
+ this.emit("add", nodes);
1487
+ this.emit("change", {
1488
+ type: "add",
1489
+ data: nodes.map((node) => ({
1490
+ node,
1491
+ page: this.getPageOfNode(node.id)
1492
+ })),
1493
+ historySource: source,
1494
+ doNotPushHistory: true
1495
+ });
1496
+ }
1266
1497
  };
1267
1498
  var editor_default = new Editor();
1268
1499
  //#endregion