@tmagic/stage 1.2.0-beta.2 → 1.2.0-beta.21

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.
Files changed (41) hide show
  1. package/README.md +62 -1
  2. package/dist/tmagic-stage.js +2157 -0
  3. package/dist/tmagic-stage.js.map +1 -0
  4. package/dist/{tmagic-stage.umd.js → tmagic-stage.umd.cjs} +1436 -993
  5. package/dist/tmagic-stage.umd.cjs.map +1 -0
  6. package/package.json +9 -8
  7. package/src/ActionManager.ts +534 -0
  8. package/src/DragResizeHelper.ts +338 -0
  9. package/src/MoveableOptionsManager.ts +249 -0
  10. package/src/MoveableSelectParentAble.ts +76 -0
  11. package/src/Rule.ts +13 -9
  12. package/src/StageCore.ts +220 -260
  13. package/src/StageDragResize.ts +83 -339
  14. package/src/StageHighlight.ts +17 -16
  15. package/src/StageMask.ts +88 -140
  16. package/src/StageMultiDragResize.ts +97 -198
  17. package/src/StageRender.ts +90 -15
  18. package/src/TargetShadow.ts +120 -0
  19. package/src/const.ts +1 -1
  20. package/src/types.ts +97 -19
  21. package/src/util.ts +24 -11
  22. package/types/ActionManager.d.ts +141 -0
  23. package/types/DragResizeHelper.d.ts +70 -0
  24. package/types/MoveableOptionsManager.d.ts +86 -0
  25. package/types/MoveableSelectParentAble.d.ts +8 -0
  26. package/types/Rule.d.ts +4 -3
  27. package/types/StageCore.d.ts +65 -39
  28. package/types/StageDragResize.d.ts +11 -40
  29. package/types/StageHighlight.d.ts +3 -4
  30. package/types/StageMask.d.ts +23 -22
  31. package/types/StageMultiDragResize.d.ts +8 -24
  32. package/types/StageRender.d.ts +24 -6
  33. package/types/TargetShadow.d.ts +22 -0
  34. package/types/const.d.ts +1 -1
  35. package/types/types.d.ts +85 -18
  36. package/types/util.d.ts +9 -8
  37. package/dist/tmagic-stage.mjs +0 -1715
  38. package/dist/tmagic-stage.mjs.map +0 -1
  39. package/dist/tmagic-stage.umd.js.map +0 -1
  40. package/src/TargetCalibrate.ts +0 -119
  41. package/types/TargetCalibrate.d.ts +0 -20
package/src/StageMask.ts CHANGED
@@ -16,22 +16,16 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import KeyController from 'keycon';
20
- import { throttle } from 'lodash-es';
19
+ import { createDiv, getDocument, injectStyle } from '@tmagic/utils';
21
20
 
22
- import { createDiv, injectStyle } from '@tmagic/utils';
23
-
24
- import { Mode, MouseButton, ZIndex } from './const';
21
+ import { Mode, ZIndex } from './const';
25
22
  import Rule from './Rule';
26
- import type StageCore from './StageCore';
27
- import type { StageMaskConfig } from './types';
28
23
  import { getScrollParent, isFixedParent } from './util';
29
24
 
30
25
  const wrapperClassName = 'editor-mask-wrapper';
31
- const throttleTime = 100;
32
26
 
33
27
  const hideScrollbar = () => {
34
- injectStyle(globalThis.document, `.${wrapperClassName}::-webkit-scrollbar { width: 0 !important; display: none }`);
28
+ injectStyle(getDocument(), `.${wrapperClassName}::-webkit-scrollbar { width: 0 !important; display: none }`);
35
29
  };
36
30
 
37
31
  const createContent = (): HTMLDivElement =>
