import { useMemo, useState } from 'react' import { api, type DocSummary, type Tag, type TagColor } from '../../api/client' import { DocListItem } from './DocListItem' import { SearchBox } from './SearchBox' import { TagChip } from './TagChip' interface Props { docs: DocSummary[] roster: Tag[] selectedId: string | null onSelect: (id: string) => void onCreate: () => void onDelete: (id: string) => void onDuplicate: (id: string) => void onToggleTag: (docId: string, tag: Tag) => void onCreateTag: (docId: string, name: string, color: TagColor) => void } // Sidebar sort orders. 'recent' keeps the server's updated_at-desc ordering. type SortMode = 'recent' | 'title' | 'longest' const SORTS: { value: SortMode; label: string }[] = [ { value: 'recent', label: '最近 · Recent' }, { value: 'title', label: '标题 · Title' }, { value: 'longest', label: '字数 · Longest' }, ] // DocList is the sidebar: a cross-document search box, a tag filter bar, the New // button, and the document browser (each row showing its tag chips). export function DocList({ docs, roster, selectedId, onSelect, onCreate, onDelete, onDuplicate, onToggleTag, onCreateTag, }: Props) { // Active tag filter (null = show all). Cleared automatically if the tag // disappears from the roster. const [filterId, setFilterId] = useState(null) const activeFilter = filterId && roster.some((t) => t.id === filterId) ? filterId : null const [sort, setSort] = useState('recent') const filtered = useMemo(() => { const base = activeFilter ? docs.filter((d) => d.tags.some((t) => t.id === activeFilter)) : docs if (sort === 'recent') return base // already updated_at-desc from the server const arr = [...base] if (sort === 'title') { arr.sort((a, b) => (a.title || 'Untitled').localeCompare(b.title || 'Untitled', undefined, { sensitivity: 'base' }), ) } else { arr.sort((a, b) => b.word_count - a.word_count) } return arr }, [docs, activeFilter, sort]) // Only surface tags that are actually in use, so the filter bar stays tidy. const usedTags = useMemo(() => roster.filter((t) => (t.doc_count ?? 0) > 0), [roster]) return ( ) }