@tmagic/stage 1.0.0-beta.8 → 1.0.0-rc.2
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/tmagic-stage.es.js +482 -220
- package/dist/tmagic-stage.es.js.map +1 -1
- package/dist/tmagic-stage.umd.js +486 -226
- package/dist/tmagic-stage.umd.js.map +1 -1
- package/dist/types/src/StageCore.d.ts +14 -4
- package/dist/types/src/StageDragResize.d.ts +9 -6
- package/dist/types/src/StageHighlight.d.ts +27 -0
- package/dist/types/src/StageMask.d.ts +22 -0
- package/dist/types/src/TargetCalibrate.d.ts +18 -0
- package/dist/types/src/const.d.ts +5 -1
- package/dist/types/src/types.d.ts +13 -2
- package/dist/types/src/util.d.ts +2 -0
- package/package.json +12 -7
- package/src/StageCore.ts +87 -24
- package/src/StageDragResize.ts +110 -59
- package/src/StageHighlight.ts +81 -0
- package/src/StageMask.ts +84 -25
- package/src/StageRender.ts +13 -0
- package/src/TargetCalibrate.ts +135 -0
- package/src/const.ts +7 -0
- package/src/types.ts +15 -3
- package/src/util.ts +31 -1
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -27
|
@@ -2,7 +2,9 @@ import { MoveableOptions } from 'react-moveable/declaration/types';
|
|
|
2
2
|
import { Id, MApp, MNode } from '@tmagic/schema';
|
|
3
3
|
import { GuidesType } from './const';
|
|
4
4
|
import StageCore from './StageCore';
|
|
5
|
-
|
|
5
|
+
import StageDragResize from './StageDragResize';
|
|
6
|
+
import StageMask from './StageMask';
|
|
7
|
+
export declare type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
|
|
6
8
|
export declare type StageCoreConfig = {
|
|
7
9
|
/** 需要对齐的dom节点的CSS选择器字符串 */
|
|
8
10
|
snapElementQuerySelector?: string;
|
|
@@ -61,7 +63,7 @@ export interface RemoveData {
|
|
|
61
63
|
}
|
|
62
64
|
export interface Runtime {
|
|
63
65
|
beforeSelect?: (el: HTMLElement) => Promise<boolean> | boolean;
|
|
64
|
-
getSnapElements?: (el
|
|
66
|
+
getSnapElements?: (el?: HTMLElement) => HTMLElement[];
|
|
65
67
|
updateRootConfig: (config: MApp) => void;
|
|
66
68
|
updatePageId?: (id: Id) => void;
|
|
67
69
|
select?: (id: Id) => Promise<HTMLElement> | HTMLElement;
|
|
@@ -78,3 +80,12 @@ export interface Magic {
|
|
|
78
80
|
export interface RuntimeWindow extends Window {
|
|
79
81
|
magic: Magic;
|
|
80
82
|
}
|
|
83
|
+
export interface StageHighlightConfig {
|
|
84
|
+
core: StageCore;
|
|
85
|
+
container: HTMLElement;
|
|
86
|
+
}
|
|
87
|
+
export interface TargetCalibrateConfig {
|
|
88
|
+
parent: HTMLElement;
|
|
89
|
+
mask: StageMask;
|
|
90
|
+
dr: StageDragResize;
|
|
91
|
+
}
|
package/dist/types/src/util.d.ts
CHANGED
|
@@ -18,3 +18,5 @@ export declare const createDiv: ({ className, cssText }: {
|
|
|
18
18
|
className: string;
|
|
19
19
|
cssText: string;
|
|
20
20
|
}) => HTMLDivElement;
|
|
21
|
+
export declare const removeSelectedClassName: (doc: Document) => void;
|
|
22
|
+
export declare const addSelectedClassName: (el: Element, doc: Document) => void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.0-
|
|
2
|
+
"version": "1.0.0-rc.2",
|
|
3
3
|
"name": "@tmagic/stage",
|
|
4
|
-
"sideEffects":
|
|
4
|
+
"sideEffects": [
|
|
5
|
+
"dist/*"
|
|
6
|
+
],
|
|
5
7
|
"main": "dist/tmagic-stage.umd.js",
|
|
6
8
|
"module": "dist/tmagic-stage.es.js",
|
|
7
9
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -9,8 +11,10 @@
|
|
|
9
11
|
".": {
|
|
10
12
|
"import": "./dist/tmagic-stage.es.js",
|
|
11
13
|
"require": "./dist/tmagic-stage.umd.js"
|
|
12
|
-
}
|
|
14
|
+
},
|
|
15
|
+
"./*": "./*"
|
|
13
16
|
},
|
|
17
|
+
"license": "Apache-2.0",
|
|
14
18
|
"scripts": {
|
|
15
19
|
"build": "vite build"
|
|
16
20
|
},
|
|
@@ -23,11 +27,12 @@
|
|
|
23
27
|
},
|
|
24
28
|
"dependencies": {
|
|
25
29
|
"@scena/guides": "^0.17.0",
|
|
26
|
-
"@tmagic/schema": "^1.0.0-
|
|
27
|
-
"@tmagic/utils": "^1.0.0-
|
|
30
|
+
"@tmagic/schema": "^1.0.0-rc.2",
|
|
31
|
+
"@tmagic/utils": "^1.0.0-rc.2",
|
|
28
32
|
"events": "^3.3.0",
|
|
29
33
|
"lodash-es": "^4.17.21",
|
|
30
|
-
"moveable": "^0.
|
|
34
|
+
"moveable": "^0.29.4",
|
|
35
|
+
"moveable-helper": "^0.4.0"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
38
|
"@types/events": "^3.0.0",
|
|
@@ -38,5 +43,5 @@
|
|
|
38
43
|
"vite": "^2.3.7",
|
|
39
44
|
"vite-plugin-dts": "^0.9.6"
|
|
40
45
|
},
|
|
41
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "3e309780ed33640ab3fc986a93dd0e7b15c167e5"
|
|
42
47
|
}
|
package/src/StageCore.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { Id } from '@tmagic/schema';
|
|
|
22
22
|
|
|
23
23
|
import { DEFAULT_ZOOM, GHOST_EL_ID_PREFIX } from './const';
|
|
24
24
|
import StageDragResize from './StageDragResize';
|
|
25
|
+
import StageHighlight from './StageHighlight';
|
|
25
26
|
import StageMask from './StageMask';
|
|
26
27
|
import StageRender from './StageRender';
|
|
27
28
|
import {
|
|
@@ -34,15 +35,19 @@ import {
|
|
|
34
35
|
UpdateData,
|
|
35
36
|
UpdateEventData,
|
|
36
37
|
} from './types';
|
|
38
|
+
import { addSelectedClassName, removeSelectedClassName } from './util';
|
|
37
39
|
|
|
38
40
|
export default class StageCore extends EventEmitter {
|
|
39
41
|
public selectedDom: Element | undefined;
|
|
42
|
+
public highlightedDom: Element | undefined;
|
|
40
43
|
|
|
41
44
|
public renderer: StageRender;
|
|
42
45
|
public mask: StageMask;
|
|
43
46
|
public dr: StageDragResize;
|
|
47
|
+
public highlightLayer: StageHighlight;
|
|
44
48
|
public config: StageCoreConfig;
|
|
45
49
|
public zoom = DEFAULT_ZOOM;
|
|
50
|
+
public container?: HTMLDivElement;
|
|
46
51
|
private canSelect: CanSelect;
|
|
47
52
|
|
|
48
53
|
constructor(config: StageCoreConfig) {
|
|
@@ -56,9 +61,14 @@ export default class StageCore extends EventEmitter {
|
|
|
56
61
|
this.renderer = new StageRender({ core: this });
|
|
57
62
|
this.mask = new StageMask({ core: this });
|
|
58
63
|
this.dr = new StageDragResize({ core: this, container: this.mask.content });
|
|
64
|
+
this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
|
|
59
65
|
|
|
60
|
-
this.renderer.on('runtime-ready', (runtime: Runtime) =>
|
|
61
|
-
|
|
66
|
+
this.renderer.on('runtime-ready', (runtime: Runtime) => {
|
|
67
|
+
this.emit('runtime-ready', runtime);
|
|
68
|
+
});
|
|
69
|
+
this.renderer.on('page-el-update', (el: HTMLElement) => {
|
|
70
|
+
this.mask?.observe(el);
|
|
71
|
+
});
|
|
62
72
|
|
|
63
73
|
this.mask
|
|
64
74
|
.on('beforeSelect', (event: MouseEvent) => {
|
|
@@ -70,6 +80,18 @@ export default class StageCore extends EventEmitter {
|
|
|
70
80
|
.on('changeGuides', (data: GuidesEventData) => {
|
|
71
81
|
this.dr.setGuidelines(data.type, data.guides);
|
|
72
82
|
this.emit('changeGuides', data);
|
|
83
|
+
})
|
|
84
|
+
.on('highlight', async (event: MouseEvent) => {
|
|
85
|
+
await this.setElementFromPoint(event);
|
|
86
|
+
if (this.highlightedDom === this.selectedDom) {
|
|
87
|
+
this.highlightLayer.clearHighlight();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.highlightLayer.highlight(this.highlightedDom as HTMLElement);
|
|
91
|
+
this.emit('highlight', this.highlightedDom);
|
|
92
|
+
})
|
|
93
|
+
.on('clearHighlight', async () => {
|
|
94
|
+
this.highlightLayer.clearHighlight();
|
|
73
95
|
});
|
|
74
96
|
|
|
75
97
|
// 要先触发select,在触发update
|
|
@@ -102,8 +124,12 @@ export default class StageCore extends EventEmitter {
|
|
|
102
124
|
let stopped = false;
|
|
103
125
|
const stop = () => (stopped = true);
|
|
104
126
|
for (const el of els) {
|
|
105
|
-
if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.canSelect(el, stop))) {
|
|
127
|
+
if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.canSelect(el, event, stop))) {
|
|
106
128
|
if (stopped) break;
|
|
129
|
+
if (event.type === 'mousemove') {
|
|
130
|
+
this.highlight(el);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
107
133
|
this.select(el, event);
|
|
108
134
|
break;
|
|
109
135
|
}
|
|
@@ -115,58 +141,82 @@ export default class StageCore extends EventEmitter {
|
|
|
115
141
|
* @param idOrEl 组件Dom节点的id属性,或者Dom节点
|
|
116
142
|
*/
|
|
117
143
|
public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
|
|
118
|
-
|
|
119
|
-
if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
|
|
120
|
-
const runtime = await this.renderer?.getRuntime();
|
|
121
|
-
el = await runtime?.select?.(`${idOrEl}`);
|
|
122
|
-
if (!el) throw new Error(`不存在ID为${idOrEl}的元素`);
|
|
123
|
-
} else {
|
|
124
|
-
el = idOrEl;
|
|
125
|
-
}
|
|
144
|
+
const el = await this.getTargetElement(idOrEl);
|
|
126
145
|
|
|
127
146
|
if (el === this.selectedDom) return;
|
|
128
147
|
|
|
129
|
-
const runtime = await this.renderer
|
|
148
|
+
const runtime = await this.renderer.getRuntime();
|
|
149
|
+
|
|
150
|
+
await runtime?.select?.(el.id);
|
|
151
|
+
|
|
130
152
|
if (runtime?.beforeSelect) {
|
|
131
153
|
await runtime.beforeSelect(el);
|
|
132
154
|
}
|
|
133
155
|
|
|
134
156
|
this.mask.setLayout(el);
|
|
135
|
-
this.dr
|
|
157
|
+
this.dr.select(el, event);
|
|
158
|
+
this.mask.scrollIntoView(el);
|
|
159
|
+
|
|
136
160
|
this.selectedDom = el;
|
|
161
|
+
|
|
162
|
+
if (this.renderer.contentWindow) {
|
|
163
|
+
removeSelectedClassName(this.renderer.contentWindow.document);
|
|
164
|
+
if (this.selectedDom) {
|
|
165
|
+
addSelectedClassName(this.selectedDom, this.renderer.contentWindow.document);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
137
168
|
}
|
|
138
169
|
|
|
139
170
|
/**
|
|
140
171
|
* 更新选中的节点
|
|
141
172
|
* @param data 更新的数据
|
|
142
173
|
*/
|
|
143
|
-
public update(data: UpdateData): void {
|
|
174
|
+
public update(data: UpdateData): Promise<void> {
|
|
144
175
|
const { config } = data;
|
|
145
176
|
|
|
146
|
-
this.renderer?.getRuntime().then((runtime) => {
|
|
177
|
+
return this.renderer?.getRuntime().then((runtime) => {
|
|
147
178
|
runtime?.update?.(data);
|
|
148
179
|
// 更新配置后,需要等组件渲染更新
|
|
149
180
|
setTimeout(() => {
|
|
150
181
|
const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
|
|
151
|
-
|
|
182
|
+
// 有可能dom已经重新渲染,不再是原来的dom了,所以这里判断id,而不是判断el === this.selectedDom
|
|
183
|
+
if (el && el.id === this.selectedDom?.id) {
|
|
184
|
+
this.selectedDom = el;
|
|
152
185
|
// 更新了组件的布局,需要重新设置mask是否可以滚动
|
|
153
186
|
this.mask.setLayout(el);
|
|
154
|
-
this.dr
|
|
187
|
+
this.dr.updateMoveable(el);
|
|
155
188
|
}
|
|
156
189
|
}, 0);
|
|
157
190
|
});
|
|
158
191
|
}
|
|
159
192
|
|
|
160
|
-
|
|
161
|
-
|
|
193
|
+
/**
|
|
194
|
+
* 高亮选中组件
|
|
195
|
+
* @param el 页面Dom节点
|
|
196
|
+
*/
|
|
197
|
+
public async highlight(idOrEl: HTMLElement | Id): Promise<void> {
|
|
198
|
+
let el;
|
|
199
|
+
try {
|
|
200
|
+
el = await this.getTargetElement(idOrEl);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
this.highlightLayer.clearHighlight();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (el === this.highlightedDom) return;
|
|
206
|
+
this.highlightLayer.highlight(el);
|
|
207
|
+
this.highlightedDom = el;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public sortNode(data: SortEventData): Promise<void> {
|
|
211
|
+
return this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
|
|
162
212
|
}
|
|
163
213
|
|
|
164
|
-
public add(data: UpdateData): void {
|
|
165
|
-
this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
|
|
214
|
+
public add(data: UpdateData): Promise<void> {
|
|
215
|
+
return this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
|
|
166
216
|
}
|
|
167
217
|
|
|
168
|
-
public remove(data: RemoveData): void {
|
|
169
|
-
this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
|
|
218
|
+
public remove(data: RemoveData): Promise<void> {
|
|
219
|
+
return this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
|
|
170
220
|
}
|
|
171
221
|
|
|
172
222
|
public setZoom(zoom: number = DEFAULT_ZOOM): void {
|
|
@@ -178,6 +228,7 @@ export default class StageCore extends EventEmitter {
|
|
|
178
228
|
* @param el 将stage挂载到该Dom节点上
|
|
179
229
|
*/
|
|
180
230
|
public mount(el: HTMLDivElement): void {
|
|
231
|
+
this.container = el;
|
|
181
232
|
const { mask, renderer } = this;
|
|
182
233
|
|
|
183
234
|
renderer.mount(el);
|
|
@@ -198,12 +249,24 @@ export default class StageCore extends EventEmitter {
|
|
|
198
249
|
* 销毁实例
|
|
199
250
|
*/
|
|
200
251
|
public destroy(): void {
|
|
201
|
-
const { mask, renderer, dr } = this;
|
|
252
|
+
const { mask, renderer, dr, highlightLayer } = this;
|
|
202
253
|
|
|
203
254
|
renderer.destroy();
|
|
204
255
|
mask.destroy();
|
|
205
256
|
dr.destroy();
|
|
257
|
+
highlightLayer.destroy();
|
|
206
258
|
|
|
207
259
|
this.removeAllListeners();
|
|
260
|
+
|
|
261
|
+
this.container = undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private async getTargetElement(idOrEl: Id | HTMLElement): Promise<HTMLElement> {
|
|
265
|
+
if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
|
|
266
|
+
const el = this.renderer.contentWindow?.document.getElementById(`${idOrEl}`);
|
|
267
|
+
if (!el) throw new Error(`不存在ID为${idOrEl}的元素`);
|
|
268
|
+
return el;
|
|
269
|
+
}
|
|
270
|
+
return idOrEl;
|
|
208
271
|
}
|
|
209
272
|
}
|
package/src/StageDragResize.ts
CHANGED
|
@@ -21,10 +21,11 @@ import { EventEmitter } from 'events';
|
|
|
21
21
|
|
|
22
22
|
import type { MoveableOptions } from 'moveable';
|
|
23
23
|
import Moveable from 'moveable';
|
|
24
|
+
import MoveableHelper from 'moveable-helper';
|
|
24
25
|
|
|
25
|
-
import { GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
|
|
26
|
+
import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
|
|
26
27
|
import StageCore from './StageCore';
|
|
27
|
-
import type { SortEventData, StageDragResizeConfig } from './types';
|
|
28
|
+
import type { Offset, Runtime, SortEventData, StageDragResizeConfig } from './types';
|
|
28
29
|
import { getAbsolutePosition, getMode, getOffset } from './util';
|
|
29
30
|
|
|
30
31
|
enum ActionStatus {
|
|
@@ -40,21 +41,26 @@ export default class StageDragResize extends EventEmitter {
|
|
|
40
41
|
public core: StageCore;
|
|
41
42
|
public container: HTMLElement;
|
|
42
43
|
public target?: HTMLElement;
|
|
43
|
-
public dragEl
|
|
44
|
+
public dragEl: HTMLElement;
|
|
44
45
|
public moveable?: Moveable;
|
|
45
46
|
public horizontalGuidelines: number[] = [];
|
|
46
47
|
public verticalGuidelines: number[] = [];
|
|
48
|
+
public elementGuidelines: HTMLElement[] = [];
|
|
49
|
+
public mode: Mode = Mode.ABSOLUTE;
|
|
47
50
|
|
|
48
51
|
private moveableOptions: MoveableOptions = {};
|
|
49
52
|
private dragStatus: ActionStatus = ActionStatus.END;
|
|
50
53
|
private ghostEl: HTMLElement | undefined;
|
|
51
|
-
private
|
|
54
|
+
private moveableHelper?: MoveableHelper;
|
|
52
55
|
|
|
53
56
|
constructor(config: StageDragResizeConfig) {
|
|
54
57
|
super();
|
|
55
58
|
|
|
56
59
|
this.core = config.core;
|
|
57
60
|
this.container = config.container;
|
|
61
|
+
|
|
62
|
+
this.dragEl = globalThis.document.createElement('div');
|
|
63
|
+
this.container.append(this.dragEl);
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
/**
|
|
@@ -63,23 +69,24 @@ export default class StageDragResize extends EventEmitter {
|
|
|
63
69
|
* @param el 选中组件的Dom节点元素
|
|
64
70
|
* @param event 鼠标事件
|
|
65
71
|
*/
|
|
66
|
-
public
|
|
72
|
+
public select(el: HTMLElement, event?: MouseEvent): void {
|
|
73
|
+
const oldTarget = this.target;
|
|
67
74
|
this.target = el;
|
|
68
|
-
// 如果有滚动条会导致resize时获取到width,height不准确
|
|
69
|
-
if (/(auto|scroll)/.test(this.target.style.overflow)) {
|
|
70
|
-
this.target.style.overflow = 'hidden';
|
|
71
|
-
}
|
|
72
|
-
this.mode = getMode(el);
|
|
73
|
-
this.destroyDragEl();
|
|
74
|
-
this.destroyGhostEl();
|
|
75
75
|
|
|
76
|
-
this.
|
|
76
|
+
this.init(el);
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
// 从不能拖动到能拖动的节点之间切换,要重新创建moveable,不然dragStart不生效
|
|
79
|
+
if (!this.moveable || this.target !== oldTarget) {
|
|
80
|
+
this.moveableHelper = MoveableHelper.create({
|
|
81
|
+
useBeforeRender: true,
|
|
82
|
+
useRender: false,
|
|
83
|
+
createAuto: true,
|
|
84
|
+
});
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
this.initMoveable();
|
|
87
|
+
} else {
|
|
88
|
+
this.updateMoveable();
|
|
89
|
+
}
|
|
83
90
|
|
|
84
91
|
if (event) {
|
|
85
92
|
this.moveable?.dragStart(event);
|
|
@@ -89,11 +96,15 @@ export default class StageDragResize extends EventEmitter {
|
|
|
89
96
|
/**
|
|
90
97
|
* 初始化选中框并渲染出来
|
|
91
98
|
*/
|
|
92
|
-
public
|
|
99
|
+
public updateMoveable(el = this.target): void {
|
|
93
100
|
if (!this.moveable) throw new Error('未初始化moveable');
|
|
101
|
+
if (!el) throw new Error('为选中任何节点');
|
|
94
102
|
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
this.target = el;
|
|
104
|
+
|
|
105
|
+
this.init(el);
|
|
106
|
+
|
|
107
|
+
Object.entries(this.moveableOptions).forEach(([key, value]) => {
|
|
97
108
|
(this.moveable as any)[key] = value;
|
|
98
109
|
});
|
|
99
110
|
this.moveable.updateTarget();
|
|
@@ -102,17 +113,21 @@ export default class StageDragResize extends EventEmitter {
|
|
|
102
113
|
public setGuidelines(type: GuidesType, guidelines: number[]): void {
|
|
103
114
|
if (type === GuidesType.HORIZONTAL) {
|
|
104
115
|
this.horizontalGuidelines = guidelines;
|
|
116
|
+
this.moveableOptions.horizontalGuidelines = guidelines;
|
|
105
117
|
} else if (type === GuidesType.VERTICAL) {
|
|
106
118
|
this.verticalGuidelines = guidelines;
|
|
119
|
+
this.moveableOptions.verticalGuidelines = guidelines;
|
|
107
120
|
}
|
|
108
121
|
|
|
109
|
-
this.
|
|
122
|
+
this.updateMoveable();
|
|
110
123
|
}
|
|
111
124
|
|
|
112
125
|
public clearGuides() {
|
|
113
|
-
this.verticalGuidelines = [];
|
|
114
126
|
this.horizontalGuidelines = [];
|
|
115
|
-
this.
|
|
127
|
+
this.verticalGuidelines = [];
|
|
128
|
+
this.moveableOptions.horizontalGuidelines = [];
|
|
129
|
+
this.moveableOptions.verticalGuidelines = [];
|
|
130
|
+
this.updateMoveable();
|
|
116
131
|
}
|
|
117
132
|
|
|
118
133
|
/**
|
|
@@ -126,6 +141,22 @@ export default class StageDragResize extends EventEmitter {
|
|
|
126
141
|
this.removeAllListeners();
|
|
127
142
|
}
|
|
128
143
|
|
|
144
|
+
private init(el: HTMLElement): void {
|
|
145
|
+
// 如果有滚动条会导致resize时获取到width,height不准确
|
|
146
|
+
if (/(auto|scroll)/.test(el.style.overflow)) {
|
|
147
|
+
el.style.overflow = 'hidden';
|
|
148
|
+
}
|
|
149
|
+
this.mode = getMode(el);
|
|
150
|
+
|
|
151
|
+
this.destroyGhostEl();
|
|
152
|
+
|
|
153
|
+
this.updateDragEl(el);
|
|
154
|
+
|
|
155
|
+
this.moveableOptions = this.getOptions({
|
|
156
|
+
target: this.dragEl,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
129
160
|
private initMoveable() {
|
|
130
161
|
this.moveable?.destroy();
|
|
131
162
|
|
|
@@ -188,34 +219,42 @@ export default class StageDragResize extends EventEmitter {
|
|
|
188
219
|
private bindDragEvent(): void {
|
|
189
220
|
if (!this.moveable) throw new Error('moveable 为初始化');
|
|
190
221
|
|
|
222
|
+
let offset: Offset | null = null;
|
|
223
|
+
|
|
191
224
|
this.moveable
|
|
192
|
-
.on('dragStart', () => {
|
|
225
|
+
.on('dragStart', (e) => {
|
|
193
226
|
if (!this.target) throw new Error('未选中组件');
|
|
194
227
|
|
|
195
228
|
this.dragStatus = ActionStatus.START;
|
|
196
229
|
|
|
230
|
+
this.moveableHelper?.onDragStart(e);
|
|
231
|
+
|
|
197
232
|
if (this.mode === Mode.SORTABLE) {
|
|
198
233
|
this.ghostEl = this.generateGhostEl(this.target);
|
|
199
234
|
}
|
|
200
235
|
})
|
|
201
|
-
.on('drag', (
|
|
236
|
+
.on('drag', (e) => {
|
|
202
237
|
if (!this.target || !this.dragEl) return;
|
|
238
|
+
|
|
239
|
+
if (!offset) {
|
|
240
|
+
offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
|
|
241
|
+
}
|
|
242
|
+
|
|
203
243
|
this.dragStatus = ActionStatus.ING;
|
|
204
244
|
|
|
205
|
-
const
|
|
245
|
+
const { left, top } = e;
|
|
206
246
|
|
|
207
247
|
// 流式布局
|
|
208
248
|
if (this.ghostEl) {
|
|
209
249
|
this.dragEl.style.top = `${top}px`;
|
|
210
|
-
this.ghostEl.style.top = `${offset.top}px`;
|
|
250
|
+
this.ghostEl.style.top = `${top + offset.top}px`;
|
|
211
251
|
return;
|
|
212
252
|
}
|
|
213
253
|
|
|
214
|
-
this.
|
|
215
|
-
this.dragEl.style.top = `${top}px`;
|
|
254
|
+
this.moveableHelper?.onDrag(e);
|
|
216
255
|
|
|
217
|
-
this.target.style.left = `${offset.left}px`;
|
|
218
|
-
this.target.style.top = `${offset.top}px`;
|
|
256
|
+
this.target.style.left = `${left + offset.left}px`;
|
|
257
|
+
this.target.style.top = `${top + offset.top}px`;
|
|
219
258
|
})
|
|
220
259
|
.on('dragEnd', () => {
|
|
221
260
|
// 点击不拖动时会触发dragStart和dragEnd,但是不会有drag事件
|
|
@@ -228,24 +267,23 @@ export default class StageDragResize extends EventEmitter {
|
|
|
228
267
|
this.update();
|
|
229
268
|
}
|
|
230
269
|
}
|
|
270
|
+
offset = null;
|
|
231
271
|
|
|
232
272
|
this.dragStatus = ActionStatus.END;
|
|
233
273
|
this.destroyGhostEl();
|
|
234
274
|
});
|
|
235
275
|
}
|
|
236
276
|
|
|
237
|
-
private
|
|
238
|
-
const { renderer } = this.core;
|
|
277
|
+
private getSnapElements(runtime: Runtime, el?: HTMLElement): HTMLElement[] {
|
|
278
|
+
const { renderer, mask } = this.core;
|
|
239
279
|
const getSnapElements =
|
|
240
|
-
|
|
280
|
+
runtime?.getSnapElements ||
|
|
241
281
|
(() => {
|
|
242
282
|
const doc = renderer.contentWindow?.document;
|
|
243
283
|
return doc ? Array.from(doc.querySelectorAll('[id]')) : [];
|
|
244
284
|
});
|
|
245
|
-
return (
|
|
246
|
-
|
|
247
|
-
// 排除掉当前组件本身
|
|
248
|
-
.filter((element) => element !== this.target && !this.target?.contains(element))
|
|
285
|
+
return getSnapElements(el).filter(
|
|
286
|
+
(element) => element !== this.target && !this.target?.contains(element) && element !== mask.page,
|
|
249
287
|
);
|
|
250
288
|
}
|
|
251
289
|
|
|
@@ -291,7 +329,7 @@ export default class StageDragResize extends EventEmitter {
|
|
|
291
329
|
|
|
292
330
|
const ghostEl = el.cloneNode(true) as HTMLElement;
|
|
293
331
|
const { top, left } = getAbsolutePosition(el, getOffset(el));
|
|
294
|
-
ghostEl.id = `${GHOST_EL_ID_PREFIX}${
|
|
332
|
+
ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
|
|
295
333
|
ghostEl.style.zIndex = '5';
|
|
296
334
|
ghostEl.style.opacity = '.5';
|
|
297
335
|
ghostEl.style.position = 'absolute';
|
|
@@ -306,34 +344,31 @@ export default class StageDragResize extends EventEmitter {
|
|
|
306
344
|
this.ghostEl = undefined;
|
|
307
345
|
}
|
|
308
346
|
|
|
309
|
-
private
|
|
310
|
-
if (this.dragEl) {
|
|
311
|
-
this.destroyDragEl();
|
|
312
|
-
}
|
|
313
|
-
|
|
347
|
+
private updateDragEl(el: HTMLElement) {
|
|
314
348
|
const { width, height } = el.getBoundingClientRect();
|
|
315
349
|
const offset = getOffset(el);
|
|
316
|
-
|
|
317
|
-
dragEl.style.cssText = `
|
|
350
|
+
|
|
351
|
+
this.dragEl.style.cssText = `
|
|
318
352
|
position: absolute;
|
|
319
353
|
left: ${offset.left}px;
|
|
320
354
|
top: ${offset.top}px;
|
|
321
355
|
width: ${width}px;
|
|
322
356
|
height: ${height}px;
|
|
357
|
+
z-index: 9;
|
|
323
358
|
`;
|
|
324
|
-
|
|
325
|
-
|
|
359
|
+
|
|
360
|
+
this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
|
|
326
361
|
}
|
|
327
362
|
|
|
328
363
|
private destroyDragEl(): void {
|
|
329
364
|
this.dragEl?.remove();
|
|
330
|
-
this.dragEl = undefined;
|
|
331
365
|
}
|
|
332
366
|
|
|
333
|
-
private
|
|
367
|
+
private getOptions(options: MoveableOptions = {}): MoveableOptions {
|
|
334
368
|
if (!this.target) return {};
|
|
335
369
|
|
|
336
370
|
const isAbsolute = this.mode === Mode.ABSOLUTE;
|
|
371
|
+
const isFixed = this.mode === Mode.FIXED;
|
|
337
372
|
|
|
338
373
|
let { moveableOptions = {} } = this.core.config;
|
|
339
374
|
|
|
@@ -342,20 +377,36 @@ export default class StageDragResize extends EventEmitter {
|
|
|
342
377
|
}
|
|
343
378
|
|
|
344
379
|
return {
|
|
345
|
-
|
|
346
|
-
|
|
380
|
+
origin: false,
|
|
381
|
+
rootContainer: this.core.container,
|
|
347
382
|
zoom: 1,
|
|
348
|
-
dragArea:
|
|
383
|
+
dragArea: false,
|
|
349
384
|
draggable: true,
|
|
350
385
|
resizable: true,
|
|
351
|
-
snappable: isAbsolute,
|
|
352
|
-
snapGap: isAbsolute,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
386
|
+
snappable: isAbsolute || isFixed,
|
|
387
|
+
snapGap: isAbsolute || isFixed,
|
|
388
|
+
snapThreshold: 5,
|
|
389
|
+
snapDigit: 0,
|
|
390
|
+
throttleDrag: 0,
|
|
391
|
+
isDisplaySnapDigit: isAbsolute,
|
|
392
|
+
snapDirections: {
|
|
393
|
+
top: isAbsolute,
|
|
394
|
+
right: isAbsolute,
|
|
395
|
+
bottom: isAbsolute,
|
|
396
|
+
left: isAbsolute,
|
|
397
|
+
center: isAbsolute,
|
|
398
|
+
middle: isAbsolute,
|
|
399
|
+
},
|
|
400
|
+
elementSnapDirections: {
|
|
401
|
+
top: isAbsolute,
|
|
402
|
+
right: isAbsolute,
|
|
403
|
+
bottom: isAbsolute,
|
|
404
|
+
left: isAbsolute,
|
|
405
|
+
},
|
|
406
|
+
isDisplayInnerSnapDigit: true,
|
|
357
407
|
horizontalGuidelines: this.horizontalGuidelines,
|
|
358
408
|
verticalGuidelines: this.verticalGuidelines,
|
|
409
|
+
elementGuidelines: this.elementGuidelines,
|
|
359
410
|
|
|
360
411
|
bounds: {
|
|
361
412
|
top: 0,
|