form: Rename FormField to Field. (#1539)

This commit is contained in:
Jason Lee 2025-11-07 18:43:01 +08:00 committed by GitHub
parent 945db0be56
commit a2c55dda46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 253 additions and 256 deletions

View file

@ -9,7 +9,7 @@ use gpui_component::{
color_picker::{ColorPicker, ColorPickerState}, color_picker::{ColorPicker, ColorPickerState},
date_picker::{DatePicker, DatePickerState}, date_picker::{DatePicker, DatePickerState},
divider::Divider, divider::Divider,
form::{form_field, v_form}, form::{field, v_form},
h_flex, h_flex,
input::{Input, InputState}, input::{Input, InputState},
select::{Select, SelectState}, select::{Select, SelectState},
@ -189,7 +189,7 @@ impl Render for FormStory {
.columns(self.columns) .columns(self.columns)
.label_width(px(if is_multi_column { 100. } else { 140. })) .label_width(px(if is_multi_column { 100. } else { 140. }))
.child( .child(
form_field().label_fn(|_, _| "Name").child( field().label_fn(|_, _| "Name").child(
h_flex() h_flex()
.gap_2() .gap_2()
.border_1() .border_1()
@ -210,13 +210,13 @@ impl Render for FormStory {
), ),
) )
.child( .child(
form_field() field()
.label("Email") .label("Email")
.child(Input::new(&self.email_input)) .child(Input::new(&self.email_input))
.required(true), .required(true),
) )
.child( .child(
form_field() field()
.label("Bio") .label("Bio")
.when(self.layout.is_vertical(), |this| this.items_start()) .when(self.layout.is_vertical(), |this| this.items_start())
.child(Input::new(&self.bio_input)) .child(Input::new(&self.bio_input))
@ -225,21 +225,21 @@ impl Render for FormStory {
}), }),
) )
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.when(is_multi_column, |this| this.col_span(2)) .when(is_multi_column, |this| this.col_span(2))
.child("This is a full width form field."), .child("This is a full width form field."),
) )
.child( .child(
form_field() field()
.label("Please select your birthday") .label("Please select your birthday")
.description("Select your birthday, we will send you a gift.") .description("Select your birthday, we will send you a gift.")
.child(DatePicker::new(&self.date)), .child(DatePicker::new(&self.date)),
) )
.child( .child(
form_field() field()
.when(is_horizontal && is_multi_column, |this| { .when(is_horizontal && is_multi_column, |this| {
this.no_label_indent() this.label_indent(false)
}) })
.when(is_multi_column, |this| this.col_start(1)) .when(is_multi_column, |this| this.col_start(1))
.child( .child(
@ -253,9 +253,9 @@ impl Render for FormStory {
), ),
) )
.child( .child(
form_field() field()
.when(is_horizontal && is_multi_column, |this| { .when(is_horizontal && is_multi_column, |this| {
this.no_label_indent() this.label_indent(false)
}) })
.child( .child(
ColorPicker::new(&self.color_state) ColorPicker::new(&self.color_state)
@ -264,9 +264,9 @@ impl Render for FormStory {
), ),
) )
.child( .child(
form_field() field()
.when(is_horizontal && is_multi_column, |this| { .when(is_horizontal && is_multi_column, |this| {
this.no_label_indent() this.label_indent(false)
}) })
.child( .child(
Checkbox::new("use-vertical-layout") Checkbox::new("use-vertical-layout")

View file

@ -1,129 +1,35 @@
use std::rc::{Rc, Weak}; use std::rc::Rc;
use gpui::{ use gpui::{
div, prelude::FluentBuilder as _, px, AlignItems, AnyElement, AnyView, App, Axis, Div, Element, div, prelude::FluentBuilder as _, px, AlignItems, AnyElement, AnyView, App, Axis, Div, Element,
ElementId, FocusHandle, InteractiveElement as _, IntoElement, ParentElement, Pixels, Rems, ElementId, InteractiveElement as _, IntoElement, ParentElement, Pixels, Rems, RenderOnce,
RenderOnce, SharedString, Styled, Window, SharedString, Styled, Window,
}; };
use crate::{h_flex, v_flex, ActiveTheme as _, AxisExt, Sizable, Size, StyledExt}; use crate::{h_flex, v_flex, ActiveTheme as _, AxisExt, Size, StyledExt};
/// Create a new form with a vertical layout.
pub fn v_form() -> Form {
Form::vertical()
}
/// Create a new form with a horizontal layout.
pub fn h_form() -> Form {
Form::horizontal()
}
/// Create a new form field.
pub fn form_field() -> FormField {
FormField::new()
}
/// A form element that contains multiple form fields.
#[derive(IntoElement)]
pub struct Form {
fields: Vec<FormField>,
props: FieldProps,
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct FieldProps { pub(super) struct FieldProps {
size: Size, pub(super) size: Size,
label_width: Option<Pixels>, pub(super) layout: Axis,
label_text_size: Option<Rems>, pub(super) columns: usize,
layout: Axis,
/// Field gap pub(super) label_width: Option<Pixels>,
gap: Option<Pixels>, pub(super) label_text_size: Option<Rems>,
columns: usize,
} }
impl Default for FieldProps { impl Default for FieldProps {
fn default() -> Self { fn default() -> Self {
Self { Self {
label_width: Some(px(140.)),
label_text_size: None,
layout: Axis::Vertical, layout: Axis::Vertical,
size: Size::default(), size: Size::default(),
gap: None,
columns: 1, columns: 1,
label_width: Some(px(140.)),
label_text_size: None,
} }
} }
} }
impl Form {
fn new() -> Self {
Self {
props: FieldProps::default(),
fields: Vec::new(),
}
}
/// Creates a new form with a horizontal layout.
pub fn horizontal() -> Self {
Self::new().layout(Axis::Horizontal)
}
/// Creates a new form with a vertical layout.
pub fn vertical() -> Self {
Self::new().layout(Axis::Vertical)
}
/// Set the layout for the form, default is `Axis::Vertical`.
pub fn layout(mut self, layout: Axis) -> Self {
self.props.layout = layout;
self
}
/// Set the width of the labels in the form. Default is `px(100.)`.
pub fn label_width(mut self, width: Pixels) -> Self {
self.props.label_width = Some(width);
self
}
/// Set the text size of the labels in the form. Default is `None`.
pub fn label_text_size(mut self, size: Rems) -> Self {
self.props.label_text_size = Some(size);
self
}
/// Set the gap between the form fields.
pub fn gap(mut self, gap: Pixels) -> Self {
self.props.gap = Some(gap);
self
}
/// Add a child to the form.
pub fn child(mut self, field: impl Into<FormField>) -> Self {
self.fields.push(field.into());
self
}
/// Add multiple children to the form.
pub fn children(mut self, fields: impl IntoIterator<Item = FormField>) -> Self {
self.fields.extend(fields);
self
}
/// Set the column count for the form.
///
/// Default is 1.
pub fn columns(mut self, columns: usize) -> Self {
self.props.columns = columns;
self
}
}
impl Sizable for Form {
fn with_size(mut self, size: impl Into<Size>) -> Self {
self.props.size = size.into();
self
}
}
pub enum FieldBuilder { pub enum FieldBuilder {
String(SharedString), String(SharedString),
Element(Rc<dyn Fn(&mut Window, &mut App) -> AnyElement>), Element(Rc<dyn Fn(&mut Window, &mut App) -> AnyElement>),
@ -170,38 +76,35 @@ impl From<SharedString> for FieldBuilder {
} }
} }
/// Form field element.
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct FormField { pub struct Field {
id: ElementId, id: ElementId,
form: Weak<Form>, props: FieldProps,
label: Option<FieldBuilder>, label: Option<FieldBuilder>,
no_label_indent: bool, label_indent: bool,
focus_handle: Option<FocusHandle>,
description: Option<FieldBuilder>, description: Option<FieldBuilder>,
/// Used to render the actual form field, e.g.: Input, Switch... /// Used to render the actual form field, e.g.: Input, Switch...
child: Div, children: Vec<AnyElement>,
visible: bool, visible: bool,
required: bool, required: bool,
/// Alignment of the form field. /// Alignment of the form field.
align_items: Option<AlignItems>, align_items: Option<AlignItems>,
props: FieldProps,
col_span: u16, col_span: u16,
col_start: Option<i16>, col_start: Option<i16>,
col_end: Option<i16>, col_end: Option<i16>,
} }
impl FormField { impl Field {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
id: 0.into(), id: 0.into(),
form: Weak::new(),
label: None, label: None,
description: None, description: None,
child: div(), children: Vec::new(),
visible: true, visible: true,
required: false, required: false,
no_label_indent: false, label_indent: true,
focus_handle: None,
align_items: None, align_items: None,
props: FieldProps::default(), props: FieldProps::default(),
col_span: 1, col_span: 1,
@ -216,13 +119,13 @@ impl FormField {
self self
} }
/// Sets not indent with the label width (in Horizontal layout). /// Sets indent with the label width (in Horizontal layout), default is `true`.
/// ///
/// Sometimes you want to align the input form left (Default is align after the label width in Horizontal layout). /// Sometimes you want to align the input form left (Default is align after the label width in Horizontal layout).
/// ///
/// This is only work when the `label` is not set. /// This is only work when the `label` is not set.
pub fn no_label_indent(mut self) -> Self { pub fn label_indent(mut self, indent: bool) -> Self {
self.no_label_indent = true; self.label_indent = indent;
self self
} }
@ -268,23 +171,10 @@ impl FormField {
self self
} }
/// Set the focus handle for the form field.
///
/// If not set, the form field will not be focusable.
pub fn track_focus(mut self, focus_handle: &FocusHandle) -> Self {
self.focus_handle = Some(focus_handle.clone());
self
}
pub fn parent(mut self, form: &Rc<Form>) -> Self {
self.form = Rc::downgrade(form);
self
}
/// Set the properties for the form field. /// Set the properties for the form field.
/// ///
/// This is internal API for sync props from From. /// This is internal API for sync props from From.
fn props(mut self, ix: usize, props: FieldProps) -> Self { pub(super) fn props(mut self, ix: usize, props: FieldProps) -> Self {
self.id = ix.into(); self.id = ix.into();
self.props = props; self.props = props;
self self
@ -328,13 +218,14 @@ impl FormField {
self self
} }
} }
impl ParentElement for FormField {
impl ParentElement for Field {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) { fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.child.extend(elements); self.children.extend(elements);
} }
} }
impl RenderOnce for FormField { impl RenderOnce for Field {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let layout = self.props.layout; let layout = self.props.layout;
@ -343,7 +234,7 @@ impl RenderOnce for FormField {
} else { } else {
self.props.label_width self.props.label_width
}; };
let has_label = !self.no_label_indent; let has_label = self.label_indent;
#[inline] #[inline]
fn wrap_div(layout: Axis) -> Div { fn wrap_div(layout: Axis) -> Div {
@ -359,13 +250,10 @@ impl RenderOnce for FormField {
div().when_some(label_width, |this, width| this.w(width).flex_shrink_0()) div().when_some(label_width, |this, width| this.w(width).flex_shrink_0())
} }
let gap = match self.props.gap { let gap = match self.props.size {
Some(v) => v, Size::Large => px(8.),
None => match self.props.size { Size::XSmall | Size::Small => px(4.),
Size::Large => px(8.), _ => px(4.),
Size::XSmall | Size::Small => px(4.),
_ => px(4.),
},
}; };
let inner_gap = if layout.is_horizontal() { let inner_gap = if layout.is_horizontal() {
gap gap
@ -429,7 +317,7 @@ impl RenderOnce for FormField {
.w_full() .w_full()
.flex_1() .flex_1()
.overflow_x_hidden() .overflow_x_hidden()
.child(self.child), .children(self.children),
), ),
) )
.child( .child(
@ -453,27 +341,3 @@ impl RenderOnce for FormField {
) )
} }
} }
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)),
)
}
}

