@tmagic/stage 1.1.0-beta.2 → 1.1.0-beta.5
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/dist/tmagic-stage.es.js +352 -114
- package/dist/tmagic-stage.es.js.map +1 -1
- package/dist/tmagic-stage.umd.js +358 -116
- package/dist/tmagic-stage.umd.js.map +1 -1
- package/dist/types/src/StageCore.d.ts +13 -2
- package/dist/types/src/StageDragResize.d.ts +7 -18
- package/dist/types/src/StageMask.d.ts +1 -0
- package/dist/types/src/StageMultiDragResize.d.ts +49 -0
- package/dist/types/src/StageRender.d.ts +1 -0
- package/dist/types/src/const.d.ts +1 -0
- package/dist/types/src/types.d.ts +3 -1
- package/dist/types/src/util.d.ts +16 -1
- package/package.json +5 -4
- package/src/StageCore.ts +92 -18
- package/src/StageDragResize.ts +40 -118
- package/src/StageHighlight.ts +1 -1
- package/src/StageMask.ts +21 -4
- package/src/StageMultiDragResize.ts +231 -0
- package/src/StageRender.ts +28 -18
- package/src/TargetCalibrate.ts +2 -1
- package/src/const.ts +2 -0
- package/src/style.css +1 -0
- package/src/types.ts +3 -1
- package/src/util.ts +87 -2
package/src/StageDragResize.ts
CHANGED
|
@@ -23,12 +23,13 @@ import type { MoveableOptions } from 'moveable';
|
|
|
23
23
|
import Moveable from 'moveable';
|
|
24
24
|
import MoveableHelper from 'moveable-helper';
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import { removeClassNameByClassName } from '@tmagic/utils';
|
|
27
27
|
|
|
28
28
|
import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, Mode, ZIndex } from './const';
|
|
29
29
|
import StageCore from './StageCore';
|
|
30
|
-
import
|
|
31
|
-
import {
|
|
30
|
+
import StageMask from './StageMask';
|
|
31
|
+
import type { StageDragResizeConfig } from './types';
|
|
32
|
+
import { calcValueByFontsize, down, getAbsolutePosition, getMode, getOffset, getTargetElStyle, up } from './util';
|
|
32
33
|
|
|
33
34
|
/** 拖动状态 */
|
|
34
35
|
enum ActionStatus {
|
|
@@ -45,12 +46,13 @@ enum ActionStatus {
|
|
|
45
46
|
*/
|
|
46
47
|
export default class StageDragResize extends EventEmitter {
|
|
47
48
|
public core: StageCore;
|
|
49
|
+
public mask: StageMask;
|
|
48
50
|
/** 画布容器 */
|
|
49
51
|
public container: HTMLElement;
|
|
50
52
|
/** 目标节点 */
|
|
51
53
|
public target?: HTMLElement;
|
|
52
54
|
/** 目标节点在蒙层中的占位节点 */
|
|
53
|
-
public dragEl
|
|
55
|
+
public dragEl?: HTMLDivElement;
|
|
54
56
|
/** Moveable拖拽类实例 */
|
|
55
57
|
public moveable?: Moveable;
|
|
56
58
|
/** 水平参考线 */
|
|
@@ -74,9 +76,7 @@ export default class StageDragResize extends EventEmitter {
|
|
|
74
76
|
|
|
75
77
|
this.core = config.core;
|
|
76
78
|
this.container = config.container;
|
|
77
|
-
|
|
78
|
-
this.dragEl = globalThis.document.createElement('div');
|
|
79
|
-
this.container.append(this.dragEl);
|
|
79
|
+
this.mask = config.mask;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -147,6 +147,17 @@ export default class StageDragResize extends EventEmitter {
|
|
|
147
147
|
this.updateMoveable();
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
public clearSelectStatus(): void {
|
|
151
|
+
if (!this.moveable) return;
|
|
152
|
+
this.destroyDragEl();
|
|
153
|
+
this.moveable.target = null;
|
|
154
|
+
this.moveable.updateTarget();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public destroyDragEl(): void {
|
|
158
|
+
this.dragEl?.remove();
|
|
159
|
+
}
|
|
160
|
+
|
|
150
161
|
/**
|
|
151
162
|
* 销毁实例
|
|
152
163
|
*/
|
|
@@ -166,9 +177,15 @@ export default class StageDragResize extends EventEmitter {
|
|
|
166
177
|
this.mode = getMode(el);
|
|
167
178
|
|
|
168
179
|
this.destroyGhostEl();
|
|
180
|
+
this.destroyDragEl();
|
|
181
|
+
this.dragEl = globalThis.document.createElement('div');
|
|
182
|
+
this.container.append(this.dragEl);
|
|
183
|
+
this.dragEl.style.cssText = getTargetElStyle(el);
|
|
184
|
+
this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
|
|
169
185
|
|
|
170
|
-
this.updateDragEl
|
|
171
|
-
|
|
186
|
+
if (typeof this.core.config.updateDragEl === 'function') {
|
|
187
|
+
this.core.config.updateDragEl(this.dragEl, el);
|
|
188
|
+
}
|
|
172
189
|
this.moveableOptions = this.getOptions({
|
|
173
190
|
target: this.dragEl,
|
|
174
191
|
});
|
|
@@ -294,20 +311,7 @@ export default class StageDragResize extends EventEmitter {
|
|
|
294
311
|
timeout = undefined;
|
|
295
312
|
}
|
|
296
313
|
|
|
297
|
-
timeout =
|
|
298
|
-
const els = this.core.getElementsFromPoint(e.inputEvent);
|
|
299
|
-
for (const el of els) {
|
|
300
|
-
if (
|
|
301
|
-
doc &&
|
|
302
|
-
!el.id.startsWith(GHOST_EL_ID_PREFIX) &&
|
|
303
|
-
el !== this.target &&
|
|
304
|
-
(await this.core.isContainer(el))
|
|
305
|
-
) {
|
|
306
|
-
addClassName(el, doc, this.core.containerHighlightClassName);
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}, this.core.containerHighlightDuration);
|
|
314
|
+
timeout = this.core.getAddContainerHighlightClassNameTimeout(e.inputEvent, [this.target]);
|
|
311
315
|
|
|
312
316
|
this.dragStatus = ActionStatus.ING;
|
|
313
317
|
|
|
@@ -467,6 +471,7 @@ export default class StageDragResize extends EventEmitter {
|
|
|
467
471
|
}
|
|
468
472
|
|
|
469
473
|
const ghostEl = el.cloneNode(true) as HTMLElement;
|
|
474
|
+
this.setGhostElChildrenId(ghostEl);
|
|
470
475
|
const { top, left } = getAbsolutePosition(el, getOffset(el));
|
|
471
476
|
ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
|
|
472
477
|
ghostEl.style.zIndex = ZIndex.GHOST_EL;
|
|
@@ -478,34 +483,21 @@ export default class StageDragResize extends EventEmitter {
|
|
|
478
483
|
return ghostEl;
|
|
479
484
|
}
|
|
480
485
|
|
|
481
|
-
private
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
private updateDragEl(el: HTMLElement) {
|
|
487
|
-
const offset = getOffset(el);
|
|
488
|
-
const { transform } = getComputedStyle(el);
|
|
489
|
-
|
|
490
|
-
this.dragEl.style.cssText = `
|
|
491
|
-
position: absolute;
|
|
492
|
-
transform: ${transform};
|
|
493
|
-
left: ${offset.left}px;
|
|
494
|
-
top: ${offset.top}px;
|
|
495
|
-
width: ${el.clientWidth}px;
|
|
496
|
-
height: ${el.clientHeight}px;
|
|
497
|
-
z-index: ${ZIndex.DRAG_EL};
|
|
498
|
-
`;
|
|
499
|
-
|
|
500
|
-
this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
|
|
486
|
+
private setGhostElChildrenId(el: Element) {
|
|
487
|
+
for (const child of el.children) {
|
|
488
|
+
if (child.id) {
|
|
489
|
+
child.id = `${GHOST_EL_ID_PREFIX}${child.id}`;
|
|
490
|
+
}
|
|
501
491
|
|
|
502
|
-
|
|
503
|
-
|
|
492
|
+
if (child.children.length) {
|
|
493
|
+
this.setGhostElChildrenId(child);
|
|
494
|
+
}
|
|
504
495
|
}
|
|
505
496
|
}
|
|
506
497
|
|
|
507
|
-
private
|
|
508
|
-
this.
|
|
498
|
+
private destroyGhostEl(): void {
|
|
499
|
+
this.ghostEl?.remove();
|
|
500
|
+
this.ghostEl = undefined;
|
|
509
501
|
}
|
|
510
502
|
|
|
511
503
|
private getOptions(options: MoveableOptions = {}): MoveableOptions {
|
|
@@ -566,7 +558,7 @@ export default class StageDragResize extends EventEmitter {
|
|
|
566
558
|
top: 0,
|
|
567
559
|
// 设置0的话无法移动到left为0,所以只能设置为-1
|
|
568
560
|
left: -1,
|
|
569
|
-
right: this.container.clientWidth,
|
|
561
|
+
right: this.container.clientWidth - 1,
|
|
570
562
|
bottom: this.container.clientHeight,
|
|
571
563
|
...(moveableOptions.bounds || {}),
|
|
572
564
|
},
|
|
@@ -575,73 +567,3 @@ export default class StageDragResize extends EventEmitter {
|
|
|
575
567
|
};
|
|
576
568
|
}
|
|
577
569
|
}
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* 下移组件位置
|
|
581
|
-
* @param {number} deltaTop 偏移量
|
|
582
|
-
* @param {Object} detail 当前选中的组件配置
|
|
583
|
-
*/
|
|
584
|
-
export const down = (deltaTop: number, target: HTMLElement | SVGElement): SortEventData | void => {
|
|
585
|
-
let swapIndex = 0;
|
|
586
|
-
let addUpH = target.clientHeight;
|
|
587
|
-
const brothers = Array.from(target.parentNode?.children || []).filter(
|
|
588
|
-
(node) => !node.id.startsWith(GHOST_EL_ID_PREFIX),
|
|
589
|
-
);
|
|
590
|
-
const index = brothers.indexOf(target);
|
|
591
|
-
// 往下移动
|
|
592
|
-
const downEls = brothers.slice(index + 1) as HTMLElement[];
|
|
593
|
-
|
|
594
|
-
for (let i = 0; i < downEls.length; i++) {
|
|
595
|
-
const ele = downEls[i];
|
|
596
|
-
// 是 fixed 不做处理
|
|
597
|
-
if (ele.style?.position === 'fixed') {
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
addUpH += ele.clientHeight / 2;
|
|
601
|
-
if (deltaTop <= addUpH) {
|
|
602
|
-
break;
|
|
603
|
-
}
|
|
604
|
-
addUpH += ele.clientHeight / 2;
|
|
605
|
-
swapIndex = i;
|
|
606
|
-
}
|
|
607
|
-
return {
|
|
608
|
-
src: target.id,
|
|
609
|
-
dist: downEls.length && swapIndex > -1 ? downEls[swapIndex].id : target.id,
|
|
610
|
-
};
|
|
611
|
-
};
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* 上移组件位置
|
|
615
|
-
* @param {Array} brothers 处于同一容器下的所有子组件配置
|
|
616
|
-
* @param {number} index 当前组件所处的位置
|
|
617
|
-
* @param {number} deltaTop 偏移量
|
|
618
|
-
* @param {Object} detail 当前选中的组件配置
|
|
619
|
-
*/
|
|
620
|
-
export const up = (deltaTop: number, target: HTMLElement | SVGElement): SortEventData | void => {
|
|
621
|
-
const brothers = Array.from(target.parentNode?.children || []).filter(
|
|
622
|
-
(node) => !node.id.startsWith(GHOST_EL_ID_PREFIX),
|
|
623
|
-
);
|
|
624
|
-
const index = brothers.indexOf(target);
|
|
625
|
-
// 往上移动
|
|
626
|
-
const upEls = brothers.slice(0, index) as HTMLElement[];
|
|
627
|
-
|
|
628
|
-
let addUpH = target.clientHeight;
|
|
629
|
-
let swapIndex = upEls.length - 1;
|
|
630
|
-
|
|
631
|
-
for (let i = upEls.length - 1; i >= 0; i--) {
|
|
632
|
-
const ele = upEls[i];
|
|
633
|
-
if (!ele) continue;
|
|
634
|
-
// 是 fixed 不做处理
|
|
635
|
-
if (ele.style.position === 'fixed') continue;
|
|
636
|
-
|
|
637
|
-
addUpH += ele.clientHeight / 2;
|
|
638
|
-
if (-deltaTop <= addUpH) break;
|
|
639
|
-
addUpH += ele.clientHeight / 2;
|
|
640
|
-
|
|
641
|
-
swapIndex = i;
|
|
642
|
-
}
|
|
643
|
-
return {
|
|
644
|
-
src: target.id,
|
|
645
|
-
dist: upEls.length && swapIndex > -1 ? upEls[swapIndex].id : target.id,
|
|
646
|
-
};
|
|
647
|
-
};
|
package/src/StageHighlight.ts
CHANGED
package/src/StageMask.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import KeyController from 'keycon';
|
|
19
20
|
import { throttle } from 'lodash-es';
|
|
20
21
|
|
|
21
22
|
import { createDiv, injectStyle } from '@tmagic/utils';
|
|
@@ -82,6 +83,7 @@ export default class StageMask extends Rule {
|
|
|
82
83
|
public maxScrollTop = 0;
|
|
83
84
|
public maxScrollLeft = 0;
|
|
84
85
|
public intersectionObserver: IntersectionObserver | null = null;
|
|
86
|
+
public isMultiSelectStatus: Boolean = false;
|
|
85
87
|
|
|
86
88
|
private mode: Mode = Mode.ABSOLUTE;
|
|
87
89
|
private pageResizeObserver: ResizeObserver | null = null;
|
|
@@ -106,6 +108,14 @@ export default class StageMask extends Rule {
|
|
|
106
108
|
this.content.addEventListener('wheel', this.mouseWheelHandler);
|
|
107
109
|
this.content.addEventListener('mousemove', this.highlightHandler);
|
|
108
110
|
this.content.addEventListener('mouseleave', this.mouseLeaveHandler);
|
|
111
|
+
KeyController.global.keydown('shift', (e) => {
|
|
112
|
+
e.inputEvent.preventDefault();
|
|
113
|
+
this.isMultiSelectStatus = true;
|
|
114
|
+
});
|
|
115
|
+
KeyController.global.keyup('shift', (e) => {
|
|
116
|
+
e.inputEvent.preventDefault();
|
|
117
|
+
this.isMultiSelectStatus = false;
|
|
118
|
+
});
|
|
109
119
|
}
|
|
110
120
|
|
|
111
121
|
public setMode(mode: Mode) {
|
|
@@ -292,21 +302,28 @@ export default class StageMask extends Rule {
|
|
|
292
302
|
*/
|
|
293
303
|
private mouseDownHandler = (event: MouseEvent): void => {
|
|
294
304
|
this.emit('clearHighlight');
|
|
295
|
-
|
|
296
305
|
event.stopImmediatePropagation();
|
|
297
306
|
event.stopPropagation();
|
|
298
307
|
|
|
299
308
|
if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT) return;
|
|
300
309
|
|
|
301
|
-
//
|
|
310
|
+
// 如果单击多选选中区域,则不需要再触发选中了,而可能是拖动行为
|
|
311
|
+
if (!this.isMultiSelectStatus && (event.target as HTMLDivElement).className.indexOf('moveable-area') !== -1) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// 点击对象如果是边框锚点,则可能是resize
|
|
302
315
|
if ((event.target as HTMLDivElement).className.indexOf('moveable-control') !== -1) {
|
|
303
316
|
return;
|
|
304
317
|
}
|
|
305
318
|
|
|
306
319
|
this.content.removeEventListener('mousemove', this.highlightHandler);
|
|
307
320
|
|
|
308
|
-
|
|
309
|
-
|
|
321
|
+
// 判断触发多选还是单选
|
|
322
|
+
if (this.isMultiSelectStatus) {
|
|
323
|
+
this.emit('beforeMultiSelect', event);
|
|
324
|
+
} else {
|
|
325
|
+
this.emit('beforeSelect', event);
|
|
326
|
+
}
|
|
310
327
|
// 如果是右键点击,这里的mouseup事件监听没有效果
|
|
311
328
|
globalThis.document.addEventListener('mouseup', this.mouseUpHandler);
|
|
312
329
|
};
|
|
@@ -0,0 +1,231 @@
|
|
|
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
|
+
import { EventEmitter } from 'events';
|
|
20
|
+
|
|
21
|
+
import type { MoveableOptions } from 'moveable';
|
|
22
|
+
import Moveable from 'moveable';
|
|
23
|
+
import MoveableHelper from 'moveable-helper';
|
|
24
|
+
|
|
25
|
+
import { DRAG_EL_ID_PREFIX, PAGE_CLASS } from './const';
|
|
26
|
+
import StageCore from './StageCore';
|
|
27
|
+
import StageMask from './StageMask';
|
|
28
|
+
import { StageDragResizeConfig } from './types';
|
|
29
|
+
import { calcValueByFontsize, getMode, getTargetElStyle } from './util';
|
|
30
|
+
export default class StageMultiDragResize extends EventEmitter {
|
|
31
|
+
public core: StageCore;
|
|
32
|
+
public mask: StageMask;
|
|
33
|
+
/** 画布容器 */
|
|
34
|
+
public container: HTMLElement;
|
|
35
|
+
/** 多选:目标节点组 */
|
|
36
|
+
public targetList: HTMLElement[] = [];
|
|
37
|
+
/** 多选:目标节点在蒙层中的占位节点组 */
|
|
38
|
+
public dragElList: HTMLDivElement[] = [];
|
|
39
|
+
/** Moveable多选拖拽类实例 */
|
|
40
|
+
public moveableForMulti?: Moveable;
|
|
41
|
+
private multiMoveableHelper?: MoveableHelper;
|
|
42
|
+
|
|
43
|
+
constructor(config: StageDragResizeConfig) {
|
|
44
|
+
super();
|
|
45
|
+
|
|
46
|
+
this.core = config.core;
|
|
47
|
+
this.container = config.container;
|
|
48
|
+
this.mask = config.mask;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 多选
|
|
53
|
+
* @param els
|
|
54
|
+
*/
|
|
55
|
+
public multiSelect(els: HTMLElement[]): void {
|
|
56
|
+
this.targetList = els;
|
|
57
|
+
this.core.dr.destroyDragEl();
|
|
58
|
+
this.destroyDragElList();
|
|
59
|
+
// 生成虚拟多选节点
|
|
60
|
+
this.dragElList = els.map((elItem) => {
|
|
61
|
+
const dragElDiv = globalThis.document.createElement('div');
|
|
62
|
+
this.container.append(dragElDiv);
|
|
63
|
+
dragElDiv.style.cssText = getTargetElStyle(elItem);
|
|
64
|
+
dragElDiv.id = `${DRAG_EL_ID_PREFIX}${elItem.id}`;
|
|
65
|
+
// 业务方校准
|
|
66
|
+
if (typeof this.core.config.updateDragEl === 'function') {
|
|
67
|
+
this.core.config.updateDragEl(dragElDiv, elItem);
|
|
68
|
+
}
|
|
69
|
+
return dragElDiv;
|
|
70
|
+
});
|
|
71
|
+
this.moveableForMulti?.destroy();
|
|
72
|
+
this.multiMoveableHelper?.clear();
|
|
73
|
+
|
|
74
|
+
this.moveableForMulti = new Moveable(
|
|
75
|
+
this.container,
|
|
76
|
+
this.getOptions({
|
|
77
|
+
target: this.dragElList,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
this.multiMoveableHelper = MoveableHelper.create({
|
|
81
|
+
useBeforeRender: true,
|
|
82
|
+
useRender: false,
|
|
83
|
+
createAuto: true,
|
|
84
|
+
});
|
|
85
|
+
const frames: { left: number; top: number; id: string }[] = [];
|
|
86
|
+
this.moveableForMulti
|
|
87
|
+
.on('dragGroupStart', (params) => {
|
|
88
|
+
const { events } = params;
|
|
89
|
+
this.multiMoveableHelper?.onDragGroupStart(params);
|
|
90
|
+
// 记录拖动前快照
|
|
91
|
+
events.forEach((ev) => {
|
|
92
|
+
// 实际目标元素
|
|
93
|
+
const matchEventTarget = this.targetList.find(
|
|
94
|
+
(targetItem) => targetItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ''),
|
|
95
|
+
);
|
|
96
|
+
if (!matchEventTarget) return;
|
|
97
|
+
frames.push({
|
|
98
|
+
left: matchEventTarget.offsetLeft,
|
|
99
|
+
top: matchEventTarget.offsetTop,
|
|
100
|
+
id: matchEventTarget.id,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
})
|
|
104
|
+
.on('dragGroup', (params) => {
|
|
105
|
+
const { events } = params;
|
|
106
|
+
// 拖动过程更新
|
|
107
|
+
events.forEach((ev) => {
|
|
108
|
+
const frameSnapShot = frames.find(
|
|
109
|
+
(frameItem) => frameItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ''),
|
|
110
|
+
);
|
|
111
|
+
if (!frameSnapShot) return;
|
|
112
|
+
const targeEl = this.targetList.find(
|
|
113
|
+
(targetItem) => targetItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ''),
|
|
114
|
+
);
|
|
115
|
+
if (!targeEl) return;
|
|
116
|
+
// 元素与其所属组同时加入多选列表时,只更新父元素
|
|
117
|
+
const isParentIncluded = this.targetList.find((targetItem) => targetItem.id === targeEl.parentElement?.id);
|
|
118
|
+
if (!isParentIncluded) {
|
|
119
|
+
// 更新页面元素位置
|
|
120
|
+
targeEl.style.left = `${frameSnapShot.left + ev.beforeTranslate[0]}px`;
|
|
121
|
+
targeEl.style.top = `${frameSnapShot.top + ev.beforeTranslate[1]}px`;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
this.multiMoveableHelper?.onDragGroup(params);
|
|
125
|
+
})
|
|
126
|
+
.on('dragGroupEnd', () => {
|
|
127
|
+
this.update();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public canSelect(el: HTMLElement): Boolean {
|
|
132
|
+
// 多选不可以选中magic-ui-page
|
|
133
|
+
if (el.className.includes(PAGE_CLASS)) {
|
|
134
|
+
this.core.highlightedDom = undefined;
|
|
135
|
+
this.core.highlightLayer.clearHighlight();
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
const currentTargetMode = getMode(el);
|
|
139
|
+
let selectedDomMode = '';
|
|
140
|
+
|
|
141
|
+
if (this.targetList.length === 0 && this.core.selectedDom) {
|
|
142
|
+
// 单选后添加到多选的情况
|
|
143
|
+
selectedDomMode = getMode(this.core.selectedDom);
|
|
144
|
+
} else if (this.targetList.length > 0) {
|
|
145
|
+
// 已加入多选列表的布局模式是一样的,取第一个判断
|
|
146
|
+
selectedDomMode = getMode(this.targetList[0]);
|
|
147
|
+
}
|
|
148
|
+
// 定位模式不同,不可混选
|
|
149
|
+
if (currentTargetMode !== selectedDomMode) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 清除多选状态
|
|
157
|
+
*/
|
|
158
|
+
public clearSelectStatus(): void {
|
|
159
|
+
if (!this.moveableForMulti) return;
|
|
160
|
+
this.destroyDragElList();
|
|
161
|
+
this.moveableForMulti.target = null;
|
|
162
|
+
this.moveableForMulti.updateTarget();
|
|
163
|
+
this.targetList = [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 销毁实例
|
|
168
|
+
*/
|
|
169
|
+
public destroy(): void {
|
|
170
|
+
this.moveableForMulti?.destroy();
|
|
171
|
+
this.destroyDragElList();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 清除蒙层占位节点
|
|
176
|
+
*/
|
|
177
|
+
public destroyDragElList(): void {
|
|
178
|
+
this.dragElList.forEach((dragElItem) => dragElItem?.remove());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 拖拽完成后将更新的位置信息暴露给上层业务方,业务方可以接收事件进行保存
|
|
183
|
+
* @param isResize 是否进行大小缩放
|
|
184
|
+
*/
|
|
185
|
+
private update(isResize = false): void {
|
|
186
|
+
if (this.targetList.length === 0) return;
|
|
187
|
+
|
|
188
|
+
const { contentWindow } = this.core.renderer;
|
|
189
|
+
const doc = contentWindow?.document;
|
|
190
|
+
if (!doc) return;
|
|
191
|
+
|
|
192
|
+
this.targetList.forEach((targetItem) => {
|
|
193
|
+
const offset = { left: targetItem.offsetLeft, top: targetItem.offsetTop };
|
|
194
|
+
const left = calcValueByFontsize(doc, offset.left);
|
|
195
|
+
const top = calcValueByFontsize(doc, offset.top);
|
|
196
|
+
const width = calcValueByFontsize(doc, targetItem.clientWidth);
|
|
197
|
+
const height = calcValueByFontsize(doc, targetItem.clientHeight);
|
|
198
|
+
this.emit('update', {
|
|
199
|
+
el: targetItem,
|
|
200
|
+
style: isResize ? { left, top, width, height } : { left, top },
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 获取moveable options参数
|
|
207
|
+
* @param {MoveableOptions} options
|
|
208
|
+
* @return {MoveableOptions} moveable options参数
|
|
209
|
+
*/
|
|
210
|
+
private getOptions(options: MoveableOptions = {}): MoveableOptions {
|
|
211
|
+
let { moveableOptions = {} } = this.core.config;
|
|
212
|
+
|
|
213
|
+
if (typeof moveableOptions === 'function') {
|
|
214
|
+
moveableOptions = moveableOptions(this.core);
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
defaultGroupRotate: 0,
|
|
218
|
+
defaultGroupOrigin: '50% 50%',
|
|
219
|
+
draggable: true,
|
|
220
|
+
resizable: true,
|
|
221
|
+
throttleDrag: 0,
|
|
222
|
+
startDragRotate: 0,
|
|
223
|
+
throttleDragRotate: 0,
|
|
224
|
+
zoom: 1,
|
|
225
|
+
origin: true,
|
|
226
|
+
padding: { left: 0, top: 0, right: 0, bottom: 0 },
|
|
227
|
+
...options,
|
|
228
|
+
...moveableOptions,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
package/src/StageRender.ts
CHANGED
|
@@ -72,20 +72,22 @@ export default class StageRender extends EventEmitter {
|
|
|
72
72
|
* @param el 将页面挂载到该Dom节点上
|
|
73
73
|
*/
|
|
74
74
|
public async mount(el: HTMLDivElement) {
|
|
75
|
-
if (this.iframe) {
|
|
76
|
-
if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
|
|
77
|
-
// 不同域,使用srcdoc发起异步请求,需要目标地址支持跨域
|
|
78
|
-
let html = await fetch(this.runtimeUrl).then((res) => res.text());
|
|
79
|
-
// 使用base, 解决相对路径或绝对路径的问题
|
|
80
|
-
const base = `${location.protocol}//${getHost(this.runtimeUrl)}`;
|
|
81
|
-
html = html.replace('<head>', `<head>\n<base href="${base}">`);
|
|
82
|
-
this.iframe.srcdoc = html;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
el.appendChild<HTMLIFrameElement>(this.iframe);
|
|
86
|
-
} else {
|
|
75
|
+
if (!this.iframe) {
|
|
87
76
|
throw Error('mount 失败');
|
|
88
77
|
}
|
|
78
|
+
|
|
79
|
+
if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
|
|
80
|
+
// 不同域,使用srcdoc发起异步请求,需要目标地址支持跨域
|
|
81
|
+
let html = await fetch(this.runtimeUrl).then((res) => res.text());
|
|
82
|
+
// 使用base, 解决相对路径或绝对路径的问题
|
|
83
|
+
const base = `${location.protocol}//${getHost(this.runtimeUrl)}`;
|
|
84
|
+
html = html.replace('<head>', `<head>\n<base href="${base}">`);
|
|
85
|
+
this.iframe.srcdoc = html;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
el.appendChild<HTMLIFrameElement>(this.iframe);
|
|
89
|
+
|
|
90
|
+
this.postTmagicRuntimeReady();
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
public getRuntime = (): Promise<Runtime> => {
|
|
@@ -111,26 +113,34 @@ export default class StageRender extends EventEmitter {
|
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
private loadHandler = async () => {
|
|
114
|
-
this.contentWindow
|
|
116
|
+
if (!this.contentWindow?.magic) {
|
|
117
|
+
this.postTmagicRuntimeReady();
|
|
118
|
+
}
|
|
115
119
|
|
|
116
|
-
this.contentWindow
|
|
120
|
+
if (!this.contentWindow) return;
|
|
117
121
|
|
|
118
122
|
if (this.render) {
|
|
119
123
|
const el = await this.render(this.core);
|
|
120
124
|
if (el) {
|
|
121
|
-
this.
|
|
125
|
+
this.contentWindow.document?.body?.appendChild(el);
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
this.emit('onload');
|
|
126
130
|
|
|
131
|
+
injectStyle(this.contentWindow.document, style);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
private postTmagicRuntimeReady() {
|
|
135
|
+
this.contentWindow = this.iframe?.contentWindow as RuntimeWindow;
|
|
136
|
+
|
|
137
|
+
this.contentWindow.magic = this.getMagicApi();
|
|
138
|
+
|
|
127
139
|
this.contentWindow.postMessage(
|
|
128
140
|
{
|
|
129
141
|
tmagicRuntimeReady: true,
|
|
130
142
|
},
|
|
131
143
|
'*',
|
|
132
144
|
);
|
|
133
|
-
|
|
134
|
-
injectStyle(this.contentWindow.document, style);
|
|
135
|
-
};
|
|
145
|
+
}
|
|
136
146
|
}
|
package/src/TargetCalibrate.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { EventEmitter } from 'events';
|
|
20
20
|
|
|
21
|
-
import { Mode } from './const';
|
|
21
|
+
import { Mode, ZIndex } from './const';
|
|
22
22
|
import StageCore from './StageCore';
|
|
23
23
|
import StageDragResize from './StageDragResize';
|
|
24
24
|
import StageMask from './StageMask';
|
|
@@ -57,6 +57,7 @@ export default class TargetCalibrate extends EventEmitter {
|
|
|
57
57
|
top: ${top}px;
|
|
58
58
|
width: ${el.clientWidth}px;
|
|
59
59
|
height: ${el.clientHeight}px;
|
|
60
|
+
z-index: ${ZIndex.DRAG_EL};
|
|
60
61
|
`;
|
|
61
62
|
|
|
62
63
|
this.operationEl.id = `${prefix}${el.id}`;
|
package/src/const.ts
CHANGED
package/src/style.css
CHANGED
package/src/types.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { MoveableOptions } from 'moveable';
|
|
20
20
|
|
|
21
21
|
import Core from '@tmagic/core';
|
|
22
|
-
import type { Id, MApp, MNode } from '@tmagic/schema';
|
|
22
|
+
import type { Id, MApp, MContainer, MNode } from '@tmagic/schema';
|
|
23
23
|
|
|
24
24
|
import { GuidesType } from './const';
|
|
25
25
|
import StageCore from './StageCore';
|
|
@@ -57,6 +57,7 @@ export interface StageMaskConfig {
|
|
|
57
57
|
export interface StageDragResizeConfig {
|
|
58
58
|
core: StageCore;
|
|
59
59
|
container: HTMLElement;
|
|
60
|
+
mask: StageMask;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
export type Rect = {
|
|
@@ -98,6 +99,7 @@ export interface SortEventData {
|
|
|
98
99
|
|
|
99
100
|
export interface UpdateData {
|
|
100
101
|
config: MNode;
|
|
102
|
+
parent?: MContainer;
|
|
101
103
|
root: MApp;
|
|
102
104
|
}
|
|
103
105
|
|