mirror of
https://github.com/langgenius/dify.git
synced 2026-05-13 08:57:28 +08:00
Extract complex event handling and side effects from file tree components into dedicated hooks for better separation of concerns and reusability.
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
'use client'
|
|
|
|
import type { NodeApi } from 'react-arborist'
|
|
import type { TreeNodeData } from '../type'
|
|
import { throttle } from 'es-toolkit/function'
|
|
import { useCallback, useMemo } from 'react'
|
|
import { useWorkflowStore } from '@/app/components/workflow/store'
|
|
import { useDelayedClick } from './use-delayed-click'
|
|
|
|
type UseTreeNodeHandlersOptions = {
|
|
node: NodeApi<TreeNodeData>
|
|
}
|
|
|
|
type UseTreeNodeHandlersReturn = {
|
|
handleClick: (e: React.MouseEvent) => void
|
|
handleDoubleClick: (e: React.MouseEvent) => void
|
|
handleToggle: (e: React.MouseEvent) => void
|
|
handleContextMenu: (e: React.MouseEvent) => void
|
|
handleKeyDown: (e: React.KeyboardEvent) => void
|
|
}
|
|
|
|
/**
|
|
* Hook that encapsulates all tree node interaction handlers.
|
|
* Handles click, double-click, toggle, context menu, and keyboard events.
|
|
*/
|
|
export function useTreeNodeHandlers({
|
|
node,
|
|
}: UseTreeNodeHandlersOptions): UseTreeNodeHandlersReturn {
|
|
const storeApi = useWorkflowStore()
|
|
const isFolder = node.data.node_type === 'folder'
|
|
|
|
const throttledToggle = useMemo(
|
|
() => throttle(() => node.toggle(), 300, { edges: ['leading'] }),
|
|
[node],
|
|
)
|
|
|
|
const openFilePreview = useCallback(() => {
|
|
storeApi.getState().openTab(node.data.id, { pinned: false })
|
|
}, [node.data.id, storeApi])
|
|
|
|
const openFilePinned = useCallback(() => {
|
|
storeApi.getState().openTab(node.data.id, { pinned: true })
|
|
}, [node.data.id, storeApi])
|
|
|
|
const { handleClick: handleFileClick, handleDoubleClick: handleFileDoubleClick } = useDelayedClick({
|
|
onSingleClick: openFilePreview,
|
|
onDoubleClick: openFilePinned,
|
|
})
|
|
|
|
const handleClick = useCallback((e: React.MouseEvent) => {
|
|
e.stopPropagation()
|
|
node.select()
|
|
if (isFolder)
|
|
throttledToggle()
|
|
else
|
|
handleFileClick()
|
|
}, [isFolder, node, throttledToggle, handleFileClick])
|
|
|
|
const handleDoubleClick = useCallback((e: React.MouseEvent) => {
|
|
e.stopPropagation()
|
|
if (isFolder)
|
|
throttledToggle()
|
|
else
|
|
handleFileDoubleClick()
|
|
}, [isFolder, throttledToggle, handleFileDoubleClick])
|
|
|
|
const handleToggle = useCallback((e: React.MouseEvent) => {
|
|
e.stopPropagation()
|
|
throttledToggle()
|
|
}, [throttledToggle])
|
|
|
|
const handleContextMenu = useCallback((e: React.MouseEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
|
|
storeApi.getState().setContextMenu({
|
|
top: e.clientY,
|
|
left: e.clientX,
|
|
nodeId: node.data.id,
|
|
})
|
|
}, [node.data.id, storeApi])
|
|
|
|
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault()
|
|
if (isFolder)
|
|
node.toggle()
|
|
else
|
|
storeApi.getState().openTab(node.data.id, { pinned: true })
|
|
}
|
|
}, [isFolder, node, storeApi])
|
|
|
|
return {
|
|
handleClick,
|
|
handleDoubleClick,
|
|
handleToggle,
|
|
handleContextMenu,
|
|
handleKeyDown,
|
|
}
|
|
}
|