use dioxus::prelude::*;
use crate::types::Size;
#[derive(Clone, PartialEq, Props)]
pub struct FormGroupProps {
#[props(default)]
pub label: String,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn FormGroup(props: FormGroupProps) -> Element {
let full_class = if props.class.is_empty() {
"mb-3".to_string()
} else {
format!("mb-3 {}", props.class)
};
rsx! {
div { class: "{full_class}",
..props.attributes,
if !props.label.is_empty() {
label { class: "form-label", "{props.label}" }
}
{props.children}
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct InputProps {
#[props(default = "text".to_string())]
pub r#type: String,
#[props(default)]
pub value: String,
#[props(default)]
pub uncontrolled: bool,
#[props(default)]
pub placeholder: String,
#[props(default)]
pub min: Option<String>,
#[props(default)]
pub max: Option<String>,
#[props(default)]
pub autocomplete: Option<String>,
#[props(default)]
pub list: Option<String>,
#[props(default)]
pub size: Size,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub readonly: bool,
#[props(default)]
pub oninput: Option<EventHandler<FormEvent>>,
#[props(default)]
pub onchange: Option<EventHandler<FormEvent>>,
#[props(default)]
pub onfocus: Option<EventHandler<FocusEvent>>,
#[props(default)]
pub onblur: Option<EventHandler<FocusEvent>>,
#[props(default)]
pub onkeydown: Option<EventHandler<KeyboardEvent>>,
#[props(default)]
pub onkeyup: Option<EventHandler<KeyboardEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
}
#[component]
pub fn Input(props: InputProps) -> Element {
let size_class = match props.size {
Size::Md => String::new(),
s => format!(" form-control-{s}"),
};
let full_class = if props.class.is_empty() {
format!("form-control{size_class}")
} else {
format!("form-control{size_class} {}", props.class)
};
rsx! {
input {
class: "{full_class}",
r#type: "{props.r#type}",
value: if props.uncontrolled { None } else { Some(props.value.clone()) },
placeholder: "{props.placeholder}",
min: props.min.clone(),
max: props.max.clone(),
autocomplete: props.autocomplete.clone(),
list: props.list.clone(),
disabled: props.disabled,
readonly: props.readonly,
oninput: move |evt| {
if let Some(handler) = &props.oninput {
handler.call(evt);
}
},
onchange: move |evt| {
if let Some(handler) = &props.onchange {
handler.call(evt);
}
},
onfocus: move |evt| {
if let Some(handler) = &props.onfocus {
handler.call(evt);
}
},
onblur: move |evt| {
if let Some(handler) = &props.onblur {
handler.call(evt);
}
},
onkeydown: move |evt| {
if let Some(handler) = &props.onkeydown {
handler.call(evt);
}
},
onkeyup: move |evt| {
if let Some(handler) = &props.onkeyup {
handler.call(evt);
}
},
..props.attributes,
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct SelectProps {
#[props(default)]
pub value: String,
#[props(default)]
pub size: Size,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub onchange: Option<EventHandler<FormEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn Select(props: SelectProps) -> Element {
let size_class = match props.size {
Size::Md => String::new(),
s => format!(" form-select-{s}"),
};
let full_class = if props.class.is_empty() {
format!("form-select{size_class}")
} else {
format!("form-select{size_class} {}", props.class)
};
rsx! {
select {
class: "{full_class}",
value: "{props.value}",
disabled: props.disabled,
onchange: move |evt| {
if let Some(handler) = &props.onchange {
handler.call(evt);
}
},
..props.attributes,
{props.children}
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct TextareaProps {
#[props(default)]
pub value: String,
#[props(default)]
pub uncontrolled: bool,
#[props(default = 3)]
pub rows: u32,
#[props(default)]
pub placeholder: String,
#[props(default)]
pub size: Size,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub readonly: bool,
#[props(default)]
pub oninput: Option<EventHandler<FormEvent>>,
#[props(default)]
pub onchange: Option<EventHandler<FormEvent>>,
#[props(default)]
pub onfocus: Option<EventHandler<FocusEvent>>,
#[props(default)]
pub onblur: Option<EventHandler<FocusEvent>>,
#[props(default)]
pub onkeydown: Option<EventHandler<KeyboardEvent>>,
#[props(default)]
pub onkeyup: Option<EventHandler<KeyboardEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
}
#[component]
pub fn Textarea(props: TextareaProps) -> Element {
let size_class = match props.size {
Size::Md => String::new(),
s => format!(" form-control-{s}"),
};
let full_class = if props.class.is_empty() {
format!("form-control{size_class}")
} else {
format!("form-control{size_class} {}", props.class)
};
rsx! {
textarea {
class: "{full_class}",
rows: "{props.rows}",
placeholder: "{props.placeholder}",
disabled: props.disabled,
readonly: props.readonly,
value: if props.uncontrolled { None } else { Some(props.value.clone()) },
oninput: move |evt| {
if let Some(handler) = &props.oninput {
handler.call(evt);
}
},
onchange: move |evt| {
if let Some(handler) = &props.onchange {
handler.call(evt);
}
},
onfocus: move |evt| {
if let Some(handler) = &props.onfocus {
handler.call(evt);
}
},
onblur: move |evt| {
if let Some(handler) = &props.onblur {
handler.call(evt);
}
},
onkeydown: move |evt| {
if let Some(handler) = &props.onkeydown {
handler.call(evt);
}
},
onkeyup: move |evt| {
if let Some(handler) = &props.onkeyup {
handler.call(evt);
}
},
..props.attributes,
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct CheckboxProps {
#[props(default)]
pub checked: bool,
#[props(default)]
pub input_id: Option<String>,
#[props(default)]
pub label: String,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub onchange: Option<EventHandler<FormEvent>>,
#[props(default)]
pub onclick: Option<EventHandler<MouseEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
}
#[component]
pub fn Checkbox(props: CheckboxProps) -> Element {
let full_class = if props.class.is_empty() {
"form-check".to_string()
} else {
format!("form-check {}", props.class)
};
let label_for = props.input_id.clone().unwrap_or_default();
rsx! {
div { class: "{full_class}",
..props.attributes,
input {
class: "form-check-input",
r#type: "checkbox",
id: props.input_id.unwrap_or_default(),
checked: props.checked,
disabled: props.disabled,
onclick: move |evt| {
if let Some(handler) = &props.onclick {
handler.call(evt);
}
},
onchange: move |evt| {
if let Some(handler) = &props.onchange {
handler.call(evt);
}
},
}
if !props.label.is_empty() {
label { class: "form-check-label", r#for: "{label_for}", "{props.label}" }
}
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct SwitchProps {
#[props(default)]
pub checked: bool,
#[props(default)]
pub label: String,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub onchange: Option<EventHandler<FormEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
}
#[component]
pub fn Switch(props: SwitchProps) -> Element {
let full_class = if props.class.is_empty() {
"form-check form-switch".to_string()
} else {
format!("form-check form-switch {}", props.class)
};
rsx! {
div { class: "{full_class}",
..props.attributes,
input {
class: "form-check-input",
r#type: "checkbox",
role: "switch",
checked: props.checked,
disabled: props.disabled,
onchange: move |evt| {
if let Some(handler) = &props.onchange {
handler.call(evt);
}
},
}
if !props.label.is_empty() {
label { class: "form-check-label", "{props.label}" }
}
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct RangeProps {
#[props(default)]
pub value: String,
#[props(default = "0".to_string())]
pub min: String,
#[props(default = "100".to_string())]
pub max: String,
#[props(default)]
pub step: String,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub oninput: Option<EventHandler<FormEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
}
#[component]
pub fn Range(props: RangeProps) -> Element {
let full_class = if props.class.is_empty() {
"form-range".to_string()
} else {
format!("form-range {}", props.class)
};
rsx! {
input {
class: "{full_class}",
r#type: "range",
value: "{props.value}",
min: "{props.min}",
max: "{props.max}",
step: if props.step.is_empty() { None } else { Some(props.step.clone()) },
disabled: props.disabled,
oninput: move |evt| {
if let Some(handler) = &props.oninput {
handler.call(evt);
}
},
..props.attributes,
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct FloatingLabelProps {
pub label: String,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn FloatingLabel(props: FloatingLabelProps) -> Element {
let full_class = if props.class.is_empty() {
"form-floating".to_string()
} else {
format!("form-floating {}", props.class)
};
rsx! {
div { class: "{full_class}",
..props.attributes,
{props.children}
label { "{props.label}" }
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct FormFeedbackProps {
#[props(default)]
pub valid: bool,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn FormFeedback(props: FormFeedbackProps) -> Element {
let base = if props.valid {
"valid-feedback"
} else {
"invalid-feedback"
};
let full_class = if props.class.is_empty() {
base.to_string()
} else {
format!("{base} {}", props.class)
};
rsx! {
div { class: "{full_class}", ..props.attributes, {props.children} }
}
}
#[derive(Clone, PartialEq, Props)]
pub struct FormTextProps {
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn FormText(props: FormTextProps) -> Element {
let full_class = if props.class.is_empty() {
"form-text".to_string()
} else {
format!("form-text {}", props.class)
};
rsx! {
div { class: "{full_class}", ..props.attributes, {props.children} }
}
}
#[derive(Clone, PartialEq, Props)]
pub struct RadioProps {
pub name: String,
#[props(default)]
pub checked: bool,
#[props(default)]
pub label: String,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub onchange: Option<EventHandler<FormEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
}
#[component]
pub fn Radio(props: RadioProps) -> Element {
let full_class = if props.class.is_empty() {
"form-check".to_string()
} else {
format!("form-check {}", props.class)
};
rsx! {
div { class: "{full_class}",
..props.attributes,
input {
class: "form-check-input",
r#type: "radio",
name: "{props.name}",
checked: props.checked,
disabled: props.disabled,
onchange: move |evt| {
if let Some(handler) = &props.onchange {
handler.call(evt);
}
},
}
if !props.label.is_empty() {
label { class: "form-check-label", "{props.label}" }
}
}
}
}