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