@tmagic/stage 1.0.0-beta.1 → 1.0.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/LICENSE +5432 -0
- package/README.md +1 -0
- package/dist/tmagic-stage.es.js +663 -17594
- package/dist/tmagic-stage.es.js.map +1 -1
- package/dist/tmagic-stage.umd.js +668 -17601
- 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 +21 -10
- package/dist/types/src/StageDragResize.d.ts +18 -12
- package/dist/types/src/StageHighlight.d.ts +25 -0
- package/dist/types/src/StageMask.d.ts +38 -35
- package/dist/types/src/StageRender.d.ts +1 -0
- package/dist/types/src/const.d.ts +20 -0
- package/dist/types/src/types.d.ts +10 -10
- package/dist/types/src/util.d.ts +9 -5
- package/package.json +12 -9
- package/src/Rule.ts +150 -0
- package/src/StageCore.ts +152 -104
- package/src/StageDragResize.ts +214 -182
- package/src/StageHighlight.ts +70 -0
- package/src/StageMask.ts +170 -166
- package/src/StageRender.ts +25 -9
- package/src/const.ts +26 -0
- package/src/types.ts +28 -28
- 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 { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
|
|
26
27
|
import StageCore from './StageCore';
|
|
27
|
-
import type { SortEventData, StageDragResizeConfig } from './types';
|
|
28
|
-
import { getAbsolutePosition, getMode, getOffset
|
|
28
|
+
import type { Offset, Runtime, SortEventData, StageDragResizeConfig } from './types';
|
|
29
|
+
import { getAbsolutePosition, getMode, getOffset } from './util';
|
|
29
30
|
|
|
30
31
|
enum ActionStatus {
|
|
31
32
|
START = 'start',
|
|
@@ -40,95 +41,133 @@ 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;
|
|
46
|
+
public horizontalGuidelines: number[] = [];
|
|
47
|
+
public verticalGuidelines: number[] = [];
|
|
48
|
+
public elementGuidelines: HTMLElement[] = [];
|
|
44
49
|
|
|
50
|
+
private moveableOptions: MoveableOptions = {};
|
|
45
51
|
private dragStatus: ActionStatus = ActionStatus.END;
|
|
46
|
-
private elObserver?: ResizeObserver;
|
|
47
52
|
private ghostEl: HTMLElement | undefined;
|
|
48
|
-
private horizontalGuidelines: number[] = [];
|
|
49
|
-
private verticalGuidelines: number[] = [];
|
|
50
53
|
private mode: Mode = Mode.ABSOLUTE;
|
|
54
|
+
private moveableHelper?: MoveableHelper;
|
|
51
55
|
|
|
52
56
|
constructor(config: StageDragResizeConfig) {
|
|
53
57
|
super();
|
|
54
58
|
|
|
55
59
|
this.core = config.core;
|
|
56
60
|
this.container = config.container;
|
|
57
|
-
|
|
61
|
+
|
|
62
|
+
this.dragEl = globalThis.document.createElement('div');
|
|
63
|
+
this.container.append(this.dragEl);
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
/**
|
|
61
67
|
* 将选中框渲染并覆盖到选中的组件Dom节点上方
|
|
68
|
+
* 当选中的节点是不是absolute时,会创建一个新的节点出来作为拖拽目标
|
|
62
69
|
* @param el 选中组件的Dom节点元素
|
|
70
|
+
* @param event 鼠标事件
|
|
63
71
|
*/
|
|
64
|
-
public
|
|
65
|
-
|
|
66
|
-
this.refresh();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
this.moveable?.destroy();
|
|
71
|
-
|
|
72
|
+
public select(el: HTMLElement, event?: MouseEvent): void {
|
|
73
|
+
const oldTarget = this.target;
|
|
72
74
|
this.target = el;
|
|
73
|
-
this.mode = getMode(el);
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
this.init(el);
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
this.
|
|
79
|
-
|
|
78
|
+
// 从不能拖动到能拖动的节点之间切换,要重新创建moveable,不然dragStart不生效
|
|
79
|
+
if (!this.moveable || this.target !== oldTarget) {
|
|
80
|
+
this.moveableHelper = MoveableHelper.create({
|
|
81
|
+
useBeforeRender: true,
|
|
82
|
+
useRender: false,
|
|
83
|
+
createAuto: true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
this.initMoveable();
|
|
87
|
+
} else {
|
|
88
|
+
this.updateMoveable();
|
|
89
|
+
}
|
|
80
90
|
|
|
81
|
-
|
|
91
|
+
if (event) {
|
|
92
|
+
this.moveable?.dragStart(event);
|
|
93
|
+
}
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
/**
|
|
85
97
|
* 初始化选中框并渲染出来
|
|
86
|
-
* @param param0
|
|
87
98
|
*/
|
|
99
|
+
public updateMoveable(el = this.target): void {
|
|
100
|
+
if (!this.moveable) throw new Error('未初始化moveable');
|
|
101
|
+
if (!el) throw new Error('为选中任何节点');
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
const options = await this.getOptions();
|
|
91
|
-
Object.entries(options).forEach(([key, value]) => {
|
|
92
|
-
(this.moveable as any)[key] = value;
|
|
93
|
-
});
|
|
94
|
-
this.updateMoveableTarget();
|
|
95
|
-
}
|
|
103
|
+
this.target = el;
|
|
96
104
|
|
|
97
|
-
|
|
98
|
-
this.verticalGuidelines = verticalGuidelines;
|
|
99
|
-
this.target && (await this.select(this.target));
|
|
100
|
-
}
|
|
105
|
+
this.init(el);
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
Object.entries(this.moveableOptions).forEach(([key, value]) => {
|
|
108
|
+
(this.moveable as any)[key] = value;
|
|
109
|
+
});
|
|
110
|
+
this.moveable.updateTarget();
|
|
105
111
|
}
|
|
106
112
|
|
|
107
|
-
public
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
public setGuidelines(type: GuidesType, guidelines: number[]): void {
|
|
114
|
+
if (type === GuidesType.HORIZONTAL) {
|
|
115
|
+
this.horizontalGuidelines = guidelines;
|
|
116
|
+
this.moveableOptions.horizontalGuidelines = guidelines;
|
|
117
|
+
} else if (type === GuidesType.VERTICAL) {
|
|
118
|
+
this.verticalGuidelines = guidelines;
|
|
119
|
+
this.moveableOptions.verticalGuidelines = guidelines;
|
|
112
120
|
}
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
122
|
+
this.updateMoveable();
|
|
123
|
+
}
|
|
117
124
|
|
|
118
|
-
|
|
125
|
+
public clearGuides() {
|
|
126
|
+
this.horizontalGuidelines = [];
|
|
127
|
+
this.verticalGuidelines = [];
|
|
128
|
+
this.moveableOptions.horizontalGuidelines = [];
|
|
129
|
+
this.moveableOptions.verticalGuidelines = [];
|
|
130
|
+
this.updateMoveable();
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
/**
|
|
122
134
|
* 销毁实例
|
|
123
135
|
*/
|
|
124
136
|
public destroy(): void {
|
|
125
|
-
this.destroyGhostEl();
|
|
126
137
|
this.moveable?.destroy();
|
|
138
|
+
this.destroyGhostEl();
|
|
139
|
+
this.destroyDragEl();
|
|
127
140
|
this.dragStatus = ActionStatus.END;
|
|
128
|
-
this.elObserver?.disconnect();
|
|
129
141
|
this.removeAllListeners();
|
|
130
142
|
}
|
|
131
143
|
|
|
144
|
+
private init(el: HTMLElement): void {
|
|
145
|
+
// 如果有滚动条会导致resize时获取到width,height不准确
|
|
146
|
+
if (/(auto|scroll)/.test(el.style.overflow)) {
|
|
147
|
+
el.style.overflow = 'hidden';
|
|
148
|
+
}
|
|
149
|
+
this.mode = getMode(el);
|
|
150
|
+
|
|
151
|
+
this.destroyGhostEl();
|
|
152
|
+
|
|
153
|
+
this.updateDragEl(el);
|
|
154
|
+
|
|
155
|
+
this.moveableOptions = this.getOptions({
|
|
156
|
+
target: this.dragEl,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private initMoveable() {
|
|
161
|
+
this.moveable?.destroy();
|
|
162
|
+
|
|
163
|
+
this.moveable = new Moveable(this.container, {
|
|
164
|
+
...this.moveableOptions,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
this.bindResizeEvent();
|
|
168
|
+
this.bindDragEvent();
|
|
169
|
+
}
|
|
170
|
+
|
|
132
171
|
private bindResizeEvent(): void {
|
|
133
172
|
if (!this.moveable) throw new Error('moveable 为初始化');
|
|
134
173
|
|
|
@@ -142,116 +181,110 @@ export default class StageDragResize extends EventEmitter {
|
|
|
142
181
|
const rect = this.moveable!.getRect();
|
|
143
182
|
const offset = getAbsolutePosition(e.target as HTMLElement, rect);
|
|
144
183
|
e.dragStart.set([offset.left, offset.top]);
|
|
145
|
-
|
|
146
|
-
if (this.ghostEl) {
|
|
147
|
-
this.destroyGhostEl();
|
|
148
|
-
this.updateMoveableTarget(this.target);
|
|
149
|
-
}
|
|
150
184
|
}
|
|
151
185
|
})
|
|
152
|
-
.on('resize', ({
|
|
153
|
-
if (!this.moveable) return;
|
|
154
|
-
|
|
186
|
+
.on('resize', ({ width, height, drag }) => {
|
|
187
|
+
if (!this.moveable || !this.target || !this.dragEl) return;
|
|
188
|
+
|
|
155
189
|
const { beforeTranslate } = drag;
|
|
156
190
|
frame.translate = beforeTranslate;
|
|
157
191
|
this.dragStatus = ActionStatus.ING;
|
|
158
192
|
|
|
159
|
-
target.style.width = `${width}px`;
|
|
160
|
-
target.style.height = `${height}px`;
|
|
193
|
+
this.target.style.width = `${width}px`;
|
|
194
|
+
this.target.style.height = `${height}px`;
|
|
195
|
+
this.dragEl.style.width = `${width}px`;
|
|
196
|
+
this.dragEl.style.height = `${height}px`;
|
|
161
197
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
198
|
+
// 流式布局
|
|
199
|
+
if (this.mode === Mode.SORTABLE) {
|
|
200
|
+
this.dragEl.style.top = `${beforeTranslate[1]}px`;
|
|
201
|
+
this.target.style.top = `0px`;
|
|
202
|
+
return;
|
|
165
203
|
}
|
|
204
|
+
|
|
205
|
+
this.dragEl.style.left = `${beforeTranslate[0]}px`;
|
|
206
|
+
this.dragEl.style.top = `${beforeTranslate[1]}px`;
|
|
207
|
+
|
|
208
|
+
const offset = getAbsolutePosition(this.target, { left: beforeTranslate[0], top: beforeTranslate[1] });
|
|
209
|
+
|
|
210
|
+
this.target.style.left = `${offset.left}px`;
|
|
211
|
+
this.target.style.top = `${offset.top}px`;
|
|
166
212
|
})
|
|
167
|
-
.on('resizeEnd', (
|
|
213
|
+
.on('resizeEnd', () => {
|
|
168
214
|
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
|
-
});
|
|
215
|
+
this.update(true);
|
|
189
216
|
});
|
|
190
217
|
}
|
|
191
218
|
|
|
192
219
|
private bindDragEvent(): void {
|
|
193
220
|
if (!this.moveable) throw new Error('moveable 为初始化');
|
|
194
221
|
|
|
222
|
+
let offset: Offset | null = null;
|
|
223
|
+
|
|
195
224
|
this.moveable
|
|
196
|
-
.on('dragStart', () => {
|
|
225
|
+
.on('dragStart', (e) => {
|
|
197
226
|
if (!this.target) throw new Error('未选中组件');
|
|
198
227
|
|
|
199
228
|
this.dragStatus = ActionStatus.START;
|
|
200
229
|
|
|
230
|
+
this.moveableHelper?.onDragStart(e);
|
|
231
|
+
|
|
201
232
|
if (this.mode === Mode.SORTABLE) {
|
|
202
233
|
this.ghostEl = this.generateGhostEl(this.target);
|
|
203
|
-
this.updateMoveableTarget(this.ghostEl);
|
|
204
234
|
}
|
|
205
235
|
})
|
|
206
|
-
.on('drag', (
|
|
236
|
+
.on('drag', (e) => {
|
|
237
|
+
if (!this.target || !this.dragEl) return;
|
|
238
|
+
|
|
239
|
+
if (!offset) {
|
|
240
|
+
offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
|
|
241
|
+
}
|
|
242
|
+
|
|
207
243
|
this.dragStatus = ActionStatus.ING;
|
|
208
|
-
|
|
244
|
+
|
|
245
|
+
const { left, top } = e;
|
|
246
|
+
|
|
247
|
+
// 流式布局
|
|
248
|
+
if (this.ghostEl) {
|
|
249
|
+
this.dragEl.style.top = `${top}px`;
|
|
250
|
+
this.ghostEl.style.top = `${top + offset.top}px`;
|
|
209
251
|
return;
|
|
210
252
|
}
|
|
211
253
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const offset = getAbsolutePosition(this.target, getOffset(this.target));
|
|
217
|
-
target.style.top = `${offset.top + top}px`;
|
|
218
|
-
}
|
|
254
|
+
this.moveableHelper?.onDrag(e);
|
|
255
|
+
|
|
256
|
+
this.target.style.left = `${left + offset.left}px`;
|
|
257
|
+
this.target.style.top = `${top + offset.top}px`;
|
|
219
258
|
})
|
|
220
259
|
.on('dragEnd', () => {
|
|
221
260
|
// 点击不拖动时会触发dragStart和dragEnd,但是不会有drag事件
|
|
222
|
-
if (this.dragStatus
|
|
223
|
-
|
|
261
|
+
if (this.dragStatus === ActionStatus.ING) {
|
|
262
|
+
switch (this.mode) {
|
|
263
|
+
case Mode.SORTABLE:
|
|
264
|
+
this.sort();
|
|
265
|
+
break;
|
|
266
|
+
default:
|
|
267
|
+
this.update();
|
|
268
|
+
}
|
|
224
269
|
}
|
|
225
|
-
|
|
226
|
-
if (!this.target) return;
|
|
270
|
+
offset = null;
|
|
227
271
|
|
|
228
272
|
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
273
|
this.destroyGhostEl();
|
|
240
274
|
});
|
|
241
275
|
}
|
|
242
276
|
|
|
243
|
-
private
|
|
244
|
-
const { renderer } = this.core;
|
|
277
|
+
private getSnapElements(runtime: Runtime, el?: HTMLElement): HTMLElement[] {
|
|
278
|
+
const { renderer, mask } = this.core;
|
|
245
279
|
const getSnapElements =
|
|
246
|
-
|
|
280
|
+
runtime?.getSnapElements ||
|
|
247
281
|
(() => {
|
|
248
282
|
const doc = renderer.contentWindow?.document;
|
|
249
|
-
|
|
250
|
-
// 排除掉当前组件本身
|
|
251
|
-
.filter((element) => element !== this.target && !this.target?.contains(element));
|
|
252
|
-
return elementGuidelines as HTMLElement[];
|
|
283
|
+
return doc ? Array.from(doc.querySelectorAll('[id]')) : [];
|
|
253
284
|
});
|
|
254
|
-
return getSnapElements(el)
|
|
285
|
+
return getSnapElements(el).filter(
|
|
286
|
+
(element) => element !== this.target && !this.target?.contains(element) && element !== mask.page,
|
|
287
|
+
);
|
|
255
288
|
}
|
|
256
289
|
|
|
257
290
|
private sort(): void {
|
|
@@ -273,18 +306,19 @@ export default class StageDragResize extends EventEmitter {
|
|
|
273
306
|
}
|
|
274
307
|
}
|
|
275
308
|
|
|
276
|
-
private
|
|
309
|
+
private update(isResize = false): void {
|
|
277
310
|
const rect = this.moveable!.getRect();
|
|
278
|
-
const offset =
|
|
311
|
+
const offset =
|
|
312
|
+
this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : getAbsolutePosition(this.target as HTMLElement, rect);
|
|
313
|
+
|
|
314
|
+
const left = this.calcValueByFontsize(offset.left);
|
|
315
|
+
const top = this.calcValueByFontsize(offset.top);
|
|
316
|
+
const width = this.calcValueByFontsize(rect.width);
|
|
317
|
+
const height = this.calcValueByFontsize(rect.height);
|
|
279
318
|
|
|
280
319
|
this.emit('update', {
|
|
281
320
|
el: this.target,
|
|
282
|
-
style: {
|
|
283
|
-
left: this.calcValueByFontsize(this.mode === Mode.FIXED ? rect.left : offset.left),
|
|
284
|
-
top: this.calcValueByFontsize(this.mode === Mode.FIXED ? rect.top : offset.top),
|
|
285
|
-
width: this.calcValueByFontsize(rect.width),
|
|
286
|
-
height: this.calcValueByFontsize(rect.height),
|
|
287
|
-
},
|
|
321
|
+
style: isResize ? { left, top, width, height } : { left, top },
|
|
288
322
|
});
|
|
289
323
|
}
|
|
290
324
|
|
|
@@ -295,7 +329,7 @@ export default class StageDragResize extends EventEmitter {
|
|
|
295
329
|
|
|
296
330
|
const ghostEl = el.cloneNode(true) as HTMLElement;
|
|
297
331
|
const { top, left } = getAbsolutePosition(el, getOffset(el));
|
|
298
|
-
ghostEl.id = `${GHOST_EL_ID_PREFIX}${
|
|
332
|
+
ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
|
|
299
333
|
ghostEl.style.zIndex = '5';
|
|
300
334
|
ghostEl.style.opacity = '.5';
|
|
301
335
|
ghostEl.style.position = 'absolute';
|
|
@@ -310,90 +344,88 @@ export default class StageDragResize extends EventEmitter {
|
|
|
310
344
|
this.ghostEl = undefined;
|
|
311
345
|
}
|
|
312
346
|
|
|
313
|
-
private
|
|
347
|
+
private updateDragEl(el: HTMLElement) {
|
|
348
|
+
const { width, height } = el.getBoundingClientRect();
|
|
349
|
+
const offset = getOffset(el);
|
|
350
|
+
|
|
351
|
+
this.dragEl.style.cssText = `
|
|
352
|
+
position: absolute;
|
|
353
|
+
left: ${offset.left}px;
|
|
354
|
+
top: ${offset.top}px;
|
|
355
|
+
width: ${width}px;
|
|
356
|
+
height: ${height}px;
|
|
357
|
+
`;
|
|
358
|
+
|
|
359
|
+
this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private destroyDragEl(): void {
|
|
363
|
+
this.dragEl?.remove();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private getOptions(options: MoveableOptions = {}): MoveableOptions {
|
|
314
367
|
if (!this.target) return {};
|
|
315
368
|
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
const { iframe } = renderer;
|
|
369
|
+
const isAbsolute = this.mode === Mode.ABSOLUTE;
|
|
370
|
+
const isFixed = this.mode === Mode.FIXED;
|
|
319
371
|
|
|
320
|
-
let { moveableOptions = {} } = config;
|
|
372
|
+
let { moveableOptions = {} } = this.core.config;
|
|
321
373
|
|
|
322
374
|
if (typeof moveableOptions === 'function') {
|
|
323
375
|
moveableOptions = moveableOptions(this.core);
|
|
324
376
|
}
|
|
325
377
|
|
|
326
|
-
const boundsOptions = {
|
|
327
|
-
top: 0,
|
|
328
|
-
left: 0,
|
|
329
|
-
right: iframe?.clientWidth,
|
|
330
|
-
bottom: this.mode === Mode.FIXED ? iframe?.clientHeight : mask.page?.clientHeight,
|
|
331
|
-
...(moveableOptions.bounds || {}),
|
|
332
|
-
};
|
|
333
|
-
|
|
334
378
|
return {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
origin: true,
|
|
379
|
+
origin: false,
|
|
380
|
+
rootContainer: this.core.container,
|
|
338
381
|
zoom: 1,
|
|
339
|
-
dragArea:
|
|
382
|
+
dragArea: false,
|
|
340
383
|
draggable: true,
|
|
341
384
|
resizable: true,
|
|
342
|
-
snappable:
|
|
343
|
-
snapGap:
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
385
|
+
snappable: isAbsolute || isFixed,
|
|
386
|
+
snapGap: isAbsolute || isFixed,
|
|
387
|
+
snapThreshold: 5,
|
|
388
|
+
snapDigit: 0,
|
|
389
|
+
throttleDrag: 0,
|
|
390
|
+
isDisplaySnapDigit: isAbsolute,
|
|
391
|
+
snapDirections: {
|
|
392
|
+
top: isAbsolute,
|
|
393
|
+
right: isAbsolute,
|
|
394
|
+
bottom: isAbsolute,
|
|
395
|
+
left: isAbsolute,
|
|
396
|
+
center: isAbsolute,
|
|
397
|
+
middle: isAbsolute,
|
|
398
|
+
},
|
|
399
|
+
elementSnapDirections: {
|
|
400
|
+
top: isAbsolute,
|
|
401
|
+
right: isAbsolute,
|
|
402
|
+
bottom: isAbsolute,
|
|
403
|
+
left: isAbsolute,
|
|
404
|
+
},
|
|
405
|
+
isDisplayInnerSnapDigit: true,
|
|
351
406
|
horizontalGuidelines: this.horizontalGuidelines,
|
|
352
407
|
verticalGuidelines: this.verticalGuidelines,
|
|
353
|
-
|
|
354
|
-
|
|
408
|
+
elementGuidelines: this.elementGuidelines,
|
|
409
|
+
|
|
410
|
+
bounds: {
|
|
411
|
+
top: 0,
|
|
412
|
+
left: 0,
|
|
413
|
+
right: this.container.clientWidth,
|
|
414
|
+
bottom: this.container.clientHeight,
|
|
415
|
+
...(moveableOptions.bounds || {}),
|
|
416
|
+
},
|
|
355
417
|
...options,
|
|
356
418
|
...moveableOptions,
|
|
357
419
|
};
|
|
358
420
|
}
|
|
359
421
|
|
|
360
|
-
private initObserver(): void {
|
|
361
|
-
if (typeof ResizeObserver === 'undefined') {
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
this.elObserver = new ResizeObserver(() => {
|
|
366
|
-
const doc = this.core.renderer.contentWindow?.document;
|
|
367
|
-
if (!doc || !this.target || !this.moveable) return;
|
|
368
|
-
|
|
369
|
-
/** 组件可能已经重新渲染了,所以需要重新获取新的dom */
|
|
370
|
-
const target = doc.getElementById(this.target.id);
|
|
371
|
-
|
|
372
|
-
if (this.ghostEl) {
|
|
373
|
-
this.destroyGhostEl();
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (target && target !== this.target) {
|
|
377
|
-
this.syncRect(target);
|
|
378
|
-
this.target = target;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
this.updateMoveableTarget(this.target);
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
private syncRect(el: HTMLElement): void {
|
|
386
|
-
this.elObserver?.disconnect();
|
|
387
|
-
this.elObserver?.observe(el);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
422
|
private calcValueByFontsize(value: number) {
|
|
391
423
|
const { contentWindow } = this.core.renderer;
|
|
392
424
|
const fontSize = contentWindow?.document.documentElement.style.fontSize;
|
|
393
425
|
|
|
394
426
|
if (fontSize) {
|
|
395
427
|
const times = globalThis.parseFloat(fontSize) / 100;
|
|
396
|
-
return value / times;
|
|
428
|
+
return (value / times).toFixed(2);
|
|
397
429
|
}
|
|
398
430
|
|
|
399
431
|
return value;
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
scrollable: true,
|
|
50
|
+
origin: false,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 清空高亮
|
|
56
|
+
*/
|
|
57
|
+
public clearHighlight(): void {
|
|
58
|
+
if (!this.moveable) return;
|
|
59
|
+
this.target = undefined;
|
|
60
|
+
this.moveable.target = null;
|
|
61
|
+
this.moveable.updateTarget();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 销毁实例
|
|
66
|
+
*/
|
|
67
|
+
public destroy(): void {
|
|
68
|
+
this.moveable?.destroy();
|
|
69
|
+
}
|
|
70
|
+
}
|