mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 09:58:04 +08:00
feat(portal): add useContextMenuFloating hook for coordinate-based context menus
Replace useClickAway + fixed positioning in file tree context menu with a floating-ui based hook that provides collision detection (flip/shift), ARIA role="menu", Escape/outside-click dismiss, and scroll dismiss via passive capture listener with ref-stabilized callback.
This commit is contained in:
@ -2,9 +2,10 @@
|
||||
|
||||
import type { TreeApi } from 'react-arborist'
|
||||
import type { TreeNodeData } from '../type'
|
||||
import { useClickAway } from 'ahooks'
|
||||
import { FloatingPortal } from '@floating-ui/react'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useContextMenuFloating } from '@/app/components/base/portal-to-follow-elem/use-context-menu-floating'
|
||||
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { getMenuNodeId, getNodeMenuType } from '../utils/tree-utils'
|
||||
import NodeMenu from './node-menu'
|
||||
@ -14,7 +15,6 @@ type TreeContextMenuProps = {
|
||||
}
|
||||
|
||||
const TreeContextMenu = ({ treeRef }: TreeContextMenuProps) => {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const contextMenu = useStore(s => s.contextMenu)
|
||||
const storeApi = useWorkflowStore()
|
||||
|
||||
@ -22,29 +22,42 @@ const TreeContextMenu = ({ treeRef }: TreeContextMenuProps) => {
|
||||
storeApi.getState().setContextMenu(null)
|
||||
}, [storeApi])
|
||||
|
||||
useClickAway(() => {
|
||||
handleClose()
|
||||
}, ref)
|
||||
const position = useMemo(() => ({
|
||||
x: contextMenu?.left ?? 0,
|
||||
y: contextMenu?.top ?? 0,
|
||||
}), [contextMenu?.left, contextMenu?.top])
|
||||
|
||||
const { refs, floatingStyles, getFloatingProps, isPositioned } = useContextMenuFloating({
|
||||
open: !!contextMenu,
|
||||
onOpenChange: (open) => {
|
||||
if (!open)
|
||||
handleClose()
|
||||
},
|
||||
position,
|
||||
})
|
||||
|
||||
if (!contextMenu)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="fixed z-[100]"
|
||||
style={{
|
||||
top: contextMenu.top,
|
||||
left: contextMenu.left,
|
||||
}}
|
||||
>
|
||||
<NodeMenu
|
||||
type={getNodeMenuType(contextMenu.type, contextMenu.isFolder)}
|
||||
nodeId={getMenuNodeId(contextMenu.type, contextMenu.nodeId)}
|
||||
onClose={handleClose}
|
||||
treeRef={treeRef}
|
||||
/>
|
||||
</div>
|
||||
<FloatingPortal>
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
className="z-[100]"
|
||||
style={{
|
||||
...floatingStyles,
|
||||
visibility: isPositioned ? 'visible' : 'hidden',
|
||||
}}
|
||||
{...getFloatingProps()}
|
||||
>
|
||||
<NodeMenu
|
||||
type={getNodeMenuType(contextMenu.type, contextMenu.isFolder)}
|
||||
nodeId={getMenuNodeId(contextMenu.type, contextMenu.nodeId)}
|
||||
onClose={handleClose}
|
||||
treeRef={treeRef}
|
||||
/>
|
||||
</div>
|
||||
</FloatingPortal>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user