@tmagic/editor 1.0.0-beta.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 (98) hide show
  1. package/dist/style.css +505 -0
  2. package/dist/tmagic-editor.es.js +4233 -0
  3. package/dist/tmagic-editor.es.js.map +1 -0
  4. package/dist/tmagic-editor.umd.js +4276 -0
  5. package/dist/tmagic-editor.umd.js.map +1 -0
  6. package/dist/types/src/Editor.vue.d.ts +136 -0
  7. package/dist/types/src/components/Icon.vue.d.ts +13 -0
  8. package/dist/types/src/components/ToolButton.vue.d.ts +35 -0
  9. package/dist/types/src/fields/Code.vue.d.ts +24 -0
  10. package/dist/types/src/fields/CodeLink.vue.d.ts +54 -0
  11. package/dist/types/src/fields/UISelect.vue.d.ts +32 -0
  12. package/dist/types/src/index.d.ts +19 -0
  13. package/dist/types/src/layouts/AddPageBox.vue.d.ts +5 -0
  14. package/dist/types/src/layouts/CodeEditor.vue.d.ts +46 -0
  15. package/dist/types/src/layouts/Framework.vue.d.ts +11 -0
  16. package/dist/types/src/layouts/NavMenu.vue.d.ts +24 -0
  17. package/dist/types/src/layouts/PropsPanel.vue.d.ts +13 -0
  18. package/dist/types/src/layouts/Resizer.vue.d.ts +13 -0
  19. package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +12 -0
  20. package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +31 -0
  21. package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +973 -0
  22. package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +27 -0
  23. package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +11 -0
  24. package/dist/types/src/layouts/workspace/Stage.vue.d.ts +153 -0
  25. package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +18 -0
  26. package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +37 -0
  27. package/dist/types/src/services/BaseService.d.ts +56 -0
  28. package/dist/types/src/services/editor.d.ts +122 -0
  29. package/dist/types/src/services/events.d.ts +16 -0
  30. package/dist/types/src/services/history.d.ts +51 -0
  31. package/dist/types/src/services/props.d.ts +36 -0
  32. package/dist/types/src/services/ui.d.ts +26 -0
  33. package/dist/types/src/shims-vue.d.ts +6 -0
  34. package/dist/types/src/type.d.ts +183 -0
  35. package/dist/types/src/utils/compose.d.ts +5 -0
  36. package/dist/types/src/utils/config.d.ts +4 -0
  37. package/dist/types/src/utils/editor.d.ts +45 -0
  38. package/dist/types/src/utils/index.d.ts +4 -0
  39. package/dist/types/src/utils/logger.d.ts +5 -0
  40. package/dist/types/src/utils/props.d.ts +49 -0
  41. package/dist/types/src/utils/undo-redo.d.ts +12 -0
  42. package/dist/types/src/vite-env.d.ts +1 -0
  43. package/package.json +57 -0
  44. package/src/Editor.vue +234 -0
  45. package/src/components/Icon.vue +29 -0
  46. package/src/components/ToolButton.vue +188 -0
  47. package/src/fields/Code.vue +35 -0
  48. package/src/fields/CodeLink.vue +81 -0
  49. package/src/fields/UISelect.vue +100 -0
  50. package/src/index.ts +63 -0
  51. package/src/layouts/AddPageBox.vue +41 -0
  52. package/src/layouts/CodeEditor.vue +188 -0
  53. package/src/layouts/Framework.vue +73 -0
  54. package/src/layouts/NavMenu.vue +37 -0
  55. package/src/layouts/PropsPanel.vue +81 -0
  56. package/src/layouts/Resizer.vue +61 -0
  57. package/src/layouts/sidebar/ComponentListPanel.vue +69 -0
  58. package/src/layouts/sidebar/LayerMenu.vue +117 -0
  59. package/src/layouts/sidebar/LayerPanel.vue +209 -0
  60. package/src/layouts/sidebar/Sidebar.vue +86 -0
  61. package/src/layouts/workspace/PageBar.vue +83 -0
  62. package/src/layouts/workspace/Stage.vue +221 -0
  63. package/src/layouts/workspace/ViewerMenu.vue +110 -0
  64. package/src/layouts/workspace/Workspace.vue +98 -0
  65. package/src/services/BaseService.ts +138 -0
  66. package/src/services/componentList.ts +48 -0
  67. package/src/services/editor.ts +521 -0
  68. package/src/services/events.ts +82 -0
  69. package/src/services/history.ts +124 -0
  70. package/src/services/props.ts +110 -0
  71. package/src/services/ui.ts +101 -0
  72. package/src/shims-vue.d.ts +6 -0
  73. package/src/theme/code-editor.scss +7 -0
  74. package/src/theme/common/var.scss +13 -0
  75. package/src/theme/component-list-panel.scss +102 -0
  76. package/src/theme/content-menu.scss +39 -0
  77. package/src/theme/framework.scss +72 -0
  78. package/src/theme/index.scss +13 -0
  79. package/src/theme/layer-panel.scss +68 -0
  80. package/src/theme/nav-menu.scss +69 -0
  81. package/src/theme/page-bar.scss +31 -0
  82. package/src/theme/props-panel.scss +17 -0
  83. package/src/theme/resizer.scss +51 -0
  84. package/src/theme/ruler.scss +38 -0
  85. package/src/theme/sidebar.scss +97 -0
  86. package/src/theme/stage.scss +22 -0
  87. package/src/theme/workspace.scss +5 -0
  88. package/src/type.ts +236 -0
  89. package/src/utils/compose.ts +33 -0
  90. package/src/utils/config.ts +29 -0
  91. package/src/utils/editor.ts +244 -0
  92. package/src/utils/index.ts +22 -0
  93. package/src/utils/logger.ts +47 -0
  94. package/src/utils/props.ts +218 -0
  95. package/src/utils/undo-redo.ts +76 -0
  96. package/src/vite-env.d.ts +1 -0
  97. package/tsconfig.json +9 -0
  98. package/vite.config.ts +30 -0
