@tmagic/editor 1.8.0-beta.28 → 1.8.0-beta.29

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.
@@ -225,8 +225,10 @@
225
225
  var unmasked = true;
226
226
  } catch (e) {}
227
227
  var result = nativeObjectToString$1.call(value);
228
- if (unmasked) if (isOwn) value[symToStringTag$1] = tag;
229
- else delete value[symToStringTag$1];
228
+ if (unmasked) {
229
+ if (isOwn) value[symToStringTag$1] = tag;
230
+ else delete value[symToStringTag$1];
231
+ }
230
232
  return result;
231
233
  }
232
234
  var objectProto$4, hasOwnProperty$14, nativeObjectToString$1, symToStringTag$1;
@@ -379,7 +381,7 @@
379
381
  init__arrayMap();
380
382
  init_isArray();
381
383
  init_isSymbol();
382
- INFINITY$1 = Infinity;
384
+ INFINITY$1 = 1 / 0;
383
385
  symbolProto$2 = Symbol$1 ? Symbol$1.prototype : void 0;
384
386
  symbolToString = symbolProto$2 ? symbolProto$2.toString : void 0;
385
387
  }));
@@ -2240,7 +2242,7 @@
2240
2242
  var INFINITY;
2241
2243
  var init__toKey = __esmMin((() => {
2242
2244
  init_isSymbol();
2243
- INFINITY = Infinity;
2245
+ INFINITY = 1 / 0;
2244
2246
  }));
2245
2247
  //#endregion
2246
2248
  //#region node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_baseGet.js
@@ -2350,9 +2352,10 @@
2350
2352
  result || (result = []);
2351
2353
  while (++index < length) {
2352
2354
  var value = array[index];
2353
- if (depth > 0 && predicate(value)) if (depth > 1) baseFlatten(value, depth - 1, predicate, isStrict, result);
2354
- else arrayPush(result, value);
2355
- else if (!isStrict) result[result.length] = value;
2355
+ if (depth > 0 && predicate(value)) {
2356
+ if (depth > 1) baseFlatten(value, depth - 1, predicate, isStrict, result);
2357
+ else arrayPush(result, value);
2358
+ } else if (!isStrict) result[result.length] = value;
2356
2359
  }
2357
2360
  return result;
2358
2361
  }
@@ -4314,16 +4317,17 @@
4314
4317
  if (isCommon) {
4315
4318
  var isArr = isArray(srcValue), isBuff = !isArr && isBuffer(srcValue), isTyped = !isArr && !isBuff && isTypedArray(srcValue);
4316
4319
  newValue = srcValue;
4317
- if (isArr || isBuff || isTyped) if (isArray(objValue)) newValue = objValue;
4318
- else if (isArrayLikeObject(objValue)) newValue = copyArray(objValue);
4319
- else if (isBuff) {
4320
- isCommon = false;
4321
- newValue = cloneBuffer(srcValue, true);
4322
- } else if (isTyped) {
4323
- isCommon = false;
4324
- newValue = cloneTypedArray(srcValue, true);
4325
- } else newValue = [];
4326
- else if (isPlainObject$1(srcValue) || isArguments(srcValue)) {
4320
+ if (isArr || isBuff || isTyped) {
4321
+ if (isArray(objValue)) newValue = objValue;
4322
+ else if (isArrayLikeObject(objValue)) newValue = copyArray(objValue);
4323
+ else if (isBuff) {
4324
+ isCommon = false;
4325
+ newValue = cloneBuffer(srcValue, true);
4326
+ } else if (isTyped) {
4327
+ isCommon = false;
4328
+ newValue = cloneTypedArray(srcValue, true);
4329
+ } else newValue = [];
4330
+ } else if (isPlainObject$1(srcValue) || isArguments(srcValue)) {
4327
4331
  newValue = objValue;
4328
4332
  if (isArguments(objValue)) newValue = toPlainObject(objValue);
4329
4333
  else if (!isObject(objValue) || isFunction(objValue)) newValue = initCloneObject(srcValue);
@@ -4797,7 +4801,7 @@
4797
4801
  init__Set();
4798
4802
  init_noop();
4799
4803
  init__setToArray();
4800
- createSet = !(Set$1 && 1 / setToArray(new Set$1([, -0]))[1] == Infinity) ? noop : function(values) {
4804
+ createSet = !(Set$1 && 1 / setToArray(new Set$1([, -0]))[1] == 1 / 0) ? noop : function(values) {
4801
4805
  return new Set$1(values);
4802
4806
  };
4803
4807
  }));
@@ -5022,8 +5026,10 @@
5022
5026
  return;
5023
5027
  }
