form: Rename FormField to Field. (#1539)
This commit is contained in:
parent
945db0be56
commit
a2c55dda46
5 changed files with 253 additions and 256 deletions
|
|
@ -9,7 +9,7 @@ use gpui_component::{
|
|||
color_picker::{ColorPicker, ColorPickerState},
|
||||
date_picker::{DatePicker, DatePickerState},
|
||||
divider::Divider,
|
||||
form::{form_field, v_form},
|
||||
form::{field, v_form},
|
||||
h_flex,
|
||||
input::{Input, InputState},
|
||||
select::{Select, SelectState},
|
||||
|
|
@ -189,7 +189,7 @@ impl Render for FormStory {
|
|||
.columns(self.columns)
|
||||
.label_width(px(if is_multi_column { 100. } else { 140. }))
|
||||
.child(
|
||||
form_field().label_fn(|_, _| "Name").child(
|
||||
field().label_fn(|_, _| "Name").child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.border_1()
|
||||
|
|
@ -210,13 +210,13 @@ impl Render for FormStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Email")
|
||||
.child(Input::new(&self.email_input))
|
||||
.required(true),
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Bio")
|
||||
.when(self.layout.is_vertical(), |this| this.items_start())
|
||||
.child(Input::new(&self.bio_input))
|
||||
|
|
@ -225,21 +225,21 @@ impl Render for FormStory {
|
|||
}),
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.when(is_multi_column, |this| this.col_span(2))
|
||||
.child("This is a full width form field."),
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Please select your birthday")
|
||||
.description("Select your birthday, we will send you a gift.")
|
||||
.child(DatePicker::new(&self.date)),
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.when(is_horizontal && is_multi_column, |this| {
|
||||
this.no_label_indent()
|
||||
this.label_indent(false)
|
||||
})
|
||||
.when(is_multi_column, |this| this.col_start(1))
|
||||
.child(
|
||||
|
|
@ -253,9 +253,9 @@ impl Render for FormStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.when(is_horizontal && is_multi_column, |this| {
|
||||
this.no_label_indent()
|
||||
this.label_indent(false)
|
||||
})
|
||||
.child(
|
||||
ColorPicker::new(&self.color_state)
|
||||
|
|
@ -264,9 +264,9 @@ impl Render for FormStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.when(is_horizontal && is_multi_column, |this| {
|
||||
this.no_label_indent()
|
||||
this.label_indent(false)
|
||||
})
|
||||
.child(
|
||||
Checkbox::new("use-vertical-layout")
|
||||
|
|
|
|||
|
|
@ -1,129 +1,35 @@
|
|||
use std::rc::{Rc, Weak};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, AlignItems, AnyElement, AnyView, App, Axis, Div, Element,
|
||||
ElementId, FocusHandle, InteractiveElement as _, IntoElement, ParentElement, Pixels, Rems,
|
||||
RenderOnce, SharedString, Styled, Window,
|
||||
ElementId, InteractiveElement as _, IntoElement, ParentElement, Pixels, Rems, RenderOnce,
|
||||
SharedString, Styled, Window,
|
||||
};
|
||||
|
||||
use crate::{h_flex, v_flex, ActiveTheme as _, AxisExt, Sizable, 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,
|
||||
}
|
||||
use crate::{h_flex, v_flex, ActiveTheme as _, AxisExt, Size, StyledExt};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct FieldProps {
|
||||
size: Size,
|
||||
label_width: Option<Pixels>,
|
||||
label_text_size: Option<Rems>,
|
||||
layout: Axis,
|
||||
/// Field gap
|
||||
gap: Option<Pixels>,
|
||||
columns: usize,
|
||||
pub(super) struct FieldProps {
|
||||
pub(super) size: Size,
|
||||
pub(super) layout: Axis,
|
||||
pub(super) columns: usize,
|
||||
|
||||
pub(super) label_width: Option<Pixels>,
|
||||
pub(super) label_text_size: Option<Rems>,
|
||||
}
|
||||
|
||||
impl Default for FieldProps {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
label_width: Some(px(140.)),
|
||||
label_text_size: None,
|
||||
layout: Axis::Vertical,
|
||||
size: Size::default(),
|
||||
gap: None,
|
||||
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 {
|
||||
String(SharedString),
|
||||
Element(Rc<dyn Fn(&mut Window, &mut App) -> AnyElement>),
|
||||
|
|
@ -170,38 +76,35 @@ impl From<SharedString> for FieldBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Form field element.
|
||||
#[derive(IntoElement)]
|
||||
pub struct FormField {
|
||||
pub struct Field {
|
||||
id: ElementId,
|
||||
form: Weak<Form>,
|
||||
props: FieldProps,
|
||||
label: Option<FieldBuilder>,
|
||||
no_label_indent: bool,
|
||||
focus_handle: Option<FocusHandle>,
|
||||
label_indent: bool,
|
||||
description: Option<FieldBuilder>,
|
||||
/// Used to render the actual form field, e.g.: Input, Switch...
|
||||
child: Div,
|
||||
children: Vec<AnyElement>,
|
||||
visible: bool,
|
||||
required: bool,
|
||||
/// Alignment of the form field.
|
||||
align_items: Option<AlignItems>,
|
||||
props: FieldProps,
|
||||
col_span: u16,
|
||||
col_start: Option<i16>,
|
||||
col_end: Option<i16>,
|
||||
}
|
||||
|
||||
impl FormField {
|
||||
impl Field {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
id: 0.into(),
|
||||
form: Weak::new(),
|
||||
label: None,
|
||||
description: None,
|
||||
child: div(),
|
||||
children: Vec::new(),
|
||||
visible: true,
|
||||
required: false,
|
||||
no_label_indent: false,
|
||||
focus_handle: None,
|
||||
label_indent: true,
|
||||
align_items: None,
|
||||
props: FieldProps::default(),
|
||||
col_span: 1,
|
||||
|
|
@ -216,13 +119,13 @@ impl FormField {
|
|||
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).
|
||||
///
|
||||
/// This is only work when the `label` is not set.
|
||||
pub fn no_label_indent(mut self) -> Self {
|
||||
self.no_label_indent = true;
|
||||
pub fn label_indent(mut self, indent: bool) -> Self {
|
||||
self.label_indent = indent;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -268,23 +171,10 @@ impl FormField {
|
|||
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.
|
||||
///
|
||||
/// 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.props = props;
|
||||
self
|
||||
|
|
@ -328,13 +218,14 @@ impl FormField {
|
|||
self
|
||||
}
|
||||
}
|
||||
impl ParentElement for FormField {
|
||||
|
||||
impl ParentElement for Field {
|
||||
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 {
|
||||
let layout = self.props.layout;
|
||||
|
||||
|
|
@ -343,7 +234,7 @@ impl RenderOnce for FormField {
|
|||
} else {
|
||||
self.props.label_width
|
||||
};
|
||||
let has_label = !self.no_label_indent;
|
||||
let has_label = self.label_indent;
|
||||
|
||||
#[inline]
|
||||
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())
|
||||
}
|
||||
|
||||
let gap = match self.props.gap {
|
||||
Some(v) => v,
|
||||
None => match self.props.size {
|
||||
Size::Large => px(8.),
|
||||
Size::XSmall | Size::Small => px(4.),
|
||||
_ => px(4.),
|
||||
},
|
||||
let gap = match self.props.size {
|
||||
Size::Large => px(8.),
|
||||
Size::XSmall | Size::Small => px(4.),
|
||||
_ => px(4.),
|
||||
};
|
||||
let inner_gap = if layout.is_horizontal() {
|
||||
gap
|
||||
|
|
@ -429,7 +317,7 @@ impl RenderOnce for FormField {
|
|||
.w_full()
|
||||
.flex_1()
|
||||
.overflow_x_hidden()
|
||||
.child(self.child),
|
||||
.children(self.children),
|
||||
),
|
||||
)
|
||||
.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
113
crates/ui/src/form/form.rs
Normal 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
20
crates/ui/src/form/mod.rs
Normal 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()
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ A comprehensive form component that provides structured layout for form fields w
|
|||
## Import
|
||||
|
||||
```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
|
||||
|
|
@ -20,12 +20,12 @@ use gpui_component::form::{form_field, v_form, h_form, Form, FormField};
|
|||
```rust
|
||||
v_form()
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Name")
|
||||
.child(Input::new(&name_input))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Email")
|
||||
.child(Input::new(&email_input))
|
||||
.required(true)
|
||||
|
|
@ -38,12 +38,12 @@ v_form()
|
|||
h_form()
|
||||
.label_width(px(120.))
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("First Name")
|
||||
.child(Input::new(&first_name))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Last Name")
|
||||
.child(Input::new(&last_name))
|
||||
)
|
||||
|
|
@ -55,17 +55,17 @@ h_form()
|
|||
v_form()
|
||||
.columns(2) // Two-column layout
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("First Name")
|
||||
.child(Input::new(&first_name))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Last Name")
|
||||
.child(Input::new(&last_name))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Bio")
|
||||
.col_span(2) // Span across both columns
|
||||
.child(Input::new(&bio_input))
|
||||
|
|
@ -79,8 +79,8 @@ v_form()
|
|||
```rust
|
||||
v_form()
|
||||
.gap(px(12.))
|
||||
.child(form_field().label("Name").child(input))
|
||||
.child(form_field().label("Email").child(email_input))
|
||||
.child(field().label("Name").child(input))
|
||||
.child(field().label("Email").child(email_input))
|
||||
```
|
||||
|
||||
### Horizontal Layout
|
||||
|
|
@ -88,8 +88,8 @@ v_form()
|
|||
```rust
|
||||
h_form()
|
||||
.label_width(px(100.))
|
||||
.child(form_field().label("Name").child(input))
|
||||
.child(form_field().label("Email").child(email_input))
|
||||
.child(field().label("Name").child(input))
|
||||
.child(field().label("Email").child(email_input))
|
||||
```
|
||||
|
||||
### Custom Sizing
|
||||
|
|
@ -98,11 +98,11 @@ h_form()
|
|||
v_form()
|
||||
.large() // Large form size
|
||||
.label_text_size(rems(1.2))
|
||||
.child(form_field().label("Title").child(input))
|
||||
.child(field().label("Title").child(input))
|
||||
|
||||
v_form()
|
||||
.small() // Small form size
|
||||
.child(form_field().label("Code").child(input))
|
||||
.child(field().label("Code").child(input))
|
||||
```
|
||||
|
||||
## Form Validation
|
||||
|
|
@ -110,7 +110,7 @@ v_form()
|
|||
### Required Fields
|
||||
|
||||
```rust
|
||||
form_field()
|
||||
field()
|
||||
.label("Email")
|
||||
.required(true) // Shows asterisk (*) next to label
|
||||
.child(Input::new(&email_input))
|
||||
|
|
@ -119,7 +119,7 @@ form_field()
|
|||
### Field Descriptions
|
||||
|
||||
```rust
|
||||
form_field()
|
||||
field()
|
||||
.label("Password")
|
||||
.description("Must be at least 8 characters long")
|
||||
.child(Input::new(&password_input))
|
||||
|
|
@ -128,7 +128,7 @@ form_field()
|
|||
### Dynamic Descriptions
|
||||
|
||||
```rust
|
||||
form_field()
|
||||
field()
|
||||
.label("Bio")
|
||||
.description_fn(|_, _| {
|
||||
div().child("Use at most 100 words to describe yourself.")
|
||||
|
|
@ -139,7 +139,7 @@ form_field()
|
|||
### Field Visibility
|
||||
|
||||
```rust
|
||||
form_field()
|
||||
field()
|
||||
.label("Admin Settings")
|
||||
.visible(user.is_admin()) // Conditionally show field
|
||||
.child(Switch::new("admin-mode"))
|
||||
|
|
@ -173,11 +173,11 @@ impl FormView {
|
|||
|
||||
// Form with submit button
|
||||
v_form()
|
||||
.child(form_field().label("Name").child(Input::new(&self.name_input)))
|
||||
.child(form_field().label("Email").child(Input::new(&self.email_input)))
|
||||
.child(field().label("Name").child(Input::new(&self.name_input)))
|
||||
.child(field().label("Email").child(Input::new(&self.email_input)))
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.child(
|
||||
Button::new("submit")
|
||||
.primary()
|
||||
|
|
@ -191,11 +191,11 @@ v_form()
|
|||
|
||||
```rust
|
||||
v_form()
|
||||
.child(form_field().label("Title").child(Input::new(&title)))
|
||||
.child(form_field().label("Content").child(Input::new(&content)))
|
||||
.child(field().label("Title").child(Input::new(&title)))
|
||||
.child(field().label("Content").child(Input::new(&content)))
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
|
|
@ -213,7 +213,7 @@ v_form()
|
|||
```rust
|
||||
v_form()
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Name")
|
||||
.child(
|
||||
h_flex()
|
||||
|
|
@ -223,7 +223,7 @@ v_form()
|
|||
)
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Address")
|
||||
.items_start() // Align to start for multi-line content
|
||||
.child(
|
||||
|
|
@ -243,16 +243,16 @@ v_form()
|
|||
### Custom Field Components
|
||||
|
||||
```rust
|
||||
form_field()
|
||||
field()
|
||||
.label("Theme Color")
|
||||
.child(ColorPicker::new(&color_state).small())
|
||||
|
||||
form_field()
|
||||
field()
|
||||
.label("Birth Date")
|
||||
.description("We'll send you a birthday gift!")
|
||||
.child(DatePicker::new(&date_state))
|
||||
|
||||
form_field()
|
||||
field()
|
||||
.label("Notifications")
|
||||
.child(
|
||||
v_flex()
|
||||
|
|
@ -268,18 +268,18 @@ form_field()
|
|||
```rust
|
||||
v_form()
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Account Type")
|
||||
.child(Select::new(&account_type))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Company Name")
|
||||
.visible(is_business_account) // Show only for business accounts
|
||||
.child(Input::new(&company_name))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Tax ID")
|
||||
.visible(is_business_account)
|
||||
.required(is_business_account)
|
||||
|
|
@ -294,11 +294,11 @@ v_form()
|
|||
```rust
|
||||
v_form()
|
||||
.columns(3) // Three-column grid
|
||||
.child(form_field().label("First").child(input1))
|
||||
.child(form_field().label("Second").child(input2))
|
||||
.child(form_field().label("Third").child(input3))
|
||||
.child(field().label("First").child(input1))
|
||||
.child(field().label("Second").child(input2))
|
||||
.child(field().label("Third").child(input3))
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Full Width")
|
||||
.col_span(3) // Spans all three columns
|
||||
.child(Input::new(&full_width))
|
||||
|
|
@ -310,10 +310,10 @@ v_form()
|
|||
```rust
|
||||
v_form()
|
||||
.columns(4)
|
||||
.child(form_field().label("A").child(input_a))
|
||||
.child(form_field().label("B").child(input_b))
|
||||
.child(field().label("A").child(input_a))
|
||||
.child(field().label("B").child(input_b))
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Positioned")
|
||||
.col_start(1) // Start at column 1
|
||||
.col_span(2) // Span 2 columns
|
||||
|
|
@ -326,10 +326,10 @@ v_form()
|
|||
```rust
|
||||
v_form()
|
||||
.columns(if is_mobile { 1 } else { 2 })
|
||||
.child(form_field().label("Name").child(name_input))
|
||||
.child(form_field().label("Email").child(email_input))
|
||||
.child(field().label("Name").child(name_input))
|
||||
.child(field().label("Email").child(email_input))
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Bio")
|
||||
.when(!is_mobile, |field| field.col_span(2))
|
||||
.child(bio_input)
|
||||
|
|
@ -355,9 +355,9 @@ impl Render for RegistrationForm {
|
|||
v_form()
|
||||
.large()
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Personal Information")
|
||||
.no_label_indent()
|
||||
.label_indent(false)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_3()
|
||||
|
|
@ -376,27 +376,27 @@ impl Render for RegistrationForm {
|
|||
)
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Email")
|
||||
.required(true)
|
||||
.child(Input::new(&self.email))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Password")
|
||||
.required(true)
|
||||
.description("Must be at least 8 characters")
|
||||
.child(Input::new(&self.password))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Confirm Password")
|
||||
.required(true)
|
||||
.child(Input::new(&self.confirm_password))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.child(
|
||||
Checkbox::new("terms")
|
||||
.label("I agree to the Terms of Service")
|
||||
|
|
@ -408,8 +408,8 @@ impl Render for RegistrationForm {
|
|||
)
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.child(
|
||||
Button::new("register")
|
||||
.primary()
|
||||
|
|
@ -428,54 +428,54 @@ impl Render for RegistrationForm {
|
|||
v_form()
|
||||
.column(2)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Profile")
|
||||
.no_label_indent()
|
||||
.label_indent(false)
|
||||
.col_span(2)
|
||||
.child(Divider::horizontal())
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Display Name")
|
||||
.child(Input::new(&display_name))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Email")
|
||||
.child(Input::new(&email))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Bio")
|
||||
.col_span(2)
|
||||
.items_start()
|
||||
.child(Input::new(&bio))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Preferences")
|
||||
.no_label_indent()
|
||||
.label_indent(false)
|
||||
.col_span(2)
|
||||
.child(Divider::horizontal())
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Theme")
|
||||
.child(Select::new(&theme_state))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Language")
|
||||
.child(Select::new(&language_state))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.child(Switch::new("notifications").label("Enable notifications"))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.child(Switch::new("marketing").label("Marketing emails"))
|
||||
)
|
||||
```
|
||||
|
|
@ -485,7 +485,7 @@ v_form()
|
|||
```rust
|
||||
v_form()
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Contact Information")
|
||||
.child(
|
||||
h_flex()
|
||||
|
|
@ -503,18 +503,18 @@ v_form()
|
|||
)
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Email")
|
||||
.required(true)
|
||||
.child(Input::new(&email_input))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Subject")
|
||||
.child(Select::new(&subject_state))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
field()
|
||||
.label("Message")
|
||||
.required(true)
|
||||
.items_start()
|
||||
|
|
@ -522,8 +522,8 @@ v_form()
|
|||
.child(Input::new(&message_input))
|
||||
)
|
||||
.child(
|
||||
form_field()
|
||||
.no_label_indent()
|
||||
field()
|
||||
.label_indent(false)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
|
|
|
|||
Loading…
Reference in a new issue