# `Plushie.Canvas.Interactive`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/lib/plushie/canvas/interactive.ex#L1)

Interactive canvas element.

An `interactive` is a named group that responds to user input. It
requires an explicit `id` (first positional argument) and supports
click, hover, drag, focus, and accessibility fields.

On the wire this encodes as `type: "group"` (the renderer has no
separate interactive type). The split is purely an SDK-side API
improvement: `group` is structural, `interactive` is for elements
that respond to user input.

## Example

    interactive "btn", on_click: true, cursor: "pointer" do
      rect(0, 0, 100, 40, fill: "#3498db")
    end

# `option`

```elixir
@type option() ::
  ((((((((((((((({:transforms, term()} | {:clip, term()})
                | {:on_click, boolean()})
               | {:on_hover, boolean()})
              | {:draggable, boolean()})
             | {:drag_axis, :x | :y | :both})
            | {:drag_bounds,
               %Plushie.Canvas.DragBounds{
                 max_x: term(),
                 max_y: term(),
                 min_x: term(),
                 min_y: term()
               }
               | keyword()
               | map()})
           | {:cursor, String.t() | atom()})
          | {:hit_rect,
             %Plushie.Canvas.HitRect{h: term(), w: term(), x: term(), y: term()}
             | keyword()
             | map()})
         | {:tooltip, String.t() | atom()})
        | {:hover_style,
           %Plushie.Canvas.ShapeStyle{
             fill: term(),
             opacity: term(),
             stroke: term()
           }
           | keyword()
           | map()})
       | {:pressed_style,
          %Plushie.Canvas.ShapeStyle{
            fill: term(),
            opacity: term(),
            stroke: term()
          }
          | keyword()
          | map()})
      | {:focus_style,
         %Plushie.Canvas.ShapeStyle{
           fill: term(),
           opacity: term(),
           stroke: term()
         }
         | keyword()
         | map()})
     | {:show_focus_ring, boolean()})
    | {:focus_ring_radius, number()})
   | {:a11y, term()})
  | {:focusable, boolean()}
```

# `t`

```elixir
@type t() :: %Plushie.Canvas.Interactive{
  a11y:
    term()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  children: [Plushie.Widget.ui_node()],
  clip:
    term()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  cursor:
    (String.t() | atom())
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  drag_axis:
    (:x | :y | :both)
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  drag_bounds:
    %Plushie.Canvas.DragBounds{
      max_x: term(),
      max_y: term(),
      min_x: term(),
      min_y: term()
    }
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  draggable:
    boolean()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  focus_ring_radius:
    number()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  focus_style:
    %Plushie.Canvas.ShapeStyle{fill: term(), opacity: term(), stroke: term()}
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  focusable:
    boolean()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  hit_rect:
    %Plushie.Canvas.HitRect{h: term(), w: term(), x: term(), y: term()}
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  hover_style:
    %Plushie.Canvas.ShapeStyle{fill: term(), opacity: term(), stroke: term()}
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  id: String.t() | nil,
  on_click:
    boolean()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  on_hover:
    boolean()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  pressed_style:
    %Plushie.Canvas.ShapeStyle{fill: term(), opacity: term(), stroke: term()}
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  show_focus_ring:
    boolean()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  tooltip:
    (String.t() | atom())
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil,
  transforms:
    term()
    | Plushie.Animation.Transition.t()
    | Plushie.Animation.Spring.t()
    | Plushie.Animation.Sequence.t()
    | nil
}
```

# `a11y`

```elixir
@spec a11y(widget :: t(), value :: term() | nil) :: t()
```

Accessibility annotations (role, label, etc.).

Accepts `term()`.

# `build`

```elixir
@spec build(widget :: t()) :: Plushie.Widget.ui_node()
```

Converts this widget struct to a `ui_node()` map.

# `clip`

```elixir
@spec clip(widget :: t(), value :: term() | nil) :: t()
```

Clip rectangle.

Accepts `term()`.

# `cursor`

```elixir
@spec cursor(widget :: t(), value :: (String.t() | atom()) | nil) :: t()
```

CSS cursor on hover (e.g. "pointer", "grab").

Accepts `String.t() | atom()`.

# `drag_axis`

