@tmagic/editor 1.0.0-beta.9 → 1.0.0-rc.11
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 +72 -36
- package/dist/tmagic-editor.es.js +1555 -2080
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +1555 -2082
- 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 -32
- package/dist/types/src/services/BaseService.d.ts +5 -2
- package/dist/types/src/services/editor.d.ts +11 -5
- 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 +24 -9
- package/dist/types/src/utils/editor.d.ts +4 -9
- package/dist/types/src/utils/polyfills.d.ts +0 -0
- package/dist/types/src/utils/props.d.ts +10 -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 +69 -30
- package/src/fields/UISelect.vue +14 -6
- package/src/index.ts +2 -0
- 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 +31 -54
- package/src/layouts/sidebar/Sidebar.vue +13 -52
- package/src/layouts/sidebar/TabPane.vue +68 -0
- package/src/layouts/workspace/PageBar.vue +136 -13
- package/src/layouts/workspace/Stage.vue +62 -104
- package/src/layouts/workspace/ViewerMenu.vue +119 -67
- package/src/layouts/workspace/Workspace.vue +124 -46
- package/src/services/BaseService.ts +71 -23
- package/src/services/editor.ts +196 -52
- 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 +1 -0
- package/src/theme/component-list-panel.scss +2 -0
- package/src/theme/content-menu.scss +33 -26
- package/src/theme/layer-panel.scss +2 -1
- package/src/theme/nav-menu.scss +6 -2
- package/src/theme/page-bar.scss +36 -3
- package/src/theme/props-panel.scss +19 -17
- package/src/theme/workspace.scss +4 -0
- package/src/type.ts +26 -9
- package/src/utils/editor.ts +35 -55
- package/src/utils/polyfills.ts +8 -0
- package/src/utils/props.ts +51 -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 -979
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -193
- 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"
|
|
@@ -19,11 +22,25 @@
|
|
|
19
22
|
</slot>
|
|
20
23
|
</div>
|
|
21
24
|
|
|
22
|
-
<el-popover placement="top" :width="160" trigger="hover">
|
|
25
|
+
<el-popover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
|
23
26
|
<div>
|
|
24
27
|
<slot name="page-bar-popover" :page="item">
|
|
25
|
-
<
|
|
26
|
-
|
|
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>
|
|
27
44
|
</slot>
|
|
28
45
|
</div>
|
|
29
46
|
<template #reference>
|
|
@@ -33,29 +50,135 @@
|
|
|
33
50
|
</template>
|
|
34
51
|
</el-popover>
|
|
35
52
|
</div>
|
|
36
|
-
</
|
|
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>
|
|
37
57
|
</div>
|
|
38
58
|
</template>
|
|
39
59
|
|
|
40
60
|
<script lang="ts">
|
|
41
|
-
import {
|
|
42
|
-
|
|
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';
|
|
43
74
|
|
|
44
|
-
import type { MPage } from '@tmagic/schema';
|
|
75
|
+
import type { MApp, MPage } from '@tmagic/schema';
|
|
45
76
|
import { NodeType } from '@tmagic/schema';
|
|
46
77
|
|
|
78
|
+
import ToolButton from '@editor/components/ToolButton.vue';
|
|
47
79
|
import type { Services } from '@editor/type';
|
|
48
80
|
import { generatePageNameByApp } from '@editor/utils/editor';
|
|
49
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
|
+
|
|
50
166
|
export default defineComponent({
|
|
51
|
-
components: { CaretBottom, Plus },
|
|
167
|
+
components: { ArrowLeftBold, ArrowRightBold, CaretBottom, Plus, ToolButton },
|
|
52
168
|
|
|
53
169
|
setup() {
|
|
54
170
|
const services = inject<Services>('services');
|
|
55
171
|
const editorService = services?.editorService;
|
|
56
172
|
|
|
173
|
+
const root = computed(() => editorService?.get<MApp>('root'));
|
|
174
|
+
|
|
57
175
|
return {
|
|
58
|
-
|
|
176
|
+
Delete: markRaw(Delete),
|
|
177
|
+
DocumentCopy: markRaw(DocumentCopy),
|
|
178
|
+
|
|
179
|
+
...useScroll(root),
|
|
180
|
+
|
|
181
|
+
root,
|
|
59
182
|
page: computed(() => editorService?.get('page')),
|
|
60
183
|
|
|
61
184
|
switchPage(page: MPage) {
|
|
@@ -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,41 +52,20 @@ 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', 'highlight'],
|
|
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
|
-
const highlightNode = computed(() => services?.editorService.get<MNode>('highlightNode'));
|
|
128
69
|
|
|
129
70
|
let stage: StageCore | null = null;
|
|
130
71
|
let runtime: Runtime | null = null;
|
|
@@ -133,43 +74,45 @@ export default defineComponent({
|
|
|
133
74
|
if (stage) return;
|
|
134
75
|
|
|
135
76
|
if (!stageContainer.value) return;
|
|
136
|
-
if (!(
|
|
77
|
+
if (!(stageOptions?.runtimeUrl || stageOptions?.render) || !root.value) return;
|
|
137
78
|
|
|
138
79
|
stage = new StageCore({
|
|
139
|
-
render:
|
|
140
|
-
runtimeUrl:
|
|
80
|
+
render: stageOptions.render,
|
|
81
|
+
runtimeUrl: stageOptions.runtimeUrl,
|
|
141
82
|
zoom: zoom.value,
|
|
142
|
-
|
|
143
|
-
|
|
83
|
+
autoScrollIntoView: stageOptions.autoScrollIntoView,
|
|
84
|
+
canSelect: (el, event, stop) => {
|
|
85
|
+
const elCanSelect = stageOptions.canSelect(el);
|
|
144
86
|
// 在组件联动过程中不能再往下选择,返回并触发 ui-select
|
|
145
|
-
if (uiSelectMode.value && elCanSelect) {
|
|
87
|
+
if (uiSelectMode.value && elCanSelect && event.type === 'mousedown') {
|
|
146
88
|
document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
|
|
147
89
|
return stop();
|
|
148
90
|
}
|
|
149
91
|
|
|
150
92
|
return elCanSelect;
|
|
151
93
|
},
|
|
152
|
-
moveableOptions:
|
|
94
|
+
moveableOptions: stageOptions.moveableOptions,
|
|
95
|
+
updateDragEl: stageOptions.updateDragEl,
|
|
153
96
|
});
|
|
154
97
|
|
|
155
|
-
services?.editorService.set('stage', stage);
|
|
98
|
+
services?.editorService.set('stage', markRaw(stage));
|
|
156
99
|
|
|
157
100
|
stage?.mount(stageContainer.value);
|
|
158
101
|
|
|
159
102
|
stage?.on('select', (el: HTMLElement) => {
|
|
160
|
-
|
|
103
|
+
services?.editorService.select(el.id);
|
|
161
104
|
});
|
|
162
105
|
|
|
163
106
|
stage?.on('highlight', (el: HTMLElement) => {
|
|
164
|
-
|
|
107
|
+
services?.editorService.highlight(el.id);
|
|
165
108
|
});
|
|
166
109
|
|
|
167
110
|
stage?.on('update', (ev: UpdateEventData) => {
|
|
168
|
-
|
|
111
|
+
services?.editorService.update({ id: ev.el.id, style: ev.style });
|
|
169
112
|
});
|
|
170
113
|
|
|
171
114
|
stage?.on('sort', (ev: SortEventData) => {
|
|
172
|
-
|
|
115
|
+
services?.editorService.sort(ev.src, ev.dist);
|
|
173
116
|
});
|
|
174
117
|
|
|
175
118
|
stage?.on('changeGuides', () => {
|
|
@@ -180,7 +123,7 @@ export default defineComponent({
|
|
|
180
123
|
stage?.on('runtime-ready', (rt) => {
|
|
181
124
|
runtime = rt;
|
|
182
125
|
// toRaw返回的值是一个引用而非快照,需要cloneDeep
|
|
183
|
-
root.value && runtime?.updateRootConfig(cloneDeep(toRaw(root.value)));
|
|
126
|
+
root.value && runtime?.updateRootConfig?.(cloneDeep(toRaw(root.value)));
|
|
184
127
|
page.value?.id && runtime?.updatePageId?.(page.value.id);
|
|
185
128
|
setTimeout(() => {
|
|
186
129
|
node.value && stage?.select(toRaw(node.value.id));
|
|
@@ -195,29 +138,10 @@ export default defineComponent({
|
|
|
195
138
|
|
|
196
139
|
watch(root, (root) => {
|
|
197
140
|
if (runtime && root) {
|
|
198
|
-
runtime.updateRootConfig(cloneDeep(toRaw(root)));
|
|
141
|
+
runtime.updateRootConfig?.(cloneDeep(toRaw(root)));
|
|
199
142
|
}
|
|
200
143
|
});
|
|
201
144
|
|
|
202
|
-
watch(
|
|
203
|
-
() => node.value?.id,
|
|
204
|
-
(id) => {
|
|
205
|
-
nextTick(() => {
|
|
206
|
-
// 等待相关dom变更完成后,再select,适用大多数场景
|
|
207
|
-
id && stage?.select(id);
|
|
208
|
-
});
|
|
209
|
-
},
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
watch(
|
|
213
|
-
() => highlightNode.value?.id,
|
|
214
|
-
(id) => {
|
|
215
|
-
nextTick(() => {
|
|
216
|
-
id && stage?.highlight(id);
|
|
217
|
-
});
|
|
218
|
-
},
|
|
219
|
-
);
|
|
220
|
-
|
|
221
145
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
222
146
|
for (const { contentRect } of entries) {
|
|
223
147
|
services?.uiService.set('stageContainerRect', {
|
|
@@ -240,9 +164,43 @@ export default defineComponent({
|
|
|
240
164
|
return {
|
|
241
165
|
stageWrap,
|
|
242
166
|
stageContainer,
|
|
167
|
+
menu,
|
|
243
168
|
stageRect,
|
|
244
169
|
zoom,
|
|
245
|
-
|
|
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
|
+
},
|
|
246
204
|
};
|
|
247
205
|
},
|
|
248
206
|
});
|