diff --git a/package-lock.json b/package-lock.json index 5920106d..b6bba16d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,10 @@ "workspaces": [ "packages/*" ], + "dependencies": { + "@rtcamp/frappe-ui-react": "^1.0.0-beta.0", + "classnames": "^2.5.1" + }, "devDependencies": { "@babel/plugin-transform-react-jsx": "^7.22.5", "@babel/preset-env": "^7.22.5", @@ -5585,6 +5589,26 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@tanstack/react-virtual": { "version": "3.13.12", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", @@ -5602,6 +5626,19 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/virtual-core": { "version": "3.13.12", "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", @@ -16150,6 +16187,7 @@ "@radix-ui/react-toast": "^1.2.15", "@radix-ui/react-tooltip": "^1.2.7", "@tailwindcss/vite": "^4.1.11", + "@tanstack/react-table": "^8.21.3", "dayjs": "^1.11.13", "dompurify": "^3.2.6", "echarts": "^5.6.0", diff --git a/package.json b/package.json index d9110c3d..04860b41 100644 --- a/package.json +++ b/package.json @@ -80,5 +80,9 @@ ], "*.(xml|xml.dist)": "prettier --write", "*.yml": "prettier --write" + }, + "dependencies": { + "@rtcamp/frappe-ui-react": "^1.0.0-beta.0", + "classnames": "^2.5.1" } } diff --git a/packages/frappe-ui-react/package.json b/packages/frappe-ui-react/package.json index c4aac6f5..ca5a4acf 100644 --- a/packages/frappe-ui-react/package.json +++ b/packages/frappe-ui-react/package.json @@ -34,6 +34,7 @@ "@radix-ui/react-toast": "^1.2.15", "@radix-ui/react-tooltip": "^1.2.7", "@tailwindcss/vite": "^4.1.11", + "@tanstack/react-table": "^8.21.3", "dayjs": "^1.11.13", "dompurify": "^3.2.6", "echarts": "^5.6.0", diff --git a/packages/frappe-ui-react/src/components/table/components/index.ts b/packages/frappe-ui-react/src/components/table/components/index.ts new file mode 100644 index 00000000..5c31f275 --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/index.ts @@ -0,0 +1,7 @@ +export { default as TableHeader } from "./tableHeader"; +export { default as TableBody } from "./tableBody"; +export { default as TableFooter } from "./tableFooter"; +export { default as TableHead } from "./tableHead"; +export { default as TableRow } from "./tableRow"; +export { default as TableCell } from "./tableCell"; +export { default as TableCaption } from "./tableCaption"; diff --git a/packages/frappe-ui-react/src/components/table/components/tableBody.tsx b/packages/frappe-ui-react/src/components/table/components/tableBody.tsx new file mode 100644 index 00000000..2f231cc7 --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/tableBody.tsx @@ -0,0 +1,17 @@ +import classNames from "classnames"; +import { PropsWithChildren } from "react"; + +const TableBody = ({ + className, + children, + ...props +}: PropsWithChildren>) => ( + + {children} + +); + +export default TableBody; diff --git a/packages/frappe-ui-react/src/components/table/components/tableCaption.tsx b/packages/frappe-ui-react/src/components/table/components/tableCaption.tsx new file mode 100644 index 00000000..c46ff26c --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/tableCaption.tsx @@ -0,0 +1,13 @@ +import classNames from "classnames"; +import { PropsWithChildren } from "react"; + +const TableCaption = ({ + className, + children, +}: PropsWithChildren>) => ( + + {children} + +); + +export default TableCaption; diff --git a/packages/frappe-ui-react/src/components/table/components/tableCell.tsx b/packages/frappe-ui-react/src/components/table/components/tableCell.tsx new file mode 100644 index 00000000..65f9ca37 --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/tableCell.tsx @@ -0,0 +1,18 @@ +import classNames from "classnames"; +import { PropsWithChildren } from "react"; + +const TableCell = ({ + className, + children, +}: PropsWithChildren>) => ( + + {children} + +); + +export default TableCell; diff --git a/packages/frappe-ui-react/src/components/table/components/tableFooter.tsx b/packages/frappe-ui-react/src/components/table/components/tableFooter.tsx new file mode 100644 index 00000000..29033c4d --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/tableFooter.tsx @@ -0,0 +1,18 @@ +import classNames from "classnames"; +import { PropsWithChildren } from "react"; + +const TableFooter = ({ + className, + children, +}: PropsWithChildren>) => ( + tr]:last:border-b-0", + className + )} + > + {children} + +); + +export default TableFooter; diff --git a/packages/frappe-ui-react/src/components/table/components/tableHead.tsx b/packages/frappe-ui-react/src/components/table/components/tableHead.tsx new file mode 100644 index 00000000..3c85d270 --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/tableHead.tsx @@ -0,0 +1,20 @@ +import classNames from "classnames"; +import { PropsWithChildren } from "react"; + +const TableHead = ({ + className, + children, + ...props +}: PropsWithChildren>) => ( + + {children} + +); + +export default TableHead; diff --git a/packages/frappe-ui-react/src/components/table/components/tableHeader.tsx b/packages/frappe-ui-react/src/components/table/components/tableHeader.tsx new file mode 100644 index 00000000..7552af58 --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/tableHeader.tsx @@ -0,0 +1,18 @@ +import classNames from "classnames"; +import { PropsWithChildren } from "react"; + +const TableHeader = ({ + className, + children, +}: PropsWithChildren>) => ( + + {children} + +); + +export default TableHeader; diff --git a/packages/frappe-ui-react/src/components/table/components/tableRow.tsx b/packages/frappe-ui-react/src/components/table/components/tableRow.tsx new file mode 100644 index 00000000..aacc1f22 --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/components/tableRow.tsx @@ -0,0 +1,18 @@ +import { PropsWithChildren } from "react"; +import classNames from "classnames"; + +const TableRow = ({ + className, + children, +}: PropsWithChildren>) => ( + + {children} + +); + +export default TableRow; diff --git a/packages/frappe-ui-react/src/components/table/index.tsx b/packages/frappe-ui-react/src/components/table/index.tsx new file mode 100644 index 00000000..257b1638 --- /dev/null +++ b/packages/frappe-ui-react/src/components/table/index.tsx @@ -0,0 +1,128 @@ +import { + ColumnDef, + flexRender, + getCoreRowModel, + getPaginationRowModel, + OnChangeFn, + useReactTable, +} from "@tanstack/react-table"; +import classNames from "classnames"; + +import { + TableBody, + TableCaption, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "./components"; + +interface TableProps { + className?: string; + columns: ColumnDef[]; + data: TData[]; + enablePagination?: boolean; + paginationState?: { + pageIndex: number; + pageSize: number; + }; + onPaginationChange?: OnChangeFn<{ + pageIndex: number; + pageSize: number; + }>; + componentClassNames?: { + caption?: string; + header?: string; + body?: string; + footer?: string; + head?: string; + row?: string; + cell?: string; + }; +} + +function Table({ + className, + columns, + data, + enablePagination = false, + paginationState = { pageIndex: 0, pageSize: 10 }, + onPaginationChange, + componentClassNames, +}: TableProps) { + const table = useReactTable({ + columns, + data, + getCoreRowModel: getCoreRowModel(), + manualPagination: enablePagination, + getPaginationRowModel: enablePagination + ? getPaginationRowModel() + : undefined, + onPaginationChange: enablePagination ? onPaginationChange : undefined, + state: { + pagination: + enablePagination ? paginationState : undefined, + }, + }); + + return ( +
+ + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ))} + + ))} + + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((header) => ( + + {flexRender( + header.column.columnDef.footer, + header.getContext() + )} + + ))} + + ))} + +
+
+ ); +} + +export default Table;