Skip to main content

leptos_forms_rs/components/
form_wizard.rs

1use leptos::prelude::*;
2
3/// Form wizard component for multi-step forms
4#[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        // For now, always return true
43        // In a real implementation, you'd validate the current step
44        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            // Progress bar
67            <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            // Step content
86            <div class="wizard-content">
87                {children().into_any()}
88            </div>
89
90            // Navigation buttons
91            <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}