# How the Selection System for Diagram Elements Works in DrawDB

> Explore the DrawDB selection system architecture. Discover how React context enables single and multi-element focus across canvas, sidebar, and toolbar components via the useSelect hook.

- Repository: [drawDB/drawdb](https://github.com/drawdb-io/drawdb)
- Tags: architecture
- Published: 2026-08-14

---

**DrawDB implements a centralized selection system for diagram elements using a React context provider that maintains single-element focus and multi-selection arrays, exposing them through a lightweight `useSelect` hook consumed across canvas, sidebar, and toolbar components.**

The selection system for diagram elements in drawdb-io/drawdb eliminates prop-drilling by storing all selection state in a top-level React context wrapped around the entire editor in [`src/pages/Editor.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/pages/Editor.jsx). By maintaining both individual element focus and bulk selection arrays in a single source of truth, the application ensures that canvas components, sidebar panels, and toolbar controls synchronize automatically when users interact with tables, relationships, notes, or areas.

## Core Architecture: SelectContext and State Management

The architecture centers on `SelectContext`, defined in [`src/context/SelectContext.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/context/SelectContext.jsx), which exports the `SelectContextProvider` component. This provider wraps the application and maintains two distinct pieces of state that drive the entire UI.

### Single Element Selection State

The `selectedElement` object tracks the currently active element using enums defined in [`src/data/constants.js`](https://github.com/drawdb-io/drawdb/blob/main/src/data/constants.js). When populated, it contains:

```js
{
  element: ObjectType.TABLE,   // type from constants.js (TABLE, AREA, NOTE, etc.)
  id: 42,                      // unique identifier from the data model
  open: true,                  // controls pop-over/sidesheet visibility
  openFromToolbar: false,      // flag for toolbar-triggered selections
  currentTab: Tab.TABLES,      // active sidebar tab enumeration
  // additional UI-specific flags
}

```

### Multi-Selection State

For bulk operations, the provider maintains `bulkSelectedElements`, an array of element IDs. This array powers features like multi-delete or batch moves when users hold `Shift` or `Ctrl` while clicking canvas elements. Operations iterate over this array before the component clears it via `setBulkSelectedElements([])`.

## The useSelect Hook

Located in [`src/hooks/useSelect.js`](https://github.com/drawdb-io/drawdb/blob/main/src/hooks/useSelect.js), this hook is a minimal wrapper around `useContext(SelectContext)`. Any component needing selection access calls:

```js
const { selectedElement, setSelectedElement, bulkSelectedElements, setBulkSelectedElements } = useSelect();

```

This pattern ensures that [`Table.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Table.jsx), [`ControlPanel.jsx`](https://github.com/drawdb-io/drawdb/blob/main/ControlPanel.jsx), and [`TablesTab.jsx`](https://github.com/drawdb-io/drawdb/blob/main/TablesTab.jsx) all read from the same state without intermediate prop passing, regardless of their depth in the component tree.

## How Components Consume Selection State

### Canvas Components

SVG-based canvas elements in [`src/components/EditorCanvas/Table.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/EditorCanvas/Table.jsx), [`Relationship.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Relationship.jsx), [`Note.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Note.jsx), and [`Area.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Area.jsx) check `selectedElement` to render selection outlines and handle click events. When a user clicks a table, the component calls `setSelectedElement()` with the table's ID and type, triggering a synchronized re-render across all consumers.

### Sidebar Panels

Components like [`src/components/EditorSidePanel/TablesTab.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/EditorSidePanel/TablesTab.jsx), [`RelationshipsTab.jsx`](https://github.com/drawdb-io/drawdb/blob/main/RelationshipsTab.jsx), and [`AreasTab.jsx`](https://github.com/drawdb-io/drawdb/blob/main/AreasTab.jsx) use the selection state to highlight the currently chosen item in lists and to drive property editing interfaces. The sidebar reacts to `currentTab` values in the selection object to display the appropriate editing interface for the active element type.

### Toolbar Controls

[`src/components/EditorHeader/ControlPanel.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/EditorHeader/ControlPanel.jsx) and related header components enable or disable action buttons based on selection validity. For example, "Delete Table" buttons check whether `selectedElement` contains a valid table ID and whether `bulkSelectedElements` contains items before allowing execution.

## Selection Interaction Flow

The system follows a unidirectional data flow:

1. **User Interaction**: A click handler in [`Table.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Table.jsx) or similar calls `setSelectedElement()` with an object containing `element`, `id`, and UI flags like `open` and `currentTab`.

2. **Context Update**: The `SelectContextProvider` updates its internal state, causing React to re-render all subscribing components.

3. **UI Response**: Canvas components display selection borders, side panels load element-specific data, and toolbars enable context-specific actions based on the new state.

4. **Bulk Operations**: When users multi-select, individual click handlers add or remove IDs from `bulkSelectedElements`. Operations like move or delete iterate over this array before clearing it.

## Practical Implementation Examples

### Selecting a Single Table

Inside [`src/components/EditorCanvas/Table.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/EditorCanvas/Table.jsx), selection updates work as follows:

```jsx
import useSelect from "../../hooks/useSelect";
import { ObjectType, Tab } from "../../data/constants";

function Table({ tableData }) {
  const { selectedElement, setSelectedElement } = useSelect();

  const onClick = () => {
    setSelectedElement({
      element: ObjectType.TABLE,
      id: tableData.id,
      open: true,
      currentTab: Tab.TABLES,
      openFromToolbar: false,
    });
  };

  return (
    <g onClick={onClick} /* …other SVG props… */>
      {/* rendering logic */}
    </g>
  );
}

```

### Performing Bulk Deletes

Toolbar components in header files handle multi-selection operations using the bulk array:

```jsx
import useSelect from "../../hooks/useSelect";
import { Action } from "../../data/constants";

function DeleteSelectedButton() {
  const { bulkSelectedElements, setBulkSelectedElements } = useSelect();

  const handleDelete = () => {
    // dispatch a delete action for each id in bulkSelectedElements
    bulkSelectedElements.forEach(id => deleteElementById(id));
    setBulkSelectedElements([]);
  };

  return (
    <button disabled={bulkSelectedElements.length === 0} onClick={handleDelete}>
      Delete ({bulkSelectedElements.length})
    </button>
  );
}

```

### Programmatic Selection from Toolbar

Components can trigger selection to open side panels programmatically:

```jsx
import useSelect from "../../hooks/useSelect";
import { ObjectType, Tab } from "../../data/constants";

function OpenNoteFromToolbar(noteId) {
  const { setSelectedElement } = useSelect();

  const open = () => {
    setSelectedElement({
      element: ObjectType.NOTE,
      id: noteId,
      open: true,
      currentTab: Tab.NOTES,
      openFromToolbar: true,
    });
  };

  return <button onClick={open}>Edit Note</button>;
}

```

## Summary

- **`SelectContext`** in [`src/context/SelectContext.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/context/SelectContext.jsx) provides the single source of truth for diagram element selection state in DrawDB.
- State splits into `selectedElement` (single focus) and `bulkSelectedElements` (multi-select array) to support both individual editing and batch operations.
- The **`useSelect`** hook in [`src/hooks/useSelect.js`](https://github.com/drawdb-io/drawdb/blob/main/src/hooks/useSelect.js) eliminates prop-drilling by exposing selection functions to any component in the tree.
- **Canvas components** like [`Table.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Table.jsx) update selection on user clicks, while **sidebar panels** and **toolbar controls** react to state changes to highlight items and enable actions.
- The system relies on `ObjectType` and `Tab` enums from [`src/data/constants.js`](https://github.com/drawdb-io/drawdb/blob/main/src/data/constants.js) to maintain type safety across the selection payload.

## Frequently Asked Questions

### How does DrawDB distinguish between single and multiple element selection?

DrawDB maintains two separate state variables in `SelectContext`: `selectedElement` for the individually focused item that drives side panels, and `bulkSelectedElements` for the array of IDs used in batch operations. Single clicks update the object, while Shift or Ctrl clicks modify the array.

### What determines the shape of the selection state object?

The selection payload structure follows the `ObjectType` and `Tab` enums defined in [`src/data/constants.js`](https://github.com/drawdb-io/drawdb/blob/main/src/data/constants.js). Every selection object must include an `element` type, unique `id`, and UI flags like `open` and `currentTab` to coordinate focus between the canvas and sidebar components.

### Why does the selection system use a React context instead of local state?

By wrapping the editor in `SelectContextProvider` at [`src/pages/Editor.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/pages/Editor.jsx), DrawDB avoids prop-drilling through SVG canvas hierarchies and sidebar trees. This ensures deeply nested [`Table.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Table.jsx) components and distant toolbar buttons access identical selection data without intermediate props.

### How do canvas components visualize the currently selected element?

Components like [`Table.jsx`](https://github.com/drawdb-io/drawdb/blob/main/Table.jsx) import `useSelect` and compare their own ID against `selectedElement.id`. When matched, they render selection outlines, while the `open` flag controls pop-over visibility for inline editing directly on the diagram canvas.