1use gpui::{IntoElement, Role, Window, prelude::*};
2
3use crate::{ButtonLike, prelude::*};
4
5/// A button that takes an underline to look like a regular web link.
6/// It also contains an arrow icon to communicate the link takes you out of Zed.
7///
8/// # Usage Example
9///
10/// ```
11/// use ui::ButtonLink;
12///
13/// let button_link = ButtonLink::new("Click me", "https://example.com");
14/// ```
15#[derive(IntoElement, RegisterComponent)]
16pub struct ButtonLink {
17 label: SharedString,
18 label_size: LabelSize,
19 label_color: Color,
20 link: String,
21 no_icon: bool,
22}
23
24impl ButtonLink {
25 pub fn new(label: impl Into<SharedString>, link: impl Into<String>) -> Self {
26 Self {
27 link: link.into(),
28 label: label.into(),
29 label_size: LabelSize::Default,
30 label_color: Color::Default,
31 no_icon: false,
32 }
33 }
34
35 pub fn no_icon(mut self, no_icon: bool) -> Self {
36 self.no_icon = no_icon;
37 self
38 }
39
40 pub fn label_size(mut self, label_size: LabelSize) -> Self {
41 self.label_size = label_size;
42 self
43 }
44
45 pub fn label_color(mut self, label_color: Color) -> Self {
46 self.label_color = label_color;
47 self
48 }
49}
50
51impl RenderOnce for ButtonLink {
52 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
53 let id = format!("{}-{}", self.label, self.link);
54
55 ButtonLike::new(id)
56 .role(Role::Link)
57 .size(ButtonSize::None)
58 .child(
59 h_flex()
60 .gap_0p5()
61 .child(
62 Label::new(self.label)
63 .size(self.label_size)
64 .color(self.label_color)
65 .underline(),
66 )
67 .when(!self.no_icon, |this| {
68 this.child(
69 Icon::new(IconName::ArrowUpRight)
70 .size(IconSize::Small)
71 .color(Color::Muted),
72 )
73 }),
74 )
75 .on_click(move |_, _, cx| cx.open_url(&self.link))
76 .into_any_element()
77 }
78}
79
80impl Component for ButtonLink {
81 fn scope() -> ComponentScope {
82 ComponentScope::Navigation
83 }
84
85 fn description() -> Option<&'static str> {
86 Some("A button that opens a URL.")
87 }
88
89 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
90 Some(
91 v_flex()
92 .gap_6()
93 .child(
94 example_group(vec![single_example(
95 "Simple",
96 ButtonLink::new("zed.dev", "https://zed.dev").into_any_element(),
97 )])
98 .vertical(),
99 )
100 .into_any_element(),
101 )
102 }
103}