mcai_workflow/components/edit_parameter/
edit_string_array.rs

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