patternfly_yew/components/form/
area.rs1use crate::prelude::{
2 focus, use_on_text_change, AsClasses, ExtendClasses, InputState, ValidatingComponent,
3 ValidatingComponentProperties, ValidationContext,
4};
5
6use std::fmt::{Display, Formatter};
7use yew::prelude::*;
8
9#[derive(Clone, Default, PartialEq, Eq)]
14pub enum ResizeOrientation {
15 Horizontal,
16 Vertical,
17 #[default]
18 Both,
19}
20
21impl AsClasses for ResizeOrientation {
22 fn extend_classes(&self, classes: &mut Classes) {
23 match self {
24 ResizeOrientation::Horizontal => classes.push("pf-m-resize-horizontal"),
25 ResizeOrientation::Vertical => classes.push("pf-m-resize-vertical"),
26 ResizeOrientation::Both => classes.push("pf-m-resize-both"),
27 }
28 }
29}
30
31#[derive(Clone, Default, PartialEq, Eq)]
32pub enum Wrap {
33 Hard,
34 #[default]
35 Soft,
36 Off,
37}
38
39impl Display for Wrap {
40 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Self::Off => f.write_str("off"),
43 Self::Soft => f.write_str("soft"),
44 Self::Hard => f.write_str("hard"),
45 }
46 }
47}
48
49#[derive(Clone, PartialEq, Properties)]
51pub struct TextAreaProperties {
52 #[prop_or_default]
53 pub name: Option<AttrValue>,
54 #[prop_or_default]
55 pub id: Option<AttrValue>,
56 #[prop_or_default]
57 pub value: String,
58 #[prop_or_default]
59 pub required: bool,
60 #[prop_or_default]
61 pub disabled: bool,
62 #[prop_or_default]
63 pub readonly: bool,
64 #[prop_or_default]
65 pub state: InputState,
66 #[prop_or_default]
67 pub placeholder: Option<AttrValue>,
68 #[prop_or_default]
69 pub autofocus: bool,
70 #[prop_or_default]
71 pub form: Option<AttrValue>,
72 #[prop_or_default]
73 pub autocomplete: Option<AttrValue>,
74
75 #[prop_or_default]
76 pub spellcheck: Option<AttrValue>,
77 #[prop_or_default]
78 pub wrap: Wrap,
79
80 #[prop_or_default]
81 pub rows: Option<usize>,
82 #[prop_or_default]
83 pub cols: Option<usize>,
84
85 #[prop_or_default]
86 pub resize: ResizeOrientation,
87
88 #[prop_or_default]
93 pub onchange: Callback<String>,
94 #[prop_or_default]
98 pub oninput: Callback<InputEvent>,
99 #[prop_or_default]
101 pub onvalidate: Callback<ValidationContext<String>>,
102
103 #[prop_or_default]
104 pub r#ref: NodeRef,
105}
106
107impl ValidatingComponent for TextArea {
108 type Value = String;
109}
110
111impl ValidatingComponentProperties<String> for TextAreaProperties {
112 fn set_onvalidate(&mut self, onvalidate: Callback<ValidationContext<String>>) {
113 self.onvalidate = onvalidate;
114 }
115
116 fn set_input_state(&mut self, state: InputState) {
117 self.state = state;
118 }
119}
120
121#[function_component(TextArea)]
158pub fn text_area(props: &TextAreaProperties) -> Html {
159 let input_ref = props.r#ref.clone();
160 let mut classes = classes!("pf-v5-c-form-control");
161
162 classes.extend_from(&props.resize);
163
164 if props.readonly {
165 classes.push("pf-m-readonly");
166 }
167
168 {
171 let value = props.value.clone();
172 let onvalidate = props.onvalidate.clone();
173 use_effect_with((), move |()| {
174 onvalidate.emit(ValidationContext {
175 value,
176 initial: true,
177 });
178 });
179 }
180
181 let (classes, aria_invalid) = props.state.convert(classes);
182
183 {
186 let input_ref = input_ref.clone();
187 use_effect_with(props.autofocus, move |autofocus| {
188 if *autofocus {
189 focus(&input_ref)
190 }
191 });
192 }
193
194 let onchange = use_callback(
197 (props.onchange.clone(), props.onvalidate.clone()),
198 |new_value: String, (onchange, onvalidate)| {
199 onchange.emit(new_value.clone());
200 onvalidate.emit(new_value.into());
201 },
202 );
203 let oninput = use_on_text_change(input_ref.clone(), props.oninput.clone(), onchange);
204
205 html!(
206 <div class={classes}>
207 <textarea
208 ref={input_ref}
209 name={&props.name}
210 id={&props.id}
211 required={props.required}
212 disabled={props.disabled}
213 readonly={props.readonly}
214 aria-invalid={aria_invalid.to_string()}
215 value={props.value.clone()}
216 placeholder={&props.placeholder}
217 form={&props.form}
218 autocomplete={&props.autocomplete}
219
220 cols={props.cols.as_ref().map(|v|v.to_string())}
221 rows={props.rows.as_ref().map(|v|v.to_string())}
222
223 wrap={props.wrap.to_string()}
224 spellcheck={&props.spellcheck}
225
226 {oninput}
227 />
228 if props.state != InputState::Default {
229 <div class="pf-v5-c-form-control__utilities">
230 <div class="pf-v5-c-form-control__icon pf-m-status">
231 {props.state.icon()}
232 </div>
233 </div>
234 }
235 </div>
236 )
237}