{
- if (showCreateButton === true) {
+ if (itemId === 'add-new' && showCreateButton === true) {
onCreate?.();
} else {
- const entity = entitiesInDropdown.findIndex(
+ const entityIndex = entitiesInDropdown.findIndex(
(entity) => entity.id === itemId,
);
- onEntitySelected(entitiesInDropdown[entity]);
+ onEntitySelected(entitiesInDropdown[entityIndex]);
}
+ handleResetSelectedPosition();
}}
>
{loading ? (
) : entitiesInDropdown.length === 0 && !isAllEntitySelectShown ? (
-
- ) : (
- <>
- {isAllEntitySelectShown &&
- selectAllLabel &&
- onAllEntitySelected && (
- onAllEntitySelected()}
- LeftIcon={SelectAllIcon}
- text={selectAllLabel}
- selected={!!isAllEntitySelected}
- />
- )}
- {emptyLabel && (
- onEntitySelected()}
- LeftIcon={EmptyIcon}
- text={emptyLabel}
- selected={!selectedEntity}
- />
- )}
- >
- )}
- {entitiesInDropdown?.map((entity) => (
-
- ))}
- {showCreateButton && (
<>
+
{entitiesToSelect.length > 0 && }
>
+ ) : (
+ entitiesInDropdown?.map((entity) => {
+ switch (entity.id) {
+ case 'add-new': {
+ return (
+ <>
+ {entitiesToSelect.length > 0 && }
+
+ >
+ );
+ }
+ case 'select-none': {
+ return (
+ emptyLabel && (
+ onEntitySelected()}
+ LeftIcon={EmptyIcon}
+ text={emptyLabel}
+ selected={!selectedEntity}
+ hovered={isSelectedSelectNoneButton}
+ />
+ )
+ );
+ }
+ case 'select-all': {
+ return (
+ isAllEntitySelectShown &&
+ selectAllLabel &&
+ onAllEntitySelected && (
+ onAllEntitySelected()}
+ LeftIcon={SelectAllIcon}
+ text={selectAllLabel}
+ selected={!!isAllEntitySelected}
+ hovered={isSelectedSelectAllButton}
+ />
+ )
+ );
+ }
+ default: {
+ return (
+
+ );
+ }
+ }
+ })
)}
diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx
index 2c609e0e3..74c054132 100644
--- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx
+++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx
@@ -79,6 +79,7 @@ export const SingleEntitySelectMenuItemsWithSearch = ({
? entities.selectedEntities[0]
: undefined)
}
+ hotkeyScope={relationPickerScopeId}
onCreate={onCreateWithInput}
{...{
EmptyIcon,
diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleEntitySelectBaseList.ts b/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleEntitySelectBaseList.ts
new file mode 100644
index 000000000..3aead1418
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleEntitySelectBaseList.ts
@@ -0,0 +1 @@
+export const SINGLE_ENTITY_SELECT_BASE_LIST = 'single-entity-select-base-list';
diff --git a/packages/twenty-front/src/modules/object-record/select/components/MultipleRecordSelectDropdown.tsx b/packages/twenty-front/src/modules/object-record/select/components/MultipleRecordSelectDropdown.tsx
index 86e9ef5aa..e8a286ad4 100644
--- a/packages/twenty-front/src/modules/object-record/select/components/MultipleRecordSelectDropdown.tsx
+++ b/packages/twenty-front/src/modules/object-record/select/components/MultipleRecordSelectDropdown.tsx
@@ -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 (
-
- {recordsInDropdown?.map((record) => (
-
- handleRecordSelectChange(record, newCheckedValue)
- }
- avatar={
- {
+ const record = recordsInDropdown.findIndex(
+ (entity) => entity.id === itemId,
+ );
+ const recordIsSelectedInDropwdown = filteredSelectedRecords.find(
+ (entity) => entity.id === itemId,
+ );
+ handleRecordSelectChange(
+ recordsInDropdown[record],
+ !recordIsSelectedInDropwdown,
+ );
+ handleResetSelectedPosition();
+ }}
+ >
+
+ {recordsInDropdown?.map((record) => {
+ return (
+ {
+ handleResetSelectedPosition();
+ handleRecordSelectChange(record, newCheckedValue);
+ }}
+ avatar={
+
+ }
+ text={record.name}
/>
- }
- text={record.name}
- />
- ))}
- {showNoResult && }
- {loadingRecords && }
-
+ );
+ })}
+ {showNoResult && }
+ {loadingRecords && }
+
+
);
};
diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx
index 14ba36441..ed24815dc 100644
--- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx
@@ -10,7 +10,9 @@ import { useRef } from 'react';
import { Keys } from 'react-hotkeys-hook';
import { Key } from 'ts-key-enum';
+import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
+import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
@@ -66,6 +68,10 @@ export const Dropdown = ({
const { isDropdownOpen, toggleDropdown, closeDropdown, dropdownWidth } =
useDropdown(dropdownId);
+
+ const { handleResetSelectedPosition } = useSelectableList(
+ SINGLE_ENTITY_SELECT_BASE_LIST,
+ );
const offsetMiddlewares = [];
if (isDefined(dropdownOffset.x)) {
@@ -94,6 +100,7 @@ export const Dropdown = ({
if (isDropdownOpen) {
closeDropdown();
+ handleResetSelectedPosition();
}
},
});
@@ -107,9 +114,10 @@ export const Dropdown = ({
[Key.Escape],
() => {
closeDropdown();
+ handleResetSelectedPosition();
},
dropdownHotkeyScope.scope,
- [closeDropdown],
+ [closeDropdown, handleResetSelectedPosition],
);
return (
diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts
index 37098d3c9..89518de5d 100644
--- a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts
+++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts
@@ -1,6 +1,12 @@
-import { useResetRecoilState, useSetRecoilState } from 'recoil';
+import {
+ useRecoilCallback,
+ useResetRecoilState,
+ useSetRecoilState,
+} from 'recoil';
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
+import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
+import { isDefined } from '~/utils/isDefined';
export const useSelectableList = (selectableListId?: string) => {
const {
@@ -24,6 +30,18 @@ export const useSelectableList = (selectableListId?: string) => {
resetSelectedItemIdState();
};
+ const handleResetSelectedPosition = useRecoilCallback(
+ ({ snapshot, set }) =>
+ () => {
+ const selectedItemId = getSnapshotValue(snapshot, selectedItemIdState);
+ if (isDefined(selectedItemId)) {
+ set(selectedItemIdState, null);
+ set(isSelectedItemIdSelector(selectedItemId), false);
+ }
+ },
+ [selectedItemIdState, isSelectedItemIdSelector],
+ );
+
return {
selectableListId: scopeId,
@@ -31,5 +49,6 @@ export const useSelectableList = (selectableListId?: string) => {
isSelectedItemIdSelector,
setSelectableListOnEnter,
resetSelectedItem,
+ handleResetSelectedPosition,
};
};
diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect.tsx b/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect.tsx
index dafaeae1d..83400066d 100644
--- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect.tsx
@@ -17,6 +17,7 @@ type MenuItemMultiSelectProps = {
color?: ThemeColor;
LeftIcon?: IconComponent;
selected: boolean;
+ isKeySelected?: boolean;
text: string;
className: string;
onSelectChange?: (selected: boolean) => void;
@@ -27,6 +28,7 @@ export const MenuItemMultiSelect = ({
LeftIcon,
text,
selected,
+ isKeySelected,
className,
onSelectChange,
}: MenuItemMultiSelectProps) => {
@@ -35,7 +37,11 @@ export const MenuItemMultiSelect = ({
};
return (
-
+
{color ? (
diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx b/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx
index e1c941c45..8c8d94dc7 100644
--- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx
@@ -14,6 +14,7 @@ import {
type MenuItemMultiSelectTagProps = {
selected: boolean;
className?: string;
+ isKeySelected?: boolean;
onClick?: () => void;
color: ThemeColor;
text: string;
@@ -24,10 +25,15 @@ export const MenuItemMultiSelectTag = ({
selected,
className,
onClick,
+ isKeySelected,
text,
}: MenuItemMultiSelectTagProps) => {
return (
-
+
void;
color: ThemeColor | 'transparent';
@@ -17,6 +18,7 @@ type MenuItemSelectTagProps = {
export const MenuItemSelectTag = ({
color,
selected,
+ isKeySelected,
className,
onClick,
text,
@@ -29,6 +31,7 @@ export const MenuItemSelectTag = ({
onClick={onClick}
className={className}
selected={selected}
+ isKeySelected={isKeySelected}
>
diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/StyledMenuItemBase.tsx b/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/StyledMenuItemBase.tsx
index a2489def3..f361efc64 100644
--- a/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/StyledMenuItemBase.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/StyledMenuItemBase.tsx
@@ -32,6 +32,9 @@ export const StyledMenuItemBase = styled.div`
padding: var(--vertical-padding) var(--horizontal-padding);
+ ${({ theme, isKeySelected }) =>
+ isKeySelected ? `background: ${theme.background.transparent.light};` : ''}
+
${({ isHoverBackgroundDisabled }) =>
isHoverBackgroundDisabled ?? HOVER_BACKGROUND};