import { useCallback, memo } from 'react' import type { FolderCreationParent, FolderNode, SidebarSelection, VaultEntry, ViewDefinition, 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 { computeReorder, useEntryCounts, useSidebarCollapsed, useSidebarSections, } from './sidebar/sidebarHooks' import { ContextMenuOverlay, CustomizeOverlay, FavoritesSection, type SidebarSectionProps, SidebarTitleBar, SidebarTopNav, TypesSection, ViewsSection, } from './sidebar/SidebarSections' import { SidebarCreatableLoadingSection, SidebarFavoritesLoadingSection, SidebarTypesLoadingSection, } from './sidebar/SidebarLoadingSections' import { useSidebarTypeInteractions } from './sidebar/useSidebarTypeInteractions' import type { AppLocale } from '../lib/i18n' import type { FolderFileActions } from '../hooks/useFileActions' import type { AllNotesFileVisibility } from '../utils/allNotesFileVisibility' import { isTypeSectionVisible } from '../utils/typeVisibility' 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 onDeleteType?: (typeName: string) => void onToggleTypeVisibility?: (typeName: string, typeEntryPath?: string) => void onSelectFavorite?: (entry: VaultEntry) => void onReorderFavorites?: (orderedPaths: string[]) => void views?: ViewFile[] onCreateView?: () => void onEditView?: (filename: string, rootPath?: string) => void onDeleteView?: (filename: string, rootPath?: string) => void onUpdateViewDefinition?: (filename: string, patch: Partial, rootPath?: string) => void onReorderViews?: (orderedFilenames: string[]) => void folders?: FolderNode[] onCreateFolder?: (name: string, parent?: FolderCreationParent) => Promise | boolean onRenameFolder?: (folderPath: string, nextName: string) => Promise | boolean onDeleteFolder?: (folderPath: string) => void folderFileActions?: FolderFileActions renamingFolderPath?: string | null onStartRenameFolder?: (folderPath: string) => void onCancelRenameFolder?: () => void vaultRootPath?: string workspaceOrder?: readonly string[] showInbox?: boolean inboxCount?: number allNotesFileVisibility?: AllNotesFileVisibility pluralizeTypeLabels?: boolean locale?: AppLocale onCollapse?: () => void onGoBack?: () => void onGoForward?: () => void canGoBack?: boolean canGoForward?: boolean loading?: boolean } interface SidebarNavigationProps extends Pick< SidebarProps, | 'entries' | 'selection' | 'onSelect' | 'onSelectFavorite' | 'onReorderFavorites' | 'views' | 'onCreateView' | 'onEditView' | 'onDeleteView' | 'onUpdateViewDefinition' | 'onReorderViews' | 'folders' | 'onCreateFolder' | 'onRenameFolder' | 'onDeleteFolder' | 'folderFileActions' | 'renamingFolderPath' | 'onStartRenameFolder' | 'onCancelRenameFolder' | 'vaultRootPath' | 'workspaceOrder' | 'showInbox' | 'inboxCount' | 'onCreateNewType' | 'locale' | 'loading' > { activeCount: number archivedCount: number groupCollapsed: ReturnType['collapsed'] toggleGroup: ReturnType['toggle'] visibleSections: ReturnType['visibleSections'] allSectionGroups: ReturnType['allSectionGroups'] sectionIds: string[] sensors: ReturnType handleDragEnd: (event: DragEndEvent) => void sectionProps: SidebarSectionProps typeInteractions: ReturnType isSectionVisible: (type: string) => boolean toggleVisibility: (type: string, typeEntryPath?: string) => void } type SidebarFavoritesNavigationProps = Pick< SidebarNavigationProps, | 'loading' | 'entries' | 'selection' | 'onSelect' | 'onSelectFavorite' | 'onReorderFavorites' | 'groupCollapsed' | 'toggleGroup' | 'locale' > type SidebarViewsNavigationProps = Pick< SidebarNavigationProps, | 'loading' | 'views' | 'selection' | 'onSelect' | 'onCreateView' | 'onEditView' | 'onDeleteView' | 'onUpdateViewDefinition' | 'onReorderViews' | 'groupCollapsed' | 'toggleGroup' | 'sensors' | 'entries' | 'locale' > type SidebarTypesNavigationProps = Pick< SidebarNavigationProps, | 'loading' | 'entries' | 'visibleSections' | 'allSectionGroups' | 'sectionIds' | 'sensors' | 'handleDragEnd' | 'sectionProps' | 'groupCollapsed' | 'toggleGroup' | 'typeInteractions' | 'isSectionVisible' | 'toggleVisibility' | 'onCreateNewType' | 'workspaceOrder' | 'locale' > type SidebarFoldersNavigationProps = Pick< SidebarNavigationProps, | 'loading' | 'folders' | 'selection' | 'onSelect' | 'onCreateFolder' | 'onRenameFolder' | 'onDeleteFolder' | 'folderFileActions' | 'renamingFolderPath' | 'onStartRenameFolder' | 'onCancelRenameFolder' | 'vaultRootPath' | 'groupCollapsed' | 'toggleGroup' | 'locale' > function SidebarFavoritesNavigation({ loading, entries, selection, onSelect, onSelectFavorite, onReorderFavorites, groupCollapsed, toggleGroup, locale, }: SidebarFavoritesNavigationProps) { if (loading) { return ( toggleGroup('favorites')} /> ) } return (
toggleGroup('favorites')} />
) } function SidebarViewsNavigation({ loading, views, selection, onSelect, onCreateView, onEditView, onDeleteView, onUpdateViewDefinition, onReorderViews, groupCollapsed, toggleGroup, sensors, entries, locale, }: SidebarViewsNavigationProps) { if (loading) { return ( toggleGroup('views')} /> ) } return ( toggleGroup('views')} onCreateView={onCreateView} onEditView={onEditView} onDeleteView={onDeleteView} onUpdateViewDefinition={onUpdateViewDefinition} onReorderViews={onReorderViews} sensors={sensors} entries={entries} locale={locale} /> ) } function SidebarTypesNavigation({ loading, visibleSections, allSectionGroups, sectionIds, sensors, handleDragEnd, sectionProps, groupCollapsed, toggleGroup, typeInteractions, isSectionVisible, toggleVisibility, onCreateNewType, workspaceOrder, locale, }: SidebarTypesNavigationProps) { if (loading) { return ( toggleGroup('sections')} /> ) } return ( toggleGroup('sections')} showCustomize={typeInteractions.showCustomize} setShowCustomize={typeInteractions.setShowCustomize} isSectionVisible={isSectionVisible} toggleVisibility={toggleVisibility} onCreateNewType={onCreateNewType} customizeRef={typeInteractions.customizeRef} workspaceOrder={workspaceOrder} locale={locale} /> ) } function SidebarFoldersNavigation({ loading, folders, selection, onSelect, onCreateFolder, onRenameFolder, onDeleteFolder, folderFileActions, renamingFolderPath, onStartRenameFolder, onCancelRenameFolder, vaultRootPath, groupCollapsed, toggleGroup, locale, }: SidebarFoldersNavigationProps) { if (loading) { return ( toggleGroup('folders')} /> ) } return ( toggleGroup('folders')} vaultRootPath={vaultRootPath} /> ) } function SidebarTopNavigation(props: SidebarNavigationProps) { return ( <> {(props.loading || props.entries.some((entry) => entry.favorite && !entry.archived)) && ( )} ) } function SidebarViewAndTypeNavigation(props: SidebarNavigationProps) { const views = props.views ?? [] const hasViews = props.loading || views.length > 0 || !!props.onCreateView return ( <> {hasViews && ( )} ) } function SidebarNavigation(props: SidebarNavigationProps) { return ( ) } function useSidebarDndSensors() { return useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ) } function invokeTypeVisibilityToggle( onToggleTypeVisibility: SidebarProps['onToggleTypeVisibility'], type: string, typeEntryPath?: string, ) { if (typeEntryPath) { onToggleTypeVisibility?.(type, typeEntryPath) return } onToggleTypeVisibility?.(type) } function useSidebarRuntime({ entries, selection, onSelect, onSelectNote, onCustomizeType, onUpdateTypeTemplate, onReorderSections, onRenameSection, onDeleteType, onToggleTypeVisibility, allNotesFileVisibility, pluralizeTypeLabels = true, locale = 'en', }: SidebarProps) { const { typeEntryMap, typeVisibility, allSectionGroups, visibleSections, sectionIds, } = useSidebarSections(entries, pluralizeTypeLabels) const { activeCount, archivedCount } = useEntryCounts(entries, allNotesFileVisibility) const { collapsed: groupCollapsed, toggle: toggleGroup } = useSidebarCollapsed() const typeInteractions = useSidebarTypeInteractions({ allSectionGroups, typeEntryMap, onCustomizeType, onUpdateTypeTemplate, onRenameSection, onDeleteType, }) const isSectionVisible = useCallback((type: string) => ( isTypeSectionVisible(entries, type, typeVisibility) ), [entries, typeVisibility]) const toggleVisibility = useCallback((type: string, typeEntryPath?: string) => { invokeTypeVisibilityToggle(onToggleTypeVisibility, type, typeEntryPath) }, [onToggleTypeVisibility]) const selectTypeNote = useCallback((type: string) => { const typeEntry = (Reflect.get(typeEntryMap, type) as VaultEntry | undefined) ?? (Reflect.get(typeEntryMap, type.toLowerCase()) as VaultEntry | undefined) if (typeEntry) onSelectNote?.(typeEntry) }, [onSelectNote, typeEntryMap]) const sensors = useSidebarDndSensors() 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 sectionProps: SidebarSectionProps = { entries, selection, locale, onSelect, onContextMenu: typeInteractions.handleContextMenu, renamingType: typeInteractions.renamingType, renameInitialValue: typeInteractions.renameInitialValue, onRenameSubmit: typeInteractions.handleRenameSubmit, onRenameCancel: typeInteractions.cancelRename, onStartRename: typeInteractions.handleStartRename, onSelectTypeNote: selectTypeNote, } return { activeCount, allSectionGroups, archivedCount, groupCollapsed, handleDragEnd, isSectionVisible, sectionIds, sectionProps, sensors, toggleGroup, toggleVisibility, typeEntryMap, typeVisibility, typeInteractions, visibleSections, } } function SidebarRuntimeNavigation({ props, runtime, }: { props: SidebarProps runtime: ReturnType }) { return ( ) } function SidebarInteractionOverlays({ locale, runtime, }: { locale: AppLocale runtime: ReturnType }) { return ( <> ) } export const Sidebar = memo(function Sidebar(props: SidebarProps) { const locale = props.locale ?? 'en' const runtime = useSidebarRuntime(props) return ( ) })