waterui_layout/containers/collection_transition.rs
1//! Opt-in animation for reactive-collection membership changes.
2
3use waterui_core::animation::Animation;
4use waterui_core::{Environment, Metadata, View};
5
6/// Environment request to animate reactive-collection membership changes.
7///
8/// While present in scope, reactive collections (`ForEach` / `List`) animate
9/// membership changes: an item fades and grows in when it appears, and fades and
10/// collapses out when it disappears, over [`animation`], instead of popping
11/// instantly.
12///
13/// A backend that supports collection transitions reads this from the
14/// environment when it renders a collection; backends without support ignore it
15/// (the collection still renders correctly, just without the transition).
16///
17/// [`animation`]: Self::animation
18#[derive(Debug, Clone)]
19pub struct CollectionTransition {
20 /// Timing curve for an item's enter and exit.
21 pub animation: Animation,
22}
23
24impl CollectionTransition {
25 /// Creates a transition with the given animation timing.
26 #[must_use]
27 pub const fn new(animation: Animation) -> Self {
28 Self { animation }
29 }
30}
31
32/// Wraps `content` so every reactive collection within it animates membership
33/// changes with `animation` (see [`CollectionTransition`]).
34///
35/// Free-function constructor, mirroring [`background`](fn@crate::background) and the
36/// other layout composers: it scopes the transition to `content`'s subtree
37/// through the environment.
38pub fn collection_transition(content: impl View, animation: Animation) -> impl View {
39 CollectionTransitionScope { content, animation }
40}
41
42struct CollectionTransitionScope<V> {
43 content: V,
44 animation: Animation,
45}
46
47impl<V: View> View for CollectionTransitionScope<V> {
48 fn body(self, env: &Environment) -> impl View {
49 Metadata::new(
50 self.content,
51 env.extending(CollectionTransition::new(self.animation)),
52 )
53 }
54}