@tmagic/editor 1.8.0-beta.11 → 1.8.0-beta.12

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 (111) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +7 -4
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +4 -0
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +2 -1
  4. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +5 -2
  5. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +60 -14
  6. package/dist/es/components/ScrollBar.js +0 -1
  7. package/dist/es/editorProps.js +6 -1
  8. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +15 -3
  9. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +2 -17
  10. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +2 -1
  11. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +7 -0
  12. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +5 -17
  13. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +4 -0
  14. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +29 -3
  15. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +50 -88
  16. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +2 -5
  17. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +48 -23
  18. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +6 -3
  19. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +28 -22
  20. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +28 -22
  21. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +28 -22
  22. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +6 -3
  23. package/dist/es/fields/UISelect.js +0 -1
  24. package/dist/es/index.js +9 -6
  25. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +21 -6
  26. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +9 -6
  27. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +4 -1
  28. package/dist/es/layouts/history-list/composables.js +1 -1
  29. package/dist/es/layouts/history-list/useHistoryRevert.js +7 -3
  30. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +44 -5
  31. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +15 -8
  32. package/dist/es/layouts/props-panel/use-style-panel.js +3 -2
  33. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +15 -31
  34. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
  35. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +18 -3
  36. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +4 -0
  37. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
  38. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +18 -3
  39. package/dist/es/layouts/sidebar/layer/LayerNodeContent.js +5 -0
  40. package/dist/es/layouts/sidebar/layer/LayerNodeContent.vue_vue_type_script_setup_true_lang.js +46 -0
  41. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +2 -1
  42. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +8 -2
  43. package/dist/es/plugin.js +3 -1
  44. package/dist/es/services/editor.js +104 -5
  45. package/dist/es/services/props.js +2 -1
  46. package/dist/es/services/storage.js +1 -1
  47. package/dist/es/services/ui.js +3 -0
  48. package/dist/es/style.css +104 -55
  49. package/dist/es/utils/data-source/index.js +30 -11
  50. package/dist/es/utils/event.js +123 -0
  51. package/dist/es/utils/monaco-editor.js +22 -6
  52. package/dist/es/utils/props.js +94 -8
  53. package/dist/es/utils/type-match-rules.js +360 -0
  54. package/dist/style.css +104 -55
  55. package/dist/tmagic-editor.umd.cjs +16895 -15757
  56. package/package.json +8 -8
  57. package/src/Editor.vue +3 -1
  58. package/src/components/CodeBlockEditor.vue +3 -0
  59. package/src/components/CompareForm.vue +7 -0
  60. package/src/components/FloatingBox.vue +77 -8
  61. package/src/components/ScrollBar.vue +0 -67
  62. package/src/editorProps.ts +13 -0
  63. package/src/fields/CodeSelect.vue +4 -1
  64. package/src/fields/CondOpSelect.vue +2 -24
  65. package/src/fields/DataSourceFields.vue +5 -0
  66. package/src/fields/DataSourceInput.vue +7 -25
  67. package/src/fields/DataSourceMocks.vue +3 -0
  68. package/src/fields/DisplayConds.vue +14 -0
  69. package/src/fields/EventSelect.vue +84 -129
  70. package/src/fields/StyleSetter/Index.vue +2 -8
  71. package/src/fields/StyleSetter/pro/Background.vue +94 -55
  72. package/src/fields/StyleSetter/pro/Border.vue +9 -11
  73. package/src/fields/StyleSetter/pro/Font.vue +66 -64
  74. package/src/fields/StyleSetter/pro/Layout.vue +140 -138
  75. package/src/fields/StyleSetter/pro/Position.vue +69 -67
  76. package/src/fields/StyleSetter/pro/Transform.vue +23 -25
  77. package/src/fields/UISelect.vue +0 -12
  78. package/src/index.ts +1 -0
  79. package/src/layouts/CodeEditor.vue +32 -6
  80. package/src/layouts/Framework.vue +7 -6
  81. package/src/layouts/history-list/HistoryDiffDialog.vue +11 -1
  82. package/src/layouts/history-list/useHistoryRevert.ts +4 -0
  83. package/src/layouts/props-panel/FormPanel.vue +66 -5
  84. package/src/layouts/props-panel/PropsPanel.vue +20 -6
  85. package/src/layouts/props-panel/use-style-panel.ts +4 -3
  86. package/src/layouts/sidebar/Sidebar.vue +2 -0
  87. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +4 -1
  88. package/src/layouts/sidebar/code-block/useContentMenu.ts +13 -1
  89. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +3 -0
  90. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +2 -2
  91. package/src/layouts/sidebar/data-source/useContentMenu.ts +13 -1
  92. package/src/layouts/sidebar/layer/LayerNodeContent.vue +57 -0
  93. package/src/layouts/sidebar/layer/LayerPanel.vue +4 -1
  94. package/src/layouts/workspace/viewer/NodeListMenu.vue +4 -1
  95. package/src/plugin.ts +3 -1
  96. package/src/services/editor.ts +168 -3
  97. package/src/services/ui.ts +6 -0
  98. package/src/theme/event.scss +8 -0
  99. package/src/theme/floating-box.scss +9 -1
  100. package/src/theme/layer-node-content.scss +14 -0
  101. package/src/theme/scroll-bar.scss +64 -0
  102. package/src/theme/theme.scss +3 -0
  103. package/src/theme/ui-select.scss +9 -0
  104. package/src/type.ts +53 -2
  105. package/src/utils/data-source/index.ts +30 -12
  106. package/src/utils/event.ts +224 -0
  107. package/src/utils/index.ts +2 -0
  108. package/src/utils/monaco-editor.ts +27 -0
  109. package/src/utils/props.ts +160 -2
  110. package/src/utils/type-match-rules.ts +678 -0
  111. package/types/index.d.ts +372 -85
