feat: content header with collapse toggle + realtime indicator
- New SidebarProvider context for shared collapse state - ContentHeader visible on desktop: collapse icon left, "Live" right - Collapse button removed from sidebar bottom (moved to header) - Keyboard shortcut [ handled by context, not sidebar - Realtime indicator polls every 5s, ticks every 1s for freshness
This commit is contained in:
68
lib/sidebar-context.tsx
Normal file
68
lib/sidebar-context.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useState, useCallback, useEffect } from 'react'
|
||||
|
||||
const SIDEBAR_KEY = 'pulse_sidebar_collapsed'
|
||||
|
||||
interface SidebarState {
|
||||
collapsed: boolean
|
||||
toggle: () => void
|
||||
expand: () => void
|
||||
collapse: () => void
|
||||
}
|
||||
|
||||
const SidebarContext = createContext<SidebarState>({
|
||||
collapsed: true,
|
||||
toggle: () => {},
|
||||
expand: () => {},
|
||||
collapse: () => {},
|
||||
})
|
||||
|
||||
export function SidebarProvider({ children }: { children: React.ReactNode }) {
|
||||
const [collapsed, setCollapsed] = useState(() => {
|
||||
if (typeof window === 'undefined') return true
|
||||
return localStorage.getItem(SIDEBAR_KEY) !== 'false'
|
||||
})
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
setCollapsed((prev) => {
|
||||
const next = !prev
|
||||
localStorage.setItem(SIDEBAR_KEY, String(next))
|
||||
return next
|
||||
})
|
||||
}, [])
|
||||
|
||||
const expand = useCallback(() => {
|
||||
setCollapsed(false)
|
||||
localStorage.setItem(SIDEBAR_KEY, 'false')
|
||||
}, [])
|
||||
|
||||
const collapse = useCallback(() => {
|
||||
setCollapsed(true)
|
||||
localStorage.setItem(SIDEBAR_KEY, 'true')
|
||||
}, [])
|
||||
|
||||
// Keyboard shortcut: [ to toggle
|
||||
useEffect(() => {
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
const tag = (e.target as HTMLElement)?.tagName
|
||||
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return
|
||||
if (e.key === '[' && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
||||
e.preventDefault()
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', handler)
|
||||
return () => document.removeEventListener('keydown', handler)
|
||||
}, [toggle])
|
||||
|
||||
return (
|
||||
<SidebarContext.Provider value={{ collapsed, toggle, expand, collapse }}>
|
||||
{children}
|
||||
</SidebarContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useSidebar() {
|
||||
return useContext(SidebarContext)
|
||||
}
|
||||
Reference in New Issue
Block a user