Compare commits

...

23 commits

Author SHA1 Message Date
Maccesch
21b7389eb5 release 0.14.0-gamma1 2024-10-10 03:08:34 +02:00
Maccesch
c8236dfc37 ported use_web_lock and use_window_size examples 2024-10-10 03:00:46 +02:00
Maccesch
0d0d4616f9 chore: clippy 2024-10-09 23:11:51 +02:00
Maccesch
e35a09148f made use_drop_zone usable again 2024-10-09 21:35:25 +02:00
Maccesch
39f3be4015 finished porting use_active_element 2024-10-09 21:15:17 +02:00
Maccesch
0bdc8e16c9 fixed use_color_mode example 2024-10-09 14:15:30 +02:00
Marc-Stefan Cassola
31181c38d9
Merge pull request #174 from BakerNet/update/leptos-0.7-gamma
Fix tests & formatting
2024-10-09 11:25:02 +01:00
BakerNet
171bc334ff Fix tests 2024-10-08 22:23:15 -07:00
Marc-Stefan Cassola
c6d22e19cb
Merge pull request #173 from nikessel/leptos-0.7 2024-10-08 10:51:16 +01:00
Niklas
3f81160091 Fix import issues with leptos version 0.7.0-gamma2 2024-10-08 07:33:12 +02:00
Maccesch
c4152ca358 added total downloads badge 2024-09-24 01:24:30 +02:00
Maccesch
49f89bcb36 ported use_textarea_autosize 2024-09-15 22:30:38 +02:00
Maccesch
406b7b2e16 Merge branch 'main' into leptos-0.7
# Conflicts:
#	CHANGELOG.md
#	Cargo.toml
#	src/use_user_media.rs
#	src/use_websocket.rs
2024-09-15 20:53:59 +02:00
Maccesch
10b1a8a0e8 added use_textarea_autosize 2024-09-15 20:41:51 +02:00
Maccesch
68fd2d7567 added crates.io wasm category 2024-09-13 16:57:14 +01:00
Maccesch
ad186d101c chore: rustfmt 2024-09-11 18:21:01 +01:00
Maccesch
cd88876389 element maybe signal refactoring 2024-09-11 18:19:01 +01:00
Maccesch
874854857f release 0.14.0-beta2 2024-09-09 20:42:56 +01:00
Maccesch
bf96858d6d Merge remote-tracking branch 'origin/leptos-0.7' into leptos-0.7 2024-09-09 16:10:39 +01:00
Marc-Stefan Cassola
e28c480046
Merge pull request #171 from BakerNet/fix/notify-from-trigger
fix: notify from trigger (0.7 beta change)
2024-09-09 16:05:24 +01:00
Maccesch
82e7655d14 use_websocket now returns a signal for the websocket instance 2024-09-05 00:54:08 +01:00
Maccesch
96c907aaaa Merge branch 'main' into leptos-0.7
# Conflicts:
#	CHANGELOG.md
#	Cargo.toml
2024-09-02 23:40:32 +01:00
Maccesch
1a189cf7a4 fixed use_color_mode with cookie enabled.
fixes #170
2024-09-02 18:21:21 +01:00
101 changed files with 1848 additions and 1613 deletions

View file

@ -46,30 +46,30 @@ jobs:
run: cargo test --features math,docs,ssr,actix --doc use_locale
#### mdbook
# - name: Install mdbook I
# uses: taiki-e/install-action@v2
# with:
# tool: cargo-binstall,mdbook
# - name: Install mdbook II
# run: |
# cargo binstall -y mdbook-cmdrun
# cargo binstall -y trunk@0.17.5
# rustup target add wasm32-unknown-unknown
# - name: Setup Pages
# id: pages
# uses: actions/configure-pages@v3
# - name: Build mdbook # TODO : run mdbook tests
# run: |
# cd docs/book
# mdbook build
# python3 post_build.py
# - name: Upload artifact
# uses: actions/upload-pages-artifact@v1
# with:
# path: ./docs/book/book
# - name: Deploy book to github pages
# id: deployment
# uses: actions/deploy-pages@v2
- name: Install mdbook I
uses: taiki-e/install-action@v2
with:
tool: cargo-binstall,mdbook
- name: Install mdbook II
run: |
cargo binstall -y mdbook-cmdrun
cargo binstall -y trunk@0.17.5
rustup target add wasm32-unknown-unknown
- name: Setup Pages
id: pages
uses: actions/configure-pages@v3
- name: Build mdbook # TODO : run mdbook tests
run: |
cd docs/book
mdbook build
python3 post_build.py
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: ./docs/book/book
- name: Deploy book to github pages
id: deployment
uses: actions/deploy-pages@v2
##### mdbook end
- name: Publish crate leptos-use

1
.idea/leptos-use.iml generated
View file

@ -85,6 +85,7 @@
<sourceFolder url="file://$MODULE_DIR$/examples/use_web_lock/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples/use_window_size/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples/aaaaa/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples/use_textarea_autosize/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/examples/use_event_listener/target" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/docs/book/book" />

View file

@ -3,11 +3,56 @@
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).
## [0.14.0-gamma1] - 2024-10-10
- Adapted to the latest changes in Leptos (thanks to @BakerNet and @nikessel)
- Fixed all the examples
- `use_active_element` ported
- `use_drop_zone` now returns `Signal<Vec<SendSignal<web_sys::File>>>` instead of `Signal<Vec<web_sys::File>, LocalStorage>`
to make it easier to use with `<For>`
## [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
### Breaking Changes 🛠
- Refactored `ElementMaybeSignal` and `ElementsMaybeSignal` to have a simpler implementation. For the vast majority
of cases this should continue to work as before.
## [0.14.0-beta2] - 2024-09-09
### Change 🔥
- Latest Leptos 0.7 beta changed the trigger trait method (thanks to @BakerNet)
- Latest changes from version 0.13.3 ported
## [0.14.0-beta1] - 2024-09-02
Ported everything to Leptos 0.7
Some example don't run yet.
## [0.13.5] - 2024-09-15
### New Function 🚀
- `use_textarea_autosize`
## [0.13.4] - 2024-09-05
### Fix 🍕
- `use_websocket` now returns a signal for the websocket instance so the user can actually use it. Before it always
returned `None`.
## [0.13.3] - 2024-09-02
### Fix 🍕
- Fixed `use_color_mode` with cookies enabled
## [0.13.2] - 2024-09-02
### Fix 🍕

View file

