@tmagic/stage 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/tmagic-stage.es.js +516 -297
- package/dist/tmagic-stage.es.js.map +1 -1
- package/dist/tmagic-stage.umd.js +517 -299
- package/dist/tmagic-stage.umd.js.map +1 -1
- package/dist/types/src/StageCore.d.ts +4 -4
- package/dist/types/src/StageDragResize.d.ts +21 -7
- package/dist/types/src/StageHighlight.d.ts +2 -0
- package/dist/types/src/StageMask.d.ts +18 -0
- package/dist/types/src/TargetCalibrate.d.ts +20 -0
- package/dist/types/src/const.d.ts +26 -1
- package/dist/types/src/types.d.ts +22 -6
- package/dist/types/src/util.d.ts +3 -2
- package/package.json +12 -8
- package/src/Rule.ts +13 -5
- package/src/StageCore.ts +43 -23
- package/src/StageDragResize.ts +214 -98
- package/src/StageHighlight.ts +16 -1
- package/src/StageMask.ts +93 -27
- package/src/StageRender.ts +4 -16
- package/src/TargetCalibrate.ts +119 -0
- package/src/const.ts +28 -2
- package/src/types.ts +23 -6
- package/src/util.ts +40 -29
- package/LICENSE +0 -5432
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -27
package/src/StageMask.ts
CHANGED
|
@@ -81,6 +81,9 @@ export default class StageMask extends Rule {
|
|
|
81
81
|
public height = 0;
|
|
82
82
|
public wrapperHeight = 0;
|
|
83
83
|
public wrapperWidth = 0;
|
|
84
|
+
public maxScrollTop = 0;
|
|
85
|
+
public maxScrollLeft = 0;
|
|
86
|
+
public intersectionObserver: IntersectionObserver | null = null;
|
|
84
87
|
|
|
85
88
|
private mode: Mode = Mode.ABSOLUTE;
|
|
86
89
|
private pageResizeObserver: ResizeObserver | null = null;
|
|
@@ -104,6 +107,7 @@ export default class StageMask extends Rule {
|
|
|
104
107
|
this.wrapper.appendChild(this.content);
|
|
105
108
|
this.content.addEventListener('wheel', this.mouseWheelHandler);
|
|
106
109
|
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
110
|
+
this.content.addEventListener('mouseleave', this.mouseLeaveHandler);
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
public setMode(mode: Mode) {
|
|
@@ -129,6 +133,27 @@ export default class StageMask extends Rule {
|
|
|
129
133
|
this.page = page;
|
|
130
134
|
this.pageScrollParent = getScrollParent(page) || this.core.renderer.contentWindow?.document.documentElement || null;
|
|
131
135
|
this.pageResizeObserver?.disconnect();
|
|
136
|
+
this.wrapperResizeObserver?.disconnect();
|
|
137
|
+
this.intersectionObserver?.disconnect();
|
|
138
|
+
|
|
139
|
+
if (typeof IntersectionObserver !== 'undefined') {
|
|
140
|
+
this.intersectionObserver = new IntersectionObserver(
|
|
141
|
+
(entries) => {
|
|
142
|
+
entries.forEach((entry) => {
|
|
143
|
+
const { target, intersectionRatio } = entry;
|
|
144
|
+
if (intersectionRatio <= 0) {
|
|
145
|
+
this.scrollIntoView(target);
|
|
146
|
+
}
|
|
147
|
+
this.intersectionObserver?.unobserve(target);
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
root: this.pageScrollParent,
|
|
152
|
+
rootMargin: '0px',
|
|
153
|
+
threshold: 1.0,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
}
|
|
132
157
|
|
|
133
158
|
if (typeof ResizeObserver !== 'undefined') {
|
|
134
159
|
this.pageResizeObserver = new ResizeObserver((entries) => {
|
|
@@ -136,6 +161,11 @@ export default class StageMask extends Rule {
|
|
|
136
161
|
const { clientHeight, clientWidth } = entry.target;
|
|
137
162
|
this.setHeight(clientHeight);
|
|
138
163
|
this.setWidth(clientWidth);
|
|
164
|
+
|
|
165
|
+
this.scroll();
|
|
166
|
+
if (this.core.dr.moveable) {
|
|
167
|
+
this.core.dr.updateMoveable();
|
|
168
|
+
}
|
|
139
169
|
});
|
|
140
170
|
|
|
141
171
|
this.pageResizeObserver.observe(page);
|
|
@@ -145,6 +175,8 @@ export default class StageMask extends Rule {
|
|
|
145
175
|
const { clientHeight, clientWidth } = entry.target;
|
|
146
176
|
this.wrapperHeight = clientHeight;
|
|
147
177
|
this.wrapperWidth = clientWidth;
|
|
178
|
+
this.setMaxScrollLeft();
|
|
179
|
+
this.setMaxScrollTop();
|
|
148
180
|
});
|
|
149
181
|
this.wrapperResizeObserver.observe(this.wrapper);
|
|
150
182
|
}
|
|
@@ -164,6 +196,14 @@ export default class StageMask extends Rule {
|
|
|
164
196
|
this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE);
|
|
165
197
|
}
|
|
166
198
|
|
|
199
|
+
public scrollIntoView(el: Element): void {
|
|
200
|
+
el.scrollIntoView();
|
|
201
|
+
if (!this.pageScrollParent) return;
|
|
202
|
+
this.scrollLeft = this.pageScrollParent.scrollLeft;
|
|
203
|
+
this.scrollTop = this.pageScrollParent.scrollTop;
|
|
204
|
+
this.scroll();
|
|
205
|
+
}
|
|
206
|
+
|
|
167
207
|
/**
|
|
168
208
|
* 销毁实例
|
|
169
209
|
*/
|
|
@@ -173,12 +213,23 @@ export default class StageMask extends Rule {
|
|
|
173
213
|
this.pageScrollParent = null;
|
|
174
214
|
this.pageResizeObserver?.disconnect();
|
|
175
215
|
this.wrapperResizeObserver?.disconnect();
|
|
216
|
+
|
|
217
|
+
this.content.removeEventListener('mouseleave', this.mouseLeaveHandler);
|
|
176
218
|
super.destroy();
|
|
177
219
|
}
|
|
178
220
|
|
|
179
221
|
private scroll() {
|
|
222
|
+
this.fixScrollValue();
|
|
223
|
+
|
|
180
224
|
let { scrollLeft, scrollTop } = this;
|
|
181
225
|
|
|
226
|
+
if (this.pageScrollParent) {
|
|
227
|
+
this.pageScrollParent.scrollTo({
|
|
228
|
+
top: scrollTop,
|
|
229
|
+
left: scrollLeft,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
182
233
|
if (this.mode === Mode.FIXED) {
|
|
183
234
|
scrollLeft = 0;
|
|
184
235
|
scrollTop = 0;
|
|
@@ -198,6 +249,7 @@ export default class StageMask extends Rule {
|
|
|
198
249
|
*/
|
|
199
250
|
private setHeight(height: number): void {
|
|
200
251
|
this.height = height;
|
|
252
|
+
this.setMaxScrollTop();
|
|
201
253
|
this.content.style.height = `${height}px`;
|
|
202
254
|
}
|
|
203
255
|
|
|
@@ -207,14 +259,42 @@ export default class StageMask extends Rule {
|
|
|
207
259
|
*/
|
|
208
260
|
private setWidth(width: number): void {
|
|
209
261
|
this.width = width;
|
|
262
|
+
this.setMaxScrollLeft();
|
|
210
263
|
this.content.style.width = `${width}px`;
|
|
211
264
|
}
|
|
212
265
|
|
|
266
|
+
/**
|
|
267
|
+
* 计算并设置最大滚动宽度
|
|
268
|
+
*/
|
|
269
|
+
private setMaxScrollLeft(): void {
|
|
270
|
+
this.maxScrollLeft = Math.max(this.width - this.wrapperWidth, 0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 计算并设置最大滚动高度
|
|
275
|
+
*/
|
|
276
|
+
private setMaxScrollTop(): void {
|
|
277
|
+
this.maxScrollTop = Math.max(this.height - this.wrapperHeight, 0);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 修复滚动距离
|
|
282
|
+
* 由于滚动容器变化等因素,会导致当前滚动的距离不正确
|
|
283
|
+
*/
|
|
284
|
+
private fixScrollValue(): void {
|
|
285
|
+
if (this.scrollTop < 0) this.scrollTop = 0;
|
|
286
|
+
if (this.scrollLeft < 0) this.scrollLeft = 0;
|
|
287
|
+
if (this.maxScrollTop < this.scrollTop) this.scrollTop = this.maxScrollTop;
|
|
288
|
+
if (this.maxScrollLeft < this.scrollLeft) this.scrollLeft = this.maxScrollLeft;
|
|
289
|
+
}
|
|
290
|
+
|
|
213
291
|
/**
|
|
214
292
|
* 点击事件处理函数
|
|
215
293
|
* @param event 事件对象
|
|
216
294
|
*/
|
|
217
295
|
private mouseDownHandler = (event: MouseEvent): void => {
|
|
296
|
+
this.emit('clearHighlight');
|
|
297
|
+
|
|
218
298
|
event.stopImmediatePropagation();
|
|
219
299
|
event.stopPropagation();
|
|
220
300
|
|
|
@@ -225,12 +305,12 @@ export default class StageMask extends Rule {
|
|
|
225
305
|
return;
|
|
226
306
|
}
|
|
227
307
|
|
|
308
|
+
this.content.removeEventListener('mousemove', this.highlightHandler);
|
|
309
|
+
|
|
228
310
|
this.emit('beforeSelect', event);
|
|
229
311
|
|
|
230
312
|
// 如果是右键点击,这里的mouseup事件监听没有效果
|
|
231
313
|
globalThis.document.addEventListener('mouseup', this.mouseUpHandler);
|
|
232
|
-
this.content.removeEventListener('mousemove', this.highlightHandler);
|
|
233
|
-
this.emit('clearHighlight');
|
|
234
314
|
};
|
|
235
315
|
|
|
236
316
|
private mouseUpHandler = (): void => {
|
|
@@ -240,42 +320,28 @@ export default class StageMask extends Rule {
|
|
|
240
320
|
};
|
|
241
321
|
|
|
242
322
|
private mouseWheelHandler = (event: WheelEvent) => {
|
|
323
|
+
this.emit('clearHighlight');
|
|
243
324
|
if (!this.page) throw new Error('page 未初始化');
|
|
244
325
|
|
|
245
326
|
const { deltaY, deltaX } = event;
|
|
246
|
-
const { height, wrapperHeight, width, wrapperWidth } = this;
|
|
247
327
|
|
|
248
|
-
|
|
249
|
-
|
|
328
|
+
if (this.page.clientHeight < this.wrapperHeight && deltaY) return;
|
|
329
|
+
if (this.page.clientWidth < this.wrapperWidth && deltaX) return;
|
|
250
330
|
|
|
251
|
-
if (maxScrollTop > 0) {
|
|
252
|
-
|
|
253
|
-
this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
|
|
254
|
-
} else {
|
|
255
|
-
this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
|
|
256
|
-
}
|
|
331
|
+
if (this.maxScrollTop > 0) {
|
|
332
|
+
this.scrollTop = this.scrollTop + deltaY;
|
|
257
333
|
}
|
|
258
334
|
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
this.scrollLeft = this.scrollLeft + Math.min(maxScrollLeft - this.scrollLeft, deltaX);
|
|
262
|
-
} else {
|
|
263
|
-
this.scrollLeft = Math.max(this.scrollLeft + deltaX, 0);
|
|
264
|
-
}
|
|
335
|
+
if (this.maxScrollLeft > 0) {
|
|
336
|
+
this.scrollLeft = this.scrollLeft + deltaX;
|
|
265
337
|
}
|
|
266
338
|
|
|
267
|
-
if (this.mode !== Mode.FIXED) {
|
|
268
|
-
this.scrollTo(this.scrollLeft, this.scrollTop);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (this.pageScrollParent) {
|
|
272
|
-
this.pageScrollParent.scrollTo({
|
|
273
|
-
top: this.scrollTop,
|
|
274
|
-
left: this.scrollLeft,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
339
|
this.scroll();
|
|
278
340
|
|
|
279
341
|
this.emit('scroll', event);
|
|
280
342
|
};
|
|
343
|
+
|
|
344
|
+
private mouseLeaveHandler = () => {
|
|
345
|
+
setTimeout(() => this.emit('clearHighlight'), throttleTime);
|
|
346
|
+
};
|
|
281
347
|
}
|
package/src/StageRender.ts
CHANGED
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
|
|
19
19
|
import { EventEmitter } from 'events';
|
|
20
20
|
|
|
21
|
-
import { SELECTED_CLASS, ZIndex } from './const';
|
|
22
21
|
import StageCore from './StageCore';
|
|
23
22
|
import type { Runtime, RuntimeWindow, StageRenderConfig } from './types';
|
|
24
23
|
import { getHost, isSameDomain } from './util';
|
|
@@ -82,10 +81,6 @@ export default class StageRender extends EventEmitter {
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
el.appendChild<HTMLIFrameElement>(this.iframe);
|
|
85
|
-
|
|
86
|
-
this.contentWindow = this.iframe?.contentWindow as RuntimeWindow;
|
|
87
|
-
|
|
88
|
-
this.contentWindow.magic = this.getMagicApi();
|
|
89
84
|
} else {
|
|
90
85
|
throw Error('mount 失败');
|
|
91
86
|
}
|
|
@@ -114,7 +109,9 @@ export default class StageRender extends EventEmitter {
|
|
|
114
109
|
}
|
|
115
110
|
|
|
116
111
|
private loadHandler = async () => {
|
|
117
|
-
this.
|
|
112
|
+
this.contentWindow = this.iframe?.contentWindow as RuntimeWindow;
|
|
113
|
+
|
|
114
|
+
this.contentWindow.magic = this.getMagicApi();
|
|
118
115
|
|
|
119
116
|
if (this.render) {
|
|
120
117
|
const el = await this.render(this.core);
|
|
@@ -123,15 +120,6 @@ export default class StageRender extends EventEmitter {
|
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
122
|
|
|
126
|
-
|
|
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
|
+
this.emit('onload');
|
|
136
124
|
};
|
|
137
125
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
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 StageCore from './StageCore';
|
|
24
|
+
import StageDragResize from './StageDragResize';
|
|
25
|
+
import StageMask from './StageMask';
|
|
26
|
+
import type { Offset, TargetCalibrateConfig } from './types';
|
|
27
|
+
import { getMode } from './util';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 将选中的节点修正定位后,添加一个操作节点到蒙层上
|
|
31
|
+
*/
|
|
32
|
+
export default class TargetCalibrate extends EventEmitter {
|
|
33
|
+
public parent: HTMLElement;
|
|
34
|
+
public mask: StageMask;
|
|
35
|
+
public dr: StageDragResize;
|
|
36
|
+
public core: StageCore;
|
|
37
|
+
public operationEl: HTMLDivElement;
|
|
38
|
+
|
|
39
|
+
constructor(config: TargetCalibrateConfig) {
|
|
40
|
+
super();
|
|
41
|
+
|
|
42
|
+
this.parent = config.parent;
|
|
43
|
+
this.mask = config.mask;
|
|
44
|
+
this.dr = config.dr;
|
|
45
|
+
this.core = config.core;
|
|
46
|
+
|
|
47
|
+
this.operationEl = globalThis.document.createElement('div');
|
|
48
|
+
this.parent.append(this.operationEl);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public update(el: HTMLElement, prefix: String): HTMLElement {
|
|
52
|
+
const { left, top } = this.getOffset(el);
|
|
53
|
+
const { transform } = getComputedStyle(el);
|
|
54
|
+
this.operationEl.style.cssText = `
|
|
55
|
+
position: absolute;
|
|
56
|
+
transform: ${transform};
|
|
57
|
+
left: ${left}px;
|
|
58
|
+
top: ${top}px;
|
|
59
|
+
width: ${el.clientWidth}px;
|
|
60
|
+
height: ${el.clientHeight}px;
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
this.operationEl.id = `${prefix}${el.id}`;
|
|
64
|
+
|
|
65
|
+
if (typeof this.core.config.updateDragEl === 'function') {
|
|
66
|
+
this.core.config.updateDragEl(this.operationEl, el);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return this.operationEl;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public destroy(): void {
|
|
73
|
+
this.operationEl?.remove();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private getOffset(el: HTMLElement): Offset {
|
|
77
|
+
const { offsetParent } = el;
|
|
78
|
+
|
|
79
|
+
const left = el.offsetLeft;
|
|
80
|
+
const top = el.offsetTop;
|
|
81
|
+
|
|
82
|
+
if (offsetParent) {
|
|
83
|
+
const parentOffset = this.getOffset(offsetParent as HTMLElement);
|
|
84
|
+
return {
|
|
85
|
+
left: left + parentOffset.left,
|
|
86
|
+
top: top + parentOffset.top,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 选中固定定位元素后editor-mask高度被置为视窗大小
|
|
91
|
+
if (this.dr.mode === Mode.FIXED) {
|
|
92
|
+
// 弹窗的情况
|
|
93
|
+
if (getMode(el) === Mode.FIXED) {
|
|
94
|
+
return {
|
|
95
|
+
left,
|
|
96
|
+
top,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
left: left - this.mask.scrollLeft,
|
|
102
|
+
top: top - this.mask.scrollTop,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 无父元素的固定定位需按滚动值计算
|
|
107
|
+
if (getMode(el) === Mode.FIXED) {
|
|
108
|
+
return {
|
|
109
|
+
left: left + this.mask.scrollLeft,
|
|
110
|
+
top: top + this.mask.scrollTop,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
left,
|
|
116
|
+
top,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
package/src/const.ts
CHANGED
|
@@ -16,32 +16,58 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/** 流式布局下拖动时需要clone一个镜像节点,镜像节点的id前缀 */
|
|
20
20
|
export const GHOST_EL_ID_PREFIX = 'ghost_el_';
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
/** 拖动的时候需要在蒙层中创建一个占位节点,该节点的id前缀 */
|
|
23
|
+
export const DRAG_EL_ID_PREFIX = 'drag_el_';
|
|
24
|
+
|
|
25
|
+
/** 高亮事需要在蒙层中创建一个占位节点,该节点的id前缀 */
|
|
26
|
+
export const HIGHLIGHT_EL_ID_PREFIX = 'highlight_el_';
|
|
27
|
+
|
|
28
|
+
/** 默认放到缩小倍数 */
|
|
23
29
|
export const DEFAULT_ZOOM = 1;
|
|
24
30
|
|
|
31
|
+
/** 参考线类型 */
|
|
25
32
|
export enum GuidesType {
|
|
33
|
+
/** 水平 */
|
|
26
34
|
HORIZONTAL = 'horizontal',
|
|
35
|
+
/** 垂直 */
|
|
27
36
|
VERTICAL = 'vertical',
|
|
28
37
|
}
|
|
29
38
|
|
|
39
|
+
/** css z-index */
|
|
30
40
|
export enum ZIndex {
|
|
41
|
+
/** 蒙层,用于监听用户操作,需要置于顶层 */
|
|
31
42
|
MASK = '99999',
|
|
43
|
+
/** 选中的节点 */
|
|
32
44
|
SELECTED_EL = '666',
|
|
45
|
+
GHOST_EL = '700',
|
|
46
|
+
DRAG_EL = '9',
|
|
33
47
|
}
|
|
34
48
|
|
|
49
|
+
/** 鼠标按键 */
|
|
35
50
|
export enum MouseButton {
|
|
51
|
+
/** 左键 */
|
|
36
52
|
LEFT = 0,
|
|
53
|
+
/** z中健 */
|
|
37
54
|
MIDDLE = 1,
|
|
55
|
+
/** 右键 */
|
|
38
56
|
RIGHT = 2,
|
|
39
57
|
}
|
|
40
58
|
|
|
59
|
+
/** 布局方式 */
|
|
41
60
|
export enum Mode {
|
|
61
|
+
/** 绝对定位布局 */
|
|
42
62
|
ABSOLUTE = 'absolute',
|
|
63
|
+
/** 固定定位布局 */
|
|
43
64
|
FIXED = 'fixed',
|
|
65
|
+
/** 流式布局 */
|
|
44
66
|
SORTABLE = 'sortable',
|
|
45
67
|
}
|
|
46
68
|
|
|
69
|
+
/** 选中节点的class name */
|
|
47
70
|
export const SELECTED_CLASS = 'tmagic-stage-selected-area';
|
|
71
|
+
|
|
72
|
+
export const H_GUIDE_LINE_STORAGE_KEY = '$MagicStageHorizontalGuidelinesData';
|
|
73
|
+
export const V_GUIDE_LINE_STORAGE_KEY = '$MagicStageVerticalGuidelinesData';
|
package/src/types.ts
CHANGED
|
@@ -16,14 +16,17 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { MoveableOptions } from '
|
|
19
|
+
import { MoveableOptions } from 'moveable';
|
|
20
20
|
|
|
21
|
+
import Core from '@tmagic/core';
|
|
21
22
|
import { Id, MApp, MNode } from '@tmagic/schema';
|
|
22
23
|
|
|
23
24
|
import { GuidesType } from './const';
|
|
24
25
|
import StageCore from './StageCore';
|
|
26
|
+
import StageDragResize from './StageDragResize';
|
|
27
|
+
import StageMask from './StageMask';
|
|
25
28
|
|
|
26
|
-
export type CanSelect = (el: HTMLElement, stop: () => boolean) => boolean | Promise<boolean>;
|
|
29
|
+
export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
|
|
27
30
|
|
|
28
31
|
export type StageCoreConfig = {
|
|
29
32
|
/** 需要对齐的dom节点的CSS选择器字符串 */
|
|
@@ -35,6 +38,8 @@ export type StageCoreConfig = {
|
|
|
35
38
|
/** runtime 的HTML地址,可以是一个HTTP地址,如果和编辑器不同域,需要设置跨域,也可以是一个相对或绝对路径 */
|
|
36
39
|
runtimeUrl?: string;
|
|
37
40
|
render?: (renderer: StageCore) => Promise<HTMLElement> | HTMLElement;
|
|
41
|
+
autoScrollIntoView?: boolean;
|
|
42
|
+
updateDragEl?: (el: HTMLDivElement, target: HTMLElement) => void;
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
export interface StageRenderConfig {
|
|
@@ -69,10 +74,14 @@ export interface UpdateEventData {
|
|
|
69
74
|
el: HTMLElement;
|
|
70
75
|
ghostEl: HTMLElement;
|
|
71
76
|
style: {
|
|
72
|
-
width
|
|
73
|
-
height
|
|
77
|
+
width?: number;
|
|
78
|
+
height?: number;
|
|
74
79
|
left?: number;
|
|
75
80
|
top?: number;
|
|
81
|
+
transform?: {
|
|
82
|
+
rotate?: string;
|
|
83
|
+
scale?: string;
|
|
84
|
+
};
|
|
76
85
|
};
|
|
77
86
|
}
|
|
78
87
|
|
|
@@ -93,9 +102,10 @@ export interface RemoveData {
|
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
export interface Runtime {
|
|
105
|
+
getApp?: () => Core;
|
|
96
106
|
beforeSelect?: (el: HTMLElement) => Promise<boolean> | boolean;
|
|
97
|
-
getSnapElements?: (el
|
|
98
|
-
updateRootConfig
|
|
107
|
+
getSnapElements?: (el?: HTMLElement) => HTMLElement[];
|
|
108
|
+
updateRootConfig?: (config: MApp) => void;
|
|
99
109
|
updatePageId?: (id: Id) => void;
|
|
100
110
|
select?: (id: Id) => Promise<HTMLElement> | HTMLElement;
|
|
101
111
|
add?: (data: UpdateData) => void;
|
|
@@ -119,3 +129,10 @@ export interface StageHighlightConfig {
|
|
|
119
129
|
core: StageCore;
|
|
120
130
|
container: HTMLElement;
|
|
121
131
|
}
|
|
132
|
+
|
|
133
|
+
export interface TargetCalibrateConfig {
|
|
134
|
+
parent: HTMLElement;
|
|
135
|
+
mask: StageMask;
|
|
136
|
+
dr: StageDragResize;
|
|
137
|
+
core: StageCore;
|
|
138
|
+
}
|
package/src/util.ts
CHANGED
|
@@ -16,39 +16,24 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { Mode, SELECTED_CLASS } from './const';
|
|
19
|
+
import { GuidesType, H_GUIDE_LINE_STORAGE_KEY, Mode, SELECTED_CLASS, V_GUIDE_LINE_STORAGE_KEY } 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
|
-
const { transform } = getComputedStyle(el);
|
|
24
33
|
const { offsetParent } = el;
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (transform.indexOf('matrix') > -1) {
|
|
30
|
-
let a = 1;
|
|
31
|
-
let b = 1;
|
|
32
|
-
let c = 1;
|
|
33
|
-
let d = 1;
|
|
34
|
-
let e = 0;
|
|
35
|
-
let f = 0;
|
|
36
|
-
transform.replace(
|
|
37
|
-
/matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/,
|
|
38
|
-
($0: string, $1: string, $2: string, $3: string, $4: string, $5: string, $6: string): string => {
|
|
39
|
-
a = +$1;
|
|
40
|
-
b = +$2;
|
|
41
|
-
c = +$3;
|
|
42
|
-
d = +$4;
|
|
43
|
-
e = +$5;
|
|
44
|
-
f = +$6;
|
|
45
|
-
return transform;
|
|
46
|
-
},
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
left = a * left + c * top + e;
|
|
50
|
-
top = b * left + d * top + f;
|
|
51
|
-
}
|
|
35
|
+
const left = el.offsetLeft;
|
|
36
|
+
const top = el.offsetTop;
|
|
52
37
|
|
|
53
38
|
if (offsetParent) {
|
|
54
39
|
const parentOffset = getOffset(offsetParent as HTMLElement);
|
|
@@ -161,10 +146,36 @@ export const removeSelectedClassName = (doc: Document) => {
|
|
|
161
146
|
if (oldEl) {
|
|
162
147
|
oldEl.classList.remove(SELECTED_CLASS);
|
|
163
148
|
(oldEl.parentNode as HTMLDivElement)?.classList.remove(`${SELECTED_CLASS}-parent`);
|
|
149
|
+
doc.querySelectorAll(`.${SELECTED_CLASS}-parents`).forEach((item) => {
|
|
150
|
+
item.classList.remove(`${SELECTED_CLASS}-parents`);
|
|
151
|
+
});
|
|
164
152
|
}
|
|
165
153
|
};
|
|
166
154
|
|
|
167
|
-
export const addSelectedClassName = (el: Element) => {
|
|
155
|
+
export const addSelectedClassName = (el: Element, doc: Document) => {
|
|
168
156
|
el.classList.add(SELECTED_CLASS);
|
|
169
157
|
(el.parentNode as Element)?.classList.add(`${SELECTED_CLASS}-parent`);
|
|
158
|
+
getParents(el, doc.body).forEach((item) => {
|
|
159
|
+
item.classList.add(`${SELECTED_CLASS}-parents`);
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const getGuideLineFromCache = (type: GuidesType): number[] => {
|
|
164
|
+
const key = {
|
|
165
|
+
[GuidesType.HORIZONTAL]: H_GUIDE_LINE_STORAGE_KEY,
|
|
166
|
+
[GuidesType.VERTICAL]: V_GUIDE_LINE_STORAGE_KEY,
|
|
167
|
+
}[type];
|
|
168
|
+
|
|
169
|
+
if (!key) return [];
|
|
170
|
+
|
|
171
|
+
const guideLineCacheData = globalThis.localStorage.getItem(key);
|
|
172
|
+
if (guideLineCacheData) {
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(guideLineCacheData) || [];
|
|
175
|
+
} catch (e) {
|
|
176
|
+
console.error(e);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return [];
|
|
170
181
|
};
|