@tmagic/stage 1.5.0-beta.1 → 1.5.0-beta.11

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.
@@ -65,9 +65,9 @@ const defaultContainerHighlightDuration = 800;
65
65
  * @extends EventEmitter
66
66
  */
67
67
  export default class ActionManager extends EventEmitter {
68
- private dr: StageDragResize;
69
- private multiDr?: StageMultiDragResize;
70
- private highlightLayer: StageHighlight;
68
+ private dr: StageDragResize | null = null;
69
+ private multiDr: StageMultiDragResize | null = null;
70
+ private highlightLayer: StageHighlight | null = null;
71
71
  /** 单选、多选、高亮的容器(蒙层的content) */
72
72
  private container: HTMLElement;
73
73
  /** 当前选中的节点 */
@@ -143,7 +143,7 @@ export default class ActionManager extends EventEmitter {
143
143
  this.disabledMultiSelect = true;
144
144
  if (this.multiDr) {
145
145
  this.multiDr.destroy();
146
- this.multiDr = undefined;
146
+ this.multiDr = null;
147
147
  }
148
148
  }
149
149
 
@@ -161,7 +161,7 @@ export default class ActionManager extends EventEmitter {
161
161
  * @param guidelines 参考线坐标数组
162
162
  */
163
163
  public setGuidelines(type: GuidesType, guidelines: number[]): void {
164
- this.dr.setGuidelines(type, guidelines);
164
+ this.dr?.setGuidelines(type, guidelines);
165
165
  this.multiDr?.setGuidelines(type, guidelines);
166
166
  }
167
167
 
@@ -169,7 +169,7 @@ export default class ActionManager extends EventEmitter {
169
169
  * 清空所有参考线
170
170
  */
171
171
  public clearGuides(): void {
172
- this.dr.clearGuides();
172
+ this.dr?.clearGuides();
173
173
  this.multiDr?.clearGuides();
174
174
  }
175
175
 
@@ -178,7 +178,7 @@ export default class ActionManager extends EventEmitter {
178
178
  * @param el 变更的元素
179
179
  */
180
180
  public updateMoveable(el?: HTMLElement): void {
181
- this.dr.updateMoveable(el);
181
+ this.dr?.updateMoveable(el);
182
182
  // 多选时不可配置元素,因此不存在多选元素变更,不需要传el
183
183
  this.multiDr?.updateMoveable();
184
184
  }
@@ -204,7 +204,7 @@ export default class ActionManager extends EventEmitter {
204
204
  }
205
205
 
206
206
  public getMoveableOption<K extends keyof MoveableOptions>(key: K): MoveableOptions[K] | undefined {
207
- if (this.dr.getTarget()) {
207
+ if (this.dr?.getTarget()) {
208
208
  return this.dr.getOption(key);
209
209
  }
210
210
  if (this.multiDr?.targetList.length) {
@@ -271,7 +271,7 @@ export default class ActionManager extends EventEmitter {
271
271
  public select(el: HTMLElement | null, event?: MouseEvent): void {
272
272
  this.setSelectedEl(el);
273
273
  this.clearSelectStatus(SelectStatus.MULTI_SELECT);
274
- this.dr.select(el, event);
274
+ this.dr?.select(el, event);
275
275
  }
276
276
 
277
277
  public multiSelect(ids: Id[]): void {
@@ -310,14 +310,14 @@ export default class ActionManager extends EventEmitter {
310
310
  }
311
311
  if (el === this.highlightedEl || !el) return;
312
312
 
313
- this.highlightLayer.highlight(el);
313
+ this.highlightLayer?.highlight(el);
314
314
  this.highlightedEl = el;
315
315
  this.emit('highlight', el);
316
316
  }
317
317
 
318
318
  public clearHighlight(): void {
319
319
  this.setHighlightEl(undefined);
320
- this.highlightLayer.clearHighlight();
320
+ this.highlightLayer?.clearHighlight();
321
321
  }
322
322
 
323
323
  /**
@@ -329,7 +329,7 @@ export default class ActionManager extends EventEmitter {
329
329
  this.multiDr?.clearSelectStatus();
330
330
  this.selectedElList = [];
331
331
  } else {
332
- this.dr.clearSelectStatus();
332
+ this.dr?.clearSelectStatus();
333
333
  }
334
334
  }
335
335
 
@@ -373,7 +373,7 @@ export default class ActionManager extends EventEmitter {
373
373
  }
374
374
 
375
375
  public getDragStatus() {
376
- return this.dr.getDragStatus();
376
+ return this.dr?.getDragStatus();
377
377
  }
378
378
 
379
379
  public destroy(): void {
@@ -382,9 +382,16 @@ export default class ActionManager extends EventEmitter {
382
382
  this.container.removeEventListener('mouseleave', this.mouseLeaveHandler);
383
383
  this.container.removeEventListener('wheel', this.mouseWheelHandler);
384
384
  this.container.removeEventListener('dblclick', this.dblclickHandler);
385
- this.dr.destroy();
385
+ this.selectedEl = null;
386
+ this.selectedElList = [];
387
+
388
+ this.dr?.destroy();
386
389
  this.multiDr?.destroy();
387
- this.highlightLayer.destroy();
390
+ this.highlightLayer?.destroy();
391
+
392
+ this.dr = null;
393
+ this.multiDr = null;
394
+ this.highlightLayer = null;
388
395
  }
389
396
 
390
397
  public on<Name extends keyof ActionManagerEvents, Param extends ActionManagerEvents[Name]>(
@@ -431,7 +438,7 @@ export default class ActionManager extends EventEmitter {
431
438
  this.emit('select-parent');
432
439
  })
433
440
  .on(AbleActionEventType.REMOVE, () => {
434
- const drTarget = this.dr.getTarget();
441
+ const drTarget = this.dr?.getTarget();
435
442
  if (!drTarget) return;
436
443
  const data: RemoveEventData = {
437
444
  data: [{ el: drTarget }],
@@ -39,8 +39,6 @@ import TargetShadow from './TargetShadow';
39
39
  import type { DragResizeHelperConfig, Rect, TargetElement } from './types';
40
40
  import { getAbsolutePosition, getBorderWidth, getMarginValue, getOffset } from './util';
41
41
 
42
- const getId = getIdFromEl();
43
-
44
42
  /**
45
43
  * 拖拽/改变大小等操作发生时,moveable会抛出各种状态事件,DragResizeHelper负责响应这些事件,对目标节点target和拖拽节点targetShadow进行修改;
46
44
  * 其中目标节点是DragResizeHelper直接改的,targetShadow作为直接被操作的拖拽框,是调用moveableHelper改的;
@@ -50,7 +48,7 @@ export default class DragResizeHelper {
50
48
  /** 目标节点在蒙层上的占位节点,用于跟鼠标交互,避免鼠标事件直接作用到目标节点 */
51
49
  private targetShadow: TargetShadow;
52
50
  /** 要操作的原始目标节点 */
53
- private target!: HTMLElement;
51
+ private target: HTMLElement | null = null;
54
52
  /** 多选:目标节点组 */
55
53
  private targetList: HTMLElement[] = [];
56
54
  /** 响应拖拽的状态事件,修改绝对定位布局下targetShadow的dom。
@@ -84,6 +82,8 @@ export default class DragResizeHelper {
84
82
  }
85
83
 
86
84
  public destroy(): void {
85
+ this.target = null;
86
+ this.targetList = [];
87
87
  this.targetShadow.destroy();
88
88
  this.destroyGhostEl();
89
89
  this.moveableHelper.clear();
@@ -114,8 +114,8 @@ export default class DragResizeHelper {
114
114
  public onResizeStart(e: OnResizeStart): void {
115
115
  this.moveableHelper.onResizeStart(e);
116
116
 
117
- this.frameSnapShot.top = this.target.offsetTop;
118
- this.frameSnapShot.left = this.target.offsetLeft;
117
+ this.frameSnapShot.top = this.target!.offsetTop;
118
+ this.frameSnapShot.left = this.target!.offsetLeft;
119
119
  }
120
120
 
121
121
  public onResize(e: OnResize): void {
@@ -123,33 +123,33 @@ export default class DragResizeHelper {
123
123
  const { beforeTranslate } = drag;
124
124
  // 流式布局
125
125
  if (this.mode === Mode.SORTABLE) {
126
- this.target.style.top = '0px';
126
+ this.target!.style.top = '0px';
127
127
  if (this.targetShadow.el) {
128
128
  this.targetShadow.el.style.width = `${width}px`;
129
129
  this.targetShadow.el.style.height = `${height}px`;
130
130
  }
131
131
  } else {
132
132
  this.moveableHelper.onResize(e);
133
- const { marginLeft, marginTop } = getMarginValue(this.target);
134
- this.target.style.left = `${this.frameSnapShot.left + beforeTranslate[0] - marginLeft}px`;
135
- this.target.style.top = `${this.frameSnapShot.top + beforeTranslate[1] - marginTop}px`;
133
+ const { marginLeft, marginTop } = getMarginValue(this.target!);
134
+ this.target!.style.left = `${this.frameSnapShot.left + beforeTranslate[0] - marginLeft}px`;
135
+ this.target!.style.top = `${this.frameSnapShot.top + beforeTranslate[1] - marginTop}px`;
136
136
  }
137
137
 
138
- const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = getBorderWidth(this.target);
138
+ const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = getBorderWidth(this.target!);
139
139
 
140
- this.target.style.width = `${width + borderLeftWidth + borderRightWidth}px`;
141
- this.target.style.height = `${height + borderTopWidth + borderBottomWidth}px`;
140
+ this.target!.style.width = `${width + borderLeftWidth + borderRightWidth}px`;
141
+ this.target!.style.height = `${height + borderTopWidth + borderBottomWidth}px`;
142
142
  }
143
143
 
144
144
  public onDragStart(e: OnDragStart): void {
145
145
  this.moveableHelper.onDragStart(e);
146
146
 
147
147
  if (this.mode === Mode.SORTABLE) {
148
- this.ghostEl = this.generateGhostEl(this.target);
148
+ this.ghostEl = this.generateGhostEl(this.target!);
149
149
  }
150
150
 
151
- this.frameSnapShot.top = this.target.offsetTop;
152
- this.frameSnapShot.left = this.target.offsetLeft;
151
+ this.frameSnapShot.top = this.target!.offsetTop;
152
+ this.frameSnapShot.left = this.target!.offsetLeft;
153
153
  }
154
154
 
155
155
  public onDrag(e: OnDrag): void {
@@ -161,10 +161,10 @@ export default class DragResizeHelper {
161
161
 
162
162
  this.moveableHelper.onDrag(e);
163
163
 
164
- const { marginLeft, marginTop } = getMarginValue(this.target);
164
+ const { marginLeft, marginTop } = getMarginValue(this.target!);
165
165
 
166
- this.target.style.left = `${this.frameSnapShot.left + e.beforeTranslate[0] - marginLeft}px`;
167
- this.target.style.top = `${this.frameSnapShot.top + e.beforeTranslate[1] - marginTop}px`;
166
+ this.target!.style.left = `${this.frameSnapShot.left + e.beforeTranslate[0] - marginLeft}px`;
167
+ this.target!.style.top = `${this.frameSnapShot.top + e.beforeTranslate[1] - marginTop}px`;
168
168
  }
169
169
 
170
170
  public onRotateStart(e: OnRotateStart): void {
@@ -174,7 +174,7 @@ export default class DragResizeHelper {
174
174
  public onRotate(e: OnRotate): void {
175
175
  this.moveableHelper.onRotate(e);
176
176
  const frame = this.moveableHelper.getFrame(e.target);
177
- this.target.style.transform = frame?.toCSSObject().transform || '';
177
+ this.target!.style.transform = frame?.toCSSObject().transform || '';
178
178
  }
179
179
 
180
180
  public onScaleStart(e: OnScaleStart): void {
@@ -184,7 +184,7 @@ export default class DragResizeHelper {
184
184
  public onScale(e: OnScale): void {
185
185
  this.moveableHelper.onScale(e);
186
186
  const frame = this.moveableHelper.getFrame(e.target);
187
- this.target.style.transform = frame?.toCSSObject().transform || '';
187
+ this.target!.style.transform = frame?.toCSSObject().transform || '';
188
188
  }
189
189
 
190
190
  public getGhostEl(): HTMLElement | undefined {
@@ -241,15 +241,17 @@ export default class DragResizeHelper {
241
241
  events.forEach((ev) => {
242
242
  const { width, height, beforeTranslate } = ev.drag;
243
243
  const frameSnapShot = this.framesSnapShot.find(
244
- (frameItem) => frameItem.id === getId(ev.target)?.replace(DRAG_EL_ID_PREFIX, ''),
244
+ (frameItem) => frameItem.id === getIdFromEl()(ev.target)?.replace(DRAG_EL_ID_PREFIX, ''),
245
245
  );
246
246
  if (!frameSnapShot) return;
247
247
  const targeEl = this.targetList.find(
248
- (targetItem) => targetItem.id === getId(ev.target)?.replace(DRAG_EL_ID_PREFIX, ''),
248
+ (targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(ev.target)?.replace(DRAG_EL_ID_PREFIX, ''),
249
249
  );
250
250
  if (!targeEl) return;
251
251
  // 元素与其所属组同时加入多选列表时,只更新父元素
252
- const isParentIncluded = this.targetList.find((targetItem) => getId(targetItem) === getId(targeEl.parentElement));
252
+ const isParentIncluded = this.targetList.find(
253
+ (targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(targeEl.parentElement),
254
+ );
253
255
 
254
256
  if (!isParentIncluded) {
255
257
  // 更新页面元素位置
@@ -279,18 +281,21 @@ export default class DragResizeHelper {
279
281
  // 拖动过程更新
280
282
  events.forEach((ev) => {
281
283
  const frameSnapShot = this.framesSnapShot.find(
282
- (frameItem) => getId(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) && getId(ev.target)?.endsWith(frameItem.id),
284
+ (frameItem) =>
285
+ getIdFromEl()(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) && getIdFromEl()(ev.target)?.endsWith(frameItem.id),
283
286
  );
284
287
  if (!frameSnapShot) return;
285
288
  const targeEl = this.targetList.find(
286
289
  (targetItem) =>
287
- getId(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) &&
288
- getId(targetItem) &&
289
- getId(ev.target)?.endsWith(getId(targetItem)!),
290
+ getIdFromEl()(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) &&
291
+ getIdFromEl()(targetItem) &&
292
+ getIdFromEl()(ev.target)?.endsWith(getIdFromEl()(targetItem)!),
290
293
  );
291
294
  if (!targeEl) return;
292
295
  // 元素与其所属组同时加入多选列表时,只更新父元素
293
- const isParentIncluded = this.targetList.find((targetItem) => getId(targetItem) === getId(targeEl.parentElement));
296
+ const isParentIncluded = this.targetList.find(
297
+ (targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(targeEl.parentElement),
298
+ );
294
299
  if (!isParentIncluded) {
295
300
  // 更新页面元素位置
296
301
  const { marginLeft, marginTop } = getMarginValue(targeEl);
@@ -317,7 +322,7 @@ export default class DragResizeHelper {
317
322
  const shadowEls = this.getShadowEls();
318
323
 
319
324
  if (shadowEls.length) {
320
- shadowEl = shadowEls.find((item) => getId(item)?.endsWith(getId(el) || ''));
325
+ shadowEl = shadowEls.find((item) => getIdFromEl()(item)?.endsWith(getIdFromEl()(el) || ''));
321
326
  }
322
327
 
323
328
  if (parentEl && this.mode === Mode.ABSOLUTE && shadowEl) {
@@ -354,11 +359,12 @@ export default class DragResizeHelper {
354
359
  // 实际目标元素
355
360
  const matchEventTarget = this.targetList.find(
356
361
  (targetItem) =>
357
- getId(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) && getId(ev.target)?.endsWith(getId(targetItem) || ''),
362
+ getIdFromEl()(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) &&
363
+ getIdFromEl()(ev.target)?.endsWith(getIdFromEl()(targetItem) || ''),
358
364
  );
359
365
  if (!matchEventTarget) return;
360
366
 
361
- const id = getId(matchEventTarget);
367
+ const id = getIdFromEl()(matchEventTarget);
362
368
  id &&
363
369
  this.framesSnapShot.push({
364
370
  left: matchEventTarget.offsetLeft,
@@ -376,25 +382,31 @@ export default class DragResizeHelper {
376
382
  this.destroyGhostEl();
377
383
  }
378
384
 
379
- const ghostEl = el.cloneNode(true) as HTMLElement;
380
- this.setGhostElChildrenId(ghostEl);
385
+ const ghostEl = document.createElement('div') as HTMLElement;
381
386
  const { top, left } = getAbsolutePosition(el, getOffset(el));
382
- setIdToEl()(ghostEl, `${GHOST_EL_ID_PREFIX}${getId(el)}`);
383
- ghostEl.style.zIndex = ZIndex.GHOST_EL;
384
- ghostEl.style.opacity = '.5';
385
- ghostEl.style.position = 'absolute';
386
- ghostEl.style.left = `${left}px`;
387
- ghostEl.style.top = `${top}px`;
388
- ghostEl.style.marginLeft = '0';
389
- ghostEl.style.marginTop = '0';
387
+
388
+ setIdToEl()(ghostEl, `${GHOST_EL_ID_PREFIX}${getIdFromEl()(el)}`);
389
+
390
+ ghostEl.style.cssText = `
391
+ z-index: ${ZIndex.GHOST_EL};
392
+ opacity: .6;
393
+ position: absolute;
394
+ left: ${left}px;
395
+ top: ${top}px;
396
+ margin: 0;
397
+ background: blue;
398
+ width: ${el.clientWidth}px;
399
+ height: ${el.clientHeight}px;
400
+ `;
390
401
  el.after(ghostEl);
402
+
391
403
  return ghostEl;
392
404
  }
393
405
 
394
406
  private setGhostElChildrenId(el: Element): void {
395
407
  for (const child of Array.from(el.children)) {
396
408
  const el = child as HTMLElement;
397
- const id = getId(el);
409
+ const id = getIdFromEl()(el);
398
410
  if (id) {
399
411
  setIdToEl()(el, `${GHOST_EL_ID_PREFIX}${id}`);
400
412
  }
@@ -1,11 +1,24 @@
1
- import { MoveableManagerInterface, Renderer } from 'moveable';
1
+ import type { MoveableManagerInterface, Renderer } from 'moveable';
2
2
 
3
3
  import { AbleActionEventType } from './const';
4
4
  import ableCss from './moveable-able.css?raw';
5
5
 
6
- export default (handler: (type: AbleActionEventType) => void) => ({
6
+ export interface AbleCustomizedButton {
7
+ props: {
8
+ className?: string;
9
+ onClick?(e: MouseEvent): void;
10
+ [key: string]: any;
11
+ };
12
+ children: ReturnType<Renderer['createElement']>[];
13
+ }
14
+
15
+ export default (
16
+ handler: (type: AbleActionEventType) => void,
17
+ customizedButton: ((react: Renderer) => AbleCustomizedButton)[] = [],
18
+ ) => ({
7
19
  name: 'actions',
8
20
  props: [],
21
+ always: true,
9
22
  events: [],
10
23
  render(moveable: MoveableManagerInterface<any, any>, React: Renderer) {
11
24
  const rect = moveable.getRect();
@@ -33,10 +46,17 @@ export default (handler: (type: AbleActionEventType) => void) => ({
33
46
  {
34
47
  className: 'moveable-editable',
35
48
  style: {
36
- transform: `translate(${pos2[0] - 60}px, ${pos2[1] - 28}px) rotate(${rect.rotation}deg)`,
49
+ transform: `translate(${pos2[0] - (customizedButton.length + 3) * 20}px, ${pos2[1] - 28}px) rotate(${
50
+ rect.rotation
51
+ }deg)`,
37
52
  },
38
53
  },
39
54
  [
55
+ ...customizedButton.map((buttonRenderer) => {
56
+ const options = buttonRenderer(React);
57
+ return React.createElement('button', options.props || {}, ...(options.children || []));
58
+ }),
59
+
40
60
  React.createElement(
41
61
  'button',
42
62
  {
@@ -112,9 +112,10 @@ export default class MoveableOptionsManager extends EventEmitter {
112
112
  */
113
113
  protected getOptions(isMultiSelect: boolean, runtimeOptions: MoveableOptions = {}): MoveableOptions {
114
114
  const defaultOptions = this.getDefaultOptions(isMultiSelect);
115
- const customizedOptions = this.getCustomizeOptions();
115
+ const customizedOptions = this.getCustomizeOptions() || {};
116
116
 
117
117
  this.options = merge(defaultOptions, customizedOptions, runtimeOptions);
118
+
118
119
  return this.options;
119
120
  }
120
121