# `Tela.Component`
[🔗](https://github.com/idabmat/tela/blob/v0.1.0/lib/tela/component.ex#L1)

Behaviour for reusable Tela UI components.

A component is a self-contained, pure widget: it owns its own state struct
and exposes three callbacks — `c:init/1`, `c:handle_event/2`, and `c:view/1`
— mirroring the shape of the top-level `Tela` behaviour. Components are
embedded in a parent model; the parent is responsible for forwarding key
events and side-effect messages to the component.

## Usage

    defmodule MyWidget do
      use Tela.Component

      @impl Tela.Component
      def init(opts), do: %{value: Keyword.get(opts, :value, "")}

      @impl Tela.Component
      def handle_event(model, _key), do: {model, nil}

      @impl Tela.Component
      def view(model), do: Tela.Frame.new(model.value)
    end

## Contract

### `c:init/1`

Receives a keyword list of options and returns the initial component model.
Returns a plain model — **not** `{model, cmd}`. Components do not fire
startup commands; the parent is responsible for any initial cmd (such as
starting a spinner tick via `Tela.Component.Spinner.tick_cmd/1`).

### `c:handle_event/2`

Receives the component model and a `Tela.Key` struct. Returns
`{new_model, cmd}`. Must be a pure function.

Display-only components (such as `Tela.Component.Spinner`) always return
`{model, nil}`.

### `c:view/1`

Receives the component model and returns a `Tela.Frame.t()`. Must be a pure
function. Compose multiple component frames with `Tela.Frame.join/2` in the
parent's `view/1`.

## Side-effect messages

Components that produce side effects (e.g. animation ticks) expose a
dedicated handler function outside the behaviour — for example,
`Tela.Component.Spinner.handle_tick/2`. The parent routes relevant messages
from `handle_info/2` to the component's handler and merges the returned cmd.

# `handle_event`

```elixir
@callback handle_event(model :: term(), key :: Tela.Key.t()) :: {term(), Tela.cmd()}
```

Called for key events forwarded by the parent.

Returns `{new_model, cmd}`. Must be a pure function.

# `init`

```elixir
@callback init(opts :: keyword()) :: model :: term()
```

Called once to initialise the component. Returns the initial model.

Receives a keyword list of options. Returns a plain model term — not a
`{model, cmd}` tuple. Any startup commands (such as an initial tick) must
be launched by the parent.

# `view`

```elixir
@callback view(model :: term()) :: Tela.Frame.t()
```

Called by the parent's `view/1` to render the component.

Returns a `Tela.Frame.t()` containing the component's rendered content and
an optional cursor position relative to the component's own top-left. Must
be a pure function.

Parents compose child frames using `Tela.Frame.join/2`, which adjusts
cursor row offsets automatically.

---

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