diff --git a/src/components/custom_button/custom_button_component.rs b/src/components/custom_button/custom_button_component.rs new file mode 100644 index 0000000..edf86a6 --- /dev/null +++ b/src/components/custom_button/custom_button_component.rs @@ -0,0 +1,13 @@ +use dioxus::prelude::*; +use crate::components::custom_button::custom_button_props::CustomButtonProps; + +#[allow(non_snake_case)] +pub fn CustomButton(props: CustomButtonProps) -> Element { + rsx! { + button { + class: "{props.css}", + onclick: props.onclick, + { props.label } + } + } +} diff --git a/src/components/custom_button/custom_button_props.rs b/src/components/custom_button/custom_button_props.rs new file mode 100644 index 0000000..226b824 --- /dev/null +++ b/src/components/custom_button/custom_button_props.rs @@ -0,0 +1,10 @@ +use dioxus::prelude::*; + +#[derive(Props, Clone, PartialEq)] +pub struct CustomButtonProps { + #[props(into)] + pub label: String, + #[props(into)] + pub css: String, + pub onclick: Callback, +} diff --git a/src/components/custom_button/mod.rs b/src/components/custom_button/mod.rs new file mode 100644 index 0000000..4975a68 --- /dev/null +++ b/src/components/custom_button/mod.rs @@ -0,0 +1,2 @@ +pub mod custom_button_component; +pub mod custom_button_props; diff --git a/src/components/custom_dropdown/custom_dropdown_component.rs b/src/components/custom_dropdown/custom_dropdown_component.rs new file mode 100644 index 0000000..038ec91 --- /dev/null +++ b/src/components/custom_dropdown/custom_dropdown_component.rs @@ -0,0 +1,22 @@ +use dioxus::prelude::*; +use crate::components::custom_dropdown::custom_dropdown_props::CustomDropdownProps; + +#[allow(non_snake_case)] +pub fn CustomDropdown(props: CustomDropdownProps) -> Element { + let onchange = props.onchange; + + rsx! { + select { + class: "{props.css}", + onchange: move |event| onchange.call(event.value()), + for opt in props.options.clone() { + option { + key: "{opt}", + value: "{opt}", + selected: opt == props.selected, + "{opt}" + } + } + } + } +} diff --git a/src/components/custom_dropdown/custom_dropdown_props.rs b/src/components/custom_dropdown/custom_dropdown_props.rs new file mode 100644 index 0000000..c9f35b8 --- /dev/null +++ b/src/components/custom_dropdown/custom_dropdown_props.rs @@ -0,0 +1,13 @@ +use dioxus::prelude::*; + +#[derive(Props, Clone, PartialEq)] +pub struct CustomDropdownProps { + // ponytail: Option-Label == Option-Wert. Wenn beides mal auseinanderfallen muss, + // wird daraus Vec<(String, String)> — betrifft nur diese Datei und die Call-Sites. + pub options: Vec, + #[props(into)] + pub selected: String, + #[props(into)] + pub css: String, + pub onchange: Callback, +} diff --git a/src/components/custom_dropdown/mod.rs b/src/components/custom_dropdown/mod.rs new file mode 100644 index 0000000..a0dc205 --- /dev/null +++ b/src/components/custom_dropdown/mod.rs @@ -0,0 +1,2 @@ +pub mod custom_dropdown_component; +pub mod custom_dropdown_props; diff --git a/src/components/custom_input/custom_input_component.rs b/src/components/custom_input/custom_input_component.rs new file mode 100644 index 0000000..1d9b567 --- /dev/null +++ b/src/components/custom_input/custom_input_component.rs @@ -0,0 +1,16 @@ +use dioxus::prelude::*; +use crate::components::custom_input::custom_input_props::CustomInputProps; + +#[allow(non_snake_case)] +pub fn CustomInput(props: CustomInputProps) -> Element { + let oninput = props.oninput; + + rsx! { + input { + class: "{props.css}", + value: "{props.value}", + placeholder: "{props.placeholder}", + oninput: move |event| oninput.call(event.value()), + } + } +} diff --git a/src/components/custom_input/custom_input_props.rs b/src/components/custom_input/custom_input_props.rs new file mode 100644 index 0000000..423b71a --- /dev/null +++ b/src/components/custom_input/custom_input_props.rs @@ -0,0 +1,13 @@ +use dioxus::prelude::*; + +#[derive(Props, Clone, PartialEq)] +pub struct CustomInputProps { + /// Aktueller Wert — die Komponente ist controlled, der Zustand liegt beim Aufrufer. + #[props(into)] + pub value: String, + #[props(into, default)] + pub placeholder: String, + #[props(into)] + pub css: String, + pub oninput: Callback, +} diff --git a/src/components/custom_input/mod.rs b/src/components/custom_input/mod.rs new file mode 100644 index 0000000..7f1a7cb --- /dev/null +++ b/src/components/custom_input/mod.rs @@ -0,0 +1,2 @@ +pub mod custom_input_component; +pub mod custom_input_props; diff --git a/src/components/custom_multi_select/custom_multi_select_component.rs b/src/components/custom_multi_select/custom_multi_select_component.rs new file mode 100644 index 0000000..8b86a8c --- /dev/null +++ b/src/components/custom_multi_select/custom_multi_select_component.rs @@ -0,0 +1,55 @@ +use dioxus::prelude::*; +use crate::components::custom_multi_select::custom_multi_select_props::CustomMultiSelectProps; + +#[allow(non_snake_case)] +pub fn CustomMultiSelect(props: CustomMultiSelectProps) -> Element { + let onchange = props.onchange; + let selected = props.selected.clone(); + + rsx! { + div { + class: "{props.css}", + for opt in props.options.clone() { + label { + key: "{opt}", + input { + r#type: "checkbox", + checked: selected.contains(&opt), + onchange: { + let selected = selected.clone(); + let opt = opt.clone(); + move |_| onchange.call(toggled(&selected, &opt)) + }, + } + "{opt}" + } + } + } + } +} + +/// Auswahl mit `option` umschalten. Reihenfolge der übrigen Einträge bleibt erhalten. +fn toggled(selected: &[String], option: &str) -> Vec { + let mut next = selected.to_vec(); + match next.iter().position(|entry| entry == option) { + Some(index) => { + next.remove(index); + } + None => next.push(option.to_string()), + } + next +} + +#[cfg(test)] +mod tests { + use super::toggled; + + #[test] + fn toggles_in_and_out_without_reordering() { + let selected: Vec = vec!["a".into(), "b".into()]; + + assert_eq!(toggled(&selected, "c"), vec!["a", "b", "c"]); + assert_eq!(toggled(&selected, "a"), vec!["b"]); + assert_eq!(toggled(&toggled(&selected, "a"), "a"), vec!["b", "a"]); + } +} diff --git a/src/components/custom_multi_select/custom_multi_select_props.rs b/src/components/custom_multi_select/custom_multi_select_props.rs new file mode 100644 index 0000000..4d857f8 --- /dev/null +++ b/src/components/custom_multi_select/custom_multi_select_props.rs @@ -0,0 +1,11 @@ +use dioxus::prelude::*; + +#[derive(Props, Clone, PartialEq)] +pub struct CustomMultiSelectProps { + pub options: Vec, + pub selected: Vec, + #[props(into)] + pub css: String, + /// Bekommt die komplette neue Auswahl, nicht nur die angeklickte Option. + pub onchange: Callback>, +} diff --git a/src/components/custom_multi_select/mod.rs b/src/components/custom_multi_select/mod.rs new file mode 100644 index 0000000..c918832 --- /dev/null +++ b/src/components/custom_multi_select/mod.rs @@ -0,0 +1,2 @@ +pub mod custom_multi_select_component; +pub mod custom_multi_select_props; diff --git a/src/components/custom_single_select/custom_single_select_component.rs b/src/components/custom_single_select/custom_single_select_component.rs new file mode 100644 index 0000000..53ab5d5 --- /dev/null +++ b/src/components/custom_single_select/custom_single_select_component.rs @@ -0,0 +1,29 @@ +use dioxus::prelude::*; +use crate::components::custom_single_select::custom_single_select_props::CustomSingleSelectProps; + +#[allow(non_snake_case)] +pub fn CustomSingleSelect(props: CustomSingleSelectProps) -> Element { + let onchange = props.onchange; + + rsx! { + div { + class: "{props.css}", + for opt in props.options.clone() { + label { + key: "{opt}", + input { + r#type: "radio", + name: "{props.name}", + value: "{opt}", + checked: opt == props.selected, + onchange: { + let opt = opt.clone(); + move |_| onchange.call(opt.clone()) + }, + } + "{opt}" + } + } + } + } +} diff --git a/src/components/custom_single_select/custom_single_select_props.rs b/src/components/custom_single_select/custom_single_select_props.rs new file mode 100644 index 0000000..1144373 --- /dev/null +++ b/src/components/custom_single_select/custom_single_select_props.rs @@ -0,0 +1,15 @@ +use dioxus::prelude::*; + +#[derive(Props, Clone, PartialEq)] +pub struct CustomSingleSelectProps { + /// Gruppenname der Radio-Buttons — muss pro Gruppe eindeutig sein, + /// sonst schalten sich zwei Gruppen gegenseitig ab. + #[props(into)] + pub name: String, + pub options: Vec, + #[props(into)] + pub selected: String, + #[props(into)] + pub css: String, + pub onchange: Callback, +} diff --git a/src/components/custom_single_select/mod.rs b/src/components/custom_single_select/mod.rs new file mode 100644 index 0000000..5712ccd --- /dev/null +++ b/src/components/custom_single_select/mod.rs @@ -0,0 +1,2 @@ +pub mod custom_single_select_component; +pub mod custom_single_select_props; diff --git a/src/components/mod.rs b/src/components/mod.rs index c73b5d6..49f973d 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1 +1,6 @@ +pub mod custom_button; +pub mod custom_dropdown; +pub mod custom_input; pub mod custom_link; +pub mod custom_multi_select; +pub mod custom_single_select;