5024
5028
  let codeContent = "";
5025
- if (codeBlock.content) if (typeof codeBlock.content !== "string") codeContent = codeBlock.content.toString();
5026
- else codeContent = codeBlock.content;
5029
+ if (codeBlock.content) {
5030
+ if (typeof codeBlock.content !== "string") codeContent = codeBlock.content.toString();
5031
+ else codeContent = codeBlock.content;
5032
+ }
5027
5033
  codeConfig.value = {
5028
5034
  ...cloneDeep$1(codeBlock),
5029
5035
  content: codeContent
@@ -6953,8 +6959,10 @@
6953
6959
  const secondaryInvalid = isInvalid(secondaryValue);
6954
6960
  if (primaryInvalid && !secondaryInvalid) delete style[primary];
6955
6961
  else if (secondaryInvalid && !primaryInvalid) delete style[secondary];
6956
- else if (primaryInvalid && secondaryInvalid) if (secondaryValue === 0 && primaryValue !== 0) delete style[primary];
6957
- else delete style[secondary];
6962
+ else if (primaryInvalid && secondaryInvalid) {
6963
+ if (secondaryValue === 0 && primaryValue !== 0) delete style[primary];
6964
+ else delete style[secondary];
6965
+ }
6958
6966
  };
6959
6967
  getInitPositionStyle = (style = {}, layout) => {
6960
6968
  if (layout === Layout.ABSOLUTE) {
@@ -7796,13 +7804,15 @@
7796
7804
  }
7797
7805
  await Promise.all(nodes.map((node) => this.doRemove(node, options)));
7798
7806
  this.removeInvalidNodesBySubtree(nodes);
7799
- if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
7800
- diff: removedItems,
7801
- pageData: pageForOp,
7802
- historyDescription,
7803
- source: historySource
7804
- });
7805
- else this.selectionBeforeOp = null;
7807
+ if (removedItems.length > 0 && pageForOp) {
7808
+ if (!doNotPushHistory) this.pushOpHistory("remove", {
7809
+ diff: removedItems,
7810
+ pageData: pageForOp,
7811
+ historyDescription,
7812
+ source: historySource
7813
+ });
7814
+ else this.selectionBeforeOp = null;
7815
+ }
7806
7816
  this.emit("remove", nodes);
7807
7817
  this.emit("change", {
7808
7818
  type: "remove",
@@ -7882,22 +7892,24 @@
7882
7892
  }));
7883
7893
  this.applyInvalidInfo(config, invalidInfo);
7884
7894
  if (updateData[0].oldNode?.type !== _tmagic_core.NodeType.ROOT) {
7885
- if (this.get("nodes").length) if (!doNotPushHistory) {
7886
- const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
7887
- this.pushOpHistory("update", {
7888
- diff: buildUpdateDiff(updateData.map((d) => ({
7889
- oldNode: cloneDeep$1(d.oldNode),
7890
- newNode: cloneDeep$1(d.newNode),
7891
- changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
7892
- }))),
7893
- pageData: {
7894
- name: pageForOp?.name || "",
7895
- id: pageForOp.id
7896
- },
7897
- historyDescription,
7898
- source: historySource
7899
- });
7900
- } else this.selectionBeforeOp = null;
7895
+ if (this.get("nodes").length) {
7896
+ if (!doNotPushHistory) {
7897
+ const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
7898
+ this.pushOpHistory("update", {
7899
+ diff: buildUpdateDiff(updateData.map((d) => ({
7900
+ oldNode: cloneDeep$1(d.oldNode),
7901
+ newNode: cloneDeep$1(d.newNode),
7902
+ changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
7903
+ }))),
7904
+ pageData: {
7905
+ name: pageForOp?.name || "",
7906
+ id: pageForOp.id
7907
+ },
7908
+ historyDescription,
7909
+ source: historySource
7910
+ });
7911
+ } else this.selectionBeforeOp = null;
7912
+ }
7901
7913
  }
7902
7914
  this.emit("update", updateData);
