ui/components/
skeleton.rs1use gpui::{Animation, AnimationExt, pulsating_between};
2use std::time::Duration;
3
4use crate::prelude::*;
5
6#[derive(IntoElement, RegisterComponent)]
8pub struct Skeleton {
9 id: ElementId,
10 width: Option<DefiniteLength>,
11 height: Option<DefiniteLength>,
12 rounded: bool,
13}
14
15impl Skeleton {
16 pub fn new() -> Self {
17 Self {
18 id: ElementId::Name("skeleton".into()),
19 width: None,
20 height: None,
21 rounded: true,
22 }
23 }
24
25 pub fn id(mut self, id: impl Into<ElementId>) -> Self {
29 self.id = id.into();
30 self
31 }
32
33 pub fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
34 self.width = Some(width.into());
35 self
36 }
37
38 pub fn height(mut self, height: impl Into<DefiniteLength>) -> Self {
39 self.height = Some(height.into());
40 self
41 }
42
43 pub fn rounded(mut self, rounded: bool) -> Self {
44 self.rounded = rounded;
45 self
46 }
47}
48
49impl Default for Skeleton {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55impl RenderOnce for Skeleton {
56 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
57 let muted = semantic::muted_bg(cx);
58
59 let id = self.id;
60
61 div()
62 .id(id.clone())
63 .map(|this| {
64 if let Some(width) = self.width {
65 this.w(width)
66 } else {
67 this.w_full()
68 }
69 })
70 .h(self.height.unwrap_or_else(|| rems(1.).into()))
71 .when(self.rounded, |this| this.rounded_md())
72 .bg(muted)
73 .with_animation(
74 id,
75 Animation::new(Duration::from_millis(1500))
76 .repeat()
77 .with_easing(pulsating_between(0.4, 1.0)),
78 |el, delta| el.opacity(delta),
79 )
80 }
81}
82
83impl Component for Skeleton {
84 fn scope() -> ComponentScope {
85 ComponentScope::Loading
86 }
87
88 fn description() -> Option<&'static str> {
89 Some("A pulsing placeholder block shown while content is loading.")
90 }
91
92 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
93 Some(
94 v_flex()
95 .gap_3()
96 .w(px(320.))
97 .child(Skeleton::new().height(rems(1.)))
98 .child(Skeleton::new().width(relative(0.75)).height(rems(1.)))
99 .child(Skeleton::new().width(relative(0.5)).height(rems(1.)))
100 .into_any_element(),
101 )
102 }
103}