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,6 +6,85 @@ use thaw::*;
#[component]
pub fn App() -> impl IntoView {
let is_routing = create_rw_signal(false);
let set_is_routing = SignalSetter::map(move |is_routing_data| {
is_routing.set(is_routing_data);
});
provide_meta_context();
view! {
<Router base="/thaw" set_is_routing>
<TheProvider>
<TheRouter is_routing/>
</TheProvider>
</Router>
}
}
#[component]
fn TheRouter(is_routing: RwSignal<bool>) -> impl IntoView {
let loading_bar = use_loading_bar();
_ = is_routing.watch(move |is_routing| {
if *is_routing {
loading_bar.start();
} else {
loading_bar.finish();
}
});
view! {
<Routes base="/thaw".to_string()>
<Route path="/" view=Home/>
<Route path="/guide" view=GuidePage>
<Route path="/installation" view=InstallationPage/>
<Route path="/usage" view=UsagePage/>
</Route>
<Route path="/components" view=ComponentsPage>
<Route path="/menu" view=MenuPage/>
<Route path="/slider" view=SliderPage/>
<Route path="/tabbar" view=TabbarPage/>
<Route path="/nav-bar" view=NavBarPage/>
<Route path="/input" view=InputPage/>
<Route path="/image" view=ImagePage/>
<Route path="/modal" view=ModalPage/>
<Route path="/button" view=ButtonPage/>
<Route path="/checkbox" view=CheckboxPage/>
<Route path="/toast" view=ToastPage/>
<Route path="/tabs" view=TabsPage/>
<Route path="/select" view=SelectPage/>
<Route path="/space" view=SpacePage/>
<Route path="/table" view=TablePage/>
<Route path="/color-picker" view=ColorPickerPage/>
<Route path="/alert" view=AlertPage/>
<Route path="/grid" view=GridPage/>
<Route path="/auto-complete" view=AutoCompletePage/>
<Route path="/avatar" view=AvatarPage/>
<Route path="/badge" view=BadgePage/>
<Route path="/card" view=CardPage/>
<Route path="/divider" view=DividerPage/>
<Route path="/input-number" view=InputNumberPage/>
<Route path="/icon" view=IconPage/>
<Route path="/message" view=MessagePage/>
<Route path="/radio" view=RadioPage/>
<Route path="/skeleton" view=SkeletonPage/>
<Route path="/switch" view=SwitchPage/>
<Route path="/tag" view=TagPage/>
<Route path="/upload" view=UploadPage/>
<Route path="/loading-bar" view=LoadingBarPage/>
<Route path="/breadcrumb" view=BreadcrumbPage/>
<Route path="/layout" view=LayoutPage/>
<Route path="/progress" view=ProgressPage/>
<Route path="/theme" view=ThemePage/>
</Route>
<Route path="/mobile/tabbar" view=TabbarDemoPage/>
<Route path="/mobile/nav-bar" view=NavBarDemoPage/>
<Route path="/mobile/toast" view=ToastDemoPage/>
</Routes>
}
}
#[component]
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()?;
@ -22,86 +101,11 @@ pub fn App() -> impl IntoView {
});
let theme = create_rw_signal(theme);
provide_meta_context();
view! {
<Provider theme>
<TheRouter />
</Provider>
}
}
#[component]
fn TheRouter() -> impl IntoView {
let loading_bar = use_loading_bar();
let set_is_routing = SignalSetter::map(move |is_routing| {
if is_routing {
loading_bar.start();
} else {
loading_bar.finish();
}
});
view! {
<Router base="/thaw" set_is_routing>
<Routes base="/thaw".to_string()>
<Route path="/" view=Home/>
<Route path="/guide" view=GuidePage>
<Route path="/installation" view=InstallationPage/>
<Route path="/usage" view=UsagePage/>
</Route>
<Route path="/components" view=ComponentsPage>
<Route path="/menu" view=MenuPage/>
<Route path="/slider" view=SliderPage/>
<Route path="/tabbar" view=TabbarPage/>
<Route path="/nav-bar" view=NavBarPage/>
<Route path="/input" view=InputPage/>
<Route path="/image" view=ImagePage/>
<Route path="/modal" view=ModalPage/>
<Route path="/button" view=ButtonPage/>
<Route path="/checkbox" view=CheckboxPage/>
<Route path="/toast" view=ToastPage/>
<Route path="/tabs" view=TabsPage/>
<Route path="/select" view=SelectPage/>
<Route path="/space" view=SpacePage/>
<Route path="/table" view=TablePage/>
<Route path="/color-picker" view=ColorPickerPage/>
<Route path="/alert" view=AlertPage/>
<Route path="/grid" view=GridPage/>
<Route path="/auto-complete" view=AutoCompletePage/>
<Route path="/avatar" view=AvatarPage/>
<Route path="/badge" view=BadgePage/>
<Route path="/card" view=CardPage/>
<Route path="/divider" view=DividerPage/>
<Route path="/input-number" view=InputNumberPage/>
<Route path="/icon" view=IconPage/>
<Route path="/message" view=MessagePage/>
<Route path="/radio" view=RadioPage/>
<Route path="/skeleton" view=SkeletonPage/>
<Route path="/switch" view=SwitchPage/>
<Route path="/tag" view=TagPage/>
<Route path="/upload" view=UploadPage/>
<Route path="/loading-bar" view=LoadingBarPage/>
<Route path="/breadcrumb" view=BreadcrumbPage/>
<Route path="/layout" view=LayoutPage/>
<Route path="/progress" view=ProgressPage/>
<Route path="/theme" view=ThemePage/>
</Route>
<Route path="/mobile/tabbar" view=TabbarDemoPage/>
<Route path="/mobile/nav-bar" view=NavBarDemoPage/>
<Route path="/mobile/toast" view=ToastDemoPage/>
</Routes>
</Router>
}
}
#[component]
fn Provider(theme: RwSignal<Theme>, children: Children) -> impl IntoView {
view! {
<ThemeProvider theme>
<GlobalStyle />
<GlobalStyle/>
<MessageProvider>
<LoadingBarProvider>
{children()}
</LoadingBarProvider>
<LoadingBarProvider>{children()}</LoadingBarProvider>
</MessageProvider>
</ThemeProvider>
}

View file

@ -5,6 +5,7 @@ use thaw::*;
#[component]
pub fn ThemePage() -> impl IntoView {
let theme = create_rw_signal(Theme::light());
let customize_theme = create_rw_signal(Theme::light());
let on_customize_theme = move |_| {
customize_theme.update(|theme| {
@ -16,7 +17,40 @@ pub fn ThemePage() -> impl IntoView {
view! {
<div style="width: 896px; margin: 0 auto;">
<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>
<p>"You can use GlobalStyle to sync common global style to the body element."</p>
<Demo>
@ -106,36 +140,3 @@ pub fn ThemePage() -> impl IntoView {
</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
/>
</div>
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart width=FollowerWidth::Target>
<div
class="thaw-auto-complete__menu"
style=move || menu_css_vars.get()
>
<Follower
slot
show=is_show_menu
placement=FollowerPlacement::BottomStart
width=FollowerWidth::Target
>
<div class="thaw-auto-complete__menu" style=move || menu_css_vars.get()>
{move || {
options

View file

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

View file

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

View file

@ -204,9 +204,7 @@ fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
.attr("style", move || content_style.get())
.child(children()),
);
view! {
<Teleport element=children />
}
view! { <Teleport element=children/> }
}
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",
move || animation_timeout_handle.with(|handle| handle.is_some()),
)
ref=wave_ref
></div>
}

View file

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

View file

@ -97,12 +97,17 @@ pub fn Input(
css_vars
});
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 {
view! { <div class="thaw-input__prefix">{(prefix.children)()}</div> }.into()
} else {
None
}}
<input
type=move || variant.get().as_str()
prop:value=move || {

View file

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

View file

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

View file

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

View file

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

View file

@ -89,27 +89,29 @@ pub fn Progress(
};
view! {
<div class="thaw-progress" style=move || css_vars.get() >
<div class="thaw-progress" style=move || css_vars.get()>
<div class=class>
<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">
{
move || {
format!("{}%", percentage.get())
}
}
{move || { format!("{}%", percentage.get()) }}
</div>
</Show>
</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">
{
move || {
format!("{}%", percentage.get())
}
}
{move || { format!("{}%", percentage.get()) }}
</div>
</Show>
</div>

View file

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

View file

@ -1,6 +1,10 @@
mod tab;
use crate::{theme::use_theme, utils::mount_style, Theme};
use crate::{
theme::use_theme,
utils::{mount_style, Provider},
Theme,
};
use leptos::*;
pub use tab::*;
@ -9,10 +13,6 @@ pub use tab::*;
pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView {
mount_style("tabs", include_str!("./tabs.css"));
let tab_options_vec = create_rw_signal(vec![]);
provide_context(TabsInjection {
active_key: value,
tab_options_vec,
});
let theme = use_theme(Theme::light);
let css_vars = create_memo(move |_| {
let mut css_vars = String::new();
@ -36,66 +36,71 @@ pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children)
let label_list_ref = create_node_ref::<html::Div>();
view! {
<div class="thaw-tabs" style=move || css_vars.get()>
<div class="thaw-tabs__label-list" ref=label_list_ref>
<For
each=move || tab_options_vec.get()
key=move |v| v.key.clone()
children=move |option| {
let label_ref = create_node_ref::<html::Span>();
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(),
}),
);
});
<Provider value=TabsInjection {
active_key: value,
tab_options_vec,
}>
<div class="thaw-tabs" style=move || css_vars.get()>
<div class="thaw-tabs__label-list" ref=label_list_ref>
<For
each=move || tab_options_vec.get()
key=move |v| v.key.clone()
children=move |option| {
let label_ref = create_node_ref::<html::Span>();
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(),
}),
);
});
}
}
}
});
view! {
<span
class="thaw-tabs__label"
class=(
"thaw-tabs__label--active",
{
});
view! {
<span
class="thaw-tabs__label"
class=(
"thaw-tabs__label--active",
{
let key = key.clone();
move || key == value.get()
},
)
on:click={
let key = key.clone();
move || key == value.get()
},
)
move |_| value.set(key.clone())
}
on:click={
let key = key.clone();
move |_| value.set(key.clone())
}
ref=label_ref
>
{label}
</span>
ref=label_ref
>
{label}
</span>
}
}
}
/>
/>
<span class="thaw-tabs-label__line" style=move || label_line_style.get()></span>
<span class="thaw-tabs-label__line" style=move || label_line_style.get()></span>
</div>
<div>{children()}</div>
</div>
<div>{children()}</div>
</div>
</Provider>
}
}

View file

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

View file

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