dxc_components/input/
input.rs1use 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 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 });
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 || show_clear()
61 || show_password()
62 || is_word_limit_visible()
63 || (validate_state().is_empty() && need_status_icon())
64 });
65
66 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 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 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 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}