@tmagic/editor 1.0.0-beta.8 → 1.0.0-rc.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 (73) hide show
  1. package/README.md +1 -0
  2. package/dist/style.css +94 -45
  3. package/dist/tmagic-editor.es.js +1622 -2069
  4. package/dist/tmagic-editor.es.js.map +1 -1
  5. package/dist/tmagic-editor.umd.js +1621 -2071
  6. package/dist/tmagic-editor.umd.js.map +1 -1
  7. package/dist/types/src/Editor.vue.d.ts +50 -2
  8. package/dist/types/src/fields/Code.vue.d.ts +4 -4
  9. package/dist/types/src/fields/CodeLink.vue.d.ts +1 -3
  10. package/dist/types/src/index.d.ts +1 -0
  11. package/dist/types/src/layouts/CodeEditor.vue.d.ts +13 -4
  12. package/dist/types/src/layouts/Framework.vue.d.ts +15 -3
  13. package/dist/types/src/layouts/PropsPanel.vue.d.ts +4 -5
  14. package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
  15. package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +2 -3
  16. package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +14 -0
  17. package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +5 -31
  18. package/dist/types/src/services/BaseService.d.ts +5 -2
  19. package/dist/types/src/services/editor.d.ts +16 -6
  20. package/dist/types/src/services/history.d.ts +2 -18
  21. package/dist/types/src/services/props.d.ts +8 -2
  22. package/dist/types/src/services/ui.d.ts +2 -0
  23. package/dist/types/src/type.d.ts +25 -9
  24. package/dist/types/src/utils/editor.d.ts +5 -10
  25. package/dist/types/src/utils/polyfills.d.ts +0 -0
  26. package/dist/types/src/utils/props.d.ts +8 -1
  27. package/package.json +35 -12
  28. package/src/Editor.vue +44 -9
  29. package/src/components/ContentMenu.vue +140 -0
  30. package/src/components/ScrollViewer.vue +1 -1
  31. package/src/components/ToolButton.vue +72 -31
  32. package/src/fields/UISelect.vue +14 -6
  33. package/src/index.ts +2 -0
  34. package/src/layouts/AddPageBox.vue +3 -1
  35. package/src/layouts/CodeEditor.vue +20 -13
  36. package/src/layouts/Framework.vue +20 -7
  37. package/src/layouts/PropsPanel.vue +5 -4
  38. package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
  39. package/src/layouts/sidebar/LayerMenu.vue +93 -95
  40. package/src/layouts/sidebar/LayerPanel.vue +80 -59
  41. package/src/layouts/sidebar/Sidebar.vue +13 -52
  42. package/src/layouts/sidebar/TabPane.vue +68 -0
  43. package/src/layouts/workspace/PageBar.vue +145 -17
  44. package/src/layouts/workspace/Stage.vue +65 -91
  45. package/src/layouts/workspace/ViewerMenu.vue +122 -68
  46. package/src/layouts/workspace/Workspace.vue +125 -42
  47. package/src/services/BaseService.ts +71 -23
  48. package/src/services/editor.ts +231 -69
  49. package/src/services/history.ts +7 -0
  50. package/src/services/props.ts +64 -9
  51. package/src/services/ui.ts +35 -5
  52. package/src/theme/common/var.scss +3 -2
  53. package/src/theme/component-list-panel.scss +2 -0
  54. package/src/theme/content-menu.scss +33 -26
  55. package/src/theme/layer-panel.scss +10 -0
  56. package/src/theme/nav-menu.scss +6 -2
  57. package/src/theme/page-bar.scss +42 -2
  58. package/src/theme/props-panel.scss +19 -17
  59. package/src/theme/workspace.scss +4 -0
  60. package/src/type.ts +27 -9
  61. package/src/utils/editor.ts +37 -56
  62. package/src/utils/polyfills.ts +8 -0
  63. package/src/utils/props.ts +50 -8
  64. package/LICENSE +0 -5432
  65. package/dist/types/src/components/ToolButton.vue.d.ts +0 -35
  66. package/dist/types/src/fields/UISelect.vue.d.ts +0 -32
  67. package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
  68. package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -973
  69. package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
  70. package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -192
  71. package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
  72. package/tsconfig.json +0 -9
  73. package/vite.config.ts +0 -30
@@ -1,36 +1,26 @@
1
1
  <template>