7903
7915
  this.emit("change", {
@@ -8025,8 +8037,10 @@
8025
8037
  historyDescription,
8026
8038
  historySource
8027
8039
  });
8028
- if (!doNotSelect) if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
8029
- else await stage?.select(newNodes[0].id);
8040
+ if (!doNotSelect) {
8041
+ if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
8042
+ else await stage?.select(newNodes[0].id);
8043
+ }
8030
8044
  return newNode;
8031
8045
  }
8032
8046
  /**
@@ -12265,9 +12279,10 @@
12265
12279
  };
12266
12280
  const toString = (v, language) => {
12267
12281
  let value;
12268
- if (typeof v !== "string") if (language === "json") value = JSON.stringify(v, null, 2);
12269
- else value = serializeConfig(v);
12270
- else value = v;
12282
+ if (typeof v !== "string") {
12283
+ if (language === "json") value = JSON.stringify(v, null, 2);
12284
+ else value = serializeConfig(v);
12285
+ } else value = v;
12271
12286
  if (language === "javascript" && value.startsWith("{") && value.endsWith("}")) value = `(${value})`;
12272
12287
  return value;
12273
12288
  };
@@ -15096,13 +15111,14 @@
15096
15111
  const historySource = eventData ? "props" : "code";
15097
15112
  const replace = historySource === "code";
15098
15113
  let newValue;
15099
- if (replace) if (source === "style") newValue = {
15100
- ...values.value,
15101
- id: v.id || values.value.id,
15102
- style: { ...v.style || {} }
15103
- };
15104
- else newValue = { ...v };
15105
- else {
15114
+ if (replace) {
15115
+ if (source === "style") newValue = {
15116
+ ...values.value,
15117
+ id: v.id || values.value.id,
15118
+ style: { ...v.style || {} }
15119
+ };
15120
+ else newValue = { ...v };
15121
+ } else {
15106
15122
  newValue = {
15107
15123
  ...v,
15108
15124
  style: {}
@@ -17456,16 +17472,17 @@
17456
17472
  let targetParent = targetInfo.parent;
17457
17473
  if (!targetParent || !targetNode) return;
17458
17474
  let targetIndex = -1;
17459
- if (Array.isArray(targetNode.items) && dragState.dropType === "inner") if (dragState.redirectedTargetId !== void 0) {
17460
- const redirectedNode = editorService.getNodeInfo(dragState.redirectedTargetId, false).node;
17461
- if (!redirectedNode || !Array.isArray(redirectedNode.items)) return;
17462
- targetParent = redirectedNode;
17463
- targetIndex = redirectedNode.items.length;
17464
- } else {
17465
- targetIndex = targetNode.items.length;
17466
- targetParent = targetNode;
17467
- }
17468
- else targetIndex = getNodeIndex(dragState.dragOverNodeId, targetParent);
17475
+ if (Array.isArray(targetNode.items) && dragState.dropType === "inner") {
17476
+ if (dragState.redirectedTargetId !== void 0) {
17477
+ const redirectedNode = editorService.getNodeInfo(dragState.redirectedTargetId, false).node;
17478
+ if (!redirectedNode || !Array.isArray(redirectedNode.items)) return;
17479
+ targetParent = redirectedNode;
17480
+ targetIndex = redirectedNode.items.length;
17481
+ } else {
17482
+ targetIndex = targetNode.items.length;
17483
+ targetParent = targetNode;
17484
+ }
17485
+ } else targetIndex = getNodeIndex(dragState.dragOverNodeId, targetParent);
17469
17486
  if (dragState.dropType === "after") targetIndex += 1;
17470
17487
  const selectedNodes = editorService.get("nodes");
17471
17488
  if (selectedNodes.find((n) => `${n.id}` === `${node.id}`)) editorService.dragTo(selectedNodes, targetParent, targetIndex);
@@ -18299,9 +18316,10 @@
18299
18316
  return props.scrollSize * ratio;
18300
18317
  };
18301
18318
  const scrollBy = (delta) => {
18302
- if (delta < 0) if (props.pos <= 0) emit("scroll", 0);
18303
- else emit("scroll", -Math.min(-delta, props.pos));
18304
- else {
18319
+ if (delta < 0) {
18320
+ if (props.pos <= 0) emit("scroll", 0);
18321
+ else emit("scroll", -Math.min(-delta, props.pos));
18322
+ } else {
18305
18323
  const leftPos = props.size - (thumbSize.value + thumbPos.value);
18306
18324
  if (leftPos <= 0) emit("scroll", 0);
18307
18325
  else emit("scroll", Math.min(delta, leftPos));
@@ -18905,11 +18923,44 @@
18905
18923
  height: contentRect.height
18906
18924
  });
18907
18925
  });
18926
+ const parseDSL = getEditorConfig("parseDSL");
18927
+ /**
18928
+ * 本次拖拽是否由编辑器文档内部发起
18929
+ *
18930
+ * drop 的 text/json 里可能带函数(组件配置的事件、钩子等),还原只能交给 parseDSL,
18931
+ * 而 parseDSL 的默认实现是 eval;HTML 拖放又允许其他源的页面在 DataTransfer 中投递
18932
+ * 自定义 MIME 数据,跨源页面只要诱导用户拖拽一次,就能让 eval 执行任意脚本。
18933
+ *
18934
+ * 跨源页面既不会在本文档触发 dragstart,也无法往本文档创建的 DataTransfer 中写数据,
18935
+ * 因此只有起源于本文档的拖拽才交给 parseDSL 还原。
18936
+ * 同源拖拽源写入的 text/json 由业务保证可信。
18937
+ */
18938
+ let isInternalDrag = false;
18939
+ let internalDragSession = 0;
18940
+ let clearInternalDragTimer;
18941
+ const documentDragStartHandler = () => {
18942
+ internalDragSession += 1;
18943
+ isInternalDrag = true;
18944
+ if (clearInternalDragTimer !== void 0) {
18945
+ globalThis.clearTimeout(clearInternalDragTimer);
18946
+ clearInternalDragTimer = void 0;
18947
+ }
18948
+ };
18949
+ const documentDragEndHandler = () => {
18950
+ const session = internalDragSession;
18951
+ if (clearInternalDragTimer !== void 0) globalThis.clearTimeout(clearInternalDragTimer);
18952
+ clearInternalDragTimer = globalThis.setTimeout(() => {
18953
+ clearInternalDragTimer = void 0;
18954
+ if (session === internalDragSession) isInternalDrag = false;
18955
+ }, 0);
18956
+ };
18908
18957
  (0, vue.onMounted)(() => {
18909
18958
  if (stageWrapRef.value?.container) {
18910
18959
  resizeObserver.observe(stageWrapRef.value.container);
18911
18960
  keybindingService.registerEl(KeyBindingContainerKey.STAGE, stageWrapRef.value.container);
18912
18961
  }
18962
+ globalThis.document.addEventListener("dragstart", documentDragStartHandler, true);
18963
+ globalThis.document.addEventListener("dragend", documentDragEndHandler, true);
18913
18964
  });
