<template> <div class="context-menu context-menu-container" :class="openPosition" v-if="visible" :style="contextMenuPosition" v-click-outside="close" @click="close" @mouseenter="visble=true" @mouseleave="visible=false" ref="contextMenu" > <ul> <context-menu-item :field="field" v-for="(menuItem, index) in menuItems" :item="menuItem" :key="index"> </context-menu-item> </ul> </div> </template> <script> /* eslint-disable prefer-destructuring */ import ContextMenuItem from './ContextMenuItem.vue'; /** * A simple context menu component * * ```html * <ContextMenu :menu-items="[....]"/> * ``` */ export default { props: { menuItems: { type: Array, required: true, }, field:{ type:String, default:'title' } }, provide(){ let me=this; return { getActiveKey(){ return me.activeKey; }, setActiveKey(key){ me.activeKey = key; } } }, data() { return { visible: false, activeKey:'', contextMenuPosition: { top: 0, left: 0, }, openPosition: 'context-menu-open-right', }; }, methods: { close() { this.visible = false; }, /** * Accepts an Object with an `x, y` position or an instance of Event */ open(position) { this.visible = true; this.$nextTick(() => { let x = 0; let y = 0; if (typeof position !== 'undefined' && typeof position === 'object') { if (position instanceof Event) { const windowWidth = window.innerWidth; // const contextMenuWidth = this.$refs.contextMenu.getBoundingClientRect().width; //ie getBoundingClientRect null const contextMenuWidth = this.$refs.contextMenu.offsetWidth; let pageX = position.pageX; let pageY = position.pageY; // let elRect = position.currentTarget.getBoundingClientRect(); //ie getBoundingClientRect null let elRect = (position.currentTarget||position.target).getBoundingClientRect(); if( position.type != "contextmenu"){ pageX = elRect.left; pageY = elRect.top+elRect.height; if(pageX>=(windowWidth - contextMenuWidth*2)){ this.openPosition = 'context-menu-open-left'; x = windowWidth<(pageX+contextMenuWidth)?(windowWidth-contextMenuWidth):pageX; }else{ this.openPosition = 'context-menu-open-right'; x = pageX; } }else{ if (pageX >= (windowWidth - contextMenuWidth)) { this.openPosition = 'context-menu-open-left'; x = windowWidth - contextMenuWidth - 10; } else { this.openPosition = 'context-menu-open-right'; x = pageX; } } y = pageY; } else { x = position.x; y = position.y; } } this.contextMenuPosition = { left: `${x}px`, top: `${y}px`, }; }); }, }, components: { 'context-menu-item': ContextMenuItem, }, directives: { 'click-outside': { inserted: function (el) { document.body.appendChild(el) }, bind(el, binding, vnode) { el.clickOutsideEvent = (event) => { // here I check that click was outside the el and his childrens if (!(el === event.target || el.contains(event.target))) { // and if it did, call method provided in attribute value vnode.context[binding.expression](event); } }; document.body.addEventListener('click', el.clickOutsideEvent); }, unbind(el) { document.body.removeEventListener('click', el.clickOutsideEvent); }, } }, }; </script> <style lang="less"> @context-menu-border-radius: 0px; .context-menu-container { position: absolute; user-select: none; z-index: 100; } .context-menu { background: #fff; border-radius: @context-menu-border-radius; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); ul { margin: 0; padding: 0; list-style: none; :first-child { border-top-right-radius: @context-menu-border-radius; border-top-left-radius: @context-menu-border-radius; } :last-child { border-bottom-right-radius: @context-menu-border-radius; border-bottom-left-radius: @context-menu-border-radius; } li { position: relative; padding: 5px; display: block; line-height: 25px; cursor: pointer; .label { // display: flex; white-space: nowrap; .iconfont{ margin-right: 5px; } .item-label { margin-right: 10px; } .children-indicator{ float: right; } } &.item-disabled { cursor: not-allowed; opacity: 0.3; } ul { display: none; } &.item-active{ background: #0066cc; >.label{ color:#fff; } } &:hover { background: #0066cc; >.label{ color:#fff; } > ul { position: absolute; // left: calc(100% + 2px); left:100%; top: 0; display: inline-block; min-width: 100px; } } } } &.context-menu-open-left { ul { li { &:hover { > ul { left: auto; // right: calc(100% + 2px); right: 100%; } } } } } } </style>