@tmagic/editor 1.3.0-alpha.5 → 1.3.0-alpha.7

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 (37) hide show
  1. package/dist/style.css +23 -5
  2. package/dist/tmagic-editor.js +502 -246
  3. package/dist/tmagic-editor.js.map +1 -1
  4. package/dist/tmagic-editor.umd.cjs +505 -247
  5. package/dist/tmagic-editor.umd.cjs.map +1 -1
  6. package/package.json +10 -10
  7. package/src/Editor.vue +5 -0
  8. package/src/components/ContentMenu.vue +15 -8
  9. package/src/components/FunctionEditor.vue +6 -4
  10. package/src/{layouts → components}/Resizer.vue +15 -6
  11. package/src/components/{Layout.vue → SplitView.vue} +1 -1
  12. package/src/fields/CodeLink.vue +4 -2
  13. package/src/index.ts +5 -3
  14. package/src/initService.ts +2 -0
  15. package/src/layouts/Framework.vue +6 -5
  16. package/src/layouts/sidebar/LayerPanel.vue +58 -22
  17. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +3 -3
  18. package/src/layouts/workspace/Stage.vue +13 -3
  19. package/src/layouts/workspace/Workspace.vue +4 -133
  20. package/src/services/codeBlock.ts +3 -2
  21. package/src/services/keybinding.ts +185 -0
  22. package/src/services/storage.ts +3 -2
  23. package/src/theme/resizer.scss +25 -10
  24. package/src/theme/stage.scss +4 -0
  25. package/src/type.ts +59 -1
  26. package/src/utils/config.ts +1 -1
  27. package/src/utils/keybinding-config.ts +120 -0
  28. package/types/components/ContentMenu.vue.d.ts +2 -0
  29. package/types/index.d.ts +3 -2
  30. package/types/initService.d.ts +1 -1
  31. package/types/layouts/workspace/Workspace.vue.d.ts +1 -1
  32. package/types/services/keybinding.d.ts +20 -0
  33. package/types/type.d.ts +56 -1
  34. package/types/utils/config.d.ts +1 -1
  35. package/types/utils/keybinding-config.d.ts +3 -0
  36. /package/types/{layouts → components}/Resizer.vue.d.ts +0 -0
  37. /package/types/components/{Layout.vue.d.ts → SplitView.vue.d.ts} +0 -0
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.0-alpha.5",
2
+ "version": "1.3.0-alpha.7",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -46,13 +46,13 @@
46
46
  "dependencies": {
47
47
  "@babel/core": "^7.18.0",
48
48
  "@element-plus/icons-vue": "^2.0.9",
49
- "@tmagic/core": "1.3.0-alpha.5",
50
- "@tmagic/design": "1.3.0-alpha.5",
51
- "@tmagic/form": "1.3.0-alpha.5",
52
- "@tmagic/schema": "1.3.0-alpha.5",
53
- "@tmagic/stage": "1.3.0-alpha.5",
54
- "@tmagic/table": "1.3.0-alpha.5",
55
- "@tmagic/utils": "1.3.0-alpha.5",
49
+ "@tmagic/core": "1.3.0-alpha.7",
50
+ "@tmagic/design": "1.3.0-alpha.7",
51
+ "@tmagic/form": "1.3.0-alpha.7",
52
+ "@tmagic/schema": "1.3.0-alpha.7",
53
+ "@tmagic/stage": "1.3.0-alpha.7",
54
+ "@tmagic/table": "1.3.0-alpha.7",
55
+ "@tmagic/utils": "1.3.0-alpha.7",
56
56
  "buffer": "^6.0.3",
57
57
  "color": "^3.1.3",
58
58
  "events": "^3.3.0",
@@ -64,8 +64,8 @@
64
64
  "vue": "^3.3.4"
65
65
  },
66
66
  "peerDependencies": {
67
- "@tmagic/design": "1.3.0-alpha.5",
68
- "@tmagic/form": "1.3.0-alpha.5",
67
+ "@tmagic/design": "1.3.0-alpha.7",
68
+ "@tmagic/form": "1.3.0-alpha.7",
69
69
  "monaco-editor": "^0.34.0",
70
70
  "vue": "^3.3.4"
71
71
  },
