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

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 (117) hide show
  1. package/README.md +1 -0
  2. package/coverage/clover.xml +1560 -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 +787 -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 +304 -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 +757 -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 +586 -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 +3708 -0
  55. package/dist/style.css +65 -33
  56. package/dist/tmagic-editor.es.js +1379 -806
  57. package/dist/tmagic-editor.es.js.map +1 -1
  58. package/dist/tmagic-editor.umd.js +1381 -808
  59. package/dist/tmagic-editor.umd.js.map +1 -1
  60. package/dist/types/src/Editor.vue.d.ts +1 -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/PropsPanel.vue.d.ts +2 -1
  66. package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
  67. package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +2 -3
  68. package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +14 -0
  69. package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +10 -3
  70. package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +4 -7
  71. package/dist/types/src/services/BaseService.d.ts +4 -1
  72. package/dist/types/src/services/editor.d.ts +5 -2
  73. package/dist/types/src/services/history.d.ts +1 -0
  74. package/dist/types/src/services/props.d.ts +2 -2
  75. package/dist/types/src/services/ui.d.ts +2 -0
  76. package/dist/types/src/type.d.ts +15 -9
  77. package/dist/types/src/utils/editor.d.ts +4 -3
  78. package/dist/types/src/utils/polyfills.d.ts +0 -0
  79. package/package.json +36 -12
  80. package/src/components/ContentMenu.vue +107 -0
  81. package/src/components/ScrollViewer.vue +1 -1
  82. package/src/components/ToolButton.vue +69 -30
  83. package/src/fields/UISelect.vue +14 -6
  84. package/src/index.ts +2 -0
  85. package/src/layouts/CodeEditor.vue +20 -10
  86. package/src/layouts/Framework.vue +6 -6
  87. package/src/layouts/PropsPanel.vue +4 -3
  88. package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
  89. package/src/layouts/sidebar/LayerMenu.vue +91 -96
  90. package/src/layouts/sidebar/LayerPanel.vue +31 -54
  91. package/src/layouts/sidebar/Sidebar.vue +13 -52
  92. package/src/layouts/sidebar/TabPane.vue +68 -0
  93. package/src/layouts/workspace/PageBar.vue +136 -13
  94. package/src/layouts/workspace/Stage.vue +50 -75
  95. package/src/layouts/workspace/ViewerMenu.vue +104 -67
  96. package/src/layouts/workspace/Workspace.vue +106 -23
  97. package/src/services/BaseService.ts +70 -22
  98. package/src/services/editor.ts +148 -39
  99. package/src/services/history.ts +7 -0
  100. package/src/services/props.ts +3 -3
  101. package/src/services/ui.ts +35 -5
  102. package/src/theme/common/var.scss +1 -0
  103. package/src/theme/component-list-panel.scss +2 -0
  104. package/src/theme/content-menu.scss +33 -26
  105. package/src/theme/layer-panel.scss +2 -1
  106. package/src/theme/nav-menu.scss +6 -2
  107. package/src/theme/page-bar.scss +35 -3
  108. package/src/theme/props-panel.scss +13 -13
  109. package/src/type.ts +16 -9
  110. package/src/utils/editor.ts +31 -6
  111. package/{vite.config.ts → src/utils/polyfills.ts} +4 -12
  112. package/src/utils/props.ts +20 -1
  113. package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
  114. package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -979
  115. package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -193
  116. package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
  117. package/tsconfig.json +0 -9
@@ -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,21 +73,19 @@ export default defineComponent({
110
73
  },
111
74
  },
112
75
 
113
- emits: ['select', 'update', 'sort', 'highlight'],
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
- const highlightNode = computed(() => services?.editorService.get<MNode>('highlightNode'));
128
89
 
129
90
  let stage: StageCore | null = null;
130
91
  let runtime: Runtime | null = null;
