@tmagic/stage 1.8.0-beta.2 → 1.8.0-beta.3
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/es/StageCore.js +12 -2
- package/dist/es/StageFlashHighlight.js +110 -0
- package/dist/tmagic-stage.umd.cjs +117 -2
- package/package.json +2 -2
- package/src/StageCore.ts +18 -3
- package/src/StageFlashHighlight.ts +142 -0
- package/src/types.ts +5 -0
- package/types/index.d.ts +45 -1
package/dist/es/StageCore.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./const.js";
|
|
2
2
|
import ActionManager from "./ActionManager.js";
|
|
3
|
+
import StageFlashHighlight from "./StageFlashHighlight.js";
|
|
3
4
|
import StageMask from "./StageMask.js";
|
|
4
5
|
import StageRender from "./StageRender.js";
|
|
5
6
|
import { EventEmitter } from "events";
|
|
@@ -13,13 +14,17 @@ var StageCore = class extends EventEmitter {
|
|
|
13
14
|
renderer = null;
|
|
14
15
|
mask = null;
|
|
15
16
|
actionManager = null;
|
|
17
|
+
flashHighlight = null;
|
|
16
18
|
pageResizeObserver = null;
|
|
17
19
|
autoScrollIntoView;
|
|
18
20
|
customizedRender;
|
|
21
|
+
/** 非点击画布选中组件时,是否对选中区域做高亮闪烁提示,默认开启 */
|
|
22
|
+
disabledFlashTip;
|
|
19
23
|
constructor(config) {
|
|
20
24
|
super();
|
|
21
25
|
this.autoScrollIntoView = config.autoScrollIntoView;
|
|
22
26
|
this.customizedRender = config.render;
|
|
27
|
+
this.disabledFlashTip = config.disabledFlashTip ?? false;
|
|
23
28
|
this.renderer = new StageRender({
|
|
24
29
|
runtimeUrl: config.runtimeUrl,
|
|
25
30
|
zoom: config.zoom,
|
|
@@ -34,6 +39,7 @@ var StageCore = class extends EventEmitter {
|
|
|
34
39
|
disabledRule: config.disabledRule
|
|
35
40
|
});
|
|
36
41
|
this.actionManager = new ActionManager(this.getActionManagerConfig(config));
|
|
42
|
+
this.flashHighlight = new StageFlashHighlight({ container: this.mask.content });
|
|
37
43
|
this.initRenderEvent();
|
|
38
44
|
this.initActionEvent();
|
|
39
45
|
this.initMaskEvent();
|
|
@@ -42,7 +48,7 @@ var StageCore = class extends EventEmitter {
|
|
|
42
48
|
* 单选选中元素
|
|
43
49
|
* @param id 选中的id
|
|
44
50
|
*/
|
|
45
|
-
async select(id, event) {
|
|
51
|
+
async select(id, event, options = {}) {
|
|
46
52
|
if (!this.renderer) return;
|
|
47
53
|
let el = this.renderer.getTargetElement(id) || null;
|
|
48
54
|
if (!el) el = await new Promise((resolve) => {
|
|
@@ -73,6 +79,8 @@ var StageCore = class extends EventEmitter {
|
|
|
73
79
|
if (el) this.mask?.setLayout(el);
|
|
74
80
|
this.actionManager?.select(el, event);
|
|
75
81
|
if (el && (this.autoScrollIntoView || el.dataset.autoScrollIntoView)) this.mask?.observerIntersection(el);
|
|
82
|
+
const isPage = el === this.mask?.page || el?.className.includes("magic-ui-page");
|
|
83
|
+
if (el && !event && options.flash !== false && !this.disabledFlashTip && !isPage) this.flashHighlight?.flash(el);
|
|
76
84
|
}
|
|
77
85
|
/**
|
|
78
86
|
* 多选选中多个元素
|
|
@@ -198,16 +206,18 @@ var StageCore = class extends EventEmitter {
|
|
|
198
206
|
* 销毁实例
|
|
199
207
|
*/
|
|
200
208
|
destroy() {
|
|
201
|
-
const { mask, renderer, actionManager, pageResizeObserver } = this;
|
|
209
|
+
const { mask, renderer, actionManager, flashHighlight, pageResizeObserver } = this;
|
|
202
210
|
renderer?.destroy();
|
|
203
211
|
mask?.destroy();
|
|
204
212
|
actionManager?.destroy();
|
|
213
|
+
flashHighlight?.destroy();
|
|
205
214
|
pageResizeObserver?.disconnect();
|
|
206
215
|
this.removeAllListeners();
|
|
207
216
|
this.container = void 0;
|
|
208
217
|
this.renderer = null;
|
|
209
218
|
this.mask = null;
|
|
210
219
|
this.actionManager = null;
|
|
220
|
+
this.flashHighlight = null;
|
|
211
221
|
this.pageResizeObserver = null;
|
|
212
222
|
}
|
|
213
223
|
on(eventName, listener) {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ZIndex } from "./const.js";
|
|
2
|
+
import { getOffset } from "./util.js";
|
|
3
|
+
import { getDocument, injectStyle } from "@tmagic/core";
|
|
4
|
+
//#region packages/stage/src/StageFlashHighlight.ts
|
|
5
|
+
/** 闪烁提示节点的 class name */
|
|
6
|
+
var FLASH_TIP_CLASS_NAME = "tmagic-stage-flash-tip";
|
|
7
|
+
/** 闪烁动画名称 */
|
|
8
|
+
var FLASH_ANIMATION_NAME = "tmagic-stage-flash";
|
|
9
|
+
/** 闪烁动画时长(ms) */
|
|
10
|
+
var FLASH_DURATION = 1500;
|
|
11
|
+
/**
|
|
12
|
+
* 选中区域闪烁提示
|
|
13
|
+
* @description 当组件不是通过点击画布选中(如从图层树、面包屑等外部选中)时,在画布上对选中区域做一次高亮闪烁,
|
|
14
|
+
* 帮助用户快速定位组件在画布中的位置。
|
|
15
|
+
*/
|
|
16
|
+
var StageFlashHighlight = class {
|
|
17
|
+
/** 闪烁节点挂载的容器(蒙层的content) */
|
|
18
|
+
container;
|
|
19
|
+
el;
|
|
20
|
+
timer;
|
|
21
|
+
styleEl;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.container = config.container;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 在目标元素所在区域做一次高亮闪烁
|
|
27
|
+
* @param el 选中组件的Dom节点元素
|
|
28
|
+
*/
|
|
29
|
+
flash(el) {
|
|
30
|
+
if (!el) return;
|
|
31
|
+
this.injectStyle();
|
|
32
|
+
this.clear();
|
|
33
|
+
const offset = getOffset(el);
|
|
34
|
+
const { transform } = getComputedStyle(el);
|
|
35
|
+
const flashEl = globalThis.document.createElement("div");
|
|
36
|
+
flashEl.className = FLASH_TIP_CLASS_NAME;
|
|
37
|
+
flashEl.style.cssText = `
|
|
38
|
+
position: absolute;
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
pointer-events: none;
|
|
41
|
+
transform: ${transform};
|
|
42
|
+
left: ${offset.left}px;
|
|
43
|
+
top: ${offset.top}px;
|
|
44
|
+
width: ${el.clientWidth}px;
|
|
45
|
+
height: ${el.clientHeight}px;
|
|
46
|
+
z-index: ${ZIndex.SELECTED_EL};
|
|
47
|
+
`;
|
|
48
|
+
this.container.appendChild(flashEl);
|
|
49
|
+
this.el = flashEl;
|
|
50
|
+
this.timer = globalThis.setTimeout(() => {
|
|
51
|
+
this.clear();
|
|
52
|
+
}, FLASH_DURATION);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 清除闪烁节点
|
|
56
|
+
*/
|
|
57
|
+
clear() {
|
|
58
|
+
if (this.timer) {
|
|
59
|
+
globalThis.clearTimeout(this.timer);
|
|
60
|
+
this.timer = void 0;
|
|
61
|
+
}
|
|
62
|
+
this.el?.remove();
|
|
63
|
+
this.el = void 0;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 销毁实例
|
|
67
|
+
*/
|
|
68
|
+
destroy() {
|
|
69
|
+
this.clear();
|
|
70
|
+
this.styleEl?.remove();
|
|
71
|
+
this.styleEl = void 0;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 注入闪烁动画样式(仅注入一次)
|
|
75
|
+
*/
|
|
76
|
+
injectStyle() {
|
|
77
|
+
if (this.styleEl) return;
|
|
78
|
+
this.styleEl = injectStyle(getDocument(), `
|
|
79
|
+
@keyframes ${FLASH_ANIMATION_NAME} {
|
|
80
|
+
0% {
|
|
81
|
+
opacity: 0;
|
|
82
|
+
background-color: rgba(146, 84, 222, 0);
|
|
83
|
+
}
|
|
84
|
+
20% {
|
|
85
|
+
opacity: 1;
|
|
86
|
+
background-color: rgba(146, 84, 222, 0.7);
|
|
87
|
+
}
|
|
88
|
+
45% {
|
|
89
|
+
opacity: 0.6;
|
|
90
|
+
background-color: rgba(146, 84, 222, 0.4);
|
|
91
|
+
}
|
|
92
|
+
70% {
|
|
93
|
+
opacity: 1;
|
|
94
|
+
background-color: rgba(146, 84, 222, 0.7);
|
|
95
|
+
}
|
|
96
|
+
100% {
|
|
97
|
+
opacity: 0;
|
|
98
|
+
background-color: rgba(146, 84, 222, 0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
.${FLASH_TIP_CLASS_NAME} {
|
|
102
|
+
border: 2px solid #9254de;
|
|
103
|
+
border-radius: 2px;
|
|
104
|
+
animation: ${FLASH_ANIMATION_NAME} ${FLASH_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1) both;
|
|
105
|
+
}
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
110
|
+
export { StageFlashHighlight as default };
|
|
@@ -3993,6 +3993,112 @@
|
|
|
3993
3993
|
};
|
|
3994
3994
|
};
|
|
3995
3995
|
//#endregion
|
|
3996
|
+
//#region packages/stage/src/StageFlashHighlight.ts
|
|
3997
|
+
/** 闪烁提示节点的 class name */
|
|
3998
|
+
var FLASH_TIP_CLASS_NAME = "tmagic-stage-flash-tip";
|
|
3999
|
+
/** 闪烁动画名称 */
|
|
4000
|
+
var FLASH_ANIMATION_NAME = "tmagic-stage-flash";
|
|
4001
|
+
/** 闪烁动画时长(ms) */
|
|
4002
|
+
var FLASH_DURATION = 1500;
|
|
4003
|
+
/**
|
|
4004
|
+
* 选中区域闪烁提示
|
|
4005
|
+
* @description 当组件不是通过点击画布选中(如从图层树、面包屑等外部选中)时,在画布上对选中区域做一次高亮闪烁,
|
|
4006
|
+
* 帮助用户快速定位组件在画布中的位置。
|
|
4007
|
+
*/
|
|
4008
|
+
var StageFlashHighlight = class {
|
|
4009
|
+
/** 闪烁节点挂载的容器(蒙层的content) */
|
|
4010
|
+
container;
|
|
4011
|
+
el;
|
|
4012
|
+
timer;
|
|
4013
|
+
styleEl;
|
|
4014
|
+
constructor(config) {
|
|
4015
|
+
this.container = config.container;
|
|
4016
|
+
}
|
|
4017
|
+
/**
|
|
4018
|
+
* 在目标元素所在区域做一次高亮闪烁
|
|
4019
|
+
* @param el 选中组件的Dom节点元素
|
|
4020
|
+
*/
|
|
4021
|
+
flash(el) {
|
|
4022
|
+
if (!el) return;
|
|
4023
|
+
this.injectStyle();
|
|
4024
|
+
this.clear();
|
|
4025
|
+
const offset = getOffset(el);
|
|
4026
|
+
const { transform } = getComputedStyle(el);
|
|
4027
|
+
const flashEl = globalThis.document.createElement("div");
|
|
4028
|
+
flashEl.className = FLASH_TIP_CLASS_NAME;
|
|
4029
|
+
flashEl.style.cssText = `
|
|
4030
|
+
position: absolute;
|
|
4031
|
+
box-sizing: border-box;
|
|
4032
|
+
pointer-events: none;
|
|
4033
|
+
transform: ${transform};
|
|
4034
|
+
left: ${offset.left}px;
|
|
4035
|
+
top: ${offset.top}px;
|
|
4036
|
+
width: ${el.clientWidth}px;
|
|
4037
|
+
height: ${el.clientHeight}px;
|
|
4038
|
+
z-index: ${ZIndex.SELECTED_EL};
|
|
4039
|
+
`;
|
|
4040
|
+
this.container.appendChild(flashEl);
|
|
4041
|
+
this.el = flashEl;
|
|
4042
|
+
this.timer = globalThis.setTimeout(() => {
|
|
4043
|
+
this.clear();
|
|
4044
|
+
}, FLASH_DURATION);
|
|
4045
|
+
}
|
|
4046
|
+
/**
|
|
4047
|
+
* 清除闪烁节点
|
|
4048
|
+
*/
|
|
4049
|
+
clear() {
|
|
4050
|
+
if (this.timer) {
|
|
4051
|
+
globalThis.clearTimeout(this.timer);
|
|
4052
|
+
this.timer = void 0;
|
|
4053
|
+
}
|
|
4054
|
+
this.el?.remove();
|
|
4055
|
+
this.el = void 0;
|
|
4056
|
+
}
|
|
4057
|
+
/**
|
|
4058
|
+
* 销毁实例
|
|
4059
|
+
*/
|
|
4060
|
+
destroy() {
|
|
4061
|
+
this.clear();
|
|
4062
|
+
this.styleEl?.remove();
|
|
4063
|
+
this.styleEl = void 0;
|
|
4064
|
+
}
|
|
4065
|
+
/**
|
|
4066
|
+
* 注入闪烁动画样式(仅注入一次)
|
|
4067
|
+
*/
|
|
4068
|
+
injectStyle() {
|
|
4069
|
+
if (this.styleEl) return;
|
|
4070
|
+
this.styleEl = (0, _tmagic_core.injectStyle)((0, _tmagic_core.getDocument)(), `
|
|
4071
|
+
@keyframes ${FLASH_ANIMATION_NAME} {
|
|
4072
|
+
0% {
|
|
4073
|
+
opacity: 0;
|
|
4074
|
+
background-color: rgba(146, 84, 222, 0);
|
|
4075
|
+
}
|
|
4076
|
+
20% {
|
|
4077
|
+
opacity: 1;
|
|
4078
|
+
background-color: rgba(146, 84, 222, 0.7);
|
|
4079
|
+
}
|
|
4080
|
+
45% {
|
|
4081
|
+
opacity: 0.6;
|
|
4082
|
+
background-color: rgba(146, 84, 222, 0.4);
|
|
4083
|
+
}
|
|
4084
|
+
70% {
|
|
4085
|
+
opacity: 1;
|
|
4086
|
+
background-color: rgba(146, 84, 222, 0.7);
|
|
4087
|
+
}
|
|
4088
|
+
100% {
|
|
4089
|
+
opacity: 0;
|
|
4090
|
+
background-color: rgba(146, 84, 222, 0);
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
.${FLASH_TIP_CLASS_NAME} {
|
|
4094
|
+
border: 2px solid #9254de;
|
|
4095
|
+
border-radius: 2px;
|
|
4096
|
+
animation: ${FLASH_ANIMATION_NAME} ${FLASH_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1) both;
|
|
4097
|
+
}
|
|
4098
|
+
`);
|
|
4099
|
+
}
|
|
4100
|
+
};
|
|
4101
|
+
//#endregion
|
|
3996
4102
|
//#region packages/stage/src/Rule.ts
|
|
3997
4103
|
var guidesClass = "tmagic-stage-guides";
|
|
3998
4104
|
var Rule = class extends events.default {
|
|
@@ -4586,13 +4692,17 @@
|
|
|
4586
4692
|
renderer = null;
|
|
4587
4693
|
mask = null;
|
|
4588
4694
|
actionManager = null;
|
|
4695
|
+
flashHighlight = null;
|
|
4589
4696
|
pageResizeObserver = null;
|
|
4590
4697
|
autoScrollIntoView;
|
|
4591
4698
|
customizedRender;
|
|
4699
|
+
/** 非点击画布选中组件时,是否对选中区域做高亮闪烁提示,默认开启 */
|
|
4700
|
+
disabledFlashTip;
|
|
4592
4701
|
constructor(config) {
|
|
4593
4702
|
super();
|
|
4594
4703
|
this.autoScrollIntoView = config.autoScrollIntoView;
|
|
4595
4704
|
this.customizedRender = config.render;
|
|
4705
|
+
this.disabledFlashTip = config.disabledFlashTip ?? false;
|
|
4596
4706
|
this.renderer = new StageRender({
|
|
4597
4707
|
runtimeUrl: config.runtimeUrl,
|
|
4598
4708
|
zoom: config.zoom,
|
|
@@ -4607,6 +4717,7 @@
|
|
|
4607
4717
|
disabledRule: config.disabledRule
|
|
4608
4718
|
});
|
|
4609
4719
|
this.actionManager = new ActionManager(this.getActionManagerConfig(config));
|
|
4720
|
+
this.flashHighlight = new StageFlashHighlight({ container: this.mask.content });
|
|
4610
4721
|
this.initRenderEvent();
|
|
4611
4722
|
this.initActionEvent();
|
|
4612
4723
|
this.initMaskEvent();
|
|
@@ -4615,7 +4726,7 @@
|
|
|
4615
4726
|
* 单选选中元素
|
|
4616
4727
|
* @param id 选中的id
|
|
4617
4728
|
*/
|
|
4618
|
-
async select(id, event) {
|
|
4729
|
+
async select(id, event, options = {}) {
|
|
4619
4730
|
if (!this.renderer) return;
|
|
4620
4731
|
let el = this.renderer.getTargetElement(id) || null;
|
|
4621
4732
|
if (!el) el = await new Promise((resolve) => {
|
|
@@ -4646,6 +4757,8 @@
|
|
|
4646
4757
|
if (el) this.mask?.setLayout(el);
|
|
4647
4758
|
this.actionManager?.select(el, event);
|
|
4648
4759
|
if (el && (this.autoScrollIntoView || el.dataset.autoScrollIntoView)) this.mask?.observerIntersection(el);
|
|
4760
|
+
const isPage = el === this.mask?.page || el?.className.includes("magic-ui-page");
|
|
4761
|
+
if (el && !event && options.flash !== false && !this.disabledFlashTip && !isPage) this.flashHighlight?.flash(el);
|
|
4649
4762
|
}
|
|
4650
4763
|
/**
|
|
4651
4764
|
* 多选选中多个元素
|
|
@@ -4771,16 +4884,18 @@
|
|
|
4771
4884
|
* 销毁实例
|
|
4772
4885
|
*/
|
|
4773
4886
|
destroy() {
|
|
4774
|
-
const { mask, renderer, actionManager, pageResizeObserver } = this;
|
|
4887
|
+
const { mask, renderer, actionManager, flashHighlight, pageResizeObserver } = this;
|
|
4775
4888
|
renderer?.destroy();
|
|
4776
4889
|
mask?.destroy();
|
|
4777
4890
|
actionManager?.destroy();
|
|
4891
|
+
flashHighlight?.destroy();
|
|
4778
4892
|
pageResizeObserver?.disconnect();
|
|
4779
4893
|
this.removeAllListeners();
|
|
4780
4894
|
this.container = void 0;
|
|
4781
4895
|
this.renderer = null;
|
|
4782
4896
|
this.mask = null;
|
|
4783
4897
|
this.actionManager = null;
|
|
4898
|
+
this.flashHighlight = null;
|
|
4784
4899
|
this.pageResizeObserver = null;
|
|
4785
4900
|
}
|
|
4786
4901
|
on(eventName, listener) {
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.3",
|
|
3
3
|
"name": "@tmagic/stage",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"typescript": "^6.0.3",
|
|
47
|
-
"@tmagic/core": "1.8.0-beta.
|
|
47
|
+
"@tmagic/core": "1.8.0-beta.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|
|
50
50
|
"typescript": {
|
package/src/StageCore.ts
CHANGED
|
@@ -25,7 +25,8 @@ import type { Id } from '@tmagic/core';
|
|
|
25
25
|
import { getIdFromEl } from '@tmagic/core';
|
|
26
26
|
|
|
27
27
|
import ActionManager from './ActionManager';
|
|
28
|
-
import { DEFAULT_ZOOM } from './const';
|
|
28
|
+
import { DEFAULT_ZOOM, PAGE_CLASS } from './const';
|
|
29
|
+
import StageFlashHighlight from './StageFlashHighlight';
|
|
29
30
|
import StageMask from './StageMask';
|
|
30
31
|
import StageRender from './StageRender';
|
|
31
32
|
import type {
|
|
@@ -51,16 +52,20 @@ export default class StageCore extends EventEmitter {
|
|
|
51
52
|
public renderer: StageRender | null = null;
|
|
52
53
|
public mask: StageMask | null = null;
|
|
53
54
|
public actionManager: ActionManager | null = null;
|
|
55
|
+
public flashHighlight: StageFlashHighlight | null = null;
|
|
54
56
|
|
|
55
57
|
private pageResizeObserver: ResizeObserver | null = null;
|
|
56
58
|
private autoScrollIntoView: boolean | undefined;
|
|
57
59
|
private customizedRender?: CustomizeRender;
|
|
60
|
+
/** 非点击画布选中组件时,是否对选中区域做高亮闪烁提示,默认开启 */
|
|
61
|
+
private disabledFlashTip: boolean;
|
|
58
62
|
|
|
59
63
|
constructor(config: StageCoreConfig) {
|
|
60
64
|
super();
|
|
61
65
|
|
|
62
66
|
this.autoScrollIntoView = config.autoScrollIntoView;
|
|
63
67
|
this.customizedRender = config.render;
|
|
68
|
+
this.disabledFlashTip = config.disabledFlashTip ?? false;
|
|
64
69
|
|
|
65
70
|
this.renderer = new StageRender({
|
|
66
71
|
runtimeUrl: config.runtimeUrl,
|
|
@@ -78,6 +83,7 @@ export default class StageCore extends EventEmitter {
|
|
|
78
83
|
disabledRule: config.disabledRule,
|
|
79
84
|
});
|
|
80
85
|
this.actionManager = new ActionManager(this.getActionManagerConfig(config));
|
|
86
|
+
this.flashHighlight = new StageFlashHighlight({ container: this.mask.content });
|
|
81
87
|
|
|
82
88
|
this.initRenderEvent();
|
|
83
89
|
this.initActionEvent();
|
|
@@ -88,7 +94,7 @@ export default class StageCore extends EventEmitter {
|
|
|
88
94
|
* 单选选中元素
|
|
89
95
|
* @param id 选中的id
|
|
90
96
|
*/
|
|
91
|
-
public async select(id: Id, event?: MouseEvent): Promise<void> {
|
|
97
|
+
public async select(id: Id, event?: MouseEvent, options: { flash?: boolean } = {}): Promise<void> {
|
|
92
98
|
if (!this.renderer) {
|
|
93
99
|
return;
|
|
94
100
|
}
|
|
@@ -133,6 +139,13 @@ export default class StageCore extends EventEmitter {
|
|
|
133
139
|
if (el && (this.autoScrollIntoView || el.dataset.autoScrollIntoView)) {
|
|
134
140
|
this.mask?.observerIntersection(el);
|
|
135
141
|
}
|
|
142
|
+
|
|
143
|
+
// 非点击画布选中(如从图层树、面包屑等外部选中,此时没有鼠标事件)时,对选中区域做一次高亮闪烁,帮助用户定位组件
|
|
144
|
+
// 选中页面(mask.page 或带有 magic-ui-page class 的节点)时不做闪烁提示
|
|
145
|
+
const isPage = el === this.mask?.page || el?.className.includes(PAGE_CLASS);
|
|
146
|
+
if (el && !event && options.flash !== false && !this.disabledFlashTip && !isPage) {
|
|
147
|
+
this.flashHighlight?.flash(el);
|
|
148
|
+
}
|
|
136
149
|
}
|
|
137
150
|
|
|
138
151
|
/**
|
|
@@ -300,11 +313,12 @@ export default class StageCore extends EventEmitter {
|
|
|
300
313
|
* 销毁实例
|
|
301
314
|
*/
|
|
302
315
|
public destroy(): void {
|
|
303
|
-
const { mask, renderer, actionManager, pageResizeObserver } = this;
|
|
316
|
+
const { mask, renderer, actionManager, flashHighlight, pageResizeObserver } = this;
|
|
304
317
|
|
|
305
318
|
renderer?.destroy();
|
|
306
319
|
mask?.destroy();
|
|
307
320
|
actionManager?.destroy();
|
|
321
|
+
flashHighlight?.destroy();
|
|
308
322
|
pageResizeObserver?.disconnect();
|
|
309
323
|
|
|
310
324
|
this.removeAllListeners();
|
|
@@ -313,6 +327,7 @@ export default class StageCore extends EventEmitter {
|
|
|
313
327
|
this.renderer = null;
|
|
314
328
|
this.mask = null;
|
|
315
329
|
this.actionManager = null;
|
|
330
|
+
this.flashHighlight = null;
|
|
316
331
|
this.pageResizeObserver = null;
|
|
317
332
|
}
|
|
318
333
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2025 Tencent. 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 { getDocument, injectStyle } from '@tmagic/core';
|
|
20
|
+
|
|
21
|
+
import { ZIndex } from './const';
|
|
22
|
+
import { getOffset } from './util';
|
|
23
|
+
|
|
24
|
+
/** 闪烁提示节点的 class name */
|
|
25
|
+
export const FLASH_TIP_CLASS_NAME = 'tmagic-stage-flash-tip';
|
|
26
|
+
/** 闪烁动画名称 */
|
|
27
|
+
const FLASH_ANIMATION_NAME = 'tmagic-stage-flash';
|
|
28
|
+
/** 闪烁动画时长(ms) */
|
|
29
|
+
const FLASH_DURATION = 1500;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 选中区域闪烁提示
|
|
33
|
+
* @description 当组件不是通过点击画布选中(如从图层树、面包屑等外部选中)时,在画布上对选中区域做一次高亮闪烁,
|
|
34
|
+
* 帮助用户快速定位组件在画布中的位置。
|
|
35
|
+
*/
|
|
36
|
+
export default class StageFlashHighlight {
|
|
37
|
+
/** 闪烁节点挂载的容器(蒙层的content) */
|
|
38
|
+
private container: HTMLElement;
|
|
39
|
+
private el?: HTMLElement;
|
|
40
|
+
private timer?: NodeJS.Timeout;
|
|
41
|
+
private styleEl?: HTMLStyleElement;
|
|
42
|
+
|
|
43
|
+
constructor(config: { container: HTMLElement }) {
|
|
44
|
+
this.container = config.container;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 在目标元素所在区域做一次高亮闪烁
|
|
49
|
+
* @param el 选中组件的Dom节点元素
|
|
50
|
+
*/
|
|
51
|
+
public flash(el: HTMLElement): void {
|
|
52
|
+
if (!el) return;
|
|
53
|
+
|
|
54
|
+
this.injectStyle();
|
|
55
|
+
this.clear();
|
|
56
|
+
|
|
57
|
+
const offset = getOffset(el);
|
|
58
|
+
const { transform } = getComputedStyle(el);
|
|
59
|
+
|
|
60
|
+
const flashEl = globalThis.document.createElement('div');
|
|
61
|
+
flashEl.className = FLASH_TIP_CLASS_NAME;
|
|
62
|
+
flashEl.style.cssText = `
|
|
63
|
+
position: absolute;
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
pointer-events: none;
|
|
66
|
+
transform: ${transform};
|
|
67
|
+
left: ${offset.left}px;
|
|
68
|
+
top: ${offset.top}px;
|
|
69
|
+
width: ${el.clientWidth}px;
|
|
70
|
+
height: ${el.clientHeight}px;
|
|
71
|
+
z-index: ${ZIndex.SELECTED_EL};
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
this.container.appendChild(flashEl);
|
|
75
|
+
this.el = flashEl;
|
|
76
|
+
|
|
77
|
+
this.timer = globalThis.setTimeout(() => {
|
|
78
|
+
this.clear();
|
|
79
|
+
}, FLASH_DURATION);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 清除闪烁节点
|
|
84
|
+
*/
|
|
85
|
+
public clear(): void {
|
|
86
|
+
if (this.timer) {
|
|
87
|
+
globalThis.clearTimeout(this.timer);
|
|
88
|
+
this.timer = undefined;
|
|
89
|
+
}
|
|
90
|
+
this.el?.remove();
|
|
91
|
+
this.el = undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 销毁实例
|
|
96
|
+
*/
|
|
97
|
+
public destroy(): void {
|
|
98
|
+
this.clear();
|
|
99
|
+
this.styleEl?.remove();
|
|
100
|
+
this.styleEl = undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 注入闪烁动画样式(仅注入一次)
|
|
105
|
+
*/
|
|
106
|
+
private injectStyle(): void {
|
|
107
|
+
if (this.styleEl) return;
|
|
108
|
+
|
|
109
|
+
this.styleEl = injectStyle(
|
|
110
|
+
getDocument(),
|
|
111
|
+
`
|
|
112
|
+
@keyframes ${FLASH_ANIMATION_NAME} {
|
|
113
|
+
0% {
|
|
114
|
+
opacity: 0;
|
|
115
|
+
background-color: rgba(146, 84, 222, 0);
|
|
116
|
+
}
|
|
117
|
+
20% {
|
|
118
|
+
opacity: 1;
|
|
119
|
+
background-color: rgba(146, 84, 222, 0.7);
|
|
120
|
+
}
|
|
121
|
+
45% {
|
|
122
|
+
opacity: 0.6;
|
|
123
|
+
background-color: rgba(146, 84, 222, 0.4);
|
|
124
|
+
}
|
|
125
|
+
70% {
|
|
126
|
+
opacity: 1;
|
|
127
|
+
background-color: rgba(146, 84, 222, 0.7);
|
|
128
|
+
}
|
|
129
|
+
100% {
|
|
130
|
+
opacity: 0;
|
|
131
|
+
background-color: rgba(146, 84, 222, 0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
.${FLASH_TIP_CLASS_NAME} {
|
|
135
|
+
border: 2px solid #9254de;
|
|
136
|
+
border-radius: 2px;
|
|
137
|
+
animation: ${FLASH_ANIMATION_NAME} ${FLASH_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1) both;
|
|
138
|
+
}
|
|
139
|
+
`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -90,6 +90,11 @@ export interface StageCoreConfig {
|
|
|
90
90
|
*/
|
|
91
91
|
alwaysMultiSelect?: boolean;
|
|
92
92
|
disabledRule?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* 是否禁用「非点击画布选中组件时,对选中区域做高亮闪烁提示」,默认 false(即默认开启闪烁提示)。
|
|
95
|
+
* 用于从图层树、面包屑等外部选中组件时,帮助用户快速定位组件在画布中的位置。
|
|
96
|
+
*/
|
|
97
|
+
disabledFlashTip?: boolean;
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
export interface ActionManagerConfig {
|
package/types/index.d.ts
CHANGED
|
@@ -215,6 +215,11 @@ interface StageCoreConfig {
|
|
|
215
215
|
*/
|
|
216
216
|
alwaysMultiSelect?: boolean;
|
|
217
217
|
disabledRule?: boolean;
|
|
218
|
+
/**
|
|
219
|
+
* 是否禁用「非点击画布选中组件时,对选中区域做高亮闪烁提示」,默认 false(即默认开启闪烁提示)。
|
|
220
|
+
* 用于从图层树、面包屑等外部选中组件时,帮助用户快速定位组件在画布中的位置。
|
|
221
|
+
*/
|
|
222
|
+
disabledFlashTip?: boolean;
|
|
218
223
|
}
|
|
219
224
|
interface ActionManagerConfig {
|
|
220
225
|
container: HTMLElement;
|
|
@@ -591,6 +596,40 @@ declare class ActionManager extends EventEmitter$1 {
|
|
|
591
596
|
private dblclickHandler;
|
|
592
597
|
}
|
|
593
598
|
//#endregion
|
|
599
|
+
//#region temp/packages/stage/src/StageFlashHighlight.d.ts
|
|
600
|
+
/**
|
|
601
|
+
* 选中区域闪烁提示
|
|
602
|
+
* @description 当组件不是通过点击画布选中(如从图层树、面包屑等外部选中)时,在画布上对选中区域做一次高亮闪烁,
|
|
603
|
+
* 帮助用户快速定位组件在画布中的位置。
|
|
604
|
+
*/
|
|
605
|
+
declare class StageFlashHighlight {
|
|
606
|
+
/** 闪烁节点挂载的容器(蒙层的content) */
|
|
607
|
+
private container;
|
|
608
|
+
private el?;
|
|
609
|
+
private timer?;
|
|
610
|
+
private styleEl?;
|
|
611
|
+
constructor(config: {
|
|
612
|
+
container: HTMLElement;
|
|
613
|
+
});
|
|
614
|
+
/**
|
|
615
|
+
* 在目标元素所在区域做一次高亮闪烁
|
|
616
|
+
* @param el 选中组件的Dom节点元素
|
|
617
|
+
*/
|
|
618
|
+
flash(el: HTMLElement): void;
|
|
619
|
+
/**
|
|
620
|
+
* 清除闪烁节点
|
|
621
|
+
*/
|
|
622
|
+
clear(): void;
|
|
623
|
+
/**
|
|
624
|
+
* 销毁实例
|
|
625
|
+
*/
|
|
626
|
+
destroy(): void;
|
|
627
|
+
/**
|
|
628
|
+
* 注入闪烁动画样式(仅注入一次)
|
|
629
|
+
*/
|
|
630
|
+
private injectStyle;
|
|
631
|
+
}
|
|
632
|
+
//#endregion
|
|
594
633
|
//#region temp/packages/stage/src/Rule.d.ts
|
|
595
634
|
declare class Rule extends EventEmitter$1 {
|
|
596
635
|
hGuides?: Guides;
|
|
@@ -790,15 +829,20 @@ declare class StageCore extends EventEmitter {
|
|
|
790
829
|
renderer: StageRender | null;
|
|
791
830
|
mask: StageMask | null;
|
|
792
831
|
actionManager: ActionManager | null;
|
|
832
|
+
flashHighlight: StageFlashHighlight | null;
|
|
793
833
|
private pageResizeObserver;
|
|
794
834
|
private autoScrollIntoView;
|
|
795
835
|
private customizedRender?;
|
|
836
|
+
/** 非点击画布选中组件时,是否对选中区域做高亮闪烁提示,默认开启 */
|
|
837
|
+
private disabledFlashTip;
|
|
796
838
|
constructor(config: StageCoreConfig);
|
|
797
839
|
/**
|
|
798
840
|
* 单选选中元素
|
|
799
841
|
* @param id 选中的id
|
|
800
842
|
*/
|
|
801
|
-
select(id: Id, event?: MouseEvent
|
|
843
|
+
select(id: Id, event?: MouseEvent, options?: {
|
|
844
|
+
flash?: boolean;
|
|
845
|
+
}): Promise<void>;
|
|
802
846
|
/**
|
|
803
847
|
* 多选选中多个元素
|
|
804
848
|
* @param ids 选中元素的id列表
|