summary refs log tree commit diff stats
path: root/src/components/form.rs
blob: fd5589712d4349e3a9b15024b0b2999073b4469a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
macro_rules! Form {
    (
        on_submit = |$bound:pat_param| $on_submit:block
        $(
            <Input
                name=$name:ident,
                signal_name_get=$signal_name_get:ident,
                signal_name_set=$signal_name_set:ident,
                rust_type=$rust_type:ty,
                html_type=$input_type:literal,
                label=$label:literal $(,)*
            />
        )*
    ) => {{
        use leptos::{
            view,
            prelude::{
                Get,
                NodeRef,
                ElementChild,
                ClassAttribute,
                OnAttribute,
                signal,
                Set,
                Show,
            },
            html::Input,
            web_sys::SubmitEvent
        };

        use log::info;


        $(
            let ($signal_name_get, $signal_name_set) = signal(None);
            let $name: NodeRef<Input> = NodeRef::new();
        )*

        let on_submit = move |ev: SubmitEvent| {
            struct Inputs {
                $(
                    $name: $rust_type
                ),*
            }

            // stop the page from reloading!
            ev.prevent_default();

            $(
                let value = {
                    let output = $name
                        .get()
                        // event handlers can only fire after the view
                        // is mounted to the DOM, so the `NodeRef` will be `Some`
                        .expect("<input> to exist")
                        .value();

                    let fin: Result<$rust_type, leptos::error::Error> = output
                                .parse()
                                .map_err(Into::<leptos::error::Error>::into);
                    fin
                };

                let $name = match value {
                    Ok(ok) => {
                        // Reset the signal
                        $signal_name_set.set(None);

                        ok
                    } ,
                    Err(err) => {
                        $signal_name_set.set(Some(err));

                        // Skip running the real `on_submit`
                        return
                    }
                };
            )*

            let real_on_submit = |$bound| $on_submit;
            real_on_submit(Inputs {
                $(
                    $name
                ),*
            })
        };


        view! {
            <form class="flex flex-col contents-start m-2 g-2" on:submit=on_submit>
                $(
                     <InputPlaceholder input_type=$input_type label=$label node_ref=$name />
                     <Show
                        when=move || $signal_name_get.get().is_some()
                        fallback=|| ()
                     >
                         <p class="ps-2 text-red-300">{move ||
                             format!(
                                 "Input is invalid for type {}: {}",
                                 stringify!($rust_type),
                                 $signal_name_get.get().expect("Was `is_some`")
                             )
                         }</p>
                     </Show>
                )*

                <div class="static">
                    <input
                        type="submit"
                        value="Submit"
                        class="absolute bottom-0 right-0 h-20 w-20 rounded-lg bg-green-300 m-2"
                    />
                </div>
            </form>
        }
    }};
}

pub(crate) use Form;