113
crates/ui/src/form/form.rs Normal file
View file

@ -0,0 +1,113 @@
use gpui::{
px, App, Axis, IntoElement, ParentElement, Pixels, Rems, RenderOnce, StyleRefinement, Styled,
Window,
};
use crate::{
form::{Field, FieldProps},
v_flex, Sizable, Size,
};
/// A form element that contains multiple form fields.
#[derive(IntoElement)]
pub struct Form {
style: StyleRefinement,
fields: Vec<Field>,
props: FieldProps,
}
impl Form {
fn new() -> Self {
Self {
style: StyleRefinement::default(),
props: FieldProps::default(),
fields: Vec::new(),
}
}
/// Creates a new form with a horizontal layout.
pub fn horizontal() -> Self {
Self::new().layout(Axis::Horizontal)
}
/// Creates a new form with a vertical layout.
pub fn vertical() -> Self {
Self::new().layout(Axis::Vertical)
}
/// Set the layout for the form, default is `Axis::Vertical`.
pub fn layout(mut self, layout: Axis) -> Self {
self.props.layout = layout;
self
}
/// Set the width of the labels in the form. Default is `px(100.)`.
pub fn label_width(mut self, width: Pixels) -> Self {
self.props.label_width = Some(width);
self
}
/// Set the text size of the labels in the form. Default is `None`.
pub fn label_text_size(mut self, size: Rems) -> Self {
self.props.label_text_size = Some(size);
self
}
/// Add a child to the form.
pub fn child(mut self, field: impl Into<Field>) -> Self {
self.fields.push(field.into());
self
}
/// Add multiple children to the form.
pub fn children(mut self, fields: impl IntoIterator<Item = Field>) -> Self {
self.fields.extend(fields);
self
}
/// Set the column count for the form.
///
/// Default is 1.
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)),
)
}
}

