import * as React from 'react';
import { type ReactNode } from 'react';
import type { UseQueryOptions } from '@tanstack/react-query';
import { Exporter, FilterPayload, RaRecord, SortPayload } from '../../types';
import { useRecordContext } from '../record';
import { useReferenceArrayFieldController } from './useReferenceArrayFieldController';
import { ResourceContextProvider } from '../../core';
import { ListContextProvider, ListControllerResult } from '../list';
import { BaseFieldProps } from './types';
/**
* A container component that fetches records from another resource specified
* by an array of *ids* in current record.
*
* You must define the fields to be passed to the iterator component as children.
*
* @example Display all the products of the current order as datagrid
* // order = {
* // id: 123,
* // product_ids: [456, 457, 458],
* // }
*
*
*
*
*
*
*
*
*
* @example Display all the categories of the current product as a list of chips
* // product = {
* // id: 456,
* // category_ids: [11, 22, 33],
* // }
*
*
*
*
*
*
* By default, restricts the displayed values to 1000. You can extend this limit
* by setting the `perPage` prop.
*
* @example
*
* ...
*
*
* By default, the field displays the results in the order in which they are referenced
* (i.e. in the order of the list of ids). You can change this order
* by setting the `sort` prop (an object with `field` and `order` properties).
*
* @example
*
* ...
*
*
* Also, you can filter the results to display only a subset of values. Use the
* `filter` prop for that.
*
* @example
*
* ...
*
*/
export const ReferenceArrayFieldBase = <
RecordType extends RaRecord = RaRecord,
ReferenceRecordType extends RaRecord = RaRecord,
>(
props: ReferenceArrayFieldBaseProps
) => {
const {
children,
render,
error,
loading,
empty,
filter,
exporter,
offline,
page = 1,
perPage,
reference,
resource,
sort,
source,
queryOptions,
} = props;
const record = useRecordContext(props);
const controllerProps = useReferenceArrayFieldController<
RecordType,
ReferenceRecordType
>({
filter,
exporter,
page,
perPage,
record,
reference,
resource,
sort,
source,
queryOptions,
});
if (!render && !children) {
throw new Error(
" requires either a 'render' prop or 'children' prop"
);
}
const {
error: controllerError,
isPending,
isPaused,
isPlaceholderData,
} = controllerProps;
const shouldRenderLoading =
isPending && !isPaused && loading !== undefined && loading !== false;
const shouldRenderOffline =
isPaused &&
(isPending || isPlaceholderData) &&
offline !== undefined &&
offline !== false;
const shouldRenderError =
!isPending &&
!isPaused &&
controllerError &&
error !== undefined &&
error !== false;
const shouldRenderEmpty = // there is an empty page component
empty &&
// there is no error
!controllerProps.error &&
// the list is not loading data for the first time
!controllerProps.isPending &&
// the API returned no data (using either normal or partial pagination)
(controllerProps.total === 0 ||
(controllerProps.total == null &&
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
controllerProps.hasPreviousPage === false &&
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
controllerProps.hasNextPage === false &&
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
controllerProps.data.length === 0)) &&
// the user didn't set any filters
!Object.keys(controllerProps.filterValues).length;
return (
{shouldRenderLoading
? loading
: shouldRenderOffline
? offline
: shouldRenderError
? error
: shouldRenderEmpty
? empty
: render
? render(controllerProps)
: children}
);
};
export interface ReferenceArrayFieldBaseProps<
RecordType extends RaRecord = RaRecord,
ReferenceRecordType extends RaRecord = RaRecord,
> extends BaseFieldProps {
children?: ReactNode;
render?: (props: ListControllerResult) => ReactNode;
error?: ReactNode;
loading?: ReactNode;
empty?: ReactNode;
filter?: FilterPayload;
exporter?: Exporter | false;
offline?: ReactNode;
page?: number;
perPage?: number;
reference: string;
sort?: SortPayload;
queryOptions?: Omit<
UseQueryOptions,
'queryFn' | 'queryKey'
>;
}