impulse_thaw/tab_list/
tab.rs1use 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 #[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}