///|
pub struct InputOptions {
disabled : Bool
read_only : Bool
invalid : Bool
placeholder : String
maxlength : Int
input_type : @minimoon.InputType
password : Bool
focus : Bool
name : String
selection_start : Int?
selection_end : Int?
} derive(Eq, Debug)
///|
pub fn input_options(
disabled? : Bool = false,
read_only? : Bool = false,
invalid? : Bool = false,
placeholder? : String = "",
maxlength? : Int = 140,
input_type? : @minimoon.InputType = @minimoon.TextInput,
password? : Bool = false,
focus? : Bool = false,
name? : String = "",
selection_start? : Int,
selection_end? : Int,
) -> InputOptions {
guard maxlength >= -1 else { abort("maxlength must be -1 or nonnegative") }
{
disabled,
read_only,
invalid,
placeholder,
maxlength,
input_type,
password,
focus,
name,
selection_start,
selection_end,
}
}
///|
/// Pure controlled input, following RUI's input/on_input authoring convention.
/// `focused` controls the focus style; `options.focus` requests native focus.
/// No state is allocated here, so this builder is safe inside a Val view.
pub fn input(
id~ : String,
value~ : String,
on_input~ : @minimoon.Emit[String],
options? : InputOptions = input_options(),
focused? : Bool = false,
on_confirm? : @minimoon.Emit[String],
semantics? : @minimoon.Semantics,
on_focus? : @minimoon.Emit[@minimoon.FocusDetail],
on_blur? : @minimoon.Emit[@minimoon.InputBlurDetail],
) -> @minimoon.Node {
@minimoon.input(
id~,
event_key=id + "/input",
class="mmui-input " +
(if focused { "mmui-focused " } else { "" }) +
(if options.read_only { "mmui-readonly " } else { "" }) +
(if options.invalid { "mmui-invalid" } else { "" }),
value~,
on_input=if options.disabled || options.read_only {
@minimoon.Emit(_ => @minimoon.none)
} else {
on_input
},
input_type=options.input_type,
maxlength=options.maxlength,
password=options.password,
placeholder=options.placeholder,
name=options.name,
disabled=options.disabled || options.read_only,
selection_start?=options.selection_start,
selection_end?=options.selection_end,
focus=options.focus,
on_confirm?,
semantics?,
on_focus?,
on_blur?,
)
}
///|
/// A convenience input that owns its value and focus for this component lifetime.
pub fn input_stateful(
id~ : String,
initial_value? : String = "",
options? : InputOptions = input_options(),
on_confirm? : @minimoon.Emit[String],
semantics? : @minimoon.Semantics,
on_focus? : @minimoon.Emit[@minimoon.FocusDetail],
on_blur? : @minimoon.Emit[@minimoon.InputBlurDetail],
) -> @minimoon.Val[@minimoon.Node] {
let (value, update) = @minimoon.create_variable(initial_value)
input_with_focus(
id~,
value~,
on_input=update.map(value => _ => value),
options~,
on_confirm?,
semantics?,
on_focus?,
on_blur?,
)
}
///|
/// Pure controlled textarea; native focus/blur details remain typed.
pub fn textarea(
id~ : String,
value~ : String,
on_input~ : @minimoon.Emit[String],
options? : InputOptions = input_options(),
focused? : Bool = false,
on_confirm? : @minimoon.Emit[String],
semantics? : @minimoon.Semantics,
on_focus? : @minimoon.Emit[@minimoon.FocusDetail],
on_blur? : @minimoon.Emit[@minimoon.TextareaBlurDetail],
) -> @minimoon.Node {
@minimoon.textarea(
id~,
input_key=id + "/input",
class="mmui-textarea " +
(if focused { "mmui-focused " } else { "" }) +
(if options.read_only { "mmui-readonly " } else { "" }) +
(if options.invalid { "mmui-invalid" } else { "" }),
value~,
on_input=if options.disabled || options.read_only {
@minimoon.Emit(_ => @minimoon.none)
} else {
on_input
},
maxlength=options.maxlength,
placeholder=options.placeholder,
name=options.name,
disabled=options.disabled || options.read_only,
focus=options.focus,
on_confirm?,
semantics?,
on_focus?,
on_blur?,
)
}
///|
/// A convenience textarea that owns its value and native focus state.
pub fn textarea_stateful(
id~ : String,
initial_value? : String = "",
options? : InputOptions = input_options(),
on_confirm? : @minimoon.Emit[String],
semantics? : @minimoon.Semantics,
on_focus? : @minimoon.Emit[@minimoon.FocusDetail],
on_blur? : @minimoon.Emit[@minimoon.TextareaBlurDetail],
) -> @minimoon.Val[@minimoon.Node] {
let (value, update) = @minimoon.create_variable(initial_value)
textarea_with_focus(
id~,
value~,
on_input=update.map(value => _ => value),
options~,
on_confirm?,
semantics?,
on_focus?,
on_blur?,
)
}
///|
fn[T] input_focus_event(
focused : Bool,
update : @minimoon.Emit[(Bool) -> Bool],
callback : @minimoon.Emit[T]?,
) -> @minimoon.Emit[T] {
@minimoon.Emit(detail => {
@minimoon.batch([
update(_ => focused),
callback.map(emit => emit(detail)).unwrap_or(@minimoon.none),
])
})
}
///|
fn input_with_focus(
id~ : String,
value~ : @minimoon.Val[String],
on_input~ : @minimoon.Emit[String],
options~ : InputOptions,
on_confirm? : @minimoon.Emit[String],
semantics? : @minimoon.Semantics,
on_focus? : @minimoon.Emit[@minimoon.FocusDetail],
on_blur? : @minimoon.Emit[@minimoon.InputBlurDetail],
) -> @minimoon.Val[@minimoon.Node] {
let (focused, update_focus) = @minimoon.create_variable(false)
@minimoon.Val::view2(value, focused, (value, focused) => {
input(
id~,
value~,
on_input~,
options~,
focused~,
on_confirm?,
semantics?,
on_focus=input_focus_event(true, update_focus, on_focus),
on_blur=input_focus_event(false, update_focus, on_blur),
)
})
}
///|
fn textarea_with_focus(
id~ : String,
value~ : @minimoon.Val[String],
on_input~ : @minimoon.Emit[String],
options~ : InputOptions,
on_confirm? : @minimoon.Emit[String],
semantics? : @minimoon.Semantics,
on_focus? : @minimoon.Emit[@minimoon.FocusDetail],
on_blur? : @minimoon.Emit[@minimoon.TextareaBlurDetail],
) -> @minimoon.Val[@minimoon.Node] {
let (focused, update_focus) = @minimoon.create_variable(false)
@minimoon.Val::view2(value, focused, (value, focused) => {
textarea(
id~,
value~,
on_input~,
options~,
focused~,
on_confirm?,
semantics?,
on_focus=input_focus_event(true, update_focus, on_focus),
on_blur=input_focus_event(false, update_focus, on_blur),
)
})
}