<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;
position: relative;
.iconfont{
margin-right: 5px;
}
.item-label {
margin-right: 10px;
}
.children-indicator{
margin-left:auto;
}
&.withHoverBtn{
padding-right:28px;
.hoverBtn,.chooseIcon{
height:24px;
line-height:24px;
width:24px;
position: absolute;
top:0;
bottom:0;
right:0;
margin:auto;
display: none;
&:hover{
color:#fc0;
}
i{
font-size: 20px;
line-height:24px;
font-weight: bold;
}
}
.chooseIcon,&:hover .hoverBtn{
display: block;
}
.chooseIcon{
color:#fc0;
}
}
}
&.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;
overflow: auto;
}
}
}
}
&.context-menu-open-left {
ul {
li {
&:hover {
> ul {
left: auto;
// right: calc(100% + 2px);
right: 100%;
}
}
}
}
}
}
</style>