1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Bar animation playground preview (CH-03 example #13).
use *;
use component_doc;
/// Interactive bar animation demo with skip toggle and series/category sliders.
///
/// Explore how pop-in animation responds to data shape changes. Use the **Skip animation**
/// switch to verify reduced-motion behavior and the `data-orbital-chart-skip-animation` attribute.
///
/// # When to use
///
/// - Validating motion tokens during chart development.
/// - Demonstrating `skip_animation` for users who prefer reduced motion.
/// - Tuning perceived performance when series or category counts change dynamically.
///
/// # Usage
///
/// 1. Open this preview while running the preview host on `:3010`.
/// 2. Toggle **Skip animation** and confirm the chart root attribute updates.
/// 3. Adjust series and category sliders to see staggered bar entrance on data changes.
/// 4. Mirror the `Switch` / `Slider` control pattern when building your own chart playgrounds.
///
/// # Best Practices
///
/// ## Do's
///
/// * Leave `skip_animation` unset in production so OS reduced-motion preferences apply automatically.
/// * Use `ChartMotion` for duration and curve overrides — not ad-hoc CSS transitions.
/// * Pair animation toggles with visible chart content in docs and QA previews.
///
/// ## Don'ts
///
/// * Do not force animation on when the user prefers reduced motion.
/// * Do not use this playground as the only bar chart example — see `bar-chart` for API docs.
///
/// # Examples
///
/// ## Animation playground
/// Live controls adjust series count, category count, and skip animation. Bars re-enter with
/// pop-in motion when data changes unless skip is enabled or reduced motion is active.
/// <!-- preview -->
/// ```rust,ignore
/// use leptos::prelude::*;
/// use orbital_core_components::{
/// Slider, SliderAppearance, SliderBind, SliderLabel, Switch, SwitchBind,
/// };
/// use crate::preview::fixtures::quarter_categories;
/// use crate::{AxisDef, BarChart, BarLabelConfig, ChartMotion, ScaleType, SeriesDef};
/// let skip = RwSignal::new(false);
/// let series_count = RwSignal::new(2.0_f64);
/// let category_count = RwSignal::new(4.0_f64);
/// view! {
/// <div data-testid="bar-chart-animation-preview" style="display: flex; flex-direction: column; gap: 16px;">
/// <div style="display: flex; flex-wrap: wrap; gap: 24px; align-items: center;">
/// <Switch bind=skip label="Skip animation" />
/// <Slider
/// bind=series_count
/// appearance=SliderAppearance {
/// min: Signal::from(1.0),
/// max: Signal::from(4.0),
/// step: MaybeProp::from(1.0),
/// ..Default::default()
/// }
/// >
/// <SliderLabel value=series_count.read_only()>"Series count"</SliderLabel>
/// </Slider>
/// <Slider
/// bind=category_count
/// appearance=SliderAppearance {
/// min: Signal::from(2.0),
/// max: Signal::from(8.0),
/// step: MaybeProp::from(1.0),
/// ..Default::default()
/// }
/// >
/// <SliderLabel value=category_count.read_only()>"Category count"</SliderLabel>
/// </Slider>
/// </div>
/// {move || {
/// let n = series_count.get().round().clamp(1.0, 4.0) as usize;
/// let cats = category_count.get().round().clamp(2.0, 8.0) as usize;
/// let series: Vec<SeriesDef> = (0..n)
/// .map(|i| SeriesDef {
/// id: format!("series-{i}"),
/// label: Some(format!("Series {}", i + 1)),
/// data: Some((0..cats).map(|c| (i + 1 + c) as f64 * 1000.0).collect()),
/// ..Default::default()
/// })
/// .collect();
/// let categories: Vec<String> = quarter_categories()
/// .into_iter()
/// .cycle()
/// .take(cats)
/// .collect();
/// view! {
/// <BarChart
/// series=series
/// x_axis=vec![AxisDef {
/// id: "x".into(),
/// scale_type: ScaleType::Band,
/// data: Some(categories),
/// ..Default::default()
/// }]
/// skip_animation=skip.get()
/// motion=ChartMotion {
/// skip_animation: false,
/// ..Default::default()
/// }
/// bar_label=BarLabelConfig { show: Some(true), ..Default::default() }
/// corner_radius=4.0
/// width=520.0
/// height=320.0
/// />
/// }
/// }}
/// </div>
/// }
/// ```