diff --git a/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx b/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx index 173b36c4e..69d1f42d8 100644 --- a/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx +++ b/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx @@ -57,6 +57,7 @@ export function GridSuggestionMenuWrapper(props: { items, columns, onItemClickCloseMenu, + items.length > 0 ); // set basic aria attributes when the menu is open diff --git a/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/hooks/useGridSuggestionMenuKeyboardNavigation.ts b/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/hooks/useGridSuggestionMenuKeyboardNavigation.ts index 9f435158a..a246edc2c 100644 --- a/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/hooks/useGridSuggestionMenuKeyboardNavigation.ts +++ b/packages/react/src/components/SuggestionMenu/GridSuggestionMenu/hooks/useGridSuggestionMenuKeyboardNavigation.ts @@ -9,6 +9,7 @@ export function useGridSuggestionMenuKeyboardNavigation( items: Item[], columns: number, onItemClick?: (item: Item) => void, + isMenuVisible?: boolean, ) { const [selectedIndex, setSelectedIndex] = useState(0); @@ -16,6 +17,10 @@ export function useGridSuggestionMenuKeyboardNavigation( useEffect(() => { const handleMenuNavigationKeys = (event: KeyboardEvent) => { + if (isMenuVisible === false) { + return false; + } + if (event.key === "ArrowLeft") { event.preventDefault(); if (items.length) { @@ -53,12 +58,24 @@ export function useGridSuggestionMenuKeyboardNavigation( } if (event.key === "Enter" && !event.isComposing) { + + if (!items.length || selectedIndex < 0 || selectedIndex >= items.length) { + return false; + } + + const suggestionMenuElement = document.querySelector('#bn-grid-suggestion-menu, .bn-grid-suggestion-menu'); + const isMenuInDOM = !!suggestionMenuElement; + + if (!isMenuVisible || !isMenuInDOM) { + return false; + } + event.stopPropagation(); event.preventDefault(); - if (items.length) { - onItemClick?.(items[selectedIndex]); - } + if (onItemClick) { + onItemClick(items[selectedIndex]); + } return true; } @@ -79,7 +96,7 @@ export function useGridSuggestionMenuKeyboardNavigation( true, ); }; - }, [editor.domElement, items, selectedIndex, onItemClick, columns, isGrid]); + }, [editor.domElement, items, selectedIndex, onItemClick, columns, isGrid, isMenuVisible]); // Resets index when items change useEffect(() => { diff --git a/packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx b/packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx index 391bcb1b3..5b36fb2f9 100644 --- a/packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx +++ b/packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx @@ -54,6 +54,7 @@ export function SuggestionMenuWrapper(props: { query, items, onItemClickCloseMenu, + items.length > 0 ); // set basic aria attributes when the menu is open diff --git a/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardHandler.ts b/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardHandler.ts index 8d367ffdc..76537d60b 100644 --- a/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardHandler.ts +++ b/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardHandler.ts @@ -4,7 +4,8 @@ import React, { useState } from "react"; // & down arrow keys are used to select an item, enter is used to execute it. export function useSuggestionMenuKeyboardHandler( items: Item[], - onItemClick?: (item: Item) => void + onItemClick?: (item: Item) => void, + isMenuVisible?: boolean | undefined ) { const [selectedIndex, setSelectedIndex] = useState(0); @@ -12,6 +13,10 @@ export function useSuggestionMenuKeyboardHandler( selectedIndex, setSelectedIndex, handler: (event: KeyboardEvent | React.KeyboardEvent) => { + if (isMenuVisible === false) { + return false; + } + if (event.key === "ArrowUp") { event.preventDefault(); @@ -37,12 +42,24 @@ export function useSuggestionMenuKeyboardHandler( ? event.nativeEvent.isComposing : event.isComposing; if (event.key === "Enter" && !isComposing) { + + if (!items.length || selectedIndex < 0 || selectedIndex >= items.length) { + return false; + } + + const suggestionMenuElement = document.querySelector('#bn-suggestion-menu, .bn-suggestion-menu, #ai-suggestion-menu'); + const isMenuInDOM = !!suggestionMenuElement; + + if (!isMenuVisible || !isMenuInDOM) { + return false; + } + event.preventDefault(); event.stopPropagation(); - if (items.length) { - onItemClick?.(items[selectedIndex]); - } + if (onItemClick) { + onItemClick(items[selectedIndex]); + } return true; } diff --git a/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardNavigation.ts b/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardNavigation.ts index 99c72a8b6..8c2b837db 100644 --- a/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardNavigation.ts +++ b/packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardNavigation.ts @@ -9,22 +9,22 @@ export function useSuggestionMenuKeyboardNavigation( query: string, items: Item[], onItemClick?: (item: Item) => void, - element?: HTMLElement, + isMenuVisible?: boolean, ) { const { selectedIndex, setSelectedIndex, handler } = - useSuggestionMenuKeyboardHandler(items, onItemClick); + useSuggestionMenuKeyboardHandler(items, onItemClick, isMenuVisible); useEffect(() => { - (element || editor.domElement)?.addEventListener("keydown", handler, true); + editor.domElement?.addEventListener("keydown", handler, true); return () => { - (element || editor.domElement)?.removeEventListener( + editor.domElement?.removeEventListener( "keydown", handler, true, ); }; - }, [editor.domElement, items, selectedIndex, onItemClick, element, handler]); + }, [editor.domElement, items, selectedIndex, onItemClick, handler]); // Resets index when items change useEffect(() => {