defelement macro
- add location: :keep to all calls to quote
This commit is contained in:
parent
b0a7f9da11
commit
74e7f3a25e
8 changed files with 153 additions and 73 deletions
|
@ -1,6 +1,6 @@
|
|||
defmodule Temple do
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
import Temple
|
||||
import Temple.Tags
|
||||
import Temple.Form
|
||||
|
@ -32,7 +32,7 @@ defmodule Temple do
|
|||
```
|
||||
"""
|
||||
defmacro temple([do: block] = _block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
import Kernel, except: [div: 2]
|
||||
|
||||
with {:ok, var!(buff, Temple.Tags)} <- Temple.Utils.start_buffer([]) do
|
||||
|
@ -61,7 +61,7 @@ defmodule Temple do
|
|||
```
|
||||
"""
|
||||
defmacro text(text) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_buffer(
|
||||
var!(buff, Temple.Tags),
|
||||
unquote(text) |> Temple.Utils.escape_content()
|
||||
|
@ -96,7 +96,7 @@ defmodule Temple do
|
|||
```
|
||||
"""
|
||||
defmacro partial(partial) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_buffer(
|
||||
var!(buff, Temple.Tags),
|
||||
unquote(partial) |> Temple.Utils.from_safe()
|
||||
|
@ -144,7 +144,7 @@ defmodule Temple do
|
|||
```
|
||||
"""
|
||||
defmacro defcomponent(name, [do: _] = block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
defmacro unquote(name)() do
|
||||
outer = unquote(Macro.escape(block))
|
||||
|
||||
|
|
|
@ -1,15 +1,74 @@
|
|||
defmodule Temple.Elements do
|
||||
@moduledoc """
|
||||
This module contains the primitives used to generate the macros in the `Temple.Tags` module.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Defines an element.
|
||||
|
||||
*Note*: Underscores are converted to hypens.
|
||||
|
||||
```elixir
|
||||
defmodule MyElements do
|
||||
import Temple.Elements
|
||||
|
||||
defelement :super_select, :nonvoid # <super-select></super-select>
|
||||
defelement :super_input, :void # <super-input>
|
||||
end
|
||||
```
|
||||
"""
|
||||
|
||||
defmacro defelement(name, type)
|
||||
|
||||
defmacro defelement(name, :nonvoid) do
|
||||
quote location: :keep do
|
||||
defmacro unquote(name)() do
|
||||
Temple.Elements.nonvoid_element(unquote(name))
|
||||
end
|
||||
|
||||
defmacro unquote(name)(attrs_or_content_or_block)
|
||||
|
||||
defmacro unquote(name)([{:do, _inner}] = block) do
|
||||
Temple.Elements.nonvoid_element(unquote(name), block)
|
||||
end
|
||||
|
||||
defmacro unquote(name)(attrs_or_content) do
|
||||
Temple.Elements.nonvoid_element(unquote(name), attrs_or_content)
|
||||
end
|
||||
|
||||
defmacro unquote(name)(attrs_or_content, block_or_attrs)
|
||||
|
||||
defmacro unquote(name)(attrs, [{:do, _inner}] = block) do
|
||||
Temple.Elements.nonvoid_element(unquote(name), attrs, block)
|
||||
end
|
||||
|
||||
defmacro unquote(name)(content, attrs) do
|
||||
Temple.Elements.nonvoid_element(unquote(name), content, attrs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro defelement(name, :void) do
|
||||
quote location: :keep do
|
||||
defmacro unquote(name)(attrs \\ []) do
|
||||
Temple.Elements.void_element(unquote(name), attrs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def nonvoid_element(el) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote(el), [])
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def nonvoid_element(el, attrs_or_content_or_block)
|
||||
|
||||
def nonvoid_element(el, [{:do, inner}]) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote(el), [])
|
||||
_ = unquote(inner)
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
|
@ -17,16 +76,17 @@ defmodule Temple.Elements do
|
|||
end
|
||||
|
||||
def nonvoid_element(el, attrs_or_content) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote(el), unquote(attrs_or_content))
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def nonvoid_element(el, attrs_or_content, block_or_attrs)
|
||||
|
||||
def nonvoid_element(el, attrs, [{:do, inner}] = _block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote_splicing([el, attrs]))
|
||||
_ = unquote(inner)
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
|
@ -34,21 +94,17 @@ defmodule Temple.Elements do
|
|||
end
|
||||
|
||||
def nonvoid_element(el, content, attrs) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote_splicing([el, attrs]))
|
||||
text unquote(content)
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def void_element(el, attrs \\ []) do
|
||||
quote do
|
||||
attrs = unquote(attrs)
|
||||
|
||||
Temple.Utils.put_buffer(
|
||||
var!(buff, Temple.Tags),
|
||||
"<#{unquote(el)}#{Temple.Utils.compile_attrs(attrs)}>"
|
||||
)
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_void_tag(var!(buff, Temple.Tags), unquote_splicing([el, attrs]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule Temple.Engine do
|
|||
|
||||
# your_app_web.ex
|
||||
def view do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
# ...
|
||||
use Temple # Replaces the call to import Phoenix.HTML
|
||||
end
|
||||
|
@ -75,7 +75,7 @@ defmodule Temple.Engine do
|
|||
|> Code.string_to_quoted!(file: path)
|
||||
|> handle_assigns()
|
||||
|
||||
quote do
|
||||
quote location: :keep do
|
||||
use Temple
|
||||
|
||||
temple do: unquote(template)
|
||||
|
@ -86,7 +86,7 @@ defmodule Temple.Engine do
|
|||
quoted
|
||||
|> Macro.prewalk(fn
|
||||
{:@, _, [{key, _, _}]} ->
|
||||
quote do
|
||||
quote location: :keep do
|
||||
case Access.fetch(var!(assigns), unquote(key)) do
|
||||
{:ok, val} ->
|
||||
val
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule Temple.Form do
|
|||
See `Temple.Form.form_for/4` for more details
|
||||
"""
|
||||
defmacro form_for(form_data, action) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
form_for(unquote_splicing([form_data, action]), [])
|
||||
end
|
||||
end
|
||||
|
@ -42,7 +42,7 @@ defmodule Temple.Form do
|
|||
```
|
||||
"""
|
||||
defmacro form_for(form_data, action, opts \\ [], block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
var!(form) = HTML.Form.form_for(unquote_splicing([form_data, action, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), var!(form) |> HTML.Safe.to_iodata())
|
||||
|
@ -79,7 +79,7 @@ defmodule Temple.Form do
|
|||
defmacro unquote(helper)(form, field, opts \\ []) do
|
||||
helper = unquote(helper)
|
||||
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} =
|
||||
apply(Phoenix.HTML.Form, unquote(helper), [unquote_splicing([form, field, opts])])
|
||||
|
||||
|
@ -94,7 +94,7 @@ defmodule Temple.Form do
|
|||
Note: Temple defines this function as `text_area` with an underscore, whereas Phoenix.HTML defines it as `textarea` without an underscore.
|
||||
"""
|
||||
defmacro text_area(form, field, opts \\ []) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.textarea(unquote_splicing([form, field, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -105,7 +105,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.reset/2` for details.
|
||||
"""
|
||||
defmacro reset(value, opts \\ []) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.reset(unquote_splicing([value, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -116,7 +116,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.submit/1` for details.
|
||||
"""
|
||||
defmacro submit(do: block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, content} =
|
||||
temple do
|
||||
unquote(block)
|
||||
|
@ -129,7 +129,7 @@ defmodule Temple.Form do
|
|||
end
|
||||
|
||||
defmacro submit(value) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.submit(unquote(value))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -140,7 +140,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.submit/1` for details.
|
||||
"""
|
||||
defmacro submit(opts, do: block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, content} =
|
||||
temple do
|
||||
unquote(block)
|
||||
|
@ -153,7 +153,7 @@ defmodule Temple.Form do
|
|||
end
|
||||
|
||||
defmacro submit(value, opts) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.submit(unquote_splicing([value, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -164,7 +164,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.label/2` for details.
|
||||
"""
|
||||
defmacro phx_label(form, field) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.label(unquote_splicing([form, field]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -175,7 +175,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.label/3` for details.
|
||||
"""
|
||||
defmacro phx_label(form, field, do: block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, content} =
|
||||
temple do
|
||||
unquote(block)
|
||||
|
@ -188,7 +188,7 @@ defmodule Temple.Form do
|
|||
end
|
||||
|
||||
defmacro phx_label(form, field, text_or_opts) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.label(unquote_splicing([form, field, text_or_opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -199,7 +199,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.label/4` for details.
|
||||
"""
|
||||
defmacro phx_label(form, field, opts, do: block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, content} =
|
||||
temple do
|
||||
unquote(block)
|
||||
|
@ -212,7 +212,7 @@ defmodule Temple.Form do
|
|||
end
|
||||
|
||||
defmacro phx_label(form, field, text, opts) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.label(unquote_splicing([form, field, text, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -223,7 +223,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.radio_button/4` for details.
|
||||
"""
|
||||
defmacro radio_button(form, field, value, attrs \\ []) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} =
|
||||
Phoenix.HTML.Form.radio_button(unquote_splicing([form, field, value, attrs]))
|
||||
|
||||
|
@ -235,7 +235,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.multiple_select/4` for details.
|
||||
"""
|
||||
defmacro multiple_select(form, field, options, attrs \\ []) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} =
|
||||
Phoenix.HTML.Form.multiple_select(unquote_splicing([form, field, options, attrs]))
|
||||
|
||||
|
@ -247,7 +247,7 @@ defmodule Temple.Form do
|
|||
Please see `Phoenix.HTML.Form.select/4` for details.
|
||||
"""
|
||||
defmacro select(form, field, options, attrs \\ []) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.select(unquote_splicing([form, field, options, attrs]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
|
@ -292,7 +292,7 @@ defmodule Temple.Form do
|
|||
```
|
||||
"""
|
||||
defmacro inputs_for(form, field, options \\ [], do: block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
form = unquote(form)
|
||||
field = unquote(field)
|
||||
options = unquote(options)
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Temple.Link do
|
|||
Please see `Phoenix.HTML.Link.link/2` for details.
|
||||
"""
|
||||
defmacro phx_link(opts, do: block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, content} =
|
||||
temple do
|
||||
unquote(block)
|
||||
|
@ -23,7 +23,7 @@ defmodule Temple.Link do
|
|||
end
|
||||
|
||||
defmacro phx_link(content, opts) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, link} = HTML.Link.link(unquote_splicing([content, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), link)
|
||||
|
@ -34,7 +34,7 @@ defmodule Temple.Link do
|
|||
Please see `Phoenix.HTML.Link.button/2` for details.
|
||||
"""
|
||||
defmacro phx_button(opts, do: block) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, content} =
|
||||
temple do
|
||||
unquote(block)
|
||||
|
@ -47,7 +47,7 @@ defmodule Temple.Link do
|
|||
end
|
||||
|
||||
defmacro phx_button(content, opts) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
{:safe, link} = HTML.Link.button(unquote_splicing([content, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), link)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
defmodule Temple.Tags do
|
||||
require Temple.Elements
|
||||
|
||||
@moduledoc """
|
||||
The `Temple.Tags` module defines macros for all HTML5 compliant elements.
|
||||
|
||||
|
@ -23,7 +25,7 @@ defmodule Temple.Tags do
|
|||
|
||||
# non-void element with keyword list attributes
|
||||
div class: "text-red", id: "my-el"
|
||||
#
|
||||
|
||||
# non-void element with map attributes
|
||||
div %{:class => "text-red", "id" => "my-el"}
|
||||
|
||||
|
@ -87,42 +89,18 @@ defmodule Temple.Tags do
|
|||
|
||||
for el <- @nonvoid_elements do
|
||||
@doc if File.exists?("./tmp/docs/#{el}.txt"), do: File.read!("./tmp/docs/#{el}.txt")
|
||||
defmacro unquote(el)() do
|
||||
Temple.Elements.nonvoid_element(unquote(el))
|
||||
end
|
||||
|
||||
defmacro unquote(el)(attrs_or_content_or_block)
|
||||
|
||||
defmacro unquote(el)([{:do, _inner}] = block) do
|
||||
Temple.Elements.nonvoid_element(unquote(el), block)
|
||||
end
|
||||
|
||||
defmacro unquote(el)(attrs_or_content) do
|
||||
Temple.Elements.nonvoid_element(unquote(el), attrs_or_content)
|
||||
end
|
||||
|
||||
defmacro unquote(el)(attrs_or_content, block_or_attrs)
|
||||
|
||||
defmacro unquote(el)(attrs, [{:do, _inner}] = block) do
|
||||
Temple.Elements.nonvoid_element(unquote(el), attrs, block)
|
||||
end
|
||||
|
||||
defmacro unquote(el)(content, attrs) do
|
||||
Temple.Elements.nonvoid_element(unquote(el), content, attrs)
|
||||
end
|
||||
Temple.Elements.defelement(unquote(el), :nonvoid)
|
||||
end
|
||||
|
||||
for el <- @void_elements do
|
||||
@doc if File.exists?("./tmp/docs/#{el}.txt"), do: File.read!("./tmp/docs/#{el}.txt")
|
||||
defmacro unquote(el)(attrs \\ []) do
|
||||
Temple.Elements.void_element(unquote(el), attrs)
|
||||
end
|
||||
Temple.Elements.defelement(unquote(el), :void)
|
||||
end
|
||||
|
||||
@doc if File.exists?("./tmp/docs/html.txt"), do: File.read!("./tmp/docs/html.txt")
|
||||
defmacro html(attrs \\ [], [{:do, _inner}] = block) do
|
||||
doc_type =
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_buffer(var!(buff, Temple.Tags), "<!DOCTYPE html>")
|
||||
end
|
||||
|
||||
|
|
|
@ -2,19 +2,31 @@ defmodule Temple.Utils do
|
|||
@moduledoc false
|
||||
|
||||
def put_open_tag(buff, el, attrs) when is_list(attrs) or is_map(attrs) do
|
||||
el = el |> snake_to_kebab
|
||||
|
||||
put_buffer(buff, "<#{el}#{compile_attrs(attrs)}>")
|
||||
end
|
||||
|
||||
def put_open_tag(buff, el, content)
|
||||
when is_binary(content) or is_number(content) or is_atom(content) do
|
||||
el = el |> snake_to_kebab
|
||||
|
||||
put_buffer(buff, "<#{el}>")
|
||||
put_buffer(buff, escape_content(content))
|
||||
end
|
||||
|
||||
def put_close_tag(buff, el) do
|
||||
el = el |> snake_to_kebab
|
||||
|
||||
put_buffer(buff, "</#{el}>")
|
||||
end
|
||||
|
||||
def put_void_tag(buff, el, attrs) do
|
||||
el = el |> snake_to_kebab
|
||||
|
||||
put_buffer(buff, "<#{el}#{Temple.Utils.compile_attrs(attrs)}>")
|
||||
end
|
||||
|
||||
def from_safe({:safe, partial}) do
|
||||
partial
|
||||
end
|
||||
|
@ -28,7 +40,7 @@ defmodule Temple.Utils do
|
|||
end
|
||||
|
||||
def insert_props({:@, _, [{name, _, _}]}, props, _) when is_atom(name) do
|
||||
quote do
|
||||
quote location: :keep do
|
||||
Access.get(unquote_splicing([props, name]))
|
||||
end
|
||||
end
|
||||
|
@ -39,7 +51,7 @@ defmodule Temple.Utils do
|
|||
|
||||
def compile_attrs(attrs) do
|
||||
for {name, value} <- attrs, into: "" do
|
||||
name = name |> to_string() |> String.replace("_", "-")
|
||||
name = snake_to_kebab(name)
|
||||
|
||||
" " <> name <> "=\"" <> to_string(value) <> "\""
|
||||
end
|
||||
|
@ -61,8 +73,11 @@ defmodule Temple.Utils do
|
|||
|> Phoenix.HTML.safe_to_string()
|
||||
end
|
||||
|
||||
defp snake_to_kebab(stringable),
|
||||
do: stringable |> to_string() |> String.replace("_", "-")
|
||||
|
||||
def __quote__(outer) do
|
||||
quote do: unquote(outer)
|
||||
quote [location: :keep], do: unquote(outer)
|
||||
end
|
||||
|
||||
def __insert_props__(block, props, inner) do
|
||||
|
|
31
test/temple/elements_test.exs
Normal file
31
test/temple/elements_test.exs
Normal file
|
@ -0,0 +1,31 @@
|
|||
defmodule Temple.ElementsTest do
|
||||
use ExUnit.Case, async: true
|
||||
import Temple.Elements, only: [defelement: 2]
|
||||
import Temple, only: [temple: 1, text: 1]
|
||||
import Temple.Tags, only: [option: 2]
|
||||
|
||||
defelement(:my_select, :nonvoid)
|
||||
defelement(:my_input, :void)
|
||||
|
||||
test "defines a nonvoid element" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
my_select class: "hello" do
|
||||
option "A", value: "A"
|
||||
option "B", value: "B"
|
||||
end
|
||||
end
|
||||
|
||||
assert result ==
|
||||
~s{<my-select class="hello"><option value="A">A</option><option value="B">B</option></my-select>}
|
||||
end
|
||||
|
||||
test "defines a void element" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
my_input(class: "hello")
|
||||
end
|
||||
|
||||
assert result == ~s{<my-input class="hello">}
|
||||
end
|
||||
end
|
Reference in a new issue