QUI React Table

Sorting

First Name
Last Name
Age
Visits
Status
Profile Progress
Created At
{
"sorting": []
}
import {useMemo, useState} from "react"
import dayjs from "dayjs"
import {ArrowDownUp, ArrowUp} from "lucide-react"
import {clsx} from "@qui/base"
import {CodeHighlight} from "@qui/mdx-docs"
import {QButton, QIcon, QIconButton, QProgressCircle} from "@qui/react"
import {
ColumnDef,
flexRender,
getCoreRowModel,
getSortedRowModel,
QTable,
QTbody,
QTd,
QTh,
QThead,
QTr,
SortingState,
useReactTable,
} from "@qui/react-table"
import {Person, usePersonData} from "~utils/data"
export default function Sorting() {
const [sorting, setSorting] = useState<SortingState>([])
const columns = useMemo<ColumnDef<Person>[]>(
() => [
{
accessorKey: "firstName",
cell: (info) => info.getValue(),
header: "First Name",
},
{
accessorFn: (row) => row.lastName,
cell: (info) => info.getValue(),
header: "Last Name",
id: "lastName",
},
{
accessorKey: "age",
header: "Age",
},
{
accessorKey: "visits",
header: "Visits",
},
{
accessorKey: "status",
header: "Status",
},
{
accessorKey: "progress",
header: "Profile Progress",
minSize: 175,
},
{
accessorKey: "createdAt",
header: "Created At",
// we override this column's default sorting function for compatibility with
// formatted date strings.
sortingFn: (rowA, rowB, columnId) => {
const valueA: string = rowA.getValue(columnId)
const valueB: string = rowB.getValue(columnId)
return dayjs(valueA).isAfter(dayjs(valueB)) ? 1 : -1
},
},
],
[],
)
const {data = [], isFetching, refetch} = usePersonData(20)
const refreshData = () => refetch()
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
sortDescFirst: true,
state: {
sorting,
},
})
return (
<div className="overflow-x-auto p-2">
<div className="mb-3 flex items-center gap-3">
<QButton onClick={refreshData} variant="outline">
Refresh Data
</QButton>
{isFetching ? <QProgressCircle size="xs" /> : null}
</div>
<div className="overflow-x-auto">
<QTable>
<QThead>
{table.getHeaderGroups().map((headerGroup) => (
<QTr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
if (header.isPlaceholder) {
return (
<QTh
key={header.id}
colSpan={header.colSpan}
style={{width: header.getSize()}}
/>
)
}
const canSort = header.column.getCanSort()
const sorted = header.column.getIsSorted()
return (
<QTh
key={header.id}
colSpan={header.colSpan}
style={{width: header.getSize()}}
>
<div className="inline-flex items-center gap-2">
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
{canSort ? (
<QIconButton
onClick={header.column.getToggleSortingHandler()}
size="s"
>
<QIcon
className={clsx(
"transition-[transform] ease-in-out",
{"rotate-180": sorted === "desc"},
)}
icon={sorted ? ArrowUp : ArrowDownUp}
/>
</QIconButton>
) : null}
</div>
</QTh>
)
})}
</QTr>
))}
</QThead>
<QTbody>
{table.getRowModel().rows.map((row) => {
return (
<QTr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<QTd key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</QTd>
)
})}
</QTr>
)
})}
</QTbody>
</QTable>
</div>
<CodeHighlight
className="mt-4 w-fit"
code={JSON.stringify({sorting: table.getState().sorting}, null, 2)}
disableCopy
language="json"
/>
</div>
)
}