mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 14:38:06 +08:00
refactor(skill-editor): simplify code by extracting MenuItem component and removing dead code
- Extract reusable MenuItem component for menu buttons in FileOperationsMenu - Remove unused handleUploadFileClick/handleUploadFolderClick callbacks - Remove unused handleDropdownClose callback, inline directly - Remove unused _fileId parameter from revealFile function - Simplify toOpensObject using Object.fromEntries
This commit is contained in:
@ -22,6 +22,30 @@ import { cn } from '@/utils/classnames'
|
||||
* - Upload Folder: Upload entire folder structure (webkitdirectory)
|
||||
*/
|
||||
|
||||
type MenuItemProps = {
|
||||
icon: React.ElementType
|
||||
label: string
|
||||
onClick: () => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
const MenuItem: React.FC<MenuItemProps> = ({ icon: Icon, label, onClick, disabled }) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-lg px-3 py-2',
|
||||
'hover:bg-state-base-hover disabled:cursor-not-allowed disabled:opacity-50',
|
||||
)}
|
||||
>
|
||||
<Icon className="size-4 text-text-tertiary" />
|
||||
<span className="system-sm-regular text-text-secondary">
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
|
||||
type FileOperationsMenuProps = {
|
||||
/** Target folder ID, or 'root' for root level */
|
||||
nodeId: string
|
||||
@ -122,16 +146,6 @@ const FileOperationsMenu: FC<FileOperationsMenuProps> = ({
|
||||
}
|
||||
}, [appId, createFolder, onClose, parentId, t])
|
||||
|
||||
// Handle Upload File button click
|
||||
const handleUploadFileClick = useCallback(() => {
|
||||
fileInputRef.current?.click()
|
||||
}, [])
|
||||
|
||||
// Handle Upload Folder button click
|
||||
const handleUploadFolderClick = useCallback(() => {
|
||||
folderInputRef.current?.click()
|
||||
}, [])
|
||||
|
||||
// Handle file input change (single or multiple files)
|
||||
const handleFileChange = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = Array.from(e.target.files || [])
|
||||
@ -280,72 +294,34 @@ const FileOperationsMenu: FC<FileOperationsMenuProps> = ({
|
||||
onChange={handleFolderChange}
|
||||
/>
|
||||
|
||||
{/* New File */}
|
||||
<button
|
||||
type="button"
|
||||
<MenuItem
|
||||
icon={RiFileAddLine}
|
||||
label={t('skillSidebar.menu.newFile')}
|
||||
onClick={handleNewFile}
|
||||
disabled={isLoading}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-lg px-3 py-2',
|
||||
'hover:bg-state-base-hover disabled:cursor-not-allowed disabled:opacity-50',
|
||||
)}
|
||||
>
|
||||
<RiFileAddLine className="size-4 text-text-tertiary" />
|
||||
<span className="system-sm-regular text-text-secondary">
|
||||
{t('skillSidebar.menu.newFile')}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{/* New Folder */}
|
||||
<button
|
||||
type="button"
|
||||
/>
|
||||
<MenuItem
|
||||
icon={RiFolderAddLine}
|
||||
label={t('skillSidebar.menu.newFolder')}
|
||||
onClick={handleNewFolder}
|
||||
disabled={isLoading}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-lg px-3 py-2',
|
||||
'hover:bg-state-base-hover disabled:cursor-not-allowed disabled:opacity-50',
|
||||
)}
|
||||
>
|
||||
<RiFolderAddLine className="size-4 text-text-tertiary" />
|
||||
<span className="system-sm-regular text-text-secondary">
|
||||
{t('skillSidebar.menu.newFolder')}
|
||||
</span>
|
||||
</button>
|
||||
/>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="my-1 h-px bg-divider-subtle" />
|
||||
|
||||
{/* Upload File */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleUploadFileClick}
|
||||
<MenuItem
|
||||
icon={RiUploadLine}
|
||||
label={t('skillSidebar.menu.uploadFile')}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={isLoading}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-lg px-3 py-2',
|
||||
'hover:bg-state-base-hover disabled:cursor-not-allowed disabled:opacity-50',
|
||||
)}
|
||||
>
|
||||
<RiUploadLine className="size-4 text-text-tertiary" />
|
||||
<span className="system-sm-regular text-text-secondary">
|
||||
{t('skillSidebar.menu.uploadFile')}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{/* Upload Folder */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleUploadFolderClick}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={RiFolderUploadLine}
|
||||
label={t('skillSidebar.menu.uploadFolder')}
|
||||
onClick={() => folderInputRef.current?.click()}
|
||||
disabled={isLoading}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-lg px-3 py-2',
|
||||
'hover:bg-state-base-hover disabled:cursor-not-allowed disabled:opacity-50',
|
||||
)}
|
||||
>
|
||||
<RiFolderUploadLine className="size-4 text-text-tertiary" />
|
||||
<span className="system-sm-regular text-text-secondary">
|
||||
{t('skillSidebar.menu.uploadFolder')}
|
||||
</span>
|
||||
</button>
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -77,11 +77,6 @@ const FileTreeNode = ({ node, style, dragHandle }: NodeRendererProps<TreeNodeDat
|
||||
})
|
||||
}, [isFolder, node.data.id, storeApi])
|
||||
|
||||
// Dropdown close handler
|
||||
const handleDropdownClose = useCallback(() => {
|
||||
setShowDropdown(false)
|
||||
}, [])
|
||||
|
||||
// More button click handler
|
||||
const handleMoreClick = useCallback((e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
@ -164,7 +159,7 @@ const FileTreeNode = ({ node, style, dragHandle }: NodeRendererProps<TreeNodeDat
|
||||
<PortalToFollowElemContent className="z-[100]">
|
||||
<FileOperationsMenu
|
||||
nodeId={node.data.id}
|
||||
onClose={handleDropdownClose}
|
||||
onClose={() => setShowDropdown(false)}
|
||||
/>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
|
||||
@ -92,7 +92,7 @@ const Files: React.FC<FilesProps> = ({ className }) => {
|
||||
|
||||
// Update store for state persistence
|
||||
if (ancestors.length > 0)
|
||||
storeApi.getState().revealFile(activeTabId, ancestors)
|
||||
storeApi.getState().revealFile(ancestors)
|
||||
|
||||
// Use Tree API for immediate UI update (initialOpenState only applies on first render)
|
||||
const timeoutId = setTimeout(() => {
|
||||
|
||||
@ -98,7 +98,7 @@ export type FileTreeSliceShape = {
|
||||
/** Toggle a folder's expanded state */
|
||||
toggleFolder: (folderId: string) => void
|
||||
/** Reveal a file by expanding all ancestor folders */
|
||||
revealFile: (fileId: string, ancestorFolderIds: string[]) => void
|
||||
revealFile: (ancestorFolderIds: string[]) => void
|
||||
}
|
||||
|
||||
export const createFileTreeSlice: StateCreator<FileTreeSliceShape> = (set, get) => ({
|
||||
@ -119,7 +119,7 @@ export const createFileTreeSlice: StateCreator<FileTreeSliceShape> = (set, get)
|
||||
set({ expandedFolderIds: newSet })
|
||||
},
|
||||
|
||||
revealFile: (_fileId: string, ancestorFolderIds: string[]) => {
|
||||
revealFile: (ancestorFolderIds: string[]) => {
|
||||
const { expandedFolderIds } = get()
|
||||
const newSet = new Set(expandedFolderIds)
|
||||
// Expand all ancestors
|
||||
|
||||
@ -102,11 +102,7 @@ export function getAncestorIds(nodeId: string, nodes: AppAssetTreeView[]): strin
|
||||
* @returns Object for react-arborist opens prop
|
||||
*/
|
||||
export function toOpensObject(expandedIds: Set<string>): Record<string, boolean> {
|
||||
const opens: Record<string, boolean> = {}
|
||||
expandedIds.forEach((id) => {
|
||||
opens[id] = true
|
||||
})
|
||||
return opens
|
||||
return Object.fromEntries([...expandedIds].map(id => [id, true]))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user