@tmagic/stage 1.0.0-beta.7 → 1.0.0-beta.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.
package/src/StageCore.ts CHANGED
@@ -18,16 +18,22 @@
18
18
 
19
19
  import { EventEmitter } from 'events';
20
20
 
21
- import { GuidesEvents } from '@scena/guides';
22
-
23
21
  import { Id } from '@tmagic/schema';
24
22
 
25
- import { DEFAULT_ZOOM } from './const';
23
+ import { DEFAULT_ZOOM, GHOST_EL_ID_PREFIX } from './const';
26
24
  import StageDragResize from './StageDragResize';
27
25
  import StageMask from './StageMask';
28
26
  import StageRender from './StageRender';
29
- import { RemoveData, Runtime, SortEventData, StageCoreConfig, UpdateData, UpdateEventData } from './types';
30
- import { isFixed } from './util';
27
+ import {
28
+ CanSelect,
29
+ GuidesEventData,
30
+ RemoveData,
31
+ Runtime,
32
+ SortEventData,
33
+ StageCoreConfig,
34
+ UpdateData,
35
+ UpdateEventData,
36
+ } from './types';
31
37
 
32
38
  export default class StageCore extends EventEmitter {
33
39
  public selectedDom: Element | undefined;
@@ -37,6 +43,7 @@ export default class StageCore extends EventEmitter {
37
43
  public dr: StageDragResize;
38
44
  public config: StageCoreConfig;
39
45
  public zoom = DEFAULT_ZOOM;
46
+ private canSelect: CanSelect;
40
47
 
41
48
  constructor(config: StageCoreConfig) {
42
49
  super();
@@ -44,6 +51,7 @@ export default class StageCore extends EventEmitter {
44
51
  this.config = config;
45
52
 
46
53
  this.setZoom(config.zoom);
54
+ this.canSelect = config.canSelect || ((el: HTMLElement) => !!el.id);
47
55
 
48
56
  this.renderer = new StageRender({ core: this });
49
57
  this.mask = new StageMask({ core: this });
@@ -52,23 +60,61 @@ export default class StageCore extends EventEmitter {
52
60
  this.renderer.on('runtime-ready', (runtime: Runtime) => this.emit('runtime-ready', runtime));
53
61
  this.renderer.on('page-el-update', (el: HTMLElement) => this.mask?.observe(el));
54
62
 
55
- this.mask.on('select', async (el: Element) => {
56
- await this.dr?.select(el as HTMLElement);
57
- });
58
- this.mask.on('selected', (el: Element) => {
59
- this.select(el as HTMLElement);
60
- this.emit('select', el);
61
- });
63
+ this.mask
64
+ .on('beforeSelect', (event: MouseEvent) => {
65
+ this.setElementFromPoint(event);
66
+ })
67
+ .on('select', () => {
68
+ this.emit('select', this.selectedDom);
69
+ })
70
+ .on('changeGuides', (data: GuidesEventData) => {
71
+ this.dr.setGuidelines(data.type, data.guides);
72
+ this.emit('changeGuides', data);
73
+ });
74
+
75
+ // 要先触发select,在触发update
76
+ this.dr
77
+ .on('update', (data: UpdateEventData) => {
78
+ setTimeout(() => this.emit('update', data));
79
+ })
80
+ .on('sort', (data: UpdateEventData) => {
81
+ setTimeout(() => this.emit('sort', data));
82
+ });
83
+ }
84
+
85
+ public async setElementFromPoint(event: MouseEvent) {
86
+ const { renderer, zoom } = this;
87
+
88
+ const doc = renderer.contentWindow?.document;
89
+ let x = event.clientX;
90
+ let y = event.clientY;
62
91
 
63
- this.dr.on('update', (data: UpdateEventData) => this.emit('update', data));
64
- this.dr.on('sort', (data: UpdateEventData) => this.emit('sort', data));
92
+ if (renderer.iframe) {
93
+ const rect = renderer.iframe.getClientRects()[0];
94
+ if (rect) {
95
+ x = x - rect.left;
96
+ y = y - rect.top;
97
+ }
98
+ }
99
+
100
+ const els = doc?.elementsFromPoint(x / zoom, y / zoom) as HTMLElement[];
101
+
102
+ let stopped = false;
103
+ const stop = () => (stopped = true);
104
+ for (const el of els) {
105
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.canSelect(el, stop))) {
106
+ if (stopped) break;
107
+ this.select(el, event);
108
+ break;
109
+ }
110
+ }
65
111
  }
66
112
 
67
113
  /**
68
114
  * 选中组件
69
115
  * @param idOrEl 组件Dom节点的id属性,或者Dom节点
70
116
  */
71
- public async select(idOrEl: Id | HTMLElement): Promise<void> {
117
+ public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
72
118
  let el;
73
119
  if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
74
120
  const runtime = await this.renderer?.getRuntime();
@@ -78,15 +124,22 @@ export default class StageCore extends EventEmitter {
78
124
  el = idOrEl;
79
125
  }
80
126
 
81
- if (this.selectedDom === el) return;
127
+ if (el === this.selectedDom) return;
82
128
 
83
- await this.beforeSelect(el);
129
+ const runtime = await this.renderer?.getRuntime();
130
+ if (runtime?.beforeSelect) {
131
+ await runtime.beforeSelect(el);
132
+ }
84
133
 
134
+ this.mask.setLayout(el);
135
+ this.dr?.select(el, event);
85
136
  this.selectedDom = el;
86
- this.setMaskLayout(el);
87
- this.dr?.select(el);
88
137
  }
89
138
 
139
+ /**
140
+ * 更新选中的节点
141
+ * @param data 更新的数据
142
+ */
90
143
  public update(data: UpdateData): void {
91
144
  const { config } = data;
92
145
 
@@ -97,9 +150,9 @@ export default class StageCore extends EventEmitter {
97
150
  const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
98
151
  if (el) {
99
152
  // 更新了组件的布局,需要重新设置mask是否可以滚动
100
- this.setMaskLayout(el);
153
+ this.mask.setLayout(el);
154
+ this.dr?.select(el);
101
155
  }
102
- this.dr?.refresh();
103
156
  }, 0);
104
157
  });
