# Interaction

Import these from `@grafloria/engine`.

## Functions

### `areSiblingLanes`

True when `from` and `to` are two lanes of the same pool.

```ts
function areSiblingLanes(diagram: DiagramModel, from: GroupModel, to: GroupModel | undefined): boolean
```

### `clampBoxInto`

The top-left that keeps a `width`×`height` box inside `rect` while moving it
as little as possible. A box larger than the rect pins to its top-left.

```ts
function clampBoxInto(
  rect: GroupRect,
  x: number,
  y: number,
  width: number,
  height: number
): { x: number; y: number }
```

### `containingGroup`

The group that directly holds `nodeId` as a member, if any.

```ts
function containingGroup(diagram: DiagramModel, nodeId: string): GroupModel | undefined
```

### `laneAtPoint`

The lane whose band contains `point`, or undefined outside every lane.

```ts
function laneAtPoint(
  diagram: DiagramModel,
  pool: GroupModel,
  point: { x: number; y: number }
): GroupModel | undefined
```

### `lanesOfPool`

A pool's lanes in band order.

```ts
function lanesOfPool(diagram: DiagramModel, pool: GroupModel): GroupModel[]
```

### `matchesRule`

Evaluate a declarative rule against a node's `data`. Pure + serializable.

```ts
function matchesRule(rule: MembershipRule, node: NodeModel): boolean
```

### `memberConfinement`

The rectangle a dragged member must stay inside, or null when nothing
confines it. See the module note for the rule.

```ts
function memberConfinement(diagram: DiagramModel, nodeId: string): GroupRect | null
```

### `poolOfLane`

The pool a lane belongs to (its parent group with the pool role).

```ts
function poolOfLane(diagram: DiagramModel, lane: GroupModel): GroupModel | undefined
```

## Classes

### `GroupCollapseService`

```ts
class GroupCollapseService
```

**Methods**

- `constructor(private readonly diagram: DiagramModel)`
- `collapse(group: GroupModel, options?: CollapseOptions): void` — Collapse `group`: hide members, save layout, re-home boundary edges to an
aggregated proxy, and shrink the group to a placeholder. No-op if the group
is already collapsed. Members-less groups collapse "lightly" (flag only, no
placeholder) so trivial groups don't spawn stray nodes.
- `expand(group: GroupModel): void` — Expand `group` back to exactly its pre-collapse state using the snapshot on
the group. No-op if the group is not collapsed / has no snapshot.

### `GroupMembershipService`

```ts
class GroupMembershipService
```

**Methods**

