跳转到内容

PinInput 分格输入 ​

把一串短码拆成若干格子,一格一个字符。

用法 ​

每格都是原生输入框,输入一个字符自动跳到下一格;粘贴整串会从落点格起按格铺开

组件结构 ​

加粗的是必需部件。

data-scope="pin-input":root · label · group · input · separator · hidden-input

示例 ​

一次性验证码 ​

otp 补上 autocomplete=one-time-code,隐藏输入把拼接后的整串交给表单,填满时发出 value-complete

填满时拿到:(未填满)

遮蔽与字符类别 ​

mask 把每格转为密码框,type 决定哪类字符可以输入,其余按键既不进入值也不留在框中

禁用与校验失败 ​

disabled 使每格都带原生 disabled 且不参与提交,invalid 只做标注、照常可以修改

变体 ​

variant 只改变每格的颜色槽位,跳格与粘贴铺开的行为三档一致

颜色 ​

tone 决定使用哪族颜色,与 variant 正交;这里固定 outline 只查看语气的差别

尺寸 ​

每格的边长随 size 换档,不传 size 即默认档

分组排布 ​

格子由作者逐个写出,中间可插入任意内容;下标接续排列,跳格与整串粘贴仍按文档序进行

—

填满才可提交 ​

每格都有字符才算填满,作者据此启用提交按钮;重填一次清空整组

四格都填满才能提交

只读 ​

格子带上原生 readonly,值受控且宿主不回写:可聚焦、可选中复制,不可修改

点进任意一格可以选中复制,敲键盘与粘贴都改不动它。

自定义准入字符 ​

pattern 是一段正则源码,逐个字符整格匹配;写法无效时退回 type 的准入表

设计指引 ​

何时使用 ​

  • 一次性验证码、支付密码、邀请码等定长的短串。

何时不用 ​

  • 长度不固定或较长时,使用文本字段。
  • 输入密码时,使用 type="password" 的文本输入,密码管理器能识别它。

特性 ​

  • otp 开启后接入平台的验证码自动填充。
  • 按顺序录入:焦点落在第一个空格上,尚未轮到的格子既不可点击、也不是 Tab 停靠点;回退修改已填的格子照常可用,填满之后任一格都可修改。readOnly 与 disabled 不设此限制。
  • 粘贴整串时按格拆开填入。
  • mask 遮蔽字符,type 与 pattern 限制可输入的字符类别。
  • onValueComplete 在填满时发出一次,用于自动提交。
  • group 与 separator 把格子分段排列(123-456),下标仍按文档序计算。
  • readOnly 让格子只能查看与复制,required 为每格补上原生必填。

组合 ​

  • 与表单配合,填满后才允许提交。

最佳实践 ​

  • 验证码务必开启 otp,否则短信中的码需要用户手动输入。
  • 填满后自动提交,不让用户再寻找按钮。

反模式 ​

  • 格数超过八个,视觉上不再是一串短码。
  • 遮蔽验证码,用户无法看到输错的位置。

API 参考 ​

产物 ​

层值
自定义元素<xh-pin-input>
Vue 组件XhPinInputGroup XhPinInputHiddenInput XhPinInputInput XhPinInputLabel XhPinInputRoot XhPinInputSeparator
组合式函数usePinInput
状态机pinInputMachine
皮肤@xihan-ui/styles/pin-input.css

Props ​

属性类型必填说明
valuestring[]逐格的值。提供即受控:cell 直读 prop,写入只发 onValueChange 不落内部值。
defaultValuestring[]
lengthnumber格数,默认 6。值的长度恒归一到它。
typePinInputType接受的字符类别,默认 numeric。同时决定移动端弹出的键盘类型。
patternstring自定义准入:一段正则源码,逐个字符整格匹配(内部自动加首尾锚与 u 标志, 因此写 [0-9A-Fa-f] 即可,不必自行写 ^...$)。提供后覆盖 type 的准入表。 弹出的键盘类型仍由 type 决定:准入放宽到字母时需要把 type 一并修改, 否则移动端弹出的仍是数字键盘,用户无法输入这些字符。 无法编译为正则时回退为 type 的准入表,不抛错。
maskboolean遮蔽显示:输入框改为 type=password。
otpboolean一次性验证码:补充 autocomplete=one-time-code,短信验证码才能被系统自动填入。
placeholderstring空格子的占位字符。
disabledboolean禁用:每格都带原生 disabled(不可聚焦、不可输入),隐藏输入不参与提交。
readOnlyboolean只读:每格仍可聚焦、可复制,不可写入;隐藏输入照常参与提交。
requiredboolean必填标注:每格都带原生 required。
invalidboolean校验失败标注。
blurOnCompleteboolean填满即移走焦点,常用于填满后自动提交的表单。
namestring表单字段名;提供后隐藏输入才带 name,整串值随表单一并提交。
variantControlVariant形态:outline / subtle / ghost,决定底色与描边的绘制方式。默认 outline。
toneTone语气:brand / neutral / success / warning / danger / info,决定使用哪族颜色。
sizeSize尺寸:sm / md / lg。
translationsPartial<PinInputTranslations>
onValueChange(details: PinInputValueChangeDetails) => voidvalue 变化意图回调;受控时是唯一出口,非受控时随内部写入一并通知。
onValueComplete(details: PinInputValueChangeDetails) => void每格都填满时触发;值未实际变化时不重复触发。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
value-changePinInputValueChangeDetails值变化;detail 为 { value: string[], valueAsString: string }
value-completePinInputValueChangeDetails每格都填满;detail 同上

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhPinInputRootdefaultPinInputRootSlotProps

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhPinInputInputindexnumber | string是下标由作者声明;兼收字符串,与另外两个适配器读取的是同一份声明。
XhPinInputRootchildrenSlotChildren<PinInputRootSlotProps>

