Files
stash-ui/site/sections/Inputs.tsx
T
stash 0d5d7753e2 feat(textarea): 新增多行文本组件
- rows / resize / invalid,样式与 Input 一致(焦点只换边框)
- 契约、展示页(#inputs)、README 计数 40 → 41、变更记录同步
2026-10-29 12:58:32 +08:00

163 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react'
import { Button, FormField, Icon, Input, 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')
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>
<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: '透传原生属性' },
]}
/>
</Section>
)
}