mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-18 18:54:07 +00:00
Add collapsible sidebar with icon-only mode and tooltips
Desktop sidebar can be toggled between full width (w-56/w-64) and collapsed icon-only mode (w-16). Collapsed state persists in localStorage. Icons enlarge to w-6 h-6 when collapsed, labels hidden, v-tooltip shows item names on hover. Group labels replaced with thin dividers when collapsed. Toggle button pinned at bottom with ChevronLeft/Right icon. Content area padding animates smoothly with transition-all duration-300. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,8 +12,8 @@
|
||||
|
||||
<main
|
||||
:class="[
|
||||
'h-screen h-screen-ios overflow-y-auto min-h-0',
|
||||
hasCompany ? 'md:pl-56 xl:pl-64' : '',
|
||||
'h-screen h-screen-ios overflow-y-auto min-h-0 transition-all duration-300',
|
||||
hasCompany ? (globalStore.isSidebarCollapsed ? 'md:pl-16' : 'md:pl-56 xl:pl-64') : '',
|
||||
]"
|
||||
>
|
||||
<div class="pt-16 pb-16">
|
||||
|
||||
@@ -118,39 +118,48 @@
|
||||
|
||||
<!-- DESKTOP MENU -->
|
||||
<div
|
||||
:class="[
|
||||
globalStore.isSidebarCollapsed ? 'w-16' : 'w-56 xl:w-64',
|
||||
]"
|
||||
class="
|
||||
hidden
|
||||
w-56
|
||||
h-screen
|
||||
pb-32
|
||||
overflow-y-auto
|
||||
pb-16
|
||||
overflow-y-auto overflow-x-hidden
|
||||
bg-surface/80 backdrop-blur-xl
|
||||
border-r border-white/10
|
||||
xl:w-64
|
||||
md:fixed md:flex md:flex-col md:inset-y-0
|
||||
pt-16
|
||||
transition-all duration-300
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(menu, index) in globalStore.menuGroups"
|
||||
:key="index"
|
||||
class="p-0 m-0 mt-6 list-none"
|
||||
class="p-0 m-0 mt-4 list-none"
|
||||
>
|
||||
<div
|
||||
v-if="menu[0] && menu[0].group_label"
|
||||
class="px-6 mt-6 mb-2 text-xs font-semibold text-subtle uppercase tracking-wider"
|
||||
v-if="menu[0] && menu[0].group_label && !globalStore.isSidebarCollapsed"
|
||||
class="px-6 mt-6 mb-2 text-xs font-semibold text-subtle uppercase tracking-wider whitespace-nowrap"
|
||||
>
|
||||
{{ $t(menu[0].group_label) }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="menu[0] && menu[0].group_label && globalStore.isSidebarCollapsed"
|
||||
class="mx-3 my-2 border-t border-line-light"
|
||||
/>
|
||||
<router-link
|
||||
v-for="item in menu"
|
||||
:key="item"
|
||||
:to="item.link"
|
||||
v-tooltip="globalStore.isSidebarCollapsed ? { content: $t(item.title), placement: 'right' } : null"
|
||||
:class="[
|
||||
hasActiveUrl(item.link)
|
||||
? 'text-primary-600 bg-primary-50 font-semibold'
|
||||
: 'text-body hover:bg-hover',
|
||||
'cursor-pointer mx-3 px-3 py-2.5 group flex items-center rounded-lg text-sm not-italic font-medium transition-colors',
|
||||
globalStore.isSidebarCollapsed
|
||||
? 'cursor-pointer mx-2 px-0 py-2.5 group flex items-center justify-center rounded-lg text-sm font-medium transition-colors'
|
||||
: 'cursor-pointer mx-3 px-3 py-2.5 group flex items-center rounded-lg text-sm not-italic font-medium transition-colors',
|
||||
]"
|
||||
>
|
||||
<BaseIcon
|
||||
@@ -159,13 +168,34 @@
|
||||
hasActiveUrl(item.link)
|
||||
? 'text-primary-500'
|
||||
: 'text-subtle group-hover:text-body',
|
||||
'mr-3 shrink-0 h-5 w-5',
|
||||
globalStore.isSidebarCollapsed
|
||||
? 'shrink-0 h-6 w-6'
|
||||
: 'mr-3 shrink-0 h-5 w-5',
|
||||
]"
|
||||
/>
|
||||
|
||||
{{ $t(item.title) }}
|
||||
<span v-if="!globalStore.isSidebarCollapsed" class="whitespace-nowrap">
|
||||
{{ $t(item.title) }}
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<!-- Collapse Toggle -->
|
||||
<div class="sticky bottom-0 mt-auto pt-2 pb-2 bg-surface/80 backdrop-blur-xl">
|
||||
<button
|
||||
class="mx-3 px-3 py-2 w-[calc(100%-1.5rem)] flex items-center rounded-lg text-subtle hover:text-body hover:bg-hover transition-colors"
|
||||
:class="globalStore.isSidebarCollapsed ? '!justify-center !mx-2 !px-0' : ''"
|
||||
@click="globalStore.toggleSidebarCollapse()"
|
||||
>
|
||||
<BaseIcon
|
||||
:name="globalStore.isSidebarCollapsed ? 'ChevronRightIcon' : 'ChevronLeftIcon'"
|
||||
class="shrink-0 h-5 w-5"
|
||||
/>
|
||||
<span v-if="!globalStore.isSidebarCollapsed" class="ml-3 text-sm font-medium whitespace-nowrap">
|
||||
{{ $t('general.collapse') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
6
resources/scripts/admin/stores/global.js
vendored
6
resources/scripts/admin/stores/global.js
vendored
@@ -33,6 +33,7 @@ export const useGlobalStore = (useWindow = false) => {
|
||||
// Boolean Flags
|
||||
isAppLoaded: false,
|
||||
isSidebarOpen: false,
|
||||
isSidebarCollapsed: window.Ls?.get('sidebarCollapsed') === 'true',
|
||||
areCurrenciesLoading: false,
|
||||
|
||||
downloadReport: null,
|
||||
@@ -270,6 +271,11 @@ export const useGlobalStore = (useWindow = false) => {
|
||||
this.isSidebarOpen = val
|
||||
},
|
||||
|
||||
toggleSidebarCollapse() {
|
||||
this.isSidebarCollapsed = !this.isSidebarCollapsed
|
||||
window.Ls.set('sidebarCollapsed', this.isSidebarCollapsed ? 'true' : 'false')
|
||||
},
|
||||
|
||||
setIsAppLoaded(isAppLoaded) {
|
||||
this.isAppLoaded = isAppLoaded
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user