@tmagic/stage 1.1.0-beta.1 → 1.1.0-beta.12

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,22 +1,24 @@
1
1
  {
2
- "version": "1.1.0-beta.1",
2
+ "version": "1.1.0-beta.12",
3
3
  "name": "@tmagic/stage",
4
4
  "sideEffects": [
5
5
  "dist/*"
6
6
  ],
7
7
  "main": "dist/tmagic-stage.umd.js",
8
- "module": "dist/tmagic-stage.es.js",
9
- "types": "dist/types/src/index.d.ts",
8
+ "module": "dist/tmagic-stage.mjs",
9
+ "types": "types/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "import": "./dist/tmagic-stage.es.js",
12
+ "import": "./dist/tmagic-stage.mjs",
13
13
  "require": "./dist/tmagic-stage.umd.js"
14
14
  },
15
15
  "./*": "./*"
16
16
  },
17
17
  "license": "Apache-2.0",
18
18
  "scripts": {
19
- "build": "vite build"
19
+ "build": "npm run build:type && vite build",
20
+ "build:type": "npm run clear:type && tsc --declaration --emitDeclarationOnly --project tsconfig.build.json",
21
+ "clear:type": "rimraf ./types"
20
22
  },
21
23
  "engines": {
22
24
  "node": ">=14"
@@ -27,10 +29,11 @@
27
29
  },
28
30
  "dependencies": {
29
31
  "@scena/guides": "^0.17.0",
30
- "@tmagic/core": "1.1.0-beta.1",
31
- "@tmagic/schema": "1.1.0-beta.1",
32
- "@tmagic/utils": "1.1.0-beta.1",
32
+ "@tmagic/core": "1.1.0-beta.12",
33
+ "@tmagic/schema": "1.1.0-beta.12",
34
+ "@tmagic/utils": "1.1.0-beta.12",
33
35
  "events": "^3.3.0",
36
+ "keycon": "^1.1.2",
34
37
  "lodash-es": "^4.17.21",
35
38
  "moveable": "^0.30.0",
36
39
  "moveable-helper": "^0.4.0"
@@ -39,9 +42,9 @@
39
42
  "@types/events": "^3.0.0",
40
43
  "@types/lodash-es": "^4.17.4",
41
44
  "@types/node": "^15.12.4",
45
+ "rimraf": "^3.0.2",
42
46
  "sass": "^1.35.1",
43
- "typescript": "^4.3.4",
44
- "vite": "^2.9.13",
45
- "vite-plugin-dts": "^0.9.6"
47
+ "typescript": "^4.7.4",
48
+ "vite": "^3.0.4"
46
49
  }
47
50
  }
package/src/StageCore.ts CHANGED
@@ -19,20 +19,24 @@
19
19
  import { EventEmitter } from 'events';
20
20
 
21
21
  import type { Id } from '@tmagic/schema';
22
+ import { addClassName } from '@tmagic/utils';
22
23
 
23
- import { DEFAULT_ZOOM, GHOST_EL_ID_PREFIX } from './const';
24
+ import { CONTAINER_HIGHLIGHT_CLASS, DEFAULT_ZOOM, GHOST_EL_ID_PREFIX, PAGE_CLASS } from './const';
24
25
  import StageDragResize from './StageDragResize';
25
26
  import StageHighlight from './StageHighlight';
26
27
  import StageMask from './StageMask';
28
+ import StageMultiDragResize from './StageMultiDragResize';
27
29
  import StageRender from './StageRender';
28
30
  import {
29
31
  CanSelect,
32
+ ContainerHighlightType,
30
33
  GuidesEventData,
31
34
  IsContainer,
32
35
  RemoveData,
33
36
  Runtime,
34
37
  SortEventData,
35
38
  StageCoreConfig,
39
+ StageDragStatus,
36
40
  UpdateData,
37
41
  UpdateEventData,
38
42
  } from './types';
@@ -40,17 +44,21 @@ import { addSelectedClassName, removeSelectedClassName } from './util';
40
44
 
