diff --git a/packages/editor/src/utils/stage.ts b/packages/editor/src/utils/stage.ts index c30e8ac0..15f2a217 100644 --- a/packages/editor/src/utils/stage.ts +++ b/packages/editor/src/utils/stage.ts @@ -1,6 +1,6 @@ import { computed } from 'vue'; -import StageCore, { GuidesType, SortEventData, UpdateEventData } from '@tmagic/stage'; +import StageCore, { GuidesType, RemoveEventData, SortEventData, UpdateEventData } from '@tmagic/stage'; import editorService from '../services/editor'; import uiService from '../services/ui'; @@ -73,6 +73,14 @@ export const useStage = (stageOptions: StageOptions) => { editorService.sort(ev.src, ev.dist); }); + stage.on('remove', (ev: RemoveEventData) => { + editorService.remove( + ev.data.map(({ el }) => ({ + id: el.id, + })), + ); + }); + stage.on('select-parent', () => { const parent = editorService.get('parent'); if (!parent) throw new Error('父节点为空'); diff --git a/packages/stage/src/ActionManager.ts b/packages/stage/src/ActionManager.ts index f1b6e6cf..751e9f48 100644 --- a/packages/stage/src/ActionManager.ts +++ b/packages/stage/src/ActionManager.ts @@ -39,6 +39,7 @@ import { GetTargetElement, IsContainer, Point, + RemoveEventData, SelectStatus, StageDragStatus, UpdateEventData, @@ -454,6 +455,14 @@ export default class ActionManager extends EventEmitter { }) .on('select-parent', () => { this.emit('select-parent'); + }) + .on('remove', () => { + const drTarget = this.dr.getTarget(); + if (!drTarget) return; + const data: RemoveEventData = { + data: [{ el: drTarget }], + }; + this.emit('remove', data); }); this.multiDr diff --git a/packages/stage/src/MoveableActionsAble.ts b/packages/stage/src/MoveableActionsAble.ts new file mode 100644 index 00000000..45fc1dd3 --- /dev/null +++ b/packages/stage/src/MoveableActionsAble.ts @@ -0,0 +1,109 @@ +import { MoveableManagerInterface, Renderer } from 'moveable'; + +import { AbleActionEventType } from './types'; + +export default (handler: (type: AbleActionEventType) => void) => ({ + name: 'actions', + props: { + selectParent: Boolean, + }, + events: {}, + render(moveable: MoveableManagerInterface, React: Renderer) { + const rect = moveable.getRect(); + const { pos2 } = moveable.state; + + // use css for able + const editableViewer = moveable.useCSS( + 'div', + ` + { + position: absolute; + left: 0px; + top: 0px; + will-change: transform; + transform-origin: 0px 0px; + display: flex; + } + .moveable-button { + width: 20px; + height: 20px; + background: #4af; + border-radius: 4px; + appearance: none; + border: 0; + color: white; + font-size: 12px; + font-weight: bold; + margin-left: 2px; + position: relative; + } + .moveable-remove-button:before, .moveable-remove-button:after { + content: ""; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) rotate(45deg); + width: 14px; + height: 2px; + background: #fff; + border-radius: 1px; + cursor: pointer; + } + .moveable-remove-button:after { + transform: translate(-50%, -50%) rotate(-45deg); + } + `, + ); + // Add key (required) + // Add class prefix moveable-(required) + return React.createElement( + editableViewer, + { + className: 'moveable-editable', + style: { + transform: `translate(${pos2[0] - 48}px, ${pos2[1] - 28}px) rotate(${rect.rotation}deg) translate(10px)`, + }, + }, + [ + React.createElement( + 'button', + { + className: 'moveable-button', + title: '选中父组件', + onClick: () => { + handler(AbleActionEventType.SELECT_PARENT); + }, + }, + React.createElement( + 'svg', + { + width: '20px', + height: '20px', + viewBox: '0 0 16 16', + fill: 'none', + xmlns: 'http://www.w3.org/2000/svg', + style: { + transform: 'rotate(90deg)', + position: 'absolute', + left: 0, + top: 0, + }, + }, + React.createElement('path', { + d: 'M13.0001 4V10H4.20718L5.85363 8.35355L5.14652 7.64645L2.64652 10.1464C2.45126 10.3417 2.45126 10.6583 2.64652 10.8536L5.14652 13.3536L5.85363 12.6464L4.20718 11H13.0001C13.5524 11 14.0001 10.5523 14.0001 10V4H13.0001Z', + fill: 'currentColor', + fillOpacity: '0.9', + }), + ), + ), + React.createElement('button', { + className: 'moveable-button moveable-remove-button', + title: '删除', + onClick: () => { + handler(AbleActionEventType.REMOVE); + }, + }), + ], + ); + }, +}); diff --git a/packages/stage/src/MoveableOptionsManager.ts b/packages/stage/src/MoveableOptionsManager.ts index a66d1752..f718237f 100644 --- a/packages/stage/src/MoveableOptionsManager.ts +++ b/packages/stage/src/MoveableOptionsManager.ts @@ -22,8 +22,8 @@ import { merge } from 'lodash-es'; import { MoveableOptions } from 'moveable'; import { GuidesType, Mode } from './const'; -import selectParentAbles from './MoveableSelectParentAble'; -import { GetRootContainer, MoveableOptionsManagerConfig } from './types'; +import MoveableActionsAble from './MoveableActionsAble'; +import { AbleActionEventType, GetRootContainer, MoveableOptionsManagerConfig } from './types'; import { getOffset } from './util'; /** @@ -173,10 +173,10 @@ export default class MoveableOptionsManager extends EventEmitter { isDisplayInnerSnapDigit: true, props: { - selectParent: true, + actions: true, }, - ables: [selectParentAbles(this.selectParentHandler.bind(this))], + ables: [MoveableActionsAble(this.actionHandler.bind(this))], }; } @@ -208,8 +208,8 @@ export default class MoveableOptionsManager extends EventEmitter { /** * 这是给selectParentAbles的回调函数,用于触发选中父元素事件 */ - private selectParentHandler(): void { - this.emit('select-parent'); + private actionHandler(type: AbleActionEventType): void { + this.emit(type); } /** diff --git a/packages/stage/src/MoveableSelectParentAble.ts b/packages/stage/src/MoveableSelectParentAble.ts deleted file mode 100644 index 66c1b74b..00000000 --- a/packages/stage/src/MoveableSelectParentAble.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { MoveableManagerInterface, Renderer } from 'moveable'; - -export default (selectParentHandler: () => void) => ({ - name: 'selectParent', - props: {}, - events: {}, - render(moveable: MoveableManagerInterface, React: Renderer) { - const rect = moveable.getRect(); - const { pos2 } = moveable.state; - - // use css for able - const editableViewer = moveable.useCSS( - 'div', - ` - { - position: absolute; - left: 0px; - top: 0px; - will-change: transform; - transform-origin: 0px 0px; - display: flex; - } - .moveable-button { - width: 20px; - height: 20px; - background: #4af; - border-radius: 4px; - appearance: none; - border: 0; - color: white; - font-size: 12px; - font-weight: bold; - } - `, - ); - // Add key (required) - // Add class prefix moveable-(required) - return React.createElement( - editableViewer, - { - className: 'moveable-editable', - style: { - transform: `translate(${pos2[0] - 25}px, ${pos2[1] - 30}px) rotate(${rect.rotation}deg) translate(10px)`, - }, - }, - React.createElement( - 'button', - { - className: 'moveable-button', - title: '选中父组件', - onClick: () => { - selectParentHandler(); - }, - }, - React.createElement( - 'svg', - { - width: '1em', - height: '1em', - viewBox: '0 0 16 16', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - style: { - transform: 'rotate(90deg)', - }, - }, - React.createElement('path', { - d: 'M13.0001 4V10H4.20718L5.85363 8.35355L5.14652 7.64645L2.64652 10.1464C2.45126 10.3417 2.45126 10.6583 2.64652 10.8536L5.14652 13.3536L5.85363 12.6464L4.20718 11H13.0001C13.5524 11 14.0001 10.5523 14.0001 10V4H13.0001Z', - fill: 'currentColor', - fillOpacity: '0.9', - }), - ), - ), - ); - }, -}); diff --git a/packages/stage/src/StageCore.ts b/packages/stage/src/StageCore.ts index 9f715d67..a24359e6 100644 --- a/packages/stage/src/StageCore.ts +++ b/packages/stage/src/StageCore.ts @@ -30,6 +30,7 @@ import { GuidesEventData, Point, RemoveData, + RemoveEventData, Runtime, StageCoreConfig, UpdateData, @@ -313,6 +314,9 @@ export default class StageCore extends EventEmitter { }) .on('select-parent', () => { this.emit('select-parent'); + }) + .on('remove', (data: RemoveEventData) => { + this.emit('remove', data); }); } diff --git a/packages/stage/src/StageDragResize.ts b/packages/stage/src/StageDragResize.ts index d8a2ab76..38ac1264 100644 --- a/packages/stage/src/StageDragResize.ts +++ b/packages/stage/src/StageDragResize.ts @@ -63,6 +63,10 @@ export default class StageDragResize extends MoveableOptionsManager { }); } + public getTarget() { + return this.target; + } + /** * 将选中框渲染并覆盖到选中的组件Dom节点上方 * 当选中的节点不是absolute时,会创建一个新的节点出来作为拖拽目标 diff --git a/packages/stage/src/types.ts b/packages/stage/src/types.ts index 565c7400..a34927e5 100644 --- a/packages/stage/src/types.ts +++ b/packages/stage/src/types.ts @@ -190,6 +190,12 @@ export interface UpdateEventData { parentEl: HTMLElement | null; } +export interface RemoveEventData { + data: { + el: HTMLElement; + }[]; +} + export interface SortEventData { src: Id; dist: Id; @@ -244,3 +250,8 @@ export interface TargetShadowConfig { updateDragEl?: UpdateDragEl; idPrefix?: string; } + +export enum AbleActionEventType { + SELECT_PARENT = 'select-parent', + REMOVE = 'remove', +}