cabin/html/elements/
textarea.rs

1use std::borrow::Cow;
2use std::fmt;
3
4use cabin_macros::Attribute;
5
6use super::button::{Disabled, Form, Name};
7use super::common::Common;
8use super::global::Global;
9use super::input::{
10    AutoComplete, Dirname, MaxLength, MinLength, OnChange, OnInput, Placeholder, ReadOnly, Required,
11};
12use crate::error::InternalError;
13use crate::html::attributes::{Attributes, WithAttribute};
14use crate::html::events::InputEvent;
15use crate::html::{Aria, Html};
16use crate::View;
17
18/// The `textarea` element represents a multiline plain text edit control for the element's raw
19/// value. The contents of the control represent the control's default value.
20pub fn textarea(content: impl Into<Cow<'static, str>>) -> Html<marker::Textarea, (), impl View> {
21    Html::new("textarea", (), content.into())
22}
23
24pub mod marker {
25    pub struct Textarea;
26}
27
28impl<A: Attributes, V: 'static> Textarea for Html<marker::Textarea, A, V> {}
29impl<A: Attributes, V: 'static> Common for Html<marker::Textarea, A, V> {}
30impl<A: Attributes, V: 'static> Global for Html<marker::Textarea, A, V> {}
31impl<A: Attributes, V: 'static> Aria for Html<marker::Textarea, A, V> {}
32
33/// The `textarea` element represents a multiline plain text edit control for the element's raw
34/// value. The contents of the control represent the control's default value.
35pub trait Textarea: WithAttribute {
36    /// Hint for form autofill feature.
37    fn autocomplete(self, autocomplete: AutoComplete) -> Self::Output<AutoComplete> {
38        self.with_attribute(autocomplete)
39    }
40
41    /// Maximum number of characters per line.
42    fn cols(self, cols: u32) -> Self::Output<Cols> {
43        self.with_attribute(Cols(cols))
44    }
45
46    /// Name of form control to use for sending the element's directionality in form submission.
47    fn dirname(self, dirname: impl Into<Cow<'static, str>>) -> Self::Output<Dirname> {
48        self.with_attribute(Dirname(dirname.into()))
49    }
50
51    /// Whether the form control is disabled.
52    fn disabled(self) -> Self::Output<Disabled> {
53        self.with_disabled(true)
54    }
55
56    /// Whether the form control is disabled.
57    fn with_disabled(self, disabled: bool) -> Self::Output<Disabled> {
58        self.with_attribute(Disabled(disabled))
59    }
60
61    /// Associates the element with a [super::form] element.
62    fn form(self, form: impl Into<Cow<'static, str>>) -> Self::Output<Form> {
63        self.with_attribute(Form(form.into()))
64    }
65
66    /// Maximum length of value.
67    fn max_length(self, max_length: i32) -> Self::Output<MaxLength> {
68        self.with_attribute(MaxLength(max_length))
69    }
70
71    /// Minimum length of value.
72    fn min_length(self, min_length: i32) -> Self::Output<MinLength> {
73        self.with_attribute(MinLength(min_length))
74    }
75
76    /// Name of the element to use for form submission.
77    fn name(self, name: impl Into<Cow<'static, str>>) -> Self::Output<Name> {
78        self.with_attribute(Name(name.into()))
79    }
80
81    /// User-visible label to be placed within the form control.
82    fn placeholder(self, placeholder: impl Into<Cow<'static, str>>) -> Self::Output<Placeholder> {
83        self.with_attribute(Placeholder(placeholder.into()))
84    }
85
86    /// Whether to allow the value to be edited by the user.
87    fn read_only(self) -> Self::Output<ReadOnly> {
88        self.with_read_only(true)
89    }
90
91    /// Whether to allow the value to be edited by the user.
92    fn with_read_only(self, read_only: bool) -> Self::Output<ReadOnly> {
93        self.with_attribute(ReadOnly(read_only))
94    }
95
96    /// Whether the control is required for form submission.
97    fn required(self) -> Self::Output<Required> {
98        self.with_required(true)
99    }
100
101    /// Whether the control is required for form submission.
102    fn with_required(self, required: bool) -> Self::Output<Required> {
103        self.with_attribute(Required(required))
104    }
105
106    /// Number of lines to show.
107    fn rows(self, rows: u32) -> Self::Output<Rows> {
108        self.with_attribute(Rows(rows))
109    }
110
111    /// How the value of the form control is to be wrapped for form submission.
112    fn wrap(self, wrap: Wrap) -> Self::Output<Wrap> {
113        self.with_attribute(wrap)
114    }
115
116    /// How the value of the form control is to be wrapped for form submission.
117    fn wrap_hard(self) -> Self::Output<Wrap> {
118        self.with_attribute(Wrap::Hard)
119    }
120
121    fn on_input<E>(self, event: impl FnOnce(InputEvent) -> E) -> Self::Output<OnInput>
122    where
123        E: ::serde::Serialize + 'static,
124    {
125        let event = event(InputEvent::default());
126        self.with_attribute(OnInput(Box::new(move || {
127            use std::hash::{Hash, Hasher};
128
129            let mut hasher = twox_hash::XxHash32::default();
130            std::any::TypeId::of::<E>().hash(&mut hasher);
131            let hash = hasher.finish() as u32;
132            serde_json::to_string(&event)
133                .map_err(|err| InternalError::Serialize {
134                    what: "on_input event",
135                    err,
136                })
137                .map(|json| (hash, json))
138        })))
139    }
140
141    fn on_change<E>(self, event: impl FnOnce(InputEvent) -> E) -> Self::Output<OnChange>
142    where
143        E: ::serde::Serialize + 'static,
144    {
145        let event = event(InputEvent::default());
146        self.with_attribute(OnChange(Box::new(move || {
147            use std::hash::{Hash, Hasher};
148
149            let mut hasher = twox_hash::XxHash32::default();
150            std::any::TypeId::of::<E>().hash(&mut hasher);
151            let hash = hasher.finish() as u32;
152            serde_json::to_string(&event)
153                .map_err(|err| InternalError::Serialize {
154                    what: "on_change event",
155                    err,
156                })
157                .map(|json| (hash, json))
158        })))
159    }
160}
161
162/// Maximum number of characters per line.
163#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Attribute)]
164pub struct Cols(pub u32);
165
166/// Number of lines to show.
167#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Attribute)]
168pub struct Rows(pub u32);
169
170/// Data type of an input element.
171#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Attribute)]
172pub enum Wrap {
173    /// The text is not to be wrapped when it is submitted.
174    #[default]
175    Soft,
176    /// The text is to have newlines added by the user agent so that the text is wrapped when it is
177    /// submitted.
178    Hard,
179}
180
181impl fmt::Display for Wrap {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        f.write_str(match self {
184            Wrap::Soft => "soft",
185            Wrap::Hard => "hard",
186        })
187    }
188}