feat: add loading bar component

This commit is contained in:
luoxiao 2023-11-06 23:35:30 +08:00
parent 29068acd1a
commit a05cda35d7
5 changed files with 208 additions and 40 deletions

View file

@ -24,50 +24,57 @@ pub fn App() -> impl IntoView {
provide_context(theme); provide_context(theme);
view! { view! {
<Provider theme> <Provider theme>
<Router base="/thaw"> <TheRouter />
<Routes base="/thaw".to_string()>
<Route path="/" view=Home/>
<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>
<Route path="/mobile/tabbar" view=TabbarDemoPage/>
<Route path="/mobile/nav-bar" view=NavBarDemoPage/>
<Route path="/mobile/toast" view=ToastDemoPage/>
</Routes>
</Router>
</Provider> </Provider>
} }
} }
#[component]
fn TheRouter() -> impl IntoView {
view! {
<Router base="/thaw">
<Routes base="/thaw".to_string()>
<Route path="/" view=Home/>
<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>
<Route path="/mobile/tabbar" view=TabbarDemoPage/>
<Route path="/mobile/nav-bar" view=NavBarDemoPage/>
<Route path="/mobile/toast" view=ToastDemoPage/>
</Routes>
</Router>
}
}
#[component] #[component]
fn Provider(theme: RwSignal<Theme>, children: Children) -> impl IntoView { fn Provider(theme: RwSignal<Theme>, children: Children) -> impl IntoView {
view! { view! {

View file

@ -16,6 +16,7 @@ mod image;
mod input; mod input;
mod input_number; mod input_number;
mod layout; mod layout;
mod loading_bar;
mod menu; mod menu;
mod message; mod message;
pub mod mobile; pub mod mobile;
@ -53,6 +54,7 @@ pub use image::*;
pub use input::*; pub use input::*;
pub use input_number::*; pub use input_number::*;
pub use layout::*; pub use layout::*;
pub use loading_bar::*;
pub use menu::*; pub use menu::*;
pub use message::*; pub use message::*;
pub use modal::*; pub use modal::*;

View file

@ -0,0 +1,11 @@
.thaw-loading-bar-container {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.thaw-loading-bar {
height: 2px;
max-width: 0;
}

View file

@ -0,0 +1,46 @@
use super::{LoadingBar, LoadingBarRef};
use crate::{teleport::Teleport, utils::ComponentRef};
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>
}
}
#[derive(Clone)]
pub struct LoadingBarInjection {
loading_bar_ref: ComponentRef<LoadingBarRef>,
}
impl Copy for LoadingBarInjection {}
impl LoadingBarInjection {
pub fn start(&self) {
if let Some(loading_bar_ref) = self.loading_bar_ref.get_untracked() {
loading_bar_ref.start();
}
}
pub fn finish(&self) {
if let Some(loading_bar_ref) = self.loading_bar_ref.get_untracked() {
loading_bar_ref.finish();
}
}
pub fn error(&self) {
if let Some(loading_bar_ref) = self.loading_bar_ref.get_untracked() {
loading_bar_ref.error();
}
}
}
pub fn use_loading_bar() -> LoadingBarInjection {
expect_context::<LoadingBarInjection>()
}

102
src/loading_bar/mod.rs Normal file
View file

@ -0,0 +1,102 @@
mod loading_bar_provider;
use crate::{mount_style, use_theme, utils::ComponentRef, Theme};
use leptos::*;
pub use loading_bar_provider::{use_loading_bar, LoadingBarProvider};
#[derive(Clone)]
pub struct LoadingBarRef {
start: Callback<()>,
finish: Callback<()>,
error: Callback<()>,
}
impl LoadingBarRef {
pub fn start(&self) {
self.start.call(());
}
pub fn finish(&self) {
self.finish.call(());
}
pub fn error(&self) {
self.error.call(());
}
}
#[component]
pub fn LoadingBar(#[prop(optional)] comp_ref: ComponentRef<LoadingBarRef>) -> impl IntoView {
mount_style("loading-bar", include_str!("./loading-bar.css"));
let theme = use_theme(Theme::light);
let css_vars = create_memo(move |_| {
let mut css_vars = String::new();
theme.with(|theme| {
css_vars.push_str(&format!(
"--thaw-background-color: {};",
theme.common.color_success
));
css_vars.push_str(&format!(
"--thaw-background-color-error: {};",
theme.common.color_error
));
});
css_vars
});
let loading_bar_ref = create_node_ref::<html::Div>();
let loading = create_rw_signal(false);
let start = Callback::new(move |_| {
loading.set(true);
if let Some(loading_bar_ref) = loading_bar_ref.get_untracked() {
let loading_bar_ref = loading_bar_ref
.style("background-color", "var(--thaw-background-color)")
.style("transition", "none")
.style("max-width", "0");
_ = loading_bar_ref.offset_width();
loading_bar_ref
.style("transition", "max-width 4s linear")
.style("max-width", "80%");
}
});
let finish = Callback::new(move |_| {
if let Some(loading_bar_ref) = loading_bar_ref.get_untracked() {
let loading_bar_ref = loading_bar_ref
.style("background-color", "var(--thaw-background-color)")
.style("transition", "max-width 0.5s linear")
.style("max-width", "1000%");
loading_bar_ref.on(ev::transitionend, move |_| {
loading.set(false);
});
}
});
let error = Callback::new(move |_| {
if let Some(loading_bar_ref) = loading_bar_ref.get_untracked() {
if !loading.get() {
loading.set(true);
let loading_bar_ref = loading_bar_ref.clone();
let loading_bar_ref = loading_bar_ref
.style("transition", "none")
.style("max-width", "0");
_ = loading_bar_ref.offset_width();
}
let loading_bar_ref = loading_bar_ref
.style("background-color", "var(--thaw-background-color-error)")
.style("transition", "max-width 0.5s linear")
.style("max-width", "100%");
loading_bar_ref.on(ev::transitionend, move |_| {
loading.set(false);
});
}
});
comp_ref.load(LoadingBarRef {
start,
finish,
error,
});
view! {
<div class="thaw-loading-bar-container">
<div class="thaw-loading-bar" ref=loading_bar_ref style=move || css_vars.get()>
</div>
</div>
}
}