Managing the tree column and expand/collapse icon
When rendering a tree, you have to use the <TreeGrid />
component instead of <InfiniteTable />
.
The <TreeGrid />
component is simply an <InfiniteTable />
component with some props removed - those don't make sense for tree scenarios.
Note
By default no tree column is rendered.
To specify the tree column, you have to to set the columns.renderTreeIcon
prop to true
for your column of choice.
const columns: Record<string, InfiniteTableColumn<FileSystemNode>> = {
name: {
renderTreeIcon: true,
field: 'name',
header: 'Name'
},
type: { field: 'type', header: 'Type' },
size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' },
};
This is very similar to how you specify the selection column for multi-select configurations.
import { InfiniteTableColumn, TreeDataSource, TreeGrid, } from '@infinite-table/infinite-react'; import { useMemo, useState } from 'react'; type FileSystemNode = { id: string; name: string; type: 'folder' | 'file'; extension?: string; mimeType?: string; sizeInKB: number; children?: FileSystemNode[]; }; const allColumns: Record<string, InfiniteTableColumn<FileSystemNode>> = { name: { field: 'name', header: 'Name' }, type: { field: 'type', header: 'Type' }, extension: { field: 'extension', header: 'Extension' }, mimeType: { field: 'mimeType', header: 'Mime Type' }, size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' }, }; export default function App() { const [treeIcon, setTreeIcon] = useState<string>('name'); const columns = useMemo(() => { const cols = { ...allColumns }; cols[treeIcon] = { ...cols[treeIcon], renderTreeIcon: true, }; return cols; }, [treeIcon]); return ( <> <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource}> <div style={{ color: 'var(--infinite-cell-color)', padding: '10px', }} > <p>Select the tree column</p> <select value={treeIcon} onChange={(e) => setTreeIcon(e.target.value)} title="Select column to use for the tree icon" > {Object.keys(allColumns).map((key) => ( <option value={key}>{key}</option> ))} <option value="none">No tree column</option> </select> </div> <TreeGrid columns={columns} /> </TreeDataSource> </> ); } const dataSource = () => { const nodes: FileSystemNode[] = [ { id: '1', name: 'Documents', sizeInKB: 1200, type: 'folder', children: [ { id: '10', name: 'Private', sizeInKB: 100, type: 'folder', children: [ { id: '100', name: 'Report.docx', sizeInKB: 210, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '101', name: 'Vacation.docx', sizeInKB: 120, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '102', name: 'CV.pdf', sizeInKB: 108, type: 'file', extension: 'pdf', mimeType: 'application/pdf', }, ], }, ], }, { id: '2', name: 'Desktop', sizeInKB: 1000, type: 'folder', children: [], }, { id: '3', name: 'Media', sizeInKB: 1000, type: 'folder', children: [ { id: '30', name: 'Music', sizeInKB: 0, type: 'folder', children: [], }, { id: '31', name: 'Videos', sizeInKB: 5400, type: 'folder', children: [ { id: '310', name: 'Vacation.mp4', sizeInKB: 108, type: 'file', extension: 'mp4', mimeType: 'video/mp4', }, ], }, ], }, ]; return Promise.resolve(nodes); };
Customizing the expand/collapse icon
Using the column.renderTreeIcon=true
is obviously not enough to customize the expand/collapse icon.
This prop can also be a function that returns a React node.
Note
With the default value of true
for columns.renderTreeIcon
, an icon will be rendered only for parent nodes.
If you want to render an icon for all nodes, specify a function (and differentiate between parent and leaf nodes), and it will be called regardless of whether the node is a parent or a leaf.
import { InfiniteTableColumn, TreeDataSource, TreeGrid, } from '@infinite-table/infinite-react'; import { useMemo, useState } from 'react'; type FileSystemNode = { id: string; name: string; type: 'folder' | 'file'; extension?: string; mimeType?: string; sizeInKB: number; children?: FileSystemNode[]; }; const allColumns: Record<string, InfiniteTableColumn<FileSystemNode>> = { name: { field: 'name', header: 'Name' }, type: { field: 'type', header: 'Type' }, extension: { field: 'extension', header: 'Extension' }, mimeType: { field: 'mimeType', header: 'Mime Type' }, size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' }, }; export default function App() { const [treeIcon, setTreeIcon] = useState<string>('name'); const columns = useMemo(() => { const cols = { ...allColumns }; cols[treeIcon] = { ...cols[treeIcon], renderTreeIcon: ({ rowInfo, toggleCurrentTreeNode }) => ( <div onClick={toggleCurrentTreeNode} style={{ width: 24, fontSize: 12, display: 'inline-block', cursor: 'pointer', }} > {rowInfo.isParentNode ? (rowInfo.nodeExpanded ? '👇' : '👉') : '🔴'} </div> ), }; return cols; }, [treeIcon]); return ( <> <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource}> <div style={{ color: 'var(--infinite-cell-color)', padding: '10px', }} > <p>Select the tree column</p> <select value={treeIcon} onChange={(e) => setTreeIcon(e.target.value)} title="Select column to use for the tree icon" > {Object.keys(allColumns).map((key) => ( <option value={key}>{key}</option> ))} <option value="no">No tree column</option> </select> </div> <TreeGrid columns={columns} /> </TreeDataSource> </> ); } const dataSource = () => { const nodes: FileSystemNode[] = [ { id: '1', name: 'Documents', sizeInKB: 1200, type: 'folder', children: [ { id: '10', name: 'Private', sizeInKB: 100, type: 'folder', children: [ { id: '100', name: 'Report.docx', sizeInKB: 210, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '101', name: 'Vacation.docx', sizeInKB: 120, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '102', name: 'CV.pdf', sizeInKB: 108, type: 'file', extension: 'pdf', mimeType: 'application/pdf', }, ], }, ], }, { id: '2', name: 'Desktop', sizeInKB: 1000, type: 'folder', children: [ { id: '20', name: 'unknown.txt', sizeInKB: 108, type: 'file', extension: 'txt', mimeType: 'text/plain', }, ], }, { id: '3', name: 'Media', sizeInKB: 1000, type: 'folder', children: [ { id: '30', name: 'Music', sizeInKB: 0, type: 'folder', children: [], }, { id: '31', name: 'Videos', sizeInKB: 5400, type: 'folder', children: [ { id: '310', name: 'Vacation.mp4', sizeInKB: 108, type: 'file', extension: 'mp4', mimeType: 'video/mp4', }, ], }, ], }, ]; return Promise.resolve(nodes); };