@@ -39,6 +39,7 @@ var DataSourceConfigPanel_vue_vue_type_script_setup_true_lang_default = /*@__PUR
39
39
  const dataSourceConfig = ref([]);
40
40
  const { height: editorHeight } = useEditorContentHeight();
41
41
  const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, inject("parentFloating", ref(null)));
42
+ const frameworkWidth = computed(() => uiService.get("frameworkRect")?.width || 0);
42
43
  /** 供「方法定义」tab 内的字段消费,用于打开数据源详情后自动打开指定方法 */
43
44
  provide("editingDataSourceMethodName", computed(() => props.editMethodName));
44
45
  /** 供「数据定义」tab 内的字段消费,用于打开数据源详情后自动打开指定字段 */
@@ -77,6 +78,7 @@ var DataSourceConfigPanel_vue_vue_type_script_setup_true_lang_default = /*@__PUR
77
78
  });
78
79
  return (_ctx, _cache) => {
79
80
  return openBlock(), createBlock(FloatingBox_default, {
81
+ "body-style": { padding: "0 16px" },
80
82
  visible: boxVisible.value,
81
83
  "onUpdate:visible": _cache[0] || (_cache[0] = ($event) => boxVisible.value = $event),
82
84
  width: width.value,
@@ -84,6 +86,7 @@ var DataSourceConfigPanel_vue_vue_type_script_setup_true_lang_default = /*@__PUR
84
86
  height: unref(editorHeight),
85
87
  "onUpdate:height": _cache[2] || (_cache[2] = ($event) => isRef(editorHeight) ? editorHeight.value = $event : null),
86
88
  title: __props.title,
89
+ "framework-width": frameworkWidth.value,
87
90
  position: unref(boxPosition)
88
91
  }, {
89
92
  body: withCtx(() => [createVNode(unref(MFormBox), {
@@ -107,6 +110,7 @@ var DataSourceConfigPanel_vue_vue_type_script_setup_true_lang_default = /*@__PUR
107
110
  "width",
108
111
  "height",
109
112
  "title",
113
+ "framework-width",
110
114
  "position"
111
115
  ]);
112
116
  };
@@ -85,8 +85,8 @@ var DataSourceListPanel_vue_vue_type_script_setup_true_lang_default = /*@__PURE_
85
85
  eventBus?.on("remove-data-source", (id) => {
86
86
  removeHandler(id);
87
87
  });
88
- const { nodeContentMenuHandler, menuData: contentMenuData, contentMenuHideHandler } = useContentMenu();
89
- const menuData = computed(() => props.customContentMenu(contentMenuData, "data-source"));
88
+ const { nodeContentMenuHandler, menuData: contentMenuData, contentMenuHideHandler, getTarget } = useContentMenu();
89
+ const menuData = computed(() => props.customContentMenu(contentMenuData, "data-source", getTarget));
90
90
  return (_ctx, _cache) => {
91
91
  return openBlock(), createElementBlock(Fragment, null, [
92
92
  createVNode(unref(TMagicScrollbar), { class: "data-source-list-panel m-editor-layer-panel" }, {
@@ -6,6 +6,14 @@ var useContentMenu = () => {
6
6
  const eventBus = inject("eventBus");
7
7
  const menuRef = useTemplateRef("menu");
8
8
  let selectId = "";
9
+ let selectData = null;
10
+ const getTarget = () => {
11
+ if (!selectId) return null;
12
+ return {
13
+ id: selectId,
14
+ data: selectData ?? void 0
15
+ };
16
+ };
9
17
  const menuData = [
10
18
  {
11
19
  type: "button",
@@ -42,17 +50,24 @@ var useContentMenu = () => {
42
50
  event.preventDefault();
43
51
  if (data.type === "ds") {
44
52
  menuRef.value?.show(event);
45
- if (data.id) selectId = `${data.id}`;
46
- else selectId = "";
53
+ if (data.id) {
54
+ selectId = `${data.id}`;
55
+ selectData = data;
56
+ } else {
57
+ selectId = "";
58
+ selectData = null;
59
+ }
47
60
  }
48
61
  };
49
62
  const contentMenuHideHandler = () => {
50
63
  selectId = "";
64
+ selectData = null;
51
65
  };
52
66
  return {
53
67
  menuData,
54
68
  nodeContentMenuHandler,
55
- contentMenuHideHandler
69
+ contentMenuHideHandler,
70
+ getTarget
56
71
  };
57
72
  };
58
73
  //#endregion
@@ -0,0 +1,5 @@
1
+ import LayerNodeContent_vue_vue_type_script_setup_true_lang_default from "./LayerNodeContent.vue_vue_type_script_setup_true_lang.js";
2
+ //#region packages/editor/src/layouts/sidebar/layer/LayerNodeContent.vue
3
+ var LayerNodeContent_default = LayerNodeContent_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { LayerNodeContent_default as default };
@@ -0,0 +1,46 @@
1
+ import { useServices } from "../../../hooks/use-services.js";
2
+ import Icon_default from "../../../components/Icon.js";
3
+ import { TMagicTooltip, stripValidateSuggestion } from "@tmagic/design";
4
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, toDisplayString, unref, withCtx } from "vue";
5
+ import { WarningFilled } from "@element-plus/icons-vue";
6
+ //#region packages/editor/src/layouts/sidebar/layer/LayerNodeContent.vue?vue&type=script&setup=true&lang.ts
7
+ var _hoisted_1 = { class: "m-editor-layer-node-label" };
8
+ var _hoisted_2 = ["innerHTML"];
9
+ var LayerNodeContent_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
10
+ name: "MEditorLayerNodeContent",
11
+ __name: "LayerNodeContent",
12
+ props: { data: {} },
13
+ setup(__props) {
14
+ const props = __props;
15
+ const { editorService } = useServices();
16
+ const label = computed(() => `${props.data.name} (${props.data.id})`);
17
+ /** 读取集中存储的校验错误状态,建立响应式依赖 */
18
+ const invalidInfo = computed(() => editorService.get("invalidNodeIds").get(props.data.id));
19
+ const hasError = computed(() => Boolean(invalidInfo.value?.props || invalidInfo.value?.style));
20
+ /**
21
+ * 去掉单条校验文案中的「修改建议」部分。
22
+ *
23
+ * 错误文案可能是多条错误以 `<br>` 拼接的 HTML,每条本身形如 `主文案\n\n建议`。
24
+ * 组件树 tooltip 仅展示错误描述,不展示修改建议(建议仅在错误汇总面板展示),
25
+ * 故先按 `<br>` 拆分,每段用 `stripValidateSuggestion` 截断建议后再拼回。
26
+ */
27
+ const stripSuggestion = (text) => String(text ?? "").split(/<br\s*\/?>/i).map((segment) => stripValidateSuggestion(segment)).join("<br>");
28
+ /** 合并属性表单与样式表单的错误文案(去掉建议,本身可能仍是含 <br> 的 HTML) */
29
+ const errorMessage = computed(() => [stripSuggestion(invalidInfo.value?.props), stripSuggestion(invalidInfo.value?.style)].filter(Boolean).join("<br>"));
30
+ return (_ctx, _cache) => {
31
+ return openBlock(), createElementBlock("span", { class: normalizeClass(["m-editor-layer-node-content", { "is-invalid": hasError.value }]) }, [createElementVNode("span", _hoisted_1, toDisplayString(label.value), 1), hasError.value ? (openBlock(), createBlock(unref(TMagicTooltip), {
32
+ key: 0,
33
+ placement: "top"
34
+ }, {
35
+ content: withCtx(() => [createElementVNode("span", { innerHTML: errorMessage.value }, null, 8, _hoisted_2)]),
36
+ default: withCtx(() => [createVNode(Icon_default, {
37
+ class: "m-editor-layer-node-error-icon",
38
+ icon: unref(WarningFilled)
39
+ }, null, 8, ["icon"])]),
40
+ _: 1
41
+ })) : createCommentVNode("v-if", true)], 2);
42
+ };
43
+ }
44
+ });
45
+ //#endregion
46
+ export { LayerNodeContent_vue_vue_type_script_setup_true_lang_default as default };
@@ -3,6 +3,7 @@ import { useFilter } from "../../../hooks/use-filter.js";
3
3
  import SearchInput_default from "../../../components/SearchInput.js";
