# InteractionController

The `InteractionController` class is the core unit for managing interactions on an element. It handles the connection between DOM elements and the interaction system, manages state, and coordinates with the `Interact` class.

## Overview

The `InteractionController` is used internally by:

- The `interact-element` custom element (web approach)
- The `Interaction` component and `createInteractRef` (React approach)
- Direct DOM manipulation via `add()` and `remove()` functions

In most cases, you won't need to instantiate this class directly, as it's created automatically by the higher-level APIs.

## Import

```typescript
// The controller is created internally; you typically access it via:
import { Interact } from '@wix/interact/web';

const controller = Interact.getController('my-element');
```

## Interface

```typescript
interface IInteractionController {
  element: HTMLElement;
  key: string | undefined;
  connected: boolean;
  sheet: CSSStyleSheet | null;
  useFirstChild: boolean;

  connect(key?: string): void;
  disconnect(options?: { removeFromCache?: boolean }): void;
  update(): void;
  toggleEffect(
    effectId: string,
    stateAction: StateAction,
    item?: HTMLElement | null,
    isLegacy?: boolean,
  ): void;
  getActiveEffects(): string[];
  renderStyle(cssRules: string[]): void;
  watchChildList(listContainer: string): void;
}
```

## Properties

### `element: HTMLElement`

The DOM element this controller manages.

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller) {
  console.log('Managing element:', controller.element);
  console.log('Tag name:', controller.element.tagName);
}
```

### `key: string | undefined`

The unique identifier for this element's interactions.

**Example:**

```typescript
const controller = Interact.getController('hero');
if (controller) {
  console.log('Key:', controller.key); // 'hero'
}
```

### `connected: boolean`

Whether the controller is currently connected to the interaction system.

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller?.connected) {
  console.log('Controller is active and listening for events');
} else {
  console.log('Controller is not connected');
}
```

### `sheet: CSSStyleSheet | null`

The adopted stylesheet for dynamic CSS rules generated by interactions.

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller?.sheet) {
  console.log('CSS rules count:', controller.sheet.cssRules.length);
}
```

### `useFirstChild: boolean`

When `true`, the interaction target is the first child (e.g. with `<interact-element>`). Set via constructor options.

## Methods

### `connect(key?: string)`

Connects the controller to the interaction system, setting up all event listeners and effects.

**Signature:**

```typescript
connect(key?: string): void
```

**Parameters:**

- `key?: string` - Optional key override. If not provided, uses `this.key` or `element.dataset.interactKey`

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller && !controller.connected) {
  controller.connect();
}
```

**Behavior:**

1. Returns early if already connected
2. Resolves the key from parameter, instance property, or data attribute
3. Logs warning if no key is found
4. Calls internal `add()` function to set up interactions
5. Sets `connected = true` on success

### `disconnect(options?)`

Disconnects the controller and cleans up resources.

**Signature:**

```typescript
disconnect(options?: { removeFromCache?: boolean }): void
```

**Parameters:** `options.removeFromCache` - When `true`, removes the controller from `Interact.controllerCache` (e.g. when calling `remove(key)`).

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller?.connected) {
  controller.disconnect({ removeFromCache: true });
}
```

**Behavior:** Calls internal `remove()`, removes adopted stylesheet, clears observers, sets `connected = false`.

### `update()`

Disconnects and reconnects the controller, effectively refreshing all interactions.

**Signature:**

```typescript
update(): void
```

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller) {
  // After changing configuration
  controller.update();
}
```

**Use Cases:**

- After dynamically changing interaction configuration
- To reset all interaction state
- After modifying the element's structure

### `toggleEffect(effectId, stateAction, item?, isLegacy?)`

Toggles a CSS state effect on the element or a specific list item.

**Signature:**

```typescript
toggleEffect(
  effectId: string,
  stateAction: StateAction,
  item?: HTMLElement | null,
  isLegacy?: boolean
): void
```

**Parameters:**

- `effectId: string` - The effect identifier
- `stateAction` - How to change the state:
  - `'add'` - Add the effect
  - `'remove'` - Remove the effect
  - `'toggle'` - Toggle the effect on/off
  - `'clear'` - Clear all effects
