This repository has been archived on 2023-08-07. You can view files and clone it, but cannot push or open issues or pull requests.
temple/lib/temple/ast/components.ex

105 lines
2.4 KiB
Elixir

defmodule Temple.Ast.Components do
@moduledoc false
@behaviour Temple.Parser
use TypedStruct
typedstruct do
field :function, function()
field :arguments, map()
field :slots, [function()]
end
@impl true
def applicable?({:c, _, _}) do
true
end
def applicable?(_), do: false
@impl true
def run({:c, _meta, [component_function | args]}) do
{do_and_else, args} =
args
|> Temple.Ast.Utils.split_args()
{do_and_else, arguments} = Temple.Ast.Utils.consolidate_blocks(do_and_else, args)
{default_slot, {_, named_slots}} =
if children = do_and_else[:do] do
Macro.prewalk(
children,
{component_function, []},
fn
{:c, _, [name | _]} = node, {_, named_slots} ->
{node, {name, named_slots}}
{:slot, _, [name | args]} = node, {^component_function, named_slots} ->
{arguments, slot} = split_assigns_and_children(args, nil)
if is_nil(slot) do
{node, {component_function, named_slots}}
else
{parameter, attributes} = Keyword.pop(arguments || [], :let!)
new_slot = {name, %{parameter: parameter, slot: slot, attributes: attributes}}
{nil, {component_function, named_slots ++ [new_slot]}}
end
node, acc ->
{node, acc}
end
)
else
{nil, {nil, %{}}}
end
children =
if default_slot == nil do
[]
else
[
Temple.Ast.new(
Temple.Ast.Slottable,
name: :inner_block,
content: Temple.Parser.parse(default_slot)
)
]
end
slots =
for {name, %{slot: slot, parameter: parameter, attributes: attributes}} <- named_slots do
Temple.Ast.new(
Temple.Ast.Slottable,
name: name,
content: Temple.Parser.parse(slot),
parameter: parameter,
attributes: attributes
)
end
slots = children ++ slots
Temple.Ast.new(__MODULE__,
function: component_function,
arguments: arguments,
slots: slots
)
end
defp split_assigns_and_children(args, empty) do
case args do
[arguments, [do: block]] ->
{arguments, block}
[[do: block]] ->
{empty, block}
[arguments] ->
{arguments, nil}
_ ->
{empty, nil}
end
end
end