freya_components/
sidebar.rs1use freya_core::prelude::*;
2use torin::{
3 gaps::Gaps,
4 size::Size,
5};
6
7use crate::{
8 activable_route_context::use_activable_route,
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)]
72pub struct SideBarItem {
73 pub(crate) theme: Option<SideBarItemThemePartial>,
75 children: Vec<Element>,
77 on_press: Option<EventHandler<Event<PressEventData>>>,
79 overflow: Overflow,
81 key: DiffKey,
82}
83
84impl KeyExt for SideBarItem {
85 fn write_key(&mut self) -> &mut DiffKey {
86 &mut self.key
87 }
88}
89
90impl Default for SideBarItem {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl ChildrenExt for SideBarItem {
97 fn get_children(&mut self) -> &mut Vec<Element> {
98 &mut self.children
99 }
100}
101
102impl SideBarItem {
103 pub fn new() -> Self {
104 Self {
105 theme: None,
106 children: Vec::new(),
107 on_press: None,
108 overflow: Overflow::Clip,
109 key: DiffKey::None,
110 }
111 }
112
113 pub fn on_press(mut self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
114 self.on_press = Some(on_press.into());
115 self
116 }
117
118 pub fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
119 self.overflow = overflow.into();
120 self
121 }
122
123 pub fn get_theme(&self) -> Option<&SideBarItemThemePartial> {
125 self.theme.as_ref()
126 }
127
128 pub fn theme(mut self, theme: SideBarItemThemePartial) -> Self {
130 self.theme = Some(theme);
131 self
132 }
133}
134
135impl Component for SideBarItem {
136 fn render(&self) -> impl IntoElement {
137 let SideBarItemTheme {
138 margin,
139 hover_background,
140 active_background,
141 background,
142 focus_border_fill,
143 corner_radius,
144 padding,
145 color,
146 } = get_theme!(&self.theme, SideBarItemThemePreference, "sidebar_item");
147 let mut status = use_state(SideBarItemStatus::default);
148 let is_active = use_activable_route();
149 let a11y_id = use_a11y();
150 let focus = use_focus(a11y_id);
151
152 let on_pointer_enter = move |_| {
153 status.set(SideBarItemStatus::Hovering);
154 };
155
156 let on_pointer_leave = move |_| {
157 status.set(SideBarItemStatus::default());
158 };
159
160 let background = match *status.read() {
161 _ if is_active => active_background,
162 SideBarItemStatus::Hovering => hover_background,
163 SideBarItemStatus::Idle => background,
164 };
165
166 let border = (focus() == Focus::Keyboard).then(|| {
167 Border::new()
168 .fill(focus_border_fill)
169 .width(2.)
170 .alignment(BorderAlignment::Inner)
171 });
172
173 let on_press = self.on_press.clone();
174 rect()
175 .a11y_id(a11y_id)
176 .a11y_focusable(true)
177 .a11y_role(AccessibilityRole::Link)
178 .on_press(move |e: Event<PressEventData>| {
179 if let Some(handler) = &on_press {
180 handler.call(e);
181 }
182 })
183 .on_pointer_enter(on_pointer_enter)
184 .on_pointer_leave(on_pointer_leave)
185 .overflow(self.overflow)
186 .width(Size::fill())
187 .margin(margin)
188 .padding(padding)
189 .color(color)
190 .background(background)
191 .corner_radius(corner_radius)
192 .map(border, |rect, border| rect.border(border))
193 .children(self.children.clone())
194 }
195
196 fn render_key(&self) -> DiffKey {
197 self.key.clone().or(self.default_key())
198 }
199}