Improve pagination + styles

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-04-09 10:00:30 +02:00
parent 2d1e0db379
commit 9721266532
10 changed files with 129 additions and 165 deletions
+55 -40
View File
@@ -10,15 +10,14 @@ import type { CSSProperties } from 'react';
import { FC } from 'react';
import { FocusEvent as FocusEvent_2 } from 'react';
import { ForwardRefExoticComponent } from 'react';
import { HTMLAttributes } from 'react';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import { Menu as Menu_2 } from '@base-ui-components/react/menu';
import { default as React_2 } from 'react';
import { ReactNode } from 'react';
import { RefAttributes } from 'react';
import type { RemixiconComponentType } from '@remixicon/react';
import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area';
import { TdHTMLAttributes } from 'react';
import { ThHTMLAttributes } from 'react';
import { Table as Table_2 } from '@tanstack/react-table';
import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip';
import type { useRender } from '@base-ui-components/react/use-render';
@@ -219,6 +218,28 @@ export interface ContainerProps {
style?: React.CSSProperties;
}
// @public
export const DataTable: {
Root: ForwardRefExoticComponent<
DataTableRootProps & RefAttributes<HTMLDivElement>
>;
Pagination: <TData>(
props: DataTablePaginationProps<TData> & {
ref?: React.ForwardedRef<HTMLDivElement>;
},
) => React.ReactElement;
};
// @public (undocumented)
export interface DataTablePaginationProps<TData>
extends React.HTMLAttributes<HTMLDivElement> {
table?: Table_2<TData>;
}
// @public (undocumented)
export interface DataTableRootProps
extends React.HTMLAttributes<HTMLDivElement> {}
// @public (undocumented)
export type Display = 'none' | 'flex' | 'block' | 'inline';
@@ -1028,43 +1049,37 @@ export type StylingPropDef = {
parseValue?: (value: string) => string | undefined;
};
// @public (undocumented)
export const Table: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableElement> & RefAttributes<HTMLTableElement>
>;
// @public (undocumented)
export const TableBody: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
// @public (undocumented)
export const TableCell: ForwardRefExoticComponent<
TdHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
// @public (undocumented)
export const TableFooter: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
// @public (undocumented)
export const TableHead: ForwardRefExoticComponent<
ThHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
// @public (undocumented)
export const TableHeader: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
// @public (undocumented)
export const TableRow: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableRowElement> & RefAttributes<HTMLTableRowElement>
>;
// @public
export const Table: {
Root: React_2.ForwardRefExoticComponent<
React_2.HTMLAttributes<HTMLTableElement> &
React_2.RefAttributes<HTMLTableElement>
>;
Header: React_2.ForwardRefExoticComponent<
React_2.HTMLAttributes<HTMLTableSectionElement> &
React_2.RefAttributes<HTMLTableSectionElement>
>;
Body: React_2.ForwardRefExoticComponent<
React_2.HTMLAttributes<HTMLTableSectionElement> &
React_2.RefAttributes<HTMLTableSectionElement>
>;
Head: React_2.ForwardRefExoticComponent<
React_2.ThHTMLAttributes<HTMLTableCellElement> &
React_2.RefAttributes<HTMLTableCellElement>
>;
Row: React_2.ForwardRefExoticComponent<
React_2.HTMLAttributes<HTMLTableRowElement> &
React_2.RefAttributes<HTMLTableRowElement>
>;
Cell: React_2.ForwardRefExoticComponent<
React_2.TdHTMLAttributes<HTMLTableCellElement> &
React_2.RefAttributes<HTMLTableCellElement>
>;
Caption: React_2.ForwardRefExoticComponent<
React_2.HTMLAttributes<HTMLTableCaptionElement> &
React_2.RefAttributes<HTMLTableCaptionElement>
>;
};
// @public (undocumented)
const Text_2: ForwardRefExoticComponent<
@@ -14,11 +14,10 @@
* limitations under the License.
*/
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Table } from '../Table';
import { DataTable } from '.';
import { components } from './mocked-components';
import { components, Component } from './mocked-components';
import { columns } from './mocked-columns';
import {
flexRender,
@@ -38,7 +37,7 @@ type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => {
const table = useReactTable({
const table = useReactTable<Component>({
data: components,
columns,
getCoreRowModel: getCoreRowModel(),
@@ -97,16 +96,7 @@ export const Default: Story = {
)}
</Table.Body>
</Table.Root>
<DataTable.Pagination
pageIndex={table.getState().pagination.pageIndex}
pageSize={table.getState().pagination.pageSize}
totalRows={table.getRowCount()}
onClickPrevious={() => table.previousPage()}
onClickNext={() => table.nextPage()}
canPrevious={table.getCanPreviousPage()}
canNext={table.getCanNextPage()}
setPageSize={pageSize => table.setPageSize(pageSize)}
/>
<DataTable.Pagination table={table} />
</DataTable.Root>
);
},
@@ -16,6 +16,15 @@
import type { Meta, StoryObj } from '@storybook/react';
import { DataTablePagination } from './DataTablePagination';
import {
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table';
import { components, Component } from '../mocked-components';
import { columns } from '../mocked-columns';
const meta = {
title: 'Components/DataTable/Pagination',
@@ -26,14 +35,16 @@ export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
pageIndex: 0,
pageSize: 10,
totalRows: 100,
onClickPrevious: () => {},
onClickNext: () => {},
canPrevious: true,
canNext: true,
setPageSize: () => {},
render: () => {
const table = useReactTable<Component>({
data: components,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});
return <DataTablePagination table={table} />;
},
};
@@ -4,7 +4,6 @@
justify-content: space-between;
padding-top: var(--canon-space-3);
border-top: 1px solid var(--canon-border);
margin-top: var(--canon-space-3);
}
.canon-TablePagination--left {
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
import { forwardRef } from 'react';
import { Text } from '../../Text';
import { DataTablePaginationProps } from './types';
import { IconButton } from '../../IconButton';
@@ -21,25 +22,19 @@ import clsx from 'clsx';
import { Select } from '../../Select';
/** @public */
const DataTablePagination = React.forwardRef<
HTMLDivElement,
DataTablePaginationProps
>(({ className, ...props }, ref) => {
const {
pageIndex,
pageSize,
onClickPrevious,
onClickNext,
canPrevious,
canNext,
totalRows,
setPageSize,
} = props;
function DataTablePagination<TData>(
props: DataTablePaginationProps<TData>,
ref: React.ForwardedRef<HTMLDivElement>,
) {
const { className, table, ...rest } = props;
const pageIndex = table?.getState().pagination.pageIndex;
const pageSize = table?.getState().pagination.pageSize;
return (
<div
ref={ref}
className={clsx('canon-TablePagination', className)}
{...props}
{...rest}
>
<div className="canon-TablePagination--left">
<Select
@@ -55,32 +50,37 @@ const DataTablePagination = React.forwardRef<
]}
value={pageSize?.toString()}
onValueChange={value => {
setPageSize?.(Number(value));
table?.setPageSize(Number(value));
}}
/>
</div>
<div className="canon-TablePagination--right">
<Text variant="body">{`${(pageIndex ?? 0) * (pageSize ?? 10) + 1} - ${
((pageIndex ?? 0) + 1) * (pageSize ?? 10)
} of ${totalRows}`}</Text>
} of ${table?.getRowCount()}`}</Text>
<IconButton
variant="secondary"
size="small"
onClick={onClickPrevious}
disabled={!canPrevious}
onClick={() => table?.previousPage()}
disabled={!table?.getCanPreviousPage()}
icon="chevron-left"
/>
<IconButton
variant="secondary"
size="small"
onClick={onClickNext}
disabled={!canNext}
onClick={() => table?.nextPage()}
disabled={!table?.getCanNextPage()}
icon="chevron-right"
/>
</div>
</div>
);
});
DataTablePagination.displayName = 'DataTablePagination';
}
export { DataTablePagination };
const ForwardedDataTablePagination = forwardRef(DataTablePagination) as <TData>(
props: DataTablePaginationProps<TData> & {
ref?: React.ForwardedRef<HTMLDivElement>;
},
) => React.ReactElement;
export { ForwardedDataTablePagination as DataTablePagination };
@@ -14,39 +14,13 @@
* limitations under the License.
*/
import { Table } from '@tanstack/react-table';
/** @public */
export interface DataTablePaginationProps
export interface DataTablePaginationProps<TData>
extends React.HTMLAttributes<HTMLDivElement> {
/**
* The current page index.
* The table instance.
*/
pageIndex?: number;
/**
* The current page size.
*/
pageSize?: number;
/**
* The total number of rows.
*/
totalRows?: number;
/**
* The function to call when the previous button is clicked.
*/
onClickPrevious?: () => void;
/**
* The function to call when the next button is clicked.
*/
onClickNext?: () => void;
/**
* Whether the previous button is disabled.
*/
canPrevious?: boolean;
/**
* Whether the next button is disabled.
*/
canNext?: boolean;
/**
* The function to call when the page size is changed.
*/
setPageSize?: (pageSize: number) => void;
table?: Table<TData>;
}
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { DataTableRoot } from './DataTableRoot';
import { DataTablePagination } from '../Pagination/DataTablePagination';
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { ColumnDef } from '@tanstack/react-table';
import { Component } from './mocked-components';
import { Checkbox } from '../Checkbox';
@@ -14,14 +14,13 @@
* limitations under the License.
*/
import { forwardRef } from 'react';
import clsx from 'clsx';
const TableRoot = forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="table">
<table ref={ref} className={className} {...props} />
</div>
<table ref={ref} className={clsx('canon-TableRoot', className)} {...props} />
));
TableRoot.displayName = 'TableRoot';
@@ -31,7 +30,7 @@ const TableHeader = forwardRef<
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={['table-header', className].join(' ')}
className={clsx('canon-TableHeader', className)}
{...props}
/>
));
@@ -41,7 +40,7 @@ const TableBody = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody ref={ref} className={['table-body', className].join(' ')} {...props} />
<tbody ref={ref} className={clsx('canon-TableBody', className)} {...props} />
));
TableBody.displayName = 'TableBody';
@@ -49,7 +48,7 @@ const TableRow = forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr ref={ref} className={['table-row', className].join(' ')} {...props}>
<tr ref={ref} className={clsx('canon-TableRow', className)} {...props}>
{props.children}
</tr>
));
@@ -59,7 +58,7 @@ const TableHead = forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th ref={ref} className={['table-head', className].join(' ')} {...props} />
<th ref={ref} className={clsx('canon-TableHead', className)} {...props} />
));
TableHead.displayName = 'TableHead';
@@ -67,7 +66,7 @@ const TableCell = forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td ref={ref} className={['table-cell', className].join(' ')} {...props} />
<td ref={ref} className={clsx('canon-TableCell', className)} {...props} />
));
TableCell.displayName = 'TableCell';
@@ -77,7 +76,7 @@ const TableCaption = forwardRef<
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={['table-caption', className].join(' ')}
className={clsx('canon-TableCaption', className)}
{...props}
/>
));
+12 -35
View File
@@ -1,54 +1,31 @@
.table {
position: relative;
overflow: auto;
.canon-TableRoot {
width: 100%;
table {
width: 100%;
caption-side: bottom;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
caption-side: bottom;
border-collapse: collapse;
}
.table-head {
.canon-TableHead {
text-align: left;
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
}
.table-body {
.canon-TableBody {
tr:last-child {
border-bottom: none;
}
}
.table-row {
.canon-TableRow {
border-bottom: 1px solid var(--canon-border);
transition: color 0.2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1),
inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1),
inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
&:hover {
background-color: var(--canon-bg-tint-hover);
}
}
.table-cell {
.canon-TableCell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1),
inset 0px -2px 0 0 var(--canon-bg-surface-1);
font-size: var(--canon-font-size-3);
}