import type { ExtractRecordPaths, LinkToType, RaRecord, UseReferenceFieldControllerResult, } from "ra-core"; import { ReferenceFieldBase, useFieldValue, useGetRecordRepresentation, useReferenceFieldContext, useTranslate, } from "ra-core"; import type { MouseEvent, ReactNode } from "react"; import { Link } from "react-router"; import type { UseQueryOptions } from "@tanstack/react-query"; /** * Displays a field from a related record by following a foreign key relationship. * * This field fetches the related record using the foreign key value and displays it using the record representation. * It supports linking to the related record's show or edit page. * To be used with RecordField or DataTable.Col components, or anywhere a RecordContext is available. * * @see {@link https://marmelab.com/shadcn-admin-kit/docs/referencefield/ ReferenceField documentation} * * @example * import { List, DataTable, ReferenceField } from '@/components/admin'; * * const PostList = () => ( * * * * * * * * * ); */ export const ReferenceField = < RecordType extends RaRecord = RaRecord, ReferenceRecordType extends RaRecord = RaRecord, >( props: ReferenceFieldProps, ) => { const { loading, error, empty, render, ...rest } = props; const id = useFieldValue(props); const translate = useTranslate(); return id == null ? ( typeof empty === "string" ? ( <>{empty && translate(empty, { _: empty })} ) : ( empty ) ) : ( render={render} loading={loading} error={error} {...rest} /> ); }; export interface ReferenceFieldProps< RecordType extends RaRecord = RaRecord, ReferenceRecordType extends RaRecord = RaRecord, > extends Partial> { children?: ReactNode; queryOptions?: UseQueryOptions & { // eslint-disable-next-line @typescript-eslint/no-explicit-any meta?: any; }; record?: RecordType; reference: string; translateChoice?: ((record: ReferenceRecordType) => string) | boolean; link?: LinkToType; source: ExtractRecordPaths; } // useful to prevent click bubbling in a datagrid with rowClick const stopPropagation = (e: MouseEvent) => e.stopPropagation(); export const ReferenceFieldView = < ReferenceRecordType extends RaRecord = RaRecord, >( props: ReferenceFieldViewProps, ) => { const { children, className, empty, error: errorElement, render, reference, loading, } = props; const referenceFieldContext = useReferenceFieldContext(); const { error, link, isPending, referenceRecord } = referenceFieldContext; const getRecordRepresentation = useGetRecordRepresentation(reference); const translate = useTranslate(); if (error && errorElement !== false) { return errorElement; } if (isPending && loading !== false) { return loading; } if (!referenceRecord && empty !== false) { return typeof empty === "string" ? ( <>{empty && translate(empty, { _: empty })} ) : ( empty ); } const child = render ? render(referenceFieldContext) : children || {getRecordRepresentation(referenceRecord)}; if (link) { return (
{child}
); } return <>{child}; }; export interface ReferenceFieldViewProps< ReferenceRecordType extends RaRecord = RaRecord, > { children?: ReactNode; className?: string; empty?: ReactNode; loading?: ReactNode; render?: (props: UseReferenceFieldControllerResult) => ReactNode; reference: string; source: string; resource?: string; translateChoice?: ((record: ReferenceRecordType) => string) | boolean; resourceLinkPath?: LinkToType; error?: ReactNode; }