mcai_workflow/components/edit_parameter/
edit_extended.rs

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