@tmagic/stage 1.0.0-beta.8 → 1.0.0-beta.9
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/tmagic-stage.es.js +142 -25
- package/dist/tmagic-stage.es.js.map +1 -1
- package/dist/tmagic-stage.umd.js +145 -29
- package/dist/tmagic-stage.umd.js.map +1 -1
- package/dist/types/src/StageCore.d.ts +10 -0
- package/dist/types/src/StageDragResize.d.ts +1 -0
- package/dist/types/src/StageHighlight.d.ts +25 -0
- package/dist/types/src/StageMask.d.ts +5 -0
- package/dist/types/src/const.d.ts +3 -1
- package/dist/types/src/types.d.ts +4 -0
- package/dist/types/src/util.d.ts +2 -0
- package/package.json +5 -4
- package/src/StageCore.ts +56 -10
- package/src/StageDragResize.ts +38 -14
- package/src/StageHighlight.ts +67 -0
- package/src/StageMask.ts +15 -1
- package/src/StageRender.ts +13 -0
- package/src/const.ts +3 -0
- package/src/types.ts +5 -0
- package/src/util.ts +15 -1
package/src/StageDragResize.ts
CHANGED
|
@@ -21,6 +21,7 @@ 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
26
|
import { GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
|
|
26
27
|
import StageCore from './StageCore';
|
|
@@ -49,6 +50,7 @@ export default class StageDragResize extends EventEmitter {
|
|
|
49
50
|
private dragStatus: ActionStatus = ActionStatus.END;
|
|
50
51
|
private ghostEl: HTMLElement | undefined;
|
|
51
52
|
private mode: Mode = Mode.ABSOLUTE;
|
|
53
|
+
private moveableHelper?: MoveableHelper;
|
|
52
54
|
|
|
53
55
|
constructor(config: StageDragResizeConfig) {
|
|
54
56
|
super();
|
|
@@ -79,6 +81,12 @@ export default class StageDragResize extends EventEmitter {
|
|
|
79
81
|
target: this.dragEl || this.target,
|
|
80
82
|
});
|
|
81
83
|
|
|
84
|
+
this.moveableHelper = MoveableHelper.create({
|
|
85
|
+
useBeforeRender: true,
|
|
86
|
+
useRender: false,
|
|
87
|
+
createAuto: true,
|
|
88
|
+
});
|
|
89
|
+
|
|
82
90
|
this.initMoveable();
|
|
83
91
|
|
|
84
92
|
if (event) {
|
|
@@ -188,34 +196,42 @@ export default class StageDragResize extends EventEmitter {
|
|
|
188
196
|
private bindDragEvent(): void {
|
|
189
197
|
if (!this.moveable) throw new Error('moveable 为初始化');
|
|
190
198
|
|
|
199
|
+
let offset = {
|
|
200
|
+
left: 0,
|
|
201
|
+
top: 0,
|
|
202
|
+
};
|
|
203
|
+
|
|
191
204
|
this.moveable
|
|
192
|
-
.on('dragStart', () => {
|
|
205
|
+
.on('dragStart', (e) => {
|
|
193
206
|
if (!this.target) throw new Error('未选中组件');
|
|
194
207
|
|
|
195
208
|
this.dragStatus = ActionStatus.START;
|
|
196
209
|
|
|
210
|
+
this.moveableHelper?.onDragStart(e);
|
|
211
|
+
|
|
212
|
+
offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
|
|
213
|
+
|
|
197
214
|
if (this.mode === Mode.SORTABLE) {
|
|
198
215
|
this.ghostEl = this.generateGhostEl(this.target);
|
|
199
216
|
}
|
|
200
217
|
})
|
|
201
|
-
.on('drag', (
|
|
218
|
+
.on('drag', (e) => {
|
|
202
219
|
if (!this.target || !this.dragEl) return;
|
|
203
220
|
this.dragStatus = ActionStatus.ING;
|
|
204
221
|
|
|
205
|
-
const
|
|
222
|
+
const { left, top } = e;
|
|
206
223
|
|
|
207
224
|
// 流式布局
|
|
208
225
|
if (this.ghostEl) {
|
|
209
226
|
this.dragEl.style.top = `${top}px`;
|
|
210
|
-
this.ghostEl.style.top = `${offset.top}px`;
|
|
227
|
+
this.ghostEl.style.top = `${top + offset.top}px`;
|
|
211
228
|
return;
|
|
212
229
|
}
|
|
213
230
|
|
|
214
|
-
this.
|
|
215
|
-
this.dragEl.style.top = `${top}px`;
|
|
231
|
+
this.moveableHelper?.onDrag(e);
|
|
216
232
|
|
|
217
|
-
this.target.style.left = `${offset.left}px`;
|
|
218
|
-
this.target.style.top = `${offset.top}px`;
|
|
233
|
+
this.target.style.left = `${left + offset.left}px`;
|
|
234
|
+
this.target.style.top = `${top + offset.top}px`;
|
|
219
235
|
})
|
|
220
236
|
.on('dragEnd', () => {
|
|
221
237
|
// 点击不拖动时会触发dragStart和dragEnd,但是不会有drag事件
|
|
@@ -342,18 +358,26 @@ export default class StageDragResize extends EventEmitter {
|
|
|
342
358
|
}
|
|
343
359
|
|
|
344
360
|
return {
|
|
345
|
-
scrollable: true,
|
|
346
361
|
origin: true,
|
|
362
|
+
rootContainer: this.core.container,
|
|
347
363
|
zoom: 1,
|
|
348
|
-
dragArea:
|
|
364
|
+
dragArea: false,
|
|
349
365
|
draggable: true,
|
|
350
366
|
resizable: true,
|
|
351
367
|
snappable: isAbsolute,
|
|
352
368
|
snapGap: isAbsolute,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
369
|
+
snapThreshold: 5,
|
|
370
|
+
snapDigit: 0,
|
|
371
|
+
throttleDrag: 0,
|
|
372
|
+
isDisplaySnapDigit: isAbsolute,
|
|
373
|
+
snapDirections: {
|
|
374
|
+
top: isAbsolute,
|
|
375
|
+
right: isAbsolute,
|
|
376
|
+
bottom: isAbsolute,
|
|
377
|
+
left: isAbsolute,
|
|
378
|
+
center: isAbsolute,
|
|
379
|
+
middle: isAbsolute,
|
|
380
|
+
},
|
|
357
381
|
horizontalGuidelines: this.horizontalGuidelines,
|
|
358
382
|
verticalGuidelines: this.verticalGuidelines,
|
|
359
383
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* eslint-disable no-param-reassign */
|
|
20
|
+
import { EventEmitter } from 'events';
|
|
21
|
+
|
|
22
|
+
import Moveable from 'moveable';
|
|
23
|
+
|
|
24
|
+
import StageCore from './StageCore';
|
|
25
|
+
import type { StageHighlightConfig } from './types';
|
|
26
|
+
export default class StageHighlight extends EventEmitter {
|
|
27
|
+
public core: StageCore;
|
|
28
|
+
public container: HTMLElement;
|
|
29
|
+
public target?: HTMLElement;
|
|
30
|
+
public moveable?: Moveable;
|
|
31
|
+
|
|
32
|
+
constructor(config: StageHighlightConfig) {
|
|
33
|
+
super();
|
|
34
|
+
|
|
35
|
+
this.core = config.core;
|
|
36
|
+
this.container = config.container;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 高亮鼠标悬停的组件
|
|
41
|
+
* @param el 选中组件的Dom节点元素
|
|
42
|
+
*/
|
|
43
|
+
public highlight(el: HTMLElement): void {
|
|
44
|
+
if (!el || el === this.target) return;
|
|
45
|
+
this.target = el;
|
|
46
|
+
this.moveable?.destroy();
|
|
47
|
+
this.moveable = new Moveable(this.container, {
|
|
48
|
+
target: this.target,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 清空高亮
|
|
54
|
+
*/
|
|
55
|
+
public clearHighlight(): void {
|
|
56
|
+
if (!this.moveable) return;
|
|
57
|
+
this.moveable.target = null;
|
|
58
|
+
this.moveable.updateTarget();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 销毁实例
|
|
63
|
+
*/
|
|
64
|
+
public destroy(): void {
|
|
65
|
+
this.moveable?.destroy();
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/StageMask.ts
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { throttle } from 'lodash-es';
|
|
20
|
+
|
|
19
21
|
import { Mode, MouseButton, ZIndex } from './const';
|
|
20
22
|
import Rule from './Rule';
|
|
21
23
|
import type StageCore from './StageCore';
|
|
@@ -23,6 +25,7 @@ import type { StageMaskConfig } from './types';
|
|
|
23
25
|
import { createDiv, getScrollParent, isFixedParent } from './util';
|
|
24
26
|
|
|
25
27
|
const wrapperClassName = 'editor-mask-wrapper';
|
|
28
|
+
const throttleTime = 100;
|
|
26
29
|
|
|
27
30
|
const hideScrollbar = () => {
|
|
28
31
|
const style = globalThis.document.createElement('style');
|
|
@@ -82,6 +85,13 @@ export default class StageMask extends Rule {
|
|
|
82
85
|
private mode: Mode = Mode.ABSOLUTE;
|
|
83
86
|
private pageResizeObserver: ResizeObserver | null = null;
|
|
84
87
|
private wrapperResizeObserver: ResizeObserver | null = null;
|
|
88
|
+
/**
|
|
89
|
+
* 高亮事件处理函数
|
|
90
|
+
* @param event 事件对象
|
|
91
|
+
*/
|
|
92
|
+
private highlightHandler = throttle((event: MouseEvent): void => {
|
|
93
|
+
this.emit('highlight', event);
|
|
94
|
+
}, throttleTime);
|
|
85
95
|
|
|
86
96
|
constructor(config: StageMaskConfig) {
|
|
87
97
|
const wrapper = createWrapper();
|
|
@@ -93,6 +103,7 @@ export default class StageMask extends Rule {
|
|
|
93
103
|
this.content.addEventListener('mousedown', this.mouseDownHandler);
|
|
94
104
|
this.wrapper.appendChild(this.content);
|
|
95
105
|
this.content.addEventListener('wheel', this.mouseWheelHandler);
|
|
106
|
+
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
public setMode(mode: Mode) {
|
|
@@ -203,7 +214,7 @@ export default class StageMask extends Rule {
|
|
|
203
214
|
* 点击事件处理函数
|
|
204
215
|
* @param event 事件对象
|
|
205
216
|
*/
|
|
206
|
-
private mouseDownHandler =
|
|
217
|
+
private mouseDownHandler = (event: MouseEvent): void => {
|
|
207
218
|
event.stopImmediatePropagation();
|
|
208
219
|
event.stopPropagation();
|
|
209
220
|
|
|
@@ -218,10 +229,13 @@ export default class StageMask extends Rule {
|
|
|
218
229
|
|
|
219
230
|
// 如果是右键点击,这里的mouseup事件监听没有效果
|
|
220
231
|
globalThis.document.addEventListener('mouseup', this.mouseUpHandler);
|
|
232
|
+
this.content.removeEventListener('mousemove', this.highlightHandler);
|
|
233
|
+
this.emit('clearHighlight');
|
|
221
234
|
};
|
|
222
235
|
|
|
223
236
|
private mouseUpHandler = (): void => {
|
|
224
237
|
globalThis.document.removeEventListener('mouseup', this.mouseUpHandler);
|
|
238
|
+
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
225
239
|
this.emit('select');
|
|
226
240
|
};
|
|
227
241
|
|
package/src/StageRender.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { EventEmitter } from 'events';
|
|
20
20
|
|
|
21
|
+
import { SELECTED_CLASS, ZIndex } from './const';
|
|
21
22
|
import StageCore from './StageCore';
|
|
22
23
|
import type { Runtime, RuntimeWindow, StageRenderConfig } from './types';
|
|
23
24
|
import { getHost, isSameDomain } from './util';
|
|
@@ -114,11 +115,23 @@ export default class StageRender extends EventEmitter {
|
|
|
114
115
|
|
|
115
116
|
private loadHandler = async () => {
|
|
116
117
|
this.emit('onload');
|
|
118
|
+
|
|
117
119
|
if (this.render) {
|
|
118
120
|
const el = await this.render(this.core);
|
|
119
121
|
if (el) {
|
|
120
122
|
this.iframe?.contentDocument?.body?.appendChild(el);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
125
|
+
|
|
126
|
+
if (this.contentWindow) {
|
|
127
|
+
const style = this.contentWindow.document.createElement('style');
|
|
128
|
+
style.id = 'tmagic-stage-render';
|
|
129
|
+
style.innerHTML = `
|
|
130
|
+
.${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
|
|
131
|
+
z-index: ${ZIndex.SELECTED_EL};
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
this.contentWindow.document.head.appendChild(style);
|
|
135
|
+
}
|
|
123
136
|
};
|
|
124
137
|
}
|
package/src/const.ts
CHANGED
|
@@ -29,6 +29,7 @@ export enum GuidesType {
|
|
|
29
29
|
|
|
30
30
|
export enum ZIndex {
|
|
31
31
|
MASK = '99999',
|
|
32
|
+
SELECTED_EL = '666',
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export enum MouseButton {
|
|
@@ -42,3 +43,5 @@ export enum Mode {
|
|
|
42
43
|
FIXED = 'fixed',
|
|
43
44
|
SORTABLE = 'sortable',
|
|
44
45
|
}
|
|
46
|
+
|
|
47
|
+
export const SELECTED_CLASS = 'tmagic-stage-selected-area';
|
package/src/types.ts
CHANGED
package/src/util.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { Mode } from './const';
|
|
19
|
+
import { Mode, SELECTED_CLASS } from './const';
|
|
20
20
|
import type { Offset } from './types';
|
|
21
21
|
|
|
22
22
|
export const getOffset = (el: HTMLElement): Offset => {
|
|
@@ -154,3 +154,17 @@ export const createDiv = ({ className, cssText }: { className: string; cssText:
|
|
|
154
154
|
el.style.cssText = cssText;
|
|
155
155
|
return el;
|
|
156
156
|
};
|
|
157
|
+
|
|
158
|
+
export const removeSelectedClassName = (doc: Document) => {
|
|
159
|
+
const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
|
|
160
|
+
|
|
161
|
+
if (oldEl) {
|
|
162
|
+
oldEl.classList.remove(SELECTED_CLASS);
|
|
163
|
+
(oldEl.parentNode as HTMLDivElement)?.classList.remove(`${SELECTED_CLASS}-parent`);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const addSelectedClassName = (el: Element) => {
|
|
168
|
+
el.classList.add(SELECTED_CLASS);
|
|
169
|
+
(el.parentNode as Element)?.classList.add(`${SELECTED_CLASS}-parent`);
|
|
170
|
+
};
|