refactor wip
This commit is contained in:
184
portal/src/components/common/ui/loading_spinner.rs
Normal file
184
portal/src/components/common/ui/loading_spinner.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
//! 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>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user