UI / frontend /components /Sidebar.tsx
Chan-Y's picture
Initial commit for HF Space
a94ab76
import React, { useState } from 'react';
import { Chat } from '../types';
import { MessageSquarePlus, MessageSquare, Trash2, Moon, Sun, PanelLeftClose, PanelLeft, Search } from 'lucide-react';
import { useTheme } from '../contexts/ThemeContext';
interface SidebarProps {
chats: Chat[];
currentChatId: string | null;
onSelectChat: (chatId: string) => void;
onNewChat: () => void;
onDeleteChat: (chatId: string) => void;
isCollapsed: boolean;
onToggleCollapse: () => void;
}
export const Sidebar: React.FC<SidebarProps> = ({
chats,
currentChatId,
onSelectChat,
onNewChat,
onDeleteChat,
isCollapsed,
onToggleCollapse
}) => {
const { theme, toggleTheme } = useTheme();
const [searchQuery, setSearchQuery] = useState('');
const [isSearchOpen, setIsSearchOpen] = useState(false);
const searchInputRef = React.useRef<HTMLInputElement>(null);
// Sohbetleri arama terimine göre filtrele
const filteredChats = chats.filter(chat =>
chat.title.toLowerCase().includes(searchQuery.toLowerCase())
);
// Arama açıldığında input'a focus yap
React.useEffect(() => {
if (isSearchOpen && searchInputRef.current) {
searchInputRef.current.focus();
}
}, [isSearchOpen]);
return (
<div className={`${isCollapsed ? 'w-14' : 'w-72'} bg-gray-900 dark:bg-gray-100 border-r border-gray-800 dark:border-gray-300 flex flex-col h-screen transition-all duration-300`}>
{/* Header */}
<div className="p-2 space-y-1">
{!isCollapsed ? (
<>
<button
onClick={onToggleCollapse}
className="w-full flex items-center px-3 py-2 text-gray-400 dark:text-gray-600 hover:text-gray-200 dark:hover:text-gray-900 hover:bg-gray-800 dark:hover:bg-gray-200 rounded-lg transition-all"
title="Sidebar'ı Daralt"
>
<PanelLeftClose size={18} />
</button>
<button
onClick={onNewChat}
className="w-full flex items-center gap-2 px-3 py-2.5 text-gray-200 dark:text-gray-900 hover:bg-gray-800 dark:hover:bg-gray-200 rounded-lg transition-all font-medium text-sm"
>
<MessageSquarePlus size={18} />
<span>Yeni sohbet</span>
</button>
{/* Search Input */}
{!isSearchOpen ? (
<button
onClick={() => setIsSearchOpen(true)}
className="w-full flex items-center gap-2 px-3 py-2 text-gray-400 dark:text-gray-600 hover:text-gray-200 dark:hover:text-gray-900 hover:bg-gray-800 dark:hover:bg-gray-200 rounded-lg transition-all text-sm"
>
<Search size={18} />
<span>Sohbet ara</span>
</button>
) : (
<div className="relative">
<Search size={16} className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 dark:text-gray-600" />
<input
ref={searchInputRef}
type="text"
placeholder="Sohbet ara..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onBlur={() => {
if (!searchQuery) {
setIsSearchOpen(false);
}
}}
className="w-full pl-9 pr-3 py-2 bg-gray-800 dark:bg-gray-200 text-gray-200 dark:text-gray-900 placeholder-gray-500 dark:placeholder-gray-600 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-600 dark:focus:ring-gray-400 transition-all"
/>
</div>
)}
</>
) : (
<>
<button
onClick={onToggleCollapse}
className="w-full flex items-center justify-center py-2.5 text-gray-400 dark:text-gray-600 hover:text-gray-200 dark:hover:text-gray-900 hover:bg-gray-800 dark:hover:bg-gray-200 rounded-lg transition-all"
title="Sidebar'ı Genişlet"
>
<PanelLeft size={20} />
</button>
<button
onClick={onNewChat}
className="w-full flex items-center justify-center py-2.5 text-gray-200 dark:text-gray-900 hover:bg-gray-800 dark:hover:bg-gray-200 rounded-lg transition-all"
title="Yeni sohbet"
>
<MessageSquarePlus size={20} />
</button>
</>
)}
</div>
{/* Chat List */}
<div className="flex-1 overflow-y-auto px-2">
{chats.length === 0 ? (
!isCollapsed && (
<div className="p-6 text-center">
<MessageSquare size={32} className="mx-auto mb-3 text-gray-600 dark:text-gray-400" />
<p className="text-sm text-gray-400 dark:text-gray-600">Henüz sohbet yok</p>
</div>
)
) : filteredChats.length === 0 ? (
!isCollapsed && (
<div className="p-6 text-center">
<Search size={32} className="mx-auto mb-3 text-gray-600 dark:text-gray-400" />
<p className="text-sm text-gray-400 dark:text-gray-600">Sonuç bulunamadı</p>
</div>
)
) : (
<div className="space-y-1">
{filteredChats.map((chat) => (
<div
key={chat.id}
className={`group relative flex items-center ${isCollapsed ? 'justify-center py-2.5' : 'gap-2 px-3 py-2'} rounded-lg cursor-pointer transition-all ${
currentChatId === chat.id
? 'bg-gray-800 dark:bg-gray-200'
: 'hover:bg-gray-800 dark:hover:bg-gray-200'
}`}
onClick={() => onSelectChat(chat.id)}
title={isCollapsed ? chat.title : ''}
>
<MessageSquare
size={isCollapsed ? 20 : 16}
className={`flex-shrink-0 ${
currentChatId === chat.id
? 'text-gray-200 dark:text-gray-900'
: 'text-gray-400 dark:text-gray-600'
}`}
/>
{!isCollapsed && (
<>
<div className="flex-1 min-w-0">
<p className="text-sm truncate text-gray-200 dark:text-gray-900">
{chat.title}
</p>
</div>
<button
onClick={(e) => {
e.stopPropagation();
onDeleteChat(chat.id);
}}
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-gray-700 dark:hover:bg-gray-300 text-gray-400 dark:text-gray-600 rounded transition-all"
>
<Trash2 size={14} />
</button>
</>
)}
</div>
))}
</div>
)}
</div>
{/* Footer */}
<div className="p-2 border-t border-gray-800 dark:border-gray-300">
<button
onClick={toggleTheme}
className={`w-full flex items-center ${isCollapsed ? 'justify-center py-2.5' : 'gap-2 px-3 py-2'} text-sm text-gray-400 dark:text-gray-600 hover:text-gray-200 dark:hover:text-gray-900 hover:bg-gray-800 dark:hover:bg-gray-200 rounded-lg transition-colors`}
title={isCollapsed ? (theme === 'light' ? 'Karanlık tema' : 'Aydınlık tema') : ''}
>
{theme === 'light' ? (
<>
<Moon size={isCollapsed ? 20 : 16} />
{!isCollapsed && <span>Karanlık tema</span>}
</>
) : (
<>
<Sun size={isCollapsed ? 20 : 16} />
{!isCollapsed && <span>Aydınlık tema</span>}
</>
)}
</button>
</div>
</div>
);
};