> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# useSelection

> Table multi-select and bulk operations management

`useSelection` is a hook that manages multi-select functionality via checkboxes in list pages. It provides features like Shift+click range selection and select all/deselect all.

## Core Features

<CardGroup cols={2}>
  <Card title="Multi-select" icon="check-double">
    Manage individual/all selection

    Set-based efficient state
  </Card>

  <Card title="Range Selection" icon="hand-pointer">
    Shift+click support

    Select consecutive items at once
  </Card>

  <Card title="Auto Validation" icon="shield-check">
    Auto cleanup on page change

    Keep only existing items
  </Card>

  <Card title="Type Safety" icon="code">
    Generic type support

    Use any type as key
  </Card>
</CardGroup>

## Basic Usage

### Import and Initialization

```typescript theme={null}
import { useSelection } from "@sonamu-kit/react-components";

export function UserListPage() {
  const { data } = UserService.useUsers("A", listParams);
  const allIds = data?.rows.map((row) => row.id) ?? [];

  // Initialize useSelection
  const {
    selectedKeys, // Array of selected keys
    getSelected, // Check if specific key is selected
    toggle, // Toggle individual item selection
    selectAll, // Select all
    deselectAll, // Deselect all
    isAllSelected, // Check if all selected
    handleCheckboxClick, // Shift+click range selection
  } = useSelection(allIds, []); // allIds: all keys, []: initial selection

  // ...
}
```

**Parameter explanation**:

* `allIds`: Array of all keys on current page (e.g., `[1, 2, 3, 4, 5]`)
* `defaultSelectedKeys`: Initial selection state (optional, default: `[]`)

### Individual Item Selection

```typescript theme={null}
import { Checkbox } from "@sonamu-kit/react-components/components";

const { getSelected, toggle } = useSelection(allIds, []);

<Checkbox
  checked={getSelected(row.id)}
  onValueChange={() => toggle(row.id)}
/>
```

**How it works**:

* `getSelected(key)`: Returns `true` if key is selected, `false` otherwise
* `toggle(key)`: Toggle selection state

### Select All/Deselect All

```typescript theme={null}
const { isAllSelected, selectAll, deselectAll } = useSelection(allIds, []);

<Checkbox
  checked={isAllSelected}
  onValueChange={(checked) => {
    if (checked) {
      selectAll();
    } else {
      deselectAll();
    }
  }}
/>
```

**How it works**:

* `isAllSelected`: Whether all items on current page are selected
* `selectAll()`: Select all items on current page
* `deselectAll()`: Deselect all selections

### Shift+Click Range Selection

```typescript theme={null}
const { handleCheckboxClick } = useSelection(allIds, []);

{rows.map((row, index) => (
  <tr key={row.id}>
    <td onClick={(e) => handleCheckboxClick(e, index)}>
      <Checkbox
        checked={getSelected(row.id)}
        onValueChange={() => toggle(row.id)}
      />
    </td>
    {/* ... */}
  </tr>
))}
```

**How it works**:

1. Normal click: Toggle individual item selection
2. Shift+click: Select all items from last selection to current position

<Info>
  **How Shift+click works**

  `handleCheckboxClick` remembers the last click index and selects all items in between when Shift+clicking.

  ```
  Items: [A, B, C, D, E]
  1. Click A → A selected
  2. Shift+click D → A, B, C, D all selected
  ```
</Info>

## Real-world Examples

### Complete Table Implementation

```typescript theme={null}
import { useSelection } from "@sonamu-kit/react-components";
import { Checkbox, Button, Table } from "@sonamu-kit/react-components/components";
import { UserService } from "@/services/services.generated";

export function UserListPage() {
  const { data } = UserService.useUsers("A", listParams);
  const { rows } = data ?? {};
  const allIds = rows?.map(row => row.id) ?? [];

  const {
    selectedKeys,
    getSelected,
    toggle,
    selectAll,
    deselectAll,
    isAllSelected,
    handleCheckboxClick,
  } = useSelection(allIds, []);

  // Bulk delete
  const handleBulkDelete = async () => {
    if (selectedKeys.length === 0) {
      alert("Select items to delete");
      return;
    }

    if (confirm(`Delete ${selectedKeys.length} items?`)) {
      await UserService.del(selectedKeys);
      deselectAll();  // Deselect after deletion
    }
  };

  return (
    <div>
      {/* Bulk action buttons */}
      {selectedKeys.length > 0 && (
        <div className="mb-4 flex gap-2">
          <span>{selectedKeys.length} selected</span>
          <Button variant="red" onClick={handleBulkDelete}>
            Delete Selected
          </Button>
        </div>
      )}

      <Table>
        <thead>
          <tr>
            {/* Select all checkbox */}
            <th className="w-[40px]">
              <Checkbox
                checked={isAllSelected}
                onValueChange={(checked) => {
                  if (checked) {
                    selectAll();
                  } else {
                    deselectAll();
                  }
                }}
              />
            </th>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
          </tr>
        </thead>
        <tbody>
          {rows?.map((row, index) => (
            <tr key={row.id}>
              {/* Individual checkbox (with Shift+click support) */}
              <td onClick={(e) => handleCheckboxClick(e, index)}>
                <Checkbox
                  checked={getSelected(row.id)}
                  onValueChange={() => toggle(row.id)}
                />
              </td>
              <td>{row.id}</td>
              <td>{row.username}</td>
              <td>{row.email}</td>
            </tr>
          ))}
        </tbody>
      </Table>
    </div>
  );
}
```

### Auto Validation on Page Change

`useSelection` automatically validates selection state when `allIds` changes.

