dioxus_material/
tab_row.rs1use crate::{use_theme, Ripple};
2use dioxus::prelude::*;
3use dioxus_resize_observer::{use_resize, Rect};
4use dioxus_spring::{use_animated, use_spring};
5use dioxus_use_mounted::use_mounted;
6use std::{collections::HashMap, time::Duration};
7
8#[component]
9pub fn TabRow(tabs: Vec<Element>, selected: usize, onselect: EventHandler<usize>) -> Element {
10 let sizes = use_signal(HashMap::new);
11
12 let width = sizes
13 .read()
14 .get(&selected)
15 .map(|rect: &Rect| rect.width() as f32)
16 .unwrap_or_default();
17 let left: f32 = (0..selected)
18 .map(|idx| {
19 sizes
20 .read()
21 .get(&idx)
22 .map(|rect: &Rect| rect.width() as f32)
23 .unwrap_or_default()
24 })
25 .sum();
26
27 let value_ref = use_spring([width, left], Duration::from_millis(200));
28 let animated_ref = use_mounted();
29
30 let theme = use_theme();
31 let primary_color = theme.primary_color.clone();
32
33 use_animated(animated_ref, value_ref, move |[width, left]| {
34 format!(
35 r"
36 position: absolute;
37 bottom: 0;
38 left: {left}px;
39 width: {width}px;
40 height: 4px;
41 background: {primary_color};
42 "
43 )
44 });
45
46 rsx!(
47 div { position: "relative",
48 ul {
49 display: "flex",
50 flex_direction: "row",
51 justify_content: "space-evenly",
52 list_style: "none",
53 margin: 0,
54 padding: 0,
55 {tabs.iter().enumerate().map(|(idx, tab)| rsx!(TabRowItem { index: idx, sizes: sizes, onselect: move |idx| onselect.call(idx), {tab} }))}
56 }
57 div { onmounted: move |event| animated_ref.onmounted(event) }
58 }
59 )
60}
61
62#[component]
63fn TabRowItem(
64 children: Element,
65 index: usize,
66 sizes: Signal<HashMap<usize, Rect>>,
67 onselect: EventHandler<usize>,
68) -> Element {
69 let mounted = use_mounted();
70 let resize = use_resize(mounted);
71
72 use_effect(move || {
73 if let Some(content_rect) = &*resize.read() {
74 sizes
75 .write()
76 .entry(index)
77 .and_modify(|rect| *rect = content_rect.clone())
78 .or_insert(content_rect.clone());
79 }
80 });
81
82 rsx!(
83 li {
84 display: "flex",
85 flex_direction: "row",
86 flex: 1,
87 margin: 0,
88 padding: 0,
89 onmounted: move |event| mounted.onmounted(event),
90 Ripple { onclick: move |_| onselect.call(index), children }
91 }
92 )
93}