Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/components/table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ declare module '@tanstack/react-table' {
* Merged into the `style` prop of the default-rendered `<th>` element.
*/
thStyle?: CSSProperties;

/**
* Merged into the `style` prop of the default-rendered `<td>` element.
*/
tdStyle?: CSSProperties;
}
}
60 changes: 60 additions & 0 deletions src/components/table/table_body.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Icon } from '@blueprintjs/core';
import styled from '@emotion/styled';
import type { Row, RowData } from '@tanstack/react-table';
import type { VirtualItem, Virtualizer } from '@tanstack/react-virtual';
import { notUndefined } from '@tanstack/react-virtual';
Expand All @@ -23,6 +25,14 @@ interface TableBodyProps<TData extends RowData> {
virtualizer: Virtualizer<HTMLDivElement, Element>;
isReorderingEnabled: boolean;
renderRowPreview?: TableRowPreviewRenderer<TData>;

emptyContent: ReactNode;
emptyIcon: ReactNode;

/**
* Bound to `EmptyRow`, it is needed for td colSpan.
*/
columns: number;
}

export function TableBody<TData extends RowData>(props: TableBodyProps<TData>) {
Expand All @@ -36,6 +46,9 @@ export function TableBody<TData extends RowData>(props: TableBodyProps<TData>) {
) as TableRowTrRenderer<TData>,
virtualizer,
virtualizeRows,
emptyContent,
emptyIcon,
columns,
} = props;

if (virtualizeRows) {
Expand Down Expand Up @@ -72,6 +85,13 @@ export function TableBody<TData extends RowData>(props: TableBodyProps<TData>) {
}}
/>
))}
{virtualItems.length === 0 && (
<EmptyRow
emptyContent={emptyContent}
emptyIcon={emptyIcon}
columns={columns}
/>
)}
{after > 0 && (
<tr>
<td style={{ height: after }} />
Expand All @@ -91,10 +111,50 @@ export function TableBody<TData extends RowData>(props: TableBodyProps<TData>) {
}
/>
))}
{rows.length === 0 && (
<EmptyRow
emptyContent={emptyContent}
emptyIcon={emptyIcon}
columns={columns}
/>
)}
</tbody>
);
}

interface EmptyRowProps {
emptyContent: ReactNode;
emptyIcon: ReactNode;
columns: number;
}

function EmptyRow(props: EmptyRowProps) {
const {
emptyIcon = <Icon icon="eye-off" />,
emptyContent = 'No data',
columns,
} = props;

return (
<tr>
<td colSpan={columns}>
<EmptyState>
{emptyIcon}
{emptyContent}
</EmptyState>
</td>
</tr>
);
}

const EmptyState = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 0.25em;
gap: 0.5em;
`;

type TableRowRenderer<TData extends RowData> = (row: Row<TData>) => ReactNode;

function TableRow<TData>({
Expand Down
32 changes: 29 additions & 3 deletions src/components/table/table_root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import {
useReactTable,
} from '@tanstack/react-table';
import { useVirtualizer } from '@tanstack/react-virtual';
import type { CSSProperties, RefObject, TableHTMLAttributes } from 'react';
import type {
CSSProperties,
ReactNode,
RefObject,
TableHTMLAttributes,
} from 'react';
import { useEffect, useMemo, useRef } from 'react';
import { match } from 'ts-pattern';

Expand Down Expand Up @@ -185,6 +190,18 @@ interface TableBaseProps<TData extends RowData> {
* Ignored when using custom row rendering with `renderRowTr`.
*/
renderRowPreview?: TableRowPreviewRenderer<TData>;

/**
* Icon to display when the table is empty.
* @default `<Icon icon="eye-off" />`
*/
emptyIcon?: ReactNode;

/**
* Content to display when the table is empty.
* @default `'No data'`
*/
emptyContent?: ReactNode;
}

interface RegularTableProps<
Expand Down Expand Up @@ -244,6 +261,9 @@ export function Table<TData extends RowData>(props: TableProps<TData>) {
renderRowPreview,

scrollToRowRef,

emptyIcon,
emptyContent,
} = props;
const isReorderingEnabled = !!onRowOrderChanged;
const virtualScrollElementRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -294,13 +314,16 @@ export function Table<TData extends RowData>(props: TableProps<TData>) {
}),
[className, getTdProps, renderRowTr, columns, compact],
);

const rows = table.getRowModel().rows;

return (
<FlashedRowProvider>
<PreviewTablePropsContextProvider
value={tablePreviewProps as PreviewTablePropsContextValue<unknown>}
>
<ItemOrderProvider
items={table.getRowModel().rows}
items={rows}
onOrderChanged={(items) => {
onRowOrderChanged?.(items.map((item) => item.original));
}}
Expand Down Expand Up @@ -341,14 +364,17 @@ export function Table<TData extends RowData>(props: TableProps<TData>) {
/>
)}
<TableBody
rows={table.getRowModel().rows}
rows={rows}
renderRowTr={renderRowTr}
tdStyle={tdStyle}
getTdProps={getTdProps}
virtualizer={tanstackVirtualizer}
virtualizeRows={virtualizeRows}
renderRowPreview={renderRowPreview}
isReorderingEnabled={isReorderingEnabled}
emptyIcon={emptyIcon}
emptyContent={emptyContent}
columns={table.getAllColumns().length}
/>
</CustomHTMLTable>
</ScrollContainer>
Expand Down
3 changes: 2 additions & 1 deletion src/components/table/table_row_cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ export function TableRowCell<TData extends RowData>(
) {
const { cell, tdStyle, getTdProps } = props;

const tdStyleMeta = cell.column.columnDef.meta?.tdStyle;
const tdProps = getTdProps?.(cell);
const style = { ...tdStyle, ...tdProps?.style };
const style = { ...tdStyleMeta, ...tdStyle, ...tdProps?.style };

return (
<td {...tdProps} style={style}>
Expand Down
17 changes: 16 additions & 1 deletion stories/components/table.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { GetTdProps, TableProps } from '../../src/components/index.js';
import { Table, TableRowTr } from '../../src/components/index.js';
import { table } from '../data/data.js';

import { columns } from './table_columns.js';
import { columns, columnsNativeMeta } from './table_columns.js';

type TableRecord = (typeof table)[number];

Expand Down Expand Up @@ -111,3 +111,18 @@ export const WithTdStyle = {
export const Virtualized: Story = {
args: { virtualizeRows: true, estimatedRowHeight: () => 172 },
} satisfies Story;

export const WithMetaThAndTdStyle: Story = {
args: {
data: table.slice(0, 1),
columns: columnsNativeMeta,
},
};

export const EmptyTable: Story = {
args: {
data: [],
emptyContent: 'No molecules',
emptyIcon: <span aria-hidden="true">🪹</span>,
},
};
20 changes: 20 additions & 0 deletions stories/components/table_columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,23 @@ export const columns = [
},
}),
];

export const columnsNativeMeta = [
columnHelper.accessor('ocl.idCode', {
header: 'Molecule',
cell: ({ getValue }) => <IdcodeSvgRenderer idcode={getValue()} />,
}),
columnHelper.accessor('name', {
header: 'Name',
meta: {
thStyle: {
backgroundColor: 'black',
color: '#FFF903',
},
tdStyle: {
backgroundColor: '#DB261D',
color: '#FFF903',
},
},
}),
];
Loading