/*! Strand UI | MIT License | dillingerstaffing.com */ import type { ComponentChildren, JSX } from "preact"; import { forwardRef } from "preact/compat"; import { useState } from "preact/hooks"; import { cx } from "../../internal/index.js"; export type TableRow = Record; export type TableSort = { key: string; direction: "asc" | "desc" }; export interface TableColumn { /** Field key in each row. */ key: string; header: string; sortable?: boolean; width?: string; /** Renders the cell from the whole row; the field value renders by default. */ render?: (row: TableRow, index: number) => ComponentChildren; } export interface TableProps extends Omit, "data"> { columns: TableColumn[]; data: TableRow[]; /** Field whose value keys each row, or a function of the row; the row index by default. */ rowKey?: string | ((row: TableRow, index: number) => string | number); /** The table's accessible name, rendered as a visually hidden caption. */ label?: string; /** A visible caption. */ caption?: ComponentChildren; /** Text of the single cell shown when there are no rows. */ emptyLabel?: string; /** Controlled sort; leave unset to let the table own it. */ sort?: TableSort | null; /** Called with the column key and the direction after a sortable header is pressed. */ onSort?: (key: string, direction: "asc" | "desc") => void; } /** * Data table with sortable headers. * * @example * */ export const Table = forwardRef( ({ columns, data, rowKey, label, caption, emptyLabel = "No rows", sort, onSort, className = "", ...rest }, ref) => { const [ownSort, setOwnSort] = useState(null); const current = sort === undefined ? ownSort : sort; const handleSort = (key: string) => { const direction = current?.key === key && current.direction === "asc" ? "desc" : "asc"; if (sort === undefined) setOwnSort({ key, direction }); onSort?.(key, direction); }; const keyOf = (row: TableRow, index: number) => (typeof rowKey === "function" ? rowKey(row, index) : rowKey ? String(row[rowKey]) : index); return (
{(caption || label) && } {columns.map((col) => { const sorted = current?.key === col.key ? current.direction : null; return ( ); })} {data.length === 0 ? ( ) : ( data.map((row, rowIndex) => ( {columns.map((col) => ( ))} )) )}
{caption ?? label}
{col.sortable ? ( ) : ( col.header )}
{emptyLabel}
{col.render ? col.render(row, rowIndex) : (row[col.key] as ComponentChildren)}
); }, ); Table.displayName = "Table";