@tmagic/editor 1.0.0-beta.2 → 1.0.0-beta.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 (52) hide show
  1. package/package.json +8 -6
  2. package/src/Editor.vue +5 -5
  3. package/src/components/ScrollViewer.vue +65 -0
  4. package/src/layouts/CodeEditor.vue +35 -59
  5. package/src/layouts/workspace/Stage.vue +62 -50
  6. package/src/layouts/workspace/Workspace.vue +2 -21
  7. package/src/services/ui.ts +36 -2
  8. package/src/theme/stage.scss +7 -7
  9. package/src/type.ts +17 -6
  10. package/src/utils/scroll-viewer.ts +213 -0
  11. package/dist/style.css +0 -505
  12. package/dist/tmagic-editor.es.js +0 -4233
  13. package/dist/tmagic-editor.es.js.map +0 -1
  14. package/dist/tmagic-editor.umd.js +0 -4276
  15. package/dist/tmagic-editor.umd.js.map +0 -1
  16. package/dist/types/src/Editor.vue.d.ts +0 -136
  17. package/dist/types/src/components/Icon.vue.d.ts +0 -13
  18. package/dist/types/src/components/ToolButton.vue.d.ts +0 -35
  19. package/dist/types/src/fields/Code.vue.d.ts +0 -24
  20. package/dist/types/src/fields/CodeLink.vue.d.ts +0 -54
  21. package/dist/types/src/fields/UISelect.vue.d.ts +0 -32
  22. package/dist/types/src/index.d.ts +0 -19
  23. package/dist/types/src/layouts/AddPageBox.vue.d.ts +0 -5
  24. package/dist/types/src/layouts/CodeEditor.vue.d.ts +0 -46
  25. package/dist/types/src/layouts/Framework.vue.d.ts +0 -11
  26. package/dist/types/src/layouts/NavMenu.vue.d.ts +0 -24
  27. package/dist/types/src/layouts/PropsPanel.vue.d.ts +0 -13
  28. package/dist/types/src/layouts/Resizer.vue.d.ts +0 -13
  29. package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +0 -12
  30. package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
  31. package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -973
  32. package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +0 -27
  33. package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
  34. package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -153
  35. package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
  36. package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +0 -37
  37. package/dist/types/src/services/BaseService.d.ts +0 -56
  38. package/dist/types/src/services/editor.d.ts +0 -122
  39. package/dist/types/src/services/events.d.ts +0 -16
  40. package/dist/types/src/services/history.d.ts +0 -51
  41. package/dist/types/src/services/props.d.ts +0 -36
  42. package/dist/types/src/services/ui.d.ts +0 -26
  43. package/dist/types/src/shims-vue.d.ts +0 -6
  44. package/dist/types/src/type.d.ts +0 -183
  45. package/dist/types/src/utils/compose.d.ts +0 -5
  46. package/dist/types/src/utils/config.d.ts +0 -4
  47. package/dist/types/src/utils/editor.d.ts +0 -45
  48. package/dist/types/src/utils/index.d.ts +0 -4
  49. package/dist/types/src/utils/logger.d.ts +0 -5
  50. package/dist/types/src/utils/props.d.ts +0 -49
  51. package/dist/types/src/utils/undo-redo.d.ts +0 -12
  52. package/dist/types/src/vite-env.d.ts +0 -1
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
- "version": "1.0.0-beta.2",
2
+ "version": "1.0.0-beta.3",
3
3
  "name": "@tmagic/editor",
4
+ "sideEffects": false,
4
5
  "main": "dist/tmagic-editor.umd.js",
5
6
  "module": "dist/tmagic-editor.es.js",
6
7
  "style": "dist/style.css",
@@ -27,15 +28,16 @@
27
28
  },
