defmodule Temple.Component do
@moduledoc """
Use this module to create your own component implementation.
This is only required if you are not using a component implementation from another framework,
like Phoenix LiveView.
At it's core, a component implmentation includes the following functions
- `component/2`
- `inner_block/2`
- `render_slot/2`
These functions are used by the template compiler, so you won't be calling them directly.
## Usage
Invoke the `__using__/1` macro to create your own module, and then import that module where you
need to define define or use components (usually everywhere).
We'll use an example that is similar to what Temple uses in its own test suite..
```elixir
defmodule MyAppWeb.Component do
use Temple.Component
defmacro __using__(_) do
quote do
import Temple
import unquote(__MODULE__)
end
```
Then you can `use` your module when you want to define or use a component.
defmodule MyAppWeb.Components do
use MyAppWeb.Component
def basic_component(_assigns) do
temple do
div do
"I am a basic component"
"""
@doc false
def component(func, assigns, _) do
apply(func, [assigns])
defmacro inner_block(_name, do: do_block) do
__inner_block__(do_block)
def __inner_block__([{:->, meta, _} | _] = do_block) do
inner_fun = {:fn, meta, do_block}
fn arg ->
_ = var!(assigns)
unquote(inner_fun).(arg)
def __inner_block__(do_block) do
unquote(do_block)
defmacro render_slot(slot, arg) do
unquote(__MODULE__).__render_slot__(unquote(slot), unquote(arg))
def __render_slot__([], _), do: nil
def __render_slot__([entry], argument) do
call_inner_block!(entry, argument)
def __render_slot__(entries, argument) when is_list(entries) do
assigns = %{}
_ = assigns
for entry <- entries do
def __render_slot__(entry, argument) when is_map(entry) do
entry.inner_block.(argument)
defp call_inner_block!(entry, argument) do
if !entry.inner_block do
message = "attempted to render slot #{entry.__slot__} but the slot has no inner content"
raise RuntimeError, message