JSFiddle - React, Tailwind, and code Playground
by ChangJoo Park
HTML
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
<div class="message-wrapper">
<message-item v-for="message in messages" class="message-item" v-bind:item="message"></message-item>
</div>
<context-menu ref="contextMenu"></context-menu>
</div>
<template id="message-item">
<div>
{{item}}
</div>
</template>
<template id="context-menu">
<div class="context-menu" v-show="isOpen">
{{selectedMessage}}
<div v-for="item in items" class="context-menu-item">
{{item}}
</div>
</div>
</template>
CSS
html,
body {
box-sizing: border-box;
user-select: none;
}
.message-item {
padding: 20px;
border-bottom: 1px solid tomato;
border-left: 1px solid tomato;
border-right: 1px solid tomato;
cursor: pointer;
}
.message-item:first-child {
border-top: 1px solid tomato;
}
.context-menu {
border: 1px solid orange;
display: inline-block;
position: absolute;
width: 100px;
background-color: orange;
color: #fff;
}
.context-menu-item {
text-align: center;
padding: 3px;
border-bottom: 1px solid orange;
cursor: pointer;
}
.context-menu-item:last-child {
border-bottom: none;
}
JavaScript
var itemClassName = 'message-item';
var itemInContext;
function clickInsideElement(e, className) {
var el = e.srcElement || e.target;
if (el.classList.contains(className)) {
return el;
} else {
while (el = el.parentNode) {
if (el.classList && el.classList.contains(className)) {
return el;
}
}
}
return false;
}
function getPosition(e) {
var posx = 0;
var posy = 0;
if (!e) var e = window.event;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
} else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return {
x: posx,
y: posy
}
}
Vue.component('context-menu', {
template: '#context-menu',
data: function() {
return {
isOpen: false,
items: ['A', 'B', 'C', 'D'],
selectedMessage: ''
}
}
});
Vue.component('message-item', {
template: '#message-item',
mounted: function() {
console.log(this.$$)
this.$el.addEventListener('contextmenu', function(event) {
taskItemInContext = clickInsideElement( event, itemClassName );
var contextMenuComponent = this.$parent.$refs.contextMenu;
if ( taskItemInContext ) {
contextMenuComponent.$data.isOpen = true;
contextMenuComponent.$data.selectedMessage = this.item;
var contextMenuDOM = contextMenuComponent.$el;
var position = getPosition(event);
this.$nextTick(function() {
contextMenuDOM.style.left = position.x + 'px';
contextMenuDOM.style.top = position.y + 'px';
})
event.preventDefault();
} else {
contextMenuComponent.$data.isOpen = false;
contextMenuComponent.$data.selectedMessage = undefined;
taskItemInContext = null;
}
}.bind(this));
},
props: ['item']
});
new Vue({
el: '#app',
data: function() {
return {
...