@tmagic/editor 1.7.9 → 1.7.10

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 (47) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +3 -1
  2. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +17 -7
  3. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +38 -9
  4. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +3 -1
  5. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +38 -5
  6. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +3 -2
  7. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +5 -0
  8. package/dist/es/fields/StyleSetter/pro/Transform.js +5 -0
  9. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +54 -0
  10. package/dist/es/hooks/use-code-block-edit.js +3 -2
  11. package/dist/es/index.js +3 -3
  12. package/dist/es/initService.js +4 -4
  13. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +64 -1
  14. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -5
  15. package/dist/es/services/editor.js +191 -174
  16. package/dist/es/services/history.js +1 -9
  17. package/dist/es/type.js +28 -1
  18. package/dist/es/utils/dep/worker.js +1 -1
  19. package/dist/es/utils/editor-history.js +100 -0
  20. package/dist/es/utils/editor.js +178 -4
  21. package/dist/es/utils/props.js +4 -1
  22. package/dist/es/utils/undo-redo.js +7 -4
  23. package/dist/tmagic-editor.umd.cjs +744 -234
  24. package/package.json +8 -8
  25. package/src/Editor.vue +1 -0
  26. package/src/components/CodeBlockEditor.vue +1 -1
  27. package/src/components/CodeParams.vue +23 -7
  28. package/src/editorProps.ts +2 -0
  29. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +39 -6
  30. package/src/fields/DataSourceFieldSelect/Index.vue +1 -0
  31. package/src/fields/DataSourceMethodSelect.vue +54 -13
  32. package/src/fields/DataSourceMethods.vue +9 -5
  33. package/src/fields/StyleSetter/Index.vue +5 -1
  34. package/src/fields/StyleSetter/pro/Transform.vue +54 -0
  35. package/src/fields/StyleSetter/pro/index.ts +1 -0
  36. package/src/hooks/use-code-block-edit.ts +8 -4
  37. package/src/initService.ts +6 -6
  38. package/src/layouts/workspace/viewer/Stage.vue +89 -1
  39. package/src/layouts/workspace/viewer/StageOverlay.vue +1 -6
  40. package/src/services/editor.ts +231 -270
  41. package/src/services/history.ts +1 -9
  42. package/src/type.ts +65 -5
  43. package/src/utils/editor-history.ts +138 -0
  44. package/src/utils/editor.ts +249 -4
  45. package/src/utils/props.ts +4 -0
  46. package/src/utils/undo-redo.ts +7 -6
  47. package/types/index.d.ts +179 -53
@@ -1,43 +1,17 @@
1
- import { LayerOffset, Layout } from "../type.js";
1
+ import { Layout, canUsePluginMethods } from "../type.js";
2
2
  import BaseService_default from "./BaseService.js";
3
3
  import props_default from "./props.js";
4
4
  import history_default from "./history.js";
5
5
  import storage_default, { Protocol } from "./storage.js";
6
- import { COPY_STORAGE_KEY, Fixed2Other, change2Fixed, fixNodePosition, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, moveItemsInContainer, setChildrenLayout, setLayout } from "../utils/editor.js";
6
+ import { COPY_STORAGE_KEY, calcAlignCenterStyle, calcLayerTargetIndex, calcMoveStyle, classifyDragSources, collectRelatedNodes, editorNodeMergeCustomizer, fixNodePosition, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, moveItemsInContainer, resolveSelectedNode, setChildrenLayout, setLayout, toggleFixedPosition } from "../utils/editor.js";
7
+ import { applyHistoryAddOp, applyHistoryRemoveOp, applyHistoryUpdateOp } from "../utils/editor-history.js";
7
8
  import { beforePaste, getAddParent } from "../utils/operator.js";
8
9
  import { isFixed } from "@tmagic/stage";
9
- import { calcValueByFontsize, getElById, getNodeInfo, getNodePath, isNumber, isPage, isPageFragment, isPop } from "@tmagic/utils";
10
- import { NodeType, Target, Watcher } from "@tmagic/core";
10
+ import { getNodeInfo, getNodePath, isPage, isPageFragment } from "@tmagic/utils";
11
+ import { NodeType } from "@tmagic/core";
11
12
  import { reactive, toRaw } from "vue";
12
- import { cloneDeep, get, isObject, mergeWith, uniq } from "lodash-es";
13
+ import { cloneDeep, isObject, mergeWith, uniq } from "lodash-es";
13
14
  //#region packages/editor/src/services/editor.ts
