2020-08-03 13:54:37 +03:00
|
|
|
<!--
|
|
|
|
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
|
|
|
-
|
|
|
|
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
|
|
|
-
|
|
|
|
- @license GNU AGPL version 3 or any later version
|
|
|
|
-
|
|
|
|
- This program is free software: you can redistribute it and/or modify
|
|
|
|
- it under the terms of the GNU Affero General Public License as
|
|
|
|
- published by the Free Software Foundation, either version 3 of the
|
|
|
|
- License, or (at your option) any later version.
|
|
|
|
-
|
|
|
|
- This program is distributed in the hope that it will be useful,
|
|
|
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
- GNU Affero General Public License for more details.
|
|
|
|
-
|
|
|
|
- You should have received a copy of the GNU Affero General Public License
|
|
|
|
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
-
|
|
|
|
-->
|
|
|
|
<template>
|
2021-01-18 15:43:46 +03:00
|
|
|
<div
|
|
|
|
:id="id"
|
|
|
|
v-click-outside="clickOutsideConfig"
|
|
|
|
:class="{ 'header-menu--opened': opened }"
|
|
|
|
class="header-menu">
|
2020-08-03 13:54:37 +03:00
|
|
|
<a class="header-menu__trigger"
|
|
|
|
href="#"
|
|
|
|
:aria-controls="`header-menu-${id}`"
|
|
|
|
:aria-expanded="opened"
|
|
|
|
aria-haspopup="true"
|
|
|
|
@click.prevent="toggleMenu">
|
|
|
|
<slot name="trigger" />
|
|
|
|
</a>
|
|
|
|
<div v-if="opened"
|
|
|
|
:id="`header-menu-${id}`"
|
|
|
|
class="header-menu__wrapper"
|
|
|
|
role="menu">
|
|
|
|
<div class="header-menu__carret" />
|
|
|
|
<div class="header-menu__content">
|
|
|
|
<slot />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import { directive as ClickOutside } from 'v-click-outside'
|
2020-08-05 15:42:08 +03:00
|
|
|
import excludeClickOutsideClasses from '@nextcloud/vue/dist/Mixins/excludeClickOutsideClasses'
|
2020-08-03 13:54:37 +03:00
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'HeaderMenu',
|
|
|
|
|
|
|
|
directives: {
|
|
|
|
ClickOutside,
|
|
|
|
},
|
|
|
|
|
2020-08-05 15:42:08 +03:00
|
|
|
mixins: [
|
|
|
|
excludeClickOutsideClasses,
|
|
|
|
],
|
|
|
|
|
2020-08-03 13:54:37 +03:00
|
|
|
props: {
|
|
|
|
id: {
|
|
|
|
type: String,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
open: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
opened: this.open,
|
2020-08-05 15:42:08 +03:00
|
|
|
clickOutsideConfig: {
|
|
|
|
handler: this.closeMenu,
|
|
|
|
middleware: this.clickOutsideMiddleware,
|
|
|
|
},
|
2020-08-03 13:54:37 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
watch: {
|
|
|
|
open(newVal) {
|
|
|
|
this.opened = newVal
|
|
|
|
this.$nextTick(() => {
|
|
|
|
if (this.opened) {
|
|
|
|
this.openMenu()
|
|
|
|
} else {
|
|
|
|
this.closeMenu()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
mounted() {
|
|
|
|
document.addEventListener('keydown', this.onKeyDown)
|
|
|
|
},
|
|
|
|
beforeDestroy() {
|
2020-09-11 13:18:10 +03:00
|
|
|
document.removeEventListener('keydown', this.onKeyDown)
|
2020-08-03 13:54:37 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
/**
|
|
|
|
* Toggle the current menu open state
|
|
|
|
*/
|
|
|
|
toggleMenu() {
|
|
|
|
// Toggling current state
|
|
|
|
if (!this.opened) {
|
|
|
|
this.openMenu()
|
|
|
|
} else {
|
|
|
|
this.closeMenu()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close the current menu
|
|
|
|
*/
|
|
|
|
closeMenu() {
|
|
|
|
if (!this.opened) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.opened = false
|
|
|
|
this.$emit('close')
|
|
|
|
this.$emit('update:open', false)
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open the current menu
|
|
|
|
*/
|
|
|
|
openMenu() {
|
|
|
|
if (this.opened) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.opened = true
|
|
|
|
this.$emit('open')
|
|
|
|
this.$emit('update:open', true)
|
|
|
|
},
|
|
|
|
|
|
|
|
onKeyDown(event) {
|
|
|
|
// If opened and escape pressed, close
|
|
|
|
if (event.key === 'Escape' && this.opened) {
|
|
|
|
event.preventDefault()
|
2020-09-11 13:18:10 +03:00
|
|
|
|
|
|
|
/** user cancelled the menu by pressing escape */
|
|
|
|
this.$emit('cancel')
|
|
|
|
|
|
|
|
/** we do NOT fire a close event to differentiate cancel and close */
|
|
|
|
this.opened = false
|
|
|
|
this.$emit('update:open', false)
|
2020-08-03 13:54:37 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.header-menu {
|
|
|
|
&__trigger {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
width: 50px;
|
|
|
|
height: 100%;
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
cursor: pointer;
|
|
|
|
opacity: .6;
|
|
|
|
}
|
|
|
|
|
|
|
|
&--opened &__trigger,
|
|
|
|
&__trigger:hover,
|
|
|
|
&__trigger:focus,
|
|
|
|
&__trigger:active {
|
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
&__wrapper {
|
|
|
|
position: absolute;
|
|
|
|
z-index: 2000;
|
|
|
|
top: 50px;
|
|
|
|
right: 5px;
|
|
|
|
box-sizing: border-box;
|
|
|
|
margin: 0;
|
|
|
|
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
|
|
|
background-color: var(--color-main-background);
|
|
|
|
|
|
|
|
filter: drop-shadow(0 1px 5px var(--color-box-shadow));
|
|
|
|
}
|
|
|
|
|
|
|
|
&__carret {
|
|
|
|
position: absolute;
|
|
|
|
right: 10px;
|
|
|
|
bottom: 100%;
|
|
|
|
width: 0;
|
|
|
|
height: 0;
|
|
|
|
content: ' ';
|
|
|
|
pointer-events: none;
|
|
|
|
border: 10px solid transparent;
|
|
|
|
border-bottom-color: var(--color-main-background);
|
|
|
|
}
|
|
|
|
|
|
|
|
&__content {
|
|
|
|
overflow: auto;
|
|
|
|
width: 350px;
|
|
|
|
max-width: 350px;
|
|
|
|
min-height: calc(44px * 1.5);
|
|
|
|
max-height: calc(100vh - 50px * 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
</style>
|