From ebf8a7f6f364c0db4635b5d5f48b8be0f74c7466 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Fri, 9 Aug 2024 15:55:48 +0800 Subject: [PATCH] Add Skeleton (#128) https://github.com/user-attachments/assets/deb57e8d-f894-41b6-b44b-cc2b4a307429 --- README.md | 2 +- crates/story/src/progress_story.rs | 13 +++++ crates/ui/{src => }/default-colors.json | 0 crates/ui/src/colors.rs | 3 +- crates/ui/src/lib.rs | 1 + crates/ui/src/skeleton.rs | 72 ++++++++----------------- crates/ui/src/theme.rs | 2 + 7 files changed, 42 insertions(+), 51 deletions(-) rename crates/ui/{src => }/default-colors.json (100%) diff --git a/README.md b/README.md index 4bc8a7bc..cdf64a04 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ A UI components for building desktop application by using [GPUI](https://gpui.rs - [x] ProgressBar - [x] Indicator - [x] Slider -- [ ] Skeleton +- [x] Skeleton - [ ] DatePicker - [x] DatePicker - [x] Calendar diff --git a/crates/story/src/progress_story.rs b/crates/story/src/progress_story.rs index 56fd7741..d9661ab0 100644 --- a/crates/story/src/progress_story.rs +++ b/crates/story/src/progress_story.rs @@ -8,6 +8,7 @@ use ui::{ h_flex, indicator::Indicator, progress::Progress, + skeleton::Skeleton, slider::{Slider, SliderEvent}, v_flex, Clickable, IconName, Sizable, }; @@ -144,5 +145,17 @@ impl Render for ProgressStory { .child(self.slider2.clone()) .child(format!("Slider 2: {}", self.slider2_value)), ) + .child( + h_flex() + .mt_5() + .gap_4() + .child(Skeleton::new().size_12().rounded_full()) + .child( + v_flex() + .gap_2() + .child(Skeleton::new().w(px(250.)).h_4()) + .child(Skeleton::new().w(px(240.)).h_4()), + ), + ) } } diff --git a/crates/ui/src/default-colors.json b/crates/ui/default-colors.json similarity index 100% rename from crates/ui/src/default-colors.json rename to crates/ui/default-colors.json diff --git a/crates/ui/src/colors.rs b/crates/ui/src/colors.rs index 78ff7323..8cce7d84 100644 --- a/crates/ui/src/colors.rs +++ b/crates/ui/src/colors.rs @@ -7,7 +7,8 @@ use crate::theme::hsl; use anyhow::Result; static DEFAULT_COLOR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| { - serde_json::from_str(include_str!("default-colors.json")).expect("failed to parse default-json") + serde_json::from_str(include_str!("../default-colors.json")) + .expect("failed to parse default-json") }); type ColorScales = HashMap; diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index af5377d3..b01600c4 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -29,6 +29,7 @@ pub mod progress; pub mod radio; pub mod resizable; pub mod scroll; +pub mod skeleton; pub mod slider; pub mod switch; pub mod tab; diff --git a/crates/ui/src/skeleton.rs b/crates/ui/src/skeleton.rs index 1aa33e9d..d8f66fa1 100644 --- a/crates/ui/src/skeleton.rs +++ b/crates/ui/src/skeleton.rs @@ -1,18 +1,21 @@ use std::time::Duration; use gpui::{ - div, Animation, AnimationExt, Div, Element, InteractiveElement, Interactivity, IntoElement, - ParentElement as _, RenderOnce, Stateful, Styled, + bounce, div, ease_in_out, hsla, px, Animation, AnimationExt, Div, IntoElement, + ParentElement as _, RenderOnce, Styled, }; +use crate::theme::{ActiveTheme, Colorize}; + +#[derive(IntoElement)] pub struct Skeleton { - base: Stateful
, + base: Div, } impl Skeleton { pub fn new() -> Self { Self { - base: div().id("skeleton").w_full().h_4(), + base: div().w_full().h_4().rounded_md(), } } } @@ -23,51 +26,22 @@ impl Styled for Skeleton { } } -impl IntoElement for Skeleton { - type Element = Self; +impl RenderOnce for Skeleton { + fn render(self, cx: &mut gpui::WindowContext) -> impl IntoElement { + let color = cx.theme().skeleton; - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for Skeleton { - type RequestLayoutState = (); - - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn request_layout( - &mut self, - id: Option<&gpui::GlobalElementId>, - cx: &mut gpui::WindowContext, - ) -> (gpui::LayoutId, Self::RequestLayoutState) { - let (layout_id, _) = self.base.request_layout(id, cx); - (layout_id, ()) - } - - fn prepaint( - &mut self, - id: Option<&gpui::GlobalElementId>, - bounds: gpui::Bounds, - request_layout: &mut Self::RequestLayoutState, - cx: &mut gpui::WindowContext, - ) -> Self::PrepaintState { - self.base.prepaint(id, bounds, request_layout, cx); - () - } - - fn paint( - &mut self, - id: Option<&gpui::GlobalElementId>, - bounds: gpui::Bounds, - request_layout: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - cx: &mut gpui::WindowContext, - ) { - todo!() + div().child( + self.base.with_animation( + "skeleton", + Animation::new(Duration::from_secs(2)) + .repeat() + .with_easing(bounce(ease_in_out)), + move |this, delta| { + let v = 1.0 - delta * 0.5; + let color = color.opacity(v); + this.bg(color) + }, + ), + ) } } diff --git a/crates/ui/src/theme.rs b/crates/ui/src/theme.rs index 68909cfd..29de7c6a 100644 --- a/crates/ui/src/theme.rs +++ b/crates/ui/src/theme.rs @@ -313,6 +313,7 @@ pub struct Theme { pub link_hover: Hsla, pub link_active: Hsla, pub menu: Hsla, + pub skeleton: Hsla, } impl Global for Theme {} @@ -391,6 +392,7 @@ impl From for Theme { link_hover: colors.link.lighten(0.2), link_active: colors.link.darken(0.2), menu: colors.menu, + skeleton: hsla(colors.primary.h, colors.primary.s, colors.primary.l, 0.1), } } }