diff options
Diffstat (limited to 'src/components/form-examples/color-file-fields.tsx')
| -rw-r--r-- | src/components/form-examples/color-file-fields.tsx | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/components/form-examples/color-file-fields.tsx b/src/components/form-examples/color-file-fields.tsx new file mode 100644 index 0000000..e9d0e74 --- /dev/null +++ b/src/components/form-examples/color-file-fields.tsx @@ -0,0 +1,69 @@ +import FieldGrid from './field-grid' +import { useState } from 'react' +import { ColorArea, parseColor } from '@react-spectrum/s2/ColorArea' +import { ColorField } from '@react-spectrum/s2/ColorField' +import { ColorSlider } from '@react-spectrum/s2/ColorSlider' +import { ColorWheel } from '@react-spectrum/s2/ColorWheel' +import { ColorSwatch, ColorSwatchPicker } from '@react-spectrum/s2/ColorSwatchPicker' +import { DropZone } from '@react-spectrum/s2/DropZone' +import { FileTrigger } from '@react-spectrum/s2/FileTrigger' +import { Button } from '@react-spectrum/s2/Button' +import styles from './form-examples.module.css' + +export default function ColorFileFields({ disabled }: { disabled: boolean }) { + const [color, setColor] = useState(() => parseColor('hsl(265, 75%, 60%)')) + const [files, setFiles] = useState<File[]>([]) + const [fileError, setFileError] = useState('') + return ( + <> + <section className={styles.section} aria-labelledby="color-fields-title"> + <h2 id="color-fields-title">颜色选择</h2> + <p>输入框、色板、色轮和滑块共享同一个颜色值。</p> + <FieldGrid> + <ColorField label="颜色值" value={color} onChange={(next) => { if (next) setColor(next) }} /> + <ColorSlider isDisabled={disabled} label="色相" channel="hue" colorSpace="hsl" value={color} onChange={setColor} /> + <ColorSlider isDisabled={disabled} label="透明度" channel="alpha" value={color} onChange={setColor} /> + <div className={styles.stack}> + <span>预设色板</span> + <ColorSwatchPicker value={color} onChange={setColor}> + <ColorSwatch color="#8b5cf6" /><ColorSwatch color="#3b82f6" /><ColorSwatch color="#22c55e" /><ColorSwatch color="#f97316" /> + </ColorSwatchPicker> + </div> + </FieldGrid> + <div className={styles.calendars}> + <div className={styles.stack}><span id="color-area-label">二维颜色面板</span><ColorArea isDisabled={disabled} aria-labelledby="color-area-label" colorSpace="hsl" xChannel="saturation" yChannel="lightness" value={color} onChange={setColor} /></div> + <div className={styles.stack}><span id="color-wheel-label">色轮</span><ColorWheel isDisabled={disabled} aria-labelledby="color-wheel-label" value={color} onChange={setColor} /></div> + </div> + <input type="hidden" name="color" value={color.toString('hexa')} /> + <output className={styles.output}>{color.toString('hexa')}</output> + </section> + <section className={styles.section} aria-labelledby="file-fields-title"> + <h2 id="file-fields-title">文件选择与拖放</h2> + <DropZone aria-label="附件拖放区域" getDropOperation={(types) => !disabled && types.has('Files') ? 'copy' : 'cancel'} + onDrop={async (event) => { + try { + const next = await Promise.all(event.items.filter((item) => item.kind === 'file').map((item) => item.getFile())) + setFiles(next) + setFileError('') + } catch { + setFileError('无法读取文件,请重新选择。') + } + }}> + <div className={styles.dropContent}> + <p>将文件拖放到这里,或从设备中选择。</p> + <FileTrigger allowsMultiple onSelect={(selected) => { if (selected) { setFiles(Array.from(selected)); setFileError('') } }}> + <Button type="button" variant="secondary">选择文件</Button> + </FileTrigger> + <p>仅在本页预览文件信息,不上传。</p> + </div> + </DropZone> + {fileError && <p role="alert">{fileError}</p>} + <div aria-live="polite"> + {files.length ? <ul>{files.map((file, index) => <li key={`${file.name}-${index}`}>{file.name} · {Math.ceil(file.size / 1024)} KB</li>)}</ul> : <p>尚未选择文件。</p>} + </div> + {files.length > 0 && <Button type="button" variant="secondary" onPress={() => setFiles([])}>清空文件</Button>} + <input type="hidden" name="files" value={JSON.stringify(files.map(({ name, size, type }) => ({ name, size, type })))} /> + </section> + </> + ) +} |
