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)};