mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2026-05-24 23:40:40 +00:00
- Fixed all Signal<T> attribute patterns to use move || signal.get() syntax - Updated reserved keyword handling (type -> r#type) - Removed attr: prefixes for standard HTML attributes - Fixed import paths in form example (shadcn_ui_leptos_* -> leptos_shadcn_*) - Added comprehensive TDD tests for Leptos v0.8 compatibility - All 46 component packages now fully compatible with Leptos v0.8 - Version bump to 0.4.0 for breaking changes Resolves: Leptos v0.8 attribute system compatibility issues Tested: 200+ tests passing across all packages
149 lines
5.7 KiB
Rust
149 lines
5.7 KiB
Rust
use leptos::prelude::*;
|
|
use leptos_shadcn_form::{Form, FormField, FormItem, FormLabel, FormControl, FormMessage, FormDescription};
|
|
use leptos_shadcn_form::default::FormData;
|
|
use leptos_shadcn_input::Input;
|
|
use leptos_shadcn_button::Button;
|
|
|
|
#[component]
|
|
pub fn FormExample() -> impl IntoView {
|
|
let (form_data, set_form_data) = signal(FormData::new());
|
|
let (errors, set_errors) = signal(Vec::<(String, String)>::new());
|
|
|
|
let handle_submit = Callback::new(move |data: FormData| {
|
|
set_form_data.set(data.clone());
|
|
|
|
// Simple validation
|
|
let mut new_errors = Vec::new();
|
|
|
|
if let Some(email) = data.get("email") {
|
|
if email.is_empty() {
|
|
new_errors.push(("email".to_string(), "Email is required".to_string()));
|
|
} else if !email.contains('@') {
|
|
new_errors.push(("email".to_string(), "Please enter a valid email".to_string()));
|
|
}
|
|
}
|
|
|
|
if let Some(password) = data.get("password") {
|
|
if password.is_empty() {
|
|
new_errors.push(("password".to_string(), "Password is required".to_string()));
|
|
} else if password.len() < 6 {
|
|
new_errors.push(("password".to_string(), "Password must be at least 6 characters".to_string()));
|
|
}
|
|
}
|
|
|
|
if let Some(name) = data.get("name") {
|
|
if name.is_empty() {
|
|
new_errors.push(("name".to_string(), "Name is required".to_string()));
|
|
}
|
|
}
|
|
|
|
set_errors.set(new_errors);
|
|
|
|
if errors.get().is_empty() {
|
|
// Form submitted successfully!
|
|
}
|
|
});
|
|
|
|
let get_error = move |field: &str| {
|
|
errors.get()
|
|
.iter()
|
|
.find(|(f, _)| f == field)
|
|
.map(|(_, msg)| msg.clone())
|
|
};
|
|
|
|
view! {
|
|
<div class="p-8 space-y-6">
|
|
<div class="space-y-2">
|
|
<h2 class="text-2xl font-bold">Form Example</h2>
|
|
<p class="text-muted-foreground">
|
|
"A complete form with validation and accessibility features."
|
|
</p>
|
|
</div>
|
|
|
|
<Form on_submit=handle_submit>
|
|
<FormField name="name">
|
|
<FormItem>
|
|
<FormLabel for_field="name">"Name"</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
id="name"
|
|
placeholder="Enter your name"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage message=Signal::derive(move || get_error("name")) />
|
|
</FormItem>
|
|
</FormField>
|
|
|
|
<FormField name="email">
|
|
<FormItem>
|
|
<FormLabel for_field="email">"Email"</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
id="email"
|
|
input_type="email"
|
|
placeholder="Enter your email"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage message=Signal::derive(move || get_error("email")) />
|
|
<FormDescription>
|
|
"We'll never share your email with anyone else."
|
|
</FormDescription>
|
|
</FormItem>
|
|
</FormField>
|
|
|
|
<FormField name="password">
|
|
<FormItem>
|
|
<FormLabel for_field="password">"Password"</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
id="password"
|
|
input_type="password"
|
|
placeholder="Enter your password"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage message=Signal::derive(move || get_error("password")) />
|
|
<FormDescription>
|
|
"Password must be at least 6 characters long."
|
|
</FormDescription>
|
|
</FormItem>
|
|
</FormField>
|
|
|
|
<Button class="w-full">
|
|
"Submit"
|
|
</Button>
|
|
</Form>
|
|
|
|
{move || {
|
|
let fields = form_data.get().fields;
|
|
if !fields.is_empty() {
|
|
view! {
|
|
<div class="p-4 bg-muted rounded-md">
|
|
<h3 class="text-lg font-semibold mb-2">"Submitted Data:"</h3>
|
|
<pre class="text-sm">
|
|
{format!("{:#?}", fields)}
|
|
</pre>
|
|
</div>
|
|
}.into_any()
|
|
} else {
|
|
view! { <div class="hidden"></div> }.into_any()
|
|
}
|
|
}}
|
|
|
|
<div class="space-y-4">
|
|
<h3 class="text-lg font-semibold">"Features Demonstrated:"</h3>
|
|
<ul class="list-disc list-inside space-y-1 text-sm text-muted-foreground">
|
|
<li>"Form submission handling"</li>
|
|
<li>"Field validation with error messages"</li>
|
|
<li>"Accessible form labels and descriptions"</li>
|
|
<li>"Form data collection and processing"</li>
|
|
<li>"Responsive design"</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
mount_to_body(|| view! { <FormExample /> })
|
|
}
|