PineUiPhoenix.Component (Pine UI v0.2.1)

Copy Markdown View Source

The base every Pine UI component is built on.

defmodule MyComponent do
  use PineUiPhoenix.Component

  attr :class, :string, default: nil
  attr :rest, :global
  slot :inner_block, required: true

  def thing(assigns) do
    ~H|<div class={cx("p-4 rounded-md", @class)} {@rest}><%= render_slot(@inner_block) %></div>|
  end
end

Why this exists rather than plain use Phoenix.Component

LiveView's default global-attribute prefixes are exactly phx-, aria- and data- (see Phoenix.Component's :global_prefixes option). x- is not among them, so writing <.dropdown x-on:keydown.escape="open = false"> produces

warning: undefined attribute "x-on:click" for component ...

This macro registers x- so that call sites inside Pine UI's own modules compile cleanly — one component invoking another, as pagination/1 does with its internal page_link/1.

Global prefixes are resolved against the caller

Phoenix.Component.Declarative's __global__?/3 is passed the module containing the call site, not the module defining the component. Registering x- here therefore does nothing for application code that calls Pine UI components.

use PineUiPhoenix handles that for consuming applications by defining __global__?/1 on the caller. See PineUiPhoenix.__using__/1.

Note the attribute is still forwarded to @rest either way — the consequence of a missing registration is a compiler warning, not a dropped attribute. But a library that makes every consumer's build noisy is not one people keep.

Alpine shorthands are not supported

Alpine accepts @click as shorthand for x-on:click and :class for x-bind:class. Neither works in HEEx: @ introduces an assign and : introduces a special attribute like :if, so they cannot be registered as global prefixes. Always write the long form:

x-on:click="open = !open"     # not @click
x-bind:class="open && 'rotate-180'"   # not :class

Declared attributes shadow global ones

class, id, title, type, role, style and placeholder are all global attributes. Because an explicitly declared attr takes precedence over :rest, declaring attr :title, :string for a component's heading text means the caller can no longer set the HTML title tooltip attribute directly. Pass it through :rest instead:

<.card title="Billing" rest={%{title: "Tooltip text"}} />