DataTable and DataTableCore are based on TreeListCore, so please have a look at
the simpler TreeList/TreeListCore components first. The difference is that while
TreeList is a tree of "items", DataTable is a tree of "rows" and therefore adds
a set of columns and column headers. If you want a plain table rather than a tree,
simply don't provide properties that would make it a tree.
The only required props are columns and list (or tree). list is an array of any
type T, while columns is typically an array of DataTableColumn<T>.
DataTable is the usual choice. DataTableCore is the same, but omits the outer
<div> and <table> elements (which is useful if you want to change the rendering
method, e.g. to avoid table in favor of flexbox).
The table uses table-layout: fixed, so the widths of the columns are determined by
the header row alone. If you provide a numeric/Holder width for a column, the user
can resize it by dragging the right edge of the column header.
To make a tree, provide a getChildren or getParents prop and set isTreeCol: true
on exactly one of the columns.
If you want to mix different kinds of rows on a single table (often parents and
children are different things, for example), the getRowConfig prop lets you
configure behavior for each row type individually (see example below).
Most important types
DataTableProps: props of DataTable. Inherits DataTableCoreProps, TreeListMainPropsTreeListAuxProps and most of ListCoreProps.
DataTableColumn<T,...>: settings for a single column. Inherits from
DataTableCellConfig<T,T,...>.
Support for multiple row types
A table has a single set of columns, but the way values are chosen for a column can be
specified separately for each row type. Here is a simple example of a "file browser" to
explain the principle:
class FileInfo {
constructor(public name: string, public folderPath: string, public size: number, public modified: Date) {}
}
const files = [
new FileInfo('README.md', '', 1345, new Date(2024,0,2)),
new FileInfo('index.html', 'Documents', 1345, new Date(2024,0,2)),
new FileInfo('Life 3.0.pdf', 'Documents/Books/Nonfiction', 5235091, new Date(2022,2,3)),
new FileInfo('Rules for Radicals.pdf', 'Documents/Books/Nonfiction', 583020, new Date(2022,0,1)),
new FileInfo('IMG_20240106_101958_861.jpg', 'Documents/Photos', 5830220, new Date(2024,0,6)),
new FileInfo('IMG_20240107_141614_680.jpg', 'Documents/Photos', 5731990, new Date(2024,0,7)),
new FileInfo('test.pdf', 'Books/Nonfiction', 5235091, new Date(2022,2,3)),
];
export function FileTree() {
// You don't have to memoize the column configuration, but hey, why not?
const { columns, folderConfig } = useMemo(getColumnsAndRowConfigs, []);
return (
// TypeScript can infer the type argument(s), but in case of a type error, the
// error message tends to be easier to understand if the type argument is explicit.
<DataTable<FileInfo|string>
columns={columns}
list={files}
// `getParents` is only called for items in `list`, all of which are `FileInfo`.
getParents={item => getParentPathsOf((item as FileInfo).folderPath, '/', true)}
getRowConfig={item => {
if (typeof item.item === 'string') {
return folderConfig;
}
}}
// Note: if your folders close, or the selection is lost, whenever the outer
// component is re-rendered, it means you need to add a `getSelKey` prop.
// But in this example the folders are just strings, which serve as
// their own keys.
/>);
function getColumnsAndRowConfigs() {
// Basic column configuration
let columns: DataTableColumn<string|FileInfo>[] = [
{
name: 'name',
heading: 'Name',
width: 300,
isTreeCol: true,
},
{
name: 'size',
heading: 'Size',
width: 100,
renderCell(node, props) {
let size = props.value as number;
if (size > 1024 * 1024) {
return `${(size / (1 << 20)).toFixed(1)} MiB`;
} else {
return size > 1024 ? `${(size / 1024).toFixed(1)} KiB` : `${size} bytes`;
}
}
},
{
name: 'modified',
heading: 'Modified',
width: 220,
renderCell(node, props) {
return (props.value as unknown as Date).toLocaleString();
}
},
];
// Configures "folder" (parent) rows
let folder = new Map<string, DataTableCellConfig<string|FileInfo, string>>([
['name', {
getValue: path => path.substring(path.lastIndexOf('/') + 1),
getColSpan: _ => 3,
}],
]);
return { columns, folderConfig: { columns: folder } };
}
}
In this example, files are FileInfo while folders are simply strings. The main columns
list contains default settings, and configuration for a specific row type is specified with
an object of type ItemAndColumns ({ columns: Map<string, DataTableCellConfig<...>>, ... }).
In this example we only need a single Map<string, DataTableCellConfig> for folders, but
often it's more convenient to define one for every row type.
Note that the folders are derived on-the-fly from the files using getParentPathsOf
(e.g. 'Books/Nonfiction' => ['Books', 'Books/Nonfiction']). You cannot simply use
folderPath.split('/') because if a folder such as 'Nonfiction' appears as part of multiple
paths, as it does in this example, DataTable will treat it like the "same" item, which can
cause problems, e.g. if you click one of the 'Nonfiction' folders, the other one will be
selected too. getParentPathsOf ensures that the two 'Nonfiction' folders are different
strings, and a getValue method is also required so that 'Books/Nonfiction' shows up as
'Nonfiction'.
DataTableoverviewDataTableandDataTableCoreare based onTreeListCore, so please have a look at the simplerTreeList/TreeListCorecomponents first. The difference is that whileTreeListis a tree of "items",DataTableis a tree of "rows" and therefore adds a set of columns and column headers. If you want a plain table rather than a tree, simply don't provide properties that would make it a tree.columnsandlist(ortree).listis an array of any typeT, whilecolumnsis typically an array ofDataTableColumn<T>.DataTableis the usual choice.DataTableCoreis the same, but omits the outer<div>and<table>elements (which is useful if you want to change the rendering method, e.g. to avoidtablein favor of flexbox).table-layout: fixed, so the widths of the columns are determined by the header row alone. If you provide a numeric/Holderwidthfor a column, the user can resize it by dragging the right edge of the column header.getChildrenorgetParentsprop and setisTreeCol: trueon exactly one of the columns.getRowConfigprop lets you configure behavior for each row type individually (see example below).Most important types
DataTableProps: props ofDataTable. InheritsDataTableCoreProps,TreeListMainPropsTreeListAuxPropsand most ofListCoreProps.DataTableColumn<T,...>: settings for a single column. Inherits fromDataTableCellConfig<T,T,...>.Support for multiple row types
A table has a single set of columns, but the way values are chosen for a column can be specified separately for each row type. Here is a simple example of a "file browser" to explain the principle:
In this example, files are
FileInfowhile folders are simply strings. The maincolumnslist contains default settings, and configuration for a specific row type is specified with an object of typeItemAndColumns({ columns: Map<string, DataTableCellConfig<...>>, ... }). In this example we only need a singleMap<string, DataTableCellConfig>for folders, but often it's more convenient to define one for every row type.Note that the folders are derived on-the-fly from the files using
getParentPathsOf(e.g. 'Books/Nonfiction' => ['Books', 'Books/Nonfiction']). You cannot simply usefolderPath.split('/')because if a folder such as 'Nonfiction' appears as part of multiple paths, as it does in this example,DataTablewill treat it like the "same" item, which can cause problems, e.g. if you click one of the 'Nonfiction' folders, the other one will be selected too.getParentPathsOfensures that the two 'Nonfiction' folders are different strings, and agetValuemethod is also required so that 'Books/Nonfiction' shows up as 'Nonfiction'.