Make project group headers collapsible in nav panel (#10278)
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Douwe M Osinga <douwe@sidewalklabs.com> Co-authored-by: vincenzopalazzo <vincenzopalazzo@users.noreply.github.com> Co-authored-by: Lifei Zhou <lifei@squareup.com>
This commit is contained in:
@@ -135,8 +135,14 @@ export const Navigation: React.FC<{ className?: string }> = ({ className }) => {
|
||||
|
||||
const isActive = useCallback((path: string) => location.pathname === path, [location.pathname]);
|
||||
|
||||
const { recentSessions, recentSessionsByProject, activeSessionId, fetchSessions, handleNavClick, handleSessionClick } =
|
||||
useNavigationSessions();
|
||||
const {
|
||||
recentSessions,
|
||||
recentSessionsByProject,
|
||||
activeSessionId,
|
||||
fetchSessions,
|
||||
handleNavClick,
|
||||
handleSessionClick,
|
||||
} = useNavigationSessions();
|
||||
|
||||
const [sessionStatuses, setSessionStatuses] = useState<Map<string, SessionStatus>>(new Map());
|
||||
|
||||
@@ -181,6 +187,19 @@ export const Navigation: React.FC<{ className?: string }> = ({ className }) => {
|
||||
}, [isNavExpanded, fetchSessions]);
|
||||
|
||||
const [isChatsExpanded, setIsChatsExpanded] = useState(true);
|
||||
const [collapsedProjects, setCollapsedProjects] = useState<Set<string>>(new Set());
|
||||
|
||||
const toggleProjectCollapsed = useCallback((path: string) => {
|
||||
setCollapsedProjects((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(path)) {
|
||||
next.delete(path);
|
||||
} else {
|
||||
next.add(path);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!isNavExpanded) return null;
|
||||
|
||||
@@ -226,29 +245,40 @@ export const Navigation: React.FC<{ className?: string }> = ({ className }) => {
|
||||
{intl.formatMessage(i18n.noChats)}
|
||||
</div>
|
||||
) : recentSessionsByProject.length > 1 ? (
|
||||
recentSessionsByProject.map((group: ProjectGroup) => (
|
||||
<React.Fragment key={group.path}>
|
||||
<div
|
||||
className="px-3 pt-2 pb-0.5 text-[10px] uppercase tracking-wider text-text-tertiary truncate"
|
||||
title={group.path}
|
||||
>
|
||||
{group.label}
|
||||
</div>
|
||||
{group.sessions.map((session) => (
|
||||
<SessionRow
|
||||
key={session.id}
|
||||
session={session}
|
||||
active={session.id === activeSessionId}
|
||||
status={sessionStatuses.get(session.id)}
|
||||
onClick={() => {
|
||||
clearUnread(session.id);
|
||||
handleSessionClick(session.id);
|
||||
}}
|
||||
onRenamed={fetchSessions}
|
||||
/>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))
|
||||
recentSessionsByProject.map((group: ProjectGroup) => {
|
||||
const isCollapsed = collapsedProjects.has(group.path);
|
||||
return (
|
||||
<React.Fragment key={group.path}>
|
||||
<button
|
||||
onClick={() => toggleProjectCollapsed(group.path)}
|
||||
aria-expanded={!isCollapsed}
|
||||
className="flex items-center gap-1 w-full px-3 pt-2 pb-0.5 text-[10px] uppercase tracking-wider text-text-tertiary hover:text-text-secondary transition-colors"
|
||||
title={group.path}
|
||||
>
|
||||
{isCollapsed ? (
|
||||
<ChevronRight className="w-3 h-3 flex-shrink-0" />
|
||||
) : (
|
||||
<ChevronDown className="w-3 h-3 flex-shrink-0" />
|
||||
)}
|
||||
<span className="truncate">{group.label}</span>
|
||||
</button>
|
||||
{!isCollapsed &&
|
||||
group.sessions.map((session) => (
|
||||
<SessionRow
|
||||
key={session.id}
|
||||
session={session}
|
||||
active={session.id === activeSessionId}
|
||||
status={sessionStatuses.get(session.id)}
|
||||
onClick={() => {
|
||||
clearUnread(session.id);
|
||||
handleSessionClick(session.id);
|
||||
}}
|
||||
onRenamed={fetchSessions}
|
||||
/>
|
||||
))}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
recentSessions.map((session) => (
|
||||
<SessionRow
|
||||
|
||||
Reference in New Issue
Block a user