| <template> | |
| <div | |
| class="mask" | |
| @contextmenu.prevent="removeContextmenu()" | |
| @mousedown.left="removeContextmenu()" | |
| ></div> | |
| <div | |
| class="contextmenu" | |
| :style="{ | |
| left: style.left + 'px', | |
| top: style.top + 'px', | |
| }" | |
| @contextmenu.prevent | |
| > | |
| <MenuContent | |
| :menus="menus" | |
| :handleClickMenuItem="handleClickMenuItem" | |
| /> | |
| </div> | |
| </template> | |
| <script lang="ts" setup> | |
| import { computed } from 'vue' | |
| import type { ContextmenuItem, Axis } from './types' | |
| import MenuContent from './MenuContent.vue' | |
| const props = defineProps<{ | |
| axis: Axis | |
| el: HTMLElement | |
| menus: ContextmenuItem[] | |
| removeContextmenu: () => void | |
| }>() | |
| const style = computed(() => { | |
| const MENU_WIDTH = 180 | |
| const MENU_HEIGHT = 30 | |
| const DIVIDER_HEIGHT = 11 | |
| const PADDING = 5 | |
| const { x, y } = props.axis | |
| const menuCount = props.menus.filter(menu => !(menu.divider || menu.hide)).length | |
| const dividerCount = props.menus.filter(menu => menu.divider).length | |
| const menuWidth = MENU_WIDTH | |
| const menuHeight = menuCount * MENU_HEIGHT + dividerCount * DIVIDER_HEIGHT + PADDING * 2 | |
| const screenWidth = document.body.clientWidth | |
| const screenHeight = document.body.clientHeight | |
| return { | |
| left: screenWidth <= x + menuWidth ? x - menuWidth : x, | |
| top: screenHeight <= y + menuHeight ? y - menuHeight : y, | |
| } | |
| }) | |
| const handleClickMenuItem = (item: ContextmenuItem) => { | |
| if (item.disable) return | |
| if (item.children && !item.handler) return | |
| if (item.handler) item.handler(props.el) | |
| props.removeContextmenu() | |
| } | |
| </script> | |
| <style lang="scss"> | |
| .mask { | |
| position: fixed; | |
| left: 0; | |
| top: 0; | |
| width: 100vw; | |
| height: 100vh; | |
| z-index: 9998; | |
| } | |
| .contextmenu { | |
| position: fixed; | |
| z-index: 9999; | |
| user-select: none; | |
| } | |
| </style> |