use std::time::Duration;
use gpui_kit::{
Animation, AnimationExt as _, App, ElementId, IntoElement, RenderOnce, StyleRefinement, Styled,
Window, base::StyledExt as _, bounce, div, ease_in_out,
};
use crate::{ActiveTheme as _, Theme};
#[derive(IntoElement)]
pub struct Skeleton {
id: ElementId,
style: StyleRefinement,
}
impl Skeleton {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
style: StyleRefinement::default(),
}
}
}
impl Styled for Skeleton {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
const PULSE: Duration = Duration::from_secs(2);
const PULSE_FLOOR: f32 = 0.5;
impl RenderOnce for Skeleton {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let (fill, radius, height) = {
let theme = cx.theme();
(
theme.accent(),
theme.radius_md(),
theme.text_control.line_height,
)
};
let block = div()
.w_full()
.h(height)
.rounded(radius)
.bg(fill)
.refine_style(&self.style);
if Theme::global(cx).motion.slow.is_zero() {
return block.opacity(PULSE_FLOOR).into_any_element();
}
block
.with_animation(
self.id,
Animation::new(PULSE)
.repeat()
.with_easing(bounce(ease_in_out)),
|block, delta| block.opacity(1. - delta * (1. - PULSE_FLOOR)),
)
.into_any_element()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builders_refine_the_block() {
let skeleton = Skeleton::new("s").w_full();
assert!(skeleton.style.size.width.is_some());
}
}