@tmagic/stage 1.0.0-beta.8 → 1.0.0-rc.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.
@@ -1,8 +1,12 @@
1
1
  import EventEmitter$1, { EventEmitter } from 'events';
2
2
  import Moveable from 'moveable';
3
+ import MoveableHelper from 'moveable-helper';
4
+ import { throttle } from 'lodash-es';
3
5
  import Guides from '@scena/guides';
4
6
 
5
7
  const GHOST_EL_ID_PREFIX = "ghost_el_";
8
+ const DRAG_EL_ID_PREFIX = "drag_el_";
9
+ const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
6
10
  const DEFAULT_ZOOM = 1;
7
11
  var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
8
12
  GuidesType2["HORIZONTAL"] = "horizontal";
@@ -11,6 +15,9 @@ var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
11
15
  })(GuidesType || {});
12
16
  var ZIndex = /* @__PURE__ */ ((ZIndex2) => {
13
17
  ZIndex2["MASK"] = "99999";
18
+ ZIndex2["SELECTED_EL"] = "666";
19
+ ZIndex2["GHOST_EL"] = "700";
20
+ ZIndex2["DRAG_EL"] = "9";
14
21
  return ZIndex2;
15
22
  })(ZIndex || {});
16
23
  var MouseButton = /* @__PURE__ */ ((MouseButton2) => {
@@ -25,31 +32,23 @@ var Mode = /* @__PURE__ */ ((Mode2) => {
25
32
  Mode2["SORTABLE"] = "sortable";
26
33
  return Mode2;
27
34
  })(Mode || {});
35
+ const SELECTED_CLASS = "tmagic-stage-selected-area";
36
+ const H_GUIDE_LINE_STORAGE_KEY = "$MagicStageHorizontalGuidelinesData";
37
+ const V_GUIDE_LINE_STORAGE_KEY = "$MagicStageVerticalGuidelinesData";
28
38
 
39
+ const getParents = (el, relative) => {
40
+ let cur = el.parentElement;
41
+ const parents = [];
42
+ while (cur && cur !== relative) {
43
+ parents.push(cur);
44
+ cur = cur.parentElement;
45
+ }
46
+ return parents;
47
+ };
29
48
  const getOffset = (el) => {
30
- const { transform } = getComputedStyle(el);
31
49
  const { offsetParent } = el;
32
- let left = el.offsetLeft;
33
- let top = el.offsetTop;
34
- if (transform.indexOf("matrix") > -1) {
35
- let a = 1;
36
- let b = 1;
37
- let c = 1;
38
- let d = 1;
39
- let e = 0;
40
- let f = 0;
41
- transform.replace(/matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/, ($0, $1, $2, $3, $4, $5, $6) => {
42
- a = +$1;
43
- b = +$2;
44
- c = +$3;
45
- d = +$4;
46
- e = +$5;
47
- f = +$6;
48
- return transform;
49
- });
50
- left = a * left + c * top + e;
51
- top = b * left + d * top + f;
52
- }
50
+ const left = el.offsetLeft;
51
+ const top = el.offsetTop;
53
52
  if (offsetParent) {
54
53
  const parentOffset = getOffset(offsetParent);
55
54
  return {
@@ -145,46 +144,81 @@ const createDiv = ({ className, cssText }) => {
145
144
  el.style.cssText = cssText;
146
145
  return el;
147
146
  };
147
+ const removeSelectedClassName = (doc) => {
148
+ const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
149
+ if (oldEl) {
150
+ oldEl.classList.remove(SELECTED_CLASS);
151
+ oldEl.parentNode?.classList.remove(`${SELECTED_CLASS}-parent`);
152
+ doc.querySelectorAll(`.${SELECTED_CLASS}-parents`).forEach((item) => {
153
+ item.classList.remove(`${SELECTED_CLASS}-parents`);
154
+ });
155
+ }
156
+ };
157
+ const addSelectedClassName = (el, doc) => {
158
+ el.classList.add(SELECTED_CLASS);
159
+ el.parentNode?.classList.add(`${SELECTED_CLASS}-parent`);
160
+ getParents(el, doc.body).forEach((item) => {
161
+ item.classList.add(`${SELECTED_CLASS}-parents`);
162
+ });
163
+ };
164
+ const getGuideLineFromCache = (type) => {
165
+ const key = {
166
+ [GuidesType.HORIZONTAL]: H_GUIDE_LINE_STORAGE_KEY,
167
+ [GuidesType.VERTICAL]: V_GUIDE_LINE_STORAGE_KEY
168
+ }[type];
169
+ if (!key)
170
+ return [];
171
+ const guideLineCacheData = globalThis.localStorage.getItem(key);
172
+ if (guideLineCacheData) {
173
+ try {
174
+ return JSON.parse(guideLineCacheData) || [];
175
+ } catch (e) {
176
+ console.error(e);
177
+ }
178
+ }
179
+ return [];
180
+ };
148
181
 
149
182
  class StageDragResize extends EventEmitter {
150
- core;
151
- container;
152
- target;
153
- dragEl;
154
- moveable;
155
- horizontalGuidelines = [];
156
- verticalGuidelines = [];
157
- moveableOptions = {};
158
- dragStatus = "end" /* END */;
159
- ghostEl;
160
- mode = Mode.ABSOLUTE;
161
183
  constructor(config) {
162
184
  super();
185
+ this.horizontalGuidelines = getGuideLineFromCache(GuidesType.HORIZONTAL);
186
+ this.verticalGuidelines = getGuideLineFromCache(GuidesType.VERTICAL);
187
+ this.elementGuidelines = [];
188
+ this.mode = Mode.ABSOLUTE;
189
+ this.moveableOptions = {};
190
+ this.dragStatus = "end" /* END */;
163
191
  this.core = config.core;
164
192
  this.container = config.container;
193
+ this.dragEl = globalThis.document.createElement("div");
194
+ this.container.append(this.dragEl);
165
195
  }
166
- async select(el, event) {
196
+ select(el, event) {
197
+ const oldTarget = this.target;
167
198
  this.target = el;
168
- if (/(auto|scroll)/.test(this.target.style.overflow)) {
169
- this.target.style.overflow = "hidden";
199
+ if (!this.moveable || this.target !== oldTarget) {
200
+ this.init(el);
201
+ this.moveableHelper = MoveableHelper.create({
202
+ useBeforeRender: true,
203
+ useRender: false,
204
+ createAuto: true
205
+ });
206
+ this.initMoveable();
207
+ } else {
208
+ this.updateMoveable();
170
209
  }
171
- this.mode = getMode(el);
172
- this.destroyDragEl();
173
- this.destroyGhostEl();
174
- this.dragEl = this.generateDragEl(el);
175
- this.moveableOptions = await this.getOptions({
176
- target: this.dragEl || this.target
177
- });
178
- this.initMoveable();
179
210
  if (event) {
180
211
  this.moveable?.dragStart(event);
181
212
  }
182
213
  }
183
- async refresh() {
214
+ updateMoveable(el = this.target) {
184
215
  if (!this.moveable)
185
216
  throw new Error("\u672A\u521D\u59CB\u5316moveable");
186
- const options = await this.getOptions();
187
- Object.entries(options).forEach(([key, value]) => {
217
+ if (!el)
218
+ throw new Error("\u4E3A\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9");
219
+ this.target = el;
220
+ this.init(el);
221
+ Object.entries(this.moveableOptions).forEach(([key, value]) => {
188
222
  this.moveable[key] = value;
189
223
  });
190
224
  this.moveable.updateTarget();
@@ -192,15 +226,19 @@ class StageDragResize extends EventEmitter {
192
226
  setGuidelines(type, guidelines) {
193
227
  if (type === GuidesType.HORIZONTAL) {
194
228
  this.horizontalGuidelines = guidelines;
229
+ this.moveableOptions.horizontalGuidelines = guidelines;
195
230
  } else if (type === GuidesType.VERTICAL) {
196
231
  this.verticalGuidelines = guidelines;
232
+ this.moveableOptions.verticalGuidelines = guidelines;
197
233
  }
198
- this.refresh();
234
+ this.updateMoveable();
199
235
  }
200
236
  clearGuides() {
201
- this.verticalGuidelines = [];
202
237
  this.horizontalGuidelines = [];
203
- this.refresh();
238
+ this.verticalGuidelines = [];
239
+ this.moveableOptions.horizontalGuidelines = [];
240
+ this.moveableOptions.verticalGuidelines = [];
241
+ this.updateMoveable();
204
242
  }
205
243
  destroy() {
206
244
  this.moveable?.destroy();
@@ -209,6 +247,37 @@ class StageDragResize extends EventEmitter {
209
247
  this.dragStatus = "end" /* END */;
210
248
  this.removeAllListeners();
211
249
  }
250
+ init(el) {
251
+ if (/(auto|scroll)/.test(el.style.overflow)) {
252
+ el.style.overflow = "hidden";
253
+ }
254
+ this.mode = getMode(el);
255
+ this.destroyGhostEl();
256
+ this.updateDragEl(el);
257
+ this.moveableOptions = this.getOptions({
258
+ target: this.dragEl
259
+ });
260
+ }
261
+ setElementGuidelines(nodes) {
262
+ this.elementGuidelines.forEach((node) => {
263
+ node.remove();
264
+ });
265
+ this.elementGuidelines = [];
266
+ if (this.mode === Mode.ABSOLUTE) {
267
+ const frame = document.createDocumentFragment();
268
+ for (const node of nodes) {
269
+ const { width, height } = node.getBoundingClientRect();
270
+ if (node === this.target)
271
+ continue;
272
+ const { left, top } = getOffset(node);
273
+ const elementGuideline = document.createElement("div");
274
+ elementGuideline.style.cssText = `position: absolute;width: ${width}px;height: ${height}px;top: ${top}px;left: ${left}px`;
275
+ this.elementGuidelines.push(elementGuideline);
276
+ frame.append(elementGuideline);
277
+ }
278
+ this.container.append(frame);
279
+ }
280
+ }
212
281
  initMoveable() {
213
282
  this.moveable?.destroy();
214
283
  this.moveable = new Moveable(this.container, {
@@ -216,35 +285,38 @@ class StageDragResize extends EventEmitter {
216
285
  });
217
286
  this.bindResizeEvent();
218
287
  this.bindDragEvent();
288
+ this.bindRotateEvent();
289
+ this.bindScaleEvent();
219
290
  }
220
291
  bindResizeEvent() {
221
292
  if (!this.moveable)
222
293
  throw new Error("moveable \u4E3A\u521D\u59CB\u5316");
294
+ const frame = {
295
+ left: 0,
296
+ top: 0
297
+ };
223
298
  this.moveable.on("resizeStart", (e) => {
224
- if (e.dragStart) {
225
- const rect = this.moveable.getRect();
226
- const offset = getAbsolutePosition(e.target, rect);
227
- e.dragStart.set([offset.left, offset.top]);
228
- }
229
- }).on("resize", ({ width, height, drag }) => {
299
+ if (!this.target)
300
+ return;
301
+ this.dragStatus = "start" /* START */;
302
+ this.moveableHelper?.onResizeStart(e);
303
+ frame.top = this.target.offsetTop;
304
+ frame.left = this.target.offsetLeft;
305
+ }).on("resize", (e) => {
306
+ const { width, height, drag } = e;
230
307
  if (!this.moveable || !this.target || !this.dragEl)
231
308
  return;
232
309
  const { beforeTranslate } = drag;
233
310
  this.dragStatus = "ing" /* ING */;
234
- this.target.style.width = `${width}px`;
235
- this.target.style.height = `${height}px`;
236
- this.dragEl.style.width = `${width}px`;
237
- this.dragEl.style.height = `${height}px`;
311
+ this.moveableHelper?.onResize(e);
238
312
  if (this.mode === Mode.SORTABLE) {
239
- this.dragEl.style.top = `${beforeTranslate[1]}px`;
240
- this.target.style.top = `0px`;
241
- return;
313
+ this.target.style.top = "0px";
314
+ } else {
315
+ this.target.style.left = `${frame.left + beforeTranslate[0]}px`;
316
+ this.target.style.top = `${frame.top + beforeTranslate[1]}px`;
242
317
  }
243
- this.dragEl.style.left = `${beforeTranslate[0]}px`;
244
- this.dragEl.style.top = `${beforeTranslate[1]}px`;
245
- const offset = getAbsolutePosition(this.target, { left: beforeTranslate[0], top: beforeTranslate[1] });
246
- this.target.style.left = `${offset.left}px`;
247
- this.target.style.top = `${offset.top}px`;
318
+ this.target.style.width = `${width}px`;
319
+ this.target.style.height = `${height}px`;
248
320
  }).on("resizeEnd", () => {
249
321
  this.dragStatus = "end" /* END */;
250
322
  this.update(true);
@@ -253,27 +325,31 @@ class StageDragResize extends EventEmitter {
253
325
  bindDragEvent() {
254
326
  if (!this.moveable)
255
327
  throw new Error("moveable \u4E3A\u521D\u59CB\u5316");
256
- this.moveable.on("dragStart", () => {
328
+ const frame = {
329
+ left: 0,
330
+ top: 0
331
+ };
332
+ this.moveable.on("dragStart", (e) => {
257
333
  if (!this.target)
258
334
  throw new Error("\u672A\u9009\u4E2D\u7EC4\u4EF6");
259
335
  this.dragStatus = "start" /* START */;
336
+ this.moveableHelper?.onDragStart(e);
260
337
  if (this.mode === Mode.SORTABLE) {
261
338
  this.ghostEl = this.generateGhostEl(this.target);
262
339
  }
263
- }).on("drag", ({ left, top }) => {
340
+ frame.top = this.target.offsetTop;
341
+ frame.left = this.target.offsetLeft;
342
+ }).on("drag", (e) => {
264
343
  if (!this.target || !this.dragEl)
265
344
  return;
266
345
  this.dragStatus = "ing" /* ING */;
267
- const offset = getAbsolutePosition(this.target, { left, top });
268
346
  if (this.ghostEl) {
269
- this.dragEl.style.top = `${top}px`;
270
- this.ghostEl.style.top = `${offset.top}px`;
347
+ this.ghostEl.style.top = `${frame.top + e.beforeTranslate[1]}px`;
271
348
  return;
272
349
  }
273
- this.dragEl.style.left = `${left}px`;
274
- this.dragEl.style.top = `${top}px`;
275
- this.target.style.left = `${offset.left}px`;
276
- this.target.style.top = `${offset.top}px`;
350
+ this.moveableHelper?.onDrag(e);
351
+ this.target.style.left = `${frame.left + e.beforeTranslate[0]}px`;
352
+ this.target.style.top = `${frame.top + e.beforeTranslate[1]}px`;
277
353
  }).on("dragEnd", () => {
278
354
  if (this.dragStatus === "ing" /* ING */) {
279
355
  switch (this.mode) {
@@ -288,13 +364,53 @@ class StageDragResize extends EventEmitter {
288
364
  this.destroyGhostEl();
289
365
  });
290
366
  }
291
- async getSnapElements(el) {
292
- const { renderer } = this.core;
293
- const getSnapElements = (await renderer.getRuntime())?.getSnapElements || (() => {
294
- const doc = renderer.contentWindow?.document;
295
- return doc ? Array.from(doc.querySelectorAll("[id]")) : [];
367
+ bindRotateEvent() {
368
+ if (!this.moveable)
369
+ throw new Error("moveable \u4E3A\u521D\u59CB\u5316");
370
+ this.moveable.on("rotateStart", (e) => {
371
+ this.dragStatus = "start" /* START */;
372
+ this.moveableHelper?.onRotateStart(e);
373
+ }).on("rotate", (e) => {
374
+ if (!this.target || !this.dragEl)
375
+ return;
376
+ this.dragStatus = "ing" /* ING */;
377
+ this.moveableHelper?.onRotate(e);
378
+ const frame = this.moveableHelper?.getFrame(e.target);
379
+ this.target.style.transform = frame?.toCSSObject().transform || "";
380
+ }).on("rotateEnd", (e) => {
381
+ this.dragStatus = "end" /* END */;
382
+ const frame = this.moveableHelper?.getFrame(e.target);
383
+ this.emit("update", {
384
+ el: this.target,
385
+ style: {
386
+ transform: frame?.get("transform")
387
+ }
388
+ });
389
+ });
390
+ }
391
+ bindScaleEvent() {
392
+ if (!this.moveable)
393
+ throw new Error("moveable \u4E3A\u521D\u59CB\u5316");
394
+ this.moveable.on("scaleStart", (e) => {
395
+ this.dragStatus = "start" /* START */;
396
+ this.moveableHelper?.onScaleStart(e);
397
+ }).on("scale", (e) => {
398
+ if (!this.target || !this.dragEl)
399
+ return;
400
+ this.dragStatus = "ing" /* ING */;
401
+ this.moveableHelper?.onScale(e);
402
+ const frame = this.moveableHelper?.getFrame(e.target);
403
+ this.target.style.transform = frame?.toCSSObject().transform || "";
404
+ }).on("scaleEnd", (e) => {
405
+ this.dragStatus = "end" /* END */;
406
+ const frame = this.moveableHelper?.getFrame(e.target);
407
+ this.emit("update", {
408
+ el: this.target,
409
+ style: {
410
+ transform: frame?.get("transform")
411
+ }
412
+ });
296
413
  });
297
- return getSnapElements(el).filter((element) => element !== this.target && !this.target?.contains(element));
298
414
  }
299
415
  sort() {
300
416
  if (!this.target || !this.ghostEl)
@@ -316,12 +432,13 @@ class StageDragResize extends EventEmitter {
316
432
  }
317
433
  }
318
434
  update(isResize = false) {
319
- const rect = this.moveable.getRect();
320
- const offset = this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : getAbsolutePosition(this.target, rect);
435
+ if (!this.target)
436
+ return;
437
+ const offset = this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : { left: this.target.offsetLeft, top: this.target.offsetTop };
321
438
  const left = this.calcValueByFontsize(offset.left);
322
439
  const top = this.calcValueByFontsize(offset.top);
323
- const width = this.calcValueByFontsize(rect.width);
324
- const height = this.calcValueByFontsize(rect.height);
440
+ const width = this.calcValueByFontsize(this.target.clientWidth);
441
+ const height = this.calcValueByFontsize(this.target.clientHeight);
325
442
  this.emit("update", {
326
443
  el: this.target,
327
444
  style: isResize ? { left, top, width, height } : { left, top }
@@ -333,8 +450,8 @@ class StageDragResize extends EventEmitter {
333
450
  }
334
451
  const ghostEl = el.cloneNode(true);
335
452
  const { top, left } = getAbsolutePosition(el, getOffset(el));
336
- ghostEl.id = `${GHOST_EL_ID_PREFIX}${ghostEl.id}`;
337
- ghostEl.style.zIndex = "5";
453
+ ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
454
+ ghostEl.style.zIndex = ZIndex.GHOST_EL;
338
455
  ghostEl.style.opacity = ".5";
339
456
  ghostEl.style.position = "absolute";
340
457
  ghostEl.style.left = `${left}px`;
@@ -346,49 +463,73 @@ class StageDragResize extends EventEmitter {
346
463
  this.ghostEl?.remove();
347
464
  this.ghostEl = void 0;
348
465
  }
349
- generateDragEl(el) {
350
- if (this.dragEl) {
351
- this.destroyDragEl();
352
- }
353
- const { width, height } = el.getBoundingClientRect();
466
+ updateDragEl(el) {
354
467
  const offset = getOffset(el);
355
- const dragEl = globalThis.document.createElement("div");
356
- dragEl.style.cssText = `
468
+ const { transform } = getComputedStyle(el);
469
+ this.dragEl.style.cssText = `
357
470
  position: absolute;
471
+ transform: ${transform};
358
472
  left: ${offset.left}px;
359
473
  top: ${offset.top}px;
360
- width: ${width}px;
361
- height: ${height}px;
474
+ width: ${el.clientWidth}px;
475
+ height: ${el.clientHeight}px;
476
+ z-index: ${ZIndex.DRAG_EL};
362
477
  `;
363
- this.container.append(dragEl);
364
- return dragEl;
478
+ this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
479
+ if (typeof this.core.config.updateDragEl === "function") {
480
+ this.core.config.updateDragEl(this.dragEl, el);
481
+ }
365
482
  }
366
483
  destroyDragEl() {
367
484
  this.dragEl?.remove();
368
- this.dragEl = void 0;
369
485
  }
370
- async getOptions(options = {}) {
486
+ getOptions(options = {}) {
371
487
  if (!this.target)
372
488
  return {};
373
489
  const isAbsolute = this.mode === Mode.ABSOLUTE;
490
+ const isFixed = this.mode === Mode.FIXED;
374
491
  let { moveableOptions = {} } = this.core.config;
375
492
  if (typeof moveableOptions === "function") {
376
493
  moveableOptions = moveableOptions(this.core);
377
494
  }
495
+ const elementGuidelines = moveableOptions.elementGuidelines || this.target.parentElement?.children || [];
496
+ this.setElementGuidelines(elementGuidelines);
497
+ if (moveableOptions.elementGuidelines) {
498
+ delete moveableOptions.elementGuidelines;
499
+ }
378
500
  return {
379
- scrollable: true,
380
- origin: true,
501
+ origin: false,
502
+ rootContainer: this.core.container,
381
503
  zoom: 1,
382
- dragArea: true,
504
+ dragArea: false,
383
505
  draggable: true,
384
506
  resizable: true,
385
- snappable: isAbsolute,
386
- snapGap: isAbsolute,
387
- snapDirections: { center: isAbsolute, middle: isAbsolute },
388
- elementSnapDirections: { center: isAbsolute, middle: isAbsolute },
389
- elementGuidelines: isAbsolute ? await this.getSnapElements(this.target) : [],
507
+ scalable: false,
508
+ rotatable: false,
509
+ snappable: isAbsolute || isFixed,
510
+ snapGap: isAbsolute || isFixed,
511
+ snapThreshold: 5,
512
+ snapDigit: 0,
513
+ throttleDrag: 0,
514
+ isDisplaySnapDigit: isAbsolute,
515
+ snapDirections: {
516
+ top: isAbsolute,
517
+ right: isAbsolute,
518
+ bottom: isAbsolute,
519
+ left: isAbsolute,
520
+ center: isAbsolute,
521
+ middle: isAbsolute
522
+ },
523
+ elementSnapDirections: {
524
+ top: isAbsolute,
525
+ right: isAbsolute,
526
+ bottom: isAbsolute,
527
+ left: isAbsolute
528
+ },
529
+ isDisplayInnerSnapDigit: true,
390
530
  horizontalGuidelines: this.horizontalGuidelines,
391
531
  verticalGuidelines: this.verticalGuidelines,
532
+ elementGuidelines: this.elementGuidelines,
392
533
  bounds: {
393
534
  top: 0,
394
535
  left: 0,
@@ -457,18 +598,150 @@ const up = (deltaTop, target) => {
457
598
  };
458
599
  };
459
600
 
601
+ class TargetCalibrate extends EventEmitter {
602
+ constructor(config) {
603
+ super();
604
+ this.parent = config.parent;
605
+ this.mask = config.mask;
606
+ this.dr = config.dr;
607
+ this.core = config.core;
608
+ this.operationEl = globalThis.document.createElement("div");
609
+ this.parent.append(this.operationEl);
610
+ }
611
+ update(el, prefix) {
612
+ const { left, top } = this.getOffset(el);
613
+ const { transform } = getComputedStyle(el);
614
+ this.operationEl.style.cssText = `
615
+ position: absolute;
616
+ transform: ${transform};
617
+ left: ${left}px;
618
+ top: ${top}px;
619
+ width: ${el.clientWidth}px;
620
+ height: ${el.clientHeight}px;
621
+ `;
622
+ this.operationEl.id = `${prefix}${el.id}`;
623
+ if (typeof this.core.config.updateDragEl === "function") {
624
+ this.core.config.updateDragEl(this.operationEl, el);
625
+ }
626
+ return this.operationEl;
627
+ }
628
+ destroy() {
629
+ this.operationEl?.remove();
630
+ }
631
+ getOffset(el) {
632
+ const { offsetParent } = el;
633
+ const left = el.offsetLeft;
634
+ const top = el.offsetTop;
635
+ if (offsetParent) {
636
+ const parentOffset = this.getOffset(offsetParent);
637
+ return {
638
+ left: left + parentOffset.left,
639
+ top: top + parentOffset.top
640
+ };
641
+ }
642
+ if (this.dr.mode === Mode.FIXED) {
643
+ if (getMode(el) === Mode.FIXED) {
644
+ return {
645
+ left,
646
+ top
647
+ };
648
+ }
649
+ return {
650
+ left: left - this.mask.scrollLeft,
651
+ top: top - this.mask.scrollTop
652
+ };
653
+ }
654
+ if (getMode(el) === Mode.FIXED) {
655
+ return {
656
+ left: left + this.mask.scrollLeft,
657
+ top: top + this.mask.scrollTop
658
+ };
659
+ }
660
+ return {
661
+ left,
662
+ top
663
+ };
664
+ }
665
+ }
666
+
667
+ class StageHighlight extends EventEmitter {
668
+ constructor(config) {
669
+ super();
670
+ this.core = config.core;
671
+ this.container = config.container;
672
+ this.calibrationTarget = new TargetCalibrate({
673
+ parent: this.core.mask.content,
674
+ mask: this.core.mask,
675
+ dr: this.core.dr,
676
+ core: this.core
677
+ });
678
+ }
679
+ highlight(el) {
680
+ if (!el || el === this.target)
681
+ return;
682
+ this.target = el;
683
+ this.moveable?.destroy();
684
+ this.moveable = new Moveable(this.container, {
685
+ target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
686
+ origin: false,
687
+ rootContainer: this.core.container,
688
+ zoom: 1
689
+ });
690
+ }
691
+ clearHighlight() {
692
+ if (!this.moveable)
693
+ return;
694
+ this.target = void 0;
695
+ this.moveable.target = null;
696
+ this.moveable.updateTarget();
697
+ }
698
+ destroy() {
699
+ this.moveable?.destroy();
700
+ this.calibrationTarget.destroy();
701
+ }
702
+ }
703
+
460
704
  class Rule extends EventEmitter$1 {
461
- hGuides;
462
- vGuides;
463
- horizontalGuidelines = [];
464
- verticalGuidelines = [];
465
- container;
466
- containerResizeObserver;
467
705
  constructor(container) {
468
706
  super();
707
+ this.horizontalGuidelines = getGuideLineFromCache(GuidesType.HORIZONTAL);
708
+ this.verticalGuidelines = getGuideLineFromCache(GuidesType.VERTICAL);
709
+ this.getGuidesStyle = (type) => ({
710
+ position: "fixed",
711
+ zIndex: 1,
712
+ left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
713
+ top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
714
+ width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
715
+ height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
716
+ });
717
+ this.createGuides = (type, defaultGuides = []) => new Guides(this.container, {
718
+ type,
719
+ defaultGuides,
720
+ displayDragPos: true,
721
+ backgroundColor: "#fff",
722
+ lineColor: "#000",
723
+ textColor: "#000",
724
+ style: this.getGuidesStyle(type)
725
+ });
726
+ this.hGuidesChangeGuidesHandler = (e) => {
727
+ this.horizontalGuidelines = e.guides;
728
+ this.emit("changeGuides", {
729
+ type: GuidesType.HORIZONTAL,
730
+ guides: this.horizontalGuidelines
731
+ });
732
+ globalThis.localStorage.setItem(H_GUIDE_LINE_STORAGE_KEY, JSON.stringify(e.guides));
733
+ };
734
+ this.vGuidesChangeGuidesHandler = (e) => {
735
+ this.verticalGuidelines = e.guides;
736
+ this.emit("changeGuides", {
737
+ type: GuidesType.VERTICAL,
738
+ guides: this.verticalGuidelines
739
+ });
740
+ globalThis.localStorage.setItem(V_GUIDE_LINE_STORAGE_KEY, JSON.stringify(e.guides));
741
+ };
469
742
  this.container = container;
470
- this.hGuides = this.createGuides(GuidesType.HORIZONTAL);
471
- this.vGuides = this.createGuides(GuidesType.VERTICAL);
743
+ this.hGuides = this.createGuides(GuidesType.HORIZONTAL, this.horizontalGuidelines);
744
+ this.vGuides = this.createGuides(GuidesType.VERTICAL, this.verticalGuidelines);
472
745
  this.hGuides.on("changeGuides", this.hGuidesChangeGuidesHandler);
473
746
  this.vGuides.on("changeGuides", this.vGuidesChangeGuidesHandler);
474
747
  this.containerResizeObserver = new ResizeObserver(() => {
@@ -502,6 +775,8 @@ class Rule extends EventEmitter$1 {
502
775
  type: GuidesType.HORIZONTAL,
503
776
  guides: []
504
777
  });
778
+ globalThis.localStorage.setItem(V_GUIDE_LINE_STORAGE_KEY, "[]");
779
+ globalThis.localStorage.setItem(H_GUIDE_LINE_STORAGE_KEY, "[]");
505
780
  }
506
781
  showRule(show = true) {
507
782
  if (show) {
@@ -534,40 +809,10 @@ class Rule extends EventEmitter$1 {
534
809
  this.containerResizeObserver.disconnect();
535
810
  this.removeAllListeners();
536
811
  }
537
- getGuidesStyle = (type) => ({
538
- position: "fixed",
539
- zIndex: 1,
540
- left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
541
- top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
542
- width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
543
- height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
544
- });
545
- createGuides = (type, defaultGuides = []) => new Guides(this.container, {
546
- type,
547
- defaultGuides,
548
- displayDragPos: true,
549
- backgroundColor: "#fff",
550
- lineColor: "#000",
551
- textColor: "#000",
552
- style: this.getGuidesStyle(type)
553
- });
554
- hGuidesChangeGuidesHandler = (e) => {
555
- this.horizontalGuidelines = e.guides;
556
- this.emit("changeGuides", {
557
- type: GuidesType.HORIZONTAL,
558
- guides: this.horizontalGuidelines
559
- });
560
- };
561
- vGuidesChangeGuidesHandler = (e) => {
562
- this.verticalGuidelines = e.guides;
563
- this.emit("changeGuides", {
564
- type: GuidesType.VERTICAL,
565
- guides: this.verticalGuidelines
566
- });
567
- };
568
812
  }
569
813
 
570
814
  const wrapperClassName = "editor-mask-wrapper";
815
+ const throttleTime = 100;
571
816
  const hideScrollbar = () => {
572
817
  const style = globalThis.document.createElement("style");
573
818
  style.innerHTML = `
@@ -601,28 +846,73 @@ const createWrapper = () => {
601
846
  return el;
602
847
  };
603
848
  class StageMask extends Rule {
604
- content = createContent();
605
- wrapper;
606
- core;
607
- page = null;
608
- pageScrollParent = null;
609
- scrollTop = 0;
610
- scrollLeft = 0;
611
- width = 0;
612
- height = 0;
613
- wrapperHeight = 0;
614
- wrapperWidth = 0;
615
- mode = Mode.ABSOLUTE;
616
- pageResizeObserver = null;
617
- wrapperResizeObserver = null;
618
849
  constructor(config) {
619
850
  const wrapper = createWrapper();
620
851
  super(wrapper);
852
+ this.content = createContent();
853
+ this.page = null;
854
+ this.pageScrollParent = null;
855
+ this.scrollTop = 0;
856
+ this.scrollLeft = 0;
857
+ this.width = 0;
858
+ this.height = 0;
859
+ this.wrapperHeight = 0;
860
+ this.wrapperWidth = 0;
861
+ this.maxScrollTop = 0;
862
+ this.maxScrollLeft = 0;
863
+ this.intersectionObserver = null;
864
+ this.mode = Mode.ABSOLUTE;
865
+ this.pageResizeObserver = null;
866
+ this.wrapperResizeObserver = null;
867
+ this.highlightHandler = throttle((event) => {
868
+ this.emit("highlight", event);
869
+ }, throttleTime);
870
+ this.mouseDownHandler = (event) => {
871
+ this.emit("clearHighlight");
872
+ event.stopImmediatePropagation();
873
+ event.stopPropagation();
874
+ if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
875
+ return;
876
+ if (event.target.className.indexOf("moveable-control") !== -1) {
877
+ return;
878
+ }
879
+ this.content.removeEventListener("mousemove", this.highlightHandler);
880
+ this.emit("beforeSelect", event);
881
+ globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
882
+ };
883
+ this.mouseUpHandler = () => {
884
+ globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
885
+ this.content.addEventListener("mousemove", this.highlightHandler);
886
+ this.emit("select");
887
+ };
888
+ this.mouseWheelHandler = (event) => {
889
+ this.emit("clearHighlight");
890
+ if (!this.page)
891
+ throw new Error("page \u672A\u521D\u59CB\u5316");
892
+ const { deltaY, deltaX } = event;
893
+ if (this.page.clientHeight < this.wrapperHeight && deltaY)
894
+ return;
895
+ if (this.page.clientWidth < this.wrapperWidth && deltaX)
896
+ return;
897
+ if (this.maxScrollTop > 0) {
898
+ this.scrollTop = this.scrollTop + deltaY;
899
+ }
900
+ if (this.maxScrollLeft > 0) {
901
+ this.scrollLeft = this.scrollLeft + deltaX;
902
+ }
903
+ this.scroll();
904
+ this.emit("scroll", event);
905
+ };
906
+ this.mouseLeaveHandler = () => {
907
+ setTimeout(() => this.emit("clearHighlight"), throttleTime);
908
+ };
621
909
  this.wrapper = wrapper;
622
910
  this.core = config.core;
623
911
  this.content.addEventListener("mousedown", this.mouseDownHandler);
624
912
  this.wrapper.appendChild(this.content);
625
913
  this.content.addEventListener("wheel", this.mouseWheelHandler);
914
+ this.content.addEventListener("mousemove", this.highlightHandler);
915
+ this.content.addEventListener("mouseleave", this.mouseLeaveHandler);
626
916
  }
627
917
  setMode(mode) {
628
918
  this.mode = mode;
@@ -641,12 +931,33 @@ class StageMask extends Rule {
641
931
  this.page = page;
642
932
  this.pageScrollParent = getScrollParent(page) || this.core.renderer.contentWindow?.document.documentElement || null;
643
933
  this.pageResizeObserver?.disconnect();
934
+ this.wrapperResizeObserver?.disconnect();
935
+ this.intersectionObserver?.disconnect();
936
+ if (typeof IntersectionObserver !== "undefined") {
937
+ this.intersectionObserver = new IntersectionObserver((entries) => {
938
+ entries.forEach((entry) => {
939
+ const { target, intersectionRatio } = entry;
940
+ if (intersectionRatio <= 0) {
941
+ this.scrollIntoView(target);
942
+ }
943
+ this.intersectionObserver?.unobserve(target);
944
+ });
945
+ }, {
946
+ root: this.pageScrollParent,
947
+ rootMargin: "0px",
948
+ threshold: 1
949
+ });
950
+ }
644
951
  if (typeof ResizeObserver !== "undefined") {
645
952
  this.pageResizeObserver = new ResizeObserver((entries) => {
646
953
  const [entry] = entries;
647
954
  const { clientHeight, clientWidth } = entry.target;
648
955
  this.setHeight(clientHeight);
649
956
  this.setWidth(clientWidth);
957
+ this.scroll();
958
+ if (this.core.dr.moveable) {
959
+ this.core.dr.updateMoveable();
960
+ }
650
961
  });
651
962
  this.pageResizeObserver.observe(page);
652
963
  this.wrapperResizeObserver = new ResizeObserver((entries) => {
@@ -654,6 +965,8 @@ class StageMask extends Rule {
654
965
  const { clientHeight, clientWidth } = entry.target;
655
966
  this.wrapperHeight = clientHeight;
656
967
  this.wrapperWidth = clientWidth;
968
+ this.setMaxScrollLeft();
969
+ this.setMaxScrollTop();
657
970
  });
658
971
  this.wrapperResizeObserver.observe(this.wrapper);
659
972
  }
@@ -666,16 +979,32 @@ class StageMask extends Rule {
666
979
  setLayout(el) {
667
980
  this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE);
668
981
  }
982
+ scrollIntoView(el) {
983
+ el.scrollIntoView();
984
+ if (!this.pageScrollParent)
985
+ return;
986
+ this.scrollLeft = this.pageScrollParent.scrollLeft;
987
+ this.scrollTop = this.pageScrollParent.scrollTop;
988
+ this.scroll();
989
+ }
669
990
  destroy() {
670
991
  this.content?.remove();
671
992
  this.page = null;
672
993
  this.pageScrollParent = null;
673
994
  this.pageResizeObserver?.disconnect();
674
995
  this.wrapperResizeObserver?.disconnect();
996
+ this.content.removeEventListener("mouseleave", this.mouseLeaveHandler);
675
997
  super.destroy();
676
998
  }
677
999
  scroll() {
1000
+ this.fixScrollValue();
678
1001
  let { scrollLeft, scrollTop } = this;
1002
+ if (this.pageScrollParent) {
1003
+ this.pageScrollParent.scrollTo({
1004
+ top: scrollTop,
1005
+ left: scrollLeft
1006
+ });
1007
+ }
679
1008
  if (this.mode === Mode.FIXED) {
680
1009
  scrollLeft = 0;
681
1010
  scrollTop = 0;
@@ -688,71 +1017,65 @@ class StageMask extends Rule {
688
1017
  }
689
1018
  setHeight(height) {
690
1019
  this.height = height;
1020
+ this.setMaxScrollTop();
691
1021
  this.content.style.height = `${height}px`;
692
1022
  }
693
1023
  setWidth(width) {
694
1024
  this.width = width;
1025
+ this.setMaxScrollLeft();
695
1026
  this.content.style.width = `${width}px`;
696
1027
  }
697
- mouseDownHandler = async (event) => {
698
- event.stopImmediatePropagation();
699
- event.stopPropagation();
700
- if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
701
- return;
702
- if (event.target.className.indexOf("moveable-control") !== -1) {
703
- return;
704
- }
705
- this.emit("beforeSelect", event);
706
- globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
707
- };
708
- mouseUpHandler = () => {
709
- globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
710
- this.emit("select");
711
- };
712
- mouseWheelHandler = (event) => {
713
- if (!this.page)
714
- throw new Error("page \u672A\u521D\u59CB\u5316");
715
- const { deltaY, deltaX } = event;
716
- const { height, wrapperHeight, width, wrapperWidth } = this;
717
- const maxScrollTop = height - wrapperHeight;
718
- const maxScrollLeft = width - wrapperWidth;
719
- if (maxScrollTop > 0) {
720
- if (deltaY > 0) {
721
- this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
722
- } else {
723
- this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
724
- }
725
- }
726
- if (width > wrapperWidth) {
727
- if (deltaX > 0) {
728
- this.scrollLeft = this.scrollLeft + Math.min(maxScrollLeft - this.scrollLeft, deltaX);
729
- } else {
730
- this.scrollLeft = Math.max(this.scrollLeft + deltaX, 0);
731
- }
732
- }
733
- if (this.mode !== Mode.FIXED) {
734
- this.scrollTo(this.scrollLeft, this.scrollTop);
735
- }
736
- if (this.pageScrollParent) {
737
- this.pageScrollParent.scrollTo({
738
- top: this.scrollTop,
739
- left: this.scrollLeft
740
- });
741
- }
742
- this.scroll();
743
- this.emit("scroll", event);
744
- };
1028
+ setMaxScrollLeft() {
1029
+ this.maxScrollLeft = Math.max(this.width - this.wrapperWidth, 0);
1030
+ }
1031
+ setMaxScrollTop() {
1032
+ this.maxScrollTop = Math.max(this.height - this.wrapperHeight, 0);
1033
+ }
1034
+ fixScrollValue() {
1035
+ if (this.scrollTop < 0)
1036
+ this.scrollTop = 0;
1037
+ if (this.scrollLeft < 0)
1038
+ this.scrollLeft = 0;
1039
+ if (this.maxScrollTop < this.scrollTop)
1040
+ this.scrollTop = this.maxScrollTop;
1041
+ if (this.maxScrollLeft < this.scrollLeft)
1042
+ this.scrollLeft = this.maxScrollLeft;
1043
+ }
745
1044
  }
746
1045
 
747
1046
  class StageRender extends EventEmitter {
748
- contentWindow = null;
749
- runtime = null;
750
- iframe;
751
- runtimeUrl;
752
- core;
753
- render;
754
1047
  constructor({ core }) {
755
1048
  super();
1049
+ this.contentWindow = null;
1050
+ this.runtime = null;
1051
+ this.getMagicApi = () => ({
1052
+ onPageElUpdate: (el) => this.emit("page-el-update", el),
1053
+ onRuntimeReady: (runtime) => {
1054
+ this.runtime = runtime;
1055
+ globalThis.runtime = runtime;
1056
+ this.emit("runtime-ready", runtime);
1057
+ }
1058
+ });
1059
+ this.getRuntime = () => {
1060
+ if (this.runtime)
1061
+ return Promise.resolve(this.runtime);
1062
+ return new Promise((resolve) => {
1063
+ const listener = (runtime) => {
1064
+ this.off("runtime-ready", listener);
1065
+ resolve(runtime);
1066
+ };
1067
+ this.on("runtime-ready", listener);
1068
+ });
1069
+ };
1070
+ this.loadHandler = async () => {
1071
+ this.emit("onload");
1072
+ if (this.render) {
1073
+ const el = await this.render(this.core);
1074
+ if (el) {
1075
+ this.iframe?.contentDocument?.body?.appendChild(el);
1076
+ }
1077
+ }
1078
+ };
756
1079
  this.core = core;
757
1080
  this.runtimeUrl = core.config.runtimeUrl || "";
758
1081
  this.render = core.config.render;
@@ -765,14 +1088,6 @@ class StageRender extends EventEmitter {
765
1088
  `;
766
1089
  this.iframe.addEventListener("load", this.loadHandler);
767
1090
  }
768
- getMagicApi = () => ({
769
- onPageElUpdate: (el) => this.emit("page-el-update", el),
770
- onRuntimeReady: (runtime) => {
771
- this.runtime = runtime;
772
- globalThis.runtime = runtime;
773
- this.emit("runtime-ready", runtime);
774
- }
775
- });
776
1091
  async mount(el) {
777
1092
  if (this.iframe) {
778
1093
  if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
@@ -789,17 +1104,6 @@ class StageRender extends EventEmitter {
789
1104
  throw Error("mount \u5931\u8D25");
790
1105
  }
791
1106
  }
792
- getRuntime = () => {
793
- if (this.runtime)
794
- return Promise.resolve(this.runtime);
795
- return new Promise((resolve) => {
796
- const listener = (runtime) => {
797
- this.off("runtime-ready", listener);
798
- resolve(runtime);
799
- };
800
- this.on("runtime-ready", listener);
801
- });
802
- };
803
1107
  destroy() {
804
1108
  this.iframe?.removeEventListener("load", this.loadHandler);
805
1109
  this.contentWindow = null;
@@ -807,35 +1111,25 @@ class StageRender extends EventEmitter {
807
1111
  this.iframe = void 0;
808
1112
  this.removeAllListeners();
809
1113
  }
810
- loadHandler = async () => {
811
- this.emit("onload");
812
- if (this.render) {
813
- const el = await this.render(this.core);
814
- if (el) {
815
- this.iframe?.contentDocument?.body?.appendChild(el);
816
- }
817
- }
818
- };
819
1114
  }
820
1115
 
821
1116
  class StageCore extends EventEmitter {
822
- selectedDom;
823
- renderer;
824
- mask;
825
- dr;
826
- config;
827
- zoom = DEFAULT_ZOOM;
828
- canSelect;
829
1117
  constructor(config) {
830
1118
  super();
1119
+ this.zoom = DEFAULT_ZOOM;
831
1120
  this.config = config;
832
1121
  this.setZoom(config.zoom);
833
1122
  this.canSelect = config.canSelect || ((el) => !!el.id);
834
1123
  this.renderer = new StageRender({ core: this });
835
1124
  this.mask = new StageMask({ core: this });
836
1125
  this.dr = new StageDragResize({ core: this, container: this.mask.content });
837
- this.renderer.on("runtime-ready", (runtime) => this.emit("runtime-ready", runtime));
838
- this.renderer.on("page-el-update", (el) => this.mask?.observe(el));
1126
+ this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
1127
+ this.renderer.on("runtime-ready", (runtime) => {
1128
+ this.emit("runtime-ready", runtime);
1129
+ });
1130
+ this.renderer.on("page-el-update", (el) => {
1131
+ this.mask?.observe(el);
1132
+ });
839
1133
  this.mask.on("beforeSelect", (event) => {
840
1134
  this.setElementFromPoint(event);
841
1135
  }).on("select", () => {
@@ -843,6 +1137,16 @@ class StageCore extends EventEmitter {
843
1137
  }).on("changeGuides", (data) => {
844
1138
  this.dr.setGuidelines(data.type, data.guides);
845
1139
  this.emit("changeGuides", data);
1140
+ }).on("highlight", async (event) => {
1141
+ await this.setElementFromPoint(event);
1142
+ if (this.highlightedDom === this.selectedDom) {
1143
+ this.highlightLayer.clearHighlight();
1144
+ return;
1145
+ }
1146
+ this.highlightLayer.highlight(this.highlightedDom);
1147
+ this.emit("highlight", this.highlightedDom);
1148
+ }).on("clearHighlight", async () => {
1149
+ this.highlightLayer.clearHighlight();
846
1150
  });
847
1151
  this.dr.on("update", (data) => {
848
1152
  setTimeout(() => this.emit("update", data));
@@ -866,60 +1170,81 @@ class StageCore extends EventEmitter {
866
1170
  let stopped = false;
867
1171
  const stop = () => stopped = true;
868
1172
  for (const el of els) {
869
- if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, stop)) {
1173
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, event, stop)) {
870
1174
  if (stopped)
871
1175
  break;
1176
+ if (event.type === "mousemove") {
1177
+ this.highlight(el);
1178
+ break;
1179
+ }
872
1180
  this.select(el, event);
873
1181
  break;
874
1182
  }
875
1183
  }
876
1184
  }
877
1185
  async select(idOrEl, event) {
878
- let el;
879
- if (typeof idOrEl === "string" || typeof idOrEl === "number") {
880
- const runtime2 = await this.renderer?.getRuntime();
881
- el = await runtime2?.select?.(`${idOrEl}`);
882
- if (!el)
883
- throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
884
- } else {
885
- el = idOrEl;
886
- }
1186
+ const el = await this.getTargetElement(idOrEl);
887
1187
  if (el === this.selectedDom)
888
1188
  return;
889
- const runtime = await this.renderer?.getRuntime();
1189
+ const runtime = await this.renderer.getRuntime();
1190
+ await runtime?.select?.(el.id);
890
1191
  if (runtime?.beforeSelect) {
891
1192
  await runtime.beforeSelect(el);
892
1193
  }
893
1194
  this.mask.setLayout(el);
894
- this.dr?.select(el, event);
1195
+ this.dr.select(el, event);
1196
+ if (this.config.autoScrollIntoView || el.dataset.autoScrollIntoView) {
1197
+ this.mask.intersectionObserver?.observe(el);
1198
+ }
895
1199
  this.selectedDom = el;
1200
+ if (this.renderer.contentWindow) {
1201
+ removeSelectedClassName(this.renderer.contentWindow.document);
1202
+ if (this.selectedDom) {
1203
+ addSelectedClassName(this.selectedDom, this.renderer.contentWindow.document);
1204
+ }
1205
+ }
896
1206
  }
897
1207
  update(data) {
898
1208
  const { config } = data;
899
- this.renderer?.getRuntime().then((runtime) => {
1209
+ return this.renderer?.getRuntime().then((runtime) => {
900
1210
  runtime?.update?.(data);
901
1211
  setTimeout(() => {
902
1212
  const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
903
- if (el) {
1213
+ if (el && el.id === this.selectedDom?.id) {
1214
+ this.selectedDom = el;
904
1215
  this.mask.setLayout(el);
905
- this.dr?.select(el);
1216
+ this.dr.updateMoveable(el);
906
1217
  }
907
1218
  }, 0);
908
1219
  });
909
1220
  }
1221
+ async highlight(idOrEl) {
1222
+ let el;
1223
+ try {
1224
+ el = await this.getTargetElement(idOrEl);
1225
+ } catch (error) {
1226
+ this.highlightLayer.clearHighlight();
1227
+ return;
1228
+ }
1229
+ if (el === this.highlightedDom)
1230
+ return;
1231
+ this.highlightLayer.highlight(el);
1232
+ this.highlightedDom = el;
1233
+ }
910
1234
  sortNode(data) {
911
- this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
1235
+ return this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
912
1236
  }
913
1237
  add(data) {
914
- this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
1238
+ return this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
915
1239
  }
916
1240
  remove(data) {
917
- this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
1241
+ return this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
918
1242
  }
919
1243
  setZoom(zoom = DEFAULT_ZOOM) {
920
1244
  this.zoom = zoom;
921
1245
  }
922
1246
  mount(el) {
1247
+ this.container = el;
923
1248
  const { mask, renderer } = this;
924
1249
  renderer.mount(el);
925
1250
  mask.mount(el);
@@ -930,11 +1255,22 @@ class StageCore extends EventEmitter {
930
1255
  this.dr.clearGuides();
931
1256
  }
932
1257
  destroy() {
933
- const { mask, renderer, dr } = this;
1258
+ const { mask, renderer, dr, highlightLayer } = this;
934
1259
  renderer.destroy();
935
1260
  mask.destroy();
936
1261
  dr.destroy();
1262
+ highlightLayer.destroy();
937
1263
  this.removeAllListeners();
1264
+ this.container = void 0;
1265
+ }
1266
+ async getTargetElement(idOrEl) {
1267
+ if (typeof idOrEl === "string" || typeof idOrEl === "number") {
1268
+ const el = this.renderer.contentWindow?.document.getElementById(`${idOrEl}`);
1269
+ if (!el)
1270
+ throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
1271
+ return el;
1272
+ }
1273
+ return idOrEl;
938
1274
  }
939
1275
  }
940
1276