14
- var canUsePluginMethods = {
15
- async: [
16
- "getLayout",
17
- "highlight",
18
- "select",
19
- "multiSelect",
20
- "doAdd",
21
- "add",
22
- "doRemove",
23
- "remove",
24
- "doUpdate",
25
- "update",
26
- "sort",
27
- "copy",
28
- "paste",
29
- "doPaste",
30
- "doAlignCenter",
31
- "alignCenter",
32
- "moveLayer",
33
- "moveToContainer",
34
- "dragTo",
35
- "undo",
36
- "redo",
37
- "move"
38
- ],
39
- sync: []
40
- };
41
15
  var Editor = class extends BaseService_default {
42
16
  state = reactive({
43
17
  root: null,
@@ -54,6 +28,7 @@ var Editor = class extends BaseService_default {
54
28
  disabledMultiSelect: false
55
29
  });
56
30
  isHistoryStateChange = false;
31
+ selectionBeforeOp = null;
57
32
  constructor() {
58
33
  super(canUsePluginMethods.async.map((methodName) => ({
59
34
  name: methodName,
@@ -249,6 +224,7 @@ var Editor = class extends BaseService_default {
249
224
  * @returns 添加后的节点
250
225
  */
251
226
  async add(addNode, parent) {
227
+ this.captureSelectionBeforeOp();
252
228
  const stage = this.get("stage");
253
229
  const addNodes = [];
254
230
  if (!Array.isArray(addNode)) {
@@ -273,7 +249,20 @@ var Editor = class extends BaseService_default {
273
249
  else if (isPageFragment(newNodes[0])) this.state.pageFragmentLength += 1;
274
250
  else stage?.select(newNodes[0].id);
275
251
  }
276
- if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) this.pushHistoryState();
252
+ if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
253
+ const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
254
+ this.pushOpHistory("add", {
255
+ nodes: newNodes.map((n) => cloneDeep(toRaw(n))),
256
+ parentId: (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id,
257
+ indexMap: Object.fromEntries(newNodes.map((n) => {
258
+ const p = this.getParentById(n.id, false);
259
+ return [n.id, p ? getNodeIndex(n.id, p) : -1];
260
+ }))
261
+ }, {
262
+ name: pageForOp?.name || "",
263
+ id: pageForOp.id
264
+ });
265
+ }
277
266
  this.emit("add", newNodes);
278
267
  return Array.isArray(addNode) ? newNodes : newNodes[0];
279
268
  }
@@ -322,9 +311,27 @@ var Editor = class extends BaseService_default {
322
311
  * @param {Object} node
323
312
  */
324
313
  async remove(nodeOrNodeList) {
314
+ this.captureSelectionBeforeOp();
325
315
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
316
+ const removedItems = [];
317
+ let pageForOp = null;
318
+ if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) for (const n of nodes) {
319
+ const { parent, node: curNode, page } = this.getNodeInfo(n.id, false);
320
+ if (parent && curNode) {
321
+ if (!pageForOp && page) pageForOp = {
322
+ name: page.name || "",
323
+ id: page.id
324
+ };
325
+ const idx = getNodeIndex(curNode.id, parent);
326
+ removedItems.push({
327
+ node: cloneDeep(toRaw(curNode)),
328
+ parentId: parent.id,
329
+ index: typeof idx === "number" ? idx : -1
330
+ });
331
+ }
332
+ }
326
333
  await Promise.all(nodes.map((node) => this.doRemove(node)));
327
- if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) this.pushHistoryState();
334
+ if (removedItems.length > 0 && pageForOp) this.pushOpHistory("remove", { removedItems }, pageForOp);
328
335
  this.emit("remove", nodes);
329
336
  }
330
337
  async doUpdate(config, { changeRecords = [] } = {}) {
@@ -334,12 +341,8 @@ var Editor = class extends BaseService_default {
334
341
  const info = this.getNodeInfo(config.id, false);
335
342
  if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
336
343
  const node = toRaw(info.node);
337
- let newConfig = await this.toggleFixedPosition(toRaw(config), node, root);
338
- newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue, key, object, source) => {
339
- if (typeof srcValue === "undefined" && Object.hasOwn(source, key)) return "";
340
- if (isObject(srcValue) && Array.isArray(objValue)) return srcValue;
341
- if (Array.isArray(srcValue)) return srcValue;
342
- });
344
+ let newConfig = await toggleFixedPosition(toRaw(config), node, root, this.getLayout);
345
+ newConfig = mergeWith(cloneDeep(node), newConfig, editorNodeMergeCustomizer);
343
346
  if (!newConfig.type) throw new Error("配置缺少type值");
344
347
  if (newConfig.type === NodeType.ROOT) {
345
348
  this.set("root", newConfig);
@@ -377,9 +380,23 @@ var Editor = class extends BaseService_default {
377
380
  * @returns 更新后的节点配置
378
381
  */
379
382
  async update(config, data = {}) {
383
+ this.captureSelectionBeforeOp();
380
384
  const nodes = Array.isArray(config) ? config : [config];
381
385
  const updateData = await Promise.all(nodes.map((node) => this.doUpdate(node, data)));
382
- if (updateData[0].oldNode?.type !== NodeType.ROOT) this.pushHistoryState();
386
+ if (updateData[0].oldNode?.type !== NodeType.ROOT) {
387
+ const curNodes = this.get("nodes");
388
+ if (!this.isHistoryStateChange && curNodes.length) {
389
+ const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
390
+ this.pushOpHistory("update", { updatedItems: updateData.map((d) => ({
391
+ oldNode: cloneDeep(d.oldNode),
392
+ newNode: cloneDeep(toRaw(d.newNode))
393
+ })) }, {
394
+ name: pageForOp?.name || "",
395
+ id: pageForOp.id
396
+ });
397
+ }
398
+ this.isHistoryStateChange = false;
399
+ }
383
400
  this.emit("update", updateData);
384
401
  return Array.isArray(config) ? updateData.map((item) => item.newNode) : updateData[0].newNode;
385
402
  }
@@ -390,6 +407,7 @@ var Editor = class extends BaseService_default {
390
407
  * @returns void
391
408
  */
392
409
  async sort(id1, id2) {
410
+ this.captureSelectionBeforeOp();
393
411
  const root = this.get("root");
394
412
  if (!root) throw new Error("root为空");
395
413
  const node = this.get("node");
@@ -407,8 +425,6 @@ var Editor = class extends BaseService_default {
407
425
  parentId: parent.id,
408
426
  root: cloneDeep(root)
409
427
  });
410
- this.addModifiedNodeId(parent.id);
411
- this.pushHistoryState();
412
428
  }
413
429
  /**
414
430
  * 将组件节点配置存储到localStorage中
@@ -425,23 +441,7 @@ var Editor = class extends BaseService_default {
425
441
  */
426
442
  copyWithRelated(config, collectorOptions) {
427
443
  const copyNodes = Array.isArray(config) ? config : [config];
428
- if (collectorOptions && typeof collectorOptions.isTarget === "function") {
429
- const customTarget = new Target({ ...collectorOptions });
430
- const coperWatcher = new Watcher();
431
- coperWatcher.addTarget(customTarget);
432
- coperWatcher.collect(copyNodes, {}, true, collectorOptions.type);
433
- Object.keys(customTarget.deps).forEach((nodeId) => {
434
- const node = this.getNodeById(nodeId);
435
- if (!node) return;
436
- customTarget.deps[nodeId].keys.forEach((key) => {
437
- const relateNodeId = get(node, key);
438
- if (!copyNodes.find((node) => node.id === relateNodeId)) {
439
- const relateNode = this.getNodeById(relateNodeId);
440
- if (relateNode) copyNodes.push(relateNode);
441
- }
442
- });
443
- });
444
- }
444
+ if (collectorOptions && typeof collectorOptions.isTarget === "function") collectRelatedNodes(copyNodes, collectorOptions, (id) => this.getNodeById(id));
445
445
  storage_default.setItem(COPY_STORAGE_KEY, copyNodes, { protocol: Protocol.OBJECT });
446
446
  }
447
447
  /**
@@ -472,20 +472,10 @@ var Editor = class extends BaseService_default {
472
472
  if (!parent) throw new Error("找不到父节点");
473
473
  const node = cloneDeep(toRaw(config));
474
474
  const layout = await this.getLayout(parent, node);
475
- if (layout === Layout.RELATIVE) return config;
476
- if (!node.style) return config;
477
475
  const doc = this.get("stage")?.renderer?.contentWindow?.document;
478
- if (doc) {
479
- const el = getElById()(doc, node.id);
480
- const parentEl = layout === Layout.FIXED ? doc.body : el?.offsetParent;
481
- if (parentEl && el) {
482
- node.style.left = calcValueByFontsize(doc, (parentEl.clientWidth - el.clientWidth) / 2);
483
- node.style.right = "";
484
- }
485
- } else if (parent.style && isNumber(parent.style?.width) && isNumber(node.style?.width)) {
486
- node.style.left = (parent.style.width - node.style.width) / 2;
487
- node.style.right = "";
488
- }
476
+ const newStyle = calcAlignCenterStyle(node, parent, layout, doc);
477
+ if (!newStyle) return config;
478
+ node.style = newStyle;
489
479
  return node;
490
480
  }
491
481
  /**
@@ -507,6 +497,7 @@ var Editor = class extends BaseService_default {
507
497
  * @param offset 偏移量
508
498
  */
509
499
  async moveLayer(offset) {
500
+ this.captureSelectionBeforeOp();
510
501
  const root = this.get("root");
511
502
  if (!root) throw new Error("root为空");
512
503
  const parent = this.get("parent");
@@ -516,11 +507,9 @@ var Editor = class extends BaseService_default {
516
507
  const brothers = parent.items || [];
517
508
  const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
518
509
  const isRelative = await this.getLayout(parent, node) === Layout.RELATIVE;
519
- let offsetIndex;
520
- if (offset === LayerOffset.TOP) offsetIndex = isRelative ? 0 : brothers.length;
521
- else if (offset === LayerOffset.BOTTOM) offsetIndex = isRelative ? brothers.length : 0;
522
- else offsetIndex = index + (isRelative ? -offset : offset);
510
+ const offsetIndex = calcLayerTargetIndex(index, offset, brothers.length, isRelative);
523
511
  if (offsetIndex > 0 && offsetIndex > brothers.length || offsetIndex < 0) return;
512
+ const oldParent = cloneDeep(toRaw(parent));
524
513
  brothers.splice(index, 1);
525
514
  brothers.splice(offsetIndex, 0, node);
526
515
  const grandparent = this.getParentById(parent.id);
@@ -530,7 +519,14 @@ var Editor = class extends BaseService_default {
530
519
  root: cloneDeep(root)
531
520
  });
532
521
  this.addModifiedNodeId(parent.id);
533
- this.pushHistoryState();
522
+ const pageForOp = this.getNodeInfo(node.id, false).page;
523
+ this.pushOpHistory("update", { updatedItems: [{
524
+ oldNode: oldParent,
525
+ newNode: cloneDeep(toRaw(parent))
526
+ }] }, {
527
+ name: pageForOp?.name || "",
528
+ id: pageForOp.id
529
+ });
534
530
  this.emit("move-layer", offset);
535
531
  }
536
532
  /**
@@ -539,11 +535,14 @@ var Editor = class extends BaseService_default {
539
535
  * @param targetId 容器ID
540
536
  */
541
537
  async moveToContainer(config, targetId) {
538
+ this.captureSelectionBeforeOp();
542
539
  const root = this.get("root");
543
- const { node, parent } = this.getNodeInfo(config.id, false);
540
+ const { node, parent, page: pageForOp } = this.getNodeInfo(config.id, false);
544
541
  const target = this.getNodeById(targetId, false);
545
542
  const stage = this.get("stage");
546
543
  if (root && node && parent && stage) {
544
+ const oldSourceParent = cloneDeep(toRaw(parent));
545
+ const oldTarget = cloneDeep(toRaw(target));
547
546
  const index = getNodeIndex(node.id, parent);
548
547
  parent.items?.splice(index, 1);
549
548
  await stage.remove({
@@ -568,36 +567,42 @@ var Editor = class extends BaseService_default {
568
567
  stage.select(newConfig.id);
569
568
  this.addModifiedNodeId(target.id);
570
569
  this.addModifiedNodeId(parent.id);
571
- this.pushHistoryState();
570
+ this.pushOpHistory("update", { updatedItems: [{
571
+ oldNode: oldSourceParent,
572
+ newNode: cloneDeep(toRaw(parent))
573
+ }, {
574
+ oldNode: oldTarget,
575
+ newNode: cloneDeep(toRaw(target))
576
+ }] }, {
577
+ name: pageForOp?.name || "",
578
+ id: pageForOp.id
579
+ });
572
580
  return newConfig;
573
581
  }
574
582
  }
575
583
  async dragTo(config, targetParent, targetIndex) {
584
+ this.captureSelectionBeforeOp();
576
585
  if (!targetParent || !Array.isArray(targetParent.items)) return;
577
586
  const configs = Array.isArray(config) ? config : [config];
578
- const sourceIndicesInTargetParent = [];
579
- const sourceOutTargetParent = [];
587
+ const beforeSnapshots = /* @__PURE__ */ new Map();
588
+ for (const cfg of configs) {
589
+ const { parent } = this.getNodeInfo(cfg.id, false);
590
+ if (parent && !beforeSnapshots.has(`${parent.id}`)) beforeSnapshots.set(`${parent.id}`, cloneDeep(toRaw(parent)));
591
+ }
592
+ if (!beforeSnapshots.has(`${targetParent.id}`)) beforeSnapshots.set(`${targetParent.id}`, cloneDeep(toRaw(targetParent)));
580
593
  const newLayout = await this.getLayout(targetParent);
581
- forConfigs: for (const config of configs) {
582
- const { parent, node: curNode } = this.getNodeInfo(config.id, false);
583
- if (!parent || !curNode) continue;
584
- const path = getNodePath(curNode.id, parent.items);
585
- for (const node of path) if (targetParent.id === node.id) continue forConfigs;
586
- const index = getNodeIndex(curNode.id, parent);
587
- if (parent.id === targetParent.id) {
588
- if (typeof index !== "number" || index === -1) return;
589
- sourceIndicesInTargetParent.push(index);
590
- } else {
591
- if (newLayout !== await this.getLayout(parent)) setLayout(config, newLayout);
592
- parent.items?.splice(index, 1);
593
- sourceOutTargetParent.push(config);
594
- this.addModifiedNodeId(parent.id);
595
- }
594
+ const { sameParentIndices, crossParentConfigs, aborted } = classifyDragSources(configs, targetParent, (id, raw) => this.getNodeInfo(id, raw));
595
+ if (aborted) return;
596
+ for (const { config: crossConfig, parent } of crossParentConfigs) {
597
+ if (newLayout !== await this.getLayout(parent)) setLayout(crossConfig, newLayout);
598
+ const index = getNodeIndex(crossConfig.id, parent);
599
+ parent.items?.splice(index, 1);
600
+ this.addModifiedNodeId(parent.id);
596
601
  }
597
- moveItemsInContainer(sourceIndicesInTargetParent, targetParent, targetIndex);
598
- sourceOutTargetParent.forEach((config, index) => {
599
- targetParent.items?.splice(targetIndex + index, 0, config);
600
- this.addModifiedNodeId(config.id);
602
+ moveItemsInContainer(sameParentIndices, targetParent, targetIndex);
603
+ crossParentConfigs.forEach(({ config: crossConfig }, index) => {
604
+ targetParent.items?.splice(targetIndex + index, 0, crossConfig);
605
+ this.addModifiedNodeId(crossConfig.id);
601
606
  });
602
607
  const page = this.get("page");
603
608
  const root = this.get("root");
@@ -607,7 +612,19 @@ var Editor = class extends BaseService_default {
607
612
  parentId: root.id,
608
613
  root: cloneDeep(root)
609
614
  });
610
- this.pushHistoryState();
615
+ const updatedItems = [];
616
+ for (const oldNode of beforeSnapshots.values()) {
617
+ const newNode = this.getNodeById(oldNode.id, false);
618
+ if (newNode) updatedItems.push({
619
+ oldNode,
620
+ newNode: cloneDeep(toRaw(newNode))
621
+ });
622
+ }
623
+ const pageForOp = this.getNodeInfo(configs[0].id, false).page;
624
+ this.pushOpHistory("update", { updatedItems }, {
625
+ name: pageForOp?.name || "",
626
+ id: pageForOp.id
627
+ });
611
628
  this.emit("drag-to", {
612
629
  targetIndex,
613
630
  configs,
@@ -616,56 +633,32 @@ var Editor = class extends BaseService_default {
616
633
  }
617
634
  /**
618
635
  * 撤销当前操作
619
- * @returns 上一次数据
636
+ * @returns 被撤销的操作
620
637
  */
621
638
  async undo() {
622
639
  const value = history_default.undo();
623
- await this.changeHistoryState(value);
640
+ if (value) await this.applyHistoryOp(value, true);
624
641
  return value;
625
642
  }
626
643
  /**
627
644
  * 恢复到下一步
628
- * @returns 下一步数据
645
+ * @returns 被恢复的操作
629
646
  */
630
647
  async redo() {
631
648
  const value = history_default.redo();
632
- await this.changeHistoryState(value);
649
+ if (value) await this.applyHistoryOp(value, false);
633
650
  return value;
634
651
  }
635
652
  async move(left, top) {
636
653
  const node = toRaw(this.get("node"));
637
654
  if (!node || isPage(node)) return;
638
- const { style, id, type } = node;
639
- if (!style || !["absolute", "fixed"].includes(style.position)) return;
640
- const update = (style) => this.update({
641
- id,
642
- type,
643
- style
655
+ const newStyle = calcMoveStyle(node.style || {}, left, top);
656
+ if (!newStyle) return;
657
+ await this.update({
658
+ id: node.id,
659
+ type: node.type,
660
+ style: newStyle
644
661
  });
645
- if (top) {
646
- if (isNumber(style.top)) update({
647
- ...style,
648
- top: Number(style.top) + Number(top),
649
- bottom: ""
650
- });
651
- else if (isNumber(style.bottom)) update({
652
- ...style,
653
- bottom: Number(style.bottom) - Number(top),
654
- top: ""
655
- });
656
- }
657
- if (left) {
658
- if (isNumber(style.left)) update({
659
- ...style,
660
- left: Number(style.left) + Number(left),
661
- right: ""
662
- });
663
- else if (isNumber(style.right)) update({
664
- ...style,
665
- right: Number(style.right) - Number(left),
666
- left: ""
667
- });
668
- }
669
662
  }
670
663
  resetState() {
671
664
  this.set("root", null);
@@ -701,50 +694,74 @@ var Editor = class extends BaseService_default {
701
694
  addModifiedNodeId(id) {
702
695
  if (!this.isHistoryStateChange) this.get("modifiedNodeIds").set(id, id);
703
696
  }
704
- pushHistoryState() {
705
- const curNode = cloneDeep(toRaw(this.get("node")));
706
- const page = this.get("page");
707
- if (!this.isHistoryStateChange && curNode && page) history_default.push({
708
- data: cloneDeep(toRaw(page)),
709
- modifiedNodeIds: this.get("modifiedNodeIds"),
710
- nodeId: curNode.id
711
- });
697
+ captureSelectionBeforeOp() {
698
+ if (this.isHistoryStateChange || this.selectionBeforeOp) return;
699
+ this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
700
+ }
701
+ pushOpHistory(opType, extra, pageData) {
702
+ if (this.isHistoryStateChange) {
703
+ this.selectionBeforeOp = null;
704
+ return;
705
+ }
706
+ const step = {
707
+ data: pageData,
708
+ opType,
709
+ selectedBefore: this.selectionBeforeOp ?? [],
710
+ selectedAfter: this.get("nodes").map((n) => n.id),
711
+ modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
712
+ ...extra
713
+ };
714
+ history_default.push(step);
715
+ this.selectionBeforeOp = null;
712
716
  this.isHistoryStateChange = false;
713
717
  }
714
- async changeHistoryState(value) {
715
- if (!value) return;
718
+ /**
719
+ * 应用历史操作(撤销 / 重做)
720
+ * @param step 操作记录
721
+ * @param reverse true = 撤销,false = 重做
722
+ */
723
+ async applyHistoryOp(step, reverse) {
716
724
  this.isHistoryStateChange = true;
717
- await this.update(value.data);
718
- this.set("modifiedNodeIds", value.modifiedNodeIds);
719
- setTimeout(() => {
720
- if (!value.nodeId) return;
721
- this.select(value.nodeId).then(() => {
722
- this.get("stage")?.select(value.nodeId);
723
- });
724
- }, 0);
725
- this.emit("history-change", value.data);
726
- }
727
- async toggleFixedPosition(dist, src, root) {
728
- const newConfig = cloneDeep(dist);
729
- if (!isPop(src) && newConfig.style?.position) {
730
- if (isFixed(newConfig.style) && !isFixed(src.style || {})) newConfig.style = change2Fixed(newConfig, root);
731
- else if (!isFixed(newConfig.style) && isFixed(src.style || {})) newConfig.style = await Fixed2Other(newConfig, root, this.getLayout);
725
+ const root = this.get("root");
726
+ const stage = this.get("stage");
727
+ if (!root) return;
728
+ const ctx = {
729
+ root,
730
+ stage,
731
+ getNodeById: (id, raw) => this.getNodeById(id, raw),
732
+ getNodeInfo: (id, raw) => this.getNodeInfo(id, raw),
733
+ setRoot: (r) => this.set("root", r),
734
+ setPage: (p) => this.set("page", p),
735
+ getPage: () => this.get("page")
736
+ };
737
+ switch (step.opType) {
738
+ case "add":
739
+ await applyHistoryAddOp(step, reverse, ctx);
740
+ break;
741
+ case "remove":
742
+ await applyHistoryRemoveOp(step, reverse, ctx);
743
+ break;
744
+ case "update":
745
+ await applyHistoryUpdateOp(step, reverse, ctx);
746
+ break;
732
747
  }
733
- return newConfig;
748
+ this.set("modifiedNodeIds", step.modifiedNodeIds);
749
+ const page = toRaw(this.get("page"));
750
+ if (page) {
751
+ const selectIds = reverse ? step.selectedBefore : step.selectedAfter;
752
+ setTimeout(() => {
753
+ if (!selectIds.length) return;
754
+ if (selectIds.length > 1) {
755
+ this.multiSelect(selectIds);
756
+ stage?.multiSelect(selectIds);
757
+ } else this.select(selectIds[0]).then(() => stage?.select(selectIds[0])).catch(() => {});
758
+ }, 0);
759
+ this.emit("history-change", page);
760
+ }
761
+ this.isHistoryStateChange = false;
734
762
  }
735
763
  selectedConfigExceptionHandler(config) {
736
- let id;
737
- if (typeof config === "string" || typeof config === "number") id = config;
738
- else id = config.id;
739
- if (!id) throw new Error("没有ID,无法选中");
740
- const { node, parent, page } = this.getNodeInfo(id);
741
- if (!node) throw new Error("获取不到组件信息");
742
- if (node.id === this.state.root?.id) throw new Error("不能选根节点");
743
- return {
744
- node,
745
- parent,
746
- page
747
- };
764
+ return resolveSelectedNode(config, (id) => this.getNodeInfo(id), this.state.root?.id);
748
765
  }
749
766
  };
750
767
  var editor_default = new Editor();
@@ -25,15 +25,7 @@ var History = class extends BaseService_default {
25
25
  changePage(page) {
26
26
  if (!page) return;
27
27
  this.state.pageId = page.id;
28
- if (!this.state.pageSteps[this.state.pageId]) {
29
- const undoRedo = new UndoRedo();
30
- undoRedo.pushElement({
31
- data: page,
32
- modifiedNodeIds: /* @__PURE__ */ new Map(),
33
- nodeId: page.id
34
- });
35
- this.state.pageSteps[this.state.pageId] = undoRedo;
36
- }
28
+ if (!this.state.pageSteps[this.state.pageId]) this.state.pageSteps[this.state.pageId] = new UndoRedo();
37
29
  this.setCanUndoRedo();
38
30
  this.emit("page-change", this.state.pageSteps[this.state.pageId]);
39
31
  }
package/dist/es/type.js CHANGED
@@ -86,5 +86,32 @@ var DragType = /* @__PURE__ */ function(DragType) {
86
86
  DragType["LAYER_TREE"] = "layer-tree";
87
87
  return DragType;
88
88
  }({});
89
+ var canUsePluginMethods = {
90
+ async: [
91
+ "getLayout",
92
+ "highlight",
93
+ "select",
94
+ "multiSelect",
95
+ "doAdd",
96
+ "add",
97
+ "doRemove",
98
+ "remove",
99
+ "doUpdate",
100
+ "update",
101
+ "sort",
102
+ "copy",
103
+ "paste",
104
+ "doPaste",
105
+ "doAlignCenter",
106
+ "alignCenter",
107
+ "moveLayer",
108
+ "moveToContainer",
109
+ "dragTo",
110
+ "undo",
111
+ "redo",
112
+ "move"
113
+ ],
114
+ sync: []
115
+ };
89
116
  //#endregion
90
- export { CODE_DRAFT_STORAGE_KEY, CodeDeleteErrorType, ColumnLayout, DragType, KeyBindingCommand, Keys, LayerOffset, Layout, SideItemKey };
117
+ export { CODE_DRAFT_STORAGE_KEY, CodeDeleteErrorType, ColumnLayout, DragType, KeyBindingCommand, Keys, LayerOffset, Layout, SideItemKey, canUsePluginMethods };