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.ex

230 lines
4.9 KiB
Elixir
Raw Normal View History

2019-07-02 02:48:51 +00:00
defmodule Temple do
alias Temple.Parser
2019-05-11 16:49:34 +00:00
2020-06-16 19:28:21 +00:00
@moduledoc """
2021-01-02 18:21:48 +00:00
Temple syntax is available inside the `temple`, and is compiled into EEx at build time.
2019-05-11 16:49:34 +00:00
## Usage
2019-05-11 16:49:34 +00:00
2020-06-16 19:28:21 +00:00
```elixir
temple do
# You can define attributes by passing a keyword list to the element, the values can be literals or variables.
class = "text-blue"
id = "jumbotron"
2019-05-11 16:49:34 +00:00
2020-06-16 19:28:21 +00:00
div class: class, id: id do
# Text nodes can be emitted as string literals or variables.
"Bob"
2019-05-11 16:49:34 +00:00
2020-06-16 19:28:21 +00:00
id
end
# Attributes that result in boolean values will be emitted as a boolean attribute. Examples of boolean attributes are `disabled` and `checked`.
input type: "text", disabled: true
# <input type="text" disabled>
input type: "text", disabled: false
# <input type="text">
# The class attribute also can take a keyword list of classes to conditionally render, based on the boolean result of the value.
div class: ["text-red-500": false, "text-green-500": true] do
"Alert!"
end
# <div class="text-green-500">Alert!</div>
2020-06-16 19:28:21 +00:00
# if and unless expressions can be used to conditionally render content
if 5 > 0 do
p do
"Greater than 0!"
2019-05-11 16:49:34 +00:00
end
end
2020-06-16 19:28:21 +00:00
unless 5 > 0 do
p do
"Less than 0!"
end
end
# You can loop over items using for comprehensions
for x <- 0..5 do
div do
x
end
end
# You can use multiline anonymous functions, like if you're building a form in Phoenix
form_for @changeset, Routes.user_path(@conn, :create), fn f ->
"Name: "
text_input f, :name
end
2021-01-02 18:21:48 +00:00
# You can explicitly emit a tag by prefixing with the Temple module
2020-06-16 19:28:21 +00:00
Temple.div do
"Foo"
end
# You can also pass children as a do key instead of a block
div do: "Alice", class: "text-yellow"
end
2019-05-11 16:49:34 +00:00
```
## Whitespace Control
By default, Temple will emit internal whitespace into tags, something like this.
```elixir
span do
"Hello, world!"
end
```
```html
<span>
Hello, world!
</span>
```
If you need to create a "tight" tag, you can call the "bang" version of the desired tag.
```elixir
span! do
"Hello, world!"
end
```
```html
<span>Hello, world!</span>
```
## Configuration
### Mode
There are two "modes", `:normal` (the default) and `:live_view`.
In `:live_view` mode, Temple emits markup that uses functions provided by Phoenix LiveView in order to be fully "diff trackable". These LiveView functions have not been released yet, so if you are going to combine Temple with LiveView, you need to use the latest unreleased default branch from GitHub.
You should use `:live_view` mode even if you only have a single LiveView.
2019-05-11 16:49:34 +00:00
```elixir
config :temple, :mode, :normal # default
# or
2019-05-11 16:49:34 +00:00
config :temple, :mode, :live_view
```
2020-06-16 19:28:21 +00:00
### Aliases
2020-06-16 19:28:21 +00:00
You can add an alias for an element if there is a namespace collision with a function. If you are using `Phoenix.HTML`, there will be namespace collisions with the `<link>` and `<label>` elements.
```elixir
config :temple, :aliases,
label: :_label,
link: :_link,
select: :_select
2020-06-16 19:28:21 +00:00
2019-07-09 02:29:41 +00:00
temple do
2020-06-16 19:28:21 +00:00
_label do
"Email"
2019-05-11 16:49:34 +00:00
end
2020-06-16 19:28:21 +00:00
_link href: "/css/site.css"
2019-05-11 16:49:34 +00:00
end
2020-06-16 19:28:21 +00:00
```
This will result in:
2019-05-11 16:49:34 +00:00
2020-06-16 19:28:21 +00:00
```html
<label>
Email
</label>
<link href="/css/site.css">
2019-05-11 16:49:34 +00:00
```
"""
2020-06-16 19:28:21 +00:00
defmacro __using__(_) do
quote location: :keep do
2020-06-16 19:28:21 +00:00
import Temple
2021-05-14 01:28:15 +00:00
require Temple.Component
2019-05-11 16:49:34 +00:00
end
end
2021-01-02 18:21:48 +00:00
@doc """
Context for temple markup.
Returns an EEx string that can be passed into an EEx template engine.
2021-01-02 18:21:48 +00:00
## Usage
```elixir
import Temple
temple do
div class: @class do
"Hello, world!"
end
end
# <div class="<%= @class %>">
# Hello, world!
# </div>
```
"""
2020-06-16 19:28:21 +00:00
defmacro temple([do: block] = _block) do
markup =
block
|> Parser.parse()
|> Enum.map(fn parsed -> Temple.Generator.to_eex(parsed, 0) end)
|> Enum.intersperse("\n")
|> :erlang.iolist_to_binary()
2020-06-16 19:28:21 +00:00
quote location: :keep do
unquote(markup)
2019-05-11 16:49:34 +00:00
end
end
2020-06-16 19:28:21 +00:00
defmacro temple(block) do
quote location: :keep do
unquote(block)
|> Parser.parse()
|> Enum.map(fn parsed -> Temple.Generator.to_eex(parsed, 0) end)
|> Enum.intersperse("\n")
|> :erlang.iolist_to_binary()
2020-06-16 19:28:21 +00:00
end
end
2019-05-11 16:49:34 +00:00
2021-01-02 18:21:48 +00:00
@doc """
Compiles temple markup into a quoted expression using the given EEx Engine.
Returns the same output that Phoenix templates output into the `render/1` function of their view modules.
2021-01-02 18:21:48 +00:00
## Usage
```elixir
require Temple
Temple.compile Phoenix.HTML.Engine do
div class: @class do
"Hello, world!"
end
end
```
"""
defmacro compile(engine, [do: block] = _block) do
markup =
block
|> Parser.parse()
|> Enum.map(fn parsed -> Temple.Generator.to_eex(parsed, 0) end)
|> Enum.intersperse("\n")
|> :erlang.iolist_to_binary()
2020-06-16 19:28:21 +00:00
2021-01-02 18:21:48 +00:00
EEx.compile_string(markup, engine: engine, line: __CALLER__.line, file: __CALLER__.file)
2019-05-11 16:49:34 +00:00
end
2019-04-15 01:44:39 +00:00
end