mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-09-06 14:39:10 +00:00
* 🗂️ feat: Sidebar Icon Toggle & New Chat History Switch
Add collapse-on-active-click for sidebar icons (VSCode-style) and optionally switch to Chat History panel when creating a new chat.
* fix: Address review findings — extract DEFAULT_PANEL constant, add tests
Export DEFAULT_PANEL from ActivePanelContext and use it in ExpandedPanel
instead of hardcoding 'conversations'. Add ExpandedPanel tests covering
NavIconButton collapse toggle and NewChatButton panel switch behaviors.
* fix: Address review — prop-drill setActive, test disabled setting, strengthen assertions
Pass setActive as a prop to NewChatButton instead of subscribing to
ActivePanelContext, avoiding wasted re-renders on every panel switch.
Add negative-path test for switchToHistory=false. Add positive panel
assertions to inactive-icon click tests. Fix import order.
45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
import { createContext, useCallback, useContext, useMemo, useState, ReactNode } from 'react';
|
|
|
|
const STORAGE_KEY = 'side:active-panel';
|
|
export const DEFAULT_PANEL = 'conversations';
|
|
|
|
function getInitialActivePanel(): string {
|
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
return saved ? saved : DEFAULT_PANEL;
|
|
}
|
|
|
|
interface ActivePanelContextType {
|
|
active: string;
|
|
setActive: (id: string) => void;
|
|
}
|
|
|
|
const ActivePanelContext = createContext<ActivePanelContextType | undefined>(undefined);
|
|
|
|
export function ActivePanelProvider({ children }: { children: ReactNode }) {
|
|
const [active, _setActive] = useState<string>(getInitialActivePanel);
|
|
|
|
const setActive = useCallback((id: string) => {
|
|
localStorage.setItem(STORAGE_KEY, id);
|
|
_setActive(id);
|
|
}, []);
|
|
|
|
const value = useMemo(() => ({ active, setActive }), [active, setActive]);
|
|
|
|
return <ActivePanelContext.Provider value={value}>{children}</ActivePanelContext.Provider>;
|
|
}
|
|
|
|
export function useActivePanel() {
|
|
const context = useContext(ActivePanelContext);
|
|
if (context === undefined) {
|
|
throw new Error('useActivePanel must be used within an ActivePanelProvider');
|
|
}
|
|
return context;
|
|
}
|
|
|
|
/** Returns `active` when it matches a known link, otherwise the first link's id. */
|
|
export function resolveActivePanel(active: string, links: { id: string }[]): string {
|
|
if (links.length > 0 && links.some((l) => l.id === active)) {
|
|
return active;
|
|
}
|
|
return links[0]?.id ?? active;
|
|
}
|