use crate::class::Class;
use dioxus::prelude::*;
use std::mem;
use zino_core::SharedString;
pub fn Textarea(props: TextareaProps) -> Element {
let mut composing = use_signal(|| false);
let mut cached_value = use_signal(String::new);
rsx! {
textarea {
id: if let Some(id) = props.id { "{id}" },
class: "{props.class}",
class: if !props.color.is_empty() { "is-{props.color}" },
class: if !props.size.is_empty() { "is-{props.size}" },
class: if !props.state.is_empty() { "is-{props.state}" },
value: if let Some(value) = props.initial_value { "{value}" },
onmounted: move |event| {
if props.auto_focus {
spawn(async move {
if let Err(err) = event.data.set_focus(true).await {
tracing::error!("fail to focus on the textarea: {err}");
}
});
}
},
onchange: move |event| {
if let Some(handler) = props.on_change.as_ref() {
handler.call(event.value());
}
},
oninput: move |event| {
cached_value.set(event.value());
if !composing()
&& let Some(handler) = props.on_input.as_ref() {
handler.call(mem::take(&mut cached_value.write()));
}
},
onkeydown: move |event| {
if let Some(handler) = props.on_keydown.as_ref() {
event.stop_propagation();
if !composing() {
handler.call(event);
}
}
},
oncompositionstart: move |_event| {
composing.set(true);
},
oncompositionend: move |_event| {
composing.set(false);
if let Some(handler) = props.on_input.as_ref() {
handler.call(mem::take(&mut cached_value.write()));
}
},
..props.attributes,
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct TextareaProps {
#[props(into, default)]
pub id: Option<String>,
#[props(into, default = "textarea")]
pub class: Class,
#[props(into, default)]
pub color: SharedString,
#[props(into, default)]
pub size: SharedString,
#[props(into, default)]
pub state: SharedString,
#[props(default)]
pub auto_focus: bool,
#[props(into, default)]
pub initial_value: Option<String>,
pub on_change: Option<EventHandler<String>>,
pub on_input: Option<EventHandler<String>>,
pub on_keydown: Option<EventHandler<KeyboardEvent>>,
#[props(extends = textarea)]
attributes: Vec<Attribute>,
}