@tmagic/stage 1.0.6 → 1.1.0-beta.10

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.0.6",
2
+ "version": "1.1.0-beta.10",
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.0.6",
31
- "@tmagic/schema": "1.0.6",
32
- "@tmagic/utils": "1.0.6",
32
+ "@tmagic/core": "1.1.0-beta.10",
33
+ "@tmagic/schema": "1.1.0-beta.10",
34
+ "@tmagic/utils": "1.1.0-beta.10",
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,35 +19,48 @@
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,
34
+ IsContainer,
31
35
  RemoveData,
32
36
  Runtime,
33
37
  SortEventData,
34
38
  StageCoreConfig,
39
+ StageDragStatus,
35
40
  UpdateData,
36
41
  UpdateEventData,
37
42
  } from './types';
38
43
  import { addSelectedClassName, removeSelectedClassName } from './util';
39
44
 
40
45
  export default class StageCore extends EventEmitter {
41
- public selectedDom: Element | undefined;
46
+ public container?: HTMLDivElement;
47
+ // 当前选中的节点
48
+ public selectedDom: HTMLElement | undefined;
49
+ // 多选选中的节点组
50
+ public selectedDomList: HTMLElement[] = [];
42
51
  public highlightedDom: Element | undefined;
43
-
44
52
  public renderer: StageRender;
45
53
  public mask: StageMask;
46
54
  public dr: StageDragResize;
55
+ public multiDr: StageMultiDragResize;
47
56
  public highlightLayer: StageHighlight;
48
57
  public config: StageCoreConfig;
49
58
  public zoom = DEFAULT_ZOOM;
50
- public container?: HTMLDivElement;
59
+ public containerHighlightClassName: string;
60
+ public containerHighlightDuration: number;
61
+ public containerHighlightType?: ContainerHighlightType;
62
+ public isContainer: IsContainer;
63
+
51
64
  private canSelect: CanSelect;
52
65
 
53
66
  constructor(config: StageCoreConfig) {
@@ -57,10 +70,15 @@ export default class StageCore extends EventEmitter {
57
70
 
58
71
  this.setZoom(config.zoom);
59
72
  this.canSelect = config.canSelect || ((el: HTMLElement) => !!el.id);
73
+ this.isContainer = config.isContainer;
74
+ this.containerHighlightClassName = config.containerHighlightClassName || CONTAINER_HIGHLIGHT_CLASS;
75
+ this.containerHighlightDuration = config.containerHighlightDuration || 800;
76
+ this.containerHighlightType = config.containerHighlightType;
60
77
 
61
78
  this.renderer = new StageRender({ core: this });
62
79
  this.mask = new StageMask({ core: this });
63
- 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 });
64
82
  this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
65
83
 
66
84
  this.renderer.on('runtime-ready', (runtime: Runtime) => {
@@ -71,8 +89,11 @@ export default class StageCore extends EventEmitter {
71
89
  });
72
90
 
73
91
  this.mask
74
- .on('beforeSelect', (event: MouseEvent) => {
75
- 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);
76
97
  })
77
98
  .on('select', () => {
78
99
  this.emit('select', this.selectedDom);
@@ -82,16 +103,37 @@ export default class StageCore extends EventEmitter {
82
103
  this.emit('changeGuides', data);
83
104
  })
84
105
  .on('highlight', async (event: MouseEvent) => {
85
- 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);
86
111
  if (this.highlightedDom === this.selectedDom) {
87
112
  this.highlightLayer.clearHighlight();
88
113
  return;
89
114
  }
90
- this.highlightLayer.highlight(this.highlightedDom as HTMLElement);
91
115
  this.emit('highlight', this.highlightedDom);
92
116
  })
93
117
  .on('clearHighlight', async () => {
94
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);
95
137
  });
96
138
 
97
139
  // 要先触发select,在触发update
@@ -102,9 +144,19 @@ export default class StageCore extends EventEmitter {
102
144
  .on('sort', (data: UpdateEventData) => {
103
145
  setTimeout(() => this.emit('sort', data));
104
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
+ });
105
157
  }
106
158
 
107
- public async setElementFromPoint(event: MouseEvent) {
159
+ public getElementsFromPoint(event: MouseEvent) {
108
160
  const { renderer, zoom } = this;
109
161
 
110
162
  const doc = renderer.contentWindow?.document;
@@ -119,28 +171,38 @@ export default class StageCore extends EventEmitter {
119
171
  }
120
172
  }
121
173
 
122
- const els = doc?.elementsFromPoint(x / zoom, y / zoom) as HTMLElement[];
174
+ return doc?.elementsFromPoint(x / zoom, y / zoom) as HTMLElement[];
175
+ }
123
176
 
177
+ public async getElementFromPoint(event: MouseEvent) {
178
+ const els = this.getElementsFromPoint(event);
124
179
  let stopped = false;
125
180
  const stop = () => (stopped = true);
126
181
  for (const el of els) {
127
- 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))) {
128
183
  if (stopped) break;
129
- if (event.type === 'mousemove') {
130
- this.highlight(el);
131
- break;
132
- }
133
- this.select(el, event);
134
- break;
184
+ return el;
135
185
  }
136
186
  }
137
187
  }
138
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
+
139
200
  /**
140
201
  * 选中组件
141
202
  * @param idOrEl 组件Dom节点的id属性,或者Dom节点
142
203
  */
143
204
  public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
205
+ this.clearSelectStatus('multiSelect');
144
206
  const el = await this.getTargetElement(idOrEl);
145
207
 
146
208
  if (el === this.selectedDom) return;
@@ -170,6 +232,17 @@ export default class StageCore extends EventEmitter {
170
232
  }
171
233
  }
172
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
+
173
246
  /**
174
247
  * 更新选中的节点
175
248
  * @param data 更新的数据
@@ -205,7 +278,7 @@ export default class StageCore extends EventEmitter {
205
278
  this.highlightLayer.clearHighlight();
206
279
  return;
207
280
  }
208
- if (el === this.highlightedDom) return;
281
+ if (el === this.highlightedDom || !el) return;
209
282
  this.highlightLayer.highlight(el);
210
283
  this.highlightedDom = el;
211
284
  }
@@ -226,6 +299,19 @@ export default class StageCore extends EventEmitter {
226
299
  this.zoom = zoom;
227
300
  }
228
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
+
229
315
  /**
230
316
  * 挂载Dom节点
231
317
  * @param el 将stage挂载到该Dom节点上
@@ -248,6 +334,27 @@ export default class StageCore extends EventEmitter {
248
334
  this.dr.clearGuides();
249
335
  }
250
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
+
251
358
  /**
252
359
  * 销毁实例
253
360
  */