Skip to content

Instantly share code, notes, and snippets.

@weiying-chen
Created May 1, 2024 02:38
Show Gist options
  • Save weiying-chen/3242691b40c12e83ea05ff8b962e105d to your computer and use it in GitHub Desktop.
Save weiying-chen/3242691b40c12e83ea05ff8b962e105d to your computer and use it in GitHub Desktop.
import {
Table,
TableBody,
TableHead,
TableHeader,
TableRow,
TableCell,
} from '@repo/ui/radix/table';
import { ListRenderer, KeyValue } from '@repo/ui/types';
import { useSort } from '@repo/ui/hooks/useSort';
import { usePagination } from '@repo/ui/hooks/usePagination';
import { PaginationControls } from '@repo/ui/PaginationControls';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { ArrowDown, ArrowUp } from 'lucide-react';
import { useCallback, useState } from 'react';
type ListProps<T> = {
items: T[];
pageSize: number;
renderer: ListRenderer<T>;
};
export default function List<T extends KeyValue>({
items,
pageSize,
renderer,
}: ListProps<T>) {
const searchParams = useSearchParams();
const { replace } = useRouter();
const pathname = usePathname();
const params = new URLSearchParams(searchParams);
const currentPage = parseInt(searchParams.get('page') || '', 10) || 1;
// const initialSortColumn = useMemo(() => {
// const firstSortableColumn = renderer.find((column) => column.label);
// return firstSortableColumn ? firstSortableColumn.name : '';
// }, [renderer]);
// const [sortColumn, setSortColumn] = useState(initialSortColumn);
const [sortColumn, setSortColumn] = useState('');
const sortValueFn = useCallback((item: T) => item[sortColumn], [sortColumn]);
const { sortedItems, sortDirection, setSortDirection } = useSort(
items,
sortValueFn,
'asc',
);
const { pageNumbers, paginatedItems } = usePagination(
sortedItems,
pageSize,
currentPage,
);
const handleColumnSort = (columnName: string) => () => {
if (sortColumn === columnName) {
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
} else {
setSortColumn(columnName);
setSortDirection('asc');
}
params.set('page', '1');
replace(`${pathname}?${params}`);
};
const handlePageChange = (page: number) => {
params.set('page', page.toString());
replace(`${pathname}?${params}`);
};
const tableHeadRenderer = () => {
return renderer.map(({ name, label, hasSort = true }, index) => {
const shouldSort = hasSort && label !== '';
const sortIcon =
sortColumn === name && shouldSort ? (
sortDirection === 'asc' ? (
<ArrowUp size={16} />
) : (
<ArrowDown size={16} />
)
) : null;
return (
<TableHead
className={`cursor-pointer ${!shouldSort && 'cursor-default'}`}
onClick={shouldSort ? handleColumnSort(name) : undefined}
key={index}
>
<div className="flex items-center">
<span>{label}</span>
{sortIcon && <span className="ml-1">{sortIcon}</span>}
</div>
</TableHead>
);
});
};
const tableCellRenderer = (paginatedItem: T) => {
return renderer.map(({ body }, index) => (
<TableCell key={index}>{body(paginatedItem)}</TableCell>
));
};
return (
<>
<Table>
<TableHeader>
<TableRow>{tableHeadRenderer()}</TableRow>
</TableHeader>
<TableBody>
{paginatedItems.map((paginatedItem) => (
<TableRow key={paginatedItem.id} className="animate-fade-in">
{tableCellRenderer(paginatedItem)}
</TableRow>
))}
</TableBody>
</Table>
{pageNumbers.length > 1 && (
<PaginationControls
currentPage={currentPage}
handlePageChange={handlePageChange}
pageNumbers={pageNumbers}
/>
)}
</>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment