Merge pull request #23 from thaw-ui/docs/theme

fix: provide context
This commit is contained in:
luoxiaozero 2023-11-19 14:11:47 +08:00 committed by GitHub
commit 787e25cc71
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 311 additions and 243 deletions

View file

@ -6,42 +6,33 @@ use thaw::*;
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
fn use_query_value(key: &str) -> Option<String> { let is_routing = create_rw_signal(false);
let href = window().location().href().ok()?; let set_is_routing = SignalSetter::map(move |is_routing_data| {
let url = Url::try_from(href.as_str()).ok()?; is_routing.set(is_routing_data);
url.search_params.get(key).cloned()
}
let theme = use_query_value("theme").map_or_else(Theme::light, |name| {
if name == "light" {
Theme::light()
} else if name == "dark" {
Theme::dark()
} else {
Theme::light()
}
}); });
let theme = create_rw_signal(theme);
provide_meta_context(); provide_meta_context();
view! { view! {
<Provider theme> <Router base="/thaw" set_is_routing>
<TheRouter /> <TheProvider>
</Provider> <TheRouter is_routing/>
</TheProvider>
</Router>
} }
} }
#[component] #[component]
fn TheRouter() -> impl IntoView { fn TheRouter(is_routing: RwSignal<bool>) -> impl IntoView {
let loading_bar = use_loading_bar(); let loading_bar = use_loading_bar();
let set_is_routing = SignalSetter::map(move |is_routing| { _ = is_routing.watch(move |is_routing| {
if is_routing { if *is_routing {
loading_bar.start(); loading_bar.start();
} else { } else {
loading_bar.finish(); loading_bar.finish();
} }
}); });
view! { view! {
<Router base="/thaw" set_is_routing>
<Routes base="/thaw".to_string()> <Routes base="/thaw".to_string()>
<Route path="/" view=Home/> <Route path="/" view=Home/>
<Route path="/guide" view=GuidePage> <Route path="/guide" view=GuidePage>
@ -89,19 +80,32 @@ fn TheRouter() -> impl IntoView {
<Route path="/mobile/nav-bar" view=NavBarDemoPage/> <Route path="/mobile/nav-bar" view=NavBarDemoPage/>
<Route path="/mobile/toast" view=ToastDemoPage/> <Route path="/mobile/toast" view=ToastDemoPage/>
</Routes> </Routes>
</Router>
} }
} }
#[component] #[component]
fn Provider(theme: RwSignal<Theme>, children: Children) -> impl IntoView { fn TheProvider(children: Children) -> impl IntoView {
fn use_query_value(key: &str) -> Option<String> {
let href = window().location().href().ok()?;
let url = Url::try_from(href.as_str()).ok()?;
url.search_params.get(key).cloned()
}
let theme = use_query_value("theme").map_or_else(Theme::light, |name| {
if name == "light" {
Theme::light()
} else if name == "dark" {
Theme::dark()
} else {
Theme::light()
}
});
let theme = create_rw_signal(theme);
view! { view! {
<ThemeProvider theme> <ThemeProvider theme>
<GlobalStyle /> <GlobalStyle/>
<MessageProvider> <MessageProvider>
<LoadingBarProvider> <LoadingBarProvider>{children()}</LoadingBarProvider>
{children()}
</LoadingBarProvider>
</MessageProvider> </MessageProvider>
</ThemeProvider> </ThemeProvider>
} }

View file

@ -5,6 +5,7 @@ use thaw::*;
#[component] #[component]
pub fn ThemePage() -> impl IntoView { pub fn ThemePage() -> impl IntoView {
let theme = create_rw_signal(Theme::light());
let customize_theme = create_rw_signal(Theme::light()); let customize_theme = create_rw_signal(Theme::light());
let on_customize_theme = move |_| { let on_customize_theme = move |_| {
customize_theme.update(|theme| { customize_theme.update(|theme| {
@ -16,7 +17,40 @@ pub fn ThemePage() -> impl IntoView {
view! { view! {
<div style="width: 896px; margin: 0 auto;"> <div style="width: 896px; margin: 0 auto;">
<h1>"Theme"</h1> <h1>"Theme"</h1>
<ThemeProviderPage/> <h3>"ThemeProvider"</h3>
<Demo>
<ThemeProvider theme>
<Card>
<Space>
<Button on_click=move |_| theme.set(Theme::light())>"Light"</Button>
<Button on_click=move |_| theme.set(Theme::dark())>"Dark"</Button>
</Space>
</Card>
</ThemeProvider>
<DemoCode
slot
html=highlight_str!(
r#"
let theme = create_rw_signal(Theme::light());
view! {
<ThemeProvider theme>
<Card>
<Space>
<Button on_click=move |_| theme.set(Theme::light())>"Light"</Button>
<Button on_click=move |_| theme.set(Theme::dark())>"Dark"</Button>
</Space>
</Card>
</ThemeProvider>
}
"#,
"rust"
)
>
""
</DemoCode>
</Demo>
<h3>"GlobalStyle"</h3> <h3>"GlobalStyle"</h3>
<p>"You can use GlobalStyle to sync common global style to the body element."</p> <p>"You can use GlobalStyle to sync common global style to the body element."</p>
<Demo> <Demo>
@ -106,36 +140,3 @@ pub fn ThemePage() -> impl IntoView {
</div> </div>
} }
} }
#[component]
fn ThemeProviderPage() -> impl IntoView {
view! {
<h3>"ThemeProvider"</h3>
<Demo>
""
<DemoCode
slot
html=highlight_str!(
r#"
let theme = create_rw_signal(Theme::light());
view! {
<ThemeProvider theme>
<Card>
<Space>
<Button on_click=move |_| theme.set(Theme::light())>"Light"</Button>
<Button on_click=move |_| theme.set(Theme::dark())>"Dark"</Button>
</Space>
</Card>
</ThemeProvider>
}
"#,
"rust"
)
>
""
</DemoCode>
</Demo>
}
}

View file

@ -61,11 +61,13 @@ pub fn AutoComplete(
allow_value allow_value
/> />
</div> </div>
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart width=FollowerWidth::Target> <Follower
<div slot
class="thaw-auto-complete__menu" show=is_show_menu
style=move || menu_css_vars.get() placement=FollowerPlacement::BottomStart
width=FollowerWidth::Target
> >
<div class="thaw-auto-complete__menu" style=move || menu_css_vars.get()>
{move || { {move || {
options options

View file

@ -1,7 +1,11 @@
mod breadcrumb_item; mod breadcrumb_item;
mod theme; mod theme;
use crate::{use_theme, utils::mount_style, Theme}; use crate::{
use_theme,
utils::{mount_style, Provider},
Theme,
};
pub use breadcrumb_item::BreadcrumbItem; pub use breadcrumb_item::BreadcrumbItem;
use leptos::*; use leptos::*;
pub use theme::BreadcrumbTheme; pub use theme::BreadcrumbTheme;
@ -31,11 +35,12 @@ pub fn Breadcrumb(
}); });
css_vars css_vars
}); });
provide_context(BreadcrumbSeparatorInjection(separator));
view! { view! {
<Provider value=BreadcrumbSeparatorInjection(separator)>
<nav class="thaw-breadcrumb" style=move || css_vars.get()> <nav class="thaw-breadcrumb" style=move || css_vars.get()>
<ul>{children()}</ul> <ul>{children()}</ul>
</nav> </nav>
</Provider>
} }
} }

View file

@ -1,3 +1,4 @@
use crate::utils::Provider;
use leptos::*; use leptos::*;
use std::collections::HashSet; use std::collections::HashSet;
@ -6,9 +7,7 @@ pub fn CheckboxGroup(
#[prop(optional, into)] value: RwSignal<HashSet<String>>, #[prop(optional, into)] value: RwSignal<HashSet<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
provide_context(CheckboxGroupInjection(value)); view! { <Provider value=CheckboxGroupInjection(value) children/> }
children()
} }
#[derive(Clone)] #[derive(Clone)]

View file

@ -204,9 +204,7 @@ fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
.attr("style", move || content_style.get()) .attr("style", move || content_style.get())
.child(children()), .child(children()),
); );
view! { view! { <Teleport element=children/> }
<Teleport element=children />
}
} }
fn get_scroll_parent(element: Option<HtmlElement<AnyElement>>) -> Option<HtmlElement<AnyElement>> { fn get_scroll_parent(element: Option<HtmlElement<AnyElement>>) -> Option<HtmlElement<AnyElement>> {

View file

@ -50,6 +50,7 @@ pub fn Wave(#[prop(optional)] comp_ref: ComponentRef<WaveRef>) -> impl IntoView
"thaw-wave--active", "thaw-wave--active",
move || animation_timeout_handle.with(|handle| handle.is_some()), move || animation_timeout_handle.with(|handle| handle.is_some()),
) )
ref=wave_ref ref=wave_ref
></div> ></div>
} }

View file

@ -1,5 +1,6 @@
mod grid_item; mod grid_item;
use crate::utils::Provider;
pub use grid_item::*; pub use grid_item::*;
use leptos::*; use leptos::*;
@ -10,9 +11,6 @@ pub fn Grid(
#[prop(optional, into)] y_gap: MaybeSignal<u16>, #[prop(optional, into)] y_gap: MaybeSignal<u16>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let grid_injection_key = GridInjection::new(x_gap);
provide_context(grid_injection_key);
let style = create_memo(move |_| { let style = create_memo(move |_| {
let mut style = String::from("display: grid;"); let mut style = String::from("display: grid;");
style.push_str(&format!( style.push_str(&format!(
@ -24,9 +22,11 @@ pub fn Grid(
}); });
view! { view! {
<Provider value=GridInjection::new(x_gap)>
<div class="thaw-grid" style=move || style.get()> <div class="thaw-grid" style=move || style.get()>
{children()} {children()}
</div> </div>
</Provider>
} }
} }

View file

@ -97,12 +97,17 @@ pub fn Input(
css_vars css_vars
}); });
view! { view! {
<div class="thaw-input" class=("thaw-input--focus", move || is_focus.get()) style=move || css_vars.get()> <div
class="thaw-input"
class=("thaw-input--focus", move || is_focus.get())
style=move || css_vars.get()
>
{if let Some(prefix) = input_prefix { {if let Some(prefix) = input_prefix {
view! { <div class="thaw-input__prefix">{(prefix.children)()}</div> }.into() view! { <div class="thaw-input__prefix">{(prefix.children)()}</div> }.into()
} else { } else {
None None
}} }}
<input <input
type=move || variant.get().as_str() type=move || variant.get().as_str()
prop:value=move || { prop:value=move || {

View file

@ -1,17 +1,22 @@
use super::{LoadingBar, LoadingBarRef}; use super::{LoadingBar, LoadingBarRef};
use crate::{components::Teleport, utils::ComponentRef}; use crate::{
components::Teleport,
utils::{ComponentRef, Provider},
};
use leptos::*; use leptos::*;
#[component] #[component]
pub fn LoadingBarProvider(children: Children) -> impl IntoView { pub fn LoadingBarProvider(children: Children) -> impl IntoView {
let loading_bar_ref = ComponentRef::<LoadingBarRef>::default(); let loading_bar_ref = ComponentRef::<LoadingBarRef>::default();
provide_context(LoadingBarInjection { loading_bar_ref });
view! { view! {
{children()} <Provider value=LoadingBarInjection {
<Teleport> loading_bar_ref,
}>
{children()} <Teleport>
<LoadingBar comp_ref=loading_bar_ref/> <LoadingBar comp_ref=loading_bar_ref/>
</Teleport> </Teleport>
</Provider>
} }
} }

View file

@ -2,6 +2,7 @@ mod menu_group;
mod menu_item; mod menu_item;
mod theme; mod theme;
use crate::utils::Provider;
use leptos::*; use leptos::*;
pub use menu_group::MenuGroup; pub use menu_group::MenuGroup;
pub use menu_item::*; pub use menu_item::*;
@ -9,8 +10,11 @@ pub use theme::MenuTheme;
#[component] #[component]
pub fn Menu(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView { pub fn Menu(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView {
provide_context(MenuInjection(value)); view! {
view! { <div class="thaw-menu">{children()}</div> } <Provider value=MenuInjection(value)>
<div class="thaw-menu">{children()}</div>
</Provider>
}
} }
#[derive(Clone)] #[derive(Clone)]

View file

@ -1,7 +1,10 @@
use std::time::Duration; use std::time::Duration;
use super::{message_environment::MessageEnvironment, MessageVariant}; use super::{message_environment::MessageEnvironment, MessageVariant};
use crate::{components::Teleport, utils::mount_style}; use crate::{
components::Teleport,
utils::{mount_style, Provider},
};
use leptos::*; use leptos::*;
use uuid::Uuid; use uuid::Uuid;
@ -10,7 +13,6 @@ pub fn MessageProvider(children: Children) -> impl IntoView {
mount_style("message", include_str!("./message.css")); mount_style("message", include_str!("./message.css"));
let message_list = create_rw_signal::<Vec<MessageType>>(vec![]); let message_list = create_rw_signal::<Vec<MessageType>>(vec![]);
provide_context(MessageInjection::new(message_list));
let handle_after_leave = move |id| { let handle_after_leave = move |id| {
message_list.update(move |message_list| { message_list.update(move |message_list| {
@ -22,6 +24,9 @@ pub fn MessageProvider(children: Children) -> impl IntoView {
}; };
view! { view! {
<Provider value=MessageInjection::new(
message_list,
)>
{children()} {children()}
<Teleport> <Teleport>
<div class="thaw-message-container"> <div class="thaw-message-container">
@ -30,13 +35,17 @@ pub fn MessageProvider(children: Children) -> impl IntoView {
key=|message| message.0 key=|message| message.0
children=move |message| { children=move |message| {
view! { view! {
<MessageEnvironment message on_internal_after_leave=handle_after_leave/> <MessageEnvironment
message
on_internal_after_leave=handle_after_leave
/>
} }
} }
/> />
</div> </div>
</Teleport> </Teleport>
</Provider>
} }
} }

View file

@ -1,7 +1,11 @@
mod tabbar_item; mod tabbar_item;
mod theme; mod theme;
use crate::{use_theme, utils::mount_style, Theme}; use crate::{
use_theme,
utils::{mount_style, Provider},
Theme,
};
use leptos::*; use leptos::*;
pub use tabbar_item::*; pub use tabbar_item::*;
pub use theme::TabbarTheme; pub use theme::TabbarTheme;
@ -21,11 +25,13 @@ pub fn Tabbar(
) )
}) })
}); });
provide_context(TabbarInjection(value));
view! { view! {
<Provider value=TabbarInjection(value)>
<div class="thaw-tabbar" style=move || css_vars.get()> <div class="thaw-tabbar" style=move || css_vars.get()>
{children()} {children()}
</div> </div>
</Provider>
} }
} }

View file

@ -89,27 +89,29 @@ pub fn Progress(
}; };
view! { view! {
<div class="thaw-progress" style=move || css_vars.get() > <div class="thaw-progress" style=move || css_vars.get()>
<div class=class> <div class=class>
<div class="thaw-progress__progress-inner" style=style> <div class="thaw-progress__progress-inner" style=style>
<Show when=move || show_indicator.get() && indicator_placement.get() == ProgressIndicatorPlacement::Inside> <Show when=move || {
show_indicator.get()
&& indicator_placement.get() == ProgressIndicatorPlacement::Inside
}>
<div class="thaw-progress__indicator--inside"> <div class="thaw-progress__indicator--inside">
{
move || { {move || { format!("{}%", percentage.get()) }}
format!("{}%", percentage.get())
}
}
</div> </div>
</Show> </Show>
</div> </div>
</div> </div>
<Show when=move || show_indicator.get() && indicator_placement.get() == ProgressIndicatorPlacement::Outside> <Show when=move || {
show_indicator.get()
&& indicator_placement.get() == ProgressIndicatorPlacement::Outside
}>
<div class="thaw-progress__indicator--outside"> <div class="thaw-progress__indicator--outside">
{
move || { {move || { format!("{}%", percentage.get()) }}
format!("{}%", percentage.get())
}
}
</div> </div>
</Show> </Show>
</div> </div>

View file

@ -104,17 +104,23 @@ where
}); });
view! { view! {
<Binder target_ref=trigger_ref> <Binder target_ref=trigger_ref>
<div class="thaw-select" ref=trigger_ref on:click=show_menu style=move || css_vars.get()> <div
class="thaw-select"
ref=trigger_ref
on:click=show_menu
style=move || css_vars.get()
>
{move || select_option_label.get()} {move || select_option_label.get()}
</div> </div>
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart width=FollowerWidth::Target> <Follower
<div slot
class="thaw-select-menu" show=is_show_menu
style=move || menu_css_vars.get() placement=FollowerPlacement::BottomStart
ref=menu_ref width=FollowerWidth::Target
> >
<div class="thaw-select-menu" style=move || menu_css_vars.get() ref=menu_ref>
<For <For
each=move || options.get() each=move || options.get()
key=move |item| item.value.clone() key=move |item| item.value.clone()

View file

@ -1,6 +1,10 @@
mod tab; mod tab;
use crate::{theme::use_theme, utils::mount_style, Theme}; use crate::{
theme::use_theme,
utils::{mount_style, Provider},
Theme,
};
use leptos::*; use leptos::*;
pub use tab::*; pub use tab::*;
@ -9,10 +13,6 @@ pub use tab::*;
pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView { pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView {
mount_style("tabs", include_str!("./tabs.css")); mount_style("tabs", include_str!("./tabs.css"));
let tab_options_vec = create_rw_signal(vec![]); let tab_options_vec = create_rw_signal(vec![]);
provide_context(TabsInjection {
active_key: value,
tab_options_vec,
});
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
let css_vars = create_memo(move |_| { let css_vars = create_memo(move |_| {
let mut css_vars = String::new(); let mut css_vars = String::new();
@ -36,6 +36,10 @@ pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children)
let label_list_ref = create_node_ref::<html::Div>(); let label_list_ref = create_node_ref::<html::Div>();
view! { view! {
<Provider value=TabsInjection {
active_key: value,
tab_options_vec,
}>
<div class="thaw-tabs" style=move || css_vars.get()> <div class="thaw-tabs" style=move || css_vars.get()>
<div class="thaw-tabs__label-list" ref=label_list_ref> <div class="thaw-tabs__label-list" ref=label_list_ref>
<For <For
@ -96,6 +100,7 @@ pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children)
</div> </div>
<div>{children()}</div> <div>{children()}</div>
</div> </div>
</Provider>
} }
} }

View file

@ -3,6 +3,7 @@ mod common;
use self::common::CommonTheme; use self::common::CommonTheme;
use crate::{ use crate::{
mobile::{NavBarTheme, TabbarTheme}, mobile::{NavBarTheme, TabbarTheme},
utils::Provider,
AlertTheme, AutoCompleteTheme, AvatarTheme, BreadcrumbTheme, ButtonTheme, ColorPickerTheme, AlertTheme, AutoCompleteTheme, AvatarTheme, BreadcrumbTheme, ButtonTheme, ColorPickerTheme,
InputTheme, MenuTheme, MessageTheme, ProgressTheme, SelectTheme, SkeletionTheme, SliderTheme, InputTheme, MenuTheme, MessageTheme, ProgressTheme, SelectTheme, SkeletionTheme, SliderTheme,
SwitchTheme, TableTheme, TagTheme, UploadTheme, SwitchTheme, TableTheme, TagTheme, UploadTheme,
@ -111,8 +112,8 @@ pub fn ThemeProvider(
} else { } else {
create_rw_signal(Theme::light()) create_rw_signal(Theme::light())
}; };
provide_context(theme);
children() view! { <Provider value=theme children/> }
} }
pub fn use_theme(default: impl Fn() -> Theme) -> ReadSignal<Theme> { pub fn use_theme(default: impl Fn() -> Theme) -> ReadSignal<Theme> {

View file

@ -2,6 +2,7 @@
mod component_ref; mod component_ref;
mod event_listener; mod event_listener;
mod mount_style; mod mount_style;
mod provider;
mod signal; mod signal;
mod stored_maybe_signal; mod stored_maybe_signal;
@ -9,5 +10,6 @@ mod stored_maybe_signal;
pub(crate) use component_ref::ComponentRef; pub(crate) use component_ref::ComponentRef;
pub(crate) use event_listener::*; pub(crate) use event_listener::*;
pub(crate) use mount_style::mount_style; pub(crate) use mount_style::mount_style;
pub(crate) use provider::Provider;
pub use signal::SignalWatch; pub use signal::SignalWatch;
pub(crate) use stored_maybe_signal::*; pub(crate) use stored_maybe_signal::*;

13
src/utils/provider.rs Normal file
View file

@ -0,0 +1,13 @@
/// https://github.com/leptos-rs/leptos/issues/2038
use leptos::*;
#[component]
pub fn Provider<T>(value: T, children: Children) -> impl IntoView
where
T: Clone + 'static,
{
run_as_child(move || {
provide_context(value);
children()
})
}