# Cell Selection

> InfiniteTable DataGrid component supports single and multiple cell selection.

Canonical page: https://infinite-table.com/docs/learn/selection/cell-selection

To use multi-cell selection, you need to configure the `<DataSource />` component with `selectionMode="multi-cell"` - see [`selectionMode`](https://infinite-table.com/docs/reference/datasource-props/index.md#selectionMode) for details. For selecting rows, see the [Row Selection](https://infinite-table.com/docs/learn/selection/row-selection.md) page.

```tsx title="Configuring the selection mode"
<DataSource selectionMode="multi-cell" />

// can be "single-row", "multi-row", "multi-cell" or false
```

**Example: Multiple cell selection example**

Click cells in the grid to add to the selection.

Use `Shift+Click` to select a range of cells and `Cmd/Ctrl+Click` to add single cells to the selection.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 60 },
  firstName: { field: 'firstName' },
  preferredLanguage: { field: 'preferredLanguage' },
  stack: { field: 'stack' },
  country: { field: 'country' },
  age: { field: 'age', type: 'number' },
  salary: { field: 'salary', type: 'number' },
  currency: { field: 'currency', type: 'number' },
};

export default function App() {
  return (
    <DataSource<Developer>
      primaryKey="id"
      data={dataSource}
      selectionMode="multi-cell"
    >
      <InfiniteTable<Developer>
        debugId="cell-selection-default-example"
        columns={columns}
        columnDefaultWidth={100}
      />
    </DataSource>
  );
}
```

## Using default selection

You can specify a default value for cell selection by using the [`defaultCellSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultCellSelection) prop.

```tsx title="Using default selection"

const defaultCellSelection = {
  defaultSelection: false,
  selectedCells: [
    [3, "stack"], // rowId + colId
    [5, "stack"], // rowId + colId
    [0, "firstName"], // rowId + colId
  ]
}

<DataSource
  selectionMode="multi-cell"
  defaultCellSelection={defaultCellSelection}
/>
```

