use serde::{Deserialize, Serialize};
use crate::card::{
components::{content_components::plain_text::PlainText, CardElement},
icon::FeishuCardTextIcon,
};
#[derive(Debug, Serialize, Deserialize)]
pub struct CollapsiblePanel {
tag: String,
#[serde(skip_serializing_if = "Option::is_none")]
expanded: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
background_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
header: Option<CollapsibleHeader>,
#[serde(skip_serializing_if = "Option::is_none")]
border: Option<CollapsibleBorder>,
#[serde(skip_serializing_if = "Option::is_none")]
vertical_spacing: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
padding: Option<String>,
elements: Vec<CardElement>,
}
impl Default for CollapsiblePanel {
fn default() -> Self {
Self {
tag: "collapsible_panel".to_string(),
expanded: None,
background_color: None,
header: None,
border: None,
vertical_spacing: None,
padding: None,
elements: vec![],
}
}
}
impl CollapsiblePanel {
pub fn new() -> Self {
Self::default()
}
pub fn expanded(mut self, expanded: bool) -> Self {
self.expanded = Some(expanded);
self
}
pub fn background_color(mut self, background_color: &str) -> Self {
self.background_color = Some(background_color.to_string());
self
}
pub fn header(mut self, header: CollapsibleHeader) -> Self {
self.header = Some(header);
self
}
pub fn border(mut self, color: &str, corner_radius: &str) -> Self {
self.border = Some(CollapsibleBorder {
color: Some(color.to_string()),
corner_radius: Some(corner_radius.to_string()),
});
self
}
pub fn vertical_spacing(mut self, vertical_spacing: &str) -> Self {
self.vertical_spacing = Some(vertical_spacing.to_string());
self
}
pub fn padding(mut self, padding: &str) -> Self {
self.padding = Some(padding.to_string());
self
}
pub fn elements(mut self, elements: Vec<CardElement>) -> Self {
self.elements = elements;
self
}
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct CollapsibleHeader {
title: PlainText,
#[serde(skip_serializing_if = "Option::is_none")]
background_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
vertical_align: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
padding: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
icon: Option<FeishuCardTextIcon>,
#[serde(skip_serializing_if = "Option::is_none")]
icon_position: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
icon_expanded_angle: Option<i32>,
}
impl CollapsibleHeader {
pub fn new(title: &str) -> Self {
Self {
title: PlainText::text(title),
background_color: None,
vertical_align: None,
padding: None,
icon: None,
icon_position: None,
icon_expanded_angle: None,
}
}
pub fn title(mut self, title: PlainText) -> Self {
self.title = title;
self
}
pub fn background_color(mut self, background_color: &str) -> Self {
self.background_color = Some(background_color.to_string());
self
}
pub fn vertical_align(mut self, vertical_align: &str) -> Self {
self.vertical_align = Some(vertical_align.to_string());
self
}
pub fn padding(mut self, padding: &str) -> Self {
self.padding = Some(padding.to_string());
self
}
pub fn icon(mut self, icon: FeishuCardTextIcon) -> Self {
self.icon = Some(icon);
self
}
pub fn icon_position(mut self, icon_position: &str) -> Self {
self.icon_position = Some(icon_position.to_string());
self
}
pub fn icon_expanded_angle(mut self, icon_expanded_angle: i32) -> Self {
self.icon_expanded_angle = Some(icon_expanded_angle);
self
}
}
#[derive(Debug, Serialize, Deserialize, Default)]
struct CollapsibleBorder {
#[serde(skip_serializing_if = "Option::is_none")]
color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
corner_radius: Option<String>,
}
#[cfg(test)]
mod test {
use serde_json::json;
use crate::card::{
components::{
containers::collapsible_panel::{CollapsibleHeader, CollapsiblePanel},
content_components::{plain_text::PlainText, rich_text::FeishuCardMarkdown},
CardElement,
},
icon::FeishuCardTextIcon,
};
#[test]
fn test_collapsible() {
let panel = CollapsiblePanel::new()
.expanded(true)
.background_color("grey")
.header(
CollapsibleHeader::default()
.title(PlainText::text("**面板标题文本**").tag("markdown"))
.vertical_align("center")
.padding("4px 0px 4px 8px")
.icon(
FeishuCardTextIcon::new()
.token("chat-forbidden_outlined")
.color("orange")
.img_key("img_v2_38811724")
.size("16px 16px"),
)
.icon_position("follow_text")
.icon_expanded_angle(-180),
)
.border("grey", "5px")
.vertical_spacing("8px")
.padding("8px 8px 8px 8px")
.elements(vec![CardElement::Markdown(FeishuCardMarkdown::new(
"很长的文本",
))]);
let expect = json!({
"tag": "collapsible_panel", "expanded": true, "background_color": "grey", "header": {
"title": {
"tag": "markdown",
"content": "**面板标题文本**"
},
"vertical_align": "center", "padding": "4px 0px 4px 8px", "icon": {
"tag": "standard_icon", "token": "chat-forbidden_outlined", "color": "orange", "img_key": "img_v2_38811724", "size": "16px 16px" },
"icon_position": "follow_text", "icon_expanded_angle": -180 },
"border": {
"color": "grey", "corner_radius": "5px" },
"vertical_spacing": "8px", "padding": "8px 8px 8px 8px", "elements": [
{
"tag": "markdown",
"content": "很长的文本"
}
]
});
assert_eq!(serde_json::to_value(&panel).unwrap(), expect);
}
}