Compare commits

...

3 Commits

Author SHA1 Message Date
Emilia Allison 12684c0eea
Merge plates
Gitea Scan/plate-tool/pipeline/head This commit looks good Details
i wrote this so late it probably does not work ughhhhhhhhh
2024-02-20 23:31:56 -05:00
Emilia Allison 0ec29f6783
Reorganize tree_callbacks
I am going to make too many
2024-02-20 21:26:24 -05:00
Emilia Allison e01468b63a
Reorganize main_window_callbacks
There were so many
2024-02-20 21:18:53 -05:00
15 changed files with 607 additions and 306 deletions

View File

@ -36,6 +36,12 @@ dialog > form[method="dialog"] {
} }
} }
.shifted_dialog {
top: 50vh;
left: 50vw;
margin: 0;
}
.close_button { .close_button {
color: red; color: red;
position: absolute; position: absolute;

View File

@ -1,232 +0,0 @@
#![allow(non_snake_case)]
use js_sys::Array;
use wasm_bindgen::{prelude::*, JsCast, JsValue};
use web_sys::{
Blob, HtmlAnchorElement, HtmlDialogElement, HtmlElement,
HtmlFormElement, HtmlInputElement, Url,
};
use yew::prelude::*;
use yewdux::prelude::*;
use crate::components::states::{CurrentTransfer, MainState};
use crate::state_to_csv;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn create_close_button(close: &Closure<dyn FnMut(Event)>) -> HtmlElement {
let document = web_sys::window().unwrap().document().unwrap();
let close_button = document
.create_element("button")
.unwrap()
.dyn_into::<HtmlElement>()
.unwrap();
close_button.set_text_content(Some(""));
close_button.set_class_name("close_button");
close_button.set_onclick(Some(close.as_ref().unchecked_ref()));
close_button
}
pub fn toggle_in_transfer_hashes_callback(
main_dispatch: Dispatch<MainState>,
) -> Callback<web_sys::MouseEvent> {
let main_dispatch = main_dispatch.clone();
Callback::from(move |_| {
main_dispatch.reduce_mut(|state| {
state.preferences.in_transfer_hashes ^= true;
})
})
}
pub fn toggle_volume_heatmap_callback(
main_dispatch: Dispatch<MainState>,
) -> Callback<web_sys::MouseEvent> {
let main_dispatch = main_dispatch.clone();
Callback::from(move |_| {
main_dispatch.reduce_mut(|state| {
state.preferences.volume_heatmap ^= true;
})
})
}
pub fn new_plate_dialog_callback(
new_plate_dialog_is_open: UseStateHandle<bool>,
) -> NoParamsCallback {
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
Box::new(move |_| {
new_plate_dialog_is_open.set(false);
})
}
pub fn open_new_plate_dialog_callback(
new_plate_dialog_is_open: UseStateHandle<bool>,
) -> NoParamsCallback {
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
Box::new(move |_| {
new_plate_dialog_is_open.set(true);
})
}
pub fn new_button_callback(
main_dispatch: Dispatch<MainState>,
ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<web_sys::MouseEvent> {
Callback::from(move |_| {
let window = web_sys::window().unwrap();
let confirm =
window.confirm_with_message("This will reset all plates and transfers. Proceed?");
if let Ok(confirm) = confirm {
if confirm {
main_dispatch.set(MainState::default());
ct_dispatch.set(CurrentTransfer::default());
}
}
})
}
fn save_str(data: &str, name: &str) {
let blob =
Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data))));
if let Ok(blob) = blob {
let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?");
// Beneath is the cool hack to download files
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let anchor = document
.create_element("a")
.unwrap()
.dyn_into::<HtmlAnchorElement>()
.unwrap();
anchor.set_download(name);
anchor.set_href(&url);
anchor.click();
}
}
pub fn export_csv_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
Callback::from(move |_| {
if main_state.transfers.is_empty() {
web_sys::window()
.unwrap()
.alert_with_message("No transfers to export.")
.unwrap();
return;
}
web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap();
if let Ok(csv) = state_to_csv(&main_state) {
save_str(&csv, "transfers.csv");
}
})
}
pub fn export_json_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
Callback::from(move |_| {
if let Ok(json) = serde_json::to_string(&main_state) {
save_str(&json, "plate-tool-state.json");
} else {
web_sys::window()
.unwrap()
.alert_with_message("Failed to export.")
.unwrap();
}
})
}
pub fn input_json_input_callback(
main_dispatch: Dispatch<MainState>,
modal: HtmlDialogElement,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::new(move |e: Event| {
if let Some(input) = e.current_target() {
let input = input
.dyn_into::<HtmlInputElement>()
.expect("We know this is an input.");
if let Some(files) = input.files() {
if let Some(file) = files.get(0) {
let fr = web_sys::FileReader::new().unwrap();
fr.read_as_text(&file).unwrap();
let fr1 = fr.clone(); // Clone to avoid outliving closure
let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut
// trait
let modal = modal.clone();
let onload = Closure::<dyn FnMut(_)>::new(move |_: Event| {
if let Some(value) = &fr1.result().ok().and_then(|v| v.as_string()) {
let ms = serde_json::from_str::<MainState>(value);
match ms {
Ok(ms) => main_dispatch.set(ms),
Err(e) => log::debug!("{:?}", e),
};
modal.close();
}
});
fr.set_onload(Some(onload.as_ref().unchecked_ref()));
onload.forget(); // Magic (don't touch)
}
}
}
})
}
pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callback<MouseEvent> {
Callback::from(move |_| {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();
let modal = document
.create_element("dialog")
.unwrap()
.dyn_into::<HtmlDialogElement>()
.unwrap();
modal.set_text_content(Some("Import File:"));
let onclose_callback = {
let modal = modal.clone();
Closure::<dyn FnMut(_)>::new(move |_: Event| {
modal.remove();
})
};
modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
let close_button = create_close_button(&onclose_callback);
onclose_callback.forget();
modal.append_child(&close_button).unwrap();
let form = document
.create_element("form")
.unwrap()
.dyn_into::<HtmlFormElement>()
.unwrap();
let input = document
.create_element("input")
.unwrap()
.dyn_into::<HtmlInputElement>()
.unwrap();
input.set_type("file");
input.set_accept(".json");
form.append_child(&input).unwrap();
let input_callback = {
let main_dispatch = main_dispatch.clone();
let modal = modal.clone();
input_json_input_callback(main_dispatch, modal)
};
input.set_onchange(Some(input_callback.as_ref().unchecked_ref()));
input_callback.forget(); // Magic straight from the docs, don't touch :(
modal.append_child(&form).unwrap();
body.append_child(&modal).unwrap();
modal.show_modal().unwrap();
})
}
pub use super::import_csv_callbacks::import_transfer_csv_callback;
pub use super::import_csv_callbacks::import_transfer_csv_submit_callback;
pub use super::import_csv_callbacks::import_transfer_csv_onload_callback;
pub use super::import_csv_callbacks::import_transfer_csv_input_callback;

