I was trying to make a contextmenu component based on slots, which should be used like
<template>
<Contextmenu>
<template #contextmenu>
<!--customized contextmenu here-->
<template>
<template #default>
<!--contents here-->
<template>
<Contextmenu>
<template>
When right clicking on default slot, contextmenu slot would appear, and clicking on contextmenu would emit an event.
Inside the component it's like
<template>
<div class="contextmenu-wrapper" v-if="showContextMenu">
<slot name="contextmenu"></slot>
</div>
<div class="content-wrapper" @contextmenu.prevent.stop="onRightclickContent">
<slot name="default"></slot>
</div>
</template>
div.contextmenu-wrapper
is fixed, and onRightclickContent
will set it's top
and left
to put it in the right place. On component mounted a event listener will be registered to close the context menu after clicking outside it.
Things work fine but when I try this:
<template>
<div class="wrapper">
<ContextMenu>
<template #contextmenu>
<div class="contextmenu"></div>
</template>
<div class="inner"></div>
<div class="inner"></div>
<div class="inner inner3"></div>
</ContextMenu>
</div>
</template>
<style>
.wrapper {
display: flex;
justify-content: space-evenly;
}
.inner {
width: 250px;
height: 100px;
border: 1px solid #ccc;
background-color: bisque;
}
.inner3 {
flex-grow: 1;
}
.content {
max-width: 100px;
background-color: #3498db;
}
.contextmenu {
width: 100px;
height: 100px;
background-color: azure;
}
</style>
The problem is the div.content-wrapper
blocks the flex layout. But without it how can I listen to the right click event on content?
A solution will be like this: wrap the whole component with a div, so classes will fall through to it, then layout it in the parent component:
// ContextMenu.vue
<template>
<div class="wrapper" @contextmenu.prevent.stop="onRightclickContent">
<div class="contextmenu-wrapper" v-if="showContextMenu">
<slot name="contextmenu"></slot>
</div>
<slot name="default"></slot>
</div>
</template>
// parent component
<template>
<div class="wrapper">
<ContextMenu v-for="i in 3" class="inner">
<template #contextmenu>
<div class="contextmenu"></div>
</template>
<div class="content"></div>
</ContextMenu>
</div>
</template>
But in this way there will be three same context menus, and it will be messy with complex content and bringing troubles to users. Please help me out of a way.
Problem solved.
The problem is, I shouldn't try to affect slot contents in the parent component. So instead of binding event on slot#content while keep its layout, I let the component itself become the flex container:
class="wrapper"
will fall through to root div in ContextMenu then it becomes the flex container.