diff --git a/memento-note/components/notebooks-list.tsx b/memento-note/components/notebooks-list.tsx index 2ef4752..0d8e021 100644 --- a/memento-note/components/notebooks-list.tsx +++ b/memento-note/components/notebooks-list.tsx @@ -4,7 +4,7 @@ import { useState, useCallback, useRef } from 'react' import Link from 'next/link' import { usePathname, useRouter, useSearchParams } from 'next/navigation' import { cn } from '@/lib/utils' -import { StickyNote, Plus, Tag, Folder, ChevronDown, ChevronRight } from 'lucide-react' +import { StickyNote, Plus, Tag, Folder, ChevronDown, ChevronRight, GripVertical } from 'lucide-react' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { useNotebooks } from '@/context/notebooks-context' import { useNotebookDrag } from '@/context/notebook-drag-context' @@ -35,7 +35,7 @@ export function NotebooksList() { const searchParams = useSearchParams() const router = useRouter() const { t, language } = useLanguage() - const { notebooks, currentNotebook, deleteNotebook, moveNoteToNotebookOptimistic, isLoading } = useNotebooks() + const { notebooks, currentNotebook, deleteNotebook, moveNoteToNotebookOptimistic, updateNotebookOrderOptimistic, isLoading } = useNotebooks() const { draggedNoteId, dragOverNotebookId, dragOver } = useNotebookDrag() const { labels } = useLabels() @@ -46,29 +46,77 @@ export function NotebooksList() { const [expandedNotebook, setExpandedNotebook] = useState(null) const [labelsDialogOpen, setLabelsDialogOpen] = useState(false) + // ── Notebook reorder drag state ── + const draggingNbRef = useRef(null) + const [draggingNbId, setDraggingNbId] = useState(null) + const [overNbId, setOverNbId] = useState(null) + const currentNotebookId = searchParams.get('notebook') - // Handle drop on a notebook + // Handle drop on a notebook (note-to-notebook OR notebook reorder) const handleDrop = useCallback(async (e: React.DragEvent, notebookId: string | null) => { e.preventDefault() e.stopPropagation() + + const sourceNbId = e.dataTransfer.getData('application/x-notebook') const noteId = e.dataTransfer.getData('text/plain') - if (noteId) { + if (sourceNbId && notebookId && sourceNbId !== notebookId) { + // ── Reorder notebooks ── + const currentIds = notebooks.map((nb: Notebook) => nb.id) + const fromIdx = currentIds.indexOf(sourceNbId) + const toIdx = currentIds.indexOf(notebookId) + if (fromIdx !== -1 && toIdx !== -1) { + const newIds = [...currentIds] + newIds.splice(fromIdx, 1) + newIds.splice(toIdx, 0, sourceNbId) + await updateNotebookOrderOptimistic(newIds) + } + } else if (noteId) { + // ── Move note to notebook ── await moveNoteToNotebookOptimistic(noteId, notebookId) } dragOver(null) - }, [moveNoteToNotebookOptimistic, dragOver]) + draggingNbRef.current = null + setDraggingNbId(null) + setOverNbId(null) + }, [notebooks, moveNoteToNotebookOptimistic, updateNotebookOrderOptimistic, dragOver]) // Handle drag over a notebook const handleDragOver = useCallback((e: React.DragEvent, notebookId: string | null) => { e.preventDefault() - dragOver(notebookId) + if (draggingNbRef.current) { + // Notebook reorder mode — just track the insertion target + setOverNbId(notebookId) + } else { + // Note-to-notebook mode + dragOver(notebookId) + } }, [dragOver]) // Handle drag leave const handleDragLeave = useCallback(() => { + if (draggingNbRef.current) { + setOverNbId(null) + } else { + dragOver(null) + } + }, [dragOver]) + + // ── Notebook reorder handlers ── + const handleNotebookDragStart = useCallback((e: React.DragEvent, notebookId: string) => { + e.dataTransfer.setData('application/x-notebook', notebookId) + e.dataTransfer.effectAllowed = 'move' + draggingNbRef.current = notebookId + // Slight delay so the drag ghost renders before opacity change + setTimeout(() => setDraggingNbId(notebookId), 0) + }, []) + + const handleNotebookDragEnd = useCallback(() => { + draggingNbRef.current = null + setDraggingNbId(null) + setOverNbId(null) dragOver(null) }, [dragOver]) @@ -142,7 +190,20 @@ export function NotebooksList() { const NotebookIcon = getNotebookIcon(notebook.icon || 'folder') return ( -
+
handleNotebookDragStart(e, notebook.id)} + onDragEnd={handleNotebookDragEnd} + > + {/* Insertion indicator above this notebook when reordering */} + {overNbId === notebook.id && draggingNbId !== notebook.id && ( +
+ )} {isActive ? ( // Active notebook with expanded labels
handleSelectNotebook(notebook.id)} className={cn( - "pointer-events-auto flex items-center gap-4 px-6 py-3 rounded-e-full me-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors w-full pe-14", + "pointer-events-auto flex items-center gap-3 px-4 py-3 rounded-e-full me-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors w-full pe-14", isDragOver && "opacity-50" )} > + {notebook.name}