dxc_components/input/
input.rs

1use super::props::InputProps;
2use crate::DxcIcon;
3use dioxus::prelude::*;
4use dxc_hooks::UseNamespace;
5use dxc_icons::{spawn_icon, CircleClose, Hide, View};
6use dxc_macros::classes;
7use dxc_types::namespace::Block;
8
9#[component]
10pub fn DxcInput(props: InputProps) -> Element {
11    // State
12    let input_id = use_signal(|| props.id());
13    let input_type = props.type_.clone().unwrap_or("text".to_string());
14    let input_resize = use_signal(|| props.resize().to_string());
15    let input_size = use_signal(|| props.size().to_string());
16
17    let mut input_value = props.value();
18    let input_disable = use_signal(|| props.disabled());
19
20    let clearable = use_signal(|| props.clearable());
21    let read_only = use_signal(|| props.read_only());
22    let show_word_limit = use_signal(|| props.show_word_limit());
23    let show_password = use_signal(|| props.show_password());
24
25    let validate_state = use_signal(|| String::new());
26
27    let need_status_icon = use_signal(|| false);
28
29    let mut is_focused = use_signal(|| false);
30
31    let mut hovering = use_signal(|| false);
32    let mut password_visible = use_signal(|| false);
33
34    let show_clear = use_memo(move || {
35        clearable() && !input_disable() && !read_only() && !input_value().is_empty()
36        // && (is_focused() || hovering())
37    });
38
39    let show_pwd_visible =
40        use_signal(|| show_password() && !input_disable() && input_value().is_empty());
41
42    let is_word_limit_visible = use_signal(|| {
43        show_word_limit()
44            && !!props.max_length.is_some()
45            && (props.type_.as_deref() == Some("text")
46                || props.type_.as_deref() == Some("textarea"))
47            && !input_disable()
48            && !read_only()
49            && !show_password()
50    });
51    let text_length = use_signal(move || input_value().chars().count());
52    let input_exceed =
53        use_signal(|| !!is_word_limit_visible() && (text_length() > props.max_length.unwrap_or(0)));
54
55    let is_suffix = props.suffix.is_some();
56
57    let suffix_visible = use_memo(move || {
58        is_suffix
59            // || !!props.suffix.is_some()
60            || show_clear()
61            || show_password()
62            || is_word_limit_visible()
63            || (validate_state().is_empty() && need_status_icon())
64    });
65
66    // Styles
67    let ns_textarea = UseNamespace::new(Block::Textarea, None);
68    let ns_input = UseNamespace::new(Block::Input, None);
69
70    let container_classes = classes! {
71        if props.type_ == Some("textarea".to_string()) {&ns_textarea.b()} else {&ns_input.b();},
72        &ns_input.m_(input_size()),
73        &ns_input.is_(String::from("disabled"), Some(input_disable())),
74        &ns_input.is_(String::from("exceed"), Some(input_exceed())),
75
76        if props.append.is_some() || props.prepend.is_some() {&ns_input.b_(String::from("group"))},
77        if props.prepend.is_some() || props.prefix.is_some() {&ns_input.m_(String::from("prefix"))},
78        if props.suffix.is_some() || props.suffix.is_some() || clearable() || show_password() {&ns_input.m_(String::from("suffix"))},
79        if show_clear() && show_pwd_visible() {&ns_input.bm_(String::from("suffix"),String::from("password-clear"))},
80
81        if props.append.is_some() { &ns_input.bm_(String::from("group"), String::from("append")) },
82        if props.prepend.is_some() { &ns_input.bm_(String::from("group"), String::from("prepend")) },
83
84        &props.class.as_deref().unwrap_or(""),
85    };
86    let wrapper_classes = classes! {
87        ns_input.e_(String::from("wrapper")),
88        ns_input.is_(String::from("focus"), Some(is_focused()))
89    };
90
91    let textarea_calc_style = format!("");
92    let textarea_style = format!(
93        "{} {} {}",
94        props.input_style.unwrap_or(String::new()),
95        textarea_calc_style,
96        input_resize()
97    );
98
99    rsx! {
100        div {
101            id: input_id,
102            class:container_classes,
103
104            if "textarea" != input_type{
105
106                if props.prepend.is_some() {
107                    div {
108                        class:ns_input.be_(String::from("group"), String::from("prepend")),
109                        {props.prepend}
110                    }
111                }
112                div {
113                    class: wrapper_classes,
114                    // prefix slot
115                    if props.prefix.is_some() || props.prefix_icon.is_some() {
116                        span {
117                            class: ns_input.e_(String::from("prefix")),
118                            span {
119                                class: ns_input.e_(String::from("prefix-inner")),
120                                {props.prefix}
121                                DxcIcon {
122                                    children: spawn_icon(props.prefix_icon.as_deref().unwrap_or("")),
123                                }
124                            }
125                        }
126                    }
127
128                    input {
129                        class: ns_input.e_(String::from("inner")),
130                        name: props.name,
131                        minlength: props.minlength,
132                        maxlength: props.max_length,
133                        r#type: match (show_password(), password_visible()) {
134                            (true, true) => "text".to_string(),
135                            (true, false) => "password".to_string(),
136                            (false, _) => input_type,
137                        },
138                        disabled: input_disable(),
139                        readonly: read_only(),
140                        value: "{input_value()}",
141                        autocomplete: props.auto_complete,
142                        tabindex: props.tab_index,
143                        aria_label: props.aria_label,
144                        placeholder: props.placeholder,
145                        style: props.style,
146                        form: props.form,
147                        autofocus: props.autofocus,
148                        role: props.container_role,
149                        inputmode: props.inputmode,
150                        oninput: move |envent | {
151                            input_value.set(envent.value());
152                            props.oninput.clone().unwrap_or_default();
153                        },
154                        onfocus: move |_| {
155                            is_focused.set(true);
156                            props.onfocus.clone().unwrap_or_default();
157                        },
158                        onblur: move |_| {
159                            is_focused.set(false);
160                            props.onblur.clone().unwrap_or_default();
161                        },
162                        onmouseover: move |_| hovering.set(true),
163                        onmouseleave: move |_| hovering.set(false),
164                        onchange: props.onchange.clone().unwrap_or_default(),
165                        onkeydown: props.onkeydown.clone().unwrap_or_default(),
166                    }
167
168                    // suffix slot
169                    if suffix_visible() || props.suffix_icon.is_some() {
170                        span {
171                            class: ns_input.e_(String::from("suffix")),
172                            span {
173                                class: ns_input.e_(String::from("suffix-inner")),
174
175                                if !show_clear() || !show_pwd_visible() || !is_word_limit_visible() {
176                                    {props.suffix}
177                                    DxcIcon {
178                                        class: ns_input.e_(String::from("icon")),
179                                        children: spawn_icon(props.suffix_icon.as_deref().unwrap_or(""))
180                                    }
181                                }
182
183                                if show_clear() {
184                                    DxcIcon {
185                                        class: format!("{} {}", ns_input.e_(String::from("icon")), ns_input.e_(String::from("clear"))),
186                                        onclick: move |event:MouseEvent| {
187                                            event.prevent_default();
188                                            input_value.set(String::new());
189                                        },
190                                        CircleClose { }
191                                    }
192                                }
193
194                                if show_pwd_visible() {
195                                    DxcIcon {
196                                        class: format!("{} {}", ns_input.e_(String::from("icon")),ns_input.e_(String::from("password"))),
197                                        onclick: move |_| {
198                                            password_visible.set(!password_visible());
199                                        },
200                                        if password_visible() {
201                                            View {}
202                                        } else {
203                                            Hide {}
204                                        },
205                                    }
206                                }
207                            }
208                        }
209                    }
210                }
211
212                // append slot
213                if props.append.is_some(){
214                    div {
215                        class: ns_input.be_(String::from("group"), String::from("append")),
216                        {props.append}
217                    }
218                }
219            } else {
220
221                div {
222                    textarea {
223                        style: textarea_style,
224                    }
225                }
226            }
227        }
228    }
229}