@tmagic/editor 1.0.0-beta.9 → 1.0.0-rc.3

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 (120) hide show
  1. package/README.md +1 -0
  2. package/coverage/clover.xml +1556 -0
  3. package/coverage/coverage-final.json +36 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +206 -0
  8. package/coverage/lcov-report/prettify.css +1 -0
  9. package/coverage/lcov-report/prettify.js +2 -0
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +196 -0
  12. package/coverage/lcov-report/src/Editor.vue.html +814 -0
  13. package/coverage/lcov-report/src/components/ContentMenu.vue.html +406 -0
  14. package/coverage/lcov-report/src/components/Icon.vue.html +172 -0
  15. package/coverage/lcov-report/src/components/ScrollViewer.vue.html +283 -0
  16. package/coverage/lcov-report/src/components/ToolButton.vue.html +772 -0
  17. package/coverage/lcov-report/src/components/index.html +161 -0
  18. package/coverage/lcov-report/src/index.html +131 -0
  19. package/coverage/lcov-report/src/layouts/AddPageBox.vue.html +214 -0
  20. package/coverage/lcov-report/src/layouts/Framework.vue.html +343 -0
  21. package/coverage/lcov-report/src/layouts/NavMenu.vue.html +208 -0
  22. package/coverage/lcov-report/src/layouts/PropsPanel.vue.html +319 -0
  23. package/coverage/lcov-report/src/layouts/Resizer.vue.html +268 -0
  24. package/coverage/lcov-report/src/layouts/index.html +176 -0
  25. package/coverage/lcov-report/src/layouts/sidebar/ComponentListPanel.vue.html +358 -0
  26. package/coverage/lcov-report/src/layouts/sidebar/LayerMenu.vue.html +421 -0
  27. package/coverage/lcov-report/src/layouts/sidebar/LayerPanel.vue.html +775 -0
  28. package/coverage/lcov-report/src/layouts/sidebar/Sidebar.vue.html +226 -0
  29. package/coverage/lcov-report/src/layouts/sidebar/TabPane.vue.html +289 -0
  30. package/coverage/lcov-report/src/layouts/sidebar/index.html +176 -0
  31. package/coverage/lcov-report/src/layouts/workspace/PageBar.vue.html +718 -0
  32. package/coverage/lcov-report/src/layouts/workspace/Stage.vue.html +766 -0
  33. package/coverage/lcov-report/src/layouts/workspace/ViewerMenu.vue.html +532 -0
  34. package/coverage/lcov-report/src/layouts/workspace/Workspace.vue.html +604 -0
  35. package/coverage/lcov-report/src/layouts/workspace/index.html +161 -0
  36. package/coverage/lcov-report/src/services/BaseService.ts.html +643 -0
  37. package/coverage/lcov-report/src/services/componentList.ts.html +229 -0
  38. package/coverage/lcov-report/src/services/editor.ts.html +2077 -0
  39. package/coverage/lcov-report/src/services/events.ts.html +331 -0
  40. package/coverage/lcov-report/src/services/history.ts.html +478 -0
  41. package/coverage/lcov-report/src/services/index.html +206 -0
  42. package/coverage/lcov-report/src/services/props.ts.html +415 -0
  43. package/coverage/lcov-report/src/services/ui.ts.html +580 -0
  44. package/coverage/lcov-report/src/type.ts.html +850 -0
  45. package/coverage/lcov-report/src/utils/compose.ts.html +184 -0
  46. package/coverage/lcov-report/src/utils/config.ts.html +172 -0
  47. package/coverage/lcov-report/src/utils/editor.ts.html +838 -0
  48. package/coverage/lcov-report/src/utils/index.html +221 -0
  49. package/coverage/lcov-report/src/utils/index.ts.html +151 -0
  50. package/coverage/lcov-report/src/utils/logger.ts.html +226 -0
  51. package/coverage/lcov-report/src/utils/props.ts.html +796 -0
  52. package/coverage/lcov-report/src/utils/scroll-viewer.ts.html +733 -0
  53. package/coverage/lcov-report/src/utils/undo-redo.ts.html +313 -0
  54. package/coverage/lcov.info +3747 -0
  55. package/dist/style.css +68 -36
  56. package/dist/tmagic-editor.es.js +1431 -2048
  57. package/dist/tmagic-editor.es.js.map +1 -1
  58. package/dist/tmagic-editor.umd.js +1431 -2048
  59. package/dist/tmagic-editor.umd.js.map +1 -1
  60. package/dist/types/src/Editor.vue.d.ts +15 -1
  61. package/dist/types/src/components/ToolButton.vue.d.ts +12 -1
  62. package/dist/types/src/fields/UISelect.vue.d.ts +4 -1
  63. package/dist/types/src/index.d.ts +1 -0
  64. package/dist/types/src/layouts/CodeEditor.vue.d.ts +13 -4
  65. package/dist/types/src/layouts/Framework.vue.d.ts +15 -3
  66. package/dist/types/src/layouts/PropsPanel.vue.d.ts +2 -1
  67. package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
  68. package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +2 -3
  69. package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +14 -0
  70. package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +10 -3
  71. package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +9 -8
  72. package/dist/types/src/services/BaseService.d.ts +4 -1
  73. package/dist/types/src/services/editor.d.ts +5 -2
  74. package/dist/types/src/services/history.d.ts +1 -0
  75. package/dist/types/src/services/props.d.ts +2 -2
  76. package/dist/types/src/services/ui.d.ts +2 -0
  77. package/dist/types/src/type.d.ts +15 -9
  78. package/dist/types/src/utils/editor.d.ts +4 -3
  79. package/dist/types/src/utils/polyfills.d.ts +0 -0
  80. package/package.json +44 -12
  81. package/src/Editor.vue +10 -1
  82. package/src/components/ContentMenu.vue +107 -0
  83. package/src/components/ScrollViewer.vue +1 -1
  84. package/src/components/ToolButton.vue +69 -30
  85. package/src/fields/UISelect.vue +14 -6
  86. package/src/index.ts +2 -0
  87. package/src/layouts/CodeEditor.vue +20 -13
  88. package/src/layouts/Framework.vue +20 -7
  89. package/src/layouts/PropsPanel.vue +5 -4
  90. package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
  91. package/src/layouts/sidebar/LayerMenu.vue +91 -96
  92. package/src/layouts/sidebar/LayerPanel.vue +31 -54
  93. package/src/layouts/sidebar/Sidebar.vue +13 -52
  94. package/src/layouts/sidebar/TabPane.vue +68 -0
  95. package/src/layouts/workspace/PageBar.vue +136 -13
  96. package/src/layouts/workspace/Stage.vue +53 -75
  97. package/src/layouts/workspace/ViewerMenu.vue +104 -67
  98. package/src/layouts/workspace/Workspace.vue +111 -22
  99. package/src/services/BaseService.ts +70 -22
  100. package/src/services/editor.ts +148 -39
  101. package/src/services/history.ts +7 -0
  102. package/src/services/props.ts +3 -3
  103. package/src/services/ui.ts +35 -5
  104. package/src/theme/common/var.scss +1 -0
  105. package/src/theme/component-list-panel.scss +2 -0
  106. package/src/theme/content-menu.scss +33 -26
  107. package/src/theme/layer-panel.scss +2 -1
  108. package/src/theme/nav-menu.scss +6 -2
  109. package/src/theme/page-bar.scss +35 -3
  110. package/src/theme/props-panel.scss +19 -17
  111. package/src/type.ts +16 -9
  112. package/src/utils/editor.ts +31 -6
  113. package/src/utils/polyfills.ts +8 -0
  114. package/src/utils/props.ts +20 -1
  115. package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
  116. package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -979
  117. package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -193
  118. package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
  119. package/tsconfig.json +0 -9
  120. package/vite.config.ts +0 -30