@@ -139,10 +100,10 @@ export default defineComponent({
139
100
  render: props.render,
140
101
  runtimeUrl: props.runtimeUrl,
141
102
  zoom: zoom.value,
142
- canSelect: (el, stop) => {
103
+ canSelect: (el, event, stop) => {
143
104
  const elCanSelect = props.canSelect(el);
144
105
  // 在组件联动过程中不能再往下选择,返回并触发 ui-select
145
- if (uiSelectMode.value && elCanSelect) {
106
+ if (uiSelectMode.value && elCanSelect && event.type === 'mousedown') {
146
107
  document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
147
108
  return stop();
148
109
  }
@@ -152,24 +113,24 @@ export default defineComponent({
152
113
  moveableOptions: props.moveableOptions,
153
114
  });
154
115
 
155
- services?.editorService.set('stage', stage);
116
+ services?.editorService.set('stage', markRaw(stage));
156
117
 
157
118
  stage?.mount(stageContainer.value);
158
119
 
159
120
  stage?.on('select', (el: HTMLElement) => {
160
- emit('select', el);
121
+ services?.editorService.select(el.id);
161
122
  });
162
123
 
163
124
  stage?.on('highlight', (el: HTMLElement) => {
164
- emit('highlight', el);
125
+ services?.editorService.highlight(el.id);
165
126
  });
166
127
 
167
128
  stage?.on('update', (ev: UpdateEventData) => {
168
- emit('update', { id: ev.el.id, style: ev.style });
129
+ services?.editorService.update({ id: ev.el.id, style: ev.style });
169
130
  });
170
131
 
171
132
  stage?.on('sort', (ev: SortEventData) => {
172
- emit('sort', ev);
133
+ services?.editorService.sort(ev.src, ev.dist);
173
134
  });
174
135
 
175
136
  stage?.on('changeGuides', () => {
@@ -199,25 +160,6 @@ export default defineComponent({
199
160
  }
200
161
  });
201
162
 
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
163
  const resizeObserver = new ResizeObserver((entries) => {
222
164
  for (const { contentRect } of entries) {
223
165
  services?.uiService.set('stageContainerRect', {
@@ -240,9 +182,42 @@ export default defineComponent({
240
182
  return {
241
183
  stageWrap,
242
184
  stageContainer,
185
+ menu,
243
186
  stageRect,
244
187
  zoom,
245
- ...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
+ position: 'absolute',
213
+ top: e.clientY - containerRect.top + scrollTop,
214
+ left: e.clientX - containerRect.left + scrollLeft,
215
+ };
216
+ }
217
+
218
+ services?.editorService.add(config, page.value);
219
+ }
220
+ },
246
221
  };
247
222
  },
248
223
  });
@@ -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
  },
@@ -1,15 +1,11 @@
1
1
  <template>
2
- <div class="m-editor-workspace">
2
+ <div class="m-editor-workspace" tabindex="1" ref="workspace">
3
3
  <magic-stage
4
4
  :key="page?.id"
5
5
  :runtime-url="runtimeUrl"
6
6
  :render="render"
7
7
  :moveable-options="moveableOptions"
8
8
  :can-select="canSelect"
9
- @select="selectHandler"
10
- @highlight="highlightHandler"
11
- @update="updateNodeHandler"
12
- @sort="sortNodeHandler"
13
9
  ></magic-stage>
14
10
 
15
11
  <slot name="workspace-content"></slot>
@@ -22,11 +18,13 @@
22
18
  </template>
23
19
 
24
20
  <script lang="ts">
25
- import { computed, defineComponent, inject, PropType } from 'vue';
21
+ import { computed, defineComponent, inject, onMounted, onUnmounted, PropType, ref } from 'vue';
22
+ import KeyController from 'keycon';
26
23
 
27
- import type { MComponent, MContainer, MPage } from '@tmagic/schema';
28
- import type { MoveableOptions, SortEventData } from '@tmagic/stage';
24
+ import type { MNode, MPage } from '@tmagic/schema';
25
+ import type { MoveableOptions } from '@tmagic/stage';
29
26
  import StageCore from '@tmagic/stage';
27
+ import { isPage } from '@tmagic/utils';
30
28
 
31
29
  import type { Services } from '@editor/type';
32
30
 
@@ -59,25 +57,110 @@ export default defineComponent({
59
57
 
60
58
  setup() {
61
59
  const services = inject<Services>('services');
60
+ const workspace = ref<HTMLDivElement>();
61
+ const node = computed(() => services?.editorService.get<MNode>('node'));
62
+ let keycon: KeyController;
62
63
 
63
- return {
64
- page: computed(() => services?.editorService.get<MPage>('page')),
65
-
66
- selectHandler(el: HTMLElement) {
67
- services?.editorService.select(el.id);
68
- },
64
+ const mouseenterHandler = () => {
65
+ workspace.value?.focus();
66
+ };
69
67
 
70
- updateNodeHandler(node: MComponent | MContainer | MPage) {
71
- services?.editorService.update(node);
72
- },
68
+ onMounted(() => {
69
+ workspace.value?.addEventListener('mouseenter', mouseenterHandler);
70
+
71
+ keycon = new KeyController(workspace.value);
72
+
73
+ const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
74
+
75
+ const ctrl = isMac ? 'meta' : 'ctrl';
76
+
77
+ keycon
78
+ .keyup('delete', (e) => {
79
+ e.inputEvent.preventDefault();
80
+ if (!node.value || isPage(node.value)) return;
81
+ services?.editorService.remove(node.value);
82
+ })
83
+ .keydown([ctrl, 'c'], (e) => {
84
+ e.inputEvent.preventDefault();
85
+ node.value && services?.editorService.copy(node.value);
86
+ })
87
+ .keydown([ctrl, 'v'], (e) => {
88
+ e.inputEvent.preventDefault();
89
+ node.value && services?.editorService.paste();
90
+ })
91
+ .keydown([ctrl, 'x'], (e) => {
92
+ e.inputEvent.preventDefault();
93
+ if (!node.value || isPage(node.value)) return;
94
+ services?.editorService.copy(node.value);
95
+ services?.editorService.remove(node.value);
96
+ })
97
+ .keydown([ctrl, 'z'], (e) => {
98
+ e.inputEvent.preventDefault();
99
+ services?.editorService.undo();
100
+ })
101
+ .keydown([ctrl, 'shift', 'z'], (e) => {
102
+ e.inputEvent.preventDefault();
103
+ services?.editorService.redo();
104
+ })
105
+ .keydown('up', (e) => {
106
+ e.inputEvent.preventDefault();
107
+ services?.editorService.move(0, -1);
108
+ })
109
+ .keydown('down', (e) => {
110
+ e.inputEvent.preventDefault();
111
+ services?.editorService.move(0, 1);
112
+ })
113
+ .keydown('left', (e) => {
114
+ e.inputEvent.preventDefault();
115
+ services?.editorService.move(-1, 0);
116
+ })
117
+ .keydown('right', (e) => {
118
+ e.inputEvent.preventDefault();
119
+ services?.editorService.move(1, 0);
120
+ })
121
+ .keydown([ctrl, 'up'], (e) => {
122
+ e.inputEvent.preventDefault();
123
+ services?.editorService.move(0, -10);
124
+ })
125
+ .keydown([ctrl, 'down'], (e) => {
126
+ e.inputEvent.preventDefault();
127
+ services?.editorService.move(0, 10);
128
+ })
129
+ .keydown([ctrl, 'left'], (e) => {
130
+ e.inputEvent.preventDefault();
131
+ services?.editorService.move(-10, 0);
132
+ })
133
+ .keydown([ctrl, 'right'], (e) => {
134
+ e.inputEvent.preventDefault();
135
+ services?.editorService.move(10, 0);
136
+ })
137
+ .keydown('tab', (e) => {
138
+ e.inputEvent.preventDefault();
139
+ services?.editorService.selectNextNode();
140
+ })
141
+ .keydown([ctrl, 'tab'], (e) => {
142
+ e.inputEvent.preventDefault();
143
+ services?.editorService.selectNextPage();
144
+ })
145
+ .keydown([ctrl, '='], (e) => {
146
+ e.inputEvent.preventDefault();
147
+ services?.uiService.zoom(0.1);
148
+ })
149
+ .keydown([ctrl, '-'], (e) => {
150
+ e.inputEvent.preventDefault();
151
+ services?.uiService.zoom(-0.1);
152
+ });
153
+ });
154
+
155
+ onUnmounted(() => {
156
+ workspace.value?.removeEventListener('mouseenter', mouseenterHandler);
157
+ keycon.destroy();
158
+ });
73
159
 
74
- sortNodeHandler(ev: SortEventData) {
75
- services?.editorService.sort(ev.src, ev.dist);
76
- },
160
+ return {
161
+ workspace,
77
162
 
78
- highlightHandler(el: HTMLElement) {
79
- services?.editorService.highlight(el.id);
80
- },
163
+ page: computed(() => services?.editorService.get<MPage>('page')),
81
164
  };
82
165
  },
83
166
  });