@tmagic/editor 1.1.0-beta.8 → 1.1.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.
- package/dist/style.css +46 -0
- package/dist/tmagic-editor.mjs +1052 -873
- package/dist/tmagic-editor.mjs.map +1 -1
- package/dist/tmagic-editor.umd.js +1054 -872
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/package.json +10 -10
- package/src/Editor.vue +31 -9
- package/src/components/ContentMenu.vue +4 -4
- package/src/components/Icon.vue +6 -20
- package/src/components/ScrollBar.vue +147 -0
- package/src/components/ScrollViewer.vue +89 -44
- package/src/components/ToolButton.vue +40 -138
- package/src/fields/UISelect.vue +2 -2
- package/src/index.ts +2 -0
- package/src/layouts/NavMenu.vue +156 -23
- package/src/layouts/PropsPanel.vue +49 -51
- package/src/layouts/sidebar/LayerMenu.vue +3 -3
- package/src/layouts/sidebar/LayerPanel.vue +39 -8
- package/src/layouts/workspace/PageBar.vue +1 -1
- package/src/layouts/workspace/Stage.vue +8 -86
- package/src/layouts/workspace/ViewerMenu.vue +6 -6
- package/src/layouts/workspace/Workspace.vue +133 -138
- package/src/services/componentList.ts +5 -0
- package/src/services/editor.ts +148 -76
- package/src/services/events.ts +8 -2
- package/src/services/props.ts +31 -52
- package/src/services/storage.ts +4 -0
- package/src/services/ui.ts +32 -30
- package/src/theme/nav-menu.scss +6 -0
- package/src/type.ts +28 -11
- package/src/utils/index.ts +1 -0
- package/src/utils/operator.ts +6 -60
- package/src/utils/props.ts +0 -3
- package/src/utils/scroll-viewer.ts +90 -158
- package/src/utils/stage.ts +91 -0
- package/types/Editor.vue.d.ts +17 -8
- package/types/components/ContentMenu.vue.d.ts +8 -8
- package/types/components/Icon.vue.d.ts +62 -12
- package/types/components/ScrollBar.vue.d.ts +83 -0
- package/types/components/ScrollViewer.vue.d.ts +131 -19
- package/types/components/ToolButton.vue.d.ts +56 -59
- package/types/index.d.ts +2 -0
- package/types/layouts/NavMenu.vue.d.ts +92 -23
- package/types/layouts/PropsPanel.vue.d.ts +25 -208
- package/types/layouts/sidebar/LayerMenu.vue.d.ts +7 -7
- package/types/layouts/sidebar/LayerPanel.vue.d.ts +17 -15
- package/types/layouts/workspace/Workspace.vue.d.ts +54 -4
- package/types/services/componentList.d.ts +1 -0
- package/types/services/editor.d.ts +7 -9
- package/types/services/events.d.ts +1 -0
- package/types/services/props.d.ts +41 -9
- package/types/services/storage.d.ts +1 -0
- package/types/services/ui.d.ts +3 -1
- package/types/type.d.ts +25 -11
- package/types/utils/index.d.ts +1 -0
- package/types/utils/operator.d.ts +1 -7
- package/types/utils/props.d.ts +0 -1
- package/types/utils/scroll-viewer.d.ts +16 -18
- package/types/utils/stage.d.ts +3 -0
package/src/type.ts
CHANGED
|
@@ -21,7 +21,7 @@ import type { Component } from 'vue';
|
|
|
21
21
|
import type { FormConfig } from '@tmagic/form';
|
|
22
22
|
import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
23
23
|
import type StageCore from '@tmagic/stage';
|
|
24
|
-
import type { MoveableOptions } from '@tmagic/stage';
|
|
24
|
+
import type { ContainerHighlightType, MoveableOptions } from '@tmagic/stage';
|
|
25
25
|
|
|
26
26
|
import type { ComponentListService } from './services/componentList';
|
|
27
27
|
import type { EditorService } from './services/editor';
|
|
@@ -53,6 +53,7 @@ export interface StageOptions {
|
|
|
53
53
|
autoScrollIntoView: boolean;
|
|
54
54
|
containerHighlightClassName: string;
|
|
55
55
|
containerHighlightDuration: number;
|
|
56
|
+
containerHighlightType: ContainerHighlightType;
|
|
56
57
|
render: () => HTMLDivElement;
|
|
57
58
|
moveableOptions: MoveableOptions | ((core?: StageCore) => MoveableOptions);
|
|
58
59
|
canSelect: (el: HTMLElement) => boolean | Promise<boolean>;
|
|
@@ -81,16 +82,22 @@ export interface ComponentGroupState {
|
|
|
81
82
|
list: ComponentGroup[];
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
export enum ColumnLayout {
|
|
86
|
+
LEFT = 'left',
|
|
87
|
+
CENTER = 'center',
|
|
88
|
+
RIGHT = 'right',
|
|
89
|
+
}
|
|
90
|
+
|
|
84
91
|
export interface SetColumnWidth {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
[ColumnLayout.LEFT]?: number;
|
|
93
|
+
[ColumnLayout.CENTER]?: number | 'auto';
|
|
94
|
+
[ColumnLayout.RIGHT]?: number;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
export interface GetColumnWidth {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
[ColumnLayout.LEFT]: number;
|
|
99
|
+
[ColumnLayout.CENTER]: number;
|
|
100
|
+
[ColumnLayout.RIGHT]: number;
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
export interface StageRect {
|
|
@@ -173,6 +180,7 @@ export interface MenuButton {
|
|
|
173
180
|
/** type为button/dropdown时点击运行的方法 */
|
|
174
181
|
handler?: (data: Services, event: MouseEvent) => Promise<any> | any;
|
|
175
182
|
/** type为dropdown时,下拉的菜单列表, 或者有子菜单时 */
|
|
183
|
+
className?: string;
|
|
176
184
|
items?: MenuButton[];
|
|
177
185
|
}
|
|
178
186
|
|
|
@@ -186,6 +194,7 @@ export interface MenuComponent {
|
|
|
186
194
|
listeners?: Record<string, Function>;
|
|
187
195
|
slots?: Record<string, any>;
|
|
188
196
|
/** 是否显示,默认为true */
|
|
197
|
+
className?: string;
|
|
189
198
|
display?: boolean | ((data?: Services) => Promise<boolean> | boolean);
|
|
190
199
|
}
|
|
191
200
|
|
|
@@ -208,16 +217,17 @@ export type MenuItem =
|
|
|
208
217
|
| 'guides'
|
|
209
218
|
| 'rule'
|
|
210
219
|
| MenuButton
|
|
211
|
-
| MenuComponent
|
|
220
|
+
| MenuComponent
|
|
221
|
+
| string;
|
|
212
222
|
|
|
213
223
|
/** 工具栏 */
|
|
214
224
|
export interface MenuBarData {
|
|
215
225
|
/** 顶部工具栏左边项 */
|
|
216
|
-
|
|
226
|
+
[ColumnLayout.LEFT]?: MenuItem[];
|
|
217
227
|
/** 顶部工具栏中间项 */
|
|
218
|
-
|
|
228
|
+
[ColumnLayout.CENTER]?: MenuItem[];
|
|
219
229
|
/** 顶部工具栏右边项 */
|
|
220
|
-
|
|
230
|
+
[ColumnLayout.RIGHT]?: MenuItem[];
|
|
221
231
|
}
|
|
222
232
|
|
|
223
233
|
export interface SideComponent extends MenuComponent {
|
|
@@ -286,3 +296,10 @@ export enum Keys {
|
|
|
286
296
|
|
|
287
297
|
export const H_GUIDE_LINE_STORAGE_KEY = '$MagicStageHorizontalGuidelinesData';
|
|
288
298
|
export const V_GUIDE_LINE_STORAGE_KEY = '$MagicStageVerticalGuidelinesData';
|
|
299
|
+
|
|
300
|
+
export interface ScrollViewerEvent {
|
|
301
|
+
scrollLeft: number;
|
|
302
|
+
scrollTop: number;
|
|
303
|
+
scrollHeight: number;
|
|
304
|
+
scrollWidth: number;
|
|
305
|
+
}
|
package/src/utils/index.ts
CHANGED
package/src/utils/operator.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { toRaw } from 'vue';
|
|
2
|
-
import {
|
|
2
|
+
import { isEmpty } from 'lodash-es';
|
|
3
3
|
|
|
4
|
-
import { Id, MApp, MContainer, MNode
|
|
4
|
+
import { Id, MApp, MContainer, MNode } from '@tmagic/schema';
|
|
5
5
|
import StageCore from '@tmagic/stage';
|
|
6
6
|
import { isPage } from '@tmagic/utils';
|
|
7
7
|
|
|
8
8
|
import editorService from '../services/editor';
|
|
9
|
-
import historyService from '../services/history';
|
|
10
9
|
import propsService from '../services/props';
|
|
11
10
|
import { AddMNode, PastePosition } from '../type';
|
|
12
|
-
import { generatePageNameByApp, getInitPositionStyle
|
|
11
|
+
import { generatePageNameByApp, getInitPositionStyle } from '../utils/editor';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* 粘贴前置操作:返回分配了新id以及校准了坐标的配置
|
|
@@ -43,7 +42,7 @@ export const beforePaste = async (position: PastePosition, config: MNode[]): Pro
|
|
|
43
42
|
pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
const pasteConfig = await propsService.setNewItemId(configItem
|
|
45
|
+
const pasteConfig = await propsService.setNewItemId(configItem);
|
|
47
46
|
|
|
48
47
|
if (pasteConfig.style) {
|
|
49
48
|
const { left, top } = pasteConfig.style;
|
|
@@ -87,64 +86,11 @@ export const getPositionInContainer = (position: PastePosition = {}, id: Id) =>
|
|
|
87
86
|
};
|
|
88
87
|
};
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
* 删除前置操作:实现了在编辑器中删除元素节点,并返回父级元素信息以供stage更新
|
|
92
|
-
* @param node 待删除的节点
|
|
93
|
-
* @returns 父级元素,root根元素
|
|
94
|
-
*/
|
|
95
|
-
export const beforeRemove = async (node: MNode): Promise<MContainer | void> => {
|
|
96
|
-
if (!node?.id) return;
|
|
97
|
-
|
|
98
|
-
const stage = editorService.get<StageCore | null>('stage');
|
|
99
|
-
const root = editorService.get<MApp | null>('root');
|
|
100
|
-
|
|
101
|
-
if (!root) throw new Error('没有root');
|
|
102
|
-
|
|
103
|
-
const { parent, node: curNode } = editorService.getNodeInfo(node.id, false);
|
|
104
|
-
|
|
105
|
-
if (!parent || !curNode) throw new Error('找不要删除的节点');
|
|
106
|
-
|
|
107
|
-
const index = getNodeIndex(curNode, parent);
|
|
108
|
-
|
|
109
|
-
if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
|
|
110
|
-
// 从配置中删除元素
|
|
111
|
-
parent.items?.splice(index, 1);
|
|
112
|
-
|
|
113
|
-
// 通知stage更新
|
|
114
|
-
stage?.remove({ id: node.id, root: cloneDeep(root) });
|
|
115
|
-
|
|
116
|
-
if (node.type === NodeType.PAGE) {
|
|
117
|
-
editorService.state.pageLength -= 1;
|
|
118
|
-
|
|
119
|
-
if (root.items[0]) {
|
|
120
|
-
await editorService.select(root.items[0]);
|
|
121
|
-
stage?.select(root.items[0].id);
|
|
122
|
-
} else {
|
|
123
|
-
editorService.set('node', null);
|
|
124
|
-
editorService.set('nodes', []);
|
|
125
|
-
editorService.set('parent', null);
|
|
126
|
-
editorService.set('page', null);
|
|
127
|
-
editorService.set('stage', null);
|
|
128
|
-
editorService.set('highlightNode', null);
|
|
129
|
-
editorService.resetModifiedNodeId();
|
|
130
|
-
historyService.reset();
|
|
131
|
-
|
|
132
|
-
editorService.emit('remove', node);
|
|
133
|
-
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
await editorService.select(parent);
|
|
138
|
-
stage?.select(parent.id);
|
|
139
|
-
}
|
|
140
|
-
return parent;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
export const getAddParent = (addNode: AddMNode | MNode[]) => {
|
|
89
|
+
export const getAddParent = (node: MNode) => {
|
|
144
90
|
const curNode = editorService.get<MContainer>('node');
|
|
145
91
|
|
|
146
92
|
let parentNode;
|
|
147
|
-
if (
|
|
93
|
+
if (isPage(node)) {
|
|
148
94
|
parentNode = editorService.get<MApp>('root');
|
|
149
95
|
} else if (curNode.items) {
|
|
150
96
|
parentNode = curNode;
|
package/src/utils/props.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
2
|
|
|
3
3
|
interface ScrollViewerOptions {
|
|
4
4
|
container: HTMLDivElement;
|
|
@@ -6,10 +6,7 @@ interface ScrollViewerOptions {
|
|
|
6
6
|
zoom: number;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export class ScrollViewer {
|
|
10
|
-
private enter = false;
|
|
11
|
-
private targetEnter = false;
|
|
12
|
-
private keydown = false;
|
|
9
|
+
export class ScrollViewer extends EventEmitter {
|
|
13
10
|
private container: HTMLDivElement;
|
|
14
11
|
private target: HTMLDivElement;
|
|
15
12
|
private zoom = 1;
|
|
@@ -17,200 +14,135 @@ export class ScrollViewer {
|
|
|
17
14
|
private scrollLeft = 0;
|
|
18
15
|
private scrollTop = 0;
|
|
19
16
|
|
|
20
|
-
private
|
|
21
|
-
private
|
|
17
|
+
private scrollHeight = 0;
|
|
18
|
+
private scrollWidth = 0;
|
|
22
19
|
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
const { width, height } = contentRect;
|
|
26
|
-
const targetRect = this.target.getBoundingClientRect();
|
|
27
|
-
const targetWidth = targetRect.width * this.zoom;
|
|
28
|
-
const targetMarginTop = Number(this.target.style.marginTop) || 0;
|
|
29
|
-
const targetHeight = (targetRect.height + targetMarginTop) * this.zoom;
|
|
20
|
+
private width = 0;
|
|
21
|
+
private height = 0;
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
if (targetHeight < height) {
|
|
35
|
-
(this.target as any)._top = 0;
|
|
36
|
-
}
|
|
23
|
+
private translateXCorrectionValue = 0;
|
|
24
|
+
private translateYCorrectionValue = 0;
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
private resizeObserver = new ResizeObserver(() => {
|
|
27
|
+
this.setSize();
|
|
28
|
+
this.setScrollSize();
|
|
40
29
|
});
|
|
41
30
|
|
|
42
31
|
constructor(options: ScrollViewerOptions) {
|
|
32
|
+
super();
|
|
33
|
+
|
|
43
34
|
this.container = options.container;
|
|
44
35
|
this.target = options.target;
|
|
45
36
|
this.zoom = options.zoom;
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
globalThis.addEventListener('keyup', this.keyupHandler);
|
|
49
|
-
|
|
50
|
-
this.container.addEventListener('mouseenter', this.mouseEnterHandler);
|
|
51
|
-
this.container.addEventListener('mouseleave', this.mouseLeaveHandler);
|
|
52
|
-
this.target.addEventListener('mouseenter', this.targetMouseEnterHandler);
|
|
53
|
-
this.target.addEventListener('mouseleave', this.targetMouseLeaveHandler);
|
|
54
|
-
|
|
55
|
-
this.container.addEventListener('wheel', this.wheelHandler);
|
|
38
|
+
this.container.addEventListener('wheel', this.wheelHandler, false);
|
|
56
39
|
|
|
40
|
+
this.setSize();
|
|
41
|
+
this.setScrollSize();
|
|
57
42
|
this.resizeObserver.observe(this.container);
|
|
58
43
|
}
|
|
59
44
|
|
|
60
45
|
public destroy() {
|
|
61
46
|
this.resizeObserver.disconnect();
|
|
62
|
-
|
|
63
|
-
this.
|
|
64
|
-
this.container.removeEventListener('mouseleave', this.mouseLeaveHandler);
|
|
65
|
-
this.target.removeEventListener('mouseenter', this.targetMouseEnterHandler);
|
|
66
|
-
this.target.removeEventListener('mouseleave', this.targetMouseLeaveHandler);
|
|
67
|
-
globalThis.removeEventListener('keydown', this.keydownHandler);
|
|
68
|
-
globalThis.removeEventListener('keyup', this.keyupHandler);
|
|
47
|
+
this.container.removeEventListener('wheel', this.wheelHandler, false);
|
|
48
|
+
this.removeAllListeners();
|
|
69
49
|
}
|
|
70
50
|
|
|
71
51
|
public setZoom(zoom: number) {
|
|
72
52
|
this.zoom = zoom;
|
|
53
|
+
this.setScrollSize();
|
|
73
54
|
}
|
|
74
55
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
56
|
+
public scrollTo({ left, top }: { left?: number; top?: number }) {
|
|
57
|
+
if (typeof left !== 'undefined') {
|
|
58
|
+
this.scrollLeft = left;
|
|
59
|
+
}
|
|
78
60
|
|
|
79
|
-
|
|
80
|
-
|
|
61
|
+
if (typeof top !== 'undefined') {
|
|
62
|
+
this.scrollTop = top;
|
|
63
|
+
}
|
|
81
64
|
|
|
82
|
-
|
|
83
|
-
this.
|
|
84
|
-
this.target.
|
|
85
|
-
document.removeEventListener('mousemove', this.mousemoveHandler);
|
|
86
|
-
document.removeEventListener('mouseup', this.mouseupHandler);
|
|
65
|
+
const translateX = -this.scrollLeft + this.translateXCorrectionValue;
|
|
66
|
+
const translateY = -this.scrollTop + this.translateYCorrectionValue;
|
|
67
|
+
this.target.style.transform = `translate(${translateX}px, ${translateY}px)`;
|
|
87
68
|
}
|
|
88
69
|
|
|
89
70
|
private wheelHandler = (event: WheelEvent) => {
|
|
90
|
-
if (this.targetEnter) return;
|
|
91
|
-
|
|
92
71
|
const { deltaX, deltaY, currentTarget } = event;
|
|
93
72
|
|
|
94
73
|
if (currentTarget !== this.container) return;
|
|
95
74
|
|
|
96
|
-
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
this.scrollTop = (this.target as any)._top;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
private mouseEnterHandler = () => {
|
|
103
|
-
this.enter = true;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
private mouseLeaveHandler = () => {
|
|
107
|
-
this.enter = false;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
private targetMouseEnterHandler = () => {
|
|
111
|
-
this.targetEnter = true;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
private targetMouseLeaveHandler = () => {
|
|
115
|
-
this.targetEnter = false;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
private mousedownHandler = (event: MouseEvent) => {
|
|
119
|
-
if (!this.keydown) return;
|
|
120
|
-
|
|
121
|
-
event.stopImmediatePropagation();
|
|
122
|
-
event.stopPropagation();
|
|
123
|
-
|
|
124
|
-
this.target.style.cursor = 'grabbing';
|
|
125
|
-
|
|
126
|
-
this.x = event.clientX;
|
|
127
|
-
this.y = event.clientY;
|
|
128
|
-
|
|
129
|
-
document.addEventListener('mousemove', this.mousemoveHandler);
|
|
130
|
-
document.addEventListener('mouseup', this.mouseupHandler);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
private mouseupHandler = () => {
|
|
134
|
-
this.x = 0;
|
|
135
|
-
this.y = 0;
|
|
136
|
-
|
|
137
|
-
this.scrollLeft = (this.target as any)._left;
|
|
138
|
-
this.scrollTop = (this.target as any)._top;
|
|
139
|
-
this.removeHandler();
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
private mousemoveHandler = (event: MouseEvent) => {
|
|
143
|
-
event.stopImmediatePropagation();
|
|
144
|
-
event.stopPropagation();
|
|
145
|
-
|
|
146
|
-
const deltaX = event.clientX - this.x;
|
|
147
|
-
const deltaY = event.clientY - this.y;
|
|
148
|
-
|
|
149
|
-
this.setScrollOffset(deltaX, deltaY);
|
|
150
|
-
this.scroll();
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
private keydownHandler = (event: KeyboardEvent) => {
|
|
154
|
-
if (event.code === Keys.ESCAPE && this.enter) {
|
|
155
|
-
event.preventDefault();
|
|
156
|
-
event.stopImmediatePropagation();
|
|
157
|
-
event.stopPropagation();
|
|
75
|
+
let top: number | undefined;
|
|
76
|
+
if (this.scrollHeight > this.height) {
|
|
77
|
+
top = this.scrollTop + this.getPos(deltaY, this.scrollTop, this.scrollHeight, this.height);
|
|
158
78
|
}
|
|
159
79
|
|
|
160
|
-
|
|
161
|
-
|
|
80
|
+
let left: number | undefined;
|
|
81
|
+
if (this.scrollWidth > this.width) {
|
|
82
|
+
left = this.scrollLeft + this.getPos(deltaX, this.scrollLeft, this.scrollWidth, this.width);
|
|
162
83
|
}
|
|
163
84
|
|
|
164
|
-
this.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
85
|
+
this.scrollTo({ left, top });
|
|
86
|
+
this.emit('scroll', {
|
|
87
|
+
scrollLeft: this.scrollLeft,
|
|
88
|
+
scrollTop: this.scrollTop,
|
|
89
|
+
scrollHeight: this.scrollHeight,
|
|
90
|
+
scrollWidth: this.scrollWidth,
|
|
91
|
+
});
|
|
168
92
|
};
|
|
169
93
|
|
|
170
|
-
private
|
|
171
|
-
|
|
172
|
-
|
|
94
|
+
private getPos(delta: number, scrollPos: number, scrollSize: number, size: number) {
|
|
95
|
+
let pos = 0;
|
|
96
|
+
if (delta < 0) {
|
|
97
|
+
if (scrollPos > 0) {
|
|
98
|
+
pos = Math.max(delta, -scrollPos);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
const leftPos = scrollSize - size - scrollPos;
|
|
102
|
+
if (leftPos > 0) {
|
|
103
|
+
pos = Math.min(delta, leftPos);
|
|
104
|
+
}
|
|
173
105
|
}
|
|
106
|
+
return pos;
|
|
107
|
+
}
|
|
174
108
|
|
|
175
|
-
|
|
176
|
-
event.stopImmediatePropagation();
|
|
177
|
-
event.stopPropagation();
|
|
178
|
-
|
|
179
|
-
this.keydown = false;
|
|
180
|
-
|
|
181
|
-
event.preventDefault();
|
|
182
|
-
|
|
183
|
-
this.removeHandler();
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
private setScrollOffset(deltaX: number, deltaY: number) {
|
|
187
|
-
const { width, height } = this.container.getBoundingClientRect();
|
|
109
|
+
private setScrollSize = () => {
|
|
188
110
|
const targetRect = this.target.getBoundingClientRect();
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
111
|
+
this.scrollWidth = targetRect.width * this.zoom + 100;
|
|
112
|
+
const targetMarginTop = Number(this.target.style.marginTop) || 0;
|
|
113
|
+
this.scrollHeight = (targetRect.height + targetMarginTop) * this.zoom + 100;
|
|
114
|
+
|
|
115
|
+
let left: number | undefined;
|
|
116
|
+
let top: number | undefined;
|
|
117
|
+
if (this.scrollWidth < this.width) {
|
|
118
|
+
left = 0;
|
|
119
|
+
this.translateXCorrectionValue = 0;
|
|
120
|
+
} else {
|
|
121
|
+
this.translateXCorrectionValue = (this.scrollWidth - this.width) / 2;
|
|
201
122
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
x = this.scrollLeft + Math.min(targetWidth - width - this.scrollLeft, deltaX);
|
|
208
|
-
} else {
|
|
209
|
-
x = this.scrollLeft + Math.max(-(targetWidth - width + this.scrollLeft), deltaX);
|
|
210
|
-
}
|
|
123
|
+
if (this.scrollHeight < this.height) {
|
|
124
|
+
top = 0;
|
|
125
|
+
this.translateYCorrectionValue = 0;
|
|
126
|
+
} else {
|
|
127
|
+
this.translateYCorrectionValue = (this.scrollHeight - this.height) / 2;
|
|
211
128
|
}
|
|
212
129
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
130
|
+
this.scrollTo({
|
|
131
|
+
left,
|
|
132
|
+
top,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
this.emit('scroll', {
|
|
136
|
+
scrollLeft: this.scrollLeft,
|
|
137
|
+
scrollTop: this.scrollTop,
|
|
138
|
+
scrollHeight: this.scrollHeight,
|
|
139
|
+
scrollWidth: this.scrollWidth,
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
private setSize = () => {
|
|
144
|
+
const { width, height } = this.container.getBoundingClientRect();
|
|
145
|
+
this.width = width;
|
|
146
|
+
this.height = height;
|
|
147
|
+
};
|
|
216
148
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
|
|
3
|
+
import { MApp, MPage } from '@tmagic/schema';
|
|
4
|
+
import StageCore, { GuidesType, SortEventData, UpdateEventData } from '@tmagic/stage';
|
|
5
|
+
|
|
6
|
+
import editorService from '../services/editor';
|
|
7
|
+
import uiService from '../services/ui';
|
|
8
|
+
import { H_GUIDE_LINE_STORAGE_KEY, StageOptions, V_GUIDE_LINE_STORAGE_KEY } from '../type';
|
|
9
|
+
|
|
10
|
+
import { getGuideLineFromCache } from './editor';
|
|
11
|
+
|
|
12
|
+
const root = computed(() => editorService.get<MApp>('root'));
|
|
13
|
+
const page = computed(() => editorService.get<MPage>('page'));
|
|
14
|
+
const zoom = computed(() => uiService.get<number>('zoom') || 1);
|
|
15
|
+
const uiSelectMode = computed(() => uiService.get<boolean>('uiSelectMode'));
|
|
16
|
+
|
|
17
|
+
const getGuideLineKey = (key: string) => `${key}_${root.value?.id}_${page.value?.id}`;
|
|
18
|
+
|
|
19
|
+
export const useStage = (stageOptions: StageOptions) => {
|
|
20
|
+
const stage = new StageCore({
|
|
21
|
+
render: stageOptions.render,
|
|
22
|
+
runtimeUrl: stageOptions.runtimeUrl,
|
|
23
|
+
zoom: zoom.value,
|
|
24
|
+
autoScrollIntoView: stageOptions.autoScrollIntoView,
|
|
25
|
+
isContainer: stageOptions.isContainer,
|
|
26
|
+
containerHighlightClassName: stageOptions.containerHighlightClassName,
|
|
27
|
+
containerHighlightDuration: stageOptions.containerHighlightDuration,
|
|
28
|
+
containerHighlightType: stageOptions.containerHighlightType,
|
|
29
|
+
canSelect: (el, event, stop) => {
|
|
30
|
+
const elCanSelect = stageOptions.canSelect(el);
|
|
31
|
+
// 在组件联动过程中不能再往下选择,返回并触发 ui-select
|
|
32
|
+
if (uiSelectMode.value && elCanSelect && event.type === 'mousedown') {
|
|
33
|
+
document.dispatchEvent(new CustomEvent('ui-select', { detail: el }));
|
|
34
|
+
return stop();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return elCanSelect;
|
|
38
|
+
},
|
|
39
|
+
moveableOptions: stageOptions.moveableOptions,
|
|
40
|
+
updateDragEl: stageOptions.updateDragEl,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
stage.mask.setGuides([
|
|
44
|
+
getGuideLineFromCache(getGuideLineKey(H_GUIDE_LINE_STORAGE_KEY)),
|
|
45
|
+
getGuideLineFromCache(getGuideLineKey(V_GUIDE_LINE_STORAGE_KEY)),
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
stage.on('select', (el: HTMLElement) => {
|
|
49
|
+
editorService.select(el.id);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
stage.on('highlight', (el: HTMLElement) => {
|
|
53
|
+
editorService.highlight(el.id);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
stage.on('multiSelect', (els: HTMLElement[]) => {
|
|
57
|
+
editorService.multiSelect(els.map((el) => el.id));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
stage.on('update', (ev: UpdateEventData) => {
|
|
61
|
+
if (ev.parentEl) {
|
|
62
|
+
for (const data of ev.data) {
|
|
63
|
+
editorService.moveToContainer({ id: data.el.id, style: data.style }, ev.parentEl.id);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
editorService.update(ev.data.map((data) => ({ id: data.el.id, style: data.style })));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
stage.on('sort', (ev: SortEventData) => {
|
|
72
|
+
editorService.sort(ev.src, ev.dist);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
stage.on('changeGuides', (e) => {
|
|
76
|
+
uiService.set('showGuides', true);
|
|
77
|
+
|
|
78
|
+
if (!root.value || !page.value) return;
|
|
79
|
+
|
|
80
|
+
const storageKey = getGuideLineKey(
|
|
81
|
+
e.type === GuidesType.HORIZONTAL ? H_GUIDE_LINE_STORAGE_KEY : V_GUIDE_LINE_STORAGE_KEY,
|
|
82
|
+
);
|
|
83
|
+
if (e.guides.length) {
|
|
84
|
+
globalThis.localStorage.setItem(storageKey, JSON.stringify(e.guides));
|
|
85
|
+
} else {
|
|
86
|
+
globalThis.localStorage.removeItem(storageKey);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return stage;
|
|
91
|
+
};
|
package/types/Editor.vue.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { PropType } from 'vue';
|
|
|
2
2
|
import type { FormConfig } from '@tmagic/form';
|
|
3
3
|
import type { MApp, MNode } from '@tmagic/schema';
|
|
4
4
|
import type StageCore from '@tmagic/stage';
|
|
5
|
-
import { MoveableOptions } from '@tmagic/stage';
|
|
6
|
-
import type { ComponentGroup, MenuBarData,
|
|
5
|
+
import { ContainerHighlightType, MoveableOptions } from '@tmagic/stage';
|
|
6
|
+
import type { ComponentGroup, MenuBarData, MenuButton, MenuComponent, Services, SideBarData, StageRect } from './type';
|
|
7
7
|
declare const _default: import("vue").DefineComponent<{
|
|
8
8
|
/** 页面初始值 */
|
|
9
9
|
modelValue: {
|
|
@@ -21,11 +21,11 @@ declare const _default: import("vue").DefineComponent<{
|
|
|
21
21
|
type: PropType<SideBarData>;
|
|
22
22
|
};
|
|
23
23
|
layerContentMenu: {
|
|
24
|
-
type: PropType<
|
|
24
|
+
type: PropType<(MenuButton | MenuComponent)[]>;
|
|
25
25
|
default: () => never[];
|
|
26
26
|
};
|
|
27
27
|
stageContentMenu: {
|
|
28
|
-
type: PropType<
|
|
28
|
+
type: PropType<(MenuButton | MenuComponent)[]>;
|
|
29
29
|
default: () => never[];
|
|
30
30
|
};
|
|
31
31
|
/** 顶部工具栏配置 */
|
|
@@ -83,6 +83,10 @@ declare const _default: import("vue").DefineComponent<{
|
|
|
83
83
|
type: NumberConstructor;
|
|
84
84
|
default: number;
|
|
85
85
|
};
|
|
86
|
+
containerHighlightType: {
|
|
87
|
+
type: PropType<ContainerHighlightType>;
|
|
88
|
+
default: ContainerHighlightType;
|
|
89
|
+
};
|
|
86
90
|
stageRect: {
|
|
87
91
|
type: PropType<StageRect>;
|
|
88
92
|
};
|
|
@@ -110,11 +114,11 @@ declare const _default: import("vue").DefineComponent<{
|
|
|
110
114
|
type: PropType<SideBarData>;
|
|
111
115
|
};
|
|
112
116
|
layerContentMenu: {
|
|
113
|
-
type: PropType<
|
|
117
|
+
type: PropType<(MenuButton | MenuComponent)[]>;
|
|
114
118
|
default: () => never[];
|
|
115
119
|
};
|
|
116
120
|
stageContentMenu: {
|
|
117
|
-
type: PropType<
|
|
121
|
+
type: PropType<(MenuButton | MenuComponent)[]>;
|
|
118
122
|
default: () => never[];
|
|
119
123
|
};
|
|
120
124
|
/** 顶部工具栏配置 */
|
|
@@ -172,6 +176,10 @@ declare const _default: import("vue").DefineComponent<{
|
|
|
172
176
|
type: NumberConstructor;
|
|
173
177
|
default: number;
|
|
174
178
|
};
|
|
179
|
+
containerHighlightType: {
|
|
180
|
+
type: PropType<ContainerHighlightType>;
|
|
181
|
+
default: ContainerHighlightType;
|
|
182
|
+
};
|
|
175
183
|
stageRect: {
|
|
176
184
|
type: PropType<StageRect>;
|
|
177
185
|
};
|
|
@@ -188,8 +196,8 @@ declare const _default: import("vue").DefineComponent<{
|
|
|
188
196
|
}, {
|
|
189
197
|
menu: MenuBarData;
|
|
190
198
|
codeOptions: Record<string, any>;
|
|
191
|
-
layerContentMenu:
|
|
192
|
-
stageContentMenu:
|
|
199
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
200
|
+
stageContentMenu: (MenuButton | MenuComponent)[];
|
|
193
201
|
modelValue: MApp;
|
|
194
202
|
componentGroupList: ComponentGroup[];
|
|
195
203
|
autoScrollIntoView: boolean;
|
|
@@ -200,5 +208,6 @@ declare const _default: import("vue").DefineComponent<{
|
|
|
200
208
|
isContainer: (el: HTMLElement) => boolean | Promise<boolean>;
|
|
201
209
|
containerHighlightClassName: string;
|
|
202
210
|
containerHighlightDuration: number;
|
|
211
|
+
containerHighlightType: ContainerHighlightType;
|
|
203
212
|
}>;
|
|
204
213
|
export default _default;
|