import React, { useState, useEffect, useRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { FileText, FolderPlus, Pencil, Trash2, Copy, Download, RefreshCw } from 'lucide-react'; import { cn } from '../lib/utils'; /** * FileContextMenu Component * Right-click context menu for file/directory operations */ const FileContextMenu = ({ children, item, onRename, onDelete, onNewFile, onNewFolder, onRefresh, onCopyPath, onDownload, isLoading = false, className = '' }) => { const { t } = useTranslation(); const [isOpen, setIsOpen] = useState(false); const [position, setPosition] = useState({ x: 0, y: 0 }); const menuRef = useRef(null); const triggerRef = useRef(null); const isDirectory = item?.type === 'directory'; const isFile = item?.type === 'file'; const isBackground = !item; // Clicked on empty space // Handle right-click const handleContextMenu = useCallback((e) => { e.preventDefault(); e.stopPropagation(); const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX; const y = e.clientY; // Adjust position if menu would go off screen const menuWidth = 200; const menuHeight = 300; let adjustedX = x; let adjustedY = y; if (x + menuWidth > window.innerWidth) { adjustedX = window.innerWidth - menuWidth - 10; } if (y + menuHeight > window.innerHeight) { adjustedY = window.innerHeight - menuHeight - 10; } setPosition({ x: adjustedX, y: adjustedY }); setIsOpen(true); }, []); // Close menu const closeMenu = useCallback(() => { setIsOpen(false); }, []); // Close on click outside useEffect(() => { const handleClickOutside = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) { closeMenu(); } }; const handleEscape = (e) => { if (e.key === 'Escape') { closeMenu(); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleEscape); } return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleEscape); }; }, [isOpen, closeMenu]); // Handle keyboard navigation useEffect(() => { if (!isOpen) return; const handleKeyDown = (e) => { const menuItems = menuRef.current?.querySelectorAll('[role="menuitem"]'); if (!menuItems || menuItems.length === 0) return; const currentIndex = Array.from(menuItems).findIndex( (item) => item === document.activeElement ); switch (e.key) { case 'ArrowDown': e.preventDefault(); const nextIndex = currentIndex < menuItems.length - 1 ? currentIndex + 1 : 0; menuItems[nextIndex]?.focus(); break; case 'ArrowUp': e.preventDefault(); const prevIndex = currentIndex > 0 ? currentIndex - 1 : menuItems.length - 1; menuItems[prevIndex]?.focus(); break; case 'Enter': case ' ': if (document.activeElement?.hasAttribute('role', 'menuitem')) { e.preventDefault(); document.activeElement.click(); } break; } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [isOpen]); // Handle action click const handleAction = (action, ...args) => { closeMenu(); action?.(...args); }; // Menu item component const MenuItem = ({ icon: Icon, label, onClick, danger = false, disabled = false, shortcut }) => ( ); // Menu divider const MenuDivider = () => (
); // Build menu items based on context const renderMenuItems = () => { if (isFile) { return ( <>