Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile Menu #144

Merged
merged 2 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 91 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,107 @@
}
</style>


<style>
.container {
display: none;
}

.container.show {
display: block;
}

.pageload-overlay {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
visibility: hidden;
}

.pageload-overlay.show {
visibility: visible;
}

.pageload-overlay svg {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}

.pageload-overlay svg path {
fill: #fff;
}

.pageload-overlay::after,
.pageload-overlay::before {
content: '';
position: fixed;
width: 20px;
height: 20px;
top: 50%;
left: 50%;
margin: -10px 0 0 -10px;
border-radius: 50%;
visibility: hidden;
opacity: 0;
z-index: 1000;
-webkit-transition: opacity 0.15s, visibility 0s 0.15s;
transition: opacity 0.15s, visibility 0s 0.15s;
}

.pageload-overlay::after {
background: #6cc88a;
-webkit-transform: translateX(-20px);
transform: translateX(-20px);
-webkit-animation: moveRight 0.6s linear infinite alternate;
animation: moveRight 0.6s linear infinite alternate;
}

.pageload-overlay::before {
background: #4fc3f7;
-webkit-transform: translateX(20px);
transform: translateX(20px);
-webkit-animation: moveLeft 0.6s linear infinite alternate;
animation: moveLeft 0.6s linear infinite alternate;
}

@-webkit-keyframes moveRight {
to { -webkit-transform: translateX(20px); }
}

@keyframes moveRight {
to { transform: translateX(20px); }
}

@-webkit-keyframes moveLeft {
to { -webkit-transform: translateX(-20px); }
}

@keyframes moveLeft {
to { transform: translateX(-20px); }
}

.pageload-loading.pageload-overlay::after,
.pageload-loading.pageload-overlay::before {
opacity: 1;
visibility: visible;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
}
</style>
</head>

<body class="font-inter">

<div id="app">
<div class="preloader">
<div class="loader"></div>
<h1>Yummy Admin</h1>
<h2>Free Vue 3 Dashboard</h2>
</div>
</div>

