yew_transition_group/
transition.rs1use crate::timeout::Timeout;
2use yew::prelude::*;
3
4#[derive(Debug, PartialEq, Properties)]
6pub struct TransitionProps {
7 pub children: Children,
9 pub enter: Option<bool>,
11
12 pub timeout: Timeout,
14
15 pub notification: Callback<TransitionState>,
17}
18
19impl TransitionProps {
20 fn enter(&self) -> bool {
21 self.enter.unwrap_or(false)
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
27pub enum TransitionState {
28 Entering,
30 Entered,
32 Exiting,
34 Exited,
36}
37
38#[derive(Debug)]
39pub enum TransitionStateComplete {
40 BeforeEnter,
42 Mounted,
44 TransitionState(TransitionState),
46}
47
48#[derive(Debug)]
49pub struct Tick;
50
51#[derive(Debug)]
53pub struct Transition {
54 state: TransitionStateComplete,
55 saved_enter: Option<bool>,
56}
57
58impl Component for Transition {
59 type Message = Tick;
60
61 type Properties = TransitionProps;
62
63 fn create(_ctx: &Context<Self>) -> Self {
64 Transition {
65 state: TransitionStateComplete::BeforeEnter,
66 saved_enter: Some(false),
67 }
68 }
69
70 fn update(&mut self, ctx: &Context<Self>, _msg: Self::Message) -> bool {
71 self.state = match self.state {
72 TransitionStateComplete::BeforeEnter => TransitionStateComplete::Mounted,
73 TransitionStateComplete::Mounted => {
74 let enter = ctx.props().timeout.enter();
75 ctx.link().send_future(async move {
76 gloo_timers::future::TimeoutFuture::new(enter).await;
77 Tick
78 });
79 ctx.props().notification.emit(TransitionState::Entering);
80
81 TransitionStateComplete::TransitionState(TransitionState::Entering)
82 }
83 TransitionStateComplete::TransitionState(state) => {
84 let new_state = match state {
85 TransitionState::Entering => TransitionState::Entered,
86 TransitionState::Entered => TransitionState::Exiting,
87 TransitionState::Exiting => TransitionState::Exited,
88 TransitionState::Exited => TransitionState::Exited,
89 };
90 ctx.props().notification.emit(new_state);
91
92 TransitionStateComplete::TransitionState(new_state)
93 }
94 };
95 true
96 }
97
98 fn changed(&mut self, ctx: &Context<Self>) -> bool {
99 match (ctx.props().enter(), self.saved_enter) {
100 (true, Some(true)) => true,
101 (true, Some(false)) => {
102 let duration = if ctx.props().timeout.appear() == 0 {
103 self.state =
104 TransitionStateComplete::TransitionState(TransitionState::Entering);
105 ctx.props().notification.emit(TransitionState::Entering);
106 ctx.props().timeout.enter()
107 } else {
108 self.state = TransitionStateComplete::Mounted;
109 ctx.props().timeout.appear()
110 };
111
112 ctx.link().send_future(async move {
113 gloo_timers::future::TimeoutFuture::new(duration).await;
114 Tick
115 });
116
117 self.saved_enter = Some(true);
118 true
119 }
120 (false, Some(true)) => {
121 if ctx.props().timeout.exit() != 0 {
122 self.state = TransitionStateComplete::TransitionState(TransitionState::Exiting);
123
124 let duration = ctx.props().timeout.exit();
125
126 ctx.link().send_future(async move {
127 gloo_timers::future::TimeoutFuture::new(duration).await;
128 Tick
129 });
130 } else {
131 self.state = TransitionStateComplete::TransitionState(TransitionState::Exited);
132 }
133 self.saved_enter = Some(false);
134 true
135 }
136 (false, Some(false)) => true,
137 (true, None) => true,
138 (false, None) => true,
139 }
140 }
141
142 fn view(&self, ctx: &Context<Self>) -> Html {
143 match self.state {
144 TransitionStateComplete::BeforeEnter => html! {<> </>},
145 TransitionStateComplete::Mounted => html! { <>{ ctx.props().children.clone() } </> },
146 TransitionStateComplete::TransitionState(_) => {
147 html! {
148 <>{ ctx.props().children.clone() }</>
149
150 }
151 }
152 }
153 }
154}