From 31736b0e2126930580bd516327f17904c1eb1933 Mon Sep 17 00:00:00 2001 From: Floyd Wang Date: Fri, 10 Jan 2025 14:28:27 +0800 Subject: [PATCH] badge: Add new component (#540) image image --- crates/story/src/main.rs | 45 ++++++++------------- crates/ui/src/badge.rs | 87 ++++++++++++++++++++++++++++++++++++++++ crates/ui/src/lib.rs | 1 + 3 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 crates/ui/src/badge.rs diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index dc1be956..68169664 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -10,10 +10,10 @@ use story::{ TooltipStory, }; use ui::{ + badge::Badge, button::{Button, ButtonVariants as _}, color_picker::{ColorPicker, ColorPickerEvent}, dock::{DockArea, DockAreaState, DockEvent, DockItem, DockPlacement}, - h_flex, popup_menu::PopupMenuExt, scroll::ScrollbarShow, theme::{ActiveTheme, Theme}, @@ -553,41 +553,28 @@ impl Render for StoryWorkspace { .child(self.locale_selector.clone()) .child(self.font_size_selector.clone()) .child( - Button::new("github") - .icon(IconName::GitHub) - .small() - .ghost() - .on_click(|_, cx| { - cx.open_url("https://github.com/longbridge/gpui-component") - }), + Badge::new().dot().count(1).child( + Button::new("github") + .icon(IconName::GitHub) + .small() + .ghost() + .on_click(|_, cx| { + cx.open_url( + "https://github.com/longbridge/gpui-component", + ) + }), + ), ) .child( - div() - .relative() - .child( + div().relative().child( + Badge::new().count(notifications_count).max(99).child( Button::new("bell") .small() .ghost() .compact() .icon(IconName::Bell), - ) - .when(notifications_count > 0, |this| { - this.child( - h_flex() - .absolute() - .rounded_full() - .top(px(-2.)) - .right(px(-2.)) - .p(px(1.)) - .min_w(px(12.)) - .bg(ui::red_500()) - .text_color(ui::white()) - .justify_center() - .text_size(px(10.)) - .line_height(relative(1.)) - .child(format!("{}", notifications_count.min(99))), - ) - }), + ), + ), ), ), ) diff --git a/crates/ui/src/badge.rs b/crates/ui/src/badge.rs new file mode 100644 index 00000000..eb129387 --- /dev/null +++ b/crates/ui/src/badge.rs @@ -0,0 +1,87 @@ +use gpui::{ + div, prelude::FluentBuilder, px, relative, AnyElement, Div, IntoElement, ParentElement, + RenderOnce, Styled, WindowContext, +}; + +use crate::{h_flex, red_500, white}; + +#[derive(Default)] +enum BadgeStyle { + Dot, + #[default] + Number, +} + +#[derive(IntoElement)] +pub struct Badge { + base: Div, + count: usize, + max: usize, + style: BadgeStyle, +} + +impl Badge { + pub fn new() -> Self { + Self { + base: div(), + count: 0, + max: 99, + style: Default::default(), + } + } + + pub fn dot(mut self) -> Self { + self.style = BadgeStyle::Dot; + self + } + + pub fn count(mut self, count: usize) -> Self { + self.count = count; + self + } + + pub fn max(mut self, max: usize) -> Self { + self.max = max; + self + } +} + +impl ParentElement for Badge { + fn extend(&mut self, elements: impl IntoIterator) { + self.base.extend(elements); + } +} + +impl RenderOnce for Badge { + fn render(self, _: &mut WindowContext) -> impl IntoElement { + self.base.relative().when(self.count > 0, |this| { + this.child( + h_flex() + .absolute() + .justify_center() + .rounded_full() + .bg(red_500()) + .map(|this| match self.style { + BadgeStyle::Dot => this.top(px(0.)).right(px(0.)).size(px(6.)), + BadgeStyle::Number => { + let count = if self.count > self.max { + format!("{}+", self.max) + } else { + self.count.to_string() + }; + + this.top(px(-2.)) + .right_neg_1p5() + .py_0p5() + .px_0p5() + .min_w_3p5() + .text_color(white()) + .text_size(px(10.)) + .line_height(relative(1.)) + .child(count) + } + }), + ) + }) + } +} diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index 4b990b54..9ae18126 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -11,6 +11,7 @@ mod window_border; pub mod accordion; pub mod animation; +pub mod badge; pub mod breadcrumb; pub mod button; pub mod button_group;