mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-05 19:41:40 +08:00
parent
42f1f28b02
commit
fff56a97f7
@ -41,7 +41,7 @@ class StageOverlay extends BaseService {
|
||||
this.state[name] = value;
|
||||
}
|
||||
|
||||
public openOverlay(el: HTMLElement | undefined | null) {
|
||||
public openOverlay(el: HTMLElement | null) {
|
||||
const stageOptions = this.get('stageOptions');
|
||||
if (!el || !stageOptions) return;
|
||||
|
||||
@ -168,7 +168,7 @@ class StageOverlay extends BaseService {
|
||||
});
|
||||
|
||||
if (await stageOptions?.canSelect?.(contentEl)) {
|
||||
subStage?.select(contentEl);
|
||||
subStage?.select(contentEl.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,15 +25,25 @@ import { Env } from '@tmagic/core';
|
||||
import type { Id } from '@tmagic/schema';
|
||||
import { addClassName, getDocument, removeClassNameByClassName } from '@tmagic/utils';
|
||||
|
||||
import { CONTAINER_HIGHLIGHT_CLASS_NAME, GHOST_EL_ID_PREFIX, GuidesType, MouseButton, PAGE_CLASS } from './const';
|
||||
import {
|
||||
AbleActionEventType,
|
||||
CONTAINER_HIGHLIGHT_CLASS_NAME,
|
||||
ContainerHighlightType,
|
||||
GHOST_EL_ID_PREFIX,
|
||||
GuidesType,
|
||||
MouseButton,
|
||||
PAGE_CLASS,
|
||||
SelectStatus,
|
||||
StageDragStatus,
|
||||
} from './const';
|
||||
import DragResizeHelper from './DragResizeHelper';
|
||||
import StageDragResize from './StageDragResize';
|
||||
import StageHighlight from './StageHighlight';
|
||||
import StageMultiDragResize from './StageMultiDragResize';
|
||||
import {
|
||||
import type {
|
||||
ActionManagerConfig,
|
||||
ActionManagerEvents,
|
||||
CanSelect,
|
||||
ContainerHighlightType,
|
||||
CustomizeMoveableOptions,
|
||||
CustomizeMoveableOptionsCallbackConfig,
|
||||
GetElementsFromPoint,
|
||||
@ -42,8 +52,7 @@ import {
|
||||
IsContainer,
|
||||
Point,
|
||||
RemoveEventData,
|
||||
SelectStatus,
|
||||
StageDragStatus,
|
||||
SortEventData,
|
||||
UpdateEventData,
|
||||
} from './types';
|
||||
import { isMoveableButton } from './util';
|
||||
@ -62,7 +71,7 @@ export default class ActionManager extends EventEmitter {
|
||||
/** 单选、多选、高亮的容器(蒙层的content) */
|
||||
private container: HTMLElement;
|
||||
/** 当前选中的节点 */
|
||||
private selectedEl: HTMLElement | undefined;
|
||||
private selectedEl: HTMLElement | null = null;
|
||||
/** 多选选中的节点组 */
|
||||
private selectedElList: HTMLElement[] = [];
|
||||
/** 当前高亮的节点 */
|
||||
@ -96,7 +105,7 @@ export default class ActionManager extends EventEmitter {
|
||||
}
|
||||
|
||||
this.emit('mousemove', event);
|
||||
this.highlight(el);
|
||||
this.highlight(el.id);
|
||||
}, throttleTime);
|
||||
|
||||
constructor(config: ActionManagerConfig) {
|
||||
@ -181,11 +190,11 @@ export default class ActionManager extends EventEmitter {
|
||||
return el.id === this.selectedEl?.id;
|
||||
}
|
||||
|
||||
public setSelectedEl(el?: HTMLElement): void {
|
||||
public setSelectedEl(el: HTMLElement | null): void {
|
||||
this.selectedEl = el;
|
||||
}
|
||||
|
||||
public getSelectedEl(): HTMLElement | undefined {
|
||||
public getSelectedEl(): HTMLElement | null {
|
||||
return this.selectedEl;
|
||||
}
|
||||
|
||||
@ -207,7 +216,7 @@ export default class ActionManager extends EventEmitter {
|
||||
* @param event 鼠标事件
|
||||
* @returns 鼠标下方第一个可选中元素
|
||||
*/
|
||||
public async getElementFromPoint(event: MouseEvent): Promise<HTMLElement | undefined> {
|
||||
public async getElementFromPoint(event: MouseEvent): Promise<HTMLElement | null> {
|
||||
const els = this.getElementsFromPoint(event as Point);
|
||||
|
||||
this.emit('get-elements-from-point', els);
|
||||
@ -220,6 +229,7 @@ export default class ActionManager extends EventEmitter {
|
||||
return el;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -257,14 +267,20 @@ export default class ActionManager extends EventEmitter {
|
||||
return this.multiDr?.canSelect(el, selectedEl) || false;
|
||||
}
|
||||
|
||||
public select(el: HTMLElement, event: MouseEvent | undefined): void {
|
||||
public select(el: HTMLElement | null, event?: MouseEvent): void {
|
||||
this.setSelectedEl(el);
|
||||
this.clearSelectStatus(SelectStatus.MULTI_SELECT);
|
||||
this.dr.select(el, event);
|
||||
}
|
||||
|
||||
public multiSelect(idOrElList: HTMLElement[] | Id[]): void {
|
||||
this.selectedElList = idOrElList.map((idOrEl) => this.getTargetElement(idOrEl));
|
||||
public multiSelect(ids: Id[]): void {
|
||||
this.selectedElList = [];
|
||||
ids.forEach((id) => {
|
||||
const el = this.getTargetElement(id);
|
||||
if (el) {
|
||||
this.selectedElList.push(el);
|
||||
}
|
||||
});
|
||||
this.clearSelectStatus(SelectStatus.SELECT);
|
||||
this.multiDr?.multiSelect(this.selectedElList);
|
||||
}
|
||||
@ -277,10 +293,10 @@ export default class ActionManager extends EventEmitter {
|
||||
this.highlightedEl = el;
|
||||
}
|
||||
|
||||
public highlight(idOrEl: Id | HTMLElement): void {
|
||||
public highlight(id: Id): void {
|
||||
let el;
|
||||
try {
|
||||
el = this.getTargetElement(idOrEl);
|
||||
el = this.getTargetElement(id);
|
||||
} catch (error) {
|
||||
this.clearHighlight();
|
||||
return;
|
||||
@ -366,6 +382,20 @@ export default class ActionManager extends EventEmitter {
|
||||
this.highlightLayer.destroy();
|
||||
}
|
||||
|
||||
public on<Name extends keyof ActionManagerEvents, Param extends ActionManagerEvents[Name]>(
|
||||
eventName: Name,
|
||||
listener: (...args: Param) => void,
|
||||
) {
|
||||
return super.on(eventName, listener as any);
|
||||
}
|
||||
|
||||
public emit<Name extends keyof ActionManagerEvents, Param extends ActionManagerEvents[Name]>(
|
||||
eventName: Name,
|
||||
...args: Param
|
||||
) {
|
||||
return super.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
private createDr(config: ActionManagerConfig) {
|
||||
const createDrHelper = () =>
|
||||
new DragResizeHelper({
|
||||
@ -388,14 +418,14 @@ export default class ActionManager extends EventEmitter {
|
||||
// 点击组件并立即拖动的场景,要保证select先被触发,延迟update通知
|
||||
setTimeout(() => this.emit('update', data));
|
||||
})
|
||||
.on('sort', (data: UpdateEventData) => {
|
||||
.on('sort', (data: SortEventData) => {
|
||||
// 点击组件并立即拖动的场景,要保证select先被触发,延迟update通知
|
||||
setTimeout(() => this.emit('sort', data));
|
||||
})
|
||||
.on('select-parent', () => {
|
||||
.on(AbleActionEventType.SELECT_PARENT, () => {
|
||||
this.emit('select-parent');
|
||||
})
|
||||
.on('remove', () => {
|
||||
.on(AbleActionEventType.REMOVE, () => {
|
||||
const drTarget = this.dr.getTarget();
|
||||
if (!drTarget) return;
|
||||
const data: RemoveEventData = {
|
||||
@ -430,11 +460,10 @@ export default class ActionManager extends EventEmitter {
|
||||
?.on('update', (data: UpdateEventData) => {
|
||||
this.emit('multi-update', data);
|
||||
})
|
||||
.on('change-to-select', async (id: Id, e: MouseEvent) => {
|
||||
.on('change-to-select', (id: Id, e: MouseEvent) => {
|
||||
// 如果还在多选状态,不触发切换到单选
|
||||
if (this.isMultiSelectStatus) return false;
|
||||
const el = this.getTargetElement(id);
|
||||
this.emit('change-to-select', el, e);
|
||||
if (this.isMultiSelectStatus) return;
|
||||
this.emit('change-to-select', id, e);
|
||||
});
|
||||
|
||||
return multiDr;
|
||||
@ -474,13 +503,16 @@ export default class ActionManager extends EventEmitter {
|
||||
// 如果已有单选选中元素,不是magic-ui-page就可以加入多选列表
|
||||
if (this.selectedEl && !this.selectedEl.className.includes(PAGE_CLASS)) {
|
||||
this.selectedElList.push(this.selectedEl as HTMLElement);
|
||||
this.setSelectedEl(undefined);
|
||||
this.setSelectedEl(null);
|
||||
}
|
||||
|
||||
// 判断元素是否已在多选列表
|
||||
const existIndex = this.selectedElList.findIndex((selectedDom) => selectedDom.id === el.id);
|
||||
if (existIndex !== -1) {
|
||||
// 再次点击取消选中
|
||||
this.selectedElList.splice(existIndex, 1);
|
||||
if (this.selectedElList.length > 1) {
|
||||
this.selectedElList.splice(existIndex, 1);
|
||||
}
|
||||
} else {
|
||||
this.selectedElList.push(el);
|
||||
}
|
||||
@ -580,6 +612,7 @@ export default class ActionManager extends EventEmitter {
|
||||
if (!el) return;
|
||||
this.emit('before-select', el, event);
|
||||
}
|
||||
|
||||
getDocument().addEventListener('mouseup', this.mouseUpHandler);
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
import type {
|
||||
OnDrag,
|
||||
OnDragGroup,
|
||||
OnDragGroupStart,
|
||||
@ -34,7 +34,7 @@ import MoveableHelper from 'moveable-helper';
|
||||
|
||||
import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, Mode, ZIndex } from './const';
|
||||
import TargetShadow from './TargetShadow';
|
||||
import { DragResizeHelperConfig, Rect, TargetElement } from './types';
|
||||
import type { DragResizeHelperConfig, Rect, TargetElement } from './types';
|
||||
import { calcValueByFontsize, getAbsolutePosition, getBorderWidth, getMarginValue, getOffset } from './util';
|
||||
|
||||
/**
|
||||
|
@ -28,12 +28,14 @@ import StageMask from './StageMask';
|
||||
import StageRender from './StageRender';
|
||||
import type {
|
||||
ActionManagerConfig,
|
||||
CoreEvents,
|
||||
CustomizeRender,
|
||||
GuidesEventData,
|
||||
Point,
|
||||
RemoveData,
|
||||
RemoveEventData,
|
||||
Runtime,
|
||||
SortEventData,
|
||||
StageCoreConfig,
|
||||
UpdateData,
|
||||
UpdateEventData,
|
||||
@ -81,41 +83,41 @@ export default class StageCore extends EventEmitter {
|
||||
|
||||
/**
|
||||
* 单选选中元素
|
||||
* @param idOrEl 选中的id或者元素
|
||||
* @param id 选中的id
|
||||
*/
|
||||
public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
|
||||
const el = this.renderer.getTargetElement(idOrEl);
|
||||
public async select(id: Id, event?: MouseEvent): Promise<void> {
|
||||
const el = this.renderer.getTargetElement(id);
|
||||
if (el === this.actionManager.getSelectedEl()) return;
|
||||
|
||||
await this.renderer.select([el]);
|
||||
await this.renderer.select([id]);
|
||||
|
||||
this.mask.setLayout(el);
|
||||
el && this.mask.setLayout(el);
|
||||
|
||||
this.actionManager.select(el, event);
|
||||
|
||||
if (this.autoScrollIntoView || el.dataset.autoScrollIntoView) {
|
||||
if (el && (this.autoScrollIntoView || el.dataset.autoScrollIntoView)) {
|
||||
this.mask.observerIntersection(el);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多选选中多个元素
|
||||
* @param idOrElList 选中元素的id或元素列表
|
||||
* @param ids 选中元素的id列表
|
||||
*/
|
||||
public async multiSelect(idOrElList: HTMLElement[] | Id[]): Promise<void> {
|
||||
const els = idOrElList.map((idOrEl) => this.renderer.getTargetElement(idOrEl));
|
||||
public async multiSelect(ids: Id[]): Promise<void> {
|
||||
const els = ids.map((id) => this.renderer.getTargetElement(id)).filter((el) => Boolean(el));
|
||||
if (els.length === 0) return;
|
||||
|
||||
const lastEl = els[els.length - 1];
|
||||
// 是否减少了组件选择
|
||||
const isReduceSelect = els.length < this.actionManager.getSelectedElList().length;
|
||||
await this.renderer.select(els);
|
||||
await this.renderer.select(ids);
|
||||
|
||||
this.mask.setLayout(lastEl);
|
||||
lastEl && this.mask.setLayout(lastEl);
|
||||
|
||||
this.actionManager.multiSelect(idOrElList);
|
||||
this.actionManager.multiSelect(ids);
|
||||
|
||||
if ((this.autoScrollIntoView || lastEl.dataset.autoScrollIntoView) && !isReduceSelect) {
|
||||
if (lastEl && (this.autoScrollIntoView || lastEl.dataset.autoScrollIntoView) && !isReduceSelect) {
|
||||
this.mask.observerIntersection(lastEl);
|
||||
}
|
||||
}
|
||||
@ -124,8 +126,8 @@ export default class StageCore extends EventEmitter {
|
||||
* 高亮选中元素
|
||||
* @param el 要高亮的元素
|
||||
*/
|
||||
public highlight(idOrEl: Id | HTMLElement): void {
|
||||
this.actionManager.highlight(idOrEl);
|
||||
public highlight(id: Id): void {
|
||||
this.actionManager.highlight(id);
|
||||
}
|
||||
|
||||
public clearHighlight(): void {
|
||||
@ -248,6 +250,17 @@ export default class StageCore extends EventEmitter {
|
||||
this.container = undefined;
|
||||
}
|
||||
|
||||
public on<Name extends keyof CoreEvents, Param extends CoreEvents[Name]>(
|
||||
eventName: Name,
|
||||
listener: (...args: Param) => void | Promise<void>,
|
||||
) {
|
||||
return super.on(eventName, listener as any);
|
||||
}
|
||||
|
||||
public emit<Name extends keyof CoreEvents, Param extends CoreEvents[Name]>(eventName: Name, ...args: Param) {
|
||||
return super.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听页面大小变化
|
||||
*/
|
||||
@ -280,7 +293,7 @@ export default class StageCore extends EventEmitter {
|
||||
updateDragEl: config.updateDragEl,
|
||||
getRootContainer: () => this.container,
|
||||
getRenderDocument: () => this.renderer.getDocument(),
|
||||
getTargetElement: (idOrEl: Id | HTMLElement) => this.renderer.getTargetElement(idOrEl),
|
||||
getTargetElement: (id: Id) => this.renderer.getTargetElement(id),
|
||||
getElementsFromPoint: (point: Point) => this.renderer.getElementsFromPoint(point),
|
||||
};
|
||||
|
||||
@ -322,14 +335,14 @@ export default class StageCore extends EventEmitter {
|
||||
*/
|
||||
private initActionManagerEvent(): void {
|
||||
this.actionManager
|
||||
.on('before-select', (idOrEl: Id | HTMLElement, event?: MouseEvent) => {
|
||||
this.select(idOrEl, event);
|
||||
.on('before-select', (el: HTMLElement, event?: MouseEvent) => {
|
||||
this.select(el.id, event);
|
||||
})
|
||||
.on('select', (selectedEl: HTMLElement, event: MouseEvent) => {
|
||||
this.emit('select', selectedEl, event);
|
||||
})
|
||||
.on('before-multi-select', (idOrElList: HTMLElement[] | Id[]) => {
|
||||
this.multiSelect(idOrElList);
|
||||
.on('before-multi-select', (els: HTMLElement[]) => {
|
||||
this.multiSelect(els.map((el) => el.id));
|
||||
})
|
||||
.on('multi-select', (selectedElList: HTMLElement[], event: MouseEvent) => {
|
||||
this.emit('multi-select', selectedElList, event);
|
||||
@ -347,7 +360,7 @@ export default class StageCore extends EventEmitter {
|
||||
.on('update', (data: UpdateEventData) => {
|
||||
this.emit('update', data);
|
||||
})
|
||||
.on('sort', (data: UpdateEventData) => {
|
||||
.on('sort', (data: SortEventData) => {
|
||||
this.emit('sort', data);
|
||||
})
|
||||
.on('select-parent', () => {
|
||||
@ -364,10 +377,13 @@ export default class StageCore extends EventEmitter {
|
||||
private initMulDrEvent(): void {
|
||||
this.actionManager
|
||||
// 多选切换到单选
|
||||
.on('change-to-select', (el: HTMLElement, e: MouseEvent) => {
|
||||
this.select(el);
|
||||
.on('change-to-select', (id: Id, e: MouseEvent) => {
|
||||
this.select(id);
|
||||
// 先保证画布内完成渲染,再通知外部更新
|
||||
setTimeout(() => this.emit('select', el, e));
|
||||
setTimeout(() => {
|
||||
const el = this.renderer.getTargetElement(id);
|
||||
el && this.emit('select', el, e);
|
||||
});
|
||||
})
|
||||
.on('multi-update', (data: UpdateEventData) => {
|
||||
this.emit('update', data);
|
||||
@ -378,7 +394,7 @@ export default class StageCore extends EventEmitter {
|
||||
* 初始化Highlight类通过ActionManager抛出来的事件监听
|
||||
*/
|
||||
private initHighlightEvent(): void {
|
||||
this.actionManager.on('highlight', async (highlightEl: HTMLElement) => {
|
||||
this.actionManager.on('highlight', (highlightEl: HTMLElement) => {
|
||||
this.emit('highlight', highlightEl);
|
||||
});
|
||||
}
|
||||
|
@ -19,11 +19,16 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import Moveable, { MoveableOptions } from 'moveable';
|
||||
|
||||
import { Mode } from './const';
|
||||
import { Mode, StageDragStatus } from './const';
|
||||
import DragResizeHelper from './DragResizeHelper';
|
||||
import MoveableOptionsManager from './MoveableOptionsManager';
|
||||
import type { DelayedMarkContainer, GetRenderDocument, MarkContainerEnd, StageDragResizeConfig } from './types';
|
||||
import { StageDragStatus } from './types';
|
||||
import type {
|
||||
DelayedMarkContainer,
|
||||
DrEvents,
|
||||
GetRenderDocument,
|
||||
MarkContainerEnd,
|
||||
StageDragResizeConfig,
|
||||
} from './types';
|
||||
import { down, getMode, up } from './util';
|
||||
|
||||
/**
|
||||
@ -32,7 +37,7 @@ import { down, getMode, up } from './util';
|
||||
*/
|
||||
export default class StageDragResize extends MoveableOptionsManager {
|
||||
/** 目标节点 */
|
||||
private target?: HTMLElement;
|
||||
private target: HTMLElement | null = null;
|
||||
/** Moveable拖拽类实例 */
|
||||
private moveable?: Moveable;
|
||||
/** 拖动状态 */
|
||||
@ -70,7 +75,12 @@ export default class StageDragResize extends MoveableOptionsManager {
|
||||
* @param el 选中组件的Dom节点元素
|
||||
* @param event 鼠标事件
|
||||
*/
|
||||
public select(el: HTMLElement, event?: MouseEvent): void {
|
||||
public select(el: HTMLElement | null, event?: MouseEvent): void {
|
||||
if (!el) {
|
||||
this.moveable?.destroy();
|
||||
this.moveable = undefined;
|
||||
return;
|
||||
}
|
||||
// 从不能拖动到能拖动的节点之间切换,要重新创建moveable,不然dragStart不生效
|
||||
if (!this.moveable || el !== this.target) {
|
||||
this.initMoveable(el);
|
||||
@ -119,6 +129,17 @@ export default class StageDragResize extends MoveableOptionsManager {
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
public on<Name extends keyof DrEvents, Param extends DrEvents[Name]>(
|
||||
eventName: Name,
|
||||
listener: (...args: Param) => void | Promise<void>,
|
||||
) {
|
||||
return super.on(eventName, listener as any);
|
||||
}
|
||||
|
||||
public emit<Name extends keyof DrEvents, Param extends DrEvents[Name]>(eventName: Name, ...args: Param) {
|
||||
return super.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
private init(el: HTMLElement): MoveableOptions {
|
||||
// 如果有滚动条会导致resize时获取到width,height不准确
|
||||
if (/(auto|scroll)/.test(el.style.overflow)) {
|
||||
@ -247,16 +268,19 @@ export default class StageDragResize extends MoveableOptionsManager {
|
||||
.on('rotateEnd', (e) => {
|
||||
this.dragStatus = StageDragStatus.END;
|
||||
const frame = this.dragResizeHelper?.getFrame(e.target);
|
||||
this.emit('update', {
|
||||
data: [
|
||||
{
|
||||
el: this.target,
|
||||
style: {
|
||||
transform: frame?.get('transform'),
|
||||
if (this.target && frame) {
|
||||
this.emit('update', {
|
||||
data: [
|
||||
{
|
||||
el: this.target,
|
||||
style: {
|
||||
transform: frame.get('transform'),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
],
|
||||
parentEl: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -276,16 +300,19 @@ export default class StageDragResize extends MoveableOptionsManager {
|
||||
.on('scaleEnd', (e) => {
|
||||
this.dragStatus = StageDragStatus.END;
|
||||
const frame = this.dragResizeHelper.getFrame(e.target);
|
||||
this.emit('update', {
|
||||
data: [
|
||||
{
|
||||
el: this.target,
|
||||
style: {
|
||||
transform: frame?.get('transform'),
|
||||
if (this.target && frame) {
|
||||
this.emit('update', {
|
||||
data: [
|
||||
{
|
||||
el: this.target,
|
||||
style: {
|
||||
transform: frame.get('transform'),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
],
|
||||
parentEl: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ import { createDiv, getDocument, injectStyle } from '@tmagic/utils';
|
||||
|
||||
import { Mode, ZIndex } from './const';
|
||||
import Rule from './Rule';
|
||||
import type { RuleOptions } from './types';
|
||||
import type { MaskEvents, RuleOptions } from './types';
|
||||
import { getScrollParent, isFixedParent } from './util';
|
||||
|
||||
const wrapperClassName = 'editor-mask-wrapper';
|
||||
@ -178,6 +178,17 @@ export default class StageMask extends Rule {
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
public on<Name extends keyof MaskEvents, Param extends MaskEvents[Name]>(
|
||||
eventName: Name,
|
||||
listener: (...args: Param) => void | Promise<void>,
|
||||
) {
|
||||
return super.on(eventName, listener as any);
|
||||
}
|
||||
|
||||
public emit<Name extends keyof MaskEvents, Param extends MaskEvents[Name]>(eventName: Name, ...args: Param) {
|
||||
return super.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听选中元素是否在画布可视区域内,如果目标元素不在可视区域内,通过滚动使该元素出现在可视区域
|
||||
*/
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
import Moveable from 'moveable';
|
||||
|
||||
import { DRAG_EL_ID_PREFIX, Mode } from './const';
|
||||
import { DRAG_EL_ID_PREFIX, Mode, StageDragStatus } from './const';
|
||||
import DragResizeHelper from './DragResizeHelper';
|
||||
import MoveableOptionsManager from './MoveableOptionsManager';
|
||||
import {
|
||||
@ -26,7 +26,7 @@ import {
|
||||
GetRenderDocument,
|
||||
MarkContainerEnd,
|
||||
MoveableOptionsManagerConfig,
|
||||
StageDragStatus,
|
||||
MultiDrEvents,
|
||||
StageMultiDragResizeConfig,
|
||||
} from './types';
|
||||
import { getMode } from './util';
|
||||
@ -137,7 +137,7 @@ export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
});
|
||||
}
|
||||
|
||||
public canSelect(el: HTMLElement, selectedEl: HTMLElement | undefined): boolean {
|
||||
public canSelect(el: HTMLElement, selectedEl: HTMLElement | null): boolean {
|
||||
const currentTargetMode = getMode(el);
|
||||
let selectedElMode = '';
|
||||
|
||||
@ -196,6 +196,17 @@ export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
this.dragResizeHelper.destroy();
|
||||
}
|
||||
|
||||
public on<Name extends keyof MultiDrEvents, Param extends MultiDrEvents[Name]>(
|
||||
eventName: Name,
|
||||
listener: (...args: Param) => void | Promise<void>,
|
||||
) {
|
||||
return super.on(eventName, listener as any);
|
||||
}
|
||||
|
||||
public emit<Name extends keyof MultiDrEvents, Param extends MultiDrEvents[Name]>(eventName: Name, ...args: Param) {
|
||||
return super.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽完成后将更新的位置信息暴露给上层业务方,业务方可以接收事件进行保存
|
||||
* @param isResize 是否进行大小缩放
|
||||
|
@ -21,17 +21,9 @@ import { EventEmitter } from 'events';
|
||||
import { Id } from '@tmagic/schema';
|
||||
import { getHost, injectStyle, isSameDomain } from '@tmagic/utils';
|
||||
|
||||
import { DEFAULT_ZOOM } from './const';
|
||||
import { DEFAULT_ZOOM, RenderType } from './const';
|
||||
import style from './style.css?raw';
|
||||
import {
|
||||
type Point,
|
||||
type RemoveData,
|
||||
RenderType,
|
||||
type Runtime,
|
||||
type RuntimeWindow,
|
||||
type StageRenderConfig,
|
||||
type UpdateData,
|
||||
} from './types';
|
||||
import type { Point, RemoveData, RenderEvents, Runtime, RuntimeWindow, StageRenderConfig, UpdateData } from './types';
|
||||
import { addSelectedClassName, removeSelectedClassName } from './util';
|
||||
|
||||
export default class StageRender extends EventEmitter {
|
||||
@ -87,15 +79,13 @@ export default class StageRender extends EventEmitter {
|
||||
runtime?.update?.(data);
|
||||
}
|
||||
|
||||
public async select(els: HTMLElement[]): Promise<void> {
|
||||
public async select(ids: Id[]): Promise<void> {
|
||||
const runtime = await this.getRuntime();
|
||||
|
||||
for (const el of els) {
|
||||
await runtime?.select?.(el.id);
|
||||
if (runtime?.beforeSelect) {
|
||||
await runtime.beforeSelect(el);
|
||||
}
|
||||
this.flagSelectedEl(el);
|
||||
for (const id of ids) {
|
||||
await runtime?.select?.(id);
|
||||
|
||||
this.flagSelectedEl(this.getTargetElement(id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,13 +151,8 @@ export default class StageRender extends EventEmitter {
|
||||
return this.getDocument()?.elementsFromPoint(x / this.zoom, y / this.zoom) as HTMLElement[];
|
||||
}
|
||||
|
||||
public getTargetElement(idOrEl: Id | HTMLElement): HTMLElement {
|
||||
if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
|
||||
const el = this.getDocument()?.getElementById(`${idOrEl}`);
|
||||
if (!el) throw new Error(`不存在ID为${idOrEl}的元素`);
|
||||
return el;
|
||||
}
|
||||
return idOrEl;
|
||||
public getTargetElement(id: Id): HTMLElement | null {
|
||||
return this.getDocument()?.getElementById(`${id}`) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,6 +166,17 @@ export default class StageRender extends EventEmitter {
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
public on<Name extends keyof RenderEvents, Param extends RenderEvents[Name]>(
|
||||
eventName: Name,
|
||||
listener: (...args: Param) => void | Promise<void>,
|
||||
) {
|
||||
return super.on(eventName, listener as any);
|
||||
}
|
||||
|
||||
public emit<Name extends keyof RenderEvents, Param extends RenderEvents[Name]>(eventName: Name, ...args: Param) {
|
||||
return super.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
private createIframe(): HTMLIFrameElement {
|
||||
this.iframe = globalThis.document.createElement('iframe');
|
||||
// 同源,直接加载
|
||||
@ -214,11 +210,11 @@ export default class StageRender extends EventEmitter {
|
||||
* 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
|
||||
* @param el 被选中的元素
|
||||
*/
|
||||
private flagSelectedEl(el: HTMLElement): void {
|
||||
private flagSelectedEl(el: HTMLElement | null): void {
|
||||
const doc = this.getDocument();
|
||||
if (doc) {
|
||||
removeSelectedClassName(doc);
|
||||
addSelectedClassName(el, doc);
|
||||
el && addSelectedClassName(el, doc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,3 +78,34 @@ export enum AbleActionEventType {
|
||||
SELECT_PARENT = 'select-parent',
|
||||
REMOVE = 'remove',
|
||||
}
|
||||
|
||||
/** 将组件添加到容器的方式 */
|
||||
export enum ContainerHighlightType {
|
||||
/** 默认方式:组件在容器上方悬停一段时间后加入 */
|
||||
DEFAULT = 'default',
|
||||
/** 按住alt键,并在容器上方悬停一段时间后加入 */
|
||||
ALT = 'alt',
|
||||
}
|
||||
|
||||
export enum RenderType {
|
||||
IFRAME = 'iframe',
|
||||
NATIVE = 'native',
|
||||
}
|
||||
|
||||
/** 选择状态 */
|
||||
export enum SelectStatus {
|
||||
/** 单选 */
|
||||
SELECT = 'select',
|
||||
/** 多选 */
|
||||
MULTI_SELECT = 'multiSelect',
|
||||
}
|
||||
|
||||
/** 拖动状态 */
|
||||
export enum StageDragStatus {
|
||||
/** 开始拖动 */
|
||||
START = 'start',
|
||||
/** 拖动中 */
|
||||
ING = 'ing',
|
||||
/** 拖动结束 */
|
||||
END = 'end',
|
||||
}
|
||||
|
@ -17,12 +17,12 @@
|
||||
*/
|
||||
|
||||
import type { GuidesOptions } from '@scena/guides';
|
||||
import type { MoveableOptions } from 'moveable';
|
||||
import type { MoveableOptions, OnDragStart } from 'moveable';
|
||||
|
||||
import Core from '@tmagic/core';
|
||||
import type { Id, MApp, MContainer, MNode } from '@tmagic/schema';
|
||||
|
||||
import { GuidesType, ZIndex } from './const';
|
||||
import { AbleActionEventType, ContainerHighlightType, GuidesType, RenderType, ZIndex } from './const';
|
||||
import DragResizeHelper from './DragResizeHelper';
|
||||
import StageCore from './StageCore';
|
||||
|
||||
@ -36,8 +36,8 @@ export type CustomizeMoveableOptions =
|
||||
| ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions)
|
||||
| MoveableOptions
|
||||
| undefined;
|
||||
/** render提供给的接口,如果是id则转成el,如果是el则直接返回 */
|
||||
export type GetTargetElement = (idOrEl: Id | HTMLElement) => HTMLElement;
|
||||
/** render提供给的接口,id转成el */
|
||||
export type GetTargetElement = (id: Id) => HTMLElement | null;
|
||||
/** render提供的接口,通过坐标获得坐标下所有HTML元素数组 */
|
||||
export type GetElementsFromPoint = (point: Point) => HTMLElement[];
|
||||
export type GetRenderDocument = () => Document | undefined;
|
||||
@ -45,19 +45,6 @@ export type DelayedMarkContainer = (event: MouseEvent, exclude: Element[]) => No
|
||||
export type MarkContainerEnd = () => HTMLElement | null;
|
||||
export type GetRootContainer = () => HTMLDivElement | undefined;
|
||||
|
||||
/** 将组件添加到容器的方式 */
|
||||
export enum ContainerHighlightType {
|
||||
/** 默认方式:组件在容器上方悬停一段时间后加入 */
|
||||
DEFAULT = 'default',
|
||||
/** 按住alt键,并在容器上方悬停一段时间后加入 */
|
||||
ALT = 'alt',
|
||||
}
|
||||
|
||||
export enum RenderType {
|
||||
IFRAME = 'iframe',
|
||||
NATIVE = 'native',
|
||||
}
|
||||
|
||||
export type UpdateDragEl = (el: TargetElement, target: TargetElement, container: HTMLElement) => void;
|
||||
|
||||
export interface StageCoreConfig {
|
||||
@ -106,7 +93,7 @@ export interface MoveableOptionsManagerConfig {
|
||||
}
|
||||
|
||||
export interface CustomizeMoveableOptionsCallbackConfig {
|
||||
targetEl?: HTMLElement;
|
||||
targetEl: HTMLElement | null;
|
||||
targetElId?: string;
|
||||
targetEls?: HTMLElement[];
|
||||
targetElIds?: string[];
|
||||
@ -151,24 +138,6 @@ export interface DragResizeHelperConfig {
|
||||
updateDragEl?: UpdateDragEl;
|
||||
}
|
||||
|
||||
/** 选择状态 */
|
||||
export enum SelectStatus {
|
||||
/** 单选 */
|
||||
SELECT = 'select',
|
||||
/** 多选 */
|
||||
MULTI_SELECT = 'multiSelect',
|
||||
}
|
||||
|
||||
/** 拖动状态 */
|
||||
export enum StageDragStatus {
|
||||
/** 开始拖动 */
|
||||
START = 'start',
|
||||
/** 拖动中 */
|
||||
ING = 'ing',
|
||||
/** 拖动结束 */
|
||||
END = 'end',
|
||||
}
|
||||
|
||||
export type Rect = {
|
||||
width: number;
|
||||
height: number;
|
||||
@ -234,7 +203,6 @@ export interface RemoveData {
|
||||
|
||||
export interface Runtime {
|
||||
getApp?: () => Core | undefined;
|
||||
beforeSelect?: (el: HTMLElement) => Promise<boolean> | boolean;
|
||||
updateRootConfig?: (config: MApp) => void;
|
||||
updatePageId?: (id: Id) => void;
|
||||
select?: (id: Id) => Promise<HTMLElement> | HTMLElement;
|
||||
@ -271,3 +239,71 @@ export interface TargetShadowConfig {
|
||||
export interface RuleOptions {
|
||||
guidesOptions?: Partial<GuidesOptions>;
|
||||
}
|
||||
|
||||
export interface CoreEvents {
|
||||
mounted: [];
|
||||
'runtime-ready': [runtime: Runtime];
|
||||
'page-el-update': [el: HTMLElement];
|
||||
'change-guides': [data: GuidesEventData];
|
||||
select: [selectedEl: HTMLElement, event: MouseEvent];
|
||||
'multi-select': [selectedElList: HTMLElement[], event: MouseEvent];
|
||||
dblclick: [event: MouseEvent];
|
||||
update: [data: UpdateEventData];
|
||||
sort: [data: SortEventData];
|
||||
'select-parent': [];
|
||||
remove: [data: RemoveEventData];
|
||||
highlight: [highlightEl: HTMLElement];
|
||||
mousemove: [event: MouseEvent];
|
||||
mouseleave: [event: MouseEvent];
|
||||
'drag-start': [event: OnDragStart];
|
||||
}
|
||||
|
||||
export interface RenderEvents {
|
||||
onload: [];
|
||||
'page-el-update': [el: HTMLElement];
|
||||
'runtime-ready': [runtime: Runtime];
|
||||
}
|
||||
|
||||
export interface MaskEvents {
|
||||
scroll: [event: WheelEvent];
|
||||
'change-guides': [
|
||||
data: {
|
||||
type: GuidesType.HORIZONTAL;
|
||||
guides: number[];
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export interface ActionManagerEvents {
|
||||
dblclick: [event: MouseEvent];
|
||||
mousemove: [event: MouseEvent];
|
||||
mouseleave: [event: MouseEvent];
|
||||
highlight: [el: HTMLElement];
|
||||
update: [data: UpdateEventData];
|
||||
sort: [data: SortEventData];
|
||||
remove: [data: RemoveEventData];
|
||||
select: [selectedEl: HTMLElement | null, event: MouseEvent];
|
||||
'select-parent': [];
|
||||
'drag-start': [event: OnDragStart];
|
||||
'multi-update': [data: UpdateEventData];
|
||||
'change-to-select': [id: Id, event: MouseEvent];
|
||||
'before-multi-select': [selectedElList: HTMLElement[]];
|
||||
'before-select': [el: HTMLElement, event: MouseEvent];
|
||||
'multi-select': [selectedElList: HTMLElement[], event: MouseEvent];
|
||||
'get-elements-from-point': [els: HTMLElement[]];
|
||||
}
|
||||
|
||||
export interface DrEvents {
|
||||
'update-moveable': [];
|
||||
[AbleActionEventType.REMOVE]: [];
|
||||
[AbleActionEventType.SELECT_PARENT]: [];
|
||||
'drag-start': [event: OnDragStart];
|
||||
update: [data: UpdateEventData];
|
||||
sort: [data: SortEventData];
|
||||
}
|
||||
|
||||
export interface MultiDrEvents {
|
||||
'update-moveable': [];
|
||||
'change-to-select': [id: Id, event: MouseEvent];
|
||||
update: [data: UpdateEventData];
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ export const calcValueByFontsize = (doc: Document, value: number) => {
|
||||
* @param {number} deltaTop 偏移量
|
||||
* @param {Object} detail 当前选中的组件配置
|
||||
*/
|
||||
export const down = (deltaTop: number, target: TargetElement): SortEventData | void => {
|
||||
export const down = (deltaTop: number, target: TargetElement): SortEventData => {
|
||||
let swapIndex = 0;
|
||||
let addUpH = target.clientHeight;
|
||||
const brothers = Array.from(target.parentNode?.children || []).filter(
|
||||
@ -213,7 +213,7 @@ export const down = (deltaTop: number, target: TargetElement): SortEventData | v
|
||||
* @param {number} deltaTop 偏移量
|
||||
* @param {Object} detail 当前选中的组件配置
|
||||
*/
|
||||
export const up = (deltaTop: number, target: TargetElement): SortEventData | void => {
|
||||
export const up = (deltaTop: number, target: TargetElement): SortEventData => {
|
||||
const brothers = Array.from(target.parentNode?.children || []).filter(
|
||||
(node) => !node.id.startsWith(GHOST_EL_ID_PREFIX),
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user