freya_components/
animated_router.rs

1use std::marker::PhantomData;
2
3use freya_core::prelude::*;
4use freya_router::prelude::{
5    Routable,
6    use_route,
7};
8
9#[derive(Clone)]
10pub enum AnimatedRouterContext<R: Routable + PartialEq> {
11    /// Transition from one route to another.
12    FromTo(R, R),
13    /// Settled in a route.
14    In(R),
15}
16
17impl<R: Routable + PartialEq> AnimatedRouterContext<R> {
18    /// Get the current destination route.
19    pub fn target_route(&self) -> &R {
20        match self {
21            Self::FromTo(_, to) => to,
22            Self::In(to) => to,
23        }
24    }
25
26    /// Update the destination route.
27    pub fn set_target_route(&mut self, to: R) {
28        match self {
29            Self::FromTo(old_from, old_to) => {
30                *old_from = old_to.clone();
31                *old_to = to
32            }
33            Self::In(old_to) => *self = Self::FromTo(old_to.clone(), to),
34        }
35    }
36
37    /// After the transition animation has finished, make the outlet only render the destination route.
38    pub fn settle(&mut self) {
39        if let Self::FromTo(_, to) = self {
40            *self = Self::In(to.clone())
41        }
42    }
43}
44
45#[derive(Clone, PartialEq)]
46pub struct AnimatedRouter<R> {
47    content: Element,
48    _phantom: PhantomData<R>,
49}
50
51impl<R> AnimatedRouter<R> {
52    pub fn new(children: impl Into<Element>) -> Self {
53        Self {
54            content: children.into(),
55            _phantom: PhantomData::<R>,
56        }
57    }
58}
59
60/// Provide a mechanism for [freya_router::prelude::outlet] to animate between route changes.
61///
62/// See the `animated_router.rs` example to see how to use it.
63impl<R: Routable + 'static + PartialEq> Render for AnimatedRouter<R> {
64    fn render(&self) -> impl IntoElement {
65        let route = use_route::<R>();
66        let mut prev_route = use_state(|| AnimatedRouterContext::In(route.clone()));
67        use_provide_context(move || prev_route);
68
69        if prev_route.peek().target_route() != &route {
70            prev_route.write().set_target_route(route);
71        }
72
73        self.content.clone()
74    }
75}
76
77/// Shortcut to get access to the [AnimatedRouterContext].
78pub fn use_animated_router<Route: Routable + PartialEq>() -> State<AnimatedRouterContext<Route>> {
79    use_consume()
80}