mirror of
https://github.com/langgenius/dify.git
synced 2026-05-02 08:28:03 +08:00
feat(skill): implement file tree search with debounced filtering
Add search functionality to skill sidebar using react-arborist's built-in searchTerm and searchMatch props. Search input is debounced at 300ms and filters tree nodes by name (case-insensitive). Also add success toast for rename operations.
This commit is contained in:
@ -23,6 +23,7 @@ import TreeNode from './tree-node'
|
||||
|
||||
type FileTreeProps = {
|
||||
className?: string
|
||||
searchTerm?: string
|
||||
}
|
||||
|
||||
const DropTip = () => {
|
||||
@ -37,7 +38,7 @@ const DropTip = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const FileTree: React.FC<FileTreeProps> = ({ className }) => {
|
||||
const FileTree: React.FC<FileTreeProps> = ({ className, searchTerm = '' }) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const treeRef = useRef<TreeApi<TreeNodeData>>(null)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
@ -77,6 +78,11 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
|
||||
appId,
|
||||
nodeId: id,
|
||||
payload: { name },
|
||||
}).then(() => {
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('skillSidebar.menu.renamed'),
|
||||
})
|
||||
}).catch(() => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
@ -85,6 +91,13 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
|
||||
})
|
||||
}, [appId, renameNode, t])
|
||||
|
||||
const searchMatch = useCallback(
|
||||
(node: NodeApi<TreeNodeData>, term: string) => {
|
||||
return node.data.name.toLowerCase().includes(term.toLowerCase())
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
useSyncTreeWithActiveTab({
|
||||
treeRef,
|
||||
activeTabId,
|
||||
@ -150,6 +163,8 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
|
||||
onToggle={handleToggle}
|
||||
onActivate={handleActivate}
|
||||
onRename={handleRename}
|
||||
searchTerm={searchTerm}
|
||||
searchMatch={searchMatch}
|
||||
disableDrag
|
||||
disableDrop
|
||||
>
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import type { FC } from 'react'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import EditorArea from './editor-area'
|
||||
import EditorBody from './editor-body'
|
||||
import EditorTabs from './editor-tabs'
|
||||
@ -12,12 +13,18 @@ import SkillDocEditor from './skill-doc-editor'
|
||||
import SkillPageLayout from './skill-page-layout'
|
||||
|
||||
const SkillMain: FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
|
||||
const handleSearchChange = useCallback((term: string) => {
|
||||
setSearchTerm(term)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="h-full bg-workflow-canvas-workflow-top-bar-1 pl-3 pt-[52px]">
|
||||
<SkillPageLayout>
|
||||
<Sidebar>
|
||||
<SidebarSearchAdd />
|
||||
<FileTree />
|
||||
<SidebarSearchAdd onSearchChange={handleSearchChange} />
|
||||
<FileTree searchTerm={searchTerm} />
|
||||
</Sidebar>
|
||||
<EditorArea>
|
||||
<EditorTabs />
|
||||
|
||||
@ -8,8 +8,9 @@ import {
|
||||
RiFolderUploadLine,
|
||||
RiUploadLine,
|
||||
} from '@remixicon/react'
|
||||
import { useDebounce } from 'ahooks'
|
||||
import * as React from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import {
|
||||
@ -24,6 +25,10 @@ import { useFileOperations } from './hooks/use-file-operations'
|
||||
import { useSkillAssetTreeData } from './hooks/use-skill-asset-tree'
|
||||
import { getTargetFolderIdFromSelection } from './utils/tree-utils'
|
||||
|
||||
type SidebarSearchAddProps = {
|
||||
onSearchChange?: (searchTerm: string) => void
|
||||
}
|
||||
|
||||
type MenuItemProps = {
|
||||
icon: React.ElementType
|
||||
label: string
|
||||
@ -49,11 +54,16 @@ const MenuItem: React.FC<MenuItemProps> = ({ icon: Icon, label, onClick, disable
|
||||
</button>
|
||||
)
|
||||
|
||||
const SidebarSearchAdd: FC = () => {
|
||||
const SidebarSearchAdd: FC<SidebarSearchAddProps> = ({ onSearchChange }) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const debouncedSearchValue = useDebounce(searchValue, { wait: 300 })
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
onSearchChange?.(debouncedSearchValue)
|
||||
}, [debouncedSearchValue, onSearchChange])
|
||||
|
||||
const { data: treeData } = useSkillAssetTreeData()
|
||||
const activeTabId = useStore(s => s.activeTabId)
|
||||
|
||||
|
||||
@ -1025,6 +1025,7 @@
|
||||
"skillSidebar.menu.newFolderPrompt": "Enter folder name:",
|
||||
"skillSidebar.menu.rename": "Rename",
|
||||
"skillSidebar.menu.renameError": "Failed to rename",
|
||||
"skillSidebar.menu.renamed": "Renamed successfully",
|
||||
"skillSidebar.menu.uploadError": "Failed to upload",
|
||||
"skillSidebar.menu.uploadFile": "Upload File",
|
||||
"skillSidebar.menu.uploadFolder": "Upload Folder",
|
||||
|
||||
@ -1018,6 +1018,7 @@
|
||||
"skillSidebar.menu.newFolderPrompt": "请输入文件夹名称:",
|
||||
"skillSidebar.menu.rename": "重命名",
|
||||
"skillSidebar.menu.renameError": "重命名失败",
|
||||
"skillSidebar.menu.renamed": "重命名成功",
|
||||
"skillSidebar.menu.uploadError": "上传失败",
|
||||
"skillSidebar.menu.uploadFile": "上传文件",
|
||||
"skillSidebar.menu.uploadFolder": "上传文件夹",
|
||||
|
||||
Reference in New Issue
Block a user