import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { LayoutGrid, Folder, Plus, Trash2, Edit2, Archive, FolderOpen, Settings as SettingsIcon } from 'lucide-react'; import type { Collection } from '../types'; import clsx from 'clsx'; import { ConfirmModal } from './ConfirmModal'; import { Logo } from './Logo'; interface SidebarProps { collections: Collection[]; selectedCollectionId: string | null | undefined; onSelectCollection: (id: string | null | undefined) => void; onCreateCollection: (name: string) => void; onEditCollection: (id: string, name: string) => void; onDeleteCollection: (id: string) => void; onDrop?: (e: React.DragEvent, collectionId: string | null) => void; } interface SidebarItemProps { id: string | null; // null for Unorganized icon: React.ReactNode; label: string; isActive: boolean; onClick: () => void; onDoubleClick?: () => void; onContextMenu?: (e: React.MouseEvent) => void; extraAction?: React.ReactNode; isEditing?: boolean; editValue?: string; onEditChange?: (val: string) => void; onEditSubmit?: (e: React.FormEvent) => void; onEditBlur?: () => void; onDrop?: (e: React.DragEvent, collectionId: string | null) => void; } const SidebarItem: React.FC = ({ id, icon, label, isActive, onClick, onDoubleClick, onContextMenu, extraAction, isEditing, editValue, onEditChange, onEditSubmit, onEditBlur, onDrop }) => { const [isDragOver, setIsDragOver] = useState(false); return (
{isEditing ? (
onEditChange?.(e.target.value)} className="w-full px-3 py-2 text-sm bg-white dark:bg-neutral-800 border-2 border-black dark:border-neutral-700 rounded-lg shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)] outline-none font-bold text-slate-900 dark:text-white" onBlur={onEditBlur} />
) : (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } }} onDoubleClick={onDoubleClick} onContextMenu={onContextMenu} onDragOver={(e) => { e.preventDefault(); setIsDragOver(true); }} onDragLeave={() => setIsDragOver(false)} onDrop={(e) => { e.preventDefault(); setIsDragOver(false); onDrop?.(e, id); }} className={clsx( "w-full flex items-center gap-3 px-3 py-2.5 text-sm font-bold rounded-lg transition-all duration-200 border-2 group cursor-pointer outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:focus-visible:ring-2 dark:focus-visible:ring-neutral-500", isActive || isDragOver ? "bg-indigo-50 dark:bg-neutral-800 text-indigo-900 dark:text-neutral-200 border-black dark:border-neutral-700 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)] -translate-y-0.5" : "text-slate-600 dark:text-neutral-400 border-transparent hover:bg-slate-50 dark:hover:bg-neutral-800 hover:border-black dark:hover:border-neutral-700 hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:hover:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)] hover:-translate-y-0.5" )} > {icon} {label} {extraAction && (
{extraAction}
)}
)}
); }; export const Sidebar: React.FC = ({ collections, selectedCollectionId, onSelectCollection, onCreateCollection, onEditCollection, onDeleteCollection, onDrop }) => { const [isCreating, setIsCreating] = useState(false); const [newCollectionName, setNewCollectionName] = useState(''); const [editingId, setEditingId] = useState(null); const [editName, setEditName] = useState(''); const [contextMenu, setContextMenu] = useState<{ x: number; y: number; type: 'item' | 'background'; id?: string } | null>(null); const [collectionToDelete, setCollectionToDelete] = useState(null); const [isTrashDragOver, setIsTrashDragOver] = useState(false); const navigate = useNavigate(); useEffect(() => { const handleClickOutside = () => setContextMenu(null); document.addEventListener('click', handleClickOutside); return () => document.removeEventListener('click', handleClickOutside); }, []); const handleCreateSubmit = (e: React.FormEvent) => { e.preventDefault(); if (newCollectionName.trim()) { onCreateCollection(newCollectionName); setNewCollectionName(''); setIsCreating(false); } }; const handleEditSubmit = (e: React.FormEvent) => { e.preventDefault(); if (editingId && editName.trim()) { onEditCollection(editingId, editName); setEditingId(null); } }; const handleItemContextMenu = (e: React.MouseEvent, id: string) => { e.preventDefault(); e.stopPropagation(); setContextMenu({ x: e.clientX, y: e.clientY, type: 'item', id }); }; const handleBackgroundContextMenu = (e: React.MouseEvent) => { e.preventDefault(); setContextMenu({ x: e.clientX, y: e.clientY, type: 'background' }); }; return ( <>

ExcaliDash BETA

{/* Context Menu */} {contextMenu && (
setContextMenu(null)} onContextMenu={(e) => { e.preventDefault(); setContextMenu(null); }} >
e.stopPropagation()} > {contextMenu.type === 'item' && contextMenu.id ? ( <> ) : ( )}
)} { if (collectionToDelete) { onDeleteCollection(collectionToDelete); setCollectionToDelete(null); } }} onCancel={() => setCollectionToDelete(null)} /> ); };