4
4
  import Tree_default from "../../../components/Tree.js";
5
5
  import LayerMenu_default from "./LayerMenu.js";
6
+ import LayerNodeContent_default from "./LayerNodeContent.js";
6
7
  import LayerNodeTool_default from "./LayerNodeTool.js";
7
8
  import { useClick } from "./use-click.js";
8
9
  import { useDrag } from "./use-drag.js";
@@ -83,7 +84,7 @@ var LayerPanel_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
83
84
  }, {
84
85
  "tree-node-content": withCtx(({ data: nodeData }) => [renderSlot(_ctx.$slots, "layer-node-content", { data: nodeData })]),
85
86
  "tree-node-tool": withCtx(({ data: nodeData }) => [renderSlot(_ctx.$slots, "layer-node-tool", { data: nodeData }, () => [createVNode(LayerNodeTool_default, { data: nodeData }, null, 8, ["data"])])]),
86
- "tree-node-label": withCtx(({ data: nodeData }) => [renderSlot(_ctx.$slots, "layer-node-label", { data: nodeData })]),
87
+ "tree-node-label": withCtx(({ data: nodeData }) => [renderSlot(_ctx.$slots, "layer-node-label", { data: nodeData }, () => [createVNode(LayerNodeContent_default, { data: nodeData }, null, 8, ["data"])])]),
87
88
  _: 3
88
89
  }, 8, [
89
90
  "data",
@@ -11,7 +11,8 @@ var NodeListMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
11
11
  __name: "NodeListMenu",
12
12
  setup(__props) {
13
13
  const services = useServices();
14
- const { editorService } = services;
14
+ const { editorService, uiService } = services;
15
+ const frameworkWidth = computed(() => uiService.get("frameworkRect")?.width || 0);
15
16
  const visible = ref(false);
16
17
  const buttonVisible = ref(false);
17
18
  const buttonEl = useTemplateRef("button");
@@ -71,6 +72,7 @@ var NodeListMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
71
72
  visible: visible.value,
72
73
  "onUpdate:visible": _cache[1] || (_cache[1] = ($event) => visible.value = $event),
73
74
  title: "当前位置下的组件",
75
+ "framework-width": frameworkWidth.value,
74
76
  position: menuPosition.value
75
77
  }, {
76
78
  body: withCtx(() => [createVNode(Tree_default, {
@@ -80,7 +82,11 @@ var NodeListMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
80
82
  onNodeClick: clickHandler
81
83
  }, null, 8, ["data", "node-status-map"])]),
82
84
  _: 1
83
- }, 8, ["visible", "position"])) : createCommentVNode("v-if", true)], 64);
85
+ }, 8, [
86
+ "visible",
87
+ "framework-width",
88
+ "position"
89
+ ])) : createCommentVNode("v-if", true)], 64);
84
90
  };
