@tmagic/stage 1.0.0-beta.5 → 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,24 +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, event: MouseEvent) => {
56
- await this.dr?.select(el as HTMLElement);
57
- setTimeout(() => this.dr?.moveable?.dragStart(event));
58
- });
59
- this.mask.on('selected', (el: Element) => {
60
- this.select(el as HTMLElement);
61
- this.emit('select', el);
62
- });
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;
63
91
 
64
- this.dr.on('update', (data: UpdateEventData) => this.emit('update', data));
65
- 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
+ }
66
111
  }
67
112
 
68
113
  /**
69
114
  * 选中组件
70
115
  * @param idOrEl 组件Dom节点的id属性,或者Dom节点
71
116
  */
72
- public async select(idOrEl: Id | HTMLElement): Promise<void> {
117
+ public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
73
118
  let el;
74
119
  if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
75
120
  const runtime = await this.renderer?.getRuntime();
@@ -79,15 +124,22 @@ export default class StageCore extends EventEmitter {
79
124
  el = idOrEl;
80
125
  }
81
126
 
82
- if (this.selectedDom === el) return;
127
+ if (el === this.selectedDom) return;
83
128
 
84
- await this.beforeSelect(el);
129
+ const runtime = await this.renderer?.getRuntime();
130
+ if (runtime?.beforeSelect) {
131
+ await runtime.beforeSelect(el);
132
+ }
85
133
 
134
+ this.mask.setLayout(el);
135
+ this.dr?.select(el, event);
86
136
  this.selectedDom = el;
87
- this.setMaskLayout(el);
88
- this.dr?.select(el);
89
137
  }
90
138
 
139
+ /**
140
+ * 更新选中的节点
141
+ * @param data 更新的数据
142
+ */
91
143
  public update(data: UpdateData): void {
92
144
  const { config } = data;
93
145
 
@@ -98,9 +150,9 @@ export default class StageCore extends EventEmitter {
98
150
  const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
99
151
  if (el) {
100
152
  // 更新了组件的布局,需要重新设置mask是否可以滚动
101
- this.setMaskLayout(el);
153
+ this.mask.setLayout(el);
154
+ this.dr?.select(el);
102
155
  }
103
- this.dr?.refresh();
104
156
  }, 0);
105
157
  });
106
158
  }
@@ -131,12 +183,6 @@ export default class StageCore extends EventEmitter {
131
183
  renderer.mount(el);
132
184
  mask.mount(el);
133
185
 
134
- const { wrapper: maskWrapper, hGuides, vGuides } = mask;
135
-
136
- maskWrapper.addEventListener('scroll', this.maskScrollHandler);
137
- hGuides.on('changeGuides', this.hGuidesChangeGuidesHandler);
138
- vGuides.on('changeGuides', this.vGuidesChangeGuidesHandler);
139
-
140
186
  this.emit('mounted');
141
187
  }
142
188
 
@@ -145,8 +191,7 @@ export default class StageCore extends EventEmitter {
145
191
  */
146
192
  public clearGuides() {
147
193
  this.mask.clearGuides();
148
- this.dr.setHGuidelines([]);
149
- this.dr.setVGuidelines([]);
194
+ this.dr.clearGuides();
150
195
  }
151
196
 
152
197
  /**
@@ -154,69 +199,11 @@ export default class StageCore extends EventEmitter {
154
199
  */
155
200
  public destroy(): void {
156
201
  const { mask, renderer, dr } = this;
157
- const { wrapper: maskWrapper, hGuides, vGuides } = mask;
158
202
 
159
203
  renderer.destroy();
160
204
  mask.destroy();
161
205
  dr.destroy();
162
- maskWrapper.removeEventListener('scroll', this.maskScrollHandler);
163
- hGuides.off('changeGuides', this.hGuidesChangeGuidesHandler);
164
- vGuides.off('changeGuides', this.vGuidesChangeGuidesHandler);
165
- this.removeAllListeners();
166
- }
167
206
 
168
- private maskScrollHandler = (event: Event) => {
169
- const { mask, renderer } = this;
170
- const { wrapper: maskWrapper, hGuides, vGuides } = mask;
171
- const { scrollTop } = maskWrapper;
172
-
173
- renderer?.contentWindow?.document.documentElement.scrollTo({ top: scrollTop });
174
-
175
- hGuides.scrollGuides(scrollTop);
176
- hGuides.scroll(0);
177
-
178
- vGuides.scrollGuides(0);
179
- vGuides.scroll(scrollTop);
180
-
181
- this.emit('scroll', event);
182
- };
183
-
184
- private hGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
185
- this.dr.setHGuidelines(e.guides);
186
- this.emit('changeGuides');
187
- };
188
-
189
- private vGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
190
- this.dr.setVGuidelines(e.guides);
191
- this.emit('changeGuides');
192
- };
193
-
194
- private setMaskLayout(el: HTMLElement): void {
195
- let fixed = false;
196
- let dom = el;
197
- while (dom) {
198
- fixed = isFixed(dom);
199
- if (fixed) {
200
- break;
201
- }
202
- const { parentElement } = dom;
203
- if (!parentElement || parentElement.tagName === 'BODY') {
204
- break;
205
- }
206
- dom = parentElement;
207
- }
208
-
209
- if (fixed) {
210
- this.mask?.setFixed();
211
- } else {
212
- this.mask?.setAbsolute();
213
- }
214
- }
215
-
216
- private async beforeSelect(el: HTMLElement): Promise<void> {
217
- const runtime = await this.renderer?.getRuntime();
218
- if (runtime?.beforeSelect) {
219
- await runtime.beforeSelect(el);
220
- }
207
+ this.removeAllListeners();
221
208
  }
222
209
  }