-
-
Save weiying-chen/3242691b40c12e83ea05ff8b962e105d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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