@tmagic/stage 1.2.0-beta.9 → 1.2.1
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 +62 -1
- package/dist/{tmagic-stage.mjs → tmagic-stage.js} +1395 -1062
- package/dist/tmagic-stage.js.map +1 -0
- package/dist/{tmagic-stage.umd.js → tmagic-stage.umd.cjs} +1393 -1060
- package/dist/tmagic-stage.umd.cjs.map +1 -0
- package/package.json +9 -8
- package/src/ActionManager.ts +535 -0
- package/src/DragResizeHelper.ts +338 -0
- package/src/MoveableOptionsManager.ts +249 -0
- package/src/MoveableSelectParentAble.ts +3 -5
- package/src/Rule.ts +4 -4
- package/src/StageCore.ts +204 -276
- package/src/StageDragResize.ts +87 -349
- package/src/StageHighlight.ts +18 -17
- package/src/StageMask.ts +20 -103
- package/src/StageMultiDragResize.ts +98 -199
- package/src/StageRender.ts +86 -11
- package/src/TargetShadow.ts +120 -0
- package/src/const.ts +2 -2
- package/src/index.ts +1 -1
- package/src/logger.ts +1 -1
- package/src/types.ts +100 -20
- package/src/util.ts +19 -12
- package/types/ActionManager.d.ts +141 -0
- package/types/DragResizeHelper.d.ts +70 -0
- package/types/MoveableOptionsManager.d.ts +86 -0
- package/types/MoveableSelectParentAble.d.ts +1 -2
- package/types/StageCore.d.ts +58 -41
- package/types/StageDragResize.d.ts +12 -40
- package/types/StageHighlight.d.ts +3 -4
- package/types/StageMask.d.ts +0 -21
- package/types/StageMultiDragResize.d.ts +8 -24
- package/types/StageRender.d.ts +23 -4
- package/types/TargetShadow.d.ts +22 -0
- package/types/const.d.ts +1 -1
- package/types/types.d.ts +87 -18
- package/types/util.d.ts +8 -8
- package/dist/tmagic-stage.mjs.map +0 -1
- package/dist/tmagic-stage.umd.js.map +0 -1
- package/src/TargetCalibrate.ts +0 -119
- package/types/TargetCalibrate.d.ts +0 -20
package/src/StageRender.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -18,28 +18,30 @@
|
|
|
18
18
|
|
|
19
19
|
import { EventEmitter } from 'events';
|
|
20
20
|
|
|
21
|
+
import { Id } from '@tmagic/schema';
|
|
21
22
|
import { getHost, injectStyle, isSameDomain } from '@tmagic/utils';
|
|
22
23
|
|
|
24
|
+
import { DEFAULT_ZOOM } from './const';
|
|
23
25
|
import style from './style.css?raw';
|
|
24
|
-
import type { Runtime, RuntimeWindow, StageRenderConfig } from './types';
|
|
26
|
+
import type { Point, RemoveData, Runtime, RuntimeWindow, StageRenderConfig, UpdateData } from './types';
|
|
27
|
+
import { addSelectedClassName, removeSelectedClassName } from './util';
|
|
25
28
|
|
|
26
29
|
export default class StageRender extends EventEmitter {
|
|
27
30
|
/** 组件的js、css执行的环境,直接渲染为当前window,iframe渲染则为iframe.contentWindow */
|
|
28
31
|
public contentWindow: RuntimeWindow | null = null;
|
|
29
|
-
|
|
30
32
|
public runtime: Runtime | null = null;
|
|
31
|
-
|
|
32
33
|
public iframe?: HTMLIFrameElement;
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
private runtimeUrl?: string;
|
|
36
|
+
private zoom = DEFAULT_ZOOM;
|
|
37
|
+
private customizedRender?: () => Promise<HTMLElement | null>;
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
constructor({ runtimeUrl, render }: StageRenderConfig) {
|
|
39
|
+
constructor({ runtimeUrl, zoom, customizedRender }: StageRenderConfig) {
|
|
39
40
|
super();
|
|
40
41
|
|
|
41
42
|
this.runtimeUrl = runtimeUrl || '';
|
|
42
|
-
this.
|
|
43
|
+
this.customizedRender = customizedRender;
|
|
44
|
+
this.setZoom(zoom);
|
|
43
45
|
|
|
44
46
|
this.iframe = globalThis.document.createElement('iframe');
|
|
45
47
|
// 同源,直接加载
|
|
@@ -63,6 +65,38 @@ export default class StageRender extends EventEmitter {
|
|
|
63
65
|
},
|
|
64
66
|
});
|
|
65
67
|
|
|
68
|
+
public async add(data: UpdateData): Promise<void> {
|
|
69
|
+
const runtime = await this.getRuntime();
|
|
70
|
+
return runtime?.add?.(data);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async remove(data: RemoveData): Promise<void> {
|
|
74
|
+
const runtime = await this.getRuntime();
|
|
75
|
+
return runtime?.remove?.(data);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public async update(data: UpdateData): Promise<void> {
|
|
79
|
+
const runtime = await this.getRuntime();
|
|
80
|
+
// 更新画布中的组件
|
|
81
|
+
runtime?.update?.(data);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public async select(els: HTMLElement[]): Promise<void> {
|
|
85
|
+
const runtime = await this.getRuntime();
|
|
86
|
+
|
|
87
|
+
for (const el of els) {
|
|
88
|
+
await runtime?.select?.(el.id);
|
|
89
|
+
if (runtime?.beforeSelect) {
|
|
90
|
+
await runtime.beforeSelect(el);
|
|
91
|
+
}
|
|
92
|
+
this.flagSelectedEl(el);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public setZoom(zoom: number = DEFAULT_ZOOM): void {
|
|
97
|
+
this.zoom = zoom;
|
|
98
|
+
}
|
|
99
|
+
|
|
66
100
|
/**
|
|
67
101
|
* 挂载Dom节点
|
|
68
102
|
* @param el 将页面挂载到该Dom节点上
|
|
@@ -101,6 +135,35 @@ export default class StageRender extends EventEmitter {
|
|
|
101
135
|
return this.contentWindow?.document;
|
|
102
136
|
}
|
|
103
137
|
|
|
138
|
+
/**
|
|
139
|
+
* 通过坐标获得坐标下所有HTML元素数组
|
|
140
|
+
* @param point 坐标
|
|
141
|
+
* @returns 坐标下方所有HTML元素数组,会包含父元素直至html,元素层叠时返回顺序是从上到下
|
|
142
|
+
*/
|
|
143
|
+
public getElementsFromPoint(point: Point): HTMLElement[] {
|
|
144
|
+
let x = point.clientX;
|
|
145
|
+
let y = point.clientY;
|
|
146
|
+
|
|
147
|
+
if (this.iframe) {
|
|
148
|
+
const rect = this.iframe.getClientRects()[0];
|
|
149
|
+
if (rect) {
|
|
150
|
+
x = x - rect.left;
|
|
151
|
+
y = y - rect.top;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return this.getDocument()?.elementsFromPoint(x / this.zoom, y / this.zoom) as HTMLElement[];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public getTargetElement(idOrEl: Id | HTMLElement): HTMLElement {
|
|
159
|
+
if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
|
|
160
|
+
const el = this.getDocument()?.getElementById(`${idOrEl}`);
|
|
161
|
+
if (!el) throw new Error(`不存在ID为${idOrEl}的元素`);
|
|
162
|
+
return el;
|
|
163
|
+
}
|
|
164
|
+
return idOrEl;
|
|
165
|
+
}
|
|
166
|
+
|
|
104
167
|
/**
|
|
105
168
|
* 销毁实例
|
|
106
169
|
*/
|
|
@@ -112,6 +175,18 @@ export default class StageRender extends EventEmitter {
|
|
|
112
175
|
this.removeAllListeners();
|
|
113
176
|
}
|
|
114
177
|
|
|
178
|
+
/**
|
|
179
|
+
* 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
|
|
180
|
+
* @param el 被选中的元素
|
|
181
|
+
*/
|
|
182
|
+
private flagSelectedEl(el: HTMLElement): void {
|
|
183
|
+
const doc = this.getDocument();
|
|
184
|
+
if (doc) {
|
|
185
|
+
removeSelectedClassName(doc);
|
|
186
|
+
addSelectedClassName(el, doc);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
115
190
|
private loadHandler = async () => {
|
|
116
191
|
if (!this.contentWindow?.magic) {
|
|
117
192
|
this.postTmagicRuntimeReady();
|
|
@@ -119,8 +194,8 @@ export default class StageRender extends EventEmitter {
|
|
|
119
194
|
|
|
120
195
|
if (!this.contentWindow) return;
|
|
121
196
|
|
|
122
|
-
if (this.
|
|
123
|
-
const el = await this.
|
|
197
|
+
if (this.customizedRender) {
|
|
198
|
+
const el = await this.customizedRender();
|
|
124
199
|
if (el) {
|
|
125
200
|
this.contentWindow.document?.body?.appendChild(el);
|
|
126
201
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2023 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
|
+
import { Mode, ZIndex } from './const';
|
|
19
|
+
import type { TargetElement as ShadowElement, TargetShadowConfig, UpdateDragEl } from './types';
|
|
20
|
+
import { getTargetElStyle, isFixedParent } from './util';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 将选中的节点修正定位后,添加一个操作节点到蒙层上
|
|
24
|
+
*/
|
|
25
|
+
export default class TargetShadow {
|
|
26
|
+
public el?: ShadowElement;
|
|
27
|
+
public els: ShadowElement[] = [];
|
|
28
|
+
|
|
29
|
+
private idPrefix = 'target_calibrate_';
|
|
30
|
+
private container: HTMLElement;
|
|
31
|
+
private scrollLeft = 0;
|
|
32
|
+
private scrollTop = 0;
|
|
33
|
+
private zIndex?: ZIndex;
|
|
34
|
+
|
|
35
|
+
private updateDragEl?: UpdateDragEl;
|
|
36
|
+
|
|
37
|
+
constructor(config: TargetShadowConfig) {
|
|
38
|
+
this.container = config.container;
|
|
39
|
+
|
|
40
|
+
if (config.updateDragEl) {
|
|
41
|
+
this.updateDragEl = config.updateDragEl;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof config.zIndex !== 'undefined') {
|
|
45
|
+
this.zIndex = config.zIndex;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (config.idPrefix) {
|
|
49
|
+
this.idPrefix = config.idPrefix;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.container.addEventListener('customScroll', this.scrollHandler);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public update(target: ShadowElement): ShadowElement {
|
|
56
|
+
this.el = this.updateEl(target, this.el);
|
|
57
|
+
|
|
58
|
+
return this.el;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public updateGroup(targetGroup: ShadowElement[]): ShadowElement[] {
|
|
62
|
+
if (this.els.length > targetGroup.length) {
|
|
63
|
+
this.els.slice(targetGroup.length - 1).forEach((el) => {
|
|
64
|
+
el.remove();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.els = targetGroup.map((target, index) => this.updateEl(target, this.els[index]));
|
|
69
|
+
|
|
70
|
+
return this.els;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public destroyEl(): void {
|
|
74
|
+
this.el?.remove();
|
|
75
|
+
this.el = undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public destroyEls(): void {
|
|
79
|
+
this.els.forEach((el) => {
|
|
80
|
+
el.remove();
|
|
81
|
+
});
|
|
82
|
+
this.els = [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public destroy(): void {
|
|
86
|
+
this.container.removeEventListener('customScroll', this.scrollHandler);
|
|
87
|
+
this.destroyEl();
|
|
88
|
+
this.destroyEls();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private updateEl(target: ShadowElement, src?: ShadowElement): ShadowElement {
|
|
92
|
+
const el = src || globalThis.document.createElement('div');
|
|
93
|
+
|
|
94
|
+
el.id = `${this.idPrefix}${target.id}`;
|
|
95
|
+
|
|
96
|
+
el.style.cssText = getTargetElStyle(target, this.zIndex);
|
|
97
|
+
|
|
98
|
+
if (typeof this.updateDragEl === 'function') {
|
|
99
|
+
this.updateDragEl(el, target);
|
|
100
|
+
}
|
|
101
|
+
const isFixed = isFixedParent(target);
|
|
102
|
+
const mode = this.container.dataset.mode || Mode.ABSOLUTE;
|
|
103
|
+
if (isFixed && mode !== Mode.FIXED) {
|
|
104
|
+
el.style.transform = `translate3d(${this.scrollLeft}px, ${this.scrollTop}px, 0)`;
|
|
105
|
+
} else if (!isFixed && mode === Mode.FIXED) {
|
|
106
|
+
el.style.transform = `translate3d(${-this.scrollLeft}px, ${-this.scrollTop}px, 0)`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!globalThis.document.getElementById(el.id)) {
|
|
110
|
+
this.container.append(el);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return el;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private scrollHandler = (e: any) => {
|
|
117
|
+
this.scrollLeft = e.detail.scrollLeft;
|
|
118
|
+
this.scrollTop = e.detail.scrollTop;
|
|
119
|
+
};
|
|
120
|
+
}
|
package/src/const.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -25,7 +25,7 @@ export const DRAG_EL_ID_PREFIX = 'drag_el_';
|
|
|
25
25
|
/** 高亮时需要在蒙层中创建一个占位节点,该节点的id前缀 */
|
|
26
26
|
export const HIGHLIGHT_EL_ID_PREFIX = 'highlight_el_';
|
|
27
27
|
|
|
28
|
-
export const
|
|
28
|
+
export const CONTAINER_HIGHLIGHT_CLASS_NAME = 'tmagic-stage-container-highlight';
|
|
29
29
|
|
|
30
30
|
export const PAGE_CLASS = 'magic-ui-page';
|
|
31
31
|
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
package/src/logger.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -16,25 +16,44 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { MoveableOptions } from 'moveable';
|
|
19
|
+
import type { MoveableOptions } from 'moveable';
|
|
20
20
|
|
|
21
21
|
import Core from '@tmagic/core';
|
|
22
22
|
import type { Id, MApp, MContainer, MNode } from '@tmagic/schema';
|
|
23
23
|
|
|
24
|
-
import { GuidesType } from './const';
|
|
24
|
+
import { GuidesType, ZIndex } from './const';
|
|
25
25
|
import StageCore from './StageCore';
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
|
|
27
|
+
export type TargetElement = HTMLElement | SVGElement;
|
|
28
28
|
|
|
29
29
|
export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
|
|
30
30
|
export type IsContainer = (el: HTMLElement) => boolean | Promise<boolean>;
|
|
31
|
-
|
|
31
|
+
export type CustomizeRender = (renderer: StageCore) => Promise<HTMLElement> | HTMLElement;
|
|
32
|
+
/** 业务方自定义的moveableOptions,可以是配置,也可以是回调函数 */
|
|
33
|
+
export type CustomizeMoveableOptions =
|
|
34
|
+
| ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions)
|
|
35
|
+
| MoveableOptions
|
|
36
|
+
| undefined;
|
|
37
|
+
/** render提供给的接口,如果是id则转成el,如果是el则直接返回 */
|
|
38
|
+
export type GetTargetElement = (idOrEl: Id | HTMLElement) => HTMLElement;
|
|
39
|
+
/** render提供的接口,通过坐标获得坐标下所有HTML元素数组 */
|
|
40
|
+
export type GetElementsFromPoint = (point: Point) => HTMLElement[];
|
|
41
|
+
export type GetRenderDocument = () => Document | undefined;
|
|
42
|
+
export type DelayedMarkContainer = (event: MouseEvent, exclude: Element[]) => NodeJS.Timeout | undefined;
|
|
43
|
+
export type MarkContainerEnd = () => HTMLElement | null;
|
|
44
|
+
export type GetRootContainer = () => HTMLDivElement | undefined;
|
|
45
|
+
|
|
46
|
+
/** 将组件添加到容器的方式 */
|
|
32
47
|
export enum ContainerHighlightType {
|
|
48
|
+
/** 默认方式:组件在容器上方悬停一段时间后加入 */
|
|
33
49
|
DEFAULT = 'default',
|
|
50
|
+
/** 按住alt键,并在容器上方悬停一段时间后加入 */
|
|
34
51
|
ALT = 'alt',
|
|
35
52
|
}
|
|
36
53
|
|
|
37
|
-
export type
|
|
54
|
+
export type UpdateDragEl = (el: TargetElement, target: TargetElement) => void;
|
|
55
|
+
|
|
56
|
+
export interface StageCoreConfig {
|
|
38
57
|
/** 需要对齐的dom节点的CSS选择器字符串 */
|
|
39
58
|
snapElementQuerySelector?: string;
|
|
40
59
|
/** 放大倍数,默认1倍 */
|
|
@@ -44,18 +63,47 @@ export type StageCoreConfig = {
|
|
|
44
63
|
containerHighlightClassName?: string;
|
|
45
64
|
containerHighlightDuration?: number;
|
|
46
65
|
containerHighlightType?: ContainerHighlightType;
|
|
47
|
-
moveableOptions?:
|
|
48
|
-
multiMoveableOptions?:
|
|
66
|
+
moveableOptions?: CustomizeMoveableOptions;
|
|
67
|
+
multiMoveableOptions?: CustomizeMoveableOptions;
|
|
49
68
|
/** runtime 的HTML地址,可以是一个HTTP地址,如果和编辑器不同域,需要设置跨域,也可以是一个相对或绝对路径 */
|
|
50
69
|
runtimeUrl?: string;
|
|
51
70
|
render?: (renderer: StageCore) => Promise<HTMLElement> | HTMLElement;
|
|
52
71
|
autoScrollIntoView?: boolean;
|
|
53
|
-
updateDragEl?:
|
|
54
|
-
|
|
72
|
+
updateDragEl?: UpdateDragEl;
|
|
73
|
+
disabledDragStart?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ActionManagerConfig {
|
|
77
|
+
container: HTMLElement;
|
|
78
|
+
containerHighlightClassName?: string;
|
|
79
|
+
containerHighlightDuration?: number;
|
|
80
|
+
containerHighlightType?: ContainerHighlightType;
|
|
81
|
+
moveableOptions?: CustomizeMoveableOptions;
|
|
82
|
+
multiMoveableOptions?: CustomizeMoveableOptions;
|
|
83
|
+
disabledDragStart?: boolean;
|
|
84
|
+
canSelect?: CanSelect;
|
|
85
|
+
isContainer: IsContainer;
|
|
86
|
+
getRootContainer: GetRootContainer;
|
|
87
|
+
getRenderDocument: GetRenderDocument;
|
|
88
|
+
updateDragEl?: UpdateDragEl;
|
|
89
|
+
getTargetElement: GetTargetElement;
|
|
90
|
+
getElementsFromPoint: GetElementsFromPoint;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface MoveableOptionsManagerConfig {
|
|
94
|
+
container: HTMLElement;
|
|
95
|
+
moveableOptions?: CustomizeMoveableOptions;
|
|
96
|
+
getRootContainer: GetRootContainer;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface CustomizeMoveableOptionsCallbackConfig {
|
|
100
|
+
targetElId?: string;
|
|
101
|
+
}
|
|
55
102
|
|
|
56
103
|
export interface StageRenderConfig {
|
|
57
104
|
runtimeUrl?: string;
|
|
58
|
-
|
|
105
|
+
zoom: number | undefined;
|
|
106
|
+
customizedRender?: () => Promise<HTMLElement | null>;
|
|
59
107
|
}
|
|
60
108
|
|
|
61
109
|
export interface StageMaskConfig {
|
|
@@ -63,9 +111,35 @@ export interface StageMaskConfig {
|
|
|
63
111
|
}
|
|
64
112
|
|
|
65
113
|
export interface StageDragResizeConfig {
|
|
66
|
-
core: StageCore;
|
|
67
114
|
container: HTMLElement;
|
|
68
|
-
|
|
115
|
+
moveableOptions?: CustomizeMoveableOptions;
|
|
116
|
+
disabledDragStart?: boolean;
|
|
117
|
+
getRootContainer: GetRootContainer;
|
|
118
|
+
getRenderDocument: GetRenderDocument;
|
|
119
|
+
markContainerEnd: MarkContainerEnd;
|
|
120
|
+
delayedMarkContainer: DelayedMarkContainer;
|
|
121
|
+
updateDragEl?: UpdateDragEl;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface StageMultiDragResizeConfig {
|
|
125
|
+
container: HTMLElement;
|
|
126
|
+
multiMoveableOptions?: CustomizeMoveableOptions;
|
|
127
|
+
getRootContainer: GetRootContainer;
|
|
128
|
+
getRenderDocument: GetRenderDocument;
|
|
129
|
+
updateDragEl?: UpdateDragEl;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface DragResizeHelperConfig {
|
|
133
|
+
container: HTMLElement;
|
|
134
|
+
updateDragEl?: UpdateDragEl;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 选择状态 */
|
|
138
|
+
export enum SelectStatus {
|
|
139
|
+
/** 单选 */
|
|
140
|
+
SELECT = 'select',
|
|
141
|
+
/** 多选 */
|
|
142
|
+
MULTI_SELECT = 'multiSelect',
|
|
69
143
|
}
|
|
70
144
|
|
|
71
145
|
/** 拖动状态 */
|
|
@@ -88,6 +162,11 @@ export interface Offset {
|
|
|
88
162
|
top: number;
|
|
89
163
|
}
|
|
90
164
|
|
|
165
|
+
export interface Point {
|
|
166
|
+
clientX: number;
|
|
167
|
+
clientY: number;
|
|
168
|
+
}
|
|
169
|
+
|
|
91
170
|
export interface GuidesEventData {
|
|
92
171
|
type: GuidesType;
|
|
93
172
|
guides: number[];
|
|
@@ -154,13 +233,14 @@ export interface RuntimeWindow extends Window {
|
|
|
154
233
|
}
|
|
155
234
|
|
|
156
235
|
export interface StageHighlightConfig {
|
|
157
|
-
core: StageCore;
|
|
158
236
|
container: HTMLElement;
|
|
237
|
+
updateDragEl?: UpdateDragEl;
|
|
238
|
+
getRootContainer: GetRootContainer;
|
|
159
239
|
}
|
|
160
240
|
|
|
161
|
-
export interface
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
241
|
+
export interface TargetShadowConfig {
|
|
242
|
+
container: HTMLElement;
|
|
243
|
+
zIndex?: ZIndex;
|
|
244
|
+
updateDragEl?: UpdateDragEl;
|
|
245
|
+
idPrefix?: string;
|
|
166
246
|
}
|
package/src/util.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { removeClassName } from '@tmagic/utils';
|
|
19
19
|
|
|
20
20
|
import { GHOST_EL_ID_PREFIX, Mode, SELECTED_CLASS, ZIndex } from './const';
|
|
21
|
-
import type { Offset, SortEventData } from './types';
|
|
21
|
+
import type { Offset, SortEventData, TargetElement } from './types';
|
|
22
22
|
|
|
23
23
|
const getParents = (el: Element, relative: Element) => {
|
|
24
24
|
let cur: Element | null = el.parentElement;
|
|
@@ -30,12 +30,16 @@ const getParents = (el: Element, relative: Element) => {
|
|
|
30
30
|
return parents;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export const getOffset = (el:
|
|
34
|
-
const
|
|
33
|
+
export const getOffset = (el: TargetElement): Offset => {
|
|
34
|
+
const htmlEl = el as HTMLElement;
|
|
35
|
+
const { offsetParent } = htmlEl;
|
|
35
36
|
|
|
36
|
-
const left =
|
|
37
|
-
const top =
|
|
37
|
+
const left = htmlEl.offsetLeft || 0;
|
|
38
|
+
const top = htmlEl.offsetTop || 0;
|
|
38
39
|
|
|
40
|
+
// 在 Webkit 中,如果元素为隐藏的(该元素或其祖先元素的 style.display 为 "none"),或者该元素的 style.position 被设为 "fixed",则该属性返回 null。
|
|
41
|
+
// 在 IE 9 中,如果该元素的 style.position 被设置为 "fixed",则该属性返回 null。(display:none 无影响。)
|
|
42
|
+
// body offsetParent 为 null
|
|
39
43
|
if (offsetParent) {
|
|
40
44
|
const parentOffset = getOffset(offsetParent as HTMLElement);
|
|
41
45
|
return {
|
|
@@ -51,7 +55,7 @@ export const getOffset = (el: HTMLElement): Offset => {
|
|
|
51
55
|
};
|
|
52
56
|
|
|
53
57
|
// 将蒙层占位节点覆盖在原节点上方
|
|
54
|
-
export const getTargetElStyle = (el:
|
|
58
|
+
export const getTargetElStyle = (el: TargetElement, zIndex?: ZIndex) => {
|
|
55
59
|
const offset = getOffset(el);
|
|
56
60
|
const { transform } = getComputedStyle(el);
|
|
57
61
|
return `
|
|
@@ -61,13 +65,16 @@ export const getTargetElStyle = (el: HTMLElement) => {
|
|
|
61
65
|
top: ${offset.top}px;
|
|
62
66
|
width: ${el.clientWidth}px;
|
|
63
67
|
height: ${el.clientHeight}px;
|
|
64
|
-
z-index: ${
|
|
68
|
+
${typeof zIndex !== 'undefined' ? `z-index: ${zIndex};` : ''}
|
|
65
69
|
`;
|
|
66
70
|
};
|
|
67
71
|
|
|
68
72
|
export const getAbsolutePosition = (el: HTMLElement, { top, left }: Offset) => {
|
|
69
73
|
const { offsetParent } = el;
|
|
70
74
|
|
|
75
|
+
// 在 Webkit 中,如果元素为隐藏的(该元素或其祖先元素的 style.display 为 "none"),或者该元素的 style.position 被设为 "fixed",则该属性返回 null。
|
|
76
|
+
// 在 IE 9 中,如果该元素的 style.position 被设置为 "fixed",则该属性返回 null。(display:none 无影响。)
|
|
77
|
+
// body offsetParent 为 null
|
|
71
78
|
if (offsetParent) {
|
|
72
79
|
const parentOffset = getOffset(offsetParent as HTMLElement);
|
|
73
80
|
return {
|
|
@@ -87,7 +94,7 @@ export const isStatic = (style: CSSStyleDeclaration): boolean => style.position
|
|
|
87
94
|
|
|
88
95
|
export const isFixed = (style: CSSStyleDeclaration): boolean => style.position === 'fixed';
|
|
89
96
|
|
|
90
|
-
export const isFixedParent = (el:
|
|
97
|
+
export const isFixedParent = (el: Element) => {
|
|
91
98
|
let fixed = false;
|
|
92
99
|
let dom = el;
|
|
93
100
|
while (dom) {
|
|
@@ -104,7 +111,7 @@ export const isFixedParent = (el: HTMLElement) => {
|
|
|
104
111
|
return fixed;
|
|
105
112
|
};
|
|
106
113
|
|
|
107
|
-
export const getMode = (el:
|
|
114
|
+
export const getMode = (el: Element): Mode => {
|
|
108
115
|
if (isFixedParent(el)) return Mode.FIXED;
|
|
109
116
|
const style = getComputedStyle(el);
|
|
110
117
|
if (isStatic(style) || isRelative(style)) return Mode.SORTABLE;
|
|
@@ -168,7 +175,7 @@ export const calcValueByFontsize = (doc: Document, value: number) => {
|
|
|
168
175
|
* @param {number} deltaTop 偏移量
|
|
169
176
|
* @param {Object} detail 当前选中的组件配置
|
|
170
177
|
*/
|
|
171
|
-
export const down = (deltaTop: number, target:
|
|
178
|
+
export const down = (deltaTop: number, target: TargetElement): SortEventData | void => {
|
|
172
179
|
let swapIndex = 0;
|
|
173
180
|
let addUpH = target.clientHeight;
|
|
174
181
|
const brothers = Array.from(target.parentNode?.children || []).filter(
|
|
@@ -204,7 +211,7 @@ export const down = (deltaTop: number, target: HTMLElement | SVGElement): SortEv
|
|
|
204
211
|
* @param {number} deltaTop 偏移量
|
|
205
212
|
* @param {Object} detail 当前选中的组件配置
|
|
206
213
|
*/
|
|
207
|
-
export const up = (deltaTop: number, target:
|
|
214
|
+
export const up = (deltaTop: number, target: TargetElement): SortEventData | void => {
|
|
208
215
|
const brothers = Array.from(target.parentNode?.children || []).filter(
|
|
209
216
|
(node) => !node.id.startsWith(GHOST_EL_ID_PREFIX),
|
|
210
217
|
);
|