```typescript theme={null}
// Page 1: rows = [1, 2, 3]
const selection1 = useSelection([1, 2, 3], []);
selection1.toggle(1); // Select 1
selection1.toggle(2); // Select 2
// selectedKeys = [1, 2]

// Move to page 2: rows = [4, 5, 6]
const selection2 = useSelection([4, 5, 6], []);
// selectedKeys automatically cleaned to [] (1, 2 not in allIds)
```

**Validation logic**:

```typescript theme={null}
// Inside useSelection (list-helpers.ts:84-91)
useEffect(() => {
  const selectionKeys = Array.from(selection.keys());
  const validKeys = intersection(allKeys, selectionKeys);

  // Update state only when changes are needed
  if (validKeys.length !== selectionKeys.length) {
    setSelection(new Map(validKeys.map((key) => [key, true])));
  }
}, [allKeys, selection]);
```

<Info>
  **Why is auto validation needed?**

  When navigating pages or changing filters, `rows` changes. Items selected on the previous page may not exist on the new page, so invalid selections are automatically removed.
</Info>

### Operations with Selected Items

```typescript theme={null}
const { selectedKeys } = useSelection(allIds, []);

// Get information of selected items
const selectedRows = rows?.filter((row) => selectedKeys.includes(row.id)) ?? [];

// Bulk update
const handleBulkUpdate = async () => {
  await UserService.save({
    params: selectedRows.map((row) => ({
      id: row.id,
      status: "active",
    })),
  });
};

// Bulk export
const handleBulkExport = () => {
  const csv = selectedRows.map((row) => `${row.id},${row.username},${row.email}`).join("\n");

  // Download CSV
  const blob = new Blob([csv], { type: "text/csv" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "users.csv";
  a.click();
};
```

## Advanced Usage

### Initial Selection State

```typescript theme={null}
// Restore selection state from URL parameters
const searchParams = new URLSearchParams(window.location.search);
const preselectedIds = searchParams.get("selected")?.split(",").map(Number) ?? [];

const { selectedKeys } = useSelection(allIds, preselectedIds);
```

### Saving Selection State

```typescript theme={null}
const { selectedKeys } = useSelection(allIds, []);

// Save selection state to localStorage
useEffect(() => {
  localStorage.setItem("selectedUserIds", JSON.stringify(selectedKeys));
}, [selectedKeys]);

// Restore on next visit
const savedIds = JSON.parse(localStorage.getItem("selectedUserIds") ?? "[]");
const { selectedKeys } = useSelection(allIds, savedIds);
```

### Conditional Selection Restrictions

```typescript theme={null}
const { toggle } = useSelection(allIds, []);

// Only allow selection of items meeting certain conditions
const handleToggle = (row: UserRow) => {
  if (row.role === "admin") {
    alert("Cannot select admin users");
    return;
  }
  toggle(row.id);
};

<Checkbox
  checked={getSelected(row.id)}
  onValueChange={() => handleToggle(row)}
  disabled={row.role === "admin"}
/>
```

## Internal Structure

`useSelection` manages selection state with a `Map<T, boolean>` structure.

```typescript theme={null}
// Internal state (simplified)
const [selection, setSelection] = useState<Map<number, boolean>>(
  new Map([
    [1, true], // ID 1 selected
    [2, false], // ID 2 not selected
    [3, true], // ID 3 selected
  ]),
);

// Extract selectedKeys
const selectedKeys = Array.from(selection)
  .filter(([key, value]) => value === true)
  .map(([key]) => key);
// Result: [1, 3]
```

**Why use Map**:

* O(1) time complexity for fast lookups
* No type constraints on keys (number, string, object all possible)
* More extensible than Set (can store additional metadata)

## Type Safety

`useSelection` can specify key type with generics.

```typescript theme={null}
// number type
const selection1 = useSelection<number>([1, 2, 3], []);
selection1.toggle(1); // ✅ OK
selection1.toggle("1"); // ❌ Type error

// string type
const selection2 = useSelection<string>(["a", "b", "c"], []);
selection2.toggle("a"); // ✅ OK
selection2.toggle(1); // ❌ Type error

// Complex object type
type UserKey = { id: number; email: string };
const keys: UserKey[] = [
  { id: 1, email: "a@example.com" },
  { id: 2, email: "b@example.com" },
];
const selection3 = useSelection<UserKey>(keys, []);
```

## Cautions

### 1. Always provide allIds

```typescript theme={null}
// ❌ Wrong usage
const { selectedKeys } = useSelection([], []); // Empty array

// ✅ Correct usage
const allIds = rows?.map((row) => row.id) ?? [];
const { selectedKeys } = useSelection(allIds, []);
```

### 2. To maintain selection across page changes

By default, selection is reset when navigating pages. Separate management is needed to maintain it.

```typescript theme={null}
// Global selection state (managed separately with useState)
const [globalSelectedIds, setGlobalSelectedIds] = useState<Set<number>>(new Set());

// Sync with current page selection
useEffect(() => {
  const currentPageSelected = allIds.filter((id) => globalSelectedIds.has(id));
  // Sync with useSelection state
}, [allIds, globalSelectedIds]);
```

### 3. When using handleCheckboxClick

`handleCheckboxClick` should be connected to `<td>` or checkbox container.

```typescript theme={null}
// ✅ Correct usage
<td onClick={(e) => handleCheckboxClick(e, index)}>
  <Checkbox ... />
</td>

// ❌ Wrong usage (click event not propagated)
<Checkbox
  onClick={(e) => handleCheckboxClick(e, index)}
  ...
/>
```

## Related Documentation

* [useListParams](/en/frontend-integration/core-hooks/use-list-params) - Use with list filtering
* [Table Component](/en/frontend-integration/react-components/ui-library-overview) - Table UI
* [Building List Page](/en/frontend-integration/guides/building-list-page) - Complete example