@@ -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"
@@ -19,11 +22,25 @@
19
22
  </slot>
20
23
  </div>
21
24
 
22
- <el-popover placement="top" :width="160" trigger="hover">
25
+ <el-popover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
23
26
  <div>
24
27
  <slot name="page-bar-popover" :page="item">
25
- <div class="magic-editor-content-menu-item" @click="() => copy(item)">复制</div>
26
- <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>
27
44
  </slot>
28
45
  </div>
29
46
  <template #reference>
@@ -33,29 +50,135 @@
33
50
  </template>
34
51
  </el-popover>
35
52
  </div>
36
- </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>
37
57
  </div>
38
58
  </template>
39
59
 
40
60
  <script lang="ts">
41
- import { computed, defineComponent, inject, toRaw } from 'vue';
42
- 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';
43
74
 
44
- import type { MPage } from '@tmagic/schema';
75
+ import type { MApp, MPage } from '@tmagic/schema';
45
76
  import { NodeType } from '@tmagic/schema';
46
77
 
78
+ import ToolButton from '@editor/components/ToolButton.vue';
47
79
  import type { Services } from '@editor/type';
48
80
  import { generatePageNameByApp } from '@editor/utils/editor';
49
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
+
50
166
  export default defineComponent({
51
- components: { CaretBottom, Plus },
167
+ components: { ArrowLeftBold, ArrowRightBold, CaretBottom, Plus, ToolButton },
52
168
 
53
169
  setup() {
54
170
  const services = inject<Services>('services');
55
171
  const editorService = services?.editorService;
56
172
 
173
+ const root = computed(() => editorService?.get<MApp>('root'));
174
+
57
175
  return {
58
- root: computed(() => editorService?.get('root')),
176
+ Delete: markRaw(Delete),
177
+ DocumentCopy: markRaw(DocumentCopy),
178
+
179
+ ...useScroll(root),
180
+
181
+ root,
59
182
  page: computed(() => editorService?.get('page')),
60
183
 
61
184
  switchPage(page: MPage) {
@@ -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,7 +25,7 @@ import {
23
25
  computed,
24
26
  defineComponent,
25
27
  inject,
26
- nextTick,
28
+ markRaw,
27
29
  onMounted,
28
30
  onUnmounted,
29
31
  PropType,
@@ -39,49 +41,10 @@ import type { MoveableOptions, Runtime, SortEventData, UpdateEventData } from '@
39
41
  import StageCore from '@tmagic/stage';
40
42
 
41
43
  import ScrollViewer from '@editor/components/ScrollViewer.vue';
42
- import type { Services, StageRect } from '@editor/type';
44
+ import { Layout, Services, StageRect } from '@editor/type';
43
45
 
44
46
  import ViewerMenu from './ViewerMenu.vue';
45
47
 
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
48
  export default defineComponent({
86
49
  name: 'magic-stage',
87
50
 
@@ -96,6 +59,7 @@ export default defineComponent({
96
59
  },
97
60
 
98
61
  runtimeUrl: String,
62
+ autoScrollIntoView: Boolean,
99
63
 
100
64
  canSelect: {
101
65
  type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
@@ -110,21 +74,19 @@ export default defineComponent({
110
74
  },
111
75
  },
112
76
 
113
- emits: ['select', 'update', 'sort', 'highlight'],
114
-
115
- setup(props, { emit }) {
77
+ setup(props) {
116
78
  const services = inject<Services>('services');
117
79
 
118
80
  const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
119
81
  const stageContainer = ref<HTMLDivElement>();
82
+ const menu = ref<InstanceType<typeof ViewerMenu>>();
120
83
 
121
84
  const stageRect = computed(() => services?.uiService.get<StageRect>('stageRect'));
122
85
  const uiSelectMode = computed(() => services?.uiService.get<boolean>('uiSelectMode'));
123
86
  const root = computed(() => services?.editorService.get<MApp>('root'));
124
87
  const page = computed(() => services?.editorService.get<MPage>('page'));
125
- const zoom = computed(() => services?.uiService.get<number>('zoom'));
88
+ const zoom = computed(() => services?.uiService.get<number>('zoom') || 1);
126
89
  const node = computed(() => services?.editorService.get<MNode>('node'));
127
- const highlightNode = computed(() => services?.editorService.get<MNode>('highlightNode'));
128
90
 
129
91
  let stage: StageCore | null = null;
130
92
  let runtime: Runtime | null = null;
@@ -139,10 +101,11 @@ export default defineComponent({
139
101
  render: props.render,
140
102
  runtimeUrl: props.runtimeUrl,
141
103
  zoom: zoom.value,
142
- canSelect: (el, stop) => {
104
+ autoScrollIntoView: props.autoScrollIntoView,
105
+ canSelect: (el, event, stop) => {
143
106
  const elCanSelect = props.canSelect(el);
144
107
  // 在组件联动过程中不能再往下选择,返回并触发 ui-select
145
- if (uiSelectMode.value && elCanSelect) {
108
+ if (uiSelectMode.value && elCanSelect && event.type === 'mousedown') {
146
109
  document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
147
110
  return stop();
148
111
  }
@@ -152,24 +115,24 @@ export default defineComponent({
152
115
  moveableOptions: props.moveableOptions,
153
116
  });
154
117
 
155
- services?.editorService.set('stage', stage);
118
+ services?.editorService.set('stage', markRaw(stage));
156
119
 
157
120
  stage?.mount(stageContainer.value);
158
121
 
159
122
  stage?.on('select', (el: HTMLElement) => {
160
- emit('select', el);
123
+ services?.editorService.select(el.id);
161
124
  });
162
125
 
163
126
  stage?.on('highlight', (el: HTMLElement) => {
164
- emit('highlight', el);
127
+ services?.editorService.highlight(el.id);
165
128
  });
166
129
 
167
130
  stage?.on('update', (ev: UpdateEventData) => {
168
- emit('update', { id: ev.el.id, style: ev.style });
131
+ services?.editorService.update({ id: ev.el.id, style: ev.style });
169
132
  });
170
133
 
171
134
  stage?.on('sort', (ev: SortEventData) => {
172
- emit('sort', ev);
135
+ services?.editorService.sort(ev.src, ev.dist);
173
136
  });
174
137
 
175
138
  stage?.on('changeGuides', () => {
@@ -199,25 +162,6 @@ export default defineComponent({
199
162
  }
200
163
  });
201
164
 
202
- watch(
203
- () => node.value?.id,
204
- (id) => {
205
- nextTick(() => {
206
- // 等待相关dom变更完成后,再select,适用大多数场景
207
- id && stage?.select(id);
208
- });
209
- },
210
- );
211
-
212
- watch(
213
- () => highlightNode.value?.id,
214
- (id) => {
215
- nextTick(() => {
216
- id && stage?.highlight(id);
217
- });
218
- },
219
- );
220
-
221
165
  const resizeObserver = new ResizeObserver((entries) => {
222
166
  for (const { contentRect } of entries) {
223
167
  services?.uiService.set('stageContainerRect', {
@@ -240,9 +184,43 @@ export default defineComponent({
240
184
  return {
241
185
  stageWrap,
242
186
  stageContainer,
187
+ menu,
243
188
  stageRect,
244
189
  zoom,
245
- ...useMenu(),
190
+
191
+ contextmenuHandler(e: MouseEvent) {
192
+ e.preventDefault();
193
+ menu.value?.show(e);
194
+ },
195
+
196
+ dragoverHandler(e: DragEvent) {
197
+ e.preventDefault();
198
+ if (e.dataTransfer) {
199
+ e.dataTransfer.dropEffect = 'move';
200
+ }
201
+ },
202
+
203
+ async dropHandler(e: DragEvent) {
204
+ e.preventDefault();
205
+ if (e.dataTransfer && page.value && stageContainer.value && stage) {
206
+ // eslint-disable-next-line no-eval
207
+ const config = eval(`(${e.dataTransfer.getData('data')})`);
208
+ const layout = await services?.editorService.getLayout(page.value);
209
+
210
+ const containerRect = stageContainer.value.getBoundingClientRect();
211
+ const { scrollTop, scrollLeft } = stage.mask;
212
+ if (layout === Layout.ABSOLUTE) {
213
+ config.style = {
214
+ ...(config.style || {}),
215
+ position: 'absolute',
216
+ top: e.clientY - containerRect.top + scrollTop,
217
+ left: e.clientX - containerRect.left + scrollLeft,
218
+ };
219
+ }
220
+
221
+ services?.editorService.add(config, page.value);
222
+ }
223
+ },
246
224
  };
247
225
  },
248
226
  });
@@ -1,47 +1,31 @@
1
1
  <template>
2
- <div class="magic-editor-content-menu" ref="menu">
3
- <div>
4
- <div class="magic-editor-content-menu-item" @click="() => center()" v-if="canCenter">水平居中</div>
5
- <div class="magic-editor-content-menu-item" @click="() => copy()">复制</div>
6
- <div class="magic-editor-content-menu-item" @click="paste" v-if="canPaste">粘贴</div>
7
- <template v-if="canMoveZPos">
8
- <div class="separation"></div>
9
- <div class="magic-editor-content-menu-item" @click="topItem">上移一层</div>
10
- <div class="magic-editor-content-menu-item" @click="bottomItem">下移一层</div>
11
- <div class="magic-editor-content-menu-item" @click="top">置顶</div>
12
- <div class="magic-editor-content-menu-item" @click="bottom">置底</div>
13
- </template>
14
- <template v-if="canDelete">
15
- <div class="separation"></div>
16
- <div class="magic-editor-content-menu-item" @click="() => remove()">删除</div>
17
- </template>
18
- <div class="separation"></div>
19
- <div class="magic-editor-content-menu-item" @click="clearGuides">清空参考线</div>
20
- </div>
21
- </div>
2
+ <content-menu :menu-data="menuData" ref="menu"></content-menu>
22
3
  </template>
23
4
 
24
5
  <script lang="ts">
25
- import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue';
6
+ import { computed, defineComponent, inject, markRaw, onMounted, reactive, ref, watch } from 'vue';
7
+ import { Bottom, Delete, DocumentCopy, Top } from '@element-plus/icons';
26
8
 
27
9
  import { NodeType } from '@tmagic/schema';
28
10
  import type StageCore from '@tmagic/stage';
29
11
 
30
- import { LayerOffset, Layout, Services } from '@editor/type';
12
+ import ContentMenu from '@editor/components/ContentMenu.vue';
13
+ import { LayerOffset, Layout, MenuItem, Services } from '@editor/type';
31
14
  import { COPY_STORAGE_KEY } from '@editor/utils/editor';
32
15
 
33
16
  export default defineComponent({
34
- name: 'magic-editor-ui-viewer-menu',
17
+ components: { ContentMenu },
35
18
 
36
19
  setup() {
37
20
  const services = inject<Services>('services');
38
21
  const editorService = services?.editorService;
39
- const menu = ref<HTMLDivElement>();
22
+ const menu = ref<InstanceType<typeof ContentMenu>>();
40
23
  const canPaste = ref(false);
41
24
  const canCenter = ref(false);
42
25
 
43
26
  const node = computed(() => editorService?.get('node'));
44
27
  const parent = computed(() => editorService?.get('parent'));
28
+ const isPage = computed(() => node.value?.type === NodeType.PAGE);
45
29
 
46
30
  onMounted(() => {
47
31
  const data = globalThis.localStorage.getItem(COPY_STORAGE_KEY);
@@ -62,49 +46,102 @@ export default defineComponent({
62
46
 
63
47
  return {
64
48
  menu,
65
- canPaste,
66
-
67
- canDelete: computed(() => node.value?.type !== NodeType.PAGE),
68
- canMoveZPos: computed(() => node.value?.type !== NodeType.PAGE),
69
- canCenter,
70
-
71
- center() {
72
- node.value && editorService?.alignCenter(node.value);
73
- },
74
-
75
- copy() {
76
- node.value && editorService?.copy(node.value);
77
- canPaste.value = true;
78
- },
79
-
80
- paste() {
81
- const top = menu.value?.offsetTop || 0;
82
- const left = menu.value?.offsetLeft || 0;
83
- editorService?.paste({ left, top });
84
- },
85
-
86
- remove() {
87
- node.value && editorService?.remove(node.value);
88
- },
89
-
90
- top() {
91
- editorService?.moveLayer(LayerOffset.TOP);
92
- },
93
-
94
- bottom() {
95
- editorService?.moveLayer(LayerOffset.BOTTOM);
96
- },
97
-
98
- topItem() {
99
- editorService?.moveLayer(1);
100
- },
101
-
102
- bottomItem() {
103
- editorService?.moveLayer(-1);
104
- },
105
-
106
- clearGuides() {
107
- editorService?.get<StageCore>('stage').clearGuides();
49
+ menuData: reactive<MenuItem[]>([
50
+ {
51
+ type: 'button',
52
+ text: '水平居中',
53
+ display: () => canCenter.value,
54
+ handler: () => {
55
+ node.value && editorService?.alignCenter(node.value);
56
+ },
57
+ },
58
+ {
59
+ type: 'button',
60
+ text: '复制',
61
+ icon: markRaw(DocumentCopy),
62
+ handler: () => {
63
+ node.value && editorService?.copy(node.value);
64
+ canPaste.value = true;
65
+ },
66
+ },
67
+ {
68
+ type: 'button',
69
+ text: '粘贴',
70
+ display: () => canPaste.value,
71
+ handler: () => {
72
+ const top = menu.value?.$el.offsetTop || 0;
73
+ const left = menu.value?.$el.offsetLeft || 0;
74
+ editorService?.paste({ left, top });
75
+ },
76
+ },
77
+ {
78
+ type: 'divider',
79
+ direction: 'horizontal',
80
+ display: () => !isPage.value,
81
+ },
82
+ {
83
+ type: 'button',
84
+ text: '上移一层',
85
+ icon: markRaw(Top),
86
+ display: () => !isPage.value,
87
+ handler: () => {
88
+ editorService?.moveLayer(1);
89
+ },
90
+ },
91
+ {
92
+ type: 'button',
93
+ text: '下移一层',
94
+ icon: markRaw(Bottom),
95
+ display: () => !isPage.value,
96
+ handler: () => {
97
+ editorService?.moveLayer(-1);
98
+ },
99
+ },
100
+ {
101
+ type: 'button',
102
+ text: '置顶',
103
+ display: () => !isPage.value,
104
+ handler: () => {
105
+ editorService?.moveLayer(LayerOffset.TOP);
106
+ },
107
+ },
108
+ {
109
+ type: 'button',
110
+ text: '置底',
111
+ display: () => !isPage.value,
112
+ handler: () => {
113
+ editorService?.moveLayer(LayerOffset.BOTTOM);
114
+ },
115
+ },
116
+ {
117
+ type: 'divider',
118
+ direction: 'horizontal',
119
+ display: () => !isPage.value,
120
+ },
121
+ {
122
+ type: 'button',
123
+ text: '删除',
124
+ icon: Delete,
125
+ display: () => !isPage.value,
126
+ handler: () => {
127
+ node.value && editorService?.remove(node.value);
128
+ },
129
+ },
130
+ {
131
+ type: 'divider',
132
+ direction: 'horizontal',
133
+ },
134
+ {
135
+ type: 'button',
136
+ text: '清空参考线',
137
+ handler: () => {
138
+ editorService?.get<StageCore>('stage').clearGuides();
139
+ },
140
+ },
141
+ ]),
142
+
143
+ show(e: MouseEvent) {
144
+ menu.value?.show(e);
108
145
  },
109
146
  };
110
147
  },