import { useState, useRef, useCallback, memo } from 'react' import type { VaultEntry, FolderNode, SidebarSelection, ViewFile } from '../types' import { KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent, } from '@dnd-kit/core' import { sortableKeyboardCoordinates } from '@dnd-kit/sortable' import { FolderTree } from './FolderTree' import { applyCustomization, computeReorder, useEntryCounts, useOutsideClick, useSidebarCollapsed, useSidebarSections, } from './sidebar/sidebarHooks' import { ContextMenuOverlay, CustomizeOverlay, FavoritesSection, type SidebarSectionProps, SidebarTitleBar, SidebarTopNav, TypesSection, ViewsSection, } from './sidebar/SidebarSections' interface SidebarProps { entries: VaultEntry[] selection: SidebarSelection onSelect: (selection: SidebarSelection) => void onSelectNote?: (entry: VaultEntry) => void onCreateType?: (type: string) => void onCreateNewType?: () => void onCustomizeType?: (typeName: string, icon: string, color: string) => void onUpdateTypeTemplate?: (typeName: string, template: string) => void onReorderSections?: (orderedTypes: { typeName: string; order: number }[]) => void onRenameSection?: (typeName: string, label: string) => void onToggleTypeVisibility?: (typeName: string) => void onSelectFavorite?: (entry: VaultEntry) => void onReorderFavorites?: (orderedPaths: string[]) => void views?: ViewFile[] onCreateView?: () => void onEditView?: (filename: string) => void onDeleteView?: (filename: string) => void folders?: FolderNode[] onCreateFolder?: (name: string) => void showInbox?: boolean inboxCount?: number onCollapse?: () => void } export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onCustomizeType, onUpdateTypeTemplate, onReorderSections, onRenameSection, onToggleTypeVisibility, onSelectFavorite, onReorderFavorites, views = [], onCreateView, onEditView, onDeleteView, folders = [], onCreateFolder, showInbox = true, inboxCount = 0, onCollapse, onCreateNewType, }: SidebarProps) { const [customizeTarget, setCustomizeTarget] = useState(null) const [contextMenuPos, setContextMenuPos] = useState<{ x: number; y: number } | null>(null) const [contextMenuType, setContextMenuType] = useState(null) const [renamingType, setRenamingType] = useState(null) const [renameInitialValue, setRenameInitialValue] = useState('') const [showCustomize, setShowCustomize] = useState(false) const contextMenuRef = useRef(null) const popoverRef = useRef(null) const customizeRef = useRef(null) const { typeEntryMap, allSectionGroups, visibleSections, sectionIds } = useSidebarSections(entries) const { activeCount, archivedCount } = useEntryCounts(entries) const { collapsed: groupCollapsed, toggle: toggleGroup } = useSidebarCollapsed() const isSectionVisible = useCallback((type: string) => typeEntryMap[type]?.visible !== false, [typeEntryMap]) const toggleVisibility = useCallback((type: string) => onToggleTypeVisibility?.(type), [onToggleTypeVisibility]) const closeContextMenu = useCallback(() => { setContextMenuPos(null) setContextMenuType(null) }, []) const closeCustomize = useCallback(() => setShowCustomize(false), []) const closeCustomizeTarget = useCallback(() => setCustomizeTarget(null), []) useOutsideClick(customizeRef, showCustomize, closeCustomize) useOutsideClick(contextMenuRef, !!contextMenuPos, closeContextMenu) useOutsideClick(popoverRef, !!customizeTarget, closeCustomizeTarget) const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ) const handleDragEnd = useCallback((event: DragEndEvent) => { const { active, over } = event if (!over || active.id === over.id) return const reordered = computeReorder(sectionIds, active.id as string, over.id as string) if (reordered) onReorderSections?.(reordered.map((typeName, order) => ({ typeName, order }))) }, [sectionIds, onReorderSections]) const handleContextMenu = useCallback((event: React.MouseEvent, type: string) => { event.preventDefault() event.stopPropagation() setContextMenuPos({ x: event.clientX, y: event.clientY }) setContextMenuType(type) }, []) const cancelRename = useCallback(() => setRenamingType(null), []) const handleStartRename = useCallback((type: string) => { closeContextMenu() const group = allSectionGroups.find((sectionGroup) => sectionGroup.type === type) setRenameInitialValue(group?.label ?? type) setRenamingType(type) }, [allSectionGroups, closeContextMenu]) const handleRenameSubmit = useCallback((value: string) => { if (renamingType) onRenameSection?.(renamingType, value) setRenamingType(null) }, [renamingType, onRenameSection]) const handleCustomize = useCallback((prop: 'icon' | 'color', value: string) => { applyCustomization(customizeTarget, typeEntryMap, onCustomizeType, prop, value) }, [customizeTarget, typeEntryMap, onCustomizeType]) const handleChangeTemplate = useCallback((template: string) => { if (customizeTarget) onUpdateTypeTemplate?.(customizeTarget, template) }, [customizeTarget, onUpdateTypeTemplate]) const sectionProps: SidebarSectionProps = { entries, selection, onSelect, onContextMenu: handleContextMenu, renamingType, renameInitialValue, onRenameSubmit: handleRenameSubmit, onRenameCancel: cancelRename, } const hasFavorites = entries.some((entry) => entry.favorite && !entry.archived) const hasViews = views.length > 0 || !!onCreateView return ( ) })