View File

@ -0,0 +1,64 @@
#![allow(non_snake_case)]
use js_sys::Array;
use wasm_bindgen::{prelude::*, JsCast, JsValue};
use web_sys::{
Blob, HtmlAnchorElement, HtmlDialogElement, HtmlElement, HtmlFormElement, HtmlInputElement, Url,
};
use yew::prelude::*;
use yewdux::prelude::*;
use crate::components::states::{CurrentTransfer, MainState};
use crate::state_to_csv;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn export_csv_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
Callback::from(move |_| {
if main_state.transfers.is_empty() {
web_sys::window()
.unwrap()
.alert_with_message("No transfers to export.")
.unwrap();
return;
}
web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap();
if let Ok(csv) = state_to_csv(&main_state) {
save_str(&csv, "transfers.csv");
}
})
}
pub fn export_json_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
Callback::from(move |_| {
if let Ok(json) = serde_json::to_string(&main_state) {
save_str(&json, "plate-tool-state.json");
} else {
web_sys::window()
.unwrap()
.alert_with_message("Failed to export.")
.unwrap();
}
})
}
fn save_str(data: &str, name: &str) {
let blob =
Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data))));
if let Ok(blob) = blob {
let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?");
// Beneath is the cool hack to download files
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let anchor = document
.create_element("a")
.unwrap()
.dyn_into::<HtmlAnchorElement>()
.unwrap();
anchor.set_download(name);
anchor.set_href(&url);
anchor.click();
}
}

