defmodule Pile do @moduledoc ~S""" This library allows representing HTML in Elixir. It uses tuples to represent elements, and maps to represent element attributes: iex> {:html, ...> {:head, [ ...> {:title, "Hello World"}, ...> {:link, %{rel: "stylesheet", href: "..."}}, ...> ]} ...> } ...> |> Pile.to_html() ...> "Hello World" See `Pile.to_html/2` for more details about the syntax """ @default_options [pretty: false, doctype: false, iodata: false] @spec to_html(input :: keyword(), options :: keyword()) :: String.t() @doc ~S""" Converts a tuple or list of tuples into an HTML string ## Options: - `pretty`: Passing `true` causes the HTML output to be pretty-printed. Defaults to `false`. - `doctype`: Prepend the `` to the resulting string. Defaults to `false`. - `iodata`: Return the HTML as iodata. Defaults to `false`. ## Syntax: A tuple begining with an atom represents an HTML element: iex> {:div} |> Pile.to_html() "
" An element can have a child: iex> {:div, {:p}} |> Pile.to_html() "

" If an element has more than one child, place the children in a list: iex> {:div, [{:p}, {:p}]} |> Pile.to_html() "

" Lists are automatically flattened, which is particularly useful for iteration & composition: iex> {:div, [ ...> {:p}, ...> 1..2 |> Enum.map(fn _ -> {:span} end) ...> ]} |> Pile.to_html() "

" Strings are HTML-escaped, then rendered as text: iex> {:div, "hello"} |> Pile.to_html() "
hello
" iex> {:div, ""} |> Pile.to_html() "
<span>
" To bypass HTML escaping, using the special `_rawtext` element: iex> {:div, {:_rawtext, ""}} |> Pile.to_html() "
" An element may have attributes. These are represented as a map, and if present must come right after the element name: iex> {:div, %{class: "container"}} |> Pile.to_html() "
" iex> {:div, %{class: "container"}, ["foo", {:p}]} |> Pile.to_html() "
foo

" An attribute with a boolean value is treated as an [HTML boolean attribute](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML) iex> {:div, %{readonly: true}} |> Pile.to_html() "
" iex> {:div, %{readonly: false}} |> Pile.to_html() "
" """ def to_html(tuple, opts \\ @default_options) when is_tuple(tuple) do opts = Keyword.validate!(opts, @default_options) normalized = Pile.Normalize.run(tuple) html = Pile.Visitor.traverse(normalized, Pile.Visitor.Serializer, opts) html = if opts[:doctype] do ["" | html] else html end if opts[:iodata] do html else IO.iodata_to_binary(html) end end end