2
- <el-tabs v-if="data.type === 'tabs'" class="m-editor-sidebar" v-model="activeTabName" type="card" tab-position="left">
3
- <el-tab-pane v-for="item in items" :key="item.text" :name="item.text">
4
- <template #label>
5
- <span>
6
- <m-icon v-if="item.icon" :icon="item.icon"></m-icon>
7
- <div v-if="item.text" class="magic-editor-tab-panel-title">{{ item.text }}</div>
8
- </span>
9
- </template>
10
-
11
- <component :is="item.component" v-bind="item.props || {}" v-on="item.listeners || {}">
12
- <template #layer-node-content="{ data, node }" v-if="item.slots?.layerNodeContent">
13
- <component :is="item.slots.layerNodeContent" :data="data" :node="node" />
14
- </template>
15
- </component>
16
- </el-tab-pane>
2
+ <el-tabs
3
+ v-if="data.type === 'tabs' && data.items.length"
4
+ class="m-editor-sidebar"
5
+ v-model="activeTabName"
6
+ type="card"
7
+ tab-position="left"
8
+ >
9
+ <tab-pane v-for="(item, index) in data.items" :key="index" :data="item"></tab-pane>
17
10
  </el-tabs>
18
11
  </template>
19
12
 
20
13
  <script lang="ts">
21
- import { computed, defineComponent, PropType, ref, watch } from 'vue';
22
- import { Coin, Files } from '@element-plus/icons';
14
+ import { defineComponent, PropType, ref, watch } from 'vue';
23
15
 
24
- import MIcon from '@editor/components/Icon.vue';
25
- import { SideBarData, SideComponent } from '@editor/type';
16
+ import { SideBarData } from '@editor/type';
26
17
 
27
- import ComponentListPanel from './ComponentListPanel.vue';
28
- import LayerPanel from './LayerPanel.vue';
18
+ import TabPane from './TabPane.vue';
29
19
 
