Add, modify, delete transfers
This commit is contained in:
		
							parent
							
								
									2fd1b0ca77
								
							
						
					
					
						commit
						d5d26facde
					
				| 
						 | 
					@ -2,11 +2,11 @@
 | 
				
			||||||
use yew::prelude::*;
 | 
					use yew::prelude::*;
 | 
				
			||||||
use yewdux::prelude::*;
 | 
					use yewdux::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::states::{MainState, CurrentTransfer};
 | 
					 | 
				
			||||||
use super::plates::plate_container::PlateContainer;
 | 
					 | 
				
			||||||
use super::tree::Tree;
 | 
					 | 
				
			||||||
use super::transfer_menu::TransferMenu;
 | 
					 | 
				
			||||||
use super::new_plate_dialog::NewPlateDialog;
 | 
					use super::new_plate_dialog::NewPlateDialog;
 | 
				
			||||||
 | 
					use super::plates::plate_container::PlateContainer;
 | 
				
			||||||
 | 
					use super::states::{CurrentTransfer, MainState};
 | 
				
			||||||
 | 
					use super::transfer_menu::TransferMenu;
 | 
				
			||||||
 | 
					use super::tree::Tree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::data::plate_instances::PlateInstance;
 | 
					use crate::data::plate_instances::PlateInstance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,20 +15,24 @@ pub fn MainWindow() -> Html {
 | 
				
			||||||
    let (main_state, main_dispatch) = use_store::<MainState>();
 | 
					    let (main_state, main_dispatch) = use_store::<MainState>();
 | 
				
			||||||
    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
					    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let source_plate_instance: Option<PlateInstance> = main_state.source_plates.iter()
 | 
					    let source_plate_instance: Option<PlateInstance> = main_state
 | 
				
			||||||
        .find(|spi| {spi.get_uuid() == main_state.selected_source_plate})
 | 
					        .source_plates
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .find(|spi| spi.get_uuid() == main_state.selected_source_plate)
 | 
				
			||||||
        .cloned();
 | 
					        .cloned();
 | 
				
			||||||
    if let Some(spi) = source_plate_instance.clone() {
 | 
					    if let Some(spi) = source_plate_instance.clone() {
 | 
				
			||||||
        ct_dispatch.reduce_mut(|state| {
 | 
					        ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
        state.transfer.source_plate = spi.plate;
 | 
					            state.transfer.transfer_region.source_plate = spi.plate;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let destination_plate_instance: Option<PlateInstance> = main_state.destination_plates.iter()
 | 
					    let destination_plate_instance: Option<PlateInstance> = main_state
 | 
				
			||||||
        .find(|dpi| {dpi.get_uuid() == main_state.selected_dest_plate})
 | 
					        .destination_plates
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .find(|dpi| dpi.get_uuid() == main_state.selected_dest_plate)
 | 
				
			||||||
        .cloned();
 | 
					        .cloned();
 | 
				
			||||||
    if let Some(dpi) = destination_plate_instance.clone() {
 | 
					    if let Some(dpi) = destination_plate_instance.clone() {
 | 
				
			||||||
        ct_dispatch.reduce_mut(|state| {
 | 
					        ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
        state.transfer.dest_plate = dpi.plate;
 | 
					            state.transfer.transfer_region.dest_plate = dpi.plate;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,7 +50,7 @@ pub fn MainWindow() -> Html {
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html!{
 | 
					    html! {
 | 
				
			||||||
        <div class="main_container">
 | 
					        <div class="main_container">
 | 
				
			||||||
            <Tree open_new_plate_callback={open_new_plate_dialog_callback}/>
 | 
					            <Tree open_new_plate_callback={open_new_plate_dialog_callback}/>
 | 
				
			||||||
            <TransferMenu />
 | 
					            <TransferMenu />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
pub mod states;
 | 
					 | 
				
			||||||
pub mod main_window;
 | 
					pub mod main_window;
 | 
				
			||||||
pub mod tree;
 | 
					 | 
				
			||||||
pub mod transfer_menu;
 | 
					 | 
				
			||||||
pub mod plates;
 | 
					 | 
				
			||||||
pub mod new_plate_dialog;
 | 
					pub mod new_plate_dialog;
 | 
				
			||||||
 | 
					pub mod plates;
 | 
				
			||||||
 | 
					pub mod states;
 | 
				
			||||||
 | 
					pub mod transfer_menu;
 | 
				
			||||||
 | 
					pub mod tree;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,11 +2,11 @@ use yew::prelude::*;
 | 
				
			||||||
use yewdux::prelude::*;
 | 
					use yewdux::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use wasm_bindgen::JsCast;
 | 
					use wasm_bindgen::JsCast;
 | 
				
			||||||
use web_sys::{EventTarget, HtmlFormElement, FormData, HtmlDialogElement};
 | 
					use web_sys::{EventTarget, FormData, HtmlDialogElement, HtmlFormElement};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::data::{plate_instances::PlateInstance, transfer::Transfer};
 | 
					 | 
				
			||||||
use crate::data::plate::*;
 | 
					 | 
				
			||||||
use crate::components::states::MainState;
 | 
					use crate::components::states::MainState;
 | 
				
			||||||
 | 
					use crate::data::plate::*;
 | 
				
			||||||
 | 
					use crate::data::{plate_instances::PlateInstance, transfer::Transfer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Properties)]
 | 
					#[derive(PartialEq, Properties)]
 | 
				
			||||||
pub struct NewPlateDialogProps {
 | 
					pub struct NewPlateDialogProps {
 | 
				
			||||||
| 
						 | 
					@ -41,9 +41,17 @@ pub fn NewPlateDialog(props: &NewPlateDialogProps) -> Html {
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                        dispatch.reduce_mut(|s| {
 | 
					                        dispatch.reduce_mut(|s| {
 | 
				
			||||||
                            if plate_type == PlateType::Source {
 | 
					                            if plate_type == PlateType::Source {
 | 
				
			||||||
                                s.add_source_plate(PlateInstance::new(PlateType::Source, format, name))
 | 
					                                s.add_source_plate(PlateInstance::new(
 | 
				
			||||||
 | 
					                                    PlateType::Source,
 | 
				
			||||||
 | 
					                                    format,
 | 
				
			||||||
 | 
					                                    name,
 | 
				
			||||||
 | 
					                                ))
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                s.add_dest_plate(PlateInstance::new(PlateType::Destination, format, name))
 | 
					                                s.add_dest_plate(PlateInstance::new(
 | 
				
			||||||
 | 
					                                    PlateType::Destination,
 | 
				
			||||||
 | 
					                                    format,
 | 
				
			||||||
 | 
					                                    name,
 | 
				
			||||||
 | 
					                                ))
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -57,10 +65,16 @@ pub fn NewPlateDialog(props: &NewPlateDialogProps) -> Html {
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let dialog_ref = dialog_ref.clone();
 | 
					        let dialog_ref = dialog_ref.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        use_effect_with_deps(|dialog_ref| {
 | 
					        use_effect_with_deps(
 | 
				
			||||||
            dialog_ref.cast::<HtmlDialogElement>().unwrap().show_modal().ok();
 | 
					            |dialog_ref| {
 | 
				
			||||||
 | 
					                dialog_ref
 | 
				
			||||||
 | 
					                    .cast::<HtmlDialogElement>()
 | 
				
			||||||
 | 
					                    .unwrap()
 | 
				
			||||||
 | 
					                    .show_modal()
 | 
				
			||||||
 | 
					                    .ok();
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        dialog_ref);
 | 
					            dialog_ref,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
#![allow(non_snake_case)]
 | 
					#![allow(non_snake_case)]
 | 
				
			||||||
 | 
					use std::rc::Rc;
 | 
				
			||||||
use yew::prelude::*;
 | 
					use yew::prelude::*;
 | 
				
			||||||
use yewdux::prelude::*;
 | 
					use yewdux::prelude::*;
 | 
				
			||||||
use std::rc::Rc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::components::states::CurrentTransfer;
 | 
				
			||||||
use crate::data::plate_instances::PlateInstance;
 | 
					use crate::data::plate_instances::PlateInstance;
 | 
				
			||||||
use crate::data::transfer_region::{TransferRegion, Region};
 | 
					use crate::data::transfer_region::{Region, TransferRegion};
 | 
				
			||||||
use crate::components::states::{CurrentTransfer};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::super::transfer_menu::RegionDisplay;
 | 
					use super::super::transfer_menu::RegionDisplay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,38 +18,36 @@ pub struct DestinationPlateProps {
 | 
				
			||||||
#[function_component]
 | 
					#[function_component]
 | 
				
			||||||
pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
 | 
					pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
 | 
				
			||||||
    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
					    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
				
			||||||
    let m_start_handle: UseStateHandle<Option<(u8,u8)>> = use_state_eq(|| None);
 | 
					    let m_start_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
 | 
				
			||||||
    let m_end_handle: UseStateHandle<Option<(u8,u8)>> = use_state_eq(|| None);
 | 
					    let m_end_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
 | 
				
			||||||
    let m_stat_handle: UseStateHandle<bool> = use_state_eq(|| false);
 | 
					    let m_stat_handle: UseStateHandle<bool> = use_state_eq(|| false);
 | 
				
			||||||
    let m_start = m_start_handle.clone();
 | 
					    let m_start = m_start_handle.clone();
 | 
				
			||||||
    let m_end = m_end_handle.clone();
 | 
					    let m_end = m_end_handle.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if !(*m_stat_handle) {
 | 
					    if !(*m_stat_handle) {
 | 
				
			||||||
        let (pt1, pt2) = match ct_state.transfer.dest_region {
 | 
					        let (pt1, pt2) = match ct_state.transfer.transfer_region.dest_region {
 | 
				
			||||||
            Region::Point((x,y)) => ((x,y),(x,y)),
 | 
					            Region::Point((x, y)) => ((x, y), (x, y)),
 | 
				
			||||||
            Region::Rect(c1, c2) => (c1, c2),
 | 
					            Region::Rect(c1, c2) => (c1, c2),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        m_start_handle.set(Some(pt1));
 | 
					        m_start_handle.set(Some(pt1));
 | 
				
			||||||
        m_end_handle.set(Some(pt2));
 | 
					        m_end_handle.set(Some(pt2));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let destination_wells = ct_state.transfer.get_destination_wells();
 | 
					    let destination_wells = ct_state.transfer.transfer_region.get_destination_wells();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mouse_callback = {
 | 
					    let mouse_callback = {
 | 
				
			||||||
        let m_start_handle = m_start_handle.clone();
 | 
					        let m_start_handle = m_start_handle.clone();
 | 
				
			||||||
        let m_end_handle = m_end_handle.clone();
 | 
					        let m_end_handle = m_end_handle.clone();
 | 
				
			||||||
        let m_stat_handle = m_stat_handle.clone();
 | 
					        let m_stat_handle = m_stat_handle.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Callback::from(move |(i,j,t)| {
 | 
					        Callback::from(move |(i, j, t)| match t {
 | 
				
			||||||
            match t {
 | 
					 | 
				
			||||||
            MouseEventType::MOUSEDOWN => {
 | 
					            MouseEventType::MOUSEDOWN => {
 | 
				
			||||||
                    m_start_handle.set(Some((i,j)));
 | 
					                m_start_handle.set(Some((i, j)));
 | 
				
			||||||
                    m_end_handle.set(Some((i,j)));
 | 
					                m_end_handle.set(Some((i, j)));
 | 
				
			||||||
                m_stat_handle.set(true);
 | 
					                m_stat_handle.set(true);
 | 
				
			||||||
                },
 | 
					            }
 | 
				
			||||||
            MouseEventType::MOUSEENTER => {
 | 
					            MouseEventType::MOUSEENTER => {
 | 
				
			||||||
                if *m_stat_handle {
 | 
					                if *m_stat_handle {
 | 
				
			||||||
                        m_end_handle.set(Some((i,j)));
 | 
					                    m_end_handle.set(Some((i, j)));
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -66,7 +64,7 @@ pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
 | 
				
			||||||
                if let Some(br) = *m_end_handle {
 | 
					                if let Some(br) = *m_end_handle {
 | 
				
			||||||
                    if let Ok(rd) = RegionDisplay::try_from((ul.0, ul.1, br.0, br.1)) {
 | 
					                    if let Ok(rd) = RegionDisplay::try_from((ul.0, ul.1, br.0, br.1)) {
 | 
				
			||||||
                        ct_dispatch.reduce_mut(|state| {
 | 
					                        ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                            state.transfer.dest_region = Region::from(&rd);
 | 
					                            state.transfer.transfer_region.dest_region = Region::from(&rd);
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -76,7 +74,8 @@ pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mouseleave_callback = Callback::clone(&mouseup_callback);
 | 
					    let mouseleave_callback = Callback::clone(&mouseup_callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let rows = (1..=props.destination_plate.plate.size().0).map(|i| {
 | 
					    let rows = (1..=props.destination_plate.plate.size().0)
 | 
				
			||||||
 | 
					        .map(|i| {
 | 
				
			||||||
            let row = (1..=props.destination_plate.plate.size().1).map(|j| {
 | 
					            let row = (1..=props.destination_plate.plate.size().1).map(|j| {
 | 
				
			||||||
            html! {
 | 
					            html! {
 | 
				
			||||||
                <DestPlateCell i={i} j={j}
 | 
					                <DestPlateCell i={i} j={j}
 | 
				
			||||||
| 
						 | 
					@ -91,7 +90,8 @@ pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
 | 
				
			||||||
                    { row }
 | 
					                    { row }
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
    }).collect::<Html>();
 | 
					        })
 | 
				
			||||||
 | 
					        .collect::<Html>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
        <div class="dest_plate">
 | 
					        <div class="dest_plate">
 | 
				
			||||||
| 
						 | 
					@ -111,7 +111,7 @@ pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum MouseEventType {
 | 
					pub enum MouseEventType {
 | 
				
			||||||
    MOUSEDOWN,
 | 
					    MOUSEDOWN,
 | 
				
			||||||
    MOUSEENTER
 | 
					    MOUSEENTER,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Properties, PartialEq)]
 | 
					#[derive(Properties, PartialEq)]
 | 
				
			||||||
| 
						 | 
					@ -119,7 +119,7 @@ pub struct DestPlateCellProps {
 | 
				
			||||||
    pub i: u8,
 | 
					    pub i: u8,
 | 
				
			||||||
    pub j: u8,
 | 
					    pub j: u8,
 | 
				
			||||||
    pub selected: bool,
 | 
					    pub selected: bool,
 | 
				
			||||||
    pub mouse: Callback<(u8,u8, MouseEventType)>,
 | 
					    pub mouse: Callback<(u8, u8, MouseEventType)>,
 | 
				
			||||||
    pub in_transfer: Option<bool>,
 | 
					    pub in_transfer: Option<bool>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,11 +131,11 @@ fn DestPlateCell(props: &DestPlateCellProps) -> Html {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let in_transfer_class = match props.in_transfer {
 | 
					    let in_transfer_class = match props.in_transfer {
 | 
				
			||||||
        Some(true) => Some("in_transfer"),
 | 
					        Some(true) => Some("in_transfer"),
 | 
				
			||||||
        _ => None
 | 
					        _ => None,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let mouse = Callback::clone(&props.mouse);
 | 
					    let mouse = Callback::clone(&props.mouse);
 | 
				
			||||||
    let mouse2 = Callback::clone(&props.mouse);
 | 
					    let mouse2 = Callback::clone(&props.mouse);
 | 
				
			||||||
    let (i,j) = (props.i.clone(), props.j.clone());
 | 
					    let (i, j) = (props.i.clone(), props.j.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
        <td class={classes!("plate_cell", selected_class, in_transfer_class)}
 | 
					        <td class={classes!("plate_cell", selected_class, in_transfer_class)}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
pub mod source_plate;
 | 
					 | 
				
			||||||
pub mod destination_plate;
 | 
					pub mod destination_plate;
 | 
				
			||||||
pub mod plate_container;
 | 
					pub mod plate_container;
 | 
				
			||||||
 | 
					pub mod source_plate;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,8 @@ use yew::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::data::plate_instances::PlateInstance;
 | 
					use crate::data::plate_instances::PlateInstance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::source_plate::SourcePlate;
 | 
					 | 
				
			||||||
use super::destination_plate::DestinationPlate;
 | 
					use super::destination_plate::DestinationPlate;
 | 
				
			||||||
 | 
					use super::source_plate::SourcePlate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Properties, PartialEq)]
 | 
					#[derive(Properties, PartialEq)]
 | 
				
			||||||
pub struct PlateContainerProps {
 | 
					pub struct PlateContainerProps {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
#![allow(non_snake_case)]
 | 
					#![allow(non_snake_case)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::rc::Rc;
 | 
				
			||||||
use yew::prelude::*;
 | 
					use yew::prelude::*;
 | 
				
			||||||
use yewdux::prelude::*;
 | 
					use yewdux::prelude::*;
 | 
				
			||||||
use std::rc::Rc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::components::states::CurrentTransfer;
 | 
				
			||||||
use crate::data::plate_instances::PlateInstance;
 | 
					use crate::data::plate_instances::PlateInstance;
 | 
				
			||||||
use crate::data::transfer_region::{TransferRegion, Region};
 | 
					use crate::data::transfer_region::{Region, TransferRegion};
 | 
				
			||||||
use crate::components::states::{CurrentTransfer};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::super::transfer_menu::RegionDisplay;
 | 
					use super::super::transfer_menu::RegionDisplay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,39 +19,37 @@ pub struct SourcePlateProps {
 | 
				
			||||||
#[function_component]
 | 
					#[function_component]
 | 
				
			||||||
pub fn SourcePlate(props: &SourcePlateProps) -> Html {
 | 
					pub fn SourcePlate(props: &SourcePlateProps) -> Html {
 | 
				
			||||||
    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
					    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
				
			||||||
    let m_start_handle: UseStateHandle<Option<(u8,u8)>> = use_state_eq(|| None);
 | 
					    let m_start_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
 | 
				
			||||||
    let m_end_handle: UseStateHandle<Option<(u8,u8)>> = use_state_eq(|| None);
 | 
					    let m_end_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
 | 
				
			||||||
    let m_stat_handle: UseStateHandle<bool> = use_state_eq(|| false);
 | 
					    let m_stat_handle: UseStateHandle<bool> = use_state_eq(|| false);
 | 
				
			||||||
    let m_start = m_start_handle.clone();
 | 
					    let m_start = m_start_handle.clone();
 | 
				
			||||||
    let m_end = m_end_handle.clone();
 | 
					    let m_end = m_end_handle.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if !(*m_stat_handle) {
 | 
					    if !(*m_stat_handle) {
 | 
				
			||||||
        let (pt1, pt2) = match ct_state.transfer.source_region {
 | 
					        let (pt1, pt2) = match ct_state.transfer.transfer_region.source_region {
 | 
				
			||||||
            Region::Point((x,y)) => ((x,y),(x,y)),
 | 
					            Region::Point((x, y)) => ((x, y), (x, y)),
 | 
				
			||||||
            Region::Rect(c1, c2) => (c1, c2),
 | 
					            Region::Rect(c1, c2) => (c1, c2),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        m_start_handle.set(Some(pt1));
 | 
					        m_start_handle.set(Some(pt1));
 | 
				
			||||||
        m_end_handle.set(Some(pt2));
 | 
					        m_end_handle.set(Some(pt2));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let source_wells = ct_state.transfer.get_source_wells();
 | 
					    let source_wells = ct_state.transfer.transfer_region.get_source_wells();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mouse_callback = {
 | 
					    let mouse_callback = {
 | 
				
			||||||
        let m_start_handle = m_start_handle.clone();
 | 
					        let m_start_handle = m_start_handle.clone();
 | 
				
			||||||
        let m_end_handle = m_end_handle.clone();
 | 
					        let m_end_handle = m_end_handle.clone();
 | 
				
			||||||
        let m_stat_handle = m_stat_handle.clone();
 | 
					        let m_stat_handle = m_stat_handle.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Callback::from(move |(i,j,t)| {
 | 
					        Callback::from(move |(i, j, t)| match t {
 | 
				
			||||||
            match t {
 | 
					 | 
				
			||||||
            MouseEventType::MOUSEDOWN => {
 | 
					            MouseEventType::MOUSEDOWN => {
 | 
				
			||||||
                    m_start_handle.set(Some((i,j)));
 | 
					                m_start_handle.set(Some((i, j)));
 | 
				
			||||||
                    m_end_handle.set(Some((i,j)));
 | 
					                m_end_handle.set(Some((i, j)));
 | 
				
			||||||
                m_stat_handle.set(true);
 | 
					                m_stat_handle.set(true);
 | 
				
			||||||
                },
 | 
					            }
 | 
				
			||||||
            MouseEventType::MOUSEENTER => {
 | 
					            MouseEventType::MOUSEENTER => {
 | 
				
			||||||
                if *m_stat_handle {
 | 
					                if *m_stat_handle {
 | 
				
			||||||
                        m_end_handle.set(Some((i,j)));
 | 
					                    m_end_handle.set(Some((i, j)));
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -68,7 +66,7 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
 | 
				
			||||||
                if let Some(br) = *m_end_handle {
 | 
					                if let Some(br) = *m_end_handle {
 | 
				
			||||||
                    if let Ok(rd) = RegionDisplay::try_from((ul.0, ul.1, br.0, br.1)) {
 | 
					                    if let Ok(rd) = RegionDisplay::try_from((ul.0, ul.1, br.0, br.1)) {
 | 
				
			||||||
                        ct_dispatch.reduce_mut(|state| {
 | 
					                        ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.source_region = Region::from(&rd);
 | 
					                            state.transfer.transfer_region.source_region = Region::from(&rd);
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -78,8 +76,10 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mouseleave_callback = Callback::clone(&mouseup_callback);
 | 
					    let mouseleave_callback = Callback::clone(&mouseup_callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let rows = (1..=props.source_plate.plate.size().0).map(|i| {
 | 
					    let rows = (1..=props.source_plate.plate.size().0)
 | 
				
			||||||
        let row = (1..=props.source_plate.plate.size().1).map(|j| {
 | 
					        .map(|i| {
 | 
				
			||||||
 | 
					            let row = (1..=props.source_plate.plate.size().1)
 | 
				
			||||||
 | 
					                .map(|j| {
 | 
				
			||||||
                    html! {
 | 
					                    html! {
 | 
				
			||||||
                        <SourcePlateCell i={i} j={j}
 | 
					                        <SourcePlateCell i={i} j={j}
 | 
				
			||||||
                        selected={in_rect(*m_start.clone(), *m_end.clone(), (i,j))}
 | 
					                        selected={in_rect(*m_start.clone(), *m_end.clone(), (i,j))}
 | 
				
			||||||
| 
						 | 
					@ -87,13 +87,15 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
 | 
				
			||||||
                        in_transfer={source_wells.contains(&(i,j))}
 | 
					                        in_transfer={source_wells.contains(&(i,j))}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
        }).collect::<Html>();
 | 
					                })
 | 
				
			||||||
 | 
					                .collect::<Html>();
 | 
				
			||||||
            html! {
 | 
					            html! {
 | 
				
			||||||
                <tr>
 | 
					                <tr>
 | 
				
			||||||
                    { row }
 | 
					                    { row }
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
    }).collect::<Html>();
 | 
					        })
 | 
				
			||||||
 | 
					        .collect::<Html>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
        <div class="source_plate">
 | 
					        <div class="source_plate">
 | 
				
			||||||
| 
						 | 
					@ -115,13 +117,13 @@ pub struct SourcePlateCellProps {
 | 
				
			||||||
    i: u8,
 | 
					    i: u8,
 | 
				
			||||||
    j: u8,
 | 
					    j: u8,
 | 
				
			||||||
    selected: bool,
 | 
					    selected: bool,
 | 
				
			||||||
    mouse: Callback<(u8,u8, MouseEventType)>,
 | 
					    mouse: Callback<(u8, u8, MouseEventType)>,
 | 
				
			||||||
    in_transfer: Option<bool>,
 | 
					    in_transfer: Option<bool>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum MouseEventType {
 | 
					pub enum MouseEventType {
 | 
				
			||||||
    MOUSEDOWN,
 | 
					    MOUSEDOWN,
 | 
				
			||||||
    MOUSEENTER
 | 
					    MOUSEENTER,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[function_component]
 | 
					#[function_component]
 | 
				
			||||||
| 
						 | 
					@ -132,11 +134,11 @@ fn SourcePlateCell(props: &SourcePlateCellProps) -> Html {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let in_transfer_class = match props.in_transfer {
 | 
					    let in_transfer_class = match props.in_transfer {
 | 
				
			||||||
        Some(true) => Some("in_transfer"),
 | 
					        Some(true) => Some("in_transfer"),
 | 
				
			||||||
        _ => None
 | 
					        _ => None,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let mouse = Callback::clone(&props.mouse);
 | 
					    let mouse = Callback::clone(&props.mouse);
 | 
				
			||||||
    let mouse2 = Callback::clone(&props.mouse);
 | 
					    let mouse2 = Callback::clone(&props.mouse);
 | 
				
			||||||
    let (i,j) = (props.i.clone(), props.j.clone());
 | 
					    let (i, j) = (props.i.clone(), props.j.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
        <td class={classes!("plate_cell", selected_class, in_transfer_class)}
 | 
					        <td class={classes!("plate_cell", selected_class, in_transfer_class)}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,16 @@
 | 
				
			||||||
use serde::{Serialize, Deserialize};
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
use yewdux::{prelude::*, storage};
 | 
					 | 
				
			||||||
use uuid::Uuid;
 | 
					use uuid::Uuid;
 | 
				
			||||||
 | 
					use yewdux::{prelude::*, storage};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::transfer_menu::RegionDisplay;
 | 
					use super::transfer_menu::RegionDisplay;
 | 
				
			||||||
 | 
					use crate::data::plate::*;
 | 
				
			||||||
use crate::data::plate_instances::PlateInstance;
 | 
					use crate::data::plate_instances::PlateInstance;
 | 
				
			||||||
use crate::data::transfer::Transfer;
 | 
					use crate::data::transfer::Transfer;
 | 
				
			||||||
use crate::data::plate::*;
 | 
					 | 
				
			||||||
use crate::data::transfer_region::TransferRegion;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Store)]
 | 
					#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Store)]
 | 
				
			||||||
#[store(storage = "session")]
 | 
					#[store(storage = "session")]
 | 
				
			||||||
pub struct CurrentTransfer {
 | 
					pub struct CurrentTransfer {
 | 
				
			||||||
    pub transfer: TransferRegion,
 | 
					    pub transfer: Transfer,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default, PartialEq, Clone, Serialize, Deserialize)]
 | 
					#[derive(Default, PartialEq, Clone, Serialize, Deserialize)]
 | 
				
			||||||
| 
						 | 
					@ -48,14 +47,17 @@ impl Store for MainState {
 | 
				
			||||||
impl MainState {
 | 
					impl MainState {
 | 
				
			||||||
    pub fn purge_transfers(&mut self) {
 | 
					    pub fn purge_transfers(&mut self) {
 | 
				
			||||||
        // Removes any transfers for which the associated plates are gone
 | 
					        // Removes any transfers for which the associated plates are gone
 | 
				
			||||||
        self.transfers = self.transfers.iter()
 | 
					        self.transfers = self
 | 
				
			||||||
 | 
					            .transfers
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
            .filter(|tr| {
 | 
					            .filter(|tr| {
 | 
				
			||||||
                self.source_plates.iter().any(|spi| {
 | 
					                self.source_plates
 | 
				
			||||||
                    spi.get_uuid() == tr.source_id
 | 
					                    .iter()
 | 
				
			||||||
                }) &&
 | 
					                    .any(|spi| spi.get_uuid() == tr.source_id)
 | 
				
			||||||
                self.destination_plates.iter().any(|dpi| {
 | 
					                    && self
 | 
				
			||||||
                    dpi.get_uuid() == tr.dest_id
 | 
					                        .destination_plates
 | 
				
			||||||
                })
 | 
					                        .iter()
 | 
				
			||||||
 | 
					                        .any(|dpi| dpi.get_uuid() == tr.dest_id)
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .map(|tr| tr.clone())
 | 
					            .map(|tr| tr.clone())
 | 
				
			||||||
            .collect();
 | 
					            .collect();
 | 
				
			||||||
| 
						 | 
					@ -70,10 +72,18 @@ impl MainState {
 | 
				
			||||||
        self.destination_plates.push(plate);
 | 
					        self.destination_plates.push(plate);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub fn del_plate(&mut self, id: Uuid) {
 | 
					    pub fn del_plate(&mut self, id: Uuid) {
 | 
				
			||||||
        if let Some(index) = self.source_plates.iter().position(|spi| {spi.get_uuid() == id}) {
 | 
					        if let Some(index) = self
 | 
				
			||||||
 | 
					            .source_plates
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .position(|spi| spi.get_uuid() == id)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            self.source_plates.swap_remove(index);
 | 
					            self.source_plates.swap_remove(index);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if let Some(index) = self.destination_plates.iter().position(|dpi| {dpi.get_uuid() == id}) {
 | 
					        if let Some(index) = self
 | 
				
			||||||
 | 
					            .destination_plates
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .position(|dpi| dpi.get_uuid() == id)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            self.destination_plates.swap_remove(index);
 | 
					            self.destination_plates.swap_remove(index);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,22 +1,38 @@
 | 
				
			||||||
#![allow(non_snake_case)]
 | 
					#![allow(non_snake_case)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use serde::{Serialize, Deserialize};
 | 
					use lazy_static::lazy_static;
 | 
				
			||||||
use yew::prelude::*;
 | 
					use regex::Regex;
 | 
				
			||||||
use yewdux::prelude::*;
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
use wasm_bindgen::JsCast;
 | 
					use wasm_bindgen::JsCast;
 | 
				
			||||||
use web_sys::{EventTarget, HtmlInputElement};
 | 
					use web_sys::{EventTarget, HtmlInputElement};
 | 
				
			||||||
use regex::Regex;
 | 
					use uuid::Uuid;
 | 
				
			||||||
use lazy_static::lazy_static;
 | 
					use yew::prelude::*;
 | 
				
			||||||
 | 
					use yewdux::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::data::{transfer_region::Region, transfer::Transfer};
 | 
					use crate::data::{transfer::Transfer, transfer_region::Region};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::states::{MainState, CurrentTransfer};
 | 
					use super::states::{CurrentTransfer, MainState};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[function_component]
 | 
					#[function_component]
 | 
				
			||||||
pub fn TransferMenu() -> Html {
 | 
					pub fn TransferMenu() -> Html {
 | 
				
			||||||
    let (main_state, main_dispatch) = use_store::<MainState>();
 | 
					    let (main_state, main_dispatch) = use_store::<MainState>();
 | 
				
			||||||
    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
					    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let on_name_change = {
 | 
				
			||||||
 | 
					        let ct_dispatch = ct_dispatch.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Callback::from(move |e: Event| {
 | 
				
			||||||
 | 
					            let target: Option<EventTarget> = e.target();
 | 
				
			||||||
 | 
					            let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
 | 
				
			||||||
 | 
					            if let Some(input) = input {
 | 
				
			||||||
 | 
					                ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
 | 
					                    state.transfer.name = input.value().clone();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    )};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let on_src_region_change = {
 | 
					    let on_src_region_change = {
 | 
				
			||||||
        let ct_dispatch = ct_dispatch.clone();
 | 
					        let ct_dispatch = ct_dispatch.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +42,7 @@ pub fn TransferMenu() -> Html {
 | 
				
			||||||
            if let Some(input) = input {
 | 
					            if let Some(input) = input {
 | 
				
			||||||
                if let Ok(rd) = RegionDisplay::try_from(input.value()) {
 | 
					                if let Ok(rd) = RegionDisplay::try_from(input.value()) {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.source_region = Region::from(&rd);
 | 
					                        state.transfer.transfer_region.source_region = Region::from(&rd);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    input.set_custom_validity("");
 | 
					                    input.set_custom_validity("");
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
| 
						 | 
					@ -44,7 +60,7 @@ pub fn TransferMenu() -> Html {
 | 
				
			||||||
            if let Some(input) = input {
 | 
					            if let Some(input) = input {
 | 
				
			||||||
                if let Ok(rd) = RegionDisplay::try_from(input.value()) {
 | 
					                if let Ok(rd) = RegionDisplay::try_from(input.value()) {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.dest_region = Region::from(&rd);
 | 
					                        state.transfer.transfer_region.dest_region = Region::from(&rd);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    input.set_custom_validity("");
 | 
					                    input.set_custom_validity("");
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
| 
						 | 
					@ -63,7 +79,8 @@ pub fn TransferMenu() -> Html {
 | 
				
			||||||
            if let Some(input) = input {
 | 
					            if let Some(input) = input {
 | 
				
			||||||
                if let Ok(num) = input.value().parse::<i8>() {
 | 
					                if let Ok(num) = input.value().parse::<i8>() {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.interleave_source = (num, state.transfer.interleave_source.1);
 | 
					                        state.transfer.transfer_region.interleave_source =
 | 
				
			||||||
 | 
					                            (num, state.transfer.transfer_region.interleave_source.1);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -78,7 +95,8 @@ pub fn TransferMenu() -> Html {
 | 
				
			||||||
            if let Some(input) = input {
 | 
					            if let Some(input) = input {
 | 
				
			||||||
                if let Ok(num) = input.value().parse::<i8>() {
 | 
					                if let Ok(num) = input.value().parse::<i8>() {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.interleave_source = (state.transfer.interleave_source.0, num);
 | 
					                        state.transfer.transfer_region.interleave_source =
 | 
				
			||||||
 | 
					                            (state.transfer.transfer_region.interleave_source.0, num);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -93,7 +111,8 @@ pub fn TransferMenu() -> Html {
 | 
				
			||||||
            if let Some(input) = input {
 | 
					            if let Some(input) = input {
 | 
				
			||||||
                if let Ok(num) = input.value().parse::<i8>() {
 | 
					                if let Ok(num) = input.value().parse::<i8>() {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.interleave_dest = (num, state.transfer.interleave_dest.1);
 | 
					                        state.transfer.transfer_region.interleave_dest =
 | 
				
			||||||
 | 
					                            (num, state.transfer.transfer_region.interleave_dest.1);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -108,38 +127,87 @@ pub fn TransferMenu() -> Html {
 | 
				
			||||||
            if let Some(input) = input {
 | 
					            if let Some(input) = input {
 | 
				
			||||||
                if let Ok(num) = input.value().parse::<i8>() {
 | 
					                if let Ok(num) = input.value().parse::<i8>() {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.interleave_dest = (state.transfer.interleave_dest.0, num);
 | 
					                        state.transfer.transfer_region.interleave_dest =
 | 
				
			||||||
 | 
					                            (state.transfer.transfer_region.interleave_dest.0, num);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let create_transfer_button_callback = {
 | 
					
 | 
				
			||||||
 | 
					    let new_transfer_button_callback = {
 | 
				
			||||||
 | 
					        let main_dispatch = main_dispatch.clone();
 | 
				
			||||||
 | 
					        let main_state = main_state.clone();
 | 
				
			||||||
 | 
					        let ct_dispatch = ct_dispatch.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Callback::from(move |_: MouseEvent| {
 | 
				
			||||||
 | 
					            main_dispatch.reduce_mut(|state| {
 | 
				
			||||||
 | 
					                state.selected_transfer = Uuid::nil();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
 | 
					                state.transfer = Transfer::default();
 | 
				
			||||||
 | 
					                state.transfer.source_id = main_state.selected_source_plate;
 | 
				
			||||||
 | 
					                state.transfer.dest_id = main_state.selected_dest_plate;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let save_transfer_button_callback = {
 | 
				
			||||||
        let main_dispatch = main_dispatch.clone();
 | 
					        let main_dispatch = main_dispatch.clone();
 | 
				
			||||||
        let main_state = main_state.clone();
 | 
					        let main_state = main_state.clone();
 | 
				
			||||||
        let ct_state = ct_state.clone();
 | 
					        let ct_state = ct_state.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Callback::from(move |e: MouseEvent| {
 | 
					        Callback::from(move |_: MouseEvent| {
 | 
				
			||||||
            log::debug!("Button pressed");
 | 
					            log::debug!("Button pressed");
 | 
				
			||||||
            if main_state.selected_transfer.is_nil() {
 | 
					            if main_state.selected_transfer.is_nil() {
 | 
				
			||||||
                log::debug!("Was nil");
 | 
					                if let Some(spi) = main_state
 | 
				
			||||||
                if let Some(spi) = main_state.source_plates.iter()
 | 
					                    .source_plates
 | 
				
			||||||
                    .find(|spi| spi.get_uuid() == main_state.selected_source_plate) {
 | 
					                    .iter()
 | 
				
			||||||
                    if let Some(dpi) = main_state.destination_plates.iter()
 | 
					                    .find(|spi| spi.get_uuid() == main_state.selected_source_plate)
 | 
				
			||||||
                        .find(|dpi| dpi.get_uuid() == main_state.selected_dest_plate) {
 | 
					                {
 | 
				
			||||||
 | 
					                    if let Some(dpi) = main_state
 | 
				
			||||||
 | 
					                        .destination_plates
 | 
				
			||||||
 | 
					                        .iter()
 | 
				
			||||||
 | 
					                        .find(|dpi| dpi.get_uuid() == main_state.selected_dest_plate)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
                        let new_transfer = Transfer::new(
 | 
					                        let new_transfer = Transfer::new(
 | 
				
			||||||
                            spi.clone(),
 | 
					                            spi.clone(),
 | 
				
			||||||
                            dpi.clone(),
 | 
					                            dpi.clone(),
 | 
				
			||||||
                                ct_state.transfer.clone(),
 | 
					                            ct_state.transfer.transfer_region,
 | 
				
			||||||
                                "Reginald".to_string());
 | 
					                            ct_state.transfer.name.clone()
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                        main_dispatch.reduce_mut(|state| state.transfers.push(new_transfer));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if let Some(index) = main_state.transfers.iter()
 | 
				
			||||||
 | 
					                                        .position(|t| t.get_uuid() == main_state.selected_transfer) {
 | 
				
			||||||
                    main_dispatch.reduce_mut(|state| {
 | 
					                    main_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                                state.transfers.push(new_transfer)
 | 
					                        state.transfers[index] = ct_state.transfer.clone();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                //let new_transfer = Transfer::new();
 | 
					        })
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let delete_transfer_button_callback = {
 | 
				
			||||||
 | 
					        let main_dispatch = main_dispatch.clone();
 | 
				
			||||||
 | 
					        let main_state = main_state.clone();
 | 
				
			||||||
 | 
					        let ct_state = ct_state.clone();
 | 
				
			||||||
 | 
					        let new_callback = new_transfer_button_callback.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Callback::from(move |e: MouseEvent| {
 | 
				
			||||||
 | 
					            if main_state.selected_transfer.is_nil() {
 | 
				
			||||||
 | 
					                () // Maybe reset transfer?
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if let Some(index) = main_state.transfers.iter()
 | 
				
			||||||
 | 
					                                     .position(|t| t.get_uuid() == ct_state.transfer.get_uuid()) {
 | 
				
			||||||
                    main_dispatch.reduce_mut(|state| {
 | 
					                    main_dispatch.reduce_mut(|state| {
 | 
				
			||||||
 | 
					                        state.transfers.remove(index);
 | 
				
			||||||
 | 
					                        state.selected_transfer = Uuid::nil();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					                    new_callback.emit(e); // We need a new transfer now
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					@ -147,40 +215,52 @@ pub fn TransferMenu() -> Html {
 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
        <div class="transfer_menu">
 | 
					        <div class="transfer_menu">
 | 
				
			||||||
            <form>
 | 
					            <form>
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					                <label for="name">{"Name:"}</label>
 | 
				
			||||||
 | 
					                <input type="text" name="name"
 | 
				
			||||||
 | 
					                onchange={on_name_change}
 | 
				
			||||||
 | 
					                value={ct_state.transfer.name.clone()}/>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
                <label for="src_region">{"Source Region:"}</label>
 | 
					                <label for="src_region">{"Source Region:"}</label>
 | 
				
			||||||
                <input type="text" name="src_region"
 | 
					                <input type="text" name="src_region"
 | 
				
			||||||
                onchange={on_src_region_change}
 | 
					                onchange={on_src_region_change}
 | 
				
			||||||
                value={RegionDisplay::from(&ct_state.transfer.source_region).text}/>
 | 
					                value={RegionDisplay::from(&ct_state.transfer.transfer_region.source_region).text}/>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
                <label for="dest_region">{"Destination Region:"}</label>
 | 
					                <label for="dest_region">{"Destination Region:"}</label>
 | 
				
			||||||
                <input type="text" name="dest_region"
 | 
					                <input type="text" name="dest_region"
 | 
				
			||||||
                onchange={on_dest_region_change}
 | 
					                onchange={on_dest_region_change}
 | 
				
			||||||
                value={RegionDisplay::from(&ct_state.transfer.dest_region).text}/>
 | 
					                value={RegionDisplay::from(&ct_state.transfer.transfer_region.dest_region).text}/>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
            {"Source Interleave "}
 | 
					            {"Source Interleave "}
 | 
				
			||||||
            <label for="source_interleave_x">{"Row:"}</label>
 | 
					            <label for="source_interleave_x">{"Row:"}</label>
 | 
				
			||||||
            <input type="number" name="source_interleave_x"
 | 
					            <input type="number" name="source_interleave_x"
 | 
				
			||||||
            onchange={on_source_interleave_x_change}
 | 
					            onchange={on_source_interleave_x_change}
 | 
				
			||||||
            value={ct_state.transfer.interleave_source.0.to_string()}/>
 | 
					            value={ct_state.transfer.transfer_region.interleave_source.0.to_string()}/>
 | 
				
			||||||
            <label for="source_interleave_y">{"Col:"}</label>
 | 
					            <label for="source_interleave_y">{"Col:"}</label>
 | 
				
			||||||
            <input type="number" name="source_interleave_y"
 | 
					            <input type="number" name="source_interleave_y"
 | 
				
			||||||
            onchange={on_source_interleave_y_change}
 | 
					            onchange={on_source_interleave_y_change}
 | 
				
			||||||
            value={ct_state.transfer.interleave_source.1.to_string()}/>
 | 
					            value={ct_state.transfer.transfer_region.interleave_source.1.to_string()}/>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
            {"Destination Interleave "}
 | 
					            {"Destination Interleave "}
 | 
				
			||||||
            <label for="dest_interleave_x">{"Row:"}</label>
 | 
					            <label for="dest_interleave_x">{"Row:"}</label>
 | 
				
			||||||
            <input type="number" name="dest_interleave_x"
 | 
					            <input type="number" name="dest_interleave_x"
 | 
				
			||||||
            onchange={on_dest_interleave_x_change} value={ct_state.transfer.interleave_dest.0.to_string()}/>
 | 
					            onchange={on_dest_interleave_x_change}
 | 
				
			||||||
 | 
					            value={ct_state.transfer.transfer_region.interleave_dest.0.to_string()}/>
 | 
				
			||||||
            <label for="dest_interleave_y">{"Col:"}</label>
 | 
					            <label for="dest_interleave_y">{"Col:"}</label>
 | 
				
			||||||
            <input type="number" name="dest_interleave_y"
 | 
					            <input type="number" name="dest_interleave_y"
 | 
				
			||||||
            onchange={on_dest_interleave_y_change} value={ct_state.transfer.interleave_dest.1.to_string()}/>
 | 
					            onchange={on_dest_interleave_y_change}
 | 
				
			||||||
 | 
					            value={ct_state.transfer.transfer_region.interleave_dest.1.to_string()}/>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <input type="button" name="create_transfer" onclick={create_transfer_button_callback}
 | 
					            <input type="button" name="new_transfer" onclick={new_transfer_button_callback}
 | 
				
			||||||
            value={"Create"} />
 | 
					            value={"New"} />
 | 
				
			||||||
 | 
					            <input type="button" name="save_transfer" onclick={save_transfer_button_callback}
 | 
				
			||||||
 | 
					            value={"Save"} />
 | 
				
			||||||
 | 
					            <input type="button" name="delete_transfer" onclick={delete_transfer_button_callback}
 | 
				
			||||||
 | 
					            value={"Delete"} />
 | 
				
			||||||
            </form>
 | 
					            </form>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -203,30 +283,38 @@ impl TryFrom<String> for RegionDisplay {
 | 
				
			||||||
            static ref REGION_REGEX: Regex = Regex::new(r"([A-Z]+)(\d+):([A-Z]+)(\d+)").unwrap();
 | 
					            static ref REGION_REGEX: Regex = Regex::new(r"([A-Z]+)(\d+):([A-Z]+)(\d+)").unwrap();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if let Some(captures) = REGION_REGEX.captures(&value) {
 | 
					        if let Some(captures) = REGION_REGEX.captures(&value) {
 | 
				
			||||||
            if captures.len() != 5 { return Err("Not enough capture groups") }
 | 
					            if captures.len() != 5 {
 | 
				
			||||||
 | 
					                return Err("Not enough capture groups");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            let col_start = letters_to_num(&captures[1]).ok_or("Column start failed to parse")?;
 | 
					            let col_start = letters_to_num(&captures[1]).ok_or("Column start failed to parse")?;
 | 
				
			||||||
            let col_end = letters_to_num(&captures[3]).ok_or("Column end failed to parse")?;
 | 
					            let col_end = letters_to_num(&captures[3]).ok_or("Column end failed to parse")?;
 | 
				
			||||||
            let row_start: u8 = captures[2].parse::<u8>().or(Err("Row start failed to parse"))?;
 | 
					            let row_start: u8 = captures[2]
 | 
				
			||||||
            let row_end: u8 = captures[4].parse::<u8>().or(Err("Row end failed to parse"))?;
 | 
					                .parse::<u8>()
 | 
				
			||||||
 | 
					                .or(Err("Row start failed to parse"))?;
 | 
				
			||||||
 | 
					            let row_end: u8 = captures[4]
 | 
				
			||||||
 | 
					                .parse::<u8>()
 | 
				
			||||||
 | 
					                .or(Err("Row end failed to parse"))?;
 | 
				
			||||||
            return Ok(RegionDisplay {
 | 
					            return Ok(RegionDisplay {
 | 
				
			||||||
                text: value,
 | 
					                text: value,
 | 
				
			||||||
                col_start,
 | 
					                col_start,
 | 
				
			||||||
                row_start,
 | 
					                row_start,
 | 
				
			||||||
                col_end,
 | 
					                col_end,
 | 
				
			||||||
                row_end,
 | 
					                row_end,
 | 
				
			||||||
                })
 | 
					            });
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            return Err("Regex match failed")
 | 
					            return Err("Regex match failed");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl From<&Region> for RegionDisplay {
 | 
					impl From<&Region> for RegionDisplay {
 | 
				
			||||||
    fn from(value: &Region) -> Self {
 | 
					    fn from(value: &Region) -> Self {
 | 
				
			||||||
        match *value {
 | 
					        match *value {
 | 
				
			||||||
            Region::Point((col, row)) => RegionDisplay::try_from((col,row,col,row)).ok().unwrap(),
 | 
					            Region::Point((col, row)) => {
 | 
				
			||||||
            Region::Rect(c1, c2) =>
 | 
					                RegionDisplay::try_from((col, row, col, row)).ok().unwrap()
 | 
				
			||||||
                RegionDisplay::try_from((c1.0,c1.1,c2.0,c2.1)).ok().unwrap()
 | 
					            }
 | 
				
			||||||
 | 
					            Region::Rect(c1, c2) => RegionDisplay::try_from((c1.0, c1.1, c2.0, c2.1))
 | 
				
			||||||
 | 
					                .ok()
 | 
				
			||||||
 | 
					                .unwrap(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -235,14 +323,17 @@ impl From<&RegionDisplay> for Region {
 | 
				
			||||||
        if value.col_start == value.col_end && value.row_start == value.row_end {
 | 
					        if value.col_start == value.col_end && value.row_start == value.row_end {
 | 
				
			||||||
            Region::Point((value.col_start, value.row_start))
 | 
					            Region::Point((value.col_start, value.row_start))
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Region::Rect((value.col_start, value.row_start), (value.col_end, value.row_end))
 | 
					            Region::Rect(
 | 
				
			||||||
 | 
					                (value.col_start, value.row_start),
 | 
				
			||||||
 | 
					                (value.col_end, value.row_end),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl TryFrom<(u8,u8,u8,u8)> for RegionDisplay {
 | 
					impl TryFrom<(u8, u8, u8, u8)> for RegionDisplay {
 | 
				
			||||||
    type Error = &'static str;
 | 
					    type Error = &'static str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn try_from(value: (u8,u8,u8,u8)) -> Result<Self, Self::Error> {
 | 
					    fn try_from(value: (u8, u8, u8, u8)) -> Result<Self, Self::Error> {
 | 
				
			||||||
        // (Column Start, Row Start, Column End, Row End)
 | 
					        // (Column Start, Row Start, Column End, Row End)
 | 
				
			||||||
        // This can only possibly fail if one of the coordinates is zero...
 | 
					        // This can only possibly fail if one of the coordinates is zero...
 | 
				
			||||||
        let cs = num_to_letters(value.0).ok_or("Column start failed to parse")?;
 | 
					        let cs = num_to_letters(value.0).ok_or("Column start failed to parse")?;
 | 
				
			||||||
| 
						 | 
					@ -260,13 +351,17 @@ fn letters_to_num(letters: &str) -> Option<u8> {
 | 
				
			||||||
    let mut num: u8 = 0;
 | 
					    let mut num: u8 = 0;
 | 
				
			||||||
    for (i, letter) in letters.chars().rev().enumerate() {
 | 
					    for (i, letter) in letters.chars().rev().enumerate() {
 | 
				
			||||||
        let n = letter as u8;
 | 
					        let n = letter as u8;
 | 
				
			||||||
        if n < 65 || n > 90 { return None }
 | 
					        if n < 65 || n > 90 {
 | 
				
			||||||
        num = num.checked_add((26_i32.pow(i as u32)*(n as i32 - 64)).try_into().ok()?)?;
 | 
					            return None;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    return Some(num)
 | 
					        num = num.checked_add((26_i32.pow(i as u32) * (n as i32 - 64)).try_into().ok()?)?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Some(num);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
fn num_to_letters(num: u8) -> Option<String> {
 | 
					fn num_to_letters(num: u8) -> Option<String> {
 | 
				
			||||||
    if num == 0 { return None } // Otherwise, we will not return none!
 | 
					    if num == 0 {
 | 
				
			||||||
 | 
					        return None;
 | 
				
			||||||
 | 
					    } // Otherwise, we will not return none!
 | 
				
			||||||
      // As another note, we can't represent higher than "IV" anyway;
 | 
					      // As another note, we can't represent higher than "IV" anyway;
 | 
				
			||||||
      // thus there's no reason for a loop (26^n with n>1 will NOT occur).
 | 
					      // thus there's no reason for a loop (26^n with n>1 will NOT occur).
 | 
				
			||||||
    let mut text = "".to_string();
 | 
					    let mut text = "".to_string();
 | 
				
			||||||
| 
						 | 
					@ -277,9 +372,9 @@ fn num_to_letters(num: u8) -> Option<String> {
 | 
				
			||||||
        digit2 = 26;
 | 
					        digit2 = 26;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if digit1 != 0 {
 | 
					    if digit1 != 0 {
 | 
				
			||||||
        text.push((64+digit1) as char)
 | 
					        text.push((64 + digit1) as char)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    text.push((64+digit2) as char);
 | 
					    text.push((64 + digit2) as char);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Some(text.to_string());
 | 
					    return Some(text.to_string());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -295,8 +390,8 @@ mod tests {
 | 
				
			||||||
    fn test_letters_to_num() {
 | 
					    fn test_letters_to_num() {
 | 
				
			||||||
        assert_eq!(letters_to_num("D"), Some(4));
 | 
					        assert_eq!(letters_to_num("D"), Some(4));
 | 
				
			||||||
        assert_eq!(letters_to_num("d"), None);
 | 
					        assert_eq!(letters_to_num("d"), None);
 | 
				
			||||||
        assert_eq!(letters_to_num("AD"), Some(26+4));
 | 
					        assert_eq!(letters_to_num("AD"), Some(26 + 4));
 | 
				
			||||||
        assert_eq!(letters_to_num("CG"), Some(3*26+7));
 | 
					        assert_eq!(letters_to_num("CG"), Some(3 * 26 + 7));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
| 
						 | 
					@ -312,8 +407,14 @@ mod tests {
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    #[wasm_bindgen_test]
 | 
					    #[wasm_bindgen_test]
 | 
				
			||||||
    fn test_l2n_and_n2l() {
 | 
					    fn test_l2n_and_n2l() {
 | 
				
			||||||
        assert_eq!(num_to_letters(letters_to_num("A").unwrap()), Some("A".to_string()));
 | 
					        assert_eq!(
 | 
				
			||||||
        assert_eq!(num_to_letters(letters_to_num("BJ").unwrap()), Some("BJ".to_string()));
 | 
					            num_to_letters(letters_to_num("A").unwrap()),
 | 
				
			||||||
 | 
					            Some("A".to_string())
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            num_to_letters(letters_to_num("BJ").unwrap()),
 | 
				
			||||||
 | 
					            Some("BJ".to_string())
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        for i in 1..=255 {
 | 
					        for i in 1..=255 {
 | 
				
			||||||
            assert_eq!(letters_to_num(&num_to_letters(i as u8).unwrap()), Some(i));
 | 
					            assert_eq!(letters_to_num(&num_to_letters(i as u8).unwrap()), Some(i));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -327,7 +428,7 @@ mod tests {
 | 
				
			||||||
            row_start: 1,
 | 
					            row_start: 1,
 | 
				
			||||||
            row_end: 5,
 | 
					            row_end: 5,
 | 
				
			||||||
            col_start: 1,
 | 
					            col_start: 1,
 | 
				
			||||||
            col_end: 5
 | 
					            col_end: 5,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        assert_eq!(desired, "A1:E5".to_string().try_into().unwrap());
 | 
					        assert_eq!(desired, "A1:E5".to_string().try_into().unwrap());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,11 +2,11 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use uuid::Uuid;
 | 
					use uuid::Uuid;
 | 
				
			||||||
use wasm_bindgen::JsCast;
 | 
					use wasm_bindgen::JsCast;
 | 
				
			||||||
use web_sys::{EventTarget, HtmlElement, HtmlDialogElement};
 | 
					use web_sys::{EventTarget, HtmlDialogElement, HtmlElement};
 | 
				
			||||||
use yew::prelude::*;
 | 
					use yew::prelude::*;
 | 
				
			||||||
use yewdux::prelude::*;
 | 
					use yewdux::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::components::states::{MainState, CurrentTransfer};
 | 
					use crate::components::states::{CurrentTransfer, MainState};
 | 
				
			||||||
use crate::components::transfer_menu::RegionDisplay;
 | 
					use crate::components::transfer_menu::RegionDisplay;
 | 
				
			||||||
use crate::data::transfer_region::Region;
 | 
					use crate::data::transfer_region::Region;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,10 +19,10 @@ pub struct TreeProps {
 | 
				
			||||||
pub fn Tree(props: &TreeProps) -> Html {
 | 
					pub fn Tree(props: &TreeProps) -> Html {
 | 
				
			||||||
    let (main_state, main_dispatch) = use_store::<MainState>();
 | 
					    let (main_state, main_dispatch) = use_store::<MainState>();
 | 
				
			||||||
    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
					    let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
 | 
				
			||||||
    let plate_modal_id: UseStateHandle<Option<Uuid>> = use_state(|| {None});
 | 
					    let plate_modal_id: UseStateHandle<Option<Uuid>> = use_state(|| None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let open_plate_info_callback = {
 | 
					    let open_plate_info_callback = {
 | 
				
			||||||
        let plate_menu_id =plate_modal_id.clone();
 | 
					        let plate_menu_id = plate_modal_id.clone();
 | 
				
			||||||
        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());
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ pub fn Tree(props: &TreeProps) -> Html {
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let plate_info_close_callback = {
 | 
					    let plate_info_close_callback = {
 | 
				
			||||||
        let plate_menu_id =plate_modal_id.clone();
 | 
					        let plate_menu_id = plate_modal_id.clone();
 | 
				
			||||||
        Callback::from(move |_| {
 | 
					        Callback::from(move |_| {
 | 
				
			||||||
            plate_menu_id.set(None);
 | 
					            plate_menu_id.set(None);
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -60,10 +60,11 @@ pub fn Tree(props: &TreeProps) -> Html {
 | 
				
			||||||
            if let Some(li) = li {
 | 
					            if let Some(li) = li {
 | 
				
			||||||
                if let Ok(id) = u128::from_str_radix(li.id().as_str(), 10) {
 | 
					                if let Ok(id) = u128::from_str_radix(li.id().as_str(), 10) {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.source_region = Region::default();
 | 
					                        state.transfer.transfer_region.source_region = Region::default();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    main_dispatch.reduce_mut(|state| {
 | 
					                    main_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.selected_source_plate = Uuid::from_u128(id);
 | 
					                        state.selected_source_plate = Uuid::from_u128(id);
 | 
				
			||||||
 | 
					                        state.selected_transfer = Uuid::nil();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -79,48 +80,88 @@ pub fn Tree(props: &TreeProps) -> Html {
 | 
				
			||||||
            if let Some(li) = li {
 | 
					            if let Some(li) = li {
 | 
				
			||||||
                if let Ok(id) = u128::from_str_radix(li.id().as_str(), 10) {
 | 
					                if let Ok(id) = u128::from_str_radix(li.id().as_str(), 10) {
 | 
				
			||||||
                    ct_dispatch.reduce_mut(|state| {
 | 
					                    ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.transfer.dest_region = Region::default();
 | 
					                        state.transfer.transfer_region.dest_region = Region::default();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    main_dispatch.reduce_mut(|state| {
 | 
					                    main_dispatch.reduce_mut(|state| {
 | 
				
			||||||
                        state.selected_dest_plate = Uuid::from_u128(id);
 | 
					                        state.selected_dest_plate = Uuid::from_u128(id);
 | 
				
			||||||
 | 
					                        state.selected_transfer = Uuid::nil();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let source_plates = main_state.source_plates.iter()
 | 
					    let transfer_select_callback = {
 | 
				
			||||||
 | 
					        let main_state = main_state.clone();
 | 
				
			||||||
 | 
					        let main_dispatch = main_dispatch.clone();
 | 
				
			||||||
 | 
					        let ct_dispatch = ct_dispatch.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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) = u128::from_str_radix(li.id().as_str(), 10) {
 | 
				
			||||||
 | 
					                    let id = Uuid::from_u128(id);
 | 
				
			||||||
 | 
					                    if let Some(transfer) = main_state.transfers
 | 
				
			||||||
 | 
					                                            .iter().find(|transfer| transfer.get_uuid() == id) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        main_dispatch.reduce_mut(|state| {
 | 
				
			||||||
 | 
					                            state.selected_source_plate = transfer.source_id;
 | 
				
			||||||
 | 
					                            state.selected_dest_plate = transfer.dest_id;
 | 
				
			||||||
 | 
					                            state.selected_transfer = id;
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        ct_dispatch.reduce_mut(|state| {
 | 
				
			||||||
 | 
					                            state.transfer = transfer.clone();
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let source_plates = main_state
 | 
				
			||||||
 | 
					        .source_plates
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
        .map(|spi| {
 | 
					        .map(|spi| {
 | 
				
			||||||
            html!{ <li id={spi.get_uuid().as_u128().to_string()}
 | 
					            html! { <li id={spi.get_uuid().as_u128().to_string()}
 | 
				
			||||||
            ondblclick={open_plate_info_callback.clone()}
 | 
					            ondblclick={open_plate_info_callback.clone()}
 | 
				
			||||||
            onclick={source_plate_select_callback.clone()}
 | 
					            onclick={source_plate_select_callback.clone()}
 | 
				
			||||||
            class={classes!(
 | 
					            class={classes!(
 | 
				
			||||||
                        Some(if spi.get_uuid() == main_state.selected_source_plate {Some("selected")}
 | 
					                if spi.get_uuid() == main_state.selected_source_plate {Some("selected")}
 | 
				
			||||||
                             else {None})
 | 
					                     else {None}
 | 
				
			||||||
            )}>
 | 
					            )}>
 | 
				
			||||||
                {String::from(spi)}
 | 
					                {String::from(spi)}
 | 
				
			||||||
                </li> }
 | 
					                </li> }
 | 
				
			||||||
        }).collect::<Html>();
 | 
					        })
 | 
				
			||||||
    let dest_plates = main_state.destination_plates.iter()
 | 
					        .collect::<Html>();
 | 
				
			||||||
 | 
					    let dest_plates = main_state
 | 
				
			||||||
 | 
					        .destination_plates
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
        .map(|dpi| {
 | 
					        .map(|dpi| {
 | 
				
			||||||
            html!{ <li id={dpi.get_uuid().as_u128().to_string()}
 | 
					            html! { <li id={dpi.get_uuid().as_u128().to_string()}
 | 
				
			||||||
            ondblclick={open_plate_info_callback.clone()}
 | 
					            ondblclick={open_plate_info_callback.clone()}
 | 
				
			||||||
            onclick={destination_plate_select_callback.clone()}
 | 
					            onclick={destination_plate_select_callback.clone()}
 | 
				
			||||||
            class={classes!(
 | 
					            class={classes!(
 | 
				
			||||||
                        Some(if dpi.get_uuid() == main_state.selected_dest_plate {Some("selected")}
 | 
					                if dpi.get_uuid() == main_state.selected_dest_plate {Some("selected")}
 | 
				
			||||||
                             else {None})
 | 
					                     else {None}
 | 
				
			||||||
            )}> {String::from(dpi)} </li> }
 | 
					            )}> {String::from(dpi)} </li> }
 | 
				
			||||||
        }).collect::<Html>();
 | 
					        })
 | 
				
			||||||
    let transfers = main_state.transfers.iter()
 | 
					        .collect::<Html>();
 | 
				
			||||||
 | 
					    let transfers = main_state
 | 
				
			||||||
 | 
					        .transfers
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
        .map(|transfer| {
 | 
					        .map(|transfer| {
 | 
				
			||||||
            html!{ <li id={transfer.get_uuid().as_u128().to_string()}>
 | 
					            html! { <li id={transfer.get_uuid().as_u128().to_string()}
 | 
				
			||||||
 | 
					                     onclick={transfer_select_callback.clone()}
 | 
				
			||||||
 | 
					                     class={classes!(
 | 
				
			||||||
 | 
					                         if transfer.get_uuid() == main_state.selected_transfer {Some("selected")}
 | 
				
			||||||
 | 
					                         else {None})}>
 | 
				
			||||||
                {transfer.name.clone()}
 | 
					                {transfer.name.clone()}
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .collect::<Html>();
 | 
					        .collect::<Html>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
        <div class="tree">
 | 
					        <div class="tree">
 | 
				
			||||||
            <div id="source-plates">
 | 
					            <div id="source-plates">
 | 
				
			||||||
| 
						 | 
					@ -170,19 +211,23 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
 | 
				
			||||||
    let (state, dispatch) = use_store::<MainState>();
 | 
					    let (state, dispatch) = use_store::<MainState>();
 | 
				
			||||||
    let dialog_ref = use_node_ref();
 | 
					    let dialog_ref = use_node_ref();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut plate = state.source_plates.iter()
 | 
					    let mut plate = state
 | 
				
			||||||
        .find(|spi| {spi.get_uuid() == props.id});
 | 
					        .source_plates
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .find(|spi| spi.get_uuid() == props.id);
 | 
				
			||||||
    if plate == None {
 | 
					    if plate == None {
 | 
				
			||||||
        plate = state.destination_plates.iter()
 | 
					        plate = state
 | 
				
			||||||
            .find(|dpi| {dpi.get_uuid() == props.id});
 | 
					            .destination_plates
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .find(|dpi| dpi.get_uuid() == props.id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let plate_name = match plate {
 | 
					    let plate_name = match plate {
 | 
				
			||||||
        Some(plate) => plate.name.clone(),
 | 
					        Some(plate) => plate.name.clone(),
 | 
				
			||||||
        None => "Not Found".to_string()
 | 
					        None => "Not Found".to_string(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let onclose = {
 | 
					    let onclose = {
 | 
				
			||||||
        let dialog_close_callback = props.dialog_close_callback.clone();
 | 
					        let dialog_close_callback = props.dialog_close_callback.clone();
 | 
				
			||||||
        move |_| {dialog_close_callback.emit(())}
 | 
					        move |_| dialog_close_callback.emit(())
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let delete_onclick = {
 | 
					    let delete_onclick = {
 | 
				
			||||||
| 
						 | 
					@ -197,10 +242,16 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let dialog_ref = dialog_ref.clone();
 | 
					        let dialog_ref = dialog_ref.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        use_effect_with_deps(|dialog_ref| {
 | 
					        use_effect_with_deps(
 | 
				
			||||||
            dialog_ref.cast::<HtmlDialogElement>().unwrap().show_modal().ok();
 | 
					            |dialog_ref| {
 | 
				
			||||||
 | 
					                dialog_ref
 | 
				
			||||||
 | 
					                    .cast::<HtmlDialogElement>()
 | 
				
			||||||
 | 
					                    .unwrap()
 | 
				
			||||||
 | 
					                    .show_modal()
 | 
				
			||||||
 | 
					                    .ok();
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        dialog_ref);
 | 
					            dialog_ref,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html! {
 | 
					    html! {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
pub mod plate;
 | 
					pub mod plate;
 | 
				
			||||||
pub mod transfer_region;
 | 
					 | 
				
			||||||
pub mod transfer;
 | 
					 | 
				
			||||||
pub mod plate_instances;
 | 
					pub mod plate_instances;
 | 
				
			||||||
 | 
					pub mod transfer;
 | 
				
			||||||
 | 
					pub mod transfer_region;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
use serde::{Serialize, Deserialize};
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Default, Clone, Copy, Serialize, Deserialize, Debug)]
 | 
					#[derive(PartialEq, Eq, Default, Clone, Copy, Serialize, Deserialize, Debug)]
 | 
				
			||||||
pub struct Plate {
 | 
					pub struct Plate {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
use uuid::Uuid;
 | 
					 | 
				
			||||||
use serde::{Serialize, Deserialize};
 | 
					 | 
				
			||||||
use super::plate::*;
 | 
					use super::plate::*;
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use uuid::Uuid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Clone, Serialize, Deserialize)]
 | 
					#[derive(PartialEq, Clone, Serialize, Deserialize)]
 | 
				
			||||||
pub struct PlateInstance {
 | 
					pub struct PlateInstance {
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,10 @@ pub struct PlateInstance {
 | 
				
			||||||
impl PlateInstance {
 | 
					impl PlateInstance {
 | 
				
			||||||
    pub fn new(sort: PlateType, format: PlateFormat, name: String) -> Self {
 | 
					    pub fn new(sort: PlateType, format: PlateFormat, name: String) -> Self {
 | 
				
			||||||
        PlateInstance {
 | 
					        PlateInstance {
 | 
				
			||||||
            plate: Plate { plate_type: sort, plate_format: format },
 | 
					            plate: Plate {
 | 
				
			||||||
 | 
					                plate_type: sort,
 | 
				
			||||||
 | 
					                plate_format: format,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            id: Uuid::new_v4(),
 | 
					            id: Uuid::new_v4(),
 | 
				
			||||||
            name,
 | 
					            name,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -29,6 +32,10 @@ impl PlateInstance {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<Plate> for PlateInstance {
 | 
					impl From<Plate> for PlateInstance {
 | 
				
			||||||
    fn from(value: Plate) -> Self {
 | 
					    fn from(value: Plate) -> Self {
 | 
				
			||||||
        PlateInstance { plate: value, id: Uuid::new_v4(), name: "New Plate".to_string() }
 | 
					        PlateInstance {
 | 
				
			||||||
 | 
					            plate: value,
 | 
				
			||||||
 | 
					            id: Uuid::new_v4(),
 | 
				
			||||||
 | 
					            name: "New Plate".to_string(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,10 @@
 | 
				
			||||||
 | 
					use super::plate_instances::*;
 | 
				
			||||||
 | 
					use super::transfer_region::*;
 | 
				
			||||||
use serde::Deserialize;
 | 
					use serde::Deserialize;
 | 
				
			||||||
use serde::Serialize;
 | 
					use serde::Serialize;
 | 
				
			||||||
use uuid::Uuid;
 | 
					use uuid::Uuid;
 | 
				
			||||||
use super::transfer_region::*;
 | 
					 | 
				
			||||||
use super::plate_instances::*;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Clone, Serialize, Deserialize)]
 | 
					#[derive(PartialEq, Clone, Default, Debug, Serialize, Deserialize)]
 | 
				
			||||||
pub struct Transfer {
 | 
					pub struct Transfer {
 | 
				
			||||||
    pub source_id: Uuid,
 | 
					    pub source_id: Uuid,
 | 
				
			||||||
    pub dest_id: Uuid,
 | 
					    pub dest_id: Uuid,
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,12 @@ pub struct Transfer {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Transfer {
 | 
					impl Transfer {
 | 
				
			||||||
    pub fn new(source: PlateInstance, dest: PlateInstance, tr: TransferRegion, name: String) -> Self {
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        source: PlateInstance,
 | 
				
			||||||
 | 
					        dest: PlateInstance,
 | 
				
			||||||
 | 
					        tr: TransferRegion,
 | 
				
			||||||
 | 
					        name: String,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            source_id: source.get_uuid(),
 | 
					            source_id: source.get_uuid(),
 | 
				
			||||||
            dest_id: dest.get_uuid(),
 | 
					            dest_id: dest.get_uuid(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
use serde::{Serialize, Deserialize};
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::plate::Plate;
 | 
					use super::plate::Plate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ pub enum Region {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl Default for Region {
 | 
					impl Default for Region {
 | 
				
			||||||
    fn default() -> Self {
 | 
					    fn default() -> Self {
 | 
				
			||||||
        Region::Point((1,1))
 | 
					        Region::Point((1, 1))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl TryFrom<Region> for ((u8, u8), (u8, u8)) {
 | 
					impl TryFrom<Region> for ((u8, u8), (u8, u8)) {
 | 
				
			||||||
| 
						 | 
					@ -41,8 +41,8 @@ impl Default for TransferRegion {
 | 
				
			||||||
            source_region: Region::default(),
 | 
					            source_region: Region::default(),
 | 
				
			||||||
            dest_plate: Plate::default(),
 | 
					            dest_plate: Plate::default(),
 | 
				
			||||||
            dest_region: Region::default(),
 | 
					            dest_region: Region::default(),
 | 
				
			||||||
            interleave_source: (1,1),
 | 
					            interleave_source: (1, 1),
 | 
				
			||||||
            interleave_dest: (1,1)
 | 
					            interleave_dest: (1, 1),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,8 @@ impl TransferRegion {
 | 
				
			||||||
                // We'll reassign these values (still not mutable) just in case.
 | 
					                // We'll reassign these values (still not mutable) just in case.
 | 
				
			||||||
                // This behaviour shouldn't be replicated for destination wells
 | 
					                // This behaviour shouldn't be replicated for destination wells
 | 
				
			||||||
                // because a zero step permits pooling.
 | 
					                // because a zero step permits pooling.
 | 
				
			||||||
            let (interleave_i, interleave_j) = (i8::max(interleave_i, 1), i8::max(interleave_j, 1));
 | 
					                let (interleave_i, interleave_j) =
 | 
				
			||||||
 | 
					                    (i8::max(interleave_i, 1), i8::max(interleave_j, 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for i in (ul.0..=br.0).step_by(i8::abs(interleave_i) as usize) {
 | 
					                for i in (ul.0..=br.0).step_by(i8::abs(interleave_i) as usize) {
 | 
				
			||||||
                    for j in (ul.1..=br.1).step_by(i8::abs(interleave_j) as usize) {
 | 
					                    for j in (ul.1..=br.1).step_by(i8::abs(interleave_j) as usize) {
 | 
				
			||||||
| 
						 | 
					@ -69,8 +70,8 @@ impl TransferRegion {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return wells;
 | 
					                return wells;
 | 
				
			||||||
            },
 | 
					            }
 | 
				
			||||||
            Region::Point(p) => return vec![p]
 | 
					            Region::Point(p) => return vec![p],
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,8 +109,8 @@ impl TransferRegion {
 | 
				
			||||||
        let il_source = self.interleave_source;
 | 
					        let il_source = self.interleave_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let source_corners: ((u8, u8), (u8, u8)) = match self.source_region {
 | 
					        let source_corners: ((u8, u8), (u8, u8)) = match self.source_region {
 | 
				
			||||||
            Region::Point((x,y)) => ((x,y),(x,y)),
 | 
					            Region::Point((x, y)) => ((x, y), (x, y)),
 | 
				
			||||||
            Region::Rect(c1,c2) => (c1,c2)
 | 
					            Region::Rect(c1, c2) => (c1, c2),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let (source_ul, _) = standardize_rectangle(&source_corners.0, &source_corners.1);
 | 
					        let (source_ul, _) = standardize_rectangle(&source_corners.0, &source_corners.1);
 | 
				
			||||||
        // This map is not necessarily injective or surjective,
 | 
					        // This map is not necessarily injective or surjective,
 | 
				
			||||||
| 
						 | 
					@ -147,7 +148,8 @@ impl TransferRegion {
 | 
				
			||||||
                    if source_wells.contains(&(i, j)) {
 | 
					                    if source_wells.contains(&(i, j)) {
 | 
				
			||||||
                        let possible_destination_wells = create_dense_rectangle(&c1, &c2);
 | 
					                        let possible_destination_wells = create_dense_rectangle(&c1, &c2);
 | 
				
			||||||
                        let (d_ul, d_br) = standardize_rectangle(&c1, &c2);
 | 
					                        let (d_ul, d_br) = standardize_rectangle(&c1, &c2);
 | 
				
			||||||
                        let (s_ul, s_br) = standardize_rectangle(&source_corners.0, &source_corners.1);
 | 
					                        let (s_ul, s_br) =
 | 
				
			||||||
 | 
					                            standardize_rectangle(&source_corners.0, &source_corners.1);
 | 
				
			||||||
                        let s_dims = (
 | 
					                        let s_dims = (
 | 
				
			||||||
                            s_br.0.checked_sub(s_ul.0).unwrap() + 1,
 | 
					                            s_br.0.checked_sub(s_ul.0).unwrap() + 1,
 | 
				
			||||||
                            s_br.1.checked_sub(s_ul.1).unwrap() + 1,
 | 
					                            s_br.1.checked_sub(s_ul.1).unwrap() + 1,
 | 
				
			||||||
| 
						 | 
					@ -156,24 +158,39 @@ impl TransferRegion {
 | 
				
			||||||
                            d_br.0.checked_sub(d_ul.0).unwrap() + 1,
 | 
					                            d_br.0.checked_sub(d_ul.0).unwrap() + 1,
 | 
				
			||||||
                            d_br.1.checked_sub(d_ul.1).unwrap() + 1,
 | 
					                            d_br.1.checked_sub(d_ul.1).unwrap() + 1,
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
                        let N_s = ( // Number of used source wells
 | 
					                        let N_s = (
 | 
				
			||||||
                            (s_dims.0 + il_source.0.abs() as u8 - 1).div_euclid(il_source.0.abs() as u8),
 | 
					                            // Number of used source wells
 | 
				
			||||||
                            (s_dims.1 + il_source.1.abs() as u8 - 1).div_euclid(il_source.1.abs() as u8),
 | 
					                            (s_dims.0 + il_source.0.abs() as u8 - 1)
 | 
				
			||||||
 | 
					                                .div_euclid(il_source.0.abs() as u8),
 | 
				
			||||||
 | 
					                            (s_dims.1 + il_source.1.abs() as u8 - 1)
 | 
				
			||||||
 | 
					                                .div_euclid(il_source.1.abs() as u8),
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
                        let D_per_replicate = ( // How many wells are used per replicate?
 | 
					                        let D_per_replicate = (
 | 
				
			||||||
 | 
					                            // How many wells are used per replicate?
 | 
				
			||||||
                            (N_s.0 * (il_dest.0.abs() as u8)),
 | 
					                            (N_s.0 * (il_dest.0.abs() as u8)),
 | 
				
			||||||
                            (N_s.1 * (il_dest.1.abs() as u8))
 | 
					                            (N_s.1 * (il_dest.1.abs() as u8)),
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
                        let count = ( // How many times can we replicate?
 | 
					                        let count = (
 | 
				
			||||||
                           (1..).position(
 | 
					                            // How many times can we replicate?
 | 
				
			||||||
                               |n| n*N_s.0*il_dest.0.abs() as u8 - il_dest.0.abs() as u8 + 1
 | 
					                            (1..)
 | 
				
			||||||
                                   > d_dims.0).unwrap() as u8,
 | 
					                                .position(|n| {
 | 
				
			||||||
                           (1..).position(
 | 
					                                    n * N_s.0 * il_dest.0.abs() as u8 - il_dest.0.abs() as u8 + 1
 | 
				
			||||||
                               |n| n*N_s.1*il_dest.1.abs() as u8 - il_dest.1.abs() as u8 + 1
 | 
					                                        > d_dims.0
 | 
				
			||||||
                                   > d_dims.1).unwrap() as u8,
 | 
					                                })
 | 
				
			||||||
 | 
					                                .unwrap() as u8,
 | 
				
			||||||
 | 
					                            (1..)
 | 
				
			||||||
 | 
					                                .position(|n| {
 | 
				
			||||||
 | 
					                                    n * N_s.1 * il_dest.1.abs() as u8 - il_dest.1.abs() as u8 + 1
 | 
				
			||||||
 | 
					                                        > d_dims.1
 | 
				
			||||||
 | 
					                                })
 | 
				
			||||||
 | 
					                                .unwrap() as u8,
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
                        let i = i.saturating_sub(s_ul.0).saturating_div(il_source.0.abs() as u8);
 | 
					                        let i = i
 | 
				
			||||||
                        let j = j.saturating_sub(s_ul.1).saturating_div(il_source.1.abs() as u8);
 | 
					                            .saturating_sub(s_ul.0)
 | 
				
			||||||
 | 
					                            .saturating_div(il_source.0.abs() as u8);
 | 
				
			||||||
 | 
					                        let j = j
 | 
				
			||||||
 | 
					                            .saturating_sub(s_ul.1)
 | 
				
			||||||
 | 
					                            .saturating_div(il_source.1.abs() as u8);
 | 
				
			||||||
                        // log::debug!("N_s: {:?}, d_dims: {:?}, D_per: {:?}, count: {:?}", N_s, d_dims, D_per_replicate, count);
 | 
					                        // log::debug!("N_s: {:?}, d_dims: {:?}, D_per: {:?}, count: {:?}", N_s, d_dims, D_per_replicate, count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        Some(
 | 
					                        Some(
 | 
				
			||||||
| 
						 | 
					@ -191,12 +208,16 @@ impl TransferRegion {
 | 
				
			||||||
                                    == ((il_dest.1.abs() as u8 *j))
 | 
					                                    == ((il_dest.1.abs() as u8 *j))
 | 
				
			||||||
                                        % (N_s.1 * il_dest.1.abs() as u8)
 | 
					                                        % (N_s.1 * il_dest.1.abs() as u8)
 | 
				
			||||||
                                })
 | 
					                                })
 | 
				
			||||||
                                .filter(|(x,y)| {
 | 
					                                .filter(|(x, y)| {
 | 
				
			||||||
                                    // How many times have we replicated? < How many are we allowed
 | 
					                                    // How many times have we replicated? < How many are we allowed
 | 
				
			||||||
                                    // to replicate?
 | 
					                                    // to replicate?
 | 
				
			||||||
                                    x.checked_sub(d_ul.0).unwrap().div_euclid(N_s.0 * il_dest.0.abs() as u8) 
 | 
					                                    x.checked_sub(d_ul.0)
 | 
				
			||||||
                                        < count.0 &&
 | 
					                                        .unwrap()
 | 
				
			||||||
                                    y.checked_sub(d_ul.1).unwrap().div_euclid(N_s.1 * il_dest.1.abs() as u8)
 | 
					                                        .div_euclid(N_s.0 * il_dest.0.abs() as u8)
 | 
				
			||||||
 | 
					                                        < count.0
 | 
				
			||||||
 | 
					                                        && y.checked_sub(d_ul.1)
 | 
				
			||||||
 | 
					                                            .unwrap()
 | 
				
			||||||
 | 
					                                            .div_euclid(N_s.1 * il_dest.1.abs() as u8)
 | 
				
			||||||
                                            < count.1
 | 
					                                            < count.1
 | 
				
			||||||
                                })
 | 
					                                })
 | 
				
			||||||
                                .collect(),
 | 
					                                .collect(),
 | 
				
			||||||
| 
						 | 
					@ -383,42 +404,86 @@ mod tests {
 | 
				
			||||||
            source_plate: source,
 | 
					            source_plate: source,
 | 
				
			||||||
            source_region: Region::Rect((1, 1), (3, 3)),
 | 
					            source_region: Region::Rect((1, 1), (3, 3)),
 | 
				
			||||||
            dest_plate: destination,
 | 
					            dest_plate: destination,
 | 
				
			||||||
            dest_region: Region::Point((3,3)),
 | 
					            dest_region: Region::Point((3, 3)),
 | 
				
			||||||
            interleave_source: (1,1),
 | 
					            interleave_source: (1, 1),
 | 
				
			||||||
            interleave_dest: (1,1),
 | 
					            interleave_dest: (1, 1),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let transfer1_map = transfer1.calculate_map();
 | 
					        let transfer1_map = transfer1.calculate_map();
 | 
				
			||||||
        assert_eq!(transfer1_map((1,1)), Some(vec!{(3,3)}), "Failed basic shift transfer 1");
 | 
					        assert_eq!(
 | 
				
			||||||
        assert_eq!(transfer1_map((1,2)), Some(vec!{(3,4)}), "Failed basic shift transfer 2");
 | 
					            transfer1_map((1, 1)),
 | 
				
			||||||
        assert_eq!(transfer1_map((2,2)), Some(vec!{(4,4)}), "Failed basic shift transfer 3");
 | 
					            Some(vec! {(3,3)}),
 | 
				
			||||||
 | 
					            "Failed basic shift transfer 1"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer1_map((1, 2)),
 | 
				
			||||||
 | 
					            Some(vec! {(3,4)}),
 | 
				
			||||||
 | 
					            "Failed basic shift transfer 2"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer1_map((2, 2)),
 | 
				
			||||||
 | 
					            Some(vec! {(4,4)}),
 | 
				
			||||||
 | 
					            "Failed basic shift transfer 3"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let transfer2 = TransferRegion {
 | 
					        let transfer2 = TransferRegion {
 | 
				
			||||||
            source_plate: source,
 | 
					            source_plate: source,
 | 
				
			||||||
            source_region: Region::Rect((1, 1), (3, 3)),
 | 
					            source_region: Region::Rect((1, 1), (3, 3)),
 | 
				
			||||||
            dest_plate: destination,
 | 
					            dest_plate: destination,
 | 
				
			||||||
            dest_region: Region::Point((3,3)),
 | 
					            dest_region: Region::Point((3, 3)),
 | 
				
			||||||
            interleave_source: (2,2),
 | 
					            interleave_source: (2, 2),
 | 
				
			||||||
            interleave_dest: (1,1),
 | 
					            interleave_dest: (1, 1),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let transfer2_map = transfer2.calculate_map();
 | 
					        let transfer2_map = transfer2.calculate_map();
 | 
				
			||||||
        assert_eq!(transfer2_map((1,1)), Some(vec!{(3,3)}), "Failed source interleave, type simple 1");
 | 
					        assert_eq!(
 | 
				
			||||||
        assert_eq!(transfer2_map((1,2)), None, "Failed source interleave, type simple 2");
 | 
					            transfer2_map((1, 1)),
 | 
				
			||||||
        assert_eq!(transfer2_map((2,2)), None, "Failed source interleave, type simple 3");
 | 
					            Some(vec! {(3,3)}),
 | 
				
			||||||
        assert_eq!(transfer2_map((3,3)), Some(vec!{(4,4)}), "Failed source interleave, type simple 4");
 | 
					            "Failed source interleave, type simple 1"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer2_map((1, 2)),
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            "Failed source interleave, type simple 2"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer2_map((2, 2)),
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            "Failed source interleave, type simple 3"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer2_map((3, 3)),
 | 
				
			||||||
 | 
					            Some(vec! {(4,4)}),
 | 
				
			||||||
 | 
					            "Failed source interleave, type simple 4"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let transfer3 = TransferRegion {
 | 
					        let transfer3 = TransferRegion {
 | 
				
			||||||
            source_plate: source,
 | 
					            source_plate: source,
 | 
				
			||||||
            source_region: Region::Rect((1, 1), (3, 3)),
 | 
					            source_region: Region::Rect((1, 1), (3, 3)),
 | 
				
			||||||
            dest_plate: destination,
 | 
					            dest_plate: destination,
 | 
				
			||||||
            dest_region: Region::Point((3,3)),
 | 
					            dest_region: Region::Point((3, 3)),
 | 
				
			||||||
            interleave_source: (1,1),
 | 
					            interleave_source: (1, 1),
 | 
				
			||||||
            interleave_dest: (2,3),
 | 
					            interleave_dest: (2, 3),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let transfer3_map = transfer3.calculate_map();
 | 
					        let transfer3_map = transfer3.calculate_map();
 | 
				
			||||||
        assert_eq!(transfer3_map((1,1)), Some(vec!{(3,3)}), "Failed destination interleave, type simple 1");
 | 
					        assert_eq!(
 | 
				
			||||||
        assert_eq!(transfer3_map((2,1)), Some(vec!{(5,3)}), "Failed destination interleave, type simple 2");
 | 
					            transfer3_map((1, 1)),
 | 
				
			||||||
        assert_eq!(transfer3_map((1,2)), Some(vec!{(3,6)}), "Failed destination interleave, type simple 3");
 | 
					            Some(vec! {(3,3)}),
 | 
				
			||||||
        assert_eq!(transfer3_map((2,2)), Some(vec!{(5,6)}), "Failed destination interleave, type simple 4");
 | 
					            "Failed destination interleave, type simple 1"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer3_map((2, 1)),
 | 
				
			||||||
 | 
					            Some(vec! {(5,3)}),
 | 
				
			||||||
 | 
					            "Failed destination interleave, type simple 2"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer3_map((1, 2)),
 | 
				
			||||||
 | 
					            Some(vec! {(3,6)}),
 | 
				
			||||||
 | 
					            "Failed destination interleave, type simple 3"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer3_map((2, 2)),
 | 
				
			||||||
 | 
					            Some(vec! {(5,6)}),
 | 
				
			||||||
 | 
					            "Failed destination interleave, type simple 4"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
| 
						 | 
					@ -431,17 +496,24 @@ mod tests {
 | 
				
			||||||
            source_plate: source,
 | 
					            source_plate: source,
 | 
				
			||||||
            source_region: Region::Rect((1, 1), (2, 2)),
 | 
					            source_region: Region::Rect((1, 1), (2, 2)),
 | 
				
			||||||
            dest_plate: destination,
 | 
					            dest_plate: destination,
 | 
				
			||||||
            dest_region: Region::Rect((2,2),(11,11)),
 | 
					            dest_region: Region::Rect((2, 2), (11, 11)),
 | 
				
			||||||
            interleave_source: (1,1),
 | 
					            interleave_source: (1, 1),
 | 
				
			||||||
            interleave_dest: (3,3),
 | 
					            interleave_dest: (3, 3),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let transfer1_map = transfer1.calculate_map();
 | 
					        let transfer1_map = transfer1.calculate_map();
 | 
				
			||||||
        assert_eq!(transfer1_map((1,1)), Some(vec!{(2, 2), (2, 8), (8, 2), (8, 8)}), "Failed type replicate 1");
 | 
					        assert_eq!(
 | 
				
			||||||
        assert_eq!(transfer1_map((2,1)), Some(vec!{(5, 2), (5, 8), (11, 2), (11, 8)}), "Failed type replicate 1");
 | 
					            transfer1_map((1, 1)),
 | 
				
			||||||
 | 
					            Some(vec! {(2, 2), (2, 8), (8, 2), (8, 8)}),
 | 
				
			||||||
 | 
					            "Failed type replicate 1"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            transfer1_map((2, 1)),
 | 
				
			||||||
 | 
					            Some(vec! {(5, 2), (5, 8), (11, 2), (11, 8)}),
 | 
				
			||||||
 | 
					            "Failed type replicate 1"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    #[wasm_bindgen_test]
 | 
					    #[wasm_bindgen_test]
 | 
				
			||||||
    fn test_pooling_transfer() {
 | 
					    fn test_pooling_transfer() {}
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/lib.rs
								
								
								
								
							
							
						
						
									
										10
									
								
								src/lib.rs
								
								
								
								
							| 
						 | 
					@ -2,8 +2,8 @@
 | 
				
			||||||
mod components;
 | 
					mod components;
 | 
				
			||||||
mod data;
 | 
					mod data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use yew::prelude::*;
 | 
					 | 
				
			||||||
use components::main_window::MainWindow;
 | 
					use components::main_window::MainWindow;
 | 
				
			||||||
 | 
					use yew::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(debug_assertions)]
 | 
					#[cfg(debug_assertions)]
 | 
				
			||||||
use data::*;
 | 
					use data::*;
 | 
				
			||||||
| 
						 | 
					@ -24,14 +24,14 @@ pub fn plate_test() {
 | 
				
			||||||
        source_plate: source,
 | 
					        source_plate: source,
 | 
				
			||||||
        source_region: transfer_region::Region::Rect((1, 1), (2, 2)),
 | 
					        source_region: transfer_region::Region::Rect((1, 1), (2, 2)),
 | 
				
			||||||
        dest_plate: destination,
 | 
					        dest_plate: destination,
 | 
				
			||||||
        dest_region: transfer_region::Region::Rect((2,2),(11,11)),
 | 
					        dest_region: transfer_region::Region::Rect((2, 2), (11, 11)),
 | 
				
			||||||
        interleave_source: (1,1),
 | 
					        interleave_source: (1, 1),
 | 
				
			||||||
        interleave_dest: (3,3),
 | 
					        interleave_dest: (3, 3),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    println!("{}", transfer);
 | 
					    println!("{}", transfer);
 | 
				
			||||||
    let sws = transfer.get_source_wells();
 | 
					    let sws = transfer.get_source_wells();
 | 
				
			||||||
    let m = transfer.calculate_map();
 | 
					    let m = transfer.calculate_map();
 | 
				
			||||||
    for w in sws {
 | 
					    for w in sws {
 | 
				
			||||||
        println!("{:?} -> {:?}", w,m(w));
 | 
					        println!("{:?} -> {:?}", w, m(w));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue