@tmagic/stage 1.0.0-beta.6 → 1.0.0-beta.9
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/LICENSE +5432 -0
- package/dist/tmagic-stage.es.js +582 -17579
- package/dist/tmagic-stage.es.js.map +1 -1
- package/dist/tmagic-stage.umd.js +587 -17586
- package/dist/tmagic-stage.umd.js.map +1 -1
- package/dist/types/src/Rule.d.ts +32 -0
- package/dist/types/src/StageCore.d.ts +17 -6
- package/dist/types/src/StageDragResize.d.ts +13 -9
- package/dist/types/src/StageHighlight.d.ts +25 -0
- package/dist/types/src/StageMask.d.ts +22 -36
- package/dist/types/src/StageRender.d.ts +1 -0
- package/dist/types/src/const.d.ts +19 -0
- package/dist/types/src/types.d.ts +8 -8
- package/dist/types/src/util.d.ts +9 -5
- package/package.json +7 -5
- package/src/Rule.ts +150 -0
- package/src/StageCore.ts +127 -93
- package/src/StageDragResize.ts +174 -163
- package/src/StageHighlight.ts +67 -0
- package/src/StageMask.ts +136 -184
- package/src/StageRender.ts +25 -9
- package/src/const.ts +24 -0
- package/src/types.ts +26 -26
- package/src/util.ts +57 -7
package/src/StageDragResize.ts
CHANGED
|
@@ -21,11 +21,12 @@ import { EventEmitter } from 'events';
|
|
|
21
21
|
|
|
22
22
|
import type { MoveableOptions } from 'moveable';
|
|
23
23
|
import Moveable from 'moveable';
|
|
24
|
+
import MoveableHelper from 'moveable-helper';
|
|
24
25
|
|
|
25
|
-
import { GHOST_EL_ID_PREFIX } from './const';
|
|
26
|
+
import { GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
|
|
26
27
|
import StageCore from './StageCore';
|
|
27
28
|
import type { SortEventData, StageDragResizeConfig } from './types';
|
|
28
|
-
import { getAbsolutePosition, getMode, getOffset
|
|
29
|
+
import { getAbsolutePosition, getMode, getOffset } from './util';
|
|
29
30
|
|
|
30
31
|
enum ActionStatus {
|
|
31
32
|
START = 'start',
|
|
@@ -40,95 +41,110 @@ export default class StageDragResize extends EventEmitter {
|
|
|
40
41
|
public core: StageCore;
|
|
41
42
|
public container: HTMLElement;
|
|
42
43
|
public target?: HTMLElement;
|
|
44
|
+
public dragEl?: HTMLElement;
|
|
43
45
|
public moveable?: Moveable;
|
|
44
46
|
public horizontalGuidelines: number[] = [];
|
|
45
47
|
public verticalGuidelines: number[] = [];
|
|
46
48
|
|
|
49
|
+
private moveableOptions: MoveableOptions = {};
|
|
47
50
|
private dragStatus: ActionStatus = ActionStatus.END;
|
|
48
|
-
private elObserver?: ResizeObserver;
|
|
49
51
|
private ghostEl: HTMLElement | undefined;
|
|
50
52
|
private mode: Mode = Mode.ABSOLUTE;
|
|
53
|
+
private moveableHelper?: MoveableHelper;
|
|
51
54
|
|
|
52
55
|
constructor(config: StageDragResizeConfig) {
|
|
53
56
|
super();
|
|
54
57
|
|
|
55
58
|
this.core = config.core;
|
|
56
59
|
this.container = config.container;
|
|
57
|
-
this.initObserver();
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
63
|
* 将选中框渲染并覆盖到选中的组件Dom节点上方
|
|
64
|
+
* 当选中的节点是不是absolute时,会创建一个新的节点出来作为拖拽目标
|
|
62
65
|
* @param el 选中组件的Dom节点元素
|
|
66
|
+
* @param event 鼠标事件
|
|
63
67
|
*/
|
|
64
|
-
public async select(el: HTMLElement): Promise<void> {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
public async select(el: HTMLElement, event?: MouseEvent): Promise<void> {
|
|
69
|
+
this.target = el;
|
|
70
|
+
// 如果有滚动条会导致resize时获取到width,height不准确
|
|
71
|
+
if (/(auto|scroll)/.test(this.target.style.overflow)) {
|
|
72
|
+
this.target.style.overflow = 'hidden';
|
|
68
73
|
}
|
|
74
|
+
this.mode = getMode(el);
|
|
75
|
+
this.destroyDragEl();
|
|
76
|
+
this.destroyGhostEl();
|
|
69
77
|
|
|
70
|
-
this.
|
|
78
|
+
this.dragEl = this.generateDragEl(el);
|
|
71
79
|
|
|
72
|
-
this.
|
|
73
|
-
|
|
80
|
+
this.moveableOptions = await this.getOptions({
|
|
81
|
+
target: this.dragEl || this.target,
|
|
82
|
+
});
|
|
74
83
|
|
|
75
|
-
|
|
84
|
+
this.moveableHelper = MoveableHelper.create({
|
|
85
|
+
useBeforeRender: true,
|
|
86
|
+
useRender: false,
|
|
87
|
+
createAuto: true,
|
|
88
|
+
});
|
|
76
89
|
|
|
77
|
-
this.
|
|
78
|
-
this.bindResizeEvent();
|
|
79
|
-
this.bindDragEvent();
|
|
90
|
+
this.initMoveable();
|
|
80
91
|
|
|
81
|
-
|
|
92
|
+
if (event) {
|
|
93
|
+
this.moveable?.dragStart(event);
|
|
94
|
+
}
|
|
82
95
|
}
|
|
83
96
|
|
|
84
97
|
/**
|
|
85
98
|
* 初始化选中框并渲染出来
|
|
86
|
-
* @param param0
|
|
87
99
|
*/
|
|
88
|
-
|
|
89
100
|
public async refresh() {
|
|
101
|
+
if (!this.moveable) throw new Error('未初始化moveable');
|
|
102
|
+
|
|
90
103
|
const options = await this.getOptions();
|
|
91
104
|
Object.entries(options).forEach(([key, value]) => {
|
|
92
105
|
(this.moveable as any)[key] = value;
|
|
93
106
|
});
|
|
94
|
-
this.
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
public async setVGuidelines(verticalGuidelines: number[]): Promise<void> {
|
|
98
|
-
this.verticalGuidelines = verticalGuidelines;
|
|
99
|
-
this.target && (await this.select(this.target));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
public async setHGuidelines(horizontalGuidelines: number[]): Promise<void> {
|
|
103
|
-
this.horizontalGuidelines = horizontalGuidelines;
|
|
104
|
-
this.target && (await this.select(this.target));
|
|
107
|
+
this.moveable.updateTarget();
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
public
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
this.
|
|
110
|
+
public setGuidelines(type: GuidesType, guidelines: number[]): void {
|
|
111
|
+
if (type === GuidesType.HORIZONTAL) {
|
|
112
|
+
this.horizontalGuidelines = guidelines;
|
|
113
|
+
} else if (type === GuidesType.VERTICAL) {
|
|
114
|
+
this.verticalGuidelines = guidelines;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
+
this.refresh();
|
|
118
|
+
}
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
public clearGuides() {
|
|
121
|
+
this.verticalGuidelines = [];
|
|
122
|
+
this.horizontalGuidelines = [];
|
|
123
|
+
this.refresh();
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
/**
|
|
122
127
|
* 销毁实例
|
|
123
128
|
*/
|
|
124
129
|
public destroy(): void {
|
|
125
|
-
this.destroyGhostEl();
|
|
126
130
|
this.moveable?.destroy();
|
|
131
|
+
this.destroyGhostEl();
|
|
132
|
+
this.destroyDragEl();
|
|
127
133
|
this.dragStatus = ActionStatus.END;
|
|
128
|
-
this.elObserver?.disconnect();
|
|
129
134
|
this.removeAllListeners();
|
|
130
135
|
}
|
|
131
136
|
|
|
137
|
+
private initMoveable() {
|
|
138
|
+
this.moveable?.destroy();
|
|
139
|
+
|
|
140
|
+
this.moveable = new Moveable(this.container, {
|
|
141
|
+
...this.moveableOptions,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
this.bindResizeEvent();
|
|
145
|
+
this.bindDragEvent();
|
|
146
|
+
}
|
|
147
|
+
|
|
132
148
|
private bindResizeEvent(): void {
|
|
133
149
|
if (!this.moveable) throw new Error('moveable 为初始化');
|
|
134
150
|
|
|
@@ -142,100 +158,94 @@ export default class StageDragResize extends EventEmitter {
|
|
|
142
158
|
const rect = this.moveable!.getRect();
|
|
143
159
|
const offset = getAbsolutePosition(e.target as HTMLElement, rect);
|
|
144
160
|
e.dragStart.set([offset.left, offset.top]);
|
|
145
|
-
|
|
146
|
-
if (this.ghostEl) {
|
|
147
|
-
this.destroyGhostEl();
|
|
148
|
-
this.updateMoveableTarget(this.target);
|
|
149
|
-
}
|
|
150
161
|
}
|
|
151
162
|
})
|
|
152
|
-
.on('resize', ({
|
|
153
|
-
if (!this.moveable) return;
|
|
154
|
-
|
|
163
|
+
.on('resize', ({ width, height, drag }) => {
|
|
164
|
+
if (!this.moveable || !this.target || !this.dragEl) return;
|
|
165
|
+
|
|
155
166
|
const { beforeTranslate } = drag;
|
|
156
167
|
frame.translate = beforeTranslate;
|
|
157
168
|
this.dragStatus = ActionStatus.ING;
|
|
158
169
|
|
|
159
|
-
target.style.width = `${width}px`;
|
|
160
|
-
target.style.height = `${height}px`;
|
|
170
|
+
this.target.style.width = `${width}px`;
|
|
171
|
+
this.target.style.height = `${height}px`;
|
|
172
|
+
this.dragEl.style.width = `${width}px`;
|
|
173
|
+
this.dragEl.style.height = `${height}px`;
|
|
161
174
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
// 流式布局
|
|
176
|
+
if (this.mode === Mode.SORTABLE) {
|
|
177
|
+
this.dragEl.style.top = `${beforeTranslate[1]}px`;
|
|
178
|
+
this.target.style.top = `0px`;
|
|
179
|
+
return;
|
|
165
180
|
}
|
|
181
|
+
|
|
182
|
+
this.dragEl.style.left = `${beforeTranslate[0]}px`;
|
|
183
|
+
this.dragEl.style.top = `${beforeTranslate[1]}px`;
|
|
184
|
+
|
|
185
|
+
const offset = getAbsolutePosition(this.target, { left: beforeTranslate[0], top: beforeTranslate[1] });
|
|
186
|
+
|
|
187
|
+
this.target.style.left = `${offset.left}px`;
|
|
188
|
+
this.target.style.top = `${offset.top}px`;
|
|
166
189
|
})
|
|
167
|
-
.on('resizeEnd', (
|
|
190
|
+
.on('resizeEnd', () => {
|
|
168
191
|
this.dragStatus = ActionStatus.END;
|
|
169
|
-
|
|
170
|
-
const rect = this.moveable!.getRect();
|
|
171
|
-
const offset = getAbsolutePosition(target as HTMLElement, rect);
|
|
172
|
-
|
|
173
|
-
this.updateMoveableTarget(this.target);
|
|
174
|
-
|
|
175
|
-
this.emit('update', {
|
|
176
|
-
el: this.target,
|
|
177
|
-
style: {
|
|
178
|
-
width: this.calcValueByFontsize(rect.width),
|
|
179
|
-
height: this.calcValueByFontsize(rect.height),
|
|
180
|
-
position: this.target?.style.position,
|
|
181
|
-
...(this.mode === Mode.SORTABLE
|
|
182
|
-
? {}
|
|
183
|
-
: {
|
|
184
|
-
left: this.calcValueByFontsize(offset.left),
|
|
185
|
-
top: this.calcValueByFontsize(offset.top),
|
|
186
|
-
}),
|
|
187
|
-
},
|
|
188
|
-
});
|
|
192
|
+
this.update(true);
|
|
189
193
|
});
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
private bindDragEvent(): void {
|
|
193
197
|
if (!this.moveable) throw new Error('moveable 为初始化');
|
|
194
198
|
|
|
199
|
+
let offset = {
|
|
200
|
+
left: 0,
|
|
201
|
+
top: 0,
|
|
202
|
+
};
|
|
203
|
+
|
|
195
204
|
this.moveable
|
|
196
|
-
.on('dragStart', () => {
|
|
205
|
+
.on('dragStart', (e) => {
|
|
197
206
|
if (!this.target) throw new Error('未选中组件');
|
|
198
207
|
|
|
199
208
|
this.dragStatus = ActionStatus.START;
|
|
200
209
|
|
|
210
|
+
this.moveableHelper?.onDragStart(e);
|
|
211
|
+
|
|
212
|
+
offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
|
|
213
|
+
|
|
201
214
|
if (this.mode === Mode.SORTABLE) {
|
|
202
215
|
this.ghostEl = this.generateGhostEl(this.target);
|
|
203
|
-
this.updateMoveableTarget(this.ghostEl);
|
|
204
216
|
}
|
|
205
217
|
})
|
|
206
|
-
.on('drag', (
|
|
218
|
+
.on('drag', (e) => {
|
|
219
|
+
if (!this.target || !this.dragEl) return;
|
|
207
220
|
this.dragStatus = ActionStatus.ING;
|
|
208
|
-
|
|
221
|
+
|
|
222
|
+
const { left, top } = e;
|
|
223
|
+
|
|
224
|
+
// 流式布局
|
|
225
|
+
if (this.ghostEl) {
|
|
226
|
+
this.dragEl.style.top = `${top}px`;
|
|
227
|
+
this.ghostEl.style.top = `${top + offset.top}px`;
|
|
209
228
|
return;
|
|
210
229
|
}
|
|
211
230
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const offset = getAbsolutePosition(this.target, getOffset(this.target));
|
|
217
|
-
target.style.top = `${offset.top + top}px`;
|
|
218
|
-
}
|
|
231
|
+
this.moveableHelper?.onDrag(e);
|
|
232
|
+
|
|
233
|
+
this.target.style.left = `${left + offset.left}px`;
|
|
234
|
+
this.target.style.top = `${top + offset.top}px`;
|
|
219
235
|
})
|
|
220
236
|
.on('dragEnd', () => {
|
|
221
237
|
// 点击不拖动时会触发dragStart和dragEnd,但是不会有drag事件
|
|
222
|
-
if (this.dragStatus
|
|
223
|
-
|
|
238
|
+
if (this.dragStatus === ActionStatus.ING) {
|
|
239
|
+
switch (this.mode) {
|
|
240
|
+
case Mode.SORTABLE:
|
|
241
|
+
this.sort();
|
|
242
|
+
break;
|
|
243
|
+
default:
|
|
244
|
+
this.update();
|
|
245
|
+
}
|
|
224
246
|
}
|
|
225
247
|
|
|
226
|
-
if (!this.target) return;
|
|
227
|
-
|
|
228
248
|
this.dragStatus = ActionStatus.END;
|
|
229
|
-
this.updateMoveableTarget(this.target);
|
|
230
|
-
|
|
231
|
-
switch (this.mode) {
|
|
232
|
-
case Mode.SORTABLE:
|
|
233
|
-
this.sort();
|
|
234
|
-
break;
|
|
235
|
-
default:
|
|
236
|
-
this.drag();
|
|
237
|
-
}
|
|
238
|
-
|
|
239
249
|
this.destroyGhostEl();
|
|
240
250
|
});
|
|
241
251
|
}
|
|
@@ -246,10 +256,13 @@ export default class StageDragResize extends EventEmitter {
|
|
|
246
256
|
(await renderer.getRuntime())?.getSnapElements ||
|
|
247
257
|
(() => {
|
|
248
258
|
const doc = renderer.contentWindow?.document;
|
|
249
|
-
return
|
|
259
|
+
return doc ? Array.from(doc.querySelectorAll('[id]')) : [];
|
|
250
260
|
});
|
|
251
|
-
|
|
252
|
-
|
|
261
|
+
return (
|
|
262
|
+
getSnapElements(el)
|
|
263
|
+
// 排除掉当前组件本身
|
|
264
|
+
.filter((element) => element !== this.target && !this.target?.contains(element))
|
|
265
|
+
);
|
|
253
266
|
}
|
|
254
267
|
|
|
255
268
|
private sort(): void {
|
|
@@ -271,18 +284,19 @@ export default class StageDragResize extends EventEmitter {
|
|
|
271
284
|
}
|
|
272
285
|
}
|
|
273
286
|
|
|
274
|
-
private
|
|
287
|
+
private update(isResize = false): void {
|
|
275
288
|
const rect = this.moveable!.getRect();
|
|
276
|
-
const offset =
|
|
289
|
+
const offset =
|
|
290
|
+
this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : getAbsolutePosition(this.target as HTMLElement, rect);
|
|
291
|
+
|
|
292
|
+
const left = this.calcValueByFontsize(offset.left);
|
|
293
|
+
const top = this.calcValueByFontsize(offset.top);
|
|
294
|
+
const width = this.calcValueByFontsize(rect.width);
|
|
295
|
+
const height = this.calcValueByFontsize(rect.height);
|
|
277
296
|
|
|
278
297
|
this.emit('update', {
|
|
279
298
|
el: this.target,
|
|
280
|
-
style: {
|
|
281
|
-
left: this.calcValueByFontsize(this.mode === Mode.FIXED ? rect.left : offset.left),
|
|
282
|
-
top: this.calcValueByFontsize(this.mode === Mode.FIXED ? rect.top : offset.top),
|
|
283
|
-
width: this.calcValueByFontsize(rect.width),
|
|
284
|
-
height: this.calcValueByFontsize(rect.height),
|
|
285
|
-
},
|
|
299
|
+
style: isResize ? { left, top, width, height } : { left, top },
|
|
286
300
|
});
|
|
287
301
|
}
|
|
288
302
|
|
|
@@ -308,87 +322,84 @@ export default class StageDragResize extends EventEmitter {
|
|
|
308
322
|
this.ghostEl = undefined;
|
|
309
323
|
}
|
|
310
324
|
|
|
325
|
+
private generateDragEl(el: HTMLElement): HTMLElement {
|
|
326
|
+
if (this.dragEl) {
|
|
327
|
+
this.destroyDragEl();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const { width, height } = el.getBoundingClientRect();
|
|
331
|
+
const offset = getOffset(el);
|
|
332
|
+
const dragEl = globalThis.document.createElement('div');
|
|
333
|
+
dragEl.style.cssText = `
|
|
334
|
+
position: absolute;
|
|
335
|
+
left: ${offset.left}px;
|
|
336
|
+
top: ${offset.top}px;
|
|
337
|
+
width: ${width}px;
|
|
338
|
+
height: ${height}px;
|
|
339
|
+
`;
|
|
340
|
+
this.container.append(dragEl);
|
|
341
|
+
return dragEl;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private destroyDragEl(): void {
|
|
345
|
+
this.dragEl?.remove();
|
|
346
|
+
this.dragEl = undefined;
|
|
347
|
+
}
|
|
348
|
+
|
|
311
349
|
private async getOptions(options: MoveableOptions = {}): Promise<MoveableOptions> {
|
|
312
350
|
if (!this.target) return {};
|
|
313
351
|
|
|
314
|
-
const
|
|
315
|
-
const { config, renderer, mask } = this.core;
|
|
316
|
-
const { iframe } = renderer;
|
|
352
|
+
const isAbsolute = this.mode === Mode.ABSOLUTE;
|
|
317
353
|
|
|
318
|
-
let { moveableOptions = {} } = config;
|
|
354
|
+
let { moveableOptions = {} } = this.core.config;
|
|
319
355
|
|
|
320
356
|
if (typeof moveableOptions === 'function') {
|
|
321
357
|
moveableOptions = moveableOptions(this.core);
|
|
322
358
|
}
|
|
323
359
|
|
|
324
|
-
const boundsOptions = {
|
|
325
|
-
top: 0,
|
|
326
|
-
left: 0,
|
|
327
|
-
right: iframe?.clientWidth,
|
|
328
|
-
bottom: this.mode === Mode.FIXED ? iframe?.clientHeight : mask.page?.clientHeight,
|
|
329
|
-
...(moveableOptions.bounds || {}),
|
|
330
|
-
};
|
|
331
|
-
|
|
332
360
|
return {
|
|
333
|
-
target: this.target,
|
|
334
|
-
scrollable: true,
|
|
335
361
|
origin: true,
|
|
362
|
+
rootContainer: this.core.container,
|
|
336
363
|
zoom: 1,
|
|
337
|
-
dragArea:
|
|
364
|
+
dragArea: false,
|
|
338
365
|
draggable: true,
|
|
339
366
|
resizable: true,
|
|
340
|
-
snappable:
|
|
341
|
-
snapGap:
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
367
|
+
snappable: isAbsolute,
|
|
368
|
+
snapGap: isAbsolute,
|
|
369
|
+
snapThreshold: 5,
|
|
370
|
+
snapDigit: 0,
|
|
371
|
+
throttleDrag: 0,
|
|
372
|
+
isDisplaySnapDigit: isAbsolute,
|
|
373
|
+
snapDirections: {
|
|
374
|
+
top: isAbsolute,
|
|
375
|
+
right: isAbsolute,
|
|
376
|
+
bottom: isAbsolute,
|
|
377
|
+
left: isAbsolute,
|
|
378
|
+
center: isAbsolute,
|
|
379
|
+
middle: isAbsolute,
|
|
380
|
+
},
|
|
346
381
|
horizontalGuidelines: this.horizontalGuidelines,
|
|
347
382
|
verticalGuidelines: this.verticalGuidelines,
|
|
348
383
|
|
|
349
|
-
bounds:
|
|
384
|
+
bounds: {
|
|
385
|
+
top: 0,
|
|
386
|
+
left: 0,
|
|
387
|
+
right: this.container.clientWidth,
|
|
388
|
+
bottom: this.container.clientHeight,
|
|
389
|
+
...(moveableOptions.bounds || {}),
|
|
390
|
+
},
|
|
350
391
|
...options,
|
|
351
392
|
...moveableOptions,
|
|
352
393
|
};
|
|
353
394
|
}
|
|
354
395
|
|
|
355
|
-
private initObserver(): void {
|
|
356
|
-
if (typeof ResizeObserver === 'undefined') {
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
this.elObserver = new ResizeObserver(() => {
|
|
361
|
-
const doc = this.core.renderer.contentWindow?.document;
|
|
362
|
-
if (!doc || !this.target || !this.moveable) return;
|
|
363
|
-
|
|
364
|
-
/** 组件可能已经重新渲染了,所以需要重新获取新的dom */
|
|
365
|
-
const target = doc.getElementById(this.target.id);
|
|
366
|
-
|
|
367
|
-
if (this.ghostEl) {
|
|
368
|
-
this.destroyGhostEl();
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (target && target !== this.target) {
|
|
372
|
-
this.syncRect(target);
|
|
373
|
-
this.target = target;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
this.updateMoveableTarget(this.target);
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
private syncRect(el: HTMLElement): void {
|
|
381
|
-
this.elObserver?.disconnect();
|
|
382
|
-
this.elObserver?.observe(el);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
396
|
private calcValueByFontsize(value: number) {
|
|
386
397
|
const { contentWindow } = this.core.renderer;
|
|
387
398
|
const fontSize = contentWindow?.document.documentElement.style.fontSize;
|
|
388
399
|
|
|
389
400
|
if (fontSize) {
|
|
390
401
|
const times = globalThis.parseFloat(fontSize) / 100;
|
|
391
|
-
return value / times;
|
|
402
|
+
return (value / times).toFixed(2);
|
|
392
403
|
}
|
|
393
404
|
|
|
394
405
|
return value;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* eslint-disable no-param-reassign */
|
|
20
|
+
import { EventEmitter } from 'events';
|
|
21
|
+
|
|
22
|
+
import Moveable from 'moveable';
|
|
23
|
+
|
|
24
|
+
import StageCore from './StageCore';
|
|
25
|
+
import type { StageHighlightConfig } from './types';
|
|
26
|
+
export default class StageHighlight extends EventEmitter {
|
|
27
|
+
public core: StageCore;
|
|
28
|
+
public container: HTMLElement;
|
|
29
|
+
public target?: HTMLElement;
|
|
30
|
+
public moveable?: Moveable;
|
|
31
|
+
|
|
32
|
+
constructor(config: StageHighlightConfig) {
|
|
33
|
+
super();
|
|
34
|
+
|
|
35
|
+
this.core = config.core;
|
|
36
|
+
this.container = config.container;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 高亮鼠标悬停的组件
|
|
41
|
+
* @param el 选中组件的Dom节点元素
|
|
42
|
+
*/
|
|
43
|
+
public highlight(el: HTMLElement): void {
|
|
44
|
+
if (!el || el === this.target) return;
|
|
45
|
+
this.target = el;
|
|
46
|
+
this.moveable?.destroy();
|
|
47
|
+
this.moveable = new Moveable(this.container, {
|
|
48
|
+
target: this.target,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 清空高亮
|
|
54
|
+
*/
|
|
55
|
+
public clearHighlight(): void {
|
|
56
|
+
if (!this.moveable) return;
|
|
57
|
+
this.moveable.target = null;
|
|
58
|
+
this.moveable.updateTarget();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 销毁实例
|
|
63
|
+
*/
|
|
64
|
+
public destroy(): void {
|
|
65
|
+
this.moveable?.destroy();
|
|
66
|
+
}
|
|
67
|
+
}
|