# 🎨 **Form Component Design** ## **Overview** Design for the Form component that provides form building blocks with validation and accessibility features. ## **Core Components** ### **Form Component** ```rust #[component] pub fn Form( #[prop(into, optional)] class: Option, #[prop(into, optional)] id: Option, #[prop(into, optional)] on_submit: Option>, #[prop(into, optional)] children: Option, ) -> impl IntoView { let (form_data, set_form_data) = signal(std::collections::HashMap::new()); let (is_submitting, set_is_submitting) = signal(false); let (errors, set_errors) = signal(Vec::new()); let form_class = move || { let mut classes = vec!["space-y-6"]; if let Some(custom_class) = class.as_ref() { classes.push(custom_class); } classes.join(" ") }; let handle_submit = move |ev: leptos::ev::SubmitEvent| { ev.prevent_default(); if !is_submitting.get() { set_is_submitting.set(true); set_errors.set(Vec::new()); if let Some(on_submit) = on_submit.as_ref() { let data = FormData { fields: form_data.get(), is_submitting: true, is_valid: errors.get().is_empty(), errors: errors.get(), }; on_submit.call(data); } set_is_submitting.set(false); } }; view! {
{children}
} } ``` ### **FormField Component** ```rust #[component] pub fn FormField( #[prop(into, optional)] name: Option, #[prop(into, optional)] class: Option, #[prop(into, optional)] children: Option, ) -> impl IntoView { let field_class = move || { let mut classes = vec!["space-y-2"]; if let Some(custom_class) = class.as_ref() { classes.push(custom_class); } classes.join(" ") }; view! {
{children}
} } ``` ### **FormItem Component** ```rust #[component] pub fn FormItem( #[prop(into, optional)] class: Option, #[prop(into, optional)] children: Option, ) -> impl IntoView { let item_class = move || { let mut classes = vec!["space-y-2"]; if let Some(custom_class) = class.as_ref() { classes.push(custom_class); } classes.join(" ") }; view! {
{children}
} } ``` ### **FormLabel Component** ```rust #[component] pub fn FormLabel( #[prop(into, optional)] for_id: Option, #[prop(into, optional)] class: Option, #[prop(into, optional)] children: Option, ) -> impl IntoView { let label_class = move || { let mut classes = vec!["text-sm", "font-medium", "leading-none", "peer-disabled:cursor-not-allowed", "peer-disabled:opacity-70"]; if let Some(custom_class) = class.as_ref() { classes.push(custom_class); } classes.join(" ") }; view! { } } ``` ### **FormControl Component** ```rust #[component] pub fn FormControl( #[prop(into, optional)] class: Option, #[prop(into, optional)] children: Option, ) -> impl IntoView { let control_class = move || { let mut classes = vec!["peer"]; if let Some(custom_class) = class.as_ref() { classes.push(custom_class); } classes.join(" ") }; view! {
{children}
} } ``` ### **FormMessage Component** ```rust #[component] pub fn FormMessage( #[prop(into, optional)] message: Option>>, #[prop(into, optional)] class: Option, ) -> impl IntoView { let message_class = move || { let mut classes = vec!["text-sm", "font-medium", "text-destructive"]; if let Some(custom_class) = class.as_ref() { classes.push(custom_class); } classes.join(" ") }; view! { if let Some(message) = message.as_ref() { if let Some(msg) = message.get() {

{msg}

} } } } ``` ### **FormDescription Component** ```rust #[component] pub fn FormDescription( #[prop(into, optional)] class: Option, #[prop(into, optional)] children: Option, ) -> impl IntoView { let description_class = move || { let mut classes = vec!["text-sm", "text-muted-foreground"]; if let Some(custom_class) = class.as_ref() { classes.push(custom_class); } classes.join(" ") }; view! {

{children}

} } ``` ## **Supporting Types** ### **FormData** ```rust #[derive(Debug, Clone, PartialEq)] pub struct FormData { pub fields: std::collections::HashMap, pub is_submitting: bool, pub is_valid: bool, pub errors: Vec, } impl FormData { pub fn new() -> Self { Self { fields: std::collections::HashMap::new(), is_submitting: false, is_valid: true, errors: Vec::new(), } } pub fn add_field(&mut self, name: String, value: String) { self.fields.insert(name, value); } pub fn get_field(&self, name: &str) -> Option<&String> { self.fields.get(name) } pub fn has_errors(&self) -> bool { !self.errors.is_empty() } } ``` ### **FormValidation** ```rust #[derive(Debug, Clone, PartialEq)] pub struct FormValidation { pub can_submit: bool, pub has_errors: bool, pub error_count: usize, } impl FormValidation { pub fn new() -> Self { Self { can_submit: true, has_errors: false, error_count: 0, } } pub fn with_errors(errors: Vec) -> Self { Self { can_submit: errors.is_empty(), has_errors: !errors.is_empty(), error_count: errors.len(), } } } ``` ### **FormError** ```rust #[derive(Debug, Clone, PartialEq)] pub struct FormError { pub field: String, pub message: String, pub code: String, } impl FormError { pub fn new(field: String, message: String, code: String) -> Self { Self { field, message, code, } } pub fn required(field: String) -> Self { Self::new(field, "This field is required".to_string(), "required".to_string()) } pub fn invalid_email(field: String) -> Self { Self::new(field, "Invalid email format".to_string(), "invalid_email".to_string()) } pub fn min_length(field: String, min: usize) -> Self { Self::new(field, format!("Minimum length is {} characters", min), "min_length".to_string()) } } ``` ## **Usage Examples** ### **Basic Form** ```rust let handle_submit = move |data: FormData| { println!("Form submitted: {:?}", data); }; view! {
"Name" "Enter your full name" "Email" "Enter your email address"
} ``` ### **Form with Validation** ```rust let (name_error, set_name_error) = signal(None::); let (email_error, set_email_error) = signal(None::); let handle_submit = move |data: FormData| { let mut errors = Vec::new(); if data.get_field("name").map_or(true, |v| v.is_empty()) { errors.push("Name is required".to_string()); set_name_error.set(Some("Name is required".to_string())); } else { set_name_error.set(None); } if data.get_field("email").map_or(true, |v| v.is_empty()) { errors.push("Email is required".to_string()); set_email_error.set(Some("Email is required".to_string())); } else { set_email_error.set(None); } if errors.is_empty() { println!("Form is valid: {:?}", data); } }; view! {
"Name" "Email"
} ``` ### **Form with Custom Styling** ```rust view! {
"Message"