@@ -71,9 +65,7 @@ const createWrapper = (): HTMLDivElement => {
71
65
  export default class StageMask extends Rule {
72
66
  public content: HTMLDivElement = createContent();
73
67
  public wrapper: HTMLDivElement;
74
- public core: StageCore;
75
68
  public page: HTMLElement | null = null;
76
- public pageScrollParent: HTMLElement | null = null;
77
69
  public scrollTop = 0;
78
70
  public scrollLeft = 0;
79
71
  public width = 0;
@@ -82,54 +74,28 @@ export default class StageMask extends Rule {
82
74
  public wrapperWidth = 0;
83
75
  public maxScrollTop = 0;
84
76
  public maxScrollLeft = 0;
85
- public intersectionObserver: IntersectionObserver | null = null;
86
- public isMultiSelectStatus: Boolean = false;
87
77
 
88
78
  private mode: Mode = Mode.ABSOLUTE;
89
- private pageResizeObserver: ResizeObserver | null = null;
79
+ private pageScrollParent: HTMLElement | null = null;
80
+ private intersectionObserver: IntersectionObserver | null = null;
90
81
  private wrapperResizeObserver: ResizeObserver | null = null;
91
- /**
92
- * 高亮事件处理函数
93
- * @param event 事件对象
94
- */
95
- private highlightHandler = throttle((event: MouseEvent): void => {
96
- this.emit('highlight', event);
97
- }, throttleTime);
98
82
 
99
- constructor(config: StageMaskConfig) {
83
+ constructor() {
100
84
  const wrapper = createWrapper();
101
85
  super(wrapper);
102
86
 
103
87
  this.wrapper = wrapper;
104
- this.core = config.core;
105
88
 
106
- this.content.addEventListener('mousedown', this.mouseDownHandler);
107
- this.wrapper.appendChild(this.content);
108
89
  this.content.addEventListener('wheel', this.mouseWheelHandler);
109
- this.content.addEventListener('mousemove', this.highlightHandler);
110
- this.content.addEventListener('mouseleave', this.mouseLeaveHandler);
111
-
112
- const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
113
-
114
- const ctrl = isMac ? 'meta' : 'ctrl';
115
-
116
- KeyController.global.keydown(ctrl, (e) => {
117
- e.inputEvent.preventDefault();
118
- this.isMultiSelectStatus = true;
119
- });
120
- // ctrl+tab切到其他窗口,需要将多选状态置为false
121
- KeyController.global.on('blur', () => {
122
- this.isMultiSelectStatus = false;
123
- });
124
- KeyController.global.keyup(ctrl, (e) => {
125
- e.inputEvent.preventDefault();
126
- this.isMultiSelectStatus = false;
127
- });
90
+ this.wrapper.appendChild(this.content);
128
91
  }
129
92
 
130
93
  public setMode(mode: Mode) {
131
94
  this.mode = mode;
132
95
  this.scroll();
96
+
97
+ this.content.dataset.mode = mode;
98
+
133
99
  if (mode === Mode.FIXED) {
134
100
  this.content.style.width = `${this.wrapperWidth}px`;
135
101
  this.content.style.height = `${this.wrapperHeight}px`;
@@ -140,63 +106,37 @@ export default class StageMask extends Rule {
140
106
  }
141
107
 
142
108
  /**
143
- * 监听页面大小变化
144
- * @description 同步页面与mask的大小
109
+ * 初始化视窗和蒙层监听,监听元素是否在视窗区域、监听mask蒙层所在的wrapper大小变化
110
+ * @description 初始化视窗和蒙层监听
145
111
  * @param page 页面Dom节点
146
112
  */
147
113
  public observe(page: HTMLElement): void {
148
114
  if (!page) return;
149
115
 
150
116
  this.page = page;
151
- this.pageScrollParent = getScrollParent(page) || this.core.renderer.contentWindow?.document.documentElement || null;
152
- this.pageResizeObserver?.disconnect();
153
- this.wrapperResizeObserver?.disconnect();
154
- this.intersectionObserver?.disconnect();
155
-
156
- if (typeof IntersectionObserver !== 'undefined') {
157
- this.intersectionObserver = new IntersectionObserver(
158
- (entries) => {
159
- entries.forEach((entry) => {
160
- const { target, intersectionRatio } = entry;
161
- if (intersectionRatio <= 0) {
162
- this.scrollIntoView(target);
163
- }
164
- this.intersectionObserver?.unobserve(target);
165
- });
166
- },
167
- {
168
- root: this.pageScrollParent,
169
- rootMargin: '0px',
170
- threshold: 1.0,
171
- },
172
- );
173
- }
174
-
175
- if (typeof ResizeObserver !== 'undefined') {
176
- this.pageResizeObserver = new ResizeObserver((entries) => {
177
- const [entry] = entries;
178
- const { clientHeight, clientWidth } = entry.target;
179
- this.setHeight(clientHeight);
180
- this.setWidth(clientWidth);
117
+ this.initObserverIntersection();
118
+ this.initObserverWrapper();
119
+ }
181
120
 
182
- this.scroll();
183
- if (this.core.dr.moveable) {
184
- this.core.dr.updateMoveable();
185
- }
186
- });
121
+ /**
122
+ * 处理页面大小变更,同步页面和mask大小
123
+ * @param entries ResizeObserverEntry,获取页面最新大小
124
+ */
125
+ public pageResize(entries: ResizeObserverEntry[]): void {
126
+ const [entry] = entries;
127
+ const { clientHeight, clientWidth } = entry.target;
128
+ this.setHeight(clientHeight);
129
+ this.setWidth(clientWidth);
187
130
 
188
- this.pageResizeObserver.observe(page);
131
+ this.scroll();
132
+ }
189
133
 
190
- this.wrapperResizeObserver = new ResizeObserver((entries) => {
191
- const [entry] = entries;
192
- const { clientHeight, clientWidth } = entry.target;
193
- this.wrapperHeight = clientHeight;
194
- this.wrapperWidth = clientWidth;
195
- this.setMaxScrollLeft();
196
- this.setMaxScrollTop();
197
- });
198
- this.wrapperResizeObserver.observe(this.wrapper);
199
- }
134
+ /**
135
+ * 监听一个组件是否在画布可视区域内
136
+ * @param el 被选中的组件,可能是左侧目录树中选中的
137
+ */
138
+ public observerIntersection(el: HTMLElement): void {
139
+ this.intersectionObserver?.observe(el);
200
140
  }
201
141
 
202
142
  /**
@@ -228,13 +168,56 @@ export default class StageMask extends Rule {
228
168
  this.content?.remove();
229
169
  this.page = null;
230
170
  this.pageScrollParent = null;
231
- this.pageResizeObserver?.disconnect();
232
171
  this.wrapperResizeObserver?.disconnect();
233
172
 
234
- this.content.removeEventListener('mouseleave', this.mouseLeaveHandler);
235
173
  super.destroy();
236
174
  }
237
175
 
176
+ /**
177
+ * 监听选中元素是否在画布可视区域内,如果目标元素不在可视区域内,通过滚动使该元素出现在可视区域
178
+ */
179
+ private initObserverIntersection(): void {
180
+ this.pageScrollParent = getScrollParent(this.page as HTMLElement) || null;
181
+ this.intersectionObserver?.disconnect();
182
+
183
+ if (typeof IntersectionObserver !== 'undefined') {
184
+ this.intersectionObserver = new IntersectionObserver(
185
+ (entries) => {
186
+ entries.forEach((entry) => {
187
+ const { target, intersectionRatio } = entry;
188
+ if (intersectionRatio <= 0) {
189
+ this.scrollIntoView(target);
190
+ }
191
+ this.intersectionObserver?.unobserve(target);
192
+ });
193
+ },
194
+ {
195
+ root: this.pageScrollParent,
196
+ rootMargin: '0px',
197
+ threshold: 1.0,
198
+ },
199
+ );
200
+ }
201
+ }
202
+
203
+ /**
204
+ * 监听mask的容器大小变化
205
+ */
206
+ private initObserverWrapper(): void {
207
+ this.wrapperResizeObserver?.disconnect();
208
+ if (typeof ResizeObserver !== 'undefined') {
209
+ this.wrapperResizeObserver = new ResizeObserver((entries) => {
210
+ const [entry] = entries;
211
+ const { clientHeight, clientWidth } = entry.target;
212
+ this.wrapperHeight = clientHeight;
213
+ this.wrapperWidth = clientWidth;
214
+ this.setMaxScrollLeft();
215
+ this.setMaxScrollTop();
216
+ });
217
+ this.wrapperResizeObserver.observe(this.wrapper);
218
+ }
219
+ }
220
+
238
221
  private scroll() {
239
222
  this.fixScrollValue();
240
223
 
@@ -258,6 +241,17 @@ export default class StageMask extends Rule {
258
241
 
259
242
  private scrollTo(scrollLeft: number, scrollTop: number): void {
260
243
  this.content.style.transform = `translate3d(${-scrollLeft}px, ${-scrollTop}px, 0)`;
244
+
245
+ const event = new CustomEvent<{
246
+ scrollLeft: number;
247
+ scrollTop: number;
248
+ }>('customScroll', {
249
+ detail: {
250
+ scrollLeft: this.scrollLeft,
251
+ scrollTop: this.scrollTop,
252
+ },
253
+ });
254
+ this.content.dispatchEvent(event);
261
255
  }
262
256
 
263
257
  /**
@@ -305,48 +299,7 @@ export default class StageMask extends Rule {
305
299
  if (this.maxScrollLeft < this.scrollLeft) this.scrollLeft = this.maxScrollLeft;
306
300
  }
307
301
 
308
- /**
309
- * 点击事件处理函数
310
- * @param event 事件对象
311
- */
312
- private mouseDownHandler = (event: MouseEvent): void => {
313
- this.emit('clearHighlight');
314
- event.stopImmediatePropagation();
315
- event.stopPropagation();
316
-
317
- if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT) return;
318
-
319
- // 如果单击多选选中区域,则不需要再触发选中了,而可能是拖动行为
320
- if (!this.isMultiSelectStatus && (event.target as HTMLDivElement).className.indexOf('moveable-area') !== -1) {
321
- return;
322
- }
323
- // 点击对象如果是边框锚点,则可能是resize
324
- if ((event.target as HTMLDivElement).className.indexOf('moveable-control') !== -1) {
325
- return;
326
- }
327
-
328
- this.content.removeEventListener('mousemove', this.highlightHandler);
329
-
330
- // 判断触发多选还是单选
331
- if (this.isMultiSelectStatus) {
332
- this.emit('beforeMultiSelect', event);
333
- } else {
334
- this.emit('beforeSelect', event);
335
- }
336
- // 如果是右键点击,这里的mouseup事件监听没有效果
337
- globalThis.document.addEventListener('mouseup', this.mouseUpHandler);
338
- };
339
-
340
- private mouseUpHandler = (): void => {
341
- globalThis.document.removeEventListener('mouseup', this.mouseUpHandler);
342
- this.content.addEventListener('mousemove', this.highlightHandler);
343
- if (!this.isMultiSelectStatus) {
344
- this.emit('select');
345
- }
346
- };
347
-
348
302
  private mouseWheelHandler = (event: WheelEvent) => {
349
- this.emit('clearHighlight');
350
303
  if (!this.page) throw new Error('page 未初始化');
351
304
 
352
305
  const { deltaY, deltaX } = event;
@@ -363,11 +316,6 @@ export default class StageMask extends Rule {
363
316
  }
364
317
 
365
318
  this.scroll();
366
-
367
319
  this.emit('scroll', event);
368
320
  };
369
-
370
- private mouseLeaveHandler = () => {
371
- setTimeout(() => this.emit('clearHighlight'), throttleTime);
372
- };
373
321
  }