View File

@ -15,7 +15,7 @@ use plate_tool_lib::transfer_region::{Region, TransferRegion};
use plate_tool_lib::csv::{auto, string_well_to_pt, TransferRecord}; use plate_tool_lib::csv::{auto, string_well_to_pt, TransferRecord};
use super::main_window_callbacks::create_close_button; use super::create_close_button;
pub fn import_transfer_csv_input_callback( pub fn import_transfer_csv_input_callback(
main_dispatch: Dispatch<MainState>, main_dispatch: Dispatch<MainState>,

View File

@ -0,0 +1,98 @@
#![allow(non_snake_case)]
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::{HtmlDialogElement, HtmlFormElement, HtmlInputElement};
use yew::prelude::*;
use yewdux::prelude::*;
use crate::components::states::MainState;
use super::create_close_button;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn input_json_input_callback(
main_dispatch: Dispatch<MainState>,
modal: HtmlDialogElement,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::new(move |e: Event| {
if let Some(input) = e.current_target() {
let input = input
.dyn_into::<HtmlInputElement>()
.expect("We know this is an input.");
if let Some(files) = input.files() {
if let Some(file) = files.get(0) {
let fr = web_sys::FileReader::new().unwrap();
fr.read_as_text(&file).unwrap();
let fr1 = fr.clone(); // Clone to avoid outliving closure
let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut
// trait
let modal = modal.clone();
let onload = Closure::<dyn FnMut(_)>::new(move |_: Event| {
if let Some(value) = &fr1.result().ok().and_then(|v| v.as_string()) {
let ms = serde_json::from_str::<MainState>(value);
match ms {
Ok(ms) => main_dispatch.set(ms),
Err(e) => log::debug!("{:?}", e),
};
modal.close();
}
});
fr.set_onload(Some(onload.as_ref().unchecked_ref()));
onload.forget(); // Magic (don't touch)
}
}
}
})
}
pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callback<MouseEvent> {
Callback::from(move |_| {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();
let modal = document
.create_element("dialog")
.unwrap()
.dyn_into::<HtmlDialogElement>()
.unwrap();
modal.set_text_content(Some("Import File:"));
let onclose_callback = {
let modal = modal.clone();
Closure::<dyn FnMut(_)>::new(move |_: Event| {
modal.remove();
})
};
modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
let close_button = create_close_button(&onclose_callback);
onclose_callback.forget();
modal.append_child(&close_button).unwrap();
let form = document
.create_element("form")
.unwrap()
.dyn_into::<HtmlFormElement>()
.unwrap();
let input = document
.create_element("input")
.unwrap()
.dyn_into::<HtmlInputElement>()
.unwrap();
input.set_type("file");
input.set_accept(".json");
form.append_child(&input).unwrap();
let input_callback = {
let main_dispatch = main_dispatch.clone();
let modal = modal.clone();
input_json_input_callback(main_dispatch, modal)
};
input.set_onchange(Some(input_callback.as_ref().unchecked_ref()));
input_callback.forget(); // Magic straight from the docs, don't touch :(
modal.append_child(&form).unwrap();
body.append_child(&modal).unwrap();
modal.show_modal().unwrap();
})
}

View File

@ -0,0 +1,18 @@
mod export_callbacks;
mod input_json_callbacks;
mod import_csv_callbacks;
mod settings_callbacks;
mod plate_edits_callbacks;
mod util;
pub use util::*;
pub use export_callbacks::*;
pub use input_json_callbacks::*;
pub use import_csv_callbacks::*;
pub use settings_callbacks::*;
pub use plate_edits_callbacks::*;

View File

@ -0,0 +1,51 @@
#![allow(non_snake_case)]
use js_sys::Array;
use wasm_bindgen::{prelude::*, JsCast, JsValue};
use web_sys::{
Blob, HtmlAnchorElement, HtmlDialogElement, HtmlElement, HtmlFormElement, HtmlInputElement, Url,
};
use yew::prelude::*;
use yewdux::prelude::*;
use crate::components::states::{CurrentTransfer, MainState};
use crate::state_to_csv;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn new_plate_dialog_callback(
new_plate_dialog_is_open: UseStateHandle<bool>,
) -> NoParamsCallback {
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
Box::new(move |_| {
new_plate_dialog_is_open.set(false);
})
}
pub fn open_new_plate_dialog_callback(
new_plate_dialog_is_open: UseStateHandle<bool>,
) -> NoParamsCallback {
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
Box::new(move |_| {
new_plate_dialog_is_open.set(true);
})
}
pub fn new_button_callback(
main_dispatch: Dispatch<MainState>,
ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<web_sys::MouseEvent> {
Callback::from(move |_| {
let window = web_sys::window().unwrap();
let confirm =
window.confirm_with_message("This will reset all plates and transfers. Proceed?");
if let Ok(confirm) = confirm {
if confirm {
main_dispatch.set(MainState::default());
ct_dispatch.set(CurrentTransfer::default());
}
}
})
}

View File

@ -0,0 +1,30 @@
#![allow(non_snake_case)]
use yew::prelude::*;
use yewdux::prelude::*;
use crate::components::states::MainState;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn toggle_in_transfer_hashes_callback(
main_dispatch: Dispatch<MainState>,
) -> Callback<web_sys::MouseEvent> {
let main_dispatch = main_dispatch.clone();
Callback::from(move |_| {
main_dispatch.reduce_mut(|state| {
state.preferences.in_transfer_hashes ^= true;
})
})
}
pub fn toggle_volume_heatmap_callback(
main_dispatch: Dispatch<MainState>,
) -> Callback<web_sys::MouseEvent> {
let main_dispatch = main_dispatch.clone();
Callback::from(move |_| {
main_dispatch.reduce_mut(|state| {
state.preferences.volume_heatmap ^= true;
})
})
}

View File

@ -0,0 +1,21 @@
#![allow(non_snake_case)]
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::HtmlElement;
use yew::prelude::*;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn create_close_button(close: &Closure<dyn FnMut(Event)>) -> HtmlElement {
let document = web_sys::window().unwrap().document().unwrap();
let close_button = document
.create_element("button")
.unwrap()
.dyn_into::<HtmlElement>()
.unwrap();
close_button.set_text_content(Some(""));
close_button.set_class_name("close_button");
close_button.set_onclick(Some(close.as_ref().unchecked_ref()));
close_button
}

View File

@ -2,4 +2,3 @@ pub mod main_window_callbacks;
pub mod new_plate_dialog_callbacks; pub mod new_plate_dialog_callbacks;
pub mod transfer_menu_callbacks; pub mod transfer_menu_callbacks;
pub mod tree_callbacks; pub mod tree_callbacks;
mod import_csv_callbacks;

View File

@ -0,0 +1,74 @@
use uuid::Uuid;
use wasm_bindgen::JsCast;
use web_sys::{EventTarget, HtmlElement, HtmlInputElement, HtmlOptionElement, HtmlSelectElement};
use yew::prelude::*;
use yewdux::prelude::*;
use crate::components::states::MainState;
use plate_tool_lib::plate::PlateFormat;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn open_plate_info_callback(
plate_menu_id: UseStateHandle<Option<Uuid>>,
) -> Callback<MouseEvent> {
Callback::from(move |e: MouseEvent| {
let target: Option<EventTarget> = e.target();
let li = target.and_then(|t| t.dyn_into::<HtmlElement>().ok());
if let Some(li) = li {
if let Ok(id) = li.id().as_str().parse::<u128>() {
plate_menu_id.set(Some(Uuid::from_u128(id)));
}
}
})
}
pub fn plate_info_close_callback(plate_menu_id: UseStateHandle<Option<Uuid>>) -> NoParamsCallback {
Box::new(move |_| {
plate_menu_id.set(None);
})
}
pub fn plate_info_delete_callback(
main_dispatch: Dispatch<MainState>,
plate_menu_id: UseStateHandle<Option<Uuid>>,
) -> NoParamsCallback {
Box::new(move |_| {
if let Some(id) = *plate_menu_id {
main_dispatch.reduce_mut(|state| {
state.del_plate(id);
});
}
})
}
pub fn rename_onchange(id: Uuid, main_dispatch: Dispatch<MainState>) -> Callback<Event> {
Callback::from(move |e: Event| {
log::debug!("Changed name");
let input = e
.target()
.expect("Event must have target")
.dyn_into::<HtmlInputElement>()
.unwrap();
main_dispatch.reduce_mut(|state| state.rename_plate(id, &input.value()))
})
}
pub fn format_onchange(id: Uuid, main_dispatch: Dispatch<MainState>) -> Callback<Event> {
Callback::from(move |e: Event| {
log::debug!("Changing plate format");
let new_format: Option<PlateFormat> = e
.target()
.expect("Event must have target")
.dyn_into::<HtmlSelectElement>()
.unwrap()
.selected_options()
.get_with_index(0)
.map(|el| el.dyn_into::<HtmlOptionElement>().unwrap())
.map(|opt_el| opt_el.value())
.and_then(|value| PlateFormat::try_from(value.as_str()).ok());
if let Some(format) = new_format {
main_dispatch.reduce_mut(|state| state.change_format(id, &format));
}
})
}

View File

@ -0,0 +1,217 @@
use plate_tool_lib::{plate::PlateType, plate_instances::PlateInstance};
use uuid::Uuid;
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::{
HtmlDialogElement, HtmlElement, HtmlFormElement, HtmlInputElement, HtmlOptionElement,
HtmlSelectElement,
};
use yew::prelude::*;
use yewdux::prelude::*;
use std::rc::Rc;
use std::str::FromStr;
use crate::components::callbacks::main_window_callbacks::create_close_button;
use crate::components::states::MainState;
pub fn merge_button_callback(main_dispatch: Dispatch<MainState>, id: Uuid) -> Callback<MouseEvent> {
Callback::from(move |_| {
let dialog_opt = create_merge_dialog(main_dispatch.clone(), id);
if let Some(dialog) = dialog_opt {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();
let _ = body.append_child(&dialog);
dialog.set_open(true);
}
})
}
fn create_merge_dialog(main_dispatch: Dispatch<MainState>, id: Uuid) -> Option<HtmlDialogElement> {
let other_plates = get_all_plates_except(main_dispatch.clone(), id);
if other_plates.is_empty() {
return None;
}
let dialog = create_dialog();
let select = create_select_for_plates(&other_plates);
dialog.append_child(&select).unwrap();
let select_rc = Rc::new(select);
let ok_button = create_ok_button(main_dispatch.clone(), select_rc.clone(), id);
dialog.append_child(&ok_button).unwrap();
Some(dialog)
}
fn create_dialog() -> HtmlDialogElement {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let dialog = document
.create_element("dialog")
.unwrap()
.dyn_into::<HtmlDialogElement>()
.unwrap();
dialog.set_class_name("dialog shifted_dialog");
let onclose_callback = {
let dialog = dialog.clone();
Closure::<dyn FnMut(_)>::new(move |_: Event| {
dialog.remove();
})
};
dialog.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
let close_button = create_close_button(&onclose_callback);
onclose_callback.forget();
dialog.append_child(&close_button).unwrap();
let header = document.create_element("h2").unwrap();
header.set_inner_html("Merge Plates");
dialog.append_child(&header).unwrap();
dialog
}
fn create_ok_button(
main_dispatch: Dispatch<MainState>,
select: Rc<HtmlSelectElement>,
id: Uuid,
) -> HtmlElement {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let button = document
.create_element("button")
.unwrap()
.dyn_into::<HtmlElement>()
.unwrap();
button.set_inner_html("Ok");
let callback = ok_button_callback(main_dispatch, select, id);
button.set_onclick(Some(callback.as_ref().unchecked_ref()));
callback.forget();
button
}
fn ok_button_callback(
main_dispatch: Dispatch<MainState>,
select: Rc<HtmlSelectElement>,
to_id: Uuid,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::new(move |_: Event| {
let selected = select
.selected_options()
.get_with_index(0)
.map(|el| el.dyn_into::<HtmlOptionElement>().unwrap())
.map(|opt_el| opt_el.value())
.and_then(|uuid_str| Uuid::from_str(&uuid_str).ok());
if let Some(from_id) = selected {
let plate_type = find_plate_type(main_dispatch.clone(), from_id).unwrap();
let binding = main_dispatch.get();
let merge_to = match plate_type {
PlateType::Source => binding.source_plates.iter().find(|x| x.get_uuid() == to_id).unwrap(),
PlateType::Destination => binding
.destination_plates
.iter()
.find(|x| x.get_uuid() == to_id).unwrap(),
};
main_dispatch.reduce_mut(|x| {
let merged_transfers = x.transfers.iter_mut().filter(|x| match plate_type {
PlateType::Source => x.source_id,
PlateType::Destination => x.dest_id
} == from_id);
for transfer in merged_transfers {
match plate_type {
PlateType::Source => {
transfer.source_id = to_id;
transfer.transfer_region.source_plate = merge_to.plate;
},
PlateType::Destination => {
transfer.dest_id = to_id;
transfer.transfer_region.dest_plate = merge_to.plate;
}
}
}
match plate_type {
PlateType::Source => x.source_plates.retain(|y| y.get_uuid() != from_id),
PlateType::Destination => x.destination_plates.retain(|y| y.get_uuid() != from_id)
};
});
}
})
}
fn create_select_for_plates(infos: &Vec<(Uuid, String)>) -> HtmlSelectElement {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let select = document
.create_element("select")
.unwrap()
.dyn_into::<HtmlSelectElement>()
.unwrap();
for info in infos {
let _ = select.append_child(&create_option_for_plate(info));
}
select
}
fn create_option_for_plate(info: &(Uuid, String)) -> HtmlOptionElement {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let opt = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.unwrap();
opt.set_value(&info.0.to_string());
opt.set_inner_text(&info.1);
opt
}
fn get_all_plates_except(main_dispatch: Dispatch<MainState>, id: Uuid) -> Vec<(Uuid, String)> {
let plate_type = find_plate_type(main_dispatch.clone(), id);
if plate_type.is_none() {
return Vec::new();
} // Return early if not found somehow
let plate_type = plate_type.unwrap();
let binding = main_dispatch.get();
let all_plates_iter = match plate_type {
PlateType::Source => binding.source_plates.iter(),
PlateType::Destination => binding.destination_plates.iter(),
};
all_plates_iter
.map(|x| (x.get_uuid(), x.name.to_string()))
.filter(|y| y.0 != id)
.collect()
}
fn find_plate_type(main_dispatch: Dispatch<MainState>, id: Uuid) -> Option<PlateType> {
if main_dispatch
.get()
.source_plates
.iter()
.any(|x| x.get_uuid() == id)
{
return Some(PlateType::Source);
} else if main_dispatch
.get()
.destination_plates
.iter()
.any(|x| x.get_uuid() == id)
{
return Some(PlateType::Destination);
} else {
return None;
}
}

View File

@ -0,0 +1,9 @@
mod select_callbacks;
mod info_dialog_callbacks;
mod merge_callbacks;
pub use select_callbacks::*;
pub use info_dialog_callbacks::*;
pub use merge_callbacks::*;

View File

@ -1,81 +1,12 @@
use std::rc::Rc; use std::rc::Rc;
use uuid::Uuid; use uuid::Uuid;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use web_sys::{EventTarget, HtmlElement, HtmlInputElement, HtmlSelectElement, HtmlOptionElement}; use web_sys::{EventTarget, HtmlElement};
use yew::prelude::*; use yew::prelude::*;
use yewdux::prelude::*; use yewdux::prelude::*;
use crate::components::states::{CurrentTransfer, MainState}; use crate::components::states::{CurrentTransfer, MainState};
use plate_tool_lib::{transfer_region::Region, plate::PlateFormat}; use plate_tool_lib::transfer_region::Region;
type NoParamsCallback = Box<dyn Fn(())>;
pub fn open_plate_info_callback(
plate_menu_id: UseStateHandle<Option<Uuid>>,
) -> Callback<MouseEvent> {
Callback::from(move |e: MouseEvent| {
let target: Option<EventTarget> = e.target();
let li = target.and_then(|t| t.dyn_into::<HtmlElement>().ok());
if let Some(li) = li {
if let Ok(id) = li.id().as_str().parse::<u128>() {
plate_menu_id.set(Some(Uuid::from_u128(id)));
}
}
})
}
pub fn plate_info_close_callback(
plate_menu_id: UseStateHandle<Option<Uuid>>,
) -> NoParamsCallback {
Box::new(move |_| {
plate_menu_id.set(None);
})
}
pub fn plate_info_delete_callback(
main_dispatch: Dispatch<MainState>,
plate_menu_id: UseStateHandle<Option<Uuid>>,
) -> NoParamsCallback {
Box::new(move |_| {
if let Some(id) = *plate_menu_id {
main_dispatch.reduce_mut(|state| {
state.del_plate(id);
});
}
})
}
pub fn rename_onchange(id: Uuid, main_dispatch: Dispatch<MainState>) -> Callback<Event> {
Callback::from(move |e: Event| {
log::debug!("Changed name");
let input = e
.target()
.expect("Event must have target")
.dyn_into::<HtmlInputElement>()
.unwrap();
main_dispatch.reduce_mut(|state| state.rename_plate(id, &input.value()))
})
}
pub fn format_onchange(id: Uuid, main_dispatch: Dispatch<MainState>) -> Callback<Event> {
Callback::from(move |e: Event| {
log::debug!("Changing plate format");
let new_format: Option<PlateFormat> = e.target()
.expect("Event must have target")
.dyn_into::<HtmlSelectElement>()
.unwrap()
.selected_options()
.get_with_index(0)
.map(|el| {
el.dyn_into::<HtmlOptionElement>().unwrap()
})
.map(|opt_el| opt_el.value())
.and_then(|value| PlateFormat::try_from(value.as_str()).ok());
if let Some(format) = new_format {
main_dispatch.reduce_mut(|state| state.change_format(id, &format));
}
})
}
pub fn source_plate_select_callback( pub fn source_plate_select_callback(
main_dispatch: Dispatch<MainState>, main_dispatch: Dispatch<MainState>,
@ -121,7 +52,11 @@ pub fn destination_plate_select_callback(
}) })
} }
pub fn transfer_select_callback(main_state: Rc<MainState>, main_dispatch: Dispatch<MainState>, ct_dispatch: Dispatch<CurrentTransfer>) -> Callback<MouseEvent> { pub fn transfer_select_callback(
main_state: Rc<MainState>,
main_dispatch: Dispatch<MainState>,
ct_dispatch: Dispatch<CurrentTransfer>,
) -> Callback<MouseEvent> {
Callback::from(move |e: MouseEvent| { Callback::from(move |e: MouseEvent| {
let target: Option<EventTarget> = e.target(); let target: Option<EventTarget> = e.target();
let li = target.and_then(|t| t.dyn_into::<HtmlElement>().ok()); let li = target.and_then(|t| t.dyn_into::<HtmlElement>().ok());

View File

@ -181,6 +181,11 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
move |_| dialog_close_callback.emit(()) move |_| dialog_close_callback.emit(())
}; };
let onclose_secondary = {
let dialog_close_callback = props.dialog_close_callback.clone();
move |_| dialog_close_callback.emit(())
};
let rename_onchange = tree_callbacks::rename_onchange(props.id, main_dispatch.clone()); let rename_onchange = tree_callbacks::rename_onchange(props.id, main_dispatch.clone());
let format_onchange = tree_callbacks::format_onchange(props.id, main_dispatch.clone()); let format_onchange = tree_callbacks::format_onchange(props.id, main_dispatch.clone());
@ -209,6 +214,11 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
); );
} }
let merge_button_callback = {
let main_dispatch = main_dispatch.clone();
tree_callbacks::merge_button_callback(main_dispatch, props.id)
};
html! { html! {
<dialog ref={dialog_ref} class="dialog" onclose={onclose}> <dialog ref={dialog_ref} class="dialog" onclose={onclose}>
<h2>{"Plate Info"}</h2> <h2>{"Plate Info"}</h2>
@ -219,6 +229,7 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
{ for plate_format_options } { for plate_format_options }
</select> </select>
} }
<button onclick={merge_button_callback} onclick={onclose_secondary}>{"Merge"}</button>
<form class="modal_close" method="dialog"><button /></form> <form class="modal_close" method="dialog"><button /></form>
</dialog> </dialog>
} }