+
+ "If you want to use loading bar, you need to wrap the component where you call related methods inside LoadingBarProvider and use use_loading_bar to get the API."
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ "#,
+ "rust"
+ )
+ >
+
+ ""
+ ""
+
+
+
+ }
+}
diff --git a/demo/src/pages/mod.rs b/demo/src/pages/mod.rs
index 8422b70..e52e8e2 100644
--- a/demo/src/pages/mod.rs
+++ b/demo/src/pages/mod.rs
@@ -14,6 +14,7 @@ mod icon;
mod image;
mod input;
mod input_number;
+mod loading_bar;
mod menu;
mod message;
mod mobile;
@@ -48,6 +49,7 @@ pub use icon::*;
pub use image::*;
pub use input::*;
pub use input_number::*;
+pub use loading_bar::*;
pub use menu::*;
pub use message::*;
pub use mobile::*;
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..3db30ec
--- /dev/null
+++ b/src/loading_bar/mod.rs
@@ -0,0 +1,112 @@
+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(crate) 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(crate) 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 is_on_transitionend = store_value(false);
+ let on_transitionend = move |_| {
+ if is_on_transitionend.get_value() {
+ is_on_transitionend.set_value(false);
+ loading.set(false);
+ }
+ };
+ let finish = Callback::new(move |_| {
+ if let Some(loading_bar_ref) = loading_bar_ref.get_untracked() {
+ loading_bar_ref
+ .style("background-color", "var(--thaw-background-color)")
+ .style("transition", "max-width 0.5s linear")
+ .style("max-width", "100%");
+ is_on_transitionend.set_value(true);
+ }
+ });
+ 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();
+ }
+ loading_bar_ref
+ .style("background-color", "var(--thaw-background-color-error)")
+ .style("transition", "max-width 0.5s linear")
+ .style("max-width", "100%");
+ is_on_transitionend.set_value(true);
+ }
+ });
+
+ comp_ref.load(LoadingBarRef {
+ start,
+ finish,
+ error,
+ });
+ view! {
+