105
158
  }
@@ -130,12 +183,6 @@ export default class StageCore extends EventEmitter {
130
183
  renderer.mount(el);
131
184
  mask.mount(el);
132
185
 
133
- const { wrapper: maskWrapper, hGuides, vGuides } = mask;
134
-
135
- maskWrapper.addEventListener('scroll', this.maskScrollHandler);
136
- hGuides.on('changeGuides', this.hGuidesChangeGuidesHandler);
137
- vGuides.on('changeGuides', this.vGuidesChangeGuidesHandler);
138
-
139
186
  this.emit('mounted');
140
187
  }
141
188
 
@@ -144,8 +191,7 @@ export default class StageCore extends EventEmitter {
144
191
  */
145
192
  public clearGuides() {
146
193
  this.mask.clearGuides();
147
- this.dr.setHGuidelines([]);
148
- this.dr.setVGuidelines([]);
194
+ this.dr.clearGuides();
149
195
  }
150
196
 
151
197
  /**
@@ -153,69 +199,11 @@ export default class StageCore extends EventEmitter {
153
199
  */
154
200
  public destroy(): void {
155
201
  const { mask, renderer, dr } = this;
156
- const { wrapper: maskWrapper, hGuides, vGuides } = mask;
157
202
 
158
203
  renderer.destroy();
159
204
  mask.destroy();
160
205
  dr.destroy();
161
- maskWrapper.removeEventListener('scroll', this.maskScrollHandler);
162
- hGuides.off('changeGuides', this.hGuidesChangeGuidesHandler);
163
- vGuides.off('changeGuides', this.vGuidesChangeGuidesHandler);
164
- this.removeAllListeners();
165
- }
166
206
 
167
- private maskScrollHandler = (event: Event) => {
168
- const { mask, renderer } = this;
169
- const { wrapper: maskWrapper, hGuides, vGuides } = mask;
170
- const { scrollTop } = maskWrapper;
171
-
172
- renderer?.contentWindow?.document.documentElement.scrollTo({ top: scrollTop });
173
-
174
- hGuides.scrollGuides(scrollTop);
175
- hGuides.scroll(0);
176
-
177
- vGuides.scrollGuides(0);
178
- vGuides.scroll(scrollTop);
179
-
180
- this.emit('scroll', event);
181
- };
182
-
183
- private hGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
184
- this.dr.setHGuidelines(e.guides);
185
- this.emit('changeGuides');
186
- };
187
-
188
- private vGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
189
- this.dr.setVGuidelines(e.guides);
190
- this.emit('changeGuides');
191
- };
192
-
193
- private setMaskLayout(el: HTMLElement): void {
194
- let fixed = false;
195
- let dom = el;
196
- while (dom) {
197
- fixed = isFixed(dom);
198
- if (fixed) {
199
- break;
200
- }
201
- const { parentElement } = dom;
202
- if (!parentElement || parentElement.tagName === 'BODY') {
203
- break;
204
- }
205
- dom = parentElement;
206
- }
207
-
208
- if (fixed) {
209
- this.mask?.setFixed();
210
- } else {
211
- this.mask?.setAbsolute();
212
- }
213
- }
214
-
215
- private async beforeSelect(el: HTMLElement): Promise<void> {
216
- const runtime = await this.renderer?.getRuntime();
217
- if (runtime?.beforeSelect) {
218
- await runtime.beforeSelect(el);
219
- }
207
+ this.removeAllListeners();
220
208
  }
221
209
  }