Skip to main content

liora_components/
form.rs

1use gpui::{AnyElement, App, Component, Pixels, SharedString, Window, div, prelude::*, px};
2use liora_core::Config;
3
4pub struct Form {
5    _label_width: Option<Pixels>,
6    inline: bool,
7    children: Vec<AnyElement>,
8}
9
10impl Form {
11    pub fn new() -> Self {
12        Self {
13            _label_width: None,
14            inline: false,
15            children: Vec::new(),
16        }
17    }
18
19    pub fn label_width(mut self, width: impl Into<Pixels>) -> Self {
20        self._label_width = Some(width.into());
21        self
22    }
23    pub fn inline(mut self, inline: bool) -> Self {
24        self.inline = inline;
25        self
26    }
27    pub fn child(mut self, child: impl IntoElement) -> Self {
28        self.children.push(child.into_any_element());
29        self
30    }
31}
32
33impl IntoElement for Form {
34    type Element = Component<Self>;
35    fn into_element(self) -> Self::Element {
36        Component::new(self)
37    }
38}
39
40impl RenderOnce for Form {
41    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
42        div()
43            .flex()
44            .when(self.inline, |s| s.flex_row().gap_4().flex_wrap())
45            .when(!self.inline, |s| s.flex_col().gap_4())
46            .children(self.children)
47    }
48}
49
50pub struct FormItem {
51    label: Option<SharedString>,
52    label_width: Option<Pixels>,
53    required: bool,
54    error: Option<SharedString>,
55    content: Option<AnyElement>,
56}
57
58impl FormItem {
59    pub fn new() -> Self {
60        Self {
61            label: None,
62            label_width: None,
63            required: false,
64            error: None,
65            content: None,
66        }
67    }
68
69    pub fn label(mut self, label: impl Into<SharedString>) -> Self {
70        self.label = Some(label.into());
71        self
72    }
73    pub fn label_width(mut self, width: impl Into<Pixels>) -> Self {
74        self.label_width = Some(width.into());
75        self
76    }
77    pub fn required(mut self, r: bool) -> Self {
78        self.required = r;
79        self
80    }
81    pub fn error(mut self, e: impl Into<SharedString>) -> Self {
82        self.error = Some(e.into());
83        self
84    }
85
86    pub fn child(mut self, child: impl IntoElement) -> Self {
87        self.content = Some(child.into_any_element());
88        self
89    }
90}
91
92impl IntoElement for FormItem {
93    type Element = Component<Self>;
94    fn into_element(self) -> Self::Element {
95        Component::new(self)
96    }
97}
98
99impl RenderOnce for FormItem {
100    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
101        let theme = &cx.global::<Config>().theme;
102
103        div()
104            .flex()
105            .flex_col()
106            .gap_1()
107            .child(div().flex().flex_row().items_center().gap_1().when_some(
108                self.label,
109                |this, label| {
110                    this.child(
111                        div()
112                            .flex()
113                            .flex_row()
114                            .items_center()
115                            .gap_1()
116                            .when_some(self.label_width, |s, w| s.w(w))
117                            .child(
118                                div()
119                                    .text_size(px(theme.font_size.md))
120                                    .text_color(theme.neutral.text_1)
121                                    .child(label),
122                            )
123                            .when(self.required, |this| {
124                                this.child(div().text_color(theme.danger.base).child("*"))
125                            }),
126                    )
127                },
128            ))
129            .when_some(self.content, |this, content| this.child(content))
130            .when_some(self.error, |this, error| {
131                this.child(
132                    div()
133                        .text_size(px(theme.font_size.sm))
134                        .text_color(theme.danger.base)
135                        .child(error),
136                )
137            })
138    }
139}