28
29
  "dependencies": {
29
30
  "@element-plus/icons": "0.0.11",
30
- "@tmagic/core": "^1.0.0-beta.2",
31
- "@tmagic/form": "^1.0.0-beta.2",
32
- "@tmagic/schema": "^1.0.0-beta.2",
33
- "@tmagic/stage": "^1.0.0-beta.2",
34
- "@tmagic/utils": "^1.0.0-beta.2",
31
+ "@tmagic/core": "^1.0.0-beta.3",
32
+ "@tmagic/form": "^1.0.0-beta.3",
33
+ "@tmagic/schema": "^1.0.0-beta.3",
34
+ "@tmagic/stage": "^1.0.0-beta.3",
35
+ "@tmagic/utils": "^1.0.0-beta.3",
35
36
  "color": "^3.1.3",
36
37
  "element-plus": "^2.0.2",
37
38
  "events": "^3.3.0",
38
39
  "lodash-es": "^4.17.21",
40
+ "monaco-editor": "^0.32.1",
39
41
  "serialize-javascript": "^6.0.0",
40
42
  "vue": "^3.2.0"
41
43
  },
package/src/Editor.vue CHANGED
@@ -55,7 +55,7 @@ import eventsService from '@editor/services/events';
55
55
  import historyService from '@editor/services/history';
56
56
  import propsService from '@editor/services/props';
57
57
  import uiService from '@editor/services/ui';
58
- import type { ComponentGroup, MenuBarData, Services, SideBarData } from '@editor/type';
58
+ import type { ComponentGroup, MenuBarData, Services, SideBarData, StageRect } from '@editor/type';
59
59
 
