Skip to content

Commit 9025496

Browse files
committed
feat(studio+components): typed enum fields, palette prefill, generic list rows
- closed-set stringly-typed fields become real Rust enums (stepper orientation, chart funnel direction, cursor style and path easing — serde values identical; unknown strings now fail the typed parse as blocking validate errors instead of silent fallbacks; path_easing loses its silent ease_in_out catch-all, the one behavior break, no repo example affected; cursor_style flagged as unconsumed by the painter). The schema-driven inspector renders exact-variant selects automatically - chart::DEFAULT_PALETTE goes pub; an empty chart colors list shows an 'engine palette' hint and + Add color prefills the 8 colors the canvas actually renders (pure palette_prefill map, extensible) - StringList kind for non-color string arrays (axes, categories) with per-entry inputs; third list control triggers the rule of three: generic ListRows shell (render-prop items) now backs ColorRows, NumberList and StringList; radar_data joins the structured-data exclusions
1 parent 5ba9975 commit 9025496

7 files changed

Lines changed: 371 additions & 131 deletions

File tree

crates/rustmotion-components/src/chart/funnel.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ impl Chart {
1616
h: f32,
1717
progress: f32,
1818
) -> Result<()> {
19-
let horizontal = self
20-
.direction
21-
.as_deref()
22-
.map(|d| d == "horizontal")
23-
.unwrap_or(false);
19+
let horizontal = self.direction == Some(super::ChartDirection::Horizontal);
2420

2521
if horizontal {
2622
self.render_funnel_horizontal(canvas, w, h, progress)

crates/rustmotion-components/src/chart/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,21 @@ mod radial;
2020
mod scatter;
2121
mod waterfall;
2222

23-
pub(crate) const DEFAULT_PALETTE: &[&str] = &[
23+
/// The engine's default series palette. `pub` so the studio can prefill an
24+
/// empty `colors` list with what the canvas actually renders.
25+
pub const DEFAULT_PALETTE: &[&str] = &[
2426
"#3B82F6", "#EF4444", "#22C55E", "#F59E0B", "#8B5CF6", "#EC4899", "#06B6D4", "#F97316",
2527
];
2628

29+
/// Funnel flow direction. Closed set (painter matches both variants); JSON
30+
/// values unchanged ("vertical"/"horizontal").
31+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
32+
#[serde(rename_all = "snake_case")]
33+
pub enum ChartDirection {
34+
Vertical,
35+
Horizontal,
36+
}
37+
2738
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
2839
#[serde(rename_all = "snake_case")]
2940
pub enum ChartType {
@@ -123,7 +134,7 @@ pub struct Chart {
123134
// Funnel direction
124135
/// Direction for funnel chart: "vertical" (default) or "horizontal".
125136
#[serde(default)]
126-
pub direction: Option<String>,
137+
pub direction: Option<ChartDirection>,
127138

128139
// Axes, grid, labels
129140
#[serde(default)]

crates/rustmotion-components/src/cursor.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ pub struct Cursor {
4747
pub click_duration: f32,
4848
/// Visual cursor style: "default" (arrow) or "pointer" (hand).
4949
/// Currently both render as a bar; this is metadata for future SVG cursors.
50-
#[serde(default = "default_cursor_style")]
51-
pub cursor_style: String,
50+
#[serde(default)]
51+
pub cursor_style: CursorStyle,
5252
/// Easing for movement between waypoints: "ease_in_out" (default), "linear", "ease_out".
53-
#[serde(default = "default_path_easing")]
54-
pub path_easing: String,
53+
#[serde(default)]
54+
pub path_easing: CursorPathEasing,
5555
#[serde(flatten)]
5656
pub timing: TimingConfig,
5757
#[serde(default)]
@@ -86,12 +86,24 @@ fn default_click_duration() -> f32 {
8686
0.3
8787
}
8888

89-
fn default_cursor_style() -> String {
90-
"default".to_string()
89+
/// Visual cursor style. Closed documented set; JSON values unchanged.
90+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
91+
#[serde(rename_all = "snake_case")]
92+
pub enum CursorStyle {
93+
#[default]
94+
Default,
95+
Pointer,
9196
}
9297

93-
fn default_path_easing() -> String {
94-
"ease_in_out".to_string()
98+
/// Easing of the cursor's waypoint path. Closed set, now matched
99+
/// exhaustively; previously-silent unknown values fail the typed parse.
100+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
101+
#[serde(rename_all = "snake_case")]
102+
pub enum CursorPathEasing {
103+
Linear,
104+
EaseOut,
105+
#[default]
106+
EaseInOut,
95107
}
96108

97109
rustmotion_core::impl_traits!(Cursor {
@@ -161,11 +173,10 @@ impl Cursor {
161173
let raw_t = ((time - move_start) / move_duration).clamp(0.0, 1.0);
162174

163175
// Apply easing
164-
let t = match self.path_easing.as_str() {
165-
"linear" => raw_t,
166-
"ease_out" => 1.0 - (1.0 - raw_t).powi(3),
167-
_ => {
168-
// ease_in_out
176+
let t = match self.path_easing {
177+
CursorPathEasing::Linear => raw_t,
178+
CursorPathEasing::EaseOut => 1.0 - (1.0 - raw_t).powi(3),
179+
CursorPathEasing::EaseInOut => {
169180
if raw_t < 0.5 {
170181
4.0 * raw_t * raw_t * raw_t
171182
} else {

crates/rustmotion-components/src/stepper.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@ fn default_transition_duration() -> f64 {
2020
0.5
2121
}
2222

23-
fn default_orientation() -> String {
24-
"horizontal".to_string()
23+
/// Layout axis of the stepper. Closed set, matched exhaustively by the
24+
/// painter; serde snake_case keeps the JSON values identical
25+
/// ("horizontal"/"vertical") — an unknown value now fails the typed parse
26+
/// (blocking validate error, by design).
27+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
28+
#[serde(rename_all = "snake_case")]
29+
pub enum StepperOrientation {
30+
#[default]
31+
Horizontal,
32+
Vertical,
2533
}
2634

2735
fn default_active_color() -> String {
@@ -64,8 +72,8 @@ pub struct Stepper {
6472
#[serde(default = "default_transition_duration")]
6573
pub transition_duration: f64,
6674
/// Layout direction: "horizontal" or "vertical".
67-
#[serde(default = "default_orientation")]
68-
pub orientation: String,
75+
#[serde(default)]
76+
pub orientation: StepperOrientation,
6977
/// Color of the active step node.
7078
#[serde(default = "default_active_color")]
7179
pub active_color: String,
@@ -146,7 +154,7 @@ impl Stepper {
146154
let emoji_desc_font =
147155
emoji_typeface().map(|tf| skia_safe::Font::from_typeface(tf, desc_font_size));
148156

149-
let is_horizontal = self.orientation == "horizontal";
157+
let is_horizontal = self.orientation == StepperOrientation::Horizontal;
150158

151159
if is_horizontal {
152160
let padding = r + 8.0;

0 commit comments

Comments
 (0)