Skip to main content

freya_components/
sidebar.rs

1use freya_core::prelude::*;
2use torin::size::Size;
3
4use crate::{
5    activable_route_context::use_activable_route,
6    get_theme,
7    theming::component_themes::{
8        SideBarItemTheme,
9        SideBarItemThemePartial,
10    },
11};
12#[derive(Debug, Default, PartialEq, Clone, Copy)]
13pub enum SideBarItemStatus {
14    /// Default state.
15    #[default]
16    Idle,
17    /// User is hovering the sidebar item.
18    Hovering,
19}
20
21/// Button designed for sidebars.
22///
23/// # Example
24///
25/// ```rust
26/// # use freya::prelude::*;
27/// fn app() -> impl IntoElement {
28///     rect()
29///         .horizontal()
30///         .child(
31///             rect()
32///                 .theme_background()
33///                 .padding(8.)
34///                 .width(Size::px(150.))
35///                 .height(Size::fill())
36///                 .child(SideBarItem::new().child("Home"))
37///                 .child(SideBarItem::new().child("Settings")),
38///         )
39///         .child(rect().expanded().center().child("Main content"))
40/// }
41/// # use freya_testing::prelude::*;
42/// # launch_doc(|| {
43/// #   rect().center().expanded().child(
44/// #       app()
45/// #   )
46/// # }, "./images/gallery_sidebar.png")
47/// # .with_hook(|t| { t.move_cursor((20., 20.)); t.sync_and_update(); })
48/// # .with_scale_factor(0.75)
49/// # .render();
50/// ```
51///
52/// # Preview
53/// ![SideBarItem Preview][SideBarItem]
54
55#[derive(Clone, PartialEq)]
56pub struct SideBarItem {
57    /// Theme override.
58    pub(crate) theme: Option<SideBarItemThemePartial>,
59    /// Inner child for the [SideBarItem].
60    children: Vec<Element>,
61    /// Optionally handle the `on_press` event in the [SideBarItem].
62    on_press: Option<EventHandler<Event<PressEventData>>>,
63    /// Optionally specify a custom `overflow` attribute for this component. Defaults to [OverflowMode::Clip].
64    overflow: Overflow,
65    key: DiffKey,
66}
67
68impl KeyExt for SideBarItem {
69    fn write_key(&mut self) -> &mut DiffKey {
70        &mut self.key
71    }
72}
73
74impl Default for SideBarItem {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl ChildrenExt for SideBarItem {
81    fn get_children(&mut self) -> &mut Vec<Element> {
82        &mut self.children
83    }
84}
85
86impl SideBarItem {
87    pub fn new() -> Self {
88        Self {
89            theme: None,
90            children: Vec::new(),
91            on_press: None,
92            overflow: Overflow::Clip,
93            key: DiffKey::None,
94        }
95    }
96
97    pub fn on_press(mut self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
98        self.on_press = Some(on_press.into());
99        self
100    }
101
102    pub fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
103        self.overflow = overflow.into();
104        self
105    }
106
107    /// Get the theme override for this component.
108    pub fn get_theme(&self) -> Option<&SideBarItemThemePartial> {
109        self.theme.as_ref()
110    }
111
112    /// Set a theme override for this component.
113    pub fn theme(mut self, theme: SideBarItemThemePartial) -> Self {
114        self.theme = Some(theme);
115        self
116    }
117}
118
119impl Component for SideBarItem {
120    fn render(&self) -> impl IntoElement {
121        let SideBarItemTheme {
122            margin,
123            hover_background,
124            active_background,
125            background,
126            corner_radius,
127            padding,
128            color,
129        } = get_theme!(&self.theme, sidebar_item);
130        let mut status = use_state(SideBarItemStatus::default);
131        let is_active = use_activable_route();
132
133        let on_pointer_enter = move |_| {
134            status.set(SideBarItemStatus::Hovering);
135        };
136
137        let on_pointer_leave = move |_| {
138            status.set(SideBarItemStatus::default());
139        };
140
141        let background = match *status.read() {
142            _ if is_active => active_background,
143            SideBarItemStatus::Hovering => hover_background,
144            SideBarItemStatus::Idle => background,
145        };
146
147        rect()
148            .a11y_focusable(true)
149            .a11y_role(AccessibilityRole::Link)
150            .map(self.on_press.clone(), |rect, on_press| {
151                rect.on_press(on_press)
152            })
153            .on_pointer_enter(on_pointer_enter)
154            .on_pointer_leave(on_pointer_leave)
155            .overflow(self.overflow)
156            .width(Size::fill())
157            .margin(margin)
158            .padding(padding)
159            .color(color)
160            .background(background)
161            .corner_radius(corner_radius)
162            .children(self.children.clone())
163    }
164
165    fn render_key(&self) -> DiffKey {
166        self.key.clone().or(self.default_key())
167    }
168}