@tmagic/stage 1.0.0-beta.8 → 1.0.0-beta.9

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.
@@ -21,6 +21,7 @@ import { EventEmitter } from 'events';
21
21
 
22
22
  import type { MoveableOptions } from 'moveable';
23
23
  import Moveable from 'moveable';
24
+ import MoveableHelper from 'moveable-helper';
24
25
 
25
26
  import { GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
26
27
  import StageCore from './StageCore';
@@ -49,6 +50,7 @@ export default class StageDragResize extends EventEmitter {
49
50
  private dragStatus: ActionStatus = ActionStatus.END;
50
51
  private ghostEl: HTMLElement | undefined;
51
52
  private mode: Mode = Mode.ABSOLUTE;
53
+ private moveableHelper?: MoveableHelper;
52
54
 
53
55
  constructor(config: StageDragResizeConfig) {
54
56
  super();
@@ -79,6 +81,12 @@ export default class StageDragResize extends EventEmitter {
79
81
  target: this.dragEl || this.target,
80
82
  });
81
83
 
84
+ this.moveableHelper = MoveableHelper.create({
85
+ useBeforeRender: true,
86
+ useRender: false,
87
+ createAuto: true,
88
+ });
89
+
82
90
  this.initMoveable();
83
91
 
84
92
  if (event) {
@@ -188,34 +196,42 @@ export default class StageDragResize extends EventEmitter {
188
196
  private bindDragEvent(): void {
189
197
  if (!this.moveable) throw new Error('moveable 为初始化');
190
198
 
199
+ let offset = {
200
+ left: 0,
201
+ top: 0,
202
+ };
203
+
191
204
  this.moveable
192
- .on('dragStart', () => {
205
+ .on('dragStart', (e) => {
193
206
  if (!this.target) throw new Error('未选中组件');
194
207
 
195
208
  this.dragStatus = ActionStatus.START;
196
209
 
210
+ this.moveableHelper?.onDragStart(e);
211
+
212
+ offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
213
+
197
214
  if (this.mode === Mode.SORTABLE) {
198
215
  this.ghostEl = this.generateGhostEl(this.target);
199
216
  }
200
217
  })
201
- .on('drag', ({ left, top }) => {
218
+ .on('drag', (e) => {
202
219
  if (!this.target || !this.dragEl) return;
203
220
  this.dragStatus = ActionStatus.ING;
204
221
 
205
- const offset = getAbsolutePosition(this.target, { left, top });
222
+ const { left, top } = e;
206
223
 
207
224
  // 流式布局
208
225
  if (this.ghostEl) {
209
226
  this.dragEl.style.top = `${top}px`;
210
- this.ghostEl.style.top = `${offset.top}px`;
227
+ this.ghostEl.style.top = `${top + offset.top}px`;
211
228
  return;
212
229
  }
213
230
 
214
- this.dragEl.style.left = `${left}px`;
215
- this.dragEl.style.top = `${top}px`;
231
+ this.moveableHelper?.onDrag(e);
216
232
 
217
- this.target.style.left = `${offset.left}px`;
218
- this.target.style.top = `${offset.top}px`;
233
+ this.target.style.left = `${left + offset.left}px`;
234
+ this.target.style.top = `${top + offset.top}px`;
219
235
  })
220
236
  .on('dragEnd', () => {
221
237
  // 点击不拖动时会触发dragStart和dragEnd,但是不会有drag事件
@@ -342,18 +358,26 @@ export default class StageDragResize extends EventEmitter {
342
358
  }
343
359
 
344
360
  return {
345
- scrollable: true,
346
361
  origin: true,
362
+ rootContainer: this.core.container,
347
363
  zoom: 1,
348
- dragArea: true,
364
+ dragArea: false,
349
365
  draggable: true,
350
366
  resizable: true,
351
367
  snappable: isAbsolute,
352
368
  snapGap: isAbsolute,
353
- snapDirections: { center: isAbsolute, middle: isAbsolute },
354
- elementSnapDirections: { center: isAbsolute, middle: isAbsolute },
355
-
356
- elementGuidelines: isAbsolute ? await this.getSnapElements(this.target) : [],
369
+ snapThreshold: 5,
370
+ snapDigit: 0,
371
+ throttleDrag: 0,
372
+ isDisplaySnapDigit: isAbsolute,
373
+ snapDirections: {
374
+ top: isAbsolute,
375
+ right: isAbsolute,
376
+ bottom: isAbsolute,
377
+ left: isAbsolute,
378
+ center: isAbsolute,
379
+ middle: isAbsolute,
380
+ },
357
381
  horizontalGuidelines: this.horizontalGuidelines,
358
382
  verticalGuidelines: this.verticalGuidelines,
359
383
 
@@ -0,0 +1,67 @@
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 Moveable from 'moveable';
23
+
24
+ import StageCore from './StageCore';
25
+ import type { StageHighlightConfig } from './types';
26
+ export default class StageHighlight extends EventEmitter {
27
+ public core: StageCore;
28
+ public container: HTMLElement;
29
+ public target?: HTMLElement;
30
+ public moveable?: Moveable;
31
+
32
+ constructor(config: StageHighlightConfig) {
33
+ super();
34
+
35
+ this.core = config.core;
36
+ this.container = config.container;
37
+ }
38
+
39
+ /**
40
+ * 高亮鼠标悬停的组件
41
+ * @param el 选中组件的Dom节点元素
42
+ */
43
+ public highlight(el: HTMLElement): void {
44
+ if (!el || el === this.target) return;
45
+ this.target = el;
46
+ this.moveable?.destroy();
47
+ this.moveable = new Moveable(this.container, {
48
+ target: this.target,
49
+ });
50
+ }
51
+
52
+ /**
53
+ * 清空高亮
54
+ */
55
+ public clearHighlight(): void {
56
+ if (!this.moveable) return;
57
+ this.moveable.target = null;
58
+ this.moveable.updateTarget();
59
+ }
60
+
61
+ /**
62
+ * 销毁实例
63
+ */
64
+ public destroy(): void {
65
+ this.moveable?.destroy();
66
+ }
67
+ }
package/src/StageMask.ts CHANGED
@@ -16,6 +16,8 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
+ import { throttle } from 'lodash-es';
20
+
19
21
  import { Mode, MouseButton, ZIndex } from './const';
20
22
  import Rule from './Rule';
21
23
  import type StageCore from './StageCore';
@@ -23,6 +25,7 @@ import type { StageMaskConfig } from './types';
23
25
  import { createDiv, getScrollParent, isFixedParent } from './util';
24
26
 
25
27
  const wrapperClassName = 'editor-mask-wrapper';
28
+ const throttleTime = 100;
26
29
 
27
30
  const hideScrollbar = () => {
28
31
  const style = globalThis.document.createElement('style');
@@ -82,6 +85,13 @@ export default class StageMask extends Rule {
82
85
  private mode: Mode = Mode.ABSOLUTE;
83
86
  private pageResizeObserver: ResizeObserver | null = null;
84
87
  private wrapperResizeObserver: ResizeObserver | null = null;
88
+ /**
89
+ * 高亮事件处理函数
90
+ * @param event 事件对象
91
+ */
92
+ private highlightHandler = throttle((event: MouseEvent): void => {
93
+ this.emit('highlight', event);
94
+ }, throttleTime);
85
95
 
86
96
  constructor(config: StageMaskConfig) {
87
97
  const wrapper = createWrapper();
@@ -93,6 +103,7 @@ export default class StageMask extends Rule {
93
103
  this.content.addEventListener('mousedown', this.mouseDownHandler);
94
104
  this.wrapper.appendChild(this.content);
95
105
  this.content.addEventListener('wheel', this.mouseWheelHandler);
106
+ this.content.addEventListener('mousemove', this.highlightHandler);
96
107
  }
97
108
 
98
109
  public setMode(mode: Mode) {
@@ -203,7 +214,7 @@ export default class StageMask extends Rule {
203
214
  * 点击事件处理函数
204
215
  * @param event 事件对象
205
216
  */
206
- private mouseDownHandler = async (event: MouseEvent): Promise<void> => {
217
+ private mouseDownHandler = (event: MouseEvent): void => {
207
218
  event.stopImmediatePropagation();
208
219
  event.stopPropagation();
209
220
 
@@ -218,10 +229,13 @@ export default class StageMask extends Rule {
218
229
 
219
230
  // 如果是右键点击,这里的mouseup事件监听没有效果
220
231
  globalThis.document.addEventListener('mouseup', this.mouseUpHandler);
232
+ this.content.removeEventListener('mousemove', this.highlightHandler);
233
+ this.emit('clearHighlight');
221
234
  };
222
235
 
223
236
  private mouseUpHandler = (): void => {
224
237
  globalThis.document.removeEventListener('mouseup', this.mouseUpHandler);
238
+ this.content.addEventListener('mousemove', this.highlightHandler);
225
239
  this.emit('select');
226
240
  };
227
241
 
@@ -18,6 +18,7 @@
18
18
 
19
19
  import { EventEmitter } from 'events';
20
20
 
21
+ import { SELECTED_CLASS, ZIndex } from './const';
21
22
  import StageCore from './StageCore';
22
23
  import type { Runtime, RuntimeWindow, StageRenderConfig } from './types';
23
24
  import { getHost, isSameDomain } from './util';
@@ -114,11 +115,23 @@ export default class StageRender extends EventEmitter {
114
115
 
115
116
  private loadHandler = async () => {
116
117
  this.emit('onload');
118
+
117
119
  if (this.render) {
118
120
  const el = await this.render(this.core);
119
121
  if (el) {
120
122
  this.iframe?.contentDocument?.body?.appendChild(el);
121
123
  }
122
124
  }
125
+
126
+ if (this.contentWindow) {
127
+ const style = this.contentWindow.document.createElement('style');
128
+ style.id = 'tmagic-stage-render';
129
+ style.innerHTML = `
130
+ .${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
131
+ z-index: ${ZIndex.SELECTED_EL};
132
+ }
133
+ `;
134
+ this.contentWindow.document.head.appendChild(style);
135
+ }
123
136
  };
124
137
  }
package/src/const.ts CHANGED
@@ -29,6 +29,7 @@ export enum GuidesType {
29
29
 
30
30
  export enum ZIndex {
31
31
  MASK = '99999',
32
+ SELECTED_EL = '666',
32
33
  }
33
34
 
34
35
  export enum MouseButton {
@@ -42,3 +43,5 @@ export enum Mode {
42
43
  FIXED = 'fixed',
43
44
  SORTABLE = 'sortable',
44
45
  }
46
+
47
+ export const SELECTED_CLASS = 'tmagic-stage-selected-area';
package/src/types.ts CHANGED
@@ -114,3 +114,8 @@ export interface Magic {
114
114
  export interface RuntimeWindow extends Window {
115
115
  magic: Magic;
116
116
  }
117
+
118
+ export interface StageHighlightConfig {
119
+ core: StageCore;
120
+ container: HTMLElement;
121
+ }
package/src/util.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import { Mode } from './const';
19
+ import { Mode, SELECTED_CLASS } from './const';
20
20
  import type { Offset } from './types';
21
21
 
22
22
  export const getOffset = (el: HTMLElement): Offset => {
@@ -154,3 +154,17 @@ export const createDiv = ({ className, cssText }: { className: string; cssText:
154
154
  el.style.cssText = cssText;
155
155
  return el;
156
156
  };
157
+
158
+ export const removeSelectedClassName = (doc: Document) => {
159
+ const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
160
+
161
+ if (oldEl) {
162
+ oldEl.classList.remove(SELECTED_CLASS);
163
+ (oldEl.parentNode as HTMLDivElement)?.classList.remove(`${SELECTED_CLASS}-parent`);
164
+ }
165
+ };
166
+
167
+ export const addSelectedClassName = (el: Element) => {
168
+ el.classList.add(SELECTED_CLASS);
169
+ (el.parentNode as Element)?.classList.add(`${SELECTED_CLASS}-parent`);
170
+ };