2026-04-27 01:45:16 +02:00
|
|
|
import { useCallback, useEffect, useRef, useState, type MouseEvent as ReactMouseEvent, type RefObject } from 'react'
|
2026-04-22 17:16:56 +02:00
|
|
|
import type { FolderNode } from '../../types'
|
2026-04-27 01:45:16 +02:00
|
|
|
import type { FolderFileActions } from '../../hooks/useFileActions'
|
2026-04-22 17:16:56 +02:00
|
|
|
import type { FolderContextMenuState } from './FolderContextMenu'
|
|
|
|
|
|
|
|
|
|
interface UseFolderContextMenuInput {
|
|
|
|
|
onDeleteFolder?: (folderPath: string) => void
|
2026-04-27 01:45:16 +02:00
|
|
|
folderFileActions?: FolderFileActions
|
2026-04-22 17:16:56 +02:00
|
|
|
onStartRenameFolder?: (folderPath: string) => void
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 01:45:16 +02:00
|
|
|
function useContextMenuDismiss(
|
|
|
|
|
contextMenu: FolderContextMenuState | null,
|
|
|
|
|
menuRef: RefObject<HTMLDivElement | null>,
|
|
|
|
|
closeContextMenu: () => void,
|
|
|
|
|
) {
|
2026-04-22 17:16:56 +02:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (!contextMenu) return
|
|
|
|
|
|
|
|
|
|
const handleOutsideClick = (event: MouseEvent) => {
|
|
|
|
|
if (menuRef.current && !menuRef.current.contains(event.target as Node)) closeContextMenu()
|
|
|
|
|
}
|
|
|
|
|
const handleEscape = (event: KeyboardEvent) => {
|
|
|
|
|
if (event.key === 'Escape') closeContextMenu()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.addEventListener('mousedown', handleOutsideClick)
|
|
|
|
|
document.addEventListener('keydown', handleEscape)
|
|
|
|
|
return () => {
|
|
|
|
|
document.removeEventListener('mousedown', handleOutsideClick)
|
|
|
|
|
document.removeEventListener('keydown', handleEscape)
|
|
|
|
|
}
|
2026-04-27 01:45:16 +02:00
|
|
|
}, [closeContextMenu, contextMenu, menuRef])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useFolderContextMenu({
|
|
|
|
|
onDeleteFolder,
|
|
|
|
|
folderFileActions,
|
|
|
|
|
onStartRenameFolder,
|
|
|
|
|
}: UseFolderContextMenuInput) {
|
|
|
|
|
const [contextMenu, setContextMenu] = useState<FolderContextMenuState | null>(null)
|
|
|
|
|
const menuRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
|
|
|
|
|
const closeContextMenu = useCallback(() => setContextMenu(null), [])
|
|
|
|
|
useContextMenuDismiss(contextMenu, menuRef, closeContextMenu)
|
2026-04-22 17:16:56 +02:00
|
|
|
|
|
|
|
|
const handleOpenMenu = useCallback((node: FolderNode, event: ReactMouseEvent<HTMLDivElement>) => {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
event.stopPropagation()
|
|
|
|
|
setContextMenu({
|
|
|
|
|
path: node.path,
|
|
|
|
|
x: event.clientX,
|
|
|
|
|
y: event.clientY,
|
|
|
|
|
})
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
const handleRenameFromMenu = useCallback((folderPath: string) => {
|
|
|
|
|
closeContextMenu()
|
|
|
|
|
onStartRenameFolder?.(folderPath)
|
|
|
|
|
}, [closeContextMenu, onStartRenameFolder])
|
|
|
|
|
|
|
|
|
|
const handleDeleteFromMenu = useCallback((folderPath: string) => {
|
|
|
|
|
closeContextMenu()
|
|
|
|
|
onDeleteFolder?.(folderPath)
|
|
|
|
|
}, [closeContextMenu, onDeleteFolder])
|
|
|
|
|
|
2026-04-27 01:45:16 +02:00
|
|
|
const handleRevealFromMenu = useCallback((folderPath: string) => {
|
|
|
|
|
closeContextMenu()
|
|
|
|
|
folderFileActions?.revealFolder(folderPath)
|
|
|
|
|
}, [closeContextMenu, folderFileActions])
|
|
|
|
|
|
|
|
|
|
const handleCopyPathFromMenu = useCallback((folderPath: string) => {
|
|
|
|
|
closeContextMenu()
|
|
|
|
|
folderFileActions?.copyFolderPath(folderPath)
|
|
|
|
|
}, [closeContextMenu, folderFileActions])
|
|
|
|
|
|
2026-04-22 17:16:56 +02:00
|
|
|
return {
|
|
|
|
|
closeContextMenu,
|
|
|
|
|
contextMenu,
|
2026-04-27 01:45:16 +02:00
|
|
|
handleCopyPathFromMenu,
|
2026-04-22 17:16:56 +02:00
|
|
|
handleDeleteFromMenu,
|
|
|
|
|
handleOpenMenu,
|
2026-04-27 01:45:16 +02:00
|
|
|
handleRevealFromMenu,
|
2026-04-22 17:16:56 +02:00
|
|
|
handleRenameFromMenu,
|
|
|
|
|
menuRef,
|
|
|
|
|
}
|
|
|
|
|
}
|