@tmagic/stage 1.0.0-beta.9 → 1.0.0-rc.1

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/src/StageMask.ts CHANGED
@@ -81,6 +81,8 @@ export default class StageMask extends Rule {
81
81
  public height = 0;
82
82
  public wrapperHeight = 0;
83
83
  public wrapperWidth = 0;
84
+ public maxScrollTop = 0;
85
+ public maxScrollLeft = 0;
84
86
 
85
87
  private mode: Mode = Mode.ABSOLUTE;
86
88
  private pageResizeObserver: ResizeObserver | null = null;
@@ -104,6 +106,7 @@ export default class StageMask extends Rule {
104
106
  this.wrapper.appendChild(this.content);
105
107
  this.content.addEventListener('wheel', this.mouseWheelHandler);
106
108
  this.content.addEventListener('mousemove', this.highlightHandler);
109
+ this.content.addEventListener('mouseleave', this.mouseLeaveHandler);
107
110
  }
108
111
 
109
112
  public setMode(mode: Mode) {
@@ -136,6 +139,10 @@ export default class StageMask extends Rule {
136
139
  const { clientHeight, clientWidth } = entry.target;
137
140
  this.setHeight(clientHeight);
138
141
  this.setWidth(clientWidth);
142
+
143
+ this.fixScrollValue();
144
+ this.scroll();
145
+ this.core.dr.updateMoveable();
139
146
  });
140
147
 
141
148
  this.pageResizeObserver.observe(page);
@@ -145,6 +152,8 @@ export default class StageMask extends Rule {
145
152
  const { clientHeight, clientWidth } = entry.target;
146
153
  this.wrapperHeight = clientHeight;
147
154
  this.wrapperWidth = clientWidth;
155
+ this.setMaxScrollLeft();
156
+ this.setMaxScrollTop();
148
157
  });
149
158
  this.wrapperResizeObserver.observe(this.wrapper);
150
159
  }
@@ -173,12 +182,21 @@ export default class StageMask extends Rule {
173
182
  this.pageScrollParent = null;
174
183
  this.pageResizeObserver?.disconnect();
175
184
  this.wrapperResizeObserver?.disconnect();
185
+
186
+ this.content.removeEventListener('mouseleave', this.mouseLeaveHandler);
176
187
  super.destroy();
177
188
  }
178
189
 
