2023-06-15 18:02:21 +08:00
|
|
|
mod tab;
|
2023-10-11 21:25:11 +08:00
|
|
|
|
2023-11-13 16:17:45 +08:00
|
|
|
use crate::{theme::use_theme, utils::mount_style, Theme};
|
2023-06-15 18:02:21 +08:00
|
|
|
use leptos::*;
|
2023-10-07 21:41:03 +08:00
|
|
|
|
2023-06-15 18:02:21 +08:00
|
|
|
pub use tab::*;
|
|
|
|
|
|
|
|
#[component]
|
2023-11-09 14:43:30 +08:00
|
|
|
pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView {
|
2023-10-07 21:41:03 +08:00
|
|
|
mount_style("tabs", include_str!("./tabs.css"));
|
2023-08-29 09:11:22 +08:00
|
|
|
let tab_options_vec = create_rw_signal(vec![]);
|
2023-11-09 16:46:14 +08:00
|
|
|
provide_context(TabsInjection {
|
2023-11-09 14:43:30 +08:00
|
|
|
active_key: value,
|
2023-10-18 10:09:55 +08:00
|
|
|
tab_options_vec,
|
2023-08-29 09:11:22 +08:00
|
|
|
});
|
|
|
|
let theme = use_theme(Theme::light);
|
|
|
|
let css_vars = create_memo(move |_| {
|
2023-06-15 18:02:21 +08:00
|
|
|
let mut css_vars = String::new();
|
2023-11-02 22:01:48 +08:00
|
|
|
theme.with(|theme| {
|
|
|
|
let color_primary = theme.common.color_primary.clone();
|
|
|
|
css_vars.push_str(&format!(
|
2023-11-05 16:03:58 +08:00
|
|
|
"--thaw-label-active-background-color: {color_primary};"
|
2023-11-02 22:01:48 +08:00
|
|
|
));
|
|
|
|
});
|
2023-06-15 18:02:21 +08:00
|
|
|
css_vars
|
|
|
|
});
|
2023-06-18 22:13:32 +08:00
|
|
|
|
2023-08-29 09:11:22 +08:00
|
|
|
let label_line = create_rw_signal::<Option<TabsLabelLine>>(None);
|
|
|
|
let label_line_style = create_memo(move |_| {
|
2023-06-18 22:13:32 +08:00
|
|
|
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
|
|
|
|
});
|
2023-08-29 09:11:22 +08:00
|
|
|
let label_list_ref = create_node_ref::<html::Div>();
|
2023-06-18 22:13:32 +08:00
|
|
|
|
2023-10-07 21:41:03 +08:00
|
|
|
view! {
|
2023-11-05 16:03:58 +08:00
|
|
|
<div class="thaw-tabs" style=move || css_vars.get()>
|
|
|
|
<div class="thaw-tabs__label-list" ref=label_list_ref>
|
2023-10-08 09:28:13 +08:00
|
|
|
<For
|
|
|
|
each=move || tab_options_vec.get()
|
2023-10-17 17:25:20 +08:00
|
|
|
key=move |v| v.key.clone()
|
|
|
|
children=move |option| {
|
2023-10-08 09:28:13 +08:00
|
|
|
let label_ref = create_node_ref::<html::Span>();
|
2023-10-17 17:25:20 +08:00
|
|
|
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(),
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
2023-10-08 09:28:13 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
view! {
|
|
|
|
<span
|
2023-11-05 16:03:58 +08:00
|
|
|
class="thaw-tabs__label"
|
2023-10-08 09:28:13 +08:00
|
|
|
class=(
|
2023-11-05 16:03:58 +08:00
|
|
|
"thaw-tabs__label--active",
|
2023-10-17 17:25:20 +08:00
|
|
|
{
|
|
|
|
let key = key.clone();
|
|
|
|
move || key == value.get()
|
|
|
|
},
|
2023-10-08 09:28:13 +08:00
|
|
|
)
|
|
|
|
|
2023-10-17 17:25:20 +08:00
|
|
|
on:click={
|
|
|
|
let key = key.clone();
|
|
|
|
move |_| value.set(key.clone())
|
|
|
|
}
|
2023-11-02 22:01:48 +08:00
|
|
|
|
2023-10-08 09:28:13 +08:00
|
|
|
ref=label_ref
|
|
|
|
>
|
2023-10-17 17:25:20 +08:00
|
|
|
{label}
|
2023-10-08 09:28:13 +08:00
|
|
|
</span>
|
2023-06-18 22:13:32 +08:00
|
|
|
}
|
|
|
|
}
|
2023-10-08 09:28:13 +08:00
|
|
|
/>
|
|
|
|
|
2023-11-05 16:03:58 +08:00
|
|
|
<span class="thaw-tabs-label__line" style=move || label_line_style.get()></span>
|
2023-06-15 18:02:21 +08:00
|
|
|
</div>
|
2023-10-08 09:28:13 +08:00
|
|
|
<div>{children()}</div>
|
2023-06-15 18:02:21 +08:00
|
|
|
</div>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-18 22:13:32 +08:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub(crate) struct TabsLabelLine {
|
|
|
|
width: f64,
|
|
|
|
left: f64,
|
|
|
|
}
|
|
|
|
|
2023-06-15 18:02:21 +08:00
|
|
|
#[derive(Clone)]
|
2023-11-09 16:46:14 +08:00
|
|
|
pub(crate) struct TabsInjection {
|
2023-10-17 17:25:20 +08:00
|
|
|
active_key: RwSignal<String>,
|
|
|
|
tab_options_vec: RwSignal<Vec<TabOption>>,
|
2023-06-15 18:02:21 +08:00
|
|
|
}
|
|
|
|
|
2023-11-09 16:46:14 +08:00
|
|
|
impl TabsInjection {
|
2023-10-17 17:25:20 +08:00
|
|
|
pub fn get_key(&self) -> String {
|
2023-06-15 18:02:21 +08:00
|
|
|
self.active_key.get()
|
|
|
|
}
|
|
|
|
|
2023-10-17 17:25:20 +08:00
|
|
|
pub(crate) fn push_tab_options(&self, options: TabOption) {
|
2023-06-15 18:02:21 +08:00
|
|
|
self.tab_options_vec.update(|v| {
|
|
|
|
v.push(options);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-09 16:46:14 +08:00
|
|
|
pub(crate) fn use_tabs() -> TabsInjection {
|
|
|
|
expect_context()
|
2023-06-15 18:02:21 +08:00
|
|
|
}
|