mcai_workflow/components/edit_parameter/
edit_extended.rs1use 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}