Fix command menu selection (#10248)

- Created a state `hasUserSelectedCommandState` : This state is set to
`true` when the user selects an element in the command menu list. It is
set to false upon redirection or when the command menu is closed.
- Modified `CommandMenuDefaultSelectionEffect` to have the expected
selection behavior for the command menu
This commit is contained in:
Raphaël Bosi
2025-02-17 12:02:17 +01:00
committed by GitHub
parent 14478afa7e
commit 7ed142e987
6 changed files with 44 additions and 4 deletions

View File

@ -1,3 +1,4 @@
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
@ -13,10 +14,13 @@ export const CommandMenuDefaultSelectionEffect = ({
const selectedItemId = useRecoilValue(selectedItemIdState); const selectedItemId = useRecoilValue(selectedItemIdState);
const hasUserSelectedCommand = useRecoilValue(hasUserSelectedCommandState);
useEffect(() => { useEffect(() => {
if ( if (
isDefined(selectedItemId) && isDefined(selectedItemId) &&
selectableItemIds.includes(selectedItemId) selectableItemIds.includes(selectedItemId) &&
hasUserSelectedCommand
) { ) {
return; return;
} }
@ -24,7 +28,12 @@ export const CommandMenuDefaultSelectionEffect = ({
if (selectableItemIds.length > 0) { if (selectableItemIds.length > 0) {
setSelectedItemId(selectableItemIds[0]); setSelectedItemId(selectableItemIds[0]);
} }
}, [selectableItemIds, selectedItemId, setSelectedItemId]); }, [
hasUserSelectedCommand,
selectableItemIds,
selectedItemId,
setSelectedItemId,
]);
return null; return null;
}; };

View File

@ -7,11 +7,13 @@ import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/Comman
import { RESET_CONTEXT_TO_SELECTION } from '@/command-menu/constants/ResetContextToSelection'; import { RESET_CONTEXT_TO_SELECTION } from '@/command-menu/constants/ResetContextToSelection';
import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick'; import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick';
import { useResetPreviousCommandMenuContext } from '@/command-menu/hooks/useResetPreviousCommandMenuContext'; import { useResetPreviousCommandMenuContext } from '@/command-menu/hooks/useResetPreviousCommandMenuContext';
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
import { MOBILE_VIEWPORT } from 'twenty-ui'; import { MOBILE_VIEWPORT } from 'twenty-ui';
@ -75,6 +77,10 @@ export const CommandMenuList = ({
const { resetPreviousCommandMenuContext } = const { resetPreviousCommandMenuContext } =
useResetPreviousCommandMenuContext(); useResetPreviousCommandMenuContext();
const setHasUserSelectedCommand = useSetRecoilState(
hasUserSelectedCommandState,
);
return ( return (
<> <>
<CommandMenuDefaultSelectionEffect <CommandMenuDefaultSelectionEffect
@ -109,6 +115,9 @@ export const CommandMenuList = ({
}); });
} }
}} }}
onSelect={() => {
setHasUserSelectedCommand(true);
}}
> >
{children} {children}
{commandGroups.map(({ heading, items }) => {commandGroups.map(({ heading, items }) =>

View File

@ -13,6 +13,7 @@ import {
} from '@/command-menu/states/commandMenuNavigationStackState'; } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageTitle'; import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageTitle';
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState'; import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
@ -60,6 +61,7 @@ export const useCommandMenu = () => {
set(isCommandMenuOpenedState, true); set(isCommandMenuOpenedState, true);
setHotkeyScopeAndMemorizePreviousScope(AppHotkeyScope.CommandMenuOpen); setHotkeyScopeAndMemorizePreviousScope(AppHotkeyScope.CommandMenuOpen);
set(hasUserSelectedCommandState, false);
}, },
[ [
copyContextStoreStates, copyContextStoreStates,
@ -89,6 +91,7 @@ export const useCommandMenu = () => {
set(commandMenuSearchState, ''); set(commandMenuSearchState, '');
set(commandMenuNavigationStackState, []); set(commandMenuNavigationStackState, []);
resetSelectedItem(); resetSelectedItem();
set(hasUserSelectedCommandState, false);
goBackToPreviousHotkeyScope(); goBackToPreviousHotkeyScope();
emitRightDrawerCloseEvent(); emitRightDrawerCloseEvent();
@ -173,6 +176,7 @@ export const useCommandMenu = () => {
}); });
set(commandMenuNavigationStackState, newNavigationStack); set(commandMenuNavigationStackState, newNavigationStack);
set(hasUserSelectedCommandState, false);
}; };
}, },
[closeCommandMenu], [closeCommandMenu],
@ -201,6 +205,8 @@ export const useCommandMenu = () => {
title: newNavigationStackItem?.pageTitle, title: newNavigationStackItem?.pageTitle,
Icon: newNavigationStackItem?.pageIcon, Icon: newNavigationStackItem?.pageIcon,
}); });
set(hasUserSelectedCommandState, false);
}; };
}, []); }, []);
@ -270,6 +276,8 @@ export const useCommandMenu = () => {
title: undefined, title: undefined,
Icon: undefined, Icon: undefined,
}); });
set(hasUserSelectedCommandState, false);
}; };
}, },
[copyContextStoreStates], [copyContextStoreStates],

View File

@ -0,0 +1,6 @@
import { createState } from 'twenty-ui';
export const hasUserSelectedCommandState = createState({
key: 'hasUserSelectedCommandState',
defaultValue: false,
});

View File

@ -23,8 +23,9 @@ export const SelectableList = ({
selectableItemIdArray, selectableItemIdArray,
selectableItemIdMatrix, selectableItemIdMatrix,
onEnter, onEnter,
onSelect,
}: SelectableListProps) => { }: SelectableListProps) => {
useSelectableListHotKeys(selectableListId, hotkeyScope); useSelectableListHotKeys(selectableListId, hotkeyScope, onSelect);
const { setSelectableItemIds, setSelectableListOnEnter, setSelectedItemId } = const { setSelectableItemIds, setSelectableListOnEnter, setSelectedItemId } =
useSelectableList(selectableListId); useSelectableList(selectableListId);

View File

@ -11,6 +11,7 @@ type Direction = 'up' | 'down' | 'left' | 'right';
export const useSelectableListHotKeys = ( export const useSelectableListHotKeys = (
scopeId: string, scopeId: string,
hotkeyScope: string, hotkeyScope: string,
onSelect?: (itemId: string) => void,
) => { ) => {
const findPosition = ( const findPosition = (
selectableItemIds: string[][], selectableItemIds: string[][],
@ -105,6 +106,7 @@ export const useSelectableListHotKeys = (
if (isNonEmptyString(nextId)) { if (isNonEmptyString(nextId)) {
set(isSelectedItemIdSelector(nextId), true); set(isSelectedItemIdSelector(nextId), true);
set(selectedItemIdState, nextId); set(selectedItemIdState, nextId);
onSelect?.(nextId);
} }
if (isNonEmptyString(selectedItemId)) { if (isNonEmptyString(selectedItemId)) {
@ -112,7 +114,12 @@ export const useSelectableListHotKeys = (
} }
} }
}, },
[isSelectedItemIdSelector, selectableItemIdsState, selectedItemIdState], [
isSelectedItemIdSelector,
onSelect,
selectableItemIdsState,
selectedItemIdState,
],
); );
useScopedHotkeys(Key.ArrowUp, () => handleSelect('up'), hotkeyScope, []); useScopedHotkeys(Key.ArrowUp, () => handleSelect('up'), hotkeyScope, []);