fix: collapsible sidebar
This commit is contained in:
parent
ffc8070c42
commit
cb9b4a6347
@ -1,32 +1,38 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-full justify-between">
|
||||
<div
|
||||
class="flex flex-col h-full justify-between transition-all duration-300 ease-in-out"
|
||||
:class="isSidebarCollapsed ? 'w-12' : 'w-56'"
|
||||
>
|
||||
<div>
|
||||
<div class="flex p-2">
|
||||
<UserDropdown />
|
||||
<UserDropdown :isCollapsed="isSidebarCollapsed" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<nav class="space-y-0.5 px-2">
|
||||
<NavLinks
|
||||
:links="navigations"
|
||||
class="flex items-center rounded px-2 py-1 text-gray-800 transition-all duration-300 ease-in-out"
|
||||
active="bg-white shadow-sm"
|
||||
inactive="hover:bg-gray-100"
|
||||
>
|
||||
<template v-slot="{ link }">
|
||||
<div class="flex w-full items-center space-x-2">
|
||||
<span class="grid h-5 w-6 place-items-center">
|
||||
<component
|
||||
:is="link.icon"
|
||||
class="h-4.5 w-4.5 text-gray-700"
|
||||
/>
|
||||
</span>
|
||||
<span class="text-base">{{ link.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</NavLinks>
|
||||
</nav>
|
||||
<div v-for="link in links">
|
||||
<SidebarLink
|
||||
:icon="link.icon"
|
||||
:label="link.label"
|
||||
:to="link.to"
|
||||
:isCollapsed="isSidebarCollapsed"
|
||||
class="my-0.5 mx-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SidebarLink
|
||||
:label="isSidebarCollapsed ? 'Expand' : 'Collapse'"
|
||||
:isCollapsed="isSidebarCollapsed"
|
||||
@click="isSidebarCollapsed = !isSidebarCollapsed"
|
||||
class="m-2"
|
||||
>
|
||||
<template #icon>
|
||||
<span class="grid h-5 w-6 place-items-center flex-shrink-0">
|
||||
<component
|
||||
:is="CollapseSidebar"
|
||||
class="h-4.5 w-4.5 text-gray-700 duration-300 ease-in-out"
|
||||
:class="{ '[transform:rotateY(180deg)]': isSidebarCollapsed }"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</SidebarLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -38,7 +44,42 @@ import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
||||
import NoteIcon from '@/components/Icons/NoteIcon.vue'
|
||||
import DashboardIcon from '@/components/Icons/DashboardIcon.vue'
|
||||
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
||||
import NavLinks from '@/components/NavLinks.vue'
|
||||
import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
|
||||
import SidebarLink from '@/components/SidebarLink.vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const links = [
|
||||
{
|
||||
label: 'Leads',
|
||||
icon: LeadsIcon,
|
||||
to: 'Leads',
|
||||
},
|
||||
{
|
||||
label: 'Deals',
|
||||
icon: DealsIcon,
|
||||
to: 'Deals',
|
||||
},
|
||||
{
|
||||
label: 'Contacts',
|
||||
icon: ContactsIcon,
|
||||
to: 'Contacts',
|
||||
},
|
||||
{
|
||||
label: 'Notes',
|
||||
icon: NoteIcon,
|
||||
to: 'Notes',
|
||||
},
|
||||
{
|
||||
label: 'Call Logs',
|
||||
icon: PhoneIcon,
|
||||
to: 'Call Logs',
|
||||
},
|
||||
// {
|
||||
// label: 'Dashboard',
|
||||
// icon: DashboardIcon,
|
||||
// to: 'Dashboard',
|
||||
// },
|
||||
]
|
||||
|
||||
const navigations = [
|
||||
{
|
||||
@ -72,4 +113,6 @@ const navigations = [
|
||||
// route: { name: 'Dashboard' },
|
||||
// },
|
||||
]
|
||||
|
||||
const isSidebarCollapsed = ref(false)
|
||||
</script>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex h-screen w-screen">
|
||||
<div class="h-full border-r bg-gray-50 w-[220px]">
|
||||
<div class="h-full border-r bg-gray-50">
|
||||
<AppSidebar />
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col h-full overflow-auto">
|
||||
|
||||
27
frontend/src/components/Icons/CollapseSidebar.vue
Normal file
27
frontend/src/components/Icons/CollapseSidebar.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.875 9.06223L3 9.06232"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M6.74537 5.31699L3 9.06236L6.74527 12.8076"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14.1423 4L14.1423 14.125"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
23
frontend/src/components/Icons/ExpandSidebar.vue
Normal file
23
frontend/src/components/Icons/ExpandSidebar.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.26733 9.06277L14.1423 9.06268"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.397 12.808L14.1423 9.06264L10.3971 5.31737"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M3 14.125L3 4" stroke="currentColor" stroke-linecap="round" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -1,16 +0,0 @@
|
||||
<template>
|
||||
<router-link custom :to="link.route" v-slot="{ href, isActive, navigate }">
|
||||
<a
|
||||
:href="href"
|
||||
@click="navigate"
|
||||
:class="[isActive ? active : inactive, $attrs.class]"
|
||||
>
|
||||
<slot :link="link">
|
||||
{{ link.name }}
|
||||
</slot>
|
||||
</a>
|
||||
</router-link>
|
||||
</template>
|
||||
<script setup>
|
||||
const props = defineProps(['link', 'active', 'inactive'])
|
||||
</script>
|
||||
@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<NavLink
|
||||
v-for="link in links"
|
||||
:key="link.name"
|
||||
:class="$attrs.class"
|
||||
v-bind="{ link, active, inactive }"
|
||||
>
|
||||
<slot :link="link">
|
||||
{{ link.name }}
|
||||
</slot>
|
||||
</NavLink>
|
||||
</template>
|
||||
<script setup>
|
||||
import NavLink from '@/components/NavLink.vue'
|
||||
|
||||
const props = defineProps(['links', 'active', 'inactive'])
|
||||
</script>
|
||||
57
frontend/src/components/SidebarLink.vue
Normal file
57
frontend/src/components/SidebarLink.vue
Normal file
@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center rounded text-gray-800 cursor-pointer transition-all duration-300 ease-in-out"
|
||||
:class="isActive ? 'bg-white shadow-sm' : 'hover:bg-gray-100'"
|
||||
@click="handleClick"
|
||||
>
|
||||
<div class="flex items-center p-1">
|
||||
<component :is="isCollapsed ? Tooltip : 'div'" :text="label" placement="right">
|
||||
<slot name="icon">
|
||||
<span class="grid h-5 w-6 place-items-center flex-shrink-0">
|
||||
<component :is="icon" class="h-4.5 w-4.5 text-gray-700" />
|
||||
</span>
|
||||
</slot>
|
||||
</component>
|
||||
<span
|
||||
class="flex-shrink-0 text-base duration-300 ease-in-out"
|
||||
:class="isCollapsed ? 'opacity-0 ml-0' : 'opacity-100 ml-2'"
|
||||
>
|
||||
{{ label }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Tooltip } from 'frappe-ui'
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
type: Object,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
to: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isCollapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
function handleClick() {
|
||||
router.push({ name: props.to })
|
||||
}
|
||||
|
||||
let isActive = computed(() => {
|
||||
return router.currentRoute.value.name === props.to
|
||||
})
|
||||
</script>
|
||||
@ -2,17 +2,21 @@
|
||||
<Dropdown :options="userDropdownOptions">
|
||||
<template v-slot="{ open }">
|
||||
<button
|
||||
class="flex w-full items-center space-x-2 rounded-md p-2 text-left"
|
||||
class="flex w-full items-center rounded-md px-1 py-2 text-left"
|
||||
:class="open ? 'bg-gray-300' : 'hover:bg-gray-200'"
|
||||
v-if="user"
|
||||
>
|
||||
<UserAvatar :user="user.name" size="md" />
|
||||
<span class="hidden text-base font-medium text-gray-900 sm:inline">
|
||||
<UserAvatar class="flex-shrink-0" :user="user.name" size="md" />
|
||||
<span
|
||||
class="hidden text-base font-medium text-gray-900 sm:inline duration-300 ease-in-out"
|
||||
:class="isCollapsed ? 'opacity-0 ml-0' : 'opacity-100 ml-2'"
|
||||
>
|
||||
{{ user.full_name }}
|
||||
</span>
|
||||
<FeatherIcon
|
||||
name="chevron-down"
|
||||
class="h-4 w-4 sm:inline"
|
||||
class="h-4 w-4 sm:inline duration-300 ease-in-out"
|
||||
:class="isCollapsed ? 'opacity-0 ml-0' : 'opacity-100 ml-2'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
@ -27,6 +31,13 @@ import { usersStore } from '@/stores/users'
|
||||
import { Dropdown, FeatherIcon } from 'frappe-ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
isCollapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const { logout } = sessionStore()
|
||||
const { getUser } = usersStore()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user