mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-22 07:46:15 -04:00
feat: mobile friendly menu
This commit is contained in:
parent
f872972e69
commit
1e67fa6e0b
4 changed files with 121 additions and 35 deletions
47
src/components/MenuLayout.vue
Normal file
47
src/components/MenuLayout.vue
Normal 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>
|
|
@ -2,15 +2,15 @@
|
||||||
import { NIcon } from 'naive-ui';
|
import { NIcon } from 'naive-ui';
|
||||||
import { h, ref, type Component } from 'vue';
|
import { h, ref, type Component } from 'vue';
|
||||||
import { RouterLink, useRoute } from 'vue-router';
|
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 { toolsByCategory } from '@/tools';
|
||||||
import SearchBar from '../components/SearchBar.vue';
|
import SearchBar from '../components/SearchBar.vue';
|
||||||
import { useStyleStore } from '@/stores/style.store';
|
import { useStyleStore } from '@/stores/style.store';
|
||||||
import HeroGradient from '../assets/hero-gradient.svg?component'
|
import HeroGradient from '../assets/hero-gradient.svg?component'
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { useThemeVars } from 'naive-ui'
|
||||||
|
import MenuLayout from '../components/MenuLayout.vue'
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
const collapsed = ref(false)
|
|
||||||
const activeKey = ref(null)
|
const activeKey = ref(null)
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const styleStore = useStyleStore()
|
const styleStore = useStyleStore()
|
||||||
|
@ -32,10 +32,9 @@ const m = toolsByCategory.map(category => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-layout has-sider>
|
<menu-layout class="menu-layout" :class="{ isSmallScreen: styleStore.isSmallScreen }">
|
||||||
<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">
|
|
||||||
|
|
||||||
|
<template v-slot:sider>
|
||||||
<router-link to="/" class="hero-wrapper">
|
<router-link to="/" class="hero-wrapper">
|
||||||
<hero-gradient class="gradient" />
|
<hero-gradient class="gradient" />
|
||||||
<div class="text-wrapper">
|
<div class="text-wrapper">
|
||||||
|
@ -45,18 +44,8 @@ const m = toolsByCategory.map(category => ({
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<n-menu :value="route.name" class="menu" :collapsed="collapsed" :collapsed-width="64"
|
<div class="sider-content">
|
||||||
:collapsed-icon-size="22" :options="m" v-model:value="activeKey" />
|
<n-space v-if="styleStore.isSmallScreen" justify="center">
|
||||||
|
|
||||||
</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" />
|
|
||||||
Sponsor
|
|
||||||
</n-button>
|
|
||||||
<n-button size="large" circle quaternary tag="a" href="https://github.com/CorentinTh/it-tools"
|
<n-button size="large" circle quaternary tag="a" href="https://github.com/CorentinTh/it-tools"
|
||||||
rel="noopener" target="_blank">
|
rel="noopener" target="_blank">
|
||||||
<n-icon size="25" :component="BrandGithub" />
|
<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-if="styleStore.isDarkTheme" :component="Sun" />
|
||||||
<n-icon size="25" v-else :component="Moon" />
|
<n-icon size="25" v-else :component="Moon" />
|
||||||
</n-button>
|
</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>
|
</div>
|
||||||
<slot />
|
<slot />
|
||||||
</n-layout>
|
|
||||||
</n-layout>
|
</template>
|
||||||
|
|
||||||
|
</menu-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -87,7 +124,7 @@ const m = toolsByCategory.map(category => ({
|
||||||
// background-size: @size @size;
|
// background-size: @size @size;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
.n-menu {
|
.sider-content {
|
||||||
padding-top: 160px;
|
padding-top: 160px;
|
||||||
padding-bottom: 200px;
|
padding-bottom: 200px;
|
||||||
}
|
}
|
||||||
|
@ -99,9 +136,10 @@ const m = toolsByCategory.map(category => ({
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.gradient {
|
.gradient {
|
||||||
margin-top: -80px;
|
margin-top: -65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-wrapper {
|
.text-wrapper {
|
||||||
|
@ -131,29 +169,24 @@ const m = toolsByCategory.map(category => ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar-wrapper {
|
.navigation {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
&>*:not(:first-child) {
|
&>*:not(:first-child) {
|
||||||
margin-left: 15px;
|
margin-left: 10px;
|
||||||
|
|
||||||
|
.isSmallScreen & {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
|
// width: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
|
||||||
|
|
||||||
// background-color: #f1f5f9;
|
|
||||||
::v-deep(.n-layout-scroll-container) {
|
|
||||||
padding: 26px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-layout {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
|
@ -45,19 +45,23 @@ useHead(head)
|
||||||
|
|
||||||
.tool-header {
|
.tool-header {
|
||||||
padding: 40px 0;
|
padding: 40px 0;
|
||||||
|
|
||||||
.n-h1 {
|
.n-h1 {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: rgb(161, 161, 161);
|
background: rgb(161, 161, 161);
|
||||||
|
|
||||||
margin-bottom: 10px;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useMediaQuery, useStorage } from '@vueuse/core';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
export const useStyleStore = defineStore('style', {
|
export const useStyleStore = defineStore('style', {
|
||||||
state: () => ({
|
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)'),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue