plotly/layout/
legend.rs

1use plotly_derive::FieldSetter;
2use serde::Serialize;
3
4use crate::color::Color;
5use crate::common::{Anchor, Font, Orientation, Title};
6use crate::layout::VAlign;
7
8#[derive(Serialize, Debug, Clone)]
9#[serde(rename_all = "lowercase")]
10pub enum TraceOrder {
11    Reversed,
12    Grouped,
13    #[serde(rename = "reversed+grouped")]
14    ReversedGrouped,
15    Normal,
16}
17
18#[derive(Serialize, Debug, Clone)]
19#[serde(rename_all = "lowercase")]
20pub enum ItemSizing {
21    Trace,
22    Constant,
23}
24
25#[derive(Debug, Clone)]
26pub enum ItemClick {
27    Toggle,
28    ToggleOthers,
29    False,
30}
31
32impl Serialize for ItemClick {
33    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
34    where
35        S: serde::Serializer,
36    {
37        match *self {
38            Self::Toggle => serializer.serialize_str("toggle"),
39            Self::ToggleOthers => serializer.serialize_str("toggleothers"),
40            Self::False => serializer.serialize_bool(false),
41        }
42    }
43}
44
45#[derive(Serialize, Debug, Clone)]
46#[serde(rename_all = "lowercase")]
47pub enum GroupClick {
48    ToggleItem,
49    ToggleGroup,
50}
51
52#[serde_with::skip_serializing_none]
53#[derive(Serialize, Debug, Clone, FieldSetter)]
54pub struct Legend {
55    #[serde(rename = "bgcolor")]
56    background_color: Option<Box<dyn Color>>,
57    #[serde(rename = "bordercolor")]
58    border_color: Option<Box<dyn Color>>,
59    #[serde(rename = "borderwidth")]
60    border_width: Option<usize>,
61    font: Option<Font>,
62    orientation: Option<Orientation>,
63    #[serde(rename = "traceorder")]
64    trace_order: Option<TraceOrder>,
65    #[serde(rename = "tracegroupgap")]
66    trace_group_gap: Option<usize>,
67    #[serde(rename = "itemsizing")]
68    item_sizing: Option<ItemSizing>,
69    #[serde(rename = "itemclick")]
70    item_click: Option<ItemClick>,
71    #[serde(rename = "itemdoubleclick")]
72    item_double_click: Option<ItemClick>,
73    x: Option<f64>,
74    #[serde(rename = "xanchor")]
75    x_anchor: Option<Anchor>,
76    y: Option<f64>,
77    #[serde(rename = "yanchor")]
78    y_anchor: Option<Anchor>,
79    valign: Option<VAlign>,
80    title: Option<Title>,
81    #[serde(rename = "groupclick")]
82    group_click: Option<GroupClick>,
83    #[serde(rename = "itemwidth")]
84    item_width: Option<usize>,
85}
86
87impl Legend {
88    pub fn new() -> Self {
89        Default::default()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use serde_json::{json, to_value};
96
97    use super::*;
98
99    #[test]
100    #[rustfmt::skip]
101    fn serialize_trace_order() {
102        assert_eq!(to_value(TraceOrder::Reversed).unwrap(), json!("reversed"));
103        assert_eq!(to_value(TraceOrder::Grouped).unwrap(), json!("grouped"));
104        assert_eq!(to_value(TraceOrder::ReversedGrouped).unwrap(), json!("reversed+grouped"));
105        assert_eq!(to_value(TraceOrder::Normal).unwrap(), json!("normal"));
106    }
107
108    #[test]
109    fn serialize_item_sizing() {
110        assert_eq!(to_value(ItemSizing::Trace).unwrap(), json!("trace"));
111        assert_eq!(to_value(ItemSizing::Constant).unwrap(), json!("constant"));
112    }
113
114    #[test]
115    #[rustfmt::skip]
116    fn serialize_item_click() {
117        assert_eq!(to_value(ItemClick::Toggle).unwrap(), json!("toggle"));
118        assert_eq!(to_value(ItemClick::ToggleOthers).unwrap(), json!("toggleothers"));
119        assert_eq!(to_value(ItemClick::False).unwrap(), json!(false));
120    }
121
122    #[test]
123    #[rustfmt::skip]
124    fn serialize_group_click() {
125        assert_eq!(to_value(GroupClick::ToggleItem).unwrap(), json!("toggleitem"));
126        assert_eq!(to_value(GroupClick::ToggleGroup).unwrap(), json!("togglegroup"));
127    }
128
129    #[test]
130    fn serialize_legend() {
131        let legend = Legend::new()
132            .background_color("#123123")
133            .border_color("#321321")
134            .border_width(500)
135            .font(Font::new())
136            .orientation(Orientation::Vertical)
137            .trace_order(TraceOrder::Normal)
138            .trace_group_gap(10)
139            .item_sizing(ItemSizing::Trace)
140            .item_click(ItemClick::Toggle)
141            .item_double_click(ItemClick::False)
142            .x(1.0)
143            .x_anchor(Anchor::Auto)
144            .y(2.0)
145            .y_anchor(Anchor::Left)
146            .valign(VAlign::Middle)
147            .title("title")
148            .group_click(GroupClick::ToggleItem)
149            .item_width(50);
150
151        let expected = json!({
152            "bgcolor": "#123123",
153            "bordercolor": "#321321",
154            "borderwidth": 500,
155            "font": {},
156            "orientation": "v",
157            "traceorder": "normal",
158            "tracegroupgap": 10,
159            "itemsizing": "trace",
160            "itemclick": "toggle",
161            "itemdoubleclick": false,
162            "x": 1.0,
163            "xanchor": "auto",
164            "y": 2.0,
165            "yanchor": "left",
166            "valign": "middle",
167            "title": {"text": "title"},
168            "groupclick": "toggleitem",
169            "itemwidth": 50
170        });
171
172        assert_eq!(to_value(legend).unwrap(), expected)
173    }
174}