@sinchsmb/ui-kit
Version:
UI kit for SinchSMB frontend
318 lines (317 loc) • 9.66 kB
TypeScript
import { ReactNode } from 'react';
import { CommonProps } from '../../types';
import { TableColumnVisibility, TableSelection, TableSort } from './types';
/** Props for the {@link Table} component */
export type TableProps<C extends string, D extends object> = CommonProps & {
/** The data to display in the table */
rows: D[];
/** Function that should return unique key for given row */
rowIdProvider: (row: D) => string;
/** List of columns to display in the table */
columns: TableColumnVisibility<C>[] | C[];
/** Count of all rows that can be rendered on table */
totalRows: number;
/** Current table selection */
selection?: TableSelection;
/** Function that should be called when table selection is changed */
onSelectionChanged?: (selection: TableSelection) => void;
/** Current table sort */
sort?: TableSort<C>;
/** Function that should be called when table sort is changed */
onSortChanged?: (sort: TableSort<C>) => void;
/** If `true` than loader indicator will be shown */
loading?: boolean;
/** Limit the number of table selections */
rowSelectionIncludeLimit?: number;
/**
* If `true` than user can use row actions.
*
* @default true
*/
rowActionsEnabled?: boolean;
/**
* If `true`, the "actions" and "selection" columns are always visible
*/
stickyColumns?: boolean;
children: ReactNode;
} & ({
/**
* If `true` than user can select rows.
*
* @default true
*/
rowSelectionEnabled: true;
/** Item name to show in the selection banner */
rowSelectionName: string;
} | {
rowSelectionEnabled?: false;
rowSelectionName?: string;
});
/**
* Component is used for show data in table view.
*
* It supports flexible data cell rendering, sort data, pagination and column order and visibility control.
*
* To create table you must set {@link TableProps.rows} with data, configure columns by {@link TableProps.columns}
* and set total rows count by {@link TableProps.totalRows}.
*
* Also, you should provide {@link TableProps.rowIdProvider} function that should return unique key for given row.
*
* ## Columns configuration
*
* To configure columns define list of available columns in {@link TableProps.columns} and define
* column render in {@link TableHeader} and {@link TableBody}.
*
* Pay attention that {@link TableRow} should be memoized for performance issue.
*
* ```tsx
* <Table columns={[Columns.Name]}>
* <TableHeader>
* <TableHeaderCell column={Columns.Name}>Name</TableHeaderCell>
* </TableHeader>
*
* <TableBody<RowData>>{(row) => <MyTableRowMemo row={row} />}</TableBody>
* </Table>
*
* // Somewhere in different place
* function MyTableRow({ row }: { row: RowData }) {
* return (
* <TableRow linkTo="dashboard">
* <TableCell column={Columns.Name}>{row.name}</TableCell>
* </TableRow>
* );
* }
*
* const MyTableRowMemo = memo(MyTableRow);
* ```
*
* ## Rows per page
*
* {@link Table} supports rows per change feature. To enable it you must use {@link TableRowsPerPage} component
* in {@link TableFooter}.
*
* ```tsx
* <TableFooter>
* <TableRowsPerPage rowsPerPage={rowsPerPage} onRowsPerPageChange={setRowsPerPage} />
* </TableFooter>
* ```
*
* ## Pagination
*
* {@link Table} supports pagination. To enable it you must use {@link TablePagination} component
* in {@link TableFooter}.
*
* ```tsx
* <TableFooter>
* <TablePagination
* rowsPerPage={rowsPerPage}
* currentPage={currentPage}
* linkToBuilder={(page) => `/?page=${page}`}
* />
* </TableFooter>
* ```
*
* Pagination should be implemented by some URL search query params for best user experience.
*
* ```tsx
* const { search } = useLocation();
* const currentPage = Number(new URLSearchParams(search).get('page') || '0');
* ```
*
* ## Column sort
*
* {@link Table} supports column sort by {@link TableProps.sort} and {@link TableProps.onSortChanged}
* {@link Table} props and {@link TableHeaderCellProps.sortable} {@link TableHeaderCell} props.
*
* Set current sort in {@link TableProps.sort} and define header that should be sortable by
* {@link TableHeaderCellProps.sortable}.
*
* ```tsx
* <Table
* sort={currentSort}
* onSortChanged={handleSortChanged}
* >
* <TableHeader>
* <TableHeaderCell column={Columns.Name} sortable>Name</TableHeaderCell>
* </TableHeader>
* </Table>
* ```
*
* ## Row actions
*
* {@link Table} supports row actions that will be shown on each row at rightTo enable it you must use
* {@link TableActionsCell} component in {@link TableRow}.
*
* ```tsx
* <TableRow>
* <TableActionsCell>
* <TableAction icon={IconGlyph.Edit} onClick={editClickHandler}>Edit</TableAction>
* <TableAction icon={IconGlyph.Delete} onClick={deleteClickHandler}>Delete</TableAction>
* </TableActionsCell>
* </TableRow>
* ```
*
* ## Rows selection
*
* Fot select some rows in {@link Table} provide {@link TableProps.selection} property with selection mode
* and {@link TableSelection.selectedIds} or {@link TableSelection.unselectedIds} row IDs.
* And provide {@link TableProps.onSelectionChanged} property to process selection change events.
*
* ```tsx
* <Table
* selection={currentSelection}
* onSelectionChanged={handleSelectionChanged}
* >
* // ...
* </Table>
* ```
*
* ### Disable selection
*
* To disable rows selection set `false` to {@link TableProps.rowSelectionEnabled}.
*
* ```tsx
* <Table rowSelectionEnabled={false}>
* // ...
* </Table>
* ```
*
* ## Interaction
*
* If you need to open some URL or run some code when a row is clicked, you should use
* {@link TableRowProps.linkTo} or {@link TableRowProps.onClick} props.
*
* ```tsx
* <TableRow linkTo="some_url">
* // ...
* </TableRow>
* ```
*
* ## States
*
* {@link Table} provide some special states.
*
* ### Empty state
*
* If there are no rows to display, you can set specific content that should be displayed by
* {@link TableNoRowsBody} component.
*
* ```tsx
* <Table>
* <TableNoRowsBody>
* <EmptyState>
* <EmptyStateHeader>There are no items</EmptyStateHeader>
* <EmptyStateBody>Click "Add item" button to add one</EmptyStateBody>
* <EmptyStateButton>Add item</EmptyStateButton>
* </EmptyState>
* </TableNoRowsBody>
* </Table>
* ```
*
* ### Loading state
*
* You can pass {@link TableProps.loading} props to indicate that table is in loading.
*
* If table has no data and state is loading then preload indicator will be shown.
*
* ```tsx
* <Table loading>
* // ...
* </Table>
* ```
*
* ## Complex example
*
* ```tsx
* interface RowData {
* id: string;
* name: string;
* phone: string;
* }
*
* enum Columns {
* Name,
* Phone
* }
*
* function Users() {
* const rows: RowData[] = [
* // Some data
* ];
* const totalRows = 1000;
* const [ rowsPerPage, setRowsPerPage ] = useState(25);
* const rowIdProvider = useCallback((row: RowData) => row.id, []);
*
* const { search } = useLocation();
* const currentPage = Number(new URLSearchParams(search).get('page') || '0');
*
* return (
* <Table
* columns={[Columns.Name, Columns.Phone]}
* rows={rows}
* rowIdProvider={rowIdProvider}
* totalRows={totalRows}
* >
* <TableCaption>
* <TableCaptionTitle>Table title</TableCaptionTitle>
* <TableCaptionSummary>Table sub title</TableCaptionSummary>
* </TableCaption>
*
* <TableHeader>
* <TableHeaderCell column={Columns.Name} sortable>
* Name
* </TableHeaderCell>
* <TableHeaderCell column={Columns.Phone}>
* Phone
* </TableHeaderCell>
* </TableHeader>
*
* <TableBody<RowData>>{(row) => <MyTableRowMemo row={row} />}</TableBody>
*
* <TableNoRowsBody>
* <EmptyState>
* <EmptyStateHeader>There are no items</EmptyStateHeader>
* <EmptyStateBody>Click "Add item" button to add one</EmptyStateBody>
* <EmptyStateButton>Add item</EmptyStateButton>
* </EmptyState>
* </TableNoRowsBody>
*
* <TableNoColumnsBody>
* <EmptyState>
* <EmptyStateHeader>All table columns are hidden</EmptyStateHeader>
* <EmptyStateBody>Click the "Adjust columns" button to choose columns to show</EmptyStateBody>
* </EmptyState>
* </TableNoColumnsBody>
*
* <TableFooter>
* <TableRowsPerPage rowsPerPage={rowsPerPage} onRowsPerPageChange={setRowsPerPage} />
* <TablePagination
* rowsPerPage={rowsPerPage}
* currentPage={currentPage}
* linkToBuilder={(page) => `/?page=${page}`}
* />
* </TableFooter>
* </Table>
* );
* }
*
* function MyTableRow({ row }: { row: RowData }) {
* const editClickHandler = useCallback(() => {}, []);
* const deleteClickHandler = useCallback(() => {}, []);
*
* return (
* <TableRow>
* <TableCell column={Columns.Name}>{row.name}</TableCell>
* <TableCell column={Columns.Phone}>{row.phone}</TableCell>
* <TableActionsCell>
* <TableAction icon={IconGlyph.Edit} onClick={editClickHandler}>Edit</TableAction>
* <TableAction icon={IconGlyph.Delete} onClick={deleteClickHandler}>Delete</TableAction>
* </TableActionsCell>
* </TableRow>
* );
* }
*
* const MyTableRowMemo = memo(MyTableRow);
* ```
*/
export declare function Table<C extends string, D extends object>(props: TableProps<C, D>): JSX.Element;