forked from 2sys/phonograph
136 lines
3.5 KiB
Gleam
136 lines
3.5 KiB
Gleam
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()
|
|
}
|
|
}
|