use serde::{Deserialize, Serialize};
use crate::common::{
AccessibilityAttributes, Action, Checkable, ChildList, ComponentId, DynamicBoolean,
DynamicNumber, DynamicString, DynamicStringList,
};
pub const STANDARD_CATALOG_ID: &str = "https://a2ui.org/specification/v0_10/standard_catalog.json";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct ComponentCommon {
pub id: ComponentId,
#[serde(skip_serializing_if = "Option::is_none")]
pub accessibility: Option<AccessibilityAttributes>,
#[serde(skip_serializing_if = "Option::is_none")]
pub weight: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "component")]
pub enum Component {
Text(TextComponent),
Image(ImageComponent),
Icon(IconComponent),
Video(VideoComponent),
AudioPlayer(AudioPlayerComponent),
Row(RowComponent),
Column(ColumnComponent),
List(ListComponent),
Card(CardComponent),
Tabs(TabsComponent),
Modal(ModalComponent),
Divider(DividerComponent),
Button(ButtonComponent),
TextField(TextFieldComponent),
CheckBox(CheckBoxComponent),
ChoicePicker(ChoicePickerComponent),
Slider(SliderComponent),
DateTimeInput(DateTimeInputComponent),
}
impl Component {
pub fn id(&self) -> &str {
match self {
Component::Text(c) => &c.common.id,
Component::Image(c) => &c.common.id,
Component::Icon(c) => &c.common.id,
Component::Video(c) => &c.common.id,
Component::AudioPlayer(c) => &c.common.id,
Component::Row(c) => &c.common.id,
Component::Column(c) => &c.common.id,
Component::List(c) => &c.common.id,
Component::Card(c) => &c.common.id,
Component::Tabs(c) => &c.common.id,
Component::Modal(c) => &c.common.id,
Component::Divider(c) => &c.common.id,
Component::Button(c) => &c.common.id,
Component::TextField(c) => &c.common.id,
Component::CheckBox(c) => &c.common.id,
Component::ChoicePicker(c) => &c.common.id,
Component::Slider(c) => &c.common.id,
Component::DateTimeInput(c) => &c.common.id,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TextComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub text: DynamicString,
#[serde(skip_serializing_if = "Option::is_none")]
pub variant: Option<TextVariant>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TextVariant {
H1,
H2,
H3,
H4,
H5,
Caption,
Body,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ImageComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub url: DynamicString,
#[serde(skip_serializing_if = "Option::is_none")]
pub fit: Option<ImageFit>,
#[serde(skip_serializing_if = "Option::is_none")]
pub variant: Option<ImageVariant>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum ImageFit {
Contain,
Cover,
Fill,
None,
ScaleDown,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ImageVariant {
Icon,
Avatar,
SmallFeature,
MediumFeature,
LargeFeature,
Header,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct IconComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub name: IconName,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum IconName {
Preset(PresetIcon),
Custom { path: String },
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum PresetIcon {
AccountCircle,
Add,
ArrowBack,
ArrowForward,
AttachFile,
CalendarToday,
Call,
Camera,
Check,
Close,
Delete,
Download,
Edit,
Event,
Error,
FastForward,
Favorite,
FavoriteOff,
Folder,
Help,
Home,
Info,
LocationOn,
Lock,
LockOpen,
Mail,
Menu,
MoreVert,
MoreHoriz,
NotificationsOff,
Notifications,
Pause,
Payment,
Person,
Phone,
Photo,
Play,
Print,
Refresh,
Rewind,
Search,
Send,
Settings,
Share,
ShoppingCart,
SkipNext,
SkipPrevious,
Star,
StarHalf,
StarOff,
Stop,
Upload,
Visibility,
VisibilityOff,
VolumeDown,
VolumeMute,
VolumeOff,
VolumeUp,
Warning,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct VideoComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub url: DynamicString,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AudioPlayerComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub url: DynamicString,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<DynamicString>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RowComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub children: ChildList,
#[serde(skip_serializing_if = "Option::is_none")]
pub justify: Option<JustifyContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub align: Option<AlignItems>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ColumnComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub children: ChildList,
#[serde(skip_serializing_if = "Option::is_none")]
pub justify: Option<JustifyContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub align: Option<AlignItems>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum JustifyContent {
Start,
Center,
End,
SpaceBetween,
SpaceAround,
SpaceEvenly,
Stretch,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum AlignItems {
Start,
Center,
End,
Stretch,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ListComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub children: ChildList,
#[serde(skip_serializing_if = "Option::is_none")]
pub direction: Option<ListDirection>,
#[serde(skip_serializing_if = "Option::is_none")]
pub align: Option<AlignItems>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ListDirection {
Vertical,
Horizontal,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CardComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub child: ComponentId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TabsComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub tabs: Vec<TabItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TabItem {
pub title: DynamicString,
pub child: ComponentId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ModalComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub trigger: ComponentId,
pub content: ComponentId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DividerComponent {
#[serde(flatten)]
pub common: ComponentCommon,
#[serde(skip_serializing_if = "Option::is_none")]
pub axis: Option<DividerAxis>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum DividerAxis {
Horizontal,
Vertical,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ButtonComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub child: ComponentId,
pub action: Action,
#[serde(skip_serializing_if = "Option::is_none")]
pub variant: Option<ButtonVariant>,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub checkable: Option<Checkable>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ButtonVariant {
Primary,
Borderless,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TextFieldComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub label: DynamicString,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<DynamicString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub variant: Option<TextFieldVariant>,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub checkable: Option<Checkable>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum TextFieldVariant {
ShortText,
LongText,
Number,
Obscured,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CheckBoxComponent {
#[serde(flatten)]
pub common: ComponentCommon,
pub label: DynamicString,
pub value: DynamicBoolean,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub checkable: Option<Checkable>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ChoicePickerComponent {
#[serde(flatten)]
pub common: ComponentCommon,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<DynamicString>,
pub options: Vec<ChoiceOption>,
pub value: DynamicStringList,
#[serde(skip_serializing_if = "Option::is_none")]
pub variant: Option<ChoicePickerVariant>,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub checkable: Option<Checkable>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ChoiceOption {
pub label: DynamicString,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ChoicePickerVariant {
MultipleSelection,
MutuallyExclusive,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SliderComponent {
#[serde(flatten)]
pub common: ComponentCommon,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<DynamicString>,
pub min: f64,
pub max: f64,
pub value: DynamicNumber,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub checkable: Option<Checkable>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DateTimeInputComponent {
#[serde(flatten)]
pub common: ComponentCommon,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<DynamicString>,
pub value: DynamicString,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_date: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_time: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min: Option<DynamicString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max: Option<DynamicString>,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub checkable: Option<Checkable>,
}