@tmagic/editor 1.0.0-beta.8 → 1.0.0-rc.2

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 (121) 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 +802 -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 +760 -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 +598 -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 +91 -45
  56. package/dist/tmagic-editor.es.js +1462 -2003
  57. package/dist/tmagic-editor.es.js.map +1 -1
  58. package/dist/tmagic-editor.umd.js +1461 -2003
  59. package/dist/tmagic-editor.umd.js.map +1 -1
  60. package/dist/types/src/Editor.vue.d.ts +10 -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 +4 -6
  72. package/dist/types/src/services/BaseService.d.ts +4 -1
  73. package/dist/types/src/services/editor.d.ts +16 -6
  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 +16 -9
  78. package/dist/types/src/utils/editor.d.ts +5 -4
  79. package/dist/types/src/utils/polyfills.d.ts +0 -0
  80. package/package.json +44 -12
  81. package/src/Editor.vue +6 -1
  82. package/src/components/ContentMenu.vue +107 -0
  83. package/src/components/ScrollViewer.vue +1 -1
  84. package/src/components/ToolButton.vue +72 -31
  85. package/src/fields/UISelect.vue +14 -6
  86. package/src/index.ts +2 -0
  87. package/src/layouts/AddPageBox.vue +3 -1
  88. package/src/layouts/CodeEditor.vue +20 -13
  89. package/src/layouts/Framework.vue +20 -7
  90. package/src/layouts/PropsPanel.vue +5 -4
  91. package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
  92. package/src/layouts/sidebar/LayerMenu.vue +91 -96
  93. package/src/layouts/sidebar/LayerPanel.vue +80 -59
  94. package/src/layouts/sidebar/Sidebar.vue +13 -52
  95. package/src/layouts/sidebar/TabPane.vue +68 -0
  96. package/src/layouts/workspace/PageBar.vue +145 -17
  97. package/src/layouts/workspace/Stage.vue +56 -64
  98. package/src/layouts/workspace/ViewerMenu.vue +107 -68
  99. package/src/layouts/workspace/Workspace.vue +110 -18
  100. package/src/services/BaseService.ts +70 -22
  101. package/src/services/editor.ts +197 -64
  102. package/src/services/history.ts +7 -0
  103. package/src/services/props.ts +3 -3
  104. package/src/services/ui.ts +35 -5
  105. package/src/theme/common/var.scss +3 -2
  106. package/src/theme/component-list-panel.scss +2 -0
  107. package/src/theme/content-menu.scss +33 -26
  108. package/src/theme/layer-panel.scss +10 -0
  109. package/src/theme/nav-menu.scss +6 -2
  110. package/src/theme/page-bar.scss +42 -2
  111. package/src/theme/props-panel.scss +19 -17
  112. package/src/type.ts +17 -9
  113. package/src/utils/editor.ts +35 -9
  114. package/src/utils/polyfills.ts +8 -0
  115. package/src/utils/props.ts +20 -1
  116. package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
  117. package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -973
  118. package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -192
  119. package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
  120. package/tsconfig.json +0 -9
  121. 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"
@@ -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,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
 
@@ -110,19 +73,18 @@ export default defineComponent({
110
73
  },
111
74
  },
112
75
 
