1use crate::*;
7use std::collections::HashMap;
8use std::time::Instant;
9use web_sys::{DomRect, Element};
10
11#[cfg(not(target_arch = "wasm32"))]
13struct MockLayoutTracker;
14
15#[cfg(not(target_arch = "wasm32"))]
16impl MockLayoutTracker {
17 fn new() -> Self {
18 Self
19 }
20
21 fn track_element(&self, _element: Element) -> Result<(), String> {
22 Ok(())
23 }
24
25 fn untrack_element(&self, _element_id: &str) -> Result<(), String> {
26 Ok(())
27 }
28}
29
30#[cfg(not(target_arch = "wasm32"))]
32struct MockFLIPAnimator;
33
34#[cfg(not(target_arch = "wasm32"))]
35impl MockFLIPAnimator {
36 fn new() -> Self {
37 Self
38 }
39
40 fn animate(
41 &self,
42 _element_id: String,
43 _element: Element,
44 _from_rect: DomRect,
45 _to_rect: DomRect,
46 _config: LayoutAnimationConfig,
47 ) -> Result<(), String> {
48 Ok(())
49 }
50}
51
52#[cfg(not(target_arch = "wasm32"))]
54struct MockSharedElementManager;
55
56#[cfg(not(target_arch = "wasm32"))]
57impl MockSharedElementManager {
58 fn new(_strategy: ZIndexStrategy) -> Self {
59 Self
60 }
61
62 fn start_transition(&self, _transition_id: &str) -> Result<(), String> {
63 Ok(())
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum SimplifiedEasing {
70 Linear,
72 EaseIn,
74 EaseOut,
76 EaseInOut,
78 EaseInCubic,
80 EaseOutCubic,
82 EaseInOutCubic,
84}
85
86#[derive(Debug, Clone, PartialEq)]
88pub struct SimplifiedLayoutConfig {
89 pub duration: f64,
91 pub easing: SimplifiedEasing,
93 pub hardware_accelerated: bool,
95 pub enable_flip: bool,
97 pub enable_shared_elements: bool,
99}
100
101#[derive(Debug, Clone, PartialEq)]
103pub struct SimplifiedAnimationStatus {
104 pub is_animating: bool,
106 pub is_paused: bool,
108 pub progress: f64,
110 pub start_time: Option<Instant>,
112 pub duration: f64,
114}
115
116#[derive(Debug, Clone, PartialEq)]
118pub struct SimplifiedPerformanceMetrics {
119 pub total_animations: usize,
121 pub average_duration: f64,
123 pub frame_rate: f64,
125 pub memory_usage: usize,
127 pub cpu_usage: f64,
129}
130
131pub struct SimplifiedLayoutManager {
137 #[cfg(target_arch = "wasm32")]
140 internal_tracker: LayoutTracker,
141 #[cfg(not(target_arch = "wasm32"))]
142 internal_tracker: MockLayoutTracker,
143 #[cfg(target_arch = "wasm32")]
145 internal_flip_animator: FLIPAnimator,
146 #[cfg(not(target_arch = "wasm32"))]
147 internal_flip_animator: MockFLIPAnimator,
148 #[cfg(target_arch = "wasm32")]
150 internal_shared_manager: SharedElementManager,
151 #[cfg(not(target_arch = "wasm32"))]
152 internal_shared_manager: MockSharedElementManager,
153 config: SimplifiedLayoutConfig,
155 tracked_elements: HashMap<String, Element>,
157 active_animations: HashMap<String, SimplifiedAnimationStatus>,
159 performance_metrics: SimplifiedPerformanceMetrics,
161}
162
163impl SimplifiedLayoutManager {
164 pub fn new() -> Self {
166 Self::with_config(SimplifiedLayoutConfig::default())
167 }
168
169 pub fn with_config(config: SimplifiedLayoutConfig) -> Self {
171 #[cfg(target_arch = "wasm32")]
172 {
173 Self {
174 internal_tracker: LayoutTracker::new(),
175 internal_flip_animator: FLIPAnimator::new(),
176 internal_shared_manager: SharedElementManager::new(ZIndexStrategy::default()),
177 config,
178 tracked_elements: HashMap::new(),
179 active_animations: HashMap::new(),
180 performance_metrics: SimplifiedPerformanceMetrics {
181 total_animations: 0,
182 average_duration: 0.0,
183 frame_rate: 60.0,
184 memory_usage: 0,
185 cpu_usage: 0.0,
186 },
187 }
188 }
189 #[cfg(not(target_arch = "wasm32"))]
190 {
191 Self {
192 internal_tracker: MockLayoutTracker::new(),
193 internal_flip_animator: MockFLIPAnimator::new(),
194 internal_shared_manager: MockSharedElementManager::new(ZIndexStrategy::default()),
195 config,
196 tracked_elements: HashMap::new(),
197 active_animations: HashMap::new(),
198 performance_metrics: SimplifiedPerformanceMetrics {
199 total_animations: 0,
200 average_duration: 0.0,
201 frame_rate: 60.0,
202 memory_usage: 0,
203 cpu_usage: 0.0,
204 },
205 }
206 }
207 }
208
209 pub fn start_tracking(&mut self, element_id: &str, element: &Element) -> Result<(), String> {
211 if self.tracked_elements.contains_key(element_id) {
212 return Err(format!("Element '{}' is already being tracked", element_id));
213 }
214
215 self.tracked_elements
216 .insert(element_id.to_string(), element.clone());
217 let _ = self.internal_tracker.track_element(element.clone());
218
219 Ok(())
220 }
221
222 pub fn stop_tracking(&mut self, element_id: &str) -> Result<(), String> {
224 if !self.tracked_elements.contains_key(element_id) {
225 return Err(format!("Element '{}' is not being tracked", element_id));
226 }
227
228 self.tracked_elements.remove(element_id);
229 let _ = self.internal_tracker.untrack_element(element_id);
230 self.active_animations.remove(element_id);
231
232 Ok(())
233 }
234
235 pub fn animate_layout_change(
237 &mut self,
238 element_id: &str,
239 from_layout: &LayoutInfo,
240 to_layout: &LayoutInfo,
241 ) -> Result<(), String> {
242 if !self.tracked_elements.contains_key(element_id) {
243 return Err(format!("Element '{}' is not being tracked", element_id));
244 }
245
246 let element = self.tracked_elements.get(element_id).unwrap();
247
248 let from_rect = web_sys::DomRect::new_with_x_and_y_and_width_and_height(
250 from_layout.x,
251 from_layout.y,
252 from_layout.width,
253 from_layout.height,
254 )
255 .unwrap();
256 let to_rect = web_sys::DomRect::new_with_x_and_y_and_width_and_height(
257 to_layout.x,
258 to_layout.y,
259 to_layout.width,
260 to_layout.height,
261 )
262 .unwrap();
263
264 let animation_config = LayoutAnimationConfig {
265 enabled: true,
266 duration: self.config.duration,
267 easing: self.convert_easing(self.config.easing),
268 hardware_accelerated: self.config.hardware_accelerated,
269 };
270
271 let _ = self.internal_flip_animator.animate(
272 element_id.to_string(),
273 element.clone(),
274 from_rect,
275 to_rect,
276 animation_config,
277 );
278
279 self.active_animations.insert(
281 element_id.to_string(),
282 SimplifiedAnimationStatus {
283 is_animating: true,
284 is_paused: false,
285 progress: 0.0,
286 start_time: Some(Instant::now()),
287 duration: self.config.duration,
288 },
289 );
290
291 self.performance_metrics.total_animations += 1;
293
294 Ok(())
295 }
296
297 pub fn flip_animate(
299 &mut self,
300 element_id: &str,
301 from_layout: &LayoutInfo,
302 to_layout: &LayoutInfo,
303 ) -> Result<(), String> {
304 if !self.config.enable_flip {
305 return Err("FLIP animations are disabled".to_string());
306 }
307
308 self.animate_layout_change(element_id, from_layout, to_layout)
309 }
310
311 pub fn shared_element_transition(
313 &mut self,
314 from_element_id: &str,
315 to_element_id: &str,
316 _from_layout: &LayoutInfo,
317 _to_layout: &LayoutInfo,
318 ) -> Result<(), String> {
319 if !self.config.enable_shared_elements {
320 return Err("Shared element transitions are disabled".to_string());
321 }
322
323 if !self.tracked_elements.contains_key(from_element_id) {
324 return Err(format!(
325 "Element '{}' is not being tracked",
326 from_element_id
327 ));
328 }
329
330 if !self.tracked_elements.contains_key(to_element_id) {
331 return Err(format!("Element '{}' is not being tracked", to_element_id));
332 }
333
334 let _from_element = self.tracked_elements.get(from_element_id).unwrap();
335 let _to_element = self.tracked_elements.get(to_element_id).unwrap();
336
337 let _config = SharedElementConfig {
339 maintain_aspect_ratio: true,
340 hardware_accelerated: self.config.hardware_accelerated,
341 duration: self.config.duration,
342 easing: self.convert_easing(self.config.easing),
343 };
344
345 let _ = self
346 .internal_shared_manager
347 .start_transition("shared-transition");
348
349 let status = SimplifiedAnimationStatus {
351 is_animating: true,
352 is_paused: false,
353 progress: 0.0,
354 start_time: Some(Instant::now()),
355 duration: self.config.duration,
356 };
357
358 self.active_animations
359 .insert(from_element_id.to_string(), status.clone());
360 self.active_animations
361 .insert(to_element_id.to_string(), status);
362
363 self.performance_metrics.total_animations += 1;
365
366 Ok(())
367 }
368
369 pub fn batch_start_tracking(&mut self, elements: Vec<(&str, &Element)>) -> Result<(), String> {
371 for (element_id, element) in elements {
372 self.start_tracking(element_id, element)?;
373 }
374 Ok(())
375 }
376
377 pub fn batch_animate(
379 &mut self,
380 animations: Vec<(&str, &LayoutInfo, &LayoutInfo)>,
381 ) -> Result<(), String> {
382 for (element_id, from_layout, to_layout) in animations {
383 self.animate_layout_change(element_id, from_layout, to_layout)?;
384 }
385 Ok(())
386 }
387
388 pub fn get_layout_info(&self, element_id: &str) -> Option<LayoutInfo> {
390 if let Some(element) = self.tracked_elements.get(element_id) {
391 let rect = element.get_bounding_client_rect();
392 Some(LayoutInfo::new(
393 rect.left(),
394 rect.top(),
395 rect.width(),
396 rect.height(),
397 ))
398 } else {
399 None
400 }
401 }
402
403 pub fn get_animation_status(&self, element_id: &str) -> Option<&SimplifiedAnimationStatus> {
405 self.active_animations.get(element_id)
406 }
407
408 pub fn pause_animation(&mut self, element_id: &str) -> Result<(), String> {
410 if let Some(status) = self.active_animations.get_mut(element_id) {
411 status.is_paused = true;
412 Ok(())
413 } else {
414 Err(format!("No active animation for element '{}'", element_id))
415 }
416 }
417
418 pub fn resume_animation(&mut self, element_id: &str) -> Result<(), String> {
420 if let Some(status) = self.active_animations.get_mut(element_id) {
421 status.is_paused = false;
422 Ok(())
423 } else {
424 Err(format!("No active animation for element '{}'", element_id))
425 }
426 }
427
428 pub fn cancel_animation(&mut self, element_id: &str) -> Result<(), String> {
430 if let Some(status) = self.active_animations.get_mut(element_id) {
431 status.is_animating = false;
432 status.is_paused = false;
433 status.progress = 0.0;
434 status.start_time = None;
435 Ok(())
436 } else {
437 Err(format!("No active animation for element '{}'", element_id))
438 }
439 }
440
441 pub fn clear_all(&mut self) {
443 self.tracked_elements.clear();
444 self.active_animations.clear();
445 }
450
451 pub fn get_performance_metrics(&self) -> Option<SimplifiedPerformanceMetrics> {
453 Some(self.performance_metrics.clone())
454 }
455
456 pub fn update_config(&mut self, config: SimplifiedLayoutConfig) {
458 self.config = config;
459 }
460
461 pub fn get_config(&self) -> SimplifiedLayoutConfig {
463 self.config.clone()
464 }
465
466 pub fn is_tracking(&self) -> bool {
468 !self.tracked_elements.is_empty()
469 }
470
471 pub fn tracked_count(&self) -> usize {
473 self.tracked_elements.len()
474 }
475
476 pub fn animation_count(&self) -> usize {
478 self.active_animations
479 .values()
480 .filter(|s| s.is_animating)
481 .count()
482 }
483
484 fn convert_easing(&self, easing: SimplifiedEasing) -> EasingFunction {
486 match easing {
487 SimplifiedEasing::Linear => EasingFunction::Linear,
488 SimplifiedEasing::EaseIn => EasingFunction::EaseIn,
489 SimplifiedEasing::EaseOut => EasingFunction::EaseOut,
490 SimplifiedEasing::EaseInOut => EasingFunction::EaseInOut,
491 SimplifiedEasing::EaseInCubic => EasingFunction::CubicBezier(0.55, 0.055, 0.675, 0.19),
492 SimplifiedEasing::EaseOutCubic => EasingFunction::CubicBezier(0.215, 0.61, 0.355, 1.0),
493 SimplifiedEasing::EaseInOutCubic => {
494 EasingFunction::CubicBezier(0.645, 0.045, 0.355, 1.0)
495 }
496 }
497 }
498}
499
500impl Default for SimplifiedLayoutManager {
501 fn default() -> Self {
502 Self::new()
503 }
504}
505
506impl Clone for SimplifiedLayoutManager {
507 fn clone(&self) -> Self {
508 #[cfg(target_arch = "wasm32")]
509 {
510 Self {
511 internal_tracker: LayoutTracker::new(),
512 internal_flip_animator: FLIPAnimator::new(),
513 internal_shared_manager: SharedElementManager::new(ZIndexStrategy::default()),
514 config: self.config.clone(),
515 tracked_elements: HashMap::new(), active_animations: HashMap::new(),
517 performance_metrics: self.performance_metrics.clone(),
518 }
519 }
520 #[cfg(not(target_arch = "wasm32"))]
521 {
522 Self {
523 internal_tracker: MockLayoutTracker::new(),
524 internal_flip_animator: MockFLIPAnimator::new(),
525 internal_shared_manager: MockSharedElementManager::new(ZIndexStrategy::default()),
526 config: self.config.clone(),
527 tracked_elements: HashMap::new(), active_animations: HashMap::new(),
529 performance_metrics: self.performance_metrics.clone(),
530 }
531 }
532 }
533}
534
535impl std::fmt::Debug for SimplifiedLayoutManager {
536 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
537 f.debug_struct("SimplifiedLayoutManager")
538 .field("is_tracking", &self.is_tracking())
539 .field("tracked_count", &self.tracked_count())
540 .field("animation_count", &self.animation_count())
541 .finish()
542 }
543}
544
545impl Default for SimplifiedLayoutConfig {
546 fn default() -> Self {
547 Self {
548 duration: 0.3,
549 easing: SimplifiedEasing::EaseInOut,
550 hardware_accelerated: true,
551 enable_flip: true,
552 enable_shared_elements: true,
553 }
554 }
555}
556
557impl SimplifiedLayoutConfig {
558 pub fn new() -> Self {
560 Self::default()
561 }
562
563 pub fn duration(mut self, duration: f64) -> Self {
565 self.duration = duration;
566 self
567 }
568
569 pub fn easing(mut self, easing: SimplifiedEasing) -> Self {
571 self.easing = easing;
572 self
573 }
574
575 pub fn hardware_accelerated(mut self, enabled: bool) -> Self {
577 self.hardware_accelerated = enabled;
578 self
579 }
580
581 pub fn enable_flip(mut self, enabled: bool) -> Self {
583 self.enable_flip = enabled;
584 self
585 }
586
587 pub fn enable_shared_elements(mut self, enabled: bool) -> Self {
589 self.enable_shared_elements = enabled;
590 self
591 }
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597
598 #[test]
599 fn test_simplified_layout_manager_creation() {
600 let manager = SimplifiedLayoutManager::new();
601 assert!(!manager.is_tracking());
602 assert_eq!(manager.tracked_count(), 0);
603 assert_eq!(manager.animation_count(), 0);
604 }
605
606 #[test]
607 fn test_simplified_layout_manager_with_config() {
608 let config = SimplifiedLayoutConfig::new()
609 .duration(0.5)
610 .easing(SimplifiedEasing::EaseOut);
611
612 let manager = SimplifiedLayoutManager::with_config(config);
613 assert!(!manager.is_tracking());
614 assert_eq!(manager.tracked_count(), 0);
615 }
616
617 #[test]
618 fn test_simplified_layout_config_fluent_api() {
619 let config = SimplifiedLayoutConfig::new()
620 .duration(0.5)
621 .easing(SimplifiedEasing::EaseOut)
622 .hardware_accelerated(false)
623 .enable_flip(false)
624 .enable_shared_elements(false);
625
626 assert_eq!(config.duration, 0.5);
627 assert_eq!(config.easing, SimplifiedEasing::EaseOut);
628 assert!(!config.hardware_accelerated);
629 assert!(!config.enable_flip);
630 assert!(!config.enable_shared_elements);
631 }
632
633 #[test]
634 fn test_simplified_layout_manager_clone() {
635 let manager1 = SimplifiedLayoutManager::new();
636 let manager2 = manager1.clone();
637
638 assert_eq!(manager1.is_tracking(), manager2.is_tracking());
639 assert_eq!(manager1.tracked_count(), manager2.tracked_count());
640 assert_eq!(manager1.animation_count(), manager2.animation_count());
641 }
642
643 #[test]
644 fn test_simplified_layout_manager_debug() {
645 let manager = SimplifiedLayoutManager::new();
646 let debug_str = format!("{:?}", manager);
647 assert!(debug_str.contains("SimplifiedLayoutManager"));
648 assert!(debug_str.contains("is_tracking"));
649 assert!(debug_str.contains("tracked_count"));
650 assert!(debug_str.contains("animation_count"));
651 }
652}