I use a component menu and this is displayed for each item but it all stays open if I don't click on close out I would like it to close when I click on another item with a menu.
to open and close I use a toggle on click the component has several styles
i on vuejs 3
I have the impression that each menu is independent so I don't know what to do
<nav role="navigation" :id="uuid">
<div class="app-menu">
<div class="menu-princ" #click.stop="showModal = !showModal">
<span
#click.stop="showModal = !showModal"
v-if="ellipsis"
:class="{ ellipsis: ellipsis, cells: cells }"
></span>
<span #click.stop="showModal = !showModal" v-if="ellipsis === false"
><b></b
></span>
</div>
<div
id="menu-princ"
class="menu-modal"
:class="[{ open: showModal }, { menuEllipsisPosition: ellipsis }]"
v-if="showModal"
>
<div class="wrap">
<header class="menuHeader" :class="{ ellipsisMenu: ellipsis }">
<h6
:class="{ ellipsisTitle: ellipsis, ellipsisTitleAfter: ellipsis }"
>
{{ titleMenu }}
</h6>
<u :class="{ ellipsisSubTitle: ellipsis }">{{ subTitleMenu }}</u>
<button
class="detailBtn"
:class="{ ellipsisDetailBtn: ellipsis }"
#click.stop="showModal = !showModal"
></button>
</header>
<section>
<ul v-if="disableMenuLi">
<li
#click.stop="showModal = !showModal"
v-for="([key, value], j) in Object.entries(items)"
:key="`item${j}`"
:data-label="value.label"
class="menuLiContent"
:class="{
ellipsisBorderBottom: ellipsis,
ellipsisMenuLiContent: ellipsis,
ellipsisliHover: ellipsis,
disable: disable
}"
>
<slot #click.stop="showModal = !showModal" :item="value">{{
item[key]
}}</slot>
</li>
</ul>
</section>
</div>
</div>
</div>
</nav>
</template>
<script>
import { uuid } from 'vue-uuid';
export default {
name: 'Menu',
data() {
return {
showModal: false,
uuid: uuid.v4()
};
}
};
</script>
<style lang="scss" src="./style.scss" scoped></style>
Related
I've a header components who's rendered in the layout default.vue and when the menu is open, it takes the entire page and hide the content. When i click on the link i want the menu to wait if the page where the user is redirect finished to load. But i dont see any "hooks" that tell if the page is loaded or not.
<template>
<header class="the-header" :class="{ 'the-header--open': isNavOpen }">
<div class="the-header__container container">
<a href="#" class="the-header__brand" #click="onToggleTheme" #mouseenter="$nuxt.$emit('cursor-enter', $event)" #mouseleave="$nuxt.$emit('cursor-leave', $event)">
<img src="~/assets/img/logo.png" alt="Logo" />
</a>
<div class="the-header__menu">
<div #click="onToggleNav" #mouseenter="$nuxt.$emit('cursor-enter', $event)" #mouseleave="$nuxt.$emit('cursor-leave', $event)" class="the-header__burger">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
<nav class="the-header__nav">
<nuxt-link
to="/"
#click.native="onRedirection"
class="the-header__link container"
>
<span
class="the-header__link-text hovered--no-clip"
#mouseenter="$nuxt.$emit('cursor-enter', $event)"
#mouseleave="$nuxt.$emit('cursor-leave', $event)"
>
Accueil
</span>
</nuxt-link>
<nuxt-link
to="/about"
#click.native="onRedirection"
class="the-header__link container"
>
<span
class="the-header__link-text hovered--no-clip"
#mouseenter="$nuxt.$emit('cursor-enter', $event)"
#mouseleave="$nuxt.$emit('cursor-leave', $event)"
>
A propos
</span>
</nuxt-link>
</nav>
</header>
</template>
<script>
export default {
data() {
return {
isNavOpen: false,
};
},
mounted() {
this.$nextTick(() => {
this.$nuxt.$loading.start()
setTimeout(() => this.$nuxt.$loading.finish(), 500)
})
},
methods: {
onToggleNav() {
this.isNavOpen = !this.isNavOpen;
},
onRedirection() {
setTimeout(() => {
this.isNavOpen = false;
}, 500);
},
onToggleTheme() {
let currentTheme = document.documentElement.getAttribute("data-theme");
let targetTheme = "dark";
if (currentTheme === "dark") {
targetTheme = "light";
}
document.documentElement.setAttribute("data-theme", targetTheme);
},
},
};
</script>
So I put a setTimeout on the loading function with a minimum of 500ms, but if the loading take more time then the effet doesnt work :/
sry for bad english (i'm french)
everyone!
I have modal window on Buefy. This modal open when call method:
onEditHandler(selectedPlan) {
const Component = Vue.extend(Plan);
this.$buefy.modal.open({
parent: this,
component: Component,
hasModalCard: true,
trapFocus: true,
scroll: 'keep',
ariaModal: true,
customClass: 'dialog',
canCancel: 'x',
props: {
isActive: true,
plan: selectedPlan,
roles: this.roles,
plans: this.data,
},
events: {
submit() {
},
close() {
},
},
});
}
Plan contains code:
<template>
<validation-observer v-slot="{ handleSubmit }" ref="contactObserver">
<div class="modal-card">
<header class="modal-card-head">
<h2 class="modal-card-title">Name</h2>
</header>
<section class="modal-card-body">
<validation-provider v-slot="{ errors }" rules="required">
<b-field label="Name"
:type="{ 'is-danger': errors.length }"
:message="errors + [ ' ' ]">
<b-input v-model="name"></b-input>
</b-field>
</validation-provider>
</section>
<footer class="modal-card-foot">
<button class="button" type="button" #click="cancel">Cancel</button>
<button class="button is-success"
#click="handleSubmit(submit)">Update</button>
</footer>
</div>
</validation-observer>
</template>
<script>
</script>
In Result open modal who has close button in right top corner (the image pinned in message)
Main question: How do I move the close btn to the modal window?
Thank's for answers
Modal dialog
You can add a close button on the modal's header like this:
...
<header class="modal-card-head">
<h2 class="modal-card-title">Name</h2>
<button type="button" class="delete" #click="handleCancel" />
</header>
...
And there is method handleCancle() for exmaple:
handleCancel: function() {
this.$refs.contactObserver.reset();
this.$parent.close();
},
I solved problem so:
onEditHandler(selectedPlan) {
const Component = Vue.extend(Plan);
this.$buefy.modal.open({
parent: this,
component: Component,
hasModalCard: true,
trapFocus: true,
scroll: 'keep',
ariaModal: true,
customClass: 'card-form-modal',
canCancel: ['outside'],
props: {
plan: selectedPlan,
roles: this.roles,
plans: this.data,
},
events: {
submit() {
},
close() {
}
},
});
this.changeModalPosition();
}
and Plan contains
<template>
<validation-observer v-slot="{ handleSubmit }" ref="contactObserver">
<div class="modal-card">
<header class="modal-card-head">
<h2 class="modal-card-title">Name</h2>
<button class="modal-close is-large" #click="$emit('close')"></button>
</header>
<section class="modal-card-body">
<validation-provider v-slot="{ errors }" rules="required">
<b-field label="Name"
:type="{ 'is-danger': errors.length }"
:message="errors + [ ' ' ]">
<b-input v-model="name"></b-input>
</b-field>
</validation-provider>
</section>
<footer class="modal-card-foot buttons is-right">
<button class="button" type="button" #click="cancel">Cancel</button>
<button class="button is-success"
#click="handleSubmit(submit)">Update</button>
</footer>
</div>
</validation-observer>
And screenshot
a comment that helped to understand
enter link description here
I am disabling document body scroll when my mobile nav is open which is working as expected. However it was not removing the overflow hidden when user would click a link to another route/page. I created a simple method to remove the overflow hidden when a link is clicked in the nav when the menu is open, which does work with one small caveat. When the user is on say page "home" and the mobile nav is OPEN, when they click the link "home" inside of the mobile nav it is closing the menu, and I understand I have done that with the method I created. Is there a way to prevent that event from firing when clicking the link of the page you are on?
<header :class="{ 'header-active': activeHamburger }">
<nav class="nav-row nav-row--primary" aria-label="Main Navigation">
<ul class="row--flex justify--space-between">
<li>
<router-link to="/" #click="removeOverflowHidden();
">
home
</router-link>
</li>
<li>
<router-link to="About" #click="removeOverflowHidden();
">
about
</router-link>
</li>
<li>
<router-link to="Work" #click="removeOverflowHidden();
">
work
</router-link>
</li>
<li>
<router-link to="Contact" #click="removeOverflowHidden();
">
contact
</router-link>
</li>
</ul>
</nav>
</header>
data() {
return {
activeHamburger: false
};
},
watch: {
activeHamburger: function() {
if (this.activeHamburger) {
document.documentElement.style.overflow = "hidden";
return;
}
document.documentElement.style.overflow = "auto";
}
},
methods:{
removeOverflowHidden() {
this.activeHamburger = false;
}
}
You can pass the route value to the method and check that it's not same as the current route before executing.
<template>
<header :class="{ 'header-active': activeHamburger }">
<nav class="nav-row nav-row--primary" aria-label="Main Navigation">
<ul class="row--flex justify--space-between">
<li>
<router-link to="/" #click="removeOverflowHidden('home');
">
home
</router-link>
</li>
<li>
<router-link to="About" #click="removeOverflowHidden('about');
">
about
</router-link>
</li>
<li>
<router-link to="Work" #click="removeOverflowHidden('work');
">
work
</router-link>
</li>
<li>
<router-link to="Contact" #click="removeOverflowHidden('contact');
">
contact
</router-link>
</li>
</ul>
</nav>
</header>
</template>
<script>
export default {
data() {
return {
activeHamburger: false
};
},
watch: {
activeHamburger: function() {
if (this.activeHamburger) {
document.documentElement.style.overflow = "hidden";
return;
}
document.documentElement.style.overflow = "auto";
}
},
methods:{
removeOverflowHidden(value) {
if (this.$route.path !== value) {
this.activeHamburger = false;
}
}
}
}
</script>
I haven't seen your routes but you can also use this.$route.name if you prefer and adjust accordingly the values you pass to the method.
I have created a sidebar component and set its state to false to hide the block until a user clicks on the hanmburger icon, then the state is shown. However I am trying to return(or close the sidebar menu) when the user clicks on the close 'X' icon. I have written a hideMenu method to return the state to false however it doesnt work, and there are no errors.
Here is what I have written
class SubMainNavbar extends React.Component{
constructor(){
super();
this.state = {isShown:false}
this.showMenu = this.showMenu.bind(this)
this.hideMenu = this.hideMenu.bind(this)
}
showMenu(){
this.setState({isShown: true})
}
hideMenu(){
this.setState({isShown: false})
}
render() {
return (
<div className="sub-flex-container">
<div className="co pt-4">
<ul className="flex-container sub-list">
<li>women</li>
<li>mens</li>
<li>health & beauty</li>
</ul>
<span onClick={this.showMenu} className="pl-3 hamburger hide-big pb-4"><Icon type="menu" />
<div className="mobile-menu-display" id="mySidebar" style={{display: this.state.isShown ? 'block' : 'none' }}>
<span onClick={this.hideMenu} className="float-right font-weight-bold hide-big close">X</span>
</div>
</span>
</div>
<div className="co">
{/* <Logo className="logo" /> */}
<img className="img-fluid logo" src={Logo} alt="losode logo"/>
</div>
<div className="co pt-4">
<span className="hide-small"><SearchInput /></span>
<div className="icon-mobile hide-big">
<ul className="flex-icon sub-flex-container">
<li><ion-icon name="search-outline"></ion-icon></li>
<li><ion-icon name="person-outline"></ion-icon></li>
<li><ion-icon name="basket-outline"></ion-icon></li>
</ul>
</div>
</div>
</div>
);
}
};
export default SubMainNavbar
You didn't close your tags properly. update your code as per below:
<div className="sub-flex-container">
<div className="co pt-4">
<ul className="flex-container sub-list">
<li>women</li>
<li>mens</li>
<li>health & beauty</li>
</ul>
<span onClick={this.showMenu} className="pl-3 hamburger hide-big pb-4"><Icon type="menu" /></span>
<div className="mobile-menu-display" id="mySidebar" style={{display: this.state.isShown ? 'block' : 'none' }}>
<span onClick={this.hideMenu} className="float-right font-weight-bold hide-big close">X</span>
</div>
</div>
<div className="co">
...
Your span, which you click to hide the menu, is inside the span, which triggers the showMenu function. You need to call the hideMenu function with the event parameter and use the stopPropagation function in your hideMenu function before setting the state. So the click of the span outside is not triggert and just the inner span will receive the click.
class SubMainNavbar extends React.Component{
constructor(){
super();
this.state = {isShown:false}
this.showMenu = this.showMenu.bind(this)
this.hideMenu = this.hideMenu.bind(this)
}
showMenu(){
this.setState({isShown: true})
}
hideMenu(event){
event.stopPropagation()
this.setState({isShown: false})
}
render() {
return (
<div className="sub-flex-container">
<div className="co pt-4">
<ul className="flex-container sub-list">
<li>women</li>
<li>mens</li>
<li>health & beauty</li>
</ul>
<span onClick={this.showMenu} className="pl-3 hamburger hide-big pb-4"><Icon type="menu" />
<div className="mobile-menu-display" id="mySidebar" style={{display: this.state.isShown ? 'block' : 'none' }}>
<span onClick={event => this.hideMenu(event)} className="float-right font-weight-bold hide-big close">X</span>
</div>
</span>
</div>
<div className="co">
{/* <Logo className="logo" /> */}
<img className="img-fluid logo" src={Logo} alt="losode logo"/>
</div>
<div className="co pt-4">
<span className="hide-small"><SearchInput /></span>
<div className="icon-mobile hide-big">
<ul className="flex-icon sub-flex-container">
<li><ion-icon name="search-outline"></ion-icon></li>
<li><ion-icon name="person-outline"></ion-icon></li>
<li><ion-icon name="basket-outline"></ion-icon></li>
</ul>
</div>
</div>
</div>
);
}
};
export default SubMainNavbar
ps: You can use arrow functions so you don't need to use a constructor.
For example:
this.state = { isShown: false }
showMenu = () => {
this.setState({isShown: true})
}
hideMenu = event => {
event.stopPropagation()
this.setState({isShown: false})
}
You need to ensure that the event is not passed again to the outer container.
In your case the event is triggered by the inner element which leads to a hideMenu but afterward the event is propagated to the outer container which leads to a recall of showMenu.
Additional reading about the can be found here:
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
https://www.w3schools.com/jsref/event_stoppropagation.asp
Seems like Padi Dev also found the problem before me :)
really weird but could you try imitating what I did in this code sandbox?
https://codesandbox.io/s/heuristic-fire-2jp8n?fontsize=14&hidenavigation=1&theme=dark
that should be as simple as it gets
I try to open a popover when I click on the "Delete" button.
The problem is that all the popovers of all the "Delete" buttons open at the same time.
Can you help me solve this problem?
I would like the click, only the popover concerned opens. And with the button "cancel", only the button concerned closes.
Thank you !
<template>
<div>
<div v-for="student in students.data">
<div class="col--10">
<avatar v-bind:username="student.name" :size="36" class="col--10"></avatar>
</div>
<div class="t20 col--30">{{ student.name }}</div>
<div class="t20 col--40">{{ student.email }}</div>
<div style="text-align: right" class="col--20">
<el-button icon="el-icon-edit" size="small" #click="editStudent(student)"></el-button>
<el-popover placement="top" width="160"
v-model="deleteStudentPopover">
<p>Are you sure to delete this?</p>
<div>
<el-button size="mini" type="text" #click="deleteStudentPopover = false">cancel</el-button>
<el-button type="primary" size="mini" #click="myDeleteFunction">Yes, delete!</el-button>
</div>
<el-button slot="reference" #click="deleteStudentPopover = true">Delete</el-button>
</el-popover>
</div>
</div>
</div>
</template>
export default {
data() {
return {
deleteStudentPopover: false,
students: []
}
},
}
Use <div v-for="(student, index) in students.data"> then bind/pass index when calling different functions.