@tmagic/editor 1.7.13-beta.0 → 1.7.14-beta.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 (31) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +17 -3
  2. package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +2 -1
  3. package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +8 -3
  4. package/dist/es/editorProps.js +1 -0
  5. package/dist/es/hooks/use-stage.js +5 -0
  6. package/dist/es/initService.js +3 -0
  7. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +10 -3
  8. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +18 -5
  9. package/dist/es/layouts/sidebar/layer/use-click.js +22 -3
  10. package/dist/es/layouts/sidebar/layer/use-drag.js +28 -6
  11. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +13 -2
  12. package/dist/es/services/editor.js +2 -1
  13. package/dist/es/services/props.js +12 -0
  14. package/dist/es/utils/dep/worker.js +1 -1
  15. package/dist/tmagic-editor.umd.cjs +141 -27
  16. package/package.json +7 -7
  17. package/src/Editor.vue +15 -0
  18. package/src/components/Tree.vue +1 -0
  19. package/src/components/TreeNode.vue +6 -1
  20. package/src/editorProps.ts +20 -0
  21. package/src/hooks/use-stage.ts +9 -0
  22. package/src/initService.ts +10 -0
  23. package/src/layouts/sidebar/Sidebar.vue +15 -0
  24. package/src/layouts/sidebar/layer/LayerPanel.vue +26 -2
  25. package/src/layouts/sidebar/layer/use-click.ts +36 -3
  26. package/src/layouts/sidebar/layer/use-drag.ts +62 -6
  27. package/src/layouts/workspace/viewer/Stage.vue +24 -2
  28. package/src/services/editor.ts +1 -0
  29. package/src/services/props.ts +16 -0
  30. package/src/type.ts +44 -0
  31. package/types/index.d.ts +73 -4
@@ -4787,6 +4787,9 @@
4787
4787
  });
4788
4788
  this.emit("props-configs-change");
4789
4789
  }
4790
+ getPropsConfigs() {
4791
+ return this.state.propsConfigMap;
4792
+ }
4790
4793
  async fillConfig(config, labelWidth) {
4791
4794
  return fillConfig(config, {
4792
4795
  labelWidth: typeof labelWidth !== "function" ? labelWidth : "80px",
@@ -4809,11 +4812,17 @@
4809
4812
  if (type === "area") return await this.getPropsConfig("button", data);
4810
4813
  return cloneDeep(this.state.propsConfigMap[(0, _tmagic_utils.toLine)(type)] || await this.fillConfig([]));
4811
4814
  }
4815
+ hasPropsConfig(type) {
4816
+ return !!this.state.propsConfigMap[(0, _tmagic_utils.toLine)(type)];
4817
+ }
4812
4818
  setPropsValues(values) {
4813
4819
  Object.keys(values).forEach((type) => {
4814
4820
  this.setPropsValue((0, _tmagic_utils.toLine)(type), values[type]);
4815
4821
  });
4816
4822
  }
4823
+ getPropsValues() {
4824
+ return this.state.propsValueMap;
4825
+ }
4817
4826
  /**
4818
4827
  * 为指点类型组件设置组件初始值
4819
4828
  * @param type 组件类型
@@ -4850,6 +4859,9 @@
4850
4859
  ...mergeWith({}, cloneDeep(this.state.propsValueMap[type] || {}), data)
4851
4860
  };
4852
4861
  }
4862
+ hasPropsValue(type) {
4863
+ return !!this.state.propsValueMap[(0, _tmagic_utils.toLine)(type)];
4864
+ }
4853
4865
  createId(type) {
4854
4866
  return `${type}_${(0, _tmagic_utils.guid)()}`;
4855
4867
  }
@@ -5794,7 +5806,8 @@
5794
5806
  modifiedNodeIds: /* @__PURE__ */ new Map(),
5795
5807
  pageLength: 0,
5796
5808
  pageFragmentLength: 0,
5797
- disabledMultiSelect: false
5809
+ disabledMultiSelect: false,
5810
+ alwaysMultiSelect: false
5798
5811
  });
5799
5812
  isHistoryStateChange = false;
5800
5813
  selectionBeforeOp = null;
@@ -6670,6 +6683,7 @@
6670
6683
  zoom: stageOptions.zoom ?? zoom.value,
6671
6684
  autoScrollIntoView: stageOptions.autoScrollIntoView,
6672
6685
  isContainer: stageOptions.isContainer,
6686
+ canDropIn: stageOptions.canDropIn,
6673
6687
  containerHighlightClassName: stageOptions.containerHighlightClassName,
6674
6688
  containerHighlightDuration: stageOptions.containerHighlightDuration,
6675
6689
  containerHighlightType: stageOptions.containerHighlightType,
@@ -6688,12 +6702,16 @@
6688
6702
  updateDragEl: stageOptions.updateDragEl,
6689
6703
  guidesOptions: stageOptions.guidesOptions,
6690
6704
  disabledMultiSelect: stageOptions.disabledMultiSelect,
6705
+ alwaysMultiSelect: stageOptions.alwaysMultiSelect,
6691
6706
  disabledRule: stageOptions.disabledRule
6692
6707
  });
6693
6708
  (0, vue.watch)(() => editor_default.get("disabledMultiSelect"), (disabledMultiSelect) => {
6694
6709
  if (disabledMultiSelect) stage.disableMultiSelect();
6695
6710
  else stage.enableMultiSelect();
6696
6711
  });
6712
+ (0, vue.watch)(() => editor_default.get("alwaysMultiSelect"), (alwaysMultiSelect) => {
6713
+ stage.setAlwaysMultiSelect(Boolean(alwaysMultiSelect));
6714
+ });
6697
6715
  const hGuidesCache = getGuideLineFromCache(getGuideLineKey(H_GUIDE_LINE_STORAGE_KEY));
6698
6716
  const vGuidesCache = getGuideLineFromCache(getGuideLineKey(V_GUIDE_LINE_STORAGE_KEY));
6699
6717
  stage.mask?.setGuides([hGuidesCache, vGuidesCache]);
@@ -9874,7 +9892,8 @@
9874
9892
  "node-dragend",
9875
9893
  "node-contextmenu",
9876
9894
  "node-mouseenter",
9877
- "node-click"
9895
+ "node-click",
9896
+ "node-dblclick"
9878
9897
  ],
