///|
fn find_menu_item(
id : String,
items : Array[MenuItem],
key : String,
) -> MenuItem? {
for item in items {
let path = id + "/" + item.key
if path == key {
return Some(item)
}
if !item.disabled {
match item.kind {
Branch(children) | Group(children) =>
if find_menu_item(path, children, key) is Some(found) {
return Some(found)
}
_ => ()
}
}
}
None
}
///|
fn menu_dispatch(
id : String,
items : @minimoon.Val[Array[MenuItem]],
close : @minimoon.Cmd,
open? : @minimoon.Val[Bool] = @minimoon.Val::constant(true),
) -> (@minimoon.Val[Array[String]], @minimoon.Emit[String]) {
@minimoon.create_state_with_input(
input=@minimoon.Val::map2(items, open, (items, open) => (items, open)),
init=(_, _) => @minimoon.no_cmd(([] : Array[String])),
update=(expanded, input, key : String, _) => {
let (items, open) = input
if !open {
return @minimoon.no_cmd(expanded)
}
guard find_menu_item(id, items, key) is Some(item) && !item.disabled else {
return @minimoon.no_cmd(expanded)
}
match item.kind {
Branch(_) => {
let next = expanded.copy()
if next.contains(key) {
@minimoon.no_cmd(next.filter(value => value != key))
} else {
next.push(key)
@minimoon.no_cmd(next)
}
}
Heading | Divider | Group(_) => @minimoon.no_cmd(expanded)
_ => {
let command = match item.kind {
Action(command) => command
Check(checked, emit) => emit(!checked)
Radio(_, command) => command
_ => @minimoon.none
}
let should_close = item.presentation.unwrap_or(
default_menu_presentation(item.label),
).close_on_select.unwrap_or(item.kind is Action(_))
(
expanded,
@minimoon.batch([
command,
if should_close {
close
} else {
@minimoon.none
},
]),
)
}
}
},
)
}