18914
18965
  (0, vue.onBeforeUnmount)(() => {
18915
18966
  stage?.destroy();
@@ -18917,8 +18968,13 @@
18917
18968
  resizeObserver.disconnect();
18918
18969
  editorService.set("stage", null);
18919
18970
  keybindingService.unregisterEl("stage");
18971
+ if (clearInternalDragTimer !== void 0) {
18972
+ globalThis.clearTimeout(clearInternalDragTimer);
18973
+ clearInternalDragTimer = void 0;
18974
+ }
18975
+ globalThis.document.removeEventListener("dragstart", documentDragStartHandler, true);
18976
+ globalThis.document.removeEventListener("dragend", documentDragEndHandler, true);
18920
18977
  });
18921
- const parseDSL = getEditorConfig("parseDSL");
18922
18978
  const contextmenuHandler = (e) => {
18923
18979
  e.preventDefault();
18924
18980
  menuRef.value?.show(e);
@@ -18929,11 +18985,19 @@
18929
18985
  e.dataTransfer.dropEffect = "move";
18930
18986
  };
18931
18987
  const dropHandler = async (e) => {
18988
+ const allowed = isInternalDrag;
18989
+ isInternalDrag = false;
18932
18990
  if (!e.dataTransfer) return;
18933
18991
  const data = e.dataTransfer.getData("text/json");
18934
- if (!data) return;
18935
- const config = parseDSL(`(${data})`);
18936
- if (!config || config.dragType !== DragType.COMPONENT_LIST) return;
18992
+ if (!data || !allowed) return;
18993
+ let config;
18994
+ try {
18995
+ config = parseDSL(`(${data})`);
18996
+ } catch {
18997
+ return;
18998
+ }
18999
+ if (!config || config.dragType !== DragType.COMPONENT_LIST || !config.data) return;
19000
+ const dragData = config.data;
18937
19001
  e.preventDefault();
18938
19002
  const doc = stage?.renderer?.contentWindow?.document;
18939
19003
  const parentEl = doc?.querySelector(`.${props.stageOptions?.containerHighlightClassName}`);
@@ -18955,7 +19019,7 @@
18955
19019
  const layout = await editorService.getLayout(parent);
18956
19020
  const containerRect = stageContainerEl.value.getBoundingClientRect();
18957
19021
  const { scrollTop, scrollLeft } = stage.mask;
18958
- const { style = {} } = config.data;
19022
+ const { style = {} } = dragData;
18959
19023
  let top = 0;
18960
19024
  let left = 0;
18961
19025
  let position = "relative";
@@ -18973,14 +19037,14 @@
18973
19037
  top = top - parentTop * zoom.value;
18974
19038
  }
