feat: perfect tabs component

This commit is contained in:
luoxiao 2023-06-18 22:13:32 +08:00
parent 0296c4a00b
commit c1537409a7
7 changed files with 81 additions and 11 deletions

View file

@ -49,6 +49,9 @@ pub fn App(cx: Scope) -> impl IntoView {
<Route path="/toast" view=move |cx| view! {cx, <Route path="/toast" view=move |cx| view! {cx,
<MobilePage path="/melt-ui?path=/mobile/toast" /> <MobilePage path="/melt-ui?path=/mobile/toast" />
} /> } />
<Route path="/tabs" view=move |cx| view! {cx,
<TabsPage />
} />
</Route> </Route>
</Routes> </Routes>
<Routes base="/melt-ui/mobile".to_string()> <Routes base="/melt-ui/mobile".to_string()>

View file

@ -12,7 +12,7 @@ pub fn ComponentsPage(cx: Scope) -> impl IntoView {
create_effect(cx, move |_| { create_effect(cx, move |_| {
let pathname = loaction.pathname.get(); 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 { let Some(caps) = re.captures(&pathname) else {
return; return;
}; };
@ -46,6 +46,7 @@ pub fn ComponentsPage(cx: Scope) -> impl IntoView {
<MenuItem key="button" label="button" /> <MenuItem key="button" label="button" />
<MenuItem key="checkbox" label="checkbox" /> <MenuItem key="checkbox" label="checkbox" />
<MenuItem key="toast" label="toast" /> <MenuItem key="toast" label="toast" />
<MenuItem key="tabs" label="tabs" />
</Menu> </Menu>
</LayoutSider> </LayoutSider>
<Layout> <Layout>

View file

@ -10,6 +10,7 @@ mod modal;
mod nav_bar; mod nav_bar;
mod slider; mod slider;
mod tabbar; mod tabbar;
mod tabs;
mod toast; mod toast;
pub use button::*; pub use button::*;
@ -24,4 +25,5 @@ pub use modal::*;
pub use nav_bar::*; pub use nav_bar::*;
pub use slider::*; pub use slider::*;
pub use tabbar::*; pub use tabbar::*;
pub use tabs::*;
pub use toast::*; pub use toast::*;

View 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>
}
}

View file

@ -1,5 +1,5 @@
mod tab; 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 leptos::*;
use stylers::style_sheet_str; use stylers::style_sheet_str;
pub use tab::*; 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 mut css_vars = String::new();
let theme = theme.get(); let theme = theme.get();
let color_primary = theme.common.color_primary.clone(); 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 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, view! { cx, class=class_name,
<div class="melt-tabs" style=move || css_vars.get()> <div class="melt-tabs" style=move || css_vars.get()>
<div class="melt-tabs__label-list"> <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| view! {cx, class=class_name, <For each=move || tab_options_vec.get() key=move |v| v.key.clone() view=move |cx, options| {
<span class="melt-tabs__label" class=("melt-tabs__label--active", move || options.key == active_key.get()) on:click=move |_| active_key.set(options.key)> let label_ref = create_node_ref::<html::Span>(cx);
{ options.label } create_effect(cx, move |_| {
</span> 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> </For>
<span class="melt-tabs-label__active"></span> <span class="melt-tabs-label__line" style=move || label_line_style.get()></span>
</div> </div>
<div> <div>
{ children(cx) } { 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)] #[derive(Clone)]
pub struct TabsInjectionKey { pub struct TabsInjectionKey {
active_key: RwSignal<&'static str>, active_key: RwSignal<&'static str>,

View file

@ -1,3 +1,7 @@
.melt-tab {
padding-top: 12px;
}
.melt-tab--hidden { .melt-tab--hidden {
display: none; display: none;
} }

View file

@ -10,11 +10,11 @@
cursor: pointer; cursor: pointer;
} }
.melt-tabs-label__active { .melt-tabs-label__line {
position: absolute; position: absolute;
height: 3px; height: 3px;
background-color: var(--label-active-background-color); background-color: var(--label-active-background-color);
border-radius: 3px; border-radius: 3px;
bottom: 0; bottom: 0;
left: 0; left: 0;
} }