Input 输入框
单行输入。44px 高(control.lg),8px 圆角(radius.md),灰底无边框,focus 时显橙色描边。
多行内容请用
<Textarea />。Input 不接受multiline/numberOfLines。
实时预览
下方渲染的就是 src/components/ui/Input/Input.tsx 本体,通过 react-native-web 翻译成浏览器节点。
状态
来源:src/components/ui/TextField/styles.ts(值取自 src/theme/colors.ts)。状态优先级 error > focus > filled > idle。
| 状态 | 背景 | 边框 |
|---|---|---|
idle | c.surfaceContainerHigh(亮色 #F0F0F0) | 透明 |
focus | c.surface(#FFFFFF) | 1px c.primary(#EB6E00) |
filled | c.surface(#FFFFFF) | 1px c.outline(#EDEDED) |
error | c.surface(#FFFFFF) | 1px c.error(#F4511E) |
视觉规范
来源:src/components/ui/TextField/styles.ts。
| 元素 | 规则 |
|---|---|
| 高度 | 44px(control.lg,可用 height prop 覆盖) |
| 圆角 | 8px(radius.md) |
| 内边距 | paddingHorizontal: space[5](12) |
| 字号 | t.body(15),色 c.foreground(#333333) |
| 占位文字 | c.foregroundSubtle(#999999) |
| 错误文字 | 下方独立 <Text>,t.micro(11)/ c.error |
| leading / trailing | 任意 ReactNode(自填 <Icon> 等);间距 gap: space[3](8) |
| disabled | 容器 opacity: 0.5 + editable=false |
用法
import { Input, Icon } from '@unif/react-native-design';
import { useColors } from '@unif/react-native-design';
<Input value={text} onChangeText={setText} placeholder="搜索…" />
{/* 密码:直接透传 RN TextInput 的 secureTextEntry */}
<Input
value={pwd}
onChangeText={setPwd}
secureTextEntry
placeholder="密码"
/>
{/* 多行:用 Textarea,不要再用 Input multiline */}
<Textarea
value={text}
onChangeText={setText}
minHeight={88}
placeholder="详细说明…"
/>
{/* 错误态 */}
<Input
value={text}
onChangeText={setText}
error="格式不正确"
/>
{/* 自定义前后插槽 —— 颜色走 hook */}
function MyForm() {
const c = useColors();
return (
<Input
value={text}
onChangeText={setText}
leading={<Icon name="search" size={18} color={c.foregroundSubtle} />}
trailing={text ? <Icon name="close" size={14} color={c.foregroundSubtle} /> : null}
/>
);
}
API
InputProps = Omit<TextInputProps, 'style' | 'multiline' | 'numberOfLines'> + 这 6 个扩展。所有 RN TextInput props(value / onChangeText / placeholder / secureTextEntry / keyboardType / returnKeyType / autoCapitalize 等)都直接可用。
| Prop | Type | 说明 |
|---|---|---|
leading | ReactNode? | 前置插槽(图标、label 等) |
trailing | ReactNode? | 后置插槽(清除按钮、显示密码切换等) |
error | string? | 错误文字 —— 容器进入 error 态(红描边)并在下方显示 |
height | number? | 覆盖容器高度,默认 dim.controlLg(44) |
disabled | boolean? | 整体禁用 + 视觉变灰,优先级高于 editable |
containerStyle | StyleProp<ViewStyle>? | 容器外层样式(不是 TextInput 的 style) |
testID来自透传的TextInputProps:设在外层容器,内层<TextInput>自动派生${testID}-input。style/multiline/numberOfLines被Omit掉——容器样式走containerStyle,多行请用<Textarea />。
没有内置的
clearable/leadingIcon—— 自己组合<Icon>给leading/trailing即可。Search组件就是基于这种组合的预设。多行文本请用<Textarea />。
无障碍(a11y)
来源:src/components/ui/Input/Input.tsx、TextField/TextFieldBase.tsx、types.ts。
输入框的核心 a11y 来自 RN 内置的 TextInput(Input 是 TextFieldBase 的薄 wrap,最终渲染原生 <TextInput>)。
- 默认
accessibilityRole:不显式设置 —— 继承底层<TextInput>(RN/平台自带文本框语义,无需我们硬编码 role)。 - a11y props:
InputProps=Omit<TextInputProps, 'style' | 'multiline' | 'numberOfLines'>+ 6 个扩展,所以 RNTextInput的全部 a11y / 行为 props(accessibilityLabel、placeholder、keyboardType、secureTextEntry等)都直接透传到<TextInput>。朗读文案优先来自accessibilityLabel,缺省时由placeholder/value提供。 - 包裹层唯一额外设置的 a11y 是:
disabled时对内层<TextInput>设accessibilityState={{ disabled: true }}(同时editable=false,disabled优先级高于editable);非 disabled 时不附加该状态。 error文字渲染为下方独立<Text>,未与输入框做程序化关联(无accessibilityLabelledBy/ describedBy),SR 会作为相邻文本读到。
// placeholder 无法完全替代 label;无可见 label 时显式传 accessibilityLabel
<Input value={phone} onChangeText={setPhone} placeholder="11 位手机号" accessibilityLabel="手机号" keyboardType="phone-pad" />