Extra non-tags macros to Dsl module
This commit is contained in:
parent
71e6c91c8f
commit
c80a858b48
167
lib/dsl.ex
167
lib/dsl.ex
|
@ -5,8 +5,175 @@ defmodule Dsl do
|
|||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
import unquote(__MODULE__)
|
||||
import Dsl.Tags
|
||||
import Dsl.Form
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a markup context.
|
||||
|
||||
All tags must be called inside of a `Dsl.htm/1` block.
|
||||
|
||||
Returns a safe result of the form `{:safe, result}`
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
team = ["Alice", "Bob", "Carol"]
|
||||
|
||||
htm do
|
||||
for name <- team do
|
||||
div class: "text-bold" do
|
||||
text name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# {:safe, "<div class=\"text-bold\">Alice</div><div class=\"text-bold\">Bob</div><div class=\"text-bold\">Carol</div>"}
|
||||
```
|
||||
"""
|
||||
defmacro htm([do: block] = _block) do
|
||||
quote do
|
||||
import Kernel, except: [div: 2]
|
||||
import Phoenix.HTML.Link, except: [link: 1, link: 2]
|
||||
import Phoenix.HTML.Form, only: []
|
||||
|
||||
{:ok, var!(buff, Dsl.Tags)} = Dsl.Utils.start_buffer([])
|
||||
|
||||
unquote(block)
|
||||
|
||||
markup = Dsl.Utils.get_buffer(var!(buff, Dsl.Tags))
|
||||
|
||||
:ok = Dsl.Utils.stop_buffer(var!(buff, Dsl.Tags))
|
||||
|
||||
markup |> Enum.reverse() |> Enum.join("") |> Phoenix.HTML.raw()
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Emits a text node into the markup.
|
||||
|
||||
```
|
||||
htm do
|
||||
div do
|
||||
text "Hello, world!"
|
||||
end
|
||||
end
|
||||
|
||||
# {:safe, "<div>Hello, world!</div>"}
|
||||
```
|
||||
"""
|
||||
defmacro text(text) do
|
||||
quote do
|
||||
Dsl.Utils.put_buffer(
|
||||
var!(buff, Dsl.Tags),
|
||||
unquote(text) |> to_string |> Phoenix.HTML.html_escape() |> Phoenix.HTML.safe_to_string()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Emits a Phoenix partial into the markup.
|
||||
|
||||
```
|
||||
htm do
|
||||
html lang: "en" do
|
||||
head do
|
||||
title "MyApp"
|
||||
|
||||
link rel: "stylesheet", href: Routes.static_path(@conn, "/css/app.css")
|
||||
end
|
||||
|
||||
body do
|
||||
main role: "main", class: "container" do
|
||||
p get_flash(@conn, :info), class: "alert alert-info", role: "alert"
|
||||
p get_flash(@conn, :error), class: "alert alert-danger", role: "alert"
|
||||
|
||||
partial render(@view_module, @view_template, assigns)
|
||||
end
|
||||
|
||||
script type: "text/javascript", src: Routes.static_path(@conn, "/js/app.js")
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
"""
|
||||
defmacro partial(partial) do
|
||||
quote do
|
||||
Dsl.Utils.put_buffer(
|
||||
var!(buff, Dsl.Tags),
|
||||
unquote(partial) |> Dsl.Utils.from_safe()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Defines a custom component.
|
||||
|
||||
Components are the primary way to extract partials and markup helpers.
|
||||
|
||||
## Assigns
|
||||
|
||||
Components accept a keyword list of assigns and can be referenced in the body of the component by a module attribute of the same name.
|
||||
|
||||
This works exactly the same as EEx templates.
|
||||
|
||||
## Children
|
||||
|
||||
If a block is passed to the component, it can be referenced by a special assign called `@children`.
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
defcomponent :flex do
|
||||
div id: @id, class: "flex" do
|
||||
@children
|
||||
end
|
||||
end
|
||||
|
||||
htm do
|
||||
flex id: "my-flex" do
|
||||
div "Item 1"
|
||||
div "Item 2"
|
||||
div "Item 3"
|
||||
end
|
||||
end
|
||||
|
||||
# {:safe, "<div id=\"my-flex\" class=\"flex\">
|
||||
# <div>Item 1</div>
|
||||
# <div>Item 2</div>
|
||||
# <div>Item 3</div>
|
||||
# </div>"}
|
||||
```
|
||||
"""
|
||||
defmacro defcomponent(name, [do: _] = block) do
|
||||
quote do
|
||||
defmacro unquote(name)(props \\ []) do
|
||||
outer = unquote(Macro.escape(block))
|
||||
name = unquote(name)
|
||||
|
||||
{inner, props} = Keyword.pop(props, :do, nil)
|
||||
|
||||
quote do
|
||||
unquote(name)(unquote(props), unquote(inner))
|
||||
end
|
||||
end
|
||||
|
||||
defmacro unquote(name)(props, inner) do
|
||||
import Kernel, except: [div: 2]
|
||||
|
||||
outer =
|
||||
unquote(Macro.escape(block))
|
||||
|> Macro.prewalk(&Dsl.Utils.insert_props(&1, [{:children, inner} | props]))
|
||||
|
||||
name = unquote(name)
|
||||
|
||||
quote do
|
||||
unquote(outer)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
179
lib/dsl/tags.ex
179
lib/dsl/tags.ex
|
@ -1,7 +1,4 @@
|
|||
defmodule Dsl.Tags do
|
||||
alias Phoenix.HTML
|
||||
alias Dsl.Utils
|
||||
|
||||
@nonvoid_elements ~w[
|
||||
html
|
||||
head title style script
|
||||
|
@ -30,47 +27,6 @@ defmodule Dsl.Tags do
|
|||
@doc false
|
||||
def void_elements, do: @void_elements
|
||||
|
||||
@doc """
|
||||
Creates a markup context.
|
||||
|
||||
All tags must be called inside of a `Dsl.Tags.htm/1` block.
|
||||
|
||||
Returns a safe result of the form `{:safe, result}`
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
team = ["Alice", "Bob", "Carol"]
|
||||
|
||||
htm do
|
||||
for name <- team do
|
||||
div class: "text-bold" do
|
||||
text name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# {:safe, "<div class=\"text-bold\">Alice</div><div class=\"text-bold\">Bob</div><div class=\"text-bold\">Carol</div>"}
|
||||
```
|
||||
"""
|
||||
defmacro htm([do: block] = _block) do
|
||||
quote do
|
||||
import Kernel, except: [div: 2]
|
||||
import HTML.Link, except: [link: 1, link: 2]
|
||||
import HTML.Form, only: []
|
||||
|
||||
{:ok, var!(buff, Dsl.Tags)} = Utils.start_buffer([])
|
||||
|
||||
unquote(block)
|
||||
|
||||
markup = Utils.get_buffer(var!(buff, Dsl.Tags))
|
||||
|
||||
:ok = Utils.stop_buffer(var!(buff, Dsl.Tags))
|
||||
|
||||
markup |> Enum.reverse() |> Enum.join("") |> HTML.raw()
|
||||
end
|
||||
end
|
||||
|
||||
for el <- @nonvoid_elements do
|
||||
@doc """
|
||||
#{File.read!("./tmp/docs/#{el}.txt")}
|
||||
|
@ -79,8 +35,8 @@ defmodule Dsl.Tags do
|
|||
el = unquote(el)
|
||||
|
||||
quote do
|
||||
Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), [])
|
||||
Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
Dsl.Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), [])
|
||||
Dsl.Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -89,9 +45,9 @@ defmodule Dsl.Tags do
|
|||
el = unquote(el)
|
||||
|
||||
quote do
|
||||
Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), [])
|
||||
Dsl.Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), [])
|
||||
_ = unquote(inner)
|
||||
Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
Dsl.Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -99,8 +55,8 @@ defmodule Dsl.Tags do
|
|||
el = unquote(el)
|
||||
|
||||
quote do
|
||||
Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), unquote(attrs_or_content_or_block))
|
||||
Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
Dsl.Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), unquote(attrs_or_content_or_block))
|
||||
Dsl.Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -110,9 +66,9 @@ defmodule Dsl.Tags do
|
|||
|
||||
quote do
|
||||
attrs = unquote(attrs)
|
||||
Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), attrs)
|
||||
Dsl.Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), attrs)
|
||||
_ = unquote(inner)
|
||||
Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
Dsl.Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -121,9 +77,9 @@ defmodule Dsl.Tags do
|
|||
|
||||
quote do
|
||||
attrs = unquote(attrs)
|
||||
Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), attrs)
|
||||
Dsl.Utils.put_open_tag(var!(buff, Dsl.Tags), unquote(el), attrs)
|
||||
text unquote(content)
|
||||
Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
Dsl.Utils.put_close_tag(var!(buff, Dsl.Tags), unquote(el))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -139,122 +95,11 @@ defmodule Dsl.Tags do
|
|||
quote do
|
||||
attrs = unquote(attrs)
|
||||
|
||||
Utils.put_buffer(
|
||||
Dsl.Utils.put_buffer(
|
||||
var!(buff, Dsl.Tags),
|
||||
"<#{unquote(el)}#{Utils.compile_attrs(attrs)}>"
|
||||
"<#{unquote(el)}#{Dsl.Utils.compile_attrs(attrs)}>"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Emits a text node into the markup.
|
||||
|
||||
```
|
||||
htm do
|
||||
div do
|
||||
text "Hello, world!"
|
||||
end
|
||||
end
|
||||
|
||||
# {:safe, "<div>Hello, world!</div>"}
|
||||
```
|
||||
"""
|
||||
defmacro text(text) do
|
||||
quote do
|
||||
Utils.put_buffer(
|
||||
var!(buff, Dsl.Tags),
|
||||
unquote(text) |> to_string |> HTML.html_escape() |> HTML.safe_to_string()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Emits a Phoenix partial into the markup
|
||||
|
||||
```
|
||||
htm do
|
||||
html lang: "en" do
|
||||
head do
|
||||
title "MyApp"
|
||||
|
||||
link rel: "stylesheet", href: Routes.static_path(@conn, "/css/app.css")
|
||||
end
|
||||
|
||||
body do
|
||||
main role: "main", class: "container" do
|
||||
p get_flash(@conn, :info), class: "alert alert-info", role: "alert"
|
||||
p get_flash(@conn, :error), class: "alert alert-danger", role: "alert"
|
||||
|
||||
partial render(@view_module, @view_template, assigns)
|
||||
end
|
||||
|
||||
script type: "text/javascript", src: Routes.static_path(@conn, "/js/app.js")
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
"""
|
||||
defmacro partial(partial) do
|
||||
quote do
|
||||
Utils.put_buffer(
|
||||
var!(buff, Dsl.Tags),
|
||||
unquote(partial) |> Utils.from_safe()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Defines a custom component.
|
||||
|
||||
```
|
||||
defcomponent :flex do
|
||||
div id: @id, class: "flex" do
|
||||
@children
|
||||
end
|
||||
end
|
||||
|
||||
htm do
|
||||
flex id: "my-flex" do
|
||||
div "Item 1"
|
||||
div "Item 2"
|
||||
div "Item 3"
|
||||
end
|
||||
end
|
||||
|
||||
# {:safe, "<div id=\"my-flex\" class=\"flex\">
|
||||
# <div>Item 1</div>
|
||||
# <div>Item 2</div>
|
||||
# <div>Item 3</div>
|
||||
# </div>"}
|
||||
```
|
||||
"""
|
||||
defmacro defcomponent(name, [do: _] = block) do
|
||||
quote do
|
||||
defmacro unquote(name)(props \\ []) do
|
||||
outer = unquote(Macro.escape(block))
|
||||
name = unquote(name)
|
||||
|
||||
{inner, props} = Keyword.pop(props, :do, nil)
|
||||
|
||||
quote do
|
||||
unquote(name)(unquote(props), unquote(inner))
|
||||
end
|
||||
end
|
||||
|
||||
defmacro unquote(name)(props, inner) do
|
||||
import Kernel, except: [div: 2]
|
||||
|
||||
outer =
|
||||
unquote(Macro.escape(block))
|
||||
|> Macro.prewalk(&Utils.insert_props(&1, [{:children, inner} | props]))
|
||||
|
||||
name = unquote(name)
|
||||
|
||||
quote do
|
||||
unquote(outer)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Reference in a new issue