import React, { ReactNode } from 'react';
import { ResourceContextProvider } from '../../core';
import { ListContextProvider } from '../list/ListContextProvider';
import {
useReferenceManyFieldController,
type UseReferenceManyFieldControllerParams,
} from './useReferenceManyFieldController';
import type { RaRecord } from '../../types';
import { ListControllerResult } from '../list';
/**
* Render related records to the current one.
*
* You must define the fields to be passed to the iterator component as children.
*
* @example Display all the comments of the current post as a datagrid
*
*
*
*
*
*
*
*
*
* @example Display all the books by the current author, only the title
*
*
*
*
*
*
* By default, restricts the displayed values to 25. You can extend this limit
* by setting the `perPage` prop.
*
* @example
*
* ...
*
*
* By default, orders the possible values by id desc. You can change this order
* by setting the `sort` prop (an object with `field` and `order` properties).
*
* @example
*
* ...
*
*
* Also, you can filter the query used to populate the possible values. Use the
* `filter` prop for that.
*
* @example
*
* ...
*
*/
export const ReferenceManyFieldBase = <
RecordType extends RaRecord = RaRecord,
ReferenceRecordType extends RaRecord = RaRecord,
>(
props: ReferenceManyFieldBaseProps
) => {
const {
children,
render,
debounce,
empty,
error,
loading,
filter = defaultFilter,
exporter,
offline,
page = 1,
perPage = 25,
record,
reference,
resource,
sort = defaultSort,
source = 'id',
storeKey,
target,
queryOptions,
} = props;
const controllerProps = useReferenceManyFieldController<
RecordType,
ReferenceRecordType
>({
debounce,
filter,
exporter,
page,
perPage,
record,
reference,
resource,
sort,
source,
storeKey,
target,
queryOptions,
});
if (!render && !children) {
throw new Error(
" requires either a 'render' prop or 'children' prop"
);
}
const {
data,
error: controllerError,
filterValues,
hasNextPage,
hasPreviousPage,
isPaused,
isPending,
isPlaceholderData,
total,
} = controllerProps;
const shouldRenderLoading =
isPending && !isPaused && loading !== false && loading !== undefined;
const shouldRenderOffline =
isPaused &&
(isPending || isPlaceholderData) &&
offline !== false &&
offline !== undefined;
const shouldRenderError =
controllerError && error !== false && error !== undefined;
const shouldRenderEmpty =
empty !== false &&
empty !== undefined &&
// there is no error
!error &&
// the list is not loading data for the first time
!isPending &&
// the API returned no data (using either normal or partial pagination)
(total === 0 ||
(total == null &&
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
hasPreviousPage === false &&
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
hasNextPage === false &&
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
data.length === 0)) &&
// the user didn't set any filters
!Object.keys(filterValues).length;
return (
{shouldRenderLoading
? loading
: shouldRenderOffline
? offline
: shouldRenderError
? error
: shouldRenderEmpty
? empty
: render
? render(controllerProps)
: children}
);
};
export interface ReferenceManyFieldBaseProps<
RecordType extends Record = Record,
ReferenceRecordType extends RaRecord = RaRecord,
> extends UseReferenceManyFieldControllerParams<
RecordType,
ReferenceRecordType
> {
children?: ReactNode;
render?: (props: ListControllerResult) => ReactNode;
empty?: ReactNode;
error?: ReactNode;
loading?: ReactNode;
offline?: ReactNode;
}
const defaultFilter = {};
const defaultSort = { field: 'id', order: 'DESC' as const };