content
function is lazily executed when a row is expanded to prevent creating unnecessary DOM elements, you can use this behavior to asynchronously load data for nested tables.Company › Department › Employee | Employees › Birth date |
---|---|
Sipes Inc | 58 |
Runolfsdottir - Cummerata | 45 |
Johnston LLC | 60 |
Crist and Sons | 68 |
Schmidt and Sons | 28 |
Nicolas Group | 34 |
Kub and Sons | 33 |
Jakubowski - Rolfson | 74 |
Welch - Tremblay | 50 |
Mueller, Hodkiewicz and Beahan | 50 |
export default function NestedTablesExampleAsync() {const [expandedRecordIds, setExpandedRecordIds] = useState<string[]>([]);const { cx, classes } = useStyles();return (<DataTablewithBorderwithColumnBordershighlightOnHovercolumns={[{accessor: 'name',title: 'Company › Department › Employee',render: ({ id, name }) => (<Group spacing="xs"><IconChevronRightsize="0.9em"className={cx(classes.expandIcon, {[classes.expandIconRotated]: expandedRecordIds.includes(id),})}/><IconBuilding size="0.9em" /><Text>{name}</Text></Group>),},{ accessor: 'employees', title: 'Employees › Birth date', textAlignment: 'right', width: 200 },]}records={companies}rowExpansion={{allowMultiple: true,expanded: { recordIds: expandedRecordIds, onRecordIdsChange: setExpandedRecordIds },content: ({ record }) => <DepartmentsTable companyId={record.id} />,}}/>);}
function DepartmentsTable({ companyId }: { companyId: string }) {const { records, loading } = useDepartmentsAsync({ companyId });const [expandedRecordIds, setExpandedRecordIds] = useState<string[]>([]);const { cx, classes } = useStyles();return (<DataTablenoHeaderminHeight={100}columns={[{accessor: 'name',render: ({ id, name }) => (<Group ml="lg" spacing="xs" noWrap><IconChevronRightsize="0.9em"className={cx(classes.expandIcon, {[classes.expandIconRotated]: expandedRecordIds.includes(id),})}/><IconUsers size="0.9em" /><Text>{name}</Text></Group>),},{ accessor: 'employees', textAlignment: 'right', width: 200 },]}records={records}fetching={loading}rowExpansion={{allowMultiple: true,expanded: { recordIds: expandedRecordIds, onRecordIdsChange: setExpandedRecordIds },content: ({ record }) => <EmployeesTable departmentId={record.id} />,}}/>);}
function EmployeesTable({ departmentId }: { departmentId: string }) {const { records, loading } = useEmployeesAsync({ departmentId });const { classes } = useStyles();return (<DataTablenoHeaderminHeight={100}columns={[{accessor: 'name',render: ({ firstName, lastName }) => (<Group spacing="xs" noWrap className={classes.employeeName}><IconUser size="0.9em" /><Text>{firstName} {lastName}</Text></Group>),},{accessor: 'birthDate',render: ({ birthDate }) => dayjs(birthDate).format('DD MMM YYYY'),textAlignment: 'right',width: 200,},]}records={records}fetching={loading}/>);}
const useStyles = createStyles((theme) => ({expandIcon: {transition: 'transform 0.2s ease',},expandIconRotated: {transform: 'rotate(90deg)',},employeeName: {marginLeft: px(theme.spacing.xl) * 2,},}));
import { sortBy } from 'lodash';import { useEffect, useState } from 'react';import { DataTableSortStatus } from '~/../package';import delay from '~/lib/delay';import useIsMounted from '~/lib/useIsMounted';import { companies as companyData, departments as departmentData, employees } from './index';// Departments with employees countexport const departments = departmentData.map((department) => ({...department,employees: employees.filter((employee) => employee.department.id === department.id)?.length || 0,}));// Companies with employees countexport const companies = companyData.map((company) => ({...company,employees: departments.filter((department) => department.company.id === company.id).reduce((sum, department) => sum + department.employees, 0),}));// Employeesexport { employees };// Hook simulating async companies fetchingexport function useCompaniesAsync({ sortStatus }: { sortStatus: DataTableSortStatus }) {const isMounted = useIsMounted();const [records, setRecords] = useState<typeof companies>([]);const [loading, setLoading] = useState(true);useEffect(() => {if (isMounted()) {(async () => {setLoading(true);await delay({ min: 500, max: 800 });if (isMounted()) {const newRecords = sortBy(companies,sortStatus.columnAccessor === 'details' ? 'employees' : sortStatus.columnAccessor);if (sortStatus.direction === 'desc') newRecords.reverse();setRecords(newRecords);setLoading(false);}})();}}, [isMounted, sortStatus]);return { records, loading };}// Hook simulating async departments fetching by company idexport function useDepartmentsAsync({companyId,sortStatus,}: {companyId: string;sortStatus?: DataTableSortStatus;}) {const isMounted = useIsMounted();const [records, setRecords] = useState<typeof departments>([]);const [loading, setLoading] = useState(true);useEffect(() => {if (isMounted()) {(async () => {setLoading(true);await delay({ min: 500, max: 800 });if (isMounted()) {let newRecords = departments.filter((department) => department.company.id === companyId);if (sortStatus) {newRecords = sortBy(newRecords,sortStatus.columnAccessor === 'details' ? 'employees' : sortStatus.columnAccessor);if (sortStatus.direction === 'desc') newRecords.reverse();}setRecords(newRecords);setLoading(false);}})();}}, [companyId, isMounted, sortStatus]);return { records, loading };}// Hook simulating async employees fetching by department idexport function useEmployeesAsync({departmentId,sortStatus,}: {departmentId: string;sortStatus?: DataTableSortStatus;}) {const isMounted = useIsMounted();const [records, setRecords] = useState<typeof employees>([]);const [loading, setLoading] = useState(true);useEffect(() => {if (isMounted()) {(async () => {setLoading(true);await delay({ min: 500, max: 800 });if (isMounted()) {let newRecords = employees.filter((employee) => employee.department.id === departmentId);if (sortStatus) {newRecords = sortBy(newRecords,sortStatus.columnAccessor === 'name'? ({ firstName, lastName }) => `${firstName} ${lastName}`: 'birthDate');if (sortStatus.direction === 'desc') newRecords.reverse();}setRecords(newRecords);setLoading(false);}})();}}, [departmentId, isMounted, sortStatus]);return { records, loading };}