vizia_core/views/
badge.rs

1use crate::prelude::*;
2
3/// Enum which represents the placement of a badge on its parent.
4#[derive(Default, Debug, Clone, Copy, PartialEq)]
5pub enum BadgePlacement {
6    /// The badge should be placed at the top-left of the view.
7    TopLeft,
8    /// The badge should be placed at the top of the view.
9    Top,
10    /// The badge should be placed at the top-right of the view.
11    #[default]
12    TopRight,
13    /// The badge should be placed at the left of the view.
14    Left,
15    /// The badge should be placed at the right of the view.
16    Right,
17    /// The badge should be placed at the bottom-left of the view.
18    BottomLeft,
19    /// The badge should be placed at the bottom of the view.
20    Bottom,
21    /// The badge should be placed at the bottom-right of the view.
22    BottomRight,
23}
24
25impl_res_simple!(BadgePlacement);
26
27/// A Badge view for showing notifications, counts, or status information.
28pub struct Badge {
29    placement: Signal<BadgePlacement>,
30}
31
32impl Badge {
33    fn common<F>(cx: &mut Context, content: F) -> Handle<Self>
34    where
35        F: FnOnce(&mut Context),
36    {
37        let placement = Signal::new(BadgePlacement::TopRight);
38        Self { placement }.build(cx, content).bind(placement, move |mut handle| {
39            let placement = placement.get();
40            let (t, b) = match placement {
41                BadgePlacement::TopLeft | BadgePlacement::TopRight => {
42                    (Stretch(1.0), Percentage(85.35))
43                }
44                BadgePlacement::Top => (Stretch(1.0), Percentage(100.0)),
45                BadgePlacement::Bottom => (Percentage(100.0), Stretch(1.0)),
46                BadgePlacement::BottomLeft | BadgePlacement::BottomRight => {
47                    (Percentage(85.35), Stretch(1.0))
48                }
49
50                BadgePlacement::Left | BadgePlacement::Right => (Stretch(1.0), Stretch(1.0)),
51            };
52
53            let (l, r) = match placement {
54                BadgePlacement::TopLeft | BadgePlacement::BottomLeft => {
55                    (Stretch(1.0), Percentage(85.35))
56                }
57                BadgePlacement::TopRight | BadgePlacement::BottomRight => {
58                    (Percentage(85.35), Stretch(1.0))
59                }
60                BadgePlacement::Left => (Stretch(1.0), Percentage(100.0)),
61                BadgePlacement::Right => (Percentage(100.0), Stretch(1.0)),
62                BadgePlacement::Top | BadgePlacement::Bottom => (Stretch(1.0), Stretch(1.0)),
63            };
64
65            handle = handle.top(t).bottom(b).left(l).right(r);
66
67            let translate = match placement {
68                BadgePlacement::TopLeft => (Percentage(50.0), Percentage(50.0)),
69                BadgePlacement::Top => (Percentage(0.0), Percentage(50.0)),
70                BadgePlacement::TopRight => (Percentage(-50.0), Percentage(50.0)),
71                BadgePlacement::BottomLeft => (Percentage(50.0), Percentage(-50.0)),
72                BadgePlacement::Bottom => (Percentage(0.0), Percentage(-50.0)),
73                BadgePlacement::BottomRight => (Percentage(-50.0), Percentage(-50.0)),
74                BadgePlacement::Left => (Percentage(50.0), Percentage(0.0)),
75                BadgePlacement::Right => (Percentage(-50.0), Percentage(0.0)),
76            };
77            handle.translate(translate);
78        })
79    }
80
81    /// Creates an empty badge.
82    ///
83    /// ```
84    /// # use vizia_core::prelude::*;
85    /// # use vizia_core::icons::ICON_USER;
86    /// # let cx = &mut Context::default();
87    /// Avatar::new(cx, |cx|{
88    ///     Svg::new(cx, ICON_USER);
89    /// })
90    /// .badge(|cx| Badge::empty(cx).class("error"));
91    /// ```
92    pub fn empty(cx: &mut Context) -> Handle<Self> {
93        Self::common(cx, |_| {})
94    }
95
96    /// Creates a new badge with the provided content.
97    ///
98    /// ```
99    /// # use vizia_core::prelude::*;
100    /// # use vizia_core::icons::ICON_USER;
101    /// # let cx = &mut Context::default();
102    /// Avatar::new(cx, |cx|{
103    ///     Svg::new(cx, ICON_USER);
104    /// })
105    /// .badge(|cx| Badge::new(cx, |cx| Label::new(cx, "2")));
106    /// ```
107    pub fn new<F, V>(cx: &mut Context, content: F) -> Handle<Self>
108    where
109        F: FnOnce(&mut Context) -> Handle<V>,
110        V: View,
111    {
112        Self::common(cx, |cx| {
113            (content)(cx);
114        })
115    }
116}
117
118impl View for Badge {
119    fn element(&self) -> Option<&'static str> {
120        Some("badge")
121    }
122}
123
124impl Handle<'_, Badge> {
125    /// Sets the placement of a badge relative to its parent. Accepts a value or signal of type [BadgePlacement].
126    pub fn placement<U: Into<BadgePlacement> + Clone + 'static>(
127        self,
128        placement: impl Res<U> + 'static,
129    ) -> Self {
130        let placement = placement.to_signal(self.cx);
131        self.bind(placement, move |handle| {
132            let value = placement.get();
133            let converted: BadgePlacement = value.into();
134            handle.modify(|badge| {
135                badge.placement.set(converted);
136            });
137        })
138    }
139}