# `Plushie.Widget`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.6.0/lib/plushie/widget.ex#L1)

Macro-based DSL for declaring Plushie widgets.

Supports two kinds of widget:

- `:native_widget` -- backed by a Rust crate implementing the
  `WidgetExtension` trait. Requires `rust_crate` and `rust_constructor`
  declarations.
- `:widget` -- pure Elixir widget. Features are detected at compile
  time based on what callbacks are defined:
  - Has `state` declarations -> stateful (deferred view, state
    persistence via the runtime).
  - No `state` -> stateless (immediate view in `new/2`).
  - Has `handle_event/2` -> participates in event dispatch.
  - Has `subscribe/2` -> widget-scoped subscriptions.

## Usage

    defmodule MyApp.Gauge do
      use Plushie.Widget, :native_widget

      widget :gauge

      prop :value, :number
      prop :min, :number, default: 0
      prop :max, :number, default: 100
      prop :color, :color, default: :blue
      prop :width, :length
      prop :height, :length

      rust_crate "native/my_gauge"
      rust_constructor "my_gauge::GaugeWidget::new()"

      event :value_changed, data: [value: :number]
      command :set_value, value: :number
    end

## Generated code

The macro generates:

- `type_names/0` -- returns `[:gauge]` (from the `widget` declaration)
- `native_crate/0` -- returns the `rust_crate` path (native_widget only)
- `rust_constructor/0` -- returns the Rust expression (native_widget only)
- `new/2` -- creates a `%Module{}` struct ()
- Setter functions per prop for pipeline composition
- `with_options/2` -- applies keyword options via setters
- `build/1` -- converts the struct to a `ui_node()` map
- `@type t`, `@type option` -- typespecs for dialyzer
- `Plushie.Widget` protocol implementation
- `__event_specs__/0`, `__event_spec__/1` -- typed event metadata
- Command functions (native_widget only) that wrap
  `Plushie.Command.widget_command/3`

## Prop types

Supported prop types. Values are stored raw; `Tree.normalize/1`
handles wire encoding in a single pass.

- `:number`, `:string`, `:boolean` -- pass through
- `:color` -- normalized via `Plushie.Type.Color.cast/1` (input casting)
- `:length` -- pass through (encoded by `Tree.normalize`)
- `:padding` -- pass through (encoded by `Tree.normalize`)
- `:alignment` -- pass through (encoded by `Tree.normalize`)
- `:font` -- pass through
- `:style` -- pass through (atom or StyleMap)
- `:atom` -- pass through (encoded by `Tree.normalize`)
- `:map`, `:any` -- pass through
- `{:list, _}` -- pass through

## Composite widgets

If the using module defines `view/2` (leaf) or `view/3` (container),
`new/2` delegates to it after resolving props:

    defmodule MyApp.LabeledInput do
      use Plushie.Widget

      widget :labeled_input

      prop :label, :string

      def view(id, props) do
        import Plushie.UI
        column id: id do
          text(props.label)
        end
      end
    end

### view/2 vs view/3

Use `view/2` for simple widgets:

    def view(id, props) do
      %{id: id, type: "text", props: %{content: props.label}, children: []}
    end

Use `view/3` when the widget has state (declared via `state`).
The third argument is the widget's internal state map:

    def view(id, props, state) do
      fill = if state.hover, do: "#ff0", else: "#ccc"
      ...
    end

## Special options

All widgets automatically support:

- `:a11y` -- accessibility overrides (see `Plushie.Type.A11y`)
- `:event_rate` -- maximum events per second for coalescable events
  from this widget (see the event throttling design doc)

These do not need to be declared via `prop` -- they are always
available on `new/2`.

# `child`

```elixir
@type child() :: ui_node() | struct()
```

A child element: either an already-resolved node map or a widget struct.

# `ui_node`

```elixir
@type ui_node() :: Plushie.Widget.WidgetProtocol.ui_node()
```

A UI tree node map. Every widget builder returns this shape.

# `native_crate`
*optional* 

```elixir
@callback native_crate() :: String.t()
```

Path to the Rust crate relative to the package root.

# `rust_constructor`
*optional* 

```elixir
@callback rust_constructor() :: String.t()
```

Full Rust constructor expression for the widget.

# `type_names`

```elixir
@callback type_names() :: [atom()]
```

Node type atoms this widget handles.

# `command`
*macro* 

Declares a command (native_widget only) with optional typed params.

# `event`
*macro* 

Declares a typed event emitted by a native or canvas widget.

Supports three forms:

## No payload

    event :cleared

## Typed value (goes in `WidgetEvent.value`)

    event :select, value: :number

## Structured data (goes in `WidgetEvent.data` with atom keys)

    event :change, data: [hue: :number, saturation: :number]

## Block form

    event :change do
      data do
        field :hue, :number
        field :saturation, :number
      end
    end

`value:` and `data:` are mutually exclusive.

Type identifiers can be built-in atoms (`:number`, `:string`,
`:boolean`, `:any`) or modules implementing `Plushie.Event.EventType`.

# `prop`
*macro* 

Declares a prop with name, type, and optional default.

# `rust_constructor`
*macro* 

Declares the Rust constructor expression (native_widget only).

# `rust_crate`
*macro* 

Declares the path to the Rust crate (native_widget only).

# `state`
*macro* 

Declares internal state fields for a stateful widget.

State fields are managed by the runtime, not the app model.
They persist across renders and are passed to `view/3` and
`handle_event/2`. Declaring state fields makes the widget
stateful: the view is deferred to tree normalization and the
`WidgetHandler` behaviour is injected automatically.

    state hover: nil, drag: :none, animation_progress: 0.0

# `to_node`

```elixir
@spec to_node(struct()) :: ui_node()
```

Converts a widget struct to a `ui_node()` map via the WidgetProtocol.

# `widget`
*macro* 

Declares the widget type name. Pass `container: true` for container widgets.

---

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