diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/address.rs | 103 | ||||
-rw-r--r-- | src/view/dates.rs | 75 | ||||
-rw-r--r-- | src/view/main.rs | 42 | ||||
-rw-r--r-- | src/view/mod.rs | 82 | ||||
-rw-r--r-- | src/view/name.rs | 95 | ||||
-rw-r--r-- | src/view/organizational.rs | 84 | ||||
-rw-r--r-- | src/view/property_group.rs | 81 | ||||
-rw-r--r-- | src/view/telephone.rs | 105 | ||||
-rw-r--r-- | src/view/weak_links.rs | 33 |
9 files changed, 138 insertions, 562 deletions
diff --git a/src/view/address.rs b/src/view/address.rs deleted file mode 100644 index 57ea7e4..0000000 --- a/src/view/address.rs +++ /dev/null @@ -1,103 +0,0 @@ -use super::VCardPropertyInputComponent; -use crate::view::InputProps; -use crate::viewmodel::address::*; -use crate::viewmodel::Error; -use crate::viewmodel::VCardPropertyInputObject; -use yew::prelude::*; -use yewtil::NeqAssign; - -type Props = InputProps<Address, AddressView>; - -/// View Component for a `address` field -/// -/// # Examples -/// -/// ```compile_fail -/// let html = html!{ -/// <AddressView weak_link=some_weak_component_link -/// generated=self.link.callback( -/// |n: Irc<Address>| -/// Msg::GeneratedAddress(some_address) -/// ) -/// /> -/// }; -/// ``` -#[derive(Clone, PartialEq)] -pub struct AddressView { - props: Props, - value: Address, - error: Option<Error>, -} - -pub enum Msg { - UpdatePostOfficeBox(String), - UpdateExtension(String), - UpdateStreet(String), - UpdateLocality(String), - UpdateRegion(String), - UpdateCode(String), - UpdateCountry(String), - ToggleWork, - ToggleHome, - - Generate, -} - -impl VCardPropertyInputComponent<Address> for AddressView { - fn get_input_object(&self) -> Address { - self.value.clone() - } - fn get_title(&self) -> String { - "Address".to_string() - } - fn get_error(&self) -> Option<Error> { - self.error.clone() - } -} - -impl Component for AddressView { - type Message = Msg; - type Properties = Props; - fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self { - props.weak_link.borrow_mut().replace(link); - Self { - props, - value: Address::new(), - error: None, - } - } - fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { - match msg { - Msg::UpdatePostOfficeBox(b) => self.value.post_office_box = b, - Msg::UpdateExtension(e) => self.value.extension = e, - Msg::UpdateStreet(s) => self.value.street = s, - Msg::UpdateLocality(l) => self.value.locality = l, - Msg::UpdateRegion(r) => self.value.region = r, - Msg::UpdateCode(p) => self.value.code = p, - Msg::UpdateCountry(c) => self.value.country = c, - Msg::ToggleWork => self.value.work = !self.value.work, - Msg::ToggleHome => self.value.home = !self.value.home, - Msg::Generate => { - self.props.generated.emit(self.value.clone()); - } - }; - true - } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - self.props.neq_assign(props) - } - fn view(&self) -> yew::virtual_dom::VNode { - let link = self.props.weak_link.borrow().clone().unwrap(); - - html! { - <div class="box"> - { self.render_error() } - - <h3 class="subtitle">{ self.get_title() }</h3> - - { self.get_input_object().render(&link) } - - </div> - } - } -} diff --git a/src/view/dates.rs b/src/view/dates.rs deleted file mode 100644 index b2a6dd3..0000000 --- a/src/view/dates.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::VCardPropertyInputComponent; -use crate::view::InputProps; -use crate::viewmodel::dates::*; -use crate::viewmodel::Error; -use crate::viewmodel::VCardPropertyInputObject; -use yew::prelude::*; -use yewtil::NeqAssign; - -type Props = InputProps<Dates, DatesView>; - -#[derive(Clone, PartialEq)] -pub struct DatesView { - props: Props, - value: Dates, - error: Option<Error>, -} - -pub enum Msg { - UpdateAnniversary(String), - UpdateBirthday(String), - - Generate, -} - -impl VCardPropertyInputComponent<Dates> for DatesView { - fn get_input_object(&self) -> Dates { - self.value.clone() - } - fn get_title(&self) -> std::string::String { - "Dates".to_string() - } - fn get_error(&self) -> std::option::Option<Error> { - self.error.clone() - } -} - -impl Component for DatesView { - type Message = Msg; - type Properties = Props; - fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self { - props.weak_link.borrow_mut().replace(link); - Self { - props, - value: Dates::new(), - error: None, - } - } - fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { - match msg { - Msg::UpdateAnniversary(a) => self.value.anniversary = a, - Msg::UpdateBirthday(b) => self.value.birthday = b, - Msg::Generate => { - self.props.generated.emit(self.value.clone()); - } - }; - true - } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - self.props.neq_assign(props) - } - fn view(&self) -> yew::virtual_dom::VNode { - let link = self.props.weak_link.borrow().clone().unwrap(); - - html! { - <div class="box"> - { self.render_error() } - - <h3 class="subtitle">{ self.get_title() }</h3> - - { self.get_input_object().render(&link) } - - </div> - } - } -} diff --git a/src/view/main.rs b/src/view/main.rs index 196afa0..d35fb59 100644 --- a/src/view/main.rs +++ b/src/view/main.rs @@ -1,18 +1,14 @@ -use super::address::{self, AddressView}; -use super::dates::{self, DatesView}; -use super::name::{self, NameView}; -use super::WeakComponentLink; -use crate::view::organizational::{self, OrganizationalView}; -use crate::view::telephone::{self, TelephoneView}; -use crate::viewmodel::address::Address; -use crate::viewmodel::dates::Dates; -use crate::viewmodel::name::Name; -use crate::viewmodel::organizational::Organizational; -use crate::viewmodel::telephone::Telephone; -use crate::viewmodel::utility::*; -use crate::viewmodel::vcard::VCardData; -use crate::viewmodel::Error; -use crate::viewmodel::VCardPropertyInputObject; +use crate::view::property_group::PropertyGroupInputComponent; +use crate::view::weak_links::WeakComponentLink; +use crate::model::address::*; +use crate::model::dates::*; +use crate::model::name::*; +use crate::model::organizational::*; +use crate::model::telephone::*; +use crate::model::utility::*; +use crate::model::vcard::VCardData; +use crate::model::Error; +use crate::model::VCardPropertyInputObject; use boolinator::Boolinator; use chrono::prelude::*; use genpdf::Element as _; @@ -25,6 +21,12 @@ use yew::prelude::*; use yew::services::ConsoleService; use yewtil::ptr::Mrc; +type NameView = PropertyGroupInputComponent<Name, NameMsg>; +type AddressView = PropertyGroupInputComponent<Address, AddressMsg>; +type DatesView = PropertyGroupInputComponent<Dates, DatesMsg>; +type OrganizationalView = PropertyGroupInputComponent<Organizational, OrganizationalMsg>; +type TelephoneView = PropertyGroupInputComponent<Telephone, TelephoneMsg>; + pub struct MainView { link: ComponentLink<Self>, error: Option<Error>, @@ -117,27 +119,27 @@ impl Component for MainView { { for name_link in self.name_links.iter() { let name_link = name_link.borrow().clone().unwrap(); - name_link.send_message(name::Msg::Generate); + name_link.send_message(NameMsg::Generate); } for address_link in self.address_links.iter() { let address_link = address_link.borrow().clone().unwrap(); - address_link.send_message(address::Msg::Generate); + address_link.send_message(AddressMsg::Generate); } for telephone_link in self.telephone_links.iter() { let telephone_link = telephone_link.borrow().clone().unwrap(); - telephone_link.send_message(telephone::Msg::Generate); + telephone_link.send_message(TelephoneMsg::Generate); } for dates_link in self.dates_links.iter() { let dates_link = dates_link.borrow().clone().unwrap(); - dates_link.send_message(dates::Msg::Generate) + dates_link.send_message(DatesMsg::Generate) } for organizational_links in self.organizational_links.iter() { let organizational_link = organizational_links.borrow().clone().unwrap(); - organizational_link.send_message(organizational::Msg::Generate) + organizational_link.send_message(OrganizationalMsg::Generate) } } /* diff --git a/src/view/mod.rs b/src/view/mod.rs index cbff0fd..691f19a 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -1,81 +1,3 @@ -use crate::viewmodel::*; -use std::cell::RefCell; -use std::ops::Deref; -use std::rc::Rc; -use yew::prelude::*; - -pub mod address; -pub mod dates; pub mod main; -pub mod name; -pub mod organizational; -pub mod telephone; - -#[derive(Clone, PartialEq, Properties)] -pub struct InputProps<O, C> -where - O: VCardPropertyInputObject<C> + Clone, - C: VCardPropertyInputComponent<O> + Clone, -{ - pub generated: Callback<O>, - pub weak_link: WeakComponentLink<C>, -} - -/// Trait for types that represent an input component for a vcard property. -pub trait VCardPropertyInputComponent<T: VCardPropertyInputObject<Self>>: - Component + Clone + PartialEq -{ - /// Returns the object containing the input data. - fn get_input_object(&self) -> T; - /// Getter function for the title of the component - fn get_title(&self) -> String; - /// Getter function for an eventual error. - fn get_error(&self) -> Option<Error>; - /// Returns the error as `Html` - fn render_error(&self) -> Html { - html! { - <> - { - if self.get_error().is_some() { - html!{ - <div class="notification is-danger is-light"> - { self.get_error().unwrap().msg } - </div> - } - } else { - html!{} - } - } - </> - } - } -} - -/// Weak link; Useful for being able to have a list of subcomponents. -pub struct WeakComponentLink<COMP: Component>(Rc<RefCell<Option<ComponentLink<COMP>>>>); - -impl<COMP: Component> Clone for WeakComponentLink<COMP> { - fn clone(&self) -> Self { - Self(Rc::clone(&self.0)) - } -} - -impl<COMP: Component> Default for WeakComponentLink<COMP> { - fn default() -> Self { - Self(Rc::default()) - } -} - -impl<COMP: Component> Deref for WeakComponentLink<COMP> { - type Target = Rc<RefCell<Option<ComponentLink<COMP>>>>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<COMP: Component> PartialEq for WeakComponentLink<COMP> { - fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) - } -} +pub mod property_group; +pub mod weak_links; diff --git a/src/view/name.rs b/src/view/name.rs deleted file mode 100644 index 4b7089b..0000000 --- a/src/view/name.rs +++ /dev/null @@ -1,95 +0,0 @@ -use super::VCardPropertyInputComponent; -use crate::view::InputProps; -use crate::viewmodel::name::*; -use crate::viewmodel::Error; -use crate::viewmodel::VCardPropertyInputObject; -use yew::prelude::*; -use yewtil::NeqAssign; - -type Props = InputProps<Name, NameView>; - -/// View Component for a `name` field -/// -/// # Examples -/// -/// ```compile_fail -/// let html = html!{ -/// <NameView weak_link=some_weak_component_link -/// generated=self.link.callback( -/// |n: Irc<Name>| -/// Msg::GeneratedName(some_name) -/// ) -/// /> -/// }; -/// ``` -#[derive(Clone, PartialEq)] -pub struct NameView { - props: Props, - value: Name, - error: Option<Error>, -} - -pub enum Msg { - UpdatePrefix(String), - UpdateFirstName(String), - UpdateMiddleName(String), - UpdateLastName(String), - UpdateSuffix(String), - - Generate, -} - -impl VCardPropertyInputComponent<Name> for NameView { - fn get_input_object(&self) -> Name { - self.value.clone() - } - fn get_title(&self) -> String { - "Name".to_string() - } - fn get_error(&self) -> Option<Error> { - self.error.clone() - } -} - -impl Component for NameView { - type Message = Msg; - type Properties = Props; - fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self { - props.weak_link.borrow_mut().replace(link); - Self { - props, - value: Name::new(), - error: None, - } - } - fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { - match msg { - Msg::UpdatePrefix(p) => self.value.prefix = p, - Msg::UpdateFirstName(f) => self.value.first_name = f, - Msg::UpdateMiddleName(m) => self.value.middle_name = m, - Msg::UpdateLastName(l) => self.value.last_name = l, - Msg::UpdateSuffix(s) => self.value.suffix = s, - Msg::Generate => { - self.props.generated.emit(self.value.clone()); - } - }; - true - } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - self.props.neq_assign(props) - } - fn view(&self) -> yew::virtual_dom::VNode { - let link = self.props.weak_link.borrow().clone().unwrap(); - - html! { - <div class="box"> - { self.render_error() } - - <h3 class="subtitle">{ self.get_title() }</h3> - - { self.get_input_object().render(&link) } - - </div> - } - } -} diff --git a/src/view/organizational.rs b/src/view/organizational.rs deleted file mode 100644 index 7f2ca69..0000000 --- a/src/view/organizational.rs +++ /dev/null @@ -1,84 +0,0 @@ -use super::VCardPropertyInputComponent; -use crate::view::InputProps; -use crate::viewmodel::organizational::*; -use crate::viewmodel::utility::File; -use crate::viewmodel::Error; -use crate::viewmodel::VCardPropertyInputObject; -use yew::prelude::*; -use yewtil::NeqAssign; - -type Props = InputProps<Organizational, OrganizationalView>; - -#[derive(Clone, PartialEq)] -pub struct OrganizationalView { - props: Props, - value: Organizational, - error: Option<Error>, -} - -pub enum Msg { - UpdateOrg(String), - UpdateLogo(Option<File>), - UpdateTitle(String), - UpdateRole(String), - UpdateMember(String), - UpdateRelated(String), - - Generate, -} - -impl VCardPropertyInputComponent<Organizational> for OrganizationalView { - fn get_input_object(&self) -> Organizational { - self.value.clone() - } - fn get_title(&self) -> std::string::String { - "Organizational".to_string() - } - fn get_error(&self) -> std::option::Option<Error> { - self.error.clone() - } -} - -impl Component for OrganizationalView { - type Message = Msg; - type Properties = Props; - fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self { - props.weak_link.borrow_mut().replace(link); - Self { - props, - value: Organizational::new(), - error: None, - } - } - fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { - match msg { - Msg::UpdateOrg(o) => self.value.org = o, - Msg::UpdateLogo(l) => self.value.logo = l, - Msg::UpdateTitle(t) => self.value.title = t, - Msg::UpdateRole(r) => self.value.role = r, - Msg::UpdateMember(m) => self.value.member = m, - Msg::UpdateRelated(r) => self.value.related = r, - Msg::Generate => { - self.props.generated.emit(self.value.clone()); - } - }; - true - } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - self.props.neq_assign(props) - } - fn view(&self) -> yew::virtual_dom::VNode { - let link = self.props.weak_link.borrow().clone().unwrap(); - - html! { - <div class="box"> - { self.render_error() } - - <h3 class="subtitle">{ self.get_title() }</h3> - - { self.get_input_object().render(&link) } - - </div> - } - } -} diff --git a/src/view/property_group.rs b/src/view/property_group.rs new file mode 100644 index 0000000..9c86e80 --- /dev/null +++ b/src/view/property_group.rs @@ -0,0 +1,81 @@ +use crate::model::*; +use crate::view::weak_links::WeakComponentLink; +use yew::prelude::*; +use yewtil::NeqAssign; + +#[derive(Clone, PartialEq, Properties)] +pub struct InputProps< + O: 'static + VCardPropertyInputObject<M> + Clone, + M: 'static + PartialEq + Clone, +> { + pub generated: Callback<O>, + pub weak_link: WeakComponentLink<PropertyGroupInputComponent<O, M>>, +} + +#[derive(Clone, PartialEq)] +pub struct PropertyGroupInputComponent< + O: 'static + VCardPropertyInputObject<M>, + M: 'static + PartialEq + Clone, +> { + pub props: InputProps<O, M>, + pub value: O, + pub error: Option<Error>, +} + +impl<O: 'static + VCardPropertyInputObject<M>, M: 'static + PartialEq + Clone> Component + for PropertyGroupInputComponent<O, M> +{ + type Message = M; + type Properties = InputProps<O, M>; + fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self { + props.weak_link.borrow_mut().replace(link); + Self { + props, + value: O::new(), + error: None, + } + } + fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { + self.value.update(self.props.clone(), msg) + } + fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { + self.props.neq_assign(props) + } + fn view(&self) -> yew::virtual_dom::VNode { + let link = self.props.weak_link.borrow().clone().unwrap(); + + html! { + <div class="box"> + { self.render_error() } + + <h3 class="subtitle">{ self.value.get_title() }</h3> + + { self.value.render(&link) } + + </div> + } + } +} + +impl<O: VCardPropertyInputObject<M>, M: 'static + PartialEq + Clone> + PropertyGroupInputComponent<O, M> +{ + /// Returns the error as `Html` + fn render_error(&self) -> Html { + html! { + <> + { + if self.error.is_some() { + html!{ + <div class="notification is-danger is-light"> + { self.error.clone().unwrap().msg } + </div> + } + } else { + html!{} + } + } + </> + } + } +} diff --git a/src/view/telephone.rs b/src/view/telephone.rs deleted file mode 100644 index 68389ba..0000000 --- a/src/view/telephone.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::VCardPropertyInputComponent; -use crate::view::InputProps; -use crate::viewmodel::telephone::*; -use crate::viewmodel::Error; -use crate::viewmodel::VCardPropertyInputObject; -use yew::prelude::*; -use yewtil::NeqAssign; - -type Props = InputProps<Telephone, TelephoneView>; - -/// View Component for a `telephone` field -/// -/// # Examples -/// -/// ```compile_fail -/// let html = html!{ -/// <TelephoneView weak_link=some_weak_component_link -/// generated=self.link.callback( -/// |n: Irc<Telephone>| -/// Msg::GeneratedTelephone(some_telephone) -/// ) -/// /> -/// }; -/// ``` -#[derive(Clone, PartialEq)] -pub struct TelephoneView { - props: Props, - value: Telephone, - error: Option<Error>, -} - -pub enum Msg { - UpdateNumber(String), - ToggleWork, - ToggleHome, - ToggleText, - ToggleVoice, - ToggleFax, - ToggleCell, - ToggleVideo, - TogglePager, - ToggleTextPhone, - - Generate, -} - -impl VCardPropertyInputComponent<Telephone> for TelephoneView { - fn get_input_object(&self) -> Telephone { - self.value.clone() - } - fn get_title(&self) -> String { - "Telephone".to_string() - } - fn get_error(&self) -> Option<Error> { - self.error.clone() - } -} - -impl Component for TelephoneView { - type Message = Msg; - type Properties = Props; - fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self { - props.weak_link.borrow_mut().replace(link); - Self { - props, - value: Telephone::new(), - error: None, - } - } - fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { - match msg { - Msg::UpdateNumber(n) => self.value.number = n, - Msg::ToggleWork => self.value.work = !self.value.work, - Msg::ToggleHome => self.value.home = !self.value.home, - Msg::ToggleText => self.value.text = !self.value.text, - Msg::ToggleVoice => self.value.voice = !self.value.voice, - Msg::ToggleFax => self.value.fax = !self.value.fax, - Msg::ToggleCell => self.value.cell = !self.value.cell, - Msg::ToggleVideo => self.value.video = !self.value.video, - Msg::TogglePager => self.value.pager = !self.value.pager, - Msg::ToggleTextPhone => self.value.text_phone = !self.value.text_phone, - Msg::Generate => { - self.props.generated.emit(self.value.clone()); - } - }; - true - } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - self.props.neq_assign(props) - } - fn view(&self) -> yew::virtual_dom::VNode { - let link = self.props.weak_link.borrow().clone().unwrap(); - - html! { - <div class="box"> - { self.render_error() } - - <h3 class="subtitle">{ self.get_title() }</h3> - - { self.get_input_object().render(&link) } - - </div> - } - } -} diff --git a/src/view/weak_links.rs b/src/view/weak_links.rs new file mode 100644 index 0000000..689c3c4 --- /dev/null +++ b/src/view/weak_links.rs @@ -0,0 +1,33 @@ +use std::cell::RefCell; +use std::ops::Deref; +use std::rc::Rc; +use yew::prelude::*; + +/// Weak link; Useful for being able to have a list of subcomponents. +pub struct WeakComponentLink<COMP: Component>(Rc<RefCell<Option<ComponentLink<COMP>>>>); + +impl<COMP: Component> Clone for WeakComponentLink<COMP> { + fn clone(&self) -> Self { + Self(Rc::clone(&self.0)) + } +} + +impl<COMP: Component> Default for WeakComponentLink<COMP> { + fn default() -> Self { + Self(Rc::default()) + } +} + +impl<COMP: Component> Deref for WeakComponentLink<COMP> { + type Target = Rc<RefCell<Option<ComponentLink<COMP>>>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<COMP: Component> PartialEq for WeakComponentLink<COMP> { + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.0, &other.0) + } +} |