package/src/Editor.vue CHANGED
@@ -96,9 +96,11 @@ import depService from './services/dep';
96
96
  import editorService from './services/editor';
97
97
  import eventsService from './services/events';
98
98
  import historyService from './services/history';
99
+ import keybindingService from './services/keybinding';
99
100
  import propsService from './services/props';
100
101
  import storageService from './services/storage';
101
102
  import uiService from './services/ui';
103
+ import keybindingConfig from './utils/keybinding-config';
102
104
  import editorProps from './editorProps';
103
105
  import { initServiceEvents, initServiceState } from './initService';
104
106
  import type { Services } from './type';
@@ -130,10 +132,13 @@ export default defineComponent({
130
132
  codeBlockService,
131
133
  depService,
132
134
  dataSourceService,
135
+ keybindingService,
133
136
  };
134
137
 
135
138
  initServiceEvents(props, emit, services);
136
139
  initServiceState(props, services);
140
+ keybindingService.registe(keybindingConfig);
141
+ keybindingService.registeEl('global');
137
142
 
138
143
  provide('services', services);
139
144
 
@@ -12,6 +12,7 @@
12
12
  </div>
13
13
  <teleport to="body">
14
14
  <content-menu
15
+ v-if="subMenuData.length"
15
16
  class="sub-menu"
16
17
  ref="subMenu"
17
18
  :menu-data="subMenuData"
@@ -55,6 +56,8 @@ const menuStyle = ref({
55
56
  top: '0',
56
57
  });
57
58
 
59
+ const contains = (el: HTMLElement) => menu.value?.contains(el) || subMenu.value?.contains(el);
60
+
58
61
  const hide = () => {
59
62
  if (!visible.value) return;
60
63
 
@@ -69,7 +72,7 @@ const hideHandler = (e: MouseEvent) => {
69
72
  if (!visible.value || !target) {
70
73
  return;
71
74
  }
72
- if (menu.value?.contains(target) || subMenu.value?.$el?.contains(target)) {
75
+ if (contains(target)) {
73
76
  return;
74
77
  }
75
78
  hide();
@@ -104,13 +107,15 @@ const showSubMenu = (item: MenuButton | MenuComponent) => {
104
107
  return;
105
108
  }
106
109
 
107
- subMenuData.value = menuItem.items;
108
- if (menu.value) {
109
- subMenu.value.show({
110
- clientX: menu.value.offsetLeft + menu.value.clientWidth,
111
- clientY: menu.value.offsetTop,
112
- });
113
- }
110
+ subMenuData.value = menuItem.items || [];
111
+ setTimeout(() => {
112
+ if (menu.value) {
113
+ subMenu.value?.show({
114
+ clientX: menu.value.offsetLeft + menu.value.clientWidth,
115
+ clientY: menu.value.offsetTop,
116
+ });
117
+ }
118
+ }, 0);
114
119
  };
115
120
 
116
121
  onMounted(() => {
@@ -126,7 +131,9 @@ onUnmounted(() => {
126
131
  });
127
132
 
128
133
  defineExpose({
134
+ menu,
129
135
  hide,
130
136
  show,
137
+ contains,
131
138
  });
132
139
  </script>
@@ -33,7 +33,7 @@
33
33
  </TMagicCard>
34
34
  </template>
35
35
  <script lang="ts" setup>
36
- import { inject, ref, watchEffect } from 'vue';
36
+ import { inject, provide, ref, watchEffect } from 'vue';
37
37
  import { cloneDeep } from 'lodash-es';
38
38
 
39
39
  import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
@@ -41,6 +41,7 @@ import { ColumnConfig, TableConfig } from '@tmagic/form';
41
41
  import { CodeParam, Id } from '@tmagic/schema';
42
42
 
43
43
  import type { Services } from '@editor/type';
44
+ import { getConfig } from '@editor/utils/config';
44
45
 
45
46
  import CodeDraftEditor from './CodeDraftEditor.vue';
46
47
 
@@ -48,6 +49,8 @@ defineOptions({
48
49
  name: 'MEditorFunctionEditor',
49
50
  });
50
51
 
52
+ provide('mForm', {});
53
+
51
54
  const defaultParamColConfig: ColumnConfig = {
52
55
  type: 'row',
53
56
  label: '参数类型',
@@ -147,9 +150,8 @@ initTableModel();
147
150
  // 保存前钩子
148
151
  const beforeSave = (codeValue: string): boolean => {
149
152
  try {
150
- // eval检测js代码是否存在语法错误
151
- // eslint-disable-next-line no-eval
152
- eval(codeValue);
153
+ // 检测js代码是否存在语法错误
154
+ getConfig('parseDSL')(codeValue);
153
155
  return true;
154
156
  } catch (e: any) {
155
157
  tMagicMessage.error(e.stack);
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <span ref="target" class="m-editor-resizer">
2
+ <span ref="target" class="m-editor-resizer" :class="{ 'm-editor-resizer-draging': isDraging }">
3
3
  <slot></slot>
4
4
  </span>
5
5
  </template>
@@ -15,6 +15,7 @@ defineOptions({
15
15
  const emit = defineEmits(['change']);
16
16
 
17
17
  const target = ref<HTMLSpanElement>();
18
+ const isDraging = ref(false);
18
19
 
19
20
  let getso: Gesto;
20
21
 
@@ -23,14 +24,22 @@ onMounted(() => {
23
24
  getso = new Gesto(target.value, {
24
25
  container: window,
25
26
  pinchOutside: true,
26
- }).on('drag', (e) => {
27
- if (!target.value) return;
28
-
29
- emit('change', e.deltaX);
30
- });
27
+ })
28
+ .on('drag', (e) => {
29
+ if (!target.value) return;
30
+
31
+ emit('change', e.deltaX);
32
+ })
33
+ .on('dragStart', () => {
34
+ isDraging.value = true;
35
+ })
36
+ .on('dragEnd', () => {
37
+ isDraging.value = false;
38
+ });
31
39
  });
32
40
 
33
41
  onUnmounted(() => {
34
42
  getso?.unset();
43
+ isDraging.value = false;
35
44
  });
36
45
  </script>
@@ -23,7 +23,7 @@
23
23
  <script lang="ts" setup>
24
24
  import { computed, onMounted, onUnmounted, ref } from 'vue';
25
25
 
26
- import Resizer from '@editor/layouts/Resizer.vue';
26
+ import Resizer from './Resizer.vue';
27
27
 
28
28
  defineOptions({
29
29
  name: 'MEditorLayout',
@@ -6,6 +6,8 @@
6
6
  import { computed, reactive, watch } from 'vue';
7
7
  import serialize from 'serialize-javascript';
8
8
 
9
+ import { getConfig } from '@editor/utils/config';
10
+
9
11
  defineOptions({
10
12
  name: 'MEditorCodeLink',
11
13
  });
@@ -69,8 +71,8 @@ const changeHandler = (v: Record<string, any>) => {
69
71
  if (!props.name || !props.model) return;
70
72
 
71
73
  try {
72
- // eslint-disable-next-line no-eval
73
- props.model[props.name] = eval(`(${v[props.name]})`);
74
+ const parseDSL = getConfig('parseDSL');
75
+ props.model[props.name] = parseDSL(`(${v[props.name]})`);
74
76
  emit('change', props.model[props.name]);
75
77
  } catch (e) {
76
78
  console.error(e);
package/src/index.ts CHANGED
@@ -60,14 +60,16 @@ export { default as PropsPanel } from './layouts/PropsPanel.vue';
60
60
  export { default as ToolButton } from './components/ToolButton.vue';
61
61
  export { default as ContentMenu } from './components/ContentMenu.vue';
62
62
  export { default as Icon } from './components/Icon.vue';
63
- export { default as LayoutContainer } from './components/Layout.vue';
63
+ export { default as LayoutContainer } from './components/SplitView.vue';
64
+ export { default as SplitView } from './components/SplitView.vue';
64
65
 
65
66
  const defaultInstallOpt: InstallOptions = {
66
- // @todo, 自定义图片上传方法等编辑器依赖的外部选项
67
+ // eslint-disable-next-line no-eval
68
+ parseDSL: (dsl: string) => eval(dsl),
67
69
  };
68
70
 
69
71
  export default {
70
- install: (app: App, opt?: InstallOptions): void => {
72
+ install: (app: App, opt?: Partial<InstallOptions>): void => {
71
73
  const option = Object.assign(defaultInstallOpt, opt || {});
72
74
 
73
75
  // eslint-disable-next-line no-param-reassign
@@ -25,6 +25,7 @@ export const initServiceState = (
25
25
  eventsService,
26
26
  uiService,
27
27
  codeBlockService,
28
+ keybindingService,
28
29
  }: Services,
29
30
  ) => {
30
31
  // 初始值变化,重新设置节点信息
@@ -104,6 +105,7 @@ export const initServiceState = (
104
105
  uiService.resetState();
105
106
  componentListService.resetState();
106
107
  codeBlockService.resetState();
108
+ keybindingService.reset();
107
109
  });
108
110
  };
109
111
 
@@ -10,7 +10,7 @@
10
10
  <CodeEditor class="m-editor-content" :init-values="root" :options="codeOptions" @save="saveCode"></CodeEditor>
11
11
  </slot>
12
12
 
13
- <Layout
13
+ <SplitView
14
14
  v-else
15
15
  class="m-editor-content"
16
16
  left-class="m-editor-framework-left"
@@ -38,7 +38,7 @@
38
38
  <slot name="props-panel"></slot>
39
39
  </TMagicScrollbar>
40
40
  </template>
41
- </Layout>
41
+ </SplitView>
42
42
 
43
43
  <slot name="content-after"></slot>
44
44
  <slot name="footer"></slot>
@@ -50,8 +50,9 @@ import { computed, inject, ref, watch } from 'vue';
50
50
 
51
51
  import { TMagicScrollbar } from '@tmagic/design';
52
52
 
53
- import Layout from '@editor/components/Layout.vue';
53
+ import SplitView from '@editor/components/SplitView.vue';
54
54
  import type { GetColumnWidth, Services } from '@editor/type';
55
+ import { getConfig } from '@editor/utils/config';
55
56
 
56
57
  import AddPageBox from './AddPageBox.vue';
57
58
  import CodeEditor from './CodeEditor.vue';
@@ -131,8 +132,8 @@ const columnWidthChange = (columnW: GetColumnWidth) => {
131
132
 
132
133
  const saveCode = (value: string) => {
133
134
  try {
134
- // eslint-disable-next-line no-eval
135
- editorService?.set('root', eval(value));
135
+ const parseDSL = getConfig('parseDSL');
136
+ editorService?.set('root', parseDSL(value));
136
137
  } catch (e: any) {
137
138
  console.error(e);
138
139
  }
@@ -10,6 +10,7 @@
10
10
  ref="tree"
11
11
  node-key="id"
12
12
  empty-text="页面空荡荡的"
13
+ tabindex="-1"
13
14
  draggable
14
15
  :default-expanded-keys="expandedKeys"
15
16
  :default-checked-keys="checkedKeys"
@@ -49,8 +50,7 @@
49
50
  </template>
50
51
 
51
52
  <script lang="ts" setup>
52
- import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue';
53
- import KeyController from 'keycon';
53
+ import { computed, inject, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from 'vue';
54
54
  import { difference, throttle, union } from 'lodash-es';
55
55
 
56
56
  import { TMagicScrollbar, TMagicTree } from '@tmagic/design';
@@ -75,6 +75,7 @@ defineProps<{
75
75
  const throttleTime = 150;
76
76
  const services = inject<Services>('services');
77
77
  const editorService = services?.editorService;
78
+ const keybindingService = services?.keybindingService;
78
79
 
79
80
  const tree = ref<InstanceType<typeof TMagicTree>>();
80
81
  const menu = ref<InstanceType<typeof LayerMenu>>();
@@ -246,34 +247,69 @@ const windowBlurHandler = () => {
246
247
  isCtrlKeyDown.value = false;
247
248
  };
248
249
 
249
- let keycon: KeyController;
250
+ keybindingService?.registeCommand('layer-panel-not-ctrl-keydown', (e) => {
251
+ if (e.key !== keybindingService.ctrlKey) {
252
+ isCtrlKeyDown.value = false;
253
+ }
254
+ });
255
+
256
+ keybindingService?.registeCommand('layer-panel-ctrl-keydown', () => {
257
+ isCtrlKeyDown.value = true;
258
+ });
259
+
260
+ keybindingService?.registeCommand('layer-panel-ctrl-keyup', () => {
261
+ isCtrlKeyDown.value = false;
262
+ });
263
+
264
+ keybindingService?.registeCommand('layer-panel-global-keydwon', () => {
265
+ if (!tree.value?.$el.contains(document.activeElement)) {
266
+ isCtrlKeyDown.value = false;
267
+ }
268
+ });
269
+
270
+ keybindingService?.registe([
271
+ {
272
+ command: 'layer-panel-not-ctrl-keydown',
273
+ when: [['layer-panel', 'keydown']],
274
+ },
275
+ {
276
+ command: 'layer-panel-ctrl-keydown',
277
+ keybinding: 'ctrl',
278
+ when: [['layer-panel', 'keydown']],
279
+ },
280
+ {
281
+ command: 'layer-panel-ctrl-keyup',
282
+ keybinding: 'ctrl',
283
+ when: [['layer-panel', 'keyup']],
284
+ },
285
+ {
286
+ command: 'layer-panel-global-keydwon',
287
+ keybinding: 'ctrl',
288
+ when: [['global', 'keydown']],
289
+ },
290
+ ]);
291
+
292
+ watch(tree, () => {
293
+ if (tree.value?.$el) {
294
+ keybindingService?.registeEl('layer-panel', tree.value.$el);
295
+
296
+ tree.value.$el.addEventListener('blur', windowBlurHandler);
297
+ } else {
298
+ keybindingService?.unregisteEl('layer-panel');
299
+ }
300
+ });
250
301
 
251
302
  onMounted(() => {
252
303
  editorService?.on('remove', editorServiceRemoveHandler);
253
304
 
254
- keycon = new KeyController();
255
- const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
256
- const ctrl = isMac ? 'meta' : 'ctrl';
257
-
258
- keycon
259
- .keydown((e) => {
260
- if (e.key !== ctrl) {
261
- isCtrlKeyDown.value = false;
262
- }
263
- })
264
- .keydown(ctrl, () => {
265
- isCtrlKeyDown.value = true;
266
- })
267
- .keyup(ctrl, () => {
268
- isCtrlKeyDown.value = false;
269
- });
270
-
271
305
  globalThis.addEventListener('blur', windowBlurHandler);
272
306
  });
273
307
 
274
- onUnmounted(() => {
275
- keycon.destroy();
308
+ onBeforeUnmount(() => {
309
+ tree.value?.$el.removeEventListener('blur', windowBlurHandler);
310
+ });
276
311
 
312
+ onUnmounted(() => {
277
313
  editorService?.off('remove', editorServiceRemoveHandler);
278
314
  globalThis.removeEventListener('blur', windowBlurHandler);
279
315
  });
@@ -10,7 +10,7 @@
10
10
  :size="size"
11
11
  :before-close="handleClose"
12
12
  >
13
- <Layout v-model:left="left" :min-left="45" class="code-editor-layout">
13
+ <SplitView v-model:left="left" :min-left="45" class="code-editor-layout">
14
14
  <!-- 右侧区域 -->
15
15
  <template #center>
16
16
  <div v-if="!isEmpty(codeConfig)" class="m-editor-code-block-editor-panel">
@@ -28,7 +28,7 @@
28
28
  ></FunctionEditor>
29
29
  </div>
30
30
  </template>
31
- </Layout>
31
+ </SplitView>
32
32
  </TMagicDrawer>
33
33
  </template>
34
34
 
@@ -41,7 +41,7 @@ import { ColumnConfig } from '@tmagic/form';
41
41
  import { CodeBlockContent } from '@tmagic/schema';
42
42
 
43
43
  import FunctionEditor from '@editor/components/FunctionEditor.vue';
44
- import Layout from '@editor/components/Layout.vue';
44
+ import SplitView from '@editor/components/SplitView.vue';
45
45
  import type { ListState, Services } from '@editor/type';
46
46
  import { serializeConfig } from '@editor/utils/editor';
47
47
 
@@ -2,6 +2,7 @@
2
2
  <ScrollViewer
3
3
  class="m-editor-stage"
4
4
  ref="stageWrap"
5
+ tabindex="-1"
5
6
  :width="stageRect?.width"
6
7
  :height="stageRect?.height"
7
8
  :wrap-width="stageContainerRect?.width"
@@ -35,6 +36,7 @@ import StageCore, { calcValueByFontsize, getOffset, Runtime } from '@tmagic/stag
35
36
 
36
37
  import ScrollViewer from '@editor/components/ScrollViewer.vue';
37
38
  import { Layout, MenuButton, MenuComponent, Services, StageOptions } from '@editor/type';
39
+ import { getConfig } from '@editor/utils/config';
38
40
  import { useStage } from '@editor/utils/stage';
39
41
 
40
42
  import ViewerMenu from './ViewerMenu.vue';
@@ -74,6 +76,10 @@ watchEffect(() => {
74
76
 
75
77
  stage = useStage(stageOptions);
76
78
 
79
+ stage.on('select', () => {
80
+ stageWrap.value?.container?.focus();
81
+ });
82
+
77
83
  services?.editorService.set('stage', markRaw(stage));
78
84
 
79
85
  stage?.mount(stageContainer.value);
@@ -120,13 +126,17 @@ const resizeObserver = new ResizeObserver((entries) => {
120
126
  });
121
127
 
122
128
  onMounted(() => {
123
- stageWrap.value?.container && resizeObserver.observe(stageWrap.value.container);
129
+ if (stageWrap.value?.container) {
130
+ resizeObserver.observe(stageWrap.value.container);
131
+ services?.keybindingService.registeEl('stage', stageWrap.value.container);
132
+ }
124
133
  });
125
134
 
126
135
  onUnmounted(() => {
127
136
  stage?.destroy();
128
137
  resizeObserver.disconnect();
129
138
  services?.editorService.set('stage', null);
139
+ services?.keybindingService.unregisteEl('stage');
130
140
  });
131
141
 
132
142
  const contextmenuHandler = (e: MouseEvent) => {
@@ -153,8 +163,8 @@ const dropHandler = async (e: DragEvent) => {
153
163
  }
154
164
 
155
165
  if (e.dataTransfer && parent && stageContainer.value && stage) {
156
- // eslint-disable-next-line no-eval
157
- const config = eval(`(${e.dataTransfer.getData('data')})`);
166
+ const parseDSL = getConfig('parseDSL');
167
+ const config = parseDSL(`(${e.dataTransfer.getData('data')})`);
158
168
  const layout = await services?.editorService.getLayout(parent);
159
169
 
160
170
  const containerRect = stageContainer.value.getBoundingClientRect();
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="m-editor-workspace" tabindex="-1" ref="workspace">
2
+ <div class="m-editor-workspace">
3
3
  <Breadcrumb></Breadcrumb>
4
4
 
5
5
  <slot name="stage">
@@ -16,12 +16,9 @@
16
16
  </template>
17
17
 
18
18
  <script lang="ts" setup>
19
- import { computed, inject, onMounted, onUnmounted, ref } from 'vue';
20
- import KeyController from 'keycon';
19
+ import { computed, inject } from 'vue';
21
20
 
22
- import { isPage } from '@tmagic/utils';
23
-
24
- import type { MenuButton, MenuComponent, Services } from '@editor/type';
21
+ import { MenuButton, MenuComponent, Services } from '@editor/type';
25
22
 
26
23
  import Breadcrumb from './Breadcrumb.vue';
27
24
  import PageBar from './PageBar.vue';
@@ -36,132 +33,6 @@ defineProps<{
36
33
  }>();
37
34
 
38
35
  const services = inject<Services>('services');
39
- const workspace = ref<HTMLDivElement>();
40
- const nodes = computed(() => services?.editorService.get('nodes'));
41
- const page = computed(() => services?.editorService.get('page'));
42
-
43
- const mouseenterHandler = () => {
44
- workspace.value?.focus();
45
- };
46
-
47
- const mouseleaveHandler = () => {
48
- workspace.value?.blur();
49
- };
50
-
51
- let keycon: KeyController;
52
-
53
- onMounted(() => {
54
- workspace.value?.addEventListener('mouseenter', mouseenterHandler);
55
- workspace.value?.addEventListener('mouseleave', mouseleaveHandler);
56
-
57
- keycon = new KeyController(workspace.value);
58
36
 
59
- const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
60
-
61
- const ctrl = isMac ? 'meta' : 'ctrl';
62
-
63
- keycon
64
- .keyup('delete', (e) => {
65
- e.inputEvent.preventDefault();
66
- if (!nodes.value || isPage(nodes.value[0])) return;
67
- services?.editorService.remove(nodes.value);
68
- })
69
- .keyup('backspace', (e) => {
70
- e.inputEvent.preventDefault();
71
- if (!nodes.value || isPage(nodes.value[0])) return;
72
- services?.editorService.remove(nodes.value);
73
- })
74
- .keydown([ctrl, 'c'], (e) => {
75
- e.inputEvent.preventDefault();
76
- nodes.value && services?.editorService.copy(nodes.value);
77
- })
78
- .keydown([ctrl, 'v'], (e) => {
79
- e.inputEvent.preventDefault();
80
- nodes.value && services?.editorService.paste({ offsetX: 10, offsetY: 10 });
81
- })
82
- .keydown([ctrl, 'x'], (e) => {
83
- e.inputEvent.preventDefault();
84
- if (!nodes.value || isPage(nodes.value[0])) return;
85
- services?.editorService.copy(nodes.value);
86
- services?.editorService.remove(nodes.value);
87
- })
88
- .keydown([ctrl, 'z'], (e) => {
89
- e.inputEvent.preventDefault();
90
- services?.editorService.undo();
91
- })
92
- .keydown([ctrl, 'shift', 'z'], (e) => {
93
- e.inputEvent.preventDefault();
94
- services?.editorService.redo();
95
- })
96
- .keydown('up', (e) => {
97
- e.inputEvent.preventDefault();
98
- services?.editorService.move(0, -1);
99
- })
100
- .keydown('down', (e) => {
101
- e.inputEvent.preventDefault();
102
- services?.editorService.move(0, 1);
103
- })
104
- .keydown('left', (e) => {
105
- e.inputEvent.preventDefault();
106
- services?.editorService.move(-1, 0);
107
- })
108
- .keydown('right', (e) => {
109
- e.inputEvent.preventDefault();
110
- services?.editorService.move(1, 0);
111
- })
112
- .keydown([ctrl, 'up'], (e) => {
113
- e.inputEvent.preventDefault();
114
- services?.editorService.move(0, -10);
115
- })
116
- .keydown([ctrl, 'down'], (e) => {
117
- e.inputEvent.preventDefault();
118
- services?.editorService.move(0, 10);
119
- })
120
- .keydown([ctrl, 'left'], (e) => {
121
- e.inputEvent.preventDefault();
122
- services?.editorService.move(-10, 0);
123
- })
124
- .keydown([ctrl, 'right'], (e) => {
125
- e.inputEvent.preventDefault();
126
- services?.editorService.move(10, 0);
127
- })
128
- .keydown('tab', (e) => {
129
- e.inputEvent.preventDefault();
130
- services?.editorService.selectNextNode();
131
- })
132
- .keydown([ctrl, 'tab'], (e) => {
133
- e.inputEvent.preventDefault();
134
- services?.editorService.selectNextPage();
135
- })
136
- .keydown([ctrl, '='], (e) => {
137
- e.inputEvent.preventDefault();
138
- services?.uiService.zoom(0.1);
139
- })
140
- .keydown([ctrl, 'numpadplus'], (e) => {
141
- e.inputEvent.preventDefault();
142
- services?.uiService.zoom(0.1);
143
- })
144
- .keydown([ctrl, '-'], (e) => {
145
- e.inputEvent.preventDefault();
146
- services?.uiService.zoom(-0.1);
147
- })
148
- .keydown([ctrl, 'numpad-'], (e) => {
149
- e.inputEvent.preventDefault();
150
- services?.uiService.zoom(-0.1);
151
- })
152
- .keydown([ctrl, '0'], async (e) => {
153
- e.inputEvent.preventDefault();
154
- services?.uiService.set('zoom', await services.uiService.calcZoom());
155
- })
156
- .keydown([ctrl, '1'], (e) => {
157
- e.inputEvent.preventDefault();
158
- services?.uiService.set('zoom', 1);
159
- });
160
- });
161
-
162
- onUnmounted(() => {
163
- workspace.value?.removeEventListener('mouseenter', mouseenterHandler);
164
- workspace.value?.removeEventListener('mouseleave', mouseleaveHandler);
165
- keycon.destroy();
166
- });
37
+ const page = computed(() => services?.editorService.get('page'));
167
38
  </script>