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! {
+
+ }
+}