Row Expansion
Name | Info | ||||
---|---|---|---|---|---|
First Name | Last Name | Age | Visits | Status | Profile Progress |
Show
10
{"expanded": {},"rowSelection": {}}
import {useState} from "react"import {ChevronDown, ChevronRight, ChevronRightIcon} from "lucide-react"import {CodeHighlight} from "@qui/mdx-docs"import {QButton,QCheckbox,QIconButton,QPagination,QProgressCircle,QTextInput,} from "@qui/react"import {Column,ColumnDef,ExpandedState,flexRender,getCoreRowModel,getExpandedRowModel,getFilteredRowModel,getPaginationRowModel,QTable,QTbody,QTd,QTh,QThead,QTr,Table,useReactTable,useTablePagination,} from "@qui/react-table"import {Person, usePersonData} from "~utils/data"const columns: ColumnDef<Person>[] = [{columns: [{accessorKey: "firstName",cell: ({getValue, row}) => {const indeterminate = row.getIsSomeSelected()const checked = row.getIsSelected()return (<divclassName="inline-flex items-center gap-2"style={{// Since rows are flattened by default,// we can use the row.depth property// and paddingLeft to visually indicate the depth// of the rowpaddingLeft: `${row.depth * 2}rem`,}}><><QCheckboxchecked={checked}indeterminate={indeterminate}onChange={() => row.toggleSelected(!checked)}/>{row.getCanExpand() ? (<div className="inline-flex items-center justify-center"><QIconButtonclassName="inline-flex justify-center"icon={<ChevronRightIconsize={16}style={{transform: row.getIsExpanded()? "rotate(90deg)": "unset",transition: "transform 161ms ease",}}/>}onClick={row.getToggleExpandedHandler()}size="s"/></div>) : null}<span>{getValue()}</span></></div>)},header: ({table}) => {return (<><div className="flex items-center gap-2"><QIconButtonicon={table.getIsAllRowsExpanded() ? ChevronDown : ChevronRight}onClick={table.getToggleAllRowsExpandedHandler()}size="s"/><span>First Name</span></div></>)},},{accessorFn: (row) => row.lastName,cell: (info) => info.getValue(),header: "Last Name",id: "lastName",},],header: "Name",},{columns: [{accessorKey: "age",header: "Age",},{accessorKey: "visits",header: "Visits",},{accessorKey: "status",header: "Status",},{accessorKey: "progress",header: "Profile Progress",},],header: "Info",},]export default function Expanding() {const {data = [], isFetching, refetch} = usePersonData(100, 5, 3)const refreshData = () => refetch()const [expanded, setExpanded] = useState<ExpandedState>({})const table = useReactTable({columns,data,filterFns: {},getCoreRowModel: getCoreRowModel(),getExpandedRowModel: getExpandedRowModel(),getFilteredRowModel: getFilteredRowModel(),getPaginationRowModel: getPaginationRowModel(),getSubRows: (row) => row.subRows,onExpandedChange: setExpanded,state: {expanded,},})const paginationProps = useTablePagination(table)return (<div className="flex flex-col gap-2 overflow-x-auto p-2"><div className="mb-2 inline-flex items-center gap-2"><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) => {return (<QTh key={header.id} colSpan={header.colSpan}>{header.isPlaceholder ? null : (<div className="inline-flex flex-col gap-1"><divclassName="inline-flex items-center justify-center"style={{minHeight: header.column.getCanFilter()? 28: "auto",}}>{flexRender(header.column.columnDef.header,header.getContext(),)}</div>{header.column.getCanFilter() ? (<Filter column={header.column} table={table} />) : null}</div>)}</QTh>)})}</QTr>))}</QThead><QTbody>{table.getRowModel().rows.map((row) => {return (<QTr key={row.id} isSelected={row.getIsSelected()}>{row.getVisibleCells().map((cell) => {return (<QTd key={cell.id}>{flexRender(cell.column.columnDef.cell,cell.getContext(),)}</QTd>)})}</QTr>)})}</QTbody></QTable></div><div className="mt-4"><QPaginationclassName="mt-4"{...paginationProps}renderPageMeta={(context) =>`${context.currentPage} of ${context.totalPages}`}rowsPerPageLabel="Show"rowsPerPageOptions={[10, 20, 50]}/></div><CodeHighlightclassName="max-h-[400px] overflow-y-auto"code={JSON.stringify({expanded, rowSelection: table.getState().rowSelection},null,2,)}language="json"/></div>)}function Filter({column,table,}: {column: Column<Person, any>table: Table<Person>}) {const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id)const columnFilterValue = column.getFilterValue()return typeof firstValue === "number" ? (<div className="flex gap-2"><QTextInputclassName="w-24"inputProps={{min: 0, type: "number"}}onChange={(e, value) =>column.setFilterValue((old: [number, number]) => [value, old?.[1]])}placeholder="Min"size="s"value={(columnFilterValue as [number, number])?.[0] ?? ""}/><QTextInputclassName="w-24"inputProps={{min: 0, type: "number"}}onChange={(e, value) =>column.setFilterValue((old: [number, number]) => [old?.[0], value])}placeholder="Max"size="s"value={(columnFilterValue as [number, number])?.[1] ?? ""}/></div>) : (<QTextInputclassName="w-36"onChange={(e, value) => column.setFilterValue(value)}placeholder="Search..."size="s"value={(columnFilterValue ?? "") as string}/>)}