瀏覽代碼

create example

Evan Almloff 2 年之前
父節點
當前提交
3678fa1d3f

+ 1 - 1
packages/router-macro/src/lib.rs

@@ -244,7 +244,7 @@ impl RouteEnum {
         });
 
         quote! {
-            impl<'a> TryFrom<&'a str> for #name {
+            impl<'a> core::convert::TryFrom<&'a str> for #name {
                 type Error = <Self as std::str::FromStr>::Err;
 
                 fn try_from(s: &'a str) -> Result<Self, Self::Error> {

+ 119 - 53
packages/router/examples/simple_routes.rs

@@ -1,81 +1,147 @@
 #![allow(non_snake_case)]
 
 use dioxus::prelude::*;
-use dioxus_router::{history::MemoryHistory, prelude::*};
+use dioxus_router::prelude::*;
+use serde::{Deserialize, Serialize};
+use std::str::FromStr;
 
 fn main() {
-    #[cfg(not(feature = "web"))]
-    dioxus_desktop::launch(App);
-    #[cfg(feature = "web")]
-    dioxus_web::launch(App);
-}
+    #[cfg(not(target_arch = "wasm32"))]
+    dioxus_desktop::launch(root);
 
-fn App(cx: Scope) -> Element {
-    use_router(
-        cx,
-        &|| {
-            #[cfg(not(feature = "web"))]
-            let history = MemoryHistory::default();
-            #[cfg(feature = "web")]
-            let history = dioxus_router::history::WebHistory::new(None, true);
+    #[cfg(target_arch = "wasm32")]
+    dioxus_web::launch(root);
+}
 
-            RouterConfiguration {
-                history: Box::new(history),
+fn root(cx: Scope) -> Element {
+    render! {
+        Router::<Route> {
+            config: RouterConfiguration {
+                history: {
+                    #[cfg(not(target_arch = "wasm32"))]
+                    let history = Box::<MemoryHistory::<Route>>::default();
+                    #[cfg(target_arch = "wasm32")]
+                    let history = Box::<WebHistory::<Route>>::default();
+                    history
+                },
                 ..Default::default()
             }
-        },
-        &|| {
-            Segment::content(comp(Home))
-                .fixed("apple", comp(Apple))
-                .fixed("potato", Route::content(comp(Potato)).name::<PotatoName>())
-                .fixed("earth_apple", named::<PotatoName>())
-        },
-    );
+        }
+    }
+}
 
+#[inline_props]
+fn UserFrame(cx: Scope, user_id: usize) -> Element {
     render! {
-        h1 { "Simple Example App" }
-        Outlet { }
-        Link {
-            target: named::<RootIndex>(),
-            "Go to root"
+        pre {
+            "UserFrame{{\n\tuser_id:{user_id}\n}}"
+        }
+        div {
+            background_color: "rgba(0,0,0,50%)",
+            "children:"
+            Outlet::<Route> {}
         }
     }
 }
 
-fn Home(cx: Scope) -> Element {
+#[inline_props]
+fn Route1(cx: Scope, user_id: usize, dynamic: usize, extra: String) -> Element {
     render! {
-        h2 { "Root Index" }
-        ul {
-            li { Link {
-                target: "/apple",
-                "Read about apples…"
-            } }
-            li { Link {
-                target: named::<PotatoName>(),
-                "Read about potatoes…"
-            } }
-            li { Link {
-                target: "/earth_apple",
-                "Read about earth apples (literal translation of a german word for potato)…"
-            } }
+        pre {
+            "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\textra:{extra}\n}}"
+        }
+        Link::<Route> {
+            target: Route::Route1 { user_id: *user_id, dynamic: *dynamic, extra: extra.clone() + "." },
+            "Route1 with extra+\".\""
+        }
+        p { "Footer" }
+        Link::<Route> {
+            target: Route::Route3 { dynamic: String::new() },
+            "Home"
         }
     }
 }
 
-fn Apple(cx: Scope) -> Element {
+#[inline_props]
+fn Route2(cx: Scope, user_id: usize) -> Element {
     render! {
-        h2 { "Apple" }
-        p {
-            "An apple is a tasty fruit. It grows on trees and many varieties are either red or "
-            "green."
+        pre {
+            "Route2{{\n\tuser_id:{user_id}\n}}"
+        }
+        (0..*user_id).map(|i| rsx!{ p { "{i}" } }),
+        p { "Footer" }
+        Link::<Route> {
+            target: Route::Route3 { dynamic: String::new() },
+            "Home"
         }
     }
 }
 
-struct PotatoName;
-fn Potato(cx: Scope) -> Element {
+#[inline_props]
+fn Route3(cx: Scope, dynamic: String) -> Element {
+    let router = use_router(cx);
+    let router_route = router.current();
+    let current_route = use_ref(cx, String::new);
+    let parsed = Route::from_str(&current_route.read());
+
+    let site_map = Route::SITE_MAP
+        .iter()
+        .flat_map(|seg| seg.flatten().into_iter())
+        .collect::<Vec<_>>();
+
     render! {
-        h2 { "Potato" }
-        p { "The potato grows underground. There are many recipes involving potatoes." }
+        input {
+            oninput: move |evt| {
+                *current_route.write() = evt.value.clone();
+            },
+            value: "{current_route.read()}"
+        }
+        "dynamic: {dynamic}"
+        Link::<Route> {
+            target: Route::Route2 { user_id: 8888 },
+            "hello world link"
+        }
+        p { "Site Map" }
+        pre { "{site_map:#?}" }
+        p { "Dynamic link" }
+        if let Ok(route) = parsed {
+            if route != router_route {
+                render! {
+                    Link::<Route> {
+                        target: route.clone(),
+                        "{route}"
+                    }
+                }
+            }
+            else{None}
+        }
+        else{None}
     }
 }
+
+#[rustfmt::skip]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Routable)]
+enum Route {
+    // Nests with parameters have types taken from child routes
+    #[nest("/user/:user_id")]
+        // Everything inside the nest has the added parameter `user_id: String`
+        // UserFrame is a layout component that will receive the `user_id: String` parameter
+        #[layout(UserFrame)]
+        // Route1 is a non-layout component that will receive the `user_id: String` and `dynamic: String` parameters
+        #[route("/:dynamic", Route1)]
+            Route1 {
+                // The type is taken from the first instance of the dynamic parameter
+                user_id: usize,
+                dynamic: usize,
+                extra: String,
+            },
+            // Route2 is a non-layout component that will receive the `user_id: String` parameter
+            #[route("/hello_world", Route2)]
+            // You can opt out of the layout by using the `!` prefix
+            #[layout(!UserFrame)]
+            Route2 { user_id: usize },
+        #[end_layout]
+    #[end_nest]
+    #[route("/:dynamic", Route3)]
+    Route3 { dynamic: String },
+}

+ 12 - 3
packages/router/src/components/router.rs

@@ -13,7 +13,10 @@ pub struct RouterCfg<R: Routable> {
     config: RefCell<Option<RouterConfiguration<R>>>,
 }
 
-impl<R: Routable> Default for RouterCfg<R> {
+impl<R: Routable> Default for RouterCfg<R>
+where
+    <R as FromStr>::Err: std::fmt::Display,
+{
     fn default() -> Self {
         Self {
             config: RefCell::new(Some(RouterConfiguration::default())),
@@ -31,14 +34,20 @@ impl<R: Routable> From<RouterConfiguration<R>> for RouterCfg<R> {
 
 /// The props for [`Router`].
 #[derive(Props)]
-pub struct RouterProps<R: Routable> {
+pub struct RouterProps<R: Routable>
+where
+    <R as FromStr>::Err: std::fmt::Display,
+{
     #[props(into)]
     initial_url: Option<String>,
     #[props(default, into)]
     config: RouterCfg<R>,
 }
 
-impl<R: Routable> PartialEq for RouterProps<R> {
+impl<R: Routable> PartialEq for RouterProps<R>
+where
+    <R as FromStr>::Err: std::fmt::Display,
+{
     fn eq(&self, _: &Self) -> bool {
         // prevent the router from re-rendering when the initial url or config changes
         true

+ 19 - 19
packages/router/src/history/memory.rs

@@ -6,12 +6,15 @@ use super::HistoryProvider;
 
 /// A [`HistoryProvider`] that stores all navigation information in memory.
 pub struct MemoryHistory<R: Routable> {
-    current: Option<R>,
+    current: R,
     history: Vec<R>,
     future: Vec<R>,
 }
 
-impl<R: Routable> MemoryHistory<R> {
+impl<R: Routable> MemoryHistory<R>
+where
+    <R as FromStr>::Err: std::fmt::Display,
+{
     /// Create a [`MemoryHistory`] starting at `path`.
     ///
     /// ```rust
@@ -24,16 +27,19 @@ impl<R: Routable> MemoryHistory<R> {
         let path = path.into();
 
         Ok(Self {
-            current: Some(R::from_str(&path)?),
+            current: R::from_str(&path)?,
             ..Default::default()
         })
     }
 }
 
-impl<R: Routable> Default for MemoryHistory<R> {
+impl<R: Routable> Default for MemoryHistory<R>
+where
+    <R as FromStr>::Err: std::fmt::Display,
+{
     fn default() -> Self {
         Self {
-            current: None,
+            current: "/".parse().unwrap_or_else(|err| panic!("{}", err)),
             history: Vec::new(),
             future: Vec::new(),
         }
@@ -42,7 +48,7 @@ impl<R: Routable> Default for MemoryHistory<R> {
 
 impl<R: Routable> HistoryProvider<R> for MemoryHistory<R> {
     fn current_route(&self) -> R {
-        self.current.clone().expect("current route is not set")
+        self.current.clone()
     }
 
     fn can_go_back(&self) -> bool {
@@ -51,10 +57,8 @@ impl<R: Routable> HistoryProvider<R> for MemoryHistory<R> {
 
     fn go_back(&mut self) {
         if let Some(last) = self.history.pop() {
-            let new = self.current.replace(last);
-            if let Some(new) = new {
-                self.future.push(new);
-            }
+            let old = std::mem::replace(&mut self.current, last);
+            self.future.push(old);
         }
     }
 
@@ -64,23 +68,19 @@ impl<R: Routable> HistoryProvider<R> for MemoryHistory<R> {
 
     fn go_forward(&mut self) {
         if let Some(next) = self.future.pop() {
-            let old = self.current.replace(next);
-            if let Some(old) = old {
-                self.history.push(old);
-            }
+            let old = std::mem::replace(&mut self.current, next);
+            self.history.push(old);
         }
     }
 
     fn push(&mut self, new: R) {
-        if let Some(current) = self.current.take() {
-            self.history.push(current);
-        }
-        self.current = Some(new);
+        let old = std::mem::replace(&mut self.current, new);
+        self.history.push(old);
         self.future.clear();
     }
 
     fn replace(&mut self, path: R) {
-        self.current = Some(path);
+        self.current = path;
     }
 }
 

+ 1 - 0
packages/router/src/lib.rs

@@ -46,6 +46,7 @@ pub mod hooks {
 pub mod prelude {
     pub use crate::components::*;
     pub use crate::contexts::*;
+    pub use crate::history::*;
     pub use crate::hooks::*;
     pub use crate::routable::*;
     pub use crate::router_cfg::RouterConfiguration;

+ 4 - 1
packages/router/src/router_cfg.rs

@@ -58,7 +58,10 @@ pub struct RouterConfiguration<R: Routable> {
     pub on_update: Option<RoutingCallback<R>>,
 }
 
-impl<R: Routable + Clone> Default for RouterConfiguration<R> {
+impl<R: Routable + Clone> Default for RouterConfiguration<R>
+where
+    <R as std::str::FromStr>::Err: std::fmt::Display,
+{
     fn default() -> Self {
         Self {
             failure_external_navigation: FailureExternalNavigation::<R>,