use gpui::{
AnyElement, App, Axis, IntoElement, ParentElement, Pixels, Rems, RenderOnce, StyleRefinement,
Styled, Window, prelude::FluentBuilder as _, px,
};
use crate::{
Sizable, Size,
form::{Field, FieldProps},
h_flex, v_flex,
};
#[derive(IntoElement)]
pub struct Form {
style: StyleRefinement,
fields: Vec<Field>,
footer: Option<AnyElement>,
props: FieldProps,
}
impl Default for Form {
fn default() -> Self {
Self::new()
}
}
impl Form {
pub fn new() -> Self {
Self {
style: StyleRefinement::default(),
props: FieldProps::default(),
fields: Vec::new(),
footer: None,
}
}
pub fn horizontal() -> Self {
Self::new().layout(Axis::Horizontal)
}
pub fn vertical() -> Self {
Self::new().layout(Axis::Vertical)
}
pub fn layout(self, layout: Axis) -> Self {
self.label_layout(layout)
}
pub fn label_layout(mut self, layout: Axis) -> Self {
self.props.layout = layout;
self
}
pub fn label_width(mut self, width: Pixels) -> Self {
self.props.label_width = Some(width);
self
}
pub fn label_text_size(mut self, size: Rems) -> Self {
self.props.label_text_size = Some(size);
self
}
pub fn child(mut self, field: impl Into<Field>) -> Self {
self.fields.push(field.into());
self
}
pub fn children(mut self, fields: impl IntoIterator<Item = Field>) -> Self {
self.fields.extend(fields);
self
}
pub fn footer(mut self, footer: impl IntoElement) -> Self {
self.footer = Some(footer.into_any_element());
self
}
pub fn columns(mut self, columns: usize) -> Self {
self.props.columns = columns;
self
}
}
impl Styled for Form {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl Sizable for Form {
fn with_size(mut self, size: impl Into<Size>) -> Self {
self.props.size = size.into();
self
}
}
impl RenderOnce for Form {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let props = self.props;
let gap = match props.size {
Size::XSmall | Size::Small => px(6.),
Size::Large => px(12.),
_ => px(8.),
};
v_flex()
.w_full()
.gap_x(gap * 3.)
.gap_y(gap)
.grid()
.grid_cols(props.columns as u16)
.children(
self.fields
.into_iter()
.enumerate()
.map(|(ix, field)| field.props(ix, props)),
)
.when_some(self.footer, |this, footer| {
this.child(
h_flex()
.col_span_full()
.min_w_0()
.justify_end()
.child(footer),
)
})
}
}
#[cfg(test)]
#[path = "tests.rs"]
mod tests;