I am currently working on a "context menu" component. The computed properties top
and left
are used to determine the menu position based on the $event
property. However, I encountered an issue where the menu element is not rendered yet when trying to open the context menu, making it impossible to calculate the top
value without knowing the menu's offsetHeight
. To address this, I came up with a solution using a "nextTick
hack" inside the computed property:
top() {
if (!this.menuIsRendered) { // by default, menuIsRendered is false
this.$nextTick(() => {
this.menuIsRendered = true
})
return 0
}
... // After menu element is rendered, calculate the top value
},
Do you think this approach is acceptable? I believe there might be a better way to handle this situation.
Here is the complete code for the component:
<template>
<div ref="menu" :style="{top: `${top}px`, left: `${left}px`}"
v-click-outside="close" @contextmenu.prevent v-if="show">
<slot></slot>
</div>
</template>
<script>
export default {
props: [
'show',
'event'
],
data() {
return {
menuIsRendered: null,
}
},
computed: {
top() {
if (!this.menuIsRendered) {
this.$nextTick(() => {
this.menuIsRendered = true
})
return 0
}
let top = this.event.y
let largestHeight = window.innerHeight - this.$refs.menu.offsetHeight - 25
return top > largestHeight ? largestHeight : top + 1
},
left() {
if (!this.menuIsRendered) {
return 0
}
let left = this.event.x
let largestWidth = window.innerWidth - this.$refs.menu.offsetWidth - 25
return left > largestWidth ? largestWidth : left + 1
},
},
methods: {
close() {
this.$emit('close')
},
}
}
</script>
How to use the component:
<context-menu @close="close" :event="event" :show="show">
<div @click="doAction">Perform Action</div>
<div @click="doAnotherAction">Execute Another Action</div>
</context-menu>