18975
19039
  }
18976
- config.data.style = {
19040
+ dragData.style = {
18977
19041
  ...style,
18978
19042
  position,
18979
19043
  top: (0, _tmagic_utils.calcValueByFontsize)(doc, top / zoom.value),
18980
19044
  left: (0, _tmagic_utils.calcValueByFontsize)(doc, left / zoom.value)
18981
19045
  };
18982
- config.data.inputEvent = e;
18983
- editorService.add(config.data, parent, { historySource: "component-panel" });
19046
+ dragData.inputEvent = e;
19047
+ editorService.add(dragData, parent, { historySource: "component-panel" });
18984
19048
  }
18985
19049
  };
18986
19050
  return (_ctx, _cache) => {
@@ -21858,8 +21922,10 @@
21858
21922
  let editIndex = -1;
21859
21923
  const editMethod = (method, index) => {
21860
21924
  let codeContent = "({ params, dataSource, app }) => {\n // place your code here\n}";
21861
- if (method.content) if (typeof method.content !== "string") codeContent = method.content.toString();
21862
- else codeContent = method.content;
21925
+ if (method.content) {
21926
+ if (typeof method.content !== "string") codeContent = method.content.toString();
21927
+ else codeContent = method.content;
21928
+ }
21863
21929
  codeConfig.value = {
21864
21930
  ...cloneDeep$1(method),
21865
21931
  content: codeContent
@@ -22562,7 +22628,7 @@
22562
22628
  notEditable: { type: [Boolean, Function] },
22563
22629
  dataSourceId: {}
22564
22630
  }, {
22565
- "modelValue": { default: [] },
22631
+ "modelValue": { default: () => [] },
22566
22632
  "modelModifiers": {}
22567
22633
  }),
22568
22634
  emits: /*@__PURE__*/ (0, vue.mergeModels)(["change"], ["update:modelValue"]),
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0-beta.28",
2
+ "version": "1.8.0-beta.29",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -61,27 +61,27 @@
61
61
  "keycon": "^1.4.0",
62
62
  "lodash-es": "^4.18.1",
63
63
  "moveable": "^0.53.0",
64
- "serialize-javascript": "^7.0.7",
64
+ "serialize-javascript": "^7.1.1",
65
65
  "sortablejs": "^1.15.7",
66
- "@tmagic/design": "1.8.0-beta.28",
67
- "@tmagic/stage": "1.8.0-beta.28",
68
- "@tmagic/utils": "1.8.0-beta.28",
69
- "@tmagic/form": "1.8.0-beta.28",
70
- "@tmagic/table": "1.8.0-beta.28"
66
+ "@tmagic/design": "1.8.0-beta.29",
67
+ "@tmagic/form": "1.8.0-beta.29",
68
+ "@tmagic/stage": "1.8.0-beta.29",
69
+ "@tmagic/table": "1.8.0-beta.29",
70
+ "@tmagic/utils": "1.8.0-beta.29"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@types/events": "^3.0.3",
74
74
  "@types/lodash-es": "^4.17.12",
75
75
  "@types/serialize-javascript": "^5.0.4",
76
76
  "@types/sortablejs": "^1.15.9",
77
- "@vue/test-utils": "^2.4.11"
77
+ "@vue/test-utils": "^2.5.1"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "monaco-editor": "^0.55.1 ",
81
81
  "type-fest": "^5.2.0",
82
82
  "typescript": "^6.0.3",
83
- "vue": "^3.5.41",
84
- "@tmagic/core": "1.8.0-beta.28"
83
+ "vue": "^3.5.43",
84
+ "@tmagic/core": "1.8.0-beta.29"
85
85
  },