41
45
  export default class StageCore extends EventEmitter {
42
46
  public container?: HTMLDivElement;
43
-
44
- public selectedDom: Element | undefined;
47
+ // 当前选中的节点
48
+ public selectedDom: HTMLElement | undefined;
49
+ // 多选选中的节点组
50
+ public selectedDomList: HTMLElement[] = [];
45
51
  public highlightedDom: Element | undefined;
46
52
  public renderer: StageRender;
47
53
  public mask: StageMask;
48
54
  public dr: StageDragResize;
55
+ public multiDr: StageMultiDragResize;
49
56
  public highlightLayer: StageHighlight;
50
57
  public config: StageCoreConfig;
51
58
  public zoom = DEFAULT_ZOOM;
52
59
  public containerHighlightClassName: string;
53
60
  public containerHighlightDuration: number;
61
+ public containerHighlightType?: ContainerHighlightType;
54
62
  public isContainer: IsContainer;
55
63
 
56
64
  private canSelect: CanSelect;
@@ -63,12 +71,14 @@ export default class StageCore extends EventEmitter {
63
71
  this.setZoom(config.zoom);
64
72
  this.canSelect = config.canSelect || ((el: HTMLElement) => !!el.id);
65
73
  this.isContainer = config.isContainer;
66
- this.containerHighlightClassName = config.containerHighlightClassName;
67
- this.containerHighlightDuration = config.containerHighlightDuration;
74
+ this.containerHighlightClassName = config.containerHighlightClassName || CONTAINER_HIGHLIGHT_CLASS;
75
+ this.containerHighlightDuration = config.containerHighlightDuration || 800;
76
+ this.containerHighlightType = config.containerHighlightType;
68
77
 
69
78
  this.renderer = new StageRender({ core: this });
70
79
  this.mask = new StageMask({ core: this });
71
- this.dr = new StageDragResize({ core: this, container: this.mask.content });
80
+ this.dr = new StageDragResize({ core: this, container: this.mask.content, mask: this.mask });
81
+ this.multiDr = new StageMultiDragResize({ core: this, container: this.mask.content, mask: this.mask });
72
82
  this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
73
83
 
74
84
  this.renderer.on('runtime-ready', (runtime: Runtime) => {
@@ -79,8 +89,11 @@ export default class StageCore extends EventEmitter {
79
89
  });
80
90
 
81
91
  this.mask
82
- .on('beforeSelect', (event: MouseEvent) => {
83
- this.setElementFromPoint(event);
92
+ .on('beforeSelect', async (event: MouseEvent) => {
93
+ this.clearSelectStatus('multiSelect');
94
+ const el = await this.getElementFromPoint(event);
95
+ if (!el) return;
96
+ this.select(el, event);
84
97
  })
85
98
  .on('select', () => {
86
99
  this.emit('select', this.selectedDom);
@@ -90,16 +103,37 @@ export default class StageCore extends EventEmitter {
90
103
  this.emit('changeGuides', data);
91
104
  })
92
105
  .on('highlight', async (event: MouseEvent) => {
93
- await this.setElementFromPoint(event);
106
+ const el = await this.getElementFromPoint(event);
107
+ if (!el) return;
108
+ // 如果多选组件处于拖拽状态时不进行组件高亮
109
+ if (this.multiDr.dragStatus === StageDragStatus.ING) return;
110
+ await this.highlight(el);
94
111
  if (this.highlightedDom === this.selectedDom) {
95
112
  this.highlightLayer.clearHighlight();
96
113
  return;
97
114
  }
98
- this.highlightLayer.highlight(this.highlightedDom as HTMLElement);
99
115
  this.emit('highlight', this.highlightedDom);
100
116
  })
101
117
  .on('clearHighlight', async () => {
102
118
  this.highlightLayer.clearHighlight();
119
+ })
120
+ .on('beforeMultiSelect', async (event: MouseEvent) => {
121
+ const el = await this.getElementFromPoint(event);
122
+ if (!el) return;
123
+ // 如果已有单选选中元素,不是magic-ui-page就可以加入多选列表
124
+ if (this.selectedDom && !this.selectedDom.className.includes(PAGE_CLASS)) {
125
+ this.selectedDomList.push(this.selectedDom as HTMLElement);
126
+ this.selectedDom = undefined;
127
+ }
128
+ // 判断元素是否已在多选列表
129
+ const existIndex = this.selectedDomList.findIndex((selectedDom) => selectedDom.id === el.id);
130
+ if (existIndex !== -1) {
131
+ // 再次点击取消选中
132
+ this.selectedDomList.splice(existIndex, 1);
133
+ } else {
134
+ this.selectedDomList.push(el);
135
+ }
136
+ this.multiSelect(this.selectedDomList);
103
137
  });
104
138
 
105
139
  // 要先触发select,在触发update
@@ -110,6 +144,16 @@ export default class StageCore extends EventEmitter {
110
144
  .on('sort', (data: UpdateEventData) => {
111
145
  setTimeout(() => this.emit('sort', data));
112
146
  });
147
+
148
+ this.multiDr
149
+ .on('update', (data: UpdateEventData) => {
150
+ setTimeout(() => this.emit('update', data));
151
+ })
152
+ .on('select', async (id: Id) => {
153
+ const el = await this.getTargetElement(id);
154
+ this.select(el); // 选中
155
+ setTimeout(() => this.emit('select', el)); // set node
156
+ });
113
157
  }
114
158
 
115
159
  public getElementsFromPoint(event: MouseEvent) {
@@ -130,29 +174,35 @@ export default class StageCore extends EventEmitter {
130
174
  return doc?.elementsFromPoint(x / zoom, y / zoom) as HTMLElement[];
131
175
  }
132
176
 
133
- public async setElementFromPoint(event: MouseEvent) {
177
+ public async getElementFromPoint(event: MouseEvent) {
134
178
  const els = this.getElementsFromPoint(event);
135
-
136
179
  let stopped = false;
137
180
  const stop = () => (stopped = true);
138
181
  for (const el of els) {
139
- if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.canSelect(el, event, stop))) {
182
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.isElCanSelect(el, event, stop))) {
140
183
  if (stopped) break;
141
- if (event.type === 'mousemove') {
142
- this.highlight(el);
143
- break;
144
- }
145
- this.select(el, event);
146
- break;
184
+ return el;
147
185
  }
148
186
  }
149
187
  }
150
188
 
189
+ public async isElCanSelect(el: HTMLElement, event: MouseEvent, stop: () => boolean): Promise<Boolean> {
190
+ // 执行业务方传入的判断逻辑
191
+ const canSelectByProp = await this.canSelect(el, event, stop);
192
+ if (!canSelectByProp) return false;
193
+ // 多选规则
194
+ if (this.mask.isMultiSelectStatus) {
195
+ return this.multiDr.canSelect(el, stop);
196
+ }
197
+ return true;
198
+ }
199
+
151
200
  /**
152
201
  * 选中组件
153
202
  * @param idOrEl 组件Dom节点的id属性,或者Dom节点
154
203
  */
155
204
  public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
205
+ this.clearSelectStatus('multiSelect');
156
206
  const el = await this.getTargetElement(idOrEl);
157
207
 
158
208
  if (el === this.selectedDom) return;
@@ -182,6 +232,17 @@ export default class StageCore extends EventEmitter {
182
232
  }
183
233
  }
184
234
 
235
+ /**
236
+ * 多选
237
+ * @param domList 多选节点
238
+ */
239
+ public async multiSelect(idOrElList: HTMLElement[] | Id[]): Promise<void> {
240
+ this.clearSelectStatus('select');
241
+ const elList = await Promise.all(idOrElList.map(async (idOrEl) => await this.getTargetElement(idOrEl)));
242
+ this.multiDr.multiSelect(elList);
243
+ this.emit('multiSelect', elList);
244
+ }
245
+
185
246
  /**
186
247
  * 更新选中的节点
187
248
  * @param data 更新的数据
@@ -217,7 +278,7 @@ export default class StageCore extends EventEmitter {
217
278
  this.highlightLayer.clearHighlight();
218
279
  return;
219
280
  }
220
- if (el === this.highlightedDom) return;
281
+ if (el === this.highlightedDom || !el) return;
221
282
  this.highlightLayer.highlight(el);
222
283
  this.highlightedDom = el;
223
284
  }
@@ -238,6 +299,19 @@ export default class StageCore extends EventEmitter {
238
299
  this.zoom = zoom;
239
300
  }
240
301
 
302
+ /**
303
+ * 用于在切换选择模式时清除上一次的状态
304
+ * @param selectType 需要清理的选择模式 多选:multiSelect,单选:select
305
+ */
306
+ public clearSelectStatus(selectType: String) {
307
+ if (selectType === 'multiSelect') {
308
+ this.multiDr.clearSelectStatus();
309
+ this.selectedDomList = [];
310
+ } else {
311
+ this.dr.clearSelectStatus();
312
+ }
313
+ }
314
+
241
315
  /**
242
316
  * 挂载Dom节点
243
317
  * @param el 将stage挂载到该Dom节点上
@@ -260,6 +334,27 @@ export default class StageCore extends EventEmitter {
260
334
  this.dr.clearGuides();
261
335
  }
262
336
 
337
+ public async addContainerHighlightClassName(event: MouseEvent, exclude: Element[]) {
338
+ const els = this.getElementsFromPoint(event);
339
+ const { renderer } = this;
340
+ const doc = renderer.contentWindow?.document;
341
+
342
+ if (!doc) return;
343
+
344
+ for (const el of els) {
345
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.isContainer(el)) && !exclude.includes(el)) {
346
+ addClassName(el, doc, this.containerHighlightClassName);
347
+ break;
348
+ }
349
+ }
350
+ }
351
+
352
+ public getAddContainerHighlightClassNameTimeout(event: MouseEvent, exclude: Element[] = []): NodeJS.Timeout {
353
+ return globalThis.setTimeout(() => {
354
+ this.addContainerHighlightClassName(event, exclude);
355
+ }, this.containerHighlightDuration);
356
+ }
357
+
263
358
  /**
264
359
  * 销毁实例
265
360
  */