mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
feat: add TextArea component (#77)
This commit is contained in:
parent
4aeab59e6f
commit
559add3a3c
4 changed files with 217 additions and 1 deletions
|
@ -7,6 +7,7 @@ view! {
|
|||
<Space vertical=true>
|
||||
<Input value/>
|
||||
<Input value variant=InputVariant::Password placeholder="Password"/>
|
||||
<TextArea value placeholder="Textarea"/>
|
||||
</Space>
|
||||
}
|
||||
```
|
||||
|
@ -19,6 +20,7 @@ let value = create_rw_signal(String::from("o"));
|
|||
view! {
|
||||
<Space vertical=true>
|
||||
<Input value disabled=true/>
|
||||
<TextArea value disabled=true/>
|
||||
</Space>
|
||||
}
|
||||
```
|
||||
|
@ -31,6 +33,7 @@ let value = create_rw_signal(String::from("o"));
|
|||
view! {
|
||||
<Space vertical=true>
|
||||
<Input value invalid=true/>
|
||||
<TextArea value invalid=true/>
|
||||
</Space>
|
||||
}
|
||||
```
|
||||
|
@ -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.
|
|
@ -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 {
|
||||
|
|
54
thaw/src/input/text-area.css
Normal file
54
thaw/src/input/text-area.css
Normal file
|
@ -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);
|
||||
}
|
152
thaw/src/input/text_area.rs
Normal file
152
thaw/src/input/text_area.rs
Normal file
|
@ -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<String>,
|
||||
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
|
||||
#[prop(optional, into)] placeholder: MaybeSignal<String>,
|
||||
#[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>,
|
||||
#[prop(optional, into)] on_blur: Option<Callback<ev::FocusEvent>>,
|
||||
#[prop(optional, into)] disabled: MaybeSignal<bool>,
|
||||
#[prop(optional, into)] invalid: MaybeSignal<bool>,
|
||||
#[prop(optional)] comp_ref: ComponentRef<TextAreaRef>,
|
||||
#[prop(optional, into)] class: MaybeSignal<String>,
|
||||
#[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::<html::Textarea>();
|
||||
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! {
|
||||
<div
|
||||
class=class_list![
|
||||
"thaw-textarea", ("thaw-textarea--focus", move || is_focus.get()),
|
||||
("thaw-textarea--disabled", move || disabled.get()), ("thaw-textarea--invalid", move ||
|
||||
invalid.get()), move || class.get()
|
||||
]
|
||||
|
||||
style=move || css_vars.get()
|
||||
>
|
||||
<textarea
|
||||
{..attrs}
|
||||
prop:value=move || {
|
||||
value_trigger.track();
|
||||
value.get()
|
||||
}
|
||||
|
||||
on:input=on_input
|
||||
on:focus=on_internal_focus
|
||||
on:blur=on_internal_blur
|
||||
class="thaw-textarea__textarea-el"
|
||||
disabled=move || disabled.get()
|
||||
placeholder=move || placeholder.get()
|
||||
ref=textarea_ref
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextAreaRef {
|
||||
textarea_ref: NodeRef<html::Textarea>,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue