Default

Loading interactive example. Full source is available below.

View example source
Download 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.

Setup and dependencies.

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.

Download 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 { 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;

Cell Navigation

Loading interactive example. Full source is available below.

Download source
// 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 { 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 * 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 CellNavigation = (): JSXElement => {
    const keyboardNavAttr = useArrowNavigationGroup({ axis: 'grid' });
    return (<Table {...keyboardNavAttr} role="grid" aria-label="Table with grid keyboard navigation" style={{ minWidth: '600px' }}>
      <TableHeader>
        <TableRow>
          {columns.map(column => (<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>))}
          <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">
              <TableCellLayout>
                <Button icon={<EditRegular />}>Edit</Button>
              </TableCellLayout>
            </TableCell>
          </TableRow>))}
      </TableBody>
    </Table>);
};

export default CellNavigation;

Composite Navigation

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { useTableCompositeNavigation } 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 CompositeNavigation = (): JSXElement => {
    const { tableRowTabsterAttribute, tableTabsterAttribute, onTableKeyDown } = useTableCompositeNavigation();
    return (<Table role="grid" aria-label="Table with grid keyboard navigation" noNativeElements onKeyDown={onTableKeyDown} style={{ minWidth: '500px' }} {...tableTabsterAttribute}>
      <TableHeader>
        <TableRow>
          {columns.map(column => (<TableHeaderCell tabIndex={0} key={column.columnKey}>
              {column.label}
            </TableHeaderCell>))}
        </TableRow>
      </TableHeader>
      <TableBody>
        {items.map(item => (<TableRow tabIndex={0} key={item.file.label} {...tableRowTabsterAttribute}>
            <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>
          </TableRow>))}
      </TableBody>
    </Table>);
};

export default CompositeNavigation;

Data Grid

Loading interactive example. Full source is available below.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download 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 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.

Download 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 { 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.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download 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 * 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.

Download 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 * 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.

Download source
// 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.

Download source
// 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.

Download source
// 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.

Download source
// 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;