freya_components/
sidebar.rs1use freya_core::prelude::*;
2use torin::{
3 gaps::Gaps,
4 size::Size,
5};
6
7use crate::{
8 activable_context::use_is_active,
9 define_theme,
10 get_theme,
11};
12
13define_theme! {
14 %[component]
15 pub SideBarItem {
16 %[fields]
17 color: Color,
18 background: Color,
19 hover_background: Color,
20 active_background: Color,
21 focus_border_fill: Color,
22 corner_radius: CornerRadius,
23 margin: Gaps,
24 padding: Gaps,
25 }
26}
27
28#[derive(Debug, Default, PartialEq, Clone, Copy)]
29pub enum SideBarItemStatus {
30 #[default]
32 Idle,
33 Hovering,
35}
36
37#[derive(Clone, PartialEq)]
62pub struct SideBarItem {
63 pub(crate) theme: Option<SideBarItemThemePartial>,
65 children: Vec<Element>,
67 on_press: Option<EventHandler<Event<PressEventData>>>,
69 overflow: Overflow,
71 key: DiffKey,
72}
73
74impl KeyExt for SideBarItem {
75 fn key_mut(&mut self) -> &mut DiffKey {
76 &mut self.key
77 }
78}
79
80impl Default for SideBarItem {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86impl ChildrenExt for SideBarItem {
87 fn children_mut(&mut self) -> &mut Vec<Element> {
88 &mut self.children
89 }
90}
91
92impl SideBarItem {
93 pub fn new() -> Self {
94 Self {
95 theme: None,
96 children: Vec::new(),
97 on_press: None,
98 overflow: Overflow::Clip,
99 key: DiffKey::None,
100 }
101 }
102
103 pub fn on_press(mut self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
104 self.on_press = Some(on_press.into());
105 self
106 }
107
108 pub fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
109 self.overflow = overflow.into();
110 self
111 }
112
113 pub fn get_theme(&self) -> Option<&SideBarItemThemePartial> {
115 self.theme.as_ref()
116 }
117
118 pub fn theme(mut self, theme: SideBarItemThemePartial) -> Self {
120 self.theme = Some(theme);
121 self
122 }
123}
124
125impl Component for SideBarItem {
126 fn render(&self) -> impl IntoElement {
127 let SideBarItemTheme {
128 margin,
129 hover_background,
130 active_background,
131 background,
132 focus_border_fill,
133 corner_radius,
134 padding,
135 color,
136 } = get_theme!(&self.theme, SideBarItemThemePreference, "sidebar_item");
137 let mut status = use_state(SideBarItemStatus::default);
138 let is_active = use_is_active();
139 let a11y_id = use_a11y();
140 let focus = use_focus(a11y_id);
141
142 let on_pointer_enter = move |_| {
143 status.set(SideBarItemStatus::Hovering);
144 };
145
146 let on_pointer_leave = move |_| {
147 status.set(SideBarItemStatus::default());
148 };
149
150 let background = match *status.read() {
151 _ if is_active => active_background,
152 SideBarItemStatus::Hovering => hover_background,
153 SideBarItemStatus::Idle => background,
154 };
155
156 let border = (focus() == Focus::Keyboard).then(|| {
157 Border::new()
158 .fill(focus_border_fill)
159 .width(2.)
160 .alignment(BorderAlignment::Inner)
161 });
162
163 let on_press = self.on_press.clone();
164 rect()
165 .a11y_id(a11y_id)
166 .a11y_focusable(true)
167 .a11y_role(AccessibilityRole::Link)
168 .on_press(move |e: Event<PressEventData>| {
169 if let Some(handler) = &on_press {
170 handler.call(e);
171 }
172 })
173 .on_pointer_enter(on_pointer_enter)
174 .on_pointer_leave(on_pointer_leave)
175 .overflow(self.overflow)
176 .width(Size::fill())
177 .margin(margin)
178 .padding(padding)
179 .color(color)
180 .background(background)
181 .corner_radius(corner_radius)
182 .map(border, |rect, border| rect.border(border))
183 .children(self.children.clone())
184 }
185
186 fn render_key(&self) -> DiffKey {
187 self.key.clone().or(self.default_key())
188 }
189}