- Input + IconButton 组合,type 切换、aria-pressed、尺寸对齐内建 - 展示页补「密码框」示例与 props 表;COMPONENTS.md / README 同步 - 版本 0.3.0
202 lines
8.3 KiB
TypeScript
202 lines
8.3 KiB
TypeScript
import { useState } from 'react'
|
||
import { Button, FormField, Icon, Input, PasswordInput, Space, Text, Textarea } from '../../src'
|
||
import { Demo } from '../components/Demo'
|
||
import { PropsTable } from '../components/PropsTable'
|
||
import { Section } from '../components/Section'
|
||
|
||
export function Inputs() {
|
||
const [keyword, setKeyword] = useState('')
|
||
const [host, setHost] = useState('127.0.0.1:8080')
|
||
const [email, setEmail] = useState('not-an-email')
|
||
const [password, setPassword] = useState('')
|
||
|
||
return (
|
||
<Section
|
||
id="inputs"
|
||
title="输入框"
|
||
api="Input"
|
||
desc="左侧图标 + 清除按钮 + 快捷键提示的组合式输入。事件语义与原生 input 一致,需要字符串时在使用方包一层。"
|
||
>
|
||
<Demo
|
||
title="带图标与清除"
|
||
desc="清空按钮只在有内容且传了 onClear 时出现,避免不可用的空按钮。"
|
||
layout="column"
|
||
code={`<Input
|
||
icon="search_3_line"
|
||
placeholder="搜索本地项目…"
|
||
value={keyword}
|
||
onChange={(event) => setKeyword(event.target.value)}
|
||
clearable
|
||
onClear={() => setKeyword('')}
|
||
/>`}
|
||
>
|
||
<Space gap="md" wrap>
|
||
<Input
|
||
icon="search_3_line"
|
||
placeholder="搜索本地项目…"
|
||
value={keyword}
|
||
onChange={(event) => setKeyword(event.target.value)}
|
||
clearable
|
||
onClear={() => setKeyword('')}
|
||
style={{ width: 320 }}
|
||
/>
|
||
<span style={{ alignSelf: 'center', color: 'var(--su-text-muted)', fontSize: 12 }}>
|
||
当前值:{keyword === '' ? '(空)' : keyword}
|
||
</span>
|
||
</Space>
|
||
</Demo>
|
||
|
||
<Demo
|
||
title="快捷键提示"
|
||
desc="shortcut 只是右侧展示,键盘监听由页面自己挂(例如 Ctrl/Cmd + K 聚焦)。"
|
||
layout="column"
|
||
code={`<Input icon="search_3_line" shortcut="⌘K" placeholder="搜索…" />
|
||
<Input icon="link_line" value={host} onChange={(event) => setHost(event.target.value)} />`}
|
||
>
|
||
<Input icon="search_3_line" shortcut="⌘K" placeholder="搜索…" style={{ width: 320 }} />
|
||
<Input
|
||
icon="link_line"
|
||
value={host}
|
||
onChange={(event) => setHost(event.target.value)}
|
||
style={{ width: 320 }}
|
||
/>
|
||
</Demo>
|
||
|
||
<Demo
|
||
title="禁用与表单行"
|
||
desc="输入框与按钮可以并排成一行操作区;禁用态整块降透明度,仍保留可读性。"
|
||
layout="column"
|
||
code={`<Space gap="sm" wrap>
|
||
<Input placeholder="输入项目名…" />
|
||
<Button icon={<Icon name="search_3_line" />}>搜索</Button>
|
||
</Space>
|
||
|
||
<Input disabled value="只读" />`}
|
||
>
|
||
<Space gap="sm" wrap>
|
||
<Input placeholder="输入项目名…" style={{ width: 240 }} />
|
||
<Button icon={<Icon name="search_3_line" />}>搜索</Button>
|
||
</Space>
|
||
<Input disabled value="不可编辑" style={{ width: 240 }} />
|
||
</Demo>
|
||
|
||
<Demo
|
||
title="尺寸"
|
||
desc="32 / 36 / 44 三档;搜索这类主入口用 lg,表单行用 md,表格内筛选用 sm。"
|
||
layout="column"
|
||
code={`<Input size="sm" placeholder="小号 32" />
|
||
<Input size="md" placeholder="默认 36" />
|
||
<Input size="lg" placeholder="大号 44(搜索框)" />`}
|
||
>
|
||
<Input size="sm" placeholder="小号 32" style={{ width: 320 }} />
|
||
<Input size="md" placeholder="默认 36" style={{ width: 320 }} />
|
||
<Input size="lg" placeholder="大号 44(搜索框)" style={{ width: 320 }} />
|
||
</Demo>
|
||
|
||
<Demo
|
||
title="表单字段"
|
||
desc="FormField 负责「标签 + 控件 + 提示/错误」的排版;校验逻辑留在页面,错误出现时自动 role=alert。"
|
||
layout="column"
|
||
code={`<FormField label="邮箱" htmlFor="email" hint="我们只用它发周报" error={error}>
|
||
<Input id="email" value={email} onChange={...} />
|
||
</FormField>`}
|
||
>
|
||
<div style={{ width: 320, display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||
<FormField label="项目名" htmlFor="project" required hint="显示在侧栏与首页卡片标题">
|
||
<Input id="project" placeholder="例如:常用站点" />
|
||
</FormField>
|
||
|
||
<FormField
|
||
label="邮箱"
|
||
htmlFor="email"
|
||
hint="我们只用它发周报"
|
||
error={email.includes('@') ? undefined : '邮箱格式不正确'}
|
||
>
|
||
<Input id="email" value={email} onChange={(event) => setEmail(event.target.value)} />
|
||
</FormField>
|
||
|
||
<Text size="xs" tone="muted">
|
||
当前值:{email}
|
||
</Text>
|
||
</div>
|
||
</Demo>
|
||
|
||
<Demo
|
||
title="多行文本"
|
||
desc="Textarea 用于 Markdown 正文、简介这类长文本;行数可调,默认只允许竖向拖拽。"
|
||
layout="column"
|
||
code={`<Textarea rows={5} placeholder="支持 Markdown…" />
|
||
<Textarea rows={3} resize="none" invalid />`}
|
||
>
|
||
<div style={{ width: 420, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||
<Textarea rows={5} placeholder="写点什么,支持 Markdown:## 标题、**粗体**、[链接](url)" />
|
||
<Textarea rows={3} resize="none" invalid placeholder="校验失败时的样子(invalid)" />
|
||
</div>
|
||
</Demo>
|
||
|
||
<Demo
|
||
title="密码框"
|
||
desc="PasswordInput 在 Input 右侧固定一个眼睛按钮,显隐切换与无障碍名称都封装在组件里;登录、改密这类页面直接用它,不要漏掉切换按钮。"
|
||
layout="column"
|
||
code={`<FormField label="密码" htmlFor="demo-password" required hint="至少 8 位">
|
||
<PasswordInput
|
||
id="demo-password"
|
||
value={password}
|
||
onChange={(event) => setPassword(event.target.value)}
|
||
placeholder="输入密码"
|
||
/>
|
||
</FormField>`}
|
||
>
|
||
<div style={{ width: 320 }}>
|
||
<FormField label="密码" htmlFor="demo-password" required hint="至少 8 位">
|
||
<PasswordInput
|
||
id="demo-password"
|
||
value={password}
|
||
onChange={(event) => setPassword(event.target.value)}
|
||
placeholder="输入密码"
|
||
/>
|
||
</FormField>
|
||
</div>
|
||
</Demo>
|
||
|
||
<PropsTable
|
||
rows={[
|
||
{ name: 'icon', type: 'IconName', desc: '左侧前缀图标' },
|
||
{ name: 'size', type: "'sm' | 'md' | 'lg'", defaultValue: "'md'", desc: '32 / 36 / 44px' },
|
||
{ name: 'clearable', type: 'boolean', defaultValue: 'false', desc: '有内容时显示清除按钮' },
|
||
{ name: 'onClear', type: '() => void', desc: '点击清除;不传则不显示按钮' },
|
||
{ name: 'shortcut', type: 'string', desc: '右侧快捷键提示(仅展示)' },
|
||
{ name: 'inputRef', type: 'Ref<HTMLInputElement>', desc: '拿到真实 input,用于聚焦' },
|
||
{ name: 'className / style', type: 'string / CSSProperties', desc: '作用在外层容器上(宽度、外边距)' },
|
||
{ name: '…rest', type: 'InputHTMLAttributes', desc: '透传原生属性,onChange 是原生事件' },
|
||
]}
|
||
/>
|
||
|
||
<Text size="sm" tone="muted" style={{ marginTop: 24 }}>
|
||
Textarea
|
||
</Text>
|
||
<PropsTable
|
||
rows={[
|
||
{ name: 'rows', type: 'number', defaultValue: '4', desc: '初始可见行数' },
|
||
{ name: 'resize', type: "'none' | 'vertical'", defaultValue: "'vertical'", desc: '是否允许拖拽改高' },
|
||
{ name: 'invalid', type: 'boolean', defaultValue: 'false', desc: '红边框 + aria-invalid' },
|
||
{ name: '…rest', type: 'TextareaHTMLAttributes', desc: '透传原生属性' },
|
||
]}
|
||
/>
|
||
|
||
<Text size="sm" tone="muted" style={{ marginTop: 24 }}>
|
||
PasswordInput
|
||
</Text>
|
||
<PropsTable
|
||
rows={[
|
||
{ name: 'icon', type: 'IconName | null', defaultValue: "'lock_line'", desc: '左侧图标;null 表示不显示' },
|
||
{ name: 'size', type: "'sm' | 'md' | 'lg'", defaultValue: "'md'", desc: '32 / 36 / 44px,与 Input 一致' },
|
||
{ name: 'toggle', type: 'boolean', defaultValue: 'true', desc: '是否显示右侧显隐按钮' },
|
||
{ name: 'className / style', type: 'string / CSSProperties', desc: '作用在最外层容器上' },
|
||
{ name: '…rest', type: 'InputHTMLAttributes', desc: '透传原生属性(type / size / icon 由组件接管)' },
|
||
]}
|
||
/>
|
||
</Section>
|
||
)
|
||
}
|