mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(stage,editor): 增肌删除快捷按钮
This commit is contained in:
parent
b4bee9eb82
commit
c9bacb96cd
@ -1,6 +1,6 @@
|
|||||||
import { computed } from 'vue';
|
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 editorService from '../services/editor';
|
||||||
import uiService from '../services/ui';
|
import uiService from '../services/ui';
|
||||||
@ -73,6 +73,14 @@ export const useStage = (stageOptions: StageOptions) => {
|
|||||||
editorService.sort(ev.src, ev.dist);
|
editorService.sort(ev.src, ev.dist);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
stage.on('remove', (ev: RemoveEventData) => {
|
||||||
|
editorService.remove(
|
||||||
|
ev.data.map(({ el }) => ({
|
||||||
|
id: el.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
stage.on('select-parent', () => {
|
stage.on('select-parent', () => {
|
||||||
const parent = editorService.get('parent');
|
const parent = editorService.get('parent');
|
||||||
if (!parent) throw new Error('父节点为空');
|
if (!parent) throw new Error('父节点为空');
|
||||||
|
@ -39,6 +39,7 @@ import {
|
|||||||
GetTargetElement,
|
GetTargetElement,
|
||||||
IsContainer,
|
IsContainer,
|
||||||
Point,
|
Point,
|
||||||
|
RemoveEventData,
|
||||||
SelectStatus,
|
SelectStatus,
|
||||||
StageDragStatus,
|
StageDragStatus,
|
||||||
UpdateEventData,
|
UpdateEventData,
|
||||||
@ -454,6 +455,14 @@ export default class ActionManager extends EventEmitter {
|
|||||||
})
|
})
|
||||||
.on('select-parent', () => {
|
.on('select-parent', () => {
|
||||||
this.emit('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
|
this.multiDr
|
||||||
|
109
packages/stage/src/MoveableActionsAble.ts
Normal file
109
packages/stage/src/MoveableActionsAble.ts
Normal file
@ -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<any, any>, 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);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@ -22,8 +22,8 @@ import { merge } from 'lodash-es';
|
|||||||
import { MoveableOptions } from 'moveable';
|
import { MoveableOptions } from 'moveable';
|
||||||
|
|
||||||
import { GuidesType, Mode } from './const';
|
import { GuidesType, Mode } from './const';
|
||||||
import selectParentAbles from './MoveableSelectParentAble';
|
import MoveableActionsAble from './MoveableActionsAble';
|
||||||
import { GetRootContainer, MoveableOptionsManagerConfig } from './types';
|
import { AbleActionEventType, GetRootContainer, MoveableOptionsManagerConfig } from './types';
|
||||||
import { getOffset } from './util';
|
import { getOffset } from './util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,10 +173,10 @@ export default class MoveableOptionsManager extends EventEmitter {
|
|||||||
isDisplayInnerSnapDigit: true,
|
isDisplayInnerSnapDigit: true,
|
||||||
|
|
||||||
props: {
|
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的回调函数,用于触发选中父元素事件
|
* 这是给selectParentAbles的回调函数,用于触发选中父元素事件
|
||||||
*/
|
*/
|
||||||
private selectParentHandler(): void {
|
private actionHandler(type: AbleActionEventType): void {
|
||||||
this.emit('select-parent');
|
this.emit(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
import { MoveableManagerInterface, Renderer } from 'moveable';
|
|
||||||
|
|
||||||
export default (selectParentHandler: () => void) => ({
|
|
||||||
name: 'selectParent',
|
|
||||||
props: {},
|
|
||||||
events: {},
|
|
||||||
render(moveable: MoveableManagerInterface<any, any>, 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',
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
@ -30,6 +30,7 @@ import {
|
|||||||
GuidesEventData,
|
GuidesEventData,
|
||||||
Point,
|
Point,
|
||||||
RemoveData,
|
RemoveData,
|
||||||
|
RemoveEventData,
|
||||||
Runtime,
|
Runtime,
|
||||||
StageCoreConfig,
|
StageCoreConfig,
|
||||||
UpdateData,
|
UpdateData,
|
||||||
@ -313,6 +314,9 @@ export default class StageCore extends EventEmitter {
|
|||||||
})
|
})
|
||||||
.on('select-parent', () => {
|
.on('select-parent', () => {
|
||||||
this.emit('select-parent');
|
this.emit('select-parent');
|
||||||
|
})
|
||||||
|
.on('remove', (data: RemoveEventData) => {
|
||||||
|
this.emit('remove', data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,10 @@ export default class StageDragResize extends MoveableOptionsManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将选中框渲染并覆盖到选中的组件Dom节点上方
|
* 将选中框渲染并覆盖到选中的组件Dom节点上方
|
||||||
* 当选中的节点不是absolute时,会创建一个新的节点出来作为拖拽目标
|
* 当选中的节点不是absolute时,会创建一个新的节点出来作为拖拽目标
|
||||||
|
@ -190,6 +190,12 @@ export interface UpdateEventData {
|
|||||||
parentEl: HTMLElement | null;
|
parentEl: HTMLElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RemoveEventData {
|
||||||
|
data: {
|
||||||
|
el: HTMLElement;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface SortEventData {
|
export interface SortEventData {
|
||||||
src: Id;
|
src: Id;
|
||||||
dist: Id;
|
dist: Id;
|
||||||
@ -244,3 +250,8 @@ export interface TargetShadowConfig {
|
|||||||
updateDragEl?: UpdateDragEl;
|
updateDragEl?: UpdateDragEl;
|
||||||
idPrefix?: string;
|
idPrefix?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AbleActionEventType {
|
||||||
|
SELECT_PARENT = 'select-parent',
|
||||||
|
REMOVE = 'remove',
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user