@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
|
@@ -0,0 +1,81 @@
|
|
|
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 { HIGHLIGHT_EL_ID_PREFIX } from './const';
|
|
25
|
+
import StageCore from './StageCore';
|
|
26
|
+
import TargetCalibrate from './TargetCalibrate';
|
|
27
|
+
import type { StageHighlightConfig } from './types';
|
|
28
|
+
export default class StageHighlight extends EventEmitter {
|
|
29
|
+
public core: StageCore;
|
|
30
|
+
public container: HTMLElement;
|
|
31
|
+
public target?: HTMLElement;
|
|
32
|
+
public moveable?: Moveable;
|
|
33
|
+
public calibrationTarget: TargetCalibrate;
|
|
34
|
+
|
|
35
|
+
constructor(config: StageHighlightConfig) {
|
|
36
|
+
super();
|
|
37
|
+
|
|
38
|
+
this.core = config.core;
|
|
39
|
+
this.container = config.container;
|
|
40
|
+
this.calibrationTarget = new TargetCalibrate({
|
|
41
|
+
parent: this.core.mask.content,
|
|
42
|
+
mask: this.core.mask,
|
|
43
|
+
dr: this.core.dr,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 高亮鼠标悬停的组件
|
|
49
|
+
* @param el 选中组件的Dom节点元素
|
|
50
|
+
*/
|
|
51
|
+
public highlight(el: HTMLElement): void {
|
|
52
|
+
if (!el || el === this.target) return;
|
|
53
|
+
this.target = el;
|
|
54
|
+
this.moveable?.destroy();
|
|
55
|
+
|
|
56
|
+
this.moveable = new Moveable(this.container, {
|
|
57
|
+
target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
|
|
58
|
+
origin: false,
|
|
59
|
+
rootContainer: this.core.container,
|
|
60
|
+
zoom: 1,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 清空高亮
|
|
66
|
+
*/
|
|
67
|
+
public clearHighlight(): void {
|
|
68
|
+
if (!this.moveable) return;
|
|
69
|
+
this.target = undefined;
|
|
70
|
+
this.moveable.target = null;
|
|
71
|
+
this.moveable.updateTarget();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 销毁实例
|
|
76
|
+
*/
|
|
77
|
+
public destroy(): void {
|
|
78
|
+
this.moveable?.destroy();
|
|
79
|
+
this.calibrationTarget.destroy();
|
|
80
|
+
}
|
|
81
|
+
}
|
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');
|
|
@@ -78,10 +81,19 @@ export default class StageMask extends Rule {
|
|
|
78
81
|
public height = 0;
|
|
79
82
|
public wrapperHeight = 0;
|
|
80
83
|
public wrapperWidth = 0;
|
|
84
|
+
public maxScrollTop = 0;
|
|
85
|
+
public maxScrollLeft = 0;
|
|
81
86
|
|
|
82
87
|
private mode: Mode = Mode.ABSOLUTE;
|
|
83
88
|
private pageResizeObserver: ResizeObserver | null = null;
|
|
84
89
|
private wrapperResizeObserver: ResizeObserver | null = null;
|
|
90
|
+
/**
|
|
91
|
+
* 高亮事件处理函数
|
|
92
|
+
* @param event 事件对象
|
|
93
|
+
*/
|
|
94
|
+
private highlightHandler = throttle((event: MouseEvent): void => {
|
|
95
|
+
this.emit('highlight', event);
|
|
96
|
+
}, throttleTime);
|
|
85
97
|
|
|
86
98
|
constructor(config: StageMaskConfig) {
|
|
87
99
|
const wrapper = createWrapper();
|
|
@@ -93,6 +105,8 @@ export default class StageMask extends Rule {
|
|
|
93
105
|
this.content.addEventListener('mousedown', this.mouseDownHandler);
|
|
94
106
|
this.wrapper.appendChild(this.content);
|
|
95
107
|
this.content.addEventListener('wheel', this.mouseWheelHandler);
|
|
108
|
+
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
109
|
+
this.content.addEventListener('mouseleave', this.mouseLeaveHandler);
|
|
96
110
|
}
|
|
97
111
|
|
|
98
112
|
public setMode(mode: Mode) {
|
|
@@ -125,6 +139,10 @@ export default class StageMask extends Rule {
|
|
|
125
139
|
const { clientHeight, clientWidth } = entry.target;
|
|
126
140
|
this.setHeight(clientHeight);
|
|
127
141
|
this.setWidth(clientWidth);
|
|
142
|
+
|
|
143
|
+
this.fixScrollValue();
|
|
144
|
+
this.scroll();
|
|
145
|
+
this.core.dr.updateMoveable();
|
|
128
146
|
});
|
|
129
147
|
|
|
130
148
|
this.pageResizeObserver.observe(page);
|
|
@@ -134,6 +152,8 @@ export default class StageMask extends Rule {
|
|
|
134
152
|
const { clientHeight, clientWidth } = entry.target;
|
|
135
153
|
this.wrapperHeight = clientHeight;
|
|
136
154
|
this.wrapperWidth = clientWidth;
|
|
155
|
+
this.setMaxScrollLeft();
|
|
156
|
+
this.setMaxScrollTop();
|
|
137
157
|
});
|
|
138
158
|
this.wrapperResizeObserver.observe(this.wrapper);
|
|
139
159
|
}
|
|
@@ -153,6 +173,16 @@ export default class StageMask extends Rule {
|
|
|
153
173
|
this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE);
|
|
154
174
|
}
|
|
155
175
|
|
|
176
|
+
public scrollIntoView(el: HTMLElement): void {
|
|
177
|
+
if (this.mode === Mode.FIXED) return;
|
|
178
|
+
|
|
179
|
+
el.scrollIntoView();
|
|
180
|
+
if (!this.pageScrollParent) return;
|
|
181
|
+
this.scrollLeft = this.pageScrollParent.scrollLeft;
|
|
182
|
+
this.scrollTop = this.pageScrollParent.scrollTop;
|
|
183
|
+
this.scroll();
|
|
184
|
+
}
|
|
185
|
+
|
|
156
186
|
/**
|
|
157
187
|
* 销毁实例
|
|
158
188
|
*/
|
|
@@ -162,12 +192,21 @@ export default class StageMask extends Rule {
|
|
|
162
192
|
this.pageScrollParent = null;
|
|
163
193
|
this.pageResizeObserver?.disconnect();
|
|
164
194
|
this.wrapperResizeObserver?.disconnect();
|
|
195
|
+
|
|
196
|
+
this.content.removeEventListener('mouseleave', this.mouseLeaveHandler);
|
|
165
197
|
super.destroy();
|
|
166
198
|
}
|
|
167
199
|
|
|
168
200
|
private scroll() {
|
|
169
201
|
let { scrollLeft, scrollTop } = this;
|
|
170
202
|
|
|
203
|
+
if (this.pageScrollParent) {
|
|
204
|
+
this.pageScrollParent.scrollTo({
|
|
205
|
+
top: scrollTop,
|
|
206
|
+
left: scrollLeft,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
171
210
|
if (this.mode === Mode.FIXED) {
|
|
172
211
|
scrollLeft = 0;
|
|
173
212
|
scrollTop = 0;
|
|
@@ -187,6 +226,7 @@ export default class StageMask extends Rule {
|
|
|
187
226
|
*/
|
|
188
227
|
private setHeight(height: number): void {
|
|
189
228
|
this.height = height;
|
|
229
|
+
this.setMaxScrollTop();
|
|
190
230
|
this.content.style.height = `${height}px`;
|
|
191
231
|
}
|
|
192
232
|
|
|
@@ -196,14 +236,42 @@ export default class StageMask extends Rule {
|
|
|
196
236
|
*/
|
|
197
237
|
private setWidth(width: number): void {
|
|
198
238
|
this.width = width;
|
|
239
|
+
this.setMaxScrollLeft();
|
|
199
240
|
this.content.style.width = `${width}px`;
|
|
200
241
|
}
|
|
201
242
|
|
|
243
|
+
/**
|
|
244
|
+
* 计算并设置最大滚动宽度
|
|
245
|
+
*/
|
|
246
|
+
private setMaxScrollLeft(): void {
|
|
247
|
+
this.maxScrollLeft = this.width - this.wrapperWidth;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 计算并设置最大滚动高度
|
|
252
|
+
*/
|
|
253
|
+
private setMaxScrollTop(): void {
|
|
254
|
+
this.maxScrollTop = this.height - this.wrapperHeight;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 修复滚动距离
|
|
259
|
+
* 由于滚动容器变化等因素,会导致当前滚动的距离不正确
|
|
260
|
+
*/
|
|
261
|
+
private fixScrollValue(): void {
|
|
262
|
+
if (this.scrollTop < 0) this.scrollTop = 0;
|
|
263
|
+
if (this.scrollLeft < 0) this.scrollLeft = 0;
|
|
264
|
+
if (this.maxScrollTop < this.scrollTop) this.scrollTop = this.maxScrollTop;
|
|
265
|
+
if (this.maxScrollLeft < this.scrollLeft) this.scrollLeft = this.maxScrollLeft;
|
|
266
|
+
}
|
|
267
|
+
|
|
202
268
|
/**
|
|
203
269
|
* 点击事件处理函数
|
|
204
270
|
* @param event 事件对象
|
|
205
271
|
*/
|
|
206
|
-
private mouseDownHandler =
|
|
272
|
+
private mouseDownHandler = (event: MouseEvent): void => {
|
|
273
|
+
this.emit('clearHighlight');
|
|
274
|
+
|
|
207
275
|
event.stopImmediatePropagation();
|
|
208
276
|
event.stopPropagation();
|
|
209
277
|
|
|
@@ -214,6 +282,8 @@ export default class StageMask extends Rule {
|
|
|
214
282
|
return;
|
|
215
283
|
}
|
|
216
284
|
|
|
285
|
+
this.content.removeEventListener('mousemove', this.highlightHandler);
|
|
286
|
+
|
|
217
287
|
this.emit('beforeSelect', event);
|
|
218
288
|
|
|
219
289
|
// 如果是右键点击,这里的mouseup事件监听没有效果
|
|
@@ -222,46 +292,35 @@ export default class StageMask extends Rule {
|
|
|
222
292
|
|
|
223
293
|
private mouseUpHandler = (): void => {
|
|
224
294
|
globalThis.document.removeEventListener('mouseup', this.mouseUpHandler);
|
|
295
|
+
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
225
296
|
this.emit('select');
|
|
226
297
|
};
|
|
227
298
|
|
|
228
299
|
private mouseWheelHandler = (event: WheelEvent) => {
|
|
300
|
+
this.emit('clearHighlight');
|
|
229
301
|
if (!this.page) throw new Error('page 未初始化');
|
|
230
302
|
|
|
231
303
|
const { deltaY, deltaX } = event;
|
|
232
|
-
const { height, wrapperHeight, width, wrapperWidth } = this;
|
|
233
304
|
|
|
234
|
-
|
|
235
|
-
|
|
305
|
+
if (this.page.clientHeight < this.wrapperHeight && deltaY) return;
|
|
306
|
+
if (this.page.clientWidth < this.wrapperWidth && deltaX) return;
|
|
236
307
|
|
|
237
|
-
if (maxScrollTop > 0) {
|
|
238
|
-
|
|
239
|
-
this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
|
|
240
|
-
} else {
|
|
241
|
-
this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
|
|
242
|
-
}
|
|
308
|
+
if (this.maxScrollTop > 0) {
|
|
309
|
+
this.scrollTop = this.scrollTop + deltaY;
|
|
243
310
|
}
|
|
244
311
|
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
this.scrollLeft = this.scrollLeft + Math.min(maxScrollLeft - this.scrollLeft, deltaX);
|
|
248
|
-
} else {
|
|
249
|
-
this.scrollLeft = Math.max(this.scrollLeft + deltaX, 0);
|
|
250
|
-
}
|
|
312
|
+
if (this.maxScrollLeft > 0) {
|
|
313
|
+
this.scrollLeft = this.scrollLeft + deltaX;
|
|
251
314
|
}
|
|
252
315
|
|
|
253
|
-
|
|
254
|
-
this.scrollTo(this.scrollLeft, this.scrollTop);
|
|
255
|
-
}
|
|
316
|
+
this.fixScrollValue();
|
|
256
317
|
|
|
257
|
-
if (this.pageScrollParent) {
|
|
258
|
-
this.pageScrollParent.scrollTo({
|
|
259
|
-
top: this.scrollTop,
|
|
260
|
-
left: this.scrollLeft,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
318
|
this.scroll();
|
|
264
319
|
|
|
265
320
|
this.emit('scroll', event);
|
|
266
321
|
};
|
|
322
|
+
|
|
323
|
+
private mouseLeaveHandler = () => {
|
|
324
|
+
setTimeout(() => this.emit('clearHighlight'), throttleTime);
|
|
325
|
+
};
|
|
267
326
|
}
|
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
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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 { Mode } from './const';
|
|
23
|
+
import StageDragResize from './StageDragResize';
|
|
24
|
+
import StageMask from './StageMask';
|
|
25
|
+
import type { Offset, TargetCalibrateConfig } from './types';
|
|
26
|
+
import { getMode } from './util';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 将选中的节点修正定位后,添加一个操作节点到蒙层上
|
|
30
|
+
*/
|
|
31
|
+
export default class TargetCalibrate extends EventEmitter {
|
|
32
|
+
public parent: HTMLElement;
|
|
33
|
+
public mask: StageMask;
|
|
34
|
+
public dr: StageDragResize;
|
|
35
|
+
public operationEl: HTMLElement;
|
|
36
|
+
|
|
37
|
+
constructor(config: TargetCalibrateConfig) {
|
|
38
|
+
super();
|
|
39
|
+
|
|
40
|
+
this.parent = config.parent;
|
|
41
|
+
this.mask = config.mask;
|
|
42
|
+
this.dr = config.dr;
|
|
43
|
+
|
|
44
|
+
this.operationEl = globalThis.document.createElement('div');
|
|
45
|
+
this.parent.append(this.operationEl);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public update(el: HTMLElement, prefix: String): HTMLElement {
|
|
49
|
+
const { width, height } = el.getBoundingClientRect();
|
|
50
|
+
const { left, top } = this.getOffset(el);
|
|
51
|
+
this.operationEl.style.cssText = `
|
|
52
|
+
position: absolute;
|
|
53
|
+
left: ${left}px;
|
|
54
|
+
top: ${top}px;
|
|
55
|
+
width: ${width}px;
|
|
56
|
+
height: ${height}px;
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
this.operationEl.id = `${prefix}${el.id}`;
|
|
60
|
+
return this.operationEl;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public destroy(): void {
|
|
64
|
+
this.operationEl?.remove();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private getOffset(el: HTMLElement): Offset {
|
|
68
|
+
const { transform } = getComputedStyle(el);
|
|
69
|
+
const { offsetParent } = el;
|
|
70
|
+
|
|
71
|
+
let left = el.offsetLeft;
|
|
72
|
+
let top = el.offsetTop;
|
|
73
|
+
|
|
74
|
+
if (transform.indexOf('matrix') > -1) {
|
|
75
|
+
let a = 1;
|
|
76
|
+
let b = 1;
|
|
77
|
+
let c = 1;
|
|
78
|
+
let d = 1;
|
|
79
|
+
let e = 0;
|
|
80
|
+
let f = 0;
|
|
81
|
+
transform.replace(
|
|
82
|
+
/matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/,
|
|
83
|
+
($0: string, $1: string, $2: string, $3: string, $4: string, $5: string, $6: string): string => {
|
|
84
|
+
a = +$1;
|
|
85
|
+
b = +$2;
|
|
86
|
+
c = +$3;
|
|
87
|
+
d = +$4;
|
|
88
|
+
e = +$5;
|
|
89
|
+
f = +$6;
|
|
90
|
+
return transform;
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
left = a * left + c * top + e;
|
|
95
|
+
top = b * left + d * top + f;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (offsetParent) {
|
|
99
|
+
const parentOffset = this.getOffset(offsetParent as HTMLElement);
|
|
100
|
+
return {
|
|
101
|
+
left: left + parentOffset.left,
|
|
102
|
+
top: top + parentOffset.top,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 选中固定定位元素后editor-mask高度被置为视窗大小
|
|
107
|
+
if (this.dr.mode === Mode.FIXED) {
|
|
108
|
+
// 弹窗的情况
|
|
109
|
+
if (getMode(el) === Mode.FIXED) {
|
|
110
|
+
return {
|
|
111
|
+
left,
|
|
112
|
+
top,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
left: left - this.mask.scrollLeft,
|
|
118
|
+
top: top - this.mask.scrollTop,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 无父元素的固定定位需按滚动值计算
|
|
123
|
+
if (getMode(el) === Mode.FIXED) {
|
|
124
|
+
return {
|
|
125
|
+
left: left + this.mask.scrollLeft,
|
|
126
|
+
top: top + this.mask.scrollTop,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
left,
|
|
132
|
+
top,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
package/src/const.ts
CHANGED
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
// 流式布局下拖动时需要clone一个镜像节点,镜像节点的id前缀
|
|
20
20
|
export const GHOST_EL_ID_PREFIX = 'ghost_el_';
|
|
21
21
|
|
|
22
|
+
export const DRAG_EL_ID_PREFIX = 'drag_el_';
|
|
23
|
+
|
|
24
|
+
export const HIGHLIGHT_EL_ID_PREFIX = 'highlight_el_';
|
|
25
|
+
|
|
22
26
|
// 默认放到缩小倍数
|
|
23
27
|
export const DEFAULT_ZOOM = 1;
|
|
24
28
|
|
|
@@ -29,6 +33,7 @@ export enum GuidesType {
|
|
|
29
33
|
|
|
30
34
|
export enum ZIndex {
|
|
31
35
|
MASK = '99999',
|
|
36
|
+
SELECTED_EL = '666',
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
export enum MouseButton {
|
|
@@ -42,3 +47,5 @@ export enum Mode {
|
|
|
42
47
|
FIXED = 'fixed',
|
|
43
48
|
SORTABLE = 'sortable',
|
|
44
49
|
}
|
|
50
|
+
|
|
51
|
+
export const SELECTED_CLASS = 'tmagic-stage-selected-area';
|
package/src/types.ts
CHANGED
|
@@ -22,8 +22,10 @@ import { Id, MApp, MNode } from '@tmagic/schema';
|
|
|
22
22
|
|
|
23
23
|
import { GuidesType } from './const';
|
|
24
24
|
import StageCore from './StageCore';
|
|
25
|
+
import StageDragResize from './StageDragResize';
|
|
26
|
+
import StageMask from './StageMask';
|
|
25
27
|
|
|
26
|
-
export type CanSelect = (el: HTMLElement, stop: () => boolean) => boolean | Promise<boolean>;
|
|
28
|
+
export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
|
|
27
29
|
|
|
28
30
|
export type StageCoreConfig = {
|
|
29
31
|
/** 需要对齐的dom节点的CSS选择器字符串 */
|
|
@@ -54,7 +56,6 @@ export type Rect = {
|
|
|
54
56
|
width: number;
|
|
55
57
|
height: number;
|
|
56
58
|
} & Offset;
|
|
57
|
-
|
|
58
59
|
export interface Offset {
|
|
59
60
|
left: number;
|
|
60
61
|
top: number;
|
|
@@ -94,7 +95,7 @@ export interface RemoveData {
|
|
|
94
95
|
|
|
95
96
|
export interface Runtime {
|
|
96
97
|
beforeSelect?: (el: HTMLElement) => Promise<boolean> | boolean;
|
|
97
|
-
getSnapElements?: (el
|
|
98
|
+
getSnapElements?: (el?: HTMLElement) => HTMLElement[];
|
|
98
99
|
updateRootConfig: (config: MApp) => void;
|
|
99
100
|
updatePageId?: (id: Id) => void;
|
|
100
101
|
select?: (id: Id) => Promise<HTMLElement> | HTMLElement;
|
|
@@ -114,3 +115,14 @@ export interface Magic {
|
|
|
114
115
|
export interface RuntimeWindow extends Window {
|
|
115
116
|
magic: Magic;
|
|
116
117
|
}
|
|
118
|
+
|
|
119
|
+
export interface StageHighlightConfig {
|
|
120
|
+
core: StageCore;
|
|
121
|
+
container: HTMLElement;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface TargetCalibrateConfig {
|
|
125
|
+
parent: HTMLElement;
|
|
126
|
+
mask: StageMask;
|
|
127
|
+
dr: StageDragResize;
|
|
128
|
+
}
|
package/src/util.ts
CHANGED
|
@@ -16,9 +16,19 @@
|
|
|
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
|
+
const getParents = (el: Element, relative: Element) => {
|
|
23
|
+
let cur: Element | null = el.parentElement;
|
|
24
|
+
const parents: Element[] = [];
|
|
25
|
+
while (cur && cur !== relative) {
|
|
26
|
+
parents.push(cur);
|
|
27
|
+
cur = cur.parentElement;
|
|
28
|
+
}
|
|
29
|
+
return parents;
|
|
30
|
+
};
|
|
31
|
+
|
|
22
32
|
export const getOffset = (el: HTMLElement): Offset => {
|
|
23
33
|
const { transform } = getComputedStyle(el);
|
|
24
34
|
const { offsetParent } = el;
|
|
@@ -154,3 +164,23 @@ export const createDiv = ({ className, cssText }: { className: string; cssText:
|
|
|
154
164
|
el.style.cssText = cssText;
|
|
155
165
|
return el;
|
|
156
166
|
};
|
|
167
|
+
|
|
168
|
+
export const removeSelectedClassName = (doc: Document) => {
|
|
169
|
+
const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
|
|
170
|
+
|
|
171
|
+
if (oldEl) {
|
|
172
|
+
oldEl.classList.remove(SELECTED_CLASS);
|
|
173
|
+
(oldEl.parentNode as HTMLDivElement)?.classList.remove(`${SELECTED_CLASS}-parent`);
|
|
174
|
+
doc.querySelectorAll(`.${SELECTED_CLASS}-parents`).forEach((item) => {
|
|
175
|
+
item.classList.remove(`${SELECTED_CLASS}-parents`);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const addSelectedClassName = (el: Element, doc: Document) => {
|
|
181
|
+
el.classList.add(SELECTED_CLASS);
|
|
182
|
+
(el.parentNode as Element)?.classList.add(`${SELECTED_CLASS}-parent`);
|
|
183
|
+
getParents(el, doc.body).forEach((item) => {
|
|
184
|
+
item.classList.add(`${SELECTED_CLASS}-parents`);
|
|
185
|
+
});
|
|
186
|
+
};
|
package/tsconfig.json
DELETED
package/vite.config.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
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
|
-
import { defineConfig } from 'vite';
|
|
20
|
-
|
|
21
|
-
import { getBaseConfig } from '../vite-config';
|
|
22
|
-
|
|
23
|
-
import pkg from './package.json';
|
|
24
|
-
|
|
25
|
-
const deps = Object.keys(pkg.dependencies);
|
|
26
|
-
|
|
27
|
-
export default defineConfig(getBaseConfig(deps, 'TMagicStage'));
|