85
91
  }
86
92
  });
package/dist/es/plugin.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { setEditorConfig } from "./utils/config.js";
2
+ import { editorTypeMatchRules } from "./utils/type-match-rules.js";
2
3
  import CodeEditor_default from "./layouts/CodeEditor.js";
3
4
  import Editor_default from "./Editor.js";
4
5
  import CodeSelect_default from "./fields/CodeSelect.js";
@@ -20,7 +21,7 @@ import Code_default from "./fields/Code.js";
20
21
  import CodeLink_default from "./fields/CodeLink.js";
21
22
  import UISelect_default from "./fields/UISelect.js";
22
23
  /* empty css */
23
- import formPlugin from "@tmagic/form";
24
+ import formPlugin, { registerTypeMatchRules } from "@tmagic/form";
24
25
  import tablePlugin from "@tmagic/table";
25
26
  import designPlugin from "@tmagic/design";
26
27
  //#region packages/editor/src/plugin.ts
@@ -34,6 +35,7 @@ var plugin_default = { install: (app, opt) => {
34
35
  app.use(designPlugin, opt || {});
35
36
  app.use(formPlugin, opt || {});
36
37
  app.use(tablePlugin);
38
+ registerTypeMatchRules(editorTypeMatchRules);
37
39
  app.config.globalProperties.$TMAGIC_EDITOR = option;
38
40
  setEditorConfig(option);
39
41
  app.component(`${Editor_default.name || "MEditor"}`, Editor_default);
@@ -7,7 +7,7 @@ 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, setValueByKeyPath, traverseNode } from "@tmagic/utils";
11
11
  import { NodeType } from "@tmagic/core";
12
12
  import { reactive, toRaw } from "vue";
13
13
  import { cloneDeep as cloneDeep$1, isEmpty, isEqual, isObject, mergeWith, uniq } from "lodash-es";
@@ -33,6 +33,7 @@ var Editor = class extends BaseService {
33
33
  stageLoading: true,
34
34
  highlightNode: null,
35
35
  modifiedNodeIds: /* @__PURE__ */ new Map(),
36
+ invalidNodeIds: /* @__PURE__ */ new Map(),
36
37
  pageLength: 0,
37
38
  pageFragmentLength: 0,
38
39
  disabledMultiSelect: false,
@@ -40,6 +41,11 @@ var Editor = class extends BaseService {
40
41
  });
41
42
  selectionBeforeOp = null;
42
43
  /**
44
+ * 操作前的节点校验错误快照,与 selectionBeforeOp 同时在 captureSelectionBeforeOp 中捕获,
45
+ * 供 pushOpHistory 写入 step.extra.invalidNodeIdsBefore,用于撤销时还原到「操作前」的错误状态。
46
+ */
47
+ invalidNodeIdsBeforeOp = null;
48
+ /**
43
49
  * 最近一次 pushOpHistory 写入的历史记录 uuid。
44
50
  * 供 *AndGetHistoryId 系列方法在调用普通操作后取回本次产生的历史记录 id;
45
51
  * 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
@@ -57,7 +63,7 @@ var Editor = class extends BaseService {
57
63
  }
58
64
  /**
59
65
  * 设置当前指点节点配置
60
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
66
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'invalidNodeIds' | 'pageLength' | 'pageFragmentLength
61
67
  * @param value MNode
62
68
  * @param options.historySource 设置 root 时,本次变更写入历史记录的「操作来源」(仅 name === 'root' 时生效)
63
69
  */
@@ -85,11 +91,12 @@ var Editor = class extends BaseService {
85
91
  this.state.stageLoading = false;
86
92
  }
87
93
  this.emit("root-change", value, preValue, options);
94
+ this.pruneInvalidNodeIds();
88
95
  }
