feat(editor,stage): 在画布中支持选择父组件

close #403
This commit is contained in:
roymondchen 2022-10-20 19:51:42 +08:00 committed by jia000
parent f6b7e8dad8
commit 11e0e04cbd
10 changed files with 149 additions and 4 deletions

View File

@ -0,0 +1,32 @@
<template>
<div v-if="nodes.length === 1" class="m-editor-breadcrumb">
<template v-for="(item, index) in path" :key="item.id">
<TMagicButton text :disabled="item.id === node?.id" @click="select(item)">{{ item.name }}</TMagicButton
><span v-if="index < path.length - 1">/</span>
</template>
</div>
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { TMagicButton } from '@tmagic/design';
import type { MApp, MNode } from '@tmagic/schema';
import type StageCore from '@tmagic/stage';
import { getNodePath } from '@tmagic/utils';
import type { Services } from '../../type';
const services = inject<Services>('services');
const editorService = services?.editorService;
const node = computed(() => editorService?.get<MNode>('node'));
const nodes = computed(() => editorService?.get<MNode[]>('nodes') || []);
const root = computed(() => editorService?.get<MApp>('root'));
const path = computed(() => getNodePath(node.value?.id || '', root.value?.items || []));
const select = async (node: MNode) => {
await editorService?.select(node);
editorService?.get<StageCore>('stage')?.select(node.id);
};
</script>

View File

@ -1,5 +1,7 @@
<template>
<div class="m-editor-workspace" tabindex="-1" ref="workspace">
<Breadcrumb></Breadcrumb>
<slot name="stage">
<MagicStage :key="page?.id"></MagicStage>
</slot>
@ -22,6 +24,7 @@ import { isPage } from '@tmagic/utils';
import type { Services } from '../../type';
import Breadcrumb from './Breadcrumb.vue';
import PageBar from './PageBar.vue';
import MagicStage from './Stage.vue';

View File

@ -0,0 +1,6 @@
.m-editor-breadcrumb {
position: absolute;
left: 5px;
top: 5px;
z-index: 10;
}

View File

@ -13,3 +13,4 @@
@import "./icon.scss";
@import "./code-block.scss";
@import "./layout.scss";
@import "./breadcrumb.scss";

View File

@ -73,6 +73,12 @@ export const useStage = (stageOptions: StageOptions) => {
editorService.sort(ev.src, ev.dist);
});
stage.on('select-parent', () => {
const parent = editorService.get('parent');
editorService.select(parent);
editorService.get<StageCore>('stage').select(parent.id);
});
stage.on('changeGuides', (e) => {
uiService.set('showGuides', true);

View File

@ -0,0 +1,78 @@
import { MoveableManagerInterface, Renderer } from 'moveable';
import type StageDragResize from './StageDragResize';
export default (dr: StageDragResize) => ({
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: () => {
dr.emit('select-parent');
},
},
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',
}),
),
),
);
},
});

View File

@ -144,6 +144,9 @@ export default class StageCore extends EventEmitter {
})
.on('sort', (data: UpdateEventData) => {
setTimeout(() => this.emit('sort', data));
})
.on('select-parent', () => {
this.emit('select-parent');
});
this.multiDr

View File

@ -27,6 +27,7 @@ import MoveableHelper from 'moveable-helper';
import { removeClassNameByClassName } from '@tmagic/utils';
import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, Mode, ZIndex } from './const';
import selectParentAbles from './MoveableSelectParentAble';
import StageCore from './StageCore';
import StageMask from './StageMask';
import type { StageDragResizeConfig } from './types';
@ -593,6 +594,13 @@ export default class StageDragResize extends EventEmitter {
bottom: isSortable ? undefined : this.container.clientHeight,
...(moveableOptions.bounds || {}),
},
props: {
selectParent: true,
},
ables: [selectParentAbles(this)],
...options,
...moveableOptions,
};

View File

@ -25,7 +25,7 @@ import { Mode, MouseButton, ZIndex } from './const';
import Rule from './Rule';
import type StageCore from './StageCore';
import type { StageMaskConfig } from './types';
import { getScrollParent, isFixedParent } from './util';
import { getScrollParent, isFixedParent, isMoveableButton } from './util';
const wrapperClassName = 'editor-mask-wrapper';
const throttleTime = 100;
@ -315,13 +315,18 @@ export default class StageMask extends Rule {
event.stopPropagation();
if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT) return;
if (!event.target) return;
const targetClassList = (event.target as HTMLDivElement).classList;
console.log(targetClassList);
// 如果单击多选选中区域,则不需要再触发选中了,而可能是拖动行为
if (!this.isMultiSelectStatus && (event.target as HTMLDivElement).className.indexOf('moveable-area') !== -1) {
if (!this.isMultiSelectStatus && targetClassList.contains('moveable-area')) {
return;
}
// 点击对象如果是边框锚点则可能是resize
if ((event.target as HTMLDivElement).className.indexOf('moveable-control') !== -1) {
// 点击对象如果是边框锚点则可能是resize; 点击对象是功能按钮
if (targetClassList.contains('moveable-control') || isMoveableButton(event.target as Element)) {
return;
}

View File

@ -229,3 +229,6 @@ export const up = (deltaTop: number, target: HTMLElement | SVGElement): SortEven
dist: upEls.length && swapIndex > -1 ? upEls[swapIndex].id : target.id,
};
};
export const isMoveableButton = (target: Element) =>
target.classList.contains('moveable-button') || target.parentElement?.classList.contains('moveable-button');