use std::time::Duration;
use gpui_kit::{
App, ElementId, InteractiveElement as _, IntoElement, RenderOnce, StyleRefinement, Styled,
TestSupportExt as _, Window, base::StyledExt as _, bounce, div, ease_in_out,
};
use crate::{ActiveTheme as _, Theme, looping};
#[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;
const PULSE_FPS: u32 = 30;
impl RenderOnce for Skeleton {
fn render(self, window: &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()
.id(self.id)
.test_support()
.w_full()
.h(height)
.rounded(radius)
.bg(fill)
.refine_style(&self.style);
if Theme::holds_still(cx) {
return block.opacity(PULSE_FLOOR);
}
let delta = bounce(ease_in_out)(looping::phase(PULSE, PULSE_FPS, window, cx));
block.opacity(1. - delta * (1. - PULSE_FLOOR))
}
}
#[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());
}
}