mcai_workflow/components/edit_parameter/
edit_number.rs

1use crate::{ActionButton, Button, Modal, ModalMessage};
2use css_in_rust_next::Style;
3use std::str::FromStr;
4use wasm_bindgen::JsCast;
5use web_sys::{Event, EventTarget, HtmlInputElement};
6use yew::{html, Callback, Component, Context, Html, Properties};
7use yew_feather::{plus::Plus, trash_2::Trash2};
8
9#[derive(PartialEq, Properties)]
10pub struct EditNumberProperties {
11  pub title: String,
12  pub field_name: String,
13  pub event: Callback<EditNumberMessage>,
14  pub value: Option<f64>,
15  pub number_step: f64,
16  pub required: bool,
17}
18
19pub enum EditNumberMessage {
20  Submit(Option<f64>),
21  Cancel,
22}
23
24pub enum InternalMessage {
25  Update(String),
26  Modal(ModalMessage),
27  AddValue,
28  RemoveValue,
29}
30
31pub struct EditNumber {
32  style: Style,
33  value: Option<f64>,
34}
35
36impl Component for EditNumber {
37  type Message = InternalMessage;
38  type Properties = EditNumberProperties;
39
40  fn create(ctx: &Context<Self>) -> Self {
41    let style = Style::create(
42      "Component",
43      concat!(
44        include_str!("edit_style.css"),
45        include_str!("edit_number.css")
46      ),
47    )
48    .unwrap();
49    let value = ctx.props().value;
50    Self { style, value }
51  }
52
53  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
54    match msg {
55      InternalMessage::Update(value) => {
56        self.value = Some(f64::from_str(value.as_str()).unwrap_or_default());
57        false
58      }
59      InternalMessage::Modal(message) => {
60        let event = match message {
61          ModalMessage::Submit | ModalMessage::Update => {
62            Some(EditNumberMessage::Submit(self.value))
63          }
64          ModalMessage::Cancel => Some(EditNumberMessage::Cancel),
65          ModalMessage::Delete => None,
66        };
67
68        if let Some(event) = event {
69          ctx.props().event.emit(event)
70        }
71        false
72      }
73      InternalMessage::AddValue => {
74        self.value = Some(0f64);
75        true
76      }
77      InternalMessage::RemoveValue => {
78        self.value = None;
79        true
80      }
81    }
82  }
83
84  fn view(&self, ctx: &Context<Self>) -> Html {
85    let input_number_callback = ctx.link().batch_callback(|e: Event| {
86      let target: Option<EventTarget> = e.target();
87      let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
88      input.map(|input| InternalMessage::Update(input.value()))
89    });
90
91    let action_buttons = vec![ActionButton::Submit(true)];
92
93    let inner_modal: Html = if ctx.props().required {
94      html!(
95        <input
96          type="number"
97          onchange={input_number_callback}
98          value={self.value.unwrap_or_default().to_string()}
99          />
100      )
101    } else if let Some(value) = self.value {
102      html!(
103        <>
104          <div>
105            <input type="number" step={ctx.props().number_step.to_string()} onchange={input_number_callback} value={value.to_string()}/>
106          </div>
107          <Button
108            label="Remove value"
109            icon={html!(<Trash2 />)}
110            onclick={ctx.link().callback(|_|InternalMessage::RemoveValue)}
111            />
112        </>
113      )
114    } else {
115      html!(
116        <Button
117          label="Add value"
118          icon={html!(<Plus />)}
119          onclick={ctx.link().callback(|_|InternalMessage::AddValue)}
120          />
121      )
122    };
123
124    html!(
125      <Modal
126        event={ctx.link().callback(InternalMessage::Modal)}
127        height="50vh" width="19vw"
128        modal_title={ctx.props().title.clone()}
129        actions={action_buttons}>
130        <div class={self.style.clone()}>
131          <div class="editInput">
132            {inner_modal}
133          </div>
134        </div>
135      </Modal>
136    )
137  }
138}