ported use_textarea_autosize

This commit is contained in:
Maccesch 2024-09-15 22:30:38 +02:00
parent 406b7b2e16
commit 49f89bcb36
6 changed files with 64 additions and 73 deletions

View file

@ -3,6 +3,10 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.14.0-beta4] - 2024-09-15
- Latest changes from version 0.13.4 and 0.13.5 ported
## [0.14.0-beta3] - 2024-09-02 ## [0.14.0-beta3] - 2024-09-02
### Breaking Changes 🛠 ### Breaking Changes 🛠

View file

@ -1,6 +1,6 @@
[package] [package]
name = "leptos-use" name = "leptos-use"
version = "0.14.0-beta3" version = "0.14.0-beta4"
edition = "2021" edition = "2021"
authors = ["Marc-Stefan Cassola"] authors = ["Marc-Stefan Cassola"]
categories = ["gui", "web-programming", "wasm"] categories = ["gui", "web-programming", "wasm"]
@ -27,7 +27,7 @@ http0_2 = { version = "0.2", optional = true, package = "http" }
js-sys = "0.3" js-sys = "0.3"
lazy_static = "1" lazy_static = "1"
leptos = "0.7.0-beta5" leptos = "0.7.0-beta5"
leptos_axum = { version = "0.7.0-beta5", optional = true, } leptos_axum = { version = "0.7.0-beta5", optional = true }
leptos_actix = { version = "0.7.0-beta5", optional = true } leptos_actix = { version = "0.7.0-beta5", optional = true }
leptos-spin = { version = "0.2", optional = true } leptos-spin = { version = "0.2", optional = true }
num = { version = "0.4", optional = true } num = { version = "0.4", optional = true }

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
leptos = { version = "0.6", features = ["nightly", "csr"] } leptos = { workspace = true, features = ["nightly", "csr"] }
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
console_log = "1" console_log = "1"
log = "0.4" log = "0.4"

View file

