2019-07-02 02:48:51 +00:00
|
|
|
defmodule Temple do
|
2019-04-15 02:35:42 +00:00
|
|
|
defmacro __using__(_) do
|
2019-04-15 01:44:39 +00:00
|
|
|
quote do
|
2019-07-12 19:46:45 +00:00
|
|
|
import Temple
|
2019-07-02 02:48:51 +00:00
|
|
|
import Temple.Tags
|
|
|
|
import Temple.Form
|
|
|
|
import Temple.Link
|
2019-04-15 01:44:39 +00:00
|
|
|
end
|
|
|
|
end
|
2019-05-11 16:49:34 +00:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
Creates a markup context.
|
|
|
|
|
2019-07-09 02:29:41 +00:00
|
|
|
All tags must be called inside of a `Temple.temple/1` block.
|
2019-05-11 16:49:34 +00:00
|
|
|
|
|
|
|
Returns a safe result of the form `{:safe, result}`
|
|
|
|
|
|
|
|
## Example
|
|
|
|
|
|
|
|
```
|
|
|
|
team = ["Alice", "Bob", "Carol"]
|
|
|
|
|
2019-07-09 02:29:41 +00:00
|
|
|
temple do
|
2019-05-11 16:49:34 +00:00
|
|
|
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>"}
|
|
|
|
```
|
|
|
|
"""
|
2019-07-09 02:29:41 +00:00
|
|
|
defmacro temple([do: block] = _block) do
|
2019-05-11 16:49:34 +00:00
|
|
|
quote do
|
|
|
|
import Kernel, except: [div: 2]
|
|
|
|
|
2019-07-08 02:26:32 +00:00
|
|
|
with {:ok, var!(buff, Temple.Tags)} <- Temple.Utils.start_buffer([]) do
|
2019-06-30 18:28:44 +00:00
|
|
|
unquote(block)
|
2019-05-11 16:49:34 +00:00
|
|
|
|
2019-07-02 02:48:51 +00:00
|
|
|
markup = Temple.Utils.get_buffer(var!(buff, Temple.Tags))
|
2019-05-11 16:49:34 +00:00
|
|
|
|
2019-07-02 02:48:51 +00:00
|
|
|
:ok = Temple.Utils.stop_buffer(var!(buff, Temple.Tags))
|
2019-05-11 16:49:34 +00:00
|
|
|
|
2019-07-08 02:26:32 +00:00
|
|
|
Temple.Utils.join_and_escape(markup)
|
|
|
|
end
|
2019-05-11 16:49:34 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Emits a text node into the markup.
|
|
|
|
|
|
|
|
```
|
2019-07-09 02:29:41 +00:00
|
|
|
temple do
|
2019-05-11 16:49:34 +00:00
|
|
|
div do
|
|
|
|
text "Hello, world!"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# {:safe, "<div>Hello, world!</div>"}
|
|
|
|
```
|
|
|
|
"""
|
|
|
|
defmacro text(text) do
|
|
|
|
quote do
|
2019-07-02 02:48:51 +00:00
|
|
|
Temple.Utils.put_buffer(
|
|
|
|
var!(buff, Temple.Tags),
|
2019-05-11 16:49:34 +00:00
|
|
|
unquote(text) |> to_string |> Phoenix.HTML.html_escape() |> Phoenix.HTML.safe_to_string()
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Emits a Phoenix partial into the markup.
|
|
|
|
|
|
|
|
```
|
2019-07-09 02:29:41 +00:00
|
|
|
temple do
|
2019-05-11 16:49:34 +00:00
|
|
|
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
|
2019-07-02 02:48:51 +00:00
|
|
|
Temple.Utils.put_buffer(
|
|
|
|
var!(buff, Temple.Tags),
|
|
|
|
unquote(partial) |> Temple.Utils.from_safe()
|
2019-05-11 16:49:34 +00:00
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Defines a custom component.
|
|
|
|
|
|
|
|
Components are the primary way to extract partials and markup helpers.
|
|
|
|
|
|
|
|
## Assigns
|
|
|
|
|
2019-07-04 04:16:29 +00:00
|
|
|
Components accept a keyword list or a map of assigns and can be referenced in the body of the component by a module attribute of the same name.
|
2019-05-11 16:49:34 +00:00
|
|
|
|
|
|
|
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
|
2019-06-01 04:02:49 +00:00
|
|
|
|
2019-05-11 16:49:34 +00:00
|
|
|
```
|
|
|
|
defcomponent :flex do
|
|
|
|
div id: @id, class: "flex" do
|
|
|
|
@children
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-07-09 02:29:41 +00:00
|
|
|
temple do
|
2019-05-11 16:49:34 +00:00
|
|
|
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
|
2019-07-04 04:16:29 +00:00
|
|
|
defmacro unquote(name)() do
|
2019-05-11 16:49:34 +00:00
|
|
|
outer = unquote(Macro.escape(block))
|
2019-07-04 04:16:29 +00:00
|
|
|
|
|
|
|
quote do
|
|
|
|
_ = unquote(outer)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmacro unquote(name)(props_or_block)
|
|
|
|
|
|
|
|
defmacro unquote(name)([{:do, inner}]) do
|
2019-05-11 16:49:34 +00:00
|
|
|
name = unquote(name)
|
|
|
|
|
2019-07-04 04:16:29 +00:00
|
|
|
quote do
|
|
|
|
unquote(name)([], unquote(inner))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmacro unquote(name)(props) do
|
|
|
|
name = unquote(name)
|
2019-05-11 16:49:34 +00:00
|
|
|
|
|
|
|
quote do
|
2019-07-04 04:16:29 +00:00
|
|
|
unquote(name)(unquote(props), nil)
|
2019-05-11 16:49:34 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmacro unquote(name)(props, inner) do
|
|
|
|
outer =
|
|
|
|
unquote(Macro.escape(block))
|
2019-07-04 04:16:29 +00:00
|
|
|
|> Macro.prewalk(&Temple.Utils.insert_props(&1, props, inner))
|
2019-05-11 16:49:34 +00:00
|
|
|
|
|
|
|
name = unquote(name)
|
|
|
|
|
|
|
|
quote do
|
2019-07-04 04:16:29 +00:00
|
|
|
_ = unquote(outer)
|
2019-05-11 16:49:34 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-04-15 01:44:39 +00:00
|
|
|
end
|