diff --git a/src/common/components/context-menu.js b/src/common/components/context-menu.js index 4e5fe26c..a7e01368 100644 --- a/src/common/components/context-menu.js +++ b/src/common/components/context-menu.js @@ -96,6 +96,8 @@ function ContextMenu({ params, onClose }) { const [position, setPosition] = useState({ style: {} }); const [update, setUpdate] = useState(); const containerRef = useRef(); + const searchStringRef = useRef(''); + const searchTimeoutRef = useRef(null); useEffect(() => { setUpdate({}); @@ -134,6 +136,33 @@ function ContextMenu({ params, onClose }) { } } + // Select a menuitem from typing, similar to native context menus + function handleKeyDown(event) { + let { key } = event; + // Ignore non-characters + if (key.length !== 1 || !key.match(/\S/)) return; + + // Clear search string after 3 seconds of no typing + if (searchTimeoutRef.current) { + clearTimeout(searchTimeoutRef.current); + } + searchTimeoutRef.current = setTimeout(() => { + searchStringRef.current = ''; + }, 2000); + + // Keep track of what has been typed so far + searchStringRef.current += key.toLowerCase(); + + // Find all buttons with text that start with what has been typed + let menuOptions = [...document.querySelectorAll(".context-menu button:not([disabled])")]; + let candidates = menuOptions.filter(option => option.textContent.toLowerCase().startsWith(searchStringRef.current)); + + // Focus the first match + if (candidates.length) { + candidates[0].focus(); + } + } + function handleClick(event, item) { onClose(); event.preventDefault(); @@ -143,7 +172,7 @@ function ContextMenu({ params, onClose }) { return (