| import * as ScrollArea from '@radix-ui/react-scroll-area' |
| import { AnimatePresence, motion } from 'framer-motion' |
| import { PanelLeftClose, Search, SquarePen } from 'lucide-react' |
| import Button from '../UI/Button' |
| import SessionItem from './SessionItem' |
|
|
| export default function Sidebar({ |
| open, |
| sessions, |
| loading, |
| currentSessionId, |
| searchValue, |
| onSearchChange, |
| onSelectSession, |
| onDeleteSession, |
| onNewSession, |
| onClose, |
| }) { |
| return ( |
| <> |
| <AnimatePresence> |
| {open ? ( |
| <motion.div |
| className="fixed inset-0 z-30 bg-black/35 lg:hidden" |
| initial={{ opacity: 0 }} |
| animate={{ opacity: 1 }} |
| exit={{ opacity: 0 }} |
| onClick={onClose} |
| /> |
| ) : null} |
| </AnimatePresence> |
| |
| <aside |
| className={`fixed inset-y-0 left-0 z-40 flex w-[min(18rem,88vw)] max-w-[288px] flex-col border-r border-border bg-panel transition-transform duration-300 ${ |
| open ? 'translate-x-0' : '-translate-x-full' |
| }`} |
| > |
| <div className="space-y-3 border-b border-border p-3"> |
| <div className="flex items-center justify-between gap-2"> |
| <p className="truncate px-1 text-sm font-semibold text-foreground">OwnGPT</p> |
| <button |
| type="button" |
| onClick={onClose} |
| className="rounded-lg p-2 text-muted-foreground transition hover:bg-secondary hover:text-foreground lg:hidden" |
| aria-label="Close sidebar" |
| > |
| <PanelLeftClose className="h-4 w-4" /> |
| </button> |
| </div> |
| |
| <Button variant="ghost" className="w-full justify-start" onClick={onNewSession}> |
| <SquarePen className="h-4 w-4" /> |
| New chat |
| </Button> |
| |
| <label className="field flex h-10 items-center gap-2 rounded-lg px-3"> |
| <Search className="h-4 w-4 text-muted-foreground" /> |
| <input |
| value={searchValue} |
| onChange={(event) => onSearchChange(event.target.value)} |
| placeholder="Search chats" |
| className="w-full bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground" |
| /> |
| </label> |
| </div> |
| |
| <div className="px-4 pb-2 pt-3"> |
| <p className="text-xs font-medium text-muted-foreground">Chats</p> |
| </div> |
| |
| <ScrollArea.Root className="flex-1 overflow-hidden px-2"> |
| <ScrollArea.Viewport className="h-full w-full"> |
| <div className="space-y-0.5 pb-3"> |
| {loading ? ( |
| Array.from({ length: 6 }).map((_, index) => ( |
| <div |
| key={index} |
| className="h-10 animate-pulse rounded-lg bg-secondary" |
| /> |
| )) |
| ) : sessions.length ? ( |
| sessions.map((session) => ( |
| <SessionItem |
| key={session.session_id} |
| session={session} |
| active={session.session_id === currentSessionId} |
| onSelect={onSelectSession} |
| onDelete={onDeleteSession} |
| /> |
| )) |
| ) : ( |
| <div className="px-3 py-4 text-sm text-muted-foreground"> |
| No chats yet |
| </div> |
| )} |
| </div> |
| </ScrollArea.Viewport> |
| <ScrollArea.Scrollbar orientation="vertical" className="w-2.5 bg-transparent p-0.5"> |
| <ScrollArea.Thumb className="rounded-full bg-border/80" /> |
| </ScrollArea.Scrollbar> |
| </ScrollArea.Root> |
| </aside> |
| </> |
| ) |
| } |
|
|