Skip to main content

glassy_ui/motion/
stagger.rs

1use std::time::Duration;
2
3use gpui::{
4    div, AnyElement, App, IntoElement, ParentElement, RenderOnce, SharedString, StyleRefinement,
5    Styled, Window,
6};
7
8use super::component::Motion;
9use super::slot::StyledSlot;
10use super::style::MotionStyle;
11use super::transition::Transition;
12
13/// Staggers enter animations across child [`Motion`] elements (motion.dev stagger).
14///
15/// ```ignore
16/// Stagger::new()
17///     .transition(
18///         Transition::spring()
19///             .delay_children(Duration::from_millis(40))
20///             .stagger_children(Duration::from_millis(60)),
21///     )
22///     .child(Motion::new().id("a").fade_up().child(...))
23///     .child(Motion::new().id("b").fade_up().child(...))
24/// ```
25#[derive(IntoElement, Default)]
26pub struct Stagger {
27    style: StyleRefinement,
28    transition: Transition,
29    motions: Vec<Motion>,
30    children: Vec<AnyElement>,
31}
32
33impl Stagger {
34    pub fn new() -> Self {
35        Self {
36            style: StyleRefinement::default(),
37            transition: Transition::spring()
38                .delay_children(Duration::from_millis(30))
39                .stagger_children(Duration::from_millis(55)),
40            motions: Vec::new(),
41            children: Vec::new(),
42        }
43    }
44
45    pub fn transition(mut self, transition: Transition) -> Self {
46        self.transition = transition;
47        self
48    }
49
50    /// Add a motion child that will receive stagger delay.
51    pub fn motion(mut self, motion: Motion) -> Self {
52        self.motions.push(motion);
53        self
54    }
55}
56
57impl Styled for Stagger {
58    fn style(&mut self) -> &mut StyleRefinement {
59        &mut self.style
60    }
61}
62
63impl ParentElement for Stagger {
64    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
65        self.children.extend(elements);
66    }
67}
68
69impl RenderOnce for Stagger {
70    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
71        let base_delay = self.transition.delay_children;
72        let stagger = self.transition.stagger_children;
73        let child_transition = self.transition.clone();
74
75        div()
76            .refine_style(&self.style)
77            .children(self.motions.into_iter().enumerate().map(|(index, motion)| {
78                let delay = base_delay + stagger * (index as u32);
79                let transition = child_transition.clone().with_extra_delay(delay);
80                motion.transition(transition).into_any_element()
81            }))
82            .children(self.children)
83    }
84}
85
86/// Helper to build a default staggered fade-up item.
87#[allow(dead_code)]
88pub fn stagger_item(id: impl Into<SharedString>, _style: MotionStyle) -> Motion {
89    Motion::new().id(id).fade_up()
90}