Introduce a RelationPicker component with a RelationPickerScope (#2617)
Refactor mainIdentifier into scope componetn
This commit is contained in:
@ -42,18 +42,25 @@ export const RelationPicker = ({
|
||||
|
||||
const useFindManyQuery = (options: any) => useQuery(findManyQuery, options);
|
||||
|
||||
const { mapToObjectIdentifiers } = useRelationField();
|
||||
const { identifiersMapper, searchQuery } = useRelationField();
|
||||
|
||||
const workspaceMembers = useFilteredSearchEntityQuery({
|
||||
queryHook: useFindManyQuery,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: fieldDefinition.metadata.searchFields,
|
||||
fieldNames:
|
||||
searchQuery?.filterFields?.(
|
||||
fieldDefinition.metadata.objectMetadataNameSingular,
|
||||
) ?? [],
|
||||
filter: relationPickerSearchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: mapToObjectIdentifiers,
|
||||
mappingFunction: (record: any) =>
|
||||
identifiersMapper?.(
|
||||
record,
|
||||
fieldDefinition.metadata.objectMetadataNameSingular,
|
||||
),
|
||||
selectedIds: recordId ? [recordId] : [],
|
||||
objectNamePlural: fieldDefinition.metadata.objectMetadataNamePlural,
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
import { getRelationPickerScopedStates } from '@/ui/input/components/internal/relation-picker/utils/getRelationPickerScopedStates';
|
||||
import { RecordTableScopeInternalContext } from '@/ui/object/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
export const useRelationPickerScopedStates = (args?: {
|
||||
relationPickerScopedId?: string;
|
||||
}) => {
|
||||
const { relationPickerScopedId } = args ?? {};
|
||||
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordTableScopeInternalContext,
|
||||
relationPickerScopedId,
|
||||
);
|
||||
|
||||
const { identifiersMapperState } = getRelationPickerScopedStates({
|
||||
relationPickerScopeId: scopeId,
|
||||
});
|
||||
|
||||
const { searchQueryState } = getRelationPickerScopedStates({
|
||||
relationPickerScopeId: scopeId,
|
||||
});
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
identifiersMapperState,
|
||||
searchQueryState,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,35 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useRelationPickerScopedStates } from '@/ui/input/components/internal/relation-picker/hooks/internal/useRelationPickerScopedStates';
|
||||
import { RelationPickerScopeInternalContext } from '@/ui/input/components/internal/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
type useRelationPickeProps = {
|
||||
relationPickerScopeId?: string;
|
||||
};
|
||||
|
||||
export const useRelationPicker = (props?: useRelationPickeProps) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RelationPickerScopeInternalContext,
|
||||
props?.relationPickerScopeId,
|
||||
);
|
||||
|
||||
const { identifiersMapperState, searchQueryState } =
|
||||
useRelationPickerScopedStates({
|
||||
relationPickerScopedId: scopeId,
|
||||
});
|
||||
|
||||
const [identifiersMapper, setIdentifiersMapper] = useRecoilState(
|
||||
identifiersMapperState,
|
||||
);
|
||||
|
||||
const [searchQuery, setSearchQuery] = useRecoilState(searchQueryState);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
identifiersMapper,
|
||||
setIdentifiersMapper,
|
||||
searchQuery,
|
||||
setSearchQuery,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { RelationPickerScopeInternalContext } from '@/ui/input/components/internal/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext';
|
||||
|
||||
type RelationPickerScopeProps = {
|
||||
children: ReactNode;
|
||||
relationPickerScopeId: string;
|
||||
};
|
||||
|
||||
export const RelationPickerScope = ({
|
||||
children,
|
||||
relationPickerScopeId,
|
||||
}: RelationPickerScopeProps) => {
|
||||
return (
|
||||
<RelationPickerScopeInternalContext.Provider
|
||||
value={{ scopeId: relationPickerScopeId }}
|
||||
>
|
||||
{children}
|
||||
</RelationPickerScopeInternalContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
|
||||
type RelationPickerScopeInternalContextProps = ScopedStateKey;
|
||||
|
||||
export const RelationPickerScopeInternalContext =
|
||||
createScopeInternalContext<RelationPickerScopeInternalContextProps>();
|
||||
@ -0,0 +1,8 @@
|
||||
import { IdentifiersMapper } from '@/ui/input/components/internal/relation-picker/types/IdentifiersMapper';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const identifiersMapperScopedState =
|
||||
createScopedState<IdentifiersMapper | null>({
|
||||
key: 'identifiersMapperScopedState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
import { SearchQuery } from '@/ui/input/components/internal/relation-picker/types/SearchQuery';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const searchQueryScopedState = createScopedState<SearchQuery | null>({
|
||||
key: 'searchQueryScopedState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
import { AvatarType } from '@/users/components/Avatar';
|
||||
|
||||
type RecordMappedToIdentifiers = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
avatarType: AvatarType;
|
||||
record: any;
|
||||
};
|
||||
|
||||
export type IdentifiersMapper = (
|
||||
record: any,
|
||||
relationPickerType: string,
|
||||
) => RecordMappedToIdentifiers | undefined;
|
||||
@ -0,0 +1,3 @@
|
||||
export type SearchQuery = {
|
||||
filterFields: (relationPickerType: string) => string[];
|
||||
};
|
||||
@ -0,0 +1,24 @@
|
||||
import { identifiersMapperScopedState } from '@/ui/input/components/internal/relation-picker/states/identifiersMapperScopedState';
|
||||
import { searchQueryScopedState } from '@/ui/input/components/internal/relation-picker/states/searchQueryScopedState';
|
||||
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
|
||||
|
||||
export const getRelationPickerScopedStates = ({
|
||||
relationPickerScopeId,
|
||||
}: {
|
||||
relationPickerScopeId: string;
|
||||
}) => {
|
||||
const identifiersMapperState = getScopedState(
|
||||
identifiersMapperScopedState,
|
||||
relationPickerScopeId,
|
||||
);
|
||||
|
||||
const searchQueryState = getScopedState(
|
||||
searchQueryScopedState,
|
||||
relationPickerScopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
identifiersMapperState,
|
||||
searchQueryState,
|
||||
};
|
||||
};
|
||||
@ -1,9 +1,7 @@
|
||||
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
|
||||
type DropdownScopeInternalContextProps = ScopedStateKey & {
|
||||
test?: string;
|
||||
};
|
||||
type DropdownScopeInternalContextProps = ScopedStateKey;
|
||||
|
||||
export const DropdownScopeInternalContext =
|
||||
createScopeInternalContext<DropdownScopeInternalContextProps>();
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -62,7 +62,7 @@ export const RecordTableCell = ({ cellIndex }: { cellIndex: number }) => {
|
||||
hotkeyScope: customHotkeyScope,
|
||||
isMainIdentifier:
|
||||
columnDefinition.fieldMetadataId ===
|
||||
objectMetadataConfig?.mainIdentifierFieldMetadataId,
|
||||
objectMetadataConfig?.labelIdentifierFieldMetadataId,
|
||||
}}
|
||||
>
|
||||
<TableCell customHotkeyScope={{ scope: customHotkeyScope }} />
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user