mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +08:00
Align skill tree menu behaviors
This commit is contained in:
@ -16,6 +16,7 @@ import { NODE_MENU_TYPE } from '../../constants'
|
||||
import { useFolderFileDrop } from '../../hooks/file-tree/dnd/use-folder-file-drop'
|
||||
import { useTreeNodeHandlers } from '../../hooks/file-tree/interaction/use-tree-node-handlers'
|
||||
import { useFileOperations } from '../../hooks/file-tree/operations/use-file-operations'
|
||||
import NodeDeleteConfirmDialog from './node-delete-confirm-dialog'
|
||||
import NodeMenu from './node-menu'
|
||||
import TreeEditInput from './tree-edit-input'
|
||||
import TreeGuideLines from './tree-guide-lines'
|
||||
@ -90,106 +91,117 @@ const TreeNode = ({ node, style, dragHandle, treeChildren }: TreeNodeProps) => {
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dragHandle}
|
||||
style={style}
|
||||
role="treeitem"
|
||||
tabIndex={0}
|
||||
aria-selected={isSelected}
|
||||
aria-expanded={isFolder ? node.isOpen : undefined}
|
||||
data-skill-tree-node-id={node.data.id}
|
||||
data-skill-tree-node-type={isFolder ? NODE_MENU_TYPE.FOLDER : NODE_MENU_TYPE.FILE}
|
||||
className={cn(
|
||||
'group relative flex h-6 cursor-pointer items-center rounded-md px-2',
|
||||
'hover:bg-state-base-hover',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-components-input-border-active',
|
||||
isSelected && 'bg-state-base-active',
|
||||
isDragOver && 'bg-state-accent-hover ring-1 ring-inset ring-state-accent-solid',
|
||||
isBlinking && 'animate-drag-blink',
|
||||
(isCut || node.isDragging) && 'opacity-50',
|
||||
)}
|
||||
onKeyDown={handleKeyDown}
|
||||
{...(isFolder && {
|
||||
onDragEnter: dragHandlers.onDragEnter,
|
||||
onDragOver: dragHandlers.onDragOver,
|
||||
onDrop: dragHandlers.onDrop,
|
||||
onDragLeave: dragHandlers.onDragLeave,
|
||||
})}
|
||||
>
|
||||
<TreeGuideLines level={node.level} />
|
||||
<>
|
||||
<div
|
||||
className="flex min-w-0 flex-1 items-center gap-2"
|
||||
onClick={handleClick}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
ref={dragHandle}
|
||||
style={style}
|
||||
role="treeitem"
|
||||
tabIndex={0}
|
||||
aria-selected={isSelected}
|
||||
aria-expanded={isFolder ? node.isOpen : undefined}
|
||||
data-skill-tree-node-id={node.data.id}
|
||||
data-skill-tree-node-type={isFolder ? NODE_MENU_TYPE.FOLDER : NODE_MENU_TYPE.FILE}
|
||||
className={cn(
|
||||
'group relative flex h-6 cursor-pointer items-center rounded-md px-2',
|
||||
'hover:bg-state-base-hover',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-components-input-border-active',
|
||||
isSelected && 'bg-state-base-active',
|
||||
isDragOver && 'bg-state-accent-hover ring-1 ring-inset ring-state-accent-solid',
|
||||
isBlinking && 'animate-drag-blink',
|
||||
(isCut || node.isDragging) && 'opacity-50',
|
||||
)}
|
||||
onKeyDown={handleKeyDown}
|
||||
{...(isFolder && {
|
||||
onDragEnter: dragHandlers.onDragEnter,
|
||||
onDragOver: dragHandlers.onDragOver,
|
||||
onDrop: dragHandlers.onDrop,
|
||||
onDragLeave: dragHandlers.onDragLeave,
|
||||
})}
|
||||
>
|
||||
<div className="flex size-5 shrink-0 items-center justify-center">
|
||||
<TreeNodeIcon
|
||||
isFolder={isFolder}
|
||||
isOpen={node.isOpen}
|
||||
fileName={node.data.name}
|
||||
extension={node.data.extension}
|
||||
isDirty={isDirty}
|
||||
onToggle={handleToggle}
|
||||
/>
|
||||
<TreeGuideLines level={node.level} />
|
||||
<div
|
||||
className="flex min-w-0 flex-1 items-center gap-2"
|
||||
onClick={handleClick}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
>
|
||||
<div className="flex size-5 shrink-0 items-center justify-center">
|
||||
<TreeNodeIcon
|
||||
isFolder={isFolder}
|
||||
isOpen={node.isOpen}
|
||||
fileName={node.data.name}
|
||||
extension={node.data.extension}
|
||||
isDirty={isDirty}
|
||||
onToggle={handleToggle}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{node.isEditing
|
||||
? (
|
||||
<TreeEditInput node={node} />
|
||||
)
|
||||
: (
|
||||
<span
|
||||
className={cn(
|
||||
'min-w-0 flex-1 truncate text-[13px] font-normal leading-4',
|
||||
isSelected
|
||||
? 'text-text-primary'
|
||||
: 'text-text-secondary',
|
||||
)}
|
||||
>
|
||||
{node.data.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{node.isEditing
|
||||
? (
|
||||
<TreeEditInput node={node} />
|
||||
)
|
||||
: (
|
||||
<span
|
||||
className={cn(
|
||||
'min-w-0 flex-1 truncate text-[13px] font-normal leading-4',
|
||||
isSelected
|
||||
? 'text-text-primary'
|
||||
: 'text-text-secondary',
|
||||
)}
|
||||
>
|
||||
{node.data.name}
|
||||
</span>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
type="button"
|
||||
aria-label={t('skillSidebar.menu.moreActions')}
|
||||
tabIndex={-1}
|
||||
onClick={handleMoreClick}
|
||||
className={cn(
|
||||
'flex size-5 shrink-0 items-center justify-center rounded',
|
||||
'invisible focus-visible:visible group-hover:visible data-[popup-open]:visible',
|
||||
'hover:bg-state-base-hover-alt data-[popup-open]:bg-state-base-hover-alt',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-components-input-border-active',
|
||||
)}
|
||||
>
|
||||
<span className="i-ri-more-fill size-4 text-text-tertiary" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
placement="bottom-start"
|
||||
sideOffset={4}
|
||||
popupClassName="min-w-[180px]"
|
||||
>
|
||||
<NodeMenu
|
||||
menuType="dropdown"
|
||||
type={isFolder ? 'folder' : 'file'}
|
||||
nodeId={node.data.id}
|
||||
onClose={handleMenuClose}
|
||||
fileInputRef={fileOperations.fileInputRef}
|
||||
folderInputRef={fileOperations.folderInputRef}
|
||||
isLoading={fileOperations.isLoading}
|
||||
onDownload={fileOperations.handleDownload}
|
||||
onNewFile={fileOperations.handleNewFile}
|
||||
onNewFolder={fileOperations.handleNewFolder}
|
||||
onFileChange={fileOperations.handleFileChange}
|
||||
onFolderChange={fileOperations.handleFolderChange}
|
||||
onRename={fileOperations.handleRename}
|
||||
onDeleteClick={fileOperations.handleDeleteClick}
|
||||
/>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
type="button"
|
||||
aria-label={t('skillSidebar.menu.moreActions')}
|
||||
tabIndex={-1}
|
||||
onClick={handleMoreClick}
|
||||
className={cn(
|
||||
'flex size-5 shrink-0 items-center justify-center rounded',
|
||||
'invisible focus-visible:visible group-hover:visible data-[popup-open]:visible',
|
||||
'hover:bg-state-base-hover-alt data-[popup-open]:bg-state-base-hover-alt',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-components-input-border-active',
|
||||
)}
|
||||
>
|
||||
<span className="i-ri-more-fill size-4 text-text-tertiary" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
placement="bottom-start"
|
||||
sideOffset={4}
|
||||
popupClassName="min-w-[180px]"
|
||||
>
|
||||
<NodeMenu
|
||||
menuType="dropdown"
|
||||
type={isFolder ? 'folder' : 'file'}
|
||||
nodeId={node.data.id}
|
||||
onClose={handleMenuClose}
|
||||
fileInputRef={fileOperations.fileInputRef}
|
||||
folderInputRef={fileOperations.folderInputRef}
|
||||
isLoading={fileOperations.isLoading}
|
||||
onDownload={fileOperations.handleDownload}
|
||||
onNewFile={fileOperations.handleNewFile}
|
||||
onNewFolder={fileOperations.handleNewFolder}
|
||||
onFileChange={fileOperations.handleFileChange}
|
||||
onFolderChange={fileOperations.handleFolderChange}
|
||||
onRename={fileOperations.handleRename}
|
||||
onDeleteClick={fileOperations.handleDeleteClick}
|
||||
/>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<NodeDeleteConfirmDialog
|
||||
nodeType={isFolder ? 'folder' : 'file'}
|
||||
open={fileOperations.showDeleteConfirm}
|
||||
isDeleting={fileOperations.isDeleting}
|
||||
onConfirm={() => {
|
||||
void fileOperations.handleDeleteConfirm()
|
||||
}}
|
||||
onCancel={fileOperations.handleDeleteCancel}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user