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:
@ -79,7 +79,10 @@ export const ObjectFilterDropdownFilterSelect = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFilterDefinitionUsedInDropdown(availableFilterDefinition);
|
setFilterDefinitionUsedInDropdown(availableFilterDefinition);
|
||||||
|
|
||||||
if (availableFilterDefinition.type === 'RELATION') {
|
if (
|
||||||
|
availableFilterDefinition.type === 'RELATION' ||
|
||||||
|
availableFilterDefinition.type === 'SELECT'
|
||||||
|
) {
|
||||||
setHotkeyScope(RelationPickerHotkeyScope.RelationPicker);
|
setHotkeyScope(RelationPickerHotkeyScope.RelationPicker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { Key } from 'ts-key-enum';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||||
import { useOptionsForSelect } from '@/object-record/object-filter-dropdown/hooks/useOptionsForSelect';
|
import { useOptionsForSelect } from '@/object-record/object-filter-dropdown/hooks/useOptionsForSelect';
|
||||||
|
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
|
||||||
|
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { MenuItemMultiSelect } from '@/ui/navigation/menu-item/components/MenuItemMultiSelect';
|
import { MenuItemMultiSelect } from '@/ui/navigation/menu-item/components/MenuItemMultiSelect';
|
||||||
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const EMPTY_FILTER_VALUE = '';
|
export const EMPTY_FILTER_VALUE = '';
|
||||||
@ -27,6 +35,17 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
|||||||
selectFilter,
|
selectFilter,
|
||||||
} = useFilterDropdown();
|
} = useFilterDropdown();
|
||||||
|
|
||||||
|
const { closeDropdown } = useDropdown();
|
||||||
|
|
||||||
|
const { selectedItemIdState } = useSelectableListStates({
|
||||||
|
selectableListScopeId: MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { handleResetSelectedPosition } = useSelectableList(
|
||||||
|
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||||
const filterDefinitionUsedInDropdown = useRecoilValue(
|
const filterDefinitionUsedInDropdown = useRecoilValue(
|
||||||
filterDefinitionUsedInDropdownState,
|
filterDefinitionUsedInDropdownState,
|
||||||
);
|
);
|
||||||
@ -67,6 +86,16 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
|||||||
}
|
}
|
||||||
}, [objectFilterDropdownSelectedOptionValues, selectOptions]);
|
}, [objectFilterDropdownSelectedOptionValues, selectOptions]);
|
||||||
|
|
||||||
|
useScopedHotkeys(
|
||||||
|
[Key.Escape],
|
||||||
|
() => {
|
||||||
|
closeDropdown();
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
},
|
||||||
|
RelationPickerHotkeyScope.RelationPicker,
|
||||||
|
[closeDropdown, handleResetSelectedPosition],
|
||||||
|
);
|
||||||
|
|
||||||
const handleMultipleOptionSelectChange = (
|
const handleMultipleOptionSelectChange = (
|
||||||
optionChanged: SelectOptionForFilter,
|
optionChanged: SelectOptionForFilter,
|
||||||
isSelected: boolean,
|
isSelected: boolean,
|
||||||
@ -108,6 +137,7 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
|||||||
value: newFilterValue,
|
value: newFilterValue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
handleResetSelectedPosition();
|
||||||
};
|
};
|
||||||
|
|
||||||
const optionsInDropdown = selectableOptions?.filter((option) =>
|
const optionsInDropdown = selectableOptions?.filter((option) =>
|
||||||
@ -117,22 +147,36 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const showNoResult = optionsInDropdown?.length === 0;
|
const showNoResult = optionsInDropdown?.length === 0;
|
||||||
|
const objectRecordsIds = optionsInDropdown.map((option) => option.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<SelectableList
|
||||||
{optionsInDropdown?.map((option) => (
|
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
|
||||||
<MenuItemMultiSelect
|
selectableItemIdArray={objectRecordsIds}
|
||||||
key={option.id}
|
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
|
||||||
selected={option.isSelected}
|
onEnter={(itemId) => {
|
||||||
onSelectChange={(selected) =>
|
const option = optionsInDropdown.find((option) => option.id === itemId);
|
||||||
handleMultipleOptionSelectChange(option, selected)
|
if (isDefined(option)) {
|
||||||
}
|
handleMultipleOptionSelectChange(option, !option.isSelected);
|
||||||
text={option.label}
|
}
|
||||||
color={option.color}
|
}}
|
||||||
className=""
|
>
|
||||||
/>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
))}
|
{optionsInDropdown?.map((option) => (
|
||||||
|
<MenuItemMultiSelect
|
||||||
|
key={option.id}
|
||||||
|
selected={option.isSelected}
|
||||||
|
isKeySelected={option.id === selectedItemId}
|
||||||
|
onSelectChange={(selected) =>
|
||||||
|
handleMultipleOptionSelectChange(option, selected)
|
||||||
|
}
|
||||||
|
text={option.label}
|
||||||
|
color={option.color}
|
||||||
|
className=""
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</DropdownMenuItemsContainer>
|
||||||
{showNoResult && <MenuItem text="No result" />}
|
{showNoResult && <MenuItem text="No result" />}
|
||||||
</DropdownMenuItemsContainer>
|
</SelectableList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil';
|
|||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||||
|
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||||
import { MultipleRecordSelectDropdown } from '@/object-record/select/components/MultipleRecordSelectDropdown';
|
import { MultipleRecordSelectDropdown } from '@/object-record/select/components/MultipleRecordSelectDropdown';
|
||||||
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
|
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
|
||||||
import { SelectableRecord } from '@/object-record/select/types/SelectableRecord';
|
import { SelectableRecord } from '@/object-record/select/types/SelectableRecord';
|
||||||
@ -131,6 +132,8 @@ export const ObjectFilterDropdownRecordSelect = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MultipleRecordSelectDropdown
|
<MultipleRecordSelectDropdown
|
||||||
|
selectableListId="object-filter-record-select-id"
|
||||||
|
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
|
||||||
recordsToSelect={recordsToSelect}
|
recordsToSelect={recordsToSelect}
|
||||||
filteredSelectedRecords={filteredSelectedRecords}
|
filteredSelectedRecords={filteredSelectedRecords}
|
||||||
selectedRecords={selectedRecords}
|
selectedRecords={selectedRecords}
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
|
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
|
||||||
|
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
|
||||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
|
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 { MenuItemMultiSelectTag } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectTag';
|
import { MenuItemMultiSelectTag } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectTag';
|
||||||
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
@ -23,7 +30,15 @@ export type MultiSelectFieldInputProps = {
|
|||||||
export const MultiSelectFieldInput = ({
|
export const MultiSelectFieldInput = ({
|
||||||
onCancel,
|
onCancel,
|
||||||
}: MultiSelectFieldInputProps) => {
|
}: MultiSelectFieldInputProps) => {
|
||||||
const { persistField, fieldDefinition, fieldValues } = useMultiSelectField();
|
const { selectedItemIdState } = useSelectableListStates({
|
||||||
|
selectableListScopeId: MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
|
||||||
|
});
|
||||||
|
const { handleResetSelectedPosition } = useSelectableList(
|
||||||
|
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
|
||||||
|
);
|
||||||
|
const { persistField, fieldDefinition, fieldValues, hotkeyScope } =
|
||||||
|
useMultiSelectField();
|
||||||
|
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
const [searchFilter, setSearchFilter] = useState('');
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -46,6 +61,16 @@ export const MultiSelectFieldInput = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useScopedHotkeys(
|
||||||
|
Key.Escape,
|
||||||
|
() => {
|
||||||
|
onCancel?.();
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
},
|
||||||
|
hotkeyScope,
|
||||||
|
[onCancel, handleResetSelectedPosition],
|
||||||
|
);
|
||||||
|
|
||||||
useListenClickOutside({
|
useListenClickOutside({
|
||||||
refs: [containerRef],
|
refs: [containerRef],
|
||||||
callback: (event) => {
|
callback: (event) => {
|
||||||
@ -58,34 +83,52 @@ export const MultiSelectFieldInput = ({
|
|||||||
if (weAreNotInAnHTMLInput && isDefined(onCancel)) {
|
if (weAreNotInAnHTMLInput && isDefined(onCancel)) {
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
|
handleResetSelectedPosition();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const optionIds = optionsInDropDown.map((option) => option.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRelationPickerContainer ref={containerRef}>
|
<SelectableList
|
||||||
<DropdownMenu data-select-disable>
|
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
|
||||||
<DropdownMenuSearchInput
|
selectableItemIdArray={optionIds}
|
||||||
value={searchFilter}
|
hotkeyScope={hotkeyScope}
|
||||||
onChange={(event) => setSearchFilter(event.currentTarget.value)}
|
onEnter={(itemId) => {
|
||||||
autoFocus
|
const option = optionsInDropDown.find(
|
||||||
/>
|
(option) => option.value === itemId,
|
||||||
<DropdownMenuSeparator />
|
);
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
if (isDefined(option)) {
|
||||||
{optionsInDropDown.map((option) => {
|
persistField(formatNewSelectedOptions(option.value));
|
||||||
return (
|
}
|
||||||
<MenuItemMultiSelectTag
|
}}
|
||||||
key={option.value}
|
>
|
||||||
selected={fieldValues?.includes(option.value) || false}
|
<StyledRelationPickerContainer ref={containerRef}>
|
||||||
text={option.label}
|
<DropdownMenu data-select-disable>
|
||||||
color={option.color}
|
<DropdownMenuSearchInput
|
||||||
onClick={() =>
|
value={searchFilter}
|
||||||
persistField(formatNewSelectedOptions(option.value))
|
onChange={(event) => setSearchFilter(event.currentTarget.value)}
|
||||||
}
|
autoFocus
|
||||||
/>
|
/>
|
||||||
);
|
<DropdownMenuSeparator />
|
||||||
})}
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
</DropdownMenuItemsContainer>
|
{optionsInDropDown.map((option) => {
|
||||||
</DropdownMenu>
|
return (
|
||||||
</StyledRelationPickerContainer>
|
<MenuItemMultiSelectTag
|
||||||
|
key={option.value}
|
||||||
|
selected={fieldValues?.includes(option.value) || false}
|
||||||
|
text={option.label}
|
||||||
|
color={option.color}
|
||||||
|
onClick={() =>
|
||||||
|
persistField(formatNewSelectedOptions(option.value))
|
||||||
|
}
|
||||||
|
isKeySelected={selectedItemId === option.value}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DropdownMenuItemsContainer>
|
||||||
|
</DropdownMenu>
|
||||||
|
</StyledRelationPickerContainer>
|
||||||
|
</SelectableList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,19 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
import { useClearField } from '@/object-record/record-field/hooks/useClearField';
|
import { useClearField } from '@/object-record/record-field/hooks/useClearField';
|
||||||
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
|
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
|
||||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||||
|
import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList';
|
||||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
|
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 { MenuItemSelectTag } from '@/ui/navigation/menu-item/components/MenuItemSelectTag';
|
import { MenuItemSelectTag } from '@/ui/navigation/menu-item/components/MenuItemSelectTag';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
@ -31,8 +36,15 @@ export const SelectFieldInput = ({
|
|||||||
}: SelectFieldInputProps) => {
|
}: SelectFieldInputProps) => {
|
||||||
const { persistField, fieldDefinition, fieldValue, hotkeyScope } =
|
const { persistField, fieldDefinition, fieldValue, hotkeyScope } =
|
||||||
useSelectField();
|
useSelectField();
|
||||||
|
const { selectedItemIdState } = useSelectableListStates({
|
||||||
|
selectableListScopeId: SINGLE_ENTITY_SELECT_BASE_LIST,
|
||||||
|
});
|
||||||
|
const { handleResetSelectedPosition } = useSelectableList(
|
||||||
|
SINGLE_ENTITY_SELECT_BASE_LIST,
|
||||||
|
);
|
||||||
const clearField = useClearField();
|
const clearField = useClearField();
|
||||||
|
|
||||||
|
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
const [searchFilter, setSearchFilter] = useState('');
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -69,10 +81,21 @@ export const SelectFieldInput = ({
|
|||||||
);
|
);
|
||||||
if (weAreNotInAnHTMLInput && isDefined(onCancel)) {
|
if (weAreNotInAnHTMLInput && isDefined(onCancel)) {
|
||||||
onCancel();
|
onCancel();
|
||||||
|
handleResetSelectedPosition();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useScopedHotkeys(
|
||||||
|
Key.Escape,
|
||||||
|
() => {
|
||||||
|
onCancel?.();
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
},
|
||||||
|
hotkeyScope,
|
||||||
|
[onCancel, handleResetSelectedPosition],
|
||||||
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
Key.Enter,
|
Key.Enter,
|
||||||
() => {
|
() => {
|
||||||
@ -83,45 +106,71 @@ export const SelectFieldInput = ({
|
|||||||
if (isDefined(selectedOption)) {
|
if (isDefined(selectedOption)) {
|
||||||
onSubmit?.(() => persistField(selectedOption.value));
|
onSubmit?.(() => persistField(selectedOption.value));
|
||||||
}
|
}
|
||||||
|
handleResetSelectedPosition();
|
||||||
},
|
},
|
||||||
hotkeyScope,
|
hotkeyScope,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const optionIds = [
|
||||||
|
`No ${fieldDefinition.label}`,
|
||||||
|
...optionsInDropDown.map((option) => option.value),
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRelationPickerContainer ref={containerRef}>
|
<SelectableList
|
||||||
<DropdownMenu data-select-disable>
|
selectableListId={SINGLE_ENTITY_SELECT_BASE_LIST}
|
||||||
<DropdownMenuSearchInput
|
selectableItemIdArray={optionIds}
|
||||||
value={searchFilter}
|
hotkeyScope={hotkeyScope}
|
||||||
onChange={(event) => setSearchFilter(event.currentTarget.value)}
|
onEnter={(itemId) => {
|
||||||
autoFocus
|
const option = optionsInDropDown.find(
|
||||||
/>
|
(option) => option.value === itemId,
|
||||||
<DropdownMenuSeparator />
|
);
|
||||||
|
if (isDefined(option)) {
|
||||||
|
onSubmit?.(() => persistField(option.value));
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StyledRelationPickerContainer ref={containerRef}>
|
||||||
|
<DropdownMenu data-select-disable>
|
||||||
|
<DropdownMenuSearchInput
|
||||||
|
value={searchFilter}
|
||||||
|
onChange={(event) => setSearchFilter(event.currentTarget.value)}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{fieldDefinition.metadata.isNullable && (
|
{fieldDefinition.metadata.isNullable ?? (
|
||||||
<MenuItemSelectTag
|
|
||||||
key={`No ${fieldDefinition.label}`}
|
|
||||||
selected={false}
|
|
||||||
text={`No ${fieldDefinition.label}`}
|
|
||||||
color="transparent"
|
|
||||||
variant="outline"
|
|
||||||
onClick={handleClearField}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{optionsInDropDown.map((option) => {
|
|
||||||
return (
|
|
||||||
<MenuItemSelectTag
|
<MenuItemSelectTag
|
||||||
key={option.value}
|
key={`No ${fieldDefinition.label}`}
|
||||||
selected={option.value === fieldValue}
|
selected={false}
|
||||||
text={option.label}
|
text={`No ${fieldDefinition.label}`}
|
||||||
color={option.color}
|
color="transparent"
|
||||||
onClick={() => onSubmit?.(() => persistField(option.value))}
|
variant="outline"
|
||||||
|
onClick={handleClearField}
|
||||||
|
isKeySelected={selectedItemId === `No ${fieldDefinition.label}`}
|
||||||
/>
|
/>
|
||||||
);
|
)}
|
||||||
})}
|
|
||||||
</DropdownMenuItemsContainer>
|
{optionsInDropDown.map((option) => {
|
||||||
</DropdownMenu>
|
return (
|
||||||
</StyledRelationPickerContainer>
|
<MenuItemSelectTag
|
||||||
|
key={option.value}
|
||||||
|
selected={option.value === fieldValue}
|
||||||
|
text={option.label}
|
||||||
|
color={option.color}
|
||||||
|
onClick={() => {
|
||||||
|
onSubmit?.(() => persistField(option.value));
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
}}
|
||||||
|
isKeySelected={selectedItemId === option.value}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DropdownMenuItemsContainer>
|
||||||
|
</DropdownMenu>
|
||||||
|
</StyledRelationPickerContainer>
|
||||||
|
</SelectableList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,15 +1,9 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { useCallback, useRef } from 'react';
|
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
|
||||||
|
|
||||||
import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates';
|
import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates';
|
||||||
import { MultipleObjectRecordOnClickOutsideEffect } from '@/object-record/relation-picker/components/MultipleObjectRecordOnClickOutsideEffect';
|
import { MultipleObjectRecordOnClickOutsideEffect } from '@/object-record/relation-picker/components/MultipleObjectRecordOnClickOutsideEffect';
|
||||||
import { MultipleObjectRecordSelectItem } from '@/object-record/relation-picker/components/MultipleObjectRecordSelectItem';
|
import { MultipleObjectRecordSelectItem } from '@/object-record/relation-picker/components/MultipleObjectRecordSelectItem';
|
||||||
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
|
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
|
||||||
import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates';
|
import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates';
|
||||||
import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext';
|
import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext';
|
||||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
|
||||||
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
|
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
|
||||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
@ -17,10 +11,18 @@ import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/Dropdow
|
|||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
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 { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
|
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
import { Key } from 'ts-key-enum';
|
||||||
import { IconPlus, isDefined } from 'twenty-ui';
|
import { IconPlus, isDefined } from 'twenty-ui';
|
||||||
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
export const StyledSelectableItem = styled(SelectableItem)`
|
export const StyledSelectableItem = styled(SelectableItem)`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -35,6 +37,8 @@ export const MultiRecordSelect = ({
|
|||||||
onCreate?: ((searchInput?: string) => void) | (() => void);
|
onCreate?: ((searchInput?: string) => void) | (() => void);
|
||||||
}) => {
|
}) => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
|
||||||
|
|
||||||
const relationPickerScopedId = useAvailableScopeIdOrThrow(
|
const relationPickerScopedId = useAvailableScopeIdOrThrow(
|
||||||
RelationPickerScopeInternalContext,
|
RelationPickerScopeInternalContext,
|
||||||
@ -43,6 +47,9 @@ export const MultiRecordSelect = ({
|
|||||||
const { objectRecordsIdsMultiSelectState, recordMultiSelectIsLoadingState } =
|
const { objectRecordsIdsMultiSelectState, recordMultiSelectIsLoadingState } =
|
||||||
useObjectRecordMultiSelectScopedStates(relationPickerScopedId);
|
useObjectRecordMultiSelectScopedStates(relationPickerScopedId);
|
||||||
|
|
||||||
|
const { handleResetSelectedPosition } = useSelectableList(
|
||||||
|
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
|
||||||
|
);
|
||||||
const recordMultiSelectIsLoading = useRecoilValue(
|
const recordMultiSelectIsLoading = useRecoilValue(
|
||||||
recordMultiSelectIsLoadingState,
|
recordMultiSelectIsLoadingState,
|
||||||
);
|
);
|
||||||
@ -62,6 +69,21 @@ export const MultiRecordSelect = ({
|
|||||||
leading: true,
|
leading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHotkeyScope(relationPickerScopedId);
|
||||||
|
}, [setHotkeyScope, relationPickerScopedId]);
|
||||||
|
|
||||||
|
useScopedHotkeys(
|
||||||
|
Key.Escape,
|
||||||
|
() => {
|
||||||
|
onSubmit?.();
|
||||||
|
goBackToPreviousHotkeyScope();
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
},
|
||||||
|
relationPickerScopedId,
|
||||||
|
[onSubmit, goBackToPreviousHotkeyScope, handleResetSelectedPosition],
|
||||||
|
);
|
||||||
|
|
||||||
const debouncedOnCreate = useDebouncedCallback(
|
const debouncedOnCreate = useDebouncedCallback(
|
||||||
() => onCreate?.(relationPickerSearchFilter),
|
() => onCreate?.(relationPickerSearchFilter),
|
||||||
500,
|
500,
|
||||||
@ -97,14 +119,21 @@ export const MultiRecordSelect = ({
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
|
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
|
||||||
selectableItemIdArray={objectRecordsIdsMultiSelect}
|
selectableItemIdArray={objectRecordsIdsMultiSelect}
|
||||||
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
|
hotkeyScope={relationPickerScopedId}
|
||||||
|
onEnter={(selectedId) => {
|
||||||
|
onChange?.(selectedId);
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{objectRecordsIdsMultiSelect?.map((recordId) => {
|
{objectRecordsIdsMultiSelect?.map((recordId) => {
|
||||||
return (
|
return (
|
||||||
<MultipleObjectRecordSelectItem
|
<MultipleObjectRecordSelectItem
|
||||||
key={recordId}
|
key={recordId}
|
||||||
objectRecordId={recordId}
|
objectRecordId={recordId}
|
||||||
onChange={onChange}
|
onChange={(recordId) => {
|
||||||
|
onChange?.(recordId);
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -35,13 +35,19 @@ export const MultipleObjectRecordSelectItem = ({
|
|||||||
RelationPickerScopeInternalContext,
|
RelationPickerScopeInternalContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { objectRecordMultiSelectFamilyState } =
|
const {
|
||||||
useObjectRecordMultiSelectScopedStates(scopeId);
|
objectRecordMultiSelectFamilyState,
|
||||||
|
objectRecordMultiSelectCheckedRecordsIdsState,
|
||||||
|
} = useObjectRecordMultiSelectScopedStates(scopeId);
|
||||||
|
|
||||||
const record = useRecoilValue(
|
const record = useRecoilValue(
|
||||||
objectRecordMultiSelectFamilyState(objectRecordId),
|
objectRecordMultiSelectFamilyState(objectRecordId),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const objectRecordMultiSelectCheckedRecordsIds = useRecoilValue(
|
||||||
|
objectRecordMultiSelectCheckedRecordsIdsState,
|
||||||
|
);
|
||||||
|
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -50,12 +56,18 @@ export const MultipleObjectRecordSelectItem = ({
|
|||||||
onChange?.(objectRecordId);
|
onChange?.(objectRecordId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { selected, recordIdentifier } = record;
|
const { recordIdentifier } = record;
|
||||||
|
|
||||||
if (!isDefined(recordIdentifier)) {
|
if (!isDefined(recordIdentifier)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selected = objectRecordMultiSelectCheckedRecordsIds.find(
|
||||||
|
(checkedObjectRecord) => checkedObjectRecord === objectRecordId,
|
||||||
|
)
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledSelectableItem itemId={objectRecordId} key={objectRecordId + v4()}>
|
<StyledSelectableItem itemId={objectRecordId} key={objectRecordId + v4()}>
|
||||||
<MenuItemMultiSelectAvatar
|
<MenuItemMultiSelectAvatar
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { IconComponent, IconPlus } from 'twenty-ui';
|
import { IconComponent, IconPlus } from 'twenty-ui';
|
||||||
|
|
||||||
import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect';
|
import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect';
|
||||||
|
import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList';
|
||||||
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
|
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
|
||||||
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
|
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
@ -32,6 +35,7 @@ export type SingleEntitySelectMenuItemsProps = {
|
|||||||
isAllEntitySelected?: boolean;
|
isAllEntitySelected?: boolean;
|
||||||
isAllEntitySelectShown?: boolean;
|
isAllEntitySelectShown?: boolean;
|
||||||
onAllEntitySelected?: () => void;
|
onAllEntitySelected?: () => void;
|
||||||
|
hotkeyScope?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SingleEntitySelectMenuItems = ({
|
export const SingleEntitySelectMenuItems = ({
|
||||||
@ -49,21 +53,68 @@ export const SingleEntitySelectMenuItems = ({
|
|||||||
isAllEntitySelected,
|
isAllEntitySelected,
|
||||||
isAllEntitySelectShown,
|
isAllEntitySelectShown,
|
||||||
onAllEntitySelected,
|
onAllEntitySelected,
|
||||||
|
hotkeyScope = RelationPickerHotkeyScope.RelationPicker,
|
||||||
}: SingleEntitySelectMenuItemsProps) => {
|
}: SingleEntitySelectMenuItemsProps) => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const entitiesInDropdown = [selectedEntity, ...entitiesToSelect].filter(
|
const createNewRecord = showCreateButton
|
||||||
|
? {
|
||||||
|
__typename: '',
|
||||||
|
id: 'add-new',
|
||||||
|
name: 'Add New',
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const selectNone = emptyLabel
|
||||||
|
? {
|
||||||
|
__typename: '',
|
||||||
|
id: 'select-none',
|
||||||
|
name: emptyLabel,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const selectAll = isAllEntitySelectShown
|
||||||
|
? {
|
||||||
|
__typename: '',
|
||||||
|
id: 'select-all',
|
||||||
|
name: selectAllLabel,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const entitiesInDropdown = [
|
||||||
|
selectAll,
|
||||||
|
selectNone,
|
||||||
|
selectedEntity,
|
||||||
|
...entitiesToSelect,
|
||||||
|
createNewRecord,
|
||||||
|
].filter(
|
||||||
(entity): entity is EntityForSelect =>
|
(entity): entity is EntityForSelect =>
|
||||||
isDefined(entity) && isNonEmptyString(entity.name),
|
isDefined(entity) && isNonEmptyString(entity.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { isSelectedItemIdSelector, handleResetSelectedPosition } =
|
||||||
|
useSelectableList(SINGLE_ENTITY_SELECT_BASE_LIST);
|
||||||
|
|
||||||
|
const isSelectedAddNewButton = useRecoilValue(
|
||||||
|
isSelectedItemIdSelector('add-new'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSelectedSelectNoneButton = useRecoilValue(
|
||||||
|
isSelectedItemIdSelector('select-none'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSelectedSelectAllButton = useRecoilValue(
|
||||||
|
isSelectedItemIdSelector('select-all'),
|
||||||
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.Escape],
|
[Key.Escape],
|
||||||
() => {
|
() => {
|
||||||
|
handleResetSelectedPosition();
|
||||||
onCancel?.();
|
onCancel?.();
|
||||||
},
|
},
|
||||||
RelationPickerHotkeyScope.RelationPicker,
|
hotkeyScope,
|
||||||
[onCancel],
|
[onCancel, handleResetSelectedPosition],
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectableItemIds = entitiesInDropdown.map((entity) => entity.id);
|
const selectableItemIds = entitiesInDropdown.map((entity) => entity.id);
|
||||||
@ -71,66 +122,95 @@ export const SingleEntitySelectMenuItems = ({
|
|||||||
return (
|
return (
|
||||||
<div ref={containerRef}>
|
<div ref={containerRef}>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListId="single-entity-select-base-list"
|
selectableListId={SINGLE_ENTITY_SELECT_BASE_LIST}
|
||||||
selectableItemIdArray={selectableItemIds}
|
selectableItemIdArray={selectableItemIds}
|
||||||
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
|
hotkeyScope={hotkeyScope}
|
||||||
onEnter={(itemId) => {
|
onEnter={(itemId) => {
|
||||||
if (showCreateButton === true) {
|
if (itemId === 'add-new' && showCreateButton === true) {
|
||||||
onCreate?.();
|
onCreate?.();
|
||||||
} else {
|
} else {
|
||||||
const entity = entitiesInDropdown.findIndex(
|
const entityIndex = entitiesInDropdown.findIndex(
|
||||||
(entity) => entity.id === itemId,
|
(entity) => entity.id === itemId,
|
||||||
);
|
);
|
||||||
onEntitySelected(entitiesInDropdown[entity]);
|
onEntitySelected(entitiesInDropdown[entityIndex]);
|
||||||
}
|
}
|
||||||
|
handleResetSelectedPosition();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<DropdownMenuSkeletonItem />
|
<DropdownMenuSkeletonItem />
|
||||||
) : entitiesInDropdown.length === 0 && !isAllEntitySelectShown ? (
|
) : entitiesInDropdown.length === 0 && !isAllEntitySelectShown ? (
|
||||||
<MenuItem text="No result" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{isAllEntitySelectShown &&
|
|
||||||
selectAllLabel &&
|
|
||||||
onAllEntitySelected && (
|
|
||||||
<MenuItemSelect
|
|
||||||
key="select-all"
|
|
||||||
onClick={() => onAllEntitySelected()}
|
|
||||||
LeftIcon={SelectAllIcon}
|
|
||||||
text={selectAllLabel}
|
|
||||||
selected={!!isAllEntitySelected}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{emptyLabel && (
|
|
||||||
<MenuItemSelect
|
|
||||||
key="select-none"
|
|
||||||
onClick={() => onEntitySelected()}
|
|
||||||
LeftIcon={EmptyIcon}
|
|
||||||
text={emptyLabel}
|
|
||||||
selected={!selectedEntity}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{entitiesInDropdown?.map((entity) => (
|
|
||||||
<SelectableMenuItemSelect
|
|
||||||
key={entity.id}
|
|
||||||
entity={entity}
|
|
||||||
onEntitySelected={onEntitySelected}
|
|
||||||
selectedEntity={selectedEntity}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{showCreateButton && (
|
|
||||||
<>
|
<>
|
||||||
|
<MenuItem text="No result" />
|
||||||
{entitiesToSelect.length > 0 && <DropdownMenuSeparator />}
|
{entitiesToSelect.length > 0 && <DropdownMenuSeparator />}
|
||||||
<CreateNewButton
|
<CreateNewButton
|
||||||
|
key="add-new"
|
||||||
onClick={onCreate}
|
onClick={onCreate}
|
||||||
LeftIcon={IconPlus}
|
LeftIcon={IconPlus}
|
||||||
text="Add New"
|
text="Add New"
|
||||||
|
hovered={isSelectedAddNewButton}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
) : (
|
||||||
|
entitiesInDropdown?.map((entity) => {
|
||||||
|
switch (entity.id) {
|
||||||
|
case 'add-new': {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{entitiesToSelect.length > 0 && <DropdownMenuSeparator />}
|
||||||
|
<CreateNewButton
|
||||||
|
key={entity.id}
|
||||||
|
onClick={onCreate}
|
||||||
|
LeftIcon={IconPlus}
|
||||||
|
text="Add New"
|
||||||
|
hovered={isSelectedAddNewButton}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case 'select-none': {
|
||||||
|
return (
|
||||||
|
emptyLabel && (
|
||||||
|
<MenuItemSelect
|
||||||
|
key={entity.id}
|
||||||
|
onClick={() => onEntitySelected()}
|
||||||
|
LeftIcon={EmptyIcon}
|
||||||
|
text={emptyLabel}
|
||||||
|
selected={!selectedEntity}
|
||||||
|
hovered={isSelectedSelectNoneButton}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case 'select-all': {
|
||||||
|
return (
|
||||||
|
isAllEntitySelectShown &&
|
||||||
|
selectAllLabel &&
|
||||||
|
onAllEntitySelected && (
|
||||||
|
<MenuItemSelect
|
||||||
|
key={entity.id}
|
||||||
|
onClick={() => onAllEntitySelected()}
|
||||||
|
LeftIcon={SelectAllIcon}
|
||||||
|
text={selectAllLabel}
|
||||||
|
selected={!!isAllEntitySelected}
|
||||||
|
hovered={isSelectedSelectAllButton}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return (
|
||||||
|
<SelectableMenuItemSelect
|
||||||
|
key={entity.id}
|
||||||
|
entity={entity}
|
||||||
|
onEntitySelected={onEntitySelected}
|
||||||
|
selectedEntity={selectedEntity}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
)}
|
)}
|
||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</SelectableList>
|
</SelectableList>
|
||||||
|
|||||||
@ -79,6 +79,7 @@ export const SingleEntitySelectMenuItemsWithSearch = ({
|
|||||||
? entities.selectedEntities[0]
|
? entities.selectedEntities[0]
|
||||||
: undefined)
|
: undefined)
|
||||||
}
|
}
|
||||||
|
hotkeyScope={relationPickerScopeId}
|
||||||
onCreate={onCreateWithInput}
|
onCreate={onCreateWithInput}
|
||||||
{...{
|
{...{
|
||||||
EmptyIcon,
|
EmptyIcon,
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export const SINGLE_ENTITY_SELECT_BASE_LIST = 'single-entity-select-base-list';
|
||||||
@ -1,19 +1,30 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { Key } from 'ts-key-enum';
|
||||||
import { Avatar } from 'twenty-ui';
|
import { Avatar } from 'twenty-ui';
|
||||||
|
|
||||||
import { SelectableRecord } from '@/object-record/select/types/SelectableRecord';
|
import { SelectableRecord } from '@/object-record/select/types/SelectableRecord';
|
||||||
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
|
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
|
||||||
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
|
|
||||||
export const MultipleRecordSelectDropdown = ({
|
export const MultipleRecordSelectDropdown = ({
|
||||||
|
selectableListId,
|
||||||
|
hotkeyScope,
|
||||||
recordsToSelect,
|
recordsToSelect,
|
||||||
loadingRecords,
|
loadingRecords,
|
||||||
filteredSelectedRecords,
|
filteredSelectedRecords,
|
||||||
onChange,
|
onChange,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
}: {
|
}: {
|
||||||
|
selectableListId: string;
|
||||||
|
hotkeyScope: string;
|
||||||
recordsToSelect: SelectableRecord[];
|
recordsToSelect: SelectableRecord[];
|
||||||
filteredSelectedRecords: SelectableRecord[];
|
filteredSelectedRecords: SelectableRecord[];
|
||||||
selectedRecords: SelectableRecord[];
|
selectedRecords: SelectableRecord[];
|
||||||
@ -24,6 +35,15 @@ export const MultipleRecordSelectDropdown = ({
|
|||||||
) => void;
|
) => void;
|
||||||
loadingRecords: boolean;
|
loadingRecords: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { closeDropdown } = useDropdown();
|
||||||
|
const { selectedItemIdState } = useSelectableListStates({
|
||||||
|
selectableListScopeId: selectableListId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { handleResetSelectedPosition } = useSelectableList(selectableListId);
|
||||||
|
|
||||||
|
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||||
|
|
||||||
const handleRecordSelectChange = (
|
const handleRecordSelectChange = (
|
||||||
recordToSelect: SelectableRecord,
|
recordToSelect: SelectableRecord,
|
||||||
newSelectedValue: boolean,
|
newSelectedValue: boolean,
|
||||||
@ -51,35 +71,70 @@ export const MultipleRecordSelectDropdown = ({
|
|||||||
}
|
}
|
||||||
}, [recordsToSelect, filteredSelectedRecords, loadingRecords]);
|
}, [recordsToSelect, filteredSelectedRecords, loadingRecords]);
|
||||||
|
|
||||||
|
useScopedHotkeys(
|
||||||
|
[Key.Escape],
|
||||||
|
() => {
|
||||||
|
closeDropdown();
|
||||||
|
handleResetSelectedPosition();
|
||||||
|
},
|
||||||
|
hotkeyScope,
|
||||||
|
[closeDropdown, handleResetSelectedPosition],
|
||||||
|
);
|
||||||
|
|
||||||
const showNoResult =
|
const showNoResult =
|
||||||
recordsToSelect?.length === 0 &&
|
recordsToSelect?.length === 0 &&
|
||||||
searchFilter !== '' &&
|
searchFilter !== '' &&
|
||||||
filteredSelectedRecords?.length === 0 &&
|
filteredSelectedRecords?.length === 0 &&
|
||||||
!loadingRecords;
|
!loadingRecords;
|
||||||
|
|
||||||
|
const selectableItemIds = recordsInDropdown.map((record) => record.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<SelectableList
|
||||||
{recordsInDropdown?.map((record) => (
|
selectableListId={selectableListId}
|
||||||
<MenuItemMultiSelectAvatar
|
selectableItemIdArray={selectableItemIds}
|
||||||
key={record.id}
|
hotkeyScope={hotkeyScope}
|
||||||
selected={record.isSelected}
|
onEnter={(itemId) => {
|
||||||
onSelectChange={(newCheckedValue) =>
|
const record = recordsInDropdown.findIndex(
|
||||||
handleRecordSelectChange(record, newCheckedValue)
|
(entity) => entity.id === itemId,
|
||||||
}
|
);
|
||||||
avatar={
|
const recordIsSelectedInDropwdown = filteredSelectedRecords.find(
|
||||||
<Avatar
|
(entity) => entity.id === itemId,
|
||||||
avatarUrl={record.avatarUrl}
|
);
|
||||||
placeholderColorSeed={record.id}
|
handleRecordSelectChange(
|
||||||
placeholder={record.name}
|
recordsInDropdown[record],
|
||||||
size="md"
|
!recordIsSelectedInDropwdown,
|
||||||
type={record.avatarType ?? 'rounded'}
|
);
|
||||||
|
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 />}
|
||||||
{showNoResult && <MenuItem text="No result" />}
|
</DropdownMenuItemsContainer>
|
||||||
{loadingRecords && <DropdownMenuSkeletonItem />}
|
</SelectableList>
|
||||||
</DropdownMenuItemsContainer>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,7 +10,9 @@ import { useRef } from 'react';
|
|||||||
import { Keys } from 'react-hotkeys-hook';
|
import { Keys } from 'react-hotkeys-hook';
|
||||||
import { Key } from 'ts-key-enum';
|
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 { 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 { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
@ -66,6 +68,10 @@ export const Dropdown = ({
|
|||||||
|
|
||||||
const { isDropdownOpen, toggleDropdown, closeDropdown, dropdownWidth } =
|
const { isDropdownOpen, toggleDropdown, closeDropdown, dropdownWidth } =
|
||||||
useDropdown(dropdownId);
|
useDropdown(dropdownId);
|
||||||
|
|
||||||
|
const { handleResetSelectedPosition } = useSelectableList(
|
||||||
|
SINGLE_ENTITY_SELECT_BASE_LIST,
|
||||||
|
);
|
||||||
const offsetMiddlewares = [];
|
const offsetMiddlewares = [];
|
||||||
|
|
||||||
if (isDefined(dropdownOffset.x)) {
|
if (isDefined(dropdownOffset.x)) {
|
||||||
@ -94,6 +100,7 @@ export const Dropdown = ({
|
|||||||
|
|
||||||
if (isDropdownOpen) {
|
if (isDropdownOpen) {
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
|
handleResetSelectedPosition();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -107,9 +114,10 @@ export const Dropdown = ({
|
|||||||
[Key.Escape],
|
[Key.Escape],
|
||||||
() => {
|
() => {
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
|
handleResetSelectedPosition();
|
||||||
},
|
},
|
||||||
dropdownHotkeyScope.scope,
|
dropdownHotkeyScope.scope,
|
||||||
[closeDropdown],
|
[closeDropdown, handleResetSelectedPosition],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -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 { 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) => {
|
export const useSelectableList = (selectableListId?: string) => {
|
||||||
const {
|
const {
|
||||||
@ -24,6 +30,18 @@ export const useSelectableList = (selectableListId?: string) => {
|
|||||||
resetSelectedItemIdState();
|
resetSelectedItemIdState();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleResetSelectedPosition = useRecoilCallback(
|
||||||
|
({ snapshot, set }) =>
|
||||||
|
() => {
|
||||||
|
const selectedItemId = getSnapshotValue(snapshot, selectedItemIdState);
|
||||||
|
if (isDefined(selectedItemId)) {
|
||||||
|
set(selectedItemIdState, null);
|
||||||
|
set(isSelectedItemIdSelector(selectedItemId), false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[selectedItemIdState, isSelectedItemIdSelector],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectableListId: scopeId,
|
selectableListId: scopeId,
|
||||||
|
|
||||||
@ -31,5 +49,6 @@ export const useSelectableList = (selectableListId?: string) => {
|
|||||||
isSelectedItemIdSelector,
|
isSelectedItemIdSelector,
|
||||||
setSelectableListOnEnter,
|
setSelectableListOnEnter,
|
||||||
resetSelectedItem,
|
resetSelectedItem,
|
||||||
|
handleResetSelectedPosition,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,6 +17,7 @@ type MenuItemMultiSelectProps = {
|
|||||||
color?: ThemeColor;
|
color?: ThemeColor;
|
||||||
LeftIcon?: IconComponent;
|
LeftIcon?: IconComponent;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
isKeySelected?: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
className: string;
|
className: string;
|
||||||
onSelectChange?: (selected: boolean) => void;
|
onSelectChange?: (selected: boolean) => void;
|
||||||
@ -27,6 +28,7 @@ export const MenuItemMultiSelect = ({
|
|||||||
LeftIcon,
|
LeftIcon,
|
||||||
text,
|
text,
|
||||||
selected,
|
selected,
|
||||||
|
isKeySelected,
|
||||||
className,
|
className,
|
||||||
onSelectChange,
|
onSelectChange,
|
||||||
}: MenuItemMultiSelectProps) => {
|
}: MenuItemMultiSelectProps) => {
|
||||||
@ -35,7 +37,11 @@ export const MenuItemMultiSelect = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledMenuItemBase className={className} onClick={handleOnClick}>
|
<StyledMenuItemBase
|
||||||
|
isKeySelected={isKeySelected}
|
||||||
|
className={className}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
<StyledLeftContentWithCheckboxContainer>
|
<StyledLeftContentWithCheckboxContainer>
|
||||||
<Checkbox checked={selected} />
|
<Checkbox checked={selected} />
|
||||||
{color ? (
|
{color ? (
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
type MenuItemMultiSelectTagProps = {
|
type MenuItemMultiSelectTagProps = {
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
isKeySelected?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
color: ThemeColor;
|
color: ThemeColor;
|
||||||
text: string;
|
text: string;
|
||||||
@ -24,10 +25,15 @@ export const MenuItemMultiSelectTag = ({
|
|||||||
selected,
|
selected,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
|
isKeySelected,
|
||||||
text,
|
text,
|
||||||
}: MenuItemMultiSelectTagProps) => {
|
}: MenuItemMultiSelectTagProps) => {
|
||||||
return (
|
return (
|
||||||
<StyledMenuItemBase onClick={onClick} className={className}>
|
<StyledMenuItemBase
|
||||||
|
isKeySelected={isKeySelected}
|
||||||
|
onClick={onClick}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
size={CheckboxSize.Small}
|
size={CheckboxSize.Small}
|
||||||
shape={CheckboxShape.Squared}
|
shape={CheckboxShape.Squared}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { StyledMenuItemSelect } from './MenuItemSelect';
|
|||||||
|
|
||||||
type MenuItemSelectTagProps = {
|
type MenuItemSelectTagProps = {
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
isKeySelected?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
color: ThemeColor | 'transparent';
|
color: ThemeColor | 'transparent';
|
||||||
@ -17,6 +18,7 @@ type MenuItemSelectTagProps = {
|
|||||||
export const MenuItemSelectTag = ({
|
export const MenuItemSelectTag = ({
|
||||||
color,
|
color,
|
||||||
selected,
|
selected,
|
||||||
|
isKeySelected,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
text,
|
text,
|
||||||
@ -29,6 +31,7 @@ export const MenuItemSelectTag = ({
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={className}
|
className={className}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
isKeySelected={isKeySelected}
|
||||||
>
|
>
|
||||||
<StyledMenuItemLeftContent>
|
<StyledMenuItemLeftContent>
|
||||||
<Tag variant={variant} color={color} text={text} />
|
<Tag variant={variant} color={color} text={text} />
|
||||||
|
|||||||
@ -32,6 +32,9 @@ export const StyledMenuItemBase = styled.div<MenuItemBaseProps>`
|
|||||||
|
|
||||||
padding: var(--vertical-padding) var(--horizontal-padding);
|
padding: var(--vertical-padding) var(--horizontal-padding);
|
||||||
|
|
||||||
|
${({ theme, isKeySelected }) =>
|
||||||
|
isKeySelected ? `background: ${theme.background.transparent.light};` : ''}
|
||||||
|
|
||||||
${({ isHoverBackgroundDisabled }) =>
|
${({ isHoverBackgroundDisabled }) =>
|
||||||
isHoverBackgroundDisabled ?? HOVER_BACKGROUND};
|
isHoverBackgroundDisabled ?? HOVER_BACKGROUND};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user