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
18pub 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
33pub trait Textarea: WithAttribute {
36 fn autocomplete(self, autocomplete: AutoComplete) -> Self::Output<AutoComplete> {
38 self.with_attribute(autocomplete)
39 }
40
41 fn cols(self, cols: u32) -> Self::Output<Cols> {
43 self.with_attribute(Cols(cols))
44 }
45
46 fn dirname(self, dirname: impl Into<Cow<'static, str>>) -> Self::Output<Dirname> {
48 self.with_attribute(Dirname(dirname.into()))
49 }
50
51 fn disabled(self) -> Self::Output<Disabled> {
53 self.with_disabled(true)
54 }
55
56 fn with_disabled(self, disabled: bool) -> Self::Output<Disabled> {
58 self.with_attribute(Disabled(disabled))
59 }
60
61 fn form(self, form: impl Into<Cow<'static, str>>) -> Self::Output<Form> {
63 self.with_attribute(Form(form.into()))
64 }
65
66 fn max_length(self, max_length: i32) -> Self::Output<MaxLength> {
68 self.with_attribute(MaxLength(max_length))
69 }
70
71 fn min_length(self, min_length: i32) -> Self::Output<MinLength> {
73 self.with_attribute(MinLength(min_length))
74 }
75
76 fn name(self, name: impl Into<Cow<'static, str>>) -> Self::Output<Name> {
78 self.with_attribute(Name(name.into()))
79 }
80
81 fn placeholder(self, placeholder: impl Into<Cow<'static, str>>) -> Self::Output<Placeholder> {
83 self.with_attribute(Placeholder(placeholder.into()))
84 }
85
86 fn read_only(self) -> Self::Output<ReadOnly> {
88 self.with_read_only(true)
89 }
90
91 fn with_read_only(self, read_only: bool) -> Self::Output<ReadOnly> {
93 self.with_attribute(ReadOnly(read_only))
94 }
95
96 fn required(self) -> Self::Output<Required> {
98 self.with_required(true)
99 }
100
101 fn with_required(self, required: bool) -> Self::Output<Required> {
103 self.with_attribute(Required(required))
104 }
105
106 fn rows(self, rows: u32) -> Self::Output<Rows> {
108 self.with_attribute(Rows(rows))
109 }
110
111 fn wrap(self, wrap: Wrap) -> Self::Output<Wrap> {
113 self.with_attribute(wrap)
114 }
115
116 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#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Attribute)]
164pub struct Cols(pub u32);
165
166#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Attribute)]
168pub struct Rows(pub u32);
169
170#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Attribute)]
172pub enum Wrap {
173 #[default]
175 Soft,
176 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}