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, objectRecordMultiSelectComponentFamilyState,
} from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState'; } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState';
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; 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 { 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'; import { prefillRecord } from '@/object-record/utils/prefillRecord';
type ActivityTargetInlineCellEditModeProps = { type ActivityTargetInlineCellEditModeProps = {
@ -269,7 +269,7 @@ export const ActivityTargetInlineCellEditMode = ({
selectedObjectRecordIds={selectedTargetObjectIds} selectedObjectRecordIds={selectedTargetObjectIds}
/> />
<ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect /> <ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect />
<MultiRecordSelect onSubmit={handleSubmit} onChange={handleChange} /> <MultipleRecordPicker onSubmit={handleSubmit} onChange={handleChange} />
</RecordPickerComponentInstanceContext.Provider> </RecordPickerComponentInstanceContext.Provider>
</> </>
); );

View File

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

View File

@ -4,8 +4,8 @@ import { useRecoilValue } from 'recoil';
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect'; import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider'; import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader'; import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
export const ObjectMetadataItemsProvider = ({ 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 { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
import { useCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecordsQueryVariables'; import { useCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecordsQueryVariables';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery'; 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 = ({ export const useCombinedFindManyRecords = ({
operationSignatures, operationSignatures,

View File

@ -4,7 +4,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery'; 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 = ({ export const useCombinedGetTotalCount = ({
objectMetadataItems, objectMetadataItems,

View File

@ -8,7 +8,7 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; 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 { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { 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 { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; 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 { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; 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 { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
import { useOptionsForSelect } from '@/object-record/object-filter-dropdown/hooks/useOptionsForSelect'; import { useOptionsForSelect } from '@/object-record/object-filter-dropdown/hooks/useOptionsForSelect';
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; 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 { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; 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 { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState'; import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; 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 { 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
import { MenuItem, MenuItemMultiSelect } from 'twenty-ui'; import { MenuItem, MenuItemMultiSelect } from 'twenty-ui';
@ -53,17 +54,19 @@ export const ObjectFilterDropdownOptionSelect = () => {
selectedOperandInDropdownComponentState, selectedOperandInDropdownComponentState,
); );
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
ObjectFilterDropdownComponentInstanceContext,
);
const { applyRecordFilter } = useApplyRecordFilter(); const { applyRecordFilter } = useApplyRecordFilter();
const { closeDropdown } = useDropdown(); const { closeDropdown } = useDropdown();
const { selectedItemIdState } = useSelectableListStates({ const { selectedItemIdState } = useSelectableListStates({
selectableListScopeId: MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID, selectableListScopeId: componentInstanceId,
}); });
const { resetSelectedItem } = useSelectableList( const { resetSelectedItem } = useSelectableList(componentInstanceId);
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID,
);
const selectedItemId = useRecoilValue(selectedItemIdState); const selectedItemId = useRecoilValue(selectedItemIdState);
@ -159,7 +162,7 @@ export const ObjectFilterDropdownOptionSelect = () => {
return ( return (
<SelectableList <SelectableList
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID} selectableListId={componentInstanceId}
selectableItemIdArray={objectRecordsIds} selectableItemIdArray={objectRecordsIds}
hotkeyScope={RelationPickerHotkeyScope.RelationPicker} hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
onEnter={(itemId) => { 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 { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; 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 { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect'; import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
import { SelectableItem } from '@/object-record/select/types/SelectableItem'; 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 { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter'; import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; 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 { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { SelectableItem } from '@/object-record/select/types/SelectableItem'; import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; 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 { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; 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 { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; 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 { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector'; 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 { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; 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 { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
@ -70,7 +70,7 @@ export const RecordBoardColumnNewOpportunity = ({
<RecordPickerComponentInstanceContext.Provider <RecordPickerComponentInstanceContext.Provider
value={{ instanceId: RelationPickerHotkeyScope.RelationPicker }} value={{ instanceId: RelationPickerHotkeyScope.RelationPicker }}
> >
<SingleRecordSelect <SingleRecordPicker
onCancel={() => handleCreateSuccess(position, columnId, false)} onCancel={() => handleCreateSuccess(position, columnId, false)}
onRecordSelected={(company) => onRecordSelected={(company) =>
company ? handleEntitySelect(position, company) : null company ? handleEntitySelect(position, company) : null

View File

@ -1,9 +1,9 @@
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector'; import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector';
import { useRecordSelectSearch } from '@/object-record/relation-picker/hooks/useRecordSelectSearch'; import { useRecordSelectSearch } from '@/object-record/record-picker/hooks/useRecordSelectSearch';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useCallback, useContext } from 'react'; import { useCallback, useContext } from 'react';
import { RecoilState, useRecoilCallback } from 'recoil'; import { RecoilState, useRecoilCallback } from 'recoil';
@ -71,7 +71,7 @@ export const useAddNewCard = () => {
labelValue: string, labelValue: string,
position: 'first' | 'last', position: 'first' | 'last',
isOpportunity: boolean, isOpportunity: boolean,
company?: RecordForSelect, company?: SingleRecordPickerRecord,
) => { ) => {
if ( if (
(isOpportunity && company !== null) || (isOpportunity && company !== null) ||
@ -220,7 +220,7 @@ export const useAddNewCard = () => {
const handleEntitySelect = useCallback( const handleEntitySelect = useCallback(
( (
position: 'first' | 'last', position: 'first' | 'last',
company: RecordForSelect, company: SingleRecordPickerRecord,
columnId?: string, columnId?: string,
) => { ) => {
const columnDefinitionId = getColumnDefinitionId(columnId); 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'; import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
export type NewCard = { export type NewCard = {
@ -7,7 +7,7 @@ export type NewCard = {
isCreating: boolean; isCreating: boolean;
position: 'first' | 'last'; position: 'first' | 'last';
isOpportunity: boolean; isOpportunity: boolean;
company: RecordForSelect | null; company: SingleRecordPickerRecord | null;
}; };
export const recordBoardNewRecordByColumnIdComponentFamilyState = 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 { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer'; import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; 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 { 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 { 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 { SelectOption } from '@/spreadsheet-import/types';
import { MultiSelectDisplay } from '@/ui/field/display/components/MultiSelectDisplay'; import { MultiSelectDisplay } from '@/ui/field/display/components/MultiSelectDisplay';
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput'; import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
@ -74,7 +75,8 @@ export const FormMultiSelectFieldInput = ({
const inputId = useId(); const inputId = useId();
const theme = useTheme(); const theme = useTheme();
const hotkeyScope = MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID; const hotkeyScope =
FormMultiSelectFieldInputHotKeyScope.FormMultiSelectFieldInput;
const { const {
setHotkeyScopeAndMemorizePreviousScope, setHotkeyScopeAndMemorizePreviousScope,
@ -234,6 +236,9 @@ export const FormMultiSelectFieldInput = ({
draftValue.editingMode === 'edit' && ( draftValue.editingMode === 'edit' && (
<OverlayContainer> <OverlayContainer>
<MultiSelectInput <MultiSelectInput
selectableListComponentInstanceId={
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
}
hotkeyScope={hotkeyScope} hotkeyScope={hotkeyScope}
options={options} options={options}
onCancel={onCancel} 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 { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; 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 { 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 { SelectOption } from '@/spreadsheet-import/types';
import { SelectDisplay } from '@/ui/field/display/components/SelectDisplay'; import { SelectDisplay } from '@/ui/field/display/components/SelectDisplay';
import { SelectInput } from '@/ui/field/input/components/SelectInput'; import { SelectInput } from '@/ui/field/input/components/SelectInput';
@ -141,7 +141,7 @@ export const FormSelectFieldInput = ({
const [filteredOptions, setFilteredOptions] = useState<SelectOption[]>([]); const [filteredOptions, setFilteredOptions] = useState<SelectOption[]>([]);
const { resetSelectedItem } = useSelectableList( const { resetSelectedItem } = useSelectableList(
SINGLE_RECORD_SELECT_BASE_LIST, SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID,
); );
const clearField = () => { const clearField = () => {
@ -295,7 +295,9 @@ export const FormSelectFieldInput = ({
draftValue.editingMode === 'edit' && ( draftValue.editingMode === 'edit' && (
<OverlayContainer> <OverlayContainer>
<SelectInput <SelectInput
selectableListId={SINGLE_RECORD_SELECT_BASE_LIST} selectableListComponentInstanceId={
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
}
selectableItemIdArray={optionIds} selectableItemIdArray={optionIds}
hotkeyScope={hotkeyScope} hotkeyScope={hotkeyScope}
onEnter={handleSelectEnter} 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 { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue'; import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; 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 { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue'; 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 { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2';
import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue';
import { isFieldRichTextV2Value } from '@/object-record/record-field/types/guards/isFieldRichTextValueV2'; 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 { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName';
import { FieldContext } from '../contexts/FieldContext'; import { FieldContext } from '../contexts/FieldContext';
import { isFieldBoolean } from '../types/guards/isFieldBoolean'; import { isFieldBoolean } from '../types/guards/isFieldBoolean';
@ -156,7 +156,7 @@ export const usePersistField = () => {
); );
if (fieldIsRelationToOneObject) { if (fieldIsRelationToOneObject) {
const value = valueToPersist as RecordForSelect; const value = valueToPersist as SingleRecordPickerRecord;
updateRecord?.({ updateRecord?.({
variables: { variables: {
where: { id: recordId }, 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 { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
import { FieldRelationValue } from '@/object-record/record-field/types/FieldMetadata'; import { FieldRelationValue } from '@/object-record/record-field/types/FieldMetadata';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; 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 { FieldMetadataType } from '~/generated-metadata/graphql';
import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { FieldContext } from '../../contexts/FieldContext'; import { FieldContext } from '../../contexts/FieldContext';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldRelation } from '../../types/guards/isFieldRelation'; import { isFieldRelation } from '../../types/guards/isFieldRelation';
export const useRelationField = < export const useRelationField = <
T extends RecordForSelect | RecordForSelect[], T extends SingleRecordPickerRecord | SingleRecordPickerRecord[],
>() => { >() => {
const { recordId, fieldDefinition, maxWidth } = useContext(FieldContext); const { recordId, fieldDefinition, maxWidth } = useContext(FieldContext);
const button = useGetButtonIcon(); const button = useGetButtonIcon();

View File

@ -1,4 +1,5 @@
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField'; 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'; import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
type MultiSelectFieldInputProps = { type MultiSelectFieldInputProps = {
@ -13,6 +14,9 @@ export const MultiSelectFieldInput = ({
return ( return (
<MultiSelectInput <MultiSelectInput
selectableListComponentInstanceId={
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
}
hotkeyScope={hotkeyScope} hotkeyScope={hotkeyScope}
options={fieldDefinition.metadata.options} options={fieldDefinition.metadata.options}
onCancel={onCancel} 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 { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect'; import { MultipleRecordPicker } from '@/object-record/record-picker/components/MultipleRecordPicker';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
type RelationFromManyFieldInputProps = { type RelationFromManyFieldInputProps = {
onSubmit?: FieldInputEvent; onSubmit?: FieldInputEvent;
@ -51,19 +50,16 @@ export const RelationFromManyFieldInput = ({
recordId, recordId,
}); });
const { dropdownPlacement } = useDropdown(recordPickerInstanceId);
return ( return (
<> <>
<RecordPickerComponentInstanceContext.Provider <RecordPickerComponentInstanceContext.Provider
value={{ instanceId: recordPickerInstanceId }} value={{ instanceId: recordPickerInstanceId }}
> >
<RelationFromManyFieldInputMultiRecordsEffect /> <RelationFromManyFieldInputMultiRecordsEffect />
<MultiRecordSelect <MultipleRecordPicker
onSubmit={handleSubmit} onSubmit={handleSubmit}
onChange={updateRelation} onChange={updateRelation}
onCreate={createNewRecordAndOpenRightDrawer} onCreate={createNewRecordAndOpenRightDrawer}
dropdownPlacement={dropdownPlacement}
/> />
</RecordPickerComponentInstanceContext.Provider> </RecordPickerComponentInstanceContext.Provider>
</> </>

View File

@ -5,15 +5,16 @@ import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useOb
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField'; import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField';
import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState'; import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState';
import { useRecordPickerRecordsOptions } from '@/object-record/relation-picker/hooks/useRecordPickerRecordsOptions'; import { useRecordPickerRecordsOptions } from '@/object-record/record-picker/hooks/useRecordPickerRecordsOptions';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const RelationFromManyFieldInputMultiRecordsEffect = () => { export const RelationFromManyFieldInputMultiRecordsEffect = () => {
const { fieldValue, fieldDefinition } = useRelationField<RecordForSelect[]>(); const { fieldValue, fieldDefinition } =
useRelationField<SingleRecordPickerRecord[]>();
const instanceId = useAvailableComponentInstanceIdOrThrow( const instanceId = useAvailableComponentInstanceIdOrThrow(
RecordPickerComponentInstanceContext, RecordPickerComponentInstanceContext,
); );
@ -113,7 +114,9 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => {
useEffect(() => { useEffect(() => {
setObjectRecordMultiSelectCheckedRecordsIds( setObjectRecordMultiSelectCheckedRecordsIds(
fieldValue fieldValue
? fieldValue.map((fieldValueItem: RecordForSelect) => fieldValueItem.id) ? fieldValue.map(
(fieldValueItem: SingleRecordPickerRecord) => fieldValueItem.id,
)
: [], : [],
); );
}, [fieldValue, setObjectRecordMultiSelectCheckedRecordsIds]); }, [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 { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { SearchPickerInitialValueEffect } from '@/object-record/relation-picker/components/SearchPickerInitialValueEffect'; import { SingleRecordPicker } from '@/object-record/record-picker/components/SingleRecordPicker';
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; import { SearchPickerInitialValueEffect } from '@/object-record/record-picker/legacy/components/SearchPickerInitialValueEffect';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { RelationPickerHotkeyScope } from '@/object-record/record-picker/legacy/types/RelationPickerHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
export type RelationPickerProps = { export type RelationPickerProps = {
selectedRecordId?: string; selectedRecordId?: string;
onSubmit: (selectedRecord: RecordForSelect | null) => void; onSubmit: (selectedRecord: SingleRecordPickerRecord | null) => void;
onCancel?: () => void; onCancel?: () => void;
width?: number; width?: number;
excludedRecordIds?: string[]; excludedRecordIds?: string[];
@ -33,7 +33,7 @@ export const RelationPicker = ({
const recordPickerInstanceId = RelationPickerHotkeyScope.RelationPicker; const recordPickerInstanceId = RelationPickerHotkeyScope.RelationPicker;
const handleRecordSelected = ( const handleRecordSelected = (
selectedRecord: RecordForSelect | null | undefined, selectedRecord: SingleRecordPickerRecord | null | undefined,
) => onSubmit(selectedRecord ?? null); ) => onSubmit(selectedRecord ?? null);
const { objectMetadataItem: relationObjectMetadataItem } = const { objectMetadataItem: relationObjectMetadataItem } =
@ -63,7 +63,7 @@ export const RelationPicker = ({
initialValueForSearchFilter={initialSearchFilter} initialValueForSearchFilter={initialSearchFilter}
recordPickerInstanceId={recordPickerInstanceId} recordPickerInstanceId={recordPickerInstanceId}
/> />
<SingleRecordSelect <SingleRecordPicker
EmptyIcon={IconForbid} EmptyIcon={IconForbid}
emptyLabel={'No ' + fieldDefinition.label} emptyLabel={'No ' + fieldDefinition.label}
onCancel={onCancel} onCancel={onCancel}

View File

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

View File

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

View File

@ -18,7 +18,7 @@ import {
} from '~/testing/mock-data/users'; } from '~/testing/mock-data/users';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider'; 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 { getCanvasElementForDropdownTesting } from 'twenty-ui';
import { import {
RelationToOneFieldInput, 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 { RATING_VALUES } from '@/object-record/record-field/meta-types/constants/RatingValues';
import { ZodHelperLiteral } from '@/object-record/record-field/types/ZodHelperLiteral'; 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 { ConnectedAccountProvider } from 'twenty-shared';
import * as z from 'zod'; import * as z from 'zod';
import { RelationDefinitionType } from '~/generated-metadata/graphql'; 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 FieldSelectValue = string | null;
export type FieldMultiSelectValue = 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< export type FieldRelationValue<
T extends FieldRelationToOneValue | FieldRelationFromManyValue, 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 { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation'; 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 { useInlineCell } from '../hooks/useInlineCell';
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; 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 { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId'; import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,12 +11,12 @@ import { graphqlMocks } from '~/testing/graphqlMocks';
import { getPeopleMock } from '~/testing/mock-data/people'; import { getPeopleMock } from '~/testing/mock-data/people';
import { sleep } from '~/utils/sleep'; import { sleep } from '~/utils/sleep';
import { RecordForSelect } from '../../types/RecordForSelect'; import { SingleRecordPicker } from '@/object-record/record-picker/components/SingleRecordPicker';
import { SingleRecordSelect } from '../SingleRecordSelect'; import { SingleRecordPickerRecord } from '../../types/SingleRecordPickerRecord';
const peopleMock = getPeopleMock(); const peopleMock = getPeopleMock();
const records = peopleMock.map<RecordForSelect>((person) => ({ const records = peopleMock.map<SingleRecordPickerRecord>((person) => ({
id: person.id, id: person.id,
name: person.name.firstName + ' ' + person.name.lastName, name: person.name.firstName + ' ' + person.name.lastName,
avatarUrl: 'https://picsum.photos/200', avatarUrl: 'https://picsum.photos/200',
@ -24,9 +24,9 @@ const records = peopleMock.map<RecordForSelect>((person) => ({
record: { ...person, __typename: 'Person' }, record: { ...person, __typename: 'Person' },
})); }));
const meta: Meta<typeof SingleRecordSelect> = { const meta: Meta<typeof SingleRecordPicker> = {
title: 'UI/Input/RelationPicker/SingleRecordSelect', title: 'UI/RecordPicker/SingleRecordPicker',
component: SingleRecordSelect, component: SingleRecordPicker,
decorators: [ decorators: [
ComponentDecorator, ComponentDecorator,
ComponentWithRecoilScopeDecorator, ComponentWithRecoilScopeDecorator,
@ -53,7 +53,7 @@ const meta: Meta<typeof SingleRecordSelect> = {
}; };
export default meta; export default meta;
type Story = StoryObj<typeof SingleRecordSelect>; type Story = StoryObj<typeof SingleRecordPicker>;
export const Default: Story = {}; 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 { renderHook } from '@testing-library/react';
import { RecoilRoot } from 'recoil'; import { RecoilRoot } from 'recoil';
import { useLimitPerMetadataItem } from '@/object-metadata/hooks/useLimitPerMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem'; import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
const instanceId = 'instanceId'; const instanceId = 'instanceId';
const Wrapper = ({ children }: { children: React.ReactNode }) => ( 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 { RecoilRoot, useSetRecoilState } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
const instanceId = 'instanceId'; const instanceId = 'instanceId';

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector'; import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier'; import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection'; 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 { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';

View File

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

View File

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

View File

@ -2,14 +2,14 @@ import { gql, useQuery } from '@apollo/client';
import { isNonEmptyArray } from '@sniptt/guards'; import { isNonEmptyArray } from '@sniptt/guards';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useLimitPerMetadataItem } from '@/object-metadata/hooks/useLimitPerMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery'; import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem';
import { import {
MultiObjectRecordQueryResult, MultiObjectRecordQueryResult,
useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray, useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray,
} from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; } from '@/object-record/record-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem'; import { useOrderByFieldPerMetadataItem } from '@/object-record/record-picker/hooks/useOrderByFieldPerMetadataItem';
import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId'; import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId';
import { capitalize, isDefined } from 'twenty-shared'; 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 { useFilteredSearchRecordQuery } from '@/search/hooks/useFilteredSearchRecordQuery';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -22,5 +22,5 @@ export const useRecordPickerRecordsOptions = ({
objectNameSingular, 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 { useDebouncedCallback } from 'use-debounce';
import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker';
export const useRecordSelectSearch = ({ export const useRecordSelectSearch = ({
recordPickerInstanceId, recordPickerInstanceId,
}: { }: {
recordPickerInstanceId?: string; recordPickerInstanceId?: string;
} = {}) => { } = {}) => {
const { setRecordPickerSearchFilter, setRecordPickerPreselectedId } = const setRecordPickerSearchFilter = useSetRecoilComponentStateV2(
useRecordPicker({ recordPickerInstanceId }); recordPickerSearchFilterComponentState,
recordPickerInstanceId,
);
const setRecordPickerPreselectedId = useSetRecoilComponentStateV2(
recordPickerPreselectedIdComponentState,
recordPickerInstanceId,
);
const debouncedSetSearchFilter = useDebouncedCallback( const debouncedSetSearchFilter = useDebouncedCallback(
setRecordPickerSearchFilter, setRecordPickerSearchFilter,
100, 100,

View File

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

View File

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

View File

@ -1,10 +1,11 @@
import { useEffect } from 'react'; 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 { RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID } from '@/ui/layout/right-drawer/constants/RightDrawerClickOutsideListener';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
// Todo: this effect should be deprecated to use sync hooks
export const MultipleObjectRecordOnClickOutsideEffect = ({ export const MultipleObjectRecordOnClickOutsideEffect = ({
containerRef, containerRef,
onClickOutside, onClickOutside,
@ -32,7 +33,7 @@ export const MultipleObjectRecordOnClickOutsideEffect = ({
onClickOutside(); onClickOutside();
}, },
listenerId: MULTI_OBJECT_RECORD_CLICK_OUTSIDE_LISTENER_ID, listenerId: RECORD_PICKER_CLICK_OUTSIDE_LISTENER_ID,
}); });
return <></>; 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 { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useEffect } from 'react'; import { useEffect } from 'react';
// Todo: this effect should be deprecated to use sync hooks
export const SearchPickerInitialValueEffect = ({ export const SearchPickerInitialValueEffect = ({
initialValueForSearchFilter, initialValueForSearchFilter,
recordPickerInstanceId, 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'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const recordPickerPreselectedIdComponentState = 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'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const recordPickerSearchFilterComponentState = 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[]; computeFilterFields: (relationPickerType: string) => string[];
}; };

View File

@ -1,6 +1,6 @@
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier'; import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
export type RecordForSelect = ObjectRecordIdentifier & { export type SingleRecordPickerRecord = ObjectRecordIdentifier & {
record: ObjectRecord; 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 = ( export const formatMultiObjectRecordSearchResults = (
searchResults: MultiObjectRecordQueryResult | undefined | null, 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 { RelationFromManyFieldInputMultiRecordsEffect } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect';
import { useUpdateRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useUpdateRelationFromManyFieldInput'; import { useUpdateRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useUpdateRelationFromManyFieldInput';
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; 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 { RecordDetailRelationRecordsList } from '@/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsList';
import { RecordDetailSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailSection'; 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 { RecordDetailSectionHeader } from '@/object-record/record-show/record-detail-section/components/RecordDetailSectionHeader';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; 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 { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { prefetchIndexViewIdFromObjectMetadataItemFamilySelector } from '@/prefetch/states/selector/prefetchIndexViewIdFromObjectMetadataItemFamilySelector'; import { prefetchIndexViewIdFromObjectMetadataItemFamilySelector } from '@/prefetch/states/selector/prefetchIndexViewIdFromObjectMetadataItemFamilySelector';
import { AppPath } from '@/types/AppPath'; 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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; 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 { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
import { RelationDefinitionType } from '~/generated-metadata/graphql'; import { RelationDefinitionType } from '~/generated-metadata/graphql';
@ -84,9 +85,10 @@ export const RecordDetailRelationSection = ({
const { closeDropdown, isDropdownOpen, dropdownPlacement } = const { closeDropdown, isDropdownOpen, dropdownPlacement } =
useDropdown(dropdownId); useDropdown(dropdownId);
const { setRecordPickerSearchFilter } = useRecordPicker({ const setRecordPickerSearchFilter = useSetRecoilComponentStateV2(
recordPickerInstanceId: dropdownId, recordPickerSearchFilterComponentState,
}); dropdownId,
);
const handleCloseRelationPickerDropdown = useCallback(() => { const handleCloseRelationPickerDropdown = useCallback(() => {
setRecordPickerSearchFilter(''); setRecordPickerSearchFilter('');
@ -98,7 +100,7 @@ export const RecordDetailRelationSection = ({
}); });
const handleRelationPickerEntitySelected = ( const handleRelationPickerEntitySelected = (
selectedRelationEntity?: RecordForSelect, selectedRelationEntity?: SingleRecordPickerRecord,
) => { ) => {
closeDropdown(); closeDropdown();
@ -204,7 +206,7 @@ export const RecordDetailRelationSection = ({
value={{ instanceId: dropdownId }} value={{ instanceId: dropdownId }}
> >
{isToOneObject ? ( {isToOneObject ? (
<SingleRecordSelectMenuItemsWithSearch <SingleRecordPickerMenuItemsWithSearch
EmptyIcon={IconForbid} EmptyIcon={IconForbid}
onRecordSelected={handleRelationPickerEntitySelected} onRecordSelected={handleRelationPickerEntitySelected}
selectedRecordIds={relationRecordIds} selectedRecordIds={relationRecordIds}
@ -216,7 +218,7 @@ export const RecordDetailRelationSection = ({
) : ( ) : (
<> <>
<RelationFromManyFieldInputMultiRecordsEffect /> <RelationFromManyFieldInputMultiRecordsEffect />
<MultiRecordSelect <MultipleRecordPicker
onCreate={() => { onCreate={() => {
closeDropdown(); closeDropdown();
createNewRecordAndOpenRightDrawer?.(); 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 { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; 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 { RecordUpdateContext } from '@/object-record/record-table/contexts/EntityUpdateMutationHookContext';
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext'; import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; 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 { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; 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 { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation'; import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; 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'; import { RelationDefinitionType } from '~/generated-metadata/graphql';
export const filterAvailableTableColumns = ( export const filterAvailableTableColumns = (

View File

@ -5,9 +5,9 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; 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 { 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 { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { import {
query, query,
@ -90,7 +90,7 @@ describe('useFilteredSearchRecordQuery', () => {
{ wrapper: Wrapper }, { wrapper: Wrapper },
); );
const expectedResult: RecordsForMultipleRecordSelect<any> = { const expectedResult: MultipleRecordPickerRecords<any> = {
selectedRecords: [], selectedRecords: [],
filteredSelectedRecords: [], filteredSelectedRecords: [],
recordsToSelect: [], recordsToSelect: [],

View File

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

View File

@ -3,7 +3,6 @@ import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata'; 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 { SelectOption } from '@/spreadsheet-import/types';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -19,6 +18,7 @@ import { MenuItemMultiSelectTag } from 'twenty-ui';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly'; import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
type MultiSelectInputProps = { type MultiSelectInputProps = {
selectableListComponentInstanceId: string;
values: FieldMultiSelectValue; values: FieldMultiSelectValue;
hotkeyScope: string; hotkeyScope: string;
onCancel?: () => void; onCancel?: () => void;
@ -27,6 +27,7 @@ type MultiSelectInputProps = {
}; };
export const MultiSelectInput = ({ export const MultiSelectInput = ({
selectableListComponentInstanceId,
values, values,
options, options,
hotkeyScope, hotkeyScope,
@ -34,10 +35,10 @@ export const MultiSelectInput = ({
onOptionSelected, onOptionSelected,
}: MultiSelectInputProps) => { }: MultiSelectInputProps) => {
const { selectedItemIdState } = useSelectableListStates({ const { selectedItemIdState } = useSelectableListStates({
selectableListScopeId: MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID, selectableListScopeId: selectableListComponentInstanceId,
}); });
const { resetSelectedItem } = useSelectableList( const { resetSelectedItem } = useSelectableList(
MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID, selectableListComponentInstanceId,
); );
const selectedItemId = useRecoilValue(selectedItemIdState); const selectedItemId = useRecoilValue(selectedItemIdState);
@ -96,7 +97,7 @@ export const MultiSelectInput = ({
return ( return (
<SelectableList <SelectableList
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID} selectableListId={selectableListComponentInstanceId}
selectableItemIdArray={optionIds} selectableItemIdArray={optionIds}
hotkeyScope={hotkeyScope} hotkeyScope={hotkeyScope}
onEnter={(itemId) => { 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'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
type SelectInputProps = { type SelectInputProps = {
selectableListId: string; selectableListComponentInstanceId: string;
selectableItemIdArray: string[]; selectableItemIdArray: string[];
hotkeyScope: string; hotkeyScope: string;
onEnter: (itemId: string) => void; onEnter: (itemId: string) => void;
@ -17,7 +17,7 @@ type SelectInputProps = {
}; };
export const SelectInput = ({ export const SelectInput = ({
selectableListId, selectableListComponentInstanceId,
selectableItemIdArray, selectableItemIdArray,
hotkeyScope, hotkeyScope,
onEnter, onEnter,
@ -31,7 +31,7 @@ export const SelectInput = ({
}: SelectInputProps) => { }: SelectInputProps) => {
return ( return (
<SelectableList <SelectableList
selectableListId={selectableListId} selectableListId={selectableListComponentInstanceId}
selectableItemIdArray={selectableItemIdArray} selectableItemIdArray={selectableItemIdArray}
hotkeyScope={hotkeyScope} hotkeyScope={hotkeyScope}
onEnter={onEnter} 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 { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer'; import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer'; import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect'; import { SingleRecordPicker } from '@/object-record/record-picker/components/SingleRecordPicker';
import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker'; import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { recordPickerSearchFilterComponentState } from '@/object-record/record-picker/states/recordPickerSearchFilterComponentState';
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { SingleRecordPickerRecord } from '@/object-record/record-picker/types/SingleRecordPickerRecord';
import { InputLabel } from '@/ui/input/components/InputLabel'; import { InputLabel } from '@/ui/input/components/InputLabel';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; 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 { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
import { WorkflowSingleRecordFieldChip } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip'; import { WorkflowSingleRecordFieldChip } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip';
import { WorkflowVariablesDropdown } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdown'; import { WorkflowVariablesDropdown } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdown';
@ -98,16 +99,17 @@ export const WorkflowSingleRecordPicker = ({
const { closeDropdown } = useDropdown(dropdownId); const { closeDropdown } = useDropdown(dropdownId);
const { setRecordPickerSearchFilter } = useRecordPicker({ const setRecordPickerSearchFilter = useSetRecoilComponentStateV2(
recordPickerInstanceId: dropdownId, recordPickerSearchFilterComponentState,
}); dropdownId,
);
const handleCloseRelationPickerDropdown = useCallback(() => { const handleCloseRelationPickerDropdown = useCallback(() => {
setRecordPickerSearchFilter(''); setRecordPickerSearchFilter('');
}, [setRecordPickerSearchFilter]); }, [setRecordPickerSearchFilter]);
const handleRecordSelected = ( const handleRecordSelected = (
selectedEntity: RecordForSelect | null | undefined, selectedEntity: SingleRecordPickerRecord | null | undefined,
) => { ) => {
onChange?.(selectedEntity?.record?.id ?? ''); onChange?.(selectedEntity?.record?.id ?? '');
closeDropdown(); closeDropdown();
@ -153,7 +155,7 @@ export const WorkflowSingleRecordPicker = ({
<RecordPickerComponentInstanceContext.Provider <RecordPickerComponentInstanceContext.Provider
value={{ instanceId: dropdownId }} value={{ instanceId: dropdownId }}
> >
<SingleRecordSelect <SingleRecordPicker
EmptyIcon={IconForbid} EmptyIcon={IconForbid}
emptyLabel={'No ' + objectNameSingular} emptyLabel={'No ' + objectNameSingular}
onCancel={() => closeDropdown()} onCancel={() => closeDropdown()}

View File

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