60
60
  export default defineComponent({
61
61
  name: 'm-editor',
@@ -133,8 +133,8 @@ export default defineComponent({
133
133
  type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
134
134
  },
135
135
 
136
- stageStyle: {
137
- type: [String, Object] as PropType<Record<string, string | number>>,
136
+ stageRect: {
137
+ type: [String, Object] as PropType<StageRect>,
138
138
  },
139
139
  },
140
140
 
@@ -208,8 +208,8 @@ export default defineComponent({
208
208
  );
209
209
 
210
210
  watch(
211
- () => props.stageStyle,
212
- (stageStyle) => uiService.set('stageStyle', stageStyle),
211
+ () => props.stageRect,
212
+ (stageRect) => stageRect && uiService.set('stageRect', stageRect),
213
213
  {
214
214
  immediate: true,
215
215
  },
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div class="m-editor-scroll-viewer-container" ref="container">
3
+ <div ref="el" :style="style">
4
+ <slot></slot>
5
+ </div>
6
+ </div>
7
+ </template>
8
+
9
+ <script lang="ts">
10
+ import { computed, defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
11
+
12
+ import { ScrollViewer } from '@editor/utils/scroll-viewer';
13
+
14
+ export default defineComponent({
15
+ name: 'm-editor-scroll-viewer',
16
+
17
+ props: {
18
+ width: Number,
19
+ height: Number,
20
+ zoom: {
21
+ type: Number,
22
+ default: 1,
23
+ },
24
+ },
25
+
26
+ setup(props) {
27
+ const container = ref<HTMLDivElement>();
28
+ const el = ref<HTMLDivElement>();
29
+ let scrollViewer: ScrollViewer;
30
+
31
+ onMounted(() => {
32
+ if (!container.value || !el.value) return;
33
+ scrollViewer = new ScrollViewer({
34
+ container: container.value,
35
+ target: el.value,
36
+ zoom: props.zoom,
37
+ });
38
+ });
39
+
40
+ onUnmounted(() => {
41
+ scrollViewer.destroy();
42
+ });
43
+
44
+ watch(
45
+ () => props.zoom,
46
+ () => {
47
+ scrollViewer.setZoom(props.zoom);
48
+ },
49
+ );
50
+
51
+ return {
52
+ container,
53
+ el,
54
+
55
+ style: computed(
56
+ () => `
57
+ width: ${props.width}px;
58
+ height: ${props.height}px;
59
+ position: absolute;
60
+ `,
61
+ ),
62
+ };
63
+ },
64
+ });
65
+ </script>
@@ -4,28 +4,10 @@
4
4
 
5
5
  <script lang="ts">
6
6
  import { defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
7
+ import * as monaco from 'monaco-editor';
7
8
  import serialize from 'serialize-javascript';
8
9
 
9
- import { asyncLoadJs } from '@tmagic/utils';
10
-
11
- const initEditor = () => {
12
- if ((globalThis as any).monaco) {
13
- Promise.resolve((globalThis as any).monaco);
14
- }
15
-
16
- return asyncLoadJs(`https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/loader.min.js`).then(() => {
17
- (globalThis as any).require.config({
18
- paths: { vs: `https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs` },
19
- });
20
- return new Promise((resolve) => {
21
- (globalThis as any).require(['vs/editor/editor.main'], () => {
22
- resolve((globalThis as any).monaco);
23
- });
24
- });
25
- });
26
- };
27
-
28
- const toString = (v: any, language: string): string => {
10
+ const toString = (v: string | any, language: string): string => {
29
11
  let value = '';
30
12
  if (typeof v !== 'string') {
31
13
  value = serialize(v, {
@@ -64,60 +46,63 @@ export default defineComponent({
64
46
  },
65
47
  },
66
48
 
67
- emits: ['inited', 'save'],
49
+ emits: ['initd', 'save'],
68
50
 
69
51
  setup(props, { emit }) {
70
- let vsEditor: any = null;
52
+ let vsEditor: monaco.editor.IStandaloneCodeEditor | null = null;
53
+ let vsDiffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
71
54
  const values = ref('');
72
55
  const loading = ref(false);
73
56
  const codeEditor = ref<HTMLDivElement>();
74
57
 
75
- const setEditorValue = (v: any, m: any) => {
58
+ const setEditorValue = (v: string | any, m: string | any) => {
76
59
  values.value = toString(v, props.language);
77
60
 
78
61
  if (props.type === 'diff') {
79
- const originalModel = (globalThis as any).monaco.editor.createModel(values.value, 'text/javascript');
80
- const modifiedModel = (globalThis as any).monaco.editor.createModel(
81
- toString(m, props.language),
82
- 'text/javascript',
83
- );
62
+ const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
63
+ const modifiedModel = monaco.editor.createModel(toString(m, props.language), 'text/javascript');
84
64
 
85
- return vsEditor.setModel({
65
+ return vsDiffEditor?.setModel({
86
66
  original: originalModel,
87
67
  modified: modifiedModel,
88
68
  });
89
69
  }
90
70
 
91
- return vsEditor.setValue?.(values.value);
71
+ return vsEditor?.setValue(values.value);
92
72
  };
93
73
 
94
74
  const resizeHandler = () => {
95
75
  vsEditor?.layout();
76
+ vsDiffEditor?.layout();
96
77
  };
97
78
 
98
- const getEditorValue = () => vsEditor.getValue?.() || '';
79
+ const getEditorValue = () =>
80
+ props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue();
99
81
 
100
82
  const init = async () => {
101
83
  if (!codeEditor.value) return;
102
84
 
103
- vsEditor = (globalThis as any).monaco.editor[props.type === 'diff' ? 'createDiffEditor' : 'create'](
104
- codeEditor.value,
105
- {
106
- value: values.value,
107
- language: props.language,
108
- tabSize: 2,
109
- theme: 'vs-dark',
110
- fontFamily: 'dm, Menlo, Monaco, "Courier New", monospace',
111
- fontSize: 15,
112
- formatOnPaste: true,
113
- },
114
- );
85
+ const options = {
86
+ value: values.value,
87
+ language: props.language,
88
+ tabSize: 2,
89
+ theme: 'vs-dark',
90
+ fontFamily: 'dm, Menlo, Monaco, "Courier New", monospace',
91
+ fontSize: 15,
92
+ formatOnPaste: true,
93
+ };
94
+
95
+ if (props.type === 'diff') {
96
+ vsDiffEditor = monaco.editor.createDiffEditor(codeEditor.value, options);
97
+ } else {
98
+ vsEditor = monaco.editor.create(codeEditor.value, options);
99
+ }
115
100
 
116
101
  setEditorValue(props.initValues, props.modifiedValues);
117
102
 
118
103
  loading.value = false;
119
104
 
120
- emit('inited', vsEditor);
105
+ emit('initd', vsEditor);
121
106
 
122
107
  codeEditor.value.addEventListener('keydown', (e) => {
123
108
  if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
@@ -127,7 +112,7 @@ export default defineComponent({
127
112
  });
128
113
 
129
114
  if (props.type !== 'diff') {
130
- vsEditor.onDidBlurEditorWidget(() => {
115
+ vsEditor?.onDidBlurEditorWidget(() => {
131
116
  emit('save', getEditorValue());
132
117
  });
133
118
  }
@@ -138,7 +123,7 @@ export default defineComponent({
138
123
  watch(
139
124
  () => props.initValues,
140
125
  (v, preV) => {
141
- if (vsEditor && v !== preV) {
126
+ if (v !== preV) {
142
127
  setEditorValue(props.initValues, props.modifiedValues);
143
128
  }
144
129
  },
@@ -151,17 +136,7 @@ export default defineComponent({
151
136
  onMounted(async () => {
152
137
  loading.value = true;
153
138
 
154
- await initEditor();
155
- if (!(globalThis as any).monaco) {
156
- const interval = setInterval(() => {
157
- if ((globalThis as any).monaco) {
158
- clearInterval(interval);
159
- init();
160
- }
161
- }, 300);
162
- } else {
163
- init();
164
- }
139
+ init();
165
140
  });
166
141
 
167
142
  onUnmounted(() => {
@@ -174,13 +149,14 @@ export default defineComponent({
174
149
  codeEditor,
175
150
 
176
151
  getEditor() {
177
- return vsEditor;
152
+ return vsEditor || vsDiffEditor;
178
153
  },
179
154
 
180
155
  setEditorValue,
181
156
 
182
157
  focus() {
183
- vsEditor.focus();
158
+ vsEditor?.focus();
159
+ vsDiffEditor?.focus();
184
160
  },
185
161
  };
186
162
  },
@@ -1,15 +1,21 @@
1
1
  <template>
2
- <div class="m-editor-stage">
2
+ <scroll-viewer
3
+ class="m-editor-stage"
4
+ ref="stageWrap"
5
+ :width="stageRect?.width"
6
+ :height="stageRect?.height"
7
+ :zoom="zoom"
8
+ >
3
9
  <div
4
10
  class="m-editor-stage-container"
5
11
  ref="stageContainer"
6
- :style="stageStyle"
7
12
  @contextmenu="contextmenuHandler"
13
+ :style="`transform: scale(${zoom})`"
8
14
  ></div>
9
15
  <teleport to="body">
10
16
  <viewer-menu ref="menu" :style="menuStyle"></viewer-menu>
11
17
  </teleport>
12
- </div>
18
+ </scroll-viewer>
13
19
  </template>
14
20
 
15
21
  <script lang="ts">
@@ -17,6 +23,7 @@ import {
17
23
  computed,
18
24
  defineComponent,
19
25
  inject,
26
+ nextTick,
20
27
  onMounted,
21
28
  onUnmounted,
22
29
  PropType,
@@ -31,7 +38,8 @@ import type { MApp, MNode, MPage } from '@tmagic/schema';
31
38
  import type { MoveableOptions, Runtime, SortEventData, UpdateEventData } from '@tmagic/stage';
32
39
  import StageCore from '@tmagic/stage';
33
40
 
34
- import type { Services } from '@editor/type';
41
+ import ScrollViewer from '@editor/components/ScrollViewer.vue';
42
+ import type { Services, StageRect } from '@editor/type';
35
43
 
36
44
  import ViewerMenu from './ViewerMenu.vue';
37
45
 
@@ -79,6 +87,7 @@ export default defineComponent({
79
87
 
80
88
  components: {
81
89
  ViewerMenu,
90
+ ScrollViewer,
82
91
  },
83
92
 
84
93
  props: {
@@ -88,26 +97,6 @@ export default defineComponent({
88
97
 
89
98
  runtimeUrl: String,
90
99
 
91
- root: {
92
- type: Object as PropType<MApp>,
93
- },
94
-
95
- page: {
96
- type: Object as PropType<MPage>,
97
- },
98
-
99
- node: {
100
- type: Object as PropType<MNode>,
101
- },
102
-
103
- uiSelectMode: {
104
- type: Boolean,
105
- },
106
-
107
- zoom: {
108
- type: Number,
109
- },
110
-
111
100
  canSelect: {
112
101
  type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
113
102
  default: (el: HTMLElement) => Boolean(el.id),
@@ -125,12 +114,16 @@ export default defineComponent({
125
114
 
126
115
  setup(props, { emit }) {
127
116
  const services = inject<Services>('services');
117
+
118
+ const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
128
119
  const stageContainer = ref<HTMLDivElement>();
129
120
 
130
- const stageStyle = computed(() => ({
131
- ...services?.uiService.get<Record<string, string | number>>('stageStyle'),
132
- transform: `scale(${props.zoom}) translate3d(0, -50%, 0)`,
133
- }));
121
+ const stageRect = computed(() => services?.uiService.get<StageRect>('stageRect'));
122
+ const uiSelectMode = computed(() => services?.uiService.get<boolean>('uiSelectMode'));
123
+ const root = computed(() => services?.editorService.get<MApp>('root'));
124
+ const page = computed(() => services?.editorService.get<MPage>('page'));
125
+ const zoom = computed(() => services?.uiService.get<number>('zoom'));
126
+ const node = computed(() => services?.editorService.get<MNode>('node'));
134
127
 
135
128
  let stage: StageCore | null = null;
136
129
  let runtime: Runtime | null = null;
@@ -139,16 +132,16 @@ export default defineComponent({
139
132
  if (stage) return;
140
133
 
141
134
  if (!stageContainer.value) return;
142
- if (!(props.runtimeUrl || props.render) || !props.root) return;
135
+ if (!(props.runtimeUrl || props.render) || !root.value) return;
143
136
 
144
137
  stage = new StageCore({
145
138
  render: props.render,
146
139
  runtimeUrl: props.runtimeUrl,
147
- zoom: props.zoom,
140
+ zoom: zoom.value,
148
141
  canSelect: (el, stop) => {
149
142
  const elCanSelect = props.canSelect(el);
150
143
  // 在组件联动过程中不能再往下选择,返回并触发 ui-select
151
- if (props.uiSelectMode && elCanSelect) {
144
+ if (uiSelectMode.value && elCanSelect) {
152
145
  document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
153
146
  return stop();
154
147
  }
@@ -176,45 +169,64 @@ export default defineComponent({
176
169
  services?.uiService.set('showGuides', true);
177
170
  });
178
171
 
179
- if (!props.node?.id) return;
172
+ if (!node.value?.id) return;
180
173
  stage?.on('runtime-ready', (rt) => {
181
174
  runtime = rt;
182
175
  // toRaw返回的值是一个引用而非快照,需要cloneDeep
183
- props.root && runtime?.updateRootConfig(cloneDeep(toRaw(props.root)));
184
- props.page?.id && runtime?.updatePageId?.(props.page.id);
176
+ root.value && runtime?.updateRootConfig(cloneDeep(toRaw(root.value)));
177
+ page.value?.id && runtime?.updatePageId?.(page.value.id);
185
178
  setTimeout(() => {
186
- props.node && stage?.select(toRaw(props.node.id));
179
+ node.value && stage?.select(toRaw(node.value.id));
187
180
  });
188
181
  });
189
182
  });
190
183
 
191
- watch(
192
- () => props.zoom,
193
- (zoom) => {
194
- if (!stage || !zoom) return;
195
- stage?.setZoom(zoom);
196
- },
197
- );
184
+ watch(zoom, (zoom) => {
185
+ if (!stage || !zoom) return;
186
+ stage.setZoom(zoom);
187
+ });
188
+
189
+ watch(root, (root) => {
190
+ if (runtime && root) {
191
+ runtime.updateRootConfig(cloneDeep(toRaw(root)));
192
+ }
193
+ });
198
194
 
199
195
  watch(
200
- () => props.root,
201
- (root) => {
202
- if (runtime && root) {
203
- runtime.updateRootConfig(cloneDeep(toRaw(root)));
204
- }
196
+ () => node.value?.id,
197
+ (id) => {
198
+ nextTick(() => {
199
+ // 等待相关dom变更完成后,再select,适用大多数场景
200
+ id && stage?.select(id);
201
+ });
205
202
  },
206
203
  );
207
204
 
205
+ const resizeObserver = new ResizeObserver((entries) => {
206
+ for (const { contentRect } of entries) {
207
+ services?.uiService.set('stageContainerRect', {
208
+ width: contentRect.width,
209
+ height: contentRect.height,
210
+ });
211
+ }
212
+ });
213
+
214
+ onMounted(() => {
215
+ stageWrap.value?.container && resizeObserver.observe(stageWrap.value.container);
216
+ });
217
+
208
218
  onUnmounted(() => {
209
219
  stage?.destroy();
220
+ resizeObserver.disconnect();
210
221
  services?.editorService.set('stage', null);
211
222
  });
212
223
 
213
224
  return {
214
- stageStyle,
215
- ...useMenu(),
216
-
225
+ stageWrap,
217
226
  stageContainer,
227
+ stageRect,
228
+ zoom,
229
+ ...useMenu(),
218
230
  };
219
231
  },
220
232
  });
@@ -4,11 +4,6 @@
4
4
  :key="page?.id"
5
5
  :runtime-url="runtimeUrl"
6
6
  :render="render"
7
- :ui-select-mode="uiSelectMode"
8
- :root="root"
9
- :page="page"
10
- :node="node"
11
- :zoom="zoom"
12
7
  :moveable-options="moveableOptions"
13
8
  :can-select="canSelect"
14
9
  @select="selectHandler"
@@ -26,9 +21,9 @@
26
21
  </template>
27
22
 
28
23
  <script lang="ts">
29
- import { computed, defineComponent, inject, nextTick, PropType, watch } from 'vue';
24
+ import { computed, defineComponent, inject, PropType } from 'vue';
30
25
 
31
- import type { MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
26
+ import type { MComponent, MContainer, MPage } from '@tmagic/schema';
32
27
  import type { MoveableOptions, SortEventData } from '@tmagic/stage';
33
28
  import StageCore from '@tmagic/stage';
34
29
 
@@ -63,23 +58,9 @@ export default defineComponent({
63
58
 
64
59
  setup() {
65
60
  const services = inject<Services>('services');
66
- const node = computed(() => services?.editorService.get<MNode>('node'));
67
- const stage = computed(() => services?.editorService.get<StageCore>('stage'));
68
-
69
- watch([() => node.value?.id, stage], ([id, stage]) => {
70
- nextTick(() => {
71
- // 等待相关dom变更完成后,再select,适用大多数场景
72
- id && stage?.select(id);
73
- });
74
- });
75
61
 
76
62
  return {
77
- uiSelectMode: computed(() => services?.uiService.get<boolean>('uiSelectMode')),
78
- root: computed(() => services?.editorService.get<MApp>('root')),
79
63
  page: computed(() => services?.editorService.get<MPage>('page')),
80
- zoom: computed(() => services?.uiService.get<number>('zoom')),
81
-
82
- node,
83
64
 
84
65
  selectHandler(el: HTMLElement) {
85
66
  services?.editorService.select(el.id);
@@ -21,7 +21,7 @@ import { reactive, toRaw } from 'vue';
21
21
  import type StageCore from '@tmagic/stage';
22
22
 
23
23
  import editorService from '@editor/services/editor';
24
- import { GetColumnWidth, SetColumnWidth, UiState } from '@editor/type';
24
+ import type { GetColumnWidth, SetColumnWidth, StageRect, UiState } from '@editor/type';
25
25
 
26
26
  import BaseService from './BaseService';
27
27
 
@@ -29,7 +29,14 @@ const state = reactive<UiState>({
29
29
  uiSelectMode: false,
30
30
  showSrc: false,
31
31
  zoom: 1,
32
- stageStyle: {},
32
+ stageContainerRect: {
33
+ width: 0,
34
+ height: 0,
35
+ },
36
+ stageRect: {
37
+ width: 375,
38
+ height: 817,
39
+ },
33
40
  columnWidth: {
34
41
  left: 310,
35
42
  center: globalThis.document.body.clientWidth - 310 - 400,
@@ -57,6 +64,11 @@ class Ui extends BaseService {
57
64
  return;
58
65
  }
59
66
 
67
+ if (name === 'stageRect') {
68
+ this.setStageRect(value as unknown as StageRect);
69
+ return;
70
+ }
71
+
60
72
  if (name === 'showGuides') {
61
73
  mask?.showGuides(value as unknown as boolean);
62
74
  }
@@ -66,6 +78,10 @@ class Ui extends BaseService {
66
78
  }
67
79
 
68
80
  (state as any)[name] = value;
81
+
82
+ if (name === 'stageContainerRect') {
83
+ state.zoom = this.calcZoom();
84
+ }
69
85
  }
70
86
 
71
87
  public get<T>(name: keyof typeof state): T {
@@ -94,6 +110,24 @@ class Ui extends BaseService {
94
110
 
95
111
  state.columnWidth = columnWidth;
96
112
  }
113
+
114
+ private setStageRect(value: StageRect) {
115
+ state.stageRect = {
116
+ ...state.stageRect,
117
+ ...value,
118
+ };
119
+ state.zoom = this.calcZoom();
120
+ }
121
+
122
+ private calcZoom() {
123
+ const { stageRect, stageContainerRect } = state;
124
+ const { height, width } = stageContainerRect;
125
+ if (!width || !height) return 1;
126
+ if (width > stageRect.width && height > stageRect.height) {
127
+ return 1;
128
+ }
129
+ return Math.min((width - 100) / stageRect.width || 1, (height - 100) / stageRect.height || 1);
130
+ }
97
131
  }
98
132
 
99
133
  export type UiService = Ui;
@@ -2,19 +2,19 @@
2
2
  position: relative;
3
3
  width: 100%;
4
4
  height: calc(100% - $--page-bar-height);
5
- overflow: auto;
5
+ overflow: hidden;
6
+ display: flex;
7
+ justify-content: center;
8
+ align-items: center;
6
9
  }
7
10
 
8
11
  .m-editor-stage-container {
9
- transition: transform 0.3s;
10
- transform-origin: center -50%;
12
+ width: 100%;
13
+ height: 100%;
11
14
  z-index: 0;
12
- top: 50%;
13
- margin: 0 auto;
14
15
  position: relative;
15
- width: 375px;
16
- height: 80%;
17
16
  border: 1px solid $--border-color;
17
+ transition: transform 0.3s;
18
18
 
19
19
  &::-webkit-scrollbar {
20
20
  width: 0 !important;