diff --git a/src/renderer/src/pages/notes/NotesPage.tsx b/src/renderer/src/pages/notes/NotesPage.tsx index 0bad446acf..aa3555cead 100644 --- a/src/renderer/src/pages/notes/NotesPage.tsx +++ b/src/renderer/src/pages/notes/NotesPage.tsx @@ -385,21 +385,25 @@ const NotesPage: FC = () => { }, [activeFilePath]) // 获取目标文件夹路径(选中文件夹或根目录) - const getTargetFolderPath = useCallback(() => { - if (selectedFolderId) { - const selectedNode = findNode(notesTree, selectedFolderId) - if (selectedNode && selectedNode.type === 'folder') { - return selectedNode.externalPath + const getTargetFolderPath = useCallback( + (targetFolderId?: string) => { + const folderId = targetFolderId || selectedFolderId + if (folderId) { + const selectedNode = findNode(notesTree, folderId) + if (selectedNode && selectedNode.type === 'folder') { + return selectedNode.externalPath + } } - } - return notesPath // 默认返回根目录 - }, [selectedFolderId, notesTree, notesPath]) + return notesPath // 默认返回根目录 + }, + [selectedFolderId, notesTree, notesPath] + ) // 创建文件夹 const handleCreateFolder = useCallback( - async (name: string) => { + async (name: string, targetFolderId?: string) => { try { - const targetPath = getTargetFolderPath() + const targetPath = getTargetFolderPath(targetFolderId) if (!targetPath) { throw new Error('No folder path selected') } @@ -415,11 +419,11 @@ const NotesPage: FC = () => { // 创建笔记 const handleCreateNote = useCallback( - async (name: string) => { + async (name: string, targetFolderId?: string) => { try { isCreatingNoteRef.current = true - const targetPath = getTargetFolderPath() + const targetPath = getTargetFolderPath(targetFolderId) if (!targetPath) { throw new Error('No folder path selected') } diff --git a/src/renderer/src/pages/notes/NotesSidebar.tsx b/src/renderer/src/pages/notes/NotesSidebar.tsx index e7f3db9494..af68709230 100644 --- a/src/renderer/src/pages/notes/NotesSidebar.tsx +++ b/src/renderer/src/pages/notes/NotesSidebar.tsx @@ -34,8 +34,8 @@ import { useSelector } from 'react-redux' import styled from 'styled-components' interface NotesSidebarProps { - onCreateFolder: (name: string, parentId?: string) => void - onCreateNote: (name: string, parentId?: string) => void + onCreateFolder: (name: string, targetFolderId?: string) => void + onCreateNote: (name: string, targetFolderId?: string) => void onSelectNode: (node: NotesTreeNode) => void onDeleteNode: (nodeId: string) => void onRenameNode: (nodeId: string, newName: string) => void @@ -71,6 +71,8 @@ interface TreeNodeProps { onDrop: (e: React.DragEvent, node: NotesTreeNode) => void onDragEnd: () => void renderChildren?: boolean // 控制是否渲染子节点 + openDropdownKey: string | null + onDropdownOpenChange: (key: string | null) => void } const TreeNode = memo( @@ -94,7 +96,9 @@ const TreeNode = memo( onDragLeave, onDrop, onDragEnd, - renderChildren = true + renderChildren = true, + openDropdownKey, + onDropdownOpenChange }) => { const { t } = useTranslation() @@ -119,8 +123,12 @@ const TreeNode = memo( return (
- -
+ onDropdownOpenChange(open ? node.id : null)}> +
e.stopPropagation()}> ( onDrop={onDrop} onDragEnd={onDragEnd} renderChildren={renderChildren} + openDropdownKey={openDropdownKey} + onDropdownOpenChange={onDropdownOpenChange} /> ))}
@@ -244,6 +254,7 @@ const NotesSidebar: FC = ({ const [isShowSearch, setIsShowSearch] = useState(false) const [searchKeyword, setSearchKeyword] = useState('') const [isDragOverSidebar, setIsDragOverSidebar] = useState(false) + const [openDropdownKey, setOpenDropdownKey] = useState(null) const dragNodeRef = useRef(null) const scrollbarRef = useRef(null) @@ -571,6 +582,28 @@ const NotesSidebar: FC = ({ }) } + if (node.type === 'folder') { + baseMenuItems.push( + { + label: t('notes.new_note'), + key: 'new_note', + icon: , + onClick: () => { + onCreateNote(t('notes.untitled_note'), node.id) + } + }, + { + label: t('notes.new_folder'), + key: 'new_folder', + icon: , + onClick: () => { + onCreateFolder(t('notes.untitled_folder'), node.id) + } + }, + { type: 'divider' } + ) + } + baseMenuItems.push( { label: t('notes.rename'), @@ -674,7 +707,9 @@ const NotesSidebar: FC = ({ handleDeleteNode, renamingNodeIds, handleAutoRename, - exportMenuOptions + exportMenuOptions, + onCreateNote, + onCreateFolder ] ) @@ -755,6 +790,23 @@ const NotesSidebar: FC = ({ fileInput.click() }, [onUploadFiles]) + const getEmptyAreaMenuItems = useCallback((): MenuProps['items'] => { + return [ + { + label: t('notes.new_note'), + key: 'new_note', + icon: , + onClick: handleCreateNote + }, + { + label: t('notes.new_folder'), + key: 'new_folder', + icon: , + onClick: handleCreateFolder + } + ] + }, [t, handleCreateNote, handleCreateFolder]) + return ( { @@ -784,31 +836,90 @@ const NotesSidebar: FC = ({ {shouldUseVirtualization ? ( - -
- {virtualizer.getVirtualItems().map((virtualItem) => { - const { node, depth } = flattenedNodes[virtualItem.index] - return ( -
-
+ setOpenDropdownKey(open ? 'empty-area' : null)}> + +
+ {virtualizer.getVirtualItems().map((virtualItem) => { + const { node, depth } = flattenedNodes[virtualItem.index] + return ( +
+
+ +
+
+ ) + })} +
+ {!isShowStarred && !isShowSearch && ( + + + + + + + {t('notes.drop_markdown_hint')} + + + + )} +
+
+ ) : ( + setOpenDropdownKey(open ? 'empty-area' : null)}> + + + {isShowStarred || isShowSearch + ? filteredTree.map((node) => ( = ({ onDragLeave={handleDragLeave} onDrop={handleDrop} onDragEnd={handleDragEnd} - renderChildren={false} + openDropdownKey={openDropdownKey} + onDropdownOpenChange={setOpenDropdownKey} /> -
-
- ) - })} -
- {!isShowStarred && !isShowSearch && ( - - - - - - - {t('notes.drop_markdown_hint')} - - - - )} -
- ) : ( - - - {isShowStarred || isShowSearch - ? filteredTree.map((node) => ( - - )) - : notesTree.map((node) => ( - - ))} - {!isShowStarred && !isShowSearch && ( - - - - - - - {t('notes.drop_markdown_hint')} - - - - )} - - + )) + : notesTree.map((node) => ( + + ))} + {!isShowStarred && !isShowSearch && ( + + + + + + + {t('notes.drop_markdown_hint')} + + + + )} + + +
)}