1
0
Fork 0
forked from 2sys/phonograph
phonograph/webc/src/hoverbar.gleam

137 lines
3.5 KiB
Gleam
Raw Normal View History

import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode
import gleam/list
import gleam/option.{type Option, None, Some}
import lustre/attribute as attr
import lustre/effect.{type Effect}
import lustre/element.{type Element}
import lustre/element/html
import lustre/event
pub type Model {
Model(tab_key: String, control_panel_open: Bool)
}
pub type TabItem {
TabItem(icon: String, label: String, key: String)
}
pub fn init(_) -> #(Model, Effect(Msg)) {
#(Model(tab_key: "", control_panel_open: False), effect.none())
}
pub type Msg {
UserClickedControlBar
UserClickedTabBox
UserClickedTab(String)
SomeoneToggledControlPanel(Bool)
}
pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
case msg {
UserClickedControlBar | UserClickedTabBox -> #(model, show_popover())
UserClickedTab(tab_key) -> #(Model(..model, tab_key:), show_popover())
SomeoneToggledControlPanel(control_panel_open) -> #(
Model(..model, control_panel_open:),
effect.none(),
)
}
}
fn show_popover() -> Effect(Msg) {
use _, root <- effect.before_paint
do_show_popover(root)
}
@external(javascript, "./hoverbar.ffi.mjs", "showPopover")
fn do_show_popover(in _root: Dynamic) -> Nil {
Nil
}
pub fn view(
model model: Model,
map_msg map_msg: fn(Msg) -> msg,
tabs tabs: List(TabItem),
control_bar control_bar: fn(String) -> Element(msg),
control_panel control_panel: fn(String) -> Option(Element(msg)),
) -> Element(msg) {
html.div([attr.class("container-positioner")], [
html.div([attr.class("container")], [
tab_box(model, map_msg, tabs),
view_control_bar(model, map_msg, control_bar(model.tab_key)),
view_control_panel(model, map_msg, control_panel(model.tab_key)),
]),
])
}
fn tab_box(
model: Model,
map_msg: fn(Msg) -> msg,
tabs: List(TabItem),
) -> Element(msg) {
html.ul(
[attr.class("tab-box"), event.on_click(UserClickedTabBox |> map_msg)],
tabs
|> list.map(fn(tab) {
html.li([], [
html.button(
[
attr.type_("button"),
attr.class("tab-box__button"),
case model.tab_key == tab.key {
True -> attr.class("tab-box__button--active")
False -> attr.none()
},
event.on_click(UserClickedTab(tab.key) |> map_msg),
],
[html.img([attr.src(tab.icon), attr.alt(tab.label)])],
),
])
}),
)
}
fn view_control_bar(
model: Model,
map_msg: fn(Msg) -> msg,
inner: Element(msg),
) -> Element(msg) {
html.div(
[
attr.class("control-bar"),
case model.control_panel_open {
True -> attr.class("control-bar--open")
False -> attr.none()
},
event.on_click(UserClickedControlBar |> map_msg),
],
[inner],
)
}
fn view_control_panel(
_: Model,
map_msg: fn(Msg) -> msg,
inner: Option(Element(msg)),
) -> Element(msg) {
case inner {
Some(inner_element) ->
html.div([attr.class("control-panel-positioner")], [
html.div(
[
attr.class("control-panel__container"),
attr.popover("auto"),
event.on("toggle", {
use new_state <- decode.field("newState", decode.string)
decode.success(
SomeoneToggledControlPanel(new_state == "open") |> map_msg,
)
}),
],
[html.div([attr.class("control-panel")], [inner_element])],
),
])
None -> element.none()
}
}