98ff4f3c04
Co-authored-by: bwallacee <ben@vega.xyz>
147 lines
4.4 KiB
TypeScript
147 lines
4.4 KiB
TypeScript
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
|
import classNames from 'classnames';
|
|
import { forwardRef, type ReactNode, type HTMLAttributes } from 'react';
|
|
|
|
export const BORDER_COLOR = 'border-vega-clight-500 dark:border-vega-cdark-500';
|
|
export const GRADIENT =
|
|
'bg-gradient-to-b from-vega-clight-800 dark:from-vega-cdark-800 to-transparent';
|
|
|
|
type TableColumnDefinition = {
|
|
displayName?: ReactNode;
|
|
name: string;
|
|
tooltip?: string;
|
|
className?: string;
|
|
headerClassName?: string;
|
|
testId?: string;
|
|
};
|
|
|
|
type DataEntry = {
|
|
[key: TableColumnDefinition['name']]: ReactNode;
|
|
className?: string;
|
|
};
|
|
|
|
type TableProps = {
|
|
columns: TableColumnDefinition[];
|
|
data: DataEntry[];
|
|
noHeader?: boolean;
|
|
noCollapse?: boolean;
|
|
onRowClick?: (index: number) => void;
|
|
};
|
|
|
|
const INNER_BORDER_STYLE = `border-b ${BORDER_COLOR}`;
|
|
|
|
export const Table = forwardRef<
|
|
HTMLTableElement,
|
|
TableProps & HTMLAttributes<HTMLTableElement>
|
|
>(
|
|
(
|
|
{
|
|
columns,
|
|
data,
|
|
noHeader = false,
|
|
noCollapse = false,
|
|
className,
|
|
onRowClick,
|
|
...props
|
|
},
|
|
ref
|
|
) => {
|
|
const header = (
|
|
<thead className={classNames({ 'max-md:hidden': !noCollapse })}>
|
|
<tr>
|
|
{columns.map(({ displayName, name, tooltip, headerClassName }) => (
|
|
<th
|
|
key={name}
|
|
col-id={name}
|
|
className={classNames(
|
|
'px-5 py-3 text-xs text-vega-clight-100 dark:text-vega-cdark-100 font-normal',
|
|
INNER_BORDER_STYLE,
|
|
headerClassName
|
|
)}
|
|
>
|
|
<span className="flex flex-row items-center gap-2">
|
|
<span>{displayName}</span>
|
|
{tooltip ? (
|
|
<Tooltip description={tooltip}>
|
|
<button className="text-vega-clight-400 dark:text-vega-cdark-400 no-underline decoration-transparent w-[12px] h-[12px] inline-flex">
|
|
<VegaIcon size={12} name={VegaIconNames.INFO} />
|
|
</button>
|
|
</Tooltip>
|
|
) : null}
|
|
</span>
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
);
|
|
return (
|
|
<table
|
|
ref={ref}
|
|
className={classNames(
|
|
'w-full',
|
|
'border-separate border rounded-md border-spacing-0 overflow-hidden',
|
|
BORDER_COLOR,
|
|
GRADIENT,
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{!noHeader && header}
|
|
<tbody>
|
|
{data.map((dataEntry, i) => (
|
|
<tr
|
|
key={i}
|
|
className={classNames(dataEntry['className'] as string, {
|
|
'max-md:flex flex-col w-full': !noCollapse,
|
|
// collapsed (mobile) row divider
|
|
'first:border-t-0 max-md:border-t border-vega-clight-500 dark:border-vega-cdark-500 first:mt-0 mt-1':
|
|
!noCollapse,
|
|
})}
|
|
onClick={() => {
|
|
if (onRowClick) {
|
|
onRowClick(i);
|
|
}
|
|
}}
|
|
>
|
|
{columns.map(({ name, displayName, className, testId }, j) => (
|
|
<td
|
|
className={classNames(
|
|
'px-5 py-3',
|
|
{
|
|
'max-md:flex max-md:flex-col max-md:justify-between':
|
|
!noCollapse,
|
|
},
|
|
INNER_BORDER_STYLE,
|
|
{
|
|
'border-none': i === data.length - 1 && noCollapse,
|
|
'md:border-none': i === data.length - 1,
|
|
'max-md:border-none':
|
|
i === data.length - 1 && j === columns.length - 1,
|
|
},
|
|
className
|
|
)}
|
|
key={`${i}-${name}`}
|
|
>
|
|
{/** display column name in mobile view */}
|
|
{!noCollapse && !noHeader && displayName && (
|
|
<span
|
|
aria-hidden
|
|
className="px-0 font-mono text-xs md:hidden text-vega-clight-100 dark:text-vega-cdark-100"
|
|
>
|
|
{displayName}
|
|
</span>
|
|
)}
|
|
<span data-testid={`${testId || name}-${i}`}>
|
|
{dataEntry[name]}
|
|
</span>
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
);
|
|
}
|
|
);
|
|
Table.displayName = 'Table';
|