diff --git a/demo_markdown/docs/input/mod.md b/demo_markdown/docs/input/mod.md
index a9b3356..730c14d 100644
--- a/demo_markdown/docs/input/mod.md
+++ b/demo_markdown/docs/input/mod.md
@@ -7,6 +7,7 @@ view! {
+
}
```
@@ -19,6 +20,7 @@ let value = create_rw_signal(String::from("o"));
view! {
+
}
```
@@ -31,6 +33,7 @@ let value = create_rw_signal(String::from("o"));
view! {
+
}
```
@@ -119,3 +122,7 @@ view! {
| ----- | ----------- | ------------------------ |
| focus | `Fn(&self)` | Focus the input element. |
| blur | `Fn(&self)` | Blur the input element. |
+
+### TextArea Props
+
+Removes variant and slot from Input component.
\ No newline at end of file
diff --git a/thaw/src/input/mod.rs b/thaw/src/input/mod.rs
index 58b5fce..b2b5ef9 100644
--- a/thaw/src/input/mod.rs
+++ b/thaw/src/input/mod.rs
@@ -1,11 +1,14 @@
+mod text_area;
mod theme;
+pub use text_area::{TextArea, TextAreaRef};
+pub use theme::InputTheme;
+
use crate::{
theme::{use_theme, Theme},
utils::{class_list::class_list, mount_style, ComponentRef},
};
use leptos::*;
-pub use theme::InputTheme;
#[derive(Default, Clone)]
pub enum InputVariant {
diff --git a/thaw/src/input/text-area.css b/thaw/src/input/text-area.css
new file mode 100644
index 0000000..ae3f265
--- /dev/null
+++ b/thaw/src/input/text-area.css
@@ -0,0 +1,54 @@
+.thaw-textarea {
+ display: inline-flex;
+ width: 100%;
+ box-sizing: border-box;
+ background-color: var(--thaw-background-color);
+ font-size: 14px;
+ color: var(--thaw-font-color);
+ border: 1px solid var(--thaw-border-color);
+ border-radius: var(--thaw-border-radius);
+ cursor: text;
+ transition: all 0.3s;
+}
+
+.thaw-textarea--focus,
+.thaw-textarea:hover:not(.thaw-textarea--disabled, .thaw-textarea--invalid) {
+ border-color: var(--thaw-border-color-hover);
+}
+
+.thaw-textarea--disabled,
+.thaw-textarea--disabled .thaw-textarea__textarea-el {
+ cursor: not-allowed;
+ background-color: var(--thaw-background-color-disabled);
+ color: var(--thaw-font-color-disabled);
+}
+
+.thaw-textarea--invalid {
+ border-color: var(--thaw-border-color-error);
+}
+
+.thaw-textarea--focus:not(.thaw-textarea--invalid) {
+ box-shadow: 0 0 0 2px var(--thaw-box-shadow-color);
+}
+
+.thaw-textarea--focus.thaw-textarea--invalid {
+ box-shadow: 0 0 0 2px var(--thaw-box-shadow-color-invalid);
+}
+
+.thaw-textarea__textarea-el {
+ width: 100%;
+ height: 100%;
+ min-height: 34px;
+ padding: 5px 10px;
+ background-color: transparent !important;
+ color: var(--thaw-font-color);
+ font-size: inherit;
+ line-height: 1.2;
+ border: none;
+ outline: none;
+ resize: vertical;
+}
+
+.thaw-textarea__textarea-el::placeholder {
+ color: var(--thaw-placeholder-color);
+}
diff --git a/thaw/src/input/text_area.rs b/thaw/src/input/text_area.rs
new file mode 100644
index 0000000..e476c43
--- /dev/null
+++ b/thaw/src/input/text_area.rs
@@ -0,0 +1,152 @@
+use crate::{
+ theme::{use_theme, Theme},
+ utils::{class_list::class_list, mount_style, ComponentRef},
+};
+use leptos::*;
+
+#[component]
+pub fn TextArea(
+ #[prop(optional, into)] value: RwSignal,
+ #[prop(optional, into)] allow_value: Option>,
+ #[prop(optional, into)] placeholder: MaybeSignal,
+ #[prop(optional, into)] on_focus: Option>,
+ #[prop(optional, into)] on_blur: Option>,
+ #[prop(optional, into)] disabled: MaybeSignal,
+ #[prop(optional, into)] invalid: MaybeSignal,
+ #[prop(optional)] comp_ref: ComponentRef,
+ #[prop(optional, into)] class: MaybeSignal,
+ #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
+) -> impl IntoView {
+ let theme = use_theme(Theme::light);
+ mount_style("text-area", include_str!("./text-area.css"));
+
+ let value_trigger = create_trigger();
+ let on_input = move |ev| {
+ let input_value = event_target_value(&ev);
+ if let Some(allow_value) = allow_value.as_ref() {
+ if !allow_value.call(input_value.clone()) {
+ value_trigger.notify();
+ return;
+ }
+ }
+ value.set(input_value);
+ };
+ let is_focus = create_rw_signal(false);
+ let on_internal_focus = move |ev| {
+ is_focus.set(true);
+ if let Some(on_focus) = on_focus.as_ref() {
+ on_focus.call(ev);
+ }
+ };
+ let on_internal_blur = move |ev| {
+ is_focus.set(false);
+ if let Some(on_blur) = on_blur.as_ref() {
+ on_blur.call(ev);
+ }
+ };
+
+ let css_vars = create_memo(move |_| {
+ let mut css_vars = String::new();
+ theme.with(|theme| {
+ let border_color_hover = theme.common.color_primary.clone();
+ css_vars.push_str(&format!("--thaw-border-color-hover: {border_color_hover};"));
+ css_vars.push_str(&format!("--thaw-box-shadow-color: {border_color_hover}33;"));
+ let border_radius = theme.common.border_radius.clone();
+ css_vars.push_str(&format!("--thaw-border-radius: {border_radius};"));
+ css_vars.push_str(&format!(
+ "--thaw-background-color: {};",
+ theme.input.background_color
+ ));
+ css_vars.push_str(&format!("--thaw-font-color: {};", theme.input.font_color));
+ css_vars.push_str(&format!(
+ "--thaw-border-color: {};",
+ theme.input.border_color
+ ));
+ css_vars.push_str(&format!(
+ "--thaw-border-color-error: {};",
+ theme.common.color_error
+ ));
+ css_vars.push_str(&format!(
+ "--thaw-placeholder-color: {};",
+ theme.input.placeholder_color
+ ));
+ css_vars.push_str(&format!(
+ "--thaw-background-color-disabled: {};",
+ theme.input.background_color_disabled
+ ));
+ css_vars.push_str(&format!(
+ "--thaw-font-color-disabled: {};",
+ theme.input.font_color_disabled
+ ));
+ css_vars.push_str(&format!(
+ "--thaw-box-shadow-color-invalid: {}33;",
+ theme.common.color_error
+ ));
+ });
+ css_vars
+ });
+ let textarea_ref = create_node_ref::();
+ textarea_ref.on_load(move |_| {
+ comp_ref.load(TextAreaRef { textarea_ref });
+ });
+
+ #[cfg(debug_assertions)]
+ {
+ const INNER_ATTRS: [&'static str; 3] = ["class", "disabled", "placeholder"];
+ attrs.iter().for_each(|attr| {
+ if INNER_ATTRS.contains(&attr.0) {
+ logging::warn!(
+ "Thaw: The '{}' attribute already exists on elements inside the TextArea component, which may cause conflicts.",
+ attr.0
+ );
+ }
+ });
+ }
+
+ view! {
+
+
+
+ }
+}
+
+#[derive(Clone)]
+pub struct TextAreaRef {
+ textarea_ref: NodeRef,
+}
+
+impl TextAreaRef {
+ pub fn focus(&self) {
+ if let Some(textarea_el) = self.textarea_ref.get_untracked() {
+ _ = textarea_el.focus();
+ }
+ }
+
+ pub fn blur(&self) {
+ if let Some(textarea_el) = self.textarea_ref.get_untracked() {
+ _ = textarea_el.blur();
+ }
+ }
+}