浏览代码

active_class prop for Router

Maccesch 3 年之前
父节点
当前提交
9a23ee4612

+ 2 - 1
.gitignore

@@ -8,4 +8,5 @@ Cargo.lock
 !.vscode/tasks.json
 !.vscode/launch.json
 !.vscode/extensions.json
-tarpaulin-report.html
+tarpaulin-report.html
+.idea

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

@@ -1,4 +1,5 @@
 #[derive(Default)]
 pub struct RouterCfg {
     pub base_url: Option<String>,
+    pub active_class: Option<String>,
 }

+ 15 - 4
packages/router/src/components/link.rs

@@ -28,7 +28,9 @@ pub struct LinkProps<'a> {
 
     /// Set the class added to the inner link when the current route is the same as the "to" route.
     ///
-    /// By default set to `"active"`.
+    /// To set all of the active classes inside a Router at the same time use the `active_class`
+    /// prop on the Router component. If both the Router prop as well as this prop are provided then
+    /// this one has precedence. By default set to `"active"`.
     #[props(default, strip_option)]
     pub active_class: Option<&'a str>,
 
@@ -97,13 +99,22 @@ pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
     let outerlink = (*autodetect && is_http) || *external;
     let prevent_default = if outerlink { "" } else { "onclick" };
 
+    let active_class_name = match active_class {
+        Some(c) => (*c).into(),
+        None => {
+            let active_from_router = match svc {
+                Some(service) => service.cfg.active_class.clone(),
+                None => None,
+            };
+            active_from_router.unwrap_or("active".into())
+        }
+    };
+
     let route = use_route(&cx);
     let url = route.url();
     let path = url.path();
     let active = path == cx.props.to;
-    let active_class = active
-        .then(|| active_class.unwrap_or("active"))
-        .unwrap_or("");
+    let active_class = if active { active_class_name } else { "".into() };
 
     cx.render(rsx! {
         a {

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

@@ -28,6 +28,13 @@ pub struct RouterProps<'a> {
     /// This lets you easily implement redirects
     #[props(default)]
     pub onchange: EventHandler<'a, Arc<RouterCore>>,
+
+    /// Set the active class of all Link components contained in this router.
+    ///
+    /// This is useful if you don't want to repeat the same `active_class` prop value in every Link.
+    /// By default set to `"active"`.
+    #[props(default, strip_option)]
+    pub active_class: Option<&'a str>,
 }
 
 /// A component that conditionally renders children based on the current location of the app.
@@ -40,9 +47,13 @@ pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
     let svc = cx.use_hook(|_| {
         let (tx, mut rx) = futures_channel::mpsc::unbounded::<RouteEvent>();
 
-        let base_url = cx.props.base_url.map(|s| s.to_string());
-
-        let svc = RouterCore::new(tx, RouterCfg { base_url });
+        let svc = RouterCore::new(
+            tx,
+            RouterCfg {
+                base_url: cx.props.base_url.map(|s| s.to_string()),
+                active_class: cx.props.active_class.map(|s| s.to_string()),
+            },
+        );
 
         cx.spawn({
             let svc = svc.clone();

+ 1 - 0
packages/router/tests/web_router.rs

@@ -22,6 +22,7 @@ fn simple_test() {
         cx.render(rsx! {
             Router {
                 onchange: move |route: RouterService| log::info!("route changed to {:?}", route.current_location()),
+                active_class: "is-active",
                 Route { to: "/", Home {} }
                 Route { to: "blog"
                     Route { to: "/", BlogList {} }

+ 19 - 0
packages/router/usage.md

@@ -62,6 +62,25 @@ Link { to: "/blog/welcome",
 }
 ```
 
+#### Active `Links`
+
+When your app has been navigated to a route that matches the route of a `Link`, this `Link` becomes 'active'.
+Active links have a special class attached to them. By default it is simply called `"active"` but it can be
+modified on the `Link` level or on the `Router` level. Both is done through the prop `active_class`.
+If the active class is given on both, the `Router` and the `Link`, the one on the `Link` has precedence.
+
+```rust
+Router {
+    active_class: "custom-active",  // All active links in this router get this class.
+    Link { to: "/", "Home" },
+    Link { 
+        to: "/blog",
+        active_class: "is-active",  // Only for this Link. Overwrites "custom-active" from Router.
+        "Blog" 
+    },
+}
+```
+
 ### Segments
 
 Each route in your app is comprised of segments and queries. Segments are the portions of the route delimited by forward slashes.