@ -1,9 +1,9 @@
[package]
name = "leptos-use"
version = "0.14.0-beta1"
version = "0.14.0-gamma1"
edition = "2021"
authors = ["Marc-Stefan Cassola"]
categories = ["gui", "web-programming"]
categories = ["gui", "web-programming", "wasm"]
description = "Collection of essential Leptos utilities inspired by React-Use / VueUse / SolidJS-USE"
exclude = ["examples/", "tests/"]
keywords = ["leptos", "utilities"]
@ -26,9 +26,9 @@ http1 = { version = "1", optional = true, package = "http" }
http0_2 = { version = "0.2", optional = true, package = "http" }
js-sys = "0.3"
lazy_static = "1"
leptos = "0.7.0-beta"
leptos_axum = { optional = true, version = "0.7.0-beta" }
leptos_actix = { optional = true, version = "0.7.0-beta" }
leptos = "0.7.0-gamma2"
leptos_axum = { version = "0.7.0-gamma2", optional = true }
leptos_actix = { version = "0.7.0-gamma2", optional = true }
leptos-spin = { version = "0.2", optional = true }
num = { version = "0.4", optional = true }
paste = "1"
@ -47,13 +47,14 @@ codee = { version = "0.2", features = [
"prost",
] }
getrandom = { version = "0.2", features = ["js"] }
leptos_meta = { version = "0.7.0-beta" }
leptos_meta = { version = "0.7.0-gamma2" }
rand = "0.8"
serde = { version = "1", features = ["derive"] }
unic-langid = { version = "0.9", features = ["macros"] }
[features]
default = [
"use_textarea_autosize",
"use_web_lock",
"use_window_size",
"is_err",
@ -128,6 +129,12 @@ default = [
"watch_with_options",
"whenever"
]
use_textarea_autosize = [
"use_resize_observer",
"web-sys/CssStyleDeclaration",
"web-sys/HtmlElement",
"web-sys/HtmlTextAreaElement",
]
use_web_lock = [
"web-sys/AbortSignal",
"web-sys/Lock",

View file

@ -13,7 +13,7 @@
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
<a href="https://leptos-use.rs/server_side_rendering.html"><img src="https://img.shields.io/badge/-SSR-%236a214b" alt="SSR"></a>
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-86%20functions-%23EF3939" alt="86 Functions" /></a>
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-87%20functions-%23EF3939" alt="87 Functions" /></a>
</p>
<br/>
@ -22,6 +22,7 @@
## Usage
![Crates.io Total Downloads](https://img.shields.io/crates/d/leptos-use)
[![Docs](https://docs.rs/leptos-use/badge.svg)](https://docs.rs/leptos-use/)
[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/synphonyte/leptos-use#license)
[![Build Status](https://github.com/synphonyte/leptos-use/actions/workflows/cd.yml/badge.svg)](https://github.com/synphonyte/leptos-use/actions/workflows/cd.yml)

View file

@ -51,6 +51,7 @@
- [use_preferred_dark](browser/use_preferred_dark.md)
- [use_prefers_reduced_motion](browser/use_prefers_reduced_motion.md)
- [use_service_worker](browser/use_service_worker.md)
- [use_textarea_autosize](browser/use_textarea_autosize.md)
- [use_user_media](browser/use_user_media.md)
- [use_web_lock](browser/use_web_lock.md)
- [use_web_notification](browser/use_web_notification.md)

View file

@ -0,0 +1,3 @@
# use_textarea_autosize
<!-- cmdrun python3 ../extract_doc_comment.py use_textarea_autosize use_textarea_autosize -->

View file

@ -79,7 +79,8 @@
.demo-container input[type="text"],
.demo-container input[type="search"],
.demo-container input[type="number"] {
.demo-container input[type="number"],
.demo-container textarea {
display: block;
font-size: 90%;
padding: .5em 1em .4em;
@ -96,6 +97,7 @@
.demo-container textarea {
background: var(--bg);
border: 1px solid var(--theme-popup-border);
line-height: 1.5;
}
.demo-container input[type=range],

View file

@ -12,6 +12,6 @@
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
<a href="https://leptos-use.rs/server_side_rendering.html"><img src="https://img.shields.io/badge/-SSR-%236a214b" alt="SSR"></a>
<a href="./get_started.html"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
<a href="./functions.html"><img src="https://img.shields.io/badge/-86%20functions-%23EF3939" alt="86 Functions" /></a>
<a href="./functions.html"><img src="https://img.shields.io/badge/-87%20functions-%23EF3939" alt="87 Functions" /></a>
</p>
</div>

View file

@ -7,11 +7,13 @@ members = [
"signal_throttled",
"sync_signal",
"use_abs",
"use_active_element",
"use_and",
"use_breakpoints",
"use_broadcast_channel",
"use_ceil",
"use_clipboard",
"use_color_mode",
"use_cookie",
"use_css_var",
"use_cycle_list",
@ -21,6 +23,7 @@ members = [
"use_display_media",
"use_document_visibility",
"use_draggable",
"use_drop_zone",
"use_element_bounding",
"use_element_hover",
"use_element_size",
@ -52,6 +55,7 @@ members = [
"use_service_worker",
"use_sorted",
"use_storage",
"use_textarea_autosize",
"use_throttle_fn",
"use_timeout_fn",
"use_timestamp",
@ -66,16 +70,13 @@ members = [
"watch_debounced",
"watch_pausable",
"watch_throttled",
# "use_active_element",
# "use_color_mode",
# "use_drop_zone",
# "use_webtransport",
]
exclude = ["ssr", "use_webtransport_with_server"]
[workspace.dependencies]
leptos = "0.7.0-beta"
leptos = "0.7.0-gamma2"
codee = "0.2"
console_error_panic_hook = "0.1"
console_log = "1"

View file

@ -8,16 +8,14 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
axum = { version = "0.7", optional = true }
codee.workspace = true
codee = "0.2"
console_error_panic_hook = "0.1"
console_log = "1"
cfg-if = "1"
leptos = { workspace = true, features = ["nightly"] }
leptos_axum = { git = "https://github.com/leptos-rs/leptos", optional = true }
leptos_meta = { git = "https://github.com/leptos-rs/leptos", features = [
"nightly",
] }
leptos_router = { git = "https://github.com/leptos-rs/leptos", features = [
leptos = { version = "0.7.0-beta", features = ["nightly"] }
leptos_axum = { version = "0.7.0-beta", optional = true }
leptos_meta = "0.7.0-beta"
leptos_router = { version = "0.7.0-beta", features = [
"nightly",
] }
log = "0.4"
@ -46,7 +44,7 @@ features = [
]
[features]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
hydrate = ["leptos/hydrate"]
ssr = [
"dep:axum",
"dep:tokio",

View file

@ -1,9 +1,9 @@
use crate::error_template::{AppError, ErrorTemplate};
use codee::string::FromToStringCodec;
use leptos::ev::{keypress, KeyboardEvent};
use leptos::prelude::*;
use leptos_meta::*;
use leptos_router::*;
use leptos_router::components::*;
use leptos_router::path;
use leptos_use::storage::{use_local_storage, use_local_storage_with_options, UseStorageOptions};
use leptos_use::{
use_color_mode_with_options, use_cookie_with_options, use_debounce_fn, use_event_listener,
@ -12,6 +12,25 @@ use leptos_use::{
UseIntervalReturn, UseIntlNumberFormatOptions,
};
pub fn shell(options: LeptosOptions) -> impl IntoView {
view! {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<MetaTags/>
<AutoReload options=options.clone()/>
<HydrationScripts options/>
<link rel="stylesheet" id="leptos" href="/pkg/leptos_use_ssr.css"/>
</head>
<body>
<App/>
</body>
</html>
}
}
#[component]
pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
@ -22,14 +41,10 @@ pub fn App() -> impl IntoView {
<Title text="Leptos-Use SSR Example"/>
<Router fallback=|| {
let mut outside_errors = Errors::default();
outside_errors.insert_with_default_key(AppError::NotFound);
view! { <ErrorTemplate outside_errors/> }.into_view()
}>
<Router>
<main>
<Routes>
<Route path="" view=|| view! { <HomePage/> }/>
<Routes fallback=|| "This page could not be found.">
<Route path=path!("") view=|| view! { <HomePage/> }/>
</Routes>
</main>
</Router>
@ -86,7 +101,7 @@ fn HomePage() -> impl IntoView {
let locales = use_locales();
view! {
<Html class=move || mode.get().to_string()/>
<Html {..} class=move || mode.get().to_string()/>
<h1>Leptos-Use SSR Example</h1>
<button on:click=on_click>Click Me: {count}</button>

View file

@ -1,74 +0,0 @@
use cfg_if::cfg_if;
use http::status::StatusCode;
use leptos::prelude::*;
use thiserror::Error;
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;
#[derive(Clone, Debug, Error)]
pub enum AppError {
#[error("Not Found")]
NotFound,
}
impl AppError {
pub fn status_code(&self) -> StatusCode {
match self {
AppError::NotFound => StatusCode::NOT_FOUND,
}
}
}
// A basic function to display errors served by the error boundaries.
// Feel free to do more complicated things here than just displaying the error.
#[component]
pub fn ErrorTemplate(
#[prop(optional)] outside_errors: Option<Errors>,
#[prop(optional)] errors: Option<RwSignal<Errors>>,
) -> impl IntoView {
let errors = match outside_errors {
Some(e) => create_rw_signal(e),
None => match errors {
Some(e) => e,
None => panic!("No Errors found and we expected errors!"),
},
};
// Get Errors from Signal
let errors = errors.get();
// Downcast lets us take a type that implements `std::error::Error`
let errors: Vec<AppError> = errors
.into_iter()
.filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())
.collect();
println!("Errors: {errors:#?}");
// Only the response code for the first error is actually sent from the server
// this may be customized by the specific application
cfg_if! { if #[cfg(feature="ssr")] {
let response = use_context::<ResponseOptions>();
if let Some(response) = response {
response.set_status(errors[0].status_code());
}
}}
view! {
<h1>{if errors.len() > 1 { "Errors" } else { "Error" }}</h1>
<For
// a function that returns the items we're iterating over; a signal is fine
each=move || { errors.clone().into_iter().enumerate() }
// a unique key for each item as a reference
key=|(index, _error)| *index
// renders each item to a view
children=move |error| {
let error_string = error.1.to_string();
let error_code = error.1.status_code();
view! {
<h2>{error_code.to_string()}</h2>
<p>"Error: " {error_string}</p>
}
}
/>
}
}

View file

@ -1,40 +0,0 @@
use cfg_if::cfg_if;
cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use leptos::prelude::*;
use crate::app::App;
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler = leptos_axum::render_app_to_stream(options.to_owned(), move || view!{<App/>});
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}
}}

View file

@ -1,21 +1,10 @@
use cfg_if::cfg_if;
pub mod app;
pub mod error_template;
pub mod fileserv;
cfg_if! { if #[cfg(feature = "hydrate")] {
use leptos::prelude::*;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::app::*;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::app::App;
#[wasm_bindgen]
pub fn hydrate() {
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(move || {
view! { <App/> }
});
}
}}
console_error_panic_hook::set_once();
leptos::mount::hydrate_body(App);
}

View file

@ -1,51 +1,34 @@
#[cfg(feature = "ssr")]
use crate::app::*;
use axum::Router;
use leptos::{config::get_configuration, logging};
use leptos_axum::{generate_route_list, LeptosRoutes};
use leptos_use_ssr::*;
#[tokio::main]
async fn main() {
use axum::{routing::post, Router};
use http::{HeaderMap, HeaderName, HeaderValue};
use leptos::logging::log;
use leptos::prelude::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use leptos_use_ssr::app::*;
use leptos_use_ssr::fileserv::file_and_error_handler;
use tower_default_headers::DefaultHeadersLayer;
simple_logger::init_with_level(log::Level::Error)
.expect("couldn't initialize logging");
simple_logger::init_with_level(log::Level::Info).expect("couldn't initialize logging");
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
// For deployment these variables are:
// <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>
// Alternately a file can be specified such as Some("Cargo.toml")
// The file would need to be included with the executable when moved to deployment
let conf = get_configuration(None).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(|| view! { <App/> });
let mut default_headers = HeaderMap::new();
let color_header = HeaderValue::from_static("Sec-CH-Prefers-Color-Scheme");
default_headers.insert(HeaderName::from_static("accept-ch"), color_header.clone());
default_headers.insert(HeaderName::from_static("vary"), color_header.clone());
default_headers.insert(HeaderName::from_static("critical-ch"), color_header);
let routes = generate_route_list(App);
// build our application with a route
let app = Router::new()
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
.leptos_routes(&leptos_options, routes, || view! { <App/> })
.fallback(file_and_error_handler)
.with_state(leptos_options)
.layer(DefaultHeadersLayer::new(default_headers));
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())
})
.fallback(leptos_axum::file_and_error_handler(shell))
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
log!("listening on http://{}", &addr);
logging::log!("listening on http://{}", &addr);
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
#[cfg(not(feature = "ssr"))]
pub fn main() {
// no client-side main function
// unless we want this to work with e.g., Trunk for a purely client-side app
// see lib.rs for hydration function instead
}

View file

@ -1,4 +1,5 @@
use leptos::prelude::*;
use leptos::wasm_bindgen::JsCast;
use leptos_use::docs::{demo_or_body, Note};
use leptos_use::use_active_element;
@ -10,7 +11,10 @@ fn Demo() -> impl IntoView {
"{:?}",
active_element
.get()
.map(|el| el.dataset().get("id"))
.map(|el| el
.unchecked_ref::<web_sys::HtmlElement>()
.dataset()
.get("id"))
.unwrap_or_default()
)
};

View file

@ -1,4 +1,3 @@
use leptos::html::html;
use leptos::prelude::*;
use leptos_use::docs::{demo_or_body, Note};
use leptos_use::{
@ -16,7 +15,13 @@ fn Demo() -> impl IntoView {
"navy".into(),
"ayu".into(),
])
.initial_value(ColorMode::from(html().class_name())),
.initial_value(ColorMode::from(
document()
.query_selector("html")
.unwrap()
.unwrap()
.class_name(),
)),
);
let UseCycleListReturn { state, next, .. } = use_cycle_list_with_options(

View file

@ -0,0 +1,16 @@
[package]
name = "use_textarea_autosize"
version = "0.1.0"
edition = "2021"
[dependencies]
leptos = { workspace = true, features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
leptos-use = { path = "../..", features = ["use_textarea_autosize", "docs"] }
web-sys = "0.3"
[dev-dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-test = "0.3.0"

View file

@ -0,0 +1,23 @@
A simple example for `use_textarea_autosize`.
If you don't have it installed already, install [Trunk](https://trunkrs.dev/) and [Tailwind](https://tailwindcss.com/docs/installation)
as well as the nightly toolchain for Rust and the wasm32-unknown-unknown target:
```bash
cargo install trunk
npm install -D tailwindcss @tailwindcss/forms
rustup toolchain install nightly
rustup target add wasm32-unknown-unknown
```
Then, open two terminals. In the first one, run:
```
npx tailwindcss -i ./input.css -o ./style/output.css --watch
```
In the second one, run:
```bash
trunk serve --open
```

View file

@ -0,0 +1,2 @@
[build]
public_url = "/demo/"

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link data-trunk rel="css" href="style/output.css">
</head>
<body></body>
</html>

View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View file

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

View file

@ -0,0 +1,342 @@
[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0px;
padding-top: 0.5rem;
padding-right: 0.75rem;
padding-bottom: 0.5rem;
padding-left: 0.75rem;
font-size: 1rem;
line-height: 1.5rem;
--tw-shadow: 0 0 #0000;
}
[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
border-color: #2563eb;
}
input::-moz-placeholder, textarea::-moz-placeholder {
color: #6b7280;
opacity: 1;
}
input::placeholder,textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
text-align: inherit;
}
::-webkit-datetime-edit {
display: inline-flex;
}
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
padding-top: 0;
padding-bottom: 0;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
[multiple],[size]:where(select:not([size="1"])) {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: 0.75rem;
-webkit-print-color-adjust: unset;
print-color-adjust: unset;
}
[type='checkbox'],[type='radio'] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
--tw-shadow: 0 0 #0000;
}
[type='checkbox'] {
border-radius: 0px;
}
[type='radio'] {
border-radius: 100%;
}
[type='checkbox']:focus,[type='radio']:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
[type='checkbox']:checked,[type='radio']:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type='checkbox']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
@media (forced-colors: active) {
[type='checkbox']:checked {
-webkit-appearance: auto;
-moz-appearance: auto;
appearance: auto;
}
}
[type='radio']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
@media (forced-colors: active) {
[type='radio']:checked {
-webkit-appearance: auto;
-moz-appearance: auto;
appearance: auto;
}
}
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
border-color: transparent;
background-color: currentColor;
}
[type='checkbox']:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
@media (forced-colors: active) {
[type='checkbox']:indeterminate {
-webkit-appearance: auto;
-moz-appearance: auto;
appearance: auto;
}
}
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
border-color: transparent;
background-color: currentColor;
}
[type='file'] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type='file']:focus {
outline: 1px solid ButtonText;
outline: 1px auto -webkit-focus-ring-color;
}
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
.static {
position: static;
}
.mb-4 {
margin-bottom: 1rem;
}
.box-border {
box-sizing: border-box;
}
.grow {
flex-grow: 1;
}
.resize-none {
resize: none;
}
.text-\[--brand-color\] {
color: var(--brand-color);
}
.text-green-600 {
--tw-text-opacity: 1;
color: rgb(22 163 74 / var(--tw-text-opacity));
}
.opacity-75 {
opacity: 0.75;
}
@media (prefers-color-scheme: dark) {
.dark\:text-green-500 {
--tw-text-opacity: 1;
color: rgb(34 197 94 / var(--tw-text-opacity));
}
}

View file

@ -0,0 +1,15 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: {
files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"],
},
theme: {
extend: {},
},
corePlugins: {
preflight: false,
},
plugins: [
require('@tailwindcss/forms'),
],
}

View file

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

View file

@ -1,10 +1,14 @@
use leptos::*;
use leptos::prelude::*;
use leptos_use::docs::{demo_or_body, BooleanDisplay};
use leptos_use::{use_toggle, UseToggleReturn};
#[component]
fn Demo() -> impl IntoView {
let UseToggleReturn { toggle, value, set_value } = use_toggle(true);
let UseToggleReturn {
toggle,
value,
set_value,
} = use_toggle(true);
view! {
<p>Value: <BooleanDisplay value=value /></p>
@ -18,7 +22,9 @@ fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to(demo_or_body(), || {
let unmount_handle = mount_to(demo_or_body(), || {
view! { <Demo /> }
})
});
unmount_handle.forget();
}

View file

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

View file

@ -1,5 +1,6 @@
use gloo_timers::future::sleep;
use leptos::*;
use leptos::prelude::*;
use leptos::task::spawn_local;
use leptos_use::docs::demo_or_body;
use leptos_use::use_web_lock;
use std::time::Duration;
@ -12,7 +13,7 @@ async fn my_process(_lock: web_sys::Lock) -> i32 {
#[component]
fn Demo() -> impl IntoView {
let (res, set_res) = create_signal("Not started yet".to_string());
let (res, set_res) = signal("Not started yet".to_string());
let on_click = move |_| {
set_res.set("Running...".to_string());
@ -41,7 +42,9 @@ fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to(demo_or_body(), || {
let unmount_handle = leptos::mount::mount_to(demo_or_body(), || {
view! { <Demo/> }
})
});
unmount_handle.forget();
}

View file

@ -64,7 +64,7 @@ fn Demo() -> impl IntoView {
move |_| {
let transport = transport.clone();
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
match transport.open_bidir_stream().await {
Ok(bidir_stream) => {
let i = id.get_value();

View file

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

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use leptos_use::docs::demo_or_body;
use leptos_use::{use_window_size, UseWindowSizeReturn};
@ -7,7 +7,7 @@ fn Demo() -> impl IntoView {
let UseWindowSizeReturn { width, height } = use_window_size();
view! {
<p>{{ width }} x {{ height }}</p>
<p>{ width } x { height }</p>
}
}
@ -15,7 +15,9 @@ fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to(demo_or_body(), || {
let unmount_handle = leptos::mount::mount_to(demo_or_body(), || {
view! { <Demo/> }
})
});
unmount_handle.forget();
}

View file

@ -1,11 +1,6 @@
use crate::{UseDocument, UseWindow};
use cfg_if::cfg_if;
use leptos::html::{CreateElement, ElementType};
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use send_wrapper::SendWrapper;
use std::marker::PhantomData;
use wasm_bindgen::JsCast;
use leptos::reactive::wrappers::read::Signal;
use std::ops::Deref;
/// Used as an argument type to make it easily possible to pass either
///
@ -16,333 +11,197 @@ use wasm_bindgen::JsCast;
/// * a `NodeRef`
///
/// into a function. Used for example in [`fn@crate::use_event_listener`].
pub enum ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
Static(SendWrapper<Option<T>>),
#[derive(Copy, Clone)]
#[cfg_attr(not(debug_assertions), repr(transparent))]
pub struct ElementMaybeSignal<T: 'static> {
#[cfg(debug_assertions)]
defined_at: &'static std::panic::Location<'static>,
inner: ElementMaybeSignalType<T>,
}
#[derive(Clone, Copy)]
pub enum ElementMaybeSignalType<T: 'static> {
Static(StoredValue<Option<T>, LocalStorage>),
Dynamic(Signal<Option<T>, LocalStorage>),
_Phantom(PhantomData<fn() -> E>),
}
impl<T, E> Default for ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T: 'static> Default for ElementMaybeSignalType<T> {
fn default() -> Self {
Self::Static(SendWrapper::new(None))
Self::Static(StoredValue::new_local(None))
}
}
impl<T, E> Clone for ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn clone(&self) -> Self {
match self {
Self::Static(t) => Self::Static(t.clone()),
Self::Dynamic(s) => Self::Dynamic(*s),
_ => unreachable!(),
impl<T> Default for ElementMaybeSignal<T> {
fn default() -> Self {
Self {
inner: ElementMaybeSignalType::default(),
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
}
impl<T, E> DefinedAt for ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T> DefinedAt for ElementMaybeSignal<T> {
fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
None
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T, E> With for ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T> With for ElementMaybeSignal<T> {
type Value = Option<T>;
fn with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> O {
match self {
Self::Static(t) => f(t),
Self::Dynamic(s) => {
let value = s.get();
f(&value)
}
_ => unreachable!(),
}
}
fn try_with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
match self {
Self::Static(t) => Some(f(t)),
Self::Dynamic(s) => s.try_with(f),
_ => unreachable!(),
match &self.inner {
ElementMaybeSignalType::Static(v) => v.try_with_value(f),
ElementMaybeSignalType::Dynamic(s) => s.try_with(f),
}
}
}
impl<T, E> WithUntracked for ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T> WithUntracked for ElementMaybeSignal<T> {
type Value = Option<T>;
fn with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> O {
match self {
Self::Static(t) => f(t),
Self::Dynamic(s) => s.with_untracked(f),
_ => unreachable!(),
}
}
fn try_with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
match self {
Self::Static(t) => Some(f(t)),
Self::Dynamic(s) => s.try_with_untracked(f),
_ => unreachable!(),
match &self.inner {
ElementMaybeSignalType::Static(t) => t.try_with_value(f),
ElementMaybeSignalType::Dynamic(s) => s.try_with_untracked(f),
}
}
}
pub trait IntoElementMaybeSignal<T, Marker: ?Sized> {
fn into_element_maybe_signal(self) -> ElementMaybeSignal<T>;
}
impl<El, T, Marker: ?Sized> IntoElementMaybeSignal<T, Marker> for El
where
El: IntoElementMaybeSignalType<T, Marker>,
{
fn into_element_maybe_signal(self) -> ElementMaybeSignal<T> {
ElementMaybeSignal {
inner: self.into_element_maybe_signal_type(),
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
}
pub trait IntoElementMaybeSignalType<T, Marker: ?Sized> {
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T>;
}
// From static element //////////////////////////////////////////////////////////////
impl<T, E> From<T> for ElementMaybeSignal<T, E>
/// Handles `window()` or `document()`
impl<T, Js> IntoElementMaybeSignalType<T, web_sys::Element> for Js
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
{
fn from(value: T) -> Self {
ElementMaybeSignal::Static(SendWrapper::new(Some(value)))
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType::Static(StoredValue::new_local(Some(T::from(self).clone())))
}
}
impl<T, E> From<Option<T>> for ElementMaybeSignal<T, E>
/// Handles `window().body()`
impl<T, Js> IntoElementMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
{
fn from(target: Option<T>) -> Self {
ElementMaybeSignal::Static(SendWrapper::new(target))
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType::Static(StoredValue::new_local(self.map(|el| T::from(el).clone())))
}
}
macro_rules! impl_from_deref_option {
($ty:ty, $ty2:ty) => {
impl<E> From<$ty> for ElementMaybeSignal<$ty2, E>
where
E: From<$ty2> + 'static,
{
fn from(value: $ty) -> Self {
Self::Static(SendWrapper::new((*value).clone()))
}
}
};
/// Handles `use_window()` or `use_document()`
impl<T, E, Js> IntoElementMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
where
Js: Deref<Target = Option<E>>,
E: Clone,
T: From<E> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType::Static(StoredValue::new_local(
self.as_ref().map(|e| T::from(e.clone())),
))
}
}
impl_from_deref_option!(UseWindow, web_sys::Window);
impl_from_deref_option!(UseDocument, web_sys::Document);
// From string (selector) ///////////////////////////////////////////////////////////////
impl<'a, E> From<&'a str> for ElementMaybeSignal<web_sys::Element, E>
/// Handles `"body"` or `"#app"`
impl<T, V> IntoElementMaybeSignalType<T, str> for V
where
E: From<web_sys::Element> + 'static,
V: AsRef<str>,
T: From<web_sys::Element> + Clone,
{
fn from(target: &'a str) -> Self {
cfg_if! { if #[cfg(feature = "ssr")] {
let _ = target;
Self::Static(SendWrapper::new(None))
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementMaybeSignalType::Static(StoredValue::new_local(None))
} else {
Self::Static(SendWrapper::new(document().query_selector(target).unwrap_or_default()))
}}
}
}
impl<E> From<String> for ElementMaybeSignal<web_sys::Element, E>
where
E: From<web_sys::Element> + 'static,
{
fn from(target: String) -> Self {
Self::from(target.as_str())
}
}
macro_rules! impl_from_signal_string {
($ty:ty) => {
impl<E> From<$ty> for ElementMaybeSignal<web_sys::Element, E>
where
E: From<web_sys::Element> + 'static,
{
fn from(signal: $ty) -> Self {
cfg_if! { if #[cfg(feature = "ssr")] {
let _ = signal;
Self::Dynamic(Signal::derive_local(|| None))
} else {
Self::Dynamic(
Signal::derive_local(move || document().query_selector(&signal.get()).unwrap_or_default()),
)
}}
}
ElementMaybeSignalType::Static(StoredValue::new_local(
document()
.query_selector(self.as_ref())
.unwrap_or_default()
.map(|el| T::from(el).clone()),
))
}
};
}
}
impl_from_signal_string!(Signal<String>);
impl_from_signal_string!(ReadSignal<String>);
impl_from_signal_string!(RwSignal<String>);
impl_from_signal_string!(Memo<String>);
pub struct SignalStrMarker;
impl_from_signal_string!(Signal<&'static str>);
impl_from_signal_string!(ReadSignal<&'static str>);
impl_from_signal_string!(RwSignal<&'static str>);
impl_from_signal_string!(Memo<&'static str>);
/// Handles `Signal<&str>`
impl<T, V, I> IntoElementMaybeSignalType<T, SignalStrMarker> for V
where
V: Get<Value = I> + 'static,
I: AsRef<str>,
T: From<web_sys::Element> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementMaybeSignalType::Static(StoredValue::new_local(None))
} else {
ElementMaybeSignalType::Dynamic(Signal::derive_local(move || {
document()
.query_selector(self.get().as_ref())
.unwrap_or_default()
.map(|el| T::from(el).clone())
}))
}
}
}
// From signal ///////////////////////////////////////////////////////////////
macro_rules! impl_from_signal_option {
($ty:ty) => {
impl<T, E> From<$ty> for ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(target: $ty) -> Self {
Self::Dynamic(target.into())
}
}
};
pub struct SignalMarker;
/// Handles `Signal<web_sys::*>`
impl<T, V, E> IntoElementMaybeSignalType<T, SignalMarker> for V
where
V: Get<Value = E> + 'static,
T: From<E> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType::Dynamic(Signal::derive_local(move || Some(T::from(self.get()))))
}
}
impl_from_signal_option!(Signal<Option<T>, LocalStorage>);
impl_from_signal_option!(ReadSignal<Option<T>, LocalStorage>);
impl_from_signal_option!(RwSignal<Option<T>, LocalStorage>);
impl_from_signal_option!(Memo<Option<T>, LocalStorage>);
pub struct OptionSignalMarker;
macro_rules! impl_from_signal {
($ty:ty) => {
impl<T, E> From<$ty> for ElementMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(signal: $ty) -> Self {
Self::Dynamic(Signal::derive_local(move || Some(signal.get())))
}
}
};
/// Handles `Signal<Option<web_sys::*>>` and `NodeRef` and `ElementMaybeSignal`
impl<T, V, E> IntoElementMaybeSignalType<T, OptionSignalMarker> for V
where
V: Get<Value = Option<E>> + 'static,
T: From<E> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType::Dynamic(Signal::derive_local(move || self.get().map(T::from)))
}
}
impl_from_signal!(Signal<T, LocalStorage>);
impl_from_signal!(ReadSignal<T, LocalStorage>);
impl_from_signal!(RwSignal<T, LocalStorage>);
impl_from_signal!(Memo<T, LocalStorage>);
// From NodeRef //////////////////////////////////////////////////////////////
macro_rules! impl_from_node_ref {
($ty:ty) => {
impl<R> From<NodeRef<R>> for ElementMaybeSignal<$ty, $ty>
where
R: ElementType + CreateElement<Dom> + Clone + Send + Sync + 'static,
R::Output: JsCast + Into<$ty> + Clone + 'static,
{
fn from(node_ref: NodeRef<R>) -> Self {
Self::Dynamic(Signal::derive_local(move || {
node_ref.get().map(move |el| {
let el: $ty = el.clone().into();
el
})
}))
}
}
};
}
impl_from_node_ref!(web_sys::EventTarget);
impl_from_node_ref!(web_sys::Element);
impl_from_node_ref!(web_sys::HtmlElement);
// // From leptos::html::HTMLElement ///////////////////////////////////////////////
//
// macro_rules! impl_from_html_element {
// ($ty:ty) => {
// impl<HtmlEl> From<HtmlElement<HtmlEl>> for ElementMaybeSignal<$ty, $ty>
// where
// HtmlEl: ElementDescriptor + std::ops::Deref<Target = $ty>,
// {
// fn from(value: HtmlElement<HtmlEl>) -> Self {
// let el: &$ty = value.deref();
// Self::Static(Some(el.clone()))
// }
// }
// };
// }
//
// impl_from_html_element!(web_sys::EventTarget);
// impl_from_html_element!(web_sys::Element);
// impl_from_html_element!(web_sys::HtmlElement);
//
// // From Signal<leptos::html::HTMLElement> /////////////////////////////////////////
//
// macro_rules! impl_from_signal_html_element {
// ($signal:ty, $ty:ty) => {
// impl<HtmlEl> From<$signal> for ElementMaybeSignal<$ty, $ty>
// where
// HtmlEl: ElementDescriptor + std::ops::Deref<Target = $ty> + Clone,
// {
// fn from(value: $signal) -> Self {
// Self::Dynamic(Signal::derive(move || {
// let value = value.get();
// let el: &$ty = value.deref();
// Some(el.clone())
// }))
// }
// }
// };
// }
//
// impl_from_signal_html_element!(Signal<HtmlElement<HtmlEl>>, web_sys::EventTarget);
// impl_from_signal_html_element!(ReadSignal<HtmlElement<HtmlEl>>, web_sys::EventTarget);
// impl_from_signal_html_element!(RwSignal<HtmlElement<HtmlEl>>, web_sys::EventTarget);
// impl_from_signal_html_element!(Memo<HtmlElement<HtmlEl>>, web_sys::EventTarget);
//
// impl_from_signal_html_element!(Signal<HtmlElement<HtmlEl>>, web_sys::Element);
// impl_from_signal_html_element!(ReadSignal<HtmlElement<HtmlEl>>, web_sys::Element);
// impl_from_signal_html_element!(RwSignal<HtmlElement<HtmlEl>>, web_sys::Element);
// impl_from_signal_html_element!(Memo<HtmlElement<HtmlEl>>, web_sys::Element);
//
// // From Signal<Option<leptos::html::HTMLElement>> /////////////////////////////////////////
//
// macro_rules! impl_from_signal_html_element {
// ($signal:ty, $ty:ty) => {
// impl<HtmlEl> From<$signal> for ElementMaybeSignal<$ty, $ty>
// where
// HtmlEl: ElementDescriptor + std::ops::Deref<Target = $ty> + Clone,
// {
// fn from(value: $signal) -> Self {
// Self::Dynamic(Signal::derive(move || {
// let el: Option<$ty> = value.get().map(|el| el.deref().clone());
// el
// }))
// }
// }
// };
// }
//
// impl_from_signal_html_element!(Signal<Option<HtmlElement<HtmlEl>>>, web_sys::EventTarget);
// impl_from_signal_html_element!(
// ReadSignal<Option<HtmlElement<HtmlEl>>>,
// web_sys::EventTarget
// );
// impl_from_signal_html_element!(RwSignal<Option<HtmlElement<HtmlEl>>>, web_sys::EventTarget);
// impl_from_signal_html_element!(Memo<Option<HtmlElement<HtmlEl>>>, web_sys::EventTarget);
//
// impl_from_signal_html_element!(Signal<Option<HtmlElement<HtmlEl>>>, web_sys::Element);
// impl_from_signal_html_element!(ReadSignal<Option<HtmlElement<HtmlEl>>>, web_sys::Element);
// impl_from_signal_html_element!(RwSignal<Option<HtmlElement<HtmlEl>>>, web_sys::Element);
// impl_from_signal_html_element!(Memo<Option<HtmlElement<HtmlEl>>>, web_sys::Element);
//
// impl_from_signal_html_element!(Signal<Option<HtmlElement<HtmlEl>>>, web_sys::HtmlElement);
// impl_from_signal_html_element!(
// ReadSignal<Option<HtmlElement<HtmlEl>>>,
// web_sys::HtmlElement
// );
// impl_from_signal_html_element!(RwSignal<Option<HtmlElement<HtmlEl>>>, web_sys::HtmlElement);
// impl_from_signal_html_element!(Memo<Option<HtmlElement<HtmlEl>>>, web_sys::HtmlElement);

View file

@ -1,12 +1,7 @@
use crate::core::ElementMaybeSignal;
use crate::{UseDocument, UseWindow};
use cfg_if::cfg_if;
use leptos::html::ElementType;
use crate::core::{SignalMarker, SignalStrMarker};
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use send_wrapper::SendWrapper;
use std::marker::PhantomData;
use wasm_bindgen::JsCast;
use leptos::reactive::wrappers::read::Signal;
use std::ops::Deref;
/// Used as an argument type to make it easily possible to pass either
///
@ -17,656 +12,345 @@ use wasm_bindgen::JsCast;
/// * a `NodeRef`
///
/// into a function. Used for example in [`fn@crate::use_event_listener`].
pub enum ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
Static(SendWrapper<Vec<Option<T>>>),
#[derive(Copy, Clone)]
#[cfg_attr(not(debug_assertions), repr(transparent))]
pub struct ElementsMaybeSignal<T: 'static> {
#[cfg(debug_assertions)]
defined_at: &'static std::panic::Location<'static>,
inner: ElementsMaybeSignalType<T>,
}
#[derive(Clone, Copy)]
pub enum ElementsMaybeSignalType<T: 'static> {
Static(StoredValue<Vec<Option<T>>, LocalStorage>),
Dynamic(Signal<Vec<Option<T>>, LocalStorage>),
_Phantom(PhantomData<fn() -> E>),
}
impl<T, E> Default for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T: 'static> Default for ElementsMaybeSignalType<T> {
fn default() -> Self {
Self::Static(SendWrapper::new(vec![]))
Self::Static(StoredValue::new_local(vec![]))
}
}
impl<T, E> Clone for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn clone(&self) -> Self {
match self {
Self::Static(v) => Self::Static(v.clone()),
Self::Dynamic(s) => Self::Dynamic(*s),
Self::_Phantom(_) => unreachable!(),
impl<T> Default for ElementsMaybeSignal<T> {
fn default() -> Self {
Self {
inner: ElementsMaybeSignalType::default(),
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
}
impl<T, E> DefinedAt for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T> DefinedAt for ElementsMaybeSignal<T> {
fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
None
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T, E> With for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T> With for ElementsMaybeSignal<T> {
type Value = Vec<Option<T>>;
fn with<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> O {
match self {
Self::Static(v) => f(v),
Self::Dynamic(s) => s.with(f),
Self::_Phantom(_) => unreachable!(),
}
}
fn try_with<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
match self {
Self::Static(v) => Some(f(v)),
Self::Dynamic(s) => s.try_with(f),
Self::_Phantom(_) => unreachable!(),
match &self.inner {
ElementsMaybeSignalType::Static(v) => v.try_with_value(f),
ElementsMaybeSignalType::Dynamic(s) => s.try_with(f),
}
}
}
impl<T, E> WithUntracked for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
impl<T> WithUntracked for ElementsMaybeSignal<T> {
type Value = Vec<Option<T>>;
fn with_untracked<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> O {
match self {
Self::Static(t) => f(t),
Self::Dynamic(s) => s.with_untracked(f),
Self::_Phantom(_) => unreachable!(),
}
}
fn try_with_untracked<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
match self {
Self::Static(t) => Some(f(t)),
Self::Dynamic(s) => s.try_with_untracked(f),
Self::_Phantom(_) => unreachable!(),
match self.inner {
ElementsMaybeSignalType::Static(t) => t.try_with_value(f),
ElementsMaybeSignalType::Dynamic(s) => s.try_with_untracked(f),
}
}
}
pub trait IntoElementsMaybeSignal<T, Marker: ?Sized> {
fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T>;
}
impl<El, T, Marker: ?Sized> IntoElementsMaybeSignal<T, Marker> for El
where
El: IntoElementsMaybeSignalType<T, Marker>,
{
fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T> {
ElementsMaybeSignal {
inner: self.into_elements_maybe_signal_type(),
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
}
pub trait IntoElementsMaybeSignalType<T, Marker: ?Sized> {
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T>;
}
// From single static element //////////////////////////////////////////////////////////////
impl<T, E> From<T> for ElementsMaybeSignal<T, E>
/// Handles `window()` or `document()`
impl<T, Js> IntoElementsMaybeSignalType<T, web_sys::Element> for Js
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
{
fn from(value: T) -> Self {
ElementsMaybeSignal::Static(SendWrapper::new(vec![Some(value)]))
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Static(StoredValue::new_local(vec![Some(T::from(self).clone())]))
}
}
impl<T, E> From<Option<T>> for ElementsMaybeSignal<T, E>
/// Handles `window().body()`
impl<T, Js> IntoElementsMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
{
fn from(target: Option<T>) -> Self {
ElementsMaybeSignal::Static(SendWrapper::new(vec![target]))
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Static(StoredValue::new_local(vec![
self.map(|el| T::from(el).clone())
]))
}
}
macro_rules! impl_from_deref_option {
($ty:ty, $ty2:ty) => {
impl<E> From<$ty> for ElementsMaybeSignal<$ty2, E>
where
E: From<$ty2> + 'static,
{
fn from(value: $ty) -> Self {
Self::Static(SendWrapper::new(vec![(*value).clone()]))
}
}
};
/// Handles `use_window()` or `use_document()`
impl<T, E, Js> IntoElementsMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
where
Js: Deref<Target = Option<E>>,
E: Clone,
T: From<E> + Clone,
{
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Static(StoredValue::new_local(vec![self
.as_ref()
.map(|e| T::from(e.clone()))]))
}
}
impl_from_deref_option!(UseWindow, web_sys::Window);
impl_from_deref_option!(UseDocument, web_sys::Document);
// From string (selector) ///////////////////////////////////////////////////////////////
impl<'a, E> From<&'a str> for ElementsMaybeSignal<web_sys::Node, E>
/// Handles `"body"` or `"#app"`
impl<T, V> IntoElementsMaybeSignalType<T, str> for V
where
E: From<web_sys::Node> + 'static,
V: AsRef<str>,
T: From<web_sys::Element> + Clone,
{
fn from(target: &'a str) -> Self {
cfg_if! { if #[cfg(feature = "ssr")] {
let _ = target;
Self::Static(SendWrapper::new(vec![]))
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
} else {
if let Ok(node_list) = document().query_selector_all(target) {
let mut list = Vec::with_capacity(node_list.length() as usize);
for i in 0..node_list.length() {
let node = node_list.get(i).expect("checked the range");
list.push(Some(node));
}
Self::Static(SendWrapper::new(list))
} else {
Self::Static(SendWrapper::new(vec![]))
}
}}
}
}
impl<E> From<String> for ElementsMaybeSignal<web_sys::Node, E>
where
E: From<web_sys::Node> + 'static,
{
fn from(target: String) -> Self {
Self::from(target.as_str())
}
}
macro_rules! impl_from_signal_string {
($ty:ty) => {
impl<E> From<$ty> for ElementsMaybeSignal<web_sys::Node, E>
where
E: From<web_sys::Node> + 'static,
{
fn from(signal: $ty) -> Self {
cfg_if! { if #[cfg(feature = "ssr")] {
Self::Dynamic(
Signal::derive_local(move || {
if let Ok(node_list) = document().query_selector_all(&signal.get()) {
let mut list = Vec::with_capacity(node_list.length() as usize);
for i in 0..node_list.length() {
let node = node_list.get(i).expect("checked the range");
list.push(Some(node));
}
list
} else {
vec![]
}
}),
)
} else {
let _ = signal;
Self::Dynamic(Signal::derive_local(Vec::new))
}}
}
ElementsMaybeSignalType::Static(StoredValue::new_local(vec![document()
.query_selector(self.as_ref())
.unwrap_or_default()
.map(|el| T::from(el).clone())]))
}
};
}
}
impl_from_signal_string!(Signal<String>);
impl_from_signal_string!(ReadSignal<String>);
impl_from_signal_string!(RwSignal<String>);
impl_from_signal_string!(Memo<String>);
impl_from_signal_string!(Signal<&'static str>);
impl_from_signal_string!(ReadSignal<&'static str>);
impl_from_signal_string!(RwSignal<&'static str>);
impl_from_signal_string!(Memo<&'static str>);
/// Handles `Signal<&str>`
impl<T, V, I> IntoElementsMaybeSignalType<T, SignalStrMarker> for V
where
V: Get<Value = I> + 'static,
I: AsRef<str>,
T: From<web_sys::Element> + Clone,
{
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
} else {
ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
vec![document()
.query_selector(self.get().as_ref())
.unwrap_or_default()
.map(|el| T::from(el).clone())]
}))
}
}
}
// From single signal ///////////////////////////////////////////////////////////////
macro_rules! impl_from_signal_option {
($ty:ty) => {
impl<T, E> From<$ty> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(signal: $ty) -> Self {
Self::Dynamic(Signal::derive_local(move || vec![signal.get()]))
}
}
};
/// Handles `Signal<web_sys::*>`
impl<T, V, E> IntoElementsMaybeSignalType<T, SignalMarker> for V
where
V: Get<Value = E> + 'static,
T: From<E> + Clone,
{
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
vec![Some(T::from(self.get()))]
}))
}
}
impl_from_signal_option!(Signal<Option<T>, LocalStorage>);
impl_from_signal_option!(ReadSignal<Option<T>, LocalStorage>);
impl_from_signal_option!(RwSignal<Option<T>, LocalStorage>);
impl_from_signal_option!(Memo<Option<T>, LocalStorage>);
macro_rules! impl_from_signal {
($ty:ty) => {
impl<T, E> From<$ty> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(signal: $ty) -> Self {
Self::Dynamic(Signal::derive_local(move || vec![Some(signal.get())]))
}
}
};
}
impl_from_signal!(Signal<T, LocalStorage>);
impl_from_signal!(ReadSignal<T, LocalStorage>);
impl_from_signal!(RwSignal<T, LocalStorage>);
impl_from_signal!(Memo<T, LocalStorage>);
// From single NodeRef //////////////////////////////////////////////////////////////
macro_rules! impl_from_node_ref {
($ty:ty) => {
impl<R> From<NodeRef<R>> for ElementsMaybeSignal<$ty, $ty>
where
R: ElementType + Clone + 'static,
R::Output: JsCast + Into<$ty> + Clone + 'static,
{
fn from(node_ref: NodeRef<R>) -> Self {
Self::Dynamic(Signal::derive_local(move || {
vec![node_ref.get().map(move |el| {
let el: $ty = el.clone().into();
el
})]
}))
}
}
};
}
impl_from_node_ref!(web_sys::EventTarget);
impl_from_node_ref!(web_sys::Element);
// From single leptos::html::HTMLElement ///////////////////////////////////////////
// macro_rules! impl_from_html_element {
// ($ty:ty) => {
// impl<E, At, Ch, Rndr> From<HtmlElement<E, At, Ch, Rndr>> for ElementsMaybeSignal<$ty, $ty>
// where
// E: ElementType,
// E::Output: std::ops::Deref<Target = $ty>,
// {
// fn from(value: HtmlElement<E, At, Ch, Rndr>) -> Self {
// let el: &$ty = value.deref();
// Self::Static(vec![Some(el.clone())])
// }
// }
// };
// }
//
// impl_from_html_element!(web_sys::EventTarget);
// impl_from_html_element!(web_sys::Element);
// From multiple static elements //////////////////////////////////////////////////////
impl<T, E> From<&[T]> for ElementsMaybeSignal<T, E>
/// Handles `&[web_sys::*]`
impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [web_sys::Element]> for C
where
T: Into<E> + Clone + 'static,
Js: Clone + 'a,
T: From<Js>,
C: IntoIterator<Item = &'a Js>,
{
fn from(target: &[T]) -> Self {
Self::Static(SendWrapper::new(
target.iter().map(|t| Some(t.clone())).collect(),
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Static(StoredValue::new_local(
self.into_iter().map(|t| Some(T::from(t.clone()))).collect(),
))
}
}
impl<T, E> From<&[Option<T>]> for ElementsMaybeSignal<T, E>
/// Handles `&[Option<web_sys::*>]`
impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [Option<web_sys::Element>]> for C
where
T: Into<E> + Clone + 'static,
Js: Clone + 'a,
T: From<Js>,
C: IntoIterator<Item = &'a Option<Js>>,
{
fn from(target: &[Option<T>]) -> Self {
Self::Static(SendWrapper::new(target.to_vec()))
}
}
impl<T, E> From<Vec<T>> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(target: Vec<T>) -> Self {
Self::Static(SendWrapper::new(
target.iter().map(|t| Some(t.clone())).collect(),
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Static(StoredValue::new_local(
self.into_iter().map(|t| t.clone().map(T::from)).collect(),
))
}
}
impl<T, E> From<Vec<Option<T>>> for ElementsMaybeSignal<T, E>
/// Handles `Vec<web_sys::*>`
impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<web_sys::Element>> for C
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
C: IntoIterator<Item = Js>,
{
fn from(target: Vec<Option<T>>) -> Self {
Self::Static(SendWrapper::new(target.into_iter().collect()))
}
}
impl<T, E, const C: usize> From<[T; C]> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(target: [T; C]) -> Self {
Self::Static(SendWrapper::new(
target.into_iter().map(|t| Some(t)).collect(),
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Static(StoredValue::new_local(
self.into_iter().map(|t| Some(T::from(t))).collect(),
))
}
}
impl<T, E, const C: usize> From<[Option<T>; C]> for ElementsMaybeSignal<T, E>
/// Handles `Vec<Option<web_sys::*>>`
impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<Option<web_sys::Element>>> for C
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
C: IntoIterator<Item = Option<Js>>,
{
fn from(target: [Option<T>; C]) -> Self {
Self::Static(SendWrapper::new(target.into_iter().collect()))
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Static(StoredValue::new_local(
self.into_iter().map(|t| t.map(T::from)).collect(),
))
}
}
// From multiple strings //////////////////////////////////////////////////////
macro_rules! impl_from_strings_inner {
($el_ty:ty, $str_ty:ty, $target:ident) => {
Self::Static(
SendWrapper::new(
$target
.iter()
.filter_map(|sel: &$str_ty| -> Option<Vec<Option<$el_ty>>> {
cfg_if! { if #[cfg(feature = "ssr")] {
let _ = sel;
None
} else {
use wasm_bindgen::JsCast;
if let Ok(node_list) = document().query_selector_all(sel) {
let mut list = Vec::with_capacity(node_list.length() as usize);
for i in 0..node_list.length() {
let node: $el_ty = node_list.get(i).expect("checked the range").unchecked_into();
list.push(Some(node));
}
Some(list)
} else {
None
}
}}
})
.flatten()
.collect(),
)
)
};
}
macro_rules! impl_from_strings_with_container {
($el_ty:ty, $str_ty:ty, $container_ty:ty) => {
impl From<$container_ty> for ElementsMaybeSignal<$el_ty, $el_ty> {
fn from(target: $container_ty) -> Self {
impl_from_strings_inner!($el_ty, $str_ty, target)
}
}
};
}
macro_rules! impl_from_strings {
($el_ty:ty, $str_ty:ty) => {
impl_from_strings_with_container!($el_ty, $str_ty, Vec<$str_ty>);
impl_from_strings_with_container!($el_ty, $str_ty, &[$str_ty]);
impl<const C: usize> From<[$str_ty; C]> for ElementsMaybeSignal<$el_ty, $el_ty> {
fn from(target: [$str_ty; C]) -> Self {
impl_from_strings_inner!($el_ty, $str_ty, target)
}
}
impl<const C: usize> From<&[$str_ty; C]> for ElementsMaybeSignal<$el_ty, $el_ty> {
fn from(target: &[$str_ty; C]) -> Self {
impl_from_strings_inner!($el_ty, $str_ty, target)
}
}
};
}
impl_from_strings!(web_sys::Element, &str);
impl_from_strings!(web_sys::Element, String);
impl_from_strings!(web_sys::EventTarget, &str);
impl_from_strings!(web_sys::EventTarget, String);
// From signal of vec ////////////////////////////////////////////////////////////////
impl<T, E> From<Signal<Vec<T>, LocalStorage>> for ElementsMaybeSignal<T, E>
/// Handles `["body", "#app"]`
impl<T, V, C> IntoElementsMaybeSignalType<T, &[&str]> for C
where
T: Into<E> + Clone + 'static,
V: AsRef<str>,
T: From<web_sys::Element> + Clone,
C: IntoIterator<Item = V>,
{
fn from(signal: Signal<Vec<T>, LocalStorage>) -> Self {
Self::Dynamic(Signal::derive_local(move || {
signal.get().into_iter().map(|t| Some(t)).collect()
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
} else {
ElementsMaybeSignalType::Static(StoredValue::new_local(
self.into_iter()
.map(|sel| {
document()
.query_selector(sel.as_ref())
.unwrap_or_default()
.map(|el| T::from(el).clone())
})
.collect(),
))
}
}
}
// From signal of multiple ////////////////////////////////////////////////////////////////
pub struct SignalVecMarker;
/// Handles `Signal<Vec<web_sys::*>>` and `Signal<Option<web_sys::*>>` and `NodeRef` and `ElementMaybeSignal`
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecMarker> for G
where
T: From<Js> + Clone,
G: Get<Value = C> + 'static,
C: IntoIterator<Item = Js>,
{
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
self.get().into_iter().map(|t| Some(T::from(t))).collect()
}))
}
}
impl<T, E> From<Signal<Vec<Option<T>>, LocalStorage>> for ElementsMaybeSignal<T, E>
pub struct SignalVecOptionMarker;
/// Handles `Signal<Vec<Option<web_sys::*>>>`
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecOptionMarker> for G
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
G: Get<Value = C> + 'static,
C: IntoIterator<Item = Option<Js>>,
{
fn from(target: Signal<Vec<Option<T>>, LocalStorage>) -> Self {
Self::Dynamic(target)
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
self.get().into_iter().map(|t| t.map(T::from)).collect()
}))
}
}
// From multiple signals //////////////////////////////////////////////////////////////
impl<T, E> From<&[Signal<T, LocalStorage>]> for ElementsMaybeSignal<T, E>
pub struct VecSignalMarker;
/// Handles `Vec<Signal<web_sys::*>>`
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalMarker> for C
where
T: Into<E> + Clone + 'static,
T: From<Js> + Clone,
C: IntoIterator<Item = G> + Clone + 'static,
G: Get<Value = Js>,
{
fn from(list: &[Signal<T, LocalStorage>]) -> Self {
let list = list.to_vec();
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
let signals = self.clone();
Self::Dynamic(Signal::derive_local(move || {
list.iter().map(|t| Some(t.get())).collect()
}))
}
}
impl<T, E> From<&[Signal<Option<T>, LocalStorage>]> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(list: &[Signal<Option<T>, LocalStorage>]) -> Self {
let list = list.to_vec();
Self::Dynamic(Signal::derive_local(move || {
list.iter().map(|t| t.get()).collect()
}))
}
}
impl<T, E> From<Vec<Signal<T, LocalStorage>>> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(list: Vec<Signal<T, LocalStorage>>) -> Self {
let list = list.clone();
Self::Dynamic(Signal::derive_local(move || {
list.iter().map(|t| Some(t.get())).collect()
}))
}
}
impl<T, E> From<Vec<Signal<Option<T>, LocalStorage>>> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(list: Vec<Signal<Option<T>, LocalStorage>>) -> Self {
let list = list.clone();
Self::Dynamic(Signal::derive_local(move || {
list.iter().map(|t| t.get()).collect()
}))
}
}
impl<T, E, const C: usize> From<[Signal<T, LocalStorage>; C]> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(list: [Signal<T, LocalStorage>; C]) -> Self {
let list = list.to_vec();
Self::Dynamic(Signal::derive_local(move || {
list.iter().map(|t| Some(t.get())).collect()
}))
}
}
impl<T, E, const C: usize> From<[Signal<Option<T>, LocalStorage>; C]> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(list: [Signal<Option<T>, LocalStorage>; C]) -> Self {
let list = list.to_vec();
Self::Dynamic(Signal::derive_local(move || {
list.iter().map(|t| t.get()).collect()
}))
}
}
// From multiple NodeRefs //////////////////////////////////////////////////////////////
macro_rules! impl_from_multi_node_ref_inner {
($ty:ty, $node_refs:ident) => {
Self::Dynamic(Signal::derive_local(move || {
$node_refs
.iter()
.map(|node_ref| {
node_ref.get().map(move |el| {
let el: $ty = el.clone().into();
el
})
})
ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
signals
.clone()
.into_iter()
.map(|t| Some(T::from(t.get())))
.collect()
}))
}
}
pub struct VecSignalOptionMarker;
/// Handles `Vec<Signal<Option<web_sys::*>>>`, `Vec<NodeRef>`
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalOptionMarker> for C
where
T: From<Js> + Clone,
C: IntoIterator<Item = G> + Clone + 'static,
G: Get<Value = Option<Js>>,
{
fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
let signals = self.clone();
ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
signals
.clone()
.into_iter()
.map(|t| t.get().map(T::from))
.collect()
}))
};
}
macro_rules! impl_from_multi_node_ref {
($ty:ty) => {
impl<R> From<&[NodeRef<R>]> for ElementsMaybeSignal<$ty, $ty>
where
R: ElementType + Clone + 'static,
R::Output: JsCast + Into<$ty> + Clone + 'static,
{
fn from(node_refs: &[NodeRef<R>]) -> Self {
let node_refs = node_refs.to_vec();
impl_from_multi_node_ref_inner!($ty, node_refs)
}
}
impl<R, const C: usize> From<[NodeRef<R>; C]> for ElementsMaybeSignal<$ty, $ty>
where
R: ElementType + Clone + 'static,
R::Output: JsCast + Into<$ty> + Clone + 'static,
{
fn from(node_refs: [NodeRef<R>; C]) -> Self {
let node_refs = node_refs.to_vec();
impl_from_multi_node_ref_inner!($ty, node_refs)
}
}
impl<R> From<Vec<NodeRef<R>>> for ElementsMaybeSignal<$ty, $ty>
where
R: ElementType + Clone + 'static,
R::Output: JsCast + Into<$ty> + Clone + 'static,
{
fn from(node_refs: Vec<NodeRef<R>>) -> Self {
let node_refs = node_refs.clone();
impl_from_multi_node_ref_inner!($ty, node_refs)
}
}
};
}
impl_from_multi_node_ref!(web_sys::EventTarget);
impl_from_multi_node_ref!(web_sys::Element);
// // From multiple leptos::html::HTMLElement /////////////////////////////////////////
//
// macro_rules! impl_from_multi_html_element {
// ($ty:ty) => {
// impl<E, At, Ch, Rndr> From<&[HtmlElement<E, At, Ch, Rndr>]>
// for ElementsMaybeSignal<$ty, $ty>
// where
// E: ElementType,
// E::Output: std::ops::Deref<Target = $ty>,
// {
// fn from(value: &[HtmlElement<E, At, Ch, Rndr>]) -> Self {
// Self::Static(
// value
// .iter()
// .map(|el| {
// let el: &$ty = el.deref();
// Some(el.clone())
// })
// .collect(),
// )
// }
// }
//
// impl<E, At, Ch, Rndr, const C: usize> From<[HtmlElement<E, At, Ch, Rndr>; C]>
// for ElementsMaybeSignal<$ty, $ty>
// where
// E: ElementType,
// E::Output: std::ops::Deref<Target = $ty>,
// {
// fn from(value: [HtmlElement<E, At, Ch, Rndr>; C]) -> Self {
// Self::Static(
// value
// .iter()
// .map(|el| {
// let el: &$ty = el.deref();
// Some(el.clone())
// })
// .collect(),
// )
// }
// }
//
// impl<E, At, Ch, Rndr> From<Vec<HtmlElement<E, At, Ch, Rndr>>>
// for ElementsMaybeSignal<$ty, $ty>
// where
// E: ElementType,
// E::Output: std::ops::Deref<Target = $ty>,
// {
// fn from(value: Vec<HtmlElement<E, At, Ch, Rndr>>) -> Self {
// Self::Static(
// value
// .iter()
// .map(|el| {
// let el: &$ty = el.deref();
// Some(el.clone())
// })
// .collect(),
// )
// }
// }
// };
// }
//
// impl_from_multi_html_element!(web_sys::EventTarget);
// impl_from_multi_html_element!(web_sys::Element);
// From ElementMaybeSignal //////////////////////////////////////////////////////////////
impl<T, E> From<ElementMaybeSignal<T, E>> for ElementsMaybeSignal<T, E>
where
T: Into<E> + Clone + 'static,
{
fn from(signal: ElementMaybeSignal<T, E>) -> Self {
match signal {
ElementMaybeSignal::Dynamic(signal) => {
Self::Dynamic(Signal::derive_local(move || vec![signal.get()]))
}
ElementMaybeSignal::Static(el) => {
Self::Static(SendWrapper::new(vec![SendWrapper::take(el)]))
}
ElementMaybeSignal::_Phantom(_) => unreachable!(),
}
}
}

