use crate::{
Location, NavigateOptions, NavigationError, Params, ParamsError, ParamsMap,
RouteContext, RouterContext,
};
use leptos::{create_memo, signal_prelude::*, use_context, Memo, Scope};
use std::{borrow::Cow, rc::Rc, str::FromStr};
pub fn create_query_signal<T>(
cx: Scope,
key: impl Into<Cow<'static, str>>,
) -> (Memo<Option<T>>, SignalSetter<Option<T>>)
where
T: FromStr + ToString + PartialEq,
{
let key = key.into();
let query_map = use_query_map(cx);
let navigate = use_navigate(cx);
let route = use_route(cx);
let get = create_memo(cx, {
let key = key.clone();
move |_| {
query_map
.with(|map| map.get(&key).and_then(|value| value.parse().ok()))
}
});
let set = SignalSetter::map(cx, move |value: Option<T>| {
let mut new_query_map = query_map.get();
match value {
Some(value) => {
new_query_map.insert(key.to_string(), value.to_string());
}
None => {
new_query_map.remove(&key);
}
}
let qs = new_query_map.to_query_string();
let path = route.path();
let new_url = format!("{path}{qs}");
let _ = navigate(&new_url, NavigateOptions::default());
});
(get, set)
}
pub fn use_router(cx: Scope) -> RouterContext {
if let Some(router) = use_context::<RouterContext>(cx) {
router
} else {
leptos::leptos_dom::debug_warn!(
"You must call use_router() within a <Router/> component"
);
panic!("You must call use_router() within a <Router/> component");
}
}
pub fn use_route(cx: Scope) -> RouteContext {
use_context::<RouteContext>(cx).unwrap_or_else(|| use_router(cx).base())
}
pub fn use_location(cx: Scope) -> Location {
use_router(cx).inner.location.clone()
}
pub fn use_params_map(cx: Scope) -> Memo<ParamsMap> {
let route = use_route(cx);
route.params()
}
pub fn use_params<T: Params>(cx: Scope) -> Memo<Result<T, ParamsError>>
where
T: PartialEq,
{
let route = use_route(cx);
create_memo(cx, move |_| route.params().with(T::from_map))
}
pub fn use_query_map(cx: Scope) -> Memo<ParamsMap> {
use_router(cx).inner.location.query
}
pub fn use_query<T: Params>(cx: Scope) -> Memo<Result<T, ParamsError>>
where
T: PartialEq,
{
let router = use_router(cx);
create_memo(cx, move |_| {
router.inner.location.query.with(|m| T::from_map(m))
})
}
pub fn use_resolved_path(
cx: Scope,
path: impl Fn() -> String + 'static,
) -> Memo<Option<String>> {
let route = use_route(cx);
create_memo(cx, move |_| {
let path = path();
if path.starts_with('/') {
Some(path)
} else {
route.resolve_path_tracked(&path).map(String::from)
}
})
}
pub fn use_navigate(
cx: Scope,
) -> impl Fn(&str, NavigateOptions) -> Result<(), NavigationError> {
let router = use_router(cx);
move |to, options| {
Rc::clone(&router.inner).navigate_from_route(to, &options)
}
}
pub(crate) fn use_is_back_navigation(cx: Scope) -> ReadSignal<bool> {
let router = use_router(cx);
router.inner.is_back.read_only()
}