@tmagic/stage 1.3.4 → 1.3.6

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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.4",
2
+ "version": "1.3.6",
3
3
  "name": "@tmagic/stage",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -23,7 +23,7 @@
23
23
  "clear:type": "rimraf ./types"
24
24
  },
25
25
  "engines": {
26
- "node": ">=14"
26
+ "node": ">=18"
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",
@@ -31,9 +31,9 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@scena/guides": "^0.29.2",
34
- "@tmagic/core": "1.3.4",
35
- "@tmagic/schema": "1.3.4",
36
- "@tmagic/utils": "1.3.4",
34
+ "@tmagic/core": "1.3.6",
35
+ "@tmagic/schema": "1.3.6",
36
+ "@tmagic/utils": "1.3.6",
37
37
  "events": "^3.3.0",
38
38
  "keycon": "^1.4.0",
39
39
  "lodash-es": "^4.17.21",
@@ -43,10 +43,10 @@
43
43
  "devDependencies": {
44
44
  "@types/events": "^3.0.0",
45
45
  "@types/lodash-es": "^4.17.4",
46
- "@types/node": "^15.12.4",
46
+ "@types/node": "^18.19.0",
47
47
  "rimraf": "^3.0.2",
48
48
  "sass": "^1.35.1",
49
49
  "typescript": "^5.0.4",
50
- "vite": "^4.4.4"
50
+ "vite": "^5.0.7"
51
51
  }
52
52
  }
