summaryrefslogtreecommitdiff
path: root/src/model/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/model/mod.rs')
-rw-r--r--src/model/mod.rs240
1 files changed, 240 insertions, 0 deletions
diff --git a/src/model/mod.rs b/src/model/mod.rs
new file mode 100644
index 0000000..45c91f1
--- /dev/null
+++ b/src/model/mod.rs
@@ -0,0 +1,240 @@
+use crate::model::utility::File;
+use crate::view::property_group::*;
+use wasm_bindgen::closure::Closure;
+use wasm_bindgen::JsCast;
+use web_sys::FileReader;
+use yew::prelude::*;
+use yew::services::ConsoleService;
+
+pub mod address;
+pub mod dates;
+pub mod name;
+pub mod organizational;
+pub mod telephone;
+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<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<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<PropertyGroupInputComponent<Self, M>>) -> Html {
+ html! {
+ <div class="columns is-mobile is-multiline">
+ {
+ for self.get_input_fields(link).iter().map(|field|
+ field.render()
+ )
+ }
+ </div>
+ }
+ }
+ /// Convenience function for checking if the `VCardPropertyInputObject` is empty.
+ fn is_empty(&self) -> bool;
+}
+
+/// Type for saving error messages.
+///
+/// More of a placeholder for something better later on.
+#[derive(Debug, Clone, PartialEq)]
+pub struct Error {
+ pub msg: String,
+}
+
+/// Type that represents the visiual appearance of an input field.
+pub enum VCardPropertyInputField {
+ Text {
+ label: String,
+ id: Option<String>,
+ placeholder: Option<String>,
+ oninput: Callback<InputData>,
+ value: String,
+ typ: String,
+ },
+ File {
+ label: String,
+ name: String,
+ callback: Callback<Option<File>>,
+ value: Option<File>,
+ },
+ CheckBox {
+ label: String,
+ id: Option<String>,
+ onclick: Callback<MouseEvent>,
+ value: bool,
+ },
+}
+
+impl VCardPropertyInputField {
+ /// Returns a `Html` representation of the `VCardPropertyInputField`.
+ pub fn render(&self) -> Html {
+ match self {
+ Self::Text {
+ label,
+ id,
+ placeholder,
+ oninput,
+ value: _,
+ typ,
+ } => Self::text_field_input(label, id, placeholder, oninput, typ),
+ Self::File {
+ label,
+ name,
+ callback,
+ value,
+ } => Self::file_field_input(label, name, callback, value),
+ Self::CheckBox {
+ label,
+ id,
+ onclick,
+ value,
+ } => Self::checkbox_field_input(label, id, value, onclick),
+ }
+ }
+ /// Returns an `Html` representation of a text input field with the given parameters.
+ fn text_field_input(
+ label: &str,
+ id: &Option<String>,
+ placeholder: &Option<String>,
+ oninput: &Callback<InputData>,
+ typ: &str,
+ ) -> Html {
+ html! {
+ <div class="field column
+ is-one-fifth-widescreen
+ is-one-quarter-desktop
+ is-one-third-tablet
+ is-half-mobile" >
+ <label class="label">{ label }</label>
+ <div class="control">
+ <input id=id.as_ref().unwrap_or(&"".to_string())
+ type=typ
+ placeholder=placeholder.as_ref().unwrap_or(&"".to_string())
+ oninput=oninput
+ />
+ </div>
+ </div>
+ }
+ }
+ /// Returns an `Html` representation of a file input field with the given parameters.
+ fn file_field_input(
+ label: &str,
+ name: &str,
+ callback: &Callback<Option<File>>,
+ file: &Option<File>,
+ ) -> Html {
+ let callback = callback.clone();
+ let onchange = Callback::<()>::default();
+ let onchange = onchange.reform(move |c: ChangeData| {
+ if let ChangeData::Files(files) = c {
+ match files.item(0) {
+ Some(file) => {
+ let file_reader = FileReader::new().unwrap();
+ match file_reader.read_as_data_url(&file) {
+ Ok(_) => (),
+ Err(_) => ConsoleService::warn("Error: Couldn't get file as data url."),
+ };
+
+ let callback = callback.clone();
+ let onload = Closure::wrap(Box::new(move |event: Event| {
+ let file_reader: FileReader =
+ event.target().unwrap().dyn_into().unwrap();
+ let data_url: Option<String> =
+ file_reader.result().unwrap().as_string();
+ match data_url {
+ Some(content) => callback.emit(Some(File {
+ name: file.name(),
+ content,
+ })),
+ None => {
+ ConsoleService::warn("Couldn't get data url as string.");
+ callback.emit(None);
+ }
+ };
+ }) as Box<dyn FnMut(_)>);
+
+ file_reader.set_onload(Some(onload.as_ref().unchecked_ref()));
+ onload.forget();
+ }
+ None => callback.emit(None),
+ }
+ } else {
+ callback.emit(None);
+ }
+ });
+ html! {
+ <div class="field column
+ is-one-fifth-widescreen
+ is-one-quarter-desktop
+ is-one-third-tablet
+ is-half-mobile" >
+ <label class="label">{ label }</label>
+ <div class="file has-name control">
+ <label class="file-label">
+ <input class="file-input"
+ type="file"
+ name=name
+ onchange=onchange
+ />
+ <span class="file-cta">
+ <span class="file-icon">
+ <i class="fas fa-upload"></i>
+ </span>
+ <span class="file-label">
+ { "Choose file..." }
+ </span>
+ </span>
+ <span class="file-name">
+ {
+ match file {
+ Some(file) => file.name.clone(),
+ None => String::from("No file selected"),
+ }
+ }
+ </span>
+ </label>
+ </div>
+ </div>
+ }
+ }
+ /// Returns an `Html` representation of a checkbox input field with the given parameters.
+ fn checkbox_field_input(
+ label: &str,
+ id: &Option<String>,
+ checked: &bool,
+ onclick: &Callback<MouseEvent>,
+ ) -> Html {
+ html! {
+ <div class="field column
+ is-one-fifth-widescreen
+ is-one-quarter-desktop
+ is-one-third-tablet
+ is-half-mobile" >
+ <label class="checkbox">
+ <input id=id.as_ref().unwrap_or(&"".to_string())
+ type="checkbox"
+ checked=*checked
+ onclick=onclick
+ />
+ { label }
+ </label>
+ </div>
+ }
+ }
+}