179
190
  private scroll() {
180
191
  let { scrollLeft, scrollTop } = this;
181
192
 
193
+ if (this.pageScrollParent) {
194
+ this.pageScrollParent.scrollTo({
195
+ top: scrollTop,
196
+ left: scrollLeft,
197
+ });
198
+ }
199
+
182
200
  if (this.mode === Mode.FIXED) {
183
201
  scrollLeft = 0;
184
202
  scrollTop = 0;
@@ -198,6 +216,7 @@ export default class StageMask extends Rule {
198
216
  */
199
217
  private setHeight(height: number): void {
200
218
  this.height = height;
219
+ this.setMaxScrollTop();
201
220
  this.content.style.height = `${height}px`;
202
221
  }
203
222
 
@@ -207,14 +226,42 @@ export default class StageMask extends Rule {
207
226
  */
208
227
  private setWidth(width: number): void {
209
228
  this.width = width;
229
+ this.setMaxScrollLeft();
210
230
  this.content.style.width = `${width}px`;
211
231
  }
212
232
 
233
+ /**
234
+ * 计算并设置最大滚动宽度
235
+ */
236
+ private setMaxScrollLeft(): void {
237
+ this.maxScrollLeft = this.width - this.wrapperWidth;
238
+ }
239
+
240
+ /**
241
+ * 计算并设置最大滚动高度
242
+ */
243
+ private setMaxScrollTop(): void {
244
+ this.maxScrollTop = this.height - this.wrapperHeight;
245
+ }
246
+
247
+ /**
248
+ * 修复滚动距离
249
+ * 由于滚动容器变化等因素,会导致当前滚动的距离不正确
250
+ */
251
+ private fixScrollValue(): void {
252
+ if (this.scrollTop < 0) this.scrollTop = 0;
253
+ if (this.scrollLeft < 0) this.scrollLeft = 0;
254
+ if (this.maxScrollTop < this.scrollTop) this.scrollTop = this.maxScrollTop;
255
+ if (this.maxScrollLeft < this.scrollLeft) this.scrollLeft = this.maxScrollLeft;
256
+ }
257
+
213
258
  /**
214
259
  * 点击事件处理函数
215
260
  * @param event 事件对象
216
261
  */
217
262
  private mouseDownHandler = (event: MouseEvent): void => {
263
+ this.emit('clearHighlight');
264
+
218
265
  event.stopImmediatePropagation();
219
266
  event.stopPropagation();
220
267
 
@@ -225,12 +272,12 @@ export default class StageMask extends Rule {
225
272
  return;
226
273
  }
227
274
 
275
+ this.content.removeEventListener('mousemove', this.highlightHandler);
276
+
228
277
  this.emit('beforeSelect', event);
229
278
 
230
279
  // 如果是右键点击,这里的mouseup事件监听没有效果
231
280
  globalThis.document.addEventListener('mouseup', this.mouseUpHandler);
232
- this.content.removeEventListener('mousemove', this.highlightHandler);
233
- this.emit('clearHighlight');
234
281
  };
235
282
 
236
283
  private mouseUpHandler = (): void => {
@@ -240,42 +287,30 @@ export default class StageMask extends Rule {
240
287
  };
241
288
 
242
289
  private mouseWheelHandler = (event: WheelEvent) => {
290
+ this.emit('clearHighlight');
243
291
  if (!this.page) throw new Error('page 未初始化');
244
292
 
245
293
  const { deltaY, deltaX } = event;
246
- const { height, wrapperHeight, width, wrapperWidth } = this;
247
294
 
248
- const maxScrollTop = height - wrapperHeight;
249
- const maxScrollLeft = width - wrapperWidth;
295
+ if (this.page.clientHeight < this.wrapperHeight && deltaY) return;
296
+ if (this.page.clientWidth < this.wrapperWidth && deltaX) return;
250
297
 
251
- if (maxScrollTop > 0) {
252
- if (deltaY > 0) {
253
- this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
254
- } else {
255
- this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
256
- }
298
+ if (this.maxScrollTop > 0) {
299
+ this.scrollTop = this.scrollTop + deltaY;
257
300
  }
258
301
 
259
- if (width > wrapperWidth) {
260
- if (deltaX > 0) {
261
- this.scrollLeft = this.scrollLeft + Math.min(maxScrollLeft - this.scrollLeft, deltaX);
262
- } else {
263
- this.scrollLeft = Math.max(this.scrollLeft + deltaX, 0);
264
- }
302
+ if (this.maxScrollLeft > 0) {
303
+ this.scrollLeft = this.scrollLeft + deltaX;
265
304
  }
266
305
 
267
- if (this.mode !== Mode.FIXED) {
268
- this.scrollTo(this.scrollLeft, this.scrollTop);
269
- }
306
+ this.fixScrollValue();
270
307
 
271
- if (this.pageScrollParent) {
272
- this.pageScrollParent.scrollTo({
273
- top: this.scrollTop,
274
- left: this.scrollLeft,
275
- });
276
- }
277
308
  this.scroll();
278
309
 
279
310
  this.emit('scroll', event);
280
311
  };
312
+
313
+ private mouseLeaveHandler = () => {
314
+ setTimeout(() => this.emit('clearHighlight'), throttleTime);
315
+ };
281
316
  }
@@ -0,0 +1,135 @@
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 { Mode } from './const';
23
+ import StageDragResize from './StageDragResize';
24
+ import StageMask from './StageMask';
25
+ import type { Offset, TargetCalibrateConfig } from './types';
26
+ import { getMode } from './util';
27
+
28
+ /**
29
+ * 将选中的节点修正定位后,添加一个操作节点到蒙层上
30
+ */
31
+ export default class TargetCalibrate extends EventEmitter {
32
+ public parent: HTMLElement;
33
+ public mask: StageMask;
34
+ public dr: StageDragResize;
35
+ public operationEl: HTMLElement;
36
+
37
+ constructor(config: TargetCalibrateConfig) {
38
+ super();
39
+
40
+ this.parent = config.parent;
41
+ this.mask = config.mask;
42
+ this.dr = config.dr;
43
+
44
+ this.operationEl = globalThis.document.createElement('div');
45
+ this.parent.append(this.operationEl);
46
+ }
47
+
48
+ public update(el: HTMLElement, prefix: String): HTMLElement {
49
+ const { width, height } = el.getBoundingClientRect();
50
+ const { left, top } = this.getOffset(el);
51
+ this.operationEl.style.cssText = `
52
+ position: absolute;
53
+ left: ${left}px;
54
+ top: ${top}px;
55
+ width: ${width}px;
56
+ height: ${height}px;
57
+ `;
58
+
59
+ this.operationEl.id = `${prefix}${el.id}`;
60
+ return this.operationEl;
61
+ }
62
+
63
+ public destroy(): void {
64
+ this.operationEl?.remove();
65
+ }
66
+
67
+ private getOffset(el: HTMLElement): Offset {
68
+ const { transform } = getComputedStyle(el);
69
+ const { offsetParent } = el;
70
+
71
+ let left = el.offsetLeft;
72
+ let top = el.offsetTop;
73
+
74
+ if (transform.indexOf('matrix') > -1) {
75
+ let a = 1;
76
+ let b = 1;
77
+ let c = 1;
78
+ let d = 1;
79
+ let e = 0;
80
+ let f = 0;
81
+ transform.replace(
82
+ /matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/,
83
+ ($0: string, $1: string, $2: string, $3: string, $4: string, $5: string, $6: string): string => {
84
+ a = +$1;
85
+ b = +$2;
86
+ c = +$3;
87
+ d = +$4;
88
+ e = +$5;
89
+ f = +$6;
90
+ return transform;
91
+ },
92
+ );
93
+
94
+ left = a * left + c * top + e;
95
+ top = b * left + d * top + f;
96
+ }
97
+
98
+ if (offsetParent) {
99
+ const parentOffset = this.getOffset(offsetParent as HTMLElement);
100
+ return {
101
+ left: left + parentOffset.left,
102
+ top: top + parentOffset.top,
103
+ };
104
+ }
105
+
106
+ // 选中固定定位元素后editor-mask高度被置为视窗大小
107
+ if (this.dr.mode === Mode.FIXED) {
108
+ // 弹窗的情况
109
+ if (getMode(el) === Mode.FIXED) {
110
+ return {
111
+ left,
112
+ top,
113
+ };
114
+ }
115
+
116
+ return {
117
+ left: left - this.mask.scrollLeft,
118
+ top: top - this.mask.scrollTop,
119
+ };
120
+ }
121
+
122
+ // 无父元素的固定定位需按滚动值计算
123
+ if (getMode(el) === Mode.FIXED) {
124
+ return {
125
+ left: left + this.mask.scrollLeft,
126
+ top: top + this.mask.scrollTop,
127
+ };
128
+ }
129
+
130
+ return {
131
+ left,
132
+ top,
133
+ };
134
+ }
135
+ }
package/src/const.ts CHANGED
@@ -19,6 +19,10 @@
19
19
  // 流式布局下拖动时需要clone一个镜像节点,镜像节点的id前缀
20
20
  export const GHOST_EL_ID_PREFIX = 'ghost_el_';
21
21
 
22
+ export const DRAG_EL_ID_PREFIX = 'drag_el_';
23
+
24
+ export const HIGHLIGHT_EL_ID_PREFIX = 'highlight_el_';
25
+
22
26
  // 默认放到缩小倍数
23
27
  export const DEFAULT_ZOOM = 1;
24
28
 
package/src/types.ts CHANGED
@@ -22,8 +22,10 @@ import { Id, MApp, MNode } from '@tmagic/schema';
22
22
 
23
23
  import { GuidesType } from './const';
24
24
  import StageCore from './StageCore';
25
+ import StageDragResize from './StageDragResize';
26
+ import StageMask from './StageMask';
25
27
 
26
- export type CanSelect = (el: HTMLElement, stop: () => boolean) => boolean | Promise<boolean>;
28
+ export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
27
29
 
28
30
  export type StageCoreConfig = {
29
31
  /** 需要对齐的dom节点的CSS选择器字符串 */
@@ -54,7 +56,6 @@ export type Rect = {
54
56
  width: number;
55
57
  height: number;
56
58
  } & Offset;
57
-
58
59
  export interface Offset {
59
60
  left: number;
60
61
  top: number;
@@ -94,7 +95,7 @@ export interface RemoveData {
94
95
 
95
96
  export interface Runtime {
96
97
  beforeSelect?: (el: HTMLElement) => Promise<boolean> | boolean;
97
- getSnapElements?: (el: HTMLElement) => HTMLElement[];
98
+ getSnapElements?: (el?: HTMLElement) => HTMLElement[];
98
99
  updateRootConfig: (config: MApp) => void;
99
100
  updatePageId?: (id: Id) => void;
100
101
  select?: (id: Id) => Promise<HTMLElement> | HTMLElement;
@@ -119,3 +120,9 @@ export interface StageHighlightConfig {
119
120
  core: StageCore;
120
121
  container: HTMLElement;
121
122
  }
123
+
124
+ export interface TargetCalibrateConfig {
125
+ parent: HTMLElement;
126
+ mask: StageMask;
127
+ dr: StageDragResize;
128
+ }
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "baseUrl": "../..",
5
- },
6
- "exclude": [
7
- "**/dist/**/*"
8
- ],
9
- }
package/vite.config.ts DELETED
@@ -1,27 +0,0 @@
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 { defineConfig } from 'vite';
20
-
21
- import { getBaseConfig } from '../vite-config';
22
-
23
- import pkg from './package.json';
24
-
25
- const deps = Object.keys(pkg.dependencies);
26
-
27
- export default defineConfig(getBaseConfig(deps, 'TMagicStage'));