refactor: tab

This commit is contained in:
luoxiao 2024-06-05 10:04:34 +08:00
parent 938342ab1f
commit e8942dc212
11 changed files with 95 additions and 93 deletions

View file

@ -82,8 +82,8 @@ fn TheRouter(is_routing: RwSignal<bool>) -> impl IntoView {
<Route path="/spin-button" view=SpinButtonMdPage/> <Route path="/spin-button" view=SpinButtonMdPage/>
<Route path="/spinner" view=SpinnerMdPage/> <Route path="/spinner" view=SpinnerMdPage/>
<Route path="/switch" view=SwitchMdPage/> <Route path="/switch" view=SwitchMdPage/>
<Route path="/tab-list" view=TabListMdPage/>
<Route path="/table" view=TableMdPage/> <Route path="/table" view=TableMdPage/>
<Route path="/tabs" view=TabsMdPage/>
<Route path="/tag" view=TagMdPage/> <Route path="/tag" view=TagMdPage/>
<Route path="/text" view=TextMdPage/> <Route path="/text" view=TextMdPage/>
<Route path="/time-picker" view=TimePickerMdPage/> <Route path="/time-picker" view=TimePickerMdPage/>

View file

@ -175,6 +175,10 @@ pub(crate) fn gen_menu_data() -> Vec<MenuGroupOption> {
value: "/components/spinner".into(), value: "/components/spinner".into(),
label: "Spinner".into(), label: "Spinner".into(),
}, },
MenuItemOption {
value: "/components/tab-list".into(),
label: "Tab List".into(),
},
MenuItemOption { MenuItemOption {
value: "/components/tag".into(), value: "/components/tag".into(),
label: "Tag".into(), label: "Tag".into(),
@ -251,10 +255,6 @@ pub(crate) fn gen_menu_data() -> Vec<MenuGroupOption> {
value: "/components/loading-bar".into(), value: "/components/loading-bar".into(),
label: "Loading Bar".into(), label: "Loading Bar".into(),
}, },
MenuItemOption {
value: "/components/tabs".into(),
label: "Tabs".into(),
},
MenuItemOption { MenuItemOption {
value: "/components/alert".into(), value: "/components/alert".into(),
label: "Alert".into(), label: "Alert".into(),

View file

@ -6,10 +6,16 @@ let selected_value = RwSignal::new(String::new());
view! { view! {
<TabList selected_value> <TabList selected_value>
<Tab value="apple"> <Tab value="apple">
"🍎 Apple" "Apple"
</Tab> </Tab>
<Tab value="pear"> <Tab value="pear">
"🍐 Pear" "Pear"
</Tab>
<Tab value="item1">
"Item 1"
</Tab>
<Tab value="item2">
"Item 2"
</Tab> </Tab>
</TabList> </TabList>
} }

View file

@ -64,8 +64,8 @@ pub fn include_md(_token_stream: proc_macro::TokenStream) -> proc_macro::TokenSt
"SpinButtonMdPage" => "../docs/spin_button/mod.md", "SpinButtonMdPage" => "../docs/spin_button/mod.md",
"SpinnerMdPage" => "../docs/spinner/mod.md", "SpinnerMdPage" => "../docs/spinner/mod.md",
"SwitchMdPage" => "../docs/switch/mod.md", "SwitchMdPage" => "../docs/switch/mod.md",
"TabListMdPage" => "../docs/tab_list/mod.md",
"TableMdPage" => "../docs/table/mod.md", "TableMdPage" => "../docs/table/mod.md",
"TabsMdPage" => "../docs/tabs/mod.md",
"TagMdPage" => "../docs/tag/mod.md", "TagMdPage" => "../docs/tag/mod.md",
"TimePickerMdPage" => "../docs/time_picker/mod.md", "TimePickerMdPage" => "../docs/time_picker/mod.md",
"TextMdPage" => "../docs/text/mod.md", "TextMdPage" => "../docs/text/mod.md",

View file

@ -38,7 +38,7 @@ mod spin_button;
mod spinner; mod spinner;
mod switch; mod switch;
mod table; mod table;
mod tabs; mod tab_list;
mod tag; mod tag;
mod text; mod text;
mod theme; mod theme;
@ -84,7 +84,7 @@ pub use spin_button::*;
pub use spinner::*; pub use spinner::*;
pub use switch::*; pub use switch::*;
pub use table::*; pub use table::*;
pub use tabs::*; pub use tab_list::*;
pub use tag::*; pub use tag::*;
pub use text::*; pub use text::*;
pub use thaw_utils::{create_component_ref, ComponentRef, SignalWatch}; pub use thaw_utils::{create_component_ref, ComponentRef, SignalWatch};

77
thaw/src/tab_list/tab.rs Normal file
View file

@ -0,0 +1,77 @@
use super::{TabListInjection, TabRegisterData};
use leptos::*;
use std::ops::Deref;
use thaw_utils::{class_list, mount_style};
#[component]
pub fn Tab(
#[prop(optional, into)] class: MaybeProp<String>,
#[prop(into)] value: String,
children: Children,
) -> impl IntoView {
mount_style("tab", include_str!("./tab.css"));
let tab_ref = NodeRef::<html::Button>::new();
let tab_list = TabListInjection::use_();
let value = StoredValue::new(value);
tab_list.register(TabRegisterData {
value: value.get_value(),
tab_ref,
});
on_cleanup(move || {
value.with_value(|v| tab_list.unregister(v));
});
let selected = Memo::new(move |_| {
tab_list
.selected_value
.with(|selected_value| value.with_value(|value| value == selected_value))
});
let on_select = move |_| {
tab_list.registered_tabs.with_untracked(|registered_tabs| {
if let Some(previous_selected_tab) = tab_list
.selected_value
.with_untracked(|selected_value| registered_tabs.get(selected_value))
{
let tab_el = tab_ref.get_untracked().unwrap();
let selected_tab_rect = tab_el.get_bounding_client_rect();
let previous_selected_tab_rect = previous_selected_tab
.tab_ref
.get_untracked()
.unwrap()
.get_bounding_client_rect();
let offset = previous_selected_tab_rect.x() - selected_tab_rect.x();
let scale = previous_selected_tab_rect.width() / selected_tab_rect.width();
let style = tab_el.deref().style();
let _ =
style.set_property("--thaw-tab__indicator--offset", &format!("{offset:.0}px"));
let _ = style.set_property("--thaw-tab__indicator--scale", &format!("{scale:.2}"));
request_animation_frame(move || {
let _ = style.set_property("--thaw-tab__indicator--offset", "0px");
let _ = style.set_property("--thaw-tab__indicator--scale", "1");
});
}
});
tab_list.on_select(value.get_value());
};
view! {
<button
class=class_list![
"thaw-tab", ("thaw-tab--selected", move || selected.get()), class
]
role="tab"
aria-selected=move || if selected.get() { "true" } else { "false" }
ref=tab_ref
on:click=on_select
>
<span class="thaw-tab__content">
{children()}
</span>
</button>
}
}

View file

@ -1,83 +0,0 @@
use super::{TabListInjection, TabRegisterData};
use leptos::*;
use std::ops::Deref;
use thaw_utils::{class_list, mount_style};
#[component]
pub fn Tab(
#[prop(optional, into)] class: MaybeProp<String>,
#[prop(into)] value: String,
children: Children,
) -> impl IntoView {
mount_style("tab", include_str!("./tab.css"));
let tab_ref = NodeRef::<html::Button>::new();
let tab_list = TabListInjection::use_();
let value = StoredValue::new(value);
tab_list.register(TabRegisterData {
value: value.get_value(),
tab_ref,
});
on_cleanup(move || {
value.with_value(|v| tab_list.unregister(v));
});
let selected = Memo::new(move |_| {
tab_list
.selected_value
.with(|selected_value| value.with_value(|value| value == selected_value))
});
Effect::new(move |_| {
let selected = selected.get();
if selected {
tab_list.registered_tabs.with_untracked(|registered_tabs| {
if let Some(previous_selected_tab) = tab_list
.previous_selected_value
.with_value(|selected_value| registered_tabs.get(selected_value))
{
let tab_el = tab_ref.get_untracked().unwrap();
let selected_tab_rect = tab_el.get_bounding_client_rect();
let previous_selected_tab_rect = previous_selected_tab
.tab_ref
.get_untracked()
.unwrap()
.get_bounding_client_rect();
let offset = previous_selected_tab_rect.x() - selected_tab_rect.x();
let scale = previous_selected_tab_rect.width() / selected_tab_rect.width();
let style = tab_el.deref().style();
let _ = style
.set_property("--thaw-tab__indicator--offset", &format!("{offset:.0}px"));
let _ =
style.set_property("--thaw-tab__indicator--scale", &format!("{scale:.2}"));
// let _ = style.get_property_value("offsetWidth");
// let _ = style.set_property("--thaw-tab__indicator--offset", "0px");
// let _ = style.set_property("--thaw-tab__indicator--scale", "1");
}
})
}
selected
});
view! {
<button
class=class_list![
"thaw-tab", ("thaw-tab--selected", move || selected.get()), class
]
role="tab"
aria-selected=move || if selected.get() { "true" } else { "false" }
ref=tab_ref
style="--thaw-tab__indicator--offset: 0px; --thaw-tab__indicator--scale: 1"
on:click=move |_| tab_list.on_select(value.get_value())
>
<span class="thaw-tab__content">
{children()}
</span>
</button>
}
}

View file

@ -57,6 +57,7 @@ pub struct CommonTheme {
pub spacing_horizontal_m_nudge: String, pub spacing_horizontal_m_nudge: String,
pub spacing_horizontal_m: String, pub spacing_horizontal_m: String,
pub spacing_horizontal_l: String, pub spacing_horizontal_l: String,
pub spacing_vertical_none: String,
pub spacing_vertical_s: String, pub spacing_vertical_s: String,
pub spacing_vertical_m_nudge: String, pub spacing_vertical_m_nudge: String,
pub spacing_vertical_m: String, pub spacing_vertical_m: String,
@ -137,6 +138,7 @@ impl CommonTheme {
spacing_horizontal_m_nudge: "10px".into(), spacing_horizontal_m_nudge: "10px".into(),
spacing_horizontal_m: "12px".into(), spacing_horizontal_m: "12px".into(),
spacing_horizontal_l: "16px".into(), spacing_horizontal_l: "16px".into(),
spacing_vertical_none: "0".into(),
spacing_vertical_s: "8px".into(), spacing_vertical_s: "8px".into(),
spacing_vertical_m_nudge: "10px".into(), spacing_vertical_m_nudge: "10px".into(),
spacing_vertical_m: "12px".into(), spacing_vertical_m: "12px".into(),