mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
✨ feat: perfect tabs component
This commit is contained in:
parent
0296c4a00b
commit
c1537409a7
7 changed files with 81 additions and 11 deletions
|
@ -49,6 +49,9 @@ pub fn App(cx: Scope) -> impl IntoView {
|
|||
<Route path="/toast" view=move |cx| view! {cx,
|
||||
<MobilePage path="/melt-ui?path=/mobile/toast" />
|
||||
} />
|
||||
<Route path="/tabs" view=move |cx| view! {cx,
|
||||
<TabsPage />
|
||||
} />
|
||||
</Route>
|
||||
</Routes>
|
||||
<Routes base="/melt-ui/mobile".to_string()>
|
||||
|
|
|
@ -12,7 +12,7 @@ pub fn ComponentsPage(cx: Scope) -> impl IntoView {
|
|||
create_effect(cx, move |_| {
|
||||
let pathname = loaction.pathname.get();
|
||||
|
||||
let re = Regex::new(r"^/components/(.+)$").unwrap();
|
||||
let re = Regex::new(r"^/melt-ui/components/(.+)$").unwrap();
|
||||
let Some(caps) = re.captures(&pathname) else {
|
||||
return;
|
||||
};
|
||||
|
@ -46,6 +46,7 @@ pub fn ComponentsPage(cx: Scope) -> impl IntoView {
|
|||
<MenuItem key="button" label="button" />
|
||||
<MenuItem key="checkbox" label="checkbox" />
|
||||
<MenuItem key="toast" label="toast" />
|
||||
<MenuItem key="tabs" label="tabs" />
|
||||
</Menu>
|
||||
</LayoutSider>
|
||||
<Layout>
|
||||
|
|
|
@ -10,6 +10,7 @@ mod modal;
|
|||
mod nav_bar;
|
||||
mod slider;
|
||||
mod tabbar;
|
||||
mod tabs;
|
||||
mod toast;
|
||||
|
||||
pub use button::*;
|
||||
|
@ -24,4 +25,5 @@ pub use modal::*;
|
|||
pub use nav_bar::*;
|
||||
pub use slider::*;
|
||||
pub use tabbar::*;
|
||||
pub use tabs::*;
|
||||
pub use toast::*;
|
||||
|
|
17
gh-pages/src/pages/tabs/mod.rs
Normal file
17
gh-pages/src/pages/tabs/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use leptos::*;
|
||||
use melt_ui::*;
|
||||
|
||||
#[component]
|
||||
pub fn TabsPage(cx: Scope) -> impl IntoView {
|
||||
let active_key = create_rw_signal(cx, "apple");
|
||||
view! { cx,
|
||||
<Tabs active_key>
|
||||
<Tab key="apple" label="Apple">
|
||||
"apple"
|
||||
</Tab>
|
||||
<Tab key="pear" label="Pear">
|
||||
"pear"
|
||||
</Tab>
|
||||
</Tabs>
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
mod tab;
|
||||
use crate::{utils::mount_style::mount_style, theme::use_theme, Theme};
|
||||
use crate::{theme::use_theme, utils::mount_style::mount_style, Theme};
|
||||
use leptos::*;
|
||||
use stylers::style_sheet_str;
|
||||
pub use tab::*;
|
||||
|
@ -20,19 +20,56 @@ pub fn Tabs(cx: Scope, active_key: RwSignal<&'static str>, children: Children) -
|
|||
let mut css_vars = String::new();
|
||||
let theme = theme.get();
|
||||
let color_primary = theme.common.color_primary.clone();
|
||||
css_vars.push_str(&format!("--label-active-background-color: {color_primary};"));
|
||||
css_vars.push_str(&format!(
|
||||
"--label-active-background-color: {color_primary};"
|
||||
));
|
||||
css_vars
|
||||
});
|
||||
|
||||
let label_line = create_rw_signal::<Option<TabsLabelLine>>(cx, None);
|
||||
let label_line_style = create_memo(cx, 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::<html::Div>(cx);
|
||||
|
||||
view! { cx, class=class_name,
|
||||
<div class="melt-tabs" style=move || css_vars.get()>
|
||||
<div class="melt-tabs__label-list">
|
||||
<For each=move || tab_options_vec.get() key=move |v| v.key.clone() view=move |cx, options| view! {cx, class=class_name,
|
||||
<span class="melt-tabs__label" class=("melt-tabs__label--active", move || options.key == active_key.get()) on:click=move |_| active_key.set(options.key)>
|
||||
{ options.label }
|
||||
</span>
|
||||
<div class="melt-tabs__label-list" ref=label_list_ref>
|
||||
<For each=move || tab_options_vec.get() key=move |v| v.key.clone() view=move |cx, options| {
|
||||
let label_ref = create_node_ref::<html::Span>(cx);
|
||||
create_effect(cx, move |_| {
|
||||
let Some(label) = label_ref.get() else {
|
||||
return;
|
||||
};
|
||||
let Some(label_list) = label_list_ref.get() else {
|
||||
return;
|
||||
};
|
||||
if options.key == active_key.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! {cx, class=class_name,
|
||||
<span class="melt-tabs__label" class=("melt-tabs__label--active", move || options.key == active_key.get())
|
||||
on:click=move |_| active_key.set(options.key)
|
||||
ref=label_ref
|
||||
>
|
||||
{ options.label }
|
||||
</span>
|
||||
}
|
||||
}>
|
||||
</For>
|
||||
<span class="melt-tabs-label__active"></span>
|
||||
<span class="melt-tabs-label__line" style=move || label_line_style.get()></span>
|
||||
</div>
|
||||
<div>
|
||||
{ children(cx) }
|
||||
|
@ -41,6 +78,12 @@ pub fn Tabs(cx: Scope, active_key: RwSignal<&'static str>, children: Children) -
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct TabsLabelLine {
|
||||
width: f64,
|
||||
left: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TabsInjectionKey {
|
||||
active_key: RwSignal<&'static str>,
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
.melt-tab {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.melt-tab--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.melt-tabs-label__active {
|
||||
.melt-tabs-label__line {
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
background-color: var(--label-active-background-color);
|
||||
border-radius: 3px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue