use crate::*;
use std::collections::HashMap;
use std::time::Instant;
use web_sys::{DomRect, Element};
#[cfg(not(target_arch = "wasm32"))]
struct MockLayoutTracker;
#[cfg(not(target_arch = "wasm32"))]
impl MockLayoutTracker {
fn new() -> Self {
Self
}
fn track_element(&self, _element: Element) -> Result<(), String> {
Ok(())
}
fn untrack_element(&self, _element_id: &str) -> Result<(), String> {
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
struct MockFLIPAnimator;
#[cfg(not(target_arch = "wasm32"))]
impl MockFLIPAnimator {
fn new() -> Self {
Self
}
fn animate(
&self,
_element_id: String,
_element: Element,
_from_rect: DomRect,
_to_rect: DomRect,
_config: LayoutAnimationConfig,
) -> Result<(), String> {
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
struct MockSharedElementManager;
#[cfg(not(target_arch = "wasm32"))]
impl MockSharedElementManager {
fn new(_strategy: ZIndexStrategy) -> Self {
Self
}
fn start_transition(&self, _transition_id: &str) -> Result<(), String> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SimplifiedEasing {
Linear,
EaseIn,
EaseOut,
EaseInOut,
EaseInCubic,
EaseOutCubic,
EaseInOutCubic,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SimplifiedLayoutConfig {
pub duration: f64,
pub easing: SimplifiedEasing,
pub hardware_accelerated: bool,
pub enable_flip: bool,
pub enable_shared_elements: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SimplifiedAnimationStatus {
pub is_animating: bool,
pub is_paused: bool,
pub progress: f64,
pub start_time: Option<Instant>,
pub duration: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SimplifiedPerformanceMetrics {
pub total_animations: usize,
pub average_duration: f64,
pub frame_rate: f64,
pub memory_usage: usize,
pub cpu_usage: f64,
}
pub struct SimplifiedLayoutManager {
#[cfg(target_arch = "wasm32")]
internal_tracker: LayoutTracker,
#[cfg(not(target_arch = "wasm32"))]
internal_tracker: MockLayoutTracker,
#[cfg(target_arch = "wasm32")]
internal_flip_animator: FLIPAnimator,
#[cfg(not(target_arch = "wasm32"))]
internal_flip_animator: MockFLIPAnimator,
#[cfg(target_arch = "wasm32")]
internal_shared_manager: SharedElementManager,
#[cfg(not(target_arch = "wasm32"))]
internal_shared_manager: MockSharedElementManager,
config: SimplifiedLayoutConfig,
tracked_elements: HashMap<String, Element>,
active_animations: HashMap<String, SimplifiedAnimationStatus>,
performance_metrics: SimplifiedPerformanceMetrics,
}
impl SimplifiedLayoutManager {
pub fn new() -> Self {
Self::with_config(SimplifiedLayoutConfig::default())
}
pub fn with_config(config: SimplifiedLayoutConfig) -> Self {
#[cfg(target_arch = "wasm32")]
{
Self {
internal_tracker: LayoutTracker::new(),
internal_flip_animator: FLIPAnimator::new(),
internal_shared_manager: SharedElementManager::new(ZIndexStrategy::default()),
config,
tracked_elements: HashMap::new(),
active_animations: HashMap::new(),
performance_metrics: SimplifiedPerformanceMetrics {
total_animations: 0,
average_duration: 0.0,
frame_rate: 60.0,
memory_usage: 0,
cpu_usage: 0.0,
},
}
}
#[cfg(not(target_arch = "wasm32"))]
{
Self {
internal_tracker: MockLayoutTracker::new(),
internal_flip_animator: MockFLIPAnimator::new(),
internal_shared_manager: MockSharedElementManager::new(ZIndexStrategy::default()),
config,
tracked_elements: HashMap::new(),
active_animations: HashMap::new(),
performance_metrics: SimplifiedPerformanceMetrics {
total_animations: 0,
average_duration: 0.0,
frame_rate: 60.0,
memory_usage: 0,
cpu_usage: 0.0,
},
}
}
}
pub fn start_tracking(&mut self, element_id: &str, element: &Element) -> Result<(), String> {
if self.tracked_elements.contains_key(element_id) {
return Err(format!("Element '{}' is already being tracked", element_id));
}
self.tracked_elements
.insert(element_id.to_string(), element.clone());
let _ = self.internal_tracker.track_element(element.clone());
Ok(())
}
pub fn stop_tracking(&mut self, element_id: &str) -> Result<(), String> {
if !self.tracked_elements.contains_key(element_id) {
return Err(format!("Element '{}' is not being tracked", element_id));
}
self.tracked_elements.remove(element_id);
let _ = self.internal_tracker.untrack_element(element_id);
self.active_animations.remove(element_id);
Ok(())
}
pub fn animate_layout_change(
&mut self,
element_id: &str,
from_layout: &LayoutInfo,
to_layout: &LayoutInfo,
) -> Result<(), String> {
if !self.tracked_elements.contains_key(element_id) {
return Err(format!("Element '{}' is not being tracked", element_id));
}
let element = self.tracked_elements.get(element_id).unwrap();
let from_rect = web_sys::DomRect::new_with_x_and_y_and_width_and_height(
from_layout.x,
from_layout.y,
from_layout.width,
from_layout.height,
)
.unwrap();
let to_rect = web_sys::DomRect::new_with_x_and_y_and_width_and_height(
to_layout.x,
to_layout.y,
to_layout.width,
to_layout.height,
)
.unwrap();
let animation_config = LayoutAnimationConfig {
enabled: true,
duration: self.config.duration,
easing: self.convert_easing(self.config.easing),
hardware_accelerated: self.config.hardware_accelerated,
};
let _ = self.internal_flip_animator.animate(
element_id.to_string(),
element.clone(),
from_rect,
to_rect,
animation_config,
);
self.active_animations.insert(
element_id.to_string(),
SimplifiedAnimationStatus {
is_animating: true,
is_paused: false,
progress: 0.0,
start_time: Some(Instant::now()),
duration: self.config.duration,
},
);
self.performance_metrics.total_animations += 1;
Ok(())
}
pub fn flip_animate(
&mut self,
element_id: &str,
from_layout: &LayoutInfo,
to_layout: &LayoutInfo,
) -> Result<(), String> {
if !self.config.enable_flip {
return Err("FLIP animations are disabled".to_string());
}
self.animate_layout_change(element_id, from_layout, to_layout)
}
pub fn shared_element_transition(
&mut self,
from_element_id: &str,
to_element_id: &str,
_from_layout: &LayoutInfo,
_to_layout: &LayoutInfo,
) -> Result<(), String> {
if !self.config.enable_shared_elements {
return Err("Shared element transitions are disabled".to_string());
}
if !self.tracked_elements.contains_key(from_element_id) {
return Err(format!(
"Element '{}' is not being tracked",
from_element_id
));
}
if !self.tracked_elements.contains_key(to_element_id) {
return Err(format!("Element '{}' is not being tracked", to_element_id));
}
let _from_element = self.tracked_elements.get(from_element_id).unwrap();
let _to_element = self.tracked_elements.get(to_element_id).unwrap();
let _config = SharedElementConfig {
maintain_aspect_ratio: true,
hardware_accelerated: self.config.hardware_accelerated,
duration: self.config.duration,
easing: self.convert_easing(self.config.easing),
};
let _ = self
.internal_shared_manager
.start_transition("shared-transition");
let status = SimplifiedAnimationStatus {
is_animating: true,
is_paused: false,
progress: 0.0,
start_time: Some(Instant::now()),
duration: self.config.duration,
};
self.active_animations
.insert(from_element_id.to_string(), status.clone());
self.active_animations
.insert(to_element_id.to_string(), status);
self.performance_metrics.total_animations += 1;
Ok(())
}
pub fn batch_start_tracking(&mut self, elements: Vec<(&str, &Element)>) -> Result<(), String> {
for (element_id, element) in elements {
self.start_tracking(element_id, element)?;
}
Ok(())
}
pub fn batch_animate(
&mut self,
animations: Vec<(&str, &LayoutInfo, &LayoutInfo)>,
) -> Result<(), String> {
for (element_id, from_layout, to_layout) in animations {
self.animate_layout_change(element_id, from_layout, to_layout)?;
}
Ok(())
}
pub fn get_layout_info(&self, element_id: &str) -> Option<LayoutInfo> {
if let Some(element) = self.tracked_elements.get(element_id) {
let rect = element.get_bounding_client_rect();
Some(LayoutInfo::new(
rect.left(),
rect.top(),
rect.width(),
rect.height(),
))
} else {
None
}
}
pub fn get_animation_status(&self, element_id: &str) -> Option<&SimplifiedAnimationStatus> {
self.active_animations.get(element_id)
}
pub fn pause_animation(&mut self, element_id: &str) -> Result<(), String> {
if let Some(status) = self.active_animations.get_mut(element_id) {
status.is_paused = true;
Ok(())
} else {
Err(format!("No active animation for element '{}'", element_id))
}
}
pub fn resume_animation(&mut self, element_id: &str) -> Result<(), String> {
if let Some(status) = self.active_animations.get_mut(element_id) {
status.is_paused = false;
Ok(())
} else {
Err(format!("No active animation for element '{}'", element_id))
}
}
pub fn cancel_animation(&mut self, element_id: &str) -> Result<(), String> {
if let Some(status) = self.active_animations.get_mut(element_id) {
status.is_animating = false;
status.is_paused = false;
status.progress = 0.0;
status.start_time = None;
Ok(())
} else {
Err(format!("No active animation for element '{}'", element_id))
}
}
pub fn clear_all(&mut self) {
self.tracked_elements.clear();
self.active_animations.clear();
}
pub fn get_performance_metrics(&self) -> Option<SimplifiedPerformanceMetrics> {
Some(self.performance_metrics.clone())
}
pub fn update_config(&mut self, config: SimplifiedLayoutConfig) {
self.config = config;
}
pub fn get_config(&self) -> SimplifiedLayoutConfig {
self.config.clone()
}
pub fn is_tracking(&self) -> bool {
!self.tracked_elements.is_empty()
}
pub fn tracked_count(&self) -> usize {
self.tracked_elements.len()
}
pub fn animation_count(&self) -> usize {
self.active_animations
.values()
.filter(|s| s.is_animating)
.count()
}
fn convert_easing(&self, easing: SimplifiedEasing) -> EasingFunction {
match easing {
SimplifiedEasing::Linear => EasingFunction::Linear,
SimplifiedEasing::EaseIn => EasingFunction::EaseIn,
SimplifiedEasing::EaseOut => EasingFunction::EaseOut,
SimplifiedEasing::EaseInOut => EasingFunction::EaseInOut,
SimplifiedEasing::EaseInCubic => EasingFunction::CubicBezier(0.55, 0.055, 0.675, 0.19),
SimplifiedEasing::EaseOutCubic => EasingFunction::CubicBezier(0.215, 0.61, 0.355, 1.0),
SimplifiedEasing::EaseInOutCubic => {
EasingFunction::CubicBezier(0.645, 0.045, 0.355, 1.0)
}
}
}
}
impl Default for SimplifiedLayoutManager {
fn default() -> Self {
Self::new()
}
}
impl Clone for SimplifiedLayoutManager {
fn clone(&self) -> Self {
#[cfg(target_arch = "wasm32")]
{
Self {
internal_tracker: LayoutTracker::new(),
internal_flip_animator: FLIPAnimator::new(),
internal_shared_manager: SharedElementManager::new(ZIndexStrategy::default()),
config: self.config.clone(),
tracked_elements: HashMap::new(), active_animations: HashMap::new(),
performance_metrics: self.performance_metrics.clone(),
}
}
#[cfg(not(target_arch = "wasm32"))]
{
Self {
internal_tracker: MockLayoutTracker::new(),
internal_flip_animator: MockFLIPAnimator::new(),
internal_shared_manager: MockSharedElementManager::new(ZIndexStrategy::default()),
config: self.config.clone(),
tracked_elements: HashMap::new(), active_animations: HashMap::new(),
performance_metrics: self.performance_metrics.clone(),
}
}
}
}
impl std::fmt::Debug for SimplifiedLayoutManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SimplifiedLayoutManager")
.field("is_tracking", &self.is_tracking())
.field("tracked_count", &self.tracked_count())
.field("animation_count", &self.animation_count())
.finish()
}
}
impl Default for SimplifiedLayoutConfig {
fn default() -> Self {
Self {
duration: 0.3,
easing: SimplifiedEasing::EaseInOut,
hardware_accelerated: true,
enable_flip: true,
enable_shared_elements: true,
}
}
}
impl SimplifiedLayoutConfig {
pub fn new() -> Self {
Self::default()
}
pub fn duration(mut self, duration: f64) -> Self {
self.duration = duration;
self
}
pub fn easing(mut self, easing: SimplifiedEasing) -> Self {
self.easing = easing;
self
}
pub fn hardware_accelerated(mut self, enabled: bool) -> Self {
self.hardware_accelerated = enabled;
self
}
pub fn enable_flip(mut self, enabled: bool) -> Self {
self.enable_flip = enabled;
self
}
pub fn enable_shared_elements(mut self, enabled: bool) -> Self {
self.enable_shared_elements = enabled;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simplified_layout_manager_creation() {
let manager = SimplifiedLayoutManager::new();
assert!(!manager.is_tracking());
assert_eq!(manager.tracked_count(), 0);
assert_eq!(manager.animation_count(), 0);
}
#[test]
fn test_simplified_layout_manager_with_config() {
let config = SimplifiedLayoutConfig::new()
.duration(0.5)
.easing(SimplifiedEasing::EaseOut);
let manager = SimplifiedLayoutManager::with_config(config);
assert!(!manager.is_tracking());
assert_eq!(manager.tracked_count(), 0);
}
#[test]
fn test_simplified_layout_config_fluent_api() {
let config = SimplifiedLayoutConfig::new()
.duration(0.5)
.easing(SimplifiedEasing::EaseOut)
.hardware_accelerated(false)
.enable_flip(false)
.enable_shared_elements(false);
assert_eq!(config.duration, 0.5);
assert_eq!(config.easing, SimplifiedEasing::EaseOut);
assert!(!config.hardware_accelerated);
assert!(!config.enable_flip);
assert!(!config.enable_shared_elements);
}
#[test]
fn test_simplified_layout_manager_clone() {
let manager1 = SimplifiedLayoutManager::new();
let manager2 = manager1.clone();
assert_eq!(manager1.is_tracking(), manager2.is_tracking());
assert_eq!(manager1.tracked_count(), manager2.tracked_count());
assert_eq!(manager1.animation_count(), manager2.animation_count());
}
#[test]
fn test_simplified_layout_manager_debug() {
let manager = SimplifiedLayoutManager::new();
let debug_str = format!("{:?}", manager);
assert!(debug_str.contains("SimplifiedLayoutManager"));
assert!(debug_str.contains("is_tracking"));
assert!(debug_str.contains("tracked_count"));
assert!(debug_str.contains("animation_count"));
}
}