/
ctx-menu.js
108 lines (97 loc) · 2.97 KB
/
ctx-menu.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import createBodyClickListener from './body-click-listener'
// const EVENT_LIST = ['click', 'contextmenu', 'keydown']
export default {
name: 'context-menu',
props: {
id: {
type: String,
default: 'default-ctx'
}
},
data() {
return {
locals: {},
align: 'left',
ctxTop: 0,
ctxLeft: 0,
ctxVisible: false,
bodyClickListener: createBodyClickListener(
(e) => {
let isOpen = !!this.ctxVisible
let outsideClick = isOpen && !this.$el.contains(e.target)
if (outsideClick) {
if (e.which !== 1) {
e.preventDefault()
e.stopPropagation()
return false;
} else {
this.ctxVisible = false
this.$emit('ctx-cancel', this.locals)
e.stopPropagation()
}
} else {
this.ctxVisible = false
this.$emit('ctx-close', this.locals)
}
}
)
}
},
methods: {
/*
* this function handles some cross-browser compat issues
* thanks to https://github.com/callmenick/Custom-Context-Menu
*/
setPositionFromEvent(e) {
e = e || window.event
const scrollingElement = document.scrollingElement || document.documentElement
if (e.pageX || e.pageY) {
this.ctxLeft = e.pageX
this.ctxTop = e.pageY - scrollingElement.scrollTop
} else if (e.clientX || e.clientY) {
this.ctxLeft = e.clientX + scrollingElement.scrollLeft
this.ctxTop = e.clientY + scrollingElement.scrollTop
}
this.$nextTick(() => {
const menu = this.$el
const minHeight = (menu.style.minHeight || menu.style.height).replace('px', '') || 32
const minWidth = (menu.style.minWidth || menu.style.width).replace('px', '') || 32
const scrollHeight = menu.scrollHeight || minHeight
const scrollWidth = menu.scrollWidth || minWidth
const largestHeight = window.innerHeight - scrollHeight - 25;
const largestWidth = window.innerWidth - scrollWidth - 25;
if (this.ctxTop > largestHeight) this.ctxTop = largestHeight;
if (this.ctxLeft > largestWidth) this.ctxLeft = largestWidth;
})
return e
},
open(e, data) {
if (this.ctxVisible) this.ctxVisible = false
this.ctxVisible = true
this.$emit('ctx-open', this.locals = data || {})
this.setPositionFromEvent(e)
this.$el.setAttribute('tab-index', -1)
this.bodyClickListener.start()
return this
}
},
watch: {
ctxVisible(newVal, oldVal) {
if (oldVal === true && newVal === false) {
this.bodyClickListener.stop((e) => {
// console.log('context menu sequence finished', e)
// this.locals = {}
})
}
}
},
computed: {
ctxStyle() {
return {
'display': this.ctxVisible ? 'block' : 'none',
'top': (this.ctxTop || 0) + 'px',
'left': (this.ctxLeft || 0) + 'px'
}
}
}
}