Skip to content

Commit

Permalink
Merge pull request #764 from ionicprotocol/feat/common-table
Browse files Browse the repository at this point in the history
  • Loading branch information
rhlsthrm authored Nov 7, 2024
2 parents d2d8f0f + cbaf074 commit 07986cb
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 0 deletions.
114 changes: 114 additions & 0 deletions packages/ui/app/_components/CommonTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useState } from 'react';

import {
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable
} from '@tanstack/react-table';

import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from '@ui/components/ui/table';

import { TableLoader } from './TableLoader';

import type { ColumnDef, SortingState } from '@tanstack/react-table';

interface CommonTableProps<T> {
data: T[];
columns: ColumnDef<T>[];
isLoading?: boolean;
loadingRows?: number;
showFooter?: boolean;
}

function CommonTable<T>({
data,
columns,
isLoading = false,
loadingRows = 5,
showFooter = false
}: CommonTableProps<T>) {
const [sorting, setSorting] = useState<SortingState>([]);

const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
state: {
sorting
}
});

if (isLoading) {
return (
<TableLoader
columns={columns}
rows={loadingRows}
showFooter={showFooter}
/>
);
}

return (
<Table className="w-full border-separate border-spacing-y-3">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow
key={headerGroup.id}
className="border-none hover:bg-transparent"
>
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
className="text-white/60 text-xs font-semibold h-8"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
className="hover:bg-graylite transition-all duration-200 ease-linear bg-grayUnselect rounded-xl [&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl border-none"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
);
}

export default CommonTable;
70 changes: 70 additions & 0 deletions packages/ui/app/_components/TableLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Card } from '@ui/components/ui/card';
import { Skeleton } from '@ui/components/ui/skeleton';

import type { ColumnDef } from '@tanstack/react-table';

export const TableLoader = ({
columns,
rows = 5,
showFooter = false
}: {
columns: ColumnDef<any>[];
rows: number;
showFooter?: boolean;
}) => {
return (
<Card className="w-full space-y-4 p-4">
{/* Table Headers */}
<div
className={`grid grid-cols-${columns.length} gap-4 pb-4 border-b border-white/10`}
>
{columns.map((col, index) => (
<Skeleton
key={index}
className="h-4 w-full"
/>
))}
</div>

{/* Table Rows */}
{[...Array(rows)].map((_, rowIndex) => (
<div
key={rowIndex}
className={`grid grid-cols-${columns.length} gap-4 py-4 border-b border-white/10`}
style={{
animationDelay: `${rowIndex * 150}ms`,
opacity: 1 - rowIndex * 0.15
}}
>
{columns.map((col, colIndex) => (
<div
key={colIndex}
className="flex items-center gap-2"
>
{colIndex === 0 && <Skeleton className="h-6 w-6 rounded-full" />}
<Skeleton className="h-4 flex-1" />
{col.id === 'votes' && <Skeleton className="h-3 w-3/4 mt-1" />}
</div>
))}
</div>
))}

{showFooter && (
<div className="fixed bottom-4 left-4 right-4">
<Card className="p-4 bg-[#35363D] border-t border-white/10">
<div className="flex items-center justify-between max-w-7xl mx-auto">
<div className="flex items-center gap-2">
<Skeleton className="h-4 w-4" />
<Skeleton className="h-4 w-48" />
</div>
<div className="flex items-center gap-4">
<Skeleton className="h-8 w-20" />
<Skeleton className="h-8 w-24" />
</div>
</div>
</Card>
</div>
)}
</Card>
);
};
15 changes: 15 additions & 0 deletions packages/ui/components/ui/skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { cn } from '@ui/lib/utils';

function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn('animate-pulse rounded-md bg-muted', className)}
{...props}
/>
);
}

export { Skeleton };

0 comments on commit 07986cb

Please sign in to comment.