状态 ​

以下名称仅用于内部状态机。

状态:idle

事件:VALUE.SET · VALUE.FILL · VALUE.CLEAR_AT · VALUE.CLEAR · INPUT.FOCUS · INPUT.BLUR · FORM.RESET

判据:canEdit

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
valuestring[]逐格的值,长度恒等于 length。
valueAsStringstring
completeboolean每格都已填满。作者据此启用提交按钮。
lengthnumber
focusedIndexnumber焦点应落在哪一格;焦点在组外时为 -1。按顺序录入时它不会越过第一个空格。
disabledboolean
readOnlyboolean
invalidboolean
setValue(next: string[]) => void
clear() => void
getRootProps() => T['element']
getLabelProps() => T['label']
getGroupProps() => T['element']相邻的几格划为一段(123-456 这类分段写法);纯排版,不参与下标计算。
getInputProps(props: PinInputInputProps) => T['input']
getSeparatorProps() => T['element']段与段之间的分隔;对读屏隐藏,朗读只会打断验证码。
getHiddenInputProps() => T['input']整份验证码的表单出口:一份 type=hidden 的原生输入,随表单提交拼接后的串。

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
ArrowRightfocus in a box, not disabled焦点移到下一格;越不过第一个空格,已在末格则不动,不回绕
ArrowLeftfocus in a box, not disabled焦点移到上一格;已在首格则不动,不回绕
Homefocus in a box, not disabled焦点移到首格
Endfocus in a box, not disabled焦点移到最后一格可落焦的格子:填满时是末格,还有空格时是第一个空格
Backspacefocus in a box, not disabled本格有值则清本格;本格为空则退回上一格并清掉上一格
Deletefocus in a box, not disabled清掉本格,焦点不动

ARIA ​

以下属性由 connect 生成。

部件属性值
rootaria-labelledbylabel 部件的 id
rootrole'group'
inputaria-invalid'true' | 'false'
inputaria-labellabel.input(index + 1, length)
separatoraria-hidden'true'

样式参考 ​

皮肤 ​

