use std::fmt::Display; use web_sys::{FileList, HtmlInputElement, MouseEvent}; use yew::{ classes, function_component, html, use_state, virtual_dom::{VChild, VNode}, Callback, ChangeData, Children, Html, InputData, NodeRef, Properties, }; macro_rules! css_class_enum { ($name: ident $(($prefix: literal))? { $( $variant: ident => $class: literal ),* }) => { #[derive(PartialEq, Eq, Clone, Copy)] pub enum $name { $( #[allow(dead_code)] $variant ),* } impl ToString for $name { fn to_string(&self) -> String { let suffix = match self { $( Self::$variant => $class ),* }; if suffix.is_empty() { String::default() } else { let mut acc = String::default(); $( acc += $prefix; acc.push('-'); )? acc += suffix; acc } } } }; } #[derive(Properties, PartialEq, Clone)] pub struct InputProps where T: Clone + PartialEq, E: Display + Clone + PartialEq, { pub label: &'static str, pub desc: Option<&'static str>, pub parsed: Option>, pub placeholder: Option, pub default: Option, #[prop_or(InputType::Text)] pub r#type: InputType, #[prop_or_default] pub oninput: Callback, #[prop_or_default] pub button: Option>, } css_class_enum! { InputType { Text => "text", Url => "url" } } #[function_component(Input)] pub fn input(props: &InputProps) -> Html where T: Display + Clone + PartialEq, E: Display + Clone + PartialEq, { let success = props.parsed.as_ref().map(|x| x.is_ok()).unwrap_or(false); let error = props.parsed.as_ref().map(|x| x.is_err()).unwrap_or(false); let id = props.label.to_lowercase().replace(' ', "-"); let applied_default_value = use_state(|| false); let node_ref = use_state(NodeRef::default); if let (false, Some(default), Some(input_element)) = ( *applied_default_value, props.default.as_ref(), node_ref.cast::(), ) { input_element.set_value(&default.to_string()); applied_default_value.set(true); } html! { <>
{ if let Some(parsed) = props.parsed.as_ref() { match parsed { Ok(_) => html!(), Err(_) => html!() } } else { html!() } }
{ props.button.clone().map(Html::from).unwrap_or_default() }
{ if let Some(Err(ref err)) = props.parsed.as_ref() { html!{
{ err }
} } else if let Some(desc) = props.desc { html! {

{ desc }

} } else { html!() } } } } #[derive(Properties, PartialEq, Clone)] pub struct CheckboxProps { pub label: &'static str, pub desc: Option<&'static str>, #[prop_or(false)] pub checked: bool, #[prop_or_default] pub onchange: Callback, } #[function_component(Checkbox)] pub fn checkbox(props: &CheckboxProps) -> Html { html! { <> { if let Some(desc) = props.desc { html! {

{ desc }

} } else { html!() } } } } #[derive(Properties, PartialEq, Clone)] pub struct FileUploadProps where T: Clone + PartialEq, E: Display + Clone + PartialEq, { pub label: &'static str, pub desc: Option<&'static str>, pub accept: Option<&'static str>, #[prop_or(false)] pub multiple: bool, pub parsed: Option>, #[prop_or_default] pub onchange: Callback, #[prop_or_default] pub button: Option>, } #[function_component(FileUpload)] pub fn file_upload(props: &FileUploadProps) -> Html where T: Clone + PartialEq, E: Display + Clone + PartialEq, { let success = props.parsed.as_ref().map(|x| x.is_ok()).unwrap_or(false); let error = props.parsed.as_ref().map(|x| x.is_err()).unwrap_or(false); let id = props.label.to_lowercase().replace(' ', "-"); html! { <>
file_list, _ => unreachable!() } })} /> { if let Some(parsed) = props.parsed.as_ref() { match parsed { Ok(_) => html!(), Err(_) => html!() } } else { html!() } }
{ props.button.clone().map(Html::from).unwrap_or_default() }
{ if let Some(Err(ref err)) = props.parsed.as_ref() { html!{
{ err }
} } else if let Some(desc) = props.desc { html! {

{ desc }

} } else { html!() } } } } #[derive(Properties, PartialEq, Clone)] pub struct SelectProps { #[prop_or_default] pub children: Children, #[prop_or(false)] pub disabled: bool, #[prop_or(false)] pub multiple: bool, } #[function_component(Select)] pub fn select(props: &SelectProps) -> Html { html! { } } #[derive(Properties, PartialEq, Clone)] pub struct OptionProps { #[prop_or_default] pub children: Children, #[prop_or(false)] pub selected: bool, #[prop_or(false)] pub disabled: bool, pub value: Option<&'static str>, } #[function_component(Opt)] pub fn option(props: &OptionProps) -> Html { html! { } } #[derive(Properties, PartialEq, Clone)] pub struct InputGroupProps { #[prop_or_default] pub children: Children, } #[function_component(InputGroup)] pub fn input_group(props: &InputGroupProps) -> Html { html! {
{ for props.children.iter() }
} } #[derive(Properties, PartialEq, Clone)] pub struct FormGroupProps { #[prop_or_default] pub children: Children, pub success: Option, } #[function_component(FormGroup)] pub fn form_group(props: &FormGroupProps) -> Html { html! {
{ for props.children.iter() }
} } #[derive(Properties, PartialEq, Clone)] pub struct TextAreaProps where T: Display + Clone + PartialEq, E: Display + Clone + PartialEq, { pub label: &'static str, pub desc: Option<&'static str>, pub parsed: Option>, pub placeholder: Option, pub default: Option, #[prop_or_default] pub oninput: Callback, pub rows: Option, pub cols: Option, } #[function_component(TextArea)] pub fn text_area(props: &TextAreaProps) -> Html where T: Display + Clone + PartialEq, E: Display + Clone + PartialEq, { let success = props.parsed.as_ref().map(|x| x.is_ok()).unwrap_or(false); let error = props.parsed.as_ref().map(|x| x.is_err()).unwrap_or(false); let id = props.label.to_lowercase().replace(' ', "-"); let applied_default_value = use_state(|| false); let node_ref = use_state(NodeRef::default); if let (false, Some(default), Some(input_element)) = ( *applied_default_value, props.default.as_ref(), node_ref.cast::(), ) { input_element.set_value(&default.to_string()); applied_default_value.set(true); } html! { <>