ui/components/
input_group.rs1use crate::prelude::*;
2use gpui::{AnyElement, IntoElement, ParentElement, Styled};
3
4#[derive(IntoElement, RegisterComponent)]
14pub struct InputGroup {
15 content: AnyElement,
16 leading: Option<AnyElement>,
17 trailing: Option<AnyElement>,
18 invalid: bool,
19}
20
21impl InputGroup {
22 pub fn new(content: impl IntoElement) -> Self {
23 Self {
24 content: content.into_any_element(),
25 leading: None,
26 trailing: None,
27 invalid: false,
28 }
29 }
30
31 pub fn leading(mut self, element: impl IntoElement) -> Self {
33 self.leading = Some(element.into_any_element());
34 self
35 }
36
37 pub fn trailing(mut self, element: impl IntoElement) -> Self {
39 self.trailing = Some(element.into_any_element());
40 self
41 }
42
43 pub fn invalid(mut self, invalid: bool) -> Self {
45 self.invalid = invalid;
46 self
47 }
48}
49
50impl RenderOnce for InputGroup {
51 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
52 let outer_border = if self.invalid {
53 palette::danger(500)
54 } else {
55 semantic::border(cx)
56 };
57 let divider = semantic::border_muted(cx);
58
59 h_flex()
60 .w_full()
61 .items_stretch()
62 .rounded_md()
63 .border_1()
64 .border_color(outer_border)
65 .bg(semantic::surface(cx))
66 .overflow_hidden()
67 .when_some(self.leading, |this, element| {
68 this.child(
69 h_flex()
70 .flex_none()
71 .items_center()
72 .px_3()
73 .border_r_1()
74 .border_color(divider)
75 .child(element),
76 )
77 })
78 .child(div().flex_1().min_w_0().child(self.content))
79 .when_some(self.trailing, |this, element| {
80 this.child(
81 h_flex()
82 .flex_none()
83 .items_center()
84 .px_3()
85 .border_l_1()
86 .border_color(divider)
87 .child(element),
88 )
89 })
90 }
91}
92
93impl Component for InputGroup {
94 fn scope() -> ComponentScope {
95 ComponentScope::Input
96 }
97
98 fn description() -> Option<&'static str> {
99 Some("Wraps an input with a leading/trailing slot under one unified border.")
100 }
101
102 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
103 Some(
104 v_flex()
105 .gap_4()
106 .child(
107 InputGroup::new(Label::new("you@example.com").color(Color::Placeholder))
108 .leading(
109 Icon::new(IconName::AtSign)
110 .size(IconSize::Small)
111 .color(Color::Muted),
112 )
113 .into_any_element(),
114 )
115 .child(
116 InputGroup::new(Label::new("https://example.com").color(Color::Placeholder))
117 .trailing(Button::new("input-group-copy", "Copy").into_any_element())
118 .into_any_element(),
119 )
120 .child(
121 InputGroup::new(Label::new("Invalid value").color(Color::Placeholder))
122 .invalid(true)
123 .into_any_element(),
124 )
125 .into_any_element(),
126 )
127 }
128}