This repository has been archived on 2025-09-16. You can view files and clone it, but cannot push or open issues or pull requests.
Files
freezone/portal/src/components/common/ui/loading_spinner.rs
Timur Gordon c1ea9483d7 refactor wip
2025-06-28 16:40:54 +02:00

184 lines
4.9 KiB
Rust

//! Generic loading spinner component
use yew::prelude::*;
/// Size options for the loading spinner
#[derive(Clone, PartialEq)]
pub enum SpinnerSize {
Small,
Medium,
Large,
}
impl SpinnerSize {
pub fn get_class(&self) -> &'static str {
match self {
SpinnerSize::Small => "spinner-border-sm",
SpinnerSize::Medium => "",
SpinnerSize::Large => "spinner-border-lg",
}
}
pub fn get_style(&self) -> &'static str {
match self {
SpinnerSize::Small => "width: 1rem; height: 1rem;",
SpinnerSize::Medium => "width: 1.5rem; height: 1.5rem;",
SpinnerSize::Large => "width: 2rem; height: 2rem;",
}
}
}
/// Color options for the loading spinner
#[derive(Clone, PartialEq)]
pub enum SpinnerColor {
Primary,
Secondary,
Success,
Danger,
Warning,
Info,
Light,
Dark,
}
impl SpinnerColor {
pub fn get_class(&self) -> &'static str {
match self {
SpinnerColor::Primary => "text-primary",
SpinnerColor::Secondary => "text-secondary",
SpinnerColor::Success => "text-success",
SpinnerColor::Danger => "text-danger",
SpinnerColor::Warning => "text-warning",
SpinnerColor::Info => "text-info",
SpinnerColor::Light => "text-light",
SpinnerColor::Dark => "text-dark",
}
}
}
/// Properties for LoadingSpinner component
#[derive(Properties, PartialEq)]
pub struct LoadingSpinnerProps {
/// Size of the spinner
#[prop_or(SpinnerSize::Medium)]
pub size: SpinnerSize,
/// Color of the spinner
#[prop_or(SpinnerColor::Primary)]
pub color: SpinnerColor,
/// Loading message to display
#[prop_or_default]
pub message: Option<AttrValue>,
/// Whether to center the spinner
#[prop_or(true)]
pub centered: bool,
/// Custom CSS class for container
#[prop_or_default]
pub container_class: Option<AttrValue>,
/// Whether to show as inline spinner
#[prop_or(false)]
pub inline: bool,
}
/// LoadingSpinner component
#[function_component(LoadingSpinner)]
pub fn loading_spinner(props: &LoadingSpinnerProps) -> Html {
let container_class = if props.inline {
"d-inline-flex align-items-center"
} else if props.centered {
"d-flex flex-column align-items-center justify-content-center"
} else {
"d-flex align-items-center"
};
let final_container_class = if let Some(custom_class) = &props.container_class {
format!("{} {}", container_class, custom_class.as_str())
} else {
container_class.to_string()
};
let spinner_classes = format!(
"spinner-border {} {}",
props.size.get_class(),
props.color.get_class()
);
html! {
<div class={final_container_class}>
<div
class={spinner_classes}
style={props.size.get_style()}
role="status"
aria-hidden="true"
>
<span class="visually-hidden">{"Loading..."}</span>
</div>
{if let Some(message) = &props.message {
let message_class = if props.inline {
"ms-2"
} else {
"mt-2"
};
html! {
<div class={message_class}>
{message.as_str()}
</div>
}
} else {
html! {}
}}
</div>
}
}
/// Convenience component for common loading scenarios
#[derive(Properties, PartialEq)]
pub struct LoadingOverlayProps {
/// Loading message
#[prop_or("Loading...".to_string())]
pub message: String,
/// Whether the overlay is visible
#[prop_or(true)]
pub show: bool,
/// Background opacity (0.0 to 1.0)
#[prop_or(0.8)]
pub opacity: f64,
/// Spinner color
#[prop_or(SpinnerColor::Primary)]
pub spinner_color: SpinnerColor,
}
/// LoadingOverlay component for full-screen loading
#[function_component(LoadingOverlay)]
pub fn loading_overlay(props: &LoadingOverlayProps) -> Html {
if !props.show {
return html! {};
}
let background_style = format!(
"position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 255, 255, {}); z-index: 9999;",
props.opacity
);
html! {
<div style={background_style}>
<div class="d-flex flex-column align-items-center justify-content-center h-100">
<LoadingSpinner
size={SpinnerSize::Large}
color={props.spinner_color.clone()}
message={props.message.clone()}
centered={true}
/>
</div>
</div>
}
}