import type { ComponentPropsWithRef, FC, HTMLAttributes, ReactNode, Ref, TdHTMLAttributes, ThHTMLAttributes } from "react"; import { createContext, isValidElement, useContext } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faArrowDown, faSort, faCopy, faPenToSquare, faCircleQuestion, faTrash } from "@fortawesome/pro-duotone-svg-icons"; const Edit01: FC<{ className?: string }> = ({ className }) => ; const Copy01: FC<{ className?: string }> = ({ className }) => ; const Trash01: FC<{ className?: string }> = ({ className }) => ; import type { CellProps as AriaCellProps, ColumnProps as AriaColumnProps, RowProps as AriaRowProps, TableHeaderProps as AriaTableHeaderProps, TableProps as AriaTableProps, } from "react-aria-components"; import { Cell as AriaCell, Collection as AriaCollection, Column as AriaColumn, ColumnResizer as AriaColumnResizer, Group as AriaGroup, ResizableTableContainer as AriaResizableTableContainer, Row as AriaRow, Table as AriaTable, TableBody as AriaTableBody, TableHeader as AriaTableHeader, useTableOptions, } from "react-aria-components"; import { Badge } from "@/components/base/badges/badges"; import { Checkbox } from "@/components/base/checkbox/checkbox"; import { Dropdown } from "@/components/base/dropdown/dropdown"; import { Tooltip, TooltipTrigger } from "@/components/base/tooltip/tooltip"; import { cx } from "@/utils/cx"; export const TableRowActionsDropdown = () => ( Edit Copy link Delete ); const TableContext = createContext<{ size: "sm" | "md" }>({ size: "md" }); const TableCardRoot = ({ children, className, size = "md", ...props }: HTMLAttributes & { size?: "sm" | "md" }) => { return (
{children}
); }; interface TableCardHeaderProps { /** The title of the table card header. */ title: string; /** The badge displayed next to the title. */ badge?: ReactNode; /** The description of the table card header. */ description?: string; /** The content displayed after the title and badge. */ contentTrailing?: ReactNode; /** The class name of the table card header. */ className?: string; } const TableCardHeader = ({ title, badge, description, contentTrailing, className }: TableCardHeaderProps) => { const { size } = useContext(TableContext); return (

{title}

{badge ? ( isValidElement(badge) ? ( badge ) : ( {badge} ) ) : null}
{description &&

{description}

}
{contentTrailing}
); }; interface TableRootProps extends AriaTableProps, Omit, "className" | "slot" | "style"> { size?: "sm" | "md"; } const TableRoot = ({ className, size = "md", ...props }: TableRootProps) => { const context = useContext(TableContext); return ( cx("w-full", typeof className === "function" ? className(state) : className)} {...props} /> ); }; TableRoot.displayName = "Table"; interface TableHeaderProps extends AriaTableHeaderProps, Omit, "children" | "className" | "slot" | "style"> { bordered?: boolean; } const TableHeader = ({ columns, children, bordered = true, className, ...props }: TableHeaderProps) => { const { size } = useContext(TableContext); const { selectionBehavior, selectionMode } = useTableOptions(); return ( cx( "relative bg-secondary sticky top-0 z-10", size === "sm" ? "h-9" : "h-11", // Row border—using an "after" pseudo-element to avoid the border taking up space. bordered && "[&>tr>th]:after:pointer-events-none [&>tr>th]:after:absolute [&>tr>th]:after:inset-x-0 [&>tr>th]:after:bottom-0 [&>tr>th]:after:h-px [&>tr>th]:after:bg-border-secondary [&>tr>th]:focus-visible:after:bg-transparent", typeof className === "function" ? className(state) : className, ) } > {selectionBehavior === "toggle" && ( {selectionMode === "multiple" && (
)}
)} {children}
); }; TableHeader.displayName = "TableHeader"; interface TableHeadProps extends AriaColumnProps, Omit, "children" | "className" | "style" | "id"> { label?: string; tooltip?: string; resizable?: boolean; } const TableHead = ({ className, tooltip, label, children, resizable = true, ...props }: TableHeadProps) => { const { selectionBehavior } = useTableOptions(); return ( cx( "relative p-0 px-6 py-2 outline-hidden focus-visible:z-1 focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-bg-primary focus-visible:ring-inset", selectionBehavior === "toggle" && "nth-2:pl-3", state.allowsSorting && "cursor-pointer", typeof className === "function" ? className(state) : className, ) } > {(state) => (
{label && {label}} {typeof children === "function" ? children(state) : children}
{tooltip && ( )} {state.allowsSorting && (state.sortDirection ? ( ) : ( ))} {resizable && ( )}
)}
); }; TableHead.displayName = "TableHead"; interface TableRowProps extends AriaRowProps, Omit, "children" | "className" | "onClick" | "slot" | "style" | "id"> { highlightSelectedRow?: boolean; } const TableRow = ({ columns, children, className, highlightSelectedRow = true, ...props }: TableRowProps) => { const { size } = useContext(TableContext); const { selectionBehavior } = useTableOptions(); return ( cx( "relative outline-focus-ring transition-colors after:pointer-events-none hover:bg-secondary focus-visible:outline-2 focus-visible:-outline-offset-2", size === "sm" ? "h-14" : "h-18", highlightSelectedRow && "selected:bg-secondary", // Row border—using an "after" pseudo-element to avoid the border taking up space. "[&>td]:after:absolute [&>td]:after:inset-x-0 [&>td]:after:bottom-0 [&>td]:after:h-px [&>td]:after:w-full [&>td]:after:bg-border-secondary last:[&>td]:after:hidden [&>td]:focus-visible:after:opacity-0 focus-visible:[&>td]:after:opacity-0", typeof className === "function" ? className(state) : className, ) } > {selectionBehavior === "toggle" && (
)} {children}
); }; TableRow.displayName = "TableRow"; interface TableCellProps extends AriaCellProps, Omit, "children" | "className" | "style" | "id"> { ref?: Ref; } const TableCell = ({ className, children, ...props }: TableCellProps) => { const { size } = useContext(TableContext); const { selectionBehavior } = useTableOptions(); return ( cx( "relative text-sm text-tertiary outline-focus-ring focus-visible:z-1 focus-visible:outline-2 focus-visible:-outline-offset-2", size === "sm" && "px-5 py-3", size === "md" && "px-6 py-4", selectionBehavior === "toggle" && "nth-2:pl-3", typeof className === "function" ? className(state) : className, ) } > {children} ); }; TableCell.displayName = "TableCell"; const TableCard = { Root: TableCardRoot, Header: TableCardHeader, }; const Table = TableRoot as typeof TableRoot & { Body: typeof AriaTableBody; Cell: typeof TableCell; Head: typeof TableHead; Header: typeof TableHeader; Row: typeof TableRow; }; Table.Body = AriaTableBody; Table.Cell = TableCell; Table.Head = TableHead; Table.Header = TableHeader; Table.Row = TableRow; export { Table, TableCard };