diff --git a/packages/editor/src/services/stageOverlay.ts b/packages/editor/src/services/stageOverlay.ts index bc5c63ff..5047d7fd 100644 --- a/packages/editor/src/services/stageOverlay.ts +++ b/packages/editor/src/services/stageOverlay.ts @@ -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); } } diff --git a/packages/stage/src/ActionManager.ts b/packages/stage/src/ActionManager.ts index 2362ab4d..b17f1fbd 100644 --- a/packages/stage/src/ActionManager.ts +++ b/packages/stage/src/ActionManager.ts @@ -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 { + public async getElementFromPoint(event: MouseEvent): Promise { 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( + eventName: Name, + listener: (...args: Param) => void, + ) { + return super.on(eventName, listener as any); + } + + public emit( + 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); }; diff --git a/packages/stage/src/DragResizeHelper.ts b/packages/stage/src/DragResizeHelper.ts index 89e8581a..571e223d 100644 --- a/packages/stage/src/DragResizeHelper.ts +++ b/packages/stage/src/DragResizeHelper.ts @@ -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'; /** diff --git a/packages/stage/src/StageCore.ts b/packages/stage/src/StageCore.ts index 472ca579..77bd773b 100644 --- a/packages/stage/src/StageCore.ts +++ b/packages/stage/src/StageCore.ts @@ -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 { - const el = this.renderer.getTargetElement(idOrEl); + public async select(id: Id, event?: MouseEvent): Promise { + 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 { - const els = idOrElList.map((idOrEl) => this.renderer.getTargetElement(idOrEl)); + public async multiSelect(ids: Id[]): Promise { + 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( + eventName: Name, + listener: (...args: Param) => void | Promise, + ) { + return super.on(eventName, listener as any); + } + + public emit(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); }); } diff --git a/packages/stage/src/StageDragResize.ts b/packages/stage/src/StageDragResize.ts index ad9a0f39..9f926efe 100644 --- a/packages/stage/src/StageDragResize.ts +++ b/packages/stage/src/StageDragResize.ts @@ -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( + eventName: Name, + listener: (...args: Param) => void | Promise, + ) { + return super.on(eventName, listener as any); + } + + public emit(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, + }); + } }); } diff --git a/packages/stage/src/StageMask.ts b/packages/stage/src/StageMask.ts index 316b2132..e6c838bd 100644 --- a/packages/stage/src/StageMask.ts +++ b/packages/stage/src/StageMask.ts @@ -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( + eventName: Name, + listener: (...args: Param) => void | Promise, + ) { + return super.on(eventName, listener as any); + } + + public emit(eventName: Name, ...args: Param) { + return super.emit(eventName, ...args); + } + /** * 监听选中元素是否在画布可视区域内,如果目标元素不在可视区域内,通过滚动使该元素出现在可视区域 */ diff --git a/packages/stage/src/StageMultiDragResize.ts b/packages/stage/src/StageMultiDragResize.ts index cd460a2f..b4e1dc80 100644 --- a/packages/stage/src/StageMultiDragResize.ts +++ b/packages/stage/src/StageMultiDragResize.ts @@ -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( + eventName: Name, + listener: (...args: Param) => void | Promise, + ) { + return super.on(eventName, listener as any); + } + + public emit(eventName: Name, ...args: Param) { + return super.emit(eventName, ...args); + } + /** * 拖拽完成后将更新的位置信息暴露给上层业务方,业务方可以接收事件进行保存 * @param isResize 是否进行大小缩放 diff --git a/packages/stage/src/StageRender.ts b/packages/stage/src/StageRender.ts index bebd19a6..af0ccd0e 100644 --- a/packages/stage/src/StageRender.ts +++ b/packages/stage/src/StageRender.ts @@ -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 { + public async select(ids: Id[]): Promise { 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( + eventName: Name, + listener: (...args: Param) => void | Promise, + ) { + return super.on(eventName, listener as any); + } + + public emit(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); } } diff --git a/packages/stage/src/const.ts b/packages/stage/src/const.ts index 40a6d461..7f254644 100644 --- a/packages/stage/src/const.ts +++ b/packages/stage/src/const.ts @@ -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', +} diff --git a/packages/stage/src/types.ts b/packages/stage/src/types.ts index c51ddccb..6ab02d20 100644 --- a/packages/stage/src/types.ts +++ b/packages/stage/src/types.ts @@ -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; updateRootConfig?: (config: MApp) => void; updatePageId?: (id: Id) => void; select?: (id: Id) => Promise | HTMLElement; @@ -271,3 +239,71 @@ export interface TargetShadowConfig { export interface RuleOptions { guidesOptions?: Partial; } + +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]; +} diff --git a/packages/stage/src/util.ts b/packages/stage/src/util.ts index a53ee5c5..d41c9925 100644 --- a/packages/stage/src/util.ts +++ b/packages/stage/src/util.ts @@ -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), );