# `mix phia.add`
[🔗](https://github.com/phiaui/phia_ui/blob/v0.1.5/lib/mix/tasks/phia.add.ex#L1)

Ejects a PhiaUI component into the current Phoenix project as an editable
source file.

When you run `mix phia.add`, PhiaUI renders an EEx template and writes the
result to `lib/{app_web}/components/ui/{component}.ex`. From that point on,
the file belongs to your project — edit it freely. This is the same
"copy-paste ownership" model popularised by shadcn/ui.

## Usage

    mix phia.add <component>

## Available components

    button    badge    card    table    dialog

## What gets created

Running `mix phia.add button` in a project called `my_app` creates:

    lib/my_app_web/components/ui/button.ex
    lib/my_app_web/class_merger.ex          (shared dependency, skipped if exists)
    lib/my_app_web/class_merger/cache.ex    (shared dependency, skipped if exists)
    lib/my_app_web/class_merger/groups.ex   (shared dependency, skipped if exists)
    assets/js/phia_hooks/{component}.js     (only for components that need a hook)

The web module namespace (e.g., `MyAppWeb`) is derived automatically from the
`:app` key in `mix.exs`.

## Behaviour

- Derives the web module name from `Mix.Project.config()[:app]`
  (e.g., `:my_app` → `MyAppWeb`).
- Renders the EEx template from PhiaUI's `priv/templates/components/`.
- Writes the file with `Mix.Generator.create_file/3` (conflict-aware).
- **Idempotent**: if the file already exists the user is prompted before
  any overwrite.
- The `ClassMerger` utility (required by every component) is ejected
  automatically and skipped if already present.
- Prints a confirmation message via `Mix.shell().info/1`.

## Next steps after ejecting

1. Run `mix phia.install` once (if you haven't already) to inject PhiaUI
   theme tokens into `assets/css/app.css`.
2. Add `ClassMerger.Cache` to your supervision tree:
     children = [MyAppWeb.ClassMerger.Cache, ...]
3. Import the component in any LiveView or layout:
     import MyAppWeb.Components.UI.Button

## Options

    --help    Print this help message

## Examples

    $ mix phia.add button
    * create lib/my_app_web/components/ui/button.ex

    $ mix phia.add dialog
    * create lib/my_app_web/components/ui/dialog.ex
    * create assets/js/phia_hooks/dialog.js

# `app_web_name`

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

Derives the host project's web module name from the current Mix project.

The web module name is the camelized app name suffixed with `"Web"`. For
example, a project with `app: :my_app` returns `"MyAppWeb"`.

## Example

    iex> Mix.Tasks.Phia.Add.app_web_name()
    "PhiaUiWeb"

# `component_path`

```elixir
@spec component_path(Path.t(), String.t()) :: Path.t()
```

Builds the absolute target file path for a component under `root`.

The path follows the Phoenix convention of placing UI components under
`lib/{app_web_dir}/components/ui/`.

## Example

    iex> Mix.Tasks.Phia.Add.component_path("/project", "button")
    "/project/lib/phia_ui_web/components/ui/button.ex"

# `eject_class_merger`

```elixir
@spec eject_class_merger(Path.t()) :: :ok
```

Ejects the ClassMerger dependency files into `root`.

`ClassMerger` is a Tailwind CSS class conflict resolver (similar to
`tailwind-merge`) implemented in pure Elixir. Every PhiaUI component imports
the `cn/1` helper it provides, so it must be present in the host project.

Creates three files relative to `lib/{app_web_dir}/`:

- `class_merger.ex` — main `cn/1` function
- `class_merger/cache.ex` — ETS-backed GenServer cache
- `class_merger/groups.ex` — Tailwind conflict group mappings

Skips any file that already exists, making this function safe to call
repeatedly.

# `eject_component`

```elixir
@spec eject_component(String.t(), Path.t()) ::
  :ok | :already_exists | {:error, String.t()}
```

Ejects a component from PhiaUI's templates into `root`.

This is the main entry point called by `run/1`. It runs a pipeline of
steps: validate → resolve_target → render_content → write_file.

Also automatically ejects the `ClassMerger` utility (a required dependency
shared by all components) unless it is already present.

## Return values

- `:ok` — component was created successfully.
- `:already_exists` — file already existed; user was prompted.
- `{:error, reason}` — component name is not in the known components list.

## Example

    iex> Mix.Tasks.Phia.Add.eject_component("button", "/tmp/my_project")
    :ok

# `eject_js_hooks`

```elixir
@spec eject_js_hooks(String.t(), Path.t()) :: :ok
```

Copies the JS hook file for `component_name` into `root/assets/js/phia_hooks/`
if a corresponding hook template exists at `priv/templates/js/hooks/{component}.js`.

Components such as Dialog, DropdownMenu, and Toast require a client-side
JavaScript hook for browser interactions (focus trapping, keyboard handling,
etc.). This function handles that copy automatically during ejection.

No-op (returns `:ok`) when no hook template is found for the given component.
Idempotent: skips the copy if the target hook file already exists.

# `next_steps_message`

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

Returns a next-steps message to display after ejecting `component_name`.

This message reminds the developer to run `mix phia.install`, add
`ClassMerger.Cache` to their supervision tree, and import the component.

---

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