import { Menu } from "@mantine/core"; import { useToggle } from "@mantine/hooks"; import { ucFirst } from "core/utils"; import { TbArrowDown, TbArrowUp, TbChevronLeft, TbChevronRight, TbChevronsLeft, TbChevronsRight, TbDotsVertical, TbSelector, TbSquare, TbSquareCheckFilled } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "../buttons/IconButton"; import { Dropdown, type DropdownItem } from "../overlay/Dropdown"; export const Check = () => { const [checked, toggle] = useToggle([false, true]); const Icon = checked ? TbSquareCheckFilled : TbSquare; return ( ); }; export type DataTableProps = { data: Data[]; columns?: string[]; checkable?: boolean; onClickRow?: (row: Data) => void; onClickPage?: (page: number) => void; total?: number; page?: number; perPage?: number; rowActions?: (Omit & { onClick: (row: Data, key: number) => void; })[]; perPageOptions?: number[]; sort?: { by?: string; dir?: "asc" | "desc" }; onClickSort?: (name: string) => void; onClickPerPage?: (perPage: number) => void; renderHeader?: (column: string) => React.ReactNode; renderValue?: ({ value, property }: { value: any; property: string }) => React.ReactNode; classNames?: { value?: string; }; onClickNew?: () => void; }; export function DataTable = Record>({ data = [], columns, checkable, onClickRow, onClickPage, onClickSort, total, sort, page = 1, perPage = 10, perPageOptions, onClickPerPage, classNames, renderHeader, rowActions, renderValue, onClickNew }: DataTableProps) { total = total || data.length; page = page || 1; const select = columns && columns.length > 0 ? columns : Object.keys(data[0] || {}); const pages = Math.max(Math.ceil(total / perPage), 1); const CellRender = renderValue || CellValue; return (
{onClickNew && (
{onClickNew && }
)}
{select.length > 0 ? ( {checkable && ( )} {select.map((property, key) => { const label = renderHeader?.(property) ?? ucFirst(property); return ( ); })} {rowActions && rowActions.length > 0 && ) : null} {!data || data.length === 0 ? ( ) : ( data.map((row, key) => { const rowClick = () => onClickRow?.(row); return ( 0} className="hover:bg-primary/5 active:bg-muted border-muted data-[border]:border-t cursor-pointer transition-colors" > {checkable && ( )} {Object.entries(row).map(([key, value], index) => ( ))} {rowActions && rowActions.length > 0 && ( )} ); }) )}
}
No data to show
{/* @todo: create new dropdown using popover */}
{rowActions.map((a: any) => ( a.onClick(row, key)} leftSection={a.icon && } > {a.label} ))}
{perPageOptions && (
Per Page{" "} ({ label: String(perPage), perPage }))} position="top-end" onClickItem={(item: any) => onClickPerPage?.(item.perPage)} >
)}
Page {page} of {pages}
{onClickPage && (
)}
); } export const CellValue = ({ value, property }) => { let value_mono = false; //console.log("value", property, value); if (value !== null && typeof value === "object") { value = JSON.stringify(value); value_mono = true; } if (value !== null && typeof value !== "undefined") { return {value}; } return null; }; const SortIndicator = ({ sort, field }: { sort: Pick, "sort">["sort"]; field: string; }) => { if (!sort || sort.by !== field) return ; if (sort.dir === "asc") return ; return ; }; const TableDisplay = ({ perPage, page, items, total }) => { if (total === 0) { return <>No rows to show; } if (total === 1) { return <>Showing 1 row; } return ( <> Showing {perPage * (page - 1) + 1}-{perPage * (page - 1) + items} of {total} rows ); }; type TableNavProps = { current: number; total: number; onClick?: (page: number) => void; }; const TableNav: React.FC = ({ current, total, onClick }: TableNavProps) => { const navMap = [ { value: 1, Icon: TbChevronsLeft, disabled: current === 1 }, { value: current - 1, Icon: TbChevronLeft, disabled: current === 1 }, { value: current + 1, Icon: TbChevronRight, disabled: current === total }, { value: total, Icon: TbChevronsRight, disabled: current === total } ] as const; return navMap.map((nav, key) => ( )); };