Make the sidebar collapsible and let the knowledge graph go full screen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,12 @@ function OrgSwitcher() {
|
||||
)
|
||||
}
|
||||
|
||||
// Remembered per browser; storage can throw in private windows, so it falls back to expanded.
|
||||
const COLLAPSED_KEY = 'sidebar-collapsed'
|
||||
function readCollapsed() {
|
||||
try { return localStorage.getItem(COLLAPSED_KEY) === '1' } catch { return false }
|
||||
}
|
||||
|
||||
export function AppLayout() {
|
||||
useLang()
|
||||
const [help, setHelp] = useState(false)
|
||||
@@ -138,16 +144,22 @@ export function AppLayout() {
|
||||
// Nav groups stay closed unless they hold the current page; a click on the label overrides that until the next route.
|
||||
const [toggled, setToggled] = useState<Partial<Record<Key, boolean>>>({})
|
||||
useEffect(() => setToggled({}), [pathname])
|
||||
const [collapsed, setCollapsed] = useState(readCollapsed)
|
||||
const toggleCollapsed = () => setCollapsed(c => {
|
||||
try { localStorage.setItem(COLLAPSED_KEY, c ? '0' : '1') } catch { /* keep it for this visit only */ }
|
||||
return !c
|
||||
})
|
||||
const name = user?.fullName || user?.primaryEmailAddress?.emailAddress || '-'
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<div className={collapsed ? 'app sidebar-collapsed' : 'app'}>
|
||||
<aside className="sidebar">
|
||||
<NavLink to="/production"><Brand /></NavLink>
|
||||
<OrgSwitcher />
|
||||
<nav aria-label={t('layout.mainNav')}>
|
||||
{NAV_GROUPS.map(g => {
|
||||
const open = toggled[g.label] ?? g.items.some(n => pathname.startsWith(n.to))
|
||||
// The icon rail has no group labels, so every item shows.
|
||||
const open = collapsed || (toggled[g.label] ?? g.items.some(n => pathname.startsWith(n.to)))
|
||||
return [
|
||||
<button key={g.label} type="button" className="nav-label" aria-expanded={open} aria-controls={`nav-${g.label}`}
|
||||
onClick={() => setToggled(s => ({ ...s, [g.label]: !open }))}>
|
||||
@@ -155,28 +167,36 @@ export function AppLayout() {
|
||||
</button>,
|
||||
<div key={`${g.label}-items`} id={`nav-${g.label}`} className={open ? 'nav-items' : 'nav-items closed'}>
|
||||
{g.items.map(n => (
|
||||
<NavLink key={n.to} to={n.to} className={({ isActive }) => (isActive ? 'active' : undefined)}>
|
||||
<Icon name={n.icon} />{t(n.label)}
|
||||
<NavLink key={n.to} to={n.to} className={({ isActive }) => (isActive ? 'active' : undefined)}
|
||||
title={collapsed ? t(n.label) : undefined}>
|
||||
<Icon name={n.icon} /><span className="nav-text">{t(n.label)}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
</div>,
|
||||
]
|
||||
})}
|
||||
</nav>
|
||||
<div className="sidebar-help">
|
||||
{collapsed ? (
|
||||
<button type="button" className="icon-btn sidebar-help-icon" title={t('help.open')} aria-label={t('help.open')}
|
||||
onClick={() => setHelp(true)}><Icon name="info" /></button>
|
||||
) : <div className="sidebar-help">
|
||||
<Icon name="info" />
|
||||
<b>{t('help.boxTitle')}</b>
|
||||
<p>{t('help.boxBody')}</p>
|
||||
<button type="button" onClick={() => setHelp(true)}>{t('help.open')} <Icon name="arrow-right" /></button>
|
||||
</div>
|
||||
</div>}
|
||||
<div className="profile">
|
||||
<span className="avatar">{name.charAt(0).toUpperCase()}</span>
|
||||
<span className="avatar" title={collapsed ? name : undefined}>{name.charAt(0).toUpperCase()}</span>
|
||||
<div><b>{name}</b><small>{user?.primaryEmailAddress?.emailAddress}</small></div>
|
||||
<button className="icon-btn" aria-label={t('layout.signOut')} onClick={() => signOut({ redirectUrl: '/' })}><Icon name="logout" /></button>
|
||||
</div>
|
||||
</aside>
|
||||
<div className="workspace-main">
|
||||
<header className="topbar">
|
||||
<button type="button" className="icon-btn sidebar-toggle" aria-pressed={collapsed}
|
||||
aria-label={t(collapsed ? 'layout.expandSidebar' : 'layout.collapseSidebar')}
|
||||
title={t(collapsed ? 'layout.expandSidebar' : 'layout.collapseSidebar')}
|
||||
onClick={toggleCollapsed}><Icon name="sidebar" /></button>
|
||||
<div className="breadcrumb">{t('layout.breadcrumb')} <span>/</span> <b>{current ? t(current.label) : ''}</b></div>
|
||||
<div className="topbar-right">
|
||||
<span className="demo-indicator"><i></i> {t('layout.engine')}</span>
|
||||
|
||||
Reference in New Issue
Block a user