@ -1,10 +1,10 @@
use leptos::*; use leptos::prelude::*;
use leptos_use::docs::demo_or_body; use leptos_use::docs::demo_or_body;
use leptos_use::{use_textarea_autosize, UseTextareaAutosizeReturn}; use leptos_use::{use_textarea_autosize, UseTextareaAutosizeReturn};
#[component] #[component]
fn Demo() -> impl IntoView { fn Demo() -> impl IntoView {
let textarea = create_node_ref::<html::Textarea>(); let textarea = NodeRef::new();
let UseTextareaAutosizeReturn { let UseTextareaAutosizeReturn {
content, content,
@ -15,7 +15,7 @@ fn Demo() -> impl IntoView {
view! { view! {
<div class="mb-4">Type, the textarea will grow:</div> <div class="mb-4">Type, the textarea will grow:</div>
<textarea <textarea
value=content prop:value=content
on:input=move |evt| set_content.set(event_target_value(&evt)) on:input=move |evt| set_content.set(event_target_value(&evt))
node_ref=textarea node_ref=textarea
class="resize-none box-border" class="resize-none box-border"
@ -28,7 +28,9 @@ fn main() {
_ = console_log::init_with_level(log::Level::Debug); _ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
mount_to(demo_or_body(), || { let unmount_handle = leptos::mount::mount_to(demo_or_body(), || {
view! { <Demo/> } view! { <Demo/> }
}) });
unmount_handle.forget();
} }

View file

@ -1,7 +1,7 @@
use crate::core::{ElementMaybeSignal, MaybeRwSignal}; use crate::core::{ElementMaybeSignal, IntoElementMaybeSignal, MaybeRwSignal};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::*; use leptos::prelude::*;
use std::rc::Rc; use std::sync::Arc;
/// Automatically update the height of a textarea depending on the content. /// Automatically update the height of a textarea depending on the content.
/// ///
@ -14,13 +14,13 @@ use std::rc::Rc;
/// ### Simple example /// ### Simple example
/// ///
/// ``` /// ```
/// # use leptos::*; /// # use leptos::prelude::*;
/// # use leptos::html::Textarea; /// # use leptos::html::Textarea;
/// # use leptos_use::{use_textarea_autosize, UseTextareaAutosizeReturn}; /// # use leptos_use::{use_textarea_autosize, UseTextareaAutosizeReturn};
/// # /// #
/// # #[component] /// # #[component]
/// # fn Demo() -> impl IntoView { /// # fn Demo() -> impl IntoView {
/// let textarea = create_node_ref::<Textarea>(); /// let textarea = NodeRef::new();
/// ///
/// let UseTextareaAutosizeReturn { /// let UseTextareaAutosizeReturn {
/// content, /// content,
@ -30,7 +30,7 @@ use std::rc::Rc;
/// ///
/// view! { /// view! {
/// <textarea /// <textarea
/// value=content /// prop:value=content
/// on:input=move |evt| set_content.set(event_target_value(&evt)) /// on:input=move |evt| set_content.set(event_target_value(&evt))
/// node_ref=textarea /// node_ref=textarea
/// class="resize-none" /// class="resize-none"
@ -62,13 +62,13 @@ use std::rc::Rc;
/// `style_prop` option to `"min-height"`. /// `style_prop` option to `"min-height"`.
/// ///
/// ``` /// ```
/// # use leptos::*; /// # use leptos::prelude::*;
/// # use leptos::html::Textarea; /// # use leptos::html::Textarea;
/// # use leptos_use::{use_textarea_autosize_with_options, UseTextareaAutosizeOptions, UseTextareaAutosizeReturn}; /// # use leptos_use::{use_textarea_autosize_with_options, UseTextareaAutosizeOptions, UseTextareaAutosizeReturn};
/// # /// #
/// # #[component] /// # #[component]
/// # fn Demo() -> impl IntoView { /// # fn Demo() -> impl IntoView {
/// let textarea = create_node_ref::<Textarea>(); /// let textarea = NodeRef::new();
/// ///
/// let UseTextareaAutosizeReturn { /// let UseTextareaAutosizeReturn {
/// content, /// content,
@ -81,7 +81,7 @@ use std::rc::Rc;
/// ///
/// view! { /// view! {
/// <textarea /// <textarea
/// value=content /// prop:value=content
/// on:input=move |evt| set_content.set(event_target_value(&evt)) /// on:input=move |evt| set_content.set(event_target_value(&evt))
/// node_ref=textarea /// node_ref=textarea
/// class="resize-none" /// class="resize-none"
@ -96,36 +96,30 @@ use std::rc::Rc;
/// ///
/// On the server this will always return an empty string as ´content` and a no-op `trigger_resize`. /// On the server this will always return an empty string as ´content` and a no-op `trigger_resize`.
// #[doc(cfg(feature = "use_textarea_autosize"))] // #[doc(cfg(feature = "use_textarea_autosize"))]
pub fn use_textarea_autosize<El, T>(el: El) -> UseTextareaAutosizeReturn<impl Fn() + Clone> pub fn use_textarea_autosize<El, M>(el: El) -> UseTextareaAutosizeReturn<impl Fn() + Clone>
where where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone, El: IntoElementMaybeSignal<web_sys::Element, M> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
{ {
use_textarea_autosize_with_options::<El, T, web_sys::Element>( use_textarea_autosize_with_options::<El, M>(el, UseTextareaAutosizeOptions::default())
el,
UseTextareaAutosizeOptions::default(),
)
} }
/// Version of [`fn@crate::use_textarea_autosize`] that takes a `UseTextareaAutosizeOptions`. See [`fn@crate::use_textarea_autosize`] for how to use. /// Version of [`fn@crate::use_textarea_autosize`] that takes a `UseTextareaAutosizeOptions`. See [`fn@crate::use_textarea_autosize`] for how to use.
// #[doc(cfg(feature = "use_textarea_autosize"))] // #[doc(cfg(feature = "use_textarea_autosize"))]
pub fn use_textarea_autosize_with_options<El, T, StyleT>( pub fn use_textarea_autosize_with_options<El, M>(
el: El, el: El,
options: UseTextareaAutosizeOptions<StyleT>, options: UseTextareaAutosizeOptions,
) -> UseTextareaAutosizeReturn<impl Fn() + Clone> ) -> UseTextareaAutosizeReturn<impl Fn() + Clone>
where where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone, El: IntoElementMaybeSignal<web_sys::Element, M> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
StyleT: Into<web_sys::Element> + Clone + 'static,
{ {
#[cfg(not(feature = "ssr"))] #[cfg(not(feature = "ssr"))]
{ {
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
let el = el.into(); let el = el.into_element_maybe_signal();
let textarea = Signal::derive(move || { let textarea = Signal::derive_local(move || {
el.get() el.get()
.map(|el| el.into().unchecked_into::<web_sys::HtmlTextAreaElement>()) .map(|el| el.unchecked_into::<web_sys::HtmlTextAreaElement>())
}); });
let UseTextareaAutosizeOptions { let UseTextareaAutosizeOptions {
@ -138,11 +132,11 @@ where
let (content, set_content) = content.into_signal(); let (content, set_content) = content.into_signal();
let (textarea_scroll_height, set_textarea_scroll_height) = create_signal(1); let (textarea_scroll_height, set_textarea_scroll_height) = signal(1);
let (textarea_old_width, set_textarea_old_width) = create_signal(0.0); let (textarea_old_width, set_textarea_old_width) = signal(0.0);
let trigger_resize = move || { let trigger_resize = move || {
textarea.with(|textarea| { textarea.with_untracked(|textarea| {
if let Some(textarea) = textarea { if let Some(textarea) = textarea {
let mut height = "".to_string(); let mut height = "".to_string();
@ -161,13 +155,14 @@ where
0 0
}; };
textarea.style().set_property(&style_prop, "1px").ok(); web_sys::HtmlElement::style(textarea)
.set_property(&style_prop, "1px")
.ok();
set_textarea_scroll_height.set(textarea.scroll_height() + border_offset + 1); set_textarea_scroll_height.set(textarea.scroll_height() + border_offset + 1);
if let Some(style_target) = style_target.get() { if let Some(style_target) = style_target.get() {
// If style target is provided update its height // If style target is provided update its height
style_target style_target
.into()
.unchecked_into::<web_sys::HtmlElement>() .unchecked_into::<web_sys::HtmlElement>()
.style() .style()
.set_property( .set_property(
@ -180,15 +175,17 @@ where
height = format!("{}px", textarea_scroll_height.get_untracked()); height = format!("{}px", textarea_scroll_height.get_untracked());
} }
textarea.style().set_property(&style_prop, &height).ok(); web_sys::HtmlElement::style(textarea)
.set_property(&style_prop, &height)
.ok();
} }
}) })
}; };
let _ = watch( Effect::watch(
move || { move || {
content.track(); content.with(|_| ());
textarea.track(); textarea.with(|_| ());
}, },
{ {
let trigger_resize = trigger_resize.clone(); let trigger_resize = trigger_resize.clone();
@ -200,7 +197,7 @@ where
true, true,
); );
let _ = watch( Effect::watch(
move || textarea_scroll_height.track(), move || textarea_scroll_height.track(),
move |_, _, _| { move |_, _, _| {
on_resize(); on_resize();
@ -223,7 +220,7 @@ where
} }
}); });
let _ = watch( Effect::watch(
move || watch_fn(), move || watch_fn(),
{ {
let trigger_resize = trigger_resize.clone(); let trigger_resize = trigger_resize.clone();
@ -247,7 +244,7 @@ where
let _ = el; let _ = el;
let _ = options; let _ = options;
let (content, set_content) = create_signal("".to_string()); let (content, set_content) = signal("".to_string());
UseTextareaAutosizeReturn { UseTextareaAutosizeReturn {
content: content.into(), content: content.into(),
@ -261,24 +258,21 @@ where
// #[doc(cfg(feature = "use_textarea_autosize"))] // #[doc(cfg(feature = "use_textarea_autosize"))]
#[derive(DefaultBuilder)] #[derive(DefaultBuilder)]
#[cfg_attr(feature = "ssr", allow(dead_code))] #[cfg_attr(feature = "ssr", allow(dead_code))]
pub struct UseTextareaAutosizeOptions<T> pub struct UseTextareaAutosizeOptions {
where
T: Into<web_sys::Element> + Clone + 'static,
{
/// Textarea content /// Textarea content
#[builder(into)] #[builder(into)]
content: MaybeRwSignal<String>, content: MaybeRwSignal<String>,
/// Watch sources that should trigger a textarea resize /// Watch sources that should trigger a textarea resize
watch: Rc<dyn Fn()>, watch: Arc<dyn Fn() + Send + Sync>,
/// Function called when the textarea size changes /// Function called when the textarea size changes
on_resize: Rc<dyn Fn()>, on_resize: Arc<dyn Fn() + Send + Sync>,
/// Specify style target to apply the height based on textarea content. /// Specify style target to apply the height based on textarea content.
/// If not provided it will use textarea it self. /// If not provided it will use textarea it self.
#[builder(skip)] #[builder(skip)]
style_target: ElementMaybeSignal<T, web_sys::Element>, style_target: ElementMaybeSignal<web_sys::Element>,
/// Specify the style property that will be used to manipulate height. /// Specify the style property that will be used to manipulate height.
/// Should be `"height"` or `"min-height"`. Default value is `"height"`. /// Should be `"height"` or `"min-height"`. Default value is `"height"`.
@ -286,37 +280,28 @@ where
style_prop: String, style_prop: String,
} }
impl Default for UseTextareaAutosizeOptions<web_sys::Element> { impl Default for UseTextareaAutosizeOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
content: MaybeRwSignal::default(), content: MaybeRwSignal::default(),
watch: Rc::new(|| ()), watch: Arc::new(|| ()),
on_resize: Rc::new(|| ()), on_resize: Arc::new(|| ()),
style_target: Default::default(), style_target: Default::default(),
style_prop: "height".to_string(), style_prop: "height".to_string(),
} }
} }
} }
impl<T> UseTextareaAutosizeOptions<T> impl UseTextareaAutosizeOptions {
where
T: Into<web_sys::Element> + Clone + 'static,
{
/// List of elementss that should not trigger the callback. Defaults to `[]`. /// List of elementss that should not trigger the callback. Defaults to `[]`.
#[cfg_attr(feature = "ssr", allow(dead_code))] #[cfg_attr(feature = "ssr", allow(dead_code))]
pub fn style_target<NewT>( pub fn style_target<M>(
self, self,
style_target: impl Into<ElementMaybeSignal<NewT, web_sys::Element>>, style_target: impl IntoElementMaybeSignal<web_sys::Element, M>,
) -> UseTextareaAutosizeOptions<NewT> ) -> Self {
where Self {
NewT: Into<web_sys::Element> + Clone + 'static, style_target: style_target.into_element_maybe_signal(),
{ ..self
UseTextareaAutosizeOptions {
content: self.content,
watch: self.watch,
on_resize: self.on_resize,
style_target: style_target.into(),
style_prop: self.style_prop,
} }
} }
} }

View file

@ -285,7 +285,7 @@ where
let (ready_state, set_ready_state) = signal(ConnectionReadyState::Closed); let (ready_state, set_ready_state) = signal(ConnectionReadyState::Closed);
let (message, set_message) = signal(None); let (message, set_message) = signal(None);
let ws_signal = RwSignal::new(None::<WebSocket>); let ws_signal = RwSignal::new_local(None::<WebSocket>);
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = StoredValue::new(None); let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = StoredValue::new(None);
@ -716,7 +716,7 @@ where
/// Latest message received from `WebSocket`. /// Latest message received from `WebSocket`.
pub message: Signal<Option<Rx>>, pub message: Signal<Option<Rx>>,
/// The `WebSocket` instance. /// The `WebSocket` instance.
pub ws: Signal<Option<WebSocket>>, pub ws: Signal<Option<WebSocket>, LocalStorage>,
/// Opens the `WebSocket` connection /// Opens the `WebSocket` connection
pub open: OpenFn, pub open: OpenFn,
/// Closes the `WebSocket` connection /// Closes the `WebSocket` connection