- `constructor(options: GroupMembershipServiceOptions)`
- `refresh(): void` — Rebuild the spatial index from the diagram's current groups. Group counts
are small relative to nodes, so a full refresh per interaction is cheap and
always reflects freshly-dragged geometry.
- `hitTestGroup(point: Point, options?: HitTestOptions): GroupModel | undefined` — Hit-test a point against group rectangles using the spatial index and
return the innermost matching group (deepest nesting, then smallest area),
so a nested child wins over its parent. Returns undefined when the point is
outside every group.
- `getContainingGroup(entityId: string): GroupModel | undefined` — Return the first group that currently contains `entityId` as a direct
member, or undefined. (Membership check over the small group set — this is
not the spatial hit-test.)
- `updateHover(point: Point, options?: HitTestOptions): GroupModel | undefined` — Highlight the group under the cursor during a drag and clear any previously
hovered group. Returns the hovered group (or undefined). Drives the group's
'hover:changed' emitter so a renderer can outline the drop target.
- `clearHover(): void` — Clear any active hover highlight (call on drag-end / cancel).
- `planNodeDrop(nodeId: string, point: Point): DropResult` — Decide what a drop at `point` does to what contains `nodeId` — WITHOUT doing
it: the commands that leave the current group and/or join the one under the
point, in order. A host that records the drop as ONE undo step folds them
into the move's own command, runs it, then calls {@link finishDrop}. No
commands when the node stays where it is or the drop is vetoed (`rejected`:
a confining group, or the target's validation/cycle rules).
- `finishDrop(plan: DropResult): void` — After a planned drop's commands have run: refresh both groups' derived
bounds (one gained a member, one lost one) and clear the hover highlight.
- `async handleNodeDragEnd(nodeId: string, point: Point): Promise<DropResult>` — Handle a node drag-end at `point`: re-parent the node into the group under
the cursor, or unembed it when dropped outside every group. No-op when the
node is already in the target group. Rejected (no change) when the target
group's validation/cycle rules veto the node. Each command is dispatched on
its own — see {@link planNodeDrop} to record the drop as one step instead.

### `SemanticMembershipService`

```ts
class SemanticMembershipService
```

**Methods**

- `constructor(private readonly diagram: DiagramModel)`
- `register(group: GroupModel): () => void` — Start managing `group`'s auto-membership from its serialized
`membershipRule`. Runs an initial sweep and then keeps in sync reactively. No-op if the group has no rule. Returns a disposer that stops managing it.
- `dispose(): void` — Stop managing every group and detach all model listeners.
- `evaluateNode(node: NodeModel): void` — Re-evaluate a single node against every managed rule-group.
- `evaluateGroup(group: GroupModel): void` — Re-evaluate every node against one group's rule.

### `SwimlaneService`

```ts
class SwimlaneService
```

**Methods**

- `constructor(private readonly diagram: DiagramModel)`
- `createPool(options: CreatePoolOptions): Pool` — Create a pool group with N lane bands and tile them.
- `addLane(pool: GroupModel, spec: LaneSpec, atIndex?: number): GroupModel` — Add a lane to a pool (optionally at an index) and re-tile.
- `removeLane(pool: GroupModel, laneId: string): void` — Remove a lane from a pool and re-tile the survivors.
- `resizeLane(pool: GroupModel, laneId: string, crossSize: number): void` — Resize a lane along the cross axis by pinning its band (fixedSize) and
re-laying out the pool so siblings absorb the remaining space.
- `resizePool(pool: GroupModel, bounds: GroupRect): void` — Resize the whole pool and re-tile its lanes to the new frame.
- `getLanes(pool: GroupModel): GroupModel[]` — Lanes of a pool in band order.
- `isPool(group: GroupModel): boolean` — Is this group a pool?
- `isLane(group: GroupModel): boolean` — Is this group a lane?
- `reflow(pool: GroupModel): void` — Re-tile a pool's lanes into bands. fixedSize lanes take their pixel size;
the rest split the remaining cross-axis space by weight. Each lane frame is
set (drop-in hit-testing + drag constraints follow automatically).

## Constants

### `PROXY_LINK_GROUP_KEY`

Metadata key stamped on a re-homed proxy link.

```ts
const PROXY_LINK_GROUP_KEY: "__proxyForGroup"
```

### `PROXY_NODE_GROUP_KEY`

Metadata key stamped on a placeholder node so it can be recognised/filtered.

```ts
const PROXY_NODE_GROUP_KEY: "__collapsedGroupId"
```

## Interfaces

### `CollapseOptions`

```ts
interface CollapseOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `proxyLabel?` | `(info: ProxyLabelInfo) => string` |  | Label for each aggregated proxy link. Default: the aggregated edge count as a string when >1 (single crossings get no synthetic label). Return '' to suppress. |

### `CommandDispatcher`

Minimal command dispatcher contract (satisfied by CommandManager). Injecting
this keeps membership changes on the shared undo stack.

```ts
interface CommandDispatcher
```

**Members**

- `execute(command: Command): Promise<void> | void`

### `CreatePoolOptions`

```ts
interface CreatePoolOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id?` | `string` |  |  |
| `name` | `string` |  |  |
| `orientation` | `LaneOrientation` |  |  |
| `bounds` | `GroupRect` |  | Pool frame in world coords. |
| `lanes` | `LaneSpec[]` |  |  |
| `headerSize?` | `number` |  | Title-band thickness along the main-axis start (left for horizontal). |
| `laneHeaderSize?` | `number` |  | Reserve a header band inside each lane. Default 0. |

### `DropResult`

Outcome of a node drag-end reparent attempt.

```ts
interface DropResult
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `nodeId` | `string` |  |  |
| `fromGroupId?` | `string` |  | Group the node left (if it was a member of one). |
| `toGroupId?` | `string` |  | Group the node joined (undefined when dropped outside all groups). |
| `commands` | `Command[]` |  | The membership commands, in order (empty when nothing changes). |
| `changed` | `boolean` |  | Whether membership actually changed. |
| `rejected` | `boolean` |  | A target group was under the cursor but rejected the node. |

### `GroupMembershipServiceOptions`

```ts
interface GroupMembershipServiceOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `diagram` | `DiagramModel` |  |  |
| `dispatcher?` | `CommandDispatcher` |  | Dispatcher for undoable membership changes (typically a CommandManager). When omitted, commands are executed directly against the diagram (still correct, but not tracked for undo/redo). |
| `cellSize?` | `number` |  | Grid cell size for the group spatial index (default 100). |

### `HitTestOptions`

Options for a single hit-test.

```ts
interface HitTestOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `excludeGroupIds?` | `Set<string>` |  | Group ids to skip (e.g. the group being dragged + its descendants). |

### `LaneSpec`

```ts
interface LaneSpec
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id?` | `string` |  | Optional explicit id (else generated). |
| `name` | `string` |  |  |
| `weight?` | `number` |  | Relative cross-axis size (default 1). |
| `fixedSize?` | `number` |  | Absolute cross-axis size (pins the band, overrides weight). |

### `Pool`

```ts
interface Pool
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `pool` | `GroupModel` |  |  |
| `lanes` | `GroupModel[]` |  |  |

### `ProxyLabelInfo`

Info handed to a caller's proxy-label hook.

```ts
interface ProxyLabelInfo
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `group` | `GroupModel` |  |  |
| `externalNodeId` | `string` |  |  |
| `direction` | `'out' \| 'in'` |  | 'out' = edges flow group→external; 'in' = external→group. |
| `count` | `number` |  | How many raw edges this proxy aggregates. |

## Types

### `LaneOrientation`

Also has every member of `String`, listed on its own entry.

```ts
type LaneOrientation = 'horizontal' | 'vertical';
```
