feat: mobile friendly menu

This commit is contained in:
Corentin Thomasset 2022-04-15 12:21:09 +02:00
parent f872972e69
commit 1e67fa6e0b
No known key found for this signature in database
GPG key ID: DBD997E935996158
4 changed files with 121 additions and 35 deletions

View file

@ -0,0 +1,47 @@
<template>
<n-layout has-sider>
<n-layout-sider bordered collapse-mode="width" :collapsed-width="0" :width="240" :collapsed="isMenuCollapsed"
@collapse="isMenuCollapsed = true" @expand="isMenuCollapsed = false" :show-trigger="false"
:native-scrollbar="false" :position="siderPosition">
<slot name="sider" />
</n-layout-sider>
<n-layout class="content">
<slot name="content" />
<div class="overlay" v-show="isSmallScreen && !isMenuCollapsed" @click="isMenuCollapsed = true" />
</n-layout>
</n-layout>
</template>
<script setup lang="ts">
import { useStyleStore } from '@/stores/style.store';
import { toRefs } from 'vue';
import { computed } from 'vue';
const styleStore = useStyleStore()
const { isMenuCollapsed, isSmallScreen } = toRefs(styleStore)
const siderPosition = computed(() => isSmallScreen.value ? 'absolute' : 'static')
</script>
<style lang="less" scoped>
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #00000080;
cursor: pointer;
}
.content {
// background-color: #f1f5f9;
::v-deep(.n-layout-scroll-container) {
padding: 26px;
}
}
.n-layout {
height: 100vh;
}
</style>

View file

