diff options
Diffstat (limited to 'src/components/form-examples/form-examples.tsx')
| -rw-r--r-- | src/components/form-examples/form-examples.tsx | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/src/components/form-examples/form-examples.tsx b/src/components/form-examples/form-examples.tsx new file mode 100644 index 0000000..e1dd6c8 --- /dev/null +++ b/src/components/form-examples/form-examples.tsx @@ -0,0 +1,91 @@ +import { useLayoutEffect, useRef, useState } from 'react' +import { Form } from '@react-spectrum/s2/Form' +import { Button, ButtonGroup } from '@react-spectrum/s2/ButtonGroup' +import { Picker, PickerItem } from '@react-spectrum/s2/Picker' +import { Switch } from '@react-spectrum/s2/Switch' +import { Checkbox } from '@react-spectrum/s2/Checkbox' +import TextFields from './text-fields' +import SelectionFields from './selection-fields' +import DateNumberFields from './date-number-fields' +import ColorFileFields from './color-file-fields' +import styles from './form-examples.module.css' + +function DemoForm({ onReset }: { onReset: () => void }) { + const containerRef = useRef<HTMLDivElement>(null) + const [isCompact, setIsCompact] = useState(false) + const [size, setSize] = useState<'S' | 'M' | 'L' | 'XL'>('M') + const [sideLabels, setSideLabels] = useState(false) + const [disabled, setDisabled] = useState(false) + const [errors, setErrors] = useState<Record<string, string>>({}) + const [result, setResult] = useState<Record<string, string | string[]> | null>(null) + + useLayoutEffect(() => { + const container = containerRef.current + if (!container) return + const update = () => setIsCompact(container.clientWidth < 560) + update() + const observer = new ResizeObserver(update) + observer.observe(container) + return () => observer.disconnect() + }, []) + + const useSideLabels = sideLabels && !isCompact + + return ( + <div ref={containerRef}> + <div className={styles.controls}> + <Picker label="控件尺寸" selectedKey={size} onSelectionChange={(key) => { if (key === 'S' || key === 'M' || key === 'L' || key === 'XL') setSize(key) }}> + <PickerItem id="S">S</PickerItem><PickerItem id="M">M</PickerItem><PickerItem id="L">L</PickerItem><PickerItem id="XL">XL</PickerItem> + </Picker> + <Switch isSelected={sideLabels} onChange={setSideLabels}>标签放在侧边</Switch> + <Switch isSelected={disabled} onChange={setDisabled}>禁用表单</Switch> + </div> + {sideLabels && isCompact && <p className={styles.layoutHint}>当前宽度较窄,已自动使用顶部标签。</p>} + <Form UNSAFE_className={`${styles.form}${useSideLabels ? ` ${styles.sideLabels}` : ''}`} size={size} isDisabled={disabled} labelPosition={useSideLabels ? 'side' : 'top'} necessityIndicator="label" + validationBehavior="native" validationErrors={errors} + onReset={(event) => { event.preventDefault(); onReset() }} + onSubmit={(event) => { + event.preventDefault() + const data = new FormData(event.currentTarget) + if (data.get('username') === 'admin') { + setErrors({ username: '该用户名已被占用,请换一个。(本地模拟业务校验)' }) + setResult(null) + return + } + setErrors({}) + const entries = [...new Set(data.keys())].map((key) => { + const values = data.getAll(key).map((value) => typeof value === 'string' ? value : value.name) + return [key, key === 'password' && values[0] ? '••••••••' : values.length === 1 ? values[0] : values] + }) + setResult(Object.fromEntries(entries)) + }}> + <fieldset className={styles.fields} disabled={disabled} inert={disabled}> + <legend className={styles.srOnly}>Spectrum 表单示例</legend> + <TextFields /> + <SelectionFields disabled={disabled} /> + <DateNumberFields disabled={disabled} /> + <ColorFileFields disabled={disabled} /> + <section className={styles.section} aria-labelledby="submit-title"> + <h2 id="submit-title">校验与提交</h2> + <p>用户名和邮箱为必填项。可以直接提交,体验错误提示和焦点定位。</p> + <Checkbox name="agreement" value="accepted" isRequired>我已了解这是本地表单示例</Checkbox> + <ButtonGroup> + <Button type="submit" variant="primary">提交并预览</Button> + <Button type="reset" variant="secondary">重置全部</Button> + </ButtonGroup> + </section> + </fieldset> + </Form> + <section className={styles.result} aria-labelledby="result-title"> + <h2 id="result-title">提交结果</h2> + <p role="status">{result ? '校验通过,以下为本次提交的数据。' : '填写表单并提交后,在这里查看结果。'}</p> + {result && <pre tabIndex={0}>{JSON.stringify(result, null, 2)}</pre>} + </section> + </div> + ) +} + +export default function FormExamples() { + const [version, setVersion] = useState(0) + return <DemoForm key={version} onReset={() => setVersion((value) => value + 1)} /> +} |
