perspective_viewer/components/form/
number_field.rs1use web_sys::{HtmlInputElement, InputEvent};
14use yew::{Callback, Properties, TargetCast, function_component, html};
15
16use crate::components::form::optional_field::OptionalField;
17
18#[derive(Properties, Debug, PartialEq, Clone)]
19pub struct NumberFieldProps {
20 pub label: String,
21 pub current_value: Option<f64>,
22 pub default: f64,
23 pub on_change: Callback<Option<f64>>,
24
25 #[prop_or_default]
26 pub disabled: bool,
27
28 #[prop_or_default]
29 pub min: Option<f64>,
30
31 #[prop_or_default]
32 pub max: Option<f64>,
33
34 #[prop_or_default]
35 pub step: Option<f64>,
36}
37
38#[function_component(NumberField)]
39pub fn number_field(props: &NumberFieldProps) -> yew::Html {
40 let parse_number_input = |event: InputEvent| {
41 Some(
42 event
43 .target_unchecked_into::<HtmlInputElement>()
44 .value_as_number(),
45 )
46 };
47
48 let number_input = html! {
49 <input
50 type="number"
51 class="parameter"
52 min={props.min.map(|val| val.to_string())}
53 max={props.max.map(|val| val.to_string())}
54 step={props.step.map(|val| val.to_string())}
55 value={props.current_value.unwrap_or(props.default).to_string()}
56 oninput={props.on_change.reform(parse_number_input)}
57 />
58 };
59
60 html! {
61 <div class="row">
62 <OptionalField
63 label={props.label.clone()}
64 on_check={props.on_change.reform(|_| None)}
65 checked={props.current_value.map(|val| val != props.default).unwrap_or_default()}
66 disabled={props.disabled}
67 >
68 { number_input }
69 </OptionalField>
70 </div>
71 }
72}