Packages

A shadcn/ui-inspired UI library for Gleam and Lustre

Current section

Files

Jump to
glizzy src ui tree.gleam
Raw

src/ui/tree.gleam

// MIT License
// Copyright (c) 2026 Koncreate
// See LICENSE for details
import gleam/string
import lustre/attribute.{type Attribute, attribute, class, role}
import lustre/element.{type Element}
import lustre/element/html
import ui/size as size_helpers
pub type Variant {
Default
Muted
}
pub type Size {
Small
Medium
Large
}
pub type SelectionMode {
None
Single
Multiple
}
pub fn tree(
attributes: List(Attribute(a)),
children: List(Element(a)),
) -> Element(a) {
html.div(
[
class("overflow-auto rounded-md border border-input bg-background"),
role("tree"),
attribute("tabindex", "0"),
attribute("aria-multiselectable", "false"),
attribute("aria-activedescendant", ""),
..attributes
],
children,
)
}
pub fn tree_item(
attributes: List(Attribute(a)),
children: List(Element(a)),
) -> Element(a) {
html.div(
[
role("treeitem"),
attribute("aria-selected", "false"),
attribute("tabindex", "0"),
..attributes
],
children,
)
}
pub fn tree_item_content(
attributes: List(Attribute(a)),
children: List(Element(a)),
) -> Element(a) {
html.div(
[
class(
[
"flex items-center gap-1 py-1 px-2 rounded-sm text-sm cursor-pointer",
"outline-none",
"hover:bg-accent hover:text-accent-foreground",
"focus:bg-accent focus:text-accent-foreground",
]
|> string.join(" "),
),
..attributes
],
children,
)
}
pub fn tree_item_label(
attributes: List(Attribute(a)),
children: List(Element(a)),
) -> Element(a) {
html.span([class("flex-1"), ..attributes], children)
}
pub fn tree_group(
attributes: List(Attribute(a)),
children: List(Element(a)),
) -> Element(a) {
html.div([class("pl-4"), role("group"), ..attributes], children)
}
pub fn tree_node(
attributes: List(Attribute(a)),
children: List(Element(a)),
) -> Element(a) {
html.div([class("flex items-center gap-1"), ..attributes], children)
}
pub fn tree_expand_button(
is_expanded: Bool,
attributes: List(Attribute(a)),
children: List(Element(a)),
) -> Element(a) {
html.button(
[
class(
"h-4 w-4 p-0 flex items-center justify-center rounded-sm hover:bg-accent transition-transform duration-200",
),
attribute("type", "button"),
attribute("aria-label", case is_expanded {
True -> "Collapse"
False -> "Expand"
}),
attribute("aria-expanded", case is_expanded {
True -> "true"
False -> "false"
}),
..attributes
],
children,
)
}
pub fn variant(v: Variant) -> Attribute(a) {
case v {
Default -> class("border-input bg-background")
Muted -> class("border-input bg-muted")
}
}
pub fn size(s: Size) -> Attribute(a) {
case s {
Small -> class(size_helpers.text_class(size_helpers.Small))
Medium -> class(size_helpers.text_class(size_helpers.Medium))
Large -> class(size_helpers.text_class(size_helpers.Large))
}
}
pub fn selection_mode(m: SelectionMode) -> Attribute(a) {
case m {
None -> attribute("aria-multiselectable", "false")
Single -> attribute("aria-multiselectable", "false")
Multiple -> attribute("aria-multiselectable", "true")
}
}
pub fn aria_label(label: String) -> Attribute(a) {
attribute("aria-label", label)
}
pub fn activedescendant(id: String) -> Attribute(a) {
attribute("aria-activedescendant", id)
}
pub fn expanded(is_expanded: Bool) -> Attribute(a) {
attribute("aria-expanded", case is_expanded {
True -> "true"
False -> "false"
})
}