diff --git a/docs/site/concepts/events.md b/docs/site/concepts/events.md
index 88f66f6..dc0ec8c 100644
--- a/docs/site/concepts/events.md
+++ b/docs/site/concepts/events.md
@@ -75,6 +75,15 @@ Node drag emits a sequence of `node` + position events. Dragging is controlled b
| `NODE_DRAG` | `node`, position, `event` | The node moves during a drag. |
| `NODE_DRAG_END` | `node`, position, `event` | The drag ends. |
+Dragging the empty background emits a separate, subject-less sequence, off by default and
+enabled with `interaction.backgroundDrag` - see [Selection & interaction](/concepts/interaction).
+
+| Event | Payload | Fires when |
+| --- | --- | --- |
+| `BACKGROUND_DRAG_START` | position, `event` | A background drag begins (modifier held, no node hit). |
+| `BACKGROUND_DRAG` | position, `event` | The cursor moves during a background drag. |
+| `BACKGROUND_DRAG_END` | position, `event` | The background drag ends. |
+
## Examples
### A tooltip that follows the cursor
diff --git a/docs/site/concepts/interaction.md b/docs/site/concepts/interaction.md
index 7b3a52b..57684d1 100644
--- a/docs/site/concepts/interaction.md
+++ b/docs/site/concepts/interaction.md
@@ -35,10 +35,19 @@ const orb = new OrbView(container, {
interaction: {
isDragEnabled: true, // drag nodes (default: true)
isZoomEnabled: true, // scroll to zoom, drag background to pan (default: true)
+ backgroundDrag: {
+ isEnabled: false, // emit background-drag events on a modifier + drag (default: false)
+ modifier: 'shift', // 'shift' | 'ctrl' | 'alt' | 'meta' | null (default: 'shift')
+ },
},
});
```
+- **Background drag** - off by default. When enabled, dragging the empty background with the
+ modifier held emits neutral `BACKGROUND_DRAG_*` [events](/concepts/events) instead of
+ panning; a plain drag still pans. It's the gesture [rectangle selection](#rectangle-selection)
+ is built on, and is equally usable for custom box-zoom or lasso.
+
To disable Orb's built-in selection entirely and handle it yourself, turn off the strategy
flags and drive state from [events](/concepts/events).
@@ -80,6 +89,12 @@ orb.interaction.unselectNodeById(1);
orb.interaction.unselectEdgeById(10);
orb.interaction.unselectAll();
+// Select many at once (non-cascading by default), returns the matched count
+orb.interaction.selectNodesByIds([1, 2, 3]);
+orb.interaction.unselectNodesByIds([1, 2, 3]);
+orb.interaction.selectEdgesByIds([10, 11]);
+orb.interaction.unselectEdgesByIds([10, 11]);
+
// Hover
orb.interaction.hoverNodeById(1);
orb.interaction.hoverEdgeById(10);
@@ -107,6 +122,69 @@ searchInput.addEventListener('change', (e) => {
});
```
+## Rectangle selection
+
+Selecting a whole region at once - drag a box, select the nodes inside - ships as an opt-in
+module, `@memgraph/orb/interactions`, kept out of the core bundle so you only pay for it when
+you use it.
+
+
+
+It takes **two steps**: enable the background-drag gesture on the view, then attach a
+`RectangleSelection` to it.
+
+```typescript
+import { OrbView } from '@memgraph/orb';
+import { RectangleSelection } from '@memgraph/orb/interactions';
+
+const orb = new OrbView(container, {
+ interaction: { backgroundDrag: { isEnabled: true, modifier: 'shift' } },
+});
+
+const selection = new RectangleSelection(orb);
+selection.on('select', ({ nodes, area, mode }) => {
+ // nodes are now selected; mode is 'add' or 'replace'. Want edges too? You have the
+ // nodes, so select whichever edges you like - e.g. those fully inside the box:
+ const ids = new Set(nodes.map((n) => n.getId()));
+ const edges = orb.data.getEdges((e) => ids.has(e.startNode?.getId()) && ids.has(e.endNode?.getId()));
+ orb.interaction.selectEdgesByIds(edges.map((e) => e.getId()));
+ orb.render();
+});
+```
+
+By default, **Shift-drag** over the empty background draws the box and replaces the
+selection with the nodes inside (like a fresh marquee); holding **Ctrl/Cmd** as well adds
+to the current selection instead. A too-small drag counts as a click and leaves the
+selection untouched. Dragging a node still moves it, and a plain drag still pans. Call
+`selection.destroy()` to detach it.
+
+::: warning Requires background drag
+`RectangleSelection` only listens - it does not enable the gesture. If
+`interaction.backgroundDrag.isEnabled` is not set on the view, attaching it does nothing and
+Shift-drag is a no-op.
+:::
+
+`RectangleSelection` accepts `IRectangleSelectionOptions`:
+
+| Option | Type | Default |
+| --- | --- | --- |
+| `resolveMode` | `(event: MouseEvent) => 'add' \| 'replace'` | ctrl/meta → `add`, else `replace` |
+| `style` | `Partial` | dashed blue overlay |
+
+The overlay element carries the `orb-selection-rectangle` class, so you can also style it
+from CSS.
+
+The module is built entirely on public API, so the same primitives are available if you want
+a different gesture (lasso, custom modifiers):
+
+```typescript
+import { RectangleArea } from '@memgraph/orb';
+
+const area = new RectangleArea({ x, y, width, height });
+const nodes = orb.data.getNodesInArea(area); // nodes whose center is inside
+orb.interaction.selectNodesByIds(nodes.map((n) => n.getId()));
+```
+
## Dimming the rest of the graph
On selection or hover, Orb dims everything else so the focus stands out. That transparency
diff --git a/docs/site/public/demos/rectangle-selection.html b/docs/site/public/demos/rectangle-selection.html
new file mode 100644
index 0000000..7385d25
--- /dev/null
+++ b/docs/site/public/demos/rectangle-selection.html
@@ -0,0 +1,169 @@
+
+
+
+
+
+ Orb - rectangle selection demo
+
+
+
+
+
+