UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

1,195 lines (1,063 loc) 141 kB
--- localeCode: en-US order: 40 category: Input title: Form subTitle: Form icon: doc-form dir: column --- ## Form - **Rerender on demand**, avoids unnecessary full-volume rendering, higher performance - Easy to use, **simple structure**, avoids unnecessary hierarchical nesting - Perfect accessibility support - FormState / FieldState can also be easily obtained from outside the Form Provides an external method to operate inside the form: formApi / fieldApi - Support for encapsulating custom components into form controls, and you can quickly access your team's components through the extension mechanism provided by Form (through `withField` HOC) - Support Form level / Field level assignment, verification (synchronous / asynchronous) ## Field Semi encapsulates all form field component (Input、Select、Checkbox、DatePicker etc.) with `withField` once. Taking over their data flow (`props.value` & `props.onChange`) When in use, you need to import from the Form (note: only the control imported from the Form has data synchronization) ### Supported Field Component - `Input`, `InputNumber`, `TextArea`, `Select`, `Checkbox`, `Radio`, `RadioGroup`, `Switch`, `DatePicker`, `TimePicker`, `Slider`, `InputGroup`, `TreeSelect`, `Cascader`, `Rating`, `AutoComplete`, `Upload`, `Label`, `ErrorMessage`, `Section`、`TagInput` All mounted under Form and declared directly in `<Form.Input />` and `<Form.Select />` when used. ```javascript import { Form } from '@douyinfe/semi-ui'; const FormInput = Form.Input; const FormSelect = Form.Select; const Option = FormSelect.Option; ``` The Field level component provided by Form, its `value` (or other properties specified by `valueKey`), onChange (or other callback functions specified by `onKeyChangeFnName`) Properties are hijacked by Form, so <Notice type="primary" title="Notice"> 1. No longer need to manually bind the onChange event and update the value as controled component. But you can continue to listen onChange events for the latest values if you want 2. You cannot set the state of component with attributes such as `value`, `defaultValue`, `checked`, `defaultChecked`, etc. The default value can be set by Field's `initValue` or Form's `unitValues` 3. You should not modify the value of Form State directly, all changes to the data in the Form should be done by providing `formApi`, `fieldApi` </Notice> ## Demos ### Various ways to declare form Semi Form supports multiple writing at the same time. #### Basic Usage Add `field` property to each field component. You can also set `label` properties for each field, by default is the same as field `label` can be passed in a string directly, or declared in the form of an object, configure `extra`, `required`, `optional` and other attributes to deal with more complex scenarios <Notice type='primary' title='Notice'> The field attribute is required props </Notice> ```jsx live=true dir="column" import React from 'react'; import { Form, Tooltip } from '@douyinfe/semi-ui'; import { IconHelpCircle } from '@douyinfe/semi-icons'; () => ( <Form layout='horizontal'> <Form.Input field='username' label='UserName' style={{ width: 80 }}/> <Form.Input field='password' label={{ text: 'Password', extra: <Tooltip content='More info xxx'><IconHelpCircle style={{ color: 'var(--semi-color-text-2)' }}/></Tooltip> }} style={{ width: 176 }} /> <Form.Select field="role" label={{ text: 'Role', optional: true }} style={{ width: 176 }} optionList={[ { label: 'Admin', value: 'admin' }, { label: 'User', value: 'user' }, { label: 'Guest', value: 'guest' }, ]} > </Form.Select> </Form> ); ``` #### Other declaration methods When you need to get `formState`, `formApi`, `values`, etc. directly inside the Form structure, you can use the following writing #### Via render props ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => ( <Form render={({ formState, formApi, values }) => ( <> <Form.Select field="role" label='Role' style={{ width: 120 }}> <Form.Select.Option value="admin">Admin</Form.Select.Option> <Form.Select.Option value="user">User</Form.Select.Option> <Form.Select.Option value="guest">Guest</Form.Select.Option> </Form.Select> <Form.Input field='userName' label='UserName' /> <Form.Input field='password' label='Password' /> <code style={{ marginTop: 30 }}>{JSON.stringify(formState)}</code> </> )} layout='horizontal'> </Form> ); ``` #### Via children function declare children as a function that returns all field components ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => ( <Form layout='horizontal'> { ({ formState, values, formApi }) => ( <> <Form.Select field="role" label='Role' style={{ width: 120 }}> <Form.Select.Option value="admin">Admin</Form.Select.Option> <Form.Select.Option value="user">User</Form.Select.Option> <Form.Select.Option value="guest">Guest</Form.Select.Option> </Form.Select> <Form.Input field='userName' label='UserName' /> <Form.Input field='password' label='Password' /> <code style={{ marginTop: 30 }}>{JSON.stringify(formState)}</code> </> ) } </Form> ); ``` #### Via props.component Pass the entire internal structure directly in the form through `component` attribute. ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class Demo extends React.Component { constructor() { super(); } render() { const fields = ({ formState, formApi, values }) => ( <> <Form.Input field='Role'/> <Form.Input field='UserName' /> <Form.Input field='Password' /> <code style={{ marginTop: 30 }}>{JSON.stringify(formState)}</code> </> ); return <Form component={fields} layout='horizontal'/>; } } ``` ### All supported field components ```jsx live=true dir="column" import React from 'react'; import { Form, Col, Row, Button } from '@douyinfe/semi-ui'; import { IconUpload } from '@douyinfe/semi-icons'; class BasicDemoWithInit extends React.Component { constructor() { super(); this.state = { initValues: { name: 'semi', business: ['ulikeCam'], role: 'ued', switch: true, files: [ { uid: '1', name: 'vigo.png', status: 'success', size: '130KB', preview: true, url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/vigo.png' }, { uid: '2', name: 'resso.jpeg', status: 'validateFail', size: '222KB', percent: 50, preview: true, fileInstance: new File([new ArrayBuffer(2048)], 'resso.jpeg', { type: 'image/jpeg' }), url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/Resso.png' }, { uid: '3', name: 'dy.jpeg', status: 'uploading', size: '222KB', percent: 50, preview: true, fileInstance: new File([new ArrayBuffer(2048)], 'dy.jpeg', { type: 'image/jpeg' }), url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dy.png' } ] } }; } render() { const { Input, InputNumber, AutoComplete, Select, TreeSelect, Cascader, DatePicker, TimePicker, TextArea, CheckboxGroup, Checkbox, RadioGroup, Radio, Slider, Rating, Switch, TagInput, Section } = Form; const { initValues } = this.state; const plainOptions = ['A', 'B', 'C']; const style = { width: '90%' }; const treeData = [ { label: 'Asia', value: 'Asia', key: '0', children: [ { label: 'China', value: 'China', key: '0-0', children: [ { label: 'Beijing', value: 'Beijing', key: '0-0-0', }, { label: 'Shanghai', value: 'Shanghai', key: '0-0-1', }, ], }, ], }, { label: 'North America', value: 'North America', key: '1', } ]; return ( <Form initValues={initValues} style={{ padding: 10, width: '100%' }} onValueChange={(v)=>console.log(v)} > <Section text={'Basic Info'}> <Row> <Col span={12}> <Input field="name" label="Name(Input)" initValue={'mikeya'} style={style} trigger='blur' /> </Col> <Col span={12}> <DatePicker field="date" label='Date(DatePicker)' style={style} initValue={new Date()} placeholder='Choose data' /> </Col> </Row> <Row> <Col span={12}> <Select field="role" style={style} label='Role(Select)' placeholder='Choose role'> <Select.Option value="qa">Quality Assurance</Select.Option> <Select.Option value="rd">Software Engineer</Select.Option> <Select.Option value="pm">Product Manager</Select.Option> <Select.Option value="ued">Designer</Select.Option> </Select> </Col> <Col span={12}> <Select field="business" multiple style={style} placeholder='Choose application' label="Application(Multiple Select)" > <Select.Option value="semi">Semi</Select.Option> <Select.Option value="ulikeCam">UlikeCam</Select.Option> <Select.Option value="xigua">BuzzVideo</Select.Option> </Select> </Col> </Row> <Row> <Col span={12}> <Form.Cascader placeholder="Choose Area" treeData={treeData} field='area' label='Area(Cascader)' style={style} > </Form.Cascader> </Col> <Col span={12}> <Form.TreeSelect field="tree" style={style} label='Node(TreeSelect)' placeholder='Select Service Node' treeData={treeData} filterTreeNode > </Form.TreeSelect> </Col> </Row> <Row> <Col span={12}> <TagInput field="product" label='Product(TagInput)' initValue={['abc', 'ulikeCam']} placeholder='Type and choose product name' style={style} /> </Col> </Row> <Row> <Col span={24}> <Form.Upload field='files' label='Files(Upload)' action='//semi.design/api/upload' > <Button icon={<IconUpload />} theme="light"> Click to upload </Button> </Form.Upload> </Col> </Row> </Section> <Section text='Source Detail'> <Row> <Col span={12}> <TextArea style={style} field='description' label='Apply Reason(TextArea)' /> </Col> <Col span={12}> <CheckboxGroup field="type" label='Apply type(CheckboxGroup)' initValue={['user', 'admin']} rules={[ { Requested: true } ]} > <Checkbox value="admin">admin</Checkbox> <Checkbox value="user">user</Checkbox> <Checkbox value="guest">guest</Checkbox> <Checkbox value="root">root</Checkbox> </CheckboxGroup> </Col> </Row> <Row> <Col span={12}> <RadioGroup field="isMonopolize" label='Whether exclusive resources(Radio)'> <Radio value={1}>Yes</Radio> <Radio value={0}>No</Radio> </RadioGroup> </Col> <Col span={12}> <CheckboxGroup options={plainOptions} field="checkbox" label='Type(CheckboxGroup)' direction='horizontal'/> </Col> </Row> <Row> <Col span={12}> <TimePicker field="time" label='End Time(TimePicker)' style={{ width: '90%' }}/> </Col> <Col span={12}> <InputNumber field='number' label='Number of applications(InputNumber)' initValue={20} style={style}/> </Col> </Row> <Row> <Col span={12}> <Slider field="range" label='Resource usage alarm threshold(%)(Slider)' initValue={10} style={{ width: '90%' }}/> </Col> <Col span={12}> <Switch field='switch' label='Switch(Switch)'/> </Col> </Row> <Row> <Col span={12}> <Rating field="rating" label='Satisfaction(Rating)' initValue={2} style={{ width: '90%' }}/> </Col> </Row> </Section> <Checkbox value="false" field="agree" noLabel={true}> I have read and understood the relevant regulations(Checkbox) </Checkbox> <Button type="primary" htmlType="submit" className="btn-margin-right">Submit</Button> <Button htmlType="reset">Reset</Button> </Form> ); } } ``` ### Field binding syntax Every Field component must have a `field` property. This is how the form manages the state of this field. See the field syntax section below for additional details on what you can pass in for field. The field can be a simple string, can be contained`.`Or`[]`String that supports multi-level nesting Below is an example of the field name and their mapping path in FormState | Field | Resolution | | ---------------------- | ---------------------------------- | | username | formState.values.username | | user\[0\] | formState.values.user\[0\] | | siblings.1 | formState.values.siblings\[1\] | | siblings\['2'\] | formState.values.siblings\[2\] | | parents\[0\].name | formState.values.parents\[0\].name | | parents\[1\]\['name'\] | formState.values.parents\[1\].name | ```jsx live=true dir="column" import React from 'react'; import { Form, Row, Col, Toast, TextArea } from '@douyinfe/semi-ui'; () => ( <Form onSubmit={values => Toast.info({ content: JSON.stringify(values) })} > { ({ formState, values, formApi }) => ( <Row> <Col span={12}> <Form.Input field='username' placeholder='Try input something'/> <Form.Input field='user[0]' placeholder='Try input something'/> <Form.Input field='siblings.1' placeholder='Try input something'/> <Form.Input field="siblings['2']" placeholder='Try input something'/> <Form.Input field='parents[0].name' placeholder='Try input something'/> <Form.Input field="parents[1]['name']" placeholder='Try input something'/> </Col> <Col span={10} offset={1} style={{ marginTop: 12 }}> <Form.Label text='formState.values in real time:'></Form.Label> <TextArea value={JSON.stringify(formState.values)}></TextArea> </Col> </Row> ) } </Form> ); ``` ### Form layout - Vertical Layout: Arrange each field vertically (By default) Semi Design recommends a vertical layout. ```jsx live=true dir="column" import React from 'react'; import { Form, Button, Toast } from '@douyinfe/semi-ui'; () => { const handleSubmit = (values) => { console.log(values); Toast.info('Submit Success'); }; return ( <Form onSubmit={values => handleSubmit(values)} style={{ width: 400 }}> {({ formState, values, formApi }) => ( <> <Form.Input field='phone' label='PhoneNumber' style={{ width: '100%' }} placeholder='Enter your phone number'></Form.Input> <Form.Input field='password' label='Password' style={{ width: '100%' }} placeholder='Enter your password'></Form.Input> <Form.Checkbox field='agree' noLabel>I have read and agree to the terms of service</Form.Checkbox> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <p> <span>Or</span><Button theme='borderless' style={{ color: 'var(--semi-color-primary)', marginLeft: 10, cursor: 'pointer' }}>Sign up</Button> </p> <Button disabled={!values.agree} htmlType='submit' type="tertiary">Log in</Button> </div> </> )} </Form> ); }; ``` - Horizontal Layout: Arrange each field horizontally You can use the horizontal layout by setting `layout='horizontal'` ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => ( <Form layout='horizontal'> <Form.Input field='phone' label='PhoneNumber' placeholder='Enter your phone number'></Form.Input> <Form.Input field='password' label='Password' placeholder='Enter your password'></Form.Input> </Form> ); ``` - Label Position, Label Align You can control the position of the label in the Field and the direction of text alignment by setting `labelPosition`, `labelAlign` ```jsx live=true dir="column" import React from 'react'; import { Form, Select, Checkbox, Radio } from '@douyinfe/semi-ui'; class BasicDemo extends React.Component { constructor(props) { super(props); this.state = { labelPosition: 'left', labelAlign: 'left', }; } render() { const { labelPosition, labelAlign } = this.state; const labelWidth = 120; return ( <> <div style={{ borderBottom: '1px solid var(--semi-color-text-3)', paddingBottom: 10 }}> <Form labelPosition='inset' layout='horizontal' initValues={{ labelPosition: 'left', labelAlign: 'left' }} onValueChange={values => this.setState(values)} > <Form.Select field='labelPosition' style={{ width: 240 }} label='Current Label Position:' optionList={[ { label: 'top', value: 'top' }, { label: 'left', value: 'left' }, ]} /> <Form.Select field='labelAlign' style={{ width: 240 }} label='Current Label Align:' optionList={[ { label: 'left', value: 'left' }, { label: 'right', value: 'right' }, ]} /> </Form> </div> <Form labelPosition={labelPosition} labelWidth={labelWidth} labelAlign={labelAlign} style={{ padding: '10px', width: 600 }}> <Form.Input field="input" label="PhoneNumber" trigger='blur' style={{ width: 200 }} rules={[ { required: true, message: 'required Error' }, { type: 'string', message: 'type error' }, { validator: (rule, value) => value === 'semi', message: 'not semi' } ]} /> <Form.Switch label="Agree" field='agree'/> <Form.InputNumber field='price' label='price' style={{ width: 200 }}/> <Form.Select label="Name" field='name' style={{ width: 200 }}> <Form.Select.Option value="mike">mike</Form.Select.Option> <Form.Select.Option value="jane">jane</Form.Select.Option> <Form.Select.Option value="kate">kate</Form.Select.Option> </Form.Select> <Form.CheckboxGroup label="Role" field='role' direction='horizontal'> <Checkbox value="admin">admin</Checkbox> <Checkbox value="user">user</Checkbox> <Checkbox value="guest">guest</Checkbox> <Checkbox value="root">root</Checkbox> </Form.CheckboxGroup> <Form.RadioGroup field="Sex"> <Radio value="1">man</Radio> <Radio value="2">woman</Radio> </Form.RadioGroup> </Form> </> ); } } ``` - A more complex layout. You can also combine the `Row` and `Col` provided by the `Grid` to arrange the form structure as you want. ```jsx live=true dir="column" import React from 'react'; import { Form, Row, Col } from '@douyinfe/semi-ui'; () => ( <Form labelPosition='top' getFormApi={this.getFormApi} style={{ padding: '10px' }}> <Row> <Col span={8}> <Form.Input field="nickName1" label="NickName" style={{ width: '250px' }} trigger='blur' rules={[ { required: true, message: 'required error' }, { type: 'string', message: 'type error' }, { validator: (rule, value) => value === 'semi', message: 'not semi' } ]} /> </Col> <Col span={8}> <Form.DatePicker field='date1' label='Valid Date' style={{ width: '250px' }}/> </Col> <Col span={8}> <Form.Select label="Application" field='business1' style={{ width: '250px' }}> <Form.Select.Option value="abc">Semi</Form.Select.Option> <Form.Select.Option value="hotsoon">Vigo</Form.Select.Option> <Form.Select.Option value="xigua">BussVideo</Form.Select.Option> </Form.Select> </Col> </Row> <Row> <Col span={6}> <Form.Input field="nickName2" label="NickName" style={{ width: '200px' }} trigger='blur' rules={[ { required: true, message: 'required error' }, { type: 'string', message: 'type error' }, { validator: (rule, value) => value === 'semi', message: 'not semi' } ]} /> </Col> <Col span={6}> <Form.DatePicker field='date2' label='Valid Date' style={{ width: '200px' }}/> </Col> <Col span={6}> <Form.Select label="Application" field='business2' style={{ width: '250px' }}> <Form.Select.Option value="abc">Semi</Form.Select.Option> <Form.Select.Option value="hotsoon">Vigo</Form.Select.Option> <Form.Select.Option value="xigua">BussVideo</Form.Select.Option> </Form.Select> </Col> <Col span={6}> <Form.Select field="role" style={{ width: '250px' }} label='Role(Select)' placeholder='Choose role'> <Form.Select.Option value="qa">Quality Assurance</Form.Select.Option> <Form.Select.Option value="rd">Software Engineer</Form.Select.Option> <Form.Select.Option value="pm">Product Manager</Form.Select.Option> <Form.Select.Option value="ued">Designer</Form.Select.Option> </Form.Select> </Col> </Row> </Form> ); ``` ### wrapper Col / label Col When you need to set a uniform layout for all Fields in a Form, you can set `wrapperCol` and `labelCol` on the `Form` to quickly generate the layout. No need to manually use `Row`, `Col` manual layout. `wrapperCol`,`labelCol`Property Configuration Reference [Col components](/en-US/basic/grid#Col) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => ( <Form wrapperCol={{ span: 20 }} labelCol={{ span: 2 }} labelPosition='left' labelAlign='right' > <Form.Input field='name' style={{ width: 250 }} label='Name' placeholder='Input Name' trigger='blur' /> <Form.Select field="role" label='Role' placeholder='Choose Role' style={{ width: 250 }}> <Form.Select.Option value="qa">Quality Assurance</Form.Select.Option> <Form.Select.Option value="rd">Software Engineer</Form.Select.Option> <Form.Select.Option value="pm">Product Manager</Form.Select.Option> <Form.Select.Option value="ued">Designer</Form.Select.Option> </Form.Select> </Form> ); ``` ### Remove automatically added Label Form will automatically insert `Label` for Field control. If you don't need to automatically insert the Label module, you can turn off the automatic label insertion function by setting `noLabel=true` in the Field (at this time, the Field still has the ability to automatically display ErrorMessage, so the DOM structure is still different from the original component) If you want to keep the DOM structure consistent with the original component, you can use `pure=true`. At this time, the DOM structure will not change except that the data flow is taken over (you need to be responsible for the rendering of ErrorMessage, and it cannot be used by formProps.wrapperCol property impact) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => ( <Form onSubmit={(values) => console.log(values)} style={{ width: 400 }}> <Form.Input field='name' label='Name' trigger='blur' noLabel={true} style={{ width: 250 }} validator={val => val !== 'semi' ? 'not semi' : '' } placeholder='Type your name' /> <Form.Input field='purename' pure placeholder='DOM same as origin Input component'/> </Form> ); ``` ### Embedded Label A Label can be inlined in a field control by setting labelPosition to `inset`. Components currently supporting this feature include `Input`, `InputNumber`, `DatePicker`, `TimePicker`, `Select`, `TreeSelect`, `Cascader`, `TagInput` ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => ( <Form labelPosition='inset' layout='horizontal'> <Form.Input field='name' label='Name' trigger='blur' style={{ width: 250 }} initValue='semi'/> <Form.Select field="role" label='Role' style={{ width: '250px' }} initValue='rd'> <Form.Select.Option value="operate">operate</Form.Select.Option> <Form.Select.Option value="rd">rd</Form.Select.Option> <Form.Select.Option value="pm">pm</Form.Select.Option> <Form.Select.Option value="ued">ued</Form.Select.Option> </Form.Select> <Form.DatePicker field="date" label='StartDate' style={{ width: '250px' }} initValue={new Date()}> </Form.DatePicker> </Form> ); ``` ### Export Label, ErrorMessage use When the built-in Label and ErrorMessage layout does not meet the business requirements, you need to combine the positions yourself, but you want to use the default styles of Label and ErrorMessage directly. you can import them from the `Form` module, and combine `Form.Label` / `Form.ErrorMessage` by yourself. For details of their API, refer to [Label](#Form.Label) / [ErrorMessage](#Form.ErrorMessage) ```jsx import { Form } from '@douyinfe/semi-ui'; const { Label, ErrorMessage } = Form; ``` ### Use Form.Slot When your custom component needs to maintain the same layout style as the Field component, you can place your custom component in `Form.Slot` `labelWidth`, `labelAlign`, `wrapperCol`, `labelCol` set on the Form component automatically acts on `Form.Slot` For the Slot property configuration, refer to [Form.Slot](#Form.Slot) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class AssistComponent extends React.Component { render() { return ( <Form onChange={v=>console.log(v)} onSubmit={v=>console.log(v)} style={{ width: 600 }} labelPosition='left' labelWidth={100} > <Form.Input field='effectName' label='EffectName' style={{ width: 250 }}/> <Form.ErrorMessage /> <Form.Slot label={{ text: 'SlotA' }}> <div style={{ display: 'flex', alignItems: 'center', height: '100%' }}> {`I'm Semi Form SlotA, a custom ReactNode`} </div> </Form.Slot> <Form.Slot label={{ text: 'SlotB', width: 160, align: 'right' }}> <div style={{ display: 'flex', alignItems: 'center', height: '100%' }}> {`I'm Semi Form SlotA, i have different labelWidth and textAlign.`} </div> </Form.Slot> </Form> );} } ``` ### Use helpText、extraText set prompt information You can place custom prompt information through `helpText`, and display it in the same block as the verification information (error). When both have values, the verification information will be displayed first. Additional prompt information can be placed through `extraText`. When the error message and prompt text need to appear at the same time, this configuration can be used. It is always displayed and located after helpText/error When `validateStatus` is passed in, the UI style corresponding to the value of validateStatus will be displayed first. If not passed in, the internal verification status of the field shall prevail. ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => { const [helpText, setHelpText] = useState(''); const [validateStatus, setValidateStatus] = useState('default'); const formRef = useRef(); const validator = (val, values) => { if (!val) { setValidateStatus('error'); return <span>Password can not be blank</span>; } else if (val && val.length <= 3) { setValidateStatus('warning'); setHelpText(<span style={{ color: 'var(--semi-color-warning)' }}>Password Strength: Weak</span>); // show helpText return ''; // validate pass } else { setHelpText(''); setValidateStatus('success'); return ''; } }; const random = () => { let pw = (Math.random() * 100000).toString().slice(0, 5); formRef.current.formApi.setValue('Password', pw); formRef.current.formApi.setError('Password', ''); setHelpText(''); setValidateStatus('success'); }; return ( <Form showValidateIcon={true} ref={formRef} onSubmit={(value) => console.log('submit success')} onSubmitFail={(errors) => console.log(errors)} > <Form.Input validator={validator} field="Password" validateStatus={validateStatus} helpText={helpText} extraText={ <div style={{ color: 'var(--semi-color-link)', fontSize: 14, userSelect: 'none', cursor: 'pointer' }} onClick={random} > Don't have a suitable password? Click to generate a random </div> } ></Form.Input> </Form> ); }; ``` By configuring `extraTextPosition`, you can control the display position of extraText. Optional values `bottom`, `middle` For example, when you want to display the extraText prompt information between the Label and Field component. This attribute can be configured uniformly on the Form or individually on each Field. When passing in at the same time, the configuration of the Field shall prevail. ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => { const options = [ { label: 'Lark Notification', value: 'lark' }, { label: 'Email Notification', value: 'email' }, { label: 'Banner Notification', value: 'notification' } ]; const notifyText = "When unchecked, the default is a red dot reminder, and the message enters the recipient's message list by default. For important notifications, you can check the corresponding notification methods at the same time"; const forceText = "For dialog notifications, you can specify that the message must wait for a specified amount of time before it can be marked as read."; return ( <Form extraTextPosition='middle'> <Form.CheckboxGroup direction='horizontal' field='notify' label='Method to informe' extraText={notifyText} options={options} /> <Form.InputNumber field='force' label='Force read (optional)' placeholder='seconds' extraText={forceText} extraTextPosition='bottom'/> </Form> ); }; ``` ### Using Input Group When you need to combine some fields to use, you can use `Form.InputGroup` to wrap them. In Semi Form, when you using field components like `Form.Input`、`Form.Select`, Form will insert Label module automatically for them. But usually, in`InputGroup` you only need a Label belonging to the entire Group. You can set the label property in the `InputGroup` to insert a Label belonging to the Group `label` configurable properties, see [Label](#Form.Label) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; () => ( <Form onSubmit={(values) => console.log(values)} labelPosition='top' style={{ width: 400 }}> <Form.InputGroup label={{ text: (<span>PhoneNumber</span>), required: true }} labelPosition='top'> <Form.Select style={{ width: 150 }} field='phonePrefix' initValue='+86' rules={[{ required: true }]} showClear> <Form.Select.Option value='+1'>USA +1</Form.Select.Option> <Form.Select.Option value='+86'>China +86</Form.Select.Option> <Form.Select.Option value='+81'>Japan+81</Form.Select.Option> </Form.Select> <Form.Input initValue='18912345678' style={{ width: 250 }} field='phoneNumber' rules={[{ required: true }]} showClear/> </Form.InputGroup> <Form.Input field='name' trigger='blur' initValue='Semi' label='Name'></Form.Input> <Button htmlType='submit'>Submit</Button> </Form> ); ``` ### Form in the Modal pop-up layer You can place the Form in Modal and load it as a popup. When submitting, use `formApi.validate()` to centrally verify the Field ```jsx live=true dir="column" import React from 'react'; import { Form, Modal, Select, Button, Row, Col } from '@douyinfe/semi-ui'; class ModalFormDemo extends React.Component { constructor(props) { super(props); this.state = { visible: false, }; this.showDialog = this.showDialog.bind(this); this.handleOk = this.handleOk.bind(this); this.handleCancel = this.handleCancel.bind(this); this.getFormApi = this.getFormApi.bind(this); } showDialog() { this.setState({ visible: true }); } handleOk() { this.formApi.validate() .then((values) => { console.log(values); }) .catch((errors) => { console.log(errors); }); } handleCancel() { this.setState({ visible: false }); } getFormApi(formApi) { this.formApi = formApi; } render() { const { visible } = this.state; let message = 'Required'; return ( <> <Button onClick={this.showDialog}>Open Dialog</Button> <Modal title="New" visible={visible} onOk={this.handleOk} style={{ width: 600 }} onCancel={this.handleCancel} > <Form getFormApi={this.getFormApi} > <Row> <Col span={7}> <Form.Select field='region' label="Country/Region" style={{ width: 120 }} rules={[ { required: true, message }, ]} optionList={[ { label: 'China', value: 'China' }, { label: 'USA', value: 'US' }, { label: 'Europe', value: 'Europe' }, { label: 'Japan', value: 'Japan' }, ]} > </Form.Select> </Col> <Col span={17}> <Form.Input field='owner' label="Owner" trigger='blur' rules={[ { required: true, message }, ]} /> </Col> <Col span={7}> <Form.Select field='area' label="Area" placeholder='Choose Area' style={{ width: 120 }} rules={[ { required: true, message }, ]} > <Form.Select.Option value="China">China</Form.Select.Option> <Form.Select.Option value="US">USA</Form.Select.Option> <Form.Select.Option value="Europe">Europe</Form.Select.Option> <Form.Select.Option value="Japan">Japan</Form.Select.Option> </Form.Select> </Col> <Col span={17}> <Form.Input field='department' label="Department" trigger='blur' rules={[ { required: true, message }, ]} /> </Col> </Row> </Form> </Modal> </> ); } } ``` ### Configure initial values and verification rules - You can configure check rules for each Field through `rules` The verification library inside the Form is based on `async-validator`, and more configuration rules can be found in its [official documentation](https://github.com/yiminghe/async-validator) - You can uniformly set the initial value for the entire form through the `initValues` of form, or you can set the initial value through `initValue` in each field (the latter has a higher priority) - You can configure different verification trigger timings for each Field through `trigger`, and the default is `change` (that is, when onChange is triggered, the verification is performed automatically). Also supports `change`, `blur`, `mount`, `custom` or a combination of the above. After v2.42, it supports unified configuration through FormProps. If both are configured, FieldProps shall prevail - You can use the `stopValidateWithError`` switch to decide whether to continue to trigger the validation of subsequent rules when the first rule that fails the validation is encountered. After v2.42, unified configuration through FormProps is supported. If both are configured, FieldProps shall prevail ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; () => { const initValues = { name: 'semi', shortcut: 'se' }; const style = { width: '100%' }; const { Select, Input } = Form; return ( <Form initValues={initValues}> <Input field="name" style={style} trigger='blur' rules={[ { required: true, message: 'required error' }, { type: 'string', message: 'type error' }, { validator: (rule, value) => value === 'semi', message: 'should be semi' }, { validator: (rule, value) => Boolean(value && value.startsWith('se')), message: 'should startsWith se' } ]} /> <Input field="shortcut" style={style} stopValidateWithError rules={[ { required: true, message: 'required error' }, { type: 'string', message: 'type error' }, { validator: (rule, value) => value === 'semi', message: 'should be semi' }, { validator: (rule, value) => Boolean(value && value.startsWith('se')), message: 'should startsWith se' } ]} /> <Button htmlType='submit'>提交</Button> </Form> ); }; ``` ### Custom Validate (Form Level) You can set a custom validation function for the `Form` as a whole. **Recommended:** use `validator` (`validateFields` is legacy but still compatible). It will be called on submit or when calling `formApi.validate()`. #### Synchronous Validate When validate success, you should return an empty string. When validate fails, you should return the error message (Object, key is fieldName, value is the corresponding error message) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FormLevelValidateSync extends React.Component { constructor() { super(); this.syncValidate = this.syncValidate.bind(this); } syncValidate(values) { const errors = {}; if (values.name !== 'mike') { errors.name = 'you must name mike'; } if (values.sex !== 'female') { errors.sex = 'must be woman'; } errors.familyName = [ { before: 'before errror balabala ', after: 'after error balabala' }, 'familyName[1] error balabala' ]; return errors; } render() { return ( <Form validator={this.syncValidate} layout='horizontal'> <Form.Input field='name' trigger='blur'></Form.Input> <Form.Input field='familyName[0].before' trigger='blur'></Form.Input> <Form.Input field='familyName[0].after' trigger='blur'></Form.Input> <Form.Input field='familyName[1]' trigger='blur'></Form.Input> <div style={{ display: 'flex', alignItems: 'flex-end' }}> <Button type="primary" htmlType="submit" className="btn-margin-right"> Submit </Button> <Button htmlType="reset">reset</Button> </div> </Form > ); } } ``` #### Asynchronous Validate For asynchronous validation, you should return a promise. In promise.then() you need to return the corresponding error message. ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FormLevelValidateAsync extends React.Component { constructor() { super(); this.asyncValidate = this.asyncValidate.bind(this); } asyncValidate(values) { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); return sleep(2000).then(() => { let errors = {}; if (values.name !== 'mike') { errors.name = 'you must name mike'; } if (values.sex !== 'female') { errors.sex =