>()
- })
- .unwrap_or_default();
-
- let id = ROUTE_ID.with(|id| {
- let next = id.get() + 1;
- id.set(next);
- next
- });
-
- RouteDefinition {
- id,
- path,
- children,
- view,
- ssr_mode,
- }
- }
-
- inner(
+ define_route(
cx,
children,
path.to_string(),
@@ -80,6 +43,91 @@ where
)
}
+/// Describes a route that is guarded by a certain condition. This works the same way as
+/// [``](Route), except that if the `condition` function evaluates to `false`, it
+/// redirects to `redirect_path` instead of displaying its `view`.
+#[component(transparent)]
+pub fn ProtectedRoute(
+ cx: Scope,
+ /// The path fragment that this route should match. This can be static (`users`),
+ /// include a parameter (`:id`) or an optional parameter (`:id?`), or match a
+ /// wildcard (`user/*any`).
+ path: P,
+ /// The path that will be redirected to if the condition is `false`.
+ redirect_path: P,
+ /// Condition function that returns a boolean.
+ condition: C,
+ /// View that will be exposed if the condition is `true`.
+ view: F,
+ /// The mode that this route prefers during server-side rendering. Defaults to out-of-order streaming.
+ #[prop(optional)]
+ ssr: SsrMode,
+ /// `children` may be empty or include nested routes.
+ #[prop(optional)]
+ children: Option,
+) -> impl IntoView
+where
+ E: IntoView,
+ F: Fn(Scope) -> E + 'static,
+ P: std::fmt::Display + 'static,
+ C: Fn(Scope) -> bool + 'static,
+{
+ use crate::{Redirect, RedirectProps};
+ let redirect_path = redirect_path.to_string();
+
+ define_route(
+ cx,
+ children,
+ path.to_string(),
+ Rc::new(move |cx| {
+ if condition(cx) {
+ view(cx).into_view(cx)
+ } else {
+ view! { cx, }
+ .into_view(cx)
+ }
+ }),
+ ssr,
+ )
+}
+
+pub(crate) fn define_route(
+ cx: Scope,
+ children: Option,
+ path: String,
+ view: Rc View>,
+ ssr_mode: SsrMode,
+) -> RouteDefinition {
+ let children = children
+ .map(|children| {
+ children(cx)
+ .as_children()
+ .iter()
+ .filter_map(|child| {
+ child
+ .as_transparent()
+ .and_then(|t| t.downcast_ref::())
+ })
+ .cloned()
+ .collect::>()
+ })
+ .unwrap_or_default();
+
+ let id = ROUTE_ID.with(|id| {
+ let next = id.get() + 1;
+ id.set(next);
+ next
+ });
+
+ RouteDefinition {
+ id,
+ path,
+ children,
+ view,
+ ssr_mode,
+ }
+}
+
impl IntoView for RouteDefinition {
fn into_view(self, cx: Scope) -> View {
Transparent::new(self).into_view(cx)