freya_components/
floating_tab.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 FloatingTab {
16 %[fields]
17 background: Color,
18 hover_background: Color,
19 width: Size,
20 height: Size,
21 padding: Gaps,
22 color: Color,
23 corner_radius: CornerRadius,
24 }
25}
26
27#[derive(Debug, Default, PartialEq, Clone, Copy)]
29pub enum TabStatus {
30 #[default]
32 Idle,
33 Hovering,
35}
36
37#[derive(Clone, PartialEq)]
38pub struct FloatingTab {
39 pub(crate) theme: Option<FloatingTabThemePartial>,
40 children: Vec<Element>,
41 on_press: Option<EventHandler<Event<PressEventData>>>,
43 key: DiffKey,
44}
45
46impl KeyExt for FloatingTab {
47 fn write_key(&mut self) -> &mut DiffKey {
48 &mut self.key
49 }
50}
51
52impl Default for FloatingTab {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl ChildrenExt for FloatingTab {
59 fn get_children(&mut self) -> &mut Vec<Element> {
60 &mut self.children
61 }
62}
63
64#[cfg_attr(feature = "docs",
86 doc = embed_doc_image::embed_image!("floating_tab", "images/gallery_floating_tab.png")
87)]
88impl FloatingTab {
89 pub fn new() -> Self {
90 Self {
91 children: vec![],
92 theme: None,
93 on_press: None,
94 key: DiffKey::None,
95 }
96 }
97
98 pub fn get_theme(&self) -> Option<&FloatingTabThemePartial> {
100 self.theme.as_ref()
101 }
102
103 pub fn theme(mut self, theme: FloatingTabThemePartial) -> Self {
105 self.theme = Some(theme);
106 self
107 }
108}
109
110impl Component for FloatingTab {
111 fn render(&self) -> impl IntoElement {
112 let focus = use_focus();
113 let focus_status = use_focus_status(focus);
114 let mut status = use_state(TabStatus::default);
115 let is_active = use_activable_route();
116
117 let FloatingTabTheme {
118 background,
119 hover_background,
120 padding,
121 width,
122 height,
123 color,
124 corner_radius,
125 } = get_theme!(&self.theme, FloatingTabThemePreference, "floating_tab");
126
127 let on_pointer_enter = move |_| {
128 Cursor::set(CursorIcon::Pointer);
129 status.set(TabStatus::Hovering);
130 };
131
132 let on_pointer_leave = move |_| {
133 Cursor::set(CursorIcon::default());
134 status.set(TabStatus::default());
135 };
136
137 let background = if focus_status() == FocusStatus::Keyboard
138 || is_active
139 || *status.read() == TabStatus::Hovering
140 {
141 hover_background
142 } else {
143 background
144 };
145
146 rect()
147 .a11y_id(focus.a11y_id())
148 .a11y_focusable(Focusable::Enabled)
149 .a11y_role(AccessibilityRole::Tab)
150 .on_pointer_enter(on_pointer_enter)
151 .on_pointer_leave(on_pointer_leave)
152 .map(self.on_press.clone(), |el, on_press| el.on_press(on_press))
153 .width(width)
154 .height(height)
155 .center()
156 .overflow(Overflow::Clip)
157 .padding(padding)
158 .background(background)
159 .color(color)
160 .corner_radius(corner_radius)
161 .children(self.children.clone())
162 }
163
164 fn render_key(&self) -> DiffKey {
165 self.key.clone().or(self.default_key())
166 }
167}