Refactor RecordSingleSelect and RecordMultiSelect to be synchronous (#10469)

## Context
We are experiencing a lot of re-rendering / flash on MultiRecordSelect /
SingleRecordSelect / RelationPicker.

This PR is a first step to refactor this components

## Scope

Only move files to uniformize and prepare for the upcoming refactoring

## Vision
- SingleRecordPicker
- MultipleRecordPicker
- sharing RecordPicker tooling (RecordPickerComponentInstanceContext,
searchState)

Used in other areas:
- RelationPicker (which is actually only a subcomponent of
RelationToOneFieldInput)
- RelationFromManyFieldInput
- WorkflowRelationFieldInput
- etc.

+ remove all effects
+ migrate to the latest APIs
+ make a pass on re-renders to remove them completely (we likely need to
use a bit more familyStates here)
This commit is contained in:
Charles Bochet
2025-02-25 15:48:25 +01:00
committed by GitHub
parent 589a0c7b2d
commit a1c7e3279c
80 changed files with 338 additions and 329 deletions

View File

@ -24,11 +24,11 @@ import {
objectRecordMultiSelectComponentFamilyState,
} from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState';
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
import { MultipleRecordPicker } from '@/object-record/record-picker/components/MultipleRecordPicker';
import { ActivityTargetInlineCellEditModeMultiRecordsEffect } from '@/object-record/record-picker/legacy/components/ActivityTargetInlineCellEditModeMultiRecordsEffect';
import { ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect } from '@/object-record/record-picker/legacy/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ActivityTargetInlineCellEditModeMultiRecordsEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect';
import { ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect';
import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { prefillRecord } from '@/object-record/utils/prefillRecord';
type ActivityTargetInlineCellEditModeProps = {
@ -269,7 +269,7 @@ export const ActivityTargetInlineCellEditMode = ({
selectedObjectRecordIds={selectedTargetObjectIds}
/>
<ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect />
<MultiRecordSelect onSubmit={handleSubmit} onChange={handleChange} />
<MultipleRecordPicker onSubmit={handleSubmit} onChange={handleChange} />
</RecordPickerComponentInstanceContext.Provider>
</>
);

View File

@ -7,8 +7,8 @@ import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlur
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { useMultiObjectSearch } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
import { useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap } from '@/object-record/relation-picker/hooks/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap';
import { useMultiObjectSearch } from '@/object-record/record-picker/hooks/useMultiObjectSearch';
import { useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap } from '@/object-record/record-picker/hooks/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap';
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { t } from '@lingui/core/macro';

View File

@ -4,8 +4,8 @@ import { useRecoilValue } from 'recoil';
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
export const ObjectMetadataItemsProvider = ({

View File

@ -5,7 +5,7 @@ import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
import { useCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecordsQueryVariables';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { MultiObjectRecordQueryResult } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
export const useCombinedFindManyRecords = ({
operationSignatures,

View File

@ -4,7 +4,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { MultiObjectRecordQueryResult } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
export const useCombinedGetTotalCount = ({
objectMetadataItems,

View File

@ -8,7 +8,7 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';

View File

@ -12,7 +12,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';

View File

@ -5,8 +5,6 @@ import { v4 } from 'uuid';
import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -14,13 +12,16 @@ import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/inter
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared';
import { MenuItem, MenuItemMultiSelect } from 'twenty-ui';
@ -53,17 +54,19 @@ export const ObjectFilterDropdownOptionSelect = () => {
selectedOperandInDropdownComponentState,
);
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
ObjectFilterDropdownComponentInstanceContext,
);
const { applyRecordFilter } = useApplyRecordFilter();
const { closeDropdown } = useDropdown();
const { selectedItemIdState } = useSelectableListStates({
selectableListScopeId: MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
selectableListScopeId: componentInstanceId,
});
const { resetSelectedItem } = useSelectableList(
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
);
const { resetSelectedItem } = useSelectableList(componentInstanceId);
const selectedItemId = useRecoilValue(selectedItemIdState);
@ -159,7 +162,7 @@ export const ObjectFilterDropdownOptionSelect = () => {
return (
<SelectableList
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
selectableListId={componentInstanceId}
selectableItemIdArray={objectRecordsIds}
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
onEnter={(itemId) => {

View File

@ -12,7 +12,7 @@ import { selectedFilterComponentState } from '@/object-record/object-filter-drop
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';

View File

@ -12,7 +12,7 @@ import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';

View File

@ -8,8 +8,8 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object-
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';

View File

@ -2,11 +2,11 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector';
import { SingleRecordPicker } from '@/object-record/record-picker/components/SingleRecordPicker';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
@ -70,7 +70,7 @@ export const RecordBoardColumnNewOpportunity = ({
<RecordPickerComponentInstanceContext.Provider
value={{ instanceId: RelationPickerHotkeyScope.RelationPicker }}
>
<SingleRecordSelect
<SingleRecordPicker
onCancel={() => handleCreateSuccess(position, columnId, false)}
onRecordSelected={(company) =>
company ? handleEntitySelect(position, company) : null

View File

@ -1,9 +1,9 @@
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector';
import { useRecordSelectSearch } from '@/object-record/relation-picker/hooks/useRecordSelectSearch';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { useRecordSelectSearch } from '@/object-record/record-picker/hooks/useRecordSelectSearch';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useCallback, useContext } from 'react';
import { RecoilState, useRecoilCallback } from 'recoil';
@ -71,7 +71,7 @@ export const useAddNewCard = () => {
labelValue: string,
position: 'first' | 'last',
isOpportunity: boolean,
company?: RecordForSelect,
company?: SingleRecordPickerRecord,
) => {
if (
(isOpportunity && company !== null) ||
@ -220,7 +220,7 @@ export const useAddNewCard = () => {
const handleEntitySelect = useCallback(
(
position: 'first' | 'last',
company: RecordForSelect,
company: SingleRecordPickerRecord,
columnId?: string,
) => {
const columnDefinitionId = getColumnDefinitionId(columnId);

View File

@ -1,4 +1,4 @@
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
export type NewCard = {
@ -7,7 +7,7 @@ export type NewCard = {
isCreating: boolean;
position: 'first' | 'last';
isOpportunity: boolean;
company: RecordForSelect | null;
company: SingleRecordPickerRecord | null;
};
export const recordBoardNewRecordByColumnIdComponentFamilyState =

View File

@ -4,9 +4,10 @@ import { FormFieldInputContainer } from '@/object-record/record-field/form-types
import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone';
import { FormMultiSelectFieldInputHotKeyScope } from '@/object-record/record-field/form-types/constants/FormMultiSelectFieldInputHotKeyScope';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata';
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
import { SelectOption } from '@/spreadsheet-import/types';
import { MultiSelectDisplay } from '@/ui/field/display/components/MultiSelectDisplay';
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
@ -74,7 +75,8 @@ export const FormMultiSelectFieldInput = ({
const inputId = useId();
const theme = useTheme();
const hotkeyScope = MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID;
const hotkeyScope =
FormMultiSelectFieldInputHotKeyScope.FormMultiSelectFieldInput;
const {
setHotkeyScopeAndMemorizePreviousScope,
@ -234,6 +236,9 @@ export const FormMultiSelectFieldInput = ({
draftValue.editingMode === 'edit' && (
<OverlayContainer>
<MultiSelectInput
selectableListComponentInstanceId={
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
}
hotkeyScope={hotkeyScope}
options={options}
onCancel={onCancel}

View File

@ -3,8 +3,8 @@ import { FormFieldInputInputContainer } from '@/object-record/record-field/form-
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList';
import { SelectOption } from '@/spreadsheet-import/types';
import { SelectDisplay } from '@/ui/field/display/components/SelectDisplay';
import { SelectInput } from '@/ui/field/input/components/SelectInput';
@ -141,7 +141,7 @@ export const FormSelectFieldInput = ({
const [filteredOptions, setFilteredOptions] = useState<SelectOption[]>([]);
const { resetSelectedItem } = useSelectableList(
SINGLE_RECORD_SELECT_BASE_LIST,
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID,
);
const clearField = () => {
@ -295,7 +295,9 @@ export const FormSelectFieldInput = ({
draftValue.editingMode === 'edit' && (
<OverlayContainer>
<SelectInput
selectableListId={SINGLE_RECORD_SELECT_BASE_LIST}
selectableListComponentInstanceId={
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
}
selectableItemIdArray={optionIds}
hotkeyScope={hotkeyScope}
onEnter={handleSelectEnter}

View File

@ -0,0 +1,3 @@
export enum FormMultiSelectFieldInputHotKeyScope {
FormMultiSelectFieldInput = 'form-multi-select-field-input',
}

View File

@ -24,7 +24,6 @@ import { isFieldRelationToOneValue } from '@/object-record/record-field/types/gu
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue';
@ -32,6 +31,7 @@ import { isFieldRichText } from '@/object-record/record-field/types/guards/isFie
import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2';
import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue';
import { isFieldRichTextV2Value } from '@/object-record/record-field/types/guards/isFieldRichTextValueV2';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName';
import { FieldContext } from '../contexts/FieldContext';
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
@ -156,7 +156,7 @@ export const usePersistField = () => {
);
if (fieldIsRelationToOneObject) {
const value = valueToPersist as RecordForSelect;
const value = valueToPersist as SingleRecordPickerRecord;
updateRecord?.({
variables: {
where: { id: recordId },

View File

@ -5,15 +5,15 @@ import { useGetButtonIcon } from '@/object-record/record-field/hooks/useGetButto
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
import { FieldRelationValue } from '@/object-record/record-field/types/FieldMetadata';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { FieldContext } from '../../contexts/FieldContext';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldRelation } from '../../types/guards/isFieldRelation';
export const useRelationField = <
T extends RecordForSelect | RecordForSelect[],
T extends SingleRecordPickerRecord | SingleRecordPickerRecord[],
>() => {
const { recordId, fieldDefinition, maxWidth } = useContext(FieldContext);
const button = useGetButtonIcon();

View File

@ -1,4 +1,5 @@
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
type MultiSelectFieldInputProps = {
@ -13,6 +14,9 @@ export const MultiSelectFieldInput = ({
return (
<MultiSelectInput
selectableListComponentInstanceId={
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
}
hotkeyScope={hotkeyScope}
options={fieldDefinition.metadata.options}
onCancel={onCancel}

View File

@ -7,10 +7,9 @@ import { useUpdateRelationFromManyFieldInput } from '@/object-record/record-fiel
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MultipleRecordPicker } from '@/object-record/record-picker/components/MultipleRecordPicker';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
type RelationFromManyFieldInputProps = {
onSubmit?: FieldInputEvent;
@ -51,19 +50,16 @@ export const RelationFromManyFieldInput = ({
recordId,
});
const { dropdownPlacement } = useDropdown(recordPickerInstanceId);
return (
<>
<RecordPickerComponentInstanceContext.Provider
value={{ instanceId: recordPickerInstanceId }}
>
<RelationFromManyFieldInputMultiRecordsEffect />
<MultiRecordSelect
<MultipleRecordPicker
onSubmit={handleSubmit}
onChange={updateRelation}
onCreate={createNewRecordAndOpenRightDrawer}
dropdownPlacement={dropdownPlacement}
/>
</RecordPickerComponentInstanceContext.Provider>
</>

View File

@ -5,15 +5,16 @@ import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useOb
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField';
import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState';
import { useRecordPickerRecordsOptions } from '@/object-record/relation-picker/hooks/useRecordPickerRecordsOptions';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { useRecordPickerRecordsOptions } from '@/object-record/record-picker/hooks/useRecordPickerRecordsOptions';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const RelationFromManyFieldInputMultiRecordsEffect = () => {
const { fieldValue, fieldDefinition } = useRelationField<RecordForSelect[]>();
const { fieldValue, fieldDefinition } =
useRelationField<SingleRecordPickerRecord[]>();
const instanceId = useAvailableComponentInstanceIdOrThrow(
RecordPickerComponentInstanceContext,
);
@ -113,7 +114,9 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => {
useEffect(() => {
setObjectRecordMultiSelectCheckedRecordsIds(
fieldValue
? fieldValue.map((fieldValueItem: RecordForSelect) => fieldValueItem.id)
? fieldValue.map(
(fieldValueItem: SingleRecordPickerRecord) => fieldValueItem.id,
)
: [],
);
}, [fieldValue, setObjectRecordMultiSelectCheckedRecordsIds]);

View File

@ -5,15 +5,15 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { SearchPickerInitialValueEffect } from '@/object-record/relation-picker/components/SearchPickerInitialValueEffect';
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { SingleRecordPicker } from '@/object-record/record-picker/components/SingleRecordPicker';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { SearchPickerInitialValueEffect } from '@/object-record/record-picker/legacy/components/SearchPickerInitialValueEffect';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
export type RelationPickerProps = {
selectedRecordId?: string;
onSubmit: (selectedRecord: RecordForSelect | null) => void;
onSubmit: (selectedRecord: SingleRecordPickerRecord | null) => void;
onCancel?: () => void;
width?: number;
excludedRecordIds?: string[];
@ -33,7 +33,7 @@ export const RelationPicker = ({
const recordPickerInstanceId = RelationPickerHotkeyScope.RelationPicker;
const handleRecordSelected = (
selectedRecord: RecordForSelect | null | undefined,
selectedRecord: SingleRecordPickerRecord | null | undefined,
) => onSubmit(selectedRecord ?? null);
const { objectMetadataItem: relationObjectMetadataItem } =
@ -63,7 +63,7 @@ export const RelationPicker = ({
initialValueForSearchFilter={initialSearchFilter}
recordPickerInstanceId={recordPickerInstanceId}
/>
<SingleRecordSelect
<SingleRecordPicker
EmptyIcon={IconForbid}
emptyLabel={'No ' + fieldDefinition.label}
onCancel={onCancel}

View File

@ -1,9 +1,8 @@
import { RelationPicker } from '@/object-record/relation-picker/components/RelationPicker';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { RelationPicker } from '@/object-record/record-field/meta-types/input/components/RelationPicker';
import { usePersistField } from '../../../hooks/usePersistField';
import { useRelationField } from '../../hooks/useRelationField';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { FieldInputEvent } from './DateTimeFieldInput';
export type RelationToOneFieldInputProps = {
@ -16,11 +15,11 @@ export const RelationToOneFieldInput = ({
onCancel,
}: RelationToOneFieldInputProps) => {
const { fieldDefinition, initialSearchValue, fieldValue } =
useRelationField<RecordForSelect>();
useRelationField<SingleRecordPickerRecord>();
const persistField = usePersistField();
const handleSubmit = (newEntity: RecordForSelect | null) => {
const handleSubmit = (newEntity: SingleRecordPickerRecord | null) => {
onSubmit?.(() => persistField(newEntity?.record ?? null));
};

View File

@ -1,7 +1,7 @@
import { useClearField } from '@/object-record/record-field/hooks/useClearField';
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList';
import { SelectOption } from '@/spreadsheet-import/types';
import { SelectInput } from '@/ui/field/input/components/SelectInput';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
@ -25,7 +25,7 @@ export const SelectFieldInput = ({
const [filteredOptions, setFilteredOptions] = useState<SelectOption[]>([]);
const { resetSelectedItem } = useSelectableList(
SINGLE_RECORD_SELECT_BASE_LIST,
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID,
);
const clearField = useClearField();
@ -61,7 +61,9 @@ export const SelectFieldInput = ({
return (
<SelectInput
selectableListId={SINGLE_RECORD_SELECT_BASE_LIST}
selectableListComponentInstanceId={
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
}
selectableItemIdArray={optionIds}
hotkeyScope={hotkeyScope}
onEnter={(itemId) => {

View File

@ -18,7 +18,7 @@ import {
} from '~/testing/mock-data/users';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { getCanvasElementForDropdownTesting } from 'twenty-ui';
import {
RelationToOneFieldInput,

View File

@ -0,0 +1,2 @@
export const SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID =
'select-field-input-selectable-list-component-instance-id';

View File

@ -2,7 +2,7 @@ import { ThemeColor } from 'twenty-ui';
import { RATING_VALUES } from '@/object-record/record-field/meta-types/constants/RatingValues';
import { ZodHelperLiteral } from '@/object-record/record-field/types/ZodHelperLiteral';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { ConnectedAccountProvider } from 'twenty-shared';
import * as z from 'zod';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
@ -260,9 +260,9 @@ export type FieldRatingValue = (typeof RATING_VALUES)[number] | null;
export type FieldSelectValue = string | null;
export type FieldMultiSelectValue = string[] | null;
export type FieldRelationToOneValue = RecordForSelect | null;
export type FieldRelationToOneValue = SingleRecordPickerRecord | null;
export type FieldRelationFromManyValue = RecordForSelect[] | [];
export type FieldRelationFromManyValue = SingleRecordPickerRecord[] | [];
export type FieldRelationValue<
T extends FieldRelationToOneValue | FieldRelationFromManyValue,

View File

@ -9,12 +9,12 @@ import { useGetButtonIcon } from '@/object-record/record-field/hooks/useGetButto
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { useInlineCell } from '../hooks/useInlineCell';
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';

View File

@ -1,9 +1,8 @@
import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates';
import { MultipleObjectRecordOnClickOutsideEffect } from '@/object-record/relation-picker/components/MultipleObjectRecordOnClickOutsideEffect';
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 { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState';
import { MultipleRecordPickerMenuItem } from '@/object-record/record-picker/components/MultipleRecordPickerMenuItem';
import { RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-picker/constants/RecordPickerSelectableListComponentInstanceId';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
@ -16,13 +15,13 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
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 { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import styled from '@emotion/styled';
import { Placement } from '@floating-ui/react';
import { useCallback, useEffect, useRef } from 'react';
import { useCallback, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { isDefined } from 'twenty-shared';
@ -34,7 +33,7 @@ export const StyledSelectableItem = styled(SelectableItem)`
width: 100%;
`;
export const MultiRecordSelect = ({
export const MultipleRecordPicker = ({
onChange,
onSubmit,
onCreate,
@ -46,7 +45,6 @@ export const MultiRecordSelect = ({
dropdownPlacement?: Placement | null;
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const setHotkeyScope = useSetHotkeyScope();
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
const instanceId = useAvailableComponentInstanceIdOrThrow(
@ -57,7 +55,7 @@ export const MultiRecordSelect = ({
useObjectRecordMultiSelectScopedStates(instanceId);
const { resetSelectedItem } = useSelectableList(
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID,
);
const recordMultiSelectIsLoading = useRecoilValue(
recordMultiSelectIsLoadingState,
@ -71,6 +69,7 @@ export const MultiRecordSelect = ({
recordPickerSearchFilterComponentState,
instanceId,
);
const recordPickerSearchFilter = useRecoilComponentValueV2(
recordPickerSearchFilterComponentState,
instanceId,
@ -78,21 +77,28 @@ export const MultiRecordSelect = ({
const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission();
useEffect(() => {
setHotkeyScope(instanceId);
}, [setHotkeyScope, instanceId]);
const handleSubmit = () => {
onSubmit?.();
goBackToPreviousHotkeyScope();
resetSelectedItem();
};
useScopedHotkeys(
Key.Escape,
() => {
onSubmit?.();
goBackToPreviousHotkeyScope();
resetSelectedItem();
handleSubmit();
},
instanceId,
[onSubmit, goBackToPreviousHotkeyScope, resetSelectedItem],
[handleSubmit],
);
useListenClickOutside({
refs: [containerRef],
callback: handleSubmit,
listenerId: 'MULTI_RECORD_SELECT_LISTENER_ID',
hotkeyScope: instanceId,
});
const handleFilterChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setSearchFilter(event.currentTarget.value);
@ -104,7 +110,7 @@ export const MultiRecordSelect = ({
const results = (
<DropdownMenuItemsContainer hasMaxHeight>
<SelectableList
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
selectableListId={RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID}
selectableItemIdArray={objectRecordsIdsMultiSelect}
hotkeyScope={instanceId}
onEnter={(selectedId) => {
@ -114,7 +120,7 @@ export const MultiRecordSelect = ({
>
{objectRecordsIdsMultiSelect?.map((recordId) => {
return (
<MultipleObjectRecordSelectItem
<MultipleRecordPickerMenuItem
key={recordId}
objectRecordId={recordId}
onChange={(recordId) => {
@ -137,61 +143,49 @@ export const MultiRecordSelect = ({
);
return (
<>
<MultipleObjectRecordOnClickOutsideEffect
containerRef={containerRef}
onClickOutside={() => {
onSubmit?.();
}}
<DropdownMenu ref={containerRef} data-select-disable width={200}>
{dropdownPlacement?.includes('end') && (
<>
{isDefined(onCreate) && !hasObjectReadOnlyPermission && (
<DropdownMenuItemsContainer scrollable={false}>
{createNewButton}
</DropdownMenuItemsContainer>
)}
<DropdownMenuSeparator />
{objectRecordsIdsMultiSelect?.length > 0 && results}
{recordMultiSelectIsLoading && !recordPickerSearchFilter && (
<>
<DropdownMenuSkeletonItem />
<DropdownMenuSeparator />
</>
)}
{objectRecordsIdsMultiSelect?.length > 0 && <DropdownMenuSeparator />}
</>
)}
<DropdownMenuSearchInput
value={recordPickerSearchFilter}
onChange={handleFilterChange}
autoFocus
/>
<DropdownMenu ref={containerRef} data-select-disable width={200}>
{dropdownPlacement?.includes('end') && (
<>
{isDefined(onCreate) && !hasObjectReadOnlyPermission && (
<DropdownMenuItemsContainer scrollable={false}>
{createNewButton}
</DropdownMenuItemsContainer>
)}
<DropdownMenuSeparator />
{objectRecordsIdsMultiSelect?.length > 0 && results}
{recordMultiSelectIsLoading && !recordPickerSearchFilter && (
<>
<DropdownMenuSkeletonItem />
<DropdownMenuSeparator />
</>
)}
{objectRecordsIdsMultiSelect?.length > 0 && (
{(dropdownPlacement?.includes('start') ||
isUndefinedOrNull(dropdownPlacement)) && (
<>
<DropdownMenuSeparator />
{recordMultiSelectIsLoading && !recordPickerSearchFilter && (
<>
<DropdownMenuSkeletonItem />
<DropdownMenuSeparator />
)}
</>
)}
<DropdownMenuSearchInput
value={recordPickerSearchFilter}
onChange={handleFilterChange}
autoFocus
/>
{(dropdownPlacement?.includes('start') ||
isUndefinedOrNull(dropdownPlacement)) && (
<>
<DropdownMenuSeparator />
{recordMultiSelectIsLoading && !recordPickerSearchFilter && (
<>
<DropdownMenuSkeletonItem />
<DropdownMenuSeparator />
</>
)}
{objectRecordsIdsMultiSelect?.length > 0 && results}
{objectRecordsIdsMultiSelect?.length > 0 && (
<DropdownMenuSeparator />
)}
{isDefined(onCreate) && (
<DropdownMenuItemsContainer scrollable={false}>
{createNewButton}
</DropdownMenuItemsContainer>
)}
</>
)}
</DropdownMenu>
</>
</>
)}
{objectRecordsIdsMultiSelect?.length > 0 && results}
{objectRecordsIdsMultiSelect?.length > 0 && <DropdownMenuSeparator />}
{isDefined(onCreate) && (
<DropdownMenuItemsContainer scrollable={false}>
{createNewButton}
</DropdownMenuItemsContainer>
)}
</>
)}
</DropdownMenu>
);
};

View File

@ -3,8 +3,8 @@ import { useRecoilValue } from 'recoil';
import { Avatar, MenuItemMultiSelectAvatar } from 'twenty-ui';
import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates';
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-picker/constants/RecordPickerSelectableListComponentInstanceId';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
@ -15,7 +15,7 @@ export const StyledSelectableItem = styled(SelectableItem)`
width: 100%;
`;
export const MultipleObjectRecordSelectItem = ({
export const MultipleRecordPickerMenuItem = ({
objectRecordId,
onChange,
}: {
@ -23,7 +23,7 @@ export const MultipleObjectRecordSelectItem = ({
onChange?: (changedRecordForSelectId: string) => void;
}) => {
const { isSelectedItemIdSelector } = useSelectableList(
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID,
);
const isSelectedByKeyboard = useRecoilValue(

View File

@ -1,18 +1,18 @@
import { useRef } from 'react';
import {
SingleRecordSelectMenuItemsWithSearch,
SingleRecordSelectMenuItemsWithSearchProps,
} from '@/object-record/relation-picker/components/SingleRecordSelectMenuItemsWithSearch';
SingleRecordPickerMenuItemsWithSearch,
SingleRecordPickerMenuItemsWithSearchProps,
} from '@/object-record/record-picker/components/SingleRecordPickerMenuItemsWithSearch';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from 'twenty-shared';
export type SingleRecordSelectProps = {
export type SingleRecordPickerProps = {
width?: number;
} & SingleRecordSelectMenuItemsWithSearchProps;
} & SingleRecordPickerMenuItemsWithSearchProps;
export const SingleRecordSelect = ({
export const SingleRecordPicker = ({
EmptyIcon,
emptyLabel,
excludedRecordIds,
@ -22,7 +22,7 @@ export const SingleRecordSelect = ({
objectNameSingular,
selectedRecordIds,
width = 200,
}: SingleRecordSelectProps) => {
}: SingleRecordPickerProps) => {
const containerRef = useRef<HTMLDivElement>(null);
useListenClickOutside({
@ -43,7 +43,7 @@ export const SingleRecordSelect = ({
return (
<DropdownMenu ref={containerRef} width={width} data-select-disable>
<SingleRecordSelectMenuItemsWithSearch
<SingleRecordPickerMenuItemsWithSearch
{...{
EmptyIcon,
emptyLabel,

View File

@ -2,28 +2,28 @@ import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { Avatar, MenuItemSelectAvatar } from 'twenty-ui';
import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-picker/constants/RecordPickerSelectableListComponentInstanceId';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
type SelectableMenuItemSelectProps = {
record: RecordForSelect;
onRecordSelected: (recordSelected?: RecordForSelect) => void;
selectedRecord?: RecordForSelect;
type SingleRecordPickerMenuItemProps = {
record: SingleRecordPickerRecord;
onRecordSelected: (recordSelected?: SingleRecordPickerRecord) => void;
selectedRecord?: SingleRecordPickerRecord;
};
const StyledSelectableItem = styled(SelectableItem)`
width: 100%;
`;
export const SelectableMenuItemSelect = ({
export const SingleRecordPickerMenuItem = ({
record,
onRecordSelected,
selectedRecord,
}: SelectableMenuItemSelectProps) => {
}: SingleRecordPickerMenuItemProps) => {
const { isSelectedItemIdSelector } = useSelectableList(
SINGLE_RECORD_SELECT_BASE_LIST,
RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID,
);
const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector(record.id));

View File

@ -4,8 +4,6 @@ import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { IconComponent, MenuItemSelect } from 'twenty-ui';
import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect';
import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -13,23 +11,25 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { isDefined } from 'twenty-shared';
import { RecordForSelect } from '../types/RecordForSelect';
import { RelationPickerHotkeyScope } from '../types/RelationPickerHotkeyScope';
import { SingleRecordPickerMenuItem } from '@/object-record/record-picker/components/SingleRecordPickerMenuItem';
import { RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-picker/constants/RecordPickerSelectableListComponentInstanceId';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { RelationPickerHotkeyScope } from '../legacy/types/RelationPickerHotkeyScope';
export type SingleRecordSelectMenuItemsProps = {
export type SingleRecordPickerMenuItemsProps = {
EmptyIcon?: IconComponent;
emptyLabel?: string;
recordsToSelect: RecordForSelect[];
recordsToSelect: SingleRecordPickerRecord[];
loading?: boolean;
onCancel?: () => void;
onRecordSelected: (entity?: RecordForSelect) => void;
selectedRecord?: RecordForSelect;
onRecordSelected: (entity?: SingleRecordPickerRecord) => void;
selectedRecord?: SingleRecordPickerRecord;
hotkeyScope?: string;
isFiltered: boolean;
shouldSelectEmptyOption?: boolean;
};
export const SingleRecordSelectMenuItems = ({
export const SingleRecordPickerMenuItems = ({
EmptyIcon,
emptyLabel,
recordsToSelect,
@ -40,7 +40,7 @@ export const SingleRecordSelectMenuItems = ({
hotkeyScope = RelationPickerHotkeyScope.RelationPicker,
isFiltered,
shouldSelectEmptyOption,
}: SingleRecordSelectMenuItemsProps) => {
}: SingleRecordPickerMenuItemsProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const selectNone = emptyLabel
@ -56,12 +56,12 @@ export const SingleRecordSelectMenuItems = ({
selectedRecord,
...recordsToSelect,
].filter(
(entity): entity is RecordForSelect =>
(entity): entity is SingleRecordPickerRecord =>
isDefined(entity) && isNonEmptyString(entity.name),
);
const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList(
SINGLE_RECORD_SELECT_BASE_LIST,
RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID,
);
const isSelectedSelectNoneButton = useRecoilValue(
@ -83,7 +83,7 @@ export const SingleRecordSelectMenuItems = ({
return (
<div ref={containerRef}>
<SelectableList
selectableListId={SINGLE_RECORD_SELECT_BASE_LIST}
selectableListId={RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID}
selectableItemIdArray={selectableItemIds}
hotkeyScope={hotkeyScope}
onEnter={(itemId) => {
@ -118,7 +118,7 @@ export const SingleRecordSelectMenuItems = ({
}
default: {
return (
<SelectableMenuItemSelect
<SingleRecordPickerMenuItem
key={record.id}
record={record}
onRecordSelected={onRecordSelected}

View File

@ -1,22 +1,24 @@
import {
SingleRecordSelectMenuItems,
SingleRecordSelectMenuItemsProps,
} from '@/object-record/relation-picker/components/SingleRecordSelectMenuItems';
import { useRecordPickerRecordsOptions } from '@/object-record/relation-picker/hooks/useRecordPickerRecordsOptions';
import { useRecordSelectSearch } from '@/object-record/relation-picker/hooks/useRecordSelectSearch';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
SingleRecordPickerMenuItems,
SingleRecordPickerMenuItemsProps,
} from '@/object-record/record-picker/components/SingleRecordPickerMenuItems';
import { useRecordPickerRecordsOptions } from '@/object-record/record-picker/hooks/useRecordPickerRecordsOptions';
import { useRecordSelectSearch } from '@/object-record/record-picker/hooks/useRecordSelectSearch';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { Placement } from '@floating-ui/react';
import { isDefined } from 'twenty-shared';
import { IconPlus } from 'twenty-ui';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export type SingleRecordSelectMenuItemsWithSearchProps = {
export type SingleRecordPickerMenuItemsWithSearchProps = {
excludedRecordIds?: string[];
onCreate?: ((searchInput?: string) => void) | (() => void);
objectNameSingular: string;
@ -24,7 +26,7 @@ export type SingleRecordSelectMenuItemsWithSearchProps = {
selectedRecordIds: string[];
dropdownPlacement?: Placement | null;
} & Pick<
SingleRecordSelectMenuItemsProps,
SingleRecordPickerMenuItemsProps,
| 'EmptyIcon'
| 'emptyLabel'
| 'onCancel'
@ -32,7 +34,7 @@ export type SingleRecordSelectMenuItemsWithSearchProps = {
| 'selectedRecord'
>;
export const SingleRecordSelectMenuItemsWithSearch = ({
export const SingleRecordPickerMenuItemsWithSearch = ({
EmptyIcon,
emptyLabel,
excludedRecordIds,
@ -42,7 +44,7 @@ export const SingleRecordSelectMenuItemsWithSearch = ({
objectNameSingular,
selectedRecordIds,
dropdownPlacement,
}: SingleRecordSelectMenuItemsWithSearchProps) => {
}: SingleRecordPickerMenuItemsWithSearchProps) => {
const { handleSearchFilterChange } = useRecordSelectSearch();
const recordPickerInstanceId = useAvailableComponentInstanceIdOrThrow(
@ -51,7 +53,12 @@ export const SingleRecordSelectMenuItemsWithSearch = ({
const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission();
const { records, recordPickerSearchFilter } = useRecordPickerRecordsOptions({
const recordPickerSearchFilter = useRecoilComponentValueV2(
recordPickerSearchFilterComponentState,
recordPickerInstanceId,
);
const { records } = useRecordPickerRecordsOptions({
objectNameSingular,
selectedRecordIds,
excludedRecordIds,
@ -79,7 +86,7 @@ export const SingleRecordSelectMenuItemsWithSearch = ({
)}
{records.recordsToSelect.length > 0 && <DropdownMenuSeparator />}
{shouldDisplayDropdownMenuItems && (
<SingleRecordSelectMenuItems
<SingleRecordPickerMenuItems
recordsToSelect={records.recordsToSelect}
loading={records.loading}
selectedRecord={records.selectedRecords?.[0]}
@ -107,7 +114,7 @@ export const SingleRecordSelectMenuItemsWithSearch = ({
<>
<DropdownMenuSeparator />
{shouldDisplayDropdownMenuItems && (
<SingleRecordSelectMenuItems
<SingleRecordPickerMenuItems
recordsToSelect={records.recordsToSelect}
loading={records.loading}
selectedRecord={records.selectedRecords?.[0]}

View File

@ -11,12 +11,12 @@ import { graphqlMocks } from '~/testing/graphqlMocks';
import { getPeopleMock } from '~/testing/mock-data/people';
import { sleep } from '~/utils/sleep';
import { RecordForSelect } from '../../types/RecordForSelect';
import { SingleRecordSelect } from '../SingleRecordSelect';
import { SingleRecordPicker } from '@/object-record/record-picker/components/SingleRecordPicker';
import { SingleRecordPickerRecord } from '../../types/SingleRecordPickerRecord';
const peopleMock = getPeopleMock();
const records = peopleMock.map<RecordForSelect>((person) => ({
const records = peopleMock.map<SingleRecordPickerRecord>((person) => ({
id: person.id,
name: person.name.firstName + ' ' + person.name.lastName,
avatarUrl: 'https://picsum.photos/200',
@ -24,9 +24,9 @@ const records = peopleMock.map<RecordForSelect>((person) => ({
record: { ...person, __typename: 'Person' },
}));
const meta: Meta<typeof SingleRecordSelect> = {
title: 'UI/Input/RelationPicker/SingleRecordSelect',
component: SingleRecordSelect,
const meta: Meta<typeof SingleRecordPicker> = {
title: 'UI/RecordPicker/SingleRecordPicker',
component: SingleRecordPicker,
decorators: [
ComponentDecorator,
ComponentWithRecoilScopeDecorator,
@ -53,7 +53,7 @@ const meta: Meta<typeof SingleRecordSelect> = {
};
export default meta;
type Story = StoryObj<typeof SingleRecordSelect>;
type Story = StoryObj<typeof SingleRecordPicker>;
export const Default: Story = {};

View File

@ -0,0 +1,2 @@
export const RECORD_PICKER_CLICK_OUTSIDE_LISTENER_ID =
'record-picker-click-outside-listener';

View File

@ -0,0 +1,2 @@
export const RECORD_PICKER_SELECTABLE_LIST_COMPONENT_INSTANCE_ID =
'record-picker-selectable-list-component-instance-id';

View File

@ -1,9 +1,9 @@
import { renderHook } from '@testing-library/react';
import { RecoilRoot } from 'recoil';
import { useLimitPerMetadataItem } from '@/object-metadata/hooks/useLimitPerMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
const instanceId = 'instanceId';
const Wrapper = ({ children }: { children: React.ReactNode }) => (

View File

@ -2,8 +2,8 @@ import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
const instanceId = 'instanceId';

View File

@ -2,8 +2,8 @@ import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap } from '@/object-record/relation-picker/hooks/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap } from '@/object-record/record-picker/hooks/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
const instanceId = 'instanceId';

View File

@ -2,9 +2,9 @@ import { act, renderHook } from '@testing-library/react';
import { ChangeEvent } from 'react';
import { RecoilRoot } from 'recoil';
import { useRecordSelectSearch } from '@/object-record/relation-picker/hooks/useRecordSelectSearch';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState';
import { useRecordSelectSearch } from '@/object-record/record-picker/hooks/useRecordSelectSearch';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
const instanceId = 'instanceId';

View File

@ -4,7 +4,7 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { formatMultiObjectRecordSearchResults } from '@/object-record/relation-picker/utils/formatMultiObjectRecordSearchResults';
import { formatMultiObjectRecordSearchResults } from '@/object-record/record-picker/utils/formatMultiObjectRecordSearchResults';
import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { isDefined } from 'twenty-shared';

View File

@ -1,12 +1,12 @@
import { useQuery } from '@apollo/client';
import { useRecoilValue } from 'recoil';
import { useLimitPerMetadataItem } from '@/object-metadata/hooks/useLimitPerMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { useGenerateCombinedSearchRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedSearchRecordsQuery';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem';
import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { MultiObjectRecordQueryResult } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { isObjectMetadataItemSearchableInCombinedRequest } from '@/object-record/utils/isObjectMetadataItemSearchableInCombinedRequest';
import { isDefined } from 'twenty-shared';

View File

@ -3,8 +3,8 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { formatMultiObjectRecordSearchResults } from '@/object-record/relation-picker/utils/formatMultiObjectRecordSearchResults';
import { MultiObjectRecordQueryResult } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { formatMultiObjectRecordSearchResults } from '@/object-record/record-picker/utils/formatMultiObjectRecordSearchResults';
import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { isDefined } from 'twenty-shared';

View File

@ -2,14 +2,14 @@ import { gql, useQuery } from '@apollo/client';
import { isNonEmptyArray } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { useLimitPerMetadataItem } from '@/object-metadata/hooks/useLimitPerMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem';
import {
MultiObjectRecordQueryResult,
useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray,
} from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem';
} from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { useOrderByFieldPerMetadataItem } from '@/object-record/record-picker/hooks/useOrderByFieldPerMetadataItem';
import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId';
import { capitalize, isDefined } from 'twenty-shared';

View File

@ -1,4 +1,4 @@
import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { useFilteredSearchRecordQuery } from '@/search/hooks/useFilteredSearchRecordQuery';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -22,5 +22,5 @@ export const useRecordPickerRecordsOptions = ({
objectNameSingular,
});
return { records, recordPickerSearchFilter };
return { records };
};

View File

@ -1,15 +1,22 @@
import { recordPickerPreselectedIdComponentState } from '@/object-record/record-picker/states/recordPickerPreselectedIdComponentState';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useDebouncedCallback } from 'use-debounce';
import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker';
export const useRecordSelectSearch = ({
recordPickerInstanceId,
}: {
recordPickerInstanceId?: string;
} = {}) => {
const { setRecordPickerSearchFilter, setRecordPickerPreselectedId } =
useRecordPicker({ recordPickerInstanceId });
const setRecordPickerSearchFilter = useSetRecoilComponentStateV2(
recordPickerSearchFilterComponentState,
recordPickerInstanceId,
);
const setRecordPickerPreselectedId = useSetRecoilComponentStateV2(
recordPickerPreselectedIdComponentState,
recordPickerInstanceId,
);
const debouncedSetSearchFilter = useDebouncedCallback(
setRecordPickerSearchFilter,
100,

View File

@ -9,12 +9,13 @@ import {
import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates';
import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState';
import { objectRecordMultiSelectMatchesFilterRecordsIdsComponentState } from '@/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
// Todo: this effect should be deprecated to use sync hooks
export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({
selectedObjectRecordIds,
}: {

View File

@ -3,13 +3,14 @@ import { useSetRecoilState } from 'recoil';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { objectRecordMultiSelectMatchesFilterRecordsIdsComponentState } from '@/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState';
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { useMultiObjectSearch } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState';
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { useMultiObjectSearch } from '@/object-record/record-picker/hooks/useMultiObjectSearch';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
// Todo: this effect should be deprecated to use sync hooks
export const ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect =
() => {
const instanceId = useAvailableComponentInstanceIdOrThrow(

View File

@ -1,10 +1,11 @@
import { useEffect } from 'react';
import { MULTI_OBJECT_RECORD_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordClickOutsideListenerId';
import { RECORD_PICKER_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-picker/constants/RecordPickerClickOutsideListenerId';
import { RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID } from '@/ui/layout/right-drawer/constants/RightDrawerClickOutsideListener';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
// Todo: this effect should be deprecated to use sync hooks
export const MultipleObjectRecordOnClickOutsideEffect = ({
containerRef,
onClickOutside,
@ -32,7 +33,7 @@ export const MultipleObjectRecordOnClickOutsideEffect = ({
onClickOutside();
},
listenerId: MULTI_OBJECT_RECORD_CLICK_OUTSIDE_LISTENER_ID,
listenerId: RECORD_PICKER_CLICK_OUTSIDE_LISTENER_ID,
});
return <></>;

View File

@ -1,7 +1,8 @@
import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useEffect } from 'react';
// Todo: this effect should be deprecated to use sync hooks
export const SearchPickerInitialValueEffect = ({
initialValueForSearchFilter,
recordPickerInstanceId,

View File

@ -1,4 +1,4 @@
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const recordPickerPreselectedIdComponentState = createComponentStateV2<

View File

@ -1,4 +1,4 @@
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const recordPickerSearchFilterComponentState =

View File

@ -0,0 +1,10 @@
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerSearchQuery } from '@/object-record/record-picker/types/RecordPickerSearchQuery';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const recordPickerSearchQueryComponentState =
createComponentStateV2<RecordPickerSearchQuery | null>({
key: 'recordPickerSearchQueryComponentState',
defaultValue: null,
componentInstanceContext: RecordPickerComponentInstanceContext,
});

View File

@ -0,0 +1,10 @@
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
export type MultipleRecordPickerRecords<
CustomRecordForRecordPicker extends SingleRecordPickerRecord,
> = {
selectedRecords: CustomRecordForRecordPicker[];
filteredSelectedRecords: CustomRecordForRecordPicker[];
recordsToSelect: CustomRecordForRecordPicker[];
loading: boolean;
};

View File

@ -1,3 +1,3 @@
export type SearchQuery = {
export type RecordPickerSearchQuery = {
computeFilterFields: (relationPickerType: string) => string[];
};

View File

@ -1,6 +1,6 @@
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
export type RecordForSelect = ObjectRecordIdentifier & {
export type SingleRecordPickerRecord = ObjectRecordIdentifier & {
record: ObjectRecord;
};

View File

@ -1,4 +1,4 @@
import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { MultiObjectRecordQueryResult } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
export const formatMultiObjectRecordSearchResults = (
searchResults: MultiObjectRecordQueryResult | undefined | null,

View File

@ -11,17 +11,17 @@ import { usePersistField } from '@/object-record/record-field/hooks/usePersistFi
import { RelationFromManyFieldInputMultiRecordsEffect } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect';
import { useUpdateRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useUpdateRelationFromManyFieldInput';
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { MultipleRecordPicker } from '@/object-record/record-picker/components/MultipleRecordPicker';
import { SingleRecordPickerMenuItemsWithSearch } from '@/object-record/record-picker/components/SingleRecordPickerMenuItemsWithSearch';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { RecordDetailRelationRecordsList } from '@/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsList';
import { RecordDetailSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailSection';
import { RecordDetailSectionHeader } from '@/object-record/record-show/record-detail-section/components/RecordDetailSectionHeader';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect';
import { SingleRecordSelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleRecordSelectMenuItemsWithSearch';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { prefetchIndexViewIdFromObjectMetadataItemFamilySelector } from '@/prefetch/states/selector/prefetchIndexViewIdFromObjectMetadataItemFamilySelector';
import { AppPath } from '@/types/AppPath';
@ -29,6 +29,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useLingui } from '@lingui/react/macro';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
@ -84,9 +85,10 @@ export const RecordDetailRelationSection = ({
const { closeDropdown, isDropdownOpen, dropdownPlacement } =
useDropdown(dropdownId);
const { setRecordPickerSearchFilter } = useRecordPicker({
recordPickerInstanceId: dropdownId,
});
const setRecordPickerSearchFilter = useSetRecoilComponentStateV2(
recordPickerSearchFilterComponentState,
dropdownId,
);
const handleCloseRelationPickerDropdown = useCallback(() => {
setRecordPickerSearchFilter('');
@ -98,7 +100,7 @@ export const RecordDetailRelationSection = ({
});
const handleRelationPickerEntitySelected = (
selectedRelationEntity?: RecordForSelect,
selectedRelationEntity?: SingleRecordPickerRecord,
) => {
closeDropdown();
@ -204,7 +206,7 @@ export const RecordDetailRelationSection = ({
value={{ instanceId: dropdownId }}
>
{isToOneObject ? (
<SingleRecordSelectMenuItemsWithSearch
<SingleRecordPickerMenuItemsWithSearch
EmptyIcon={IconForbid}
onRecordSelected={handleRelationPickerEntitySelected}
selectedRecordIds={relationRecordIds}
@ -216,7 +218,7 @@ export const RecordDetailRelationSection = ({
) : (
<>
<RelationFromManyFieldInputMultiRecordsEffect />
<MultiRecordSelect
<MultipleRecordPicker
onCreate={() => {
closeDropdown();
createNewRecordAndOpenRightDrawer?.();

View File

@ -5,12 +5,12 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { RecordUpdateContext } from '@/object-record/record-table/contexts/EntityUpdateMutationHookContext';
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';

View File

@ -1,2 +0,0 @@
export const MULTI_OBJECT_RECORD_CLICK_OUTSIDE_LISTENER_ID =
'multi-object-record-click-outside-listener';

View File

@ -1,2 +0,0 @@
export const MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID =
'multi-object-record-select-selectable-list';

View File

@ -1 +0,0 @@
export const SINGLE_RECORD_SELECT_BASE_LIST = 'single-record-select-base-list';

View File

@ -1,24 +0,0 @@
import { recordPickerPreselectedIdComponentState } from '@/object-record/relation-picker/states/recordPickerPreselectedIdComponentState';
import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
export const useRecordPicker = ({
recordPickerInstanceId,
}: {
recordPickerInstanceId?: string;
}) => {
const setRecordPickerSearchFilter = useSetRecoilComponentStateV2(
recordPickerSearchFilterComponentState,
recordPickerInstanceId,
);
const setRecordPickerPreselectedId = useSetRecoilComponentStateV2(
recordPickerPreselectedIdComponentState,
recordPickerInstanceId,
);
return {
setRecordPickerSearchFilter,
setRecordPickerPreselectedId,
};
};

View File

@ -1,10 +0,0 @@
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { SearchQuery } from '@/object-record/relation-picker/types/SearchQuery';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const searchQueryComponentState =
createComponentStateV2<SearchQuery | null>({
key: 'searchQueryComponentState',
defaultValue: null,
componentInstanceContext: RecordPickerComponentInstanceContext,
});

View File

@ -1,10 +0,0 @@
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
export type RecordsForMultipleRecordSelect<
CustomRecordForSelect extends RecordForSelect,
> = {
selectedRecords: CustomRecordForSelect[];
filteredSelectedRecords: CustomRecordForSelect[];
recordsToSelect: CustomRecordForSelect[];
loading: boolean;
};

View File

@ -1,7 +1,7 @@
import { TABLE_COLUMNS_DENY_LIST } from '@/object-record/constants/TableColumnsDenyList';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { TABLE_COLUMNS_DENY_LIST } from '@/object-record/relation-picker/constants/TableColumnsDenyList';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
export const filterAvailableTableColumns = (

View File

@ -5,9 +5,9 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordsForMultipleRecordSelect } from '@/object-record/relation-picker/types/RecordsForMultipleRecordSelect';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { MultipleRecordPickerRecords } from '@/object-record/record-picker/types/MultipleRecordPickerRecords';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import {
query,
@ -90,7 +90,7 @@ describe('useFilteredSearchRecordQuery', () => {
{ wrapper: Wrapper },
);
const expectedResult: RecordsForMultipleRecordSelect<any> = {
const expectedResult: MultipleRecordPickerRecords<any> = {
selectedRecords: [],
filteredSelectedRecords: [],
recordsToSelect: [],

View File

@ -1,8 +1,8 @@
import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier';
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
import { useSearchRecords } from '@/object-record/hooks/useSearchRecords';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { RecordsForMultipleRecordSelect } from '@/object-record/relation-picker/types/RecordsForMultipleRecordSelect';
import { MultipleRecordPickerRecords } from '@/object-record/record-picker/types/MultipleRecordPickerRecords';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from 'twenty-shared';
@ -21,7 +21,7 @@ export const useFilteredSearchRecordQuery = ({
excludedRecordIds?: string[];
objectNameSingular: string;
searchFilter?: string;
}): RecordsForMultipleRecordSelect<RecordForSelect> => {
}): MultipleRecordPickerRecords<SingleRecordPickerRecord> => {
const { mapToObjectRecordIdentifier } = useMapToObjectRecordIdentifier({
objectNameSingular,
});

View File

@ -3,7 +3,6 @@ import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata';
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
import { SelectOption } from '@/spreadsheet-import/types';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -19,6 +18,7 @@ import { MenuItemMultiSelectTag } from 'twenty-ui';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
type MultiSelectInputProps = {
selectableListComponentInstanceId: string;
values: FieldMultiSelectValue;
hotkeyScope: string;
onCancel?: () => void;
@ -27,6 +27,7 @@ type MultiSelectInputProps = {
};
export const MultiSelectInput = ({
selectableListComponentInstanceId,
values,
options,
hotkeyScope,
@ -34,10 +35,10 @@ export const MultiSelectInput = ({
onOptionSelected,
}: MultiSelectInputProps) => {
const { selectedItemIdState } = useSelectableListStates({
selectableListScopeId: MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
selectableListScopeId: selectableListComponentInstanceId,
});
const { resetSelectedItem } = useSelectableList(
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
selectableListComponentInstanceId,
);
const selectedItemId = useRecoilValue(selectedItemIdState);
@ -96,7 +97,7 @@ export const MultiSelectInput = ({
return (
<SelectableList
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
selectableListId={selectableListComponentInstanceId}
selectableItemIdArray={optionIds}
hotkeyScope={hotkeyScope}
onEnter={(itemId) => {

View File

@ -3,7 +3,7 @@ import { SelectInput as SelectBaseInput } from '@/ui/input/components/SelectInpu
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
type SelectInputProps = {
selectableListId: string;
selectableListComponentInstanceId: string;
selectableItemIdArray: string[];
hotkeyScope: string;
onEnter: (itemId: string) => void;
@ -17,7 +17,7 @@ type SelectInputProps = {
};
export const SelectInput = ({
selectableListId,
selectableListComponentInstanceId,
selectableItemIdArray,
hotkeyScope,
onEnter,
@ -31,7 +31,7 @@ export const SelectInput = ({
}: SelectInputProps) => {
return (
<SelectableList
selectableListId={selectableListId}
selectableListId={selectableListComponentInstanceId}
selectableItemIdArray={selectableItemIdArray}
hotkeyScope={hotkeyScope}
onEnter={onEnter}

View File

@ -2,14 +2,15 @@ import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
import { SingleRecordPicker } from '@/object-record/record-picker/components/SingleRecordPicker';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
import { WorkflowSingleRecordFieldChip } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip';
import { WorkflowVariablesDropdown } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdown';
@ -98,16 +99,17 @@ export const WorkflowSingleRecordPicker = ({
const { closeDropdown } = useDropdown(dropdownId);
const { setRecordPickerSearchFilter } = useRecordPicker({
recordPickerInstanceId: dropdownId,
});
const setRecordPickerSearchFilter = useSetRecoilComponentStateV2(
recordPickerSearchFilterComponentState,
dropdownId,
);
const handleCloseRelationPickerDropdown = useCallback(() => {
setRecordPickerSearchFilter('');
}, [setRecordPickerSearchFilter]);
const handleRecordSelected = (
selectedEntity: RecordForSelect | null | undefined,
selectedEntity: SingleRecordPickerRecord | null | undefined,
) => {
onChange?.(selectedEntity?.record?.id ?? '');
closeDropdown();
@ -153,7 +155,7 @@ export const WorkflowSingleRecordPicker = ({
<RecordPickerComponentInstanceContext.Provider
value={{ instanceId: dropdownId }}
>
<SingleRecordSelect
<SingleRecordPicker
EmptyIcon={IconForbid}
emptyLabel={'No ' + objectNameSingular}
onCancel={() => closeDropdown()}

View File

@ -1,11 +1,9 @@
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { Decorator } from '@storybook/react';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
export const RecordPickerDecorator: Decorator = (Story) => (
<RecordPickerComponentInstanceContext.Provider
value={{ instanceId: RelationPickerHotkeyScope.RelationPicker }}
value={{ instanceId: 'record-picker-decorator-instance-id' }}
>
<Story />
</RecordPickerComponentInstanceContext.Provider>