113
- emits: ['select', 'update', 'sort'],
114
-
115
- setup(props, { emit }) {
76
+ setup(props) {
116
77
  const services = inject<Services>('services');
117
78
 
118
79
  const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
119
80
  const stageContainer = ref<HTMLDivElement>();
81
+ const menu = ref<InstanceType<typeof ViewerMenu>>();
120
82
 
121
83
  const stageRect = computed(() => services?.uiService.get<StageRect>('stageRect'));
122
84
  const uiSelectMode = computed(() => services?.uiService.get<boolean>('uiSelectMode'));
123
85
  const root = computed(() => services?.editorService.get<MApp>('root'));
124
86
  const page = computed(() => services?.editorService.get<MPage>('page'));
125
- const zoom = computed(() => services?.uiService.get<number>('zoom'));
87
+ const zoom = computed(() => services?.uiService.get<number>('zoom') || 1);
126
88
  const node = computed(() => services?.editorService.get<MNode>('node'));
127
89
 
128
90
  let stage: StageCore | null = null;
@@ -138,10 +100,10 @@ export default defineComponent({
138
100
  render: props.render,
139
101
  runtimeUrl: props.runtimeUrl,
140
102
  zoom: zoom.value,
141
- canSelect: (el, stop) => {
103
+ canSelect: (el, event, stop) => {
142
104
  const elCanSelect = props.canSelect(el);
143
105
  // 在组件联动过程中不能再往下选择,返回并触发 ui-select
144
- if (uiSelectMode.value && elCanSelect) {
106
+ if (uiSelectMode.value && elCanSelect && event.type === 'mousedown') {
145
107
  document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
146
108
  return stop();
147
109
  }
@@ -151,18 +113,24 @@ export default defineComponent({
151
113
  moveableOptions: props.moveableOptions,
152
114
  });
153
115
 
154
- services?.editorService.set('stage', stage);
116
+ services?.editorService.set('stage', markRaw(stage));
155
117
 
156
118
  stage?.mount(stageContainer.value);
157
119
 
158
- stage?.on('select', (el: HTMLElement) => emit('select', el));
120
+ stage?.on('select', (el: HTMLElement) => {
121
+ services?.editorService.select(el.id);
122
+ });
123
+
124
+ stage?.on('highlight', (el: HTMLElement) => {
125
+ services?.editorService.highlight(el.id);
126
+ });
159
127
 
160
128
  stage?.on('update', (ev: UpdateEventData) => {
161
- emit('update', { id: ev.el.id, style: ev.style });
129
+ services?.editorService.update({ id: ev.el.id, style: ev.style });
162
130
  });
163
131
 
164
132
  stage?.on('sort', (ev: SortEventData) => {
165
- emit('sort', ev);
133
+ services?.editorService.sort(ev.src, ev.dist);
166
134
  });
167
135
 
168
136
  stage?.on('changeGuides', () => {
@@ -192,16 +160,6 @@ export default defineComponent({
192
160
  }
193
161
  });
194
162
 
195
- watch(
196
- () => node.value?.id,
197
- (id) => {
198
- nextTick(() => {
199
- // 等待相关dom变更完成后,再select,适用大多数场景
200
- id && stage?.select(id);
201
- });
202
- },
203
- );
204
-
205
163
  const resizeObserver = new ResizeObserver((entries) => {
206
164
  for (const { contentRect } of entries) {
207
165
  services?.uiService.set('stageContainerRect', {
@@ -224,9 +182,43 @@ export default defineComponent({
224
182
  return {
225
183
  stageWrap,
226
184
  stageContainer,
185
+ menu,
227
186
  stageRect,
228
187
  zoom,
229
- ...useMenu(),
188
+
189
+ contextmenuHandler(e: MouseEvent) {
190
+ e.preventDefault();
191
+ menu.value?.show(e);
192
+ },
193
+
194
+ dragoverHandler(e: DragEvent) {
195
+ e.preventDefault();
196
+ if (e.dataTransfer) {
197
+ e.dataTransfer.dropEffect = 'move';
198
+ }
199
+ },
200
+
201
+ async dropHandler(e: DragEvent) {
202
+ e.preventDefault();
203
+ if (e.dataTransfer && page.value && stageContainer.value && stage) {
204
+ // eslint-disable-next-line no-eval
205
+ const config = eval(`(${e.dataTransfer.getData('data')})`);
206
+ const layout = await services?.editorService.getLayout(page.value);
207
+
208
+ const containerRect = stageContainer.value.getBoundingClientRect();
209
+ const { scrollTop, scrollLeft } = stage.mask;
210
+ if (layout === Layout.ABSOLUTE) {
211
+ config.style = {
212
+ ...(config.style || {}),
213
+ position: 'absolute',
214
+ top: e.clientY - containerRect.top + scrollTop,
215
+ left: e.clientX - containerRect.left + scrollLeft,
216
+ };
217
+ }
218
+
219
+ services?.editorService.add(config, page.value);
220
+ }
221
+ },
230
222
  };
231
223
  },
232
224
  });
@@ -1,46 +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
 
9
+ import { NodeType } from '@tmagic/schema';
27
10
  import type StageCore from '@tmagic/stage';
28
11
 
29
- import { LayerOffset, Layout, Services } from '@editor/type';
12
+ import ContentMenu from '@editor/components/ContentMenu.vue';
13
+ import { LayerOffset, Layout, MenuItem, Services } from '@editor/type';
30
14
  import { COPY_STORAGE_KEY } from '@editor/utils/editor';
31
15
 
32
16
  export default defineComponent({
33
- name: 'magic-editor-ui-viewer-menu',
17
+ components: { ContentMenu },
34
18
 
35
19
  setup() {
36
20
  const services = inject<Services>('services');
37
21
  const editorService = services?.editorService;
38
- const menu = ref<HTMLDivElement>();
22
+ const menu = ref<InstanceType<typeof ContentMenu>>();
39
23
  const canPaste = ref(false);
40
24
  const canCenter = ref(false);
41
25
 
42
26
  const node = computed(() => editorService?.get('node'));
43
27
  const parent = computed(() => editorService?.get('parent'));
28
+ const isPage = computed(() => node.value?.type === NodeType.PAGE);
44
29
 
45
30
  onMounted(() => {
46
31
  const data = globalThis.localStorage.getItem(COPY_STORAGE_KEY);
@@ -53,56 +38,110 @@ export default defineComponent({
53
38
  if (!parent.value || !editorService) return (canCenter.value = false);
54
39
  const layout = await editorService.getLayout(parent.value);
55
40
  canCenter.value =
56
- [Layout.ABSOLUTE, Layout.FIXED].includes(layout) && !['app', 'page', 'pop'].includes(`${node.value?.type}`);
41
+ [Layout.ABSOLUTE, Layout.FIXED].includes(layout) &&
42
+ ![NodeType.ROOT, NodeType.PAGE, 'pop'].includes(`${node.value?.type}`);
57
43
  },
58
44
  { immediate: true },
59
45
  );
60
46
 
61
47
  return {
62
48
  menu,
63
- canPaste,
64
-
65
- canDelete: computed(() => node.value?.type !== 'page'),
66
- canMoveZPos: computed(() => node.value?.type !== 'page'),
67
- canCenter,
68
-
69
- center() {
70
- node.value && editorService?.alignCenter(node.value);
71
- },
72
-
73
- copy() {
74
- node.value && editorService?.copy(node.value);
75
- canPaste.value = true;
76
- },
77
-
78
- paste() {
79
- const top = menu.value?.offsetTop || 0;
80
- const left = menu.value?.offsetLeft || 0;
81
- editorService?.paste({ left, top });
82
- },
83
-
84
- remove() {
85
- node.value && editorService?.remove(node.value);
86
- },
87
-
88
- top() {
89
- editorService?.moveLayer(LayerOffset.TOP);
90
- },
91
-
92
- bottom() {
93
- editorService?.moveLayer(LayerOffset.BOTTOM);
94
- },
95
-
96
- topItem() {
97
- editorService?.moveLayer(1);
98
- },
99
-
100
- bottomItem() {
101
- editorService?.moveLayer(-1);
102
- },
103
-
104
- clearGuides() {
105
- 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);
106
145
  },
107
146
  };
108
147
  },