leptos_forms_rs/components/
form_wizard.rs1use leptos::prelude::*;
2
3#[component]
5pub fn FormWizard(
6 #[prop(into)] steps: Vec<String>,
7 #[prop(into)] children: Children,
8 #[prop(optional)] class: Option<String>,
9) -> impl IntoView {
10 let (current_step_index, set_current_step_index) = signal(0);
11 let steps1 = steps.clone();
12 let steps2 = steps.clone();
13 let steps3 = steps.clone();
14 let steps4 = steps.clone();
15 let steps5 = steps.clone();
16
17 let next_step = move |_| {
18 let current = current_step_index.get();
19 if current < steps1.len() - 1 {
20 set_current_step_index.set(current + 1);
21 }
22 };
23
24 let prev_step = move |_| {
25 let current = current_step_index.get();
26 if current > 0 {
27 set_current_step_index.set(current - 1);
28 }
29 };
30
31 let _go_to_step = Callback::new(move |step: usize| {
32 if step < steps2.len() {
33 set_current_step_index.set(step);
34 }
35 });
36
37 let complete = move |_| {
38 log::info!("Form wizard completed");
39 };
40
41 let validate_current_step = move || {
42 true
45 };
46
47 let can_go_next = Memo::new(move |_| {
48 let current = current_step_index.get();
49 current < steps3.len() - 1 && validate_current_step()
50 });
51
52 let can_go_prev = Memo::new(move |_| {
53 let current = current_step_index.get();
54 current > 0
55 });
56
57 let is_last_step = Memo::new(move |_| {
58 let current = current_step_index.get();
59 current == steps4.len() - 1
60 });
61
62 let wizard_class = class.unwrap_or_else(|| "form-wizard".to_string());
63
64 view! {
65 <div class={wizard_class}>
66 <div class="wizard-progress">
68 {steps5.iter().enumerate().map(|(step_index, step_name)| {
69 let is_active = Memo::new(move |_| current_step_index.get() == step_index);
70 let is_completed = Memo::new(move |_| current_step_index.get() > step_index);
71
72 view! {
73 <div class="wizard-step" class:active=move || is_active.get() class:completed=move || is_completed.get()>
74 <div class="step-indicator">
75 {if is_completed.get() { "✓".to_string() } else { (step_index + 1).to_string() }}
76 </div>
77 <div class="step-label">
78 {step_name.clone()}
79 </div>
80 </div>
81 }
82 }).collect::<Vec<_>>()}
83 </div>
84
85 <div class="wizard-content">
87 {children().into_any()}
88 </div>
89
90 <div class="wizard-navigation">
92 <button
93 type="button"
94 class="nav-button prev-button"
95 disabled=move || !can_go_prev.get()
96 on:click=prev_step
97 >
98 "Previous"
99 </button>
100
101 {if is_last_step.get() {
102 view! {
103 <button
104 type="button"
105 class="nav-button complete-button"
106 on:click=complete
107 >
108 "Complete"
109 </button>
110 }.into_any()
111 } else {
112 view! { <div></div> }.into_any()
113 }}
114
115 <button
116 type="button"
117 class="nav-button next-button"
118 disabled=move || !can_go_next.get()
119 on:click=next_step
120 >
121 "Next"
122 </button>
123 </div>
124 </div>
125 }
126}