@tmagic/editor 1.0.0-beta.8 → 1.0.0-rc.10
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.
- package/README.md +1 -0
- package/dist/style.css +94 -45
- package/dist/tmagic-editor.es.js +1622 -2069
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +1621 -2071
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/dist/types/src/Editor.vue.d.ts +50 -2
- package/dist/types/src/fields/Code.vue.d.ts +4 -4
- package/dist/types/src/fields/CodeLink.vue.d.ts +1 -3
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/layouts/CodeEditor.vue.d.ts +13 -4
- package/dist/types/src/layouts/Framework.vue.d.ts +15 -3
- package/dist/types/src/layouts/PropsPanel.vue.d.ts +4 -5
- package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
- package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +2 -3
- package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +14 -0
- package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +5 -31
- package/dist/types/src/services/BaseService.d.ts +5 -2
- package/dist/types/src/services/editor.d.ts +16 -6
- package/dist/types/src/services/history.d.ts +2 -18
- package/dist/types/src/services/props.d.ts +8 -2
- package/dist/types/src/services/ui.d.ts +2 -0
- package/dist/types/src/type.d.ts +25 -9
- package/dist/types/src/utils/editor.d.ts +5 -10
- package/dist/types/src/utils/polyfills.d.ts +0 -0
- package/dist/types/src/utils/props.d.ts +8 -1
- package/package.json +35 -12
- package/src/Editor.vue +44 -9
- package/src/components/ContentMenu.vue +140 -0
- package/src/components/ScrollViewer.vue +1 -1
- package/src/components/ToolButton.vue +72 -31
- package/src/fields/UISelect.vue +14 -6
- package/src/index.ts +2 -0
- package/src/layouts/AddPageBox.vue +3 -1
- package/src/layouts/CodeEditor.vue +20 -13
- package/src/layouts/Framework.vue +20 -7
- package/src/layouts/PropsPanel.vue +5 -4
- package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
- package/src/layouts/sidebar/LayerMenu.vue +93 -95
- package/src/layouts/sidebar/LayerPanel.vue +80 -59
- package/src/layouts/sidebar/Sidebar.vue +13 -52
- package/src/layouts/sidebar/TabPane.vue +68 -0
- package/src/layouts/workspace/PageBar.vue +145 -17
- package/src/layouts/workspace/Stage.vue +65 -91
- package/src/layouts/workspace/ViewerMenu.vue +122 -68
- package/src/layouts/workspace/Workspace.vue +125 -42
- package/src/services/BaseService.ts +71 -23
- package/src/services/editor.ts +231 -69
- package/src/services/history.ts +7 -0
- package/src/services/props.ts +64 -9
- package/src/services/ui.ts +35 -5
- package/src/theme/common/var.scss +3 -2
- package/src/theme/component-list-panel.scss +2 -0
- package/src/theme/content-menu.scss +33 -26
- package/src/theme/layer-panel.scss +10 -0
- package/src/theme/nav-menu.scss +6 -2
- package/src/theme/page-bar.scss +42 -2
- package/src/theme/props-panel.scss +19 -17
- package/src/theme/workspace.scss +4 -0
- package/src/type.ts +27 -9
- package/src/utils/editor.ts +37 -56
- package/src/utils/polyfills.ts +8 -0
- package/src/utils/props.ts +50 -8
- package/LICENSE +0 -5432
- package/dist/types/src/components/ToolButton.vue.d.ts +0 -35
- package/dist/types/src/fields/UISelect.vue.d.ts +0 -32
- package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
- package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -973
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -192
- package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -30
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<el-tabs
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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>
|
|
2
|
+
<el-tabs
|
|
3
|
+
v-if="data.type === 'tabs' && data.items.length"
|
|
4
|
+
class="m-editor-sidebar"
|
|
5
|
+
v-model="activeTabName"
|
|
6
|
+
type="card"
|
|
7
|
+
tab-position="left"
|
|
8
|
+
>
|
|
9
|
+
<tab-pane v-for="(item, index) in data.items" :key="index" :data="item"></tab-pane>
|
|
17
10
|
</el-tabs>
|
|
18
11
|
</template>
|
|
19
12
|
|
|
20
13
|
<script lang="ts">
|
|
21
|
-
import {
|
|
22
|
-
import { Coin, Files } from '@element-plus/icons';
|
|
14
|
+
import { defineComponent, PropType, ref, watch } from 'vue';
|
|
23
15
|
|
|
24
|
-
import
|
|
25
|
-
import { SideBarData, SideComponent } from '@editor/type';
|
|
16
|
+
import { SideBarData } from '@editor/type';
|
|
26
17
|
|
|
27
|
-
import
|
|
28
|
-
import LayerPanel from './LayerPanel.vue';
|
|
18
|
+
import TabPane from './TabPane.vue';
|
|
29
19
|
|
|
30
20
|
export default defineComponent({
|
|
31
|
-
|
|
21
|
+
components: { TabPane },
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
name: 'm-sidebar',
|
|
34
24
|
|
|
35
25
|
props: {
|
|
36
26
|
data: {
|
|
@@ -51,35 +41,6 @@ export default defineComponent({
|
|
|
51
41
|
|
|
52
42
|
return {
|
|
53
43
|
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
44
|
};
|
|
84
45
|
},
|
|
85
46
|
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-tab-pane v-if="config" :name="config.text">
|
|
3
|
+
<template #label>
|
|
4
|
+
<div :key="config.text">
|
|
5
|
+
<m-icon v-if="config.icon" :icon="config.icon"></m-icon>
|
|
6
|
+
<div v-if="config.text" class="magic-editor-tab-panel-title">{{ config.text }}</div>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<component :is="config.component" v-bind="config.props || {}" v-on="config?.listeners || {}">
|
|
11
|
+
<template #layer-node-content="{ data, node }" v-if="config.slots?.layerNodeContent">
|
|
12
|
+
<component :is="config.slots?.layerNodeContent" :data="data" :node="node" />
|
|
13
|
+
</template>
|
|
14
|
+
</component>
|
|
15
|
+
</el-tab-pane>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script lang="ts">
|
|
19
|
+
import { computed, defineComponent, PropType } from 'vue';
|
|
20
|
+
import { Coin, Files } from '@element-plus/icons';
|
|
21
|
+
|
|
22
|
+
import MIcon from '@editor/components/Icon.vue';
|
|
23
|
+
import { SideComponent, SideItem } from '@editor/type';
|
|
24
|
+
|
|
25
|
+
import ComponentListPanel from './ComponentListPanel.vue';
|
|
26
|
+
import LayerPanel from './LayerPanel.vue';
|
|
27
|
+
|
|
28
|
+
export default defineComponent({
|
|
29
|
+
components: { MIcon },
|
|
30
|
+
|
|
31
|
+
props: {
|
|
32
|
+
data: {
|
|
33
|
+
type: [Object, String] as PropType<SideItem>,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
setup(props) {
|
|
38
|
+
return {
|
|
39
|
+
config: computed<SideComponent | undefined>(() => {
|
|
40
|
+
if (typeof props.data !== 'string') {
|
|
41
|
+
return props.data;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
switch (props.data) {
|
|
45
|
+
case 'component-list':
|
|
46
|
+
return {
|
|
47
|
+
type: 'component',
|
|
48
|
+
icon: Coin,
|
|
49
|
+
text: '组件',
|
|
50
|
+
component: ComponentListPanel,
|
|
51
|
+
slots: {},
|
|
52
|
+
};
|
|
53
|
+
case 'layer':
|
|
54
|
+
return {
|
|
55
|
+
type: 'component',
|
|
56
|
+
icon: Files,
|
|
57
|
+
text: '已选组件',
|
|
58
|
+
component: LayerPanel,
|
|
59
|
+
slots: {},
|
|
60
|
+
};
|
|
61
|
+
default:
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
</script>
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="m-editor-page-bar">
|
|
3
|
-
<div class="m-editor-page-bar-item" @click="addPage">
|
|
4
|
-
<el-icon
|
|
2
|
+
<div class="m-editor-page-bar" ref="pageBar">
|
|
3
|
+
<div id="m-editor-page-bar-add-icon" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="addPage">
|
|
4
|
+
<el-icon><plus></plus></el-icon>
|
|
5
5
|
</div>
|
|
6
|
-
<
|
|
6
|
+
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('left')">
|
|
7
|
+
<el-icon><arrow-left-bold></arrow-left-bold></el-icon>
|
|
8
|
+
</div>
|
|
9
|
+
<div v-if="root" class="m-editor-page-bar-items" ref="itemsContainer" :style="`width: ${itemsContainerWidth}px`">
|
|
7
10
|
<div
|
|
8
11
|
v-for="item in root.items"
|
|
9
12
|
:key="item.key"
|
|
@@ -11,15 +14,33 @@
|
|
|
11
14
|
:class="{ active: page?.id === item.id }"
|
|
12
15
|
@click="switchPage(item)"
|
|
13
16
|
>
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
+
<div class="m-editor-page-bar-title">
|
|
18
|
+
<slot name="page-bar-title" :page="item">
|
|
19
|
+
<el-tooltip effect="dark" placement="top-start" :content="item.name">
|
|
20
|
+
<span>{{ item.name }}</span>
|
|
21
|
+
</el-tooltip>
|
|
22
|
+
</slot>
|
|
23
|
+
</div>
|
|
17
24
|
|
|
18
|
-
<el-popover placement="top" :width="160" trigger="hover">
|
|
25
|
+
<el-popover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
|
19
26
|
<div>
|
|
20
27
|
<slot name="page-bar-popover" :page="item">
|
|
21
|
-
<
|
|
22
|
-
|
|
28
|
+
<tool-button
|
|
29
|
+
:data="{
|
|
30
|
+
type: 'button',
|
|
31
|
+
text: '复制',
|
|
32
|
+
icon: DocumentCopy,
|
|
33
|
+
handler: () => copy(item),
|
|
34
|
+
}"
|
|
35
|
+
></tool-button>
|
|
36
|
+
<tool-button
|
|
37
|
+
:data="{
|
|
38
|
+
type: 'button',
|
|
39
|
+
text: '删除',
|
|
40
|
+
icon: Delete,
|
|
41
|
+
handler: () => remove(item),
|
|
42
|
+
}"
|
|
43
|
+
></tool-button>
|
|
23
44
|
</slot>
|
|
24
45
|
</div>
|
|
25
46
|
<template #reference>
|
|
@@ -29,28 +50,135 @@
|
|
|
29
50
|
</template>
|
|
30
51
|
</el-popover>
|
|
31
52
|
</div>
|
|
32
|
-
</
|
|
53
|
+
</div>
|
|
54
|
+
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('right')">
|
|
55
|
+
<el-icon><arrow-right-bold></arrow-right-bold></el-icon>
|
|
56
|
+
</div>
|
|
33
57
|
</div>
|
|
34
58
|
</template>
|
|
35
59
|
|
|
36
60
|
<script lang="ts">
|
|
37
|
-
import {
|
|
38
|
-
|
|
61
|
+
import {
|
|
62
|
+
computed,
|
|
63
|
+
ComputedRef,
|
|
64
|
+
defineComponent,
|
|
65
|
+
inject,
|
|
66
|
+
markRaw,
|
|
67
|
+
onMounted,
|
|
68
|
+
onUnmounted,
|
|
69
|
+
ref,
|
|
70
|
+
toRaw,
|
|
71
|
+
watch,
|
|
72
|
+
} from 'vue';
|
|
73
|
+
import { ArrowLeftBold, ArrowRightBold, CaretBottom, Delete, DocumentCopy, Plus } from '@element-plus/icons';
|
|
39
74
|
|
|
40
|
-
import type { MPage } from '@tmagic/schema';
|
|
75
|
+
import type { MApp, MPage } from '@tmagic/schema';
|
|
76
|
+
import { NodeType } from '@tmagic/schema';
|
|
41
77
|
|
|
78
|
+
import ToolButton from '@editor/components/ToolButton.vue';
|
|
42
79
|
import type { Services } from '@editor/type';
|
|
43
80
|
import { generatePageNameByApp } from '@editor/utils/editor';
|
|
44
81
|
|
|
82
|
+
const useScroll = (root: ComputedRef<MApp | undefined>) => {
|
|
83
|
+
const pageBar = ref<HTMLDivElement>();
|
|
84
|
+
const itemsContainer = ref<HTMLDivElement>();
|
|
85
|
+
|
|
86
|
+
const pageBarWidth = ref(0);
|
|
87
|
+
const canScroll = ref(false);
|
|
88
|
+
|
|
89
|
+
const itemsContainerWidth = computed(() => pageBarWidth.value - 105);
|
|
90
|
+
|
|
91
|
+
let translateLeft = 0;
|
|
92
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
93
|
+
for (const { contentRect } of entries) {
|
|
94
|
+
const { width } = contentRect;
|
|
95
|
+
pageBarWidth.value = width || 0;
|
|
96
|
+
|
|
97
|
+
setCanScroll();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const setCanScroll = () => {
|
|
102
|
+
if (itemsContainer.value) {
|
|
103
|
+
canScroll.value = itemsContainer.value.scrollWidth > pageBarWidth.value - 105;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
|
|
108
|
+
if (!itemsContainer.value) return;
|
|
109
|
+
|
|
110
|
+
const maxScrollLeft = itemsContainer.value.scrollWidth - itemsContainerWidth.value;
|
|
111
|
+
|
|
112
|
+
if (type === 'left') {
|
|
113
|
+
translateLeft += 100;
|
|
114
|
+
|
|
115
|
+
if (translateLeft > 0) {
|
|
116
|
+
translateLeft = 0;
|
|
117
|
+
}
|
|
118
|
+
} else if (type === 'right') {
|
|
119
|
+
translateLeft -= 100;
|
|
120
|
+
|
|
121
|
+
if (-translateLeft > maxScrollLeft) {
|
|
122
|
+
translateLeft = -maxScrollLeft;
|
|
123
|
+
}
|
|
124
|
+
} else if (type === 'start') {
|
|
125
|
+
translateLeft = 0;
|
|
126
|
+
} else if (type === 'end') {
|
|
127
|
+
translateLeft = -maxScrollLeft;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
onMounted(() => {
|
|
134
|
+
pageBar.value && resizeObserver.observe(pageBar.value);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
onUnmounted(() => {
|
|
138
|
+
resizeObserver.disconnect();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
watch(
|
|
142
|
+
() => root.value?.items.length,
|
|
143
|
+
(length = 0, preLength = 0) => {
|
|
144
|
+
setTimeout(() => {
|
|
145
|
+
setCanScroll();
|
|
146
|
+
if (length < preLength) {
|
|
147
|
+
scroll('start');
|
|
148
|
+
} else {
|
|
149
|
+
scroll('end');
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
pageBar,
|
|
157
|
+
itemsContainer,
|
|
158
|
+
canScroll,
|
|
159
|
+
|
|
160
|
+
itemsContainerWidth,
|
|
161
|
+
|
|
162
|
+
scroll,
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
45
166
|
export default defineComponent({
|
|
46
|
-
components: { CaretBottom, Plus },
|
|
167
|
+
components: { ArrowLeftBold, ArrowRightBold, CaretBottom, Plus, ToolButton },
|
|
47
168
|
|
|
48
169
|
setup() {
|
|
49
170
|
const services = inject<Services>('services');
|
|
50
171
|
const editorService = services?.editorService;
|
|
51
172
|
|
|
173
|
+
const root = computed(() => editorService?.get<MApp>('root'));
|
|
174
|
+
|
|
52
175
|
return {
|
|
53
|
-
|
|
176
|
+
Delete: markRaw(Delete),
|
|
177
|
+
DocumentCopy: markRaw(DocumentCopy),
|
|
178
|
+
|
|
179
|
+
...useScroll(root),
|
|
180
|
+
|
|
181
|
+
root,
|
|
54
182
|
page: computed(() => editorService?.get('page')),
|
|
55
183
|
|
|
56
184
|
switchPage(page: MPage) {
|
|
@@ -60,7 +188,7 @@ export default defineComponent({
|
|
|
60
188
|
addPage() {
|
|
61
189
|
if (!editorService) return;
|
|
62
190
|
const pageConfig = {
|
|
63
|
-
type:
|
|
191
|
+
type: NodeType.PAGE,
|
|
64
192
|
name: generatePageNameByApp(toRaw(editorService.get('root'))),
|
|
65
193
|
};
|
|
66
194
|
editorService.add(pageConfig);
|
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
<div
|
|
10
10
|
class="m-editor-stage-container"
|
|
11
11
|
ref="stageContainer"
|
|
12
|
-
@contextmenu="contextmenuHandler"
|
|
13
12
|
:style="`transform: scale(${zoom})`"
|
|
13
|
+
@contextmenu="contextmenuHandler"
|
|
14
|
+
@drop="dropHandler"
|
|
15
|
+
@dragover="dragoverHandler"
|
|
14
16
|
></div>
|
|
15
17
|
<teleport to="body">
|
|
16
|
-
<viewer-menu ref="menu"
|
|
18
|
+
<viewer-menu ref="menu"></viewer-menu>
|
|
17
19
|
</teleport>
|
|
18
20
|
</scroll-viewer>
|
|
19
21
|
</template>
|
|
@@ -23,10 +25,9 @@ import {
|
|
|
23
25
|
computed,
|
|
24
26
|
defineComponent,
|
|
25
27
|
inject,
|
|
26
|
-
|
|
28
|
+
markRaw,
|
|
27
29
|
onMounted,
|
|
28
30
|
onUnmounted,
|
|
29
|
-
PropType,
|
|
30
31
|
ref,
|
|
31
32
|
toRaw,
|
|
32
33
|
watch,
|
|
@@ -35,53 +36,14 @@ import {
|
|
|
35
36
|
import { cloneDeep } from 'lodash-es';
|
|
36
37
|
|
|
37
38
|
import type { MApp, MNode, MPage } from '@tmagic/schema';
|
|
38
|
-
import type {
|
|
39
|
+
import type { Runtime, SortEventData, UpdateEventData } from '@tmagic/stage';
|
|
39
40
|
import StageCore from '@tmagic/stage';
|
|
40
41
|
|
|
41
42
|
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
|
42
|
-
import
|
|
43
|
+
import { Layout, Services, StageOptions, StageRect } from '@editor/type';
|
|
43
44
|
|
|
44
45
|
import ViewerMenu from './ViewerMenu.vue';
|
|
45
46
|
|
|
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
47
|
export default defineComponent({
|
|
86
48
|
name: 'magic-stage',
|
|
87
49
|
|
|
@@ -90,39 +52,19 @@ export default defineComponent({
|
|
|
90
52
|
ScrollViewer,
|
|
91
53
|
},
|
|
92
54
|
|
|
93
|
-
|
|
94
|
-
render: {
|
|
95
|
-
type: Function as PropType<() => HTMLDivElement>,
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
runtimeUrl: String,
|
|
99
|
-
|
|
100
|
-
canSelect: {
|
|
101
|
-
type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
|
|
102
|
-
default: (el: HTMLElement) => Boolean(el.id),
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
moveableOptions: {
|
|
106
|
-
type: [Object, Function] as PropType<MoveableOptions | ((core?: StageCore) => MoveableOptions)>,
|
|
107
|
-
default: () => (core?: StageCore) => ({
|
|
108
|
-
container: core?.renderer?.contentWindow?.document.getElementById('app'),
|
|
109
|
-
}),
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
emits: ['select', 'update', 'sort'],
|
|
114
|
-
|
|
115
|
-
setup(props, { emit }) {
|
|
55
|
+
setup() {
|
|
116
56
|
const services = inject<Services>('services');
|
|
57
|
+
const stageOptions = inject<StageOptions>('stageOptions');
|
|
117
58
|
|
|
118
59
|
const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
|
|
119
60
|
const stageContainer = ref<HTMLDivElement>();
|
|
61
|
+
const menu = ref<InstanceType<typeof ViewerMenu>>();
|
|
120
62
|
|
|
121
63
|
const stageRect = computed(() => services?.uiService.get<StageRect>('stageRect'));
|
|
122
64
|
const uiSelectMode = computed(() => services?.uiService.get<boolean>('uiSelectMode'));
|
|
123
65
|
const root = computed(() => services?.editorService.get<MApp>('root'));
|
|
124
66
|
const page = computed(() => services?.editorService.get<MPage>('page'));
|
|
125
|
-
const zoom = computed(() => services?.uiService.get<number>('zoom'));
|
|
67
|
+
const zoom = computed(() => services?.uiService.get<number>('zoom') || 1);
|
|
126
68
|
const node = computed(() => services?.editorService.get<MNode>('node'));
|
|
127
69
|
|
|
128
70
|
let stage: StageCore | null = null;
|
|
@@ -132,37 +74,45 @@ export default defineComponent({
|
|
|
132
74
|
if (stage) return;
|
|
133
75
|
|
|
134
76
|
if (!stageContainer.value) return;
|
|
135
|
-
if (!(
|
|
77
|
+
if (!(stageOptions?.runtimeUrl || stageOptions?.render) || !root.value) return;
|
|
136
78
|
|
|
137
79
|
stage = new StageCore({
|
|
138
|
-
render:
|
|
139
|
-
runtimeUrl:
|
|
80
|
+
render: stageOptions.render,
|
|
81
|
+
runtimeUrl: stageOptions.runtimeUrl,
|
|
140
82
|
zoom: zoom.value,
|
|
141
|
-
|
|
142
|
-
|
|
83
|
+
autoScrollIntoView: stageOptions.autoScrollIntoView,
|
|
84
|
+
canSelect: (el, event, stop) => {
|
|
85
|
+
const elCanSelect = stageOptions.canSelect(el);
|
|
143
86
|
// 在组件联动过程中不能再往下选择,返回并触发 ui-select
|
|
144
|
-
if (uiSelectMode.value && elCanSelect) {
|
|
87
|
+
if (uiSelectMode.value && elCanSelect && event.type === 'mousedown') {
|
|
145
88
|
document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
|
|
146
89
|
return stop();
|
|
147
90
|
}
|
|
148
91
|
|
|
149
92
|
return elCanSelect;
|
|
150
93
|
},
|
|
151
|
-
moveableOptions:
|
|
94
|
+
moveableOptions: stageOptions.moveableOptions,
|
|
95
|
+
updateDragEl: stageOptions.updateDragEl,
|
|
152
96
|
});
|
|
153
97
|
|
|
154
|
-
services?.editorService.set('stage', stage);
|
|
98
|
+
services?.editorService.set('stage', markRaw(stage));
|
|
155
99
|
|
|
156
100
|
stage?.mount(stageContainer.value);
|
|
157
101
|
|
|
158
|
-
stage?.on('select', (el: HTMLElement) =>
|
|
102
|
+
stage?.on('select', (el: HTMLElement) => {
|
|
103
|
+
services?.editorService.select(el.id);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
stage?.on('highlight', (el: HTMLElement) => {
|
|
107
|
+
services?.editorService.highlight(el.id);
|
|
108
|
+
});
|
|
159
109
|
|
|
160
110
|
stage?.on('update', (ev: UpdateEventData) => {
|
|
161
|
-
|
|
111
|
+
services?.editorService.update({ id: ev.el.id, style: ev.style });
|
|
162
112
|
});
|
|
163
113
|
|
|
164
114
|
stage?.on('sort', (ev: SortEventData) => {
|
|
165
|
-
|
|
115
|
+
services?.editorService.sort(ev.src, ev.dist);
|
|
166
116
|
});
|
|
167
117
|
|
|
168
118
|
stage?.on('changeGuides', () => {
|
|
@@ -192,16 +142,6 @@ export default defineComponent({
|
|
|
192
142
|
}
|
|
193
143
|
});
|
|
194
144
|
|
|
195
|
-
watch(
|
|
196
|
-
() => node.value?.id,
|
|
197
|
-
(id) => {
|
|
198
|
-
nextTick(() => {
|
|
199
|
-
// 等待相关dom变更完成后,再select,适用大多数场景
|
|
200
|
-
id && stage?.select(id);
|
|
201
|
-
});
|
|
202
|
-
},
|
|
203
|
-
);
|
|
204
|
-
|
|
205
145
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
206
146
|
for (const { contentRect } of entries) {
|
|
207
147
|
services?.uiService.set('stageContainerRect', {
|
|
@@ -224,9 +164,43 @@ export default defineComponent({
|
|
|
224
164
|
return {
|
|
225
165
|
stageWrap,
|
|
226
166
|
stageContainer,
|
|
167
|
+
menu,
|
|
227
168
|
stageRect,
|
|
228
169
|
zoom,
|
|
229
|
-
|
|
170
|
+
|
|
171
|
+
contextmenuHandler(e: MouseEvent) {
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
menu.value?.show(e);
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
dragoverHandler(e: DragEvent) {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
if (e.dataTransfer) {
|
|
179
|
+
e.dataTransfer.dropEffect = 'move';
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
async dropHandler(e: DragEvent) {
|
|
184
|
+
e.preventDefault();
|
|
185
|
+
if (e.dataTransfer && page.value && stageContainer.value && stage) {
|
|
186
|
+
// eslint-disable-next-line no-eval
|
|
187
|
+
const config = eval(`(${e.dataTransfer.getData('data')})`);
|
|
188
|
+
const layout = await services?.editorService.getLayout(page.value);
|
|
189
|
+
|
|
190
|
+
const containerRect = stageContainer.value.getBoundingClientRect();
|
|
191
|
+
const { scrollTop, scrollLeft } = stage.mask;
|
|
192
|
+
if (layout === Layout.ABSOLUTE) {
|
|
193
|
+
config.style = {
|
|
194
|
+
...(config.style || {}),
|
|
195
|
+
position: 'absolute',
|
|
196
|
+
top: e.clientY - containerRect.top + scrollTop,
|
|
197
|
+
left: e.clientX - containerRect.left + scrollLeft,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
services?.editorService.add(config, page.value);
|
|
202
|
+
}
|
|
203
|
+
},
|
|
230
204
|
};
|
|
231
205
|
},
|
|
232
206
|
});
|