Skip to content

Commit 8d319aa

Browse files
committed
feat/TablePagination
1 parent 749734e commit 8d319aa

File tree

5 files changed

+223
-0
lines changed

5 files changed

+223
-0
lines changed

packages/ui/src/components/Table/Table.stories.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Meta, StoryFn } from "@storybook/react";
2+
import { useState } from "react";
23
import type { TableProps } from "./Table";
34
import { Table } from "./Table";
45

@@ -86,3 +87,97 @@ const Template: StoryFn<TableProps> = (args) => (
8687

8788
export const DefaultTable = Template.bind({});
8889
DefaultTable.storyName = "Default";
90+
91+
const TPageTemplate: StoryFn<TableProps> = (args) => {
92+
const [pageNo, setPageNo] = useState(1);
93+
const [rowsPerPage] = useState(10);
94+
95+
const handlePageChange = (newPage: number) => setPageNo(newPage);
96+
97+
return (
98+
<>
99+
<Table {...args}>
100+
<Table.Head>
101+
<Table.HeadCell>Product name</Table.HeadCell>
102+
<Table.HeadCell>Color</Table.HeadCell>
103+
<Table.HeadCell>Category</Table.HeadCell>
104+
<Table.HeadCell>Price</Table.HeadCell>
105+
<Table.HeadCell>
106+
<span className="sr-only">Edit</span>
107+
</Table.HeadCell>
108+
</Table.Head>
109+
<Table.Body className="divide-y">
110+
<Table.Row className="bg-white dark:border-gray-700 dark:bg-gray-800">
111+
<Table.Cell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
112+
{'Apple MacBook Pro 17"'}
113+
</Table.Cell>
114+
<Table.Cell>Sliver</Table.Cell>
115+
<Table.Cell>Laptop</Table.Cell>
116+
<Table.Cell>$2999</Table.Cell>
117+
<Table.Cell>
118+
<a href="/tables" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
119+
Edit
120+
</a>
121+
</Table.Cell>
122+
</Table.Row>
123+
<Table.Row className="bg-white dark:border-gray-700 dark:bg-gray-800">
124+
<Table.Cell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
125+
Microsoft Surface Pro
126+
</Table.Cell>
127+
<Table.Cell>White</Table.Cell>
128+
<Table.Cell>Laptop PC</Table.Cell>
129+
<Table.Cell>$1999</Table.Cell>
130+
<Table.Cell>
131+
<a href="/tables" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
132+
Edit
133+
</a>
134+
</Table.Cell>
135+
</Table.Row>
136+
<Table.Row className="bg-white dark:border-gray-700 dark:bg-gray-800">
137+
<Table.Cell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
138+
Magic Mouse 2
139+
</Table.Cell>
140+
<Table.Cell>Black</Table.Cell>
141+
<Table.Cell>Accessories</Table.Cell>
142+
<Table.Cell>$99</Table.Cell>
143+
<Table.Cell>
144+
<a href="/tables" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
145+
Edit
146+
</a>
147+
</Table.Cell>
148+
</Table.Row>
149+
<Table.Row className="bg-white dark:border-gray-700 dark:bg-gray-800">
150+
<Table.Cell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
151+
Google Pixel Phone
152+
</Table.Cell>
153+
<Table.Cell>Gray</Table.Cell>
154+
<Table.Cell>Phone</Table.Cell>
155+
<Table.Cell>$799</Table.Cell>
156+
<Table.Cell>
157+
<a href="/tables" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
158+
Edit
159+
</a>
160+
</Table.Cell>
161+
</Table.Row>
162+
<Table.Row className="bg-white dark:border-gray-700 dark:bg-gray-800">
163+
<Table.Cell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
164+
Apple Watch 5
165+
</Table.Cell>
166+
<Table.Cell>Red</Table.Cell>
167+
<Table.Cell>Wearables</Table.Cell>
168+
<Table.Cell>$999</Table.Cell>
169+
<Table.Cell>
170+
<a href="/tables" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
171+
Edit
172+
</a>
173+
</Table.Cell>
174+
</Table.Row>
175+
</Table.Body>
176+
</Table>
177+
<Table.Pagination count={100} onPageChange={handlePageChange} page={pageNo} rowsPerPage={rowsPerPage} />
178+
</>
179+
);
180+
};
181+
182+
export const PaginationTable = TPageTemplate.bind({});
183+
PaginationTable.storyName = "Pagination";

packages/ui/src/components/Table/Table.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import { TableCell } from "./TableCell";
1010
import { TableContext } from "./TableContext";
1111
import { TableHead, type FlowbiteTableHeadTheme } from "./TableHead";
1212
import { TableHeadCell } from "./TableHeadCell";
13+
import { TablePagination, type FlowbiteTablePaginationTheme } from "./TablePagination";
1314
import { TableRow, type FlowbiteTableRowTheme } from "./TableRow";
1415

1516
export interface FlowbiteTableTheme {
1617
root: FlowbiteTableRootTheme;
1718
head: FlowbiteTableHeadTheme;
1819
row: FlowbiteTableRowTheme;
1920
body: FlowbiteTableBodyTheme;
21+
pagination: FlowbiteTablePaginationTheme;
2022
}
2123

2224
export interface FlowbiteTableRootTheme {
@@ -56,4 +58,5 @@ export const Table = Object.assign(TableComponent, {
5658
Row: TableRow,
5759
Cell: TableCell,
5860
HeadCell: TableHeadCell,
61+
Pagination: TablePagination,
5962
});
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"use client";
2+
3+
import { forwardRef, type ComponentPropsWithRef } from "react";
4+
import { twMerge } from "tailwind-merge";
5+
import { mergeDeep } from "../../helpers/merge-deep";
6+
import { getTheme } from "../../theme-store";
7+
import type { DeepPartial } from "../../types";
8+
9+
export interface FlowbiteTablePaginationTheme {
10+
base: string;
11+
totalPages: {
12+
base: string;
13+
pageRange: string;
14+
outOf: string;
15+
};
16+
page: {
17+
base: string;
18+
previous: string;
19+
pageNo: string;
20+
next: string;
21+
};
22+
}
23+
24+
export interface TablePaginationProps extends ComponentPropsWithRef<"tfoot"> {
25+
count: number;
26+
onPageChange: (newPage: number) => void;
27+
page: number;
28+
rowsPerPage: number;
29+
theme?: DeepPartial<FlowbiteTablePaginationTheme>;
30+
}
31+
32+
export const TablePagination = forwardRef<HTMLDivElement, TablePaginationProps>(
33+
({ count, onPageChange, page, rowsPerPage, className, theme: customTheme = {}, ...props }, ref) => {
34+
const theme = mergeDeep(getTheme().table.pagination, customTheme);
35+
36+
const nPages = Math.ceil(count / rowsPerPage);
37+
const pageNumbers = [...Array(nPages + 1).keys()].slice(1);
38+
39+
const goToPrevPage = (): void => {
40+
if (page !== 1) {
41+
onPageChange(page - 1);
42+
}
43+
};
44+
45+
const goToNextPage = (): void => {
46+
if (page !== nPages) {
47+
onPageChange(page + 1);
48+
}
49+
};
50+
51+
const directPageChange = (customPageNo: number): void => {
52+
if (page !== customPageNo) {
53+
onPageChange(customPageNo);
54+
}
55+
};
56+
57+
const getLabelDisplayedRowsTo = (): number => {
58+
if (count === -1) {
59+
return (page + 1) * rowsPerPage;
60+
}
61+
62+
return rowsPerPage === -1 ? count : Math.min(count, (page + 1) * rowsPerPage);
63+
};
64+
65+
return (
66+
<div className={twMerge(theme.base, className)} aria-label="Table pagination" {...props} ref={ref}>
67+
<span className={theme.totalPages.base}>
68+
Showing{" "}
69+
<span className={theme.totalPages.pageRange}>
70+
{`${count === 0 ? 0 : page * rowsPerPage + 1}-${getLabelDisplayedRowsTo()}`}
71+
</span>
72+
of
73+
<span className={theme.totalPages.outOf}>{count === -1 ? -1 : count}</span>
74+
</span>
75+
76+
<ul className={theme.page.base}>
77+
<button onClick={goToPrevPage} className={theme.page.previous} disabled={page === 1}>
78+
Previous
79+
</button>
80+
{pageNumbers.map((pgNumber, index) => {
81+
return (
82+
<button
83+
onClick={() => {
84+
directPageChange(index + 1);
85+
}}
86+
key={index + 1}
87+
className={twMerge(theme.page.pageNo, index + 1 === page ? "bg-blue-50 text-blue-600" : "")}
88+
>
89+
{pgNumber}
90+
</button>
91+
);
92+
})}
93+
<button
94+
role="button"
95+
onClick={goToNextPage}
96+
className={theme.page.next}
97+
disabled={count !== -1 ? page >= Math.ceil(count / rowsPerPage) - 1 : false}
98+
>
99+
Next
100+
</button>
101+
</ul>
102+
</div>
103+
);
104+
},
105+
);
106+
107+
TablePagination.displayName = "Table.Pagination";

packages/ui/src/components/Table/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ export { TableHeadCell } from "./TableHeadCell";
1010
export type { FlowbiteTableHeadCellTheme, TableHeadCellProps } from "./TableHeadCell";
1111
export { TableRow } from "./TableRow";
1212
export type { FlowbiteTableRowTheme, TableRowProps } from "./TableRow";
13+
export { TablePagination } from "./TablePagination";
14+
export type { FlowbiteTablePaginationTheme, TablePaginationProps } from "./TablePagination";

packages/ui/src/components/Table/theme.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,20 @@ export const tableTheme: FlowbiteTableTheme = createTheme({
2424
hovered: "hover:bg-gray-50 dark:hover:bg-gray-600",
2525
striped: "odd:bg-white even:bg-gray-50 odd:dark:bg-gray-800 even:dark:bg-gray-700",
2626
},
27+
pagination: {
28+
base: "mt-5 flex w-full flex-col flex-wrap items-center justify-between py-4 md:flex-row",
29+
totalPages: {
30+
base: "mb-4 block w-full text-sm font-normal text-gray-500 dark:text-gray-400 md:mb-0 md:inline md:w-auto",
31+
pageRange: "mr-1 font-semibold text-gray-900 dark:text-white",
32+
outOf: "ml-1 font-semibold text-gray-900 dark:text-white",
33+
},
34+
page: {
35+
base: "inline-flex h-8 -space-x-px text-sm rtl:space-x-reverse",
36+
next: "flex h-8 items-center justify-center rounded-e-lg border border-gray-300 bg-white px-3 leading-tight text-gray-500 hover:bg-gray-100 hover:text-gray-700 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white",
37+
pageNo:
38+
"flex h-8 items-center justify-center border border-gray-300 bg-white px-3 leading-tight text-gray-500 hover:bg-gray-100 hover:text-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white",
39+
previous:
40+
"ms-0 flex h-8 items-center justify-center rounded-s-lg border border-gray-300 bg-white px-3 leading-tight text-gray-500 hover:bg-gray-100 hover:text-gray-700 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white",
41+
},
42+
},
2743
});

0 commit comments

Comments
 (0)