impulse_thaw/textarea/
mod.rs

1mod rule;
2mod types;
3
4pub use rule::*;
5pub use types::*;
6
7use crate::{FieldInjection, Rule};
8use leptos::{ev, html, prelude::*};
9use thaw_utils::{class_list, mount_style, BoxOneCallback, ComponentRef, Model};
10
11#[component]
12pub fn Textarea(
13    #[prop(optional, into)] class: MaybeProp<String>,
14    #[prop(optional, into)] id: MaybeProp<String>,
15    /// A string specifying a name for the input control.
16    /// This name is submitted along with the control's value when the form data is submitted.
17    #[prop(optional, into)]
18    name: MaybeProp<String>,
19    /// The rules to validate Field.
20    #[prop(optional, into)]
21    rules: Vec<TextareaRule>,
22    /// The value of the Textarea.
23    #[prop(optional, into)]
24    value: Model<String>,
25    /// Check the incoming value, if it returns false, input will not be accepted.
26    #[prop(optional, into)]
27    allow_value: Option<BoxOneCallback<String, bool>>,
28    /// Placeholder text for the input.
29    #[prop(optional, into)]
30    placeholder: MaybeProp<String>,
31    /// Callback triggered when the input is focussed on.
32    #[prop(optional, into)]
33    on_focus: Option<BoxOneCallback<ev::FocusEvent>>,
34    /// Callback triggered when the input is blurred.
35    #[prop(optional, into)]
36    on_blur: Option<BoxOneCallback<ev::FocusEvent>>,
37    /// Whether the input is disabled.
38    #[prop(optional, into)]
39    disabled: Signal<bool>,
40    /// Which direction the Textarea is allowed to be resized.
41    #[prop(optional, into)]
42    resize: Signal<TextareaResize>,
43    /// Size of the Textarea.
44    #[prop(optional, into)]
45    size: Signal<TextareaSize>,
46    #[prop(optional)] comp_ref: ComponentRef<TextareaRef>,
47    // #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
48) -> impl IntoView {
49    mount_style("textarea", include_str!("./textarea.css"));
50    let (id, name) = FieldInjection::use_id_and_name(id, name);
51    let validate = Rule::validate(rules, value, name);
52    let value_trigger = ArcTrigger::new();
53    let on_input = {
54        let value_trigger = value_trigger.clone();
55        move |ev| {
56            let input_value = event_target_value(&ev);
57            if let Some(allow_value) = allow_value.as_ref() {
58                if !allow_value(input_value.clone()) {
59                    value_trigger.notify();
60                    return;
61                }
62            }
63            value.set(input_value);
64            validate.run(Some(TextareaRuleTrigger::Input));
65        }
66    };
67    let on_change = move |_| {
68        validate.run(Some(TextareaRuleTrigger::Change));
69    };
70    let on_internal_focus = move |ev| {
71        if let Some(on_focus) = on_focus.as_ref() {
72            on_focus(ev);
73        }
74        validate.run(Some(TextareaRuleTrigger::Focus));
75    };
76    let on_internal_blur = move |ev| {
77        if let Some(on_blur) = on_blur.as_ref() {
78            on_blur(ev);
79        }
80        validate.run(Some(TextareaRuleTrigger::Blur));
81    };
82
83    let textarea_ref = NodeRef::<html::Textarea>::new();
84    comp_ref.load(TextareaRef { textarea_ref });
85
86    view! {
87        <span class=class_list![
88            "thaw-textarea",
89            ("thaw-textarea--disabled", move || disabled.get()),
90            move || format!("thaw-textarea--resize-{}", resize.get().as_str()),
91            move || format!("thaw-textarea--{}", size.get().as_str()),
92            class
93        ]>
94            <textarea
95                prop:value=move || {
96                    value_trigger.track();
97                    value.get()
98                }
99
100                on:input=on_input
101                on:change=on_change
102                on:focus=on_internal_focus
103                on:blur=on_internal_blur
104                class="thaw-textarea__textarea"
105                id=id
106                name=name
107                disabled=move || disabled.get()
108                placeholder=move || placeholder.get()
109                node_ref=textarea_ref
110            ></textarea>
111        </span>
112    }
113}