From b3d4439ae44400073dd3471c370f040b6eb0a147 Mon Sep 17 00:00:00 2001 From: crlang Date: Fri, 1 Jul 2022 14:20:36 +0800 Subject: [PATCH] fix(ContextMenu): reoptimize components and documentation --- src/components/ContextMenu/index.ts | 2 +- .../ContextMenu/src/ContextMenu.vue | 229 +++++++++--------- .../ContextMenu/src/createContextMenu.ts | 48 ++-- src/components/ContextMenu/src/index.scss | 69 ++++++ src/components/ContextMenu/src/typing.ts | 130 +++++++++- src/hooks/web/useContextMenu.ts | 15 +- src/views/demo/tree/EditTree.vue | 4 +- 7 files changed, 338 insertions(+), 159 deletions(-) create mode 100644 src/components/ContextMenu/src/index.scss diff --git a/src/components/ContextMenu/index.ts b/src/components/ContextMenu/index.ts index ad4ae593..dfc68459 100644 --- a/src/components/ContextMenu/index.ts +++ b/src/components/ContextMenu/index.ts @@ -1,3 +1,3 @@ export { createContextMenu, destroyContextMenu } from './src/createContextMenu' -export * from './src/typing' +export type { ContextMenuItem, CreateContextMenuOptions } from './src/typing' diff --git a/src/components/ContextMenu/src/ContextMenu.vue b/src/components/ContextMenu/src/ContextMenu.vue index 4169fbd8..d49cb738 100644 --- a/src/components/ContextMenu/src/ContextMenu.vue +++ b/src/components/ContextMenu/src/ContextMenu.vue @@ -1,54 +1,87 @@ - + diff --git a/src/components/ContextMenu/src/createContextMenu.ts b/src/components/ContextMenu/src/createContextMenu.ts index 48db2bb8..f2502908 100644 --- a/src/components/ContextMenu/src/createContextMenu.ts +++ b/src/components/ContextMenu/src/createContextMenu.ts @@ -1,8 +1,9 @@ -import contextMenuVue from './ContextMenu.vue' -import { isClient } from '@/utils/is' -import { CreateContextOptions, ContextMenuProps } from './typing' +import type { CreateContextMenuOptions, ContextMenuProps } from './typing' + import { createVNode, render } from 'vue' +import contextMenuVue from './ContextMenu.vue' + const menuManager: { domList: Element[] resolve: Fn @@ -11,33 +12,35 @@ const menuManager: { resolve: () => {}, } -export const createContextMenu = function (options: CreateContextOptions) { - const { event } = options || {} +/** + * 创建右键菜单 + * + * Create a right-click menu + * @param options CreateContextMenuOptions + */ +export const createContextMenu = function (options: CreateContextMenuOptions) { + const { event = null, showIcon = true, styles = {}, items, width } = options || {} event && event?.preventDefault() - if (!isClient) { - return - } + if (typeof window === 'undefined') return + return new Promise((resolve) => { const body = document.body const container = document.createElement('div') - const propsData: Partial = {} - if (options.styles) { - propsData.styles = options.styles - } - - if (options.items) { - propsData.items = options.items - } - - if (options.event) { - propsData.customEvent = event - propsData.axis = { x: event.clientX, y: event.clientY } + const propsData: Partial = { + event, + styles, + showIcon, + items, + width, + axis: { x: event?.clientX || 0, y: event?.clientY || 0 }, } + /** create VNode */ const vm = createVNode(contextMenuVue, propsData) + /** render VNode */ render(vm, container) const handleClick = function () { @@ -51,7 +54,7 @@ export const createContextMenu = function (options: CreateContextOptions) { try { dom && body.removeChild(dom) } catch (error) { - // continue regardless of error + // continue } }) body.removeEventListener('click', handleClick) @@ -69,6 +72,9 @@ export const createContextMenu = function (options: CreateContextOptions) { }) } +/** + * 销毁右键菜单 + */ export const destroyContextMenu = function () { if (menuManager) { menuManager.resolve('') diff --git a/src/components/ContextMenu/src/index.scss b/src/components/ContextMenu/src/index.scss new file mode 100644 index 00000000..5c3a1914 --- /dev/null +++ b/src/components/ContextMenu/src/index.scss @@ -0,0 +1,69 @@ +$prefix-cls: '#{$tonyname}-context-menu'; +$default-height: 44px; + +@mixin item-style { + li { + display: inline-block; + width: 100%; + height: $default-height; + margin: 0 !important; + line-height: $default-height; + + &.is-divider { + border-bottom: 1px solid var(--border-color, '#dedede'); + } + + &.el-sub-menu { + .el-sub-menu { + &__title { + height: $default-height; + line-height: $default-height; + } + } + } + + &:not(.is-disabled):hover { + color: var(--text-primary-color); + background-color: #f1f2f3; + } + } +} + +.#{$prefix-cls} { + @include item-style(); + + position: fixed; + top: 0; + left: 0; + z-index: 200; + display: block; + width: 156px; + margin: 0; + list-style: none; + background-color: var(--background-primary-color); + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 0.25rem; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.1), 0 1px 5px 0 rgba(0, 0, 0, 0.06); + background-clip: padding-box; + user-select: none; + + &__text { + display: flex; + align-items: center; + + span { + width: auto !important; + height: auto !important; + font-size: 14px; + visibility: visible !important; + } + } + + &__popup { + @include item-style(); + + > ul { + padding: 0; + } + } +} diff --git a/src/components/ContextMenu/src/typing.ts b/src/components/ContextMenu/src/typing.ts index 38ebd084..916af280 100644 --- a/src/components/ContextMenu/src/typing.ts +++ b/src/components/ContextMenu/src/typing.ts @@ -1,35 +1,141 @@ +import type { CSSProperties } from 'vue' + +/** + * 鼠标右击的轴数据 + * + * Right-clicked axis data + */ export interface Axis { + /** + * x轴 + * + * x-axis + */ x: number + /** + * x轴 + * + * y-axis + */ y: number } - +/** + * 右击菜单项的数据 + * + * Right-click menu item data + */ export interface ContextMenuItem { + /** + * 菜单项名称 + * + * Menu item name + */ label: string + /** + * 菜单项图标 + * + * Menu item icon + */ icon?: string + /** + * 是否禁用 + * + * Whether to disable + */ disabled?: boolean + /** + * 菜单项点击触发的函数 + * + * The function triggered by the menu item click + */ handler?: Fn + /** + * 菜单项下方是否带分割线 + * + * Whether there is a dividing line below the menu item + */ divider?: boolean + /** + * 菜单项的子项 + * + * Children of menu items + */ children?: ContextMenuItem[] } -export interface CreateContextOptions { +/** + * 右击菜单项的选项 + * + * Right-click menu item options + */ +export interface CreateContextMenuOptions { + /** + * 右击的DOM的事件 + * + * Right-clicked DOM events + */ event: MouseEvent - icon?: string - styles?: any - items?: ContextMenuItem[] + /** + * 右击菜单项的数据 + * + * Right-click menu item data + */ + items: ContextMenuItem[] + /** + * 是否显示图标 + * + * Whether to show the icon + */ + showIcon?: boolean + /** + * 右击菜单的样式 + * + * Right-click menu style + */ + styles?: CSSProperties + /** + * 右击菜单的宽度 + * + * Right-click menu width + */ + width?: number } -export interface ContextMenuProps { - event?: MouseEvent - styles?: any - items: ContextMenuItem[] - customEvent?: MouseEvent +/** + * 右击菜单Props + * + * Right click menu Props + */ +export interface ContextMenuProps extends CreateContextMenuOptions { + /** + * 右击菜单的轴偏移 + * + * Axis Offset from right-click menu + */ axis?: Axis - width?: number - showIcon?: boolean } +/** + * 菜单项内容Props + * + * Menu item content Props + */ export interface ItemContentProps { + /** + * 是否显示图标 + * + * Whether to show the icon + */ showIcon: boolean | undefined + /** + * 右击菜单项的数据 + * + * Right-click menu item data + */ item: ContextMenuItem + /** + * 操作处理 + * + * Action handling + */ handler: Fn } diff --git a/src/hooks/web/useContextMenu.ts b/src/hooks/web/useContextMenu.ts index 7bf4cc05..b929fe5f 100644 --- a/src/hooks/web/useContextMenu.ts +++ b/src/hooks/web/useContextMenu.ts @@ -1,9 +1,16 @@ +export type { ContextMenuItem } from '@/components/ContextMenu' + import { onUnmounted, getCurrentInstance } from 'vue' import { createContextMenu, destroyContextMenu } from '@/components/ContextMenu' -import type { ContextMenuItem } from '@/components/ContextMenu' -export type { ContextMenuItem } -export function useContextMenu(authRemove = true) { - if (getCurrentInstance() && authRemove) { + +/** + * 处理右键菜单 + * + * Handling right-click menus + * @param autoRemove + */ +export function useContextMenu(autoRemove = true) { + if (getCurrentInstance() && autoRemove) { onUnmounted(() => { destroyContextMenu() }) diff --git a/src/views/demo/tree/EditTree.vue b/src/views/demo/tree/EditTree.vue index aa264595..954443dd 100644 --- a/src/views/demo/tree/EditTree.vue +++ b/src/views/demo/tree/EditTree.vue @@ -24,7 +24,7 @@ import { defineComponent } from 'vue' import { ElCard, ElTree, ElRow, ElCol } from 'element-plus' import { treeData } from './data' import { useContextMenu } from '@/hooks/web/useContextMenu' -import { CreateContextOptions } from '@/components/ContextMenu' +import { CreateContextMenuOptions } from '@/components/ContextMenu' import { Delete, Position } from '@element-plus/icons' export default defineComponent({ @@ -54,7 +54,7 @@ export default defineComponent({ }, ] - const contextMenuOptions: CreateContextOptions = { event, items: MenuItems } + const contextMenuOptions: CreateContextMenuOptions = { event, items: MenuItems } if (!contextMenuOptions.items?.length) return