freya_components/
portal.rs1use std::{
2 collections::HashMap,
3 fmt::Debug,
4 time::Duration,
5};
6
7use freya_animation::prelude::*;
8use freya_core::{
9 prelude::*,
10 scope_id::ScopeId,
11};
12use torin::{
13 prelude::{
14 Area,
15 Position,
16 },
17 size::Size,
18};
19
20#[derive(PartialEq)]
21pub struct Portal<T> {
22 key: DiffKey,
23 children: Vec<Element>,
24 id: T,
25 function: Function,
26 duration: Duration,
27 ease: Ease,
28 width: Size,
29 height: Size,
30 show: bool,
31}
32
33impl<T> ChildrenExt for Portal<T> {
34 fn get_children(&mut self) -> &mut Vec<Element> {
35 &mut self.children
36 }
37}
38
39impl<T> Portal<T> {
40 pub fn new(id: T) -> Self {
41 Self {
42 key: DiffKey::None,
43 children: vec![],
44 id,
45 function: Function::default(),
46 duration: Duration::from_millis(750),
47 ease: Ease::default(),
48 width: Size::auto(),
49 height: Size::auto(),
50 show: true,
51 }
52 }
53
54 pub fn function(mut self, function: Function) -> Self {
55 self.function = function;
56 self
57 }
58
59 pub fn duration(mut self, duration: Duration) -> Self {
60 self.duration = duration;
61 self
62 }
63
64 pub fn ease(mut self, ease: Ease) -> Self {
65 self.ease = ease;
66 self
67 }
68
69 pub fn width(mut self, width: Size) -> Self {
70 self.width = width;
71 self
72 }
73
74 pub fn height(mut self, height: Size) -> Self {
75 self.height = height;
76 self
77 }
78
79 pub fn show(mut self, show: bool) -> Self {
80 self.show = show;
81 self
82 }
83}
84
85impl<T> KeyExt for Portal<T> {
86 fn write_key(&mut self) -> &mut DiffKey {
87 &mut self.key
88 }
89}
90
91impl<T: PartialEq + 'static + Clone + std::hash::Hash + Eq + Debug> Render for Portal<T> {
92 fn render(&self) -> impl IntoElement {
93 let mut positions = use_hook(|| match try_consume_context::<PortalsMap<T>>() {
94 Some(ctx) => ctx,
95 None => {
96 let ctx = PortalsMap {
97 ids: State::create_in_scope(HashMap::default(), ScopeId::ROOT),
98 };
99 provide_context_for_scope_id(ctx.clone(), ScopeId::ROOT);
100 ctx
101 }
102 });
103 let id = self.id.clone();
104 let init_size = use_hook(move || positions.ids.write().remove(&id));
105 let mut previous_size = use_state::<Option<Area>>(|| None);
106 let mut current_size = use_state::<Option<Area>>(|| None);
107
108 let mut animation = use_animation_with_dependencies(
109 &(self.function, self.duration, self.ease),
110 move |conf, (function, duration, ease)| {
111 conf.on_change(OnChange::Nothing);
112 let from_size = previous_size
113 .read()
114 .unwrap_or(init_size.unwrap_or_default());
115 let to_size = current_size.read().unwrap_or_default();
116 (
117 AnimNum::new(from_size.origin.x, to_size.origin.x)
118 .duration(*duration)
119 .ease(*ease)
120 .function(*function),
121 AnimNum::new(from_size.origin.y, to_size.origin.y)
122 .duration(*duration)
123 .ease(*ease)
124 .function(*function),
125 AnimNum::new(from_size.size.width, to_size.size.width)
126 .duration(*duration)
127 .ease(*ease)
128 .function(*function),
129 AnimNum::new(from_size.size.height, to_size.size.height)
130 .duration(*duration)
131 .ease(*ease)
132 .function(*function),
133 )
134 },
135 );
136
137 let (offset_x, offset_y, width, height) = animation.get().value();
138 let id = self.id.clone();
139 let show = self.show;
140
141 rect()
142 .a11y_focusable(false)
143 .on_sized(move |e: Event<SizedEventData>| {
144 if *current_size.peek() != Some(e.area) && show {
145 previous_size.set(current_size());
146 current_size.set(Some(e.area));
147 positions.ids.write().insert(id.clone(), e.area);
148
149 spawn(async move {
150 let has_init_size = init_size.is_some();
151 let has_previous_size = previous_size.peek().is_some();
152
153 if !*animation.has_run_yet().read() && !has_init_size {
154 animation.finish();
156 } else if has_init_size || has_previous_size {
157 animation.start();
159 }
160 });
161 }
162 })
163 .width(self.width.clone())
164 .height(self.height.clone())
165 .child(
166 rect()
167 .offset_x(offset_x)
168 .offset_y(offset_y)
169 .position(Position::new_global())
170 .child(
171 rect()
172 .width(Size::px(width))
173 .height(Size::px(height))
174 .opacity(
176 if init_size.is_some()
177 || previous_size.read().is_some()
178 || current_size.read().is_some()
179 {
180 1.
181 } else {
182 0.
183 },
184 )
185 .children(if self.show {
186 self.children.clone()
187 } else {
188 vec![]
189 }),
190 ),
191 )
192 }
193
194 fn render_key(&self) -> DiffKey {
195 self.key.clone().or(self.default_key())
196 }
197}
198
199#[derive(Clone)]
200pub struct PortalsMap<T: Clone + PartialEq + 'static> {
201 pub ids: State<HashMap<T, Area>>,
202}