History

ui: Add new component for thread sidebar panel toggle (#51441)

Danilo Leal committed 8 months ago

Commit ea5c58c1 parent 8e045235 files · +235 −3Browse files

Unified · Split

5 files
  1. assets/icons/thread.svg +2 −1
  2. crates/ui/src/components/ai.rs +2 −0
  3. crates/ui/src/components/ai/configured_api_card.rs +54 −1
  4. crates/ui/src/components/ai/copilot_configuration_callout.rs +0 −1
  5. crates/ui/src/components/ai/thread_sidebar_toggle.rs +177 −0
assets/icons/thread.svg+2−1
@@ -1,3 +1,4 @@
11 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2−<path d="M6.31254 12.549C7.3841 13.0987 8.61676 13.2476 9.78839 12.9688C10.96 12.6901 11.9936 12.0021 12.7028 11.0287C13.412 10.0554 13.7503 8.8607 13.6566 7.66002C13.5629 6.45934 13.0435 5.33159 12.1919 4.48C11.3403 3.62841 10.2126 3.10898 9.01188 3.01531C7.8112 2.92164 6.61655 3.2599 5.64319 3.96912C4.66984 4.67834 3.9818 5.71188 3.70306 6.88351C3.42432 8.05514 3.5732 9.2878 4.12289 10.3594L3 13.6719L6.31254 12.549Z" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
2+<path opacity="0.12" d="M6.31254 12.549C7.3841 13.0987 8.61676 13.2476 9.78839 12.9688C10.96 12.6901 11.9936 12.0021 12.7028 11.0287C13.412 10.0554 13.7503 8.8607 13.6566 7.66002C13.5629 6.45934 13.0435 5.33159 12.1919 4.48C11.3403 3.62841 10.2126 3.10898 9.01188 3.01531C7.8112 2.92164 6.61655 3.2599 5.64319 3.96912C4.66984 4.67834 3.9818 5.71188 3.70306 6.88351C3.42432 8.05514 3.5732 9.2878 4.12289 10.3594L3 13.6719L6.31254 12.549Z" fill="#C6CAD0"/>
3+<path d="M5.97658 12.549C7.04814 13.0987 8.2808 13.2476 9.45243 12.9688C10.624 12.6901 11.6576 12.0021 12.3668 11.0287C13.076 10.0554 13.4143 8.8607 13.3206 7.66002C13.2269 6.45934 12.7075 5.33159 11.8559 4.48C11.0043 3.62841 9.87664 3.10898 8.67592 3.01531C7.47524 2.92164 6.28059 3.2599 5.30723 3.96912C4.33388 4.67834 3.64584 5.71188 3.3671 6.88351C3.08836 8.05514 3.23724 9.2878 3.78693 10.3594L2.66404 13.6719L5.97658 12.549Z" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
34 </svg>
crates/ui/src/components/ai.rs+2−0
@@ -1,5 +1,7 @@
11 mod configured_api_card;
22 mod thread_item;
3+mod thread_sidebar_toggle;
34
45 pub use configured_api_card::*;
56 pub use thread_item::*;
7+pub use thread_sidebar_toggle::*;
crates/ui/src/components/ai/configured_api_card.rs+54−1
@@ -1,7 +1,7 @@
11 use crate::{Tooltip, prelude::*};
22 use gpui::{ClickEvent, IntoElement, ParentElement, SharedString};
33
4−#[derive(IntoElement)]
4+#[derive(IntoElement, RegisterComponent)]
55 pub struct ConfiguredApiCard {
66 label: SharedString,
77 button_label: Option<SharedString>,
@@ -52,6 +52,59 @@ impl ConfiguredApiCard { · 44 unmodified lines
5252 }
5353 }
5454
55+impl Component for ConfiguredApiCard {
56+ fn scope() -> ComponentScope {
57+ ComponentScope::Agent
58+ }
59+
60+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
61+ let container = || {
62+ v_flex()
63+ .w_72()
64+ .p_2()
65+ .gap_2()
66+ .border_1()
67+ .border_color(cx.theme().colors().border_variant)
68+ .bg(cx.theme().colors().panel_background)
69+ };
70+
71+ let examples = vec![
72+ single_example(
73+ "Default",
74+ container()
75+ .child(ConfiguredApiCard::new("API key is configured"))
76+ .into_any_element(),
77+ ),
78+ single_example(
79+ "Custom Button Label",
80+ container()
81+ .child(
82+ ConfiguredApiCard::new("OpenAI API key configured")
83+ .button_label("Remove Key"),
84+ )
85+ .into_any_element(),
86+ ),
87+ single_example(
88+ "With Tooltip",
89+ container()
90+ .child(
91+ ConfiguredApiCard::new("Anthropic API key configured")
92+ .tooltip_label("Click to reset your API key"),
93+ )
94+ .into_any_element(),
95+ ),
96+ single_example(
97+ "Disabled",
98+ container()
99+ .child(ConfiguredApiCard::new("API key is configured").disabled(true))
100+ .into_any_element(),
101+ ),
102+ ];
103+
104+ Some(example_group(examples).into_any_element())
105+ }
106+}
107+
55108 impl RenderOnce for ConfiguredApiCard {
56109 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
57110 let button_label = self.button_label.unwrap_or("Reset Key".into());
crates/ui/src/components/ai/copilot_configuration_callout.rs+0−1
@@ -1 +0,0 @@
1−
crates/ui/src/components/ai/thread_sidebar_toggle.rs+177−0
@@ -0,0 +1,177 @@
1+use gpui::{AnyView, ClickEvent};
2+use ui_macros::RegisterComponent;
3+
4+use crate::prelude::*;
5+use crate::{IconButton, IconName, Tooltip};
6+
7+#[derive(IntoElement, RegisterComponent)]
8+pub struct ThreadSidebarToggle {
9+ sidebar_selected: bool,
10+ thread_selected: bool,
11+ flipped: bool,
12+ sidebar_tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
13+ thread_tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
14+ on_sidebar_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
15+ on_thread_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
16+}
17+
18+impl ThreadSidebarToggle {
19+ pub fn new() -> Self {
20+ Self {
21+ sidebar_selected: false,
22+ thread_selected: false,
23+ flipped: false,
24+ sidebar_tooltip: None,
25+ thread_tooltip: None,
26+ on_sidebar_click: None,
27+ on_thread_click: None,
28+ }
29+ }
30+
31+ pub fn sidebar_selected(mut self, selected: bool) -> Self {
32+ self.sidebar_selected = selected;
33+ self
34+ }
35+
36+ pub fn thread_selected(mut self, selected: bool) -> Self {
37+ self.thread_selected = selected;
38+ self
39+ }
40+
41+ pub fn flipped(mut self, flipped: bool) -> Self {
42+ self.flipped = flipped;
43+ self
44+ }
45+
46+ pub fn sidebar_tooltip(
47+ mut self,
48+ tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
49+ ) -> Self {
50+ self.sidebar_tooltip = Some(Box::new(tooltip));
51+ self
52+ }
53+
54+ pub fn thread_tooltip(
55+ mut self,
56+ tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
57+ ) -> Self {
58+ self.thread_tooltip = Some(Box::new(tooltip));
59+ self
60+ }
61+
62+ pub fn on_sidebar_click(
63+ mut self,
64+ handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
65+ ) -> Self {
66+ self.on_sidebar_click = Some(Box::new(handler));
67+ self
68+ }
69+
70+ pub fn on_thread_click(
71+ mut self,
72+ handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
73+ ) -> Self {
74+ self.on_thread_click = Some(Box::new(handler));
75+ self
76+ }
77+}
78+
79+impl RenderOnce for ThreadSidebarToggle {
80+ fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
81+ let sidebar_icon = match (self.sidebar_selected, self.flipped) {
82+ (true, false) => IconName::ThreadsSidebarLeftOpen,
83+ (false, false) => IconName::ThreadsSidebarLeftClosed,
84+ (true, true) => IconName::ThreadsSidebarRightOpen,
85+ (false, true) => IconName::ThreadsSidebarRightClosed,
86+ };
87+
88+ h_flex()
89+ .min_w_0()
90+ .rounded_sm()
91+ .gap_px()
92+ .border_1()
93+ .border_color(cx.theme().colors().border)
94+ .when(self.flipped, |this| this.flex_row_reverse())
95+ .child(
96+ IconButton::new("sidebar-toggle", sidebar_icon)
97+ .icon_size(IconSize::Small)
98+ .toggle_state(self.sidebar_selected)
99+ .when_some(self.sidebar_tooltip, |this, tooltip| this.tooltip(tooltip))
100+ .when_some(self.on_sidebar_click, |this, handler| {
101+ this.on_click(handler)
102+ }),
103+ )
104+ .child(div().h_4().w_px().bg(cx.theme().colors().border))
105+ .child(
106+ IconButton::new("thread-toggle", IconName::Thread)
107+ .icon_size(IconSize::Small)
108+ .toggle_state(self.thread_selected)
109+ .when_some(self.thread_tooltip, |this, tooltip| this.tooltip(tooltip))
110+ .when_some(self.on_thread_click, |this, handler| this.on_click(handler)),
111+ )
112+ }
113+}
114+
115+impl Component for ThreadSidebarToggle {
116+ fn scope() -> ComponentScope {
117+ ComponentScope::Agent
118+ }
119+
120+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
121+ let container = || div().p_2().bg(cx.theme().colors().status_bar_background);
122+
123+ let examples = vec![
124+ single_example(
125+ "Both Unselected",
126+ container()
127+ .child(ThreadSidebarToggle::new())
128+ .into_any_element(),
129+ ),
130+ single_example(
131+ "Sidebar Selected",
132+ container()
133+ .child(ThreadSidebarToggle::new().sidebar_selected(true))
134+ .into_any_element(),
135+ ),
136+ single_example(
137+ "Thread Selected",
138+ container()
139+ .child(ThreadSidebarToggle::new().thread_selected(true))
140+ .into_any_element(),
141+ ),
142+ single_example(
143+ "Both Selected",
144+ container()
145+ .child(
146+ ThreadSidebarToggle::new()
147+ .sidebar_selected(true)
148+ .thread_selected(true),
149+ )
150+ .into_any_element(),
151+ ),
152+ single_example(
153+ "Flipped",
154+ container()
155+ .child(
156+ ThreadSidebarToggle::new()
157+ .sidebar_selected(true)
158+ .thread_selected(true)
159+ .flipped(true),
160+ )
161+ .into_any_element(),
162+ ),
163+ single_example(
164+ "With Tooltips",
165+ container()
166+ .child(
167+ ThreadSidebarToggle::new()
168+ .sidebar_tooltip(Tooltip::text("Toggle Sidebar"))
169+ .thread_tooltip(Tooltip::text("Toggle Thread")),
170+ )
171+ .into_any_element(),
172+ ),
173+ ];
174+
175+ Some(example_group(examples).into_any_element())
176+ }
177+}