@tmagic/stage 1.8.0-beta.1 → 1.8.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0-beta.1",
2
+ "version": "1.8.0-beta.10",
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.1"
47
+ "@tmagic/core": "1.8.0-beta.10"
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,10 @@ 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({
87
+ container: this.mask.content,
88
+ updateDragEl: config.updateDragEl,
89
+ });
81
90
 
82
91
  this.initRenderEvent();
83
92
  this.initActionEvent();
@@ -88,7 +97,7 @@ export default class StageCore extends EventEmitter {
88
97
  * 单选选中元素
89
98
  * @param id 选中的id
90
99
  */
91
- public async select(id: Id, event?: MouseEvent): Promise<void> {
100
+ public async select(id: Id, event?: MouseEvent, options: { flash?: boolean } = {}): Promise<void> {
92
101
  if (!this.renderer) {
93
102
  return;
94
103
  }
@@ -133,6 +142,13 @@ export default class StageCore extends EventEmitter {
133
142
  if (el && (this.autoScrollIntoView || el.dataset.autoScrollIntoView)) {
134
143
  this.mask?.observerIntersection(el);
135
144
  }
145
+
146
+ // 非点击画布选中(如从图层树、面包屑等外部选中,此时没有鼠标事件)时,对选中区域做一次高亮闪烁,帮助用户定位组件
147
+ // 选中页面(mask.page 或带有 magic-ui-page class 的节点)时不做闪烁提示
148
+ const isPage = el === this.mask?.page || el?.className.includes(PAGE_CLASS);
149
+ if (el && !event && options.flash !== false && !this.disabledFlashTip && !isPage) {
150
+ this.flashHighlight?.flash(el);
151
+ }
136
152
  }
137
153
 
138
154
  /**
@@ -300,11 +316,12 @@ export default class StageCore extends EventEmitter {
300
316
  * 销毁实例
301
317
  */
302
318
  public destroy(): void {
303
- const { mask, renderer, actionManager, pageResizeObserver } = this;
319
+ const { mask, renderer, actionManager, flashHighlight, pageResizeObserver } = this;
304
320
 
305
321
  renderer?.destroy();
306
322
  mask?.destroy();
307
323
  actionManager?.destroy();
324
+ flashHighlight?.destroy();
308
325
  pageResizeObserver?.disconnect();
309
326
 
310
327
  this.removeAllListeners();
@@ -313,6 +330,7 @@ export default class StageCore extends EventEmitter {
313
330
  this.renderer = null;
314
331
  this.mask = null;
315
332
  this.actionManager = null;
333
+ this.flashHighlight = null;
316
334
  this.pageResizeObserver = null;
317
335
  }
318
336
 
@@ -0,0 +1,135 @@
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 { FLASH_EL_ID_PREFIX, ZIndex } from './const';
22
+ import TargetShadow from './TargetShadow';
23
+ import type { StageFlashHighlightConfig } from './types';
24
+
25
+ /** 闪烁提示节点的 class name */
26
+ export const FLASH_TIP_CLASS_NAME = 'tmagic-stage-flash-tip';
27
+ /** 闪烁动画名称 */
28
+ const FLASH_ANIMATION_NAME = 'tmagic-stage-flash';
29
+ /** 闪烁动画时长(ms) */
30
+ const FLASH_DURATION = 1500;
31
+
32
+ /**
33
+ * 选中区域闪烁提示
34
+ * @description 当组件不是通过点击画布选中(如从图层树、面包屑等外部选中)时,在画布上对选中区域做一次高亮闪烁,
35
+ * 帮助用户快速定位组件在画布中的位置。
36
+ */
37
+ export default class StageFlashHighlight {
38
+ /** 复用 TargetShadow 负责节点的定位校准(updateDragEl、fixed、滚动偏移等) */
39
+ private targetShadow: TargetShadow;
40
+ private timer?: NodeJS.Timeout;
41
+ private styleEl?: HTMLStyleElement;
42
+
43
+ constructor(config: StageFlashHighlightConfig) {
44
+ this.targetShadow = new TargetShadow({
45
+ container: config.container,
46
+ updateDragEl: config.updateDragEl,
47
+ zIndex: ZIndex.SELECTED_EL,
48
+ idPrefix: FLASH_EL_ID_PREFIX,
49
+ });
50
+ }
51
+
52
+ /**
53
+ * 在目标元素所在区域做一次高亮闪烁
54
+ * @param el 选中组件的Dom节点元素
55
+ */
56
+ public flash(el: HTMLElement): void {
57
+ if (!el) return;
58
+
59
+ this.injectStyle();
60
+ // 先销毁旧节点再 update,确保每次都拿到全新节点,让闪烁动画重新播放
61
+ this.clear();
62
+
63
+ const flashEl = this.targetShadow.update(el);
64
+ flashEl.classList.add(FLASH_TIP_CLASS_NAME);
65
+ flashEl.style.boxSizing = 'border-box';
66
+ flashEl.style.pointerEvents = 'none';
67
+ // getTargetElStyle 会写入 target 的内联 border,需清掉以让动画 class 的边框生效
68
+ flashEl.style.border = '';
69
+
70
+ this.timer = globalThis.setTimeout(() => {
71
+ this.clear();
72
+ }, FLASH_DURATION);
73
+ }
74
+
75
+ /**
76
+ * 清除闪烁节点
77
+ */
78
+ public clear(): void {
79
+ if (this.timer) {
80
+ globalThis.clearTimeout(this.timer);
81
+ this.timer = undefined;
82
+ }
83
+ this.targetShadow.destroyEl();
84
+ }
85
+
86
+ /**
87
+ * 销毁实例
88
+ */
89
+ public destroy(): void {
90
+ this.clear();
91
+ this.targetShadow.destroy();
92
+ this.styleEl?.remove();
93
+ this.styleEl = undefined;
94
+ }
95
+
96
+ /**
97
+ * 注入闪烁动画样式(仅注入一次)
98
+ */
99
+ private injectStyle(): void {
100
+ if (this.styleEl) return;
101
+
102
+ this.styleEl = injectStyle(
103
+ getDocument(),
104
+ `
105
+ @keyframes ${FLASH_ANIMATION_NAME} {
106
+ 0% {
107
+ opacity: 0;
108
+ background-color: rgba(146, 84, 222, 0);
109
+ }
110
+ 20% {
111
+ opacity: 1;
112
+ background-color: rgba(146, 84, 222, 0.7);
113
+ }
114
+ 45% {
115
+ opacity: 0.6;
116
+ background-color: rgba(146, 84, 222, 0.4);
117
+ }
118
+ 70% {
119
+ opacity: 1;
120
+ background-color: rgba(146, 84, 222, 0.7);
121
+ }
122
+ 100% {
123
+ opacity: 0;
124
+ background-color: rgba(146, 84, 222, 0);
125
+ }
126
+ }
127
+ .${FLASH_TIP_CLASS_NAME} {
128
+ border: 2px solid #9254de;
129
+ border-radius: 2px;
130
+ animation: ${FLASH_ANIMATION_NAME} ${FLASH_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1) both;
131
+ }
132
+ `,
133
+ );
134
+ }
135
+ }
package/src/const.ts CHANGED
@@ -25,6 +25,9 @@ export const DRAG_EL_ID_PREFIX = 'drag_el_';
25
25
  /** 高亮时需要在蒙层中创建一个占位节点,该节点的id前缀 */
26
26
  export const HIGHLIGHT_EL_ID_PREFIX = 'highlight_el_';
27
27
 
28
+ /** 闪烁提示时需要在蒙层中创建一个占位节点,该节点的id前缀 */
29
+ export const FLASH_EL_ID_PREFIX = 'flash_el_';
30
+
28
31
  export const CONTAINER_HIGHLIGHT_CLASS_NAME = 'tmagic-stage-container-highlight';
29
32
 
30
33
  export const PAGE_CLASS = 'magic-ui-page';
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 {
@@ -264,6 +269,11 @@ export interface TargetShadowConfig {
264
269
  idPrefix?: string;
265
270
  }
266
271
 
272
+ export interface StageFlashHighlightConfig {
273
+ container: HTMLElement;
274
+ updateDragEl?: UpdateDragEl;
275
+ }
276
+
267
277
  export interface RuleOptions {
268
278
  guidesOptions?: Partial<GuidesOptions>;
269
279
  disabledRule?: boolean;
package/types/index.d.ts CHANGED
@@ -13,6 +13,8 @@ declare const GHOST_EL_ID_PREFIX = "ghost_el_";
13
13
  declare const DRAG_EL_ID_PREFIX = "drag_el_";
14
14
  /** 高亮时需要在蒙层中创建一个占位节点,该节点的id前缀 */
15
15
  declare const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
16
+ /** 闪烁提示时需要在蒙层中创建一个占位节点,该节点的id前缀 */
17
+ declare const FLASH_EL_ID_PREFIX = "flash_el_";
16
18
  declare const CONTAINER_HIGHLIGHT_CLASS_NAME = "tmagic-stage-container-highlight";
17
19
  declare const PAGE_CLASS = "magic-ui-page";
18
20
  /** 默认放到缩小倍数 */
@@ -215,6 +217,11 @@ interface StageCoreConfig {
215
217
  */
216
218
  alwaysMultiSelect?: boolean;
217
219
  disabledRule?: boolean;
220
+ /**
221
+ * 是否禁用「非点击画布选中组件时,对选中区域做高亮闪烁提示」,默认 false(即默认开启闪烁提示)。
222
+ * 用于从图层树、面包屑等外部选中组件时,帮助用户快速定位组件在画布中的位置。
223
+ */
224
+ disabledFlashTip?: boolean;
218
225
  }
219
226
  interface ActionManagerConfig {
220
227
  container: HTMLElement;
@@ -363,6 +370,10 @@ interface TargetShadowConfig {
363
370
  updateDragEl?: UpdateDragEl;
364
371
  idPrefix?: string;
365
372
  }
373
+ interface StageFlashHighlightConfig {
374
+ container: HTMLElement;
375
+ updateDragEl?: UpdateDragEl;
376
+ }
366
377
  interface RuleOptions {
367
378
  guidesOptions?: Partial<GuidesOptions$1>;
368
379
  disabledRule?: boolean;
@@ -591,6 +602,37 @@ declare class ActionManager extends EventEmitter$1 {
591
602
  private dblclickHandler;
592
603
  }
593
604
  //#endregion
605
+ //#region temp/packages/stage/src/StageFlashHighlight.d.ts
606
+ /**
607
+ * 选中区域闪烁提示
608
+ * @description 当组件不是通过点击画布选中(如从图层树、面包屑等外部选中)时,在画布上对选中区域做一次高亮闪烁,
609
+ * 帮助用户快速定位组件在画布中的位置。
610
+ */
611
+ declare class StageFlashHighlight {
612
+ /** 复用 TargetShadow 负责节点的定位校准(updateDragEl、fixed、滚动偏移等) */
613
+ private targetShadow;
614
+ private timer?;
615
+ private styleEl?;
616
+ constructor(config: StageFlashHighlightConfig);
617
+ /**
618
+ * 在目标元素所在区域做一次高亮闪烁
619
+ * @param el 选中组件的Dom节点元素
620
+ */
621
+ flash(el: HTMLElement): void;
622
+ /**
623
+ * 清除闪烁节点
624
+ */
625
+ clear(): void;
626
+ /**
627
+ * 销毁实例
628
+ */
629
+ destroy(): void;
630
+ /**
631
+ * 注入闪烁动画样式(仅注入一次)
632
+ */
633
+ private injectStyle;
634
+ }
635
+ //#endregion
594
636
  //#region temp/packages/stage/src/Rule.d.ts
595
637
  declare class Rule extends EventEmitter$1 {
596
638
  hGuides?: Guides;
@@ -790,15 +832,20 @@ declare class StageCore extends EventEmitter {
790
832
  renderer: StageRender | null;
791
833
  mask: StageMask | null;
792
834
  actionManager: ActionManager | null;
835
+ flashHighlight: StageFlashHighlight | null;
793
836
  private pageResizeObserver;
794
837
  private autoScrollIntoView;
795
838
  private customizedRender?;
839
+ /** 非点击画布选中组件时,是否对选中区域做高亮闪烁提示,默认开启 */
840
+ private disabledFlashTip;
796
841
  constructor(config: StageCoreConfig);
797
842
  /**
798
843
  * 单选选中元素
799
844
  * @param id 选中的id
800
845
  */
801
- select(id: Id, event?: MouseEvent): Promise<void>;
846
+ select(id: Id, event?: MouseEvent, options?: {
847
+ flash?: boolean;
848
+ }): Promise<void>;
802
849
  /**
803
850
  * 多选选中多个元素
804
851
  * @param ids 选中元素的id列表
@@ -1102,4 +1149,4 @@ declare const _default: (handler: (type: AbleActionEventType) => void, customize
1102
1149
  render(moveable: MoveableManagerInterface<any, any>, React: Renderer): any;
1103
1150
  };
1104
1151
  //#endregion
1105
- export { AbleActionEventType, AbleCustomizedButton, ActionManagerConfig, ActionManagerEvents, CONTAINER_HIGHLIGHT_CLASS_NAME, CanDropIn, CanSelect, ContainerHighlightType, CoreEvents, CustomizeMoveableOptions, CustomizeMoveableOptionsCallbackConfig, CustomizeMoveableOptionsFunction, CustomizeRender, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, DelayedMarkContainer, DrEvents, DragResizeHelperConfig, GHOST_EL_ID_PREFIX, GetElementsFromPoint, GetRenderDocument, GetRootContainer, GetTargetElement, GuidesEventData, type GuidesOptions, GuidesType, HIGHLIGHT_EL_ID_PREFIX, IsContainer, Magic, MarkContainerEnd, MaskEvents, Mode, MouseButton, _default as MoveableActionsAble, MoveableOptionsManagerConfig, MultiDrEvents, Offset, PAGE_CLASS, Point, Rect, RemoveData, RemoveEventData, RenderEvents, RenderType, RuleOptions, Runtime, RuntimeWindow, SELECTED_CLASS, SelectStatus, SortEventData, StageCoreConfig, StageDragResize, StageDragResizeConfig, StageDragStatus, StageHighlightConfig, StageMask, StageMaskConfig, StageMultiDragResizeConfig, StageRender, StageRenderConfig, TargetElement, TargetShadowConfig, UpdateData, UpdateDragEl, UpdateEventData, ZIndex, addSelectedClassName, StageCore as default, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, up };
1152
+ export { AbleActionEventType, AbleCustomizedButton, ActionManagerConfig, ActionManagerEvents, CONTAINER_HIGHLIGHT_CLASS_NAME, CanDropIn, CanSelect, ContainerHighlightType, CoreEvents, CustomizeMoveableOptions, CustomizeMoveableOptionsCallbackConfig, CustomizeMoveableOptionsFunction, CustomizeRender, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, DelayedMarkContainer, DrEvents, DragResizeHelperConfig, FLASH_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GetElementsFromPoint, GetRenderDocument, GetRootContainer, GetTargetElement, GuidesEventData, type GuidesOptions, GuidesType, HIGHLIGHT_EL_ID_PREFIX, IsContainer, Magic, MarkContainerEnd, MaskEvents, Mode, MouseButton, _default as MoveableActionsAble, MoveableOptionsManagerConfig, MultiDrEvents, Offset, PAGE_CLASS, Point, Rect, RemoveData, RemoveEventData, RenderEvents, RenderType, RuleOptions, Runtime, RuntimeWindow, SELECTED_CLASS, SelectStatus, SortEventData, StageCoreConfig, StageDragResize, StageDragResizeConfig, StageDragStatus, StageFlashHighlightConfig, StageHighlightConfig, StageMask, StageMaskConfig, StageMultiDragResizeConfig, StageRender, StageRenderConfig, TargetElement, TargetShadowConfig, UpdateData, UpdateDragEl, UpdateEventData, ZIndex, addSelectedClassName, StageCore as default, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, up };