import { memo, type ReactNode, type HTMLAttributes } from 'react';
import cn from 'classnames';
import Loader from './loader';
import useDataCheckboxes from '../hooks/useDataCheckboxes';
import withDataLoader, { type WrapperProps } from './data-loader';
export type Props = {
/**
* The data to be displayed
*/
data: Datum[];
/**
* Flag saying that data is loading, so we might be showing stale data
*/
loading?: boolean;
/**
* A function that returns a unique ID for each of the data objects.
* Same function signature as a map function.
*/
getIdKey: (datum: Datum, index: number, data: Datum[]) => string;
/**
* A callback that is called whenever a user selects or unselects a row.
*/
onSelectionChange?: (event: MouseEvent | KeyboardEvent) => void;
/**
* A renderer function for each item of the list.
* Make sure that it doesn't change unecessarily by wrapping it in useCallback
*/
dataRenderer: (datum: Datum) => ReactNode;
};
type BasicDatum = Record;
type DataListItemProps = {
datum: Datum;
id: string;
dataRenderer: (datum: Datum) => ReactNode;
loading: boolean;
firstItem: boolean;
};
const DataListItem = ({
datum,
id,
dataRenderer,
loading,
firstItem,
}: DataListItemProps) => {
let rendered: ReactNode;
try {
rendered = dataRenderer(datum);
} catch (error) {
/**
* We get here only if the renderer fails. If the renderer returns null of
* undefined because of a lack of data, then it will no throw and will not
* display the loader at all
*/
if (!loading) {
throw error;
} else {
rendered = firstItem && ;
}
}
return {rendered};
};
const MemoizedDataListItem = memo(DataListItem) as typeof DataListItem;
export const DataList = ({
data,
getIdKey,
dataRenderer,
loading = false,
onSelectionChange,
className,
...props
}: Props & HTMLAttributes) => {
const { checkboxContainerRef } = useDataCheckboxes(onSelectionChange);
return (
{data.map((datum, index) => {
const id = getIdKey(datum, index, data);
return (
);
})}
);
};
export const DataListWithLoader = (
props: WrapperProps & Props & HTMLAttributes
) => <>{withDataLoader(DataList)(props)}>;