1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use dioxus_lib::prelude::*;

use std::{cell::RefCell, rc::Rc, str::FromStr};

use crate::{prelude::Outlet, routable::Routable, router_cfg::RouterConfig};

/// The config for [`Router`].
#[derive(Clone)]
pub struct RouterConfigFactory<R: Routable> {
    #[allow(clippy::type_complexity)]
    config: Rc<RefCell<Option<Box<dyn FnOnce() -> RouterConfig<R>>>>>,
}

impl<R: Routable> Default for RouterConfigFactory<R>
where
    <R as FromStr>::Err: std::fmt::Display,
{
    fn default() -> Self {
        Self::from(RouterConfig::default)
    }
}

impl<R: Routable, F: FnOnce() -> RouterConfig<R> + 'static> From<F> for RouterConfigFactory<R> {
    fn from(value: F) -> Self {
        Self {
            config: Rc::new(RefCell::new(Some(Box::new(value)))),
        }
    }
}

/// The props for [`Router`].
#[derive(Props)]
pub struct RouterProps<R: Routable>
where
    <R as FromStr>::Err: std::fmt::Display,
{
    #[props(default, into)]
    config: RouterConfigFactory<R>,
}

impl<T: Routable> Clone for RouterProps<T>
where
    <T as FromStr>::Err: std::fmt::Display,
{
    fn clone(&self) -> Self {
        Self {
            config: self.config.clone(),
        }
    }
}

impl<R: Routable> Default for RouterProps<R>
where
    <R as FromStr>::Err: std::fmt::Display,
{
    fn default() -> Self {
        Self {
            config: RouterConfigFactory::default(),
        }
    }
}

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
    }
}

/// A component that renders the current route.
pub fn Router<R: Routable + Clone>(props: RouterProps<R>) -> Element
where
    <R as FromStr>::Err: std::fmt::Display,
{
    use crate::prelude::{outlet::OutletContext, RouterContext};

    use_hook(|| {
        provide_context(RouterContext::new(
            (props
                .config
                .config
                .take()
                .expect("use_context_provider ran twice"))(),
            schedule_update_any(),
        ));

        provide_context(OutletContext::<R> {
            current_level: 0,
            _marker: std::marker::PhantomData,
        });
    });

    rsx! { Outlet::<R> {} }
}