vertigo_forms/
login.rs

1use std::rc::Rc;
2use vertigo::{Css, DomNode, KeyDownEvent, Resource, Value, bind, bind_rc, css, dom, transaction};
3
4pub type OnSubmit = Rc<dyn Fn(&str, &str)>;
5
6pub struct Login<T> {
7    pub on_submit: OnSubmit,
8    pub token_result: Value<Option<Resource<T>>>,
9    pub params: LoginParams,
10}
11
12pub struct LoginParams {
13    pub css: Css,
14    pub add_css: Css,
15    pub line_css: Css,
16    pub line_add_css: Css,
17    pub input_css: Css,
18    pub submit_css: Css,
19    pub submit_add_css: Css,
20    pub error_message: Rc<dyn Fn(String) -> String>,
21    pub username_label: String,
22    pub password_label: String,
23    pub button_label: String,
24    pub waiting_label: String,
25}
26
27impl Default for LoginParams {
28    fn default() -> Self {
29        Self {
30            css: css! {"
31                width: 250px;
32                margin: auto;
33                padding: 10px;
34                margin-bottom: 10px;
35            "},
36            add_css: Css::default(),
37            line_css: css! {"
38                min-height: 1em;
39                margin-bottom: 5px;
40            "},
41            line_add_css: Css::default(),
42            input_css: Css::default(),
43            submit_css: css! {"
44                margin-top: 15px;
45            "},
46            submit_add_css: Css::default(),
47            error_message: Rc::new(|err| err),
48            username_label: "Username:".to_string(),
49            password_label: "Password:".to_string(),
50            button_label: "Login".to_string(),
51            waiting_label: "Logging in...".to_string(),
52        }
53    }
54}
55
56impl<T: Clone + PartialEq + 'static> Login<T> {
57    pub fn into_component(self) -> Self {
58        self
59    }
60
61    pub fn mount(&self) -> DomNode {
62        let Self {
63            on_submit,
64            token_result,
65            params,
66        } = self;
67
68        let username = Value::<String>::default();
69        let password = Value::<String>::default();
70
71        let submit = bind_rc!(on_submit, username, password, || transaction(|ctx| {
72            on_submit(&username.get(ctx), &password.get(ctx));
73        }));
74
75        let on_key_down = bind!(submit, |key: KeyDownEvent| {
76            if key.code == "Enter" || key.code == "NumpadEnter" {
77                submit();
78                return true;
79            }
80            false
81        });
82
83        let css = &params.css + &params.add_css;
84        let line_css = &params.line_css + &params.line_add_css;
85        let submit_css = &params.submit_css + &params.submit_add_css;
86        let error_message = params.error_message.clone();
87        let waiting_label = params.waiting_label.clone();
88
89        let message_div = bind!(
90            line_css,
91            token_result.render_value(move |token_result| {
92                let css_error = line_css.clone().push_str("color: red;");
93
94                match token_result {
95                    Some(Resource::Loading) => dom! {
96                        <div css={line_css.clone()}>{&waiting_label}</div>
97                    },
98                    Some(Resource::Error(err)) => dom! {
99                        <div css={css_error}>{error_message(err)}</div>
100                    },
101                    _ => dom! {
102                        <div css={line_css.clone()} />
103                    },
104                }
105            })
106        );
107
108        let on_username_change = bind!(username, |new_value: String| username.set(new_value));
109
110        let username_div = dom! {
111            <div css={line_css.clone()}>
112                <div>{&params.username_label}</div>
113                <input
114                    css={&params.input_css}
115                    value={username.to_computed()}
116                    on_input={on_username_change}
117                />
118            </div>
119        };
120
121        let on_password_change = bind!(password, |new_value| password.set(new_value));
122
123        let password_div = dom! {
124            <div css={line_css}>
125                <div>{&params.password_label}</div>
126                <input
127                    css={&params.input_css}
128                    value={password.to_computed()}
129                    on_input={on_password_change}
130                    type="password"
131                />
132            </div>
133        };
134
135        dom! {
136            <div css={css} {on_key_down}>
137                { message_div }
138                { username_div }
139                { password_div }
140                <div css={submit_css}>
141                    <input type="submit" value={&params.button_label} on_click={move |_| submit()} />
142                </div>
143            </div>
144        }
145    }
146}