impulse_thaw/tab_list/
tab.rs

1use super::{TabListInjection, TabRegisterData};
2use leptos::{html, prelude::*};
3use std::ops::Deref;
4use thaw_utils::{class_list, mount_style};
5
6#[component]
7pub fn Tab(
8    #[prop(optional, into)] class: MaybeProp<String>,
9    /// The indentifier of the tab.
10    #[prop(into)]
11    value: String,
12    children: Children,
13) -> impl IntoView {
14    mount_style("tab", include_str!("./tab.css"));
15
16    let tab_ref = NodeRef::<html::Button>::new();
17    let tab_list = TabListInjection::expect_context();
18    let value = StoredValue::new(value);
19    tab_list.register(TabRegisterData {
20        value: value.get_value(),
21        tab_ref,
22    });
23    on_cleanup(move || {
24        value.with_value(|v| tab_list.unregister(v));
25    });
26
27    let selected = Memo::new(move |_| {
28        tab_list
29            .selected_value
30            .with(|selected_value| value.with_value(|value| value == selected_value))
31    });
32
33    let on_select = move |_| {
34        tab_list.registered_tabs.with_untracked(|registered_tabs| {
35            if let Some(previous_selected_tab) = tab_list
36                .selected_value
37                .with_untracked(|selected_value| registered_tabs.get(selected_value))
38            {
39                let tab_el = tab_ref.get_untracked().unwrap();
40                let selected_tab_rect = tab_el.get_bounding_client_rect();
41                let previous_selected_tab_rect = previous_selected_tab
42                    .tab_ref
43                    .get_untracked()
44                    .unwrap()
45                    .get_bounding_client_rect();
46
47                let offset = previous_selected_tab_rect.x() - selected_tab_rect.x();
48                let scale = previous_selected_tab_rect.width() / selected_tab_rect.width();
49
50                let style = tab_el.deref().style();
51                let _ =
52                    style.set_property("--thaw-tab__indicator--offset", &format!("{offset:.0}px"));
53                let _ = style.set_property("--thaw-tab__indicator--scale", &format!("{scale:.2}"));
54
55                request_animation_frame(move || {
56                    let _ = style.set_property("--thaw-tab__indicator--offset", "0px");
57                    let _ = style.set_property("--thaw-tab__indicator--scale", "1");
58                });
59            }
60        });
61        tab_list.on_select(value.get_value());
62    };
63
64    view! {
65        <button
66            class=class_list!["thaw-tab", ("thaw-tab--selected", move || selected.get()), class]
67            role="tab"
68            aria-selected=move || if selected.get() { "true" } else { "false" }
69            node_ref=tab_ref
70            on:click=on_select
71        >
72            <span class="thaw-tab__content">{children()}</span>
73        </button>
74    }
75}