use crate::{
core::{
assets,
attrs::component_attributes,
measure::{custom_property, CustomProperty},
},
prelude::*,
theme::use_stylesheets,
};
const SKELETON: Bem = Bem::new("fc-skeleton");
const PROGRESS: Bem = Bem::new("fc-progress");
const RING: Bem = Bem::new("fc-progress-ring");
fc_modifier! {
pub enum SkeletonShape for SKELETON {
Default => None,
Text => "text",
Block => "block",
Circle => "circle",
}
}
fc_modifier! { enum ProgressSize for PROGRESS { Md => None, Sm => "sm" } }
fc_modifier! {
pub enum RingSize for RING {
Md => None,
Sm => "sm",
Lg => "lg",
}
}
fc_modifier! { enum RingTone for RING { Normal => None, Error => "error" } }
#[component]
pub fn Skeleton(
#[props(default)] shape: SkeletonShape,
#[props(default)] class: Option<String>,
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
) -> Element {
let attributes = component_attributes(
attributes,
ClassList::new(SKELETON.block())
.with_modifier(SKELETON, shape)
.extend(class.as_deref()),
&[],
);
rsx! {
if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_LOADING_CSS } }
span { ..attributes }
}
}
#[component]
pub fn Loading(
label: AccessibleName,
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
children: Element,
) -> Element {
let attributes: Vec<_> = attributes
.into_iter()
.filter(|attr| !["role", "aria-busy", "aria-label"].contains(&attr.name))
.collect();
rsx! {
div { role: "group", aria_busy: "true", aria_label: label.to_string(), ..attributes, {children} }
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ProgressValue {
Indeterminate,
Of {
value: f64,
max: f64,
},
}
#[component]
pub fn Progress(
label: AccessibleName,
value: ProgressValue,
#[props(default)] small: bool,
#[props(default)] class: Option<String>,
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
) -> Element {
let mut attributes = component_attributes(
attributes,
ClassList::new(PROGRESS.block())
.with_modifier(
PROGRESS,
if small {
ProgressSize::Sm
} else {
ProgressSize::Md
},
)
.extend(class.as_deref()),
&["value", "max", "aria-label"],
);
if let ProgressValue::Of { value, max } = value {
let max = if max > 0.0 { max } else { 1.0 };
attributes.push(Attribute::new(
"value",
value.clamp(0.0, max).to_string(),
None,
false,
));
attributes.push(Attribute::new("max", max.to_string(), None, false));
}
rsx! {
if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_LOADING_CSS } }
progress { aria_label: label.to_string(), ..attributes }
}
}
#[component]
pub fn ProgressRing(
label: AccessibleName,
percent: Percent,
#[props(default)] size: RingSize,
#[props(default)]
show_label: bool,
#[props(default)] error: bool,
#[props(default)] class: Option<String>,
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
) -> Element {
let mut attributes = component_attributes(
attributes,
ClassList::new(RING.block())
.with_modifier(RING, size)
.with_modifier(
RING,
if error {
RingTone::Error
} else {
RingTone::Normal
},
)
.extend(class.as_deref()),
&[
"role",
"aria-label",
"aria-valuemin",
"aria-valuemax",
"aria-valuenow",
"style",
],
);
attributes.push(custom_property(CustomProperty::ProgressValue, percent));
rsx! {
if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_LOADING_CSS } }
span { role: "progressbar", aria_label: label.to_string(), aria_valuemin: "0", aria_valuemax: "100",
aria_valuenow: "{percent}", ..attributes,
if show_label && size != RingSize::Sm { span { class: RING.element("label"), "{percent}%" } }
}
}
}