Table
Installation
npm install --save @gravity-ui/table
Utilisation
import React from 'react';
import {Table, useTable} from '@gravity-ui/table';
import type {ColumnDef} from '@gravity-ui/table/tanstack';
interface Person {
id: string;
name: string;
age: number;
}
const columns: ColumnDef<Person>[] = [
{accessorKey: 'name', header: 'Nom', size: 100},
{accessorKey: 'age', header: 'Âge', size: 100},
];
const data: Person[] = [
{id: 'name', name: 'John', age: 23},
{id: 'age', name: 'Michael', age: 27},
];
const BasicExample = () => {
const table = useTable({
columns,
data,
});
return <Table table={table} />;
};
Composants
Deux composants Table
sont disponibles :
BaseTable
: un composant avec des styles de base uniquement ;Table
: un composant avec les styles basés sur Gravity UI.
Sélection de lignes
import {selectionColumn} from '@gravity-ui/table';
import type {RowSelectionState} from '@gravity-ui/table/tanstack';
const columns: ColumnDef<Person>[] = [
selectionColumn as ColumnDef<Person>,
// ...autres colonnes
];
const data: Person[] = [
/* ... */
];
const RowSelectionExample = () => {
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
const table = useTable({
columns,
data,
enableRowSelection: true,
enableMultiRowSelection: true,
onRowSelectionChange: setRowSelection,
state: {
rowSelection,
},
});
return <Table table={table} />;
};
Colonne de sélection personnalisée par plage
Le hook useToggleRangeSelectionHandler
renvoie un gestionnaire de changement qui écoute les événements Shift+clic et effectue une sélection de lignes par plage. Il nécessite une instance de CellContext
pour avoir accès aux états internes de la table et de la ligne.
import React, {type ChangeEvent, useCallback, useState} from 'react';
import {Table, useToggleRangeSelectionHandler, useTable} from '@gravity-ui/table';
import type {CellContext, ColumnDef, RowSelectionState} from '@gravity-ui/table/tanstack';
import {Checkbox, type CheckboxProps} from '@gravity-ui/uikit';
type CustomRangedSelectionCheckboxProps = Omit<CheckboxProps, 'onChange'> & {
cellContext: CellContext<unknown, unknown>;
};
const CustomRangedSelectionCheckbox = ({
className,
cellContext,
...restProps
}: CustomRangedSelectionCheckboxProps) => {
const rowToggleRangedSelectionHandler = useToggleRangeSelectionHandler(cellContext);
const handleChange = useCallback(
(event: ChangeEvent<HTMLInputElement>): void => {
rowToggleRangedSelectionHandler(event);
},
[rowToggleRangedSelectionHandler],
);
return <Checkbox {...restProps} onChange={handleChange} />;
};
const customSelectionColumn: ColumnDef<unknown> = {
id: '_select',
header: ({table}) => (
<Checkbox
size="l"
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
),
cell: (cellContext) => (
<CustomRangedSelectionCheckbox
size="l"
checked={cellContext.row.getIsSelected()}
disabled={!cellContext.row.getCanSelect()}
indeterminate={cellContext.row.getIsSomeSelected()}
cellContext={cellContext}
/>
),
size: 41,
maxSize: 41,
minSize: 41,
enableResizing: false,
enableSorting: false,
};
const columns: ColumnDef<Person>[] = [
customSelectionColumn as ColumnDef<Person>,
// ...autres colonnes
];
const data: Person[] = [
/* ... */
];
const RowRangedSelectionExample = () => {
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const table = useTable({
columns,
data,
enableRowSelection: true,
enableMultiRowSelection: true,
onRowSelectionChange: setRowSelection,
state: {
rowSelection,
},
});
return <Table table={table} />;
};
Il existe également un composant RangedSelectionCheckbox
qui utilise le hook en interne et accepte une instance de CellContext
comme prop. Ce composant offre un raccourci pour ajouter la fonctionnalité de sélection par plage aux colonnes de sélection personnalisées.
import type {ColumnDef} from '@gravity-ui/table/tanstack';
import {RangedSelectionCheckbox, SelectionCheckbox} from '@gravity-ui/table';
export const selectionColumn: ColumnDef<unknown> = {
id: '_select',
header: ({table}) => (
<SelectionCheckbox
checked={table.getIsAllRowsSelected()}
disabled={!table.options.enableRowSelection}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
),
cell: (cellContext) => (
<RangedSelectionCheckbox
checked={cellContext.row.getIsSelected()}
disabled={!cellContext.row.getCanSelect()}
indeterminate={cellContext.row.getIsSomeSelected()}
cellContext={cellContext}
/>
),
meta: {
hideInSettings: true,
},
size: 32,
minSize: 32,
};
Par défaut, la colonne de sélection générée avec selectionColumn
inclut la fonctionnalité de sélection par plage.
import {selectionColumn} from '@gravity-ui/table';
import type {ColumnDef} from '@gravity-ui/table/tanstack';
const columns: ColumnDef<Person>[] = [
selectionColumn as ColumnDef<Person>,
// ...autres colonnes
];
Remarque : Si la table contient des lignes imbriquées, la sélection par plage ne fonctionnera pas. À l'heure actuelle, cela est considéré comme un comportement indéfini.
Tri
Découvrez les propriétés des colonnes dans la documentation de react-table.
Si vous souhaitez trier les éléments manuellement, passez la propriété manualSorting
:
const table = useTable({
// ...
manualSorting: true,
});
Groupement
import type {ExpandedState, Row} from '@gravity-ui/table/tanstack';
interface Person {
id: string;
name: string;
age: number;
}
interface PersonGroup {
id: string;
name: string;
items: Person[];
}
type Item = PersonGroup | Person;
const columns: ColumnDef<Item>[] = [
{accessorKey: 'name', header: 'Nom', size: 200},
{accessorKey: 'age', header: 'Âge', size: 100},
];
const data: Item[] = [
{
id: 'friends',
name: 'Amis',
items: [
{id: 'nick', name: 'Nick', age: 25},
{id: 'tom', name: 'Tom', age: 21},
],
},
{
id: 'relatives',
name: 'Famille',
items: [
{id: 'john', name: 'John', age: 23},
{id: 'michael', name: 'Michael', age: 27},
],
},
];
const getGroupTitle = (row: Row<Item>) => row.getValue<string>('name');
const GroupingExample = () => {
const [expanded, setExpanded] = React.useState<ExpandedState>({});
const table = useTable({
columns,
data,
enableExpanding: true,
getSubRows: (item) => ('items' in item ? item.items : undefined),
onExpandedChange: setExpanded,
state: {
expanded,
},
});
return <Table table={table} getGroupTitle={getGroupTitle} />;
};
Pour activer les styles d'imbrication, passez withNestingStyles = true
dans la configuration de la colonne.
Les indicateurs d'imbrication peuvent être désactivés en passant showTreeDepthIndicators = false
.
Pour ajouter un contrôle pour développer/réduire les lignes, enveloppez le contenu de la cellule avec le composant TreeExpandableCell
ou votre composant personnalisé similaire :
import {TreeExpandableCell} from '@gravity-ui/table';
const columns: ColumnDef<Item>[] = [
{
accessorKey: 'name',
header: 'Nom',
size: 200,
showTreeDepthIndicators: false,
withNestingStyles: true,
cell: ({row, info}) => (
<TreeExpandableCell row={row}>{info.getValue<string>()}</TreeExpandableCell>
),
},
// ...autres colonnes
];
Réorganisation
import type {ReorderingProviderProps} from '@gravity-ui/table';
import {dragHandleColumn, ReorderingProvider} from '@gravity-ui/table';
const columns: ColumnDef<Person>[] = [
dragHandleColumn,
// ...autres colonnes
];
const data: Person[] = [
/* ... */
];
const ReorderingExample = () => {
const table = useTable({
columns,
data,
getRowId: (item) => item.id,
});
const handleReorder = React.useCallback<
NonNullable<ReorderingProviderProps<Person>['onReorder']>
>(
({
draggedItemKey,
targetItemKey,
baseItemKey,
baseNextItemKey,
enableNesting,
nextChild,
pullFromParent,
}) => {
// ...
},
[],
);
return (
<ReorderingProvider table={table} onReorder={handleReorder}>
<Table table={table} />
</ReorderingProvider>
);
};
Virtualisation
Utilisez si vous souhaitez utiliser le conteneur de grille comme élément de défilement (si vous souhaitez utiliser la fenêtre, consultez la section de virtualisation de fenêtre). Assurez-vous de définir une hauteur fixe sur le conteneur ; sinon, la virtualisation ne fonctionnera pas.
import {useRowVirtualizer} from '@gravity-ui/table';
const columns: ColumnDef<Person>[] = [
/* ... */
];
const data: Person[] = [
/* ... */
];
const VirtualizationExample = () => {
const table = useTable({
columns,
data,
getRowId: (item) => item.id,
});
const containerRef = React.useRef<HTMLDivElement>(null);
const rowVirtualizer = useRowVirtualizer({
count: table.getRowModel().rows.length,
estimateSize: () => 20,
overscan: 5,
getScrollElement: () => containerRef.current,
});
return (
<div ref={containerRef} style={{height: '500px', overflow: 'auto'}}>
<Table table={table} rowVirtualizer={rowVirtualizer} />
</div>
);
};
Si vous utilisez la virtualisation avec la fonctionnalité de réorganisation, vous devez également passer l'option rangeExtractor
:
import {getVirtualRowRangeExtractor} from '@gravity-ui/table';
// ...
const tableRef = React.useRef<HTMLTableElement>(null);
const rowVirtualizer = useRowVirtualizer({
// ...
rangeExtractor: getVirtualRowRangeExtractor(tableRef.current),
});
return (
<TableWithReordering
ref={tableRef}
table={table}
rowVirtualizer={rowVirtualizer}
onReorder={handleReorder}
/>
);
Virtualisation de fenêtre
Utilisez si vous souhaitez utiliser la fenêtre comme élément de défilement
import {useWindowRowVirtualizer} from '@gravity-ui/table';
const columns: ColumnDef<Person>[] = [
/* ... */
];
const data: Person[] = [
/* ... */
];
const WindowVirtualizationExample = () => {
const table = useTable({
columns,
data,
getRowId: (item) => item.id,
});
const bodyRef = React.useRef<HTMLTableSectionElement>(null);
const rowVirtualizer = useWindowRowVirtualizer({
count: table.getRowModel().rows.length,
estimateSize: () => 20,
overscan: 5,
scrollMargin: bodyRef.current?.offsetTop ?? 0,
});
return <Table table={table} rowVirtualizer={rowVirtualizer} bodyRef={bodyRef} />;
};
Redimensionnement
const columns: ColumnDef<Person>[] = [
/* ... */
];
const data: Person[] = [
/* ... */
];
const ResizingDemo = () => {
const table = useTable({
columns,
data,
enableColumnResizing: true,
columnResizeMode: 'onChange',
});
return <Table table={table} />;
};
Paramètres des colonnes
const columns: ColumnDef<Person>[] = [
// ...autres colonnes
{
id: 'settings_column_id',
header: ({table}) => <TableSettings table={table} />,
meta: {
hideInSettings: false, // Optionnel. Permet de masquer cette colonne dans le popover des paramètres
titleInSettings: 'ReactNode', // Optionnel. Remplace le champ header pour le popover des paramètres (si vous avez besoin d'un contenu différent pour l'en-tête et le popover des paramètres)
},
}, // ou vous pouvez utiliser la fonction getSettingsColumn
];
const data: Person[] = [
/* ... */
];
const TableSettingsDemo = () => {
const [columnVisibility, onColumnVisibilityChange] = React.useState<VisibilityState>({
// pour le contrôle externe et l'état initial
column_id: false, // pour masquer par défaut
});
const [columnOrder, onColumnOrderChange] = React.useState<string[]>([
/* ids des colonnes feuilles */
]); // pour le contrôle externe et l'état initial
// Variante alternative pour obtenir l'état, les callbacks et définir les callbacks d'application des paramètres - en utilisant le hook useTableSettings :
// const {state, callbacks} = useTableSettings({initialVisibility: {}, initialOrder: []})
const table = useTable({
columns,
data,
state: {
columnVisibility,
columnOrder,
},
onColumnVisibilityChange,
onColumnOrderChange,
});
return <Table table={table} />;
};
Apprenez-en davantage sur la table et les propriétés de redimensionnement des colonnes dans la documentation de react-table.