feat: 0.7 query signals

This commit is contained in:
Greg Johnston 2024-06-21 07:14:22 -04:00
parent f7ee0c4764
commit a32c71539d
7 changed files with 75 additions and 38 deletions

View File

@ -10,8 +10,6 @@ lto = true
[dependencies]
leptos = { path = "../../leptos", features = ["csr"] }
leptos_router = { path = "../../router", features = [] }
console_log = "1"
log = "0.4"
console_error_panic_hook = "0.1.7"
[dev-dependencies]

View File

@ -1,17 +1,17 @@
use leptos::prelude::*;
use leptos_router::*;
use leptos_router::hooks::query_signal;
/// A simple counter component.
///
/// You can use doc comments like this to document your component.
#[component]
pub fn SimpleQueryCounter() -> impl IntoView {
let (count, set_count) = create_query_signal::<i32>("count");
let (count, set_count) = query_signal::<i32>("count");
let clear = move |_| set_count.set(None);
let decrement = move |_| set_count.set(Some(count.get().unwrap_or(0) - 1));
let increment = move |_| set_count.set(Some(count.get().unwrap_or(0) + 1));
let (msg, set_msg) = create_query_signal::<String>("message");
let (msg, set_msg) = query_signal::<String>("message");
let update_msg = move |ev| {
let new_msg = event_target_value(&ev);
if new_msg.is_empty() {

View File

@ -1,16 +1,13 @@
use counter_url_query::SimpleQueryCounter;
use leptos::prelude::*;
use leptos_router::*;
use leptos_router::components::Router;
pub fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|| {
leptos::mount::mount_to_body(|| {
view! {
<Router>
<Routes>
<Route path="" view=SimpleQueryCounter />
</Routes>
<SimpleQueryCounter/>
</Router>
}
})

View File

@ -25,6 +25,7 @@ paste = "1"
once_cell = "1"
send_wrapper = "0.6"
thiserror = "1"
percent-encoding = { version = "2.3", optional = true }
[dependencies.web-sys]
version = "0.3"
@ -56,7 +57,7 @@ features = [
[features]
tracing = ["dep:tracing"]
ssr = []
ssr = ["dep:percent-encoding"]
nightly = []
[package.metadata.docs.rs]

View File

@ -4,14 +4,42 @@ use crate::{
navigate::NavigateOptions,
params::{Params, ParamsError, ParamsMap},
};
use leptos::oco::Oco;
use reactive_graph::{
computed::{ArcMemo, Memo},
owner::use_context,
signal::{ArcRwSignal, ReadSignal},
traits::{Get, With},
traits::{Get, GetUntracked, With},
wrappers::write::SignalSetter,
};
use std::str::FromStr;
use tachys::renderer::Renderer;
/*
#[track_caller]
#[deprecated = "This has been renamed to `query_signal` to match Rust naming \
conventions."]
pub fn create_query_signal<T>(
key: impl Into<Oco<'static, str>>,
) -> (Memo<Option<T>>, SignalSetter<Option<T>>)
where
T: FromStr + ToString + PartialEq + Send + Sync,
{
query_signal(key)
}
#[track_caller]
#[deprecated = "This has been renamed to `query_signal_with_options` to mtch \
Rust naming conventions."]
pub fn create_query_signal_with_options<T>(
key: impl Into<Oco<'static, str>>,
nav_options: NavigateOptions,
) -> (Memo<Option<T>>, SignalSetter<Option<T>>)
where
T: FromStr + ToString + PartialEq + Send + Sync,
{
query_signal_with_options(key, nav_options)
}
/// Constructs a signal synchronized with a specific URL query parameter.
///
/// The function creates a bidirectional sync mechanism between the state encapsulated in a signal and a URL query parameter.
@ -26,12 +54,12 @@ use tachys::renderer::Renderer;
/// The URL parameter can be cleared by setting the signal to `None`.
///
/// ```rust
/// use leptos::*;
/// use leptos_router::*;
/// use leptos::prelude::*;
/// use leptos_router::hooks::query_signal;
///
/// #[component]
/// pub fn SimpleQueryCounter() -> impl IntoView {
/// let (count, set_count) = create_query_signal::<i32>("count");
/// let (count, set_count) = query_signal::<i32>("count");
/// let clear = move |_| set_count.set(None);
/// let decrement =
/// move |_| set_count.set(Some(count.get().unwrap_or(0) - 1));
@ -49,29 +77,29 @@ use tachys::renderer::Renderer;
/// }
/// ```
#[track_caller]
pub fn create_query_signal<T>(
pub fn query_signal<T>(
key: impl Into<Oco<'static, str>>,
) -> (Memo<Option<T>>, SignalSetter<Option<T>>)
where
T: FromStr + ToString + PartialEq,
T: FromStr + ToString + PartialEq + Send + Sync,
{
create_query_signal_with_options::<T>(key, NavigateOptions::default())
query_signal_with_options::<T>(key, NavigateOptions::default())
}
#[track_caller]
pub fn create_query_signal_with_options<T>(
pub fn query_signal_with_options<T>(
key: impl Into<Oco<'static, str>>,
nav_options: NavigateOptions,
) -> (Memo<Option<T>>, SignalSetter<Option<T>>)
where
T: FromStr + ToString + PartialEq,
T: FromStr + ToString + PartialEq + Send + Sync,
{
let mut key: Oco<'static, str> = key.into();
let query_map = use_query_map();
let navigate = use_navigate();
let location = use_location();
let get = create_memo({
let get = Memo::new({
let key = key.clone_inplace();
move |_| {
query_map
@ -106,7 +134,7 @@ pub(crate) fn has_router() -> bool {
/// Returns the current [`RouterContext`], containing information about the router's state.
#[track_caller]
pub fn use_router() -> RouterContext {
pub(crate) fn use_router() -> RouterContext {
if let Some(router) = use_context::<RouterContext>() {
router
} else {
@ -116,7 +144,7 @@ pub fn use_router() -> RouterContext {
);
panic!("You must call use_router() within a <Router/> component");
}
}*/
}
/// Returns the current [`Location`], which contains reactive variables
#[track_caller]

View File

@ -61,6 +61,21 @@ impl Url {
provide_context(ServerActionError::new(path, err))
}
}
pub fn escape(s: &str) -> String {
#[cfg(not(feature = "ssr"))]
{
js_sys::encode_uri_component(s).as_string().unwrap()
}
#[cfg(feature = "ssr")]
{
percent_encoding::utf8_percent_encode(
s,
percent_encoding::NON_ALPHANUMERIC,
)
.to_string()
}
}
}
/// A reactive description of the current URL, containing equivalents to the local parts of

View File

@ -1,4 +1,5 @@
use std::{borrow::Cow, str::FromStr, sync::Arc};
use crate::location::{unescape, Url};
use std::{borrow::Cow, mem, str::FromStr, sync::Arc};
use thiserror::Error;
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
@ -17,18 +18,19 @@ impl ParamsMap {
Self(Vec::with_capacity(capacity))
}
/*
/// Inserts a value into the map.
#[inline(always)]
pub fn insert(&mut self, key: String, value: String) -> Option<String> {
use crate::history::url::unescape;
let value = unescape(&value);
self.0.insert(key, value)
if let Some(prev) = self.0.iter().position(|(k, _)| k == &key) {
return Some(mem::replace(&mut self.0[prev].1, value));
}
self.0.push((key.into(), value));
None
}
*/
/// Gets an owned value from the map.
#[inline(always)]
pub fn get(&self, key: &str) -> Option<String> {
self.0
.iter()
@ -36,7 +38,6 @@ impl ParamsMap {
}
/// Gets a referenc to a value from the map.
#[inline(always)]
pub fn get_str(&self, key: &str) -> Option<&str> {
self.0
.iter()
@ -54,17 +55,15 @@ impl ParamsMap {
None
}
/*
/// Converts the map to a query string.
pub fn to_query_string(&self) -> String {
use crate::history::url::escape;
let mut buf = String::new();
if !self.0.is_empty() {
buf.push('?');
for (k, v) in &self.0 {
buf.push_str(&escape(k));
buf.push_str(&Url::escape(k));
buf.push('=');
buf.push_str(&escape(v));
buf.push_str(&Url::escape(v));
buf.push('&');
}
if buf.len() > 1 {
@ -73,7 +72,6 @@ impl ParamsMap {
}
buf
}
*/
}
impl<K, V> FromIterator<(K, V)> for ParamsMap