fix: navigate with arrow keys in select/multi-select (#5983)

closes: #4977



https://github.com/twentyhq/twenty/assets/13139771/8121814c-9a8a-4a8d-9290-1aebe145220f

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Aditya Pimpalkar
2024-08-02 19:12:46 +01:00
committed by GitHub
parent c5d95dc4c8
commit 1f5c25ac0a
17 changed files with 518 additions and 153 deletions

View File

@ -1,19 +1,30 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { Avatar } from 'twenty-ui';
import { SelectableRecord } from '@/object-record/select/types/SelectableRecord';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
export const MultipleRecordSelectDropdown = ({
selectableListId,
hotkeyScope,
recordsToSelect,
loadingRecords,
filteredSelectedRecords,
onChange,
searchFilter,
}: {
selectableListId: string;
hotkeyScope: string;
recordsToSelect: SelectableRecord[];
filteredSelectedRecords: SelectableRecord[];
selectedRecords: SelectableRecord[];
@ -24,6 +35,15 @@ export const MultipleRecordSelectDropdown = ({
) => void;
loadingRecords: boolean;
}) => {
const { closeDropdown } = useDropdown();
const { selectedItemIdState } = useSelectableListStates({
selectableListScopeId: selectableListId,
});
const { handleResetSelectedPosition } = useSelectableList(selectableListId);
const selectedItemId = useRecoilValue(selectedItemIdState);
const handleRecordSelectChange = (
recordToSelect: SelectableRecord,
newSelectedValue: boolean,
@ -51,35 +71,70 @@ export const MultipleRecordSelectDropdown = ({
}
}, [recordsToSelect, filteredSelectedRecords, loadingRecords]);
useScopedHotkeys(
[Key.Escape],
() => {
closeDropdown();
handleResetSelectedPosition();
},
hotkeyScope,
[closeDropdown, handleResetSelectedPosition],
);
const showNoResult =
recordsToSelect?.length === 0 &&
searchFilter !== '' &&
filteredSelectedRecords?.length === 0 &&
!loadingRecords;
const selectableItemIds = recordsInDropdown.map((record) => record.id);
return (
<DropdownMenuItemsContainer hasMaxHeight>
{recordsInDropdown?.map((record) => (
<MenuItemMultiSelectAvatar
key={record.id}
selected={record.isSelected}
onSelectChange={(newCheckedValue) =>
handleRecordSelectChange(record, newCheckedValue)
}
avatar={
<Avatar
avatarUrl={record.avatarUrl}
placeholderColorSeed={record.id}
placeholder={record.name}
size="md"
type={record.avatarType ?? 'rounded'}
<SelectableList
selectableListId={selectableListId}
selectableItemIdArray={selectableItemIds}
hotkeyScope={hotkeyScope}
onEnter={(itemId) => {
const record = recordsInDropdown.findIndex(
(entity) => entity.id === itemId,
);
const recordIsSelectedInDropwdown = filteredSelectedRecords.find(
(entity) => entity.id === itemId,
);
handleRecordSelectChange(
recordsInDropdown[record],
!recordIsSelectedInDropwdown,
);
handleResetSelectedPosition();
}}
>
<DropdownMenuItemsContainer hasMaxHeight>
{recordsInDropdown?.map((record) => {
return (
<MenuItemMultiSelectAvatar
key={record.id}
selected={record.isSelected}
isKeySelected={record.id === selectedItemId}
onSelectChange={(newCheckedValue) => {
handleResetSelectedPosition();
handleRecordSelectChange(record, newCheckedValue);
}}
avatar={
<Avatar
avatarUrl={record.avatarUrl}
placeholderColorSeed={record.id}
placeholder={record.name}
size="md"
type={record.avatarType ?? 'rounded'}
/>
}
text={record.name}
/>
}
text={record.name}
/>
))}
{showNoResult && <MenuItem text="No result" />}
{loadingRecords && <DropdownMenuSkeletonItem />}
</DropdownMenuItemsContainer>
);
})}
{showNoResult && <MenuItem text="No result" />}
{loadingRecords && <DropdownMenuSkeletonItem />}
</DropdownMenuItemsContainer>
</SelectableList>
);
};