Form

Form 负责字段和底部操作区的布局。应用负责字段值、校验、提交,以及根据可用宽度选择列数。

#导入

use gpui_kit::component::form::{field, v_form, h_form, Form, Field};

#组合约定

Form::new() 默认使用单列,标签位于控件上方。label_layout(Axis::Horizontal) 将标签放到控件旁边;columns(2) 独立控制字段排列为两列。原有的 horizontal()vertical()layout(Axis)h_form()v_form() 继续可用。

Form::new()
    .label_layout(Axis::Horizontal)
    .columns(2)
    .child(Field::new().label("Name").child(Input::new(&name_input)))
    .child(Field::new().label("Email").child(Input::new(&email_input)))
    .footer(Button::new("save").label("Save"))

child 接收 Field。操作按钮放入 footer;底部操作区跨越所有列,内容靠末端对齐。应用为按钮绑定提交逻辑,Form 不会自动提交。完整的状态、回调和窗口初始化用法见应用示例

#用法

#基础表单

v_form()
    .child(
        field()
            .label("Name")
            .child(Input::new(&name_input))
    )
    .child(
        field()
            .label("Email")
            .child(Input::new(&email_input))
            .required(true)
    )

#横向布局

h_form()
    .label_width(px(120.))
    .child(
        field()
            .label("First Name")
            .child(Input::new(&first_name))
    )
    .child(
        field()
            .label("Last Name")
            .child(Input::new(&last_name))
    )

#多列表单

v_form()
    .columns(2)
    .child(
        field()
            .label("First Name")
            .child(Input::new(&first_name))
    )
    .child(
        field()
            .label("Last Name")
            .child(Input::new(&last_name))
    )
    .child(
        field()
            .label("Bio")
            .col_span(2)
            .child(Input::new(&bio_input))
    )

#容器与布局

#纵向布局

v_form()
    .gap(px(12.))
    .child(field().label("Name").child(input))
    .child(field().label("Email").child(email_input))

#横向布局

h_form()
    .label_width(px(100.))
    .child(field().label("Name").child(input))
    .child(field().label("Email").child(email_input))

#自定义尺寸

v_form()
    .large()
    .label_text_size(rems(1.2))
    .child(field().label("Title").child(input))

v_form()
    .small()
    .child(field().label("Code").child(input))

#校验与说明

#必填字段

field()
    .label("Email")
    .required(true)
    .child(Input::new(&email_input))

#字段描述

field()
    .label("Password")
    .description("Must be at least 8 characters long")
    .child(Input::new(&password_input))

#动态描述

field()
    .label("Bio")
    .description_fn(|_, _| {
        div().child("Use at most 100 words to describe yourself.")
    })
    .child(Input::new(&bio_input))

#字段可见性

field()
    .label("Admin Settings")
    .visible(user.is_admin())
    .child(Switch::new("admin-mode"))

#提交处理

#基础提交模式

struct FormView {
    name_input: Entity<InputState>,
    email_input: Entity<InputState>,
}

impl FormView {
    fn submit(&mut self, cx: &mut Context<Self>) {
        let name = self.name_input.read(cx).value();
        let email = self.email_input.read(cx).value();

        if name.is_empty() || email.is_empty() {
            return;
        }

        self.handle_submit(name, email, cx);
    }
}

v_form()
    .child(field().label("Name").child(Input::new(&self.name_input)))
    .child(field().label("Email").child(Input::new(&self.email_input)))
    .child(
        field()
            .label_indent(false)
            .child(
                Button::new("submit")
                    .primary()
                    .child("Submit")
                    .on_click(cx.listener(|this, _, _, cx| this.submit(cx)))
            )
    )

#操作按钮组

v_form()
    .child(field().label("Title").child(Input::new(&title)))
    .child(field().label("Content").child(Input::new(&content)))
    .child(
        field()
            .label_indent(false)
            .child(
                h_flex()
                    .gap_2()
                    .child(Button::new("save").primary().child("Save"))
                    .child(Button::new("cancel").child("Cancel"))
                    .child(Button::new("preview").outline().child("Preview"))
            )
    )

#字段分组

#相关字段组合

v_form()
    .child(
        field()
            .label("Name")
            .child(
                h_flex()
                    .gap_2()
                    .child(div().flex_1().child(Input::new(&first_name)))
                    .child(div().flex_1().child(Input::new(&last_name)))
            )
    )
    .child(
        field()
            .label("Address")
            .items_start()
            .child(
                v_flex()
                    .gap_2()
                    .child(Input::new(&street))
                    .child(
                        h_flex()
                            .gap_2()
                            .child(div().flex_1().child(Input::new(&city)))
                            .child(div().w(px(100.)).child(Input::new(&zip)))
                    )
            )
    )

#自定义字段组件

field()
    .label("Theme Color")
    .child(ColorPicker::new(&color_state).small())

field()
    .label("Birth Date")
    .description("We'll send you a birthday gift!")
    .child(DatePicker::new(&date_state))

#条件字段

v_form()
    .child(
        field()
            .label("Account Type")
            .child(Select::new(&account_type))
    )
    .child(
        field()
            .label("Company Name")
            .visible(is_business_account)
            .child(Input::new(&company_name))
    )

#网格与定位

#列跨度

v_form()
    .columns(3)
    .child(field().label("First").child(input1))
    .child(field().label("Second").child(input2))
    .child(field().label("Third").child(input3))
    .child(
        field()
            .label("Full Width")
            .col_span(3)
            .child(Input::new(&full_width))
    )

#响应式布局

v_form()
    .columns(if is_mobile { 1 } else { 2 })
    .child(field().label("Name").child(name_input))
    .child(field().label("Email").child(email_input))
    .child(
        field()
            .label("Bio")
            .when(!is_mobile, |field| field.col_span(2))
            .child(bio_input)
    )

#示例

#注册表单

struct RegistrationForm {
    first_name: Entity<InputState>,
    last_name: Entity<InputState>,
    email: Entity<InputState>,
    password: Entity<InputState>,
    confirm_password: Entity<InputState>,
    terms_accepted: bool,
}

#设置表单

v_form()
    .column(2)
    .child(
        field()
            .label("Profile")
            .label_indent(false)
            .col_span(2)
            .child(Separator::horizontal())
    )
    .child(
        field()
            .label("Display Name")
            .child(Input::new(&display_name))
    )