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#[derive(PartialEq)]
17pub struct SideBar {
18 pub(crate) theme: Option<SideBarThemePartial>,
20 content: Option<Element>,
22 bar: Option<Element>,
24 width: Size,
26}
27
28impl Default for SideBar {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34impl SideBar {
35 pub fn new() -> Self {
36 Self {
37 theme: None,
38 content: None,
39 bar: None,
40 width: Size::px(180.),
41 }
42 }
43
44 pub fn content(mut self, content: impl Into<Element>) -> Self {
45 self.content = Some(content.into());
46 self
47 }
48
49 pub fn bar(mut self, bar: impl Into<Element>) -> Self {
50 self.bar = Some(bar.into());
51 self
52 }
53
54 pub fn width(mut self, width: impl Into<Size>) -> Self {
55 self.width = width.into();
56 self
57 }
58}
59
60impl Render for SideBar {
61 fn render(&self) -> impl IntoElement {
62 let SideBarTheme {
63 spacing,
64 padding,
65 background,
66 color,
67 } = get_theme!(&self.theme, sidebar);
68
69 rect()
70 .horizontal()
71 .width(Size::fill())
72 .height(Size::fill())
73 .color(color)
74 .child(
75 rect()
76 .overflow(Overflow::Clip)
77 .width(self.width.clone())
78 .height(Size::fill())
79 .background(background)
80 .child(
81 ScrollView::new()
82 .width(self.width.clone())
83 .spacing(spacing)
84 .child(rect().padding(padding).maybe_child(self.bar.clone())),
85 ),
86 )
87 .child(
88 rect()
89 .overflow(Overflow::Clip)
90 .expanded()
91 .maybe_child(self.content.clone()),
92 )
93 }
94}
95
96#[derive(Debug, Default, PartialEq, Clone, Copy)]
97pub enum ButtonStatus {
98 #[default]
100 Idle,
101 Hovering,
103}
104#[derive(PartialEq)]
105pub struct SideBarItem {
106 pub(crate) theme: Option<SideBarItemThemePartial>,
108 children: Vec<Element>,
110 on_press: Option<EventHandler<Event<PressEventData>>>,
112 overflow: Overflow,
114 key: DiffKey,
115}
116
117impl KeyExt for SideBarItem {
118 fn write_key(&mut self) -> &mut DiffKey {
119 &mut self.key
120 }
121}
122
123impl Default for SideBarItem {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl ChildrenExt for SideBarItem {
130 fn get_children(&mut self) -> &mut Vec<Element> {
131 &mut self.children
132 }
133}
134
135impl SideBarItem {
136 pub fn new() -> Self {
137 Self {
138 theme: None,
139 children: Vec::new(),
140 on_press: None,
141 overflow: Overflow::Clip,
142 key: DiffKey::None,
143 }
144 }
145
146 pub fn on_press(mut self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
147 self.on_press = Some(on_press.into());
148 self
149 }
150
151 pub fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
152 self.overflow = overflow.into();
153 self
154 }
155}
156
157impl Render for SideBarItem {
158 fn render(&self) -> impl IntoElement {
159 let SideBarItemTheme {
160 margin,
161 hover_background,
162 active_background,
163 background,
164 corner_radius,
165 padding,
166 color,
167 } = get_theme!(&self.theme, sidebar_item);
168 let mut status = use_state(ButtonStatus::default);
169 let is_active = use_activable_route();
170
171 use_drop(move || {
172 if status() == ButtonStatus::Hovering {
173 Cursor::set(CursorIcon::default());
174 }
175 });
176
177 let on_pointer_enter = move |_| {
178 status.set(ButtonStatus::Hovering);
179 Cursor::set(CursorIcon::Pointer);
180 };
181
182 let on_pointer_leave = move |_| {
183 status.set(ButtonStatus::default());
184 Cursor::set(CursorIcon::default());
185 };
186
187 let background = match *status.read() {
188 _ if is_active => active_background,
189 ButtonStatus::Hovering => hover_background,
190 ButtonStatus::Idle => background,
191 };
192
193 rect()
194 .a11y_focusable(true)
195 .a11y_role(AccessibilityRole::Button)
196 .map(self.on_press.clone(), |rect, on_press| {
197 rect.on_press(on_press)
198 })
199 .on_pointer_enter(on_pointer_enter)
200 .on_pointer_leave(on_pointer_leave)
201 .overflow(self.overflow)
202 .width(Size::fill())
203 .margin(margin)
204 .padding(padding)
205 .color(color)
206 .background(background)
207 .corner_radius(corner_radius)
208 .children(self.children.clone())
209 }
210
211 fn render_key(&self) -> DiffKey {
212 self.key.clone().or(self.default_key())
213 }
214}