Skip to content

Commit

Permalink
refactor(editor): 重构右键菜单
Browse files Browse the repository at this point in the history
  • Loading branch information
roymondchen authored and jia000 committed May 5, 2022
1 parent 34eb57b commit 56375b0
Show file tree
Hide file tree
Showing 8 changed files with 425 additions and 290 deletions.
107 changes: 107 additions & 0 deletions packages/editor/src/components/ContentMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<template>
<div v-if="menuData.length" v-show="visible" class="magic-editor-content-menu" ref="menu" :style="menuStyle">
<div>
<tool-button
v-for="(item, index) in menuData"
event-type="mouseup"
:data="item"
:key="index"
@mouseup="hide"
@mouseenter="showSubMenu(item)"
></tool-button>
</div>
<teleport to="body">
<content-menu class="sub-menu" ref="subMenu" :menu-data="subMenuData"></content-menu>
</teleport>
</div>
</template>

<script lang="ts">
import { defineComponent, nextTick, onMounted, PropType, ref } from 'vue';
import { MenuButton, MenuItem } from '@editor/type';
import ToolButton from './ToolButton.vue';
export default defineComponent({
components: { ToolButton },
props: {
menuData: {
type: Array as PropType<MenuItem[]>,
default: () => [],
},
},
setup() {
const menu = ref<HTMLDivElement>();
const subMenu = ref<any>();
const visible = ref(false);
const subMenuData = ref<MenuItem[]>([]);
const menuStyle = ref({
left: '0',
top: '0',
});
const hide = () => {
visible.value = false;
};
onMounted(() => {
globalThis.addEventListener(
'mousedown',
(e: MouseEvent) => {
if (!visible.value || (e.target && menu.value?.contains(e.target as HTMLElement))) {
return;
}
hide();
},
true,
);
});
return {
menu,
subMenu,
visible,
menuStyle,
subMenuData,
hide,
show(e: MouseEvent) {
visible.value = true;
nextTick(() => {
const menuHeight = menu.value?.clientHeight || 0;
let top = e.clientY;
if (menuHeight + e.clientY > document.body.clientHeight) {
top = document.body.clientHeight - menuHeight;
}
menuStyle.value = {
top: `${top}px`,
left: `${e.clientX}px`,
};
});
},
showSubMenu(item: MenuItem) {
const menuItem = item as MenuButton;
if (typeof item !== 'object' || !menuItem.items?.length) {
return;
}
subMenuData.value = menuItem.items;
if (menu.value) {
subMenu.value.show({
clientX: menu.value.offsetLeft + menu.value.clientWidth,
clientY: menu.value.offsetTop,
});
}
},
};
},
});
</script>
93 changes: 66 additions & 27 deletions packages/editor/src/components/ToolButton.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
<template>
<div v-if="display" class="menu-item">
<el-divider v-if="item.type === 'divider'" direction="vertical"></el-divider>
<div
v-if="display"
class="menu-item"
:class="item.type"
@click="clickHandler(item, $event)"
@mousedown="mousedownHandler(item, $event)"
@mouseup="mouseupHandler(item, $event)"
>
<el-divider v-if="item.type === 'divider'" :direction="item.direction || 'vertical'"></el-divider>
<div v-else-if="item.type === 'text'" class="menu-item-text">{{ item.text }}</div>

<template v-else-if="item.type === 'zoom'">
<el-button size="small" type="text"><m-icon :icon="ZoomIn" @click="zoomInHandler"></m-icon></el-button>
<tool-button
:data="{ type: 'button', icon: ZoomIn, handler: zoomInHandler, tooltip: '放大' }"
:event-type="eventType"
></tool-button>
<span class="menu-item-text" style="margin: 0 5px">{{ parseInt(`${zoom * 100}`, 10) }}%</span>
<el-button size="small" type="text"><m-icon :icon="ZoomOut" @click="zoomOutHandler"></m-icon></el-button>
<tool-button
:data="{ type: 'button', icon: ZoomOut, handler: zoomOutHandler, tooltip: '缩小' }"
:event-type="eventType"
></tool-button>
</template>