20
crates/ui/src/form/mod.rs Normal file
View file

@ -0,0 +1,20 @@
mod field;
mod form;
pub use field::*;
pub use form::*;
/// Create a new [`Form`] with a vertical layout.
pub fn v_form() -> Form {
Form::vertical()
}
/// Create a new [`Form`] with a horizontal layout.
pub fn h_form() -> Form {
Form::horizontal()
}
/// Create a new [`Field`].
pub fn field() -> Field {
Field::new()
}

View file

@ -10,7 +10,7 @@ A comprehensive form component that provides structured layout for form fields w
## Import ## Import
```rust ```rust
use gpui_component::form::{form_field, v_form, h_form, Form, FormField}; use gpui_component::form::{field, v_form, h_form, Form, Field};
``` ```
## Usage ## Usage
@ -20,12 +20,12 @@ use gpui_component::form::{form_field, v_form, h_form, Form, FormField};
```rust ```rust
v_form() v_form()
.child( .child(
form_field() field()
.label("Name") .label("Name")
.child(Input::new(&name_input)) .child(Input::new(&name_input))
) )
.child( .child(
form_field() field()
.label("Email") .label("Email")
.child(Input::new(&email_input)) .child(Input::new(&email_input))
.required(true) .required(true)
@ -38,12 +38,12 @@ v_form()
h_form() h_form()
.label_width(px(120.)) .label_width(px(120.))
.child( .child(
form_field() field()
.label("First Name") .label("First Name")
.child(Input::new(&first_name)) .child(Input::new(&first_name))
) )
.child( .child(
form_field() field()
.label("Last Name") .label("Last Name")
.child(Input::new(&last_name)) .child(Input::new(&last_name))
) )
@ -55,17 +55,17 @@ h_form()
v_form() v_form()
.columns(2) // Two-column layout .columns(2) // Two-column layout
.child( .child(
form_field() field()
.label("First Name") .label("First Name")
.child(Input::new(&first_name)) .child(Input::new(&first_name))
) )
.child( .child(
form_field() field()
.label("Last Name") .label("Last Name")
.child(Input::new(&last_name)) .child(Input::new(&last_name))
) )
.child( .child(
form_field() field()
.label("Bio") .label("Bio")
.col_span(2) // Span across both columns .col_span(2) // Span across both columns
.child(Input::new(&bio_input)) .child(Input::new(&bio_input))
@ -79,8 +79,8 @@ v_form()
```rust ```rust
v_form() v_form()
.gap(px(12.)) .gap(px(12.))
.child(form_field().label("Name").child(input)) .child(field().label("Name").child(input))
.child(form_field().label("Email").child(email_input)) .child(field().label("Email").child(email_input))
``` ```
### Horizontal Layout ### Horizontal Layout
@ -88,8 +88,8 @@ v_form()
```rust ```rust
h_form() h_form()
.label_width(px(100.)) .label_width(px(100.))
.child(form_field().label("Name").child(input)) .child(field().label("Name").child(input))
.child(form_field().label("Email").child(email_input)) .child(field().label("Email").child(email_input))
``` ```
### Custom Sizing ### Custom Sizing
@ -98,11 +98,11 @@ h_form()
v_form() v_form()
.large() // Large form size .large() // Large form size
.label_text_size(rems(1.2)) .label_text_size(rems(1.2))
.child(form_field().label("Title").child(input)) .child(field().label("Title").child(input))
v_form() v_form()
.small() // Small form size .small() // Small form size
.child(form_field().label("Code").child(input)) .child(field().label("Code").child(input))
``` ```
## Form Validation ## Form Validation
@ -110,7 +110,7 @@ v_form()
### Required Fields ### Required Fields
```rust ```rust
form_field() field()
.label("Email") .label("Email")
.required(true) // Shows asterisk (*) next to label .required(true) // Shows asterisk (*) next to label
.child(Input::new(&email_input)) .child(Input::new(&email_input))
@ -119,7 +119,7 @@ form_field()
### Field Descriptions ### Field Descriptions
```rust ```rust
form_field() field()
.label("Password") .label("Password")
.description("Must be at least 8 characters long") .description("Must be at least 8 characters long")
.child(Input::new(&password_input)) .child(Input::new(&password_input))
@ -128,7 +128,7 @@ form_field()
### Dynamic Descriptions ### Dynamic Descriptions
```rust ```rust
form_field() field()
.label("Bio") .label("Bio")
.description_fn(|_, _| { .description_fn(|_, _| {
div().child("Use at most 100 words to describe yourself.") div().child("Use at most 100 words to describe yourself.")
@ -139,7 +139,7 @@ form_field()
### Field Visibility ### Field Visibility
```rust ```rust
form_field() field()
.label("Admin Settings") .label("Admin Settings")
.visible(user.is_admin()) // Conditionally show field .visible(user.is_admin()) // Conditionally show field
.child(Switch::new("admin-mode")) .child(Switch::new("admin-mode"))
@ -173,11 +173,11 @@ impl FormView {
// Form with submit button // Form with submit button
v_form() v_form()
.child(form_field().label("Name").child(Input::new(&self.name_input))) .child(field().label("Name").child(Input::new(&self.name_input)))
.child(form_field().label("Email").child(Input::new(&self.email_input))) .child(field().label("Email").child(Input::new(&self.email_input)))
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.child( .child(
Button::new("submit") Button::new("submit")
.primary() .primary()
@ -191,11 +191,11 @@ v_form()
```rust ```rust
v_form() v_form()
.child(form_field().label("Title").child(Input::new(&title))) .child(field().label("Title").child(Input::new(&title)))
.child(form_field().label("Content").child(Input::new(&content))) .child(field().label("Content").child(Input::new(&content)))
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.child( .child(
h_flex() h_flex()
.gap_2() .gap_2()
@ -213,7 +213,7 @@ v_form()
```rust ```rust
v_form() v_form()
.child( .child(
form_field() field()
.label("Name") .label("Name")
.child( .child(
h_flex() h_flex()
@ -223,7 +223,7 @@ v_form()
) )
) )
.child( .child(
form_field() field()
.label("Address") .label("Address")
.items_start() // Align to start for multi-line content .items_start() // Align to start for multi-line content
.child( .child(
@ -243,16 +243,16 @@ v_form()
### Custom Field Components ### Custom Field Components
```rust ```rust
form_field() field()
.label("Theme Color") .label("Theme Color")
.child(ColorPicker::new(&color_state).small()) .child(ColorPicker::new(&color_state).small())
form_field() field()
.label("Birth Date") .label("Birth Date")
.description("We'll send you a birthday gift!") .description("We'll send you a birthday gift!")
.child(DatePicker::new(&date_state)) .child(DatePicker::new(&date_state))
form_field() field()
.label("Notifications") .label("Notifications")
.child( .child(
v_flex() v_flex()
@ -268,18 +268,18 @@ form_field()
```rust ```rust
v_form() v_form()
.child( .child(
form_field() field()
.label("Account Type") .label("Account Type")
.child(Select::new(&account_type)) .child(Select::new(&account_type))
) )
.child( .child(
form_field() field()
.label("Company Name") .label("Company Name")
.visible(is_business_account) // Show only for business accounts .visible(is_business_account) // Show only for business accounts
.child(Input::new(&company_name)) .child(Input::new(&company_name))
) )
.child( .child(
form_field() field()
.label("Tax ID") .label("Tax ID")
.visible(is_business_account) .visible(is_business_account)
.required(is_business_account) .required(is_business_account)
@ -294,11 +294,11 @@ v_form()
```rust ```rust
v_form() v_form()
.columns(3) // Three-column grid .columns(3) // Three-column grid
.child(form_field().label("First").child(input1)) .child(field().label("First").child(input1))
.child(form_field().label("Second").child(input2)) .child(field().label("Second").child(input2))
.child(form_field().label("Third").child(input3)) .child(field().label("Third").child(input3))
.child( .child(
form_field() field()
.label("Full Width") .label("Full Width")
.col_span(3) // Spans all three columns .col_span(3) // Spans all three columns
.child(Input::new(&full_width)) .child(Input::new(&full_width))
@ -310,10 +310,10 @@ v_form()
```rust ```rust
v_form() v_form()
.columns(4) .columns(4)
.child(form_field().label("A").child(input_a)) .child(field().label("A").child(input_a))
.child(form_field().label("B").child(input_b)) .child(field().label("B").child(input_b))
.child( .child(
form_field() field()
.label("Positioned") .label("Positioned")
.col_start(1) // Start at column 1 .col_start(1) // Start at column 1
.col_span(2) // Span 2 columns .col_span(2) // Span 2 columns
@ -326,10 +326,10 @@ v_form()
```rust ```rust
v_form() v_form()
.columns(if is_mobile { 1 } else { 2 }) .columns(if is_mobile { 1 } else { 2 })
.child(form_field().label("Name").child(name_input)) .child(field().label("Name").child(name_input))
.child(form_field().label("Email").child(email_input)) .child(field().label("Email").child(email_input))
.child( .child(
form_field() field()
.label("Bio") .label("Bio")
.when(!is_mobile, |field| field.col_span(2)) .when(!is_mobile, |field| field.col_span(2))
.child(bio_input) .child(bio_input)
@ -355,9 +355,9 @@ impl Render for RegistrationForm {
v_form() v_form()
.large() .large()
.child( .child(
form_field() field()
.label("Personal Information") .label("Personal Information")
.no_label_indent() .label_indent(false)
.child( .child(
h_flex() h_flex()
.gap_3() .gap_3()
@ -376,27 +376,27 @@ impl Render for RegistrationForm {
) )
) )
.child( .child(
form_field() field()
.label("Email") .label("Email")
.required(true) .required(true)
.child(Input::new(&self.email)) .child(Input::new(&self.email))
) )
.child( .child(
form_field() field()
.label("Password") .label("Password")
.required(true) .required(true)
.description("Must be at least 8 characters") .description("Must be at least 8 characters")
.child(Input::new(&self.password)) .child(Input::new(&self.password))
) )
.child( .child(
form_field() field()
.label("Confirm Password") .label("Confirm Password")
.required(true) .required(true)
.child(Input::new(&self.confirm_password)) .child(Input::new(&self.confirm_password))
) )
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.child( .child(
Checkbox::new("terms") Checkbox::new("terms")
.label("I agree to the Terms of Service") .label("I agree to the Terms of Service")
@ -408,8 +408,8 @@ impl Render for RegistrationForm {
) )
) )
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.child( .child(
Button::new("register") Button::new("register")
.primary() .primary()
@ -428,54 +428,54 @@ impl Render for RegistrationForm {
v_form() v_form()
.column(2) .column(2)
.child( .child(
form_field() field()
.label("Profile") .label("Profile")
.no_label_indent() .label_indent(false)
.col_span(2) .col_span(2)
.child(Divider::horizontal()) .child(Divider::horizontal())
) )
.child( .child(
form_field() field()
.label("Display Name") .label("Display Name")
.child(Input::new(&display_name)) .child(Input::new(&display_name))
) )
.child( .child(
form_field() field()
.label("Email") .label("Email")
.child(Input::new(&email)) .child(Input::new(&email))
) )
.child( .child(
form_field() field()
.label("Bio") .label("Bio")
.col_span(2) .col_span(2)
.items_start() .items_start()
.child(Input::new(&bio)) .child(Input::new(&bio))
) )
.child( .child(
form_field() field()
.label("Preferences") .label("Preferences")
.no_label_indent() .label_indent(false)
.col_span(2) .col_span(2)
.child(Divider::horizontal()) .child(Divider::horizontal())
) )
.child( .child(
form_field() field()
.label("Theme") .label("Theme")
.child(Select::new(&theme_state)) .child(Select::new(&theme_state))
) )
.child( .child(
form_field() field()
.label("Language") .label("Language")
.child(Select::new(&language_state)) .child(Select::new(&language_state))
) )
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.child(Switch::new("notifications").label("Enable notifications")) .child(Switch::new("notifications").label("Enable notifications"))
) )
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.child(Switch::new("marketing").label("Marketing emails")) .child(Switch::new("marketing").label("Marketing emails"))
) )
``` ```
@ -485,7 +485,7 @@ v_form()
```rust ```rust
v_form() v_form()
.child( .child(
form_field() field()
.label("Contact Information") .label("Contact Information")
.child( .child(
h_flex() h_flex()
@ -503,18 +503,18 @@ v_form()
) )
) )
.child( .child(
form_field() field()
.label("Email") .label("Email")
.required(true) .required(true)
.child(Input::new(&email_input)) .child(Input::new(&email_input))
) )
.child( .child(
form_field() field()
.label("Subject") .label("Subject")
.child(Select::new(&subject_state)) .child(Select::new(&subject_state))
) )
.child( .child(
form_field() field()
.label("Message") .label("Message")
.required(true) .required(true)
.items_start() .items_start()
@ -522,8 +522,8 @@ v_form()
.child(Input::new(&message_input)) .child(Input::new(&message_input))
) )
.child( .child(
form_field() field()
.no_label_indent() .label_indent(false)
.child( .child(
h_flex() h_flex()
.gap_2() .gap_2()