89
96
  }
90
97
  /**
91
98
  * 获取当前指点节点配置
92
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
99
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'invalidNodeIds' | 'pageLength' | 'pageFragmentLength'
93
100
  * @returns MNode
94
101
  */
95
102
  get(name) {
@@ -430,6 +437,7 @@ var Editor = class extends BaseService {
430
437
  doNotSelect,
431
438
  doNotSwitchPage
432
439
  })));
440
+ this.removeInvalidNodesBySubtree(nodes);
433
441
  if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
434
442
  diff: removedItems,
435
443
  pageData: pageForOp,
@@ -499,7 +507,7 @@ var Editor = class extends BaseService {
499
507
  */
500
508
  async update(config, data = {}) {
501
509
  this.captureSelectionBeforeOp();
502
- const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource } = data;
510
+ const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource, invalidInfo } = data;
503
511
  const nodes = Array.isArray(config) ? config : [config];
504
512
  const updateData = await Promise.all(nodes.map((node, index) => {
505
513
  const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
@@ -508,6 +516,7 @@ var Editor = class extends BaseService {
508
516
  historySource
509
517
  });
510
518
  }));
519
+ this.applyInvalidInfo(config, invalidInfo);
511
520
  if (updateData[0].oldNode?.type !== NodeType.ROOT) {
512
521
  if (this.get("nodes").length) if (!doNotPushHistory) {
513
522
  const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
@@ -1078,6 +1087,7 @@ var Editor = class extends BaseService {
1078
1087
  this.set("stage", null);
1079
1088
  this.set("highlightNode", null);
1080
1089
  this.set("modifiedNodeIds", /* @__PURE__ */ new Map());
1090
+ this.set("invalidNodeIds", /* @__PURE__ */ new Map());
1081
1091
  this.set("pageLength", 0);
1082
1092
  }
1083
1093
  destroy() {
@@ -1088,6 +1098,51 @@ var Editor = class extends BaseService {
1088
1098
  resetModifiedNodeId() {
1089
1099
  this.get("modifiedNodeIds").clear();
1090
1100
  }
1101
+ /**
1102
+ * 记录(或覆盖)某个节点在指定来源(属性表单 / 样式表单)上的校验错误信息。
1103
+ * @param id 节点 id
1104
+ * @param source 错误来源:'props'(属性表单)| 'style'(样式表单)
1105
+ * @param message 错误文案(可能是包含 <br> 的 HTML)
1106
+ */
1107
+ setInvalidNode(id, source, message) {
1108
+ const map = this.get("invalidNodeIds");
1109
+ const info = { ...map.get(id) || {} };
1110
+ info[source] = message;
1111
+ map.set(id, info);
1112
+ this.emit("invalid-node-change", map);
1113
+ }
1114
+ /**
1115
+ * 删除节点的校验错误记录。
1116
+ * @param id 节点 id
1117
+ * @param source 指定来源则仅删除该来源;不传则删除该节点全部来源的错误
1118
+ */
1119
+ deleteInvalidNode(id, source) {
1120
+ const map = this.get("invalidNodeIds");
1121
+ if (!map.has(id)) return;
1122
+ if (!source) map.delete(id);
1123
+ else {
1124
+ const info = { ...map.get(id) || {} };
1125
+ delete info[source];
1126
+ if (info.props || info.style) map.set(id, info);
1127
+ else map.delete(id);
1128
+ }
1129
+ this.emit("invalid-node-change", map);
1130
+ }
1131
+ /** 获取当前存在校验错误的节点错误 Map(key 为节点 id) */
1132
+ getInvalidNodeIds() {
1133
+ return this.get("invalidNodeIds");
1134
+ }
1135
+ /** 获取指定节点的校验错误信息 */
1136
+ getInvalidNodeInfo(id) {
1137
+ return this.get("invalidNodeIds").get(id);
1138
+ }
1139
+ /** 清空全部校验错误记录 */
1140
+ resetInvalidNodeId() {
1141
+ const map = this.get("invalidNodeIds");
1142
+ if (map.size === 0) return;
1143
+ map.clear();
1144
+ this.emit("invalid-node-change", map);
1145
+ }
1091
1146
  usePlugin(options) {
1092
1147
  super.usePlugin(options);
1093
1148
  }
@@ -1100,6 +1155,43 @@ var Editor = class extends BaseService {
1100
1155
  emit(eventName, ...args) {
1101
1156
  return super.emit(eventName, ...args);
1102
1157
  }
1158
+ /**
1159
+ * 应用一次属性面板提交携带的校验错误信息(在写入历史记录之前调用,使历史快照与本次变更对齐)。
1160
+ * - invalidInfo 为空(调用方未携带):认为本次更新对应的节点已无校验错误,清除其全部来源的错误;
1161
+ * - error 非空则记录错误,为空则清除对应来源的错误。
1162
+ */
1163
+ applyInvalidInfo(config, invalidInfo) {
1164
+ if (!invalidInfo) {
1165
+ (Array.isArray(config) ? config : [config]).map((node) => node.id).filter((id) => id !== void 0).forEach((id) => this.deleteInvalidNode(id));
1166
+ return;
1167
+ }
1168
+ const { id, source, error } = invalidInfo;
1169
+ if (error) this.setInvalidNode(id, source, error);
1170
+ else this.deleteInvalidNode(id, source);
1171
+ }
1172
+ /** 删除被移除节点(含其子树)的校验错误记录,避免残留误报 */
1173
+ removeInvalidNodesBySubtree(nodes) {
1174
+ const map = this.get("invalidNodeIds");
1175
+ if (map.size === 0) return;
1176
+ let changed = false;
1177
+ nodes.forEach((node) => {
1178
+ traverseNode(node, (n) => {
1179
+ if (n.id !== void 0 && map.delete(n.id)) changed = true;
1180
+ });
1181
+ });
1182
+ if (changed) this.emit("invalid-node-change", map);
1183
+ }
1184
+ /** 清理不在当前 DSL 中的失效节点错误记录(用于整体替换 root 后) */
1185
+ pruneInvalidNodeIds() {
1186
+ const map = this.get("invalidNodeIds");
1187
+ if (map.size === 0) return;
1188
+ let changed = false;
1189
+ for (const id of [...map.keys()]) if (!this.getNodeById(id, false)) {
1190
+ map.delete(id);
1191
+ changed = true;
1192
+ }
1193
+ if (changed) this.emit("invalid-node-change", map);
1194
+ }
1103
1195
  addModifiedNodeId(id) {
1104
1196
  this.get("modifiedNodeIds").set(id, id);
1105
1197
  }
@@ -1119,6 +1211,7 @@ var Editor = class extends BaseService {
1119
1211
  captureSelectionBeforeOp() {
1120
1212
  if (this.selectionBeforeOp) return;
1121
1213
  this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
1214
+ this.invalidNodeIdsBeforeOp = new Map(this.get("invalidNodeIds"));
1122
1215
  }
1123
1216
  /**
1124
1217
  * 比较「上一次 root」与「新 root」的页面 / 页面片,按页面粒度把整体替换拆成历史记录:
@@ -1191,7 +1284,9 @@ var Editor = class extends BaseService {
1191
1284
  extra: {
1192
1285
  selectedBefore: this.selectionBeforeOp ?? [],
1193
1286
  selectedAfter: this.get("nodes").map((n) => n.id),
1194
- modifiedNodeIds: new Map(this.get("modifiedNodeIds"))
1287
+ modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
1288
+ invalidNodeIdsBefore: new Map(this.invalidNodeIdsBeforeOp ?? this.get("invalidNodeIds")),
1289
+ invalidNodeIdsAfter: new Map(this.get("invalidNodeIds"))
1195
1290
  },
1196
1291
  diff
1197
1292
  };
@@ -1200,6 +1295,7 @@ var Editor = class extends BaseService {
1200
1295
  const historyId = history_default.push("page", step, pageData.id) ? step.uuid : null;
1201
1296
  this.lastPushedHistoryId = historyId;
1202
1297
  this.selectionBeforeOp = null;
1298
+ this.invalidNodeIdsBeforeOp = null;
1203
1299
  return historyId;
1204
1300
  }
1205
1301
  /**
@@ -1299,6 +1395,9 @@ var Editor = class extends BaseService {
1299
1395
  }
1300
1396
  }
1301
1397
  this.set("modifiedNodeIds", step.extra?.modifiedNodeIds ?? /* @__PURE__ */ new Map());
1398
+ const invalidToRestore = (reverse ? step.extra?.invalidNodeIdsBefore : step.extra?.invalidNodeIdsAfter) ?? /* @__PURE__ */ new Map();
1399
+ this.set("invalidNodeIds", new Map(invalidToRestore));
1400
+ this.emit("invalid-node-change", this.get("invalidNodeIds"));
1302
1401
  const page = toRaw(this.get("page"));
1303
1402
  if (page) {
1304
1403
  const selectIds = (reverse ? step.extra?.selectedBefore : step.extra?.selectedAfter) ?? [];
@@ -187,7 +187,8 @@ var Props = class extends BaseService {
187
187
  const targetConfig = path[path.length - 1];
188
188
  if (!targetConfig) return;
189
189
  target.deps[config.id]?.keys?.forEach((fullKey) => {
190
- const relateTargetId = relateIdMap[getValueByKeyPath(fullKey, config)];
190
+ const relateOriginId = getValueByKeyPath(fullKey, config);
191
+ const relateTargetId = relateIdMap[relateOriginId];
191
192
  if (!relateTargetId) return;
192
193
  setValueByKeyPath(fullKey, relateTargetId, targetConfig);
193
194
  });
@@ -124,4 +124,4 @@ var WebStorage = class extends BaseService {
124
124
  };
125
125
  var storage_default = new WebStorage();
126
126
  //#endregion
127
- export { Protocol, storage_default as default };
127
+ export { Protocol, WebStorage, storage_default as default };
@@ -23,6 +23,9 @@ var state = shallowReactive({
23
23
  center: 0,
24
24
  right: storage_default.getItem("$MagicEditorRightColumnWidthData", { protocol: Protocol.NUMBER }) || 480
25
25
  },
26
+ minLeftColumnWidth: 200,
27
+ minCenterColumnWidth: 400,
28
+ minRightColumnWidth: 300,
26
29
  showGuides: true,
27
30
  hasGuides: false,
28
31
  showRule: true,
package/dist/es/style.css CHANGED
@@ -1,55 +1,4 @@
1
- @charset "UTF-8";.m-editor-scroll-bar {
2
- position: absolute;
3
- background-color: transparent;
4
- opacity: 0.3;
5
- transition: background-color 0.2s linear, opacity 0.2s linear;
6
- }
7
- .m-editor-scroll-bar .m-editor-scroll-bar-thumb {
8
- background-color: #aaa;
9
- border-radius: 6px;
10
- position: absolute;
11
- }
12
- .m-editor-scroll-bar.horizontal {
13
- width: 100%;
14
- height: 15px;
15
- bottom: 0;
16
- }
17
- .m-editor-scroll-bar.horizontal .m-editor-scroll-bar-thumb {
18
- height: 6px;
19
- transition: background-color 0.2s linear, height 0.2s ease-in-out;
20
- bottom: 2px;
21
- }
22
- .m-editor-scroll-bar.vertical {
23
- height: 100%;
24
- width: 15px;
25
- right: 5px;
26
- }
27
- .m-editor-scroll-bar.vertical .m-editor-scroll-bar-thumb {
28
- width: 6px;
29
- transition: background-color 0.2s linear, width 0.2s ease-in-out;
30
- right: 2px;
31
- }
32
- .m-editor-scroll-bar:hover, .m-editor-scroll-bar:focus {
33
- background-color: #eee;
34
- opacity: 0.9;
35
- }
36
- .m-editor-scroll-bar:hover .m-editor-scroll-bar-thumb, .m-editor-scroll-bar:focus .m-editor-scroll-bar-thumb {
37
- background-color: #999;
38
- }
39
- .m-editor-scroll-bar:hover.horizontal .m-editor-scroll-bar-thumb, .m-editor-scroll-bar:focus.horizontal .m-editor-scroll-bar-thumb {
40
- height: 11px;
41
- }
42
- .m-editor-scroll-bar:hover.vertical .m-editor-scroll-bar-thumb, .m-editor-scroll-bar:focus.vertical .m-editor-scroll-bar-thumb {
43
- width: 11px;
44
- }.m-fields-ui-select {
45
- cursor: pointer;
46
- }
47
- .m-fields-ui-select i {
48
- margin-right: 3px;
49
- }
50
- .m-fields-ui-select span {
51
- color: #2882e0;
52
- }
1
+ @charset "UTF-8";
53
2
  .tmagic-design-popper {
54
3
  min-width: 150px;
55
4
  line-height: 1.4;
@@ -115,6 +64,22 @@
115
64
  color: #999;
116
65
  }
117
66
 
67
+ .tmagic-table-sub-actions {
68
+ display: flex;
69
+ flex-direction: column;
70
+ gap: 4px;
71
+ align-items: flex-start;
72
+ }
73
+ .tmagic-table-sub-actions .sub-action-btn {
74
+ width: 100%;
75
+ }
76
+ .tmagic-table-sub-actions .tmagic-design-button + .tmagic-design-button {
77
+ margin-left: 0;
78
+ }
79
+ .tmagic-table-sub-actions .tmagic-design-button {
80
+ justify-content: flex-start;
81
+ }
82
+
118
83
  .tmagic-design-form-inline .m-form-container {
119
84
  display: inline-flex;
120
85
  }
@@ -139,7 +104,7 @@
139
104
  .m-form-dialog .m-dialog-body {
140
105
  padding: 0 20px;
141
106
  }
142
- .m-form-dialog .el-table .m-form-item .el-form-item {
107
+ .m-form-dialog .el-table .m-form-item .el-form-item:not(.is-error) {
143
108
  margin-bottom: 0;
144
109
  }
145
110
 
@@ -363,6 +328,10 @@ fieldset.m-fieldset .m-form-tip {
363
328
  width: 95vw !important;
364
329
  }
365
330
 
331
+ .el-form-item .m-fields-table .el-form-item.is-error {
332
+ margin-bottom: 18px;
333
+ }
334
+
366
335
  .m-fields-table {
367
336
  width: 100%;
368
337
  }
@@ -385,7 +354,7 @@ fieldset.m-fieldset .m-form-tip {
385
354
  .m-fields-table .el-table__expanded-cell .m-form-tip {
386
355
  margin-left: 80px;
387
356
  }
388
- .m-fields-table .el-form-item {
357
+ .m-fields-table .el-form-item:not(.is-error) {
389
358
  margin-bottom: 0;
390
359
  }
391
360
  .m-fields-table .tmagic-form-table-drag-target {
@@ -1808,6 +1777,10 @@ fieldset.m-fieldset .m-form-tip {
1808
1777
  margin-right: 5px;
1809
1778
  }
1810
1779
 
1780
+ .el-form-item .el-form-item.is-error {
1781
+ margin-bottom: 18px;
1782
+ }
1783
+
1811
1784
  .m-editor-layout {
1812
1785
  width: 100%;
1813
1786
  display: flex;
@@ -2020,13 +1993,21 @@ fieldset.m-fieldset .m-form-tip {
2020
1993
  .m-editor-float-box .m-editor-float-box-body {
2021
1994
  overflow: auto;
2022
1995
  flex: 1;
2023
- padding: 0 16px;
2024
1996
  }
2025
1997
 
2026
1998
  .m-editor-floating-box-moveable {
2027
1999
  opacity: 0;
2028
2000
  }
2029
2001
 
2002
+ .m-editor-float-box-drag-mask {
2003
+ position: fixed;
2004
+ top: 0;
2005
+ left: 0;
2006
+ width: 100%;
2007
+ height: 100%;
2008
+ background-color: transparent;
2009
+ }
2010
+
2030
2011
  .m-fields-page-fragment-select {
2031
2012
  width: 100%;
2032
2013
  }
@@ -2076,6 +2057,74 @@ fieldset.m-fieldset .m-form-tip {
2076
2057
  padding: 5px 8px;
2077
2058
  }
2078
2059
 
2060
+ .m-editor-scroll-bar {
2061
+ position: absolute;
2062
+ background-color: transparent;
2063
+ opacity: 0.3;
2064
+ transition: background-color 0.2s linear, opacity 0.2s linear;
2065
+ }
2066
+ .m-editor-scroll-bar .m-editor-scroll-bar-thumb {
2067
+ background-color: #aaa;
2068
+ border-radius: 6px;
2069
+ position: absolute;
2070
+ }
2071
+ .m-editor-scroll-bar.horizontal {
2072
+ width: 100%;
2073
+ height: 15px;
2074
+ bottom: 0;
2075
+ }
2076
+ .m-editor-scroll-bar.horizontal .m-editor-scroll-bar-thumb {
2077
+ height: 6px;
2078
+ transition: background-color 0.2s linear, height 0.2s ease-in-out;
2079
+ bottom: 2px;
2080
+ }
2081
+ .m-editor-scroll-bar.vertical {
2082
+ height: 100%;
2083
+ width: 15px;
2084
+ right: 5px;
2085
+ }
2086
+ .m-editor-scroll-bar.vertical .m-editor-scroll-bar-thumb {
2087
+ width: 6px;
2088
+ transition: background-color 0.2s linear, width 0.2s ease-in-out;
2089
+ right: 2px;
2090
+ }
2091
+ .m-editor-scroll-bar:hover, .m-editor-scroll-bar:focus {
2092
+ background-color: #eee;
2093
+ opacity: 0.9;
2094
+ }
2095
+ .m-editor-scroll-bar:hover .m-editor-scroll-bar-thumb, .m-editor-scroll-bar:focus .m-editor-scroll-bar-thumb {
2096
+ background-color: #999;
2097
+ }
2098
+ .m-editor-scroll-bar:hover.horizontal .m-editor-scroll-bar-thumb, .m-editor-scroll-bar:focus.horizontal .m-editor-scroll-bar-thumb {
2099
+ height: 11px;
2100
+ }
2101
+ .m-editor-scroll-bar:hover.vertical .m-editor-scroll-bar-thumb, .m-editor-scroll-bar:focus.vertical .m-editor-scroll-bar-thumb {
2102
+ width: 11px;
2103
+ }
2104
+
2105
+ .m-fields-ui-select {
2106
+ cursor: pointer;
2107
+ }
2108
+ .m-fields-ui-select i {
2109
+ margin-right: 3px;
2110
+ }
2111
+ .m-fields-ui-select span {
2112
+ color: #2882e0;
2113
+ }
2114
+
2115
+ .m-editor-layer-node-content {
2116
+ display: inline-flex;
2117
+ align-items: center;
2118
+ }
2119
+ .m-editor-layer-node-content.is-invalid .m-editor-layer-node-label {
2120
+ color: var(--el-color-danger, #f56c6c);
2121
+ }
2122
+ .m-editor-layer-node-content .m-editor-layer-node-error-icon {
2123
+ margin-left: 4px;
2124
+ color: var(--el-color-danger, #f56c6c);
2125
+ cursor: help;
2126
+ }
2127
+
2079
2128
  .border-box-container {
2080
2129
  display: flex;
2081
2130
  }