@ -2,15 +2,15 @@
import { NIcon } from 'naive-ui';
import { h, ref, type Component } from 'vue';
import { RouterLink, useRoute } from 'vue-router';
import { Heart, BrandGithub, BrandTwitter, Moon, Sun } from '@vicons/tabler'
import { Heart, BrandGithub, BrandTwitter, Moon, Sun, Menu2, Home2 } from '@vicons/tabler'
import { toolsByCategory } from '@/tools';
import SearchBar from '../components/SearchBar.vue';
import { useStyleStore } from '@/stores/style.store';
import HeroGradient from '../assets/hero-gradient.svg?component'
import { useThemeVars } from 'naive-ui'
import MenuLayout from '../components/MenuLayout.vue'
const themeVars = useThemeVars()
const collapsed = ref(false)
const activeKey = ref(null)
const route = useRoute()
const styleStore = useStyleStore()
@ -32,10 +32,9 @@ const m = toolsByCategory.map(category => ({
</script>
<template>
<n-layout has-sider>
<n-layout-sider bordered collapse-mode="width" :collapsed-width="64" :width="260" :collapsed="collapsed"
@collapse="collapsed = true" @expand="collapsed = false" :show-trigger="false" :native-scrollbar="false">
<menu-layout class="menu-layout" :class="{ isSmallScreen: styleStore.isSmallScreen }">
<template v-slot:sider>
<router-link to="/" class="hero-wrapper">
<hero-gradient class="gradient" />
<div class="text-wrapper">
@ -45,18 +44,8 @@ const m = toolsByCategory.map(category => ({
</div>
</router-link>
<n-menu :value="route.name" class="menu" :collapsed="collapsed" :collapsed-width="64"
:collapsed-icon-size="22" :options="m" v-model:value="activeKey" />
</n-layout-sider>
<n-layout class="content">
<div class="bar-wrapper">
<search-bar />
<n-button type="primary" tag="a" href="https://github.com/sponsors/CorentinTh" rel="noopener"
target="_blank">
<n-icon :component="Heart" />&nbsp;
Sponsor
</n-button>
<div class="sider-content">
<n-space v-if="styleStore.isSmallScreen" justify="center">
<n-button size="large" circle quaternary tag="a" href="https://github.com/CorentinTh/it-tools"
rel="noopener" target="_blank">
<n-icon size="25" :component="BrandGithub" />
@ -69,10 +58,58 @@ const m = toolsByCategory.map(category => ({
<n-icon size="25" v-if="styleStore.isDarkTheme" :component="Sun" />
<n-icon size="25" v-else :component="Moon" />
</n-button>
</n-space>
<n-menu :value="route.name" class="menu" :collapsed-width="64" :collapsed-icon-size="22" :options="m"
v-model:value="activeKey" :indent="20" />
</div>
</template>
<template v-slot:content>
<div class="navigation">
<n-button :size="styleStore.isSmallScreen ? 'medium' : 'large'" circle quaternary
@click="styleStore.isMenuCollapsed = !styleStore.isMenuCollapsed">
<n-icon size="25" :component="Menu2" />
</n-button>
<router-link to="/" #="{ navigate, href }" custom>
<n-button tag="a" :href="href" @click="navigate"
:size="styleStore.isSmallScreen ? 'medium' : 'large'" circle quaternary>
<n-icon size="25" :component="Home2" />
</n-button>
</router-link>
<search-bar />
<n-button type="primary" tag="a" href="https://github.com/sponsors/CorentinTh" rel="noopener"
target="_blank">
<n-icon :component="Heart" style="margin-right: 5px;" v-if="!styleStore.isSmallScreen" />
Sponsor
</n-button>
<n-button size="large" circle quaternary tag="a" href="https://github.com/CorentinTh/it-tools"
rel="noopener" target="_blank" v-if="!styleStore.isSmallScreen">
<n-icon size="25" :component="BrandGithub" />
</n-button>
<n-button size="large" circle quaternary tag="a" href="https://twitter.com/cthmsst" rel="noopener"
target="_blank" v-if="!styleStore.isSmallScreen">
<n-icon size="25" :component="BrandTwitter" />
</n-button>
<n-button size="large" circle quaternary @click="styleStore.isDarkTheme = !styleStore.isDarkTheme"
v-if="!styleStore.isSmallScreen">
<n-icon size="25" v-if="styleStore.isDarkTheme" :component="Sun" />
<n-icon size="25" v-else :component="Moon" />
</n-button>
</div>
<slot />
</n-layout>
</n-layout>
</template>
</menu-layout>
</template>
<style lang="less" scoped>
@ -87,7 +124,7 @@ const m = toolsByCategory.map(category => ({
// background-size: @size @size;
// }
.n-menu {
.sider-content {
padding-top: 160px;
padding-bottom: 200px;
}
@ -99,9 +136,10 @@ const m = toolsByCategory.map(category => ({
left: 0;
width: 100%;
z-index: 10;
overflow: hidden;
.gradient {
margin-top: -80px;
margin-top: -65px;
}
.text-wrapper {
@ -131,29 +169,24 @@ const m = toolsByCategory.map(category => ({
}
}
.bar-wrapper {
.navigation {
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
&>*:not(:first-child) {
margin-left: 15px;
margin-left: 10px;
.isSmallScreen & {
margin-left: 5px;
}
}
.search-bar {
// width: 100%;
flex-grow: 1;
}
}
.content {
// background-color: #f1f5f9;
::v-deep(.n-layout-scroll-container) {
padding: 26px;
}
}
.n-layout {
height: 100vh;
}
</style>

View file

@ -45,19 +45,23 @@ useHead(head)
.tool-header {
padding: 40px 0;
.n-h1 {
opacity: 0.9;
font-size: 40px;
font-weight: 400;
margin: 0;
line-height: 1;
}
.separator {
width: 200px;
height: 2px;
background: rgb(161, 161, 161);
margin-bottom: 10px;
margin: 10px 0;
}
.description {
margin: 0;

View file

@ -1,9 +1,11 @@
import { useStorage } from '@vueuse/core';
import { useMediaQuery, useStorage } from '@vueuse/core';
import { defineStore } from 'pinia';
import type { Ref } from 'vue';
export const useStyleStore = defineStore('style', {
state: () => ({
isDarkTheme: useStorage('useDarkTheme', false) as Ref<boolean>,
isDarkTheme: useStorage('isDarkTheme', true) as Ref<boolean>,
isMenuCollapsed: useStorage('isMenuCollapsed', false) as Ref<boolean>,
isSmallScreen: useMediaQuery('(max-width: 700px)'),
}),
});