|
|
@@ -1,238 +0,0 @@ |
|
|
|
<!-- |
|
|
|
- @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> |
|
|
|
<div :id="id" |
|
|
|
v-click-outside="clickOutsideConfig" |
|
|
|
:class="{ 'header-menu--opened': opened }" |
|
|
|
class="header-menu"> |
|
|
|
<a class="header-menu__trigger" |
|
|
|
href="#" |
|
|
|
:aria-label="ariaLabel" |
|
|
|
:aria-controls="`header-menu-${id}`" |
|
|
|
:aria-expanded="opened.toString()" |
|
|
|
@click.prevent="toggleMenu"> |
|
|
|
<slot name="trigger" /> |
|
|
|
</a> |
|
|
|
<div v-show="opened" class="header-menu__carret" /> |
|
|
|
<div v-show="opened" |
|
|
|
:id="`header-menu-${id}`" |
|
|
|
class="header-menu__wrapper" |
|
|
|
role="menu" |
|
|
|
@focusout="handleFocusOut"> |
|
|
|
<div class="header-menu__content"> |
|
|
|
<slot /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { directive as ClickOutside } from 'v-click-outside' |
|
|
|
import excludeClickOutsideClasses from '@nextcloud/vue/dist/Mixins/excludeClickOutsideClasses' |
|
|
|
|
|
|
|
export default { |
|
|
|
name: 'HeaderMenu', |
|
|
|
|
|
|
|
directives: { |
|
|
|
ClickOutside, |
|
|
|
}, |
|
|
|
|
|
|
|
mixins: [ |
|
|
|
excludeClickOutsideClasses, |
|
|
|
], |
|
|
|
|
|
|
|
props: { |
|
|
|
id: { |
|
|
|
type: String, |
|
|
|
required: true, |
|
|
|
}, |
|
|
|
ariaLabel: { |
|
|
|
type: String, |
|
|
|
default: '', |
|
|
|
}, |
|
|
|
open: { |
|
|
|
type: Boolean, |
|
|
|
default: false, |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
data() { |
|
|
|
return { |
|
|
|
opened: this.open, |
|
|
|
clickOutsideConfig: { |
|
|
|
handler: this.closeMenu, |
|
|
|
middleware: this.clickOutsideMiddleware, |
|
|
|
}, |
|
|
|
shortcutsDisabled: OCP.Accessibility.disableKeyboardShortcuts(), |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
watch: { |
|
|
|
open(newVal) { |
|
|
|
this.opened = newVal |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.opened) { |
|
|
|
this.openMenu() |
|
|
|
} else { |
|
|
|
this.closeMenu() |
|
|
|
} |
|
|
|
}) |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
mounted() { |
|
|
|
document.addEventListener('keydown', this.onKeyDown) |
|
|
|
}, |
|
|
|
beforeDestroy() { |
|
|
|
document.removeEventListener('keydown', this.onKeyDown) |
|
|
|
}, |
|
|
|
|
|
|
|
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 (this.shortcutsDisabled) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// If opened and escape pressed, close |
|
|
|
if (event.key === 'Escape' && this.opened) { |
|
|
|
event.preventDefault() |
|
|
|
|
|
|
|
/** 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) |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
handleFocusOut(event) { |
|
|
|
if (!event.currentTarget.contains(event.relatedTarget)) { |
|
|
|
this.closeMenu() |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<style lang="scss" scoped> |
|
|
|
$externalMargin: 8px; |
|
|
|
|
|
|
|
.header-menu { |
|
|
|
&__trigger { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
width: 50px; |
|
|
|
height: 44px; |
|
|
|
margin: 2px 0; |
|
|
|
padding: 0; |
|
|
|
cursor: pointer; |
|
|
|
opacity: .85; |
|
|
|
} |
|
|
|
|
|
|
|
&--opened &__trigger, |
|
|
|
&__trigger:hover, |
|
|
|
&__trigger:focus, |
|
|
|
&__trigger:active { |
|
|
|
opacity: 1; |
|
|
|
} |
|
|
|
|
|
|
|
&__trigger:focus-visible { |
|
|
|
outline: none; |
|
|
|
} |
|
|
|
|
|
|
|
&__wrapper { |
|
|
|
position: fixed; |
|
|
|
z-index: 2000; |
|
|
|
top: 50px; |
|
|
|
right: 0; |
|
|
|
box-sizing: border-box; |
|
|
|
margin: 0 $externalMargin; |
|
|
|
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)); |
|
|
|
padding: 8px; |
|
|
|
border-radius: var(--border-radius-large); |
|
|
|
} |
|
|
|
|
|
|
|
&__carret { |
|
|
|
position: absolute; |
|
|
|
z-index: 2001; // Because __wrapper is 2000. |
|
|
|
left: calc(50% - 10px); |
|
|
|
bottom: 0; |
|
|
|
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: calc(100vw - 2 * $externalMargin); |
|
|
|
min-height: calc(44px * 1.5); |
|
|
|
max-height: calc(100vh - 50px * 2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
</style> |