```elixir
@spec drag_axis(widget :: t(), value :: (:x | :y | :both) | nil) :: t()
```

Constrain drag to an axis.

Accepts `:x | :y | :both`.

# `drag_bounds`

```elixir
@spec drag_bounds(
  widget :: t(),
  value ::
    (%Plushie.Canvas.DragBounds{
       max_x: term(),
       max_y: term(),
       min_x: term(),
       min_y: term()
     }
     | keyword()
     | map())
    | nil
) :: t()
```

Drag boundary constraints.

Accepts `%Plushie.Canvas.DragBounds{}`.

# `draggable`

```elixir
@spec draggable(widget :: t(), value :: boolean() | nil) :: t()
```

Enable drag events.

Accepts `boolean()`.

# `extend`

```elixir
@spec extend(widget :: t(), children :: [Plushie.Widget.child()]) :: t()
```

Appends multiple children to the widget.

# `focus_ring_radius`

```elixir
@spec focus_ring_radius(widget :: t(), value :: number() | nil) :: t()
```

Focus ring corner radius in pixels.

Accepts `number()`.

# `focus_style`

```elixir
@spec focus_style(
  widget :: t(),
  value ::
    (%Plushie.Canvas.ShapeStyle{fill: term(), opacity: term(), stroke: term()}
     | keyword()
     | map())
    | nil
) :: t()
```

Style overrides when focused.

Accepts `%Plushie.Canvas.ShapeStyle{}`.

# `focusable`

```elixir
@spec focusable(widget :: t(), value :: boolean() | nil) :: t()
```

Add to the Tab focus order.

Accepts `boolean()`.

# `hit_rect`

```elixir
@spec hit_rect(
  widget :: t(),
  value ::
    (%Plushie.Canvas.HitRect{h: term(), w: term(), x: term(), y: term()}
     | keyword()
     | map())
    | nil
) :: t()
```

Explicit hit test rectangle.

Accepts `%Plushie.Canvas.HitRect{}`.

# `hover_style`

```elixir
@spec hover_style(
  widget :: t(),
  value ::
    (%Plushie.Canvas.ShapeStyle{fill: term(), opacity: term(), stroke: term()}
     | keyword()
     | map())
    | nil
) :: t()
```

Style overrides on hover.

Accepts `%Plushie.Canvas.ShapeStyle{}`.

# `new`

```elixir
@spec new(opts :: [option()]) :: t()
```

Creates a new element without an explicit ID (auto-assigned by parent container).

# `new`

```elixir
@spec new(id :: String.t(), opts :: [option()]) :: t()
```

Creates a new element with the given ID and keyword options.

# `on_click`

```elixir
@spec on_click(widget :: t(), value :: boolean() | nil) :: t()
```

Enable click events.

Accepts `boolean()`.

# `on_hover`

```elixir
@spec on_hover(widget :: t(), value :: boolean() | nil) :: t()
```

Enable hover events.

Accepts `boolean()`.

# `pressed_style`

```elixir
@spec pressed_style(
  widget :: t(),
  value ::
    (%Plushie.Canvas.ShapeStyle{fill: term(), opacity: term(), stroke: term()}
     | keyword()
     | map())
    | nil
) :: t()
```

Style overrides when pressed.

Accepts `%Plushie.Canvas.ShapeStyle{}`.

# `push`

```elixir
@spec push(widget :: t(), child :: Plushie.Widget.child()) :: t()
```

Appends a child to the widget.

# `show_focus_ring`

```elixir
@spec show_focus_ring(widget :: t(), value :: boolean() | nil) :: t()
```

Show the default focus ring.

Accepts `boolean()`.

# `tooltip`

```elixir
@spec tooltip(widget :: t(), value :: (String.t() | atom()) | nil) :: t()
```

Tooltip text on hover.

Accepts `String.t() | atom()`.

# `transforms`

```elixir
@spec transforms(widget :: t(), value :: term() | nil) :: t()
```

List of transform descriptors.

Accepts `term()`.

# `type_name`

```elixir
@spec type_name() :: String.t()
```

Returns the element type string for the wire protocol.

# `with_options`

```elixir
@spec with_options(widget :: t(), opts :: [option()]) :: t()
```

Applies keyword options to an existing widget struct.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
