Current section
Files
Jump to
Current section
Files
src/sketch/lustre/element.gleam
import gleam/int
import gleam/list
import gleam/option
import gleam/pair
import lustre/attribute.{type Attribute}
import lustre/element as el
import lustre/internals/vdom
import sketch.{type Cache}
pub opaque type Element(msg) {
Nothing
Text(content: String)
Fragment(key: String, children: List(Element(msg)))
Map(subtree: fn() -> Element(msg))
Element(
key: String,
namespace: String,
tag: String,
class: option.Option(sketch.Class),
attributes: List(Attribute(msg)),
children: List(Element(msg)),
)
}
pub fn none() {
Nothing
}
pub fn text(content) {
Text(content)
}
pub fn element(
tag tag: String,
class class: sketch.Class,
attributes attributes: List(Attribute(msg)),
children children: List(Element(msg)),
) {
let class = option.Some(class)
Element("", "", tag, class, attributes, children)
}
pub fn element_(
tag tag: String,
attributes attributes: List(Attribute(msg)),
children children: List(Element(msg)),
) {
Element("", "", tag, option.None, attributes, children)
}
pub fn namespaced(
namespace namespace: String,
tag tag: String,
class class: sketch.Class,
attributes attributes: List(Attribute(msg)),
children children: List(Element(msg)),
) {
let class = option.Some(class)
Element("", namespace, tag, class, attributes, children)
}
pub fn namespaced_(
namespace namespace: String,
tag tag: String,
attributes attributes: List(Attribute(msg)),
children children: List(Element(msg)),
) {
Element("", namespace, tag, option.None, attributes, children)
}
pub fn fragment(children: List(Element(msg))) {
Fragment("", children)
}
pub fn keyed(
element: fn(List(Element(msg))) -> Element(msg),
children: List(#(String, Element(msg))),
) {
element({
use #(key, child) <- list.map(children)
do_keyed(child, key)
})
}
fn do_keyed(element: Element(msg), key: String) {
case element {
Nothing -> Nothing
Text(content) -> Text(content)
Map(subtree) -> Map(fn() { do_keyed(subtree(), key) })
Fragment(_, children) ->
children
|> list.index_map(fn(element, idx) {
case element {
Element(el_key, _, _, _, _, _) -> {
let new_key = case el_key {
"" -> key <> "-" <> int.to_string(idx)
_ -> key <> "-" <> el_key
}
do_keyed(element, new_key)
}
_ -> do_keyed(element, key)
}
})
|> Fragment(key, _)
Element(_, namespace, tag, attributes, children, styles) ->
Element(key, namespace, tag, attributes, children, styles)
}
}
pub fn map(element: Element(a), mapper: fn(a) -> b) {
case element {
Nothing -> Nothing
Text(content) -> Text(content)
Map(subtree) -> Map(fn() { map(subtree(), mapper) })
Fragment(key, children) -> Fragment(key, list.map(children, map(_, mapper)))
Element(key, namespace, tag, class, attributes, children) -> {
let attributes = list.map(attributes, attribute.map(_, mapper))
let children = list.map(children, map(_, mapper))
Element(key, namespace, tag, class, attributes, children)
}
}
}
pub fn styled(element: el.Element(msg)) {
case element {
vdom.Map(subtree) -> Map(fn() { styled(subtree()) })
vdom.Text(content) -> Text(content)
vdom.Fragment(elements, key) -> Fragment(key, list.map(elements, styled))
vdom.Element(key, namespace, tag, attrs, children, _, _) -> {
let class = option.None
Element(key, namespace, tag, class, attrs, list.map(children, styled))
}
}
}
pub fn unstyled(cache: Cache, element: Element(msg)) {
case element {
Nothing -> #(cache, el.none())
Text(content) -> #(cache, el.text(content))
Map(subtree) -> unstyled(cache, subtree())
Fragment(key, children) ->
unstyled_children(cache, children)
|> pair.map_second(fn(node) { vdom.Fragment(node, key) })
Element(key, namespace, tag, class, attributes, children) -> {
let class = option.map(class, sketch.class_name(_, cache))
let class_name = option.map(class, pair.second)
let cache = option.map(class, pair.first) |> option.unwrap(cache)
let #(cache, children) = unstyled_children(cache, children)
let attributes = case class_name {
option.None -> attributes
option.Some(class_name) -> {
let class_name = attribute.class(class_name)
[class_name, ..attributes]
}
}
#(cache, case el.element(tag, attributes, children) {
vdom.Element(_, _, t, a, c, s, v) ->
vdom.Element(key, namespace, t, a, c, s, v)
e -> e
})
}
}
}
fn unstyled_children(cache, children) {
list.fold(list.reverse(children), #(cache, []), fn(acc, child) {
let #(cache, children) = acc
let #(cache, child) = unstyled(cache, child)
#(cache, [child, ..children])
})
}