Files
analiticaNucleo/nuxt4-app/app/composables/useSidebarState.ts
josedario87 6ce28596c8
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 50s
Fix: Corregir icono del toggle en carga inicial de la sidebar
El icono del toggle mostraba "X" (cerrar) en lugar del icono hamburguesa
al cargar la página, aunque se corregía tras la primera interacción.

Causa raíz:
- UDashboardGroup inicializa sidebarOpen en false
- Nuestro composable inicializaba open en true por defecto
- El toggle lee el estado del contexto de DashboardGroup
- Desincronización causaba que el icono mostrara el estado incorrecto

Solución:
- Cambiar el default de open a false en el composable
- En desktop, open=false es correcto (no hay overlay, sidebar siempre visible)
- En mobile, open=false significa overlay cerrado (comportamiento esperado)
- Alineación total con el comportamiento de Nuxt UI

Archivos modificados:
- app/composables/useSidebarState.ts:43-46 (default open: false)
- app/composables/useSidebarState.ts:57 (fallback a false)

Referencias:
- app/composables/useSidebarState.ts:46
- app/composables/useSidebarState.ts:57
2025-10-30 11:23:21 -06:00

132 lines
3.2 KiB
TypeScript

/**
* Composable unificado para manejar el estado de la sidebar
*
* Centraliza todo el estado relacionado con la sidebar para evitar
* inconsistencias entre múltiples refs y watchers en cascada.
*
* Características:
* - Estado persistente en cookies
* - Sincronización automática entre open/collapsed
* - Manejo de responsive (mobile vs desktop)
* - Cierre automático en navegación (solo mobile)
*/
import { ref, computed, watch } from 'vue'
import { useRoute } from 'vue-router'
// Storage key para las cookies
const STORAGE_KEY = 'analytics-dashboard-sidebar'
// Tipos
interface SidebarState {
open: boolean
collapsed: boolean
size: number
}
// Estado global singleton
const sidebarState = ref<SidebarState | null>(null)
export function useSidebarState() {
const route = useRoute()
// Detectar si estamos en mobile (debe hacerse antes de leer el estado)
const isMobile = computed(() => {
if (import.meta.server) return false
return window.innerWidth < 1024 // lg breakpoint
})
// Inicializar estado solo una vez (singleton)
if (!sidebarState.value) {
// Leer de cookie si existe
const savedState = useCookie<SidebarState>(STORAGE_KEY, {
default: () => ({
// En desktop, open=false porque no hay overlay (la sidebar siempre está visible)
// En mobile, open=false significa que el overlay está cerrado (comportamiento correcto)
open: false,
collapsed: false,
size: 28 // defaultSize
})
})
sidebarState.value = savedState.value
}
// Referencias reactivas al estado
const open = computed({
get: () => sidebarState.value?.open ?? false,
set: (value: boolean) => {
if (sidebarState.value) {
sidebarState.value.open = value
// Persistir en cookie
const cookie = useCookie<SidebarState>(STORAGE_KEY)
cookie.value = sidebarState.value
}
}
})
const collapsed = computed({
get: () => sidebarState.value?.collapsed ?? false,
set: (value: boolean) => {
if (sidebarState.value) {
sidebarState.value.collapsed = value
// Persistir en cookie
const cookie = useCookie<SidebarState>(STORAGE_KEY)
cookie.value = sidebarState.value
}
}
})
const size = computed({
get: () => sidebarState.value?.size ?? 28,
set: (value: number) => {
if (sidebarState.value) {
sidebarState.value.size = value
// Persistir en cookie
const cookie = useCookie<SidebarState>(STORAGE_KEY)
cookie.value = sidebarState.value
}
}
})
// Funciones de control
function toggle() {
open.value = !open.value
}
function toggleCollapse() {
collapsed.value = !collapsed.value
}
function setOpen(value: boolean) {
open.value = value
}
function setCollapsed(value: boolean) {
collapsed.value = value
}
// Auto-cerrar en navegación solo en mobile
if (import.meta.client) {
watch(() => route.fullPath, () => {
if (isMobile.value) {
open.value = false
}
})
}
return {
// Estado
open,
collapsed,
size,
isMobile,
// Acciones
toggle,
toggleCollapse,
setOpen,
setCollapsed
}
}