1use gpui::prelude::*;
6use gpui::{div, px, AnyElement, App, IntoElement, SharedString, Window};
7
8use crate::theme::{theme, ColorName, Size};
9
10#[derive(IntoElement)]
12pub struct Field {
13 label: Option<SharedString>,
14 description: Option<SharedString>,
15 error: Option<SharedString>,
16 child: Option<AnyElement>,
17}
18
19impl Field {
20 pub fn new() -> Self {
21 Field {
22 label: None,
23 description: None,
24 error: None,
25 child: None,
26 }
27 }
28
29 pub fn label(mut self, label: impl Into<SharedString>) -> Self {
30 self.label = Some(label.into());
31 self
32 }
33
34 pub fn description(mut self, description: impl Into<SharedString>) -> Self {
35 self.description = Some(description.into());
36 self
37 }
38
39 pub fn error(mut self, error: impl Into<SharedString>) -> Self {
40 self.error = Some(error.into());
41 self
42 }
43
44 pub fn child(mut self, child: impl IntoElement) -> Self {
46 self.child = Some(child.into_any_element());
47 self
48 }
49}
50
51impl Default for Field {
52 fn default() -> Self {
53 Field::new()
54 }
55}
56
57impl RenderOnce for Field {
58 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
59 let t = theme(cx);
60 let text = t.text().hsla();
61 let dimmed = t.dimmed().hsla();
62 let error_color = t
63 .color(ColorName::Red, if t.scheme.is_dark() { 5 } else { 7 })
64 .hsla();
65 let font_sm = t.font_size(Size::Sm);
66 let font_xs = t.font_size(Size::Xs);
67
68 let mut column = div().flex().flex_col().gap(px(4.0));
69 if let Some(label) = self.label {
70 column = column.child(div().text_size(px(font_sm)).text_color(text).child(label));
71 }
72 if let Some(child) = self.child {
73 column = column.child(child);
74 }
75 if let Some(error) = self.error {
76 column = column.child(
77 div()
78 .text_size(px(font_xs))
79 .text_color(error_color)
80 .child(error),
81 );
82 } else if let Some(description) = self.description {
83 column = column.child(
84 div()
85 .text_size(px(font_xs))
86 .text_color(dimmed)
87 .child(description),
88 );
89 }
90 column
91 }
92}