#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub const IR_VERSION: u32 = 3;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct ViewIr {
pub version: u32,
pub root: Vec<ViewNode>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct ViewStyle {
#[serde(skip_serializing_if = "Option::is_none")]
pub padding: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_horizontal: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_vertical: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_top: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_bottom: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_left: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_right: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin_horizontal: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin_vertical: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin_top: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin_bottom: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin_left: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin_right: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub width: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub height: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_width: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_height: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_width: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_height: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub width_fraction: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub height_fraction: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_size: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_weight: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text_align: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_height: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub letter_spacing: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text_transform: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_family: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub italic: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub underline: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strikethrough: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub foreground_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub corner_radius: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub opacity: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hidden: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overflow_hidden: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flex_grow: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flex_shrink: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub align_self: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flex_wrap: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flex_direction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub z_index: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub right: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bottom: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub left: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub translate_x: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub translate_y: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scale_x: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scale_y: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rotate: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shadow_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shadow_radius: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shadow_offset_x: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shadow_offset_y: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text_overflow: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub white_space: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_clamp: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_select: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_gradient_direction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_gradient_from: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_gradient_to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub object_fit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub object_position: Option<String>,
}
impl ViewStyle {
fn is_effectively_empty(&self) -> bool {
self.padding.is_none()
&& self.padding_horizontal.is_none()
&& self.padding_vertical.is_none()
&& self.padding_top.is_none()
&& self.padding_bottom.is_none()
&& self.padding_left.is_none()
&& self.padding_right.is_none()
&& self.margin.is_none()
&& self.margin_horizontal.is_none()
&& self.margin_vertical.is_none()
&& self.margin_top.is_none()
&& self.margin_bottom.is_none()
&& self.margin_left.is_none()
&& self.margin_right.is_none()
&& self.width.is_none()
&& self.height.is_none()
&& self.min_width.is_none()
&& self.min_height.is_none()
&& self.max_width.is_none()
&& self.max_height.is_none()
&& self.width_fraction.is_none()
&& self.height_fraction.is_none()
&& self.aspect_ratio.is_none()
&& self.font_size.is_none()
&& self.font_weight.is_none()
&& self.text_align.is_none()
&& self.line_height.is_none()
&& self.letter_spacing.is_none()
&& self.text_transform.is_none()
&& self.font_family.is_none()
&& self.italic.is_none()
&& self.underline.is_none()
&& self.strikethrough.is_none()
&& self.foreground_color.is_none()
&& self.background_color.is_none()
&& self.corner_radius.is_none()
&& self.border_width.is_none()
&& self.border_color.is_none()
&& self.opacity.is_none()
&& self.hidden.is_none()
&& self.overflow_hidden.is_none()
&& self.flex_grow.is_none()
&& self.flex_shrink.is_none()
&& self.align_self.is_none()
&& self.flex_wrap.is_none()
&& self.flex_direction.is_none()
&& self.position.is_none()
&& self.z_index.is_none()
&& self.top.is_none()
&& self.right.is_none()
&& self.bottom.is_none()
&& self.left.is_none()
&& self.translate_x.is_none()
&& self.translate_y.is_none()
&& self.scale_x.is_none()
&& self.scale_y.is_none()
&& self.rotate.is_none()
&& self.shadow_color.is_none()
&& self.shadow_radius.is_none()
&& self.shadow_offset_x.is_none()
&& self.shadow_offset_y.is_none()
&& self.text_overflow.is_none()
&& self.white_space.is_none()
&& self.line_clamp.is_none()
&& self.cursor.is_none()
&& self.user_select.is_none()
&& self.background_gradient_direction.is_none()
&& self.background_gradient_from.is_none()
&& self.background_gradient_to.is_none()
&& self.object_fit.is_none()
&& self.object_position.is_none()
}
pub(crate) fn opt(self) -> Option<Self> {
if self.is_effectively_empty() {
None
} else {
Some(self)
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(
tag = "kind",
rename_all = "camelCase",
rename_all_fields = "camelCase"
)]
pub enum ViewNode {
#[serde(rename = "text")]
Text {
content: String,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "stack")]
Stack {
axis: StackAxis,
#[serde(skip_serializing_if = "Option::is_none")]
spacing: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
align_items: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
justify_content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
children: Vec<ViewNode>,
},
#[serde(rename = "button")]
Button {
label: String,
#[serde(skip_serializing_if = "Option::is_none")]
on_click: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "toggle")]
Toggle {
label: String,
#[serde(skip_serializing_if = "Option::is_none")]
bind: Option<String>,
checked: bool,
#[serde(skip_serializing_if = "Option::is_none")]
on_change: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "checkbox")]
Checkbox {
label: String,
#[serde(skip_serializing_if = "Option::is_none")]
bind: Option<String>,
checked: bool,
#[serde(skip_serializing_if = "Option::is_none")]
on_change: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "slider")]
Slider {
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
bind: Option<String>,
value: f32,
min: f32,
max: f32,
#[serde(skip_serializing_if = "Option::is_none")]
step: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "progress")]
Progress {
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
value: f32,
max: f32,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "meter")]
Meter {
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
value: f32,
min: f32,
max: f32,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "badge")]
Badge {
label: String,
#[serde(skip_serializing_if = "Option::is_none")]
tone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "divider")]
Divider {
axis: StackAxis,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "spacer")]
Spacer {
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "dropzone")]
Dropzone {
label: String,
#[serde(skip_serializing_if = "Option::is_none")]
accept: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
on_drop: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
children: Vec<ViewNode>,
},
#[serde(rename = "image")]
Image {
src: String,
#[serde(skip_serializing_if = "Option::is_none")]
alt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
placeholder: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "scroll")]
Scroll {
axis: StackAxis,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
children: Vec<ViewNode>,
},
#[serde(rename = "list")]
List {
ordered: bool,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
children: Vec<ViewNode>,
},
#[serde(rename = "listItem")]
ListItem {
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
children: Vec<ViewNode>,
},
#[serde(rename = "slotRotate")]
SlotRotate {
phrases: Vec<String>,
interval_ms: u64,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "input")]
Input {
placeholder: String,
bind: String,
#[serde(default)]
multiline: bool,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
#[serde(rename = "picker")]
Picker {
bind: String,
options: Vec<PickerOption>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ViewStyle>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct PickerOption {
pub value: String,
pub label: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "lowercase")]
pub enum StackAxis {
Row,
Column,
}