import React, { useEffect, useMemo, useRef } from "react" import { ContextMenuOptionType, ContextMenuQueryItem, getContextMenuOptions } from "../../utils/context-mentions" import { removeLeadingNonAlphanumeric } from "../common/CodeAccordian" interface ContextMenuProps { onSelect: (type: ContextMenuOptionType, value?: string) => void searchQuery: string onMouseDown: () => void selectedIndex: number setSelectedIndex: (index: number) => void selectedType: ContextMenuOptionType | null queryItems: ContextMenuQueryItem[] } const ContextMenu: React.FC = ({ onSelect, searchQuery, onMouseDown, selectedIndex, setSelectedIndex, selectedType, queryItems, }) => { const menuRef = useRef(null) const filteredOptions = useMemo( () => getContextMenuOptions(searchQuery, selectedType, queryItems), [searchQuery, selectedType, queryItems], ) useEffect(() => { if (menuRef.current) { const selectedElement = menuRef.current.children[selectedIndex] as HTMLElement if (selectedElement) { const menuRect = menuRef.current.getBoundingClientRect() const selectedRect = selectedElement.getBoundingClientRect() if (selectedRect.bottom > menuRect.bottom) { menuRef.current.scrollTop += selectedRect.bottom - menuRect.bottom } else if (selectedRect.top < menuRect.top) { menuRef.current.scrollTop -= menuRect.top - selectedRect.top } } } }, [selectedIndex]) const renderOptionContent = (option: ContextMenuQueryItem) => { switch (option.type) { case ContextMenuOptionType.Problems: return Problems case ContextMenuOptionType.URL: return Paste URL to fetch contents case ContextMenuOptionType.NoResults: return No results found case ContextMenuOptionType.Git: if (option.value) { return (
{option.label} {option.description}
) } else { return Git Commits } case ContextMenuOptionType.File: case ContextMenuOptionType.OpenedFile: case ContextMenuOptionType.Folder: if (option.value) { return ( <> / {option.value?.startsWith("/.") && .} {removeLeadingNonAlphanumeric(option.value || "") + "\u200E"} ) } else { return Add {option.type === ContextMenuOptionType.File ? "File" : "Folder"} } } } const getIconForOption = (option: ContextMenuQueryItem): string => { switch (option.type) { case ContextMenuOptionType.OpenedFile: return "window" case ContextMenuOptionType.File: return "file" case ContextMenuOptionType.Folder: return "folder" case ContextMenuOptionType.Problems: return "warning" case ContextMenuOptionType.URL: return "link" case ContextMenuOptionType.Git: return "git-commit" case ContextMenuOptionType.NoResults: return "info" default: return "file" } } const isOptionSelectable = (option: ContextMenuQueryItem): boolean => { return option.type !== ContextMenuOptionType.NoResults && option.type !== ContextMenuOptionType.URL } return (
{filteredOptions.map((option, index) => (
isOptionSelectable(option) && onSelect(option.type, option.value)} style={{ padding: "8px 12px", cursor: isOptionSelectable(option) ? "pointer" : "default", color: "var(--vscode-dropdown-foreground)", borderBottom: "1px solid var(--vscode-editorGroup-border)", display: "flex", alignItems: "center", justifyContent: "space-between", backgroundColor: index === selectedIndex && isOptionSelectable(option) ? "var(--vscode-list-activeSelectionBackground)" : "", }} onMouseEnter={() => isOptionSelectable(option) && setSelectedIndex(index)}>
{renderOptionContent(option)}
{(option.type === ContextMenuOptionType.File || option.type === ContextMenuOptionType.Folder || option.type === ContextMenuOptionType.Git) && !option.value && ( )} {(option.type === ContextMenuOptionType.Problems || ((option.type === ContextMenuOptionType.File || option.type === ContextMenuOptionType.Folder || option.type === ContextMenuOptionType.OpenedFile || option.type === ContextMenuOptionType.Git) && option.value)) && ( )}
))}
) } export default ContextMenu