- `item?: HTMLElement | null` - Specific element for list item effects; `null` skips the operation
- `isLegacy?: boolean` - Internal flag for legacy syntax handling

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller) {
  // Add an effect
  controller.toggleEffect('active', 'add');

  // Toggle an effect
  controller.toggleEffect('expanded', 'toggle');

  // Remove an effect
  controller.toggleEffect('active', 'remove');

  // Clear all effects
  controller.toggleEffect('', 'clear');

  // Toggle effect on a specific list item
  const listItem = document.querySelector('.list-item');
  controller.toggleEffect('selected', 'add', listItem);
}
```

**Behavior:**

1. Returns early if `item === null` (explicit skip)
2. If element is an `IInteractElement` with custom states, delegates to it
3. Otherwise, manages effects via `data-interact-effect` attribute
4. Effects are stored as space-separated values

### `getActiveEffects(): string[]`

Returns an array of currently active effect IDs on this element.

**Signature:**

```typescript
getActiveEffects(): string[]
```

**Returns:** `string[]` - Array of active effect identifiers

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller) {
  const effects = controller.getActiveEffects();
  console.log('Active effects:', effects);
  // e.g., ['hover', 'expanded', 'selected']

  // Check for specific effect
  if (effects.includes('expanded')) {
    console.log('Element is expanded');
  }
}
```

### `renderStyle(cssRules: string[])`

Renders CSS rules to the controller's adopted stylesheet.

**Signature:**

```typescript
renderStyle(cssRules: string[]): void
```

**Parameters:**

- `cssRules: string[]` - Array of CSS rule strings to add

**Example:**

```typescript
const controller = Interact.getController('my-element');
if (controller) {
  controller.renderStyle(['.my-element { opacity: 1; }', '.my-element.hidden { opacity: 0; }']);
}
```

**Behavior:**

1. If no stylesheet exists, creates one and adds it to `document.adoptedStyleSheets`
2. If stylesheet exists, appends rules at the end
3. Logs errors for invalid CSS rules

### `watchChildList(listContainer: string)`

Sets up a mutation observer to watch for child additions/removals in a list container.

**Signature:**

```typescript
watchChildList(listContainer: string): void
```

**Parameters:**

- `listContainer: string` - CSS selector for the container to watch

**Example:**

```typescript
const controller = Interact.getController('product-list');
if (controller) {
  // Start watching for changes in .products container
  controller.watchChildList('.products');

  // Now any DOM changes are automatically handled
  const container = controller.element.querySelector('.products');

  // Adding items - interactions applied automatically
  const newItem = document.createElement('div');
  container?.appendChild(newItem);

  // Removing items - cleanup happens automatically
  container?.querySelector('.old-item')?.remove();
}
```

**Behavior:**

1. Finds the list container within the controller's element
2. Creates a MutationObserver if one doesn't exist for this container
3. Observers are stored in WeakMap to prevent duplicates
4. Automatically calls `addListItems` for added nodes
5. Automatically calls `removeListItems` for removed nodes

## Usage Patterns

### Accessing Controllers

```typescript
import { Interact } from '@wix/interact/web';

// Get a controller by key
const controller = Interact.getController('my-element');

// Iterate all controllers
Interact.controllerCache.forEach((controller, key) => {
  console.log(`${key}: connected=${controller.connected}`);
});
```

### State and lists

```typescript
// Toggle effect by current state
const c = Interact.getController('accordion');
c?.toggleEffect('expanded', c.getActiveEffects().includes('expanded') ? 'remove' : 'add');

// Watch list container; new children get interactions automatically
Interact.getController('list-key')?.watchChildList('.items');
```

### Refreshing Interactions

```typescript
// After configuration change
const instance = Interact.getInstance('my-element');
if (instance) {
  // Update configuration
  instance.init(newConfig);

  // Refresh the controller
  const controller = Interact.getController('my-element');
  controller?.update();
}
```

## Lifecycle

### Creation Flow

1. `add(element, key)` is called (or element connects)
2. `InteractionController` is instantiated
3. `connect()` is called
4. Controller is cached in `Interact.controllerCache`
5. Interactions are set up via internal `add()` function

### Destruction Flow

1. `remove(key)` is called (or element disconnects)
2. `disconnect()` is called on the controller
3. All event listeners are removed
4. Stylesheet is removed from document
5. Observers are cleared
6. Controller is removed from cache

## Error Handling

```typescript
const controller = Interact.getController('my-element');

// Safe operations - won't throw
controller?.toggleEffect('effect', 'add');
controller?.connect(); // Safe if already connected
controller?.disconnect({}); // Safe if already disconnected

// Check connection status
if (!controller?.connected) {
  console.warn('Controller not connected');
}
```

## TypeScript Support

```typescript
import { Interact, IInteractionController } from '@wix/interact/web';

const controller: IInteractionController | undefined = Interact.getController('my-element');

if (controller) {
  // Full type support
  const effects: string[] = controller.getActiveEffects();
  const isConnected: boolean = controller.connected;
  const element: HTMLElement = controller.element;
}
```

## See Also

- [Interact Class](interact-class.md) - Main class API
- [interact-element](interact-element.md) - Custom element that uses controllers
- [Functions](functions.md) - `add()` and `remove()` functions
- [React Integration](../integration/react.md) - React approach to interactions
- [Type Definitions](types.md) - `IInteractionController` interface