@xihan-ui/styles/pin-input.css 使用 [data-scope="pin-input"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-complete''(条件成立时才出现)
rootdata-disabled''(条件成立时才出现)
rootdata-invalid''(条件成立时才出现)
rootdata-readonly''(条件成立时才出现)
rootdata-sizeprops.size
rootdata-toneprops.tone
rootdata-variantprops.variant
labeldata-disabled''(条件成立时才出现)
groupdata-disabled''(条件成立时才出现)
groupdata-invalid''(条件成立时才出现)
inputdata-disabled''(条件成立时才出现)
inputdata-empty''(条件成立时才出现)
inputdata-focus''(条件成立时才出现)
inputdata-indexString(index)
inputdata-invalid''(条件成立时才出现)
inputdata-readonly''(条件成立时才出现)
inputdata-variantprops.variant
inputdata-xh-field-chrome''
inputdata-xh-field-sizeprops.size
separatordata-disabled''(条件成立时才出现)

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-pin-input-box-autofill-bginputbox-shadow-webkit-autofill
autofill
--xh-bg-canvaspin-input 的 input 部件 box-shadow 覆盖槽。
--xh-pin-input-box-autofill-fginput-webkit-text-fill-color-webkit-autofill
autofill
--xh-fg-defaultpin-input 的 input 部件 -webkit-text-fill-color 覆盖槽。
--xh-pin-input-box-bginputbackground-colorxh-field-chrome--xh-_field-variant-bg-restpin-input 的 input 部件 background-color 覆盖槽。
--xh-pin-input-box-bg-disabledinputbackground-colordisabled
xh-field-chrome
--xh-_field-variant-bg-disabledpin-input 的 input 部件 background-color 覆盖槽。
--xh-pin-input-box-bg-hoverinputbackground-colordisabled
hover
invalid
loading
not([data-disabled])
not([data-invalid])
not([data-loading])
not([data-readonly])
readonly
xh-field-chrome
--xh-_field-variant-bg-hoverpin-input 的 input 部件 background-color 覆盖槽。
--xh-pin-input-box-bg-readonlyinputbackground-colorreadonly
xh-field-chrome
--xh-_field-variant-bg-read-onlypin-input 的 input 部件 background-color 覆盖槽。
--xh-pin-input-box-borderinputborderxh-field-chrome--xh-_field-variant-border-restpin-input 的 input 部件 border 覆盖槽。
--xh-pin-input-box-border-completeinput
root
border-colorcomplete
invalid
not([data-invalid], :disabled)
--xh-_pin-input-accentpin-input 的 input、root 部件 border-color 覆盖槽。
--xh-pin-input-box-border-focusinputborder-colordisabled
focus
focus-within
not([data-disabled])
xh-field-chrome
--xh-_field-variant-border-focuspin-input 的 input 部件 border-color 覆盖槽。
--xh-pin-input-box-border-hoverinputborder-colordisabled
hover
invalid
loading
not([data-disabled])
not([data-invalid])
not([data-loading])
not([data-readonly])
readonly
xh-field-chrome
--xh-_field-variant-border-hoverpin-input 的 input 部件 border-color 覆盖槽。
--xh-pin-input-box-border-invalidinputborder-colorinvalid
xh-field-chrome
--xh-_field-variant-border-invalidpin-input 的 input 部件 border-color 覆盖槽。
--xh-pin-input-box-fginputcolorxh-field-chrome--xh-fg-defaultpin-input 的 input 部件 color 覆盖槽。
--xh-pin-input-box-font-sizeinputfont-sizedefault--xh-_pin-input-box-font-sizepin-input 的 input 部件 font-size 覆盖槽。
--xh-pin-input-box-gapinputmargin-inline-startdefault--xh-_pin-input-box-gappin-input 的 input 部件 margin-inline-start 覆盖槽。
--xh-pin-input-box-radiusinputborder-radiusxh-field-chrome--xh-shape-controlpin-input 的 input 部件 border-radius 覆盖槽。
--xh-pin-input-box-shadowinputbox-shadowxh-field-chromenonepin-input 的 input 部件 box-shadow 覆盖槽。
--xh-pin-input-box-sizeinputblock-size
inline-size
min-block-size
default
has([data-xh-field-input][data-xh-field-layout='multi-tag'])
has([data-xh-field-input][data-xh-field-layout='single-line'])
has([data-xh-field-input][data-xh-field-layout='textarea'])
xh-field-chrome
xh-field-input
xh-field-layout=multi-tag
xh-field-layout=single-line
xh-field-layout=textarea
--xh-_pin-input-box-sizepin-input 的 input 部件 block-size、inline-size、min-block-size 覆盖槽。
--xh-pin-input-gaprootgapdefault--xh-space-1pin-input 的 root 部件 gap 覆盖槽。
--xh-pin-input-label-fglabelcolordefault--xh-fg-defaultpin-input 的 label 部件 color 覆盖槽。
--xh-pin-input-label-fg-disabledlabelcolordisabled--xh-fg-subtlepin-input 的 label 部件 color 覆盖槽。
--xh-pin-input-label-font-sizelabelfont-sizedefault--xh-text-label-sizepin-input 的 label 部件 font-size 覆盖槽。
--xh-pin-input-label-font-weightlabelfont-weightdefault--xh-text-label-weightpin-input 的 label 部件 font-weight 覆盖槽。
--xh-pin-input-placeholder-fginputcolorplaceholder--xh-fg-subtlepin-input 的 input 部件 color 覆盖槽。
--xh-pin-input-separator-fgseparatorcolordefault--xh-fg-mutedpin-input 的 separator 部件 color 覆盖槽。
--xh-pin-input-separator-fg-disabledseparatorcolordisabled--xh-fg-disabledpin-input 的 separator 部件 color 覆盖槽。
--xh-pin-input-separator-font-sizeseparatorfont-sizedefault--xh-_pin-input-box-font-sizepin-input 的 separator 部件 font-size 覆盖槽。
--xh-pin-input-separator-gapseparatormargin-inlinedefault--xh-_pin-input-box-gappin-input 的 separator 部件 margin-inline 覆盖槽。

动效 ​

本组件皮肤不含过渡与关键帧,也没有脚本驱动的动效:状态一变,外观立即到位。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License