ui: Add DiffStat component (#43192)

Release Notes:

- N/A
This commit is contained in:
Danilo Leal
2025-11-20 18:18:08 -03:00
committed by GitHub
parent b41eb3cdaf
commit a332b79189
3 changed files with 123 additions and 8 deletions
+2
View File
@@ -6,6 +6,7 @@ mod chip;
mod content_group;
mod context_menu;
mod data_table;
mod diff_stat;
mod disclosure;
mod divider;
mod dropdown_menu;
@@ -50,6 +51,7 @@ pub use chip::*;
pub use content_group::*;
pub use context_menu::*;
pub use data_table::*;
pub use diff_stat::*;
pub use disclosure::*;
pub use divider::*;
pub use dropdown_menu::*;
+85
View File
@@ -0,0 +1,85 @@
use crate::prelude::*;
#[derive(IntoElement, RegisterComponent)]
pub struct DiffStat {
id: ElementId,
added: usize,
removed: usize,
}
impl DiffStat {
pub fn new(id: impl Into<ElementId>, added: usize, removed: usize) -> Self {
Self {
id: id.into(),
added,
removed,
}
}
}
impl RenderOnce for DiffStat {
fn render(self, _: &mut Window, _cx: &mut App) -> impl IntoElement {
h_flex()
.id(self.id)
.gap_1()
.child(
h_flex()
.gap_0p5()
.child(
Icon::new(IconName::Plus)
.size(IconSize::XSmall)
.color(Color::Success),
)
.child(
Label::new(self.added.to_string())
.color(Color::Success)
.size(LabelSize::Small),
),
)
.child(
h_flex()
.gap_0p5()
.child(
Icon::new(IconName::Dash)
.size(IconSize::XSmall)
.color(Color::Error),
)
.child(
Label::new(self.removed.to_string())
.color(Color::Error)
.size(LabelSize::Small),
),
)
}
}
impl Component for DiffStat {
fn scope() -> ComponentScope {
ComponentScope::VersionControl
}
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let container = || {
h_flex()
.py_4()
.w_72()
.justify_center()
.border_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().panel_background)
};
let diff_stat_example = vec![single_example(
"Default",
container()
.child(DiffStat::new("id", 1, 2))
.into_any_element(),
)];
Some(
example_group(diff_stat_example)
.vertical()
.into_any_element(),
)
}
}
+36 -8
View File
@@ -1,4 +1,4 @@
use crate::{Chip, Indicator, SpinnerLabel, prelude::*};
use crate::{Chip, DiffStat, Indicator, SpinnerLabel, prelude::*};
use gpui::{ClickEvent, SharedString};
#[derive(IntoElement, RegisterComponent)]
@@ -10,7 +10,8 @@ pub struct ThreadItem {
running: bool,
generation_done: bool,
selected: bool,
has_changes: bool,
added: Option<usize>,
removed: Option<usize>,
worktree: Option<SharedString>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
}
@@ -25,7 +26,8 @@ impl ThreadItem {
running: false,
generation_done: false,
selected: false,
has_changes: false,
added: None,
removed: None,
worktree: None,
on_click: None,
}
@@ -56,8 +58,13 @@ impl ThreadItem {
self
}
pub fn has_changes(mut self, has_changes: bool) -> Self {
self.has_changes = has_changes;
pub fn added(mut self, added: usize) -> Self {
self.added = Some(added);
self
}
pub fn removed(mut self, removed: usize) -> Self {
self.removed = Some(removed);
self
}
@@ -90,8 +97,10 @@ impl RenderOnce for ThreadItem {
)
};
let has_no_changes = self.added.is_none() && self.removed.is_none();
v_flex()
.id(self.id)
.id(self.id.clone())
.cursor_pointer()
.p_2()
.when(self.selected, |this| {
@@ -123,12 +132,19 @@ impl RenderOnce for ThreadItem {
.color(Color::Muted)
.alpha(0.5),
)
.when(!self.has_changes, |this| {
.when(has_no_changes, |this| {
this.child(
Label::new("No Changes")
.size(LabelSize::Small)
.color(Color::Muted),
)
})
.when(self.added.is_some() || self.removed.is_some(), |this| {
this.child(DiffStat::new(
self.id,
self.added.unwrap_or(0),
self.removed.unwrap_or(0),
))
}),
)
.when_some(self.on_click, |this, on_click| this.on_click(on_click))
@@ -192,11 +208,23 @@ impl Component for ThreadItem {
)
.into_any_element(),
),
single_example(
"With Changes",
container()
.child(
ThreadItem::new("ti-5", "Managing user and project settings interactions")
.icon(IconName::AiClaude)
.timestamp("7:37 PM")
.added(10)
.removed(3),
)
.into_any_element(),
),
single_example(
"Selected Item",
container()
.child(
ThreadItem::new("ti-5", "Refine textarea interaction behavior")
ThreadItem::new("ti-6", "Refine textarea interaction behavior")
.icon(IconName::AiGemini)
.timestamp("3:00 PM")
.selected(true),