'use client'; import Link from 'next/link'; import { useEffect, useMemo, useState } from 'react'; import { useDrag, useDrop } from 'react-dnd'; import type { DocumentListDocument, Folder } from '@/types/documents'; import { PDFIcon, EPUBIcon, FileIcon } from '@/components/icons/Icons'; import { FolderIcon, ChevronRightSmall } from '../window/finderIcons'; import { DocumentPreview } from '@/components/doclist/DocumentPreview'; import { formatDocumentSize } from '@/components/doclist/formatSize'; import { useDocumentSelection } from '../dnd/DocumentSelectionContext'; import { DND_DOCUMENT, type DocumentDragItem } from '../dnd/dndTypes'; interface ColumnsViewProps { folders: Folder[]; unfolderedDocs: DocumentListDocument[]; onDeleteDoc: (doc: DocumentListDocument) => void; onDropOnFolder: (folderId: string, item: DocumentDragItem) => void; onMergeIntoFolder: (sources: DocumentListDocument[], target: DocumentListDocument) => void; } type Selected = | { kind: 'folder'; id: string } | { kind: 'doc'; doc: DocumentListDocument } | null; function KindIcon({ doc }: { doc: DocumentListDocument }) { if (doc.type === 'pdf') return ; if (doc.type === 'epub') return ; return ; } function ColumnDocRow({ doc, active, onClick, onMergeIntoFolder, }: { doc: DocumentListDocument; active: boolean; onClick: () => void; onMergeIntoFolder: (sources: DocumentListDocument[], target: DocumentListDocument) => void; }) { const selection = useDocumentSelection(); const isSelected = selection.isSelected(doc); const isInFolder = Boolean(doc.folderId); const [{ isDragging }, dragRef] = useDrag(() => ({ type: DND_DOCUMENT, item: () => { const sel = selection.getSelectedDocs(); const dragging = isSelected && sel.length > 1 ? sel : [doc]; if (!isSelected) selection.replace([doc]); return { ids: dragging.map((d) => d.id), docs: dragging, fromFolderId: doc.folderId }; }, collect: (m) => ({ isDragging: m.isDragging() }), }), [doc, isSelected]); const [{ isOver, canDrop }, dropRef] = useDrop(() => ({ accept: DND_DOCUMENT, canDrop: (item) => !isInFolder && !item.ids.includes(doc.id), drop: (item) => onMergeIntoFolder(item.docs, doc), collect: (m) => ({ isOver: m.isOver({ shallow: true }), canDrop: m.canDrop() }), }), [doc, isInFolder, onMergeIntoFolder]); const setRefs = (node: HTMLDivElement | null) => { dragRef(node); dropRef(node); }; return (
{doc.name}
); } function ColumnFolderRow({ folder, active, onClick, onDropOnFolder, }: { folder: Folder; active: boolean; onClick: () => void; onDropOnFolder: (folderId: string, item: DocumentDragItem) => void; }) { const [{ isOver, canDrop }, dropRef] = useDrop(() => ({ accept: DND_DOCUMENT, canDrop: (item) => item.docs.some((d) => d.folderId !== folder.id), drop: (item) => onDropOnFolder(folder.id, item), collect: (m) => ({ isOver: m.isOver({ shallow: true }), canDrop: m.canDrop() }), }), [folder.id, onDropOnFolder]); return (
} onClick={onClick} className={ 'group flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border transform transition-all duration-200 ease-out cursor-pointer text-left hover:scale-[1.01] ' + (active ? 'border-accent bg-offbase text-accent' : 'border-transparent text-foreground hover:border-accent hover:bg-offbase hover:text-accent') + (isOver && canDrop ? ' ring-1 ring-accent' : '') } > {folder.name} {folder.documents.length}
); } function Column({ children, title }: { children: React.ReactNode; title?: string }) { return (
{title && (
{title}
)}
{children}
); } export function ColumnsView({ folders, unfolderedDocs, onDeleteDoc, onDropOnFolder, onMergeIntoFolder, }: ColumnsViewProps) { const { setVisibleOrder } = useDocumentSelection(); const [selected, setSelected] = useState(null); const allDocs = useMemo( () => [...folders.flatMap((f) => f.documents), ...unfolderedDocs], [folders, unfolderedDocs], ); useEffect(() => { setVisibleOrder(allDocs); }, [allDocs, setVisibleOrder]); // Keep selection valid and default to the first document when entering this view. useEffect(() => { if (allDocs.length === 0) { if (selected !== null) setSelected(null); return; } if (!selected) { setSelected({ kind: 'doc', doc: allDocs[0] }); return; } if (selected.kind === 'folder') { if (!folders.find((f) => f.id === selected.id)) { setSelected({ kind: 'doc', doc: allDocs[0] }); } return; } const selectedStillExists = allDocs.some( (d) => d.id === selected.doc.id && d.type === selected.doc.type, ); if (!selectedStillExists) { setSelected({ kind: 'doc', doc: allDocs[0] }); } }, [allDocs, folders, selected]); const selectedFolder = selected?.kind === 'folder' ? folders.find((f) => f.id === selected.id) : undefined; return (
{folders.map((f) => ( setSelected({ kind: 'folder', id: f.id })} onDropOnFolder={onDropOnFolder} /> ))} {unfolderedDocs.map((d) => ( setSelected({ kind: 'doc', doc: d })} onMergeIntoFolder={onMergeIntoFolder} /> ))} {selectedFolder && ( {selectedFolder.documents.length === 0 && (

Empty folder

)} {selectedFolder.documents.map((d) => ( setSelected({ kind: 'doc', doc: d })} onMergeIntoFolder={onMergeIntoFolder} /> ))}
)} {selected?.kind === 'doc' && (

{selected.doc.name}

{selected.doc.type.toUpperCase()} • {formatDocumentSize(selected.doc.size)}

Open
)}
); }