adding object name in morph Pickers (#14977)
Morph related PR to implement this <img width="321" height="288" alt="Screenshot 2025-10-08 at 13 32 45" src="https://github.com/user-attachments/assets/b58e2caa-2792-43aa-8077-fa6717bfef5d" /> Fixes https://github.com/twentyhq/core-team-issues/issues/1308
This commit is contained in:
+2
@@ -9,6 +9,7 @@ import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-r
|
||||
import { isFieldMorphRelationManyToOne } from '@/object-record/record-field/ui/types/guards/isFieldMorphRelationManyToOne';
|
||||
import { SingleRecordPicker } from '@/object-record/record-picker/single-record-picker/components/SingleRecordPicker';
|
||||
import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useContext } from 'react';
|
||||
@@ -90,6 +91,7 @@ export const MorphRelationManyToOneFieldInput = () => {
|
||||
? 'search-bar-on-top'
|
||||
: 'search-bar-on-bottom'
|
||||
}
|
||||
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+2
@@ -5,6 +5,7 @@ import { useUpdateMorphRelationOneToManyFieldInput } from '@/object-record/recor
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/ui/states/recordFieldInputLayoutDirectionComponentState';
|
||||
import { MultipleRecordPicker } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
|
||||
@@ -40,6 +41,7 @@ export const MorphRelationOneToManyFieldInput = () => {
|
||||
? 'search-bar-on-top'
|
||||
: 'search-bar-on-bottom'
|
||||
}
|
||||
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-1
@@ -10,6 +10,7 @@ import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldCont
|
||||
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
|
||||
import { useRelationField } from '@/object-record/record-field/ui/meta-types/hooks/useRelationField';
|
||||
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
|
||||
import { useUpdateRelationOneToManyFieldInput } from '@/object-record/record-field/ui/meta-types/input/hooks/useUpdateRelationOneToManyFieldInput';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/ui/states/recordFieldInputLayoutDirectionComponentState';
|
||||
import { type FieldDefinition } from '@/object-record/record-field/ui/types/FieldDefinition';
|
||||
@@ -22,7 +23,6 @@ import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useUpdateRelationOneToManyFieldInput } from '@/object-record/record-field/ui/meta-types/input/hooks/useUpdateRelationOneToManyFieldInput';
|
||||
|
||||
export const RelationOneToManyFieldInput = () => {
|
||||
const { fieldDefinition, recordId } = useContext(FieldContext);
|
||||
|
||||
+3
-1
@@ -29,6 +29,7 @@ type MultipleRecordPickerProps = {
|
||||
onClickOutside: () => void;
|
||||
focusId: string;
|
||||
objectMetadataItemIdForCreate?: string;
|
||||
dropdownWidth?: number;
|
||||
};
|
||||
|
||||
export const MultipleRecordPicker = ({
|
||||
@@ -40,6 +41,7 @@ export const MultipleRecordPicker = ({
|
||||
componentInstanceId,
|
||||
focusId,
|
||||
objectMetadataItemIdForCreate,
|
||||
dropdownWidth,
|
||||
}: MultipleRecordPickerProps) => {
|
||||
const selectableListComponentInstanceId =
|
||||
getMultipleRecordPickerSelectableListId(componentInstanceId);
|
||||
@@ -129,7 +131,7 @@ export const MultipleRecordPicker = ({
|
||||
containerRef={containerRef}
|
||||
onClickOutside={handleClickOutside}
|
||||
/>
|
||||
<DropdownContent ref={containerRef}>
|
||||
<DropdownContent ref={containerRef} widthInPixels={dropdownWidth}>
|
||||
{layoutDirection === 'search-bar-on-bottom' && (
|
||||
<>
|
||||
{createNewButtonSection}
|
||||
|
||||
+16
@@ -10,8 +10,12 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
|
||||
|
||||
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
||||
import { type SearchRecord } from '~/generated-metadata/graphql';
|
||||
|
||||
export const StyledSelectableItem = styled(SelectableListItem)`
|
||||
@@ -62,6 +66,13 @@ export const MultipleRecordPickerMenuItemContent = ({
|
||||
searchRecord.label?.trim() ||
|
||||
`Untitled ${objectMetadataItem.labelSingular}`;
|
||||
|
||||
const searchableObjectMetadataItems = useRecoilComponentValue(
|
||||
multipleRecordPickerSearchableObjectMetadataItemsComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const showObjectName = searchableObjectMetadataItems.length > 1;
|
||||
|
||||
return (
|
||||
<StyledSelectableItem
|
||||
itemId={searchRecord.recordId}
|
||||
@@ -82,6 +93,11 @@ export const MultipleRecordPickerMenuItemContent = ({
|
||||
/>
|
||||
}
|
||||
text={displayText}
|
||||
contextualText={
|
||||
showObjectName
|
||||
? capitalize(searchRecord.objectNameSingular)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</StyledSelectableItem>
|
||||
);
|
||||
|
||||
+15
-1
@@ -3,14 +3,16 @@ import styled from '@emotion/styled';
|
||||
import { getAvatarType } from '@/object-metadata/utils/getAvatarType';
|
||||
import { searchRecordStoreFamilyState } from '@/object-record/record-picker/multiple-record-picker/states/searchRecordStoreComponentFamilyState';
|
||||
import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext';
|
||||
import { singleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSearchableObjectMetadataItemsComponentState';
|
||||
import { getSingleRecordPickerSelectableListId } from '@/object-record/record-picker/single-record-picker/utils/getSingleRecordPickerSelectableListId';
|
||||
import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { MenuItemSelectAvatar } from 'twenty-ui/navigation';
|
||||
|
||||
@@ -47,10 +49,17 @@ export const SingleRecordPickerMenuItem = ({
|
||||
searchRecordStoreFamilyState(morphItem.recordId),
|
||||
);
|
||||
|
||||
const searchableObjectMetadataItems = useRecoilComponentValue(
|
||||
singleRecordPickerSearchableObjectMetadataItemsComponentState,
|
||||
recordPickerComponentInstanceId,
|
||||
);
|
||||
|
||||
if (!isDefined(searchRecord)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showObjectName = searchableObjectMetadataItems.length > 1;
|
||||
|
||||
return (
|
||||
<StyledSelectableItem
|
||||
itemId={morphItem.recordId}
|
||||
@@ -74,6 +83,11 @@ export const SingleRecordPickerMenuItem = ({
|
||||
type={getAvatarType(searchRecord.objectNameSingular) ?? 'rounded'}
|
||||
/>
|
||||
}
|
||||
contextualText={
|
||||
showObjectName
|
||||
? capitalize(searchRecord.objectNameSingular)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</StyledSelectableItem>
|
||||
);
|
||||
|
||||
+17
-2
@@ -2,7 +2,10 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
||||
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
|
||||
import { useObjectRecordSearchRecords } from '@/object-record/hooks/useObjectRecordSearchRecords';
|
||||
import { searchRecordStoreFamilyState } from '@/object-record/record-picker/multiple-record-picker/states/searchRecordStoreComponentFamilyState';
|
||||
import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext';
|
||||
import { singleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSearchableObjectMetadataItemsComponentState';
|
||||
import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { CustomError, isDefined } from 'twenty-shared/utils';
|
||||
import { type SearchQuery } from '~/generated/graphql';
|
||||
@@ -23,6 +26,12 @@ export const useSingleRecordPickerPerformSearch = ({
|
||||
pickableMorphItems: RecordPickerPickableMorphItem[];
|
||||
loading: boolean;
|
||||
} => {
|
||||
const singleRecordPickerInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
SingleRecordPickerComponentInstanceContext,
|
||||
);
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const onSearchRecordsCompleted = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(data: SearchQuery) => {
|
||||
@@ -34,11 +43,17 @@ export const useSingleRecordPickerPerformSearch = ({
|
||||
record: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
set(
|
||||
singleRecordPickerSearchableObjectMetadataItemsComponentState.atomFamily(
|
||||
{ instanceId: singleRecordPickerInstanceId },
|
||||
),
|
||||
objectMetadataItems,
|
||||
);
|
||||
},
|
||||
[],
|
||||
[objectMetadataItems, singleRecordPickerInstanceId],
|
||||
);
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const selectedIdsFilter = { id: { in: selectedIds } };
|
||||
|
||||
const { loading: selectedRecordsLoading, searchRecords: selectedRecords } =
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const singleRecordPickerSearchableObjectMetadataItemsComponentState =
|
||||
createComponentState<ObjectMetadataItem[]>({
|
||||
key: 'singleRecordPickerSearchableObjectMetadataItemsComponentState',
|
||||
defaultValue: [],
|
||||
componentInstanceContext: SingleRecordPickerComponentInstanceContext,
|
||||
});
|
||||
+3
@@ -23,6 +23,9 @@ const StyledTextContainer = styled.div`
|
||||
align-items: center;
|
||||
flex: 1 0 0;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
type MenuItemMultiSelectAvatarProps = {
|
||||
|
||||
@@ -29,6 +29,9 @@ const StyledTextContainer = styled.div`
|
||||
align-items: center;
|
||||
flex: 1 0 0;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const MenuItemSelectAvatar = ({
|
||||
|
||||
Reference in New Issue
Block a user