summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjelemux <jeremias.weber@protonmail.com>2021-02-17 17:06:48 +0100
committerjelemux <jeremias.weber@protonmail.com>2021-02-17 17:06:48 +0100
commitcb310a66e94db4e0c4f7d0373a670156b012412a (patch)
tree501fd4ed17d892d693d4123fffaca8d942144e3b /src
parentbe3367dd2921eb30ae7970d233b83ac3af861952 (diff)
parent0660151a8b641fa0a23dde2598132029970f7ae4 (diff)
downloadwasm-card-cb310a66e94db4e0c4f7d0373a670156b012412a.tar.gz
wasm-card-cb310a66e94db4e0c4f7d0373a670156b012412a.tar.bz2
Merge branch 'main' of codeberg.org:jelemux/wasm-card
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs2
-rw-r--r--src/model/address.rs (renamed from src/viewmodel/address.rs)65
-rw-r--r--src/model/dates.rs (renamed from src/viewmodel/dates.rs)34
-rw-r--r--src/model/mod.rs (renamed from src/viewmodel/mod.rs)21
-rw-r--r--src/model/name.rs (renamed from src/viewmodel/name.rs)46
-rw-r--r--src/model/organizational.rs (renamed from src/viewmodel/organizational.rs)50
-rw-r--r--src/model/telephone.rs (renamed from src/viewmodel/telephone.rs)66
-rw-r--r--src/model/utility.rs (renamed from src/viewmodel/utility.rs)0
-rw-r--r--src/model/vcard.rs (renamed from src/viewmodel/vcard.rs)10
-rw-r--r--src/view/address.rs103
-rw-r--r--src/view/dates.rs75
-rw-r--r--src/view/main.rs42
-rw-r--r--src/view/mod.rs82
-rw-r--r--src/view/name.rs95
-rw-r--r--src/view/organizational.rs84
-rw-r--r--src/view/property_group.rs81
-rw-r--r--src/view/telephone.rs105
-rw-r--r--src/view/weak_links.rs33
18 files changed, 373 insertions, 621 deletions
diff --git a/src/lib.rs b/src/lib.rs
index b8e10a5..b5ad040 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,8 +10,8 @@ use yew::prelude::App;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
+pub mod model;
pub mod view;
-pub mod viewmodel;
fn init() {
panic::set_hook(Box::new(console_error_panic_hook::hook));
diff --git a/src/viewmodel/address.rs b/src/model/address.rs
index 9542675..2c42a98 100644
--- a/src/viewmodel/address.rs
+++ b/src/model/address.rs
@@ -1,5 +1,4 @@
use super::*;
-use crate::view::address::*;
#[derive(Clone, Debug, PartialEq)]
pub struct Address {
@@ -14,7 +13,22 @@ pub struct Address {
pub home: bool,
}
-impl VCardPropertyInputObject<AddressView> for Address {
+#[derive(Clone, PartialEq)]
+pub enum AddressMsg {
+ UpdatePostOfficeBox(String),
+ UpdateExtension(String),
+ UpdateStreet(String),
+ UpdateLocality(String),
+ UpdateRegion(String),
+ UpdateCode(String),
+ UpdateCountry(String),
+ ToggleWork,
+ ToggleHome,
+
+ Generate,
+}
+
+impl VCardPropertyInputObject<AddressMsg> for Address {
fn new() -> Self {
Self {
post_office_box: String::new(),
@@ -28,14 +42,20 @@ impl VCardPropertyInputObject<AddressView> for Address {
home: false,
}
}
- fn get_input_fields(&self, link: &ComponentLink<AddressView>) -> Vec<VCardPropertyInputField> {
+ fn get_title(&self) -> String {
+ "Address".to_string()
+ }
+ fn get_input_fields(
+ &self,
+ link: &ComponentLink<PropertyGroupInputComponent<Self, AddressMsg>>,
+ ) -> Vec<VCardPropertyInputField> {
let typ = String::from("text");
vec![
VCardPropertyInputField::Text {
label: "Post Office Box".to_string(),
id: Some("post_office_box".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdatePostOfficeBox(e.value)),
+ oninput: link.callback(|e: InputData| AddressMsg::UpdatePostOfficeBox(e.value)),
value: self.post_office_box.clone(),
typ: typ.clone(),
},
@@ -43,7 +63,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
label: "Extension".to_string(),
id: Some("extension".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateExtension(e.value)),
+ oninput: link.callback(|e: InputData| AddressMsg::UpdateExtension(e.value)),
value: self.extension.clone(),
typ: typ.clone(),
},
@@ -51,7 +71,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
label: "Street".to_string(),
id: Some("street".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateStreet(e.value)),
+ oninput: link.callback(|e: InputData| AddressMsg::UpdateStreet(e.value)),
value: self.street.clone(),
typ: typ.clone(),
},
@@ -59,7 +79,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
label: "Locality".to_string(),
id: Some("locality".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateLocality(e.value)),
+ oninput: link.callback(|e: InputData| AddressMsg::UpdateLocality(e.value)),
value: self.locality.clone(),
typ: typ.clone(),
},
@@ -67,7 +87,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
label: "Region".to_string(),
id: Some("region".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateRegion(e.value)),
+ oninput: link.callback(|e: InputData| AddressMsg::UpdateRegion(e.value)),
value: self.region.clone(),
typ: typ.clone(),
},
@@ -75,7 +95,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
label: "Code".to_string(),
id: Some("code".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateCode(e.value)),
+ oninput: link.callback(|e: InputData| AddressMsg::UpdateCode(e.value)),
value: self.code.clone(),
typ: typ.clone(),
},
@@ -83,24 +103,45 @@ impl VCardPropertyInputObject<AddressView> for Address {
label: "Country".to_string(),
id: Some("country".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateCountry(e.value)),
+ oninput: link.callback(|e: InputData| AddressMsg::UpdateCountry(e.value)),
value: self.country.clone(),
typ,
},
VCardPropertyInputField::CheckBox {
label: "Work".to_string(),
id: Some("work".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleWork),
+ onclick: link.callback(|_: MouseEvent| AddressMsg::ToggleWork),
value: self.work,
},
VCardPropertyInputField::CheckBox {
label: "Home".to_string(),
id: Some("home".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleHome),
+ onclick: link.callback(|_: MouseEvent| AddressMsg::ToggleHome),
value: self.home,
},
]
}
+ fn update(
+ &mut self,
+ props: InputProps<Self, AddressMsg>,
+ msg: <PropertyGroupInputComponent<Self, AddressMsg> as yew::Component>::Message,
+ ) -> bool {
+ match msg {
+ AddressMsg::UpdatePostOfficeBox(b) => self.post_office_box = b,
+ AddressMsg::UpdateExtension(e) => self.extension = e,
+ AddressMsg::UpdateStreet(s) => self.street = s,
+ AddressMsg::UpdateLocality(l) => self.locality = l,
+ AddressMsg::UpdateRegion(r) => self.region = r,
+ AddressMsg::UpdateCode(p) => self.code = p,
+ AddressMsg::UpdateCountry(c) => self.country = c,
+ AddressMsg::ToggleWork => self.work = !self.work,
+ AddressMsg::ToggleHome => self.home = !self.home,
+ AddressMsg::Generate => {
+ props.generated.emit(self.clone());
+ }
+ };
+ true
+ }
fn is_empty(&self) -> bool {
self.post_office_box.is_empty()
&& self.extension.is_empty()
diff --git a/src/viewmodel/dates.rs b/src/model/dates.rs
index 7d8d394..192f986 100644
--- a/src/viewmodel/dates.rs
+++ b/src/model/dates.rs
@@ -1,5 +1,4 @@
use super::*;
-use crate::view::dates::*;
/// Type that represents the vcard `anniversary` and `birthday` properties.
#[derive(Clone, Debug, PartialEq)]
@@ -8,16 +7,27 @@ pub struct Dates {
pub birthday: String,
}
-impl VCardPropertyInputObject<DatesView> for Dates {
+#[derive(Clone, PartialEq)]
+pub enum DatesMsg {
+ UpdateAnniversary(String),
+ UpdateBirthday(String),
+
+ Generate,
+}
+
+impl VCardPropertyInputObject<DatesMsg> for Dates {
fn new() -> Self {
Self {
anniversary: String::new(),
birthday: String::new(),
}
}
+ fn get_title(&self) -> std::string::String {
+ "Dates".to_string()
+ }
fn get_input_fields(
&self,
- link: &yew::html::Scope<DatesView>,
+ link: &yew::html::Scope<PropertyGroupInputComponent<Self, DatesMsg>>,
) -> std::vec::Vec<VCardPropertyInputField> {
let typ = String::from("date");
vec![
@@ -25,7 +35,7 @@ impl VCardPropertyInputObject<DatesView> for Dates {
label: "Anniversary".to_string(),
id: Some("anniversary".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateAnniversary(e.value)),
+ oninput: link.callback(|e: InputData| DatesMsg::UpdateAnniversary(e.value)),
value: self.anniversary.clone(),
typ: typ.clone(),
},
@@ -33,12 +43,26 @@ impl VCardPropertyInputObject<DatesView> for Dates {
label: "Birthday".to_string(),
id: Some("birthday".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateBirthday(e.value)),
+ oninput: link.callback(|e: InputData| DatesMsg::UpdateBirthday(e.value)),
value: self.birthday.clone(),
typ,
},
]
}
+ fn update(
+ &mut self,
+ props: InputProps<Self, DatesMsg>,
+ msg: <PropertyGroupInputComponent<Self, DatesMsg> as yew::Component>::Message,
+ ) -> bool {
+ match msg {
+ DatesMsg::UpdateAnniversary(a) => self.anniversary = a,
+ DatesMsg::UpdateBirthday(b) => self.birthday = b,
+ DatesMsg::Generate => {
+ props.generated.emit(self.clone());
+ }
+ };
+ true
+ }
fn is_empty(&self) -> bool {
self.anniversary.is_empty() && self.birthday.is_empty()
}
diff --git a/src/viewmodel/mod.rs b/src/model/mod.rs
index 0385c61..45c91f1 100644
--- a/src/viewmodel/mod.rs
+++ b/src/model/mod.rs
@@ -1,5 +1,5 @@
-use crate::view::VCardPropertyInputComponent;
-use crate::viewmodel::utility::File;
+use crate::model::utility::File;
+use crate::view::property_group::*;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use web_sys::FileReader;
@@ -15,17 +15,26 @@ pub mod utility;
pub mod vcard;
/// Trait for types that represent the data of a vcard property used inside of a `VCardPropertyInputComponent`.
-pub trait VCardPropertyInputObject<C: VCardPropertyInputComponent<Self>>:
- Clone + PartialEq
+pub trait VCardPropertyInputObject<M: 'static + PartialEq + Clone>: Clone + PartialEq
where
Self: Sized,
{
/// Function for creating a new (and empty) `VCardPropertyInputObject`.
fn new() -> Self;
+ /// Getter function for the title of the component
+ fn get_title(&self) -> String;
/// Converts each field of the `VCardPropertyInputObject` to a VCardPropertyInputField and returns them as a vector.
- fn get_input_fields(&self, link: &ComponentLink<C>) -> Vec<VCardPropertyInputField>;
+ fn get_input_fields(
+ &self,
+ link: &ComponentLink<PropertyGroupInputComponent<Self, M>>,
+ ) -> Vec<VCardPropertyInputField>;
+ fn update(
+ &mut self,
+ props: InputProps<Self, M>,
+ msg: <PropertyGroupInputComponent<Self, M> as yew::Component>::Message,
+ ) -> bool;
/// Returns a `Html` representation of the `VCardPropertyInputObject`.
- fn render(&self, link: &ComponentLink<C>) -> Html {
+ fn render(&self, link: &ComponentLink<PropertyGroupInputComponent<Self, M>>) -> Html {
html! {
<div class="columns is-mobile is-multiline">
{
diff --git a/src/viewmodel/name.rs b/src/model/name.rs
index de0aa81..915579d 100644
--- a/src/viewmodel/name.rs
+++ b/src/model/name.rs
@@ -1,5 +1,4 @@
use super::*;
-use crate::view::name::*;
/// Type that represents a vcard `name` property
///
@@ -25,7 +24,18 @@ pub struct Name {
pub suffix: String,
}
-impl VCardPropertyInputObject<NameView> for Name {
+#[derive(Clone, PartialEq)]
+pub enum NameMsg {
+ UpdatePrefix(String),
+ UpdateFirstName(String),
+ UpdateMiddleName(String),
+ UpdateLastName(String),
+ UpdateSuffix(String),
+
+ Generate,
+}
+
+impl VCardPropertyInputObject<NameMsg> for Name {
fn new() -> Self {
Self {
prefix: String::new(),
@@ -35,9 +45,12 @@ impl VCardPropertyInputObject<NameView> for Name {
suffix: String::new(),
}
}
+ fn get_title(&self) -> String {
+ "Name".to_string()
+ }
fn get_input_fields(
&self,
- link: &ComponentLink<NameView>,
+ link: &ComponentLink<PropertyGroupInputComponent<Self, NameMsg>>,
) -> std::vec::Vec<VCardPropertyInputField> {
let typ = String::from("text");
vec![
@@ -45,7 +58,7 @@ impl VCardPropertyInputObject<NameView> for Name {
label: "Prefix".to_string(),
id: Some("prefix".to_string()),
placeholder: Some("Sir".to_string()),
- oninput: link.callback(|e: InputData| Msg::UpdatePrefix(e.value)),
+ oninput: link.callback(|e: InputData| NameMsg::UpdatePrefix(e.value)),
value: self.prefix.clone(),
typ: typ.clone(),
},
@@ -53,7 +66,7 @@ impl VCardPropertyInputObject<NameView> for Name {
label: "First Name".to_string(),
id: Some("first_name".to_string()),
placeholder: Some("Arthur".to_string()),
- oninput: link.callback(|e: InputData| Msg::UpdateFirstName(e.value)),
+ oninput: link.callback(|e: InputData| NameMsg::UpdateFirstName(e.value)),
value: self.first_name.clone(),
typ: typ.clone(),
},
@@ -61,7 +74,7 @@ impl VCardPropertyInputObject<NameView> for Name {
label: "Middle Name".to_string(),
id: Some("middle_name".to_string()),
placeholder: Some("Charles".to_string()),
- oninput: link.callback(|e: InputData| Msg::UpdateMiddleName(e.value)),
+ oninput: link.callback(|e: InputData| NameMsg::UpdateMiddleName(e.value)),
value: self.middle_name.clone(),
typ: typ.clone(),
},
@@ -69,7 +82,7 @@ impl VCardPropertyInputObject<NameView> for Name {
label: "Last Name".to_string(),
id: Some("last_name".to_string()),
placeholder: Some("Clarke".to_string()),
- oninput: link.callback(|e: InputData| Msg::UpdateLastName(e.value)),
+ oninput: link.callback(|e: InputData| NameMsg::UpdateLastName(e.value)),
value: self.last_name.clone(),
typ: typ.clone(),
},
@@ -77,12 +90,29 @@ impl VCardPropertyInputObject<NameView> for Name {
label: "Suffix".to_string(),
id: Some("suffix".to_string()),
placeholder: Some("CBE FRAS".to_string()),
- oninput: link.callback(|e: InputData| Msg::UpdateSuffix(e.value)),
+ oninput: link.callback(|e: InputData| NameMsg::UpdateSuffix(e.value)),
value: self.suffix.clone(),
typ,
},
]
}
+ fn update(
+ &mut self,
+ props: InputProps<Self, NameMsg>,
+ msg: <PropertyGroupInputComponent<Self, NameMsg> as yew::Component>::Message,
+ ) -> bool {
+ match msg {
+ NameMsg::UpdatePrefix(p) => self.prefix = p,
+ NameMsg::UpdateFirstName(f) => self.first_name = f,
+ NameMsg::UpdateMiddleName(m) => self.middle_name = m,
+ NameMsg::UpdateLastName(l) => self.last_name = l,
+ NameMsg::UpdateSuffix(s) => self.suffix = s,
+ NameMsg::Generate => {
+ props.generated.emit(self.clone());
+ }
+ };
+ true
+ }
fn is_empty(&self) -> bool {
self.prefix.is_empty()
&& self.first_name.is_empty()
diff --git a/src/viewmodel/organizational.rs b/src/model/organizational.rs
index 72b19d2..f82f5d7 100644
--- a/src/viewmodel/organizational.rs
+++ b/src/model/organizational.rs
@@ -1,5 +1,4 @@
use super::*;
-use crate::view::organizational::*;
#[derive(Clone, Debug, PartialEq)]
pub struct Organizational {
@@ -11,7 +10,19 @@ pub struct Organizational {
pub related: String,
}
-impl VCardPropertyInputObject<OrganizationalView> for Organizational {
+#[derive(Clone, PartialEq)]
+pub enum OrganizationalMsg {
+ UpdateOrg(String),
+ UpdateLogo(Option<File>),
+ UpdateTitle(String),
+ UpdateRole(String),
+ UpdateMember(String),
+ UpdateRelated(String),
+
+ Generate,
+}
+
+impl VCardPropertyInputObject<OrganizationalMsg> for Organizational {
fn new() -> Self {
Self {
org: String::new(),
@@ -22,9 +33,12 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational {
related: String::new(),
}
}
+ fn get_title(&self) -> std::string::String {
+ "Organizational".to_string()
+ }
fn get_input_fields(
&self,
- link: &yew::html::Scope<OrganizationalView>,
+ link: &yew::html::Scope<PropertyGroupInputComponent<Self, OrganizationalMsg>>,
) -> std::vec::Vec<VCardPropertyInputField> {
let typ = String::from("text");
vec![
@@ -32,7 +46,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational {
label: "Organisation".to_string(),
id: Some("org".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateOrg(e.value)),
+ oninput: link.callback(|e: InputData| OrganizationalMsg::UpdateOrg(e.value)),
value: self.org.clone(),
typ: typ.clone(),
},
@@ -40,14 +54,14 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational {
// TODO: Add Upload for logo
label: "Logo".to_string(),
name: "logo".to_string(),
- callback: link.callback(|file: Option<File>| Msg::UpdateLogo(file)),
+ callback: link.callback(|file: Option<File>| OrganizationalMsg::UpdateLogo(file)),
value: self.logo.clone(),
},
VCardPropertyInputField::Text {
label: "Title".to_string(),
id: Some("title".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateTitle(e.value)),
+ oninput: link.callback(|e: InputData| OrganizationalMsg::UpdateTitle(e.value)),
value: self.title.clone(),
typ: typ.clone(),
},
@@ -55,7 +69,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational {
label: "Role".to_string(),
id: Some("role".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateRole(e.value)),
+ oninput: link.callback(|e: InputData| OrganizationalMsg::UpdateRole(e.value)),
value: self.role.clone(),
typ: typ.clone(),
},
@@ -63,7 +77,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational {
label: "Member".to_string(),
id: Some("member".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateMember(e.value)),
+ oninput: link.callback(|e: InputData| OrganizationalMsg::UpdateMember(e.value)),
value: self.member.clone(),
typ: typ.clone(),
},
@@ -71,12 +85,30 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational {
label: "Related".to_string(),
id: Some("related".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateRelated(e.value)),
+ oninput: link.callback(|e: InputData| OrganizationalMsg::UpdateRelated(e.value)),
value: self.related.clone(),
typ: typ,
},
]
}
+ fn update(
+ &mut self,
+ props: InputProps<Self, OrganizationalMsg>,
+ msg: <PropertyGroupInputComponent<Self, OrganizationalMsg> as yew::Component>::Message,
+ ) -> bool {
+ match msg {
+ OrganizationalMsg::UpdateOrg(o) => self.org = o,
+ OrganizationalMsg::UpdateLogo(l) => self.logo = l,
+ OrganizationalMsg::UpdateTitle(t) => self.title = t,
+ OrganizationalMsg::UpdateRole(r) => self.role = r,
+ OrganizationalMsg::UpdateMember(m) => self.member = m,
+ OrganizationalMsg::UpdateRelated(r) => self.related = r,
+ OrganizationalMsg::Generate => {
+ props.generated.emit(self.clone());
+ }
+ };
+ true
+ }
fn is_empty(&self) -> bool {
self.org.is_empty()
&& self.logo.is_none()
diff --git a/src/viewmodel/telephone.rs b/src/model/telephone.rs
index 44b938f..946da33 100644
--- a/src/viewmodel/telephone.rs
+++ b/src/model/telephone.rs
@@ -1,5 +1,4 @@
use super::*;
-use crate::view::telephone::*;
#[derive(Clone, Debug, PartialEq)]
pub struct Telephone {
@@ -15,7 +14,23 @@ pub struct Telephone {
pub text_phone: bool,
}
-impl VCardPropertyInputObject<TelephoneView> for Telephone {
+#[derive(Clone, PartialEq)]
+pub enum TelephoneMsg {
+ UpdateNumber(String),
+ ToggleWork,
+ ToggleHome,
+ ToggleText,
+ ToggleVoice,
+ ToggleFax,
+ ToggleCell,
+ ToggleVideo,
+ TogglePager,
+ ToggleTextPhone,
+
+ Generate,
+}
+
+impl VCardPropertyInputObject<TelephoneMsg> for Telephone {
fn new() -> Self {
Self {
number: String::new(),
@@ -30,9 +45,12 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone {
text_phone: false,
}
}
+ fn get_title(&self) -> String {
+ "Telephone".to_string()
+ }
fn get_input_fields(
&self,
- link: &ComponentLink<TelephoneView>,
+ link: &ComponentLink<PropertyGroupInputComponent<Self, TelephoneMsg>>,
) -> Vec<VCardPropertyInputField> {
let typ = String::from("tel");
vec![
@@ -40,66 +58,88 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone {
label: "Number".to_string(),
id: Some("number".to_string()),
placeholder: None,
- oninput: link.callback(|e: InputData| Msg::UpdateNumber(e.value)),
+ oninput: link.callback(|e: InputData| TelephoneMsg::UpdateNumber(e.value)),
value: self.number.clone(),
typ,
},
VCardPropertyInputField::CheckBox {
label: "Work".to_string(),
id: Some("work".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleWork),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleWork),
value: self.work,
},
VCardPropertyInputField::CheckBox {
label: "Home".to_string(),
id: Some("home".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleHome),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleHome),
value: self.home,
},
VCardPropertyInputField::CheckBox {
label: "Text".to_string(),
id: Some("text".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleText),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleText),
value: self.text,
},
VCardPropertyInputField::CheckBox {
label: "Voice".to_string(),
id: Some("voice".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleVoice),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleVoice),
value: self.voice,
},
VCardPropertyInputField::CheckBox {
label: "Fax".to_string(),
id: Some("fax".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleFax),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleFax),
value: self.fax,
},
VCardPropertyInputField::CheckBox {
label: "Cell".to_string(),
id: Some("cell".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleCell),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleCell),
value: self.cell,
},
VCardPropertyInputField::CheckBox {
label: "Video".to_string(),
id: Some("video".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleVideo),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleVideo),
value: self.video,
},
VCardPropertyInputField::CheckBox {
label: "Pager".to_string(),
id: Some("pager".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::TogglePager),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::TogglePager),
value: self.pager,
},
VCardPropertyInputField::CheckBox {
label: "Text Phone".to_string(),
id: Some("text_phone".to_string()),
- onclick: link.callback(|_: MouseEvent| Msg::ToggleTextPhone),
+ onclick: link.callback(|_: MouseEvent| TelephoneMsg::ToggleTextPhone),
value: self.text_phone,
},
]
}
+ fn update(
+ &mut self,
+ props: InputProps<Self, TelephoneMsg>,
+ msg: <PropertyGroupInputComponent<Self, TelephoneMsg> as yew::Component>::Message,
+ ) -> bool {
+ match msg {
+ TelephoneMsg::UpdateNumber(n) => self.number = n,
+ TelephoneMsg::ToggleWork => self.work = !self.work,
+ TelephoneMsg::ToggleHome => self.home = !self.home,
+ TelephoneMsg::ToggleText => self.text = !self.text,
+ TelephoneMsg::ToggleVoice => self.voice = !self.voice,
+ TelephoneMsg::ToggleFax => self.fax = !self.fax,
+ TelephoneMsg::ToggleCell => self.cell = !self.cell,
+ TelephoneMsg::ToggleVideo => self.video = !self.video,
+ TelephoneMsg::TogglePager => self.pager = !self.pager,
+ TelephoneMsg::ToggleTextPhone => self.text_phone = !self.text_phone,
+ TelephoneMsg::Generate => {
+ props.generated.emit(self.clone());
+ }
+ };
+ true
+ }
fn is_empty(&self) -> bool {
self.number.is_empty()
}
diff --git a/src/viewmodel/utility.rs b/src/model/utility.rs
index 617ee45..617ee45 100644
--- a/src/viewmodel/utility.rs
+++ b/src/model/utility.rs
diff --git a/src/viewmodel/vcard.rs b/src/model/vcard.rs
index 18ce43a..ee7e28e 100644
--- a/src/viewmodel/vcard.rs
+++ b/src/model/vcard.rs
@@ -1,8 +1,8 @@
-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::model::address::Address;
+use crate::model::dates::Dates;
+use crate::model::name::Name;
+use crate::model::organizational::Organizational;
+use crate::model::telephone::Telephone;
/// Type that represents the data structure of a vcard.
#[derive(Clone, Debug)]
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)
+ }
+}