diff --git a/demo/src/app.rs b/demo/src/app.rs index 937521d..8d5307f 100644 --- a/demo/src/app.rs +++ b/demo/src/app.rs @@ -24,50 +24,57 @@ pub fn App() -> impl IntoView { provide_context(theme); view! { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + } } +#[component] +fn TheRouter() -> impl IntoView { + view! { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } +} + #[component] fn Provider(theme: RwSignal, children: Children) -> impl IntoView { view! { diff --git a/src/lib.rs b/src/lib.rs index 0b1048e..666a758 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,7 @@ mod image; mod input; mod input_number; mod layout; +mod loading_bar; mod menu; mod message; pub mod mobile; @@ -53,6 +54,7 @@ pub use image::*; pub use input::*; pub use input_number::*; pub use layout::*; +pub use loading_bar::*; pub use menu::*; pub use message::*; pub use modal::*; diff --git a/src/loading_bar/loading-bar.css b/src/loading_bar/loading-bar.css new file mode 100644 index 0000000..5049175 --- /dev/null +++ b/src/loading_bar/loading-bar.css @@ -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; +} diff --git a/src/loading_bar/loading_bar_provider.rs b/src/loading_bar/loading_bar_provider.rs new file mode 100644 index 0000000..7f085ef --- /dev/null +++ b/src/loading_bar/loading_bar_provider.rs @@ -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::::default(); + provide_context(LoadingBarInjection { loading_bar_ref }); + + view! { + {children()} + + + + } +} + +#[derive(Clone)] +pub struct LoadingBarInjection { + loading_bar_ref: ComponentRef, +} +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::() +} diff --git a/src/loading_bar/mod.rs b/src/loading_bar/mod.rs new file mode 100644 index 0000000..fd4daf0 --- /dev/null +++ b/src/loading_bar/mod.rs @@ -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) -> 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::(); + 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! { +
+
+
+
+ } +}