UNPKG

@spaced-out/ui-design-system

Version:
153 lines (140 loc) 4.38 kB
// @flow strict import * as React from 'react'; import classify from '../../utils/classify'; import type {ClassNameComponent} from '../../utils/makeClassNameComponent'; import {makeClassNameComponent} from '../../utils/makeClassNameComponent'; import {Checkbox} from '../Checkbox'; import {CircularLoader} from '../CircularLoader'; import {BodyLarge, TEXT_COLORS} from '../Text'; import {PaddedContentCell, SingleCell} from './Cell'; import type {GenericHeaderItems} from './DefaultTableHeader'; import type {GenericObject} from './Table'; import css from './Table.module.css'; type ClassNames = $ReadOnly<{tableRow?: string, checkbox?: string}>; // When using a custom Row prop, you need to create a component that looks like // MyRow = (props: TableRowProps<Entries, Extras>): React.Node => {...} // otherwise flow will complain. // Note that b/c extras is often optional, you will need to explicitly include // `invariant(extras, 'extras exists');` in order to pull values out of // extras (flow will remind you that it is of type `U | void`) export type TableRowProps<T, U> = { data: T, extras?: U, sortedKeys?: string[], headers?: GenericHeaderItems<T, U>, selected?: boolean, disabled?: boolean, }; export type TableRow<T, U> = React.ComponentType<TableRowProps<T, U>>; export const BasicRow: ClassNameComponent<'tr'> = makeClassNameComponent( css.defaultRow, 'tr', ); type EmptyRowProps = { emptyText?: React.Node, isLoading?: boolean, headersLength: number, customLoader?: React.Node, }; export const EmptyRow = ({ isLoading, emptyText, headersLength = 0, customLoader, }: EmptyRowProps): React.Element<'tr'> => ( <tr> <td colSpan={headersLength}> <div className={css.emptyRow}> {isLoading ? ( customLoader ? ( customLoader ) : ( <div className={css.defaultLoader}> {' '} <CircularLoader colorToken="colorFillPrimary" size="large" /> </div> ) ) : ( emptyText || ( <BodyLarge color={TEXT_COLORS.secondary} className={css.defaultEmptyText} > Nothing to display here. </BodyLarge> ) )} </div> </td> </tr> ); // This is a fallback row we use to render a table when // initially stubbing out a design, the idea is you just avoid // passing in a Row component and instead let this render out // all the fields in the header in the short term // // Using the default row has the benefit that mismatches between // header and entries _will_ error out even though there are the // suppressions below export function DefaultRow<T: GenericObject, U: GenericObject>({ data, extras, headers, selected, onSelect, classNames, disabled, }: { data: T, extras?: U, headers: GenericHeaderItems<T, U>, selected?: boolean, // value dependent on checkbox checked value onSelect?: ({value: string, checked: boolean}) => mixed, classNames?: ClassNames, disabled?: boolean, }): React.Node { return ( <BasicRow className={classify( selected ? css.defaultSelectedBodyRow : css.defaultBodyRow, classNames?.tableRow, )} > {selected != null && ( <PaddedContentCell className={classify(css.checkbox, classNames?.checkbox)} > <Checkbox checked={selected ? true : false} onChange={onSelect} disabled={disabled} ariaLabel="Select row" /> </PaddedContentCell> )} {headers.map((item, index) => { const {key, render: Renderer, className: cellClassName, sticky} = item; const value = data[key]; return Renderer ? ( <Renderer // eslint-disable-next-line react/no-array-index-key key={index} data={data} extras={extras} selected={selected} disabled={disabled} className={classify({[css.stickyCell]: sticky})} /> ) : ( <SingleCell // eslint-disable-next-line react/no-array-index-key key={index} title={String(value)} className={classify(cellClassName, {[css.stickyCell]: sticky})} /> ); })} </BasicRow> ); }