From 663cb2cce7f863e4d9a4bd85f2f03d18c039e0c7 Mon Sep 17 00:00:00 2001 From: Sylvie Crowe <107814465+oneirocosm@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:26:24 -0700 Subject: [PATCH] Connection Typeahead Arrow Keys (#352) This is a first pass to get arrow keys to work with the connection typeahead. --- frontend/app/block/blockframe.tsx | 54 +++++++++----- frontend/app/modals/typeaheadmodal.less | 9 ++- frontend/app/modals/typeaheadmodal.tsx | 98 ++++++++++++++----------- 3 files changed, 98 insertions(+), 63 deletions(-) diff --git a/frontend/app/block/blockframe.tsx b/frontend/app/block/blockframe.tsx index 40ba0c94..09b4d7a2 100644 --- a/frontend/app/block/blockframe.tsx +++ b/frontend/app/block/blockframe.tsx @@ -492,6 +492,7 @@ const ChangeConnectionBlockModal = React.memo( const connStatus = jotai.useAtomValue(connStatusAtom); const [connList, setConnList] = React.useState>([]); const allConnStatus = jotai.useAtomValue(atoms.allConnStatus); + const [rowIndex, setRowIndex] = React.useState(0); const connStatusMap = new Map(); let maxActiveConnNum = 1; for (const conn of allConnStatus) { @@ -506,14 +507,10 @@ const ChangeConnectionBlockModal = React.memo( return; } const prtn = WshServer.ConnListCommand({ timeout: 2000 }); - prtn.then((connList) => { - setConnList(connList ?? []); + prtn.then((newConnList) => { + setConnList(newConnList ?? []); }).catch((e) => console.log("unable to load conn list from backend. using blank list: ", e)); - }, [changeConnModalOpen]); - - React.useEffect(() => { - console.log("connSelected is: ", connSelected); - }, [connSelected]); + }, [changeConnModalOpen, setConnList]); const changeConnection = React.useCallback( async (connName: string) => { @@ -534,7 +531,6 @@ const ChangeConnectionBlockModal = React.memo( oref: WOS.makeORef("block", blockId), meta: { connection: connName, file: newCwd }, }); - const tabId = globalStore.get(atoms.activeTabId); try { await WshServer.ConnEnsureCommand(connName, { timeout: 60000 }); } catch (e) { @@ -588,7 +584,6 @@ const ChangeConnectionBlockModal = React.memo( }; const priorityItems: Array = []; if (createNew) { - console.log("added to priority items"); priorityItems.push(newConnectionSuggestion); } if (showReconnect && (connStatus.status == "disconnected" || connStatus.status == "error")) { @@ -610,10 +605,6 @@ const ChangeConnectionBlockModal = React.memo( iconColor: "var(--grey-text-color)", value: "", label: localName, - onSelect: (_: string) => { - changeConnection(""); - globalStore.set(changeConnModalAtom, false); - }, }); } const remoteItems = filteredList.map((connName) => { @@ -647,14 +638,30 @@ const ChangeConnectionBlockModal = React.memo( suggestions.push(remoteSuggestions); } + let selectionList: Array = [ + ...prioritySuggestions.items, + ...localSuggestion.items, + ...remoteSuggestions.items, + ]; + + // quick way to change icon color when highlighted + selectionList = selectionList.map((item, index) => { + if (index == rowIndex && item.iconColor == "var(--grey-text-color)") { + item.iconColor = "var(--main-text-color)"; + } + return item; + }); + const handleTypeAheadKeyDown = React.useCallback( (waveEvent: WaveKeyboardEvent): boolean => { if (keyutil.checkKeyPressed(waveEvent, "Enter")) { - changeConnection(connSelected); - globalStore.set(changeConnModalAtom, false); - setConnSelected(""); - refocusNode(blockId); - return true; + const rowItem = selectionList[rowIndex]; + if ("onSelect" in rowItem && rowItem.onSelect) { + rowItem.onSelect(rowItem.value); + } else { + changeConnection(rowItem.value); + globalStore.set(changeConnModalAtom, false); + } } if (keyutil.checkKeyPressed(waveEvent, "Escape")) { globalStore.set(changeConnModalAtom, false); @@ -662,8 +669,16 @@ const ChangeConnectionBlockModal = React.memo( refocusNode(blockId); return true; } + if (keyutil.checkKeyPressed(waveEvent, "ArrowUp")) { + setRowIndex((idx) => Math.max(idx - 1, 0)); + return true; + } + if (keyutil.checkKeyPressed(waveEvent, "ArrowDown")) { + setRowIndex((idx) => Math.min(idx + 1, filteredList.length)); + return true; + } }, - [changeConnModalAtom, viewModel, blockId, connSelected] + [changeConnModalAtom, viewModel, blockId, connSelected, selectionList] ); // this check was also moved to BlockFrame to prevent all the above code from running unnecessarily if (!changeConnModalOpen) { @@ -678,6 +693,7 @@ const ChangeConnectionBlockModal = React.memo( changeConnection(selected); globalStore.set(changeConnModalAtom, false); }} + selectIndex={rowIndex} autoFocus={isNodeFocused} onKeyDown={(e) => keyutil.keydownWrapper(handleTypeAheadKeyDown)(e)} onChange={(current: string) => setConnSelected(current)} diff --git a/frontend/app/modals/typeaheadmodal.less b/frontend/app/modals/typeaheadmodal.less index b57fd193..4e896a7b 100644 --- a/frontend/app/modals/typeaheadmodal.less +++ b/frontend/app/modals/typeaheadmodal.less @@ -85,10 +85,15 @@ align-items: center; gap: 8px; align-self: stretch; + border-radius: 4px; - &:hover { + &.selected { + background-color: rgb(from var(--accent-color) r g b / 0.5); + color: var(--main-text-color); + } + + &:hover:not(.selected) { background-color: var(--highlight-bg-color); - border-radius: 4px; } .typeahead-item-name { diff --git a/frontend/app/modals/typeaheadmodal.tsx b/frontend/app/modals/typeaheadmodal.tsx index 6784a115..46b4fa44 100644 --- a/frontend/app/modals/typeaheadmodal.tsx +++ b/frontend/app/modals/typeaheadmodal.tsx @@ -11,51 +11,59 @@ import "./typeaheadmodal.less"; interface SuggestionsProps { suggestions?: SuggestionsType[]; onSelect?: (_: string) => void; + selectIndex: number; } -const Suggestions = forwardRef(({ suggestions, onSelect }: SuggestionsProps, ref) => { - const renderIcon = (icon: string | React.ReactNode, color: string) => { - if (typeof icon === "string") { - return ; - } - return icon; - }; +const Suggestions = forwardRef( + ({ suggestions, onSelect, selectIndex }: SuggestionsProps, ref) => { + const renderIcon = (icon: string | React.ReactNode, color: string) => { + if (typeof icon === "string") { + return ; + } + return icon; + }; - const renderItem = (item: SuggestionBaseItem | SuggestionConnectionItem, index: number) => ( -
{ - if ("onSelect" in item && item.onSelect) { - item.onSelect(item.label); - } else { - onSelect(item.label); - } - }} - className="suggestion-item" - > -
- {item.icon && renderIcon(item.icon, "iconColor" in item && item.iconColor ? item.iconColor : "inherit")} - {item.label} + const renderItem = (item: SuggestionBaseItem | SuggestionConnectionItem, index: number) => ( +
{ + if ("onSelect" in item && item.onSelect) { + item.onSelect(item.value); + } else { + onSelect(item.value); + } + }} + className={clsx("suggestion-item", { selected: selectIndex === index })} + > +
+ {item.icon && + renderIcon(item.icon, "iconColor" in item && item.iconColor ? item.iconColor : "inherit")} + {item.label} +
-
- ); + ); - return ( -
- {suggestions.map((item, index) => { - if ("headerText" in item) { - return ( -
- {item.headerText &&
{item.headerText}
} - {item.items.map((subItem, subIndex) => renderItem(subItem, subIndex))} -
- ); - } - return renderItem(item as SuggestionBaseItem, index); - })} -
- ); -}); + let fullIndex = -1; + return ( +
+ {suggestions.map((item, index) => { + if ("headerText" in item) { + return ( +
+ {item.headerText &&
{item.headerText}
} + {item.items.map((subItem, subIndex) => { + fullIndex += 1; + return renderItem(subItem, fullIndex); + })} +
+ ); + } + return renderItem(item as SuggestionBaseItem, index); + })} +
+ ); + } +); interface TypeAheadModalProps { anchorRef: React.RefObject; @@ -70,6 +78,7 @@ interface TypeAheadModalProps { onKeyDown?: (_) => void; giveFocusRef?: React.MutableRefObject<() => boolean>; autoFocus?: boolean; + selectIndex?: number; } const TypeAheadModal = ({ @@ -85,6 +94,7 @@ const TypeAheadModal = ({ onClickBackdrop, giveFocusRef, autoFocus, + selectIndex, }: TypeAheadModalProps) => { const { width, height } = useDimensions(blockRef); const modalRef = useRef(null); @@ -139,7 +149,6 @@ const TypeAheadModal = ({ let modalWidth = 300; if (modalWidth > availableWidth) { - console.log("got here!!!!!"); modalWidth = availableWidth; } @@ -226,7 +235,12 @@ const TypeAheadModal = ({ }} > {suggestions?.length > 0 && ( - + )}