<el-tooltip
v-else-if="item.type === 'button'"
effect="dark"
placement="bottom-start"
:content="item.tooltip || item.text"
>
<el-button size="small" type="text" :disabled="disabled" @click="buttonHandler(item)"
><m-icon :icon="item.icon"></m-icon><span>{{ item.text }}</span></el-button
<template v-else-if="item.type === 'button'">
<el-tooltip v-if="item.tooltip" effect="dark" placement="bottom-start" :content="item.tooltip">
<el-button size="small" type="text" :disabled="disabled"
><m-icon v-if="item.icon" :icon="item.icon"></m-icon><span>{{ item.text }}</span></el-button
>
</el-tooltip>
<el-button v-else size="small" type="text" :disabled="disabled"
><m-icon v-if="item.icon" :icon="item.icon"></m-icon><span>{{ item.text }}</span></el-button
>
</el-tooltip>
</template>

<el-dropdown v-else-if="item.type === 'dropdown'" trigger="click" :disabled="disabled" @command="dropdownHandler">
<span class="el-dropdown-link menubar-menu-button">
Expand All @@ -38,7 +51,7 @@
</template>

<script lang="ts">
import { computed, defineComponent, inject, PropType } from 'vue';
import { computed, defineComponent, inject, markRaw, PropType } from 'vue';
import { ArrowDown, Back, Delete, Grid, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons';
import { NodeType } from '@tmagic/schema';
Expand All @@ -58,6 +71,11 @@ export default defineComponent({
display: false,
}),
},
eventType: {
type: String as PropType<'mousedown' | 'mouseup' | 'click'>,
default: 'click',
},
},
setup(props) {
Expand Down Expand Up @@ -87,52 +105,52 @@ export default defineComponent({
case 'delete':
return {
type: 'button',
icon: Delete,
icon: markRaw(Delete),
tooltip: '刪除',
disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE,
handler: () => services?.editorService.remove(services?.editorService.get('node')),
};
case 'undo':
return {
type: 'button',
icon: Back,
icon: markRaw(Back),
tooltip: '后退',
disabled: () => !services?.historyService.state.canUndo,
handler: () => services?.editorService.undo(),
};
case 'redo':
return {
type: 'button',
icon: Right,
icon: markRaw(Right),
tooltip: '前进',
disabled: () => !services?.historyService.state.canRedo,
handler: () => services?.editorService.redo(),
};
case 'zoom-in':
return {
type: 'button',
icon: ZoomIn,
icon: markRaw(ZoomIn),
tooltip: '放大',
handler: zoomInHandler,
};
case 'zoom-out':
return {
type: 'button',
icon: ZoomOut,
icon: markRaw(ZoomOut),
tooltip: '縮小',
handler: zoomOutHandler,
};
case 'rule':
return {
type: 'button',
icon: ScaleToOriginal,
icon: markRaw(ScaleToOriginal),
tooltip: showRule.value ? '隐藏标尺' : '显示标尺',
handler: () => uiService?.set('showRule', !showRule.value),
};
case 'guides':
return {
type: 'button',
icon: Grid,
icon: markRaw(Grid),
tooltip: showGuides.value ? '隐藏参考线' : '显示参考线',
handler: () => uiService?.set('showGuides', !showGuides.value),
};
Expand All @@ -153,9 +171,16 @@ export default defineComponent({
return item.value.disabled;
});
const buttonHandler = (item: MenuButton | MenuComponent, event: MouseEvent) => {
if (disabled.value) return;
if (typeof (item as MenuButton).handler === 'function' && services) {
(item as MenuButton).handler?.(services, event);
}
};
return {
ZoomIn,
ZoomOut,
ZoomIn: markRaw(ZoomIn),
ZoomOut: markRaw(ZoomOut),
item,
zoom,
Expand All @@ -178,10 +203,24 @@ export default defineComponent({
}
},
buttonHandler(item: MenuButton | MenuComponent) {
if (disabled.value) return;
if (typeof (item as MenuButton).handler === 'function') {
(item as MenuButton).handler?.(services);
clickHandler(item: MenuButton | MenuComponent, event: MouseEvent) {
if (props.eventType !== 'click') return;
if (item.type === 'button') {
buttonHandler(item, event);
}
},
mousedownHandler(item: MenuButton | MenuComponent, event: MouseEvent) {
if (props.eventType !== 'mousedown') return;
if (item.type === 'button') {
buttonHandler(item, event);
}
},
mouseupHandler(item: MenuButton | MenuComponent, event: MouseEvent) {
if (props.eventType !== 'mouseup') return;
if (item.type === 'button') {
buttonHandler(item, event);
}
},
};
Expand Down
Loading

0 comments on commit 56375b0

Please sign in to comment.