Skip to main content

vertigo_forms/
login.rs

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