Introduce a RelationPicker component with a RelationPickerScope (#2617)

Refactor mainIdentifier into scope componetn
This commit is contained in:
Charles Bochet
2023-11-21 16:09:02 +01:00
committed by GitHub
parent d25f00e04f
commit f97d25d986
25 changed files with 256 additions and 168 deletions

View File

@ -5,20 +5,23 @@ import { useRelationField } from '../../hooks/useRelationField';
export const RelationFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useRelationField();
const { mapToObjectIdentifiers } = useRelationField();
const { identifiersMapper } = useRelationField();
if (!fieldValue || !fieldDefinition) {
if (!fieldValue || !fieldDefinition || !identifiersMapper) {
return <></>;
}
const objectIdentifiers = mapToObjectIdentifiers(fieldValue);
const objectIdentifiers = identifiersMapper(
fieldValue,
fieldDefinition.metadata.objectMetadataNameSingular,
);
return (
<EntityChip
entityId={fieldValue.id}
name={objectIdentifiers.name}
avatarUrl={objectIdentifiers.avatarUrl}
avatarType={objectIdentifiers.avatarType}
name={objectIdentifiers?.name ?? ''}
avatarUrl={objectIdentifiers?.avatarUrl}
avatarType={objectIdentifiers?.avatarType}
/>
);
};

View File

@ -1,6 +1,8 @@
import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
@ -30,35 +32,7 @@ export const useRelationField = () => {
const initialValue = fieldInitialValue?.isEmpty ? null : fieldValue;
const mapToObjectIdentifiers = (record: any) => {
let name = '';
for (const fieldPath of fieldDefinition.metadata
.labelIdentifierFieldPaths) {
const fieldPathParts = fieldPath.split('.');
if (fieldPathParts.length === 1) {
name += record[fieldPathParts[0]];
} else if (fieldPathParts.length === 2) {
name += record[fieldPathParts[0]][fieldPathParts[1]] + ' ';
} else {
throw new Error(
`Invalid field path ${fieldPath}. Relation picker only supports field paths with 1 or 2 parts.`,
);
}
}
const avatarUrl = record[fieldDefinition.metadata.imageIdentifierUrlField];
return {
id: record.id,
name: name.trimEnd(),
avatarUrl: avatarUrl
? fieldDefinition.metadata.imageIdentifierUrlPrefix +
record[fieldDefinition.metadata.imageIdentifierUrlField]
: '',
avatarType: fieldDefinition.metadata.imageIdentifierFormat,
record: record,
};
};
const { identifiersMapper, searchQuery } = useRelationPicker();
return {
fieldDefinition,
@ -66,6 +40,7 @@ export const useRelationField = () => {
initialValue,
initialSearchValue,
setFieldValue,
mapToObjectIdentifiers,
searchQuery,
identifiersMapper,
};
};

View File

@ -1,8 +1,8 @@
import { useEffect } from 'react';
import styled from '@emotion/styled';
import { RelationPicker } from '@/ui/input/components/internal/relation-picker/RelationPicker';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { RelationPicker } from '@/ui/object/field/meta-types/input/components/internal/RelationPicker';
import { usePersistField } from '../../../hooks/usePersistField';
import { useRelationField } from '../../hooks/useRelationField';

View File

@ -1,77 +0,0 @@
import { useEffect } from 'react';
import { useQuery } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { IconUserCircle } from '@/ui/display/icon';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { useRelationField } from '@/ui/object/field/meta-types/hooks/useRelationField';
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
export type RelationPickerProps = {
recordId: string;
onSubmit: (newUser: EntityForSelect | null) => void;
onCancel?: () => void;
width?: number;
initialSearchFilter?: string | null;
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
};
export const RelationPicker = ({
recordId,
onSubmit,
onCancel,
width,
initialSearchFilter,
fieldDefinition,
}: RelationPickerProps) => {
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
useRecoilScopedState(relationPickerSearchFilterScopedState);
useEffect(() => {
setRelationPickerSearchFilter(initialSearchFilter ?? '');
}, [initialSearchFilter, setRelationPickerSearchFilter]);
const { findManyQuery } = useObjectMetadataItem({
objectNameSingular: fieldDefinition.metadata.objectMetadataNameSingular,
});
const useFindManyQuery = (options: any) => useQuery(findManyQuery, options);
const { mapToObjectIdentifiers } = useRelationField();
const workspaceMembers = useFilteredSearchEntityQuery({
queryHook: useFindManyQuery,
filters: [
{
fieldNames: fieldDefinition.metadata.searchFields,
filter: relationPickerSearchFilter,
},
],
orderByField: 'createdAt',
mappingFunction: mapToObjectIdentifiers,
selectedIds: recordId ? [recordId] : [],
objectNamePlural: fieldDefinition.metadata.objectMetadataNamePlural,
});
const handleEntitySelected = async (selectedUser: any | null | undefined) => {
onSubmit(selectedUser ?? null);
};
return (
<SingleEntitySelect
EmptyIcon={IconUserCircle}
emptyLabel="No Owner"
entitiesToSelect={workspaceMembers.entitiesToSelect}
loading={workspaceMembers.loading}
onCancel={onCancel}
onEntitySelected={handleEntitySelected}
selectedEntity={workspaceMembers.selectedEntities[0]}
width={width}
/>
);
};

View File

@ -64,11 +64,6 @@ export type FieldRelationMetadata = {
fieldName: string;
useEditButton?: boolean;
relationType?: FieldDefinitionRelationType;
labelIdentifierFieldPaths: string[];
imageIdentifierUrlField: string;
imageIdentifierUrlPrefix: string;
imageIdentifierFormat: 'squared' | 'rounded';
searchFields: string[];
objectMetadataNameSingular: string;
objectMetadataNamePlural: string;
};

View File

@ -62,7 +62,7 @@ export const RecordTableCell = ({ cellIndex }: { cellIndex: number }) => {
hotkeyScope: customHotkeyScope,
isMainIdentifier:
columnDefinition.fieldMetadataId ===
objectMetadataConfig?.mainIdentifierFieldMetadataId,
objectMetadataConfig?.labelIdentifierFieldMetadataId,
}}
>
<TableCell customHotkeyScope={{ scope: customHotkeyScope }} />

View File

@ -1,10 +1,4 @@
import { AvatarType } from '@/users/components/Avatar';
export type ObjectMetadataConfig = {
mainIdentifierFieldMetadataId: string;
labelIdentifierFieldPaths: string[];
imageIdentifierUrlField: string;
imageIdentifierUrlPrefix: string;
imageIdentifierFormat: AvatarType;
labelIdentifierFieldMetadataId: string;
basePathToShowPage: string;
};