9879
9898
  setup(__props, { emit: __emit }) {
9880
9899
  const treeEmit = (0, vue.inject)("treeEmit");
@@ -9910,6 +9929,9 @@
9910
9929
  const nodeClickHandler = (event) => {
9911
9930
  treeEmit?.("node-click", event, props.data);
9912
9931
  };
9932
+ const nodeDblclickHandler = (event) => {
9933
+ treeEmit?.("node-dblclick", event, props.data);
9934
+ };
9913
9935
  return (_ctx, _cache) => {
9914
9936
  const _component_TreeNode = (0, vue.resolveComponent)("TreeNode", true);
9915
9937
  return (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
@@ -9937,8 +9959,9 @@
9937
9959
  onClick: expandHandler
9938
9960
  }, null, 8, ["style", "icon"]), (0, vue.createElementVNode)("div", {
9939
9961
  class: "tree-node-content",
9940
- onClick: nodeClickHandler
9941
- }, [(0, vue.renderSlot)(_ctx.$slots, "tree-node-content", { data: __props.data }, () => [(0, vue.createElementVNode)("div", _hoisted_2$15, [(0, vue.renderSlot)(_ctx.$slots, "tree-node-label", { data: __props.data }, () => [(0, vue.createTextVNode)((0, vue.toDisplayString)(`${__props.data.name} (${__props.data.id})`), 1)])]), (0, vue.createElementVNode)("div", _hoisted_3$4, [(0, vue.renderSlot)(_ctx.$slots, "tree-node-tool", { data: __props.data })])])])], 38), __props.isExpandable(__props.data, __props.nodeStatusMap) && expanded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4$3, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.data.items, (item) => {
9962
+ onClick: nodeClickHandler,
9963
+ onDblclick: nodeDblclickHandler
9964
+ }, [(0, vue.renderSlot)(_ctx.$slots, "tree-node-content", { data: __props.data }, () => [(0, vue.createElementVNode)("div", _hoisted_2$15, [(0, vue.renderSlot)(_ctx.$slots, "tree-node-label", { data: __props.data }, () => [(0, vue.createTextVNode)((0, vue.toDisplayString)(`${__props.data.name} (${__props.data.id})`), 1)])]), (0, vue.createElementVNode)("div", _hoisted_3$4, [(0, vue.renderSlot)(_ctx.$slots, "tree-node-tool", { data: __props.data })])])], 32)], 38), __props.isExpandable(__props.data, __props.nodeStatusMap) && expanded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4$3, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.data.items, (item) => {
9942
9965
  return (0, vue.openBlock)(), (0, vue.createBlock)(_component_TreeNode, {
9943
9966
  key: item.id,
9944
9967
  data: item,
@@ -9991,7 +10014,8 @@
9991
10014
  "node-dragend",
9992
10015
  "node-contextmenu",
9993
10016
  "node-mouseenter",
9994
- "node-click"
10017
+ "node-click",
10018
+ "node-dblclick"
9995
10019
  ],
9996
10020
  setup(__props, { emit: __emit }) {
9997
10021
  const emit = __emit;
@@ -11066,7 +11090,7 @@
11066
11090
  //#endregion
11067
11091
  //#region packages/editor/src/layouts/sidebar/layer/use-click.ts
11068
11092
  var useClick = ({ editorService, stageOverlayService, uiService }, isCtrlKeyDown, nodeStatusMap, menuRef) => {
11069
- const isMultiSelect = (0, vue.computed)(() => isCtrlKeyDown.value && !editorService.get("disabledMultiSelect"));
11093
+ const isMultiSelect = (0, vue.computed)(() => !editorService.get("disabledMultiSelect") && (isCtrlKeyDown.value || editorService.get("alwaysMultiSelect")));
11070
11094
  const select = async (data) => {
11071
11095
  if (!data.id) throw new Error("没有id");
11072
11096
  if (isMultiSelect.value) multiSelect(data);
@@ -11102,6 +11126,15 @@
11102
11126
  editorService.get("stage")?.highlight(data.id);
11103
11127
  stageOverlayService.get("stage")?.highlight(data.id);
11104
11128
  };
11129
+ const isNodeCanSelect = async (data) => {
11130
+ const canSelect = stageOverlayService.get("stageOptions")?.canSelect;
11131
+ if (!canSelect) return true;
11132
+ const doc = editorService.get("stage")?.renderer?.contentWindow?.document;
11133
+ if (!doc) return true;
11134
+ const el = (0, _tmagic_utils.getElById)()(doc, data.id);
11135
+ if (!el) return true;
11136
+ return Boolean(await canSelect(el));
11137
+ };
11105
11138
  const nodeClickHandler = (event, data) => {
11106
11139
  if (!nodeStatusMap?.value) return;
11107
11140
  if (uiService.get("uiSelectMode")) {
@@ -11109,13 +11142,23 @@
11109
11142
  return;
11110
11143
  }
11111
11144
  if (data.items && data.items.length > 0 && !isMultiSelect.value) updateStatus(nodeStatusMap.value, data.id, { expand: true });
11112
- (0, vue.nextTick)(() => {
11145
+ (0, vue.nextTick)(async () => {
11146
+ if (!await isNodeCanSelect(data)) return;
11113
11147
  select(data);
11114
11148
  });
11115
11149
  };
11150
+ const nodeDblclickHandler = (event, data) => {
11151
+ if (!nodeStatusMap?.value) return;
11152
+ if (uiService.get("uiSelectMode")) return;
11153
+ if (data.items && data.items.length > 0) {
11154
+ const status = nodeStatusMap.value.get(data.id);
11155
+ updateStatus(nodeStatusMap.value, data.id, { expand: !status?.expand });
11156
+ }
11157
+ };
11116
11158
  return {
11117
11159
  menuRef,
11118
11160
  nodeClickHandler,
11161
+ nodeDblclickHandler,
11119
11162
  nodeContentMenuHandler(event, data) {
11120
11163
  event.preventDefault();
11121
11164
  const nodes = editorService.get("nodes") || [];
@@ -11153,7 +11196,7 @@
11153
11196
  * dragover 属于目标节点
11154
11197
  * 这些方法并不是同一个dom事件触发的
11155
11198
  */
11156
- var useDrag = ({ editorService }) => {
11199
+ var useDrag = ({ editorService }, options = {}) => {
11157
11200
  const handleDragStart = (event) => {
11158
11201
  if (!event.dataTransfer || !event.target || !event.currentTarget) return;
11159
11202
  const targetEl = getNodeEl(event.target);
@@ -11185,19 +11228,34 @@
11185
11228
  if (parentsId.includes(`${nodeId}`) && i >= targetIdIndex) return;
11186
11229
  }
11187
11230
  }
11188
- if (distance < targetHeight / 3) {
11231
+ let canDropInTarget = isContainer;
11232
+ let redirectedTargetId;
11233
+ if (canDropInTarget && options.canDropIn && nodeId && targetNodeId !== nodeId) {
11234
+ const result = options.canDropIn([nodeId], targetNodeId);
11235
+ if (result === false) canDropInTarget = false;
11236
+ else if (typeof result === "string" || typeof result === "number") redirectedTargetId = result;
11237
+ }
11238
+ let canDropAsSibling = true;
11239
+ const directParentId = parentsId?.[parentsId.length - 1];
11240
+ if (options.canDropIn && nodeId && directParentId && directParentId !== nodeId) {
11241
+ const siblingResult = options.canDropIn([nodeId], directParentId);
11242
+ if (siblingResult === false || typeof siblingResult === "string" || typeof siblingResult === "number") canDropAsSibling = false;
11243
+ }
11244
+ dragState.dropType = "";
11245
+ if (distance < targetHeight / 3 && canDropAsSibling) {
11189
11246
  dragState.dropType = "before";
11190
11247
  (0, _tmagic_utils.addClassName)(labelEl, globalThis.document, "drag-before");
11191
- } else if (distance > targetHeight * 2 / 3) {
11248
+ } else if (distance > targetHeight * 2 / 3 && canDropAsSibling) {
11192
11249
  dragState.dropType = "after";
11193
11250
  (0, _tmagic_utils.addClassName)(labelEl, globalThis.document, "drag-after");
11194
- } else if (isContainer) {
11251
+ } else if (canDropInTarget) {
11195
11252
  dragState.dropType = "inner";
11196
11253
  (0, _tmagic_utils.addClassName)(labelEl, globalThis.document, "drag-inner");
11197
11254
  }
11198
11255
  if (!dragState.dropType) return;
11199
11256
  dragState.dragOverNodeId = targetNodeId;
11200
11257
  dragState.container = event.currentTarget;
11258
+ dragState.redirectedTargetId = dragState.dropType === "inner" ? redirectedTargetId : void 0;
11201
11259
  event.preventDefault();
11202
11260
  };
11203
11261
  const handleDragLeave = (event) => {
@@ -11219,10 +11277,16 @@
11219
11277
  let targetParent = targetInfo.parent;
11220
11278
  if (!targetParent || !targetNode) return;
11221
11279
  let targetIndex = -1;
11222
- if (Array.isArray(targetNode.items) && dragState.dropType === "inner") {
11280
+ if (Array.isArray(targetNode.items) && dragState.dropType === "inner") if (dragState.redirectedTargetId !== void 0) {
11281
+ const redirectedNode = editorService.getNodeInfo(dragState.redirectedTargetId, false).node;
11282
+ if (!redirectedNode || !Array.isArray(redirectedNode.items)) return;
11283
+ targetParent = redirectedNode;
11284
+ targetIndex = redirectedNode.items.length;
11285
+ } else {
11223
11286
  targetIndex = targetNode.items.length;
11224
11287
  targetParent = targetNode;
11225
- } else targetIndex = getNodeIndex(dragState.dragOverNodeId, targetParent);
11288
+ }
11289
+ else targetIndex = getNodeIndex(dragState.dragOverNodeId, targetParent);
11226
11290
  if (dragState.dropType === "after") targetIndex += 1;
11227
11291
  const selectedNodes = editorService.get("nodes");
11228
11292
  if (selectedNodes.find((n) => `${n.id}` === `${node.id}`)) editorService.dragTo(selectedNodes, targetParent, targetIndex);
@@ -11231,6 +11295,7 @@
11231
11295
  dragState.dragOverNodeId = "";
11232
11296
  dragState.dropType = "";
11233
11297
  dragState.container = null;
11298
+ dragState.redirectedTargetId = void 0;
11234
11299
  };
11235
11300
  return {
11236
11301
  handleDragStart,
@@ -11457,9 +11522,14 @@
11457
11522
  indent: {},
11458
11523
  nextLevelIndentIncrement: {},
11459
11524
  customContentMenu: { type: Function },
11460
- isExpandable: { type: Function }
11525
+ isExpandable: { type: Function },
11526
+ canDropIn: { type: Function },
11527
+ beforeNodeDblclick: { type: Function }
11461
11528
  },
11462
- setup(__props) {
11529
+ emits: ["node-dblclick"],
11530
+ setup(__props, { emit: __emit }) {
11531
+ const props = __props;
11532
+ const emit = __emit;
11463
11533
  const services = useServices();
11464
11534
  const { editorService } = services;
11465
11535
  const treeRef = (0, vue.useTemplateRef)("tree");
@@ -11482,8 +11552,15 @@
11482
11552
  status.expand = false;
11483
11553
  }
11484
11554
  };
11485
- const { handleDragStart, handleDragEnd, handleDragLeave, handleDragOver } = useDrag(services);
11486
- const { nodeClickHandler, nodeContentMenuHandler, highlightHandler: mouseenterHandler } = useClick(services, isCtrlKeyDown, nodeStatusMap, (0, vue.useTemplateRef)("menu"));
11555
+ const { handleDragStart, handleDragEnd, handleDragLeave, handleDragOver } = useDrag(services, { canDropIn: (sourceIds, targetId) => props.canDropIn?.(sourceIds, targetId, "layer") });
11556
+ const { nodeClickHandler, nodeDblclickHandler, nodeContentMenuHandler, highlightHandler: mouseenterHandler } = useClick(services, isCtrlKeyDown, nodeStatusMap, (0, vue.useTemplateRef)("menu"));
11557
+ const dblclickHandler = async (event, data) => {
11558
+ if (props.beforeNodeDblclick) {
11559
+ if (await props.beforeNodeDblclick(event, data) === false) return;
11560
+ }
11561
+ nodeDblclickHandler(event, data);
11562
+ emit("node-dblclick", event, data);
11563
+ };
11487
11564
  return (_ctx, _cache) => {
11488
11565
  return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicScrollbar), { class: "m-editor-layer-panel" }, {
11489
11566
  default: (0, vue.withCtx)(() => [
@@ -11504,7 +11581,8 @@
11504
11581
  onNodeDragend: (0, vue.unref)(handleDragEnd),
11505
11582
  onNodeContextmenu: (0, vue.unref)(nodeContentMenuHandler),
11506
11583
  onNodeMouseenter: (0, vue.unref)(mouseenterHandler),
11507
- onNodeClick: (0, vue.unref)(nodeClickHandler)
11584
+ onNodeClick: (0, vue.unref)(nodeClickHandler),
11585
+ onNodeDblclick: dblclickHandler
11508
11586
  }, {
11509
11587
  "tree-node-content": (0, vue.withCtx)(({ data: nodeData }) => [(0, vue.renderSlot)(_ctx.$slots, "layer-node-content", { data: nodeData })]),
11510
11588
  "tree-node-tool": (0, vue.withCtx)(({ data: nodeData }) => [(0, vue.renderSlot)(_ctx.$slots, "layer-node-tool", { data: nodeData }, () => [(0, vue.createVNode)(LayerNodeTool_default, { data: nodeData }, null, 8, ["data"])])]),
@@ -11692,9 +11770,13 @@
11692
11770
  indent: {},
11693
11771
  nextLevelIndentIncrement: {},
11694
11772
  customContentMenu: {},
11695
- layerNodeIsExpandable: {}
11773
+ layerNodeIsExpandable: {},
11774
+ canDropIn: {},
11775
+ beforeLayerNodeDblclick: {}
11696
11776
  },
11697
- setup(__props, { expose: __expose }) {
11777
+ emits: ["layer-node-dblclick"],
11778
+ setup(__props, { expose: __expose, emit: __emit }) {
11779
+ const emit = __emit;
11698
11780
  const props = __props;
11699
11781
  const { depService, uiService, propsService } = useServices();
11700
11782
  const collecting = (0, vue.computed)(() => depService.get("collecting"));
@@ -11732,8 +11814,11 @@
11732
11814
  customContentMenu: props.customContentMenu,
11733
11815
  indent: props.indent,
11734
11816
  nextLevelIndentIncrement: props.nextLevelIndentIncrement,
11735
- isExpandable: props.layerNodeIsExpandable
11817
+ isExpandable: props.layerNodeIsExpandable,
11818
+ canDropIn: props.canDropIn,
11819
+ beforeNodeDblclick: props.beforeLayerNodeDblclick
11736
11820
  },
11821
+ listeners: { "node-dblclick": (event, nodeData) => emit("layer-node-dblclick", event, nodeData) },
11737
11822
  component: LayerPanel_default,
11738
11823
  slots: {}
11739
11824
  },
@@ -12638,9 +12723,20 @@
12638
12723
  const doc = stage?.renderer?.contentWindow?.document;
12639
12724
  const parentEl = doc?.querySelector(`.${props.stageOptions?.containerHighlightClassName}`);
12640
12725
  let parent = page.value;
12726
+ let resolvedParentEl = parentEl;
12641
12727
  const parentId = (0, _tmagic_utils.getIdFromEl)()(parentEl);
12642
12728
  if (parentId) parent = editorService.getNodeById(parentId, false);
12643
12729
  if (parent && stageContainerEl.value && stage) {
12730
+ if (props.stageOptions.canDropIn) {
12731
+ const result = props.stageOptions.canDropIn([], parent.id);
12732
+ if (result === false) return;
12733
+ if (typeof result === "string" || typeof result === "number") {
12734
+ const redirectedNode = editorService.getNodeById(result, false);
12735
+ if (!redirectedNode) return;
12736
+ parent = redirectedNode;
12737
+ resolvedParentEl = stage.renderer?.getTargetElement(result) ?? null;
12738
+ }
12739
+ }
12644
12740
  const layout = await editorService.getLayout(parent);
12645
12741
  const containerRect = stageContainerEl.value.getBoundingClientRect();
12646
12742
  const { scrollTop, scrollLeft } = stage.mask;
@@ -12656,8 +12752,8 @@
12656
12752
  position = "absolute";
12657
12753
  top = e.clientY - containerRect.top + scrollTop;
12658
12754
  left = e.clientX - containerRect.left + scrollLeft;
12659
- if (parentEl) {
12660
- const { left: parentLeft, top: parentTop } = (0, _tmagic_stage.getOffset)(parentEl);
12755
+ if (resolvedParentEl) {
12756
+ const { left: parentLeft, top: parentTop } = (0, _tmagic_stage.getOffset)(resolvedParentEl);
12661
12757
  left = left - parentLeft * zoom.value;
12662
12758
  top = top - parentTop * zoom.value;
12663
12759
  }
@@ -13295,7 +13391,7 @@
13295
13391
  var dataSource_default = new DataSource();
13296
13392
  //#endregion
13297
13393
  //#region packages/editor/src/utils/dep/worker.ts?worker&inline
13298
- var jsContent = "(function() {\n //#region packages/schema/src/index.ts\n const NODE_CONDS_KEY = \"displayConds\";\n const NODE_DISABLE_DATA_SOURCE_KEY = \"_tmagic_node_disabled_data_source\";\n const NODE_DISABLE_CODE_BLOCK_KEY = \"_tmagic_node_disabled_code_block\";\n let HookType = /* @__PURE__ */ function(HookType) {\n /** 代码块钩子标识 */\n HookType[\"CODE\"] = \"code\";\n return HookType;\n }({});\n //#endregion\n //#region packages/utils/src/const.ts\n const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = \"ds-field::\";\n //#endregion\n //#region packages/utils/src/index.ts\n const isObject = (obj) => Object.prototype.toString.call(obj) === \"[object Object]\";\n const getKeysArray = (keys) => `${keys}`.replace(/\\[(\\d+)\\]/g, \".$1\").split(\".\");\n const dataSourceTemplateRegExp = /\\$\\{([\\s\\S]+?)\\}/g;\n //#endregion\n //#region packages/dep/src/types.ts\n /** 依赖收集的目标类型 */\n let DepTargetType = /* @__PURE__ */ function(DepTargetType) {\n DepTargetType[\"DEFAULT\"] = \"default\";\n /** 代码块 */\n DepTargetType[\"CODE_BLOCK\"] = \"code-block\";\n /** 数据源 */\n DepTargetType[\"DATA_SOURCE\"] = \"data-source\";\n /** 数据源方法 */\n DepTargetType[\"DATA_SOURCE_METHOD\"] = \"data-source-method\";\n /** 数据源条件 */\n DepTargetType[\"DATA_SOURCE_COND\"] = \"data-source-cond\";\n return DepTargetType;\n }({});\n //#endregion\n //#region packages/dep/src/Target.ts\n /**\n * 需要收集依赖的目标\n * 例如:一个代码块可以为一个目标\n */\n var Target = class {\n /**\n * 如何识别目标\n */\n isTarget;\n /**\n * 目标id,不可重复\n * 例如目标是代码块,则为代码块id\n */\n id;\n /**\n * 目标名称,用于显示在依赖列表中\n */\n name;\n /**\n * 不同的目标可以进行分类,例如代码块,数据源可以为两个不同的type\n */\n type = DepTargetType.DEFAULT;\n /**\n * 依赖详情\n * 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }\n */\n deps = {};\n /**\n * 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集\n */\n isCollectByDefault;\n constructor(options) {\n this.isTarget = options.isTarget;\n this.id = options.id;\n this.name = options.name;\n this.isCollectByDefault = options.isCollectByDefault ?? true;\n if (options.type) this.type = options.type;\n if (options.initialDeps) this.deps = options.initialDeps;\n }\n /**\n * 更新依赖\n * @param option 节点配置\n * @param key 哪个key配置了这个目标的id\n */\n updateDep({ id, name, key, data }) {\n const dep = this.deps[id] || {\n name,\n keys: []\n };\n dep.name = name;\n dep.data = data;\n this.deps[id] = dep;\n if (!dep.keys.includes(key)) dep.keys.push(key);\n }\n /**\n * 删除依赖\n * @param node 哪个节点的依赖需要移除,如果为空,则移除所有依赖\n * @param key 节点下哪个key需要移除,如果为空,则移除改节点下的所有依赖key\n * @returns void\n */\n removeDep(id, key) {\n if (typeof id === \"undefined\") {\n Object.keys(this.deps).forEach((depKey) => {\n delete this.deps[depKey];\n });\n return;\n }\n const dep = this.deps[id];\n if (!dep) return;\n if (key) {\n const index = dep.keys.indexOf(key);\n dep.keys.splice(index, 1);\n if (dep.keys.length === 0) delete this.deps[id];\n } else delete this.deps[id];\n }\n /**\n * 判断指定节点下的指定key是否存在在依赖列表中\n * @param node 哪个节点\n * @param key 哪个key\n * @returns boolean\n */\n hasDep(id, key) {\n return this.deps[id]?.keys.includes(key) ?? false;\n }\n destroy() {\n this.deps = {};\n }\n };\n //#endregion\n //#region packages/dep/src/utils.ts\n const INTEGER_REGEXP = /^\\d+$/;\n const createCodeBlockTarget = (id, codeBlock, initialDeps = {}) => new Target({\n type: DepTargetType.CODE_BLOCK,\n id,\n initialDeps,\n name: codeBlock.name,\n isTarget: (_key, value) => {\n if (id === value) return true;\n if (value?.hookType === HookType.CODE && Array.isArray(value.hookData)) return value.hookData.some((item) => item.codeId === id);\n return false;\n }\n });\n /**\n * ['array'] ['array', '0'] ['array', '0', 'a'] 这种返回false\n * ['array', 'a'] 这种返回true\n * @param keys\n * @param fields\n * @returns boolean\n */\n const isIncludeArrayField = (keys, fields) => {\n let f = fields;\n return keys.some((key, index) => {\n const field = f.find(({ name }) => name === key);\n f = field?.fields || [];\n return field?.type === \"array\" && index < keys.length - 1 && !INTEGER_REGEXP.test(keys[index + 1]);\n });\n };\n /**\n * 判断模板(value)是不是使用数据源Id(dsId),如:`xxx${dsId.field}xxx${dsId.field}`\n * @param value any\n * @param dsId string | number\n * @param hasArray boolean true: 一定要包含有需要迭代的模板; false: 一定要包含普通模板;\n * @returns boolean\n */\n const isDataSourceTemplate = (value, ds, hasArray = false) => {\n const templates = value.match(dataSourceTemplateRegExp) || [];\n if (templates.length <= 0) return false;\n for (const tpl of templates) {\n const keys = getKeysArray(tpl.substring(2, tpl.length - 1));\n const dsId = keys.shift();\n if (!dsId || dsId !== ds.id) continue;\n if (hasArray === isIncludeArrayField(keys, ds.fields)) return true;\n }\n return false;\n };\n /**\n * 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}\n * @param value any\n * @param dsId string | number\n * @returns boolean\n */\n const isSpecificDataSourceTemplate = (value, dsId) => value?.isBindDataSourceField && value.dataSourceId && value.dataSourceId === dsId && typeof value.template === \"string\";\n /**\n * 关联数据源字段,格式为 [前缀+数据源ID, 字段名]\n * 使用data-source-field-select value: 'value' 可以配置出来\n * @param value any[]\n * @param id string | number\n * @returns boolean\n */\n const isUseDataSourceField = (value, id) => {\n if (!Array.isArray(value) || typeof value[0] !== \"string\") return false;\n const [prefixId] = value;\n const prefixIndex = prefixId.indexOf(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX);\n if (prefixIndex === -1) return false;\n return prefixId.substring(prefixIndex + DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX.length) === id;\n };\n const isDataSourceTarget = (ds, key, value, hasArray = false) => {\n if (!value) return false;\n const valueType = typeof value;\n if (valueType !== \"string\" && valueType !== \"object\") return false;\n if (`${key}`.startsWith(\"displayConds\")) return false;\n if (valueType === \"string\") return isDataSourceTemplate(value, ds, hasArray);\n if (isObject(value) && value.isBindDataSource && value.dataSourceId === ds.id) return true;\n if (isSpecificDataSourceTemplate(value, ds.id)) return true;\n if (isUseDataSourceField(value, ds.id)) {\n const [, ...keys] = value;\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const isDataSourceCondTarget = (ds, key, value, hasArray = false) => {\n if (!Array.isArray(value) || !ds) return false;\n const [dsId, ...keys] = value;\n if (dsId !== ds.id || !`${key}`.startsWith(\"displayConds\")) return false;\n if (ds.fields?.some((field) => field.name === keys[0])) {\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const createDataSourceTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceTarget(ds, key, value)\n });\n const createDataSourceCondTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_COND,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceCondTarget(ds, key, value)\n });\n const createDataSourceMethodTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_METHOD,\n id: ds.id,\n initialDeps,\n isTarget: (_key, value) => {\n if (!Array.isArray(value)) return false;\n const [dsId, methodName] = value;\n if (!methodName || dsId !== ds.id) return false;\n if (ds.methods?.some((method) => method.name === methodName)) return true;\n if (ds.fields?.some((field) => field.name === methodName)) return false;\n return true;\n }\n });\n const traverseTarget = (targetsList, cb, type) => {\n if (type) {\n const targets = targetsList[type];\n if (targets) for (const target of Object.values(targets)) cb(target);\n return;\n }\n for (const targets of Object.values(targetsList)) for (const target of Object.values(targets)) cb(target);\n };\n //#endregion\n //#region packages/dep/src/Watcher.ts\n const DATA_SOURCE_TARGET_TYPES = new Set([\n DepTargetType.DATA_SOURCE,\n DepTargetType.DATA_SOURCE_COND,\n DepTargetType.DATA_SOURCE_METHOD\n ]);\n var Watcher = class {\n targetsList = {};\n childrenProp = \"items\";\n idProp = \"id\";\n nameProp = \"name\";\n constructor(options) {\n if (options?.initialTargets) this.targetsList = options.initialTargets;\n if (options?.childrenProp) this.childrenProp = options.childrenProp;\n }\n getTargetsList() {\n return this.targetsList;\n }\n /**\n * 获取指定类型中的所有target\n * @param type 分类\n * @returns Target[]\n */\n getTargets(type = DepTargetType.DEFAULT) {\n return this.targetsList[type] || {};\n }\n /**\n * 添加新的目标\n * @param target Target\n */\n addTarget(target) {\n const targets = this.getTargets(target.type) || {};\n this.targetsList[target.type] = targets;\n targets[target.id] = target;\n }\n /**\n * 获取指定id的target\n * @param id target id\n * @returns Target\n */\n getTarget(id, type = DepTargetType.DEFAULT) {\n return this.getTargets(type)[id];\n }\n /**\n * 判断是否存在指定id的target\n * @param id target id\n * @returns boolean\n */\n hasTarget(id, type = DepTargetType.DEFAULT) {\n return Boolean(this.getTarget(id, type));\n }\n /**\n * 判断是否存在指定类型的target\n * @param type target type\n * @returns boolean\n */\n hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {\n return Object.keys(this.getTargets(type)).length > 0;\n }\n /**\n * 删除指定id的target\n * @param id target id\n */\n removeTarget(id, type = DepTargetType.DEFAULT) {\n const targets = this.getTargets(type);\n if (targets[id]) {\n targets[id].destroy();\n delete targets[id];\n }\n }\n /**\n * 删除指定分类的所有target\n * @param type 分类\n * @returns void\n */\n removeTargets(type = DepTargetType.DEFAULT) {\n const targets = this.targetsList[type];\n if (!targets) return;\n for (const target of Object.values(targets)) target.destroy();\n delete this.targetsList[type];\n }\n /**\n * 删除所有target\n */\n clearTargets() {\n for (const key of Object.keys(this.targetsList)) delete this.targetsList[key];\n }\n /**\n * 收集依赖\n * @param nodes 需要收集的节点\n * @param deep 是否需要收集子节点\n * @param type 强制收集指定类型的依赖\n */\n collect(nodes, depExtendedData = {}, deep = false, type) {\n this.collectByCallback(nodes, type, ({ node, target }) => {\n this.removeTargetDep(target, node);\n this.collectItem(node, target, depExtendedData, deep);\n });\n }\n collectByCallback(nodes, type, cb) {\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n for (const node of nodes) cb({\n node,\n target\n });\n }, type);\n }\n /**\n * 清除所有目标的依赖\n * @param nodes 需要清除依赖的节点\n */\n clear(nodes, type) {\n let { targetsList } = this;\n if (type) targetsList = { [type]: this.getTargets(type) };\n const clearedItemsNodeIds = /* @__PURE__ */ new Set();\n traverseTarget(targetsList, (target) => {\n if (nodes) for (const node of nodes) {\n target.removeDep(node[this.idProp]);\n if (Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length && !clearedItemsNodeIds.has(node[this.idProp])) {\n clearedItemsNodeIds.add(node[this.idProp]);\n this.clear(node[this.childrenProp]);\n }\n }\n else target.removeDep();\n });\n }\n /**\n * 清除指定类型的依赖\n * @param type 类型\n * @param nodes 需要清除依赖的节点\n */\n clearByType(type, nodes) {\n this.clear(nodes, type);\n }\n collectItem(node, target, depExtendedData = {}, deep = false) {\n if (node[\"_tmagic_node_disabled_data_source\"] && DATA_SOURCE_TARGET_TYPES.has(target.type)) return;\n if (node[\"_tmagic_node_disabled_code_block\"] && target.type === DepTargetType.CODE_BLOCK) return;\n const collectTarget = (config, prop = \"\") => {\n const doCollect = (key, value) => {\n const keyIsItems = key === this.childrenProp;\n const fullKey = prop ? `${prop}.${key}` : key;\n if (target.isTarget(fullKey, value)) target.updateDep({\n id: node[this.idProp],\n name: `${node[this.nameProp] || node[this.idProp]}`,\n data: depExtendedData,\n key: fullKey\n });\n else if (!keyIsItems && Array.isArray(value)) for (let i = 0, l = value.length; i < l; i++) {\n const item = value[i];\n if (isObject(item)) collectTarget(item, `${fullKey}[${i}]`);\n }\n else if (isObject(value)) collectTarget(value, fullKey);\n if (keyIsItems && deep && Array.isArray(value)) for (const child of value) this.collectItem(child, target, depExtendedData, deep);\n };\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === \"undefined\" || value === \"\") continue;\n doCollect(key, value);\n }\n };\n collectTarget(node);\n }\n removeTargetDep(target, node, key) {\n target.removeDep(node[this.idProp], key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetDep(target, item, key);\n }\n };\n //#endregion\n //#region packages/editor/src/utils/logger.ts\n const error = (...args) => {\n console.error(\"magic editor: \", ...args);\n };\n //#endregion\n //#region packages/editor/src/utils/dep/worker.ts\n onmessage = (e) => {\n const watcher = new Watcher({ initialTargets: {} });\n const { dsl } = e.data;\n try {\n const mApp = eval(`(${dsl})`);\n if (!mApp) postMessage({});\n watcher.clearTargets();\n if (mApp.codeBlocks) for (const [id, code] of Object.entries(mApp.codeBlocks)) watcher.addTarget(createCodeBlockTarget(id, code));\n if (mApp.dataSources) for (const ds of mApp.dataSources) {\n watcher.addTarget(createDataSourceTarget(ds, {}));\n watcher.addTarget(createDataSourceMethodTarget(ds, {}));\n watcher.addTarget(createDataSourceCondTarget(ds, {}));\n }\n watcher.collectByCallback(mApp.items, void 0, ({ node, target }) => {\n watcher.collectItem(node, target, { pageId: node.id }, true);\n });\n const data = {\n [DepTargetType.DATA_SOURCE]: {},\n [DepTargetType.DATA_SOURCE_METHOD]: {},\n [DepTargetType.DATA_SOURCE_COND]: {},\n [DepTargetType.CODE_BLOCK]: {}\n };\n traverseTarget(watcher.getTargetsList(), (target) => {\n data[target.type][target.id] = target.deps;\n });\n postMessage(data);\n } catch (e) {\n error(e);\n postMessage({});\n }\n };\n //#endregion\n})();\n";
13394
+ var jsContent = "(function() {\n //#region packages/schema/src/index.ts\n const NODE_CONDS_KEY = \"displayConds\";\n const NODE_DISABLE_DATA_SOURCE_KEY = \"_tmagic_node_disabled_data_source\";\n const NODE_DISABLE_CODE_BLOCK_KEY = \"_tmagic_node_disabled_code_block\";\n let HookType = /* @__PURE__ */ function(HookType) {\n /** 代码块钩子标识 */\n HookType[\"CODE\"] = \"code\";\n return HookType;\n }({});\n //#endregion\n //#region packages/utils/src/const.ts\n const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = \"ds-field::\";\n //#endregion\n //#region packages/utils/src/index.ts\n const isObject = (obj) => Object.prototype.toString.call(obj) === \"[object Object]\";\n const getKeysArray = (keys) => `${keys}`.replace(/\\[(\\d+)\\]/g, \".$1\").split(\".\");\n const dataSourceTemplateRegExp = /\\$\\{([\\s\\S]+?)\\}/g;\n //#endregion\n //#region packages/dep/src/types.ts\n /** 依赖收集的目标类型 */\n let DepTargetType = /* @__PURE__ */ function(DepTargetType) {\n DepTargetType[\"DEFAULT\"] = \"default\";\n /** 代码块 */\n DepTargetType[\"CODE_BLOCK\"] = \"code-block\";\n /** 数据源 */\n DepTargetType[\"DATA_SOURCE\"] = \"data-source\";\n /** 数据源方法 */\n DepTargetType[\"DATA_SOURCE_METHOD\"] = \"data-source-method\";\n /** 数据源条件 */\n DepTargetType[\"DATA_SOURCE_COND\"] = \"data-source-cond\";\n return DepTargetType;\n }({});\n //#endregion\n //#region packages/dep/src/Target.ts\n /**\n * 需要收集依赖的目标\n * 例如:一个代码块可以为一个目标\n */\n var Target = class {\n /**\n * 如何识别目标\n */\n isTarget;\n /**\n * 目标id,不可重复\n * 例如目标是代码块,则为代码块id\n */\n id;\n /**\n * 目标名称,用于显示在依赖列表中\n */\n name;\n /**\n * 不同的目标可以进行分类,例如代码块,数据源可以为两个不同的type\n */\n type = DepTargetType.DEFAULT;\n /**\n * 依赖详情\n * 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }\n */\n deps = {};\n /**\n * 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集\n */\n isCollectByDefault;\n constructor(options) {\n this.isTarget = options.isTarget;\n this.id = options.id;\n this.name = options.name;\n this.isCollectByDefault = options.isCollectByDefault ?? true;\n if (options.type) this.type = options.type;\n if (options.initialDeps) this.deps = options.initialDeps;\n }\n /**\n * 更新依赖\n * @param option 节点配置\n * @param key 哪个key配置了这个目标的id\n */\n updateDep({ id, name, key, data }) {\n const dep = this.deps[id] || {\n name,\n keys: []\n };\n dep.name = name;\n dep.data = data;\n this.deps[id] = dep;\n if (!dep.keys.includes(key)) dep.keys.push(key);\n }\n /**\n * 删除依赖\n * @param node 哪个节点的依赖需要移除,如果为空,则移除所有依赖\n * @param key 节点下哪个key需要移除,如果为空,则移除改节点下的所有依赖key\n * @returns void\n */\n removeDep(id, key) {\n if (typeof id === \"undefined\") {\n Object.keys(this.deps).forEach((depKey) => {\n delete this.deps[depKey];\n });\n return;\n }\n const dep = this.deps[id];\n if (!dep) return;\n if (key) {\n const index = dep.keys.indexOf(key);\n dep.keys.splice(index, 1);\n if (dep.keys.length === 0) delete this.deps[id];\n } else delete this.deps[id];\n }\n /**\n * 判断指定节点下的指定key是否存在在依赖列表中\n * @param node 哪个节点\n * @param key 哪个key\n * @returns boolean\n */\n hasDep(id, key) {\n return this.deps[id]?.keys.includes(key) ?? false;\n }\n destroy() {\n this.deps = {};\n }\n };\n //#endregion\n //#region packages/dep/src/utils.ts\n const INTEGER_REGEXP = /^\\d+$/;\n const createCodeBlockTarget = (id, codeBlock, initialDeps = {}) => new Target({\n type: DepTargetType.CODE_BLOCK,\n id,\n initialDeps,\n name: codeBlock.name,\n isTarget: (_key, value) => {\n if (id === value) return true;\n if (value?.hookType === HookType.CODE && Array.isArray(value.hookData)) return value.hookData.some((item) => item.codeId === id);\n return false;\n }\n });\n /**\n * ['array'] ['array', '0'] ['array', '0', 'a'] 这种返回false\n * ['array', 'a'] 这种返回true\n * @param keys\n * @param fields\n * @returns boolean\n */\n const isIncludeArrayField = (keys, fields) => {\n let f = fields;\n return keys.some((key, index) => {\n const field = f.find(({ name }) => name === key);\n f = field?.fields || [];\n return field?.type === \"array\" && index < keys.length - 1 && !INTEGER_REGEXP.test(keys[index + 1]);\n });\n };\n /**\n * 判断模板(value)是不是使用数据源Id(dsId),如:`xxx${dsId.field}xxx${dsId.field}`\n * @param value any\n * @param dsId string | number\n * @param hasArray boolean true: 一定要包含有需要迭代的模板; false: 一定要包含普通模板;\n * @returns boolean\n */\n const isDataSourceTemplate = (value, ds, hasArray = false) => {\n const templates = value.match(dataSourceTemplateRegExp) || [];\n if (templates.length <= 0) return false;\n for (const tpl of templates) {\n const keys = getKeysArray(tpl.substring(2, tpl.length - 1));\n const dsId = keys.shift();\n if (!dsId || dsId !== ds.id) continue;\n if (hasArray === isIncludeArrayField(keys, ds.fields)) return true;\n }\n return false;\n };\n /**\n * 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}\n * @param value any\n * @param dsId string | number\n * @returns boolean\n */\n const isSpecificDataSourceTemplate = (value, dsId) => value?.isBindDataSourceField && value.dataSourceId && value.dataSourceId === dsId && typeof value.template === \"string\";\n /**\n * 关联数据源字段,格式为 [前缀+数据源ID, 字段名]\n * 使用data-source-field-select value: 'value' 可以配置出来\n * @param value any[]\n * @param id string | number\n * @returns boolean\n */\n const isUseDataSourceField = (value, id) => {\n if (!Array.isArray(value) || typeof value[0] !== \"string\") return false;\n const [prefixId] = value;\n const prefixIndex = prefixId.indexOf(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX);\n if (prefixIndex === -1) return false;\n return prefixId.substring(prefixIndex + DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX.length) === id;\n };\n const isDataSourceTarget = (ds, key, value, hasArray = false) => {\n if (!value) return false;\n const valueType = typeof value;\n if (valueType !== \"string\" && valueType !== \"object\") return false;\n if (`${key}`.startsWith(\"displayConds\")) return false;\n if (valueType === \"string\") return isDataSourceTemplate(value, ds, hasArray);\n if (isObject(value) && value.isBindDataSource && value.dataSourceId === ds.id) return true;\n if (isSpecificDataSourceTemplate(value, ds.id)) return true;\n if (isUseDataSourceField(value, ds.id)) {\n const [, ...keys] = value;\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const isDataSourceCondTarget = (ds, key, value, hasArray = false) => {\n if (!Array.isArray(value) || !ds) return false;\n const [dsId, ...keys] = value;\n if (dsId !== ds.id || !`${key}`.startsWith(\"displayConds\")) return false;\n if (ds.fields?.some((field) => field.name === keys[0])) {\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const createDataSourceTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceTarget(ds, key, value)\n });\n const createDataSourceCondTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_COND,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceCondTarget(ds, key, value)\n });\n const createDataSourceMethodTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_METHOD,\n id: ds.id,\n initialDeps,\n isTarget: (_key, value) => {\n if (!Array.isArray(value)) return false;\n const [dsId, methodName] = value;\n if (!methodName || dsId !== ds.id) return false;\n if (ds.methods?.some((method) => method.name === methodName)) return true;\n if (ds.fields?.some((field) => field.name === methodName)) return false;\n return true;\n }\n });\n const traverseTarget = (targetsList, cb, type) => {\n if (type) {\n const targets = targetsList[type];\n if (targets) for (const target of Object.values(targets)) cb(target);\n return;\n }\n for (const targets of Object.values(targetsList)) for (const target of Object.values(targets)) cb(target);\n };\n //#endregion\n //#region packages/dep/src/Watcher.ts\n const DATA_SOURCE_TARGET_TYPES = new Set([\n DepTargetType.DATA_SOURCE,\n DepTargetType.DATA_SOURCE_COND,\n DepTargetType.DATA_SOURCE_METHOD\n ]);\n var Watcher = class {\n targetsList = {};\n childrenProp = \"items\";\n idProp = \"id\";\n nameProp = \"name\";\n constructor(options) {\n if (options?.initialTargets) this.targetsList = options.initialTargets;\n if (options?.childrenProp) this.childrenProp = options.childrenProp;\n }\n getTargetsList() {\n return this.targetsList;\n }\n /**\n * 获取指定类型中的所有target\n * @param type 分类\n * @returns Target[]\n */\n getTargets(type = DepTargetType.DEFAULT) {\n return this.targetsList[type] || {};\n }\n /**\n * 添加新的目标\n * @param target Target\n */\n addTarget(target) {\n const targets = this.getTargets(target.type) || {};\n this.targetsList[target.type] = targets;\n targets[target.id] = target;\n }\n /**\n * 获取指定id的target\n * @param id target id\n * @returns Target\n */\n getTarget(id, type = DepTargetType.DEFAULT) {\n return this.getTargets(type)[id];\n }\n /**\n * 判断是否存在指定id的target\n * @param id target id\n * @returns boolean\n */\n hasTarget(id, type = DepTargetType.DEFAULT) {\n return Boolean(this.getTarget(id, type));\n }\n /**\n * 判断是否存在指定类型的target\n * @param type target type\n * @returns boolean\n */\n hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {\n return Object.keys(this.getTargets(type)).length > 0;\n }\n /**\n * 删除指定id的target\n * @param id target id\n */\n removeTarget(id, type = DepTargetType.DEFAULT) {\n const targets = this.getTargets(type);\n if (targets[id]) {\n targets[id].destroy();\n delete targets[id];\n }\n }\n /**\n * 删除指定分类的所有target\n * @param type 分类\n * @returns void\n */\n removeTargets(type = DepTargetType.DEFAULT) {\n const targets = this.targetsList[type];\n if (!targets) return;\n for (const target of Object.values(targets)) target.destroy();\n delete this.targetsList[type];\n }\n /**\n * 删除所有target\n */\n clearTargets() {\n for (const key of Object.keys(this.targetsList)) delete this.targetsList[key];\n }\n /**\n * 收集依赖\n * @param nodes 需要收集的节点\n * @param deep 是否需要收集子节点\n * @param type 强制收集指定类型的依赖\n */\n collect(nodes, depExtendedData = {}, deep = false, type) {\n this.collectByCallback(nodes, type, ({ node, target }) => {\n this.removeTargetDep(target, node);\n this.collectItem(node, target, depExtendedData, deep);\n });\n }\n collectByCallback(nodes, type, cb) {\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n for (const node of nodes) cb({\n node,\n target\n });\n }, type);\n }\n /**\n * 清除所有目标的依赖\n * @param nodes 需要清除依赖的节点\n */\n clear(nodes, type) {\n let { targetsList } = this;\n if (type) targetsList = { [type]: this.getTargets(type) };\n const clearedItemsNodeIds = /* @__PURE__ */ new Set();\n traverseTarget(targetsList, (target) => {\n if (nodes) for (const node of nodes) {\n target.removeDep(node[this.idProp]);\n if (Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length && !clearedItemsNodeIds.has(node[this.idProp])) {\n clearedItemsNodeIds.add(node[this.idProp]);\n this.clear(node[this.childrenProp]);\n }\n }\n else target.removeDep();\n });\n }\n /**\n * 清除指定类型的依赖\n * @param type 类型\n * @param nodes 需要清除依赖的节点\n */\n clearByType(type, nodes) {\n this.clear(nodes, type);\n }\n collectItem(node, target, depExtendedData = {}, deep = false) {\n if (node[\"_tmagic_node_disabled_data_source\"] && DATA_SOURCE_TARGET_TYPES.has(target.type)) return;\n if (node[\"_tmagic_node_disabled_code_block\"] && target.type === DepTargetType.CODE_BLOCK) return;\n const collectTarget = (config, prop = \"\") => {\n const doCollect = (key, value) => {\n const keyIsItems = key === this.childrenProp;\n const fullKey = prop ? `${prop}.${key}` : key;\n if (target.isTarget(fullKey, value)) target.updateDep({\n id: node[this.idProp],\n name: `${node[this.nameProp] || node[this.idProp]}`,\n data: depExtendedData,\n key: fullKey\n });\n else if (!keyIsItems && Array.isArray(value)) for (let i = 0, l = value.length; i < l; i++) {\n const item = value[i];\n if (isObject(item)) collectTarget(item, `${fullKey}[${i}]`);\n }\n else if (isObject(value)) collectTarget(value, fullKey);\n if (keyIsItems && deep && Array.isArray(value)) for (const child of value) this.collectItem(child, target, depExtendedData, deep);\n };\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === \"undefined\" || value === \"\") continue;\n doCollect(key, value);\n }\n };\n collectTarget(node);\n }\n removeTargetDep(target, node, key) {\n target.removeDep(node[this.idProp], key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetDep(target, item, key);\n }\n };\n //#endregion\n //#region packages/editor/src/utils/logger.ts\n const error = (...args) => {\n if (process.env.NODE_ENV === \"development\") console.error(\"magic editor: \", ...args);\n };\n //#endregion\n //#region packages/editor/src/utils/dep/worker.ts\n onmessage = (e) => {\n const watcher = new Watcher({ initialTargets: {} });\n const { dsl } = e.data;\n try {\n const mApp = eval(`(${dsl})`);\n if (!mApp) postMessage({});\n watcher.clearTargets();\n if (mApp.codeBlocks) for (const [id, code] of Object.entries(mApp.codeBlocks)) watcher.addTarget(createCodeBlockTarget(id, code));\n if (mApp.dataSources) for (const ds of mApp.dataSources) {\n watcher.addTarget(createDataSourceTarget(ds, {}));\n watcher.addTarget(createDataSourceMethodTarget(ds, {}));\n watcher.addTarget(createDataSourceCondTarget(ds, {}));\n }\n watcher.collectByCallback(mApp.items, void 0, ({ node, target }) => {\n watcher.collectItem(node, target, { pageId: node.id }, true);\n });\n const data = {\n [DepTargetType.DATA_SOURCE]: {},\n [DepTargetType.DATA_SOURCE_METHOD]: {},\n [DepTargetType.DATA_SOURCE_COND]: {},\n [DepTargetType.CODE_BLOCK]: {}\n };\n traverseTarget(watcher.getTargetsList(), (target) => {\n data[target.type][target.id] = target.deps;\n });\n postMessage(data);\n } catch (e) {\n error(e);\n postMessage({});\n }\n };\n //#endregion\n})();\n";
13299
13395
  var blob = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent], { type: "text/javascript;charset=utf-8" });
13300
13396
  function WorkerWrapper(options) {
13301
13397
  let objURL;
@@ -13869,6 +13965,7 @@
13869
13965
  var defaultEditorProps = {
13870
13966
  renderType: _tmagic_stage.RenderType.IFRAME,
13871
13967
  disabledMultiSelect: false,
13968
+ alwaysMultiSelect: false,
13872
13969
  disabledPageFragment: false,
13873
13970
  disabledStageOverlay: false,
13874
13971
  containerHighlightClassName: _tmagic_stage.CONTAINER_HIGHLIGHT_CLASS_NAME,
@@ -13904,6 +14001,9 @@
13904
14001
  (0, vue.watch)(() => props.disabledMultiSelect, (disabledMultiSelect) => {
13905
14002
  editorService.set("disabledMultiSelect", disabledMultiSelect || false);
13906
14003
  }, { immediate: true });
14004
+ (0, vue.watch)(() => props.alwaysMultiSelect, (alwaysMultiSelect) => {
14005
+ editorService.set("alwaysMultiSelect", alwaysMultiSelect || false);
14006
+ }, { immediate: true });
13907
14007
  (0, vue.watch)(() => props.componentGroupList, (componentGroupList) => componentGroupList && componentListService.setList(componentGroupList), { immediate: true });
13908
14008
  (0, vue.watch)(() => props.datasourceList, (datasourceList) => datasourceList && dataSourceService.set("datasourceTypeList", datasourceList), { immediate: true });
13909
14009
  (0, vue.watch)(() => props.propsConfigs, (configs) => configs && propsService.setPropsConfigs(configs), { immediate: true });
@@ -14313,6 +14413,7 @@
14313
14413
  disabledDragStart: { type: Boolean },
14314
14414
  guidesOptions: {},
14315
14415
  disabledMultiSelect: { type: Boolean },
14416
+ alwaysMultiSelect: { type: Boolean },
14316
14417
  disabledPageFragment: { type: Boolean },
14317
14418
  disabledStageOverlay: { type: Boolean },
14318
14419
  disabledShowSrc: { type: Boolean },
@@ -14326,7 +14427,9 @@
14326
14427
  isContainer: { type: Function },
14327
14428
  customContentMenu: { type: Function },
14328
14429
  layerNodeIsExpandable: { type: Function },
14430
+ canDropIn: { type: Function },
14329
14431
  beforeDblclick: { type: Function },
14432
+ beforeLayerNodeDblclick: { type: Function },
14330
14433
  extendFormState: { type: Function },
14331
14434
  pageBarSortOptions: {},
14332
14435
  pageFilterFunction: { type: Function }
@@ -14336,7 +14439,8 @@
14336
14439
  "props-panel-unmounted",
14337
14440
  "update:modelValue",
14338
14441
  "props-form-error",
14339
- "props-submit-error"
14442
+ "props-submit-error",
14443
+ "layer-node-dblclick"
14340
14444
  ],
14341
14445
  setup(__props, { expose: __expose, emit: __emit }) {
14342
14446
  const emit = __emit;
@@ -14367,6 +14471,7 @@
14367
14471
  canSelect: props.canSelect,
14368
14472
  updateDragEl: props.updateDragEl,
14369
14473
  isContainer: props.isContainer,
14474
+ canDropIn: props.canDropIn ? (sourceIds, targetId) => props.canDropIn(sourceIds, targetId, sourceIds.length === 0 ? "stage-add" : "stage-drag") : void 0,
14370
14475
  containerHighlightClassName: props.containerHighlightClassName,
14371
14476
  containerHighlightDuration: props.containerHighlightDuration,
14372
14477
  containerHighlightType: props.containerHighlightType,
@@ -14374,6 +14479,7 @@
14374
14479
  renderType: props.renderType,
14375
14480
  guidesOptions: props.guidesOptions,
14376
14481
  disabledMultiSelect: props.disabledMultiSelect,
14482
+ alwaysMultiSelect: props.alwaysMultiSelect,
14377
14483
  beforeDblclick: props.beforeDblclick
14378
14484
  };
14379
14485
  stageOverlay_default.set("stageOptions", stageOptions);
@@ -14393,6 +14499,9 @@
14393
14499
  const propsPanelFormErrorHandler = (e) => {
14394
14500
  emit("props-form-error", e);
14395
14501
  };
14502
+ const layerNodeDblclickHandler = (event, data) => {
14503
+ emit("layer-node-dblclick", event, data);
14504
+ };
14396
14505
  __expose(services);
14397
14506
  return (_ctx, _cache) => {
14398
14507
  return (0, vue.openBlock)(), (0, vue.createBlock)(Framework_default, {
@@ -14410,7 +14519,10 @@
14410
14519
  "custom-content-menu": __props.customContentMenu,
14411
14520
  indent: __props.treeIndent,
14412
14521
  "next-level-indent-increment": __props.treeNextLevelIndentIncrement,
14413
- "layer-node-is-expandable": __props.layerNodeIsExpandable
14522
+ "layer-node-is-expandable": __props.layerNodeIsExpandable,
14523
+ "can-drop-in": __props.canDropIn,
14524
+ "before-layer-node-dblclick": __props.beforeLayerNodeDblclick,
14525
+ onLayerNodeDblclick: layerNodeDblclickHandler
14414
14526
  }, {
14415
14527
  "layer-panel-header": (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "layer-panel-header")]),
14416
14528
  "layer-node-content": (0, vue.withCtx)(({ data }) => [(0, vue.renderSlot)(_ctx.$slots, "layer-node-content", { data })]),
@@ -14434,7 +14546,9 @@
14434
14546
  "custom-content-menu",
14435
14547
  "indent",
14436
14548
  "next-level-indent-increment",
14437
- "layer-node-is-expandable"
14549
+ "layer-node-is-expandable",
14550
+ "can-drop-in",
14551
+ "before-layer-node-dblclick"
14438
14552
  ])])]),
14439
14553
  workspace: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "workspace", { editorService: (0, vue.unref)(editor_default) }, () => [(0, vue.createVNode)(Workspace_default, {
14440
14554
  "disabled-stage-overlay": __props.disabledStageOverlay,
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.7.13-beta.0",
2
+ "version": "1.7.14-beta.0",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -58,11 +58,11 @@
58
58
  "moveable": "^0.53.0",
59
59
  "serialize-javascript": "^7.0.0",
60
60
  "sortablejs": "^1.15.6",
61
- "@tmagic/design": "1.7.13-beta.0",
62
- "@tmagic/form": "1.7.13-beta.0",
63
- "@tmagic/table": "1.7.13-beta.0",
64
- "@tmagic/stage": "1.7.13-beta.0",
65
- "@tmagic/utils": "1.7.13-beta.0"
61
+ "@tmagic/design": "1.7.14-beta.0",
62
+ "@tmagic/form": "1.7.14-beta.0",
63
+ "@tmagic/table": "1.7.14-beta.0",
64
+ "@tmagic/stage": "1.7.14-beta.0",
65
+ "@tmagic/utils": "1.7.14-beta.0"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/events": "^3.0.3",
@@ -76,7 +76,7 @@
76
76
  "type-fest": "^5.2.0",
77
77
  "typescript": "^6.0.3",
78
78
  "vue": "^3.5.33",
79
- "@tmagic/core": "1.7.13-beta.0"
79
+ "@tmagic/core": "1.7.14-beta.0"
80
80
  },
81
81
  "peerDependenciesMeta": {
82
82
  "typescript": {
package/src/Editor.vue CHANGED
@@ -27,6 +27,9 @@
27
27
  :indent="treeIndent"
28
28
  :next-level-indent-increment="treeNextLevelIndentIncrement"
29
29
  :layer-node-is-expandable="layerNodeIsExpandable"
30
+ :can-drop-in="canDropIn"
31
+ :before-layer-node-dblclick="beforeLayerNodeDblclick"
32
+ @layer-node-dblclick="layerNodeDblclickHandler"
30
33
  >
31
34
  <template #layer-panel-header>
32
35
  <slot name="layer-panel-header"></slot>
@@ -156,6 +159,7 @@ import uiService from './services/ui';
156
159
  import keybindingConfig from './utils/keybinding-config';
157
160
  import { defaultEditorProps, EditorProps } from './editorProps';
158
161
  import { initServiceEvents, initServiceState } from './initService';
162
+ import type { TreeNodeData } from './type';
159
163
  import type { EditorSlots, EventBus, Services, StageOptions } from './type';
160
164
 
161
165
  defineSlots<EditorSlots>();
@@ -170,6 +174,7 @@ const emit = defineEmits<{
170
174
  'update:modelValue': [value: MApp | null];
171
175
  'props-form-error': [e: any];
172
176
  'props-submit-error': [e: any];
177
+ 'layer-node-dblclick': [event: MouseEvent, data: TreeNodeData];
173
178
  }>();
174
179
 
175
180
  const props = withDefaults(defineProps<EditorProps>(), defaultEditorProps);
@@ -202,6 +207,11 @@ const stageOptions: StageOptions = {
202
207
  canSelect: props.canSelect,
203
208
  updateDragEl: props.updateDragEl,
204
209
  isContainer: props.isContainer,
210
+ // sourceIds 为空表示从组件列表新增(尚无 id),否则是画布上拖动已有组件
211
+ canDropIn: props.canDropIn
212
+ ? (sourceIds, targetId) =>
213
+ props.canDropIn!(sourceIds, targetId, sourceIds.length === 0 ? 'stage-add' : 'stage-drag')
214
+ : undefined,
205
215
  containerHighlightClassName: props.containerHighlightClassName,
206
216
  containerHighlightDuration: props.containerHighlightDuration,
207
217
  containerHighlightType: props.containerHighlightType,
@@ -209,6 +219,7 @@ const stageOptions: StageOptions = {
209
219
  renderType: props.renderType,
210
220
  guidesOptions: props.guidesOptions,
211
221
  disabledMultiSelect: props.disabledMultiSelect,
222
+ alwaysMultiSelect: props.alwaysMultiSelect,
212
223
  beforeDblclick: props.beforeDblclick,
213
224
  };
214
225
 
@@ -236,5 +247,9 @@ const propsPanelFormErrorHandler = (e: any) => {
236
247
  emit('props-form-error', e);
237
248
  };
238
249
 
250
+ const layerNodeDblclickHandler = (event: MouseEvent, data: TreeNodeData) => {
251
+ emit('layer-node-dblclick', event, data);
252
+ };
253
+
239
254
  defineExpose(services);
240
255
  </script>
@@ -56,6 +56,7 @@ const emit = defineEmits<{
56
56
  'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
57
57
  'node-mouseenter': [event: MouseEvent, data: TreeNodeData];
58
58
  'node-click': [event: MouseEvent, data: TreeNodeData];
59
+ 'node-dblclick': [event: MouseEvent, data: TreeNodeData];
59
60
  }>();
60
61
 
61
62
  provide('treeEmit', emit);
@@ -25,7 +25,7 @@
25
25
  @click="expandHandler"
26
26
  ></MIcon>
27
27
 
28
- <div class="tree-node-content" @click="nodeClickHandler">
28
+ <div class="tree-node-content" @click="nodeClickHandler" @dblclick="nodeDblclickHandler">
29
29
  <slot name="tree-node-content" :data="data">
30
30
  <div class="tree-node-label">
31
31
  <slot name="tree-node-label" :data="data">{{ `${data.name} (${data.id})` }}</slot>
@@ -89,6 +89,7 @@ const emit = defineEmits<{
89
89
  'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
90
90
  'node-mouseenter': [event: MouseEvent, data: TreeNodeData];
91
91
  'node-click': [event: MouseEvent, data: TreeNodeData];
92
+ 'node-dblclick': [event: MouseEvent, data: TreeNodeData];
92
93
  }>();
93
94
 
94
95
  const treeEmit = inject<typeof emit>('treeEmit');
@@ -156,4 +157,8 @@ const expandHandler = () => {
156
157
  const nodeClickHandler = (event: MouseEvent) => {
157
158
  treeEmit?.('node-click', event, props.data);
158
159
  };
160
+
161
+ const nodeDblclickHandler = (event: MouseEvent) => {
162
+ treeEmit?.('node-dblclick', event, props.data);
163
+ };
159
164
  </script>