From 34f01abb322a6451dd390ffdfb5d67baf859e1df Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Mon, 26 Jan 2026 20:57:05 +0700 Subject: [PATCH] chore: add new props (withPagination, getRowCanExpand, renderSubComponent, expanded, and getSubRows) --- src/components/Table.tsx | 171 ++++++++++++++++++++++++++------------- 1 file changed, 116 insertions(+), 55 deletions(-) diff --git a/src/components/Table.tsx b/src/components/Table.tsx index 0e095c1f..0be39fb5 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -1,11 +1,12 @@ 'use client'; -import { ReactNode, useCallback, useEffect, useState } from 'react'; +import { Fragment, ReactNode, useCallback, useEffect, useState } from 'react'; import { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, + getExpandedRowModel, getSortedRowModel, TableOptions, useReactTable, @@ -15,6 +16,7 @@ import { OnChangeFn, Row, HeaderContext, + ExpandedState, } from '@tanstack/react-table'; import { rankItem } from '@tanstack/match-sorter-utils'; import { Icon } from '@iconify/react'; @@ -33,6 +35,9 @@ interface TableClassNames { bodyRowClassName?: string; selectedBodyRowClassName?: string; bodyColumnClassName?: string; + bodySubRowClassName?: (depth: number) => string; + selectedBodySubRowClassName?: (depth: number) => string; + bodySubRowColumnClassName?: (depth: number) => string; tableFooterClassName?: string; footerRowClassName?: string; footerColumnClassName?: string; @@ -60,6 +65,7 @@ export interface TableProps { enableRowSelection?: boolean | ((row: Row) => boolean); renderFooter?: boolean; withCheckbox?: boolean; + withPagination?: boolean; rowOptions?: number[]; /** * Custom row renderer. Should return a complete element or null. @@ -67,6 +73,10 @@ export interface TableProps { * Return null to render the default row. */ renderCustomRow?: (row: Row) => ReactNode | null; + getRowCanExpand?: (row: Row) => boolean; + renderSubComponent?: (props: { row: Row }) => React.ReactElement; + expanded?: ExpandedState; + getSubRows?: (originalRow: TData, index: number) => TData[] | undefined; } const DUMMY_SKELETON_DATA = [{}, {}, {}, {}, {}]; @@ -92,7 +102,12 @@ export const TABLE_DEFAULT_STYLING = { bodyRowClassName: 'transition-all duration-200 border-t border-base-content/10 bg-transparent', selectedBodyRowClassName: 'bg-primary/5', - bodyColumnClassName: 'px-4 py-3 text-base-content', + bodyColumnClassName: 'px-4 py-3 text-base-content font-medium', + bodySubRowClassName: (depth: number) => + 'transition-all duration-200 border-t border-base-content/10 bg-transparent', + selectedBodySubRowClassName: (depth: number) => 'bg-primary/5', + bodySubRowColumnClassName: (depth: number) => + 'px-4 py-3 text-base-content font-medium', paginationClassName: 'px-3', tableFooterClassName: 'font-semibold border-base-content/10', footerRowClassName: 'bg-base-200 border-t-2 border-base-content/10', @@ -120,8 +135,13 @@ const Table = ({ enableRowSelection, renderFooter = false, withCheckbox = false, + withPagination = true, rowOptions = [10, 20, 50, 100], renderCustomRow, + getRowCanExpand, + renderSubComponent, + expanded = {}, + getSubRows, }: TableProps) => { const isServerSideTable = totalItems !== undefined && @@ -154,10 +174,14 @@ const Table = ({ getSortedRowModel: getSortedRowModel(), getPaginationRowModel: getPaginationRowModel(), onPaginationChange: setPagination, + getExpandedRowModel: getExpandedRowModel(), + getRowCanExpand: getRowCanExpand ?? (getSubRows ? undefined : () => false), + getSubRows, manualSorting, state: { pagination, globalFilter: fuzzySearchValue, + expanded, }, filterFns: { fuzzy: fuzzyFilter, @@ -228,7 +252,10 @@ const Table = ({
({ } return ( - - {row.getVisibleCells().map((cell) => ( - - {!isLoading && - flexRender( - cell.column.columnDef.cell, - cell.getContext() + + 0 + ? tableClassNames.bodySubRowClassName(row.depth) + : tableClassNames.bodyRowClassName, + { + [tableClassNames.selectedBodyRowClassName!]: + row.getIsSelected() && row.depth === 0, + [tableClassNames.selectedBodySubRowClassName( + row.depth + )!]: row.getIsSelected() && row.depth > 0, + } + )} + > + {row.getVisibleCells().map((cell) => ( + 0 + ? tableClassNames.bodySubRowColumnClassName( + row.depth + ) + : tableClassNames.bodyColumnClassName )} + > + {!isLoading && + flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} - {isLoading &&
} - - ))} - + {isLoading &&
} + + ))} + + + {row.getIsExpanded() && ( + <> + {renderSubComponent && ( + + + {renderSubComponent({ row })} + + + )} + + )} + ); })} @@ -425,30 +483,33 @@ const Table = ({ !isLoading && emptyContent} - {data.length > 0 && table.getRowModel().rows.length > 0 && !isLoading && ( -
- -
- )} + {data.length > 0 && + table.getRowModel().rows.length > 0 && + !isLoading && + withPagination && ( +
+ +
+ )}
); };