use dioxus::prelude::*;
use crate::{config::AnimationConfig, prefers_reduced_motion::prefers_reduced_motion};
#[derive(Clone)]
pub struct AnimationContext {
pub config: Signal<AnimationConfig>,
pub set_config: Callback<AnimationConfig>,
}
impl AnimationContext {
pub fn is_enabled(&self) -> bool {
self.config.read().enabled
}
pub fn duration_scale(&self) -> f32 {
self.config.read().duration_scale
}
pub fn is_reduced_motion(&self) -> bool {
self.config.read().reduced_motion
}
pub fn scale_duration(&self, duration_ms: u64) -> u64 {
self.config.read().scale_duration(duration_ms)
}
pub fn should_skip(&self) -> bool {
self.config.read().should_skip()
}
}
#[derive(Clone, Props, PartialEq)]
pub struct AnimationProviderProps {
#[props(default = true)]
pub enabled: bool,
#[props(default = 1.0)]
pub duration_scale: f32,
#[props(default = true)]
pub respect_reduced_motion: bool,
#[props(default)]
pub force_reduced_motion: Option<bool>,
children: Element,
}
#[component]
pub fn AnimationProvider(props: AnimationProviderProps) -> Element {
let system_reduced = if props.respect_reduced_motion {
prefers_reduced_motion()
} else {
false
};
let reduced_motion = props.force_reduced_motion.unwrap_or(system_reduced);
let initial_config = AnimationConfig {
enabled: props.enabled && !reduced_motion,
duration_scale: if reduced_motion {
0.0
} else {
props.duration_scale
},
reduced_motion,
};
let config = use_signal(|| initial_config);
let mut config_for_callback = config;
let set_config = Callback::new(move |new_config: AnimationConfig| {
config_for_callback.set(new_config);
});
use_context_provider(move || AnimationContext { config, set_config });
let css_vars = use_memo(move || {
let cfg = config.read();
format!(
"--hi-animation-enabled: {}; --hi-animation-duration-scale: {}; --hi-animation-reduced-motion: {};",
if cfg.enabled { 1 } else { 0 },
cfg.duration_scale,
if cfg.reduced_motion { 1 } else { 0 }
)
});
rsx! {
div {
class: "hi-animation-provider",
style: "{css_vars}",
{props.children}
}
}
}
pub fn use_animation_config() -> AnimationContext {
use_context()
}
pub fn try_use_animation_config() -> Option<AnimationContext> {
try_consume_context::<AnimationContext>()
}
pub fn use_animation_enabled() -> bool {
use_animation_config().is_enabled()
}
pub fn use_animation_duration_scale() -> f32 {
use_animation_config().duration_scale()
}
pub fn use_animation_reduced_motion() -> bool {
use_animation_config().is_reduced_motion()
}