Skip to content

Commit a332b79

Browse files
authored
ui: Add DiffStat component (zed-industries#43192)
Release Notes: - N/A
1 parent b41eb3c commit a332b79

File tree

3 files changed

+123
-8
lines changed

3 files changed

+123
-8
lines changed

crates/ui/src/components.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod chip;
66
mod content_group;
77
mod context_menu;
88
mod data_table;
9+
mod diff_stat;
910
mod disclosure;
1011
mod divider;
1112
mod dropdown_menu;
@@ -50,6 +51,7 @@ pub use chip::*;
5051
pub use content_group::*;
5152
pub use context_menu::*;
5253
pub use data_table::*;
54+
pub use diff_stat::*;
5355
pub use disclosure::*;
5456
pub use divider::*;
5557
pub use dropdown_menu::*;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use crate::prelude::*;
2+
3+
#[derive(IntoElement, RegisterComponent)]
4+
pub struct DiffStat {
5+
id: ElementId,
6+
added: usize,
7+
removed: usize,
8+
}
9+
10+
impl DiffStat {
11+
pub fn new(id: impl Into<ElementId>, added: usize, removed: usize) -> Self {
12+
Self {
13+
id: id.into(),
14+
added,
15+
removed,
16+
}
17+
}
18+
}
19+
20+
impl RenderOnce for DiffStat {
21+
fn render(self, _: &mut Window, _cx: &mut App) -> impl IntoElement {
22+
h_flex()
23+
.id(self.id)
24+
.gap_1()
25+
.child(
26+
h_flex()
27+
.gap_0p5()
28+
.child(
29+
Icon::new(IconName::Plus)
30+
.size(IconSize::XSmall)
31+
.color(Color::Success),
32+
)
33+
.child(
34+
Label::new(self.added.to_string())
35+
.color(Color::Success)
36+
.size(LabelSize::Small),
37+
),
38+
)
39+
.child(
40+
h_flex()
41+
.gap_0p5()
42+
.child(
43+
Icon::new(IconName::Dash)
44+
.size(IconSize::XSmall)
45+
.color(Color::Error),
46+
)
47+
.child(
48+
Label::new(self.removed.to_string())
49+
.color(Color::Error)
50+
.size(LabelSize::Small),
51+
),
52+
)
53+
}
54+
}
55+
56+
impl Component for DiffStat {
57+
fn scope() -> ComponentScope {
58+
ComponentScope::VersionControl
59+
}
60+
61+
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
62+
let container = || {
63+
h_flex()
64+
.py_4()
65+
.w_72()
66+
.justify_center()
67+
.border_1()
68+
.border_color(cx.theme().colors().border_variant)
69+
.bg(cx.theme().colors().panel_background)
70+
};
71+
72+
let diff_stat_example = vec![single_example(
73+
"Default",
74+
container()
75+
.child(DiffStat::new("id", 1, 2))
76+
.into_any_element(),
77+
)];
78+
79+
Some(
80+
example_group(diff_stat_example)
81+
.vertical()
82+
.into_any_element(),
83+
)
84+
}
85+
}

crates/ui/src/components/thread_item.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Chip, Indicator, SpinnerLabel, prelude::*};
1+
use crate::{Chip, DiffStat, Indicator, SpinnerLabel, prelude::*};
22
use gpui::{ClickEvent, SharedString};
33

44
#[derive(IntoElement, RegisterComponent)]
@@ -10,7 +10,8 @@ pub struct ThreadItem {
1010
running: bool,
1111
generation_done: bool,
1212
selected: bool,
13-
has_changes: bool,
13+
added: Option<usize>,
14+
removed: Option<usize>,
1415
worktree: Option<SharedString>,
1516
on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
1617
}
@@ -25,7 +26,8 @@ impl ThreadItem {
2526
running: false,
2627
generation_done: false,
2728
selected: false,
28-
has_changes: false,
29+
added: None,
30+
removed: None,
2931
worktree: None,
3032
on_click: None,
3133
}
@@ -56,8 +58,13 @@ impl ThreadItem {
5658
self
5759
}
5860

59-
pub fn has_changes(mut self, has_changes: bool) -> Self {
60-
self.has_changes = has_changes;
61+
pub fn added(mut self, added: usize) -> Self {
62+
self.added = Some(added);
63+
self
64+
}
65+
66+
pub fn removed(mut self, removed: usize) -> Self {
67+
self.removed = Some(removed);
6168
self
6269
}
6370

@@ -90,8 +97,10 @@ impl RenderOnce for ThreadItem {
9097
)
9198
};
9299

100+
let has_no_changes = self.added.is_none() && self.removed.is_none();
101+
93102
v_flex()
94-
.id(self.id)
103+
.id(self.id.clone())
95104
.cursor_pointer()
96105
.p_2()
97106
.when(self.selected, |this| {
@@ -123,12 +132,19 @@ impl RenderOnce for ThreadItem {
123132
.color(Color::Muted)
124133
.alpha(0.5),
125134
)
126-
.when(!self.has_changes, |this| {
135+
.when(has_no_changes, |this| {
127136
this.child(
128137
Label::new("No Changes")
129138
.size(LabelSize::Small)
130139
.color(Color::Muted),
131140
)
141+
})
142+
.when(self.added.is_some() || self.removed.is_some(), |this| {
143+
this.child(DiffStat::new(
144+
self.id,
145+
self.added.unwrap_or(0),
146+
self.removed.unwrap_or(0),
147+
))
132148
}),
133149
)
134150
.when_some(self.on_click, |this, on_click| this.on_click(on_click))
@@ -192,11 +208,23 @@ impl Component for ThreadItem {
192208
)
193209
.into_any_element(),
194210
),
211+
single_example(
212+
"With Changes",
213+
container()
214+
.child(
215+
ThreadItem::new("ti-5", "Managing user and project settings interactions")
216+
.icon(IconName::AiClaude)
217+
.timestamp("7:37 PM")
218+
.added(10)
219+
.removed(3),
220+
)
221+
.into_any_element(),
222+
),
195223
single_example(
196224
"Selected Item",
197225
container()
198226
.child(
199-
ThreadItem::new("ti-5", "Refine textarea interaction behavior")
227+
ThreadItem::new("ti-6", "Refine textarea interaction behavior")
200228
.icon(IconName::AiGemini)
201229
.timestamp("3:00 PM")
202230
.selected(true),

0 commit comments

Comments
 (0)