freya_components/
sidebar.rs1use freya_core::prelude::*;
2use torin::size::Size;
3
4use crate::{
5 activable_route_context::use_activable_route,
6 get_theme,
7 scrollviews::ScrollView,
8 theming::component_themes::{
9 SideBarItemTheme,
10 SideBarItemThemePartial,
11 SideBarTheme,
12 SideBarThemePartial,
13 },
14};
15
16#[cfg_attr(feature = "docs",
45 doc = embed_doc_image::embed_image!("sidebar", "images/gallery_sidebar.png"),
46)]
47#[derive(PartialEq)]
48pub struct SideBar {
49 pub(crate) theme: Option<SideBarThemePartial>,
51 content: Option<Element>,
53 bar: Option<Element>,
55 width: Size,
57}
58
59impl Default for SideBar {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65impl SideBar {
66 pub fn new() -> Self {
67 Self {
68 theme: None,
69 content: None,
70 bar: None,
71 width: Size::px(180.),
72 }
73 }
74
75 pub fn content(mut self, content: impl Into<Element>) -> Self {
76 self.content = Some(content.into());
77 self
78 }
79
80 pub fn bar(mut self, bar: impl Into<Element>) -> Self {
81 self.bar = Some(bar.into());
82 self
83 }
84
85 pub fn width(mut self, width: impl Into<Size>) -> Self {
86 self.width = width.into();
87 self
88 }
89}
90
91impl Component for SideBar {
92 fn render(&self) -> impl IntoElement {
93 let SideBarTheme {
94 spacing,
95 padding,
96 background,
97 color,
98 } = get_theme!(&self.theme, sidebar);
99
100 rect()
101 .horizontal()
102 .width(Size::fill())
103 .height(Size::fill())
104 .color(color)
105 .child(
106 rect()
107 .overflow(Overflow::Clip)
108 .width(self.width.clone())
109 .height(Size::fill())
110 .background(background)
111 .child(
112 ScrollView::new()
113 .width(self.width.clone())
114 .spacing(spacing)
115 .child(rect().padding(padding).maybe_child(self.bar.clone())),
116 ),
117 )
118 .child(
119 rect()
120 .overflow(Overflow::Clip)
121 .expanded()
122 .maybe_child(self.content.clone()),
123 )
124 }
125}
126
127#[derive(Debug, Default, PartialEq, Clone, Copy)]
128pub enum ButtonStatus {
129 #[default]
131 Idle,
132 Hovering,
134}
135#[derive(PartialEq)]
136pub struct SideBarItem {
137 pub(crate) theme: Option<SideBarItemThemePartial>,
139 children: Vec<Element>,
141 on_press: Option<EventHandler<Event<PressEventData>>>,
143 overflow: Overflow,
145 key: DiffKey,
146}
147
148impl KeyExt for SideBarItem {
149 fn write_key(&mut self) -> &mut DiffKey {
150 &mut self.key
151 }
152}
153
154impl Default for SideBarItem {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl ChildrenExt for SideBarItem {
161 fn get_children(&mut self) -> &mut Vec<Element> {
162 &mut self.children
163 }
164}
165
166impl SideBarItem {
167 pub fn new() -> Self {
168 Self {
169 theme: None,
170 children: Vec::new(),
171 on_press: None,
172 overflow: Overflow::Clip,
173 key: DiffKey::None,
174 }
175 }
176
177 pub fn on_press(mut self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
178 self.on_press = Some(on_press.into());
179 self
180 }
181
182 pub fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
183 self.overflow = overflow.into();
184 self
185 }
186}
187
188impl Component for SideBarItem {
189 fn render(&self) -> impl IntoElement {
190 let SideBarItemTheme {
191 margin,
192 hover_background,
193 active_background,
194 background,
195 corner_radius,
196 padding,
197 color,
198 } = get_theme!(&self.theme, sidebar_item);
199 let mut status = use_state(ButtonStatus::default);
200 let is_active = use_activable_route();
201
202 use_drop(move || {
203 if status() == ButtonStatus::Hovering {
204 Cursor::set(CursorIcon::default());
205 }
206 });
207
208 let on_pointer_enter = move |_| {
209 status.set(ButtonStatus::Hovering);
210 Cursor::set(CursorIcon::Pointer);
211 };
212
213 let on_pointer_leave = move |_| {
214 status.set(ButtonStatus::default());
215 Cursor::set(CursorIcon::default());
216 };
217
218 let background = match *status.read() {
219 _ if is_active => active_background,
220 ButtonStatus::Hovering => hover_background,
221 ButtonStatus::Idle => background,
222 };
223
224 rect()
225 .a11y_focusable(true)
226 .a11y_role(AccessibilityRole::Link)
227 .map(self.on_press.clone(), |rect, on_press| {
228 rect.on_press(on_press)
229 })
230 .on_pointer_enter(on_pointer_enter)
231 .on_pointer_leave(on_pointer_leave)
232 .overflow(self.overflow)
233 .width(Size::fill())
234 .margin(margin)
235 .padding(padding)
236 .color(color)
237 .background(background)
238 .corner_radius(corner_radius)
239 .children(self.children.clone())
240 }
241
242 fn render_key(&self) -> DiffKey {
243 self.key.clone().or(self.default_key())
244 }
245}