// Sidebar collapsed state (design: Navigation shell, Collapsed / tablet; // D-06, D-10). The section panel hides below about 1100px or when the admin // collapses it. The viewport only forces the collapse and never overwrites // the admin's own choice, which persists in localStorage as a boolean under // SIDEBAR_KEY. The other writers of browser storage are listState.ts, // which keeps list query parameters per list, and usePreferences.ts, which // caches the theme and locale codes; tokens and record data are never stored // (T-10-22, T-16.1-03). import { computed, readonly, ref } from 'vue' export const SIDEBAR_KEY = 'summer-admin.sidebar' export const NARROW_QUERY = '(max-width: 1099px)' const manual = ref(false) const narrow = ref(false) /** The panel is hidden: forced by the viewport or chosen by the admin. */ export const sidebarCollapsed = computed(() => narrow.value || manual.value) function storage(): Storage | null { try { return typeof window === 'undefined' ? null : window.localStorage } catch { return null } } function readPreference(): boolean { try { return storage()?.getItem(SIDEBAR_KEY) === 'true' } catch { return false } } /** Records the admin's choice; only 'true' or 'false' is stored. */ export function setCollapsed(value: boolean): void { manual.value = value try { storage()?.setItem(SIDEBAR_KEY, value ? 'true' : 'false') } catch { // Storage may be full or disabled; the choice still holds for the session. } } /** * Reads the stored preference and follows the viewport breakpoint. Returns * the unsubscribe function; the shell calls it when it unmounts. */ export function watchViewport(): () => void { manual.value = readPreference() if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { narrow.value = false return () => undefined } const query = window.matchMedia(NARROW_QUERY) narrow.value = query.matches const onChange = (event: MediaQueryListEvent): void => { narrow.value = event.matches } query.addEventListener('change', onChange) return () => query.removeEventListener('change', onChange) } export function useSidebar() { return { collapsed: sidebarCollapsed, /** The viewport is below the breakpoint; expanding is not possible. */ narrow: readonly(narrow), manual: readonly(manual), setCollapsed, watchViewport, } }