@tmagic/stage 1.4.6 → 1.4.8

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.
@@ -1,252 +0,0 @@
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
-
19
- import { EventEmitter } from 'events';
20
-
21
- import { Id } from '@tmagic/schema';
22
- import { getHost, injectStyle, isSameDomain } from '@tmagic/utils';
23
-
24
- import { DEFAULT_ZOOM, RenderType } from './const';
25
- import style from './style.css?raw';
26
- import type { Point, RemoveData, RenderEvents, Runtime, RuntimeWindow, StageRenderConfig, UpdateData } from './types';
27
- import { addSelectedClassName, removeSelectedClassName } from './util';
28
-
29
- export default class StageRender extends EventEmitter {
30
- /** 组件的js、css执行的环境,直接渲染为当前window,iframe渲染则为iframe.contentWindow */
31
- public contentWindow: RuntimeWindow | null = null;
32
- public runtime: Runtime | null = null;
33
- public iframe?: HTMLIFrameElement;
34
- public nativeContainer?: HTMLDivElement;
35
-
36
- private runtimeUrl?: string;
37
- private zoom = DEFAULT_ZOOM;
38
- private renderType: RenderType;
39
- private customizedRender?: () => Promise<HTMLElement | null>;
40
-
41
- constructor({ runtimeUrl, zoom, customizedRender, renderType = RenderType.IFRAME }: StageRenderConfig) {
42
- super();
43
-
44
- this.renderType = renderType;
45
- this.runtimeUrl = runtimeUrl || '';
46
- this.customizedRender = customizedRender;
47
- this.setZoom(zoom);
48
-
49
- if (this.renderType === RenderType.IFRAME) {
50
- this.createIframe();
51
- } else if (this.renderType === RenderType.NATIVE) {
52
- this.createNativeContainer();
53
- }
54
- }
55
-
56
- public getMagicApi = () => ({
57
- onPageElUpdate: (el: HTMLElement) => this.emit('page-el-update', el),
58
- onRuntimeReady: (runtime: Runtime) => {
59
- this.runtime = runtime;
60
- // @ts-ignore
61
- globalThis.runtime = runtime;
62
- this.emit('runtime-ready', runtime);
63
- },
64
- });
65
-
66
- public async add(data: UpdateData): Promise<void> {
67
- const runtime = await this.getRuntime();
68
- return runtime?.add?.(data);
69
- }
70
-
71
- public async remove(data: RemoveData): Promise<void> {
72
- const runtime = await this.getRuntime();
73
- return runtime?.remove?.(data);
74
- }
75
-
76
- public async update(data: UpdateData): Promise<void> {
77
- const runtime = await this.getRuntime();
78
- // 更新画布中的组件
79
- runtime?.update?.(data);
80
- }
81
-
82
- public async select(ids: Id[]): Promise<void> {
83
- const runtime = await this.getRuntime();
84
-
85
- for (const id of ids) {
86
- await runtime?.select?.(id);
87
-
88
- this.flagSelectedEl(this.getTargetElement(id));
89
- }
90
- }
91
-
92
- public setZoom(zoom: number = DEFAULT_ZOOM): void {
93
- this.zoom = zoom;
94
- }
95
-
96
- /**
97
- * 挂载Dom节点
98
- * @param el 将页面挂载到该Dom节点上
99
- */
100
- public async mount(el: HTMLDivElement) {
101
- if (this.iframe) {
102
- if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
103
- // 不同域,使用srcdoc发起异步请求,需要目标地址支持跨域
104
- let html = await fetch(this.runtimeUrl).then((res) => res.text());
105
- // 使用base, 解决相对路径或绝对路径的问题
106
- const base = `${location.protocol}//${getHost(this.runtimeUrl)}`;
107
- html = html.replace('<head>', `<head>\n<base href="${base}">`);
108
- this.iframe.srcdoc = html;
109
- }
110
-
111
- el.appendChild<HTMLIFrameElement>(this.iframe);
112
-
113
- this.postTmagicRuntimeReady();
114
- } else if (this.nativeContainer) {
115
- el.appendChild(this.nativeContainer);
116
- }
117
- }
118
-
119
- public getRuntime = (): Promise<Runtime> => {
120
- if (this.runtime) return Promise.resolve(this.runtime);
121
- return new Promise((resolve) => {
122
- const listener = (runtime: Runtime) => {
123
- this.off('runtime-ready', listener);
124
- resolve(runtime);
125
- };
126
- this.on('runtime-ready', listener);
127
- });
128
- };
129
-
130
- public getDocument(): Document | undefined {
131
- return this.contentWindow?.document;
132
- }
133
-
134
- /**
135
- * 通过坐标获得坐标下所有HTML元素数组
136
- * @param point 坐标
137
- * @returns 坐标下方所有HTML元素数组,会包含父元素直至html,元素层叠时返回顺序是从上到下
138
- */
139
- public getElementsFromPoint(point: Point): HTMLElement[] {
140
- let x = point.clientX;
141
- let y = point.clientY;
142
-
143
- if (this.iframe) {
144
- const rect = this.iframe.getClientRects()[0];
145
- if (rect) {
146
- x = x - rect.left;
147
- y = y - rect.top;
148
- }
149
- }
150
-
151
- return this.getDocument()?.elementsFromPoint(x / this.zoom, y / this.zoom) as HTMLElement[];
152
- }
153
-
154
- public getTargetElement(id: Id): HTMLElement | null {
155
- return this.getDocument()?.getElementById(`${id}`) || null;
156
- }
157
-
158
- /**
159
- * 销毁实例
160
- */
161
- public destroy(): void {
162
- this.iframe?.removeEventListener('load', this.iframeLoadHandler);
163
- this.contentWindow = null;
164
- this.iframe?.remove();
165
- this.iframe = undefined;
166
- this.removeAllListeners();
167
- }
168
-
169
- public on<Name extends keyof RenderEvents, Param extends RenderEvents[Name]>(
170
- eventName: Name,
171
- listener: (...args: Param) => void | Promise<void>,
172
- ) {
173
- return super.on(eventName, listener as any);
174
- }
175
-
176
- public emit<Name extends keyof RenderEvents, Param extends RenderEvents[Name]>(eventName: Name, ...args: Param) {
177
- return super.emit(eventName, ...args);
178
- }
179
-
180
- private createIframe(): HTMLIFrameElement {
181
- this.iframe = globalThis.document.createElement('iframe');
182
- // 同源,直接加载
183
- this.iframe.src = this.runtimeUrl && isSameDomain(this.runtimeUrl) ? this.runtimeUrl : '';
184
- this.iframe.style.cssText = `
185
- border: 0;
186
- width: 100%;
187
- height: 100%;
188
- `;
189
-
190
- this.iframe.addEventListener('load', this.iframeLoadHandler);
191
-
192
- return this.iframe;
193
- }
194
-
195
- private async createNativeContainer() {
196
- this.contentWindow = globalThis as unknown as RuntimeWindow;
197
- this.nativeContainer = globalThis.document.createElement('div');
198
-
199
- this.contentWindow.magic = this.getMagicApi();
200
-
201
- if (this.customizedRender) {
202
- const el = await this.customizedRender();
203
- if (el) {
204
- this.nativeContainer.appendChild(el);
205
- }
206
- }
207
- }
208
-
209
- /**
210
- * 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
211
- * @param el 被选中的元素
212
- */
213
- private flagSelectedEl(el: HTMLElement | null): void {
214
- const doc = this.getDocument();
215
- if (doc) {
216
- removeSelectedClassName(doc);
217
- el && addSelectedClassName(el, doc);
218
- }
219
- }
220
-
221
- private iframeLoadHandler = async () => {
222
- if (!this.contentWindow?.magic) {
223
- this.postTmagicRuntimeReady();
224
- }
225
-
226
- if (!this.contentWindow) return;
227
-
228
- if (this.customizedRender) {
229
- const el = await this.customizedRender();
230
- if (el) {
231
- this.contentWindow.document?.body?.appendChild(el);
232
- }
233
- }
234
-
235
- this.emit('onload');
236
-
237
- injectStyle(this.contentWindow.document, style);
238
- };
239
-
240
- private postTmagicRuntimeReady() {
241
- this.contentWindow = this.iframe?.contentWindow as RuntimeWindow;
242
-
243
- this.contentWindow.magic = this.getMagicApi();
244
-
245
- this.contentWindow.postMessage(
246
- {
247
- tmagicRuntimeReady: true,
248
- },
249
- '*',
250
- );
251
- }
252
- }
@@ -1,122 +0,0 @@
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 { guid } from '@tmagic/utils';
19
-
20
- import { Mode, ZIndex } from './const';
21
- import type { TargetElement as ShadowElement, TargetShadowConfig, UpdateDragEl } from './types';
22
- import { getTargetElStyle, isFixedParent } from './util';
23
-
24
- /**
25
- * 将选中的节点修正定位后,添加一个操作节点到蒙层上
26
- */
27
- export default class TargetShadow {
28
- public el?: ShadowElement;
29
- public els: ShadowElement[] = [];
30
-
31
- private idPrefix = `target_calibrate_${guid()}`;
32
- private container: HTMLElement;
33
- private scrollLeft = 0;
34
- private scrollTop = 0;
35
- private zIndex?: ZIndex;
36
-
37
- private updateDragEl?: UpdateDragEl;
38
-
39
- constructor(config: TargetShadowConfig) {
40
- this.container = config.container;
41
-
42
- if (config.updateDragEl) {
43
- this.updateDragEl = config.updateDragEl;
44
- }
45
-
46
- if (typeof config.zIndex !== 'undefined') {
47
- this.zIndex = config.zIndex;
48
- }
49
-
50
- if (config.idPrefix) {
51
- this.idPrefix = `${config.idPrefix}_${guid()}`;
52
- }
53
-
54
- this.container.addEventListener('customScroll', this.scrollHandler);
55
- }
56
-
57
- public update(target: ShadowElement): ShadowElement {
58
- this.el = this.updateEl(target, this.el);
59
-
60
- return this.el;
61
- }
62
-
63
- public updateGroup(targetGroup: ShadowElement[]): ShadowElement[] {
64
- if (this.els.length > targetGroup.length) {
65
- this.els.slice(targetGroup.length - 1).forEach((el) => {
66
- el.remove();
67
- });
68
- }
69
-
70
- this.els = targetGroup.map((target, index) => this.updateEl(target, this.els[index]));
71
-
72
- return this.els;
73
- }
74
-
75
- public destroyEl(): void {
76
- this.el?.remove();
77
- this.el = undefined;
78
- }
79
-
80
- public destroyEls(): void {
81
- this.els.forEach((el) => {
82
- el.remove();
83
- });
84
- this.els = [];
85
- }
86
-
87
- public destroy(): void {
88
- this.container.removeEventListener('customScroll', this.scrollHandler);
89
- this.destroyEl();
90
- this.destroyEls();
91
- }
92
-
93
- private updateEl(target: ShadowElement, src?: ShadowElement): ShadowElement {
94
- const el = src || globalThis.document.createElement('div');
95
-
96
- el.id = `${this.idPrefix}_${target.id}`;
97
-
98
- el.style.cssText = getTargetElStyle(target, this.zIndex);
99
-
100
- if (typeof this.updateDragEl === 'function') {
101
- this.updateDragEl(el, target, this.container);
102
- }
103
- const isFixed = isFixedParent(target);
104
- const mode = this.container.dataset.mode || Mode.ABSOLUTE;
105
- if (isFixed && mode !== Mode.FIXED) {
106
- el.style.transform = `translate3d(${this.scrollLeft}px, ${this.scrollTop}px, 0)`;
107
- } else if (!isFixed && mode === Mode.FIXED) {
108
- el.style.transform = `translate3d(${-this.scrollLeft}px, ${-this.scrollTop}px, 0)`;
109
- }
110
-
111
- if (!globalThis.document.getElementById(el.id)) {
112
- this.container.append(el);
113
- }
114
-
115
- return el;
116
- }
117
-
118
- private scrollHandler = (e: any) => {
119
- this.scrollLeft = e.detail.scrollLeft;
120
- this.scrollTop = e.detail.scrollTop;
121
- };
122
- }
package/src/const.ts DELETED
@@ -1,111 +0,0 @@
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
-
19
- /** 流式布局下拖动时需要clone一个镜像节点,镜像节点的id前缀 */
20
- export const GHOST_EL_ID_PREFIX = 'ghost_el_';
21
-
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
- export const CONTAINER_HIGHLIGHT_CLASS_NAME = 'tmagic-stage-container-highlight';
29
-
30
- export const PAGE_CLASS = 'magic-ui-page';
31
-
32
- /** 默认放到缩小倍数 */
33
- export const DEFAULT_ZOOM = 1;
34
-
35
- /** 参考线类型 */
36
- export enum GuidesType {
37
- /** 水平 */
38
- HORIZONTAL = 'horizontal',
39
- /** 垂直 */
40
- VERTICAL = 'vertical',
41
- }
42
-
43
- /** css z-index */
44
- export enum ZIndex {
45
- /** 蒙层,用于监听用户操作,需要置于顶层 */
46
- MASK = '99999',
47
- /** 选中的节点 */
48
- SELECTED_EL = '666',
49
- GHOST_EL = '700',
50
- DRAG_EL = '9',
51
- HIGHLIGHT_EL = '8',
52
- }
53
-
54
- /** 鼠标按键 */
55
- export enum MouseButton {
56
- /** 左键 */
57
- LEFT = 0,
58
- /** z中健 */
59
- MIDDLE = 1,
60
- /** 右键 */
61
- RIGHT = 2,
62
- }
63
-
64
- /** 布局方式 */
65
- export enum Mode {
66
- /** 绝对定位布局 */
67
- ABSOLUTE = 'absolute',
68
- /** 固定定位布局 */
69
- FIXED = 'fixed',
70
- /** 流式布局 */
71
- SORTABLE = 'sortable',
72
- }
73
-
74
- /** 选中节点的class name */
75
- export const SELECTED_CLASS = 'tmagic-stage-selected-area';
76
-
77
- export enum AbleActionEventType {
78
- SELECT_PARENT = 'select-parent',
79
- REMOVE = 'remove',
80
- }
81
-
82
- /** 将组件添加到容器的方式 */
83
- export enum ContainerHighlightType {
84
- /** 默认方式:组件在容器上方悬停一段时间后加入 */
85
- DEFAULT = 'default',
86
- /** 按住alt键,并在容器上方悬停一段时间后加入 */
87
- ALT = 'alt',
88
- }
89
-
90
- export enum RenderType {
91
- IFRAME = 'iframe',
92
- NATIVE = 'native',
93
- }
94
-
95
- /** 选择状态 */
96
- export enum SelectStatus {
97
- /** 单选 */
98
- SELECT = 'select',
99
- /** 多选 */
100
- MULTI_SELECT = 'multiSelect',
101
- }
102
-
103
- /** 拖动状态 */
104
- export enum StageDragStatus {
105
- /** 开始拖动 */
106
- START = 'start',
107
- /** 拖动中 */
108
- ING = 'ing',
109
- /** 拖动结束 */
110
- END = 'end',
111
- }
package/src/index.ts DELETED
@@ -1,29 +0,0 @@
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
-
19
- import StageCore from './StageCore';
20
-
21
- export type { MoveableOptions, OnDragStart } from 'moveable';
22
- export type { GuidesOptions } from '@scena/guides';
23
- export { default as StageRender } from './StageRender';
24
- export { default as StageMask } from './StageMask';
25
- export { default as StageDragResize } from './StageDragResize';
26
- export * from './types';
27
- export * from './const';
28
- export * from './util';
29
- export default StageCore;
package/src/logger.ts DELETED
@@ -1,37 +0,0 @@
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
-
19
- export const log = (...args: any[]) => {
20
- console.log(...args);
21
- };
22
-
23
- export const info = (...args: any[]) => {
24
- console.log(...args);
25
- };
26
-
27
- export const warn = (...args: any[]) => {
28
- console.warn(...args);
29
- };
30
-
31
- export const debug = (...args: any[]) => {
32
- console.debug(...args);
33
- };
34
-
35
- export const error = (...args: any[]) => {
36
- console.error(...args);
37
- };
@@ -1,108 +0,0 @@
1
- .moveable-button {
2
- width: 20px;
3
- height: 20px;
4
- background: #4af;
5
- border-radius: 4px;
6
- appearance: none;
7
- border: 0;
8
- color: white;
9
- font-size: 12px;
10
- font-weight: bold;
11
- margin-left: 2px;
12
- position: relative;
13
- }
14
- .moveable-remove-button:before, .moveable-remove-button:after {
15
- content: "";
16
- position: absolute;
17
- left: 50%;
18
- top: 50%;
19
- transform: translate(-50%, -50%) rotate(45deg);
20
- width: 14px;
21
- height: 2px;
22
- background: #fff;
23
- border-radius: 1px;
24
- cursor: pointer;
25
- }
26
- .moveable-remove-button:after {
27
- transform: translate(-50%, -50%) rotate(-45deg);
28
- }
29
-
30
- .moveable-select-parent-arrow-top-icon {
31
- transform: rotateZ(-45deg);
32
- width: 4px;
33
- height: 4px;
34
- border-color: #fff;
35
- border-width: 2px 2px 0 0;
36
- border-style: solid;
37
- position: absolute;
38
- left: 4px;
39
- top: 4px;
40
- }
41
-
42
- .moveable-select-parent-arrow-body-icon {
43
- width: 7px;
44
- height: 11px;
45
- border-color: #fff;
46
- border-width: 0 0 2px 2px;
47
- border-style: solid;
48
- }
49
-
50
- .moveable-drag-area-button {
51
- cursor: move;
52
- }
53
-
54
- .moveable-drag-area-button .moveable-select-parent-arrow-top-icon {
55
- width: 2px;
56
- height: 2px;
57
- }
58
-
59
- .moveable-drag-area-button .moveable-select-parent-arrow-top-icon-top {
60
- transform: rotateZ(-45deg) translateX(-50%);
61
- left: 50%;
62
- top: 3px;
63
- transform-origin: left;
64
- }
65
-
66
- .moveable-drag-area-button .moveable-select-parent-arrow-top-icon-bottom {
67
- transform: rotateZ(135deg) translateX(-50%);
68
- transform-origin: left;
69
- left: 50%;
70
- top: auto;
71
- bottom: 3px;
72
- }
73
-
74
- .moveable-drag-area-button .moveable-select-parent-arrow-top-icon-right {
75
- transform: rotateZ(45deg) translateY(-50%);
76
- transform-origin: top;
77
- right: 3px;
78
- left: auto;
79
- top: 50%;
80
- }
81
-
82
- .moveable-drag-area-button .moveable-select-parent-arrow-top-icon-left {
83
- transform: rotateZ(235deg) translateY(-50%);
84
- transform-origin: top;
85
- left: 3px;
86
- top: 50%;
87
- }
88
-
89
- .moveable-drag-area-button .moveable-select-parent-arrow-body-icon-horizontal {
90
- width: 2px;
91
- height: 11px;
92
- background-color: #fff;
93
- position: absolute;
94
- transform: translateX(-50%);
95
- left: 50%;
96
- top: 4px;
97
- }
98
-
99
- .moveable-drag-area-button .moveable-select-parent-arrow-body-icon-vertical {
100
- width: 11px;
101
- height: 2px;
102
- background-color: #fff;
103
- position: absolute;
104
- transform: translateY(-50%);
105
- left: 4px;
106
- top: 50%;;
107
- }
108
-
package/src/style.css DELETED
@@ -1,15 +0,0 @@
1
- .tmagic-stage-container-highlight::after {
2
- content: '';
3
- position: absolute;
4
- width: 100%;
5
- height: 100%;
6
- top: 0;
7
- left: 0;
8
- background-color: #000;
9
- opacity: .1;
10
- pointer-events: none;
11
- }
12
-
13
- .magic-ui-container.magic-layout-relative {
14
- min-height: 50px;
15
- }