View file

@ -35,7 +35,7 @@ mod signal_throttled;
#[cfg(feature = "sync_signal")]
mod sync_signal;
#[cfg(feature = "use_active_element")]
// mod use_active_element;
mod use_active_element;
#[cfg(feature = "use_breakpoints")]
mod use_breakpoints;
#[cfg(feature = "use_broadcast_channel")]
@ -126,6 +126,8 @@ mod use_service_worker;
mod use_sorted;
#[cfg(feature = "use_supported")]
mod use_supported;
#[cfg(feature = "use_textarea_autosize")]
mod use_textarea_autosize;
#[cfg(feature = "use_throttle_fn")]
mod use_throttle_fn;
#[cfg(feature = "use_timeout_fn")]
@ -181,7 +183,7 @@ pub use signal_throttled::*;
#[cfg(feature = "sync_signal")]
pub use sync_signal::*;
#[cfg(feature = "use_active_element")]
// pub use use_active_element::*;
pub use use_active_element::*;
#[cfg(feature = "use_breakpoints")]
pub use use_breakpoints::*;
#[cfg(feature = "use_broadcast_channel")]
@ -272,6 +274,8 @@ pub use use_service_worker::*;
pub use use_sorted::*;
#[cfg(feature = "use_supported")]
pub use use_supported::*;
#[cfg(feature = "use_textarea_autosize")]
pub use use_textarea_autosize::*;
#[cfg(feature = "use_throttle_fn")]
pub use use_throttle_fn::*;
#[cfg(feature = "use_timeout_fn")]

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_simple_math;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use num::Float;
use paste::paste;

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_binary_logic;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use paste::paste;
use_binary_logic!(

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_simple_math;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use num::Float;
use paste::paste;

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_simple_math;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use num::Float;
use paste::paste;

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_partial_cmp;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::cmp::Ordering;
use_partial_cmp!(

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_partial_cmp;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::cmp::Ordering;
use_partial_cmp!(

View file

@ -1,5 +1,5 @@
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Reactive `NOT` condition.
///

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_binary_logic;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use paste::paste;
use_binary_logic!(

View file

@ -1,6 +1,6 @@
use crate::math::shared::use_simple_math;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use num::Float;
use paste::paste;

View file

@ -1,4 +1,4 @@
use crate::core::{ElementMaybeSignal, ElementsMaybeSignal};
use crate::core::{ElementsMaybeSignal, IntoElementMaybeSignal, IntoElementsMaybeSignal};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
@ -79,33 +79,24 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## Server-Side Rendering
///
/// On the server this amounts to a no-op.
pub fn on_click_outside<El, T, F>(target: El, handler: F) -> impl FnOnce() + Clone
pub fn on_click_outside<El, M, F>(target: El, handler: F) -> impl FnOnce() + Clone
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
F: FnMut(web_sys::Event) + Clone + 'static,
{
on_click_outside_with_options::<_, _, _, web_sys::EventTarget>(
target,
handler,
OnClickOutsideOptions::default(),
)
on_click_outside_with_options(target, handler, OnClickOutsideOptions::default())
}
/// Version of `on_click_outside` that takes an `OnClickOutsideOptions`. See `on_click_outside` for more details.
#[cfg_attr(feature = "ssr", allow(unused_variables))]
pub fn on_click_outside_with_options<El, T, F, I>(
pub fn on_click_outside_with_options<El, M, F>(
target: El,
handler: F,
options: OnClickOutsideOptions<I>,
options: OnClickOutsideOptions,
) -> impl FnOnce() + Clone
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
F: FnMut(web_sys::Event) + Clone + 'static,
I: Into<web_sys::EventTarget> + Clone + 'static,
{
#[cfg(feature = "ssr")]
{
@ -148,25 +139,21 @@ where
let ignore = ignore.get_untracked();
ignore.into_iter().flatten().any(|element| {
let element: web_sys::EventTarget = element.into();
event_target::<web_sys::EventTarget>(event) == element
|| event.composed_path().includes(element.as_ref(), 0)
})
};
let target = target.into();
let target = target.into_element_maybe_signal();
let listener = {
let should_listen = Rc::clone(&should_listen);
let mut handler = handler.clone();
let target = target.clone();
let should_ignore = should_ignore.clone();
let target = target.clone();
move |event: web_sys::UiEvent| {
if let Some(el) = target.get_untracked() {
let el = el.into();
if el == event_target(&event) || event.composed_path().includes(el.as_ref(), 0)
{
return;
@ -182,7 +169,7 @@ where
}
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
handler(event.into());
}
@ -211,10 +198,8 @@ where
pointerdown,
move |event| {
if let Some(el) = target.get_untracked() {
should_listen.set(
!event.composed_path().includes(el.into().as_ref(), 0)
&& !should_ignore(&event),
);
should_listen
.set(!event.composed_path().includes(&el, 0) && !should_ignore(&event));
}
},
UseEventListenerOptions::default().passive(true),
@ -235,7 +220,6 @@ where
if let Some(active_element) = document().active_element() {
if active_element.tag_name() == "IFRAME"
&& !el
.into()
.unchecked_into::<web_sys::Node>()
.contains(Some(&active_element.into()))
{
@ -265,13 +249,10 @@ where
/// Options for [`on_click_outside_with_options`].
#[derive(Clone, DefaultBuilder)]
#[cfg_attr(feature = "ssr", allow(dead_code))]
pub struct OnClickOutsideOptions<T>
where
T: Into<web_sys::EventTarget> + Clone + 'static,
{
pub struct OnClickOutsideOptions {
/// List of elementss that should not trigger the callback. Defaults to `[]`.
#[builder(skip)]
ignore: ElementsMaybeSignal<T, web_sys::EventTarget>,
ignore: ElementsMaybeSignal<web_sys::EventTarget>,
/// Use capturing phase for internal event listener. Defaults to `true`.
capture: bool,
@ -280,28 +261,25 @@ where
detect_iframes: bool,
}
impl<T> Default for OnClickOutsideOptions<T>
where
T: Into<web_sys::EventTarget> + Clone + 'static,
{
impl Default for OnClickOutsideOptions {
fn default() -> Self {
Self {
ignore: Default::default(),
ignore: Vec::<web_sys::EventTarget>::new().into_elements_maybe_signal(),
capture: true,
detect_iframes: false,
}
}
}
impl<T> OnClickOutsideOptions<T>
where
T: Into<web_sys::EventTarget> + Clone + 'static,
{
/// List of elementss that should not trigger the callback. Defaults to `[]`.
impl OnClickOutsideOptions {
/// List of elements that should not trigger the callback. Defaults to `[]`.
#[cfg_attr(feature = "ssr", allow(dead_code))]
pub fn ignore(self, ignore: impl Into<ElementsMaybeSignal<T, web_sys::EventTarget>>) -> Self {
pub fn ignore<M: ?Sized>(
self,
ignore: impl IntoElementsMaybeSignal<web_sys::EventTarget, M>,
) -> Self {
Self {
ignore: ignore.into(),
ignore: ignore.into_elements_maybe_signal(),
..self
}
}

View file

@ -1,7 +1,7 @@
use crate::utils::signal_filtered;
use crate::{use_debounce_fn_with_options, DebounceOptions};
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use paste::paste;
signal_filtered!(

View file

@ -1,7 +1,7 @@
use crate::utils::signal_filtered;
use crate::{use_throttle_fn_with_options, ThrottleOptions};
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use paste::paste;
signal_filtered!(

View file

@ -1,7 +1,7 @@
use super::{use_storage_with_options, StorageType, UseStorageOptions};
use codee::{Decoder, Encoder};
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
#[allow(rustdoc::bare_urls)]
/// Reactive [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).

View file

@ -2,7 +2,7 @@ use crate::{core::MaybeRwSignal, storage::StorageType, utils::FilterOptions};
use codee::{CodecError, Decoder, Encoder};
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::sync::Arc;
use thiserror::Error;
use wasm_bindgen::JsValue;

View file

@ -226,7 +226,7 @@ where
move |new_value, _, _| {
if !is_sync_update.get_value() {
is_sync_update.set_value(true);
right.update(|right| assign_ltr(right, new_value));
right.try_update(|right| assign_ltr(right, new_value));
is_sync_update.set_value(false);
}
},
@ -240,7 +240,7 @@ where
move |new_value, _, _| {
if !is_sync_update.get_value() {
is_sync_update.set_value(true);
left.update(|left| assign_rtl(left, new_value));
left.try_update(|left| assign_rtl(left, new_value));
is_sync_update.set_value(false);
}
},

View file

@ -2,9 +2,8 @@
use crate::{use_document, use_event_listener_with_options, use_window, UseEventListenerOptions};
use leptos::ev::{blur, focus};
use leptos::html::{AnyElement, ToHtmlElement};
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::prelude::*;
use leptos::reactive::wrappers::read::Signal;
/// Reactive `document.activeElement`
///
@ -16,14 +15,13 @@ use leptos::prelude::*;
///
/// ```
/// # use leptos::prelude::*;
/// # use leptos::logging::log;
/// use leptos_use::use_active_element;
/// # use leptos_use::use_active_element;
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
/// let active_element = use_active_element();
///
/// create_effect(move |_| {
/// Effect::new(move || {
/// log!("focus changed to {:?}", active_element.get());
/// });
/// #
@ -34,14 +32,10 @@ use leptos::prelude::*;
/// ## Server-Side Rendering
///
/// On the server this returns a `Signal` that always contains the value `None`.
pub fn use_active_element() -> Signal<Option<HtmlElement<AnyElement>>> {
let get_active_element = move || {
use_document()
.active_element()
.map(|el| el.to_leptos_element())
};
pub fn use_active_element() -> Signal<Option<web_sys::Element>, LocalStorage> {
let get_active_element = move || use_document().active_element();
let (active_element, set_active_element) = signal(get_active_element());
let (active_element, set_active_element) = signal_local(get_active_element());
let listener_options = UseEventListenerOptions::default().capture(true);

View file

@ -1,7 +1,7 @@
use crate::{use_media_query, use_window};
use leptos::logging::error;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use paste::paste;
use std::collections::HashMap;
use std::fmt::Debug;

View file

@ -72,13 +72,7 @@ use wasm_bindgen::JsValue;
/// ```
pub fn use_broadcast_channel<T, C>(
name: &str,
) -> UseBroadcastChannelReturn<
T,
impl Fn(&T) + Clone,
impl Fn() + Clone,
<C as Encoder<T>>::Error,
<C as Decoder<T>>::Error,
>
) -> UseBroadcastChannelReturn<T, impl Fn(&T) + Clone, impl Fn() + Clone, C>
where
T: Send + Sync,
C: Encoder<T, Encoded = String> + Decoder<T, Encoded = str>,
@ -179,13 +173,12 @@ where
}
/// Return type of [`use_broadcast_channel`].
pub struct UseBroadcastChannelReturn<T, PFn, CFn, E, D>
pub struct UseBroadcastChannelReturn<T, PFn, CFn, C>
where
T: Send + Sync + 'static,
PFn: Fn(&T) + Clone,
CFn: Fn() + Clone,
E: Send + Sync + 'static,
D: Send + Sync + 'static,
C: Encoder<T> + Decoder<T>,
{
/// `true` if this browser supports `BroadcastChannel`s.
pub is_supported: Signal<bool>,
@ -203,12 +196,14 @@ where
pub close: CFn,
/// Latest error as reported by the `messageerror` event.
pub error: Signal<Option<UseBroadcastChannelError<E, D>>, LocalStorage>,
pub error: Signal<Option<ErrorType<T, C>>, LocalStorage>,
/// Wether the channel is closed
pub is_closed: Signal<bool>,
}
type ErrorType<T, C> = UseBroadcastChannelError<<C as Encoder<T>>::Error, <C as Decoder<T>>::Error>;
#[derive(Debug, Error)]
pub enum UseBroadcastChannelError<E, D> {
#[error("failed to post message")]

View file

@ -2,7 +2,7 @@ use crate::{js, js_fut, use_event_listener, use_supported, UseTimeoutFnReturn};
use default_struct_builder::DefaultBuilder;
use leptos::ev::{copy, cut};
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Reactive [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).
///
@ -77,7 +77,7 @@ pub fn use_clipboard_with_options(
let update_text = move |_| {
if is_supported.get() {
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
let clipboard = window().navigator().clipboard();
if let Ok(text) = js_fut!(clipboard.read_text()).await {
set_text.set(text.as_string());
@ -99,7 +99,7 @@ pub fn use_clipboard_with_options(
let start = start.clone();
let value = value.to_owned();
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
let clipboard = window().navigator().clipboard();
if js_fut!(clipboard.write_text(&value)).await.is_ok() {
set_text.set(Some(value));

View file

@ -1,5 +1,5 @@
use crate::core::url;
use crate::core::{ElementMaybeSignal, MaybeRwSignal};
use crate::core::{ElementMaybeSignal, IntoElementMaybeSignal, MaybeRwSignal};
use crate::storage::{use_storage_with_options, StorageType, UseStorageOptions};
use crate::utils::get_header;
use crate::{
@ -9,7 +9,7 @@ use crate::{
use codee::string::FromToStringCodec;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::fmt::{Display, Formatter};
use std::marker::PhantomData;
use std::str::FromStr;
@ -147,11 +147,10 @@ pub fn use_color_mode() -> UseColorModeReturn {
}
/// Version of [`use_color_mode`] that takes a `UseColorModeOptions`. See [`use_color_mode`] for how to use.
pub fn use_color_mode_with_options<El, T>(options: UseColorModeOptions<El, T>) -> UseColorModeReturn
pub fn use_color_mode_with_options<El, M>(options: UseColorModeOptions<El, M>) -> UseColorModeReturn
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
M: ?Sized,
{
let UseColorModeOptions {
target,
@ -216,11 +215,15 @@ where
let _ = sync_signal_with_options(
(cookie, set_cookie),
(store, set_store),
SyncSignalOptions::with_transforms(
move |cookie: &Option<ColorMode>| {
cookie.clone().unwrap_or_else(|| store.get_untracked())
SyncSignalOptions::with_assigns(
move |store: &mut ColorMode, cookie: &Option<ColorMode>| {
if let Some(cookie) = cookie {
*store = cookie.clone();
}
},
move |cookie: &mut Option<ColorMode>, store: &ColorMode| {
*cookie = Some(store.clone())
},
move |store: &ColorMode| Some(store.clone()),
),
);
}
@ -243,17 +246,13 @@ where
}
});
let target = target.into();
let target = target.into_element_maybe_signal();
let update_html_attrs = {
move |target: ElementMaybeSignal<T, web_sys::Element>,
attribute: String,
value: ColorMode| {
move |target: ElementMaybeSignal<web_sys::Element>, attribute: String, value: ColorMode| {
let el = target.get_untracked();
if let Some(el) = el {
let el = el.into();
let mut style: Option<web_sys::HtmlStyleElement> = None;
if !transition_enabled {
if let Ok(styl) = document().create_element("style") {
@ -414,11 +413,10 @@ impl FromStr for ColorMode {
}
#[derive(DefaultBuilder)]
pub struct UseColorModeOptions<El, T>
pub struct UseColorModeOptions<El, M>
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
M: ?Sized,
{
/// Element that the color mode will be applied to. Defaults to `"html"`.
target: El,
@ -502,12 +500,12 @@ where
ssr_color_header_getter: Arc<dyn Fn() -> Option<String> + Send + Sync>,
#[builder(skip)]
_marker: PhantomData<T>,
_marker: PhantomData<M>,
}
type OnChangedFn = Arc<dyn Fn(ColorMode, Arc<dyn Fn(ColorMode) + Send + Sync>) + Send + Sync>;
impl Default for UseColorModeOptions<&'static str, web_sys::Element> {
impl Default for UseColorModeOptions<&'static str, str> {
fn default() -> Self {
Self {
target: "html",

View file

@ -126,6 +126,8 @@ use std::sync::Arc;
/// {
/// Some("Somehow get the value of the cookie header as a string".to_owned())
/// }
/// #[cfg(not(feature = "ssr"))]
/// None
/// })
/// .ssr_set_cookie(|cookie: &Cookie| {
/// #[cfg(feature = "ssr")]
@ -233,7 +235,7 @@ where
return;
}
let value = cookie.with_untracked(|cookie| {
let value = cookie.try_with_untracked(|cookie| {
cookie.as_ref().and_then(|cookie| {
C::encode(cookie)
.map_err(|err| on_error(CodecError::Encode(err)))
@ -241,29 +243,31 @@ where
})
});
if value
== jar.with_value(|jar| jar.get(&cookie_name).map(|c| c.value().to_owned()))
{
return;
if let Some(value) = value {
if value
== jar.with_value(|jar| jar.get(&cookie_name).map(|c| c.value().to_owned()))
{
return;
}
jar.update_value(|jar| {
write_client_cookie(
&cookie_name,
&value,
jar,
max_age,
expires,
&domain,
&path,
same_site,
secure,
http_only,
Arc::clone(&ssr_cookies_header_getter),
);
});
post(&value);
}
jar.update_value(|jar| {
write_client_cookie(
&cookie_name,
&value,
jar,
max_age,
expires,
&domain,
&path,
same_site,
secure,
http_only,
Arc::clone(&ssr_cookies_header_getter),
);
});
post(&value);
}
};
@ -272,7 +276,7 @@ where
resume,
stop,
..
} = watch_pausable(move || cookie.get(), {
} = watch_pausable(move || cookie.track(), {
let on_cookie_change = on_cookie_change.clone();
move |_, _, _| {
@ -365,30 +369,29 @@ where
let domain = domain.clone();
let path = path.clone();
let value = cookie
.with(|cookie| {
cookie.as_ref().map(|cookie| {
C::encode(cookie)
.map_err(|err| on_error(CodecError::Encode(err)))
.ok()
})
if let Some(value) = cookie.try_with(|cookie| {
cookie.as_ref().map(|cookie| {
C::encode(cookie)
.map_err(|err| on_error(CodecError::Encode(err)))
.ok()
})
.flatten();
jar.update_value(|jar| {
write_server_cookie(
&cookie_name,
value,
jar,
max_age,
expires,
domain,
path,
same_site,
secure,
http_only,
Arc::clone(&ssr_set_cookie),
)
});
}) {
jar.update_value(|jar| {
write_server_cookie(
&cookie_name,
value.flatten(),
jar,
max_age,
expires,
domain,
path,
same_site,
secure,
http_only,
Arc::clone(&ssr_set_cookie),
)
});
}
}
});
}

View file

@ -1,11 +1,10 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
use crate::core::ElementMaybeSignal;
use crate::core::IntoElementMaybeSignal;
use crate::{
use_mutation_observer_with_options, watch_with_options, UseMutationObserverOptions,
WatchOptions,
};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use std::marker::PhantomData;
@ -85,15 +84,14 @@ pub fn use_css_var(
}
/// Version of [`use_css_var`] that takes a `UseCssVarOptions`. See [`use_css_var`] for how to use.
pub fn use_css_var_with_options<P, El, T>(
pub fn use_css_var_with_options<P, El, M>(
prop: P,
options: UseCssVarOptions<El, T>,
options: UseCssVarOptions<El, M>,
) -> (ReadSignal<String>, WriteSignal<String>)
where
P: Into<MaybeSignal<String>>,
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
let UseCssVarOptions {
target,
@ -104,8 +102,9 @@ where
let (variable, set_variable) = signal(initial_value.clone());
cfg_if! { if #[cfg(not(feature = "ssr"))] {
let el_signal = target.into();
#[cfg(not(feature = "ssr"))]
{
let el_signal = target.into_element_maybe_signal();
let prop = prop.into();
let update_css_var = {
@ -116,7 +115,7 @@ where
let key = prop.get_untracked();
if let Some(el) = el_signal.get_untracked() {
if let Ok(Some(style)) = window().get_computed_style(&el.into()) {
if let Ok(Some(style)) = window().get_computed_style(&el) {
if let Ok(value) = style.get_property_value(&key) {
set_variable.update(|var| *var = value.trim().to_string());
return;
@ -133,11 +132,10 @@ where
let update_css_var = update_css_var.clone();
let el_signal = el_signal.clone();
use_mutation_observer_with_options::<ElementMaybeSignal<T, web_sys::Element>, T, _>(
el_signal,
use_mutation_observer_with_options(
el_signal,
move |_, _| update_css_var(),
UseMutationObserverOptions::default()
.attribute_filter(vec!["style".to_string()]),
UseMutationObserverOptions::default().attribute_filter(vec!["style".to_string()]),
);
}
@ -149,35 +147,33 @@ where
let prop = prop.clone();
let _ = watch_with_options(
move || (el_signal.get(), prop.get()),
move || (el_signal.get(), prop.get()),
move |_, _, _| update_css_var(),
WatchOptions::default().immediate(true),
);
}
Effect::watch(
move || variable.get(),
move || variable.get(),
move |val, _, _| {
if let Some(el) = el_signal.get() {
let el = el.into().unchecked_into::<web_sys::HtmlElement>();
let el = el.unchecked_into::<web_sys::HtmlElement>();
let style = el.style();
let _ = style.set_property(&prop.get_untracked(), val);
}
},
false,
);
}}
}
(variable, set_variable)
}
/// Options for [`use_css_var_with_options`].
#[derive(DefaultBuilder)]
pub struct UseCssVarOptions<El, T>
pub struct UseCssVarOptions<El, M>
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
/// The target element to read the variable from and set the variable on.
/// Defaults to the `document.documentElement`.
@ -192,29 +188,35 @@ where
observe: bool,
#[builder(skip)]
_marker: PhantomData<T>,
_marker: PhantomData<M>,
}
cfg_if! { if #[cfg(feature = "ssr")] {
impl Default for UseCssVarOptions<Option<web_sys::Element>, web_sys::Element> {
fn default() -> Self {
Self {
target: None,
initial_value: "".into(),
observe: false,
_marker: PhantomData,
}
#[cfg(feature = "ssr")]
impl<M> Default for UseCssVarOptions<Option<web_sys::Element>, M>
where
Option<web_sys::Element>: IntoElementMaybeSignal<web_sys::Element, M>,
{
fn default() -> Self {
Self {
target: None,
initial_value: "".into(),
observe: false,
_marker: PhantomData,
}
}
} else {
impl Default for UseCssVarOptions<web_sys::Element, web_sys::Element> {
fn default() -> Self {
Self {
target: document().document_element().expect("No document element"),
initial_value: "".into(),
observe: false,
_marker: PhantomData,
}
}
#[cfg(not(feature = "ssr"))]
impl<M> Default for UseCssVarOptions<web_sys::Element, M>
where
web_sys::Element: IntoElementMaybeSignal<web_sys::Element, M>,
{
fn default() -> Self {
Self {
target: document().document_element().expect("No document element"),
initial_value: "".into(),
observe: false,
_marker: PhantomData,
}
}
}}
}

View file

@ -1,5 +1,5 @@
use cfg_if::cfg_if;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Reactive [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent).
///

View file

@ -2,7 +2,7 @@ use crate::core::MaybeRwSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use wasm_bindgen::{JsCast, JsValue};
/// Reactive [`mediaDevices.getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) streaming.
@ -83,7 +83,7 @@ pub fn use_display_media_with_options(
let start = move || {
cfg_if! { if #[cfg(not(feature = "ssr"))] {
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
_start().await;
stream.with_untracked(move |stream| {
if let Some(Ok(_)) = stream {
@ -103,7 +103,7 @@ pub fn use_display_media_with_options(
move || enabled.get(),
move |enabled, _, _| {
if *enabled {
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
_start().await;
});
} else {

View file

@ -4,7 +4,7 @@ use crate::use_event_listener;
use cfg_if::cfg_if;
use leptos::ev::visibilitychange;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Reactively track `document.visibilityState`
///

View file

@ -1,9 +1,9 @@
use crate::core::{ElementMaybeSignal, MaybeRwSignal, PointerType, Position};
use crate::core::{IntoElementMaybeSignal, MaybeRwSignal, PointerType, Position};
use crate::{use_event_listener_with_options, use_window, UseEventListenerOptions, UseWindow};
use default_struct_builder::DefaultBuilder;
use leptos::ev::{pointerdown, pointermove, pointerup};
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::marker::PhantomData;
use std::sync::Arc;
use wasm_bindgen::JsCast;
@ -45,34 +45,22 @@ use web_sys::PointerEvent;
/// }
/// # }
/// ```
pub fn use_draggable<El, T>(target: El) -> UseDraggableReturn
pub fn use_draggable<El, M>(target: El) -> UseDraggableReturn
where
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
use_draggable_with_options::<
El,
T,
UseWindow,
web_sys::Window,
web_sys::EventTarget,
web_sys::EventTarget,
>(target, UseDraggableOptions::default())
use_draggable_with_options::<El, M, _, _, _, _>(target, UseDraggableOptions::default())
}
/// Version of [`use_draggable`] that takes a `UseDraggableOptions`. See [`use_draggable`] for how to use.
pub fn use_draggable_with_options<El, T, DragEl, DragT, HandleEl, HandleT>(
pub fn use_draggable_with_options<El, M, DragEl, DragM, HandleEl, HandleM>(
target: El,
options: UseDraggableOptions<DragEl, DragT, HandleEl, HandleT>,
options: UseDraggableOptions<DragEl, DragM, HandleEl, HandleM>,
) -> UseDraggableReturn
where
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
DragEl: Clone,
DragEl: Into<ElementMaybeSignal<DragT, web_sys::EventTarget>>,
DragT: Into<web_sys::EventTarget> + Clone + 'static,
HandleEl: Into<ElementMaybeSignal<HandleT, web_sys::EventTarget>>,
HandleT: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
DragEl: IntoElementMaybeSignal<web_sys::EventTarget, DragM>,
HandleEl: IntoElementMaybeSignal<web_sys::EventTarget, HandleM>,
{
let UseDraggableOptions {
exact,
@ -88,14 +76,12 @@ where
..
} = options;
let target = target.into();
let target = target.into_element_maybe_signal();
let dragging_handle = if let Some(handle) = handle {
let handle: ElementMaybeSignal<_, _> = handle.into();
Signal::derive_local(move || handle.get().map(|handle| handle.into()))
handle.into_element_maybe_signal()
} else {
let target = target.clone();
Signal::derive_local(move || target.get().map(|target| target.into()))
target.clone()
};
let (position, set_position) = initial_value.into_signal();
@ -124,7 +110,7 @@ where
}
if let Some(target) = target.get_untracked() {
let target: web_sys::Element = target.into().unchecked_into();
let target: web_sys::Element = target.unchecked_into();
if exact.get_untracked() && event_target::<web_sys::Element>(&event) != target {
return;
@ -137,7 +123,7 @@ where
};
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
if !on_start(UseDraggableCallbackArgs {
position,
@ -172,7 +158,7 @@ where
set_position.set(position);
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_move(UseDraggableCallbackArgs {
position,
@ -197,7 +183,7 @@ where
set_start_position.set(None);
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_end(UseDraggableCallbackArgs {
position: position.get_untracked(),
@ -210,6 +196,8 @@ where
handle_event(event);
};
let dragging_element = dragging_element.into_element_maybe_signal();
let listener_options = UseEventListenerOptions::default().capture(true);
let _ = use_event_listener_with_options(
@ -246,12 +234,10 @@ where
/// Options for [`use_draggable_with_options`].
#[derive(DefaultBuilder)]
pub struct UseDraggableOptions<DragEl, DragT, HandleEl, HandleT>
pub struct UseDraggableOptions<DragEl, DragM, HandleEl, HandleM>
where
DragEl: Into<ElementMaybeSignal<DragT, web_sys::EventTarget>>,
DragT: Into<web_sys::EventTarget> + Clone + 'static,
HandleEl: Into<ElementMaybeSignal<HandleT, web_sys::EventTarget>>,
HandleT: Into<web_sys::EventTarget> + Clone + 'static,
DragEl: IntoElementMaybeSignal<web_sys::EventTarget, DragM>,
HandleEl: IntoElementMaybeSignal<web_sys::EventTarget, HandleM>,
{
/// Only start the dragging when click on the element directly. Defaults to `false`.
#[builder(into)]
@ -288,13 +274,16 @@ where
on_end: Arc<dyn Fn(UseDraggableCallbackArgs) + Send + Sync>,
#[builder(skip)]
_marker1: PhantomData<DragT>,
_marker1: PhantomData<DragM>,
#[builder(skip)]
_marker2: PhantomData<HandleT>,
_marker2: PhantomData<HandleM>,
}
impl Default
for UseDraggableOptions<UseWindow, web_sys::Window, web_sys::EventTarget, web_sys::EventTarget>
impl<DragM, HandleM> Default
for UseDraggableOptions<UseWindow, DragM, Option<web_sys::EventTarget>, HandleM>
where
UseWindow: IntoElementMaybeSignal<web_sys::EventTarget, DragM>,
Option<web_sys::EventTarget>: IntoElementMaybeSignal<web_sys::EventTarget, HandleM>,
{
fn default() -> Self {
Self {

View file

@ -1,8 +1,9 @@
use crate::core::ElementMaybeSignal;
use crate::core::IntoElementMaybeSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use send_wrapper::SendWrapper;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
@ -52,31 +53,29 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
///
/// On the server the returned `file` signal always contains an empty `Vec` and
/// `is_over_drop_zone` contains always `false`
pub fn use_drop_zone<El, T>(target: El) -> UseDropZoneReturn
pub fn use_drop_zone<El, M>(target: El) -> UseDropZoneReturn
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
use_drop_zone_with_options(target, UseDropZoneOptions::default())
}
/// Version of [`use_drop_zone`] that takes a `UseDropZoneOptions`. See [`use_drop_zone`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables))]
pub fn use_drop_zone_with_options<El, T>(
pub fn use_drop_zone_with_options<El, M>(
target: El,
options: UseDropZoneOptions,
) -> UseDropZoneReturn
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
let (is_over_drop_zone, set_over_drop_zone) = signal(false);
let (files, set_files) = signal_local(Vec::<web_sys::File>::new());
let (files, set_files) = signal(Vec::<SendWrapper<web_sys::File>>::new());
#[cfg(not(feature = "ssr"))]
{
use std::ops::Deref;
let UseDropZoneOptions {
on_drop,
on_enter,
@ -94,12 +93,15 @@ where
.unwrap_or_default()
.into_iter()
.map(web_sys::File::from)
.map(SendWrapper::new)
.collect();
set_files.update(move |f| *f = files);
}
};
let target = target.into_element_maybe_signal();
let _ = use_event_listener(target.clone(), dragenter, move |event| {
event.prevent_default();
counter.update_value(|counter| *counter += 1);
@ -108,10 +110,11 @@ where
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_enter(UseDropZoneEvent {
files: files.get_untracked().into_iter().collect(),
files: files
.with_untracked(|files| files.iter().map(|f| f.deref().clone()).collect()),
event,
});
});
@ -121,10 +124,11 @@ where
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_over(UseDropZoneEvent {
files: files.get_untracked().into_iter().collect(),
files: files
.with_untracked(|files| files.iter().map(|f| f.deref().clone()).collect()),
event,
});
});
@ -139,10 +143,11 @@ where
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_leave(UseDropZoneEvent {
files: files.get_untracked().into_iter().collect(),
files: files
.with_untracked(|files| files.iter().map(|f| f.deref().clone()).collect()),
event,
});
});
@ -155,10 +160,11 @@ where
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_drop(UseDropZoneEvent {
files: files.get_untracked().into_iter().collect(),
files: files
.with_untracked(|files| files.iter().map(|f| f.deref().clone()).collect()),
event,
});
});
@ -213,7 +219,7 @@ pub struct UseDropZoneEvent {
/// Return type of [`use_drop_zone`].
pub struct UseDropZoneReturn {
/// Files being handled
pub files: Signal<Vec<web_sys::File>, LocalStorage>,
pub files: Signal<Vec<SendWrapper<web_sys::File>>>,
/// Whether the files (dragged by the pointer) are over the drop zone
pub is_over_drop_zone: Signal<bool>,
}

View file

@ -1,8 +1,7 @@
use crate::core::ElementMaybeSignal;
use cfg_if::cfg_if;
use crate::core::IntoElementMaybeSignal;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Reactive [bounding box](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) of an HTML element
///
@ -30,22 +29,20 @@ use leptos::reactive_graph::wrappers::read::Signal;
/// ## Server-Side Rendering
///
/// On the server the returned signals always are `0.0` and `update` is a no-op.
pub fn use_element_bounding<El, T>(target: El) -> UseElementBoundingReturn<impl Fn() + Clone>
pub fn use_element_bounding<El, M>(target: El) -> UseElementBoundingReturn<impl Fn() + Clone>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
use_element_bounding_with_options(target, UseElementBoundingOptions::default())
}
/// Version of [`use_element_bounding`] that takes a `UseElementBoundingOptions`. See [`use_element_bounding`] for how to use.
pub fn use_element_bounding_with_options<El, T>(
pub fn use_element_bounding_with_options<El, M>(
target: El,
options: UseElementBoundingOptions,
) -> UseElementBoundingReturn<impl Fn() + Clone>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
let (height, set_height) = signal(0.0);
let (width, set_width) = signal(0.0);
@ -56,7 +53,10 @@ where
let (x, set_x) = signal(0.0);
let (y, set_y) = signal(0.0);
cfg_if! { if #[cfg(feature = "ssr")] {
let update;
#[cfg(feature = "ssr")]
{
let _ = target;
let _ = options;
@ -69,8 +69,11 @@ where
let _ = set_x;
let _ = set_y;
let update = move || ();
} else {
update = move || ();
}
#[cfg(not(feature = "ssr"))]
{
use crate::{
use_event_listener_with_options, use_resize_observer, use_window,
UseEventListenerOptions,
@ -84,16 +87,16 @@ where
immediate,
} = options;
let target = target.into();
let target = target.into_element_maybe_signal();
let update = {
update = {
let target = target.clone();
move || {
let el = target.get_untracked();
if let Some(el) = el {
let rect = el.into().get_bounding_client_rect();
let rect = el.get_bounding_client_rect();
set_height.set(rect.height());
set_width.set(rect.width());
@ -164,7 +167,7 @@ where
if immediate {
update();
}
}}
}
UseElementBoundingReturn {
height: height.into(),

View file

@ -1,11 +1,11 @@
use crate::core::ElementMaybeSignal;
use crate::core::IntoElementMaybeSignal;
use crate::{use_event_listener_with_options, UseEventListenerOptions};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::ev::{mouseenter, mouseleave};
use leptos::leptos_dom::helpers::TimeoutHandle;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
cfg_if! { if #[cfg(not(feature = "ssr"))] {
use std::time::Duration;
@ -38,11 +38,9 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## Server-Side Rendering
///
/// On the server this returns a `Signal` that always contains the value `false`.
pub fn use_element_hover<El, T>(el: El) -> Signal<bool>
pub fn use_element_hover<El, M>(el: El) -> Signal<bool>
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
use_element_hover_with_options(el, UseElementHoverOptions::default())
}
@ -50,14 +48,12 @@ where
/// Version of [`use_element_hover`] that takes a `UseElementHoverOptions`. See [`use_element_hover`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables, unused_mut))]
pub fn use_element_hover_with_options<El, T>(
pub fn use_element_hover_with_options<El, M>(
el: El,
options: UseElementHoverOptions,
) -> Signal<bool>
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
let UseElementHoverOptions {
delay_enter,
@ -90,6 +86,8 @@ where
let listener_options = UseEventListenerOptions::default().passive(true);
let el = el.into_element_maybe_signal();
let _ = use_event_listener_with_options(
el.clone(),
mouseenter,

View file

@ -1,8 +1,9 @@
use crate::core::{ElementMaybeSignal, Size};
use crate::core::IntoElementMaybeSignal;
use crate::core::Size;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
cfg_if! { if #[cfg(not(feature = "ssr"))] {
use crate::{use_resize_observer_with_options, UseResizeObserverOptions};
@ -47,23 +48,21 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## See also
///
/// - [`fn@crate::use_resize_observer`]
pub fn use_element_size<El, T>(target: El) -> UseElementSizeReturn
pub fn use_element_size<El, M>(target: El) -> UseElementSizeReturn
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
use_element_size_with_options(target, UseElementSizeOptions::default())
}
/// Version of [`use_element_size`] that takes a `UseElementSizeOptions`. See [`use_element_size`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables))]
pub fn use_element_size_with_options<El, T>(
pub fn use_element_size_with_options<El, M>(
target: El,
options: UseElementSizeOptions,
) -> UseElementSizeReturn
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
let UseElementSizeOptions { box_, initial_size } = options;
@ -74,7 +73,7 @@ where
{
let box_ = box_.unwrap_or(web_sys::ResizeObserverBoxOptions::ContentBox);
let target = target.into();
let target = target.into_element_maybe_signal();
let is_svg = {
let target = target.clone();
@ -82,7 +81,6 @@ where
move || {
if let Some(target) = target.get_untracked() {
target
.into()
.namespace_uri()
.map(|ns| ns.contains("svg"))
.unwrap_or(false)
@ -92,11 +90,11 @@ where
}
};
{
let target = target.clone();
let _ = use_resize_observer_with_options(
target.clone(),
{
let target = target.clone();
let _ = use_resize_observer_with_options::<ElementMaybeSignal<T, web_sys::Element>, _, _>(
target.clone(),
move |entries, _| {
let entry = &entries[0];
@ -111,7 +109,7 @@ where
if is_svg() {
if let Some(target) = target.get() {
if let Ok(Some(styles)) = window().get_computed_style(&target.into()) {
if let Ok(Some(styles)) = window().get_computed_style(&target) {
set_height.set(
styles
.get_property_value("height")
@ -155,10 +153,10 @@ where
set_width.set(entry.content_rect().width());
set_height.set(entry.content_rect().height())
}
},
UseResizeObserverOptions::default().box_(box_),
);
}
}
},
UseResizeObserverOptions::default().box_(box_),
);
let _ = watch_with_options(
move || target.get(),

View file

@ -1,4 +1,4 @@
use crate::core::ElementMaybeSignal;
use crate::core::IntoElementMaybeSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
@ -6,7 +6,7 @@ use std::marker::PhantomData;
#[cfg(not(feature = "ssr"))]
use crate::{use_intersection_observer_with_options, UseIntersectionObserverOptions};
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Tracks the visibility of an element within the viewport.
///
@ -42,12 +42,11 @@ use leptos::reactive_graph::wrappers::read::Signal;
/// ## See also
///
/// * [`fn@crate::use_intersection_observer`]
pub fn use_element_visibility<El, T>(target: El) -> Signal<bool>
pub fn use_element_visibility<El, M>(target: El) -> Signal<bool>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
use_element_visibility_with_options::<El, T, web_sys::Element, web_sys::Element>(
use_element_visibility_with_options::<El, M, web_sys::Element, _>(
target,
UseElementVisibilityOptions::default(),
)
@ -55,21 +54,19 @@ where
/// Version of [`use_element_visibility`] with that takes a `UseElementVisibilityOptions`. See [`use_element_visibility`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables))]
pub fn use_element_visibility_with_options<El, T, ContainerEl, ContainerT>(
pub fn use_element_visibility_with_options<El, M, ContainerEl, ContainerM>(
target: El,
options: UseElementVisibilityOptions<ContainerEl, ContainerT>,
options: UseElementVisibilityOptions<ContainerEl, ContainerM>,
) -> Signal<bool>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
ContainerEl: Into<ElementMaybeSignal<ContainerT, web_sys::Element>>,
ContainerT: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
ContainerEl: IntoElementMaybeSignal<web_sys::Element, ContainerM>,
{
let (is_visible, set_visible) = signal(false);
cfg_if! { if #[cfg(not(feature = "ssr"))] {
use_intersection_observer_with_options(
target.into(),
target.into_element_maybe_signal(),
move |entries, _| {
// In some circumstances Chrome passes a first (or only) entry which has a zero bounding client rect
// and returns `is_intersecting` erroneously as `false`.
@ -89,10 +86,9 @@ where
/// Options for [`use_element_visibility_with_options`].
#[derive(DefaultBuilder)]
pub struct UseElementVisibilityOptions<El, T>
pub struct UseElementVisibilityOptions<El, M>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
/// A `web_sys::Element` or `web_sys::Document` object which is an ancestor of the intended `target`,
/// whose bounding rectangle will be considered the viewport.
@ -104,10 +100,13 @@ where
viewport: Option<El>,
#[builder(skip)]
_marker: PhantomData<T>,
_marker: PhantomData<M>,
}
impl Default for UseElementVisibilityOptions<web_sys::Element, web_sys::Element> {
impl<M> Default for UseElementVisibilityOptions<web_sys::Element, M>
where
web_sys::Element: IntoElementMaybeSignal<web_sys::Element, M>,
{
fn default() -> Self {
Self {
viewport: None,

View file

@ -1,4 +1,4 @@
use crate::core::ElementMaybeSignal;
use crate::core::IntoElementMaybeSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::ev::EventDescriptor;
@ -88,11 +88,10 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## Server-Side Rendering
///
/// On the server this amounts to a noop.
pub fn use_event_listener<Ev, El, T, F>(target: El, event: Ev, handler: F) -> impl Fn() + Clone
pub fn use_event_listener<Ev, El, M, F>(target: El, event: Ev, handler: F) -> impl Fn() + Clone
where
Ev: EventDescriptor + 'static,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
F: FnMut(<Ev as EventDescriptor>::EventType) + 'static,
{
use_event_listener_with_options(target, event, handler, UseEventListenerOptions::default())
@ -101,7 +100,7 @@ where
/// Version of [`use_event_listener`] that takes `web_sys::AddEventListenerOptions`. See the docs for [`use_event_listener`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables))]
#[allow(unused_mut)]
pub fn use_event_listener_with_options<Ev, El, T, F>(
pub fn use_event_listener_with_options<Ev, El, M, F>(
target: El,
event: Ev,
mut handler: F,
@ -109,8 +108,7 @@ pub fn use_event_listener_with_options<Ev, El, T, F>(
) -> impl Fn() + Clone
where
Ev: EventDescriptor + 'static,
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
F: FnMut(<Ev as EventDescriptor>::EventType) + 'static,
{
#[cfg(feature = "ssr")]
@ -124,7 +122,7 @@ where
let event_name = event.name();
let closure_js = Closure::wrap(Box::new(move |e| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
handler(e);
}) as Box<dyn FnMut(_)>)
@ -145,7 +143,7 @@ where
let event_name = event.name();
let signal = target.into();
let signal = target.into_element_maybe_signal();
let prev_element = Rc::new(RefCell::new(None::<web_sys::EventTarget>));
@ -163,7 +161,7 @@ where
let cleanup_prev_element = cleanup_prev_element.clone();
watch_with_options(
move || signal.get().map(|e| e.into()),
move || signal.get(),
move |element, _, _| {
cleanup_prev_element();
prev_element.replace(element.clone());

View file

@ -234,9 +234,7 @@ where
);
} else {
#[cfg(debug_assertions)]
let _z =
leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter(
);
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_failed();
}

View file

@ -4,7 +4,7 @@ use crate::core::MaybeRwSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use wasm_bindgen::JsCast;
/// Reactive favicon.

View file

@ -1,7 +1,7 @@
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Reactive [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API).
///

View file

@ -4,7 +4,7 @@ use crate::utils::{DebounceOptions, FilterOptions, ThrottleOptions};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
///
///

View file

@ -1,4 +1,4 @@
use crate::core::{Direction, Directions, ElementMaybeSignal};
use crate::core::{Direction, Directions, IntoElementMaybeSignal};
use crate::{
use_element_visibility, use_scroll_with_options, ScrollOffset, UseEventListenerOptions,
UseScrollOptions, UseScrollReturn,
@ -7,7 +7,7 @@ use default_struct_builder::DefaultBuilder;
use futures_util::join;
use gloo_timers::future::sleep;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
@ -50,10 +50,9 @@ use wasm_bindgen::JsCast;
/// ```
///
/// The returned signal is `true` while new data is being loaded.
pub fn use_infinite_scroll<El, T, LFn, LFut>(el: El, on_load_more: LFn) -> Signal<bool>
pub fn use_infinite_scroll<El, M, LFn, LFut>(el: El, on_load_more: LFn) -> Signal<bool>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone + 'static,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M> + 'static,
LFn: Fn(ScrollState) -> LFut + Send + Sync + 'static,
LFut: Future<Output = ()>,
{
@ -61,14 +60,13 @@ where
}
/// Version of [`use_infinite_scroll`] that takes a `UseInfiniteScrollOptions`. See [`use_infinite_scroll`] for how to use.
pub fn use_infinite_scroll_with_options<El, T, LFn, LFut>(
pub fn use_infinite_scroll_with_options<El, M, LFn, LFut>(
el: El,
on_load_more: LFn,
options: UseInfiniteScrollOptions,
) -> Signal<bool>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone + 'static,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M> + 'static,
LFn: Fn(ScrollState) -> LFut + Send + Sync + 'static,
LFut: Future<Output = ()>,
{
@ -82,6 +80,8 @@ where
let on_load_more = StoredValue::new(on_load_more);
let el = el.into_element_maybe_signal();
let UseScrollReturn {
x,
y,
@ -108,13 +108,10 @@ where
let (is_loading, set_loading) = signal(false);
let el = el.into();
let observed_element = Signal::derive_local(move || {
let el = el.get();
el.map(|el| {
let el = el.into();
if el.is_instance_of::<web_sys::Window>() || el.is_instance_of::<web_sys::Document>() {
document()
.document_element()
@ -157,10 +154,9 @@ where
set_loading.set(true);
let measure = measure.clone();
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
#[cfg(debug_assertions)]
let zone =
leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
join!(
on_load_more.with_value(|f| f(state)),

View file

@ -1,8 +1,8 @@
use crate::core::{ElementMaybeSignal, ElementsMaybeSignal};
use crate::core::{IntoElementMaybeSignal, IntoElementsMaybeSignal};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::marker::PhantomData;
cfg_if! { if #[cfg(not(feature = "ssr"))] {
@ -55,16 +55,16 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## See also
///
/// * [`fn@crate::use_element_visibility`]
pub fn use_intersection_observer<El, T, F>(
target: El,
pub fn use_intersection_observer<Els, M, F, RootM>(
target: Els,
callback: F,
) -> UseIntersectionObserverReturn<impl Fn() + Clone, impl Fn() + Clone, impl Fn() + Clone>
where
El: Into<ElementsMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
Els: IntoElementsMaybeSignal<web_sys::Element, M>,
F: FnMut(Vec<web_sys::IntersectionObserverEntry>, web_sys::IntersectionObserver) + 'static,
web_sys::Element: IntoElementMaybeSignal<web_sys::Element, RootM>,
{
use_intersection_observer_with_options::<El, T, web_sys::Element, web_sys::Element, F>(
use_intersection_observer_with_options::<Els, M, web_sys::Element, RootM, F>(
target,
callback,
UseIntersectionObserverOptions::default(),
@ -73,16 +73,14 @@ where
/// Version of [`use_intersection_observer`] that takes a [`UseIntersectionObserverOptions`]. See [`use_intersection_observer`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables, unused_mut))]
pub fn use_intersection_observer_with_options<El, T, RootEl, RootT, F>(
target: El,
pub fn use_intersection_observer_with_options<Els, M, RootEl, RootM, F>(
target: Els,
mut callback: F,
options: UseIntersectionObserverOptions<RootEl, RootT>,
options: UseIntersectionObserverOptions<RootEl, RootM>,
) -> UseIntersectionObserverReturn<impl Fn() + Clone, impl Fn() + Clone, impl Fn() + Clone>
where
El: Into<ElementsMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
RootEl: Into<ElementMaybeSignal<RootT, web_sys::Element>>,
RootT: Into<web_sys::Element> + Clone + 'static,
Els: IntoElementsMaybeSignal<web_sys::Element, M>,
RootEl: IntoElementMaybeSignal<web_sys::Element, RootM>,
F: FnMut(Vec<web_sys::IntersectionObserverEntry>, web_sys::IntersectionObserver) + 'static,
{
let UseIntersectionObserverOptions {
@ -105,7 +103,7 @@ where
let closure_js = Closure::<dyn FnMut(js_sys::Array, web_sys::IntersectionObserver)>::new(
move |entries: js_sys::Array, observer| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
callback(
entries
@ -132,8 +130,8 @@ where
}
};
let targets = target.into();
let root = root.map(|root| (root).into());
let targets = target.into_elements_maybe_signal();
let root = root.map(|root| root.into_element_maybe_signal());
let stop_watch = {
let cleanup = cleanup.clone();
@ -166,7 +164,7 @@ where
);
if let Some(Some(root)) = root {
let root: web_sys::Element = root.clone().into();
let root = root.clone();
options.set_root(Some(&root));
}
@ -177,7 +175,7 @@ where
.expect("failed to create IntersectionObserver");
for target in targets.iter().flatten() {
let target: web_sys::Element = target.clone().into();
let target = target.clone();
obs.observe(&target);
}
@ -221,10 +219,9 @@ where
/// Options for [`use_intersection_observer_with_options`].
#[derive(DefaultBuilder)]
pub struct UseIntersectionObserverOptions<El, T>
pub struct UseIntersectionObserverOptions<El, M>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
/// If `true`, the `IntersectionObserver` will be attached immediately. Otherwise it
/// will only be attached after the returned `resume` closure is called. That is
@ -257,10 +254,13 @@ where
thresholds: Vec<f64>,
#[builder(skip)]
_marker: PhantomData<T>,
_marker: PhantomData<M>,
}
impl Default for UseIntersectionObserverOptions<web_sys::Element, web_sys::Element> {
impl<M> Default for UseIntersectionObserverOptions<web_sys::Element, M>
where
web_sys::Element: IntoElementMaybeSignal<web_sys::Element, M>,
{
fn default() -> Self {
Self {
immediate: true,

View file

@ -2,7 +2,7 @@ use crate::utils::Pausable;
use crate::{use_interval_fn_with_options, UseIntervalFnOptions};
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::rc::Rc;
/// Reactive counter increases on every interval.

View file

@ -104,7 +104,7 @@ where
move || {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
callback();
}

View file

@ -5,7 +5,7 @@ use crate::utils::js_value_from_to_string;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::fmt::Display;
use wasm_bindgen::{JsCast, JsValue};

View file

@ -4,7 +4,7 @@ use crate::use_event_listener;
use cfg_if::cfg_if;
use leptos::ev::change;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::cell::RefCell;
use std::rc::Rc;

View file

@ -1,8 +1,7 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
use crate::core::{ElementMaybeSignal, Position};
use crate::core::{IntoElementMaybeSignal, Position};
use crate::{use_event_listener_with_options, use_window, UseEventListenerOptions, UseWindow};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::ev::{dragover, mousemove, touchend, touchmove, touchstart};
use leptos::prelude::*;
@ -93,10 +92,9 @@ pub fn use_mouse() -> UseMouseReturn {
}
/// Variant of [`use_mouse`] that accepts options. Please see [`use_mouse`] for how to use.
pub fn use_mouse_with_options<El, T, Ex>(options: UseMouseOptions<El, T, Ex>) -> UseMouseReturn
pub fn use_mouse_with_options<El, M, Ex>(options: UseMouseOptions<El, M, Ex>) -> UseMouseReturn
where
El: Into<ElementMaybeSignal<T, web_sys::EventTarget>> + Clone,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
Ex: UseMouseEventExtractor + Clone + 'static,
{
let (x, set_x) = signal(options.initial_value.x);
@ -155,45 +153,47 @@ where
// TODO : event filters?
cfg_if! { if #[cfg(not(feature = "ssr"))] {
let target = options.target;
#[cfg(not(feature = "ssr"))]
{
let target = options.target.into_element_maybe_signal();
let event_listener_options = UseEventListenerOptions::default().passive(true);
let _ = use_event_listener_with_options(
target.clone(),
target.clone(),
mousemove,
mouse_handler,
event_listener_options,
);
let _ = use_event_listener_with_options(
target.clone(),
target.clone(),
dragover,
drag_handler,
event_listener_options,
);
if options.touch && !matches!(options.coord_type, UseMouseCoordType::Movement) {
let _ = use_event_listener_with_options(
target.clone(),
target.clone(),
touchstart,
touch_handler.clone(),
event_listener_options,
);
let _ = use_event_listener_with_options(
target.clone(),
target.clone(),
touchmove,
touch_handler,
event_listener_options,
);
if options.reset_on_touch_ends {
let _ = use_event_listener_with_options(
target,
target,
touchend,
move |_| reset(),
event_listener_options,
);
}
}
}}
}
UseMouseReturn {
x: x.into(),
@ -206,10 +206,9 @@ where
#[derive(DefaultBuilder)]
/// Options for [`use_mouse_with_options`].
pub struct UseMouseOptions<El, T, Ex>
pub struct UseMouseOptions<El, M, Ex>
where
El: Clone + Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
Ex: UseMouseEventExtractor + Clone,
{
/// How to extract the x, y coordinates from mouse events or touches
@ -228,10 +227,13 @@ where
initial_value: Position,
#[builder(skip)]
_marker: PhantomData<T>,
_marker: PhantomData<M>,
}
impl Default for UseMouseOptions<UseWindow, web_sys::Window, Infallible> {
impl<M> Default for UseMouseOptions<UseWindow, M, Infallible>
where
UseWindow: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
fn default() -> Self {
Self {
coord_type: UseMouseCoordType::default(),

View file

@ -1,9 +1,8 @@
use crate::core::{ElementMaybeSignal, Position};
use crate::core::{IntoElementMaybeSignal, Position};
use crate::{
use_mouse_with_options, use_window, UseMouseCoordType, UseMouseEventExtractor, UseMouseOptions,
UseMouseReturn, UseMouseSourceType, UseWindow,
};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use std::convert::Infallible;
@ -39,24 +38,21 @@ use std::marker::PhantomData;
///
/// On the server this returns simple Signals with the `initial_value` for `x` and `y`,
/// no-op for `stop`, `is_outside = true` and `0.0` for the rest of the signals.
pub fn use_mouse_in_element<El, T>(target: El) -> UseMouseInElementReturn<impl Fn() + Clone>
pub fn use_mouse_in_element<El, M>(target: El) -> UseMouseInElementReturn<impl Fn() + Clone>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
use_mouse_in_element_with_options(target, Default::default())
}
/// Version of [`use_mouse_in_element`] that takes a `UseMouseInElementOptions`. See [`use_mouse_in_element`] for how to use.
pub fn use_mouse_in_element_with_options<El, T, OptEl, OptT, OptEx>(
pub fn use_mouse_in_element_with_options<El, M, OptEl, OptM, OptEx>(
target: El,
options: UseMouseInElementOptions<OptEl, OptT, OptEx>,
options: UseMouseInElementOptions<OptEl, OptM, OptEx>,
) -> UseMouseInElementReturn<impl Fn() + Clone>
where
El: Into<ElementMaybeSignal<T, web_sys::Element>> + Clone,
T: Into<web_sys::Element> + Clone + 'static,
OptEl: Into<ElementMaybeSignal<OptT, web_sys::EventTarget>> + Clone,
OptT: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
OptEl: IntoElementMaybeSignal<web_sys::EventTarget, OptM>,
OptEx: UseMouseEventExtractor + Clone + 'static,
{
let UseMouseInElementOptions {
@ -88,8 +84,11 @@ where
let (element_height, set_element_height) = signal(0.0);
let (is_outside, set_outside) = signal(true);
cfg_if! { if #[cfg(feature = "ssr")] {
let stop = || ();
let stop;
#[cfg(feature = "ssr")]
{
stop = || ();
let _ = handle_outside;
@ -101,18 +100,21 @@ where
let _ = set_element_height;
let _ = set_outside;
let _ = target;
} else {
}
#[cfg(not(feature = "ssr"))]
{
use crate::use_event_listener;
use leptos::ev::mouseleave;
let target = target.into();
let target = target.into_element_maybe_signal();
let window = window();
let effect = Effect::watch(
move || (target.get(), x.get(), y.get()),
move |(el, x, y), _, _| {
if let Some(el) = el {
let el: web_sys::Element = el.clone().into();
let el = el.clone();
let rect = el.get_bounding_client_rect();
let left = rect.left();
let top = rect.top();
@ -146,10 +148,10 @@ where
false,
);
let stop = move || effect.stop();
stop = move || effect.stop();
let _ = use_event_listener(document(), mouseleave, move |_| set_outside.set(true));
}}
}
UseMouseInElementReturn {
x,
@ -168,10 +170,9 @@ where
/// Options for [`use_mouse_in_element_with_options`].
#[derive(DefaultBuilder)]
pub struct UseMouseInElementOptions<El, T, Ex>
pub struct UseMouseInElementOptions<El, M, Ex>
where
El: Clone + Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
T: Into<web_sys::EventTarget> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
Ex: UseMouseEventExtractor + Clone,
{
/// How to extract the x, y coordinates from mouse events or touches
@ -195,10 +196,13 @@ where
handle_outside: bool,
#[builder(skip)]
_marker: PhantomData<T>,
_marker: PhantomData<M>,
}
impl Default for UseMouseInElementOptions<UseWindow, web_sys::Window, Infallible> {
impl<M> Default for UseMouseInElementOptions<UseWindow, M, Infallible>
where
UseWindow: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
fn default() -> Self {
Self {
coord_type: UseMouseCoordType::default(),

View file

@ -1,7 +1,7 @@
use crate::core::ElementsMaybeSignal;
use crate::core::IntoElementsMaybeSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use wasm_bindgen::prelude::*;
cfg_if! { if #[cfg(not(feature = "ssr"))] {
@ -50,13 +50,12 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## Server-Side Rendering
///
/// On the server this amounts to a no-op.
pub fn use_mutation_observer<El, T, F>(
pub fn use_mutation_observer<El, M, F>(
target: El,
callback: F,
) -> UseMutationObserverReturn<impl Fn() + Clone>
where
El: Into<ElementsMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementsMaybeSignal<web_sys::Element, M>,
F: FnMut(Vec<web_sys::MutationRecord>, web_sys::MutationObserver) + 'static,
{
use_mutation_observer_with_options(target, callback, UseMutationObserverOptions::default())
@ -64,14 +63,13 @@ where
/// Version of [`use_mutation_observer`] that takes a `UseMutationObserverOptions`. See [`use_mutation_observer`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables, unused_mut))]
pub fn use_mutation_observer_with_options<El, T, F>(
pub fn use_mutation_observer_with_options<El, M, F>(
target: El,
mut callback: F,
options: UseMutationObserverOptions,
) -> UseMutationObserverReturn<impl Fn() + Clone>
where
El: Into<ElementsMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementsMaybeSignal<web_sys::Element, M>,
F: FnMut(Vec<web_sys::MutationRecord>, web_sys::MutationObserver) + 'static,
{
#[cfg(feature = "ssr")]
@ -90,7 +88,7 @@ where
let closure_js = Closure::<dyn FnMut(js_sys::Array, web_sys::MutationObserver)>::new(
move |entries: js_sys::Array, observer| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
callback(
entries
@ -120,7 +118,7 @@ where
}
};
let targets = target.into();
let targets = target.into_elements_maybe_signal();
let stop_watch = {
let cleanup = cleanup.clone();
@ -136,7 +134,7 @@ where
.expect("failed to create MutationObserver");
for target in targets.iter().flatten() {
let target: web_sys::Element = target.clone().into();
let target = target.clone();
let _ = obs.observe_with_options(&target, &options.clone().into());
}

View file

@ -1,5 +1,5 @@
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::fmt::Display;
/// Reactive [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API).
@ -46,7 +46,7 @@ pub fn use_permission(permission_name: &str) -> Signal<PermissionState> {
}
};
leptos::spawn::spawn_local({
leptos::task::spawn_local({
let permission_name = permission_name.to_owned();
async move {

View file

@ -1,6 +1,6 @@
use crate::use_media_query;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::fmt::Display;
/// Reactive [prefers-contrast](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) media query.

View file

@ -14,6 +14,7 @@ use std::sync::Arc;
/// ```
/// # use leptos::prelude::*;
/// # use leptos_use::use_prefers_reduced_motion;
/// # #[cfg(feature = "docs")]
/// # use leptos_use::docs::BooleanDisplay;
/// #
/// # #[component]

View file

@ -102,7 +102,7 @@ pub fn use_raf_fn_with_options(
};
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
callback(UseRafFnCallbackArgs { delta, timestamp });

View file

@ -1,7 +1,7 @@
use crate::core::ElementsMaybeSignal;
use crate::core::IntoElementsMaybeSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
cfg_if! { if #[cfg(not(feature = "ssr"))] {
use crate::use_supported;
@ -52,13 +52,12 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## See also
///
/// * [`fn@crate::use_element_size`]
pub fn use_resize_observer<El, T, F>(
target: El, // TODO : multiple elements?
pub fn use_resize_observer<Els, M, F>(
target: Els,
callback: F,
) -> UseResizeObserverReturn<impl Fn() + Clone>
where
El: Into<ElementsMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
Els: IntoElementsMaybeSignal<web_sys::Element, M>,
F: FnMut(Vec<web_sys::ResizeObserverEntry>, web_sys::ResizeObserver) + 'static,
{
use_resize_observer_with_options(target, callback, UseResizeObserverOptions::default())
@ -66,14 +65,13 @@ where
/// Version of [`use_resize_observer`] that takes a `web_sys::ResizeObserverOptions`. See [`use_resize_observer`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables, unused_mut))]
pub fn use_resize_observer_with_options<El, T, F>(
target: El, // TODO : multiple elements?
pub fn use_resize_observer_with_options<Els, M, F>(
target: Els,
mut callback: F,
options: UseResizeObserverOptions,
) -> UseResizeObserverReturn<impl Fn() + Clone>
where
El: Into<ElementsMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
Els: IntoElementsMaybeSignal<web_sys::Element, M>,
F: FnMut(Vec<web_sys::ResizeObserverEntry>, web_sys::ResizeObserver) + 'static,
{
#[cfg(feature = "ssr")]
@ -91,7 +89,7 @@ where
let closure_js = Closure::<dyn FnMut(js_sys::Array, web_sys::ResizeObserver)>::new(
move |entries: js_sys::Array, observer| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
callback(
entries
@ -121,7 +119,7 @@ where
}
};
let targets = target.into();
let targets = target.into_elements_maybe_signal();
let stop_watch = {
let cleanup = cleanup.clone();
@ -138,7 +136,7 @@ where
.expect("failed to create ResizeObserver");
for target in targets.iter().flatten() {
let target: web_sys::Element = target.clone().into();
let target = target.clone();
obs.observe_with_options(&target, &options.clone().into());
}
observer.replace(Some(obs));

View file

@ -1,9 +1,9 @@
use crate::core::{Direction, Directions, ElementMaybeSignal};
use crate::core::{Direction, Directions, IntoElementMaybeSignal};
use crate::UseEventListenerOptions;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::rc::Rc;
cfg_if! { if #[cfg(not(feature = "ssr"))] {
@ -176,27 +176,23 @@ const ARRIVED_STATE_THRESHOLD_PIXELS: f64 = 1.0;
/// ## Server-Side Rendering
///
/// On the server this returns signals that don't change and setters that are noops.
pub fn use_scroll<El, T>(
pub fn use_scroll<El, M>(
element: El,
) -> UseScrollReturn<impl Fn(f64) + Clone, impl Fn(f64) + Clone, impl Fn() + Clone>
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
use_scroll_with_options(element, Default::default())
}
/// Version of [`use_scroll`] with options. See [`use_scroll`] for how to use.
#[cfg_attr(feature = "ssr", allow(unused_variables))]
pub fn use_scroll_with_options<El, T>(
pub fn use_scroll_with_options<El, M>(
element: El,
options: UseScrollOptions,
) -> UseScrollReturn<impl Fn(f64) + Clone, impl Fn(f64) + Clone, impl Fn() + Clone>
where
El: Clone,
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
T: Into<web_sys::Element> + Clone + 'static,
El: IntoElementMaybeSignal<web_sys::Element, M>,
{
let (internal_x, set_internal_x) = signal(0.0);
let (internal_y, set_internal_y) = signal(0.0);
@ -216,12 +212,20 @@ where
bottom: false,
});
cfg_if! { if #[cfg(feature = "ssr")] {
let set_x = |_| {};
let set_y = |_| {};
let measure = || {};
} else {
let signal = element.into();
let set_x;
let set_y;
let measure;
#[cfg(feature = "ssr")]
{
set_x = |_| {};
set_y = |_| {};
measure = || {};
}
#[cfg(not(feature = "ssr"))]
{
let signal = element.into_element_maybe_signal();
let behavior = options.behavior;
let scroll_to = {
@ -231,8 +235,6 @@ where
let element = signal.get_untracked();
if let Some(element) = element {
let element = element.into();
let scroll_options = web_sys::ScrollToOptions::new();
scroll_options.set_behavior(behavior.get_untracked().into());
@ -248,12 +250,12 @@ where
}
};
let set_x = {
set_x = {
let scroll_to = scroll_to.clone();
move |x| scroll_to(Some(x), None)
};
let set_y = move |y| scroll_to(None, Some(y));
set_y = move |y| scroll_to(None, Some(y));
let on_scroll_end = {
let on_stop = Rc::clone(&options.on_stop);
@ -367,14 +369,14 @@ where
}
};
let target = {
let target = Signal::derive_local({
let signal = signal.clone();
Signal::derive_local(move || {
move || {
let element = signal.get();
element.map(|element| element.into().unchecked_into::<web_sys::EventTarget>())
})
};
element.map(|element| element.unchecked_into::<web_sys::EventTarget>())
}
});
if throttle >= 0.0 {
let throttled_scroll_handler = use_throttle_fn_with_arg_and_options(
@ -393,14 +395,14 @@ where
let _ = use_event_listener_with_options::<
_,
Signal<Option<web_sys::EventTarget>, LocalStorage>,
web_sys::EventTarget,
_,
_,
>(target, ev::scroll, handler, options.event_listener_options);
} else {
let _ = use_event_listener_with_options::<
_,
Signal<Option<web_sys::EventTarget>, LocalStorage>,
web_sys::EventTarget,
_,
_,
>(
target,
@ -413,7 +415,7 @@ where
let _ = use_event_listener_with_options::<
_,
Signal<Option<web_sys::EventTarget>, LocalStorage>,
web_sys::EventTarget,
_,
_,
>(
target,
@ -422,13 +424,12 @@ where
options.event_listener_options,
);
let measure = move || {
measure = move || {
if let Some(el) = signal.try_get_untracked().flatten() {
let el = el.into();
set_arrived_state(el);
}
};
}}
}
UseScrollReturn {
x: internal_x.into(),

View file

@ -1,7 +1,7 @@
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::actions::Action;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::actions::Action;
use leptos::reactive::wrappers::read::Signal;
use send_wrapper::SendWrapper;
use std::sync::Arc;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
@ -54,7 +54,7 @@ pub fn use_service_worker_with_options(
let on_controller_change = options.on_controller_change.clone();
let js_closure = Closure::wrap(Box::new(move |_event: JsValue| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_controller_change();
}) as Box<dyn FnMut(JsValue)>)

View file

@ -1,5 +1,5 @@
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::cmp::Ordering;
use std::ops::DerefMut;

View file

@ -0,0 +1,330 @@
use crate::core::{ElementMaybeSignal, IntoElementMaybeSignal, MaybeRwSignal};
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use std::sync::Arc;
/// Automatically update the height of a textarea depending on the content.
///
/// ## Demo
///
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_textarea_autosize)
///
/// ## Usage
///
/// ### Simple example
///
/// ```
/// # use leptos::prelude::*;
/// # use leptos::html::Textarea;
/// # use leptos_use::{use_textarea_autosize, UseTextareaAutosizeReturn};
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
/// let textarea = NodeRef::new();
///
/// let UseTextareaAutosizeReturn {
/// content,
/// set_content,
/// trigger_resize
/// } = use_textarea_autosize(textarea);
///
/// view! {
/// <textarea
/// prop:value=content
/// on:input=move |evt| set_content.set(event_target_value(&evt))
/// node_ref=textarea
/// class="resize-none"
/// placeholder="What's on your mind?"
/// />
/// }
/// # }
/// ```
///
/// > Make sure that you set `box-sizing: border-box` on the textarea element.
/// >
/// > It's also recommended to reset the scrollbar styles for the textarea element to avoid
/// > incorrect height values for large amounts of text.
///
/// ```css
/// textarea {
/// -ms-overflow-style: none;
/// scrollbar-width: none;
/// }
///
/// textarea::-webkit-scrollbar {
/// display: none;
/// }
/// ```
///
/// ### With `rows` attribute
///
/// If you need support for the rows attribute on a textarea element, then you should set the
/// `style_prop` option to `"min-height"`.
///
/// ```
/// # use leptos::prelude::*;
/// # use leptos::html::Textarea;
/// # use leptos_use::{use_textarea_autosize_with_options, UseTextareaAutosizeOptions, UseTextareaAutosizeReturn};
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
/// let textarea = NodeRef::new();
///
/// let UseTextareaAutosizeReturn {
/// content,
/// set_content,
/// ..
/// } = use_textarea_autosize_with_options(
/// textarea,
/// UseTextareaAutosizeOptions::default().style_prop("min-height"),
/// );
///
/// view! {
/// <textarea
/// prop:value=content
/// on:input=move |evt| set_content.set(event_target_value(&evt))
/// node_ref=textarea
/// class="resize-none"
/// placeholder="What's on your mind?"
/// rows="3"
/// />
/// }
/// # }
/// ```
///
/// ## Server-Side Rendering
///
/// On the server this will always return an empty string as ´content` and a no-op `trigger_resize`.
// #[doc(cfg(feature = "use_textarea_autosize"))]
pub fn use_textarea_autosize<El, M>(el: El) -> UseTextareaAutosizeReturn<impl Fn() + Clone>
where
El: IntoElementMaybeSignal<web_sys::Element, M> + Clone,
{
use_textarea_autosize_with_options::<El, M>(el, UseTextareaAutosizeOptions::default())
}
/// 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"))]
pub fn use_textarea_autosize_with_options<El, M>(
el: El,
options: UseTextareaAutosizeOptions,
) -> UseTextareaAutosizeReturn<impl Fn() + Clone>
where
El: IntoElementMaybeSignal<web_sys::Element, M> + Clone,
{
#[cfg(not(feature = "ssr"))]
{
use wasm_bindgen::JsCast;
let el = el.into_element_maybe_signal();
let textarea = Signal::derive_local(move || {
el.get()
.map(|el| el.unchecked_into::<web_sys::HtmlTextAreaElement>())
});
let UseTextareaAutosizeOptions {
content,
watch: watch_fn,
on_resize,
style_target,
style_prop,
} = options;
let (content, set_content) = content.into_signal();
let (textarea_scroll_height, set_textarea_scroll_height) = signal(1);
let (textarea_old_width, set_textarea_old_width) = signal(0.0);
let trigger_resize = move || {
textarea.with_untracked(|textarea| {
if let Some(textarea) = textarea {
let mut height = "".to_string();
let border_offset =
if let Ok(Some(style)) = window().get_computed_style(textarea) {
(parse_num(
&style
.get_property_value("border-top-width")
.unwrap_or_default(),
) + parse_num(
&style
.get_property_value("border-bottom-width")
.unwrap_or_default(),
)) as i32
} else {
0
};
web_sys::HtmlElement::style(textarea)
.set_property(&style_prop, "1px")
.ok();
set_textarea_scroll_height.set(textarea.scroll_height() + border_offset + 1);
if let Some(style_target) = style_target.get() {
// If style target is provided update its height
style_target
.unchecked_into::<web_sys::HtmlElement>()
.style()
.set_property(
&style_prop,
&format!("{}px", textarea_scroll_height.get_untracked()),
)
.ok();
} else {
// else update textarea's height by updating height variable
height = format!("{}px", textarea_scroll_height.get_untracked());
}
web_sys::HtmlElement::style(textarea)
.set_property(&style_prop, &height)
.ok();
}
})
};
Effect::watch(
move || {
content.with(|_| ());
textarea.with(|_| ());
},
{
let trigger_resize = trigger_resize.clone();
move |_, _, _| {
trigger_resize();
}
},
true,
);
Effect::watch(
move || textarea_scroll_height.track(),
move |_, _, _| {
on_resize();
},
false,
);
crate::use_resize_observer(textarea, {
let trigger_resize = trigger_resize.clone();
move |entries, _| {
for entry in entries {
let width = entry.content_rect().width();
if width != textarea_old_width.get_untracked() {
set_textarea_old_width.set(width);
trigger_resize();
}
}
}
});
Effect::watch(
move || watch_fn(),
{
let trigger_resize = trigger_resize.clone();
move |_, _, _| {
trigger_resize();
}
},
false,
);
UseTextareaAutosizeReturn {
content,
set_content,
trigger_resize,
}
}
#[cfg(feature = "ssr")]
{
let _ = el;
let _ = options;
let (content, set_content) = signal("".to_string());
UseTextareaAutosizeReturn {
content: content.into(),
set_content,
trigger_resize: || {},
}
}
}
/// Options for [`fn@crate::use_textarea_autosize_with_options`].
// #[doc(cfg(feature = "use_textarea_autosize"))]
#[derive(DefaultBuilder)]
#[cfg_attr(feature = "ssr", allow(dead_code))]
pub struct UseTextareaAutosizeOptions {
/// Textarea content
#[builder(into)]
content: MaybeRwSignal<String>,
/// Watch sources that should trigger a textarea resize
watch: Arc<dyn Fn() + Send + Sync>,
/// Function called when the textarea size changes
on_resize: Arc<dyn Fn() + Send + Sync>,
/// Specify style target to apply the height based on textarea content.
/// If not provided it will use textarea it self.
#[builder(skip)]
style_target: ElementMaybeSignal<web_sys::Element>,
/// Specify the style property that will be used to manipulate height.
/// Should be `"height"` or `"min-height"`. Default value is `"height"`.
#[builder(into)]
style_prop: String,
}
impl Default for UseTextareaAutosizeOptions {
fn default() -> Self {
Self {
content: MaybeRwSignal::default(),
watch: Arc::new(|| ()),
on_resize: Arc::new(|| ()),
style_target: Default::default(),
style_prop: "height".to_string(),
}
}
}
impl UseTextareaAutosizeOptions {
/// List of elementss that should not trigger the callback. Defaults to `[]`.
#[cfg_attr(feature = "ssr", allow(dead_code))]
pub fn style_target<M>(
self,
style_target: impl IntoElementMaybeSignal<web_sys::Element, M>,
) -> Self {
Self {
style_target: style_target.into_element_maybe_signal(),
..self
}
}
}
/// Return type of [`fn@crate::use_textarea_autosize`].
// #[doc(cfg(feature = "use_textarea_autosize"))]
pub struct UseTextareaAutosizeReturn<F>
where
F: Fn() + Clone,
{
/// The textarea content
pub content: Signal<String>,
/// Set the textarea content
pub set_content: WriteSignal<String>,
/// Function to trigger a textarea resize manually
pub trigger_resize: F,
}
#[cfg(not(feature = "ssr"))]
fn parse_num(s: &str) -> u32 {
s.chars()
.map_while(|c| c.to_digit(10))
.fold(0, |acc, digit| acc * 10 + digit)
}

View file

@ -93,9 +93,7 @@ where
*timer.lock().unwrap() = None;
#[cfg(debug_assertions)]
let _z =
leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter(
);
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
callback(arg);
}

View file

@ -5,7 +5,7 @@ use crate::{
};
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::rc::Rc;
/// Reactive current timestamp.
@ -87,7 +87,7 @@ pub fn use_timestamp_with_controls_and_options(options: UseTimestampOptions) ->
update();
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
callback(ts.get_untracked());
}

View file

@ -1,5 +1,4 @@
use crate::core::MaybeRwSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use wasm_bindgen::{JsCast, JsValue};
@ -62,7 +61,8 @@ pub fn use_user_media_with_options(
let (stream, set_stream) = signal_local(None::<Result<web_sys::MediaStream, JsValue>>);
let _start = move || async move {
cfg_if! { if #[cfg(not(feature = "ssr"))] {
#[cfg(not(feature = "ssr"))]
{
if stream.get_untracked().is_some() {
return;
}
@ -70,10 +70,13 @@ pub fn use_user_media_with_options(
let stream = create_media(video, audio).await;
set_stream.update(|s| *s = Some(stream));
} else {
}
#[cfg(feature = "ssr")]
{
let _ = video;
let _ = audio;
}}
}
};
let _stop = move || {
@ -87,8 +90,9 @@ pub fn use_user_media_with_options(
};
let start = move || {
cfg_if! { if #[cfg(not(feature = "ssr"))] {
leptos::spawn::spawn_local(async move {
#[cfg(not(feature = "ssr"))]
{
leptos::task::spawn_local(async move {
_start().await;
stream.with_untracked(move |stream| {
if let Some(Ok(_)) = stream {
@ -96,7 +100,7 @@ pub fn use_user_media_with_options(
}
});
});
}}
}
};
let stop = move || {
@ -108,7 +112,7 @@ pub fn use_user_media_with_options(
move || enabled.get(),
move |enabled, _, _| {
if *enabled {
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
_start().await;
});
} else {

View file

@ -31,7 +31,7 @@ pub use web_sys::LockMode;
///
/// # #[component]
/// # fn Demo() -> impl IntoView {
/// leptos::spawn::spawn_local(async {
/// leptos::task::spawn_local(async {
/// let res = use_web_lock("my_lock", my_process).await;
/// assert!(matches!(res, Ok(42)));
/// });
@ -44,7 +44,6 @@ pub use web_sys::LockMode;
///
/// On the server this returns `Err(UseWebLockError::Server)` and the task is not executed.
// #[doc(cfg(feature = "use_web_lock"))]
pub async fn use_web_lock<C, F, R>(name: &str, callback: C) -> Result<R, UseWebLockError>
where
C: FnOnce(web_sys::Lock) -> F + 'static,

View file

@ -2,7 +2,7 @@ use crate::{use_supported, use_window};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
use std::rc::Rc;
use wasm_bindgen::JsValue;
@ -76,7 +76,7 @@ pub fn use_web_notification_with_options(
let on_click = Rc::clone(&options.on_click);
move |e: web_sys::Event| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_click(e);
}
@ -87,7 +87,7 @@ pub fn use_web_notification_with_options(
let on_close = Rc::clone(&options.on_close);
move |e: web_sys::Event| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_close(e);
}
@ -98,7 +98,7 @@ pub fn use_web_notification_with_options(
let on_error = Rc::clone(&options.on_error);
move |e: web_sys::Event| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_error(e);
}
@ -109,7 +109,7 @@ pub fn use_web_notification_with_options(
let on_show = Rc::clone(&options.on_show);
move |e: web_sys::Event| {
#[cfg(debug_assertions)]
let _z = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_show(e);
}
@ -134,7 +134,7 @@ pub fn use_web_notification_with_options(
let on_error_closure = on_error_closure.clone();
let on_show_closure = on_show_closure.clone();
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
set_permission.set(request_web_notification_permission().await);
let mut notification_options = web_sys::NotificationOptions::from(&options);
@ -169,7 +169,7 @@ pub fn use_web_notification_with_options(
}
};
leptos::spawn::spawn_local(async move {
leptos::task::spawn_local(async move {
set_permission.set(request_web_notification_permission().await);
});

View file

@ -285,7 +285,7 @@ where
let (ready_state, set_ready_state) = signal(ConnectionReadyState::Closed);
let (message, set_message) = signal(None);
let ws_ref: StoredValue<Option<WebSocket>, _> = StoredValue::new_local(None);
let ws_signal = RwSignal::new_local(None::<WebSocket>);
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = StoredValue::new(None);
@ -308,8 +308,8 @@ where
if !manually_closed_ref.get_value()
&& !reconnect_limit.is_exceeded_by(reconnect_times_ref.get_value())
&& ws_ref
.get_value()
&& ws_signal
.get_untracked()
.map_or(false, |ws: WebSocket| ws.ready_state() != WebSocket::OPEN)
{
reconnect_timer_ref.set_value(
@ -338,7 +338,7 @@ where
Some(Arc::new(move || {
reconnect_timer_ref.set_value(None);
if let Some(web_socket) = ws_ref.get_value() {
if let Some(web_socket) = ws_signal.get_untracked() {
let _ = web_socket.close();
}
@ -371,7 +371,7 @@ where
}
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_open(e);
@ -412,7 +412,7 @@ where
let txt = String::from(&txt);
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_message_raw(&txt);
@ -422,7 +422,7 @@ where
match C::decode_str(&txt) {
Ok(val) => {
#[cfg(debug_assertions)]
let prev = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let prev = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_message(&val);
@ -443,7 +443,7 @@ where
let array = array.to_vec();
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_message_raw_bytes(&array);
@ -453,7 +453,7 @@ where
match C::decode_bin(array.as_slice()) {
Ok(val) => {
#[cfg(debug_assertions)]
let prev = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let prev = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_message(&val);
@ -489,7 +489,7 @@ where
}
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_error(UseWebSocketError::Event(e));
@ -518,7 +518,7 @@ where
}
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_close(e);
@ -532,7 +532,7 @@ where
onclose_closure.forget();
}
ws_ref.set_value(Some(web_socket));
ws_signal.set(Some(web_socket));
}))
});
}
@ -541,7 +541,7 @@ where
let send_str = {
Box::new(move |data: &str| {
if ready_state.get_untracked() == ConnectionReadyState::Open {
if let Some(web_socket) = ws_ref.get_value() {
if let Some(web_socket) = ws_signal.get_untracked() {
let _ = web_socket.send_with_str(data);
}
}
@ -551,7 +551,7 @@ where
// Send bytes
let send_bytes = move |data: &[u8]| {
if ready_state.get_untracked() == ConnectionReadyState::Open {
if let Some(web_socket) = ws_ref.get_value() {
if let Some(web_socket) = ws_signal.get_untracked() {
let _ = web_socket.send_with_u8_array(data);
}
}
@ -589,7 +589,7 @@ where
move || {
manually_closed_ref.set_value(true);
if let Some(web_socket) = ws_ref.get_value() {
if let Some(web_socket) = ws_signal.get_untracked() {
let _ = web_socket.close();
}
}
@ -611,7 +611,7 @@ where
UseWebSocketReturn {
ready_state: ready_state.into(),
message: message.into(),
ws: ws_ref.get_value(),
ws: ws_signal.into(),
open,
close,
send,
@ -716,7 +716,7 @@ where
/// Latest message received from `WebSocket`.
pub message: Signal<Option<Rx>>,
/// The `WebSocket` instance.
pub ws: Option<WebSocket>,
pub ws: Signal<Option<WebSocket>, LocalStorage>,
/// Opens the `WebSocket` connection
pub open: OpenFn,
/// Closes the `WebSocket` connection

View file

@ -52,7 +52,7 @@ where
let last_return_val = Arc::clone(&last_return_value);
let invoke = move || {
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
let return_value = _invoke();

View file

@ -58,7 +58,7 @@ where
let last_return_val = Arc::clone(&last_return_value);
let invoke = move || {
#[cfg(debug_assertions)]
let zone = leptos::reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
let return_value = _invoke();

View file

@ -1,4 +1,4 @@
use leptos::reactive_graph::wrappers::read::Signal;
use leptos::reactive::wrappers::read::Signal;
/// Pausable effect
pub struct Pausable<PauseFn, ResumeFn>

Some files were not shown because too many files have changed in this diff Show more