swaf/src/assets/views/components/NavMenu.svelte

127 lines
2.3 KiB
Svelte

<script>
import {onMount} from "svelte";
import Icon from "../utils/Icon.svelte";
let open = false;
let locked = false;
function stopPropagation(e) {
e.stopPropagation();
}
function openMenu() {
if (locked) return;
open = true;
}
function closeMenu() {
if (locked) return;
open = false;
}
function lock() {
locked = true;
window.requestAnimationFrame(() => {
locked = false;
});
}
let nav;
onMount(() => {
nav.querySelectorAll('ul li > a, ul li > form > button')
.forEach(el => {
el.addEventListener('focus', () => {
openMenu();
});
el.addEventListener('blur', () => {
closeMenu();
});
});
});
</script>
<style lang="scss">
@import "../../scss/vars";
@import "../../scss/helpers";
nav {
top: 0;
left: 0;
height: 100%;
padding: 16px;
font-size: 16px;
@include medium-le {
z-index: 1;
position: fixed;
padding: 16px;
@include surface(3);
transition: transform ease-out 150ms;
&:not(.open) {
transform: translateX(-100%);
}
}
ul {
display: flex;
flex-direction: column;
gap: 8px;
align-items: stretch;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
list-style: none;
@include large-ge {
flex-direction: row;
align-items: center;
}
}
}
button {
width: #{$headerHeight - 16px};
height: #{$headerHeight - 16px};
margin: 8px;
padding: 8px;
border: 0;
justify-content: center;
align-items: center;
background: var(--surface);
color: var(--on-surface);
border-radius: $headerHeight;
:global(.icon) {
--icon-size: 28px;
margin: 0;
}
@include large-ge {
display: none;
}
}
</style>
<svelte:window on:click={closeMenu}/>
<button on:click={openMenu} on:click={stopPropagation}
on:focus={openMenu} on:blur={closeMenu}
tabindex="0" aria-label="Toggle menu">
<Icon name="menu"/>
</button>
<nav class:open on:click={openMenu} on:click={stopPropagation} on:mousedown={lock} bind:this={nav}
aria-hidden={open ? 'false' : 'true'}>
<ul>
<slot/>
</ul>
</nav>