# `MishkaGervaz.Behaviours.UIAdapter`
[🔗](https://github.com/mishka-group/mishka_gervaz/blob/v0.0.1-alpha.3/lib/mishka_gervaz/behaviours/ui_adapter.ex#L1)

Behaviour for UI component adapters.

Implement this behaviour to integrate any UI library:

- Plain Tailwind CSS (default, `MishkaGervaz.UIAdapters.Tailwind`)
- A custom component library
- Database-driven dynamic components
- Anything else with the same per-component shape

## Using the macro

`use MishkaGervaz.Behaviours.UIAdapter` provides default implementations
that delegate to a fallback module (Tailwind by default). Override only
the components you need:

    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter

      # Override specific components — everything else uses Tailwind defaults.
      def button(assigns), do: MyAppWeb.Components.Button.button(assigns)
    end

## With a components module

Pass your components module to auto-generate overrides for any function
it exports:

    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter,
        components: MyAppWeb.Components
    end

## With a custom fallback

Use a different fallback module instead of Tailwind:

    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter,
        fallback: MyAppWeb.Components.Base,
        components: MyAppWeb.Components.Custom
    end

## DSL usage

    presentation do
      ui_adapter MyAppWeb.UIAdapter
    end

# `assigns`

```elixir
@type assigns() :: map()
```

Phoenix LiveView assigns map passed to every component function.

# `component`

```elixir
@type component() :: {atom(), String.t()}
```

A `{function_name, docstring}` pair.

# `alert`
*optional* 

```elixir
@callback alert(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a static alert/notice (info/warning/error/success/neutral)

# `archive_toggle`
*optional* 

```elixir
@callback archive_toggle(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render archive status toggle

# `array_fields`
*optional* 

```elixir
@callback array_fields(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a container for array-of-maps fields

# `badge`
*optional* 

```elixir
@callback badge(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a badge/tag

# `bulk_action_bar`
*optional* 

```elixir
@callback bulk_action_bar(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render bulk actions bar container

# `bulk_action_button`
*optional* 

```elixir
@callback bulk_action_button(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render individual bulk action button

# `button`
*optional* 

```elixir
@callback button(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a button

# `cell_array`
*optional* 

```elixir
@callback cell_array(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render array/list container

# `cell_code`
*optional* 

```elixir
@callback cell_code(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render code/monospace cell value

# `cell_date`
*optional* 

```elixir
@callback cell_date(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render date cell value

# `cell_datetime`
*optional* 

```elixir
@callback cell_datetime(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render datetime cell value

# `cell_empty`
*optional* 

```elixir
@callback cell_empty(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render empty cell value (nil/missing data)

# `cell_number`
*optional* 

```elixir
@callback cell_number(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render number cell value

# `cell_text`
*optional* 

```elixir
@callback cell_text(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render text cell value

# `checkbox`
*optional* 

```elixir
@callback checkbox(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a checkbox

# `combobox`
*optional* 

```elixir
@callback combobox(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a combobox (text input with dropdown suggestions)

# `date_input`
*optional* 

```elixir
@callback date_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a date input

# `date_range_container`
*optional* 

```elixir
@callback date_range_container(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a date range container

# `datetime_input`
*optional* 

```elixir
@callback datetime_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a datetime input

# `dropdown`
*optional* 

```elixir
@callback dropdown(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a dropdown menu

# `empty_state`
*optional* 

```elixir
@callback empty_state(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render empty state

# `error_state`
*optional* 

```elixir
@callback error_state(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render error state

# `field_error`
*optional* 

```elixir
@callback field_error(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a field error message display

# `field_group`
*optional* 

```elixir
@callback field_group(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a collapsible group of fields

# `field_wrapper`
*optional* 

```elixir
@callback field_wrapper(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a field wrapper with label, input, and error display

# `filter_reset_button`
*optional* 

```elixir
@callback filter_reset_button(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render filter reset/clear button

# `form_container`
*optional* 

```elixir
@callback form_container(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render the main form wrapper (phx-change, phx-submit)

# `form_footer`
*optional* 

```elixir
@callback form_footer(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a form footer (static content below the submit row)

# `form_header`
*optional* 

```elixir
@callback form_header(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a form header (title + description)

# `icon`
*optional* 

```elixir
@callback icon(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render an icon

# `json_editor`
*optional* 

```elixir
@callback json_editor(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a JSON editor (textarea with formatting)

# `load_more_select`
*optional* 

```elixir
@callback load_more_select(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a single-select with paginated load-more (no search input)

# `loading_state`
*optional* 

```elixir
@callback loading_state(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render loading state

# `multi_select`
*optional* 

```elixir
@callback multi_select(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a multi-select with search support

# `nav_link`
*optional* 

```elixir
@callback nav_link(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a navigation link

# `nested_fields`
*optional* 

```elixir
@callback nested_fields(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a container for nested form fields

# `number_input`
*optional* 

```elixir
@callback number_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a number input

# `pagination_container`
*optional* 

```elixir
@callback pagination_container(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render pagination container with page info

# `pagination_nav_button`
*optional* 

```elixir
@callback pagination_nav_button(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render pagination nav button (prev/next/first/last)

# `pagination_page_button`
*optional* 

```elixir
@callback pagination_page_button(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render pagination page number button

# `password_input`
*optional* 

```elixir
@callback password_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a password input (masked text entry)

# `range_input`
*optional* 

```elixir
@callback range_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a range slider input

# `search_select`
*optional* 

```elixir
@callback search_select(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a single-select with search support

# `select`
*optional* 

```elixir
@callback select(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a select dropdown

# `spinner`
*optional* 

```elixir
@callback spinner(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a loading spinner

# `step_indicator`
*optional* 

```elixir
@callback step_indicator(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render wizard/tabs step progress indicator

# `step_navigation`
*optional* 

```elixir
@callback step_navigation(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render wizard prev/next/submit navigation controls

# `string_list_input`
*optional* 

```elixir
@callback string_list_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a dynamic string list input with add/remove buttons

# `table`
*optional* 

```elixir
@callback table(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render table wrapper

# `table_header`
*optional* 

```elixir
@callback table_header(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render table header row

# `td`
*optional* 

```elixir
@callback td(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a table cell

# `template_switcher`
*optional* 

```elixir
@callback template_switcher(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render template switcher container with buttons

# `template_switcher_button`
*optional* 

```elixir
@callback template_switcher_button(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render template switcher button

# `text_input`
*optional* 

```elixir
@callback text_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a text input

# `textarea`
*optional* 

```elixir
@callback textarea(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a multi-line text input

# `th`
*optional* 

```elixir
@callback th(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a table header cell

# `toggle_input`
*optional* 

```elixir
@callback toggle_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a boolean toggle switch

# `tr`
*optional* 

```elixir
@callback tr(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a table row

# `upload_dropzone`
*optional* 

```elixir
@callback upload_dropzone(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a drag-drop file upload zone

# `upload_existing_file`
*optional* 

```elixir
@callback upload_existing_file(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render an existing file card with remove button (for edit mode)

# `upload_file_input`
*optional* 

```elixir
@callback upload_file_input(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a styled file input (non-dropzone) upload control

# `upload_preview`
*optional* 

```elixir
@callback upload_preview(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render a file preview thumbnail

# `upload_progress`
*optional* 

```elixir
@callback upload_progress(assigns()) :: Phoenix.LiveView.Rendered.t()
```

Render an upload progress bar

# `__using__`
*macro* 

Sets up an adapter that delegates every component to a fallback module,
and optionally overrides specific components from a `:components` module.

## Options

  * `:fallback` — module providing the default implementations.
    Defaults to `MishkaGervaz.UIAdapters.Tailwind`.

  * `:components` — module to source overrides from. Each component
    function is wired only when the target module is loaded **and**
    exports the corresponding 1-arity function.

  * `:nested_components` — when `true`, look for each component under a
    submodule (e.g. `Components.Button.button/1`). When `false` (default),
    the components module is flat (e.g. `Components.button/1`).

  * `:module_prefix` — a string prepended to each submodule name when
    `nested_components: true`. Example: `"Mishka"` makes the macro look
    under `Components.MishkaButton.button/1`.

  * `:component_prefix` — a string prepended to each function name.
    Example: `"mc_"` makes the macro look up `mc_button/1` on the target
    module.

## Examples

    # All defaults from Tailwind
    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter
    end

    # Flat components module — Components.button/1
    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter,
        components: MyAppWeb.Components
    end

    # Nested style — Components.Button.button/1
    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter,
        components: MyAppWeb.Components,
        nested_components: true
    end

    # Module prefix — Components.MishkaButton.button/1
    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter,
        components: MyAppWeb.Components,
        nested_components: true,
        module_prefix: "Mishka"
    end

    # Function prefix — Components.mc_button/1
    defmodule MyAppWeb.UIAdapter do
      use MishkaGervaz.Behaviours.UIAdapter,
        components: MyAppWeb.Components,
        component_prefix: "mc_"
    end

# `component_functions`

```elixir
@spec component_functions() :: [atom()]
```

The list of every component function name on the behaviour. Useful for
introspection and for tests that want to assert all functions are wired.

# `resolve_target`

```elixir
@spec resolve_target(atom(), module(), boolean(), String.t() | nil, String.t() | nil) ::
  {module(), atom()}
```

Resolves the `{module, function}` target a generated override should call,
given the consuming module's `:components`, `:nested_components`,
`:module_prefix`, and `:component_prefix` options.

Public so the macro can call it; also useful for testing the routing
logic directly without having to build a full adapter.

---

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