diff --git a/demo_markdown/docs/textarea/mod.md b/demo_markdown/docs/textarea/mod.md
index e1c6417..bf8160f 100644
--- a/demo_markdown/docs/textarea/mod.md
+++ b/demo_markdown/docs/textarea/mod.md
@@ -22,5 +22,18 @@ view! {
}
```
+### Resize
+
+```rust demo
+view! {
+
+
+
+}
+```
+
### Textarea Props
diff --git a/thaw/src/input/text-area.css b/thaw/src/input/text-area.css
deleted file mode 100644
index ae3f265..0000000
--- a/thaw/src/input/text-area.css
+++ /dev/null
@@ -1,54 +0,0 @@
-.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
deleted file mode 100644
index 2b40111..0000000
--- a/thaw/src/input/text_area.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use crate::theme::{use_theme, Theme};
-use leptos::*;
-use thaw_utils::{class_list, mount_style, ComponentRef, Model, OptionalProp};
-
-#[component]
-pub fn TextArea(
- #[prop(optional, into)] value: Model,
- #[prop(optional, into)] allow_value: Option>,
- #[prop(optional, into)] placeholder: OptionalProp>,
- #[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: OptionalProp>,
- #[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: [&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();
- }
- }
-}
diff --git a/thaw/src/textarea/mod.rs b/thaw/src/textarea/mod.rs
index ab7515c..d114405 100644
--- a/thaw/src/textarea/mod.rs
+++ b/thaw/src/textarea/mod.rs
@@ -1,22 +1,23 @@
use leptos::*;
-use thaw_utils::{class_list, mount_style, ComponentRef, Model, OptionalProp};
+use thaw_utils::{class_list, mount_style, ComponentRef, Model};
#[component]
pub fn Textarea(
#[prop(optional, into)] value: Model,
#[prop(optional, into)] allow_value: Option>,
- #[prop(optional, into)] placeholder: OptionalProp>,
+ #[prop(optional, into)] placeholder: MaybeProp,
#[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: OptionalProp>,
+ /// Which direction the Textarea is allowed to be resized.
+ #[prop(optional, into)] resize: MaybeSignal,
+ #[prop(optional)] comp_ref: ComponentRef,
+ #[prop(optional, into)] class: MaybeProp,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView {
mount_style("textarea", include_str!("./textarea.css"));
- let value_trigger = create_trigger();
+ let value_trigger = Trigger::new();
let on_input = move |ev| {
let input_value = event_target_value(&ev);
if let Some(allow_value) = allow_value.as_ref() {
@@ -27,23 +28,20 @@ pub fn Textarea(
}
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 textarea_ref = create_node_ref::();
+ let textarea_ref = NodeRef::::new();
textarea_ref.on_load(move |_| {
- comp_ref.load(TextAreaRef { textarea_ref });
+ comp_ref.load(TextareaRef { textarea_ref });
});
#[cfg(debug_assertions)]
@@ -62,9 +60,10 @@ pub fn Textarea(
view! {
@@ -87,11 +86,11 @@ pub fn Textarea(
}
#[derive(Clone)]
-pub struct TextAreaRef {
+pub struct TextareaRef {
textarea_ref: NodeRef,
}
-impl TextAreaRef {
+impl TextareaRef {
pub fn focus(&self) {
if let Some(textarea_el) = self.textarea_ref.get_untracked() {
_ = textarea_el.focus();
@@ -104,3 +103,23 @@ impl TextAreaRef {
}
}
}
+
+#[derive(Clone, Default)]
+pub enum TextareaResize {
+ #[default]
+ None,
+ Both,
+ Horizontal,
+ Vertical,
+}
+
+impl TextareaResize {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ TextareaResize::None => "none",
+ TextareaResize::Both => "both",
+ TextareaResize::Horizontal => "horizontal",
+ TextareaResize::Vertical => "vertical",
+ }
+ }
+}
diff --git a/thaw/src/textarea/textarea.css b/thaw/src/textarea/textarea.css
index c2341e0..f0bafdc 100644
--- a/thaw/src/textarea/textarea.css
+++ b/thaw/src/textarea/textarea.css
@@ -5,8 +5,8 @@
padding: 0 0 var(--strokeWidthThick) 0;
background-color: var(--colorNeutralBackground1);
border-radius: var(--borderRadiusMedium);
- border-bottom-color: var(--colorNeutralStrokeAccessible);
border: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
+ border-bottom-color: var(--colorNeutralStrokeAccessible);
box-sizing: border-box;
}
@@ -28,18 +28,6 @@
border-bottom-color: var(--colorNeutralStrokeAccessiblePressed);
}
-/* .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::after {
content: "";
position: absolute;
@@ -93,3 +81,30 @@
.thaw-textarea__textarea-el::placeholder {
color: var(--thaw-placeholder-color);
}
+
+.thaw-textarea.thaw-textarea--disabled {
+ background-color: var(--colorTransparentBackground);
+ border: var(--strokeWidthThin) solid var(--colorNeutralStrokeDisabled);
+}
+
+.thaw-textarea--disabled > .thaw-textarea__textarea {
+ background-color: var(--colorTransparentBackground);
+ color: var(--colorNeutralForegroundDisabled);
+ cursor: not-allowed;
+}
+
+.thaw-textarea--disabled > .thaw-textarea__textarea::placeholder {
+ color: var(--colorNeutralForegroundDisabled);
+}
+
+.thaw-textarea--resize-vertical > .thaw-textarea__textarea {
+ resize: vertical;
+}
+
+.thaw-textarea--resize-horizontal > .thaw-textarea__textarea {
+ resize: horizontal;
+}
+
+.thaw-textarea--resize-both > .thaw-textarea__textarea {
+ resize: both;
+}