mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-02 08:34:15 -05:00
✨ feat: add tabbar component
This commit is contained in:
parent
d61fa318c5
commit
90c9856469
12 changed files with 176 additions and 2 deletions
|
@ -1,4 +1,3 @@
|
|||
use crate::components::*;
|
||||
use crate::pages::*;
|
||||
use leptos::*;
|
||||
use leptos_router::*;
|
||||
|
@ -26,8 +25,16 @@ pub fn App(cx: Scope) -> impl IntoView {
|
|||
<Route path="/slider" view=move |cx| view! {cx,
|
||||
<SliderPage />
|
||||
} />
|
||||
<Route path="/tabbar" view=move |cx| view! {cx,
|
||||
<MobilePage path="/mobile/tabbar" />
|
||||
} />
|
||||
</Route>
|
||||
</Routes>
|
||||
<Routes base="/mobile".to_string()>
|
||||
<Route path="/tabbar" view=move |cx| view! {cx,
|
||||
<TabbarPage />
|
||||
} />
|
||||
</Routes>
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ mod demo_checkbox;
|
|||
mod demo_modal;
|
||||
mod demo_slider;
|
||||
mod pages;
|
||||
mod components;
|
||||
|
||||
use app::*;
|
||||
use leptos::*;
|
||||
|
|
|
@ -35,6 +35,7 @@ pub fn ComponentsPage(cx: Scope) -> impl IntoView {
|
|||
<Menu selected>
|
||||
<MenuItem key="menu" label="menu" />
|
||||
<MenuItem key="slider" label="slider" />
|
||||
<MenuItem key="tabbar" label="tabbar" />
|
||||
</Menu>
|
||||
</aside>
|
||||
<main>
|
10
examples/basic/src/pages/mobile.rs
Normal file
10
examples/basic/src/pages/mobile.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
pub fn MobilePage(cx: Scope, path: &'static str) -> impl IntoView {
|
||||
view! { cx,
|
||||
<div style="height: 100vh; display: flex; align-items: center; justify-content: center; background: #eff2f5">
|
||||
<iframe src=path style="width: 360px; height: 640px; background-color: #fff; border: none; border-radius: 16px; border: 1px solid #e1e1e1"/>
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
mod components;
|
||||
mod home;
|
||||
mod menu;
|
||||
mod mobile;
|
||||
mod slider;
|
||||
mod tabbar;
|
||||
|
||||
pub use components::*;
|
||||
pub use home::*;
|
||||
pub use menu::*;
|
||||
pub use mobile::*;
|
||||
pub use slider::*;
|
||||
pub use tabbar::*;
|
||||
|
|
23
examples/basic/src/pages/tabbar/mod.rs
Normal file
23
examples/basic/src/pages/tabbar/mod.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use leptos::*;
|
||||
use melt_ui::mobile::*;
|
||||
|
||||
#[component]
|
||||
pub fn TabbarPage(cx: Scope) -> impl IntoView {
|
||||
let selected = create_rw_signal(cx, String::from("o"));
|
||||
view! { cx,
|
||||
<div style="height: 100vh; background: #f5f5f5">
|
||||
{ move || selected.get() }
|
||||
<Tabbar selected>
|
||||
<TabbarItem name="a">
|
||||
"and"
|
||||
</TabbarItem>
|
||||
<TabbarItem name="i">
|
||||
"if"
|
||||
</TabbarItem>
|
||||
<TabbarItem name="o" icon=leptos_icons::AiIcon::AiCloseOutlined>
|
||||
"or"
|
||||
</TabbarItem>
|
||||
</Tabbar>
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ mod checkbox;
|
|||
mod components;
|
||||
mod input;
|
||||
mod menu;
|
||||
pub mod mobile;
|
||||
mod modal;
|
||||
mod progress;
|
||||
mod slider;
|
||||
|
|
3
src/mobile/mod.rs
Normal file
3
src/mobile/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod tabbar;
|
||||
|
||||
pub use tabbar::*;
|
55
src/mobile/tabbar/mod.rs
Normal file
55
src/mobile/tabbar/mod.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
mod tabbar_item;
|
||||
|
||||
use crate::utils::mount_style::mount_style;
|
||||
use leptos::*;
|
||||
use stylers::style_sheet_str;
|
||||
pub use tabbar_item::*;
|
||||
|
||||
#[component]
|
||||
pub fn Tabbar(
|
||||
cx: Scope,
|
||||
#[prop(into)] selected: RwSignal<String>,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
let class_name = mount_style("tabbar", || {
|
||||
style_sheet_str!("./src/mobile/tabbar/tabbar.css")
|
||||
});
|
||||
|
||||
let tabbar_injection_key = create_rw_signal(cx, TabbarInjectionKey::new(selected.get()));
|
||||
create_effect(cx, move |_| {
|
||||
let selected_key = selected.get();
|
||||
let key = tabbar_injection_key.get_untracked();
|
||||
if selected_key != key.value {
|
||||
tabbar_injection_key.set(TabbarInjectionKey::new(selected_key));
|
||||
}
|
||||
});
|
||||
|
||||
create_effect(cx, move |_| {
|
||||
let selected_key = selected.get_untracked();
|
||||
let key = tabbar_injection_key.get();
|
||||
if selected_key != key.value {
|
||||
selected.set(key.value);
|
||||
}
|
||||
});
|
||||
provide_context(cx, tabbar_injection_key);
|
||||
view! {cx, class=class_name,
|
||||
<div class="melt-tabbar">
|
||||
{ children(cx) }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TabbarInjectionKey {
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl TabbarInjectionKey {
|
||||
pub fn new(value: String) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_tabbar(cx: Scope) -> RwSignal<TabbarInjectionKey> {
|
||||
use_context::<RwSignal<TabbarInjectionKey>>(cx).expect("TabbarInjectionKey not exist")
|
||||
}
|
17
src/mobile/tabbar/tabbar-item.css
Normal file
17
src/mobile/tabbar/tabbar-item.css
Normal file
|
@ -0,0 +1,17 @@
|
|||
.melt-tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.melt-tabbar-item--selected {
|
||||
color: var(--font-color-selected)
|
||||
}
|
||||
|
||||
.melt-tabbar-item__content {
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
}
|
10
src/mobile/tabbar/tabbar.css
Normal file
10
src/mobile/tabbar/tabbar.css
Normal file
|
@ -0,0 +1,10 @@
|
|||
.melt-tabbar {
|
||||
background-color: #fff;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
42
src/mobile/tabbar/tabbar_item.rs
Normal file
42
src/mobile/tabbar/tabbar_item.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use super::{use_tabbar, TabbarInjectionKey};
|
||||
use crate::{theme::use_theme, utils::mount_style::mount_style, Theme};
|
||||
use leptos::*;
|
||||
use stylers::style_sheet_str;
|
||||
use crate::components::*;
|
||||
use leptos_icons::*;
|
||||
|
||||
#[component]
|
||||
pub fn TabbarItem(
|
||||
cx: Scope,
|
||||
#[prop(into)] name: MaybeSignal<&'static str>,
|
||||
#[prop(optional, into)] icon: Option<IconData>,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
let class_name = mount_style("tabbar-item", || style_sheet_str!("./src/mobile/tabbar/tabbar-item.css"));
|
||||
let theme = use_theme(cx, Theme::light);
|
||||
let tabbar = use_tabbar(cx);
|
||||
let onclick_select = move |_| {
|
||||
tabbar.set(TabbarInjectionKey::new(name.get().to_string()));
|
||||
};
|
||||
|
||||
let css_vars = create_memo(cx, move |_| {
|
||||
let mut css_vars = String::new();
|
||||
let theme = theme.get();
|
||||
let font_color = theme.common.color_primary.clone();
|
||||
css_vars.push_str(&format!("--font-color-selected: {font_color};"));
|
||||
css_vars
|
||||
});
|
||||
|
||||
view! {cx, class=class_name,
|
||||
<div class="melt-tabbar-item" class=("melt-tabbar-item--selected", move || tabbar.get().value == name.get()) on:click=onclick_select style=move || css_vars.get()>
|
||||
<OptionComp value=icon view=move |cx, icon| {
|
||||
view!{cx,
|
||||
<Icon icon=icon width="22px" height="22px" class="melt-tabbar-item__icon"/>
|
||||
}
|
||||
}/>
|
||||
<div class="melt-tabbar-item__content">
|
||||
{ children(cx) }
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue