Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Loader component #115

Merged
merged 12 commits into from
Mar 21, 2023
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions components/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ freya-elements = { path = "../elements", version = "0.1.0" }
freya-node-state = { path = "../state", version = "0.1.0" }
freya-hooks = { path = "../hooks", version = "0.1.0" }
open = "1"
tokio = { version = "1.23.0", features = ["sync", "rt-multi-thread", "time"] }

[dev-dependencies]
freya = { path = "../freya", version = "0.1.0" }
2 changes: 2 additions & 0 deletions components/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod button;
mod dropdown;
mod external_link;
mod input;
mod loader;
mod router_link;
mod scroll_views;
mod slider;
Expand All @@ -18,6 +19,7 @@ pub use button::*;
pub use dropdown::*;
pub use external_link::*;
pub use input::*;
pub use loader::*;
pub use router_link::*;
pub use scroll_views::*;
pub use slider::*;
Expand Down
53 changes: 53 additions & 0 deletions components/src/loader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::time::Duration;

use dioxus::prelude::*;
use freya_elements as dioxus_elements;
use freya_hooks::use_get_theme;
use tokio::time::interval;

/// [`Loader`] component properties. Currently empty.
#[derive(Props, PartialEq)]
pub struct LoaderProps {}

/// `Loader` component.
///
/// # Props
/// See [`LoaderProps`].
///
/// # Styling
/// Inherits the [`LoaderTheme`](freya_hooks::LoaderTheme) theme.
///
#[allow(non_snake_case)]
pub fn Loader(cx: Scope<LoaderProps>) -> Element {
let theme = use_get_theme(cx);
let degrees = use_state(cx, || 0);

let loader_theme = theme.loader;

use_effect(cx, (), move |_| {
to_owned![degrees];
async move {
let mut ticker = interval(Duration::from_millis(28));
loop {
ticker.tick().await;
if *degrees.get() > 360 {
degrees.set(0);
} else {
degrees += 10;
}
}
}
});

render!(svg {
rotate: "{degrees}",
width: "31",
height: "31",
svg_content: r#"
<svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.5235 27.6652C22.2292 27.6652 27.6652 22.2292 27.6652 15.5235C27.6652 8.81783 22.2292 3.38182 15.5235 3.38182C8.81783 3.38182 3.38182 8.81783 3.38182 15.5235C3.38182 22.2292 8.81783 27.6652 15.5235 27.6652Z" stroke="{loader_theme.primary_color}" stroke-width="4"/>
<path d="M27.6652 15.5235C27.6652 8.81859 22.2284 3.38182 15.5235 3.38182" stroke="{loader_theme.secondary_color}" stroke-width="4"/>
</svg>
"#
})
}
53 changes: 40 additions & 13 deletions examples/app_dog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
windows_subsystem = "windows"
)]

use std::time::Duration;

use freya::prelude::*;
use reqwest::Url;
use serde::Deserialize;
use tokio::time::sleep;

fn main() {
launch(app);
Expand All @@ -30,15 +33,28 @@ async fn fetch_image(url: Url) -> Option<Vec<u8>> {
Some(data.to_vec())
}

#[derive(PartialEq)]
pub enum ImageStatus {
Loading,
Stopped,
Loaded,
}

fn app(cx: Scope) -> Element {
let bytes = use_state(cx, || None);
let status = use_state(cx, || ImageStatus::Stopped);
let image_bytes = use_state::<Option<Vec<u8>>>(cx, || None);

let fetch = move || {
to_owned![bytes];
to_owned![image_bytes, status];
cx.spawn(async move {
if let Some(url) = fetch_random_dog().await {
if let Some(doggo) = fetch_image(url).await {
bytes.set(Some(doggo))
status.set(ImageStatus::Loading);
let img = fetch_image(url).await;
sleep(Duration::from_millis(1000)).await;
if let Some(img) = img {
// Image loaded
image_bytes.set(Some(img));
status.set(ImageStatus::Loaded)
}
}
})
Expand All @@ -53,21 +69,32 @@ fn app(cx: Scope) -> Element {
width: "100%",
height: "calc(100% - 58)",
radius: "25",
bytes.as_ref().map(|bytes| {
let image_data = bytes_to_data(cx, bytes);
render!{
image {
width: "100%",
height: "100%",
image_data: image_data
}
display: "center",
direction: "both",
if *status.get() == ImageStatus::Loading {
rsx!(
Loader {}
)
}else if *status.get() == ImageStatus::Loaded {
rsx!{
image_bytes.as_ref().map(|bytes| {
let image_data = bytes_to_data(cx, bytes);
rsx!(
image {
width: "100%",
height: "100%",
image_data: image_data
}
)
})
}
})
}
}
container {
padding: "10",
height: "58",
width: "100%",
direction: "horizontal",
Button {
onclick: move |_| fetch(),
label {
Expand Down
16 changes: 16 additions & 0 deletions hooks/src/use_theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ pub struct AccordionTheme {
pub background: &'static str,
}

/// Theming properties for Loader component.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LoaderTheme {
pub primary_color: &'static str,
pub secondary_color: &'static str,
}

/// Theming properties for Themes.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Theme {
Expand All @@ -122,6 +129,7 @@ pub struct Theme {
pub dropdown: DropdownTheme,
pub dropdown_item: DropdownItemTheme,
pub accordion: AccordionTheme,
pub loader: LoaderTheme,
}

/// `Light` theme
Expand Down Expand Up @@ -179,6 +187,10 @@ pub const LIGHT_THEME: Theme = Theme {
color: "white",
background: "rgb(30, 30, 30)",
},
loader: LoaderTheme {
primary_color: "rgb(50, 50, 50)",
secondary_color: "rgb(150, 150, 150)",
},
};

/// `Dark` theme
Expand Down Expand Up @@ -230,4 +242,8 @@ pub const DARK_THEME: Theme = Theme {
color: "black",
background: "rgb(215, 215, 215)",
},
loader: LoaderTheme {
primary_color: "rgb(150, 150, 150)",
secondary_color: "rgb(255, 255, 255)",
},
};