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