Table
View .mdLow-level primitives for building customizable data tables. Subcomponents: TableHeader, TableHeaderCell, TableBody, TableRow, TableCell, TableSelectionCell, TableCellLayout.
Default
Loading interactive example. Full source is available below.
View example source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import * as React from 'react';
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
];
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const Default = (): JSXElement => {
return (<Table arial-label="Default table" style={{ minWidth: '510px' }}>
<TableHeader>
<TableRow>
{columns.map((column) => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
</TableRow>
</TableHeader>
<TableBody>
{items.map((item) => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{
status: item.author.status as PresenceBadgeStatus,
}}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>
{item.lastUpdate.label}
</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default Default;
API referenceGenerated CoreAI prop and type summaries
API
Generated CoreAI prop summaries, not replacement type declarations. Most native DOM/event and ARIA props are omitted. ? means optional; JSDoc shows defaults, deprecations, and conditional requirements. Defaults assume the standard CoreAI provider.
Table API
Table component
interface TableProps {
as?: "div" | "table";
children?: any;
className?: string;
/**
* Render all table elements as divs instead of semantic table elements
* Using divs no longer uses `display: table` layout but `display: flex`
* @default false
*/
noNativeElements?: boolean;
ref?: Ref<HTMLDivElement>;
/**
* Affects the sizes of all table subcomponents
* @default medium
*/
size?: "small" | "medium" | "extra-small";
/**
* Whether the table is sortable
* @default false
*/
sortable?: boolean;
style?: CSSProperties;
}
TableBody API
TableBody component
interface TableBodyProps {
as?: "div" | "tbody";
children?: any;
className?: string;
ref?: Ref<HTMLDivElement>;
style?: CSSProperties;
}
TableCell API
TableCell component
interface TableCellProps {
as?: "div" | "td";
children?: any;
className?: string;
ref?: Ref<HTMLDivElement>;
style?: CSSProperties;
}
TableCellLayout API
TableCellLayout component
interface TableCellLayoutProps {
/**
* Renders design variants of the table cell
* @default undefined
*/
appearance?: "primary";
as?: "div";
children?: any;
className?: string;
/**
* A layout wrapper for the main and description slots
*/
content?: WithSlotShorthandValue<{ as?: "div"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ref?: ((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Secondary text that describes or complements the main text
*/
description?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Main text for the table cell. Children of the root slot are automatically rendered here
*/
main?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Slot for an icon or other visual element
*/
media?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
ref?: Ref<HTMLDivElement>;
style?: CSSProperties;
/**
* Renders content with overflow: hidden and text-overflow: ellipsis
*/
truncate?: boolean;
}
TableHeader API
TableHeader component
interface TableHeaderProps {
as?: "div" | "thead";
children?: any;
className?: string;
ref?: Ref<HTMLDivElement>;
style?: CSSProperties;
}
TableHeaderCell API
TableHeaderCell component
interface TableHeaderCellProps {
as?: "div" | "th";
/**
* aside content for anything that should be after main content of the table header cell
*/
aside?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Button handles correct narration and interactions for sorting;
*/
button?: NonNullable<WithSlotShorthandValue<ARIAButtonSlotProps> | null>;
children?: any;
className?: string;
ref?: Ref<HTMLDivElement>;
/**
* Whether the column is sortable
* @default false
*/
sortable?: boolean;
/**
* @default undefined
*/
sortDirection?: "ascending" | "descending";
sortIcon?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
style?: CSSProperties;
}
TableRow API
TableRow component
interface TableRowProps {
/**
* A table row can have different variants. These appearances are
* intended to be used with selection.
* @default none
*/
appearance?: "none" | "brand" | "neutral";
as?: "div" | "tr";
children?: any;
className?: string;
ref?: Ref<HTMLDivElement>;
style?: CSSProperties;
}
TableSelectionCell API
TableSelectionCell component
interface TableSelectionCellProps {
as?: "div" | "td";
/**
* Selection indicator if selection type is checkbox
*/
checkboxIndicator?: (Omit<ComponentProps<Partial<CheckboxSlots>, "input">, "defaultChecked" | "onChange" | "size" | "checked"> & { checked?: boolean | "mixed"; children?: undefined; defaultChecked?: boolean | "mixed"; labelPosition?: "before" | "after" | undefined; onChange?: ((ev: ChangeEvent<HTMLInputElement>, data: CheckboxOnChangeData) => void) | undefined; shape?: "circular" | "square" | undefined; size?: "medium" | "large" | undefined; } & RefAttributes<HTMLInputElement>) | null | undefined;
/**
* @default false
*/
checked?: boolean | "mixed";
children?: any;
className?: string;
/**
* Completely hides the selection cell
* @deprecated cells should never use hidden, since doing so breaks table structure and screen reader navigation. Use `invisible` instead.
*/
hidden?: boolean;
/**
* Hides the selection cell visually but takes up the same space
* @default false
*/
invisible?: boolean;
/**
* Selection indicator if selection type is radio
*/
radioIndicator?: (Omit<ComponentProps<Partial<RadioSlots>, "input">, "onChange" | "size"> & { value?: string; labelPosition?: "below" | "after"; disabled?: boolean | undefined; onChange?: ((ev: ChangeEvent<HTMLInputElement>, data: RadioOnChangeData) => void) | undefined; } & RefAttributes<HTMLInputElement>) | null | undefined;
ref?: Ref<HTMLDivElement>;
style?: CSSProperties;
/**
* Only visible when checked or the parent row is hovered/focused
* @default false
*/
subtle?: boolean;
/**
* A table can have two kinds of selection modes.
* @default checkbox
*/
type?: "checkbox" | "radio";
}
Implementation
CoreAI 0.12.0; Fluent 9.74.9.
Import components from @coreai-ds/react-components. CoreAI guidance above takes precedence over API demonstration examples.
Cell Actions
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { TableCellActions } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { bundleIcon } from "@fluentui/react-icons";
import { EditFilled } from "@fluentui/react-icons";
import { MoreHorizontalFilled } from "@fluentui/react-icons";
import { MoreHorizontalRegular } from "@fluentui/react-icons";
import { Avatar } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import * as React from 'react';
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
];
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const EditIcon = bundleIcon(EditFilled, EditRegular);
const MoreHorizontalIcon = bundleIcon(MoreHorizontalFilled, MoreHorizontalRegular);
export const CellActions = (): JSXElement => {
return (<Table aria-label="Table with cell actions" style={{ minWidth: '500px' }}>
<TableHeader>
<TableRow>
{columns.map(column => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
</TableRow>
</TableHeader>
<TableBody>
{items.map(item => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
<TableCellActions>
<Button icon={<EditIcon />} appearance="subtle" aria-label="Edit"/>
<Button icon={<MoreHorizontalIcon />} appearance="subtle" aria-label="More actions"/>
</TableCellActions>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default CellActions;
Data Grid
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import { useTableSort } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TableColumnId } from "@coreai-ds/react-components";
import { useArrowNavigationGroup } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
}),
createTableColumn<Item>({
columnId: 'author',
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const DataGrid = (): JSXElement => {
const { getRows, selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected }, sort: { getSortDirection, toggleColumnSort, sort }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
useTableSort({ defaultSortState: { sortColumn: 'file', sortDirection: 'ascending' } }),
]);
const rows = sort(getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
}));
const headerSortProps = (columnId: TableColumnId) => ({
onClick: (e: React.MouseEvent) => {
toggleColumnSort(e, columnId);
},
sortDirection: getSortDirection(columnId),
});
const keyboardNavAttr = useArrowNavigationGroup({ axis: 'grid' });
return (<Table {...keyboardNavAttr} role="grid" sortable aria-label="DataGrid implementation with Table primitives" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} aria-checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} role="checkbox" onClick={toggleAllRows} checkboxIndicator={{ 'aria-label': 'Select all rows ' }}/>
<TableHeaderCell {...headerSortProps('file')}>File</TableHeaderCell>
<TableHeaderCell {...headerSortProps('author')}>Author</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdated')}>Last updated</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdate')}>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell role="gridcell" aria-selected={selected} checked={selected} checkboxIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell tabIndex={0} role="gridcell" aria-selected={selected}>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
{item.lastUpdated.label}
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default DataGrid;
Focusable Elements In Cells
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useArrowNavigationGroup } from "@coreai-ds/react-components";
import { useFocusableGroup } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { DeleteRegular } from "@fluentui/react-icons";
import * as React from 'react';
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
{ columnKey: 'actions', label: 'Actions' },
];
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const FocusableElementsInCells = (): JSXElement => {
const keyboardNavAttr = useArrowNavigationGroup({ axis: 'grid' });
const focusableGroupAttr = useFocusableGroup({ tabBehavior: 'limited-trap-focus' });
return (<Table {...keyboardNavAttr} role="grid" aria-label="Table with grid keyboard navigation" style={{ minWidth: '620px' }}>
<TableHeader>
<TableRow>
{columns.map(column => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
</TableRow>
</TableHeader>
<TableBody>
{items.map(item => (<TableRow key={item.file.label}>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
{item.lastUpdated.label}
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
<TableCell role="gridcell" tabIndex={0} {...focusableGroupAttr}>
<TableCellLayout>
<Button icon={<EditRegular />} aria-label="Edit"/>
<Button icon={<DeleteRegular />} aria-label="Delete"/>
</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default FocusableElementsInCells;
Memoization
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import { useTableSort } from "@coreai-ds/react-components";
import type { TableColumnId } from "@coreai-ds/react-components";
import * as React from 'react';
import { useArrowNavigationGroup } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import type { TableSelectionState } from "@coreai-ds/react-components";
import type { TableRowProps } from "@coreai-ds/react-components";
import type { TableRowId } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
}),
createTableColumn<Item>({
columnId: 'author',
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
type TableSelectableRowProps = {
toggleRow: TableSelectionState['toggleRow'];
item: Item;
selected: boolean;
appearance: TableRowProps['appearance'];
rowId: TableRowId;
};
const TableSelectableRow: React.FC<TableSelectableRowProps> = props => {
const { item, selected, appearance, rowId, toggleRow } = props;
const onClick = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => toggleRow(e, rowId), [toggleRow, rowId]);
const onKeyDown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, rowId);
}
}, [toggleRow, rowId]);
return (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell role="gridcell" aria-selected={selected} checked={selected} checkboxIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell tabIndex={0} role="gridcell" aria-selected={selected}>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
{item.lastUpdated.label}
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>);
};
const TableSelectableRowMemoized = React.memo(TableSelectableRow);
export const Memoization = (): JSXElement => {
const { getRows, selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected }, sort: { getSortDirection, toggleColumnSort, sort }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
useTableSort({ defaultSortState: { sortColumn: 'file', sortDirection: 'ascending' } }),
]);
const rows = sort(getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
}));
const headerSortProps = (columnId: TableColumnId) => ({
onClick: (e: React.MouseEvent) => {
toggleColumnSort(e, columnId);
},
sortDirection: getSortDirection(columnId),
});
const keyboardNavAttr = useArrowNavigationGroup({ axis: 'grid' });
return (<Table {...keyboardNavAttr} role="grid" sortable aria-label="DataGrid implementation with Table primitives" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} aria-checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} role="checkbox" onClick={toggleAllRows} checkboxIndicator={{ 'aria-label': 'Select all rows ' }}/>
<TableHeaderCell {...headerSortProps('file')}>File</TableHeaderCell>
<TableHeaderCell {...headerSortProps('author')}>Author</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdated')}>Last updated</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdate')}>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, appearance, rowId }) => (<TableSelectableRowMemoized key={rowId} rowId={rowId} toggleRow={toggleRow} item={item} selected={selected} appearance={appearance}/>))}
</TableBody>
</Table>);
};
export default Memoization;
Multiple Select
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import * as React from 'react';
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
}),
createTableColumn<Item>({
columnId: 'author',
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const MultipleSelect = (): JSXElement => {
const { getRows, selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
]);
const rows = getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
});
const toggleAllKeydown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === ' ') {
toggleAllRows(e);
e.preventDefault();
}
}, [toggleAllRows]);
return (<Table aria-label="Table with multiselect" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} onClick={toggleAllRows} onKeyDown={toggleAllKeydown} checkboxIndicator={{ 'aria-label': 'Select all rows ' }}/>
<TableHeaderCell>File</TableHeaderCell>
<TableHeaderCell>Author</TableHeaderCell>
<TableHeaderCell>Last updated</TableHeaderCell>
<TableHeaderCell>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell checked={selected} checkboxIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default MultipleSelect;
Multiple Select Controlled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TableRowId } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
}),
createTableColumn<Item>({
columnId: 'author',
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const MultipleSelectControlled = (): JSXElement => {
const [selectedRows, setSelectedRows] = React.useState(() => new Set<TableRowId>([0, 1]));
const { getRows, selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'multiselect',
selectedItems: selectedRows,
onSelectionChange: (e, data) => setSelectedRows(data.selectedItems),
}),
]);
const rows = getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
});
const toggleAllKeydown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === ' ') {
toggleAllRows(e);
e.preventDefault();
}
}, [toggleAllRows]);
return (<Table aria-label="Table with controlled multiselect" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} onClick={toggleAllRows} onKeyDown={toggleAllKeydown} checkboxIndicator={{ 'aria-label': 'Select all rows ' }}/>
<TableHeaderCell>File</TableHeaderCell>
<TableHeaderCell>Author</TableHeaderCell>
<TableHeaderCell>Last updated</TableHeaderCell>
<TableHeaderCell>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell checked={selected} checkboxIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default MultipleSelectControlled;
Non Native Elements
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import * as React from 'react';
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
];
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const NonNativeElements = (): JSXElement => {
return (<Table noNativeElements aria-label="Table without semantic HTML elements" style={{ minWidth: '500px' }}>
<TableHeader>
<TableRow>
{columns.map(column => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
</TableRow>
</TableHeader>
<TableBody>
{items.map(item => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default NonNativeElements;
Primary Cell
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { TableCellActions } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { bundleIcon } from "@fluentui/react-icons";
import { EditFilled } from "@fluentui/react-icons";
import { MoreHorizontalFilled } from "@fluentui/react-icons";
import { MoreHorizontalRegular } from "@fluentui/react-icons";
import { Avatar } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import * as React from 'react';
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
];
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const EditIcon = bundleIcon(EditFilled, EditRegular);
const MoreHorizontalIcon = bundleIcon(MoreHorizontalFilled, MoreHorizontalRegular);
export const PrimaryCell = (): JSXElement => {
return (<Table aria-label="Table with primary cell layout" style={{ minWidth: '500px' }}>
<TableHeader>
<TableRow>
{columns.map(column => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
</TableRow>
</TableHeader>
<TableBody>
{items.map(item => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon} description="My Organization" appearance="primary">
{item.file.label}
</TableCellLayout>
<TableCellActions>
<Button icon={<EditIcon />} appearance="subtle" aria-label="Edit"/>
<Button icon={<MoreHorizontalIcon />} appearance="subtle" aria-label="More actions"/>
</TableCellActions>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default PrimaryCell;
Resizable Columns - controlled (preview)
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import type { TableColumnId } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import type { TableColumnSizingOptions } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableColumnSizing_unstable } from "@coreai-ds/react-components";
import { useTableSort } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { Menu } from "@coreai-ds/react-components";
import { MenuTrigger } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { DismissRegular } from "@fluentui/react-icons";
import { MenuPopover } from "@coreai-ds/react-components";
import { MenuList } from "@coreai-ds/react-components";
import { MenuItem } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columnNames: {
[key: TableColumnId]: string;
} = {
file: 'File',
author: 'Author',
lastUpdated: 'Last updated',
lastUpdate: 'Last update',
};
const columnsDef: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
renderHeaderCell: () => <>{columnNames.file}</>,
renderCell: (item: Item) => (<TableCellLayout truncate media={item.file.icon}>
{item.file.label}
</TableCellLayout>),
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
}),
createTableColumn<Item>({
columnId: 'author',
renderHeaderCell: () => <>{columnNames.author}</>,
renderCell: (item: Item) => (<TableCellLayout truncate media={<Avatar name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>),
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
renderHeaderCell: () => <>{columnNames.lastUpdated}</>,
renderCell: (item: Item) => <TableCellLayout truncate>{item.lastUpdated.label}</TableCellLayout>,
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
renderHeaderCell: () => <>{columnNames.lastUpdate}</>,
renderCell: (item: Item) => (<TableCellLayout truncate media={item.lastUpdate.icon}>
{item.lastUpdate.label}
</TableCellLayout>),
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const ResizableColumnsControlled = (): JSXElement => {
const [columns, setColumns] = React.useState<TableColumnDefinition<Item>[]>(columnsDef);
const [columnSizingOptions, setColumnSizingOptions] = React.useState<TableColumnSizingOptions>({
file: {
idealWidth: 300,
minWidth: 190,
},
author: {
minWidth: 170,
defaultWidth: 250,
},
lastUpdated: {
minWidth: 120,
},
lastUpdate: {
minWidth: 220,
},
});
const removeColumn = (index: number) => {
setColumns([...columns.slice(0, index), ...columns.slice(index + 1)]);
};
const addColumn = () => {
const currentColumnIds = columns.map(({ columnId }) => columnId);
const missingColumnIndex = columnsDef.findIndex(({ columnId }) => !currentColumnIds.includes(columnId));
if (missingColumnIndex !== -1) {
const missingColumn = columnsDef[missingColumnIndex];
setColumns(state => [...state.slice(0, missingColumnIndex), missingColumn, ...state.slice(missingColumnIndex)]);
}
};
const onWidthChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let newWidth = parseInt(e.target.value, 10);
if (Number.isNaN(newWidth)) {
newWidth = 0;
}
setColumnSizingOptions(state => ({
...state,
file: {
...state.file,
idealWidth: newWidth,
},
}));
};
const onMinWidthChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let newMinWidth = parseInt(e.target.value, 10);
if (Number.isNaN(newMinWidth)) {
newMinWidth = 0;
}
setColumnSizingOptions(state => ({
...state,
file: {
...state.file,
minWidth: newMinWidth,
},
}));
};
const onColumnResize = React.useCallback((_: KeyboardEvent | TouchEvent | MouseEvent | undefined, { columnId, width, }: {
columnId: TableColumnId;
width: number;
}) => {
setColumnSizingOptions(state => ({
...state,
[columnId]: {
...state[columnId],
idealWidth: width,
},
}));
}, []);
const { getRows, sort: { getSortDirection, toggleColumnSort, sort }, columnSizing_unstable: columnSizing, tableRef, } = useTableFeatures({
columns,
items,
}, [
useTableColumnSizing_unstable({ columnSizingOptions, onColumnResize }),
useTableSort({ defaultSortState: { sortColumn: 'file', sortDirection: 'ascending' } }),
]);
const headerSortProps = (columnId: TableColumnId) => ({
onClick: (e: React.MouseEvent) => {
toggleColumnSort(e, columnId);
},
sortDirection: getSortDirection(columnId),
});
const rows = sort(getRows());
const inputId = useId('first-column');
return (<>
<p>
<Label htmlFor={`${inputId}-width`}>First column width: </Label>
<Input type="number" id={`${inputId}-width`} onChange={onWidthChange} value={columnSizingOptions.file.idealWidth ? columnSizingOptions.file.idealWidth.toString() : ''}/>
</p>
<p>
<Label htmlFor={`${inputId}-minwidth`}>First column minWidth: </Label>
<Input type="number" id={`${inputId}-minwidth`} onChange={onMinWidthChange} value={columnSizingOptions.file.minWidth ? columnSizingOptions.file.minWidth?.toString() : ''}/>
</p>
<p>
<Button onClick={addColumn} disabled={columns.length === columnsDef.length}>
Add removed column
</Button>
</p>
<div style={{ overflowX: 'auto' }}>
<Table sortable aria-label="Table with sort" ref={tableRef} {...columnSizing.getTableProps()}>
<TableHeader>
<TableRow>
{columns.map((column, index) => (<Menu openOnContext key={column.columnId}>
<MenuTrigger>
<TableHeaderCell key={column.columnId} {...columnSizing.getTableHeaderCellProps(column.columnId)} {...headerSortProps(column.columnId)}>
{column.renderHeaderCell()}
<Button appearance="transparent" aria-label={`Remove ${columnNames[column.columnId]} column`} size="small" icon={<DismissRegular />} style={{ position: 'absolute', right: 0 }} onClick={() => removeColumn(index)}/>
</TableHeaderCell>
</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem onClick={columnSizing.enableKeyboardMode(column.columnId)}>
Keyboard Column Resizing
</MenuItem>
</MenuList>
</MenuPopover>
</Menu>))}
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item }) => (<TableRow key={item.file.label}>
{columns.map(column => (<TableCell key={column.columnId} {...columnSizing.getTableCellProps(column.columnId)}>
{column.renderCell(item)}
</TableCell>))}
</TableRow>))}
</TableBody>
</Table>
</div>
</>);
};
export default ResizableColumnsControlled;
Resizable Columns - Disabled container auto-fit
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableColumnSizing_unstable } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { Menu } from "@coreai-ds/react-components";
import { MenuTrigger } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { MenuPopover } from "@coreai-ds/react-components";
import { MenuList } from "@coreai-ds/react-components";
import { MenuItem } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columnsDef: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
renderHeaderCell: () => <>File</>,
}),
createTableColumn<Item>({
columnId: 'author',
renderHeaderCell: () => <>Author</>,
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
renderHeaderCell: () => <>Last updated</>,
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
renderHeaderCell: () => <>Last update</>,
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const columnSizingOptions = {
file: {
idealWidth: 300,
minWidth: 150,
},
author: {
minWidth: 110,
defaultWidth: 250,
},
lastUpdate: {
minWidth: 150,
},
};
export const ResizableColumnsDisableAutoFit = (): JSXElement => {
const [columns] = React.useState<TableColumnDefinition<Item>[]>(columnsDef);
// eslint-disable-next-line @typescript-eslint/naming-convention
const { getRows, columnSizing_unstable, tableRef } = useTableFeatures({
columns,
items,
}, [useTableColumnSizing_unstable({ columnSizingOptions, autoFitColumns: false })]);
const rows = getRows();
return (<div style={{ overflowX: 'auto' }}>
<Table ref={tableRef} {...columnSizing_unstable.getTableProps()} noNativeElements={true}>
<TableHeader>
<TableRow>
{columns.map(column => (<Menu openOnContext key={column.columnId}>
<MenuTrigger>
<TableHeaderCell key={column.columnId} {...columnSizing_unstable.getTableHeaderCellProps(column.columnId)}>
{column.renderHeaderCell()}
</TableHeaderCell>
</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem onClick={columnSizing_unstable.enableKeyboardMode(column.columnId)}>
Keyboard Column Resizing
</MenuItem>
</MenuList>
</MenuPopover>
</Menu>))}
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item }) => (<TableRow key={item.file.label}>
<TableCell {...columnSizing_unstable.getTableCellProps('file')}>
<TableCellLayout truncate media={item.file.icon}>
{item.file.label}
</TableCellLayout>
</TableCell>
<TableCell {...columnSizing_unstable.getTableCellProps('author')}>
<TableCellLayout truncate media={<Avatar name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell {...columnSizing_unstable.getTableCellProps('lastUpdated')}>
<TableCellLayout truncate>{item.lastUpdated.label}</TableCellLayout>
</TableCell>
<TableCell {...columnSizing_unstable.getTableCellProps('lastUpdate')}>
<TableCellLayout truncate media={item.lastUpdate.icon}>
{item.lastUpdate.label}
</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>
</div>);
};
export default ResizableColumnsDisableAutoFit;
Resizable Columns (preview)
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import type { TableColumnSizingOptions } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableColumnSizing_unstable } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { Menu } from "@coreai-ds/react-components";
import { MenuTrigger } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { MenuPopover } from "@coreai-ds/react-components";
import { MenuList } from "@coreai-ds/react-components";
import { MenuItem } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columnsDef: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
renderHeaderCell: () => <>File</>,
}),
createTableColumn<Item>({
columnId: 'author',
renderHeaderCell: () => <>Author</>,
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
renderHeaderCell: () => <>Last updated</>,
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
renderHeaderCell: () => <>Last update</>,
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const ResizableColumnsUncontrolled = (): JSXElement => {
const [columns] = React.useState<TableColumnDefinition<Item>[]>(columnsDef);
const [columnSizingOptions] = React.useState<TableColumnSizingOptions>({
file: {
idealWidth: 300,
minWidth: 150,
},
author: {
minWidth: 110,
defaultWidth: 250,
},
lastUpdate: {
minWidth: 150,
},
});
// eslint-disable-next-line @typescript-eslint/naming-convention
const { getRows, columnSizing_unstable, tableRef } = useTableFeatures({
columns,
items,
}, [useTableColumnSizing_unstable({ columnSizingOptions })]);
const [inputValue, setInputValue] = React.useState('300');
const onWidthChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
const numeric = parseInt(e.target.value, 10);
if (!Number.isNaN(numeric)) {
columnSizing_unstable.setColumnWidth('file', numeric);
}
};
const rows = getRows();
const inputId = useId('column-width');
return (<>
<p>
<label htmlFor={inputId}>First column width: </label>
<Input type="number" id={inputId} onChange={onWidthChange} value={inputValue ? inputValue.toString() : ''}/>
</p>
<div style={{ overflowX: 'auto' }}>
<Table sortable aria-label="Table with sort" ref={tableRef} {...columnSizing_unstable.getTableProps()}>
<TableHeader>
<TableRow>
{columns.map(column => (<Menu openOnContext key={column.columnId}>
<MenuTrigger>
<TableHeaderCell key={column.columnId} {...columnSizing_unstable.getTableHeaderCellProps(column.columnId)}>
{column.renderHeaderCell()}
</TableHeaderCell>
</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem onClick={columnSizing_unstable.enableKeyboardMode(column.columnId)}>
Keyboard Column Resizing
</MenuItem>
</MenuList>
</MenuPopover>
</Menu>))}
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item }) => (<TableRow key={item.file.label}>
<TableCell {...columnSizing_unstable.getTableCellProps('file')}>
<TableCellLayout truncate media={item.file.icon}>
{item.file.label}
</TableCellLayout>
</TableCell>
<TableCell {...columnSizing_unstable.getTableCellProps('author')}>
<TableCellLayout truncate media={<Avatar name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell {...columnSizing_unstable.getTableCellProps('lastUpdated')}>
<TableCellLayout truncate>{item.lastUpdated.label}</TableCellLayout>
</TableCell>
<TableCell {...columnSizing_unstable.getTableCellProps('lastUpdate')}>
<TableCellLayout truncate media={item.lastUpdate.icon}>
{item.lastUpdate.label}
</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>
</div>
</>);
};
export default ResizableColumnsUncontrolled;
Selection With Cell Actions
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import * as React from 'react';
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { TableCellActions } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { bundleIcon } from "@fluentui/react-icons";
import { EditFilled } from "@fluentui/react-icons";
import { MoreHorizontalFilled } from "@fluentui/react-icons";
import { MoreHorizontalRegular } from "@fluentui/react-icons";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
}),
createTableColumn<Item>({
columnId: 'author',
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const EditIcon = bundleIcon(EditFilled, EditRegular);
const MoreHorizontalIcon = bundleIcon(MoreHorizontalFilled, MoreHorizontalRegular);
export const SelectionWithCellActions = (): JSXElement => {
const { getRows, selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
]);
const rows = getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => !e.defaultPrevented && toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ' && !e.defaultPrevented) {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
});
const toggleAllKeydown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === ' ') {
toggleAllRows(e);
e.preventDefault();
}
}, [toggleAllRows]);
const onClickCellActions = (e: React.MouseEvent<HTMLDivElement>) => e.preventDefault();
const onKeyDownCellActions = (e: React.KeyboardEvent<HTMLDivElement>) => e.key === ' ' && e.preventDefault();
return (<Table aria-label="Table with multiselect" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} onClick={toggleAllRows} onKeyDown={toggleAllKeydown} checkboxIndicator={{ 'aria-label': 'Select all rows ' }}/>
<TableHeaderCell>File</TableHeaderCell>
<TableHeaderCell>Author</TableHeaderCell>
<TableHeaderCell>Last updated</TableHeaderCell>
<TableHeaderCell>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell checked={selected} checkboxIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
<TableCellActions onClick={onClickCellActions} onKeyDown={onKeyDownCellActions}>
<Button icon={<EditIcon />} appearance="subtle" aria-label="Edit"/>
<Button icon={<MoreHorizontalIcon />} appearance="subtle" aria-label="More actions"/>
</TableCellActions>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default SelectionWithCellActions;
Single Select
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import * as React from 'react';
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
}),
createTableColumn<Item>({
columnId: 'author',
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const SingleSelect = (): JSXElement => {
const { getRows, selection: { toggleRow, isRowSelected }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'single',
defaultSelectedItems: new Set([1]),
}),
]);
const rows = getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
});
return (<Table aria-label="Table with single selection" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell type="radio" invisible/>
<TableHeaderCell>File</TableHeaderCell>
<TableHeaderCell>Author</TableHeaderCell>
<TableHeaderCell>Last updated</TableHeaderCell>
<TableHeaderCell>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell checked={selected} type="radio" radioIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default SingleSelect;
Single Select Controlled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TableRowId } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
}),
createTableColumn<Item>({
columnId: 'author',
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const SingleSelectControlled = (): JSXElement => {
const [selectedRows, setSelectedRows] = React.useState(() => new Set<TableRowId>([1]));
const { getRows, selection: { toggleRow, isRowSelected }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'single',
selectedItems: selectedRows,
onSelectionChange: (e, data) => setSelectedRows(data.selectedItems),
}),
]);
const rows = getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
});
return (<Table aria-label="Table with controlled single selection" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell type="radio" invisible/>
<TableHeaderCell>File</TableHeaderCell>
<TableHeaderCell>Author</TableHeaderCell>
<TableHeaderCell>Last updated</TableHeaderCell>
<TableHeaderCell>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell checked={selected} type="radio" radioIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default SingleSelectControlled;
Size Extra Small
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import * as React from 'react';
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
];
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' as const },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' as const },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const SizeExtraSmall = (): JSXElement => {
return (<Table size="extra-small" aria-label="Table with extra-small size" style={{ minWidth: '400px' }}>
<TableHeader>
<TableRow>
{columns.map(column => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
</TableRow>
</TableHeader>
<TableBody>
{items.map(item => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default SizeExtraSmall;
Size Small
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import * as React from 'react';
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
];
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' as const },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' as const },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const SizeSmall = (): JSXElement => {
return (<Table size="small" aria-label="Table with small size" style={{ minWidth: '475px' }}>
<TableHeader>
<TableRow>
{columns.map(column => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
</TableRow>
</TableHeader>
<TableBody>
{items.map(item => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default SizeSmall;
Sort
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSort } from "@coreai-ds/react-components";
import type { TableColumnId } from "@coreai-ds/react-components";
import * as React from 'react';
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
}),
createTableColumn<Item>({
columnId: 'author',
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const Sort = (): JSXElement => {
const { getRows, sort: { getSortDirection, toggleColumnSort, sort }, } = useTableFeatures({
columns,
items,
}, [useTableSort({ defaultSortState: { sortColumn: 'file', sortDirection: 'ascending' } })]);
const headerSortProps = (columnId: TableColumnId) => ({
onClick: (e: React.MouseEvent) => {
toggleColumnSort(e, columnId);
},
sortDirection: getSortDirection(columnId),
});
const rows = sort(getRows());
return (<Table sortable aria-label="Table with sort" style={{ minWidth: '500px' }}>
<TableHeader>
<TableRow>
<TableHeaderCell {...headerSortProps('file')}>File</TableHeaderCell>
<TableHeaderCell {...headerSortProps('author')}>Author</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdated')}>Last updated</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdate')}>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item }) => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default Sort;
Sort Controlled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TableColumnId } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSort } from "@coreai-ds/react-components";
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
}),
createTableColumn<Item>({
columnId: 'author',
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const SortControlled = (): JSXElement => {
const [sortState, setSortState] = React.useState<{
sortDirection: 'ascending' | 'descending';
sortColumn: TableColumnId | undefined;
}>({
sortDirection: 'ascending' as const,
sortColumn: 'file',
});
const { getRows, sort: { getSortDirection, toggleColumnSort, sort }, } = useTableFeatures({
columns,
items,
}, [useTableSort({ sortState, onSortChange: (e, nextSortState) => setSortState(nextSortState) })]);
const headerSortProps = (columnId: TableColumnId) => ({
onClick: (e: React.MouseEvent) => toggleColumnSort(e, columnId),
sortDirection: getSortDirection(columnId),
});
const rows = sort(getRows());
return (<Table sortable aria-label="Table with controlled sort" style={{ minWidth: '500px' }}>
<TableHeader>
<TableRow>
<TableHeaderCell {...headerSortProps('file')}>File</TableHeaderCell>
<TableHeaderCell {...headerSortProps('author')}>Author</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdated')}>Last updated</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdate')}>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item }) => (<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default SortControlled;
Subtle Selection
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import type { TableColumnDefinition } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import * as React from 'react';
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type FileCell = {
label: string;
icon: JSXElement;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSXElement;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
}),
createTableColumn<Item>({
columnId: 'author',
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
}),
];
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
export const SubtleSelection = (): JSXElement => {
const { getRows, selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
]);
const rows = getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
});
const toggleAllKeydown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === ' ') {
toggleAllRows(e);
e.preventDefault();
}
}, [toggleAllRows]);
return (<Table aria-label="Table with subtle selection" style={{ minWidth: '550px' }}>
<TableHeader>
<TableRow>
<TableSelectionCell checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} onClick={toggleAllRows} onKeyDown={toggleAllKeydown} checkboxIndicator={{ 'aria-label': 'Select all rows ' }}/>
<TableHeaderCell>File</TableHeaderCell>
<TableHeaderCell>Author</TableHeaderCell>
<TableHeaderCell>Last updated</TableHeaderCell>
<TableHeaderCell>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (<TableRow key={item.file.label} onClick={onClick} onKeyDown={onKeyDown} aria-selected={selected} appearance={appearance}>
<TableSelectionCell subtle checked={selected} checkboxIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>))}
</TableBody>
</Table>);
};
export default SubtleSelection;
Virtualization
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useApplyScrollbarWidth } from "@coreai-ds/react-components";
import { useTableFeatures } from "@coreai-ds/react-components";
import { createTableColumn } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";
import { DocumentRegular } from "@fluentui/react-icons";
import { EditRegular } from "@fluentui/react-icons";
import { FolderRegular } from "@fluentui/react-icons";
import { OpenRegular } from "@fluentui/react-icons";
import { VideoRegular } from "@fluentui/react-icons";
import { DocumentPdfRegular } from "@fluentui/react-icons";
import { PeopleRegular } from "@fluentui/react-icons";
import { useTableSelection } from "@coreai-ds/react-components";
import type { TableRowData as RowStateBase } from "@coreai-ds/react-components";
import * as React from 'react';
import { Table } from "@coreai-ds/react-components";
import { TableHeader } from "@coreai-ds/react-components";
import { TableRow } from "@coreai-ds/react-components";
import { TableSelectionCell } from "@coreai-ds/react-components";
import { TableHeaderCell } from "@coreai-ds/react-components";
import { TableBody } from "@coreai-ds/react-components";
import { FixedSizeList as List } from "react-window";
import type { ListChildComponentProps } from "react-window";
import { TableCell } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
type Item = {
file: {
label: string;
icon: JSXElement;
};
author: {
label: string;
status: PresenceBadgeStatus;
};
lastUpdated: {
label: string;
timestamp: number;
};
lastUpdate: {
label: string;
icon: JSXElement;
};
};
const columns = [
createTableColumn<Item>({
columnId: 'file',
}),
createTableColumn<Item>({
columnId: 'author',
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
}),
];
const baseItems: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const items = new Array(1500).fill(0).map((_, i) => baseItems[i % baseItems.length]);
interface TableRowData extends RowStateBase<Item> {
onClick: (e: React.MouseEvent) => void;
onKeyDown: (e: React.KeyboardEvent) => void;
selected: boolean;
appearance: 'brand' | 'none';
}
interface ReactWindowRenderFnProps extends ListChildComponentProps {
data: TableRowData[];
}
const RenderRow = ({ index, style, data }: ReactWindowRenderFnProps) => {
const { item, selected, appearance, onClick, onKeyDown } = data[index];
return (<TableRow aria-rowindex={index + 2} style={style} key={item.file.label} onKeyDown={onKeyDown} onClick={onClick} appearance={appearance}>
<TableSelectionCell checked={selected} checkboxIndicator={{ 'aria-label': 'Select row' }}/>
<TableCell>
<TableCellLayout media={item.file.icon}>
<strong>[{index}] </strong>
{item.file.label}
</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout media={<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }}/>}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>);
};
export const Virtualization = (): JSXElement => {
const appliedScrollbarWidthRef = useApplyScrollbarWidth();
const { getRows, selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected }, } = useTableFeatures({
columns,
items,
}, [
useTableSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
]);
const rows: TableRowData[] = getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
});
const toggleAllKeydown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === ' ') {
toggleAllRows(e);
e.preventDefault();
}
}, [toggleAllRows]);
return (<Table noNativeElements aria-label="Table with selection" aria-rowcount={rows.length} style={{ minWidth: '650px' }}>
<TableHeader>
<TableRow aria-rowindex={1}>
<TableSelectionCell checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false} onClick={toggleAllRows} onKeyDown={toggleAllKeydown} checkboxIndicator={{ 'aria-label': 'Select all rows' }}/>
<TableHeaderCell>File</TableHeaderCell>
<TableHeaderCell>Author</TableHeaderCell>
<TableHeaderCell>Last updated</TableHeaderCell>
<TableHeaderCell>Last update</TableHeaderCell>
{/** Scrollbar alignment for the header */}
<div role="presentation" ref={appliedScrollbarWidthRef}/>
</TableRow>
</TableHeader>
<TableBody>
<List height={400} itemCount={items.length} itemSize={45} width="100%" itemData={rows}>
{RenderRow}
</List>
</TableBody>
</Table>);
};
export default Virtualization;