<script type="module" src="/src/main.ts"></script>
<noscript>This website requires JavaScript to function properly. Please enable JavaScript to continue.</noscript>
<script defer src="https://cloud.umami.is/script.js" data-website-id="19a8d926-ef84-44b6-a2a9-7e438716fd9d"></script>
Expand Down
3 changes: 0 additions & 3 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ declare module 'vue' {
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
NButton: typeof import('naive-ui')['NButton']
NColorPicker: typeof import('naive-ui')['NColorPicker']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDataTable: typeof import('naive-ui')['NDataTable']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
Expand All @@ -56,7 +55,6 @@ declare module 'vue' {
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NMenu: typeof import('naive-ui')['NMenu']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
Notifications: typeof import('./components/Navbar/Notifications.vue')['default']
NPageHeader: typeof import('naive-ui')['NPageHeader']
Expand All @@ -66,7 +64,6 @@ declare module 'vue' {
NTag: typeof import('naive-ui')['NTag']
NTooltip: typeof import('naive-ui')['NTooltip']
NTreeSelect: typeof import('naive-ui')['NTreeSelect']
NUpload: typeof import('naive-ui')['NUpload']
OrderManagement: typeof import('./components/Orders/OrderManagement.vue')['default']
PersianIcon: typeof import('./components/CustomIcons/PersianIcon.vue')['default']
ProductsManagement: typeof import('./components/Products/ProductsManagement.vue')['default']
Expand Down
2 changes: 1 addition & 1 deletion src/components/Category/CategoryManagement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const { renderDeleteActionButton, renderActionButton } = useRender()
const layout = useLayoutStore()
const { dialogPlacement } = storeToRefs(layout)
const { t } = useI18n()
const collapsed = ref(false)
const collapsed = ref(useWindowSize().width.value < 600)
const store = useCategoryStore()
const message = useMessage()

Expand Down
27 changes: 18 additions & 9 deletions src/components/Navbar/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,35 @@
import {
PanelLeftContract24Regular as CollapseIcon,
PanelLeftExpand20Regular as ExpandIcon,
Navigation20Regular as MenuIcon,
} from '@vicons/fluent'

import { storeToRefs } from 'pinia'

const layoutStore = useLayoutStore()
const { collapsed, isRtl } = storeToRefs(layoutStore)
const { collapsed, isRtl, mobileMode } = storeToRefs(layoutStore)
</script>

<template>
<n-page-header class="px-2 py-3 navbar">
<template #title>
<div class="flex items-center">
<n-button mx-2 size="small" quaternary circle :class="{ 'rotate-180': isRtl }" @click="layoutStore.toggleSidebar">
<template #icon>
<NIcon size="1.2rem">
<ExpandIcon v-if="collapsed" />
<CollapseIcon v-else />
</NIcon>
</template>
</n-button>
<div flex w-full justify-start items-center>
<img v-if="mobileMode" width="35" src="@/assets/images/logo.png" alt="logo" class="logo">

<n-button
mx-2 size="small" quaternary circle :class="{ 'rotate-180': isRtl }"
@click="layoutStore.toggleSidebar"
>
<template #icon>
<NIcon size="1.2rem">
<MenuIcon v-if="mobileMode" />
<ExpandIcon v-else-if="collapsed" />
<CollapseIcon v-else />
</NIcon>
</template>
</n-button>
</div>
<BreadCrumb />
</div>
</template>
Expand Down
74 changes: 45 additions & 29 deletions src/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
DoorArrowRight20Regular as AuthIcon,
CheckmarkStarburst16Regular as BrandsIcon,
Folder32Regular as CategoryIcon,
Dismiss20Filled as CloseIcon,
Color24Regular as ColorsIcon,
People28Regular as CustomersIcon,
Home32Regular as DashboardIcon,
Expand All @@ -22,14 +23,15 @@ import {
import { storeToRefs } from 'pinia'

const layoutStore = useLayoutStore()
const { collapsed, forceCollapsed } = storeToRefs(layoutStore)
const { collapsed, forceCollapsed, mobileMode, mobileMenuClosed } = storeToRefs(layoutStore)
const { t } = useI18n()
const { renderIcon, renderLabel } = useRender()

const isHovered = ref(false)

const effectiveCollapsed = computed(() => {
return (collapsed.value || forceCollapsed.value) && !isHovered.value
if (mobileMode.value)
return mobileMenuClosed.value

return (collapsed.value || forceCollapsed.value)
})

const menuOptions: MenuOption[] = [
Expand Down Expand Up @@ -137,31 +139,51 @@ const menuOptions: MenuOption[] = [
const route = useRoute()
const selectedMenuKey = ref('dashboard')
const menuRef = ref<MenuInst | null>(null)

onMounted(() => {
activateCurrentRoute()
})

function activateCurrentRoute() {
const keys = menuOptions.flatMap(m => m.children || m) as [{ key: string }]
if (keys !== undefined) {
selectedMenuKey.value = keys.find(k => k.key.toLowerCase() === route.name.toLowerCase())?.key ?? 'index'
menuRef.value?.showOption(selectedMenuKey.value)
}
}

const router = useRouter()
router.beforeEach(() => {
layoutStore.closeSidebar()
})
</script>

<template>
<n-layout-sider
:native-scrollbar="false" collapse-mode="width" :collapsed-width="64" :collapsed="effectiveCollapsed"
:class="{ collapsed: effectiveCollapsed }" @mouseenter="isHovered = true" @mouseleave="isHovered = false"
:native-scrollbar="false" collapse-mode="width" :collapsed-width="mobileMode ? 0 : 64"
:collapsed="effectiveCollapsed" :class="{ 'collapsed': effectiveCollapsed, 'mobile-mode': mobileMode }"
>
<div class="logo-container mb-4">
<div flex w-full justify-start items-center>
<img src="@/assets/images/logo.png" alt="logo" class="logo">
<h1 class="main-title">
{{ t('title') }}
</h1>
<div flex w-full justify-between items-center>
<div flex w-full justify-start items-center>
<img src="@/assets/images/logo.png" alt="logo" class="logo">
<h1 class="main-title">
{{ t('title') }}
</h1>
</div>

<n-button v-if="mobileMode" mx-2 size="small" tertiary circle @click="layoutStore.closeSidebar">
<template #icon>
<NIcon size="1.2rem">
<CloseIcon />
</NIcon>
</template>
</n-button>
</div>
</div>
<n-menu
ref="menuRef" v-model:value="selectedMenuKey" :collapsed-width="64" :collapsed-icon-size="22"
:options="menuOptions"
ref="menuRef" v-model:value="selectedMenuKey" :collapsed-width="mobileMode ? 0 : 64"
:collapsed-icon-size="mobileMode ? 30 : 20" :options="menuOptions"
/>
</n-layout-sider>
</template>
Expand Down Expand Up @@ -192,14 +214,22 @@ onMounted(() => {
max-width: 175px;
}
}
.mobile-mode {
max-width: 100% !important;
width: 100% !important;
}

.mobile-mode.collapsed {
max-width: 0 !important;
}
.collapsed {

.logo-container {
padding: 1.5rem 0.5rem 0.5rem .5rem;
}

.main-title {
display:none;
display: none;
}
}

Expand All @@ -212,8 +242,6 @@ onMounted(() => {
}

.collapsed {
max-width: 100px;

.p-button-label {
display: none;
}
Expand All @@ -230,25 +258,13 @@ onMounted(() => {
margin-left: 0.8rem;
margin-right: .5rem;
}

// .p-button {
// .p-button-label {
// text-align: right;
// }

// .p-button-icon-left {
// margin-right: 0;
// margin-left: 0.5rem;
// }
// }
}

.n-menu-item {
user-select: none;
}

.main-menu {

.active {
.p-button {
background: #f4f4f5;
Expand All @@ -265,7 +281,7 @@ onMounted(() => {
}

.separator {
border-bottom: solid 1px rgb(224, 224, 224);
border-bottom: solid 1px #f4f4f5;
margin-bottom: .5rem;
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,13 @@ app.config.globalProperties.$filters = {}
Object.values(import.meta.glob<any>('./common/filters/*.filter.ts', { eager: true, import: 'default' }))
.forEach(filters => Object.keys(filters).forEach(func => app.config.globalProperties.$filters[func] = filters[func]))

router.beforeEach((to, from, next) => {
router.beforeEach((to, _, next) => {
const { t } = i18n.global
let title = t('title')
if (to.meta.title)
title = `${t(`menu.${to.meta.title}`)} - ${title}`

document.title = title

next()
})

Expand Down
Loading
Loading