fix: collapsible sidebar
This commit is contained in:
parent
ffc8070c42
commit
cb9b4a6347
@ -1,32 +1,38 @@
|
|||||||
<template>
|
<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>
|
||||||
<div class="flex p-2">
|
<div class="flex p-2">
|
||||||
<UserDropdown />
|
<UserDropdown :isCollapsed="isSidebarCollapsed" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div v-for="link in links">
|
||||||
<nav class="space-y-0.5 px-2">
|
<SidebarLink
|
||||||
<NavLinks
|
:icon="link.icon"
|
||||||
:links="navigations"
|
:label="link.label"
|
||||||
class="flex items-center rounded px-2 py-1 text-gray-800 transition-all duration-300 ease-in-out"
|
:to="link.to"
|
||||||
active="bg-white shadow-sm"
|
:isCollapsed="isSidebarCollapsed"
|
||||||
inactive="hover:bg-gray-100"
|
class="my-0.5 mx-2"
|
||||||
>
|
/>
|
||||||
<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>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -38,7 +44,42 @@ import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
|||||||
import NoteIcon from '@/components/Icons/NoteIcon.vue'
|
import NoteIcon from '@/components/Icons/NoteIcon.vue'
|
||||||
import DashboardIcon from '@/components/Icons/DashboardIcon.vue'
|
import DashboardIcon from '@/components/Icons/DashboardIcon.vue'
|
||||||
import PhoneIcon from '@/components/Icons/PhoneIcon.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 = [
|
const navigations = [
|
||||||
{
|
{
|
||||||
@ -72,4 +113,6 @@ const navigations = [
|
|||||||
// route: { name: 'Dashboard' },
|
// route: { name: 'Dashboard' },
|
||||||
// },
|
// },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const isSidebarCollapsed = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen w-screen">
|
<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 />
|
<AppSidebar />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 flex flex-col h-full overflow-auto">
|
<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">
|
<Dropdown :options="userDropdownOptions">
|
||||||
<template v-slot="{ open }">
|
<template v-slot="{ open }">
|
||||||
<button
|
<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'"
|
:class="open ? 'bg-gray-300' : 'hover:bg-gray-200'"
|
||||||
v-if="user"
|
v-if="user"
|
||||||
>
|
>
|
||||||
<UserAvatar :user="user.name" size="md" />
|
<UserAvatar class="flex-shrink-0" :user="user.name" size="md" />
|
||||||
<span class="hidden text-base font-medium text-gray-900 sm:inline">
|
<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 }}
|
{{ user.full_name }}
|
||||||
</span>
|
</span>
|
||||||
<FeatherIcon
|
<FeatherIcon
|
||||||
name="chevron-down"
|
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"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
@ -27,6 +31,13 @@ import { usersStore } from '@/stores/users'
|
|||||||
import { Dropdown, FeatherIcon } from 'frappe-ui'
|
import { Dropdown, FeatherIcon } from 'frappe-ui'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
isCollapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const { logout } = sessionStore()
|
const { logout } = sessionStore()
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user