///|
struct CommandScope {
id : String
query : String
update_query : @minimoon.Emit[String]
select : @minimoon.Emit[(String, @minimoon.Cmd)]
filter : (String, String, Array[String]) -> Bool
should_filter : Bool
open : Bool
show : @minimoon.Cmd
close : @minimoon.Cmd
}
///|
pub fn command_query(scope : CommandScope) -> String {
scope.query
}
///|
pub fn command_is_open(scope : CommandScope) -> Bool {
scope.open
}
///|
pub fn command_open(scope : CommandScope) -> @minimoon.Cmd {
scope.show
}
///|
pub fn command_close(scope : CommandScope) -> @minimoon.Cmd {
scope.close
}
///|
pub fn command_input(
scope : CommandScope,
placeholder? : String = "Search commands",
disabled? : Bool = false,
) -> @minimoon.Node {
@minimoon.input(
event_key=scope.id + "/query",
value=scope.query,
disabled~,
on_input=@minimoon.Emit(value => {
if disabled {
@minimoon.none
} else {
(scope.update_query)(value)
}
}),
placeholder~,
)
}
///|
pub fn[C : @minimoon.IsChildren] command_item(
scope : CommandScope,
value~ : String,
children : C,
keywords? : Array[String] = [],
disabled? : Bool = false,
force_mount? : Bool = false,
on_select? : @minimoon.Emit[String],
on_click? : @minimoon.Cmd,
) -> @minimoon.Node {
if !force_mount &&
scope.should_filter &&
!(scope.filter)(value, scope.query, keywords) {
return @minimoon.nothing()
}
@minimoon.button_children(
event_key=scope.id + "/item/" + value,
disabled~,
on_tap=if disabled {
@minimoon.none
} else {
(scope.select)(
(
value,
@minimoon.batch([
on_select.map(emit => emit(value)).unwrap_or(@minimoon.none),
on_click.unwrap_or(@minimoon.none),
]),
),
)
},
children,
)
}
///|
fn default_command_filter(
value : String,
query : String,
keywords : Array[String],
) -> Bool {
value.to_lower().contains(query.to_lower()) ||
keywords.any(keyword => keyword.to_lower().contains(query.to_lower()))
}
///|
pub fn command_provider_controlled(
id~ : String,
query~ : @minimoon.Val[String],
on_query_change~ : @minimoon.Emit[String],
render~ : (CommandScope) -> @minimoon.Node,
on_value_select? : @minimoon.Emit[String],
should_filter? : Bool = true,
filter? : (String, String, Array[String]) -> Bool = default_command_filter,
) -> @minimoon.Val[@minimoon.Node] {
query.map(query => {
let scope = CommandScope::{
id,
query,
update_query: on_query_change,
select: @minimoon.Emit(request => {
let (value, command) = request
@minimoon.batch([
on_value_select.map(emit => emit(value)).unwrap_or(@minimoon.none),
command,
])
}),
should_filter,
filter,
open: true,
show: @minimoon.none,
close: @minimoon.none,
}
ui_container("command", id~, [render(scope)])
})
}
///|
pub fn command_provider(
id~ : String,
render~ : (CommandScope) -> @minimoon.Node,
initial_query? : String = "",
on_value_select? : @minimoon.Emit[String],
on_query_change? : @minimoon.Emit[String],
should_filter? : Bool = true,
filter? : (String, String, Array[String]) -> Bool = default_command_filter,
) -> @minimoon.Val[@minimoon.Node] {
let (query, emit) = @minimoon.create_variable(initial_query)
command_provider_controlled(
id~,
query~,
on_query_change=@minimoon.Emit(query => {
@minimoon.batch([
emit(_ => query),
on_query_change.map(emit => emit(query)).unwrap_or(@minimoon.none),
])
}),
render~,
on_value_select?,
should_filter~,
filter~,
)
}
///|
pub fn command_dialog(
context : UiContext,
id~ : String,
title~ : String,
trigger_label~ : String,
render~ : (CommandScope) -> @minimoon.Node,
initial_query? : String = "",
initially_open? : Bool = false,
should_filter? : Bool = true,
filter? : (String, String, Array[String]) -> Bool = default_command_filter,
close_on_select? : Bool = true,
close_on_overlay? : Bool = true,
on_query_change? : @minimoon.Emit[String],
on_value_select? : @minimoon.Emit[String],
on_open_change? : @minimoon.Emit[Bool],
) -> @minimoon.Val[@minimoon.Node] {
let (query, set_query) = @minimoon.create_variable(initial_query)
let (open, set_open) = @minimoon.create_variable(initially_open)
let change_open = @minimoon.Emit(open => {
@minimoon.batch([
set_open(_ => open),
on_open_change.map(emit => emit(open)).unwrap_or(@minimoon.none),
])
})
let select = form_dispatch(
@minimoon.Val::map2(open, context.visible, (open, visible) => {
open && visible
}),
(active, request : (String, @minimoon.Cmd)) => {
if !active {
return @minimoon.none
}
let (value, command) = request
@minimoon.batch([
on_value_select.map(emit => emit(value)).unwrap_or(@minimoon.none),
command,
if close_on_select {
change_open(false)
} else {
@minimoon.none
},
])
},
)
let content = @minimoon.Val::view2(query, open, (query, open) => {
render(CommandScope::{
id,
query,
update_query: @minimoon.Emit(query => {
@minimoon.batch([
set_query(_ => query),
on_query_change.map(emit => emit(query)).unwrap_or(@minimoon.none),
])
}),
select,
filter,
should_filter,
open,
show: change_open(true),
close: change_open(false),
})
})
overlay_controlled(
context,
id~,
title~,
trigger_label~,
content~,
open~,
on_change=change_open,
close_on_overlay~,
)
}