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 @@
1<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">1<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"/>
3</svg>4</svg>
crates/ui/src/components/ai.rs+2−0
@@ -1,5 +1,7 @@
1mod configured_api_card;1mod configured_api_card;
2mod thread_item;2mod thread_item;
3mod thread_sidebar_toggle;
3 4
4pub use configured_api_card::*;5pub use configured_api_card::*;
5pub use thread_item::*;6pub use thread_item::*;
7pub use thread_sidebar_toggle::*;
crates/ui/src/components/ai/configured_api_card.rs+54−1
@@ -1,7 +1,7 @@
1use crate::{Tooltip, prelude::*};1use crate::{Tooltip, prelude::*};
2use gpui::{ClickEvent, IntoElement, ParentElement, SharedString};2use gpui::{ClickEvent, IntoElement, ParentElement, SharedString};
3 3
4#[derive(IntoElement)]4#[derive(IntoElement, RegisterComponent)]
5pub struct ConfiguredApiCard {5pub struct ConfiguredApiCard {
6 label: SharedString,6 label: SharedString,
7 button_label: Option<SharedString>,7 button_label: Option<SharedString>,
@@ -52,6 +52,59 @@ impl ConfiguredApiCard { · 44 unmodified lines
52 }52 }
53}53}
54 54
55impl 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
55impl RenderOnce for ConfiguredApiCard {108impl RenderOnce for ConfiguredApiCard {
56 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {109 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
57 let button_label = self.button_label.unwrap_or("Reset Key".into());110 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 @@
1use gpui::{AnyView, ClickEvent};
2use ui_macros::RegisterComponent;
3
4use crate::prelude::*;
5use crate::{IconButton, IconName, Tooltip};
6
7#[derive(IntoElement, RegisterComponent)]
8pub 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
18impl 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
79impl 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
115impl 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}