@@ -0,0 +1,86 @@
1
+ <template>
2
+ <el-tabs v-if="data.type === 'tabs'" class="m-editor-sidebar" v-model="activeTabName" type="card" tab-position="left">
3
+ <el-tab-pane v-for="item in items" :key="item.text" :name="item.text">
4
+ <template #label>
5
+ <span>
6
+ <m-icon v-if="item.icon" :icon="item.icon"></m-icon>
7
+ <div v-if="item.text" class="magic-editor-tab-panel-title">{{ item.text }}</div>
8
+ </span>
9
+ </template>
10
+
11
+ <component :is="item.component" v-bind="item.props || {}" v-on="item.listeners || {}">
12
+ <template #layer-node-content="{ data, node }" v-if="item.slots?.layerNodeContent">
13
+ <component :is="item.slots.layerNodeContent" :data="data" :node="node" />
14
+ </template>
15
+ </component>
16
+ </el-tab-pane>
17
+ </el-tabs>
18
+ </template>
19
+
20
+ <script lang="ts">
21
+ import { computed, defineComponent, PropType, ref, watch } from 'vue';
22
+ import { Coin, Files } from '@element-plus/icons';
23
+
24
+ import MIcon from '@editor/components/Icon.vue';
25
+ import { SideBarData, SideComponent } from '@editor/type';
26
+
27
+ import ComponentListPanel from './ComponentListPanel.vue';
28
+ import LayerPanel from './LayerPanel.vue';
29
+
30
+ export default defineComponent({
31
+ name: 'm-sidebar',
32
+
33
+ components: { MIcon },
34
+
35
+ props: {
36
+ data: {
37
+ type: Object as PropType<SideBarData>,
38
+ default: () => ({ type: 'tabs', status: '组件', items: ['component-list', 'layer'] }),
39
+ },
40
+ },
41
+
42
+ setup(props) {
43
+ const activeTabName = ref(props.data?.status);
44
+
45
+ watch(
46
+ () => props.data?.status,
47
+ (status) => {
48
+ activeTabName.value = status || '0';
49
+ },
50
+ );
51
+
52
+ return {
53
+ activeTabName,
54
+
55
+ items: computed<SideComponent[] | Record<string, any>[]>(() =>
56
+ props.data?.items.map((item) => {
57
+ if (typeof item !== 'string') {
58
+ return item;
59
+ }
60
+
61
+ switch (item) {
62
+ case 'component-list':
63
+ return {
64
+ type: 'component',
65
+ icon: Coin,
66
+ text: '组件',
67
+ component: ComponentListPanel,
68
+ slots: {},
69
+ };
70
+ case 'layer':
71
+ return {
72
+ type: 'component',
73
+ icon: Files,
74
+ text: '已选组件',
75
+ component: LayerPanel,
76
+ slots: {},
77
+ };
78
+ default:
79
+ return {};
80
+ }
81
+ }),
82
+ ),
83
+ };
84
+ },
85
+ });
86
+ </script>
@@ -0,0 +1,83 @@
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>
5
+ </div>
6
+ <template v-if="root">
7
+ <div
8
+ v-for="item in root.items"
9
+ :key="item.key"
10
+ class="m-editor-page-bar-item"
11
+ :class="{ active: page?.id === item.id }"
12
+ @click="switchPage(item)"
13
+ >
14
+ <slot name="page-bar-title" :page="item">
15
+ <span>{{ item.name }}</span>
16
+ </slot>
17
+
18
+ <el-popover placement="top" :width="160" trigger="hover">
19
+ <div>
20
+ <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>
23
+ </slot>
24
+ </div>
25
+ <template #reference>
26
+ <el-icon class="m-editor-page-bar-menu-icon">
27
+ <caret-bottom></caret-bottom>
28
+ </el-icon>
29
+ </template>
30
+ </el-popover>
31
+ </div>
32
+ </template>
33
+ </div>
34
+ </template>
35
+
36
+ <script lang="ts">
37
+ import { computed, defineComponent, inject, toRaw } from 'vue';
38
+ import { CaretBottom, Plus } from '@element-plus/icons';
39
+
40
+ import type { MPage } from '@tmagic/schema';
41
+
42
+ import type { Services } from '@editor/type';
43
+ import { generatePageNameByApp } from '@editor/utils/editor';
44
+
45
+ export default defineComponent({
46
+ components: { CaretBottom, Plus },
47
+
48
+ setup() {
49
+ const services = inject<Services>('services');
50
+ const editorService = services?.editorService;
51
+
52
+ return {
53
+ root: computed(() => editorService?.get('root')),
54
+ page: computed(() => editorService?.get('page')),
55
+
56
+ switchPage(page: MPage) {
57
+ editorService?.select(page);
58
+ },
59
+
60
+ addPage() {
61
+ if (!editorService) return;
62
+ const pageConfig = {
63
+ type: 'page',
64
+ name: generatePageNameByApp(toRaw(editorService.get('root'))),
65
+ };
66
+ editorService.add(pageConfig);
67
+ },
68
+
69
+ copy(node: MPage) {
70
+ node && editorService?.copy(node);
71
+ editorService?.paste({
72
+ left: 0,
73
+ top: 0,
74
+ });
75
+ },
76
+
77
+ remove(node: MPage) {
78
+ editorService?.remove(node);
79
+ },
80
+ };
81
+ },
82
+ });
83
+ </script>
@@ -0,0 +1,221 @@
1
+ <template>
2
+ <div class="m-editor-stage">
3
+ <div
4
+ class="m-editor-stage-container"
5
+ ref="stageContainer"
6
+ :style="stageStyle"
7
+ @contextmenu="contextmenuHandler"
8
+ ></div>
9
+ <teleport to="body">
10
+ <viewer-menu ref="menu" :style="menuStyle"></viewer-menu>
11
+ </teleport>
12
+ </div>
13
+ </template>
14
+
15
+ <script lang="ts">
16
+ import {
17
+ computed,
18
+ defineComponent,
19
+ inject,
20
+ onMounted,
21
+ onUnmounted,
22
+ PropType,
23
+ ref,
24
+ toRaw,
25
+ watch,
26
+ watchEffect,
27
+ } from 'vue';
28
+ import { cloneDeep } from 'lodash-es';
29
+
30
+ import type { MApp, MNode, MPage } from '@tmagic/schema';
31
+ import type { MoveableOptions, Runtime, SortEventData, UpdateEventData } from '@tmagic/stage';
32
+ import StageCore from '@tmagic/stage';
33
+
34
+ import type { Services } from '@editor/type';
35
+
36
+ import ViewerMenu from './ViewerMenu.vue';
37
+
38
+ const useMenu = () => {
39
+ const menu = ref<InstanceType<typeof ViewerMenu>>();
40
+ const menuStyle = ref({
41
+ display: 'none',
42
+ left: '0',
43
+ top: '0',
44
+ });
45
+
46
+ onMounted(() => {
47
+ document.addEventListener(
48
+ 'click',
49
+ () => {
50
+ menuStyle.value.display = 'none';
51
+ },
52
+ true,
53
+ );
54
+ });
55
+
56
+ return {
57
+ menu,
58
+ menuStyle,
59
+
60
+ contextmenuHandler(e: MouseEvent) {
61
+ e.preventDefault();
62
+
63
+ const menuHeight = menu.value?.$el.clientHeight;
64
+ let top = e.clientY;
65
+ if (menuHeight + e.clientY > document.body.clientHeight) {
66
+ top = document.body.clientHeight - menuHeight;
67
+ }
68
+ menuStyle.value = {
69
+ display: 'block',
70
+ top: `${top}px`,
71
+ left: `${e.clientX}px`,
72
+ };
73
+ },
74
+ };
75
+ };
76
+
77
+ export default defineComponent({
78
+ name: 'magic-stage',
79
+
80
+ components: {
81
+ ViewerMenu,
82
+ },
83
+
84
+ props: {
85
+ render: {
86
+ type: Function as PropType<() => HTMLDivElement>,
87
+ },
88
+
89
+ runtimeUrl: String,
90
+
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
+ canSelect: {
112
+ type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
113
+ default: (el: HTMLElement) => Boolean(el.id),
114
+ },
115
+
116
+ moveableOptions: {
117
+ type: [Object, Function] as PropType<MoveableOptions | ((core?: StageCore) => MoveableOptions)>,
118
+ default: () => (core?: StageCore) => ({
119
+ container: core?.renderer?.contentWindow?.document.getElementById('app'),
120
+ }),
121
+ },
122
+ },
123
+
124
+ emits: ['select', 'update', 'sort'],
125
+
126
+ setup(props, { emit }) {
127
+ const services = inject<Services>('services');
128
+ const stageContainer = ref<HTMLDivElement>();
129
+
130
+ const stageStyle = computed(() => ({
131
+ ...services?.uiService.get<Record<string, string | number>>('stageStyle'),
132
+ transform: `scale(${props.zoom}) translate3d(0, -50%, 0)`,
133
+ }));
134
+
135
+ let stage: StageCore | null = null;
136
+ let runtime: Runtime | null = null;
137
+
138
+ watchEffect(() => {
139
+ if (stage) return;
140
+
141
+ if (!stageContainer.value) return;
142
+ if (!(props.runtimeUrl || props.render) || !props.root) return;
143
+
144
+ stage = new StageCore({
145
+ render: props.render,
146
+ runtimeUrl: props.runtimeUrl,
147
+ zoom: props.zoom,
148
+ canSelect: (el, stop) => {
149
+ const elCanSelect = props.canSelect(el);
150
+ // 在组件联动过程中不能再往下选择,返回并触发 ui-select
151
+ if (props.uiSelectMode && elCanSelect) {
152
+ document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
153
+ return stop();
154
+ }
155
+
156
+ return elCanSelect;
157
+ },
158
+ moveableOptions: props.moveableOptions,
159
+ });
160
+
161
+ services?.editorService.set('stage', stage);
162
+
163
+ stage?.mount(stageContainer.value);
164
+
165
+ stage?.on('select', (el: HTMLElement) => emit('select', el));
166
+
167
+ stage?.on('update', (ev: UpdateEventData) => {
168
+ emit('update', { id: ev.el.id, style: ev.style });
169
+ });
170
+
171
+ stage?.on('sort', (ev: SortEventData) => {
172
+ emit('sort', ev);
173
+ });
174
+
175
+ stage?.on('changeGuides', () => {
176
+ services?.uiService.set('showGuides', true);
177
+ });
178
+
179
+ if (!props.node?.id) return;
180
+ stage?.on('runtime-ready', (rt) => {
181
+ runtime = rt;
182
+ // toRaw返回的值是一个引用而非快照,需要cloneDeep
183
+ props.root && runtime?.updateRootConfig(cloneDeep(toRaw(props.root)));
184
+ props.page?.id && runtime?.updatePageId?.(props.page.id);
185
+ setTimeout(() => {
186
+ props.node && stage?.select(toRaw(props.node.id));
187
+ });
188
+ });
189
+ });
190
+
191
+ watch(
192
+ () => props.zoom,
193
+ (zoom) => {
194
+ if (!stage || !zoom) return;
195
+ stage?.setZoom(zoom);
196
+ },
197
+ );
198
+
199
+ watch(
200
+ () => props.root,
201
+ (root) => {
202
+ if (runtime && root) {
203
+ runtime.updateRootConfig(cloneDeep(toRaw(root)));
204
+ }
205
+ },
206
+ );
207
+
208
+ onUnmounted(() => {
209
+ stage?.destroy();
210
+ services?.editorService.set('stage', null);
211
+ });
212
+
213
+ return {
214
+ stageStyle,
215
+ ...useMenu(),
216
+
217
+ stageContainer,
218
+ };
219
+ },
220
+ });
221
+ </script>
@@ -0,0 +1,110 @@
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>
22
+ </template>
23
+
24
+ <script lang="ts">
25
+ import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue';
26
+
27
+ import type StageCore from '@tmagic/stage';
28
+
29
+ import { LayerOffset, Layout, Services } from '@editor/type';
30
+ import { COPY_STORAGE_KEY } from '@editor/utils/editor';
31
+
32
+ export default defineComponent({
33
+ name: 'magic-editor-ui-viewer-menu',
34
+
35
+ setup() {
36
+ const services = inject<Services>('services');
37
+ const editorService = services?.editorService;
38
+ const menu = ref<HTMLDivElement>();
39
+ const canPaste = ref(false);
40
+ const canCenter = ref(false);
41
+
42
+ const node = computed(() => editorService?.get('node'));
43
+ const parent = computed(() => editorService?.get('parent'));
44
+
45
+ onMounted(() => {
46
+ const data = globalThis.localStorage.getItem(COPY_STORAGE_KEY);
47
+ canPaste.value = data !== 'undefined' && !!data;
48
+ });
49
+
50
+ watch(
51
+ parent,
52
+ async () => {
53
+ if (!parent.value || !editorService) return (canCenter.value = false);
54
+ const layout = await editorService.getLayout(parent.value);
55
+ canCenter.value =
56
+ [Layout.ABSOLUTE, Layout.FIXED].includes(layout) && !['app', 'page', 'pop'].includes(`${node.value?.type}`);
57
+ },
58
+ { immediate: true },
59
+ );
60
+
61
+ return {
62
+ 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();
106
+ },
107
+ };
108
+ },
109
+ });
110
+ </script>
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <div class="m-editor-workspace">
3
+ <magic-stage
4
+ :key="page?.id"
5
+ :runtime-url="runtimeUrl"
6
+ :render="render"
7
+ :ui-select-mode="uiSelectMode"
8
+ :root="root"
9
+ :page="page"
10
+ :node="node"
11
+ :zoom="zoom"
12
+ :moveable-options="moveableOptions"
13
+ :can-select="canSelect"
14
+ @select="selectHandler"
15
+ @update="updateNodeHandler"
16
+ @sort="sortNodeHandler"
17
+ ></magic-stage>
18
+
19
+ <slot name="workspace-content"></slot>
20
+
21
+ <page-bar>
22
+ <template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
23
+ <template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
24
+ </page-bar>
25
+ </div>
26
+ </template>
27
+
28
+ <script lang="ts">
29
+ import { computed, defineComponent, inject, nextTick, PropType, watch } from 'vue';
30
+
31
+ import type { MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
32
+ import type { MoveableOptions, SortEventData } from '@tmagic/stage';
33
+ import StageCore from '@tmagic/stage';
34
+
35
+ import type { Services } from '@editor/type';
36
+
37
+ import PageBar from './PageBar.vue';
38
+ import MagicStage from './Stage.vue';
39
+
40
+ export default defineComponent({
41
+ name: 'm-editor-workspace',
42
+
43
+ components: {
44
+ PageBar,
45
+ MagicStage,
46
+ },
47
+
48
+ props: {
49
+ runtimeUrl: String,
50
+
51
+ render: {
52
+ type: Function as PropType<() => HTMLDivElement>,
53
+ },
54
+
55
+ moveableOptions: {
56
+ type: [Object, Function] as PropType<MoveableOptions | ((core?: StageCore) => MoveableOptions)>,
57
+ },
58
+
59
+ canSelect: {
60
+ type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
61
+ },
62
+ },
63
+
64
+ setup() {
65
+ 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
+
76
+ return {
77
+ uiSelectMode: computed(() => services?.uiService.get<boolean>('uiSelectMode')),
78
+ root: computed(() => services?.editorService.get<MApp>('root')),
79
+ page: computed(() => services?.editorService.get<MPage>('page')),
80
+ zoom: computed(() => services?.uiService.get<number>('zoom')),
81
+
82
+ node,
83
+
84
+ selectHandler(el: HTMLElement) {
85
+ services?.editorService.select(el.id);
86
+ },
87
+
88
+ updateNodeHandler(node: MComponent | MContainer | MPage) {
89
+ services?.editorService.update(node);
90
+ },
91
+
92
+ sortNodeHandler(ev: SortEventData) {
93
+ services?.editorService.sort(ev.src, ev.dist);
94
+ },
95
+ };
96
+ },
97
+ });
98
+ </script>