mod tab; use crate::{theme::use_theme, utils::mount_style, Theme}; use leptos::*; pub use tab::*; #[component] pub fn Tabs(#[prop(optional, into)] value: RwSignal, children: Children) -> impl IntoView { mount_style("tabs", include_str!("./tabs.css")); let tab_options_vec = create_rw_signal(vec![]); provide_context(TabsInjection { active_key: value, tab_options_vec, }); let theme = use_theme(Theme::light); let css_vars = create_memo(move |_| { let mut css_vars = String::new(); theme.with(|theme| { let color_primary = theme.common.color_primary.clone(); css_vars.push_str(&format!( "--thaw-label-active-background-color: {color_primary};" )); }); css_vars }); let label_line = create_rw_signal::>(None); let label_line_style = create_memo(move |_| { let mut style = String::new(); if let Some(line) = label_line.get() { style.push_str(&format!("width: {}px; left: {}px", line.width, line.left)) } style }); let label_list_ref = create_node_ref::(); view! {
(); let TabOption { key, label } = option; create_effect({ let key = key.clone(); move |_| { let Some(label) = label_ref.get() else { return; }; let Some(label_list) = label_list_ref.get() else { return; }; if key.clone() == value.get() { request_animation_frame(move || { let list_rect = label_list.get_bounding_client_rect(); let rect = label.get_bounding_client_rect(); label_line .set( Some(TabsLabelLine { width: rect.width(), left: rect.left() - list_rect.left(), }), ); }); } } }); view! { {label} } } />
{children()}
} } #[derive(Clone)] pub(crate) struct TabsLabelLine { width: f64, left: f64, } #[derive(Clone)] pub(crate) struct TabsInjection { active_key: RwSignal, tab_options_vec: RwSignal>, } impl TabsInjection { pub fn get_key(&self) -> String { self.active_key.get() } pub(crate) fn push_tab_options(&self, options: TabOption) { self.tab_options_vec.update(|v| { v.push(options); }); } } pub(crate) fn use_tabs() -> TabsInjection { expect_context() }