@@ -561,13 +561,13 @@ export default class ActionManager extends EventEmitter {
561
561
  /**
562
562
  * 在up事件中负责对外通知选中事件,通知画布之外的编辑器更新
563
563
  */
564
- private mouseUpHandler = (): void => {
564
+ private mouseUpHandler = (event: MouseEvent): void => {
565
565
  getDocument().removeEventListener('mouseup', this.mouseUpHandler);
566
566
  this.container.addEventListener('mousemove', this.mouseMoveHandler);
567
567
  if (this.isMultiSelectStatus) {
568
- this.emit('multi-select', this.selectedElList);
568
+ this.emit('multi-select', this.selectedElList, event);
569
569
  } else {
570
- this.emit('select', this.selectedEl);
570
+ this.emit('select', this.selectedEl, event);
571
571
  }
572
572
  };
573
573
 
package/src/StageCore.ts CHANGED
@@ -61,6 +61,7 @@ export default class StageCore extends EventEmitter {
61
61
  this.renderer = new StageRender({
62
62
  runtimeUrl: config.runtimeUrl,
63
63
  zoom: config.zoom,
64
+ renderType: config.renderType,
64
65
  customizedRender: async (): Promise<HTMLElement | null> => {
65
66
  if (this?.customizedRender) {
66
67
  return await this.customizedRender(this);
@@ -307,14 +308,14 @@ export default class StageCore extends EventEmitter {
307
308
  .on('before-select', (idOrEl: Id | HTMLElement, event?: MouseEvent) => {
308
309
  this.select(idOrEl, event);
309
310
  })
310
- .on('select', (selectedEl: HTMLElement) => {
311
- this.emit('select', selectedEl);
311
+ .on('select', (selectedEl: HTMLElement, event: MouseEvent) => {
312
+ this.emit('select', selectedEl, event);
312
313
  })
313
314
  .on('before-multi-select', (idOrElList: HTMLElement[] | Id[]) => {
314
315
  this.multiSelect(idOrElList);
315
316
  })
316
- .on('multi-select', (selectedElList: HTMLElement[]) => {
317
- this.emit('multi-select', selectedElList);
317
+ .on('multi-select', (selectedElList: HTMLElement[], event: MouseEvent) => {
318
+ this.emit('multi-select', selectedElList, event);
318
319
  });
319
320
  }
320
321
 
@@ -23,7 +23,15 @@ import { getHost, injectStyle, isSameDomain } from '@tmagic/utils';
23
23
 
24
24
  import { DEFAULT_ZOOM } from './const';
25
25
  import style from './style.css?raw';
26
- import type { Point, RemoveData, Runtime, RuntimeWindow, StageRenderConfig, UpdateData } from './types';
26
+ import {
27
+ type Point,
28
+ type RemoveData,
29
+ RenderType,
30
+ type Runtime,
31
+ type RuntimeWindow,
32
+ type StageRenderConfig,
33
+ type UpdateData,
34
+ } from './types';
27
35
  import { addSelectedClassName, removeSelectedClassName } from './util';
28
36
 
29
37
  export default class StageRender extends EventEmitter {
@@ -31,28 +39,26 @@ export default class StageRender extends EventEmitter {
31
39
  public contentWindow: RuntimeWindow | null = null;
32
40
  public runtime: Runtime | null = null;
33
41
  public iframe?: HTMLIFrameElement;
42
+ public nativeContainer?: HTMLDivElement;
34
43
 
35
44
  private runtimeUrl?: string;
36
45
  private zoom = DEFAULT_ZOOM;
46
+ private renderType: RenderType;
37
47
  private customizedRender?: () => Promise<HTMLElement | null>;
38
48
 
39
- constructor({ runtimeUrl, zoom, customizedRender }: StageRenderConfig) {
49
+ constructor({ runtimeUrl, zoom, customizedRender, renderType = RenderType.IFRAME }: StageRenderConfig) {
40
50
  super();
41
51
 
52
+ this.renderType = renderType;
42
53
  this.runtimeUrl = runtimeUrl || '';
43
54
  this.customizedRender = customizedRender;
44
55
  this.setZoom(zoom);
45
56
 
46
- this.iframe = globalThis.document.createElement('iframe');
47
- // 同源,直接加载
48
- this.iframe.src = isSameDomain(this.runtimeUrl) ? this.runtimeUrl : '';
49
- this.iframe.style.cssText = `
50
- border: 0;
51
- width: 100%;
52
- height: 100%;
53
- `;
54
-
55
- this.iframe.addEventListener('load', this.loadHandler);
57
+ if (this.renderType === RenderType.IFRAME) {
58
+ this.createIframe();
59
+ } else if (this.renderType === RenderType.NATIVE) {
60
+ this.createNativeContainer();
61
+ }
56
62
  }
57
63
 
58
64
  public getMagicApi = () => ({
@@ -102,22 +108,22 @@ export default class StageRender extends EventEmitter {
102
108
  * @param el 将页面挂载到该Dom节点上
103
109
  */
104
110
  public async mount(el: HTMLDivElement) {
105
- if (!this.iframe) {
106
- throw Error('mount 失败');
107
- }
108
-
109
- if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
110
- // 不同域,使用srcdoc发起异步请求,需要目标地址支持跨域
111
- let html = await fetch(this.runtimeUrl).then((res) => res.text());
112
- // 使用base, 解决相对路径或绝对路径的问题
113
- const base = `${location.protocol}//${getHost(this.runtimeUrl)}`;
114
- html = html.replace('<head>', `<head>\n<base href="${base}">`);
115
- this.iframe.srcdoc = html;
116
- }
111
+ if (this.iframe) {
112
+ if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
113
+ // 不同域,使用srcdoc发起异步请求,需要目标地址支持跨域
114
+ let html = await fetch(this.runtimeUrl).then((res) => res.text());
115
+ // 使用base, 解决相对路径或绝对路径的问题
116
+ const base = `${location.protocol}//${getHost(this.runtimeUrl)}`;
117
+ html = html.replace('<head>', `<head>\n<base href="${base}">`);
118
+ this.iframe.srcdoc = html;
119
+ }
117
120
 
118
- el.appendChild<HTMLIFrameElement>(this.iframe);
121
+ el.appendChild<HTMLIFrameElement>(this.iframe);
119
122
 
120
- this.postTmagicRuntimeReady();
123
+ this.postTmagicRuntimeReady();
124
+ } else if (this.nativeContainer) {
125
+ el.appendChild(this.nativeContainer);
126
+ }
121
127
  }
122
128
 
123
129
  public getRuntime = (): Promise<Runtime> => {
@@ -150,6 +156,12 @@ export default class StageRender extends EventEmitter {
150
156
  x = x - rect.left;
151
157
  y = y - rect.top;
152
158
  }
159
+ } else if (this.nativeContainer) {
160
+ const rect = this.nativeContainer.getClientRects()[0];
161
+ if (rect) {
162
+ x = x - rect.left;
163
+ y = y - rect.top;
164
+ }
153
165
  }
154
166
 
155
167
  return this.getDocument()?.elementsFromPoint(x / this.zoom, y / this.zoom) as HTMLElement[];
@@ -168,13 +180,42 @@ export default class StageRender extends EventEmitter {
168
180
  * 销毁实例
169
181
  */
170
182
  public destroy(): void {
171
- this.iframe?.removeEventListener('load', this.loadHandler);
183
+ this.iframe?.removeEventListener('load', this.iframeLoadHandler);
172
184
  this.contentWindow = null;
173
185
  this.iframe?.remove();
174
186
  this.iframe = undefined;
175
187
  this.removeAllListeners();
176
188
  }
177
189
 
190
+ private createIframe(): HTMLIFrameElement {
191
+ this.iframe = globalThis.document.createElement('iframe');
192
+ // 同源,直接加载
193
+ this.iframe.src = this.runtimeUrl && isSameDomain(this.runtimeUrl) ? this.runtimeUrl : '';
194
+ this.iframe.style.cssText = `
195
+ border: 0;
196
+ width: 100%;
197
+ height: 100%;
198
+ `;
199
+
200
+ this.iframe.addEventListener('load', this.iframeLoadHandler);
201
+
202
+ return this.iframe;
203
+ }
204
+
205
+ private async createNativeContainer() {
206
+ this.contentWindow = globalThis as unknown as RuntimeWindow;
207
+ this.nativeContainer = globalThis.document.createElement('div');
208
+
209
+ this.contentWindow.magic = this.getMagicApi();
210
+
211
+ if (this.customizedRender) {
212
+ const el = await this.customizedRender();
213
+ if (el) {
214
+ this.nativeContainer.appendChild(el);
215
+ }
216
+ }
217
+ }
218
+
178
219
  /**
179
220
  * 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
180
221
  * @param el 被选中的元素
@@ -187,7 +228,7 @@ export default class StageRender extends EventEmitter {
187
228
  }
188
229
  }
189
230
 
190
- private loadHandler = async () => {
231
+ private iframeLoadHandler = async () => {
191
232
  if (!this.contentWindow?.magic) {
192
233
  this.postTmagicRuntimeReady();
193
234
  }
@@ -96,7 +96,7 @@ export default class TargetShadow {
96
96
  el.style.cssText = getTargetElStyle(target, this.zIndex);
97
97
 
98
98
  if (typeof this.updateDragEl === 'function') {
99
- this.updateDragEl(el, target);
99
+ this.updateDragEl(el, target, this.container);
100
100
  }
101
101
  const isFixed = isFixedParent(target);
102
102
  const mode = this.container.dataset.mode || Mode.ABSOLUTE;
package/src/types.ts CHANGED
@@ -52,7 +52,12 @@ export enum ContainerHighlightType {
52
52
  ALT = 'alt',
53
53
  }
54
54
 
55
- export type UpdateDragEl = (el: TargetElement, target: TargetElement) => void;
55
+ export enum RenderType {
56
+ IFRAME = 'iframe',
57
+ NATIVE = 'native',
58
+ }
59
+
60
+ export type UpdateDragEl = (el: TargetElement, target: TargetElement, container: HTMLElement) => void;
56
61
 
57
62
  export interface StageCoreConfig {
58
63
  /** 需要对齐的dom节点的CSS选择器字符串 */
@@ -71,6 +76,7 @@ export interface StageCoreConfig {
71
76
  autoScrollIntoView?: boolean;
72
77
  updateDragEl?: UpdateDragEl;
73
78
  disabledDragStart?: boolean;
79
+ renderType?: RenderType;
74
80
  }
75
81
 
76
82
  export interface ActionManagerConfig {
@@ -107,6 +113,7 @@ export interface CustomizeMoveableOptionsCallbackConfig {
107
113
  export interface StageRenderConfig {
108
114
  runtimeUrl?: string;
109
115
  zoom: number | undefined;
116
+ renderType?: RenderType;
110
117
  customizedRender?: () => Promise<HTMLElement | null>;
111
118
  }
112
119
 
@@ -1,16 +1,18 @@
1
1
  /// <reference types="node" />
2
2
  import { EventEmitter } from 'events';
3
3
  import { Id } from '@tmagic/schema';
4
- import type { Point, RemoveData, Runtime, RuntimeWindow, StageRenderConfig, UpdateData } from './types';
4
+ import { type Point, type RemoveData, type Runtime, type RuntimeWindow, type StageRenderConfig, type UpdateData } from './types';
5
5
  export default class StageRender extends EventEmitter {
6
6
  /** 组件的js、css执行的环境,直接渲染为当前window,iframe渲染则为iframe.contentWindow */
7
7
  contentWindow: RuntimeWindow | null;
8
8
  runtime: Runtime | null;
9
9
  iframe?: HTMLIFrameElement;
10
+ nativeContainer?: HTMLDivElement;
10
11
  private runtimeUrl?;
11
12
  private zoom;
13
+ private renderType;
12
14
  private customizedRender?;
13
- constructor({ runtimeUrl, zoom, customizedRender }: StageRenderConfig);
15
+ constructor({ runtimeUrl, zoom, customizedRender, renderType }: StageRenderConfig);
14
16
  getMagicApi: () => {
15
17
  onPageElUpdate: (el: HTMLElement) => boolean;
16
18
  onRuntimeReady: (runtime: Runtime) => void;
@@ -38,11 +40,13 @@ export default class StageRender extends EventEmitter {
38
40
  * 销毁实例
39
41
  */
40
42
  destroy(): void;
43
+ private createIframe;
44
+ private createNativeContainer;
41
45
  /**
42
46
  * 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
43
47
  * @param el 被选中的元素
44
48
  */
45
49
  private flagSelectedEl;
46
- private loadHandler;
50
+ private iframeLoadHandler;
47
51
  private postTmagicRuntimeReady;
48
52
  }
package/types/types.d.ts CHANGED
@@ -26,7 +26,11 @@ export declare enum ContainerHighlightType {
26
26
  /** 按住alt键,并在容器上方悬停一段时间后加入 */
27
27
  ALT = "alt"
28
28
  }
29
- export type UpdateDragEl = (el: TargetElement, target: TargetElement) => void;
29
+ export declare enum RenderType {
30
+ IFRAME = "iframe",
31
+ NATIVE = "native"
32
+ }
33
+ export type UpdateDragEl = (el: TargetElement, target: TargetElement, container: HTMLElement) => void;
30
34
  export interface StageCoreConfig {
31
35
  /** 需要对齐的dom节点的CSS选择器字符串 */
32
36
  snapElementQuerySelector?: string;
@@ -44,6 +48,7 @@ export interface StageCoreConfig {
44
48
  autoScrollIntoView?: boolean;
45
49
  updateDragEl?: UpdateDragEl;
46
50
  disabledDragStart?: boolean;
51
+ renderType?: RenderType;
47
52
  }
48
53
  export interface ActionManagerConfig {
49
54
  container: HTMLElement;
@@ -76,6 +81,7 @@ export interface CustomizeMoveableOptionsCallbackConfig {
76
81
  export interface StageRenderConfig {
77
82
  runtimeUrl?: string;
78
83
  zoom: number | undefined;
84
+ renderType?: RenderType;
79
85
  customizedRender?: () => Promise<HTMLElement | null>;
80
86
  }
81
87
  export interface StageMaskConfig {