Skip to content

Commit

Permalink
feat(BaseTable): custom empty content support added (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
chelentos authored Sep 3, 2024
1 parent aab6643 commit b92ee11
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/components/BaseRow/BaseRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {b} from '../BaseTable/BaseTable.classname';
export interface BaseRowProps<TData, TScrollElement extends Element | Window = HTMLDivElement>
extends Omit<React.TdHTMLAttributes<HTMLTableRowElement>, 'className' | 'onClick'> {
cellClassName?: BaseCellProps<TData>['className'];
className?: string | ((row: RowType<TData>) => string);
className?: string | ((row?: RowType<TData>) => string);
getGroupTitle?: (row: RowType<TData>) => React.ReactNode;
getIsCustomRow?: (row: RowType<TData>) => boolean;
getIsGroupHeaderRow?: (row: RowType<TData>) => boolean;
Expand Down
86 changes: 57 additions & 29 deletions src/components/BaseTable/BaseTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import type {Row as RowType, Table as TableType} from '@tanstack/react-table';
import type {VirtualItem, Virtualizer} from '@tanstack/react-virtual';

import {getCellClassModes} from '../../utils';
import {BaseDraggableRow} from '../BaseDraggableRow';
import {BaseFooterRow} from '../BaseFooterRow';
import type {BaseHeaderRowProps} from '../BaseHeaderRow';
Expand All @@ -21,6 +22,7 @@ export interface BaseTableProps<TData, TScrollElement extends Element | Window =
bodyClassName?: string;
cellClassName?: BaseRowProps<TData>['cellClassName'];
className?: string;
emptyContent?: React.ReactNode | (() => React.ReactNode);
enableNesting?: boolean;
footerCellClassName?: string;
footerClassName?: string;
Expand Down Expand Up @@ -62,6 +64,7 @@ export const BaseTable = React.forwardRef(
bodyClassName,
cellClassName,
className,
emptyContent,
enableNesting,
footerCellClassName,
footerClassName,
Expand Down Expand Up @@ -116,6 +119,59 @@ export const BaseTable = React.forwardRef(
const footerRowCount = footerGroups ? footerGroups.length : 0;
const rowCount = bodyRowCount + headerRowCount + footerRowCount;

const renderBodyRows = () => {
const bodyRows = rowVirtualizer?.getVirtualItems() || rows;

if (bodyRows.length === 0) {
const rClassName =
typeof rowClassName === 'function' ? rowClassName() : rowClassName;

const cClassName =
typeof cellClassName === 'function' ? cellClassName() : cellClassName;

return (
<tr className={b('row', {}, rClassName)}>
<td
className={b('cell', getCellClassModes(), cClassName)}
colSpan={colCount}
>
{typeof emptyContent === 'function' ? emptyContent() : emptyContent}
</td>
</tr>
);
}

return bodyRows.map((virtualItemOrRow) => {
const row = rowVirtualizer
? rows[virtualItemOrRow.index]
: (virtualItemOrRow as RowType<TData>);

const rowProps: BaseRowProps<TData, TScrollElement> = {
cellClassName,
className: rowClassName,
getGroupTitle,
getIsGroupHeaderRow,
attributes: rowAttributes,
cellAttributes,
onClick: onRowClick,
renderGroupHeader,
renderGroupHeaderRowContent,
row,
rowVirtualizer,
virtualItem: rowVirtualizer
? (virtualItemOrRow as VirtualItem<HTMLTableRowElement>)
: undefined,
'aria-rowindex': headerRowCount + row.index + 1,
};

if (draggableContext) {
return <BaseDraggableRow key={row.id} {...rowProps} />;
}

return <BaseRow key={row.id} {...rowProps} />;
});
};

return (
<TableContextProvider getRowByIndex={getRowByIndex} enableNesting={enableNesting}>
<table
Expand Down Expand Up @@ -156,35 +212,7 @@ export const BaseTable = React.forwardRef(
}}
{...bodyAttributes}
>
{(rowVirtualizer?.getVirtualItems() || rows).map((virtualItemOrRow) => {
const row = rowVirtualizer
? rows[virtualItemOrRow.index]
: (virtualItemOrRow as RowType<TData>);

const rowProps: BaseRowProps<TData, TScrollElement> = {
cellClassName,
className: rowClassName,
getGroupTitle,
getIsGroupHeaderRow,
attributes: rowAttributes,
cellAttributes,
onClick: onRowClick,
renderGroupHeader,
renderGroupHeaderRowContent,
row,
rowVirtualizer,
virtualItem: rowVirtualizer
? (virtualItemOrRow as VirtualItem<HTMLTableRowElement>)
: undefined,
'aria-rowindex': headerRowCount + row.index + 1,
};

if (draggableContext) {
return <BaseDraggableRow key={row.id} {...rowProps} />;
}

return <BaseRow key={row.id} {...rowProps} />;
})}
{renderBodyRows()}
</tbody>
{footerGroups && (
<tfoot className={b('footer', {sticky: stickyFooter}, footerClassName)}>
Expand Down
4 changes: 4 additions & 0 deletions src/components/__stories__/BaseTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {ColumnPinningDemo} from './ColumnPinningDemo';
import {ColumnPinningWithReorderingDemo} from './ColumnPinningWithReorderingDemo';
import {ColumnPinningWithSelectionDemo} from './ColumnPinningWithSelectionDemo';
import {DefaultDemo} from './DefaultDemo';
import {EmptyContentDemo} from './EmptyContent';
import {GroupingDemo} from './GroupingDemo';
import {GroupingDemo2} from './GroupingDemo2';
import {GroupingWithSelectionDemo} from './GroupingWithSelectionDemo';
Expand Down Expand Up @@ -89,3 +90,6 @@ export const ColumnPinningWithSelection: StoryFn = ColumnPinningWithSelectionTem

const StickyHeaderTamplate: StoryFn = () => <StickyHeaderDemo />;
export const StickyHeader: StoryFn = StickyHeaderTamplate.bind({});

const EmptyContentTamplate: StoryFn = () => <EmptyContentDemo />;
export const EmptyContent: StoryFn = EmptyContentTamplate.bind({});
3 changes: 3 additions & 0 deletions src/components/__stories__/EmptyContent.classname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {cn} from '../../utils';

export const cnEmptyContentDemo = cn('empty-content-demo');
11 changes: 11 additions & 0 deletions src/components/__stories__/EmptyContent.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.empty-content-demo {
&__placeholder {
min-height: 20px;

display: flex;
align-items: center;
justify-content: center;

color: #ff0000;
}
}
20 changes: 20 additions & 0 deletions src/components/__stories__/EmptyContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

import {useTable} from '../../hooks';
import {BaseTable} from '../BaseTable';

import {cnEmptyContentDemo} from './EmptyContent.classname';
import {columns} from './constants/columns';

import './EmptyContent.scss';

const emptyContentPlaceholder = <div className={cnEmptyContentDemo('placeholder')}>No data</div>;

export const EmptyContentDemo = () => {
const table = useTable({
columns,
data: [],
});

return <BaseTable table={table} emptyContent={emptyContentPlaceholder} />;
};

0 comments on commit b92ee11

Please sign in to comment.