Packages
lustre
3.0.8
5.7.1
5.7.0
5.6.0
5.5.2
5.5.1
5.5.0
5.4.0
5.3.5
5.3.4
5.3.3
5.3.2
5.3.1
5.3.0
5.2.1
5.2.0
5.1.1
5.1.0
5.0.3
5.0.2
5.0.1
5.0.0
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.6
4.3.5
4.3.4
4.3.3
4.3.2
4.3.1
4.3.0
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.8
4.1.7
4.1.6
4.1.5
4.1.4
4.1.3
4.1.2
4.1.1
4.1.0
4.0.0
4.0.0-rc1
4.0.0-rc.2
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.2
3.0.0-rc.1
2.0.1
2.0.0
1.3.0
1.2.0
1.1.0
1.0.0
Create HTML templates, single page applications, Web Components, and real-time server components in Gleam!
Current section
Files
Jump to
Current section
Files
src/lustre/element.gleam
//// To read the full documentation for this module, please visit
//// [https://lustre.build/api/lustre/element](https://lustre.build/api/lustre/element)
// IMPORTS ---------------------------------------------------------------------
import gleam/list
import gleam/string
import gleam/string_builder.{StringBuilder}
import lustre/attribute.{Attribute}
// TYPES -----------------------------------------------------------------------
///
pub opaque type Element(msg) {
Text(String)
Element(String, List(Attribute(msg)), List(Element(msg)))
ElementNs(String, List(Attribute(msg)), List(Element(msg)), String)
}
// CONSTRUCTORS ----------------------------------------------------------------
///
pub fn element(
tag: String,
attrs: List(Attribute(msg)),
children: List(Element(msg)),
) -> Element(msg) {
Element(tag, attrs, children)
}
///
pub fn namespaced(
namespace: String,
tag: String,
attrs: List(Attribute(msg)),
children: List(Element(msg)),
) -> Element(msg) {
ElementNs(tag, attrs, children, namespace)
}
///
pub fn text(content: String) -> Element(msg) {
Text(content)
}
fn escape(escaped: String, content: String) -> String {
case content {
"<" <> rest -> escape(escaped <> "<", rest)
">" <> rest -> escape(escaped <> ">", rest)
"&" <> rest -> escape(escaped <> "&", rest)
"\"" <> rest -> escape(escaped <> """, rest)
"'" <> rest -> escape(escaped <> "'", rest)
_ ->
case string.pop_grapheme(content) {
Ok(#(x, xs)) -> escape(escaped <> x, xs)
Error(_) -> escaped
}
}
}
// MANIPULATIONS ---------------------------------------------------------------
///
pub fn map(element: Element(a), f: fn(a) -> b) -> Element(b) {
case element {
Text(content) -> Text(content)
Element(tag, attrs, children) ->
Element(
tag,
list.map(attrs, attribute.map(_, f)),
list.map(children, map(_, f)),
)
ElementNs(tag, attrs, children, namespace) ->
ElementNs(
tag,
list.map(attrs, attribute.map(_, f)),
list.map(children, map(_, f)),
namespace,
)
}
}
// CONVERSIONS -----------------------------------------------------------------
///
pub fn to_string(element: Element(msg)) -> String {
to_string_builder_helper(element, False)
|> string_builder.to_string
}
pub fn to_string_builder(element: Element(msg)) -> StringBuilder {
to_string_builder_helper(element, False)
}
///
pub fn to_string_builder_helper(
element: Element(msg),
raw_text: Bool,
) -> StringBuilder {
case element {
Text(content) if raw_text -> string_builder.from_string(content)
Text(content) -> string_builder.from_string(escape("", content))
Element("area" as tag, attrs, _)
| Element("base" as tag, attrs, _)
| Element("br" as tag, attrs, _)
| Element("col" as tag, attrs, _)
| Element("embed" as tag, attrs, _)
| Element("hr" as tag, attrs, _)
| Element("img" as tag, attrs, _)
| Element("input" as tag, attrs, _)
| Element("link" as tag, attrs, _)
| Element("meta" as tag, attrs, _)
| Element("param" as tag, attrs, _)
| Element("source" as tag, attrs, _)
| Element("track" as tag, attrs, _)
| Element("wbr" as tag, attrs, _) ->
string_builder.from_string("<" <> tag)
|> attrs_to_string_builder(attrs)
|> string_builder.append(">")
Element("style" as tag, attrs, children)
| Element("script" as tag, attrs, children) ->
string_builder.from_string("<" <> tag)
|> attrs_to_string_builder(attrs)
|> string_builder.append(">")
|> children_to_string_builder(children, True)
|> string_builder.append("</" <> tag <> ">")
Element(tag, attrs, children) ->
string_builder.from_string("<" <> tag)
|> attrs_to_string_builder(attrs)
|> string_builder.append(">")
|> children_to_string_builder(children, raw_text)
|> string_builder.append("</" <> tag <> ">")
ElementNs(tag, attrs, children, namespace) ->
string_builder.from_string("<" <> tag)
|> attrs_to_string_builder(attrs)
|> string_builder.append(" xmlns=\"" <> namespace <> "\"")
|> string_builder.append(">")
|> children_to_string_builder(children, raw_text)
|> string_builder.append("</" <> tag <> ">")
}
}
fn attrs_to_string_builder(
html: StringBuilder,
attrs: List(Attribute(msg)),
) -> StringBuilder {
let #(html, class, style) = {
use #(html, class, style), attr <- list.fold(attrs, #(html, "", ""))
case attribute.to_string_parts(attr) {
Ok(#("class", val)) if class == "" -> #(html, val, style)
Ok(#("class", val)) -> #(html, class <> " " <> val, style)
Ok(#("style", val)) if style == "" -> #(html, class, val)
Ok(#("style", val)) -> #(html, class, style <> " " <> val)
Ok(#(key, val)) -> #(
string_builder.append(html, " " <> key <> "=\"" <> val <> "\""),
class,
style,
)
Error(_) -> #(html, class, style)
}
}
case class, style {
"", "" -> html
_, "" -> string_builder.append(html, " class=\"" <> class <> "\"")
"", _ -> string_builder.append(html, " style=\"" <> style <> "\"")
_, _ ->
string_builder.append(
html,
" class=\"" <> class <> "\" style=\"" <> style <> "\"",
)
}
}
fn children_to_string_builder(
html: StringBuilder,
children: List(Element(msg)),
raw_text: Bool,
) -> StringBuilder {
use html, child <- list.fold(children, html)
string_builder.append_builder(html, to_string_builder_helper(child, raw_text))
}