mod group; mod props; mod textarea; pub use self::props::Props; pub use self::{group::InputGroup, textarea::TextArea}; use crate::{prelude::*, render}; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::{ borrow::Cow, fmt::{Display, Formatter, Result as FmtResult}, }; #[cfg(feature = "validate")] use validator::ValidationErrors; use yew::prelude::*; use yew::{html::IntoOptPropValue, prelude::*}; #[derive(Clone, PartialEq)] pub enum InputType { Text, Date, DateTime, Checkbox, Color, } impl IntoOptPropValue> for &InputType { fn into_opt_prop_value(self) -> Option> { match self { InputType::Text => Some(Cow::from("text")), InputType::Date => Some(Cow::from("date")), InputType::DateTime => Some(Cow::from("datetime-local")), InputType::Checkbox => Some(Cow::from("checkbox")), InputType::Color => Some(Cow::from("color")), } } } impl Display for InputType { fn fmt(&self, f: &mut Formatter) -> FmtResult { match self { Self::Text => write!(f, "text"), Self::Date => write!(f, "date"), Self::DateTime => write!(f, "datetime-local"), Self::Checkbox => write!(f, "checkbox"), Self::Color => write!(f, "color"), } } } pub struct Input { link: ComponentLink, state: String, props: Props, } #[derive(Debug)] pub struct InputChange(ChangeData); impl Component for Input { type Message = InputChange; type Properties = Props; fn create(props: Self::Properties, link: ComponentLink) -> Self { let state = props.value.clone(); Self { props, state, link } } fn update(&mut self, msg: Self::Message) -> ShouldRender { if let InputChange(ChangeData::Value(value)) = msg { self.state = value.clone(); self.props.on_change.emit(value); true } else { false } } fn change(&mut self, props: Self::Properties) -> ShouldRender { if self.props.value != props.value { self.state = props.value.clone(); } render_if_ne(&mut self.props, props) } #[cfg(not(feature = "validate"))] fn view(&self) -> Html { let input_type = self.props.input_type.as_ref().unwrap_or(&InputType::Text); let mut prefix = if self.props.readonly { vec!["form-control-plaintext"] } else { vec!["form-control"] }; prefix.push(valid_as_class(&self.props.valid)); let html = html! { }; render::render_with_prefix(&self.props, prefix, html) } #[cfg(feature = "validate")] fn view(&self) -> Html { let input_type = self.props.input_type.as_ref().unwrap_or(&InputType::Text); let mut prefix = if self.props.readonly { vec!["form-control-plaintext"] } else { vec!["form-control"] }; prefix.push(valid_as_class(&self.props.valid)); let html = html! { <> { crate::input::render_validation_feedback_from_props(&self.props) } }; render::render_with_prefix(&self.props, prefix, html) } } #[cfg(feature = "validate")] pub fn render_validation_feedback( error_key: &Option, errors: &Option, ) -> Html { // TODO keep chipping away if error_key.is_none { html! {} } else if let Some(ref errors) = errors { let errors = errors.field_errors(); if let Some(errors) = errors.get(field.as_ref()) { html! {
{ for errors.iter().filter_map(|error| error.message.as_ref()) }
} } else { html! {} } } else { html! {} } } #[cfg(feature = "validate")] fn render_validation_feedback_from_props(props: &Props) -> Html { if let Some(error_code) = props.error_code.as_ref() { render_validation_feedback(error_code, &props.errors) } else { html! {} } }