diff --git a/crates/story/src/form_story.rs b/crates/story/src/form_story.rs index 7e49d3df..bada8625 100644 --- a/crates/story/src/form_story.rs +++ b/crates/story/src/form_story.rs @@ -26,6 +26,7 @@ pub struct FormStory { date: Entity, layout: Axis, size: Size, + column: u16, } impl super::Story for FormStory { @@ -89,6 +90,7 @@ impl FormStory { subscribe_email: false, layout: Axis::Vertical, size: Size::default(), + column: 1, } } } @@ -114,6 +116,9 @@ impl Focusable for FormStory { impl Render for FormStory { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let is_multi_column = self.column > 1; + let is_horizontal = self.layout.is_horizontal(); + v_flex() .id("form-story") .size_full() @@ -126,17 +131,34 @@ impl Render for FormStory { .flex_wrap() .justify_between() .child( - Switch::new("layout") - .checked(self.layout.is_horizontal()) - .label("Horizontal") - .on_click(cx.listener(|this, checked: &bool, _, cx| { - if *checked { - this.layout = Axis::Horizontal; - } else { - this.layout = Axis::Vertical; - } - cx.notify(); - })), + h_flex() + .gap_x_3() + .child( + Switch::new("layout") + .checked(self.layout.is_horizontal()) + .label("Horizontal") + .on_click(cx.listener(|this, checked: &bool, _, cx| { + if *checked { + this.layout = Axis::Horizontal; + } else { + this.layout = Axis::Vertical; + } + cx.notify(); + })), + ) + .child( + Switch::new("column") + .checked(self.column > 1) + .label("Multi Column") + .on_click(cx.listener(|this, checked: &bool, _, cx| { + if *checked { + this.column = 2; + } else { + this.column = 1; + } + cx.notify(); + })), + ), ) .child( ButtonGroup::new("size") @@ -174,6 +196,8 @@ impl Render for FormStory { v_form() .layout(self.layout) .with_size(self.size) + .column(self.column) + .label_width(px(if is_multi_column { 100. } else { 140. })) .child( form_field().label_fn(|_, _| "Name").child( h_flex() @@ -211,46 +235,60 @@ impl Render for FormStory { .child( form_field() .no_label_indent() + .when(is_multi_column, |this| this.col_span(2)) .child("This is a full width form field."), ) .child( form_field() .label("Please select your birthday") - .child(DatePicker::new(&self.date)) - .description("Select your birthday, we will send you a gift."), + .description("Select your birthday, we will send you a gift.") + .when(is_multi_column, |this| this.col_span(2)) + .child(DatePicker::new(&self.date)), ) .child( - form_field().child( - Switch::new("subscribe-newsletter") - .label("Subscribe our newsletter") - .checked(self.subscribe_email) - .on_click(cx.listener(|this, checked: &bool, _, cx| { - this.subscribe_email = *checked; - cx.notify(); - })), - ), + form_field() + .when(is_horizontal && is_multi_column, |this| { + this.no_label_indent() + }) + .child( + Switch::new("subscribe-newsletter") + .label("Subscribe our newsletter") + .checked(self.subscribe_email) + .on_click(cx.listener(|this, checked: &bool, _, cx| { + this.subscribe_email = *checked; + cx.notify(); + })), + ), ) .child( - form_field().child( - ColorPicker::new(&self.color_state) - .small() - .label("Theme color"), - ), + form_field() + .when(is_horizontal && is_multi_column, |this| { + this.no_label_indent() + }) + .child( + ColorPicker::new(&self.color_state) + .small() + .label("Theme color"), + ), ) .child( - form_field().child( - Checkbox::new("use-vertical-layout") - .label("Vertical layout") - .checked(self.layout.is_vertical()) - .on_click(cx.listener(|this, checked: &bool, _, cx| { - this.layout = if *checked { - Axis::Vertical - } else { - Axis::Horizontal - }; - cx.notify(); - })), - ), + form_field() + .when(is_horizontal && is_multi_column, |this| { + this.no_label_indent() + }) + .child( + Checkbox::new("use-vertical-layout") + .label("Vertical layout") + .checked(self.layout.is_vertical()) + .on_click(cx.listener(|this, checked: &bool, _, cx| { + this.layout = if *checked { + Axis::Vertical + } else { + Axis::Horizontal + }; + cx.notify(); + })), + ), ), ) } diff --git a/crates/ui/src/form.rs b/crates/ui/src/form.rs index 3d733d30..8e8977d9 100644 --- a/crates/ui/src/form.rs +++ b/crates/ui/src/form.rs @@ -37,6 +37,7 @@ struct FieldProps { layout: Axis, /// Field gap gap: Option, + column: u16, } impl Default for FieldProps { @@ -47,6 +48,7 @@ impl Default for FieldProps { layout: Axis::Vertical, size: Size::default(), gap: None, + column: 1, } } } @@ -104,6 +106,14 @@ impl Form { self.fields.extend(fields); self } + + /// Set the column count for the form. + /// + /// Default is 1. + pub fn column(mut self, column: u16) -> Self { + self.props.column = column; + self + } } impl Sizable for Form { @@ -186,6 +196,7 @@ pub struct FormField { /// Alignment of the form field. align_items: Option, props: FieldProps, + col_span: u16, } impl FormField { @@ -202,6 +213,7 @@ impl FormField { focus_handle: None, align_items: None, props: FieldProps::default(), + col_span: 1, } } @@ -302,6 +314,14 @@ impl FormField { self.align_items = Some(AlignItems::Center); self } + + /// Set the column span for the form field. + /// + /// Default is 1. + pub fn col_span(mut self, col_span: u16) -> Self { + self.col_span = col_span; + self + } } impl ParentElement for FormField { fn extend(&mut self, elements: impl IntoIterator) { @@ -351,6 +371,7 @@ impl RenderOnce for FormField { v_flex() .flex_1() .gap(gap / 2.) + .col_span(self.col_span) .child( // This warp for aligning the Label + Input wrap_div(layout) @@ -433,11 +454,17 @@ impl RenderOnce for Form { _ => px(8.), }; - v_flex().w_full().gap(gap).children( - self.fields - .into_iter() - .enumerate() - .map(|(ix, field)| field.props(ix, props)), - ) + v_flex() + .w_full() + .gap_x(gap * 3.) + .gap_y(gap) + .grid() + .grid_cols(props.column) + .children( + self.fields + .into_iter() + .enumerate() + .map(|(ix, field)| field.props(ix, props)), + ) } }