86
86
  "peerDependenciesMeta": {
87
87
  "typescript": {
@@ -118,7 +118,7 @@ const emit = defineEmits<{
118
118
  change: [v: string[]];
119
119
  }>();
120
120
 
121
- const modelValue = defineModel<string[] | any>('modelValue', { default: [] });
121
+ const modelValue = defineModel<string[] | any>('modelValue', { default: () => [] });
122
122
 
123
123
  const optionComponent = getDesignConfig('components')?.option;
124
124
 
@@ -56,7 +56,14 @@ import { calcValueByFontsize, getIdFromEl } from '@tmagic/utils';
56
56
  import ScrollViewer from '@editor/components/ScrollViewer.vue';
57
57
  import { useServices } from '@editor/hooks';
58
58
  import { useStage } from '@editor/hooks/use-stage';
59
- import type { CustomContentMenuFunction, MenuButton, MenuComponent, StageOptions, StageSlots } from '@editor/type';
59
+ import type {
60
+ AddMNode,
61
+ CustomContentMenuFunction,
62
+ MenuButton,
63
+ MenuComponent,
64
+ StageOptions,
65
+ StageSlots,
66
+ } from '@editor/type';
60
67
  import { DragType, Layout } from '@editor/type';
61
68
  import { getEditorConfig } from '@editor/utils/config';
62
69
  import { KeyBindingContainerKey } from '@editor/utils/keybinding-config';
@@ -270,11 +277,56 @@ const resizeObserver = new globalThis.ResizeObserver((entries) => {
270
277
  }
271
278
  });
272
279
 
280
+ const parseDSL = getEditorConfig('parseDSL');
281
+
282
+ /**
283
+ * 本次拖拽是否由编辑器文档内部发起
284
+ *
285
+ * drop 的 text/json 里可能带函数(组件配置的事件、钩子等),还原只能交给 parseDSL,
286
+ * 而 parseDSL 的默认实现是 eval;HTML 拖放又允许其他源的页面在 DataTransfer 中投递
287
+ * 自定义 MIME 数据,跨源页面只要诱导用户拖拽一次,就能让 eval 执行任意脚本。
288
+ *
289
+ * 跨源页面既不会在本文档触发 dragstart,也无法往本文档创建的 DataTransfer 中写数据,
290
+ * 因此只有起源于本文档的拖拽才交给 parseDSL 还原。
291
+ * 同源拖拽源写入的 text/json 由业务保证可信。
292
+ */
293
+ let isInternalDrag = false;
294
+ let internalDragSession = 0;
295
+ let clearInternalDragTimer: ReturnType<typeof setTimeout> | undefined;
296
+
297
+ const documentDragStartHandler = () => {
298
+ internalDragSession += 1;
299
+ isInternalDrag = true;
300
+ if (clearInternalDragTimer !== undefined) {
301
+ globalThis.clearTimeout(clearInternalDragTimer);
302
+ clearInternalDragTimer = undefined;
303
+ }
304
+ };
305
+
306
+ const documentDragEndHandler = () => {
307
+ // WebKit 可能先 dragend 再 drop;推迟到下一个宏任务再清标志,
308
+ // 让同一次拖拽的 drop 仍能消费。session 防止误清掉下一次 dragstart。
309
+ const session = internalDragSession;
310
+ if (clearInternalDragTimer !== undefined) {
311
+ globalThis.clearTimeout(clearInternalDragTimer);
312
+ }
313
+ clearInternalDragTimer = globalThis.setTimeout(() => {
314
+ clearInternalDragTimer = undefined;
315
+ if (session === internalDragSession) {
316
+ isInternalDrag = false;
317
+ }
318
+ }, 0);
319
+ };
320
+
273
321
  onMounted(() => {
274
322
  if (stageWrapRef.value?.container) {
275
323
  resizeObserver.observe(stageWrapRef.value.container);
276
324
  keybindingService.registerEl(KeyBindingContainerKey.STAGE, stageWrapRef.value.container);
277
325
  }
326
+
327
+ // 用捕获阶段监听,确保拖拽源自身的 dragstart 无论是否阻止冒泡都能被记录
328
+ globalThis.document.addEventListener('dragstart', documentDragStartHandler, true);
329
+ globalThis.document.addEventListener('dragend', documentDragEndHandler, true);
278
330
  });
279
331
 
280
332
  onBeforeUnmount(() => {
@@ -283,9 +335,15 @@ onBeforeUnmount(() => {
283
335
  resizeObserver.disconnect();
284
336
  editorService.set('stage', null);
285
337
  keybindingService.unregisterEl('stage');
286
- });
287
338
 
288
- const parseDSL = getEditorConfig('parseDSL');
339
+ if (clearInternalDragTimer !== undefined) {
340
+ globalThis.clearTimeout(clearInternalDragTimer);
341
+ clearInternalDragTimer = undefined;
342
+ }
343
+
344
+ globalThis.document.removeEventListener('dragstart', documentDragStartHandler, true);
345
+ globalThis.document.removeEventListener('dragend', documentDragEndHandler, true);
346
+ });
289
347
 
290
348
  const contextmenuHandler = (e: MouseEvent) => {
291
349
  e.preventDefault();
@@ -299,15 +357,27 @@ const dragoverHandler = (e: DragEvent) => {
299
357
  };
300
358
 
301
359
  const dropHandler = async (e: DragEvent) => {
360
+ // 画布上的任意 drop 都消费本次内部拖拽标记(含空 text/json),避免残留到下一次 drop
361
+ const allowed = isInternalDrag;
362
+ isInternalDrag = false;
363
+
302
364
  if (!e.dataTransfer) return;
303
365
 
304
366
  const data = e.dataTransfer.getData('text/json');
305
367
 
306
- if (!data) return;
368
+ // 外部源投递的拖拽数据不可信,不能交给 parseDSL(默认实现为 eval)
369
+ if (!data || !allowed) return;
370
+
371
+ let config: { dragType?: string; data?: AddMNode } | undefined;
372
+ try {
373
+ config = parseDSL(`(${data})`);
374
+ } catch {
375
+ return;
376
+ }
307
377
 
308
- const config = parseDSL(`(${data})`);
378
+ if (!config || config.dragType !== DragType.COMPONENT_LIST || !config.data) return;
309
379
 
310
- if (!config || config.dragType !== DragType.COMPONENT_LIST) return;
380
+ const dragData = config.data;
311
381
 
312
382
  e.preventDefault();
313
383
 
@@ -349,7 +419,7 @@ const dropHandler = async (e: DragEvent) => {
349
419
 
350
420
  const containerRect = stageContainerEl.value.getBoundingClientRect();
351
421
  const { scrollTop, scrollLeft } = stage.mask!;
352
- const { style = {} } = config.data;
422
+ const { style = {} } = dragData;
353
423
 
354
424
  let top = 0;
355
425
  let left = 0;
@@ -371,16 +441,16 @@ const dropHandler = async (e: DragEvent) => {
371
441
  }
372
442
  }
373
443
 
374
- config.data.style = {
444
+ dragData.style = {
375
445
  ...style,
376
446
  position,
377
447
  top: calcValueByFontsize(doc, top / zoom.value),
378
448
  left: calcValueByFontsize(doc, left / zoom.value),
379
449
  };
380
450
 
381
- config.data.inputEvent = e;
451
+ dragData.inputEvent = e;
382
452
 
383
- editorService.add(config.data, parent, { historySource: 'component-panel' });
453
+ editorService.add(dragData, parent, { historySource: 'component-panel' });
384
454
  }
385
455
  };
386
456
  </script>
@@ -13,6 +13,5 @@ import { HeadlessFieldOptions } from "@tmagic/form/headless";
13
13
  * innerConfig 返回的 config / model / prop 与组件模板里传给内部容器的那一组保持一一对应,
14
14
  * 且配置本身与组件共用同一份工厂(`fields/configs/`)。
15
15
  */
16
- declare const editorFields: Record<string, HeadlessFieldOptions>;
17
- //#endregion
18
- export { editorFields };
16
+ export declare const editorFields: Record<string, HeadlessFieldOptions>;
17
+ //#endregion