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 a11y_id = use_a11y();
113 let focus = use_focus(a11y_id);
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 =
138 if focus() == Focus::Keyboard || is_active || *status.read() == TabStatus::Hovering {
139 hover_background
140 } else {
141 background
142 };
143
144 rect()
145 .a11y_id(a11y_id)
146 .a11y_focusable(Focusable::Enabled)
147 .a11y_role(AccessibilityRole::Tab)
148 .on_pointer_enter(on_pointer_enter)
149 .on_pointer_leave(on_pointer_leave)
150 .map(self.on_press.clone(), |el, on_press| el.on_press(on_press))
151 .width(width)
152 .height(height)
153 .center()
154 .overflow(Overflow::Clip)
155 .padding(padding)
156 .background(background)
157 .color(color)
158 .corner_radius(corner_radius)
159 .children(self.children.clone())
160 }
161
162 fn render_key(&self) -> DiffKey {
163 self.key.clone().or(self.default_key())
164 }
165}