Cell selection uses `[rowId, colId]` cell descriptors to identify cells to be marked as selected or deselected - read more in the [Cell selection format](#cell-selection-format).

**Example: Multiple cell selection with a default selection value**

By default some cells are already selected in the grid below, by using the [`defaultCellSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultCellSelection) prop on the `<DataSource />` component.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
  DataSourcePropCellSelection_MultiCell,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 60 },
  firstName: { field: 'firstName' },
  preferredLanguage: { field: 'preferredLanguage' },
  stack: { field: 'stack' },
  country: { field: 'country' },
  age: { field: 'age', type: 'number' },
  salary: { field: 'salary', type: 'number' },
  currency: { field: 'currency', type: 'number' },
};

export default function App() {
  const defaultCellSelection: DataSourcePropCellSelection_MultiCell = {
    defaultSelection: false,
    selectedCells: [
      [3, 'stack'], // rowId + colId
      [4, 'stack'],
      [5, 'stack'],
      [0, 'firstName'],
    ],
  };
  return (
    <DataSource<Developer>
      primaryKey="id"
      data={dataSource}
      defaultCellSelection={defaultCellSelection}
      selectionMode="multi-cell"
    >
      <InfiniteTable<Developer>
        debugId="cell-selection-default-selection-example"
        columns={columns}
        columnDefaultWidth={100}
      />
    </DataSource>
  );
}
```

Whe you're using cell selection with or without any default value (via the [`defaultCellSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultCellSelection)), you're using an uncontrolled prop. This means that the selection state is managed by the `<DataSource />` component and not by you. If you want to control the selection state yourself, you can use the controlled [`cellSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#cellSelection) prop instead - see [Using controlled selection](#using-controlled-selection) for details.

## Cell selection format

The [`cellSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#cellSelection) prop is an object with the following shape:

- `defaultSelection` - `boolean` - whether or not cells are selected by default.
- either:
  - `selectedCells`: `[rowId, colId][]` - an array of cells that should be selected (this is combined with `defaultSelection: false`)
- or
  - `deselectedCells`: `[rowId, colId][]` - an array of cells that should be deselected (this is combined with `defaultSelection: true`)

When `defaultSelection` is `true`, you will only need to specify the `deselectedCells` prop.

And when `defaultSelection` is `false`, you will only need to specify the `selectedCells` prop.

In this way, you can either specify which cells should be selected or which cells should be deselected - and have a default that matches the most common case.

The `selectedCells`/`deselectedCells` are arrays of `[rowId, colId]` tuples. The `rowId` is the `id` of the row ([the primary key](https://infinite-table.com/docs/reference/datasource-props/index.md#primaryKey)), and the `colId` is the `id` of the column (the identifier of the column in the [`columns`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns) prop).

The following scenarios are all possible:

```tsx title="Just a few specified cells are selected"
const defaultCellSelection = {
  defaultSelection: false,
  selectedCells: [
    ['id2', 'stack'],
    ['id2', 'stack'],
    ['id0', 'firstName'],
  ],
};
```

```tsx title="Everything is selected, except a few cells"
const defaultCellSelection = {
  defaultSelection: true,
  deselectedCells: [
    ['row2', 'stack'],
    ['row3', 'stack'],
    ['row5', 'firstName'],
  ],
};
```

### Using wildcards for selection

It's also possible to use wildcards for selecting cells. This is useful if you want to select all cells in a column, or all cells in a row.

```tsx title="Selecting all cells in a column"
const defaultCellSelection = {
  defaultSelection: false,
  selectedCells: [
    ['*', 'stack'],
    ['row2', 'firstName'],
  ],
};
```

```tsx title="Selecting all cells in a row"
const defaultCellSelection = {
  defaultSelection: false,
  selectedCells: [
    ['row1', '*'],
    ['row2', 'firstName'],
  ],
};
```

```tsx title="Selecting everything except a column"
const defaultCellSelection = {
  defaultSelection: true,
  deselectedCells: [['*', 'stack']],
};
```

## Using controlled selection

When using the controlled [`cellSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#cellSelection) you have to update the value of the property yourself, by listening to the [`onCellSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onCellSelectionChange) event.

**Example: Using controlled cell selection**

This example shows how to use the [`onCellSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onCellSelectionChange) callback prop to listen to changes to the controlled [`cellSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#cellSelection) prop on the `<DataSource />` component.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
  DataSourcePropCellSelection_MultiCell,
} from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 60 },
  firstName: { field: 'firstName' },
  preferredLanguage: { field: 'preferredLanguage' },
  stack: { field: 'stack' },
  country: { field: 'country' },
  age: { field: 'age', type: 'number' },
  salary: { field: 'salary', type: 'number' },
  currency: { field: 'currency', type: 'number' },
};

export default function App() {
  const [cellSelection, setCellSelection] =
    React.useState<DataSourcePropCellSelection_MultiCell>({
      defaultSelection: false,
      selectedCells: [
        [3, 'stack'],
        [0, 'firstName'],
      ],
    });

  return (
    <div
      style={{
        display: 'flex',
        flex: 1,
        color: 'var(--infinite-cell-color)',
        flexFlow: 'column',
        background: 'var(--infinite-background)',
      }}
    >
      <div
        style={{
          maxHeight: 200,
          overflow: 'auto',
          border: '2px solid magenta',
        }}
      >
        Current selection:
        <pre>{JSON.stringify(cellSelection, null, 2)}</pre>
      </div>

      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        cellSelection={cellSelection}
        onCellSelectionChange={setCellSelection}
        selectionMode="multi-cell"
      >
        <InfiniteTable<Developer>
          debugId="controlled-cell-selection-example"
          columns={columns}
          columnDefaultWidth={100}
        />
      </DataSource>
    </div>
  );
}
```

## Using the Cell Selection API

The `<DataSource />` component also exposes a [Cell Selection API](https://infinite-table.com/docs/reference/cell-selection-api/index.md), which you can use to select and deselect cells programmatically.

**Example: Using the CellSelectionAPI to select a column**

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
  DataSourcePropCellSelection_MultiCell,
  InfiniteTableApi,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 60 },
  firstName: { field: 'firstName' },
  preferredLanguage: { field: 'preferredLanguage' },
  stack: { field: 'stack' },
  country: { field: 'country' },
  age: { field: 'age', type: 'number' },
  salary: { field: 'salary', type: 'number' },
  currency: { field: 'currency', type: 'number' },
};

export default function App() {
  const [cellSelection, setCellSelection] =
    React.useState<DataSourcePropCellSelection_MultiCell>({
      defaultSelection: false,
      selectedCells: [
        [3, 'stack'],
        [0, 'firstName'],
      ],
    });

  const [api, setApi] = React.useState<InfiniteTableApi<Developer> | null>();

  return (
    <div
      style={{
        display: 'flex',
        flex: 1,
        color: 'var(--infinite-cell-color)',
        flexFlow: 'column',
        background: 'var(--infinite-background)',
      }}
    >
      <button
        style={{
          margin: 10,
          padding: 10,
          borderRadius: 5,
          border: '2px solid magenta',
        }}
        onClick={() => {
          api?.cellSelectionApi.selectColumn('firstName');
        }}
      >
        Select "firstName" column
      </button>
      <div
        style={{
          maxHeight: 200,
          overflow: 'auto',
          border: '2px solid magenta',
        }}
      >
        Current selection:
        <pre>{JSON.stringify(cellSelection, null, 2)}</pre>
      </div>

      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        cellSelection={cellSelection}
        onCellSelectionChange={setCellSelection}
        selectionMode="multi-cell"
      >
        <InfiniteTable<Developer>
          debugId="controlled-cell-selection-with-api-example"
          columns={columns}
          columnDefaultWidth={100}
          onReady={({ api }) => {
            setApi(api);
          }}
        />
      </DataSource>
    </div>
  );
}
```
