it almost lives?
This commit is contained in:
1 parent
d637ef187c
commit
5f0dd3af3e
43 files changed
+506
-244
No files matched your search
@@ -1,15 +1,57 @@
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use log::error;
|
||||
|
||||
use crate::navigation::NavigationTarget;
|
||||
use crate::prelude::*;
|
||||
use crate::prelude::{AnyDisplay, Routable};
|
||||
use crate::utils::use_router_internal::use_router_internal;
|
||||
|
||||
/// The properties for a [`GenericLink`].
|
||||
/// Something that can be converted into a [`NavigationTarget`].
|
||||
pub enum IntoRoutable {
|
||||
/// A raw string target.
|
||||
FromStr(String),
|
||||
/// A internal target.
|
||||
Route(Box<dyn AnyDisplay>),
|
||||
}
|
||||
|
||||
impl<R: Routable> From<R> for IntoRoutable {
|
||||
fn from(value: R) -> Self {
|
||||
IntoRoutable::Route(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Routable> From<NavigationTarget<R>> for IntoRoutable {
|
||||
fn from(value: NavigationTarget<R>) -> Self {
|
||||
match value {
|
||||
NavigationTarget::Internal(route) => IntoRoutable::Route(Box::new(route)),
|
||||
NavigationTarget::External(url) => IntoRoutable::FromStr(url),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for IntoRoutable {
|
||||
fn from(value: String) -> Self {
|
||||
IntoRoutable::FromStr(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for IntoRoutable {
|
||||
fn from(value: &String) -> Self {
|
||||
IntoRoutable::FromStr(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for IntoRoutable {
|
||||
fn from(value: &str) -> Self {
|
||||
IntoRoutable::FromStr(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// The properties for a [`Link`].
|
||||
#[derive(Props)]
|
||||
pub struct GenericLinkProps<'a, R: Routable> {
|
||||
pub struct LinkProps<'a> {
|
||||
/// A class to apply to the generate HTML anchor tag if the `target` route is active.
|
||||
pub active_class: Option<&'a str>,
|
||||
/// The children to render within the generated HTML anchor tag.
|
||||
@@ -23,7 +65,7 @@ pub struct GenericLinkProps<'a, R: Routable> {
|
||||
pub id: Option<&'a str>,
|
||||
/// When [`true`], the `target` route will be opened in a new tab.
|
||||
///
|
||||
/// This does not change whether the [`GenericLink`] is active or not.
|
||||
/// This does not change whether the [`Link`] is active or not.
|
||||
#[props(default)]
|
||||
pub new_tab: bool,
|
||||
/// The onclick event handler.
|
||||
@@ -42,10 +84,10 @@ pub struct GenericLinkProps<'a, R: Routable> {
|
||||
pub rel: Option<&'a str>,
|
||||
/// The navigation target. Roughly equivalent to the href attribute of an HTML anchor tag.
|
||||
#[props(into)]
|
||||
pub to: NavigationTarget<R>,
|
||||
pub to: IntoRoutable,
|
||||
}
|
||||
|
||||
impl<R: Routable> Debug for GenericLinkProps<'_, R> {
|
||||
impl Debug for LinkProps<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("LinkProps")
|
||||
.field("active_class", &self.active_class)
|
||||
@@ -56,27 +98,26 @@ impl<R: Routable> Debug for GenericLinkProps<'_, R> {
|
||||
.field("onclick", &self.onclick.as_ref().map(|_| "onclick is set"))
|
||||
.field("onclick_only", &self.onclick_only)
|
||||
.field("rel", &self.rel)
|
||||
.field("to", &self.to.to_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A link to navigate to another route.
|
||||
///
|
||||
/// Only works as descendant of a [`GenericRouter`] component, otherwise it will be inactive.
|
||||
/// Only works as descendant of a [`Router`] component, otherwise it will be inactive.
|
||||
///
|
||||
/// Unlike a regular HTML anchor, a [`GenericLink`] allows the router to handle the navigation and doesn't
|
||||
/// Unlike a regular HTML anchor, a [`Link`] allows the router to handle the navigation and doesn't
|
||||
/// cause the browser to load a new page.
|
||||
///
|
||||
/// However, in the background a [`GenericLink`] still generates an anchor, which you can use for styling
|
||||
/// However, in the background a [`Link`] still generates an anchor, which you can use for styling
|
||||
/// as normal.
|
||||
///
|
||||
/// # External targets
|
||||
/// When the [`GenericLink`]s target is an [`NavigationTarget::External`] target, that is used as the `href` directly. This
|
||||
/// means that a [`GenericLink`] can always navigate to an [`NavigationTarget::External`] target, even if the [`HistoryProvider`] does not support it.
|
||||
/// When the [`Link`]s target is an [`NavigationTarget::External`] target, that is used as the `href` directly. This
|
||||
/// means that a [`Link`] can always navigate to an [`NavigationTarget::External`] target, even if the [`HistoryProvider`] does not support it.
|
||||
///
|
||||
/// # Panic
|
||||
/// - When the [`GenericLink`] is not nested within a [`GenericRouter`], but
|
||||
/// - When the [`Link`] is not nested within a [`Router`], but
|
||||
/// only in debug builds.
|
||||
///
|
||||
/// # Example
|
||||
@@ -92,7 +133,7 @@ impl<R: Routable> Debug for GenericLinkProps<'_, R> {
|
||||
///
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// Router {}
|
||||
/// Router::<Route> {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@@ -122,8 +163,8 @@ impl<R: Routable> Debug for GenericLinkProps<'_, R> {
|
||||
/// # );
|
||||
/// ```
|
||||
#[allow(non_snake_case)]
|
||||
pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R>>) -> Element {
|
||||
let GenericLinkProps {
|
||||
pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
|
||||
let LinkProps {
|
||||
active_class,
|
||||
children,
|
||||
class,
|
||||
@@ -133,10 +174,11 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R
|
||||
onclick_only,
|
||||
rel,
|
||||
to,
|
||||
..
|
||||
} = cx.props;
|
||||
|
||||
// hook up to router
|
||||
let router = match use_router_internal::<R>(cx) {
|
||||
let router = match use_router_internal(cx) {
|
||||
Some(r) => r,
|
||||
#[allow(unreachable_code)]
|
||||
None => {
|
||||
@@ -148,9 +190,15 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R
|
||||
}
|
||||
};
|
||||
|
||||
let current_route = router.current();
|
||||
let current_url = current_route.to_string();
|
||||
let href = to.to_string();
|
||||
let current_url = router.current_route_string();
|
||||
let href = match to {
|
||||
IntoRoutable::FromStr(url) => url.to_string(),
|
||||
IntoRoutable::Route(route) => route.to_string(),
|
||||
};
|
||||
let parsed_route: NavigationTarget<Box<dyn Any>> = match router.route_from_str(&href) {
|
||||
Ok(route) => NavigationTarget::Internal(route.into()),
|
||||
Err(err) => NavigationTarget::External(err),
|
||||
};
|
||||
let ac = active_class
|
||||
.and_then(|active_class| (href == current_url).then(|| format!(" {active_class}")))
|
||||
.unwrap_or_default();
|
||||
@@ -159,7 +207,7 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R
|
||||
let class = format!("{}{ac}", class.unwrap_or_default());
|
||||
let tag_target = new_tab.then_some("_blank").unwrap_or_default();
|
||||
|
||||
let is_external = matches!(to, NavigationTarget::External(_));
|
||||
let is_external = matches!(parsed_route, NavigationTarget::External(_));
|
||||
let is_router_nav = !is_external && !new_tab;
|
||||
let prevent_default = is_router_nav.then_some("onclick").unwrap_or_default();
|
||||
let rel = rel
|
||||
@@ -169,7 +217,15 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R
|
||||
let do_default = onclick.is_none() || !onclick_only;
|
||||
let action = move |event| {
|
||||
if do_default && is_router_nav {
|
||||
router.push(to.clone());
|
||||
let href = match to {
|
||||
IntoRoutable::FromStr(url) => url.to_string(),
|
||||
IntoRoutable::Route(route) => route.to_string(),
|
||||
};
|
||||
let parsed_route: NavigationTarget<Box<dyn Any>> = match router.route_from_str(&href) {
|
||||
Ok(route) => NavigationTarget::Internal(route.into()),
|
||||
Err(err) => NavigationTarget::External(err),
|
||||
};
|
||||
router.push_any(parsed_route);
|
||||
}
|
||||
|
||||
if let Some(handler) = onclick {
|
||||
|
||||
Reference in new issue
Block a user