30
20
  export default defineComponent({
31
- name: 'm-sidebar',
21
+ components: { TabPane },
32
22
 
33
- components: { MIcon },
23
+ name: 'm-sidebar',
34
24
 
35
25
  props: {
36
26
  data: {
@@ -51,35 +41,6 @@ export default defineComponent({
51
41
 
52
42
  return {
53
43
  activeTabName,
54
-
55
- items: computed<SideComponent[] | Record<string, any>[]>(() =>
56
- props.data?.items.map((item) => {
57
- if (typeof item !== 'string') {
58
- return item;
59
- }
60
-
61
- switch (item) {
62
- case 'component-list':
63
- return {
64
- type: 'component',
65
- icon: Coin,
66
- text: '组件',
67
- component: ComponentListPanel,
68
- slots: {},
69
- };
70
- case 'layer':
71
- return {
72
- type: 'component',
73
- icon: Files,
74
- text: '已选组件',
75
- component: LayerPanel,
76
- slots: {},
77
- };
78
- default:
79
- return {};
80
- }
81
- }),
82
- ),
83
44
  };
84
45
  },
85
46
  });
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <el-tab-pane v-if="config" :name="config.text">
3
+ <template #label>
4
+ <div :key="config.text">
5
+ <m-icon v-if="config.icon" :icon="config.icon"></m-icon>
6
+ <div v-if="config.text" class="magic-editor-tab-panel-title">{{ config.text }}</div>
7
+ </div>
8
+ </template>
9
+
10
+ <component :is="config.component" v-bind="config.props || {}" v-on="config?.listeners || {}">
11
+ <template #layer-node-content="{ data, node }" v-if="config.slots?.layerNodeContent">
12
+ <component :is="config.slots?.layerNodeContent" :data="data" :node="node" />
13
+ </template>
14
+ </component>
15
+ </el-tab-pane>
16
+ </template>
17
+
18
+ <script lang="ts">
19
+ import { computed, defineComponent, PropType } from 'vue';
20
+ import { Coin, Files } from '@element-plus/icons';
21
+
22
+ import MIcon from '@editor/components/Icon.vue';
23
+ import { SideComponent, SideItem } from '@editor/type';
24
+
25
+ import ComponentListPanel from './ComponentListPanel.vue';
26
+ import LayerPanel from './LayerPanel.vue';
27
+
28
+ export default defineComponent({
29
+ components: { MIcon },
30
+
31
+ props: {
32
+ data: {
33
+ type: [Object, String] as PropType<SideItem>,
34
+ },
35
+ },
36
+
37
+ setup(props) {
38
+ return {
39
+ config: computed<SideComponent | undefined>(() => {
40
+ if (typeof props.data !== 'string') {
41
+ return props.data;
42
+ }
43
+
44
+ switch (props.data) {
45
+ case 'component-list':
46
+ return {
47
+ type: 'component',
48
+ icon: Coin,
49
+ text: '组件',
50
+ component: ComponentListPanel,
51
+ slots: {},
52
+ };
53
+ case 'layer':
54
+ return {
55
+ type: 'component',
56
+ icon: Files,
57
+ text: '已选组件',
58
+ component: LayerPanel,
59
+ slots: {},
60
+ };
61
+ default:
62
+ return undefined;
63
+ }
64
+ }),
65
+ };
66
+ },
67
+ });
68
+ </script>
@@ -1,9 +1,12 @@
1
1
  <template>
2
- <div class="m-editor-page-bar">
3
- <div class="m-editor-page-bar-item" @click="addPage">
4
- <el-icon class="m-editor-page-bar-menu-add-icon"><plus></plus></el-icon>
2
+ <div class="m-editor-page-bar" ref="pageBar">
3
+ <div id="m-editor-page-bar-add-icon" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="addPage">
4
+ <el-icon><plus></plus></el-icon>
5
5
  </div>
6
- <template v-if="root">
6
+ <div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('left')">
7
+ <el-icon><arrow-left-bold></arrow-left-bold></el-icon>
8
+ </div>
9
+ <div v-if="root" class="m-editor-page-bar-items" ref="itemsContainer" :style="`width: ${itemsContainerWidth}px`">
7
10
  <div
8
11
  v-for="item in root.items"
9
12
  :key="item.key"
@@ -11,15 +14,33 @@
11
14
  :class="{ active: page?.id === item.id }"
12
15
  @click="switchPage(item)"
13
16
  >
14
- <slot name="page-bar-title" :page="item">
15
- <span>{{ item.name }}</span>
16
- </slot>
17
+ <div class="m-editor-page-bar-title">
18
+ <slot name="page-bar-title" :page="item">
19
+ <el-tooltip effect="dark" placement="top-start" :content="item.name">
20
+ <span>{{ item.name }}</span>
21
+ </el-tooltip>
22
+ </slot>
23
+ </div>
17
24
 
18
- <el-popover placement="top" :width="160" trigger="hover">
25
+ <el-popover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
19
26
  <div>
20
27
  <slot name="page-bar-popover" :page="item">
21
- <div class="magic-editor-content-menu-item" @click="() => copy(item)">复制</div>
22
- <div class="magic-editor-content-menu-item" @click="() => remove(item)">删除</div>
28
+ <tool-button
29
+ :data="{
30
+ type: 'button',
31
+ text: '复制',
32
+ icon: DocumentCopy,
33
+ handler: () => copy(item),
34
+ }"
35
+ ></tool-button>
36
+ <tool-button
37
+ :data="{
38
+ type: 'button',
39
+ text: '删除',
40
+ icon: Delete,
41
+ handler: () => remove(item),
42
+ }"
43
+ ></tool-button>
23
44
  </slot>
24
45
  </div>
25
46
  <template #reference>
@@ -29,28 +50,135 @@
29
50
  </template>
30
51
  </el-popover>
31
52
  </div>
32
- </template>
53
+ </div>
54
+ <div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('right')">
55
+ <el-icon><arrow-right-bold></arrow-right-bold></el-icon>
56
+ </div>
33
57
  </div>
34
58
  </template>
35
59
 
36
60
  <script lang="ts">
37
- import { computed, defineComponent, inject, toRaw } from 'vue';
38
- import { CaretBottom, Plus } from '@element-plus/icons';
61
+ import {
62
+ computed,
63
+ ComputedRef,
64
+ defineComponent,
65
+ inject,
66
+ markRaw,
67
+ onMounted,
68
+ onUnmounted,
69
+ ref,
70
+ toRaw,
71
+ watch,
72
+ } from 'vue';
73
+ import { ArrowLeftBold, ArrowRightBold, CaretBottom, Delete, DocumentCopy, Plus } from '@element-plus/icons';
39
74
 
40
- import type { MPage } from '@tmagic/schema';
75
+ import type { MApp, MPage } from '@tmagic/schema';
76
+ import { NodeType } from '@tmagic/schema';
41
77
 
78
+ import ToolButton from '@editor/components/ToolButton.vue';
42
79
  import type { Services } from '@editor/type';
43
80
  import { generatePageNameByApp } from '@editor/utils/editor';
44
81
 
82
+ const useScroll = (root: ComputedRef<MApp | undefined>) => {
83
+ const pageBar = ref<HTMLDivElement>();
84
+ const itemsContainer = ref<HTMLDivElement>();
85
+
86
+ const pageBarWidth = ref(0);
87
+ const canScroll = ref(false);
88
+
89
+ const itemsContainerWidth = computed(() => pageBarWidth.value - 105);
90
+
91
+ let translateLeft = 0;
92
+ const resizeObserver = new ResizeObserver((entries) => {
93
+ for (const { contentRect } of entries) {
94
+ const { width } = contentRect;
95
+ pageBarWidth.value = width || 0;
96
+
97
+ setCanScroll();
98
+ }
99
+ });
100
+
101
+ const setCanScroll = () => {
102
+ if (itemsContainer.value) {
103
+ canScroll.value = itemsContainer.value.scrollWidth > pageBarWidth.value - 105;
104
+ }
105
+ };
106
+
107
+ const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
108
+ if (!itemsContainer.value) return;
109
+
110
+ const maxScrollLeft = itemsContainer.value.scrollWidth - itemsContainerWidth.value;
111
+
112
+ if (type === 'left') {
113
+ translateLeft += 100;
114
+
115
+ if (translateLeft > 0) {
116
+ translateLeft = 0;
117
+ }
118
+ } else if (type === 'right') {
119
+ translateLeft -= 100;
120
+
121
+ if (-translateLeft > maxScrollLeft) {
122
+ translateLeft = -maxScrollLeft;
123
+ }
124
+ } else if (type === 'start') {
125
+ translateLeft = 0;
126
+ } else if (type === 'end') {
127
+ translateLeft = -maxScrollLeft;
128
+ }
129
+
130
+ itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
131
+ };
132
+
133
+ onMounted(() => {
134
+ pageBar.value && resizeObserver.observe(pageBar.value);
135
+ });
136
+
137
+ onUnmounted(() => {
138
+ resizeObserver.disconnect();
139
+ });
140
+
141
+ watch(
142
+ () => root.value?.items.length,
143
+ (length = 0, preLength = 0) => {
144
+ setTimeout(() => {
145
+ setCanScroll();
146
+ if (length < preLength) {
147
+ scroll('start');
148
+ } else {
149
+ scroll('end');
150
+ }
151
+ });
152
+ },
153
+ );
154
+
155
+ return {
156
+ pageBar,
157
+ itemsContainer,
158
+ canScroll,
159
+
160
+ itemsContainerWidth,
161
+
162
+ scroll,
163
+ };
164
+ };
165
+
45
166
  export default defineComponent({
46
- components: { CaretBottom, Plus },
167
+ components: { ArrowLeftBold, ArrowRightBold, CaretBottom, Plus, ToolButton },
47
168
 
48
169
  setup() {
49
170
  const services = inject<Services>('services');
50
171
  const editorService = services?.editorService;
51
172
 
173
+ const root = computed(() => editorService?.get<MApp>('root'));
174
+
52
175
  return {
53
- root: computed(() => editorService?.get('root')),
176
+ Delete: markRaw(Delete),
177
+ DocumentCopy: markRaw(DocumentCopy),
178
+
179
+ ...useScroll(root),
180
+
181
+ root,
54
182
  page: computed(() => editorService?.get('page')),
55
183
 
56
184
  switchPage(page: MPage) {
@@ -60,7 +188,7 @@ export default defineComponent({
60
188
  addPage() {
61
189
  if (!editorService) return;
62
190
  const pageConfig = {
63
- type: 'page',
191
+ type: NodeType.PAGE,
64
192
  name: generatePageNameByApp(toRaw(editorService.get('root'))),
65
193
  };
66
194
  editorService.add(pageConfig);
@@ -9,11 +9,13 @@
9
9
  <div
10
10
  class="m-editor-stage-container"
11
11
  ref="stageContainer"
12
- @contextmenu="contextmenuHandler"
13
12
  :style="`transform: scale(${zoom})`"
13
+ @contextmenu="contextmenuHandler"
14
+ @drop="dropHandler"
15
+ @dragover="dragoverHandler"
14
16
  ></div>
15
17
  <teleport to="body">
16
- <viewer-menu ref="menu" :style="menuStyle"></viewer-menu>
18
+ <viewer-menu ref="menu"></viewer-menu>
17
19
  </teleport>
18
20
  </scroll-viewer>
19
21
  </template>
@@ -23,10 +25,9 @@ import {
23
25
  computed,
24
26
  defineComponent,
25
27
  inject,
26
- nextTick,
28
+ markRaw,
27
29
  onMounted,
28
30
  onUnmounted,
29
- PropType,
30
31
  ref,
31
32
  toRaw,
32
33
  watch,
@@ -35,53 +36,14 @@ import {
35
36
  import { cloneDeep } from 'lodash-es';
36
37
 
37
38
  import type { MApp, MNode, MPage } from '@tmagic/schema';
38
- import type { MoveableOptions, Runtime, SortEventData, UpdateEventData } from '@tmagic/stage';
39
+ import type { Runtime, SortEventData, UpdateEventData } from '@tmagic/stage';
39
40
  import StageCore from '@tmagic/stage';
40
41
 
41
42
  import ScrollViewer from '@editor/components/ScrollViewer.vue';
42
- import type { Services, StageRect } from '@editor/type';
43
+ import { Layout, Services, StageOptions, StageRect } from '@editor/type';
43
44
 
44
45
  import ViewerMenu from './ViewerMenu.vue';
45
46
 
46
- const useMenu = () => {
47
- const menu = ref<InstanceType<typeof ViewerMenu>>();
48
- const menuStyle = ref({
49
- display: 'none',
50
- left: '0',
51
- top: '0',
52
- });
53
-
54
- onMounted(() => {
55
- document.addEventListener(
56
- 'click',
57
- () => {
58
- menuStyle.value.display = 'none';
59
- },
60
- true,
61
- );
62
- });
63
-
64
- return {
65
- menu,
66
- menuStyle,
67
-
68
- contextmenuHandler(e: MouseEvent) {
69
- e.preventDefault();
70
-
71
- const menuHeight = menu.value?.$el.clientHeight;
72
- let top = e.clientY;
73
- if (menuHeight + e.clientY > document.body.clientHeight) {
74
- top = document.body.clientHeight - menuHeight;
75
- }
76
- menuStyle.value = {
77
- display: 'block',
78
- top: `${top}px`,
79
- left: `${e.clientX}px`,
80
- };
81
- },
82
- };
83
- };
84
-
85
47
  export default defineComponent({
86
48
  name: 'magic-stage',
87
49
 
@@ -90,39 +52,19 @@ export default defineComponent({
90
52
  ScrollViewer,
91
53
  },
92
54
 
93
- props: {
94
- render: {
95
- type: Function as PropType<() => HTMLDivElement>,
96
- },
97
-
98
- runtimeUrl: String,
99
-
100
- canSelect: {
101
- type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
102
- default: (el: HTMLElement) => Boolean(el.id),
103
- },
104
-
105
- moveableOptions: {
106
- type: [Object, Function] as PropType<MoveableOptions | ((core?: StageCore) => MoveableOptions)>,
107
- default: () => (core?: StageCore) => ({
108
- container: core?.renderer?.contentWindow?.document.getElementById('app'),
109
- }),
110
- },
111
- },
112
-
113
- emits: ['select', 'update', 'sort'],
114
-
115
- setup(props, { emit }) {
55
+ setup() {
116
56
  const services = inject<Services>('services');
57
+ const stageOptions = inject<StageOptions>('stageOptions');
117
58
 
118
59
  const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
119
60
  const stageContainer = ref<HTMLDivElement>();
61
+ const menu = ref<InstanceType<typeof ViewerMenu>>();
120
62
 
121
63
  const stageRect = computed(() => services?.uiService.get<StageRect>('stageRect'));
122
64
  const uiSelectMode = computed(() => services?.uiService.get<boolean>('uiSelectMode'));
123
65
  const root = computed(() => services?.editorService.get<MApp>('root'));
124
66
  const page = computed(() => services?.editorService.get<MPage>('page'));
125
- const zoom = computed(() => services?.uiService.get<number>('zoom'));
67
+ const zoom = computed(() => services?.uiService.get<number>('zoom') || 1);
126
68
  const node = computed(() => services?.editorService.get<MNode>('node'));
127
69
 
128
70
  let stage: StageCore | null = null;
@@ -132,37 +74,45 @@ export default defineComponent({
132
74
  if (stage) return;
133
75
 
134
76
  if (!stageContainer.value) return;
135
- if (!(props.runtimeUrl || props.render) || !root.value) return;
77
+ if (!(stageOptions?.runtimeUrl || stageOptions?.render) || !root.value) return;
136
78
 
137
79
  stage = new StageCore({
138
- render: props.render,
139
- runtimeUrl: props.runtimeUrl,
80
+ render: stageOptions.render,
81
+ runtimeUrl: stageOptions.runtimeUrl,
140
82
  zoom: zoom.value,
141
- canSelect: (el, stop) => {
142
- const elCanSelect = props.canSelect(el);
83
+ autoScrollIntoView: stageOptions.autoScrollIntoView,
84
+ canSelect: (el, event, stop) => {
85
+ const elCanSelect = stageOptions.canSelect(el);
143
86
  // 在组件联动过程中不能再往下选择,返回并触发 ui-select
144
- if (uiSelectMode.value && elCanSelect) {
87
+ if (uiSelectMode.value && elCanSelect && event.type === 'mousedown') {
145
88
  document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
146
89
  return stop();
147
90
  }
148
91
 
149
92
  return elCanSelect;
150
93
  },
151
- moveableOptions: props.moveableOptions,
94
+ moveableOptions: stageOptions.moveableOptions,
95
+ updateDragEl: stageOptions.updateDragEl,
152
96
  });
153
97
 
154
- services?.editorService.set('stage', stage);
98
+ services?.editorService.set('stage', markRaw(stage));
155
99
 
156
100
  stage?.mount(stageContainer.value);
157
101
 
158
- stage?.on('select', (el: HTMLElement) => emit('select', el));
102
+ stage?.on('select', (el: HTMLElement) => {
103
+ services?.editorService.select(el.id);
104
+ });
105
+
106
+ stage?.on('highlight', (el: HTMLElement) => {
107
+ services?.editorService.highlight(el.id);
108
+ });
159
109
 
160
110
  stage?.on('update', (ev: UpdateEventData) => {
161
- emit('update', { id: ev.el.id, style: ev.style });
111
+ services?.editorService.update({ id: ev.el.id, style: ev.style });
162
112
  });
163
113
 
164
114
  stage?.on('sort', (ev: SortEventData) => {
165
- emit('sort', ev);
115
+ services?.editorService.sort(ev.src, ev.dist);
166
116
  });
167
117
 
168
118
  stage?.on('changeGuides', () => {
@@ -192,16 +142,6 @@ export default defineComponent({
192
142
  }
193
143
  });
194
144
 
195
- watch(
196
- () => node.value?.id,
197
- (id) => {
198
- nextTick(() => {
199
- // 等待相关dom变更完成后,再select,适用大多数场景
200
- id && stage?.select(id);
201
- });
202
- },
203
- );
204
-
205
145
  const resizeObserver = new ResizeObserver((entries) => {
206
146
  for (const { contentRect } of entries) {
207
147
  services?.uiService.set('stageContainerRect', {
@@ -224,9 +164,43 @@ export default defineComponent({
224
164
  return {
225
165
  stageWrap,
226
166
  stageContainer,
167
+ menu,
227
168
  stageRect,
228
169
  zoom,
229
- ...useMenu(),
170
+
171
+ contextmenuHandler(e: MouseEvent) {
172
+ e.preventDefault();
173
+ menu.value?.show(e);
174
+ },
175
+
176
+ dragoverHandler(e: DragEvent) {
177
+ e.preventDefault();
178
+ if (e.dataTransfer) {
179
+ e.dataTransfer.dropEffect = 'move';
180
+ }
181
+ },
182
+
183
+ async dropHandler(e: DragEvent) {
184
+ e.preventDefault();
185
+ if (e.dataTransfer && page.value && stageContainer.value && stage) {
186
+ // eslint-disable-next-line no-eval
187
+ const config = eval(`(${e.dataTransfer.getData('data')})`);
188
+ const layout = await services?.editorService.getLayout(page.value);
189
+
190
+ const containerRect = stageContainer.value.getBoundingClientRect();
191
+ const { scrollTop, scrollLeft } = stage.mask;
192
+ if (layout === Layout.ABSOLUTE) {
193
+ config.style = {
194
+ ...(config.style || {}),
195
+ position: 'absolute',
196
+ top: e.clientY - containerRect.top + scrollTop,
197
+ left: e.clientX - containerRect.left + scrollLeft,
198
+ };
199
+ }
200
+
201
+ services?.editorService.add(config, page.value);
202
+ }
203
+ },
230
204
  };
231
205
  },
232
206
  });