@tmagic/stage 1.0.0-beta.8 → 1.0.0-rc.10
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 +609 -273
- package/dist/tmagic-stage.es.js.map +1 -1
- package/dist/tmagic-stage.umd.js +613 -279
- 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 +22 -7
- package/dist/types/src/StageHighlight.d.ts +27 -0
- package/dist/types/src/StageMask.d.ts +23 -0
- package/dist/types/src/TargetCalibrate.d.ts +20 -0
- package/dist/types/src/const.d.ts +28 -1
- package/dist/types/src/types.d.ts +25 -5
- package/dist/types/src/util.d.ts +4 -1
- package/package.json +13 -8
- package/src/Rule.ts +13 -5
- package/src/StageCore.ts +90 -24
- package/src/StageDragResize.ts +239 -99
- package/src/StageHighlight.ts +82 -0
- package/src/StageMask.ts +106 -26
- package/src/StageRender.ts +1 -0
- package/src/TargetCalibrate.ts +119 -0
- package/src/const.ts +31 -2
- package/src/types.ts +27 -5
- package/src/util.ts +53 -28
- package/LICENSE +0 -5432
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -27
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,20 @@ 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;
|
|
86
|
+
public intersectionObserver: IntersectionObserver | null = null;
|
|
81
87
|
|
|
82
88
|
private mode: Mode = Mode.ABSOLUTE;
|
|
83
89
|
private pageResizeObserver: ResizeObserver | null = null;
|
|
84
90
|
private wrapperResizeObserver: ResizeObserver | null = null;
|
|
91
|
+
/**
|
|
92
|
+
* 高亮事件处理函数
|
|
93
|
+
* @param event 事件对象
|
|
94
|
+
*/
|
|
95
|
+
private highlightHandler = throttle((event: MouseEvent): void => {
|
|
96
|
+
this.emit('highlight', event);
|
|
97
|
+
}, throttleTime);
|
|
85
98
|
|
|
86
99
|
constructor(config: StageMaskConfig) {
|
|
87
100
|
const wrapper = createWrapper();
|
|
@@ -93,6 +106,8 @@ export default class StageMask extends Rule {
|
|
|
93
106
|
this.content.addEventListener('mousedown', this.mouseDownHandler);
|
|
94
107
|
this.wrapper.appendChild(this.content);
|
|
95
108
|
this.content.addEventListener('wheel', this.mouseWheelHandler);
|
|
109
|
+
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
110
|
+
this.content.addEventListener('mouseleave', this.mouseLeaveHandler);
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
public setMode(mode: Mode) {
|
|
@@ -118,6 +133,27 @@ export default class StageMask extends Rule {
|
|
|
118
133
|
this.page = page;
|
|
119
134
|
this.pageScrollParent = getScrollParent(page) || this.core.renderer.contentWindow?.document.documentElement || null;
|
|
120
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
|
+
}
|
|
121
157
|
|
|
122
158
|
if (typeof ResizeObserver !== 'undefined') {
|
|
123
159
|
this.pageResizeObserver = new ResizeObserver((entries) => {
|
|
@@ -125,6 +161,11 @@ export default class StageMask extends Rule {
|
|
|
125
161
|
const { clientHeight, clientWidth } = entry.target;
|
|
126
162
|
this.setHeight(clientHeight);
|
|
127
163
|
this.setWidth(clientWidth);
|
|
164
|
+
|
|
165
|
+
this.scroll();
|
|
166
|
+
if (this.core.dr.moveable) {
|
|
167
|
+
this.core.dr.updateMoveable();
|
|
168
|
+
}
|
|
128
169
|
});
|
|
129
170
|
|
|
130
171
|
this.pageResizeObserver.observe(page);
|
|
@@ -134,6 +175,8 @@ export default class StageMask extends Rule {
|
|
|
134
175
|
const { clientHeight, clientWidth } = entry.target;
|
|
135
176
|
this.wrapperHeight = clientHeight;
|
|
136
177
|
this.wrapperWidth = clientWidth;
|
|
178
|
+
this.setMaxScrollLeft();
|
|
179
|
+
this.setMaxScrollTop();
|
|
137
180
|
});
|
|
138
181
|
this.wrapperResizeObserver.observe(this.wrapper);
|
|
139
182
|
}
|
|
@@ -153,6 +196,14 @@ export default class StageMask extends Rule {
|
|
|
153
196
|
this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE);
|
|
154
197
|
}
|
|
155
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
|
+
|
|
156
207
|
/**
|
|
157
208
|
* 销毁实例
|
|
158
209
|
*/
|
|
@@ -162,12 +213,23 @@ export default class StageMask extends Rule {
|
|
|
162
213
|
this.pageScrollParent = null;
|
|
163
214
|
this.pageResizeObserver?.disconnect();
|
|
164
215
|
this.wrapperResizeObserver?.disconnect();
|
|
216
|
+
|
|
217
|
+
this.content.removeEventListener('mouseleave', this.mouseLeaveHandler);
|
|
165
218
|
super.destroy();
|
|
166
219
|
}
|
|
167
220
|
|
|
168
221
|
private scroll() {
|
|
222
|
+
this.fixScrollValue();
|
|
223
|
+
|
|
169
224
|
let { scrollLeft, scrollTop } = this;
|
|
170
225
|
|
|
226
|
+
if (this.pageScrollParent) {
|
|
227
|
+
this.pageScrollParent.scrollTo({
|
|
228
|
+
top: scrollTop,
|
|
229
|
+
left: scrollLeft,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
171
233
|
if (this.mode === Mode.FIXED) {
|
|
172
234
|
scrollLeft = 0;
|
|
173
235
|
scrollTop = 0;
|
|
@@ -187,6 +249,7 @@ export default class StageMask extends Rule {
|
|
|
187
249
|
*/
|
|
188
250
|
private setHeight(height: number): void {
|
|
189
251
|
this.height = height;
|
|
252
|
+
this.setMaxScrollTop();
|
|
190
253
|
this.content.style.height = `${height}px`;
|
|
191
254
|
}
|
|
192
255
|
|
|
@@ -196,14 +259,42 @@ export default class StageMask extends Rule {
|
|
|
196
259
|
*/
|
|
197
260
|
private setWidth(width: number): void {
|
|
198
261
|
this.width = width;
|
|
262
|
+
this.setMaxScrollLeft();
|
|
199
263
|
this.content.style.width = `${width}px`;
|
|
200
264
|
}
|
|
201
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
|
+
|
|
202
291
|
/**
|
|
203
292
|
* 点击事件处理函数
|
|
204
293
|
* @param event 事件对象
|
|
205
294
|
*/
|
|
206
|
-
private mouseDownHandler =
|
|
295
|
+
private mouseDownHandler = (event: MouseEvent): void => {
|
|
296
|
+
this.emit('clearHighlight');
|
|
297
|
+
|
|
207
298
|
event.stopImmediatePropagation();
|
|
208
299
|
event.stopPropagation();
|
|
209
300
|
|
|
@@ -214,6 +305,8 @@ export default class StageMask extends Rule {
|
|
|
214
305
|
return;
|
|
215
306
|
}
|
|
216
307
|
|
|
308
|
+
this.content.removeEventListener('mousemove', this.highlightHandler);
|
|
309
|
+
|
|
217
310
|
this.emit('beforeSelect', event);
|
|
218
311
|
|
|
219
312
|
// 如果是右键点击,这里的mouseup事件监听没有效果
|
|
@@ -222,46 +315,33 @@ export default class StageMask extends Rule {
|
|
|
222
315
|
|
|
223
316
|
private mouseUpHandler = (): void => {
|
|
224
317
|
globalThis.document.removeEventListener('mouseup', this.mouseUpHandler);
|
|
318
|
+
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
225
319
|
this.emit('select');
|
|
226
320
|
};
|
|
227
321
|
|
|
228
322
|
private mouseWheelHandler = (event: WheelEvent) => {
|
|
323
|
+
this.emit('clearHighlight');
|
|
229
324
|
if (!this.page) throw new Error('page 未初始化');
|
|
230
325
|
|
|
231
326
|
const { deltaY, deltaX } = event;
|
|
232
|
-
const { height, wrapperHeight, width, wrapperWidth } = this;
|
|
233
|
-
|
|
234
|
-
const maxScrollTop = height - wrapperHeight;
|
|
235
|
-
const maxScrollLeft = width - wrapperWidth;
|
|
236
327
|
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
|
|
240
|
-
} else {
|
|
241
|
-
this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
328
|
+
if (this.page.clientHeight < this.wrapperHeight && deltaY) return;
|
|
329
|
+
if (this.page.clientWidth < this.wrapperWidth && deltaX) return;
|
|
244
330
|
|
|
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
|
-
}
|
|
331
|
+
if (this.maxScrollTop > 0) {
|
|
332
|
+
this.scrollTop = this.scrollTop + deltaY;
|
|
251
333
|
}
|
|
252
334
|
|
|
253
|
-
if (this.
|
|
254
|
-
this.
|
|
335
|
+
if (this.maxScrollLeft > 0) {
|
|
336
|
+
this.scrollLeft = this.scrollLeft + deltaX;
|
|
255
337
|
}
|
|
256
338
|
|
|
257
|
-
if (this.pageScrollParent) {
|
|
258
|
-
this.pageScrollParent.scrollTo({
|
|
259
|
-
top: this.scrollTop,
|
|
260
|
-
left: this.scrollLeft,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
339
|
this.scroll();
|
|
264
340
|
|
|
265
341
|
this.emit('scroll', event);
|
|
266
342
|
};
|
|
343
|
+
|
|
344
|
+
private mouseLeaveHandler = () => {
|
|
345
|
+
setTimeout(() => this.emit('clearHighlight'), throttleTime);
|
|
346
|
+
};
|
|
267
347
|
}
|
package/src/StageRender.ts
CHANGED
|
@@ -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,29 +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
|
+
/** 选中的节点 */
|
|
44
|
+
SELECTED_EL = '666',
|
|
45
|
+
GHOST_EL = '700',
|
|
46
|
+
DRAG_EL = '9',
|
|
32
47
|
}
|
|
33
48
|
|
|
49
|
+
/** 鼠标按键 */
|
|
34
50
|
export enum MouseButton {
|
|
51
|
+
/** 左键 */
|
|
35
52
|
LEFT = 0,
|
|
53
|
+
/** z中健 */
|
|
36
54
|
MIDDLE = 1,
|
|
55
|
+
/** 右键 */
|
|
37
56
|
RIGHT = 2,
|
|
38
57
|
}
|
|
39
58
|
|
|
59
|
+
/** 布局方式 */
|
|
40
60
|
export enum Mode {
|
|
61
|
+
/** 绝对定位布局 */
|
|
41
62
|
ABSOLUTE = 'absolute',
|
|
63
|
+
/** 固定定位布局 */
|
|
42
64
|
FIXED = 'fixed',
|
|
65
|
+
/** 流式布局 */
|
|
43
66
|
SORTABLE = 'sortable',
|
|
44
67
|
}
|
|
68
|
+
|
|
69
|
+
/** 选中节点的class name */
|
|
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,8 +102,9 @@ 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
|
|
107
|
+
getSnapElements?: (el?: HTMLElement) => HTMLElement[];
|
|
98
108
|
updateRootConfig: (config: MApp) => void;
|
|
99
109
|
updatePageId?: (id: Id) => void;
|
|
100
110
|
select?: (id: Id) => Promise<HTMLElement> | HTMLElement;
|
|
@@ -114,3 +124,15 @@ export interface Magic {
|
|
|
114
124
|
export interface RuntimeWindow extends Window {
|
|
115
125
|
magic: Magic;
|
|
116
126
|
}
|
|
127
|
+
|
|
128
|
+
export interface StageHighlightConfig {
|
|
129
|
+
core: StageCore;
|
|
130
|
+
container: HTMLElement;
|
|
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 } 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);
|
|
@@ -154,3 +139,43 @@ export const createDiv = ({ className, cssText }: { className: string; cssText:
|
|
|
154
139
|
el.style.cssText = cssText;
|
|
155
140
|
return el;
|
|
156
141
|
};
|
|
142
|
+
|
|
143
|
+
export const removeSelectedClassName = (doc: Document) => {
|
|
144
|
+
const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
|
|
145
|
+
|
|
146
|
+
if (oldEl) {
|
|
147
|
+
oldEl.classList.remove(SELECTED_CLASS);
|
|
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
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const addSelectedClassName = (el: Element, doc: Document) => {
|
|
156
|
+
el.classList.add(SELECTED_CLASS);
|
|
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 [];
|
|
181
|
+
};
|