跳转到内容

NumberField 数字字段 ​

带加减与区间约束的数值输入。

用法 ​

加减按钮与输入框共用一份状态;值是原始输入串,不传 value 即为非受控

组件结构 ​

加粗的是必需部件。

data-scope="number-field":root · label · control · prefix · input · suffix · increment-trigger · decrement-trigger

示例 ​

区间与步长 ​

方向键按 step,PageUp 与 PageDown 按 largeStep,Home 与 End 取端点;到达边界时对应按钮转灰

数值:10 · 可加:是 · 可减:是

受控 ​

传入 value 后由宿主决定;value-change 除原始串外还带一份 valueAsNumber

输入串:3 · 数值:3

禁用与只读 ​

两者都不可修改值,禁用还会把加减按钮一并关闭、值也不再随表单提交

变体 ​

variant 只改变皮肤使用颜色的方式,加减与键盘行为三档完全一致

颜色 ​

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

尺寸 ​

输入框高度与加减按钮一起换档,不传 size 即默认档

只用输入框 ​

control 仍是必需的输入外壳;加减按钮可以省略,键盘仍按 step 与 largeStep 修改值

点进框里按上下键:60

校验态 ​

invalid 由宿主自行判定,不必挂在表单上;标注之后值照常可以修改、加减按钮照常可以按下

库存只有 5 件

框内单位与货币符号 ​

前后缀图标/文字直接以流式插入 control,减、加按钮统一收在右侧

¥元
g

自定义换算 ​

parse 把显示串读为数值、format 把数值写回显示串;两个方向必须互逆,否则按一下加号值就会漂移

金额:1,234 · 体重:60 kg

设计指引 ​

何时使用 ​

  • 数量、价格、百分比等需要精确到某一位的数值。
  • 需要步进(键盘上下键、加减按钮)。

何时不用 ​

  • 用户更关心相对位置而非精确值时,使用滑块。
  • 值实际是编号或电话(不参与运算)时,使用文本字段,数字字段的千分位与步进会造成干扰。

特性 ​

  • step 与 largeStep 分别对应方向键和 PageUp / PageDown。
  • 长按加减按钮连续步进,首次延时与间隔都可调。
  • parse / format 成对,用于接入固定小数位、千分位、货币符号或自定义换算。
  • 越界的值在失焦规范化时被夹回区间。
  • prefix / suffix 在框内放置货币符、单位或图标,两段对读屏隐藏。
  • control 是必需部件,也是输入、前后缀与两个动作共用的唯一视觉盒,投影 Field Chrome 家族(data-xh-field-chrome、data-xh-field-size、data-variant),输入与前后缀分别投影 data-xh-field-input 与 data-xh-field-affix;默认即 outline:--xh-bg-canvas 底、--xh-border-control 描边、control 圆角、无阴影,不写 variant 时 root 与 control 都落 data-variant="outline",悬停与聚焦由整体盒统一反馈,聚焦描边一律 --xh-border-control-focus。减、加两颗动作依次收在右侧,走 Action Control 的 field-inset ghost 档:正方视觉盒、inset 圆角、在控件里垂直居中,悬停 --xh-bg-subtle(100)、按下 --xh-bg-subtle-hover(200)中性底并带 0.97 按压缩放,粗指针命中区由家族伪元素外扩到 44px。
  • subtle 为中性填充、ghost 为透明底,两者在悬停与聚焦时浮出描边;三档都由统一输入壳承担交互反馈。
  • comfortable 下 sm / md / lg 控件高为 32 / 36 / 40px;compact 下分别为 28 / 32 / 36px。 右侧动作区宽度跟随密度档,数字使用等宽字形,前缀、数值和后缀共用中线。
  • 粗指针环境不放大视觉盒:控件高与两颗钮的正方盒保持原档,命中区由家族 ::after 伪元素以钮盒中心外扩到至少 44px,control 保持 overflow: visible 不裁掉它。热区比钮盒大,相邻两颗钮的热区会彼此重叠并伸进输入区边缘;指针落在重叠处时由排在后面的增钮接收,落在输入区边缘的外扩带时由相邻的那颗钮接收。
  • Tab 只停在 spinbutton 输入框,聚焦环由整个 control 统一绘制;加减按钮退出 Tab 序列,但仍可由指针和公开 API 操作。到达 min / max 时只禁用对应方向,disabled / readOnly 才同时锁住两侧。
  • 输入与右侧动作组之间使用一条半高、垂直居中的柔和分隔线,画在减钮的背景层上;RTL 下换到另一边。

组合 ​

  • 外层放表单字段;单位与货币符号放进框内前后缀。

最佳实践 ​

  • 提供 min / max,让键盘用户按住方向键时有边界。
  • 显示格式与提交值分开:显示可以带千分位,提交的是纯数值。
  • 自定义加减按钮尺寸时同步检查窄容器与粗指针;两颗钮的视觉盒不能覆盖输入区,也不能彼此相交,粗指针热区的外扩与重叠由家族承担,不必也不应自己再放大钮盒。

当前边界 ​

  • 默认解析使用严格的 Number() 语义,不识别本地化小数分隔符;需要千分位、逗号小数或单位时,显式提供互逆的 parse / format。组件不推测 locale。
  • 空串与非法文本以原串保留,失焦不会改写为另一个数;此时调用步进会从 min(有值时)或 0 开始。业务校验和错误文案由表单层提供。
  • 长按按固定节奏重复:默认先等待 300ms,再每 50ms 步进一次;尚未提供加速曲线。
  • 输入使用 type="text" 与 inputmode="decimal",组件不接管滚轮,避免页面滚动时意外改值。
  • 当前结构是 control 内水平排列的可选减号、必需输入与可选加号;不支持脱离 control 的三件并排,也不提供上下堆叠动作。

反模式 ​

  • 加减按钮过小,这是移动端最常见的误触来源。
  • 用它输入年份、邮编、身份证号。

API 参考 ​

产物 ​

层值
自定义元素<xh-number-field>
Vue 组件XhNumberFieldControl XhNumberFieldDecrementTrigger XhNumberFieldIncrementTrigger XhNumberFieldInput XhNumberFieldLabel XhNumberFieldPrefix XhNumberFieldRoot XhNumberFieldSuffix
组合式函数useNumberField
状态机numberFieldMachine
皮肤@xihan-ui/styles/number-field.css

Props ​

属性类型必填说明
valuestring
defaultValuestring
minnumber
maxnumber
stepnumber方向键与加减按钮的步长,默认 1。
largeStepnumberPageUp / PageDown 的步长,默认 10 倍 step。
disabledboolean
readOnlyboolean
requiredboolean
invalidboolean
namestring表单字段名;提供后才参与提交。
changeDelaynumber按住加减按钮多久开始连发,默认 300ms。
changeIntervalnumber连发间隔,默认 50ms。
variantControlVariant形态:outline / subtle / ghost,决定底色与描边的绘制方式。默认 outline。
toneTone语气:brand / neutral / success / warning / danger / info,决定聚焦强调使用哪族颜色。
sizeSize尺寸:sm / md / lg,决定输入框与加减按钮的几何档位。
parse(text: string) => number显示串 → 数。默认按 Number() 读取('12abc' 判为非法),提供后替换为它: 千位分隔符、单位后缀、百分号等都依靠它读回。无法读出数时返回 NaN。 与 format 必须互逆:format 输出的串要能被 parse 读回同一个数, 否则按一次加号值会漂移。
format(value: number) => string数 → 显示串。默认 String(n)。只在组件自行改写显示时使用:步进、取端点、 失焦规范化三处;用户正在输入时一律不触碰,否则光标会被打断。
onValueChange(details: NumberFieldValueChangeDetails) => void

事件 ​

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

事件载荷说明
value-changeNumberFieldValueChangeDetails值变化;detail 为 { value: string, valueAsNumber: number }

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhNumberFieldRootdefaultNumberFieldRootSlotProps

React 适配器 props ​

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

React 组件属性类型必填说明
XhNumberFieldRootchildrenSlotChildren<NumberFieldRootSlotProps>

状态 ​

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

状态:idle · spinning

事件:VALUE.SET · VALUE.STEP · VALUE.TO_MIN · VALUE.TO_MAX · INPUT.BLUR · PRESS.START · PRESS.END · after.changeInterval · FORM.RESET · TRIGGER.PRESS.START · TRIGGER.PRESS.END

判据:canStep · canPressTrigger

connect API ​

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

成员类型说明
valuestring
valueAsNumbernumber
emptyboolean值为空或非法。
disabledboolean
readOnlyboolean
invalidboolean
canIncrementboolean
canDecrementboolean
setValue(next: string) => void
increment() => void
decrement() => void
getRootProps() => T['element']
getLabelProps() => T['label']
getControlProps() => T['element']必需的唯一输入壳:皮肤把视觉盒绘制在它身上,输入在左,减、加动作依次收在右侧。
getPrefixProps() => T['element']输入框前的装饰段(货币符、单位、图标);对读屏隐藏,不参与名字链。
getInputProps() => T['input']
getSuffixProps() => T['element']输入框后的装饰段;对读屏隐藏,不参与名字链。
getIncrementTriggerProps() => T['button']
getDecrementTriggerProps() => T['button']

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
ArrowUpfocus in input, not disabled/readOnly按 step 递增,越界则停在 max
ArrowDownfocus in input, not disabled/readOnly按 step 递减,越界则停在 min
PageUpfocus in input, not disabled/readOnly按 largeStep 递增(默认 10 倍 step)
PageDownfocus in input, not disabled/readOnly按 largeStep 递减
Homefocus in input, 指定了 min取 min;未指定 min 时不动
Endfocus in input, 指定了 max取 max;未指定 max 时不动
Enter / Spaceheld in increment-trigger / decrement-trigger, not disabled/readOnly, 该侧未贴住端点按住期间这颗钮投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,值贴到端点后按钮转 disabled 一并撤下。步进仍由激活时的 click 走一步,按住不连发。两颗钮不占 Tab 位,键盘这一路只在焦点落到它身上时有面

ARIA ​

以下属性由 connect 生成。

部件属性值
prefixaria-hidden'true'
inputaria-invalid'true' | 'false'
inputaria-labelledbylabel 部件的 id
inputaria-valuemaxprops.max
inputaria-valueminprops.min
inputaria-valuenowundefined | decodeNumber(value, { parse: prop('parse'), format: p…
inputrole'spinbutton'
suffixaria-hidden'true'
increment-triggeraria-hidden'true'
decrement-triggeraria-hidden'true'

样式参考 ​

皮肤 ​

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

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性 ​

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

部件属性值
rootdata-disabled''(条件成立时才出现)
rootdata-empty''(条件成立时才出现)
rootdata-invalid''(条件成立时才出现)
rootdata-readonly''(条件成立时才出现)
rootdata-sizeprops.size
rootdata-toneprops.tone
rootdata-variantprops.variant
labeldata-disabled''(条件成立时才出现)
controldata-disabled''(条件成立时才出现)
controldata-invalid''(条件成立时才出现)
controldata-readonly''(条件成立时才出现)
controldata-variantprops.variant
controldata-xh-field-chrome''
controldata-xh-field-sizeprops.size
prefixdata-disabled''(条件成立时才出现)
prefixdata-xh-field-affix'prefix'
inputdata-disabled''(条件成立时才出现)
inputdata-invalid''(条件成立时才出现)
inputdata-readonly''(条件成立时才出现)
inputdata-xh-field-input''
inputdata-xh-field-layout'single-line'
suffixdata-disabled''(条件成立时才出现)
suffixdata-xh-field-affix'suffix'
increment-triggerdata-disabled''(条件成立时才出现)
increment-triggerdata-pressed''(条件成立时才出现)
increment-triggerdata-xh-action-control''
increment-triggerdata-xh-action-display'always'
increment-triggerdata-xh-action-profile'field-inset'
increment-triggerdata-xh-action-sizeprops.size
increment-triggerdata-xh-action-variant'ghost'
decrement-triggerdata-disabled''(条件成立时才出现)
decrement-triggerdata-pressed''(条件成立时才出现)
decrement-triggerdata-xh-action-control''
decrement-triggerdata-xh-action-display'always'
decrement-triggerdata-xh-action-profile'field-inset'
decrement-triggerdata-xh-action-sizeprops.size
decrement-triggerdata-xh-action-variant'ghost'

CSS 变量 ​

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

变量部件CSS 属性状态默认来源说明
--xh-number-field-affix-fgprefix
suffix
colorxh-field-affix--xh-fg-mutednumber-field 的 prefix、suffix 部件 color 覆盖槽。
--xh-number-field-affix-fg-disabledprefix
suffix
colordisabled
xh-field-affix
--xh-fg-disablednumber-field 的 prefix、suffix 部件 color 覆盖槽。
--xh-number-field-affix-font-sizeprefix
suffix
font-sizexh-field-affix--xh-_number-field-font-sizenumber-field 的 prefix、suffix 部件 font-size 覆盖槽。
--xh-number-field-control-bgcontrolbackground-colorxh-field-chrome--xh-_field-variant-bg-restnumber-field 的 control 部件 background-color 覆盖槽。
--xh-number-field-control-bg-disabledcontrolbackground-colordisabled
xh-field-chrome
--xh-_field-variant-bg-disablednumber-field 的 control 部件 background-color 覆盖槽。
--xh-number-field-control-bg-hovercontrolbackground-colordisabled
hover
invalid
loading
not([data-disabled])
not([data-invalid])
not([data-loading])
not([data-readonly])
readonly
xh-field-chrome
--xh-_field-variant-bg-hovernumber-field 的 control 部件 background-color 覆盖槽。
--xh-number-field-control-bg-readonlycontrolbackground-colorreadonly
xh-field-chrome
--xh-_field-variant-bg-read-onlynumber-field 的 control 部件 background-color 覆盖槽。
--xh-number-field-control-bordercontrolborderxh-field-chrome--xh-_field-variant-border-restnumber-field 的 control 部件 border 覆盖槽。
--xh-number-field-control-border-focuscontrolborder-colordisabled
focus-within
not([data-disabled])
xh-field-chrome
--xh-_field-variant-border-focusnumber-field 的 control 部件 border-color 覆盖槽。
--xh-number-field-control-border-hovercontrolborder-colordisabled
hover
invalid
loading
not([data-disabled])
not([data-invalid])
not([data-loading])
not([data-readonly])
readonly
xh-field-chrome
--xh-_field-variant-border-hovernumber-field 的 control 部件 border-color 覆盖槽。
--xh-number-field-control-border-invalidcontrolborder-colorinvalid
xh-field-chrome
--xh-_field-variant-border-invalidnumber-field 的 control 部件 border-color 覆盖槽。
--xh-number-field-control-fgcontrolcolorxh-field-chrome--xh-fg-defaultnumber-field 的 control 部件 color 覆盖槽。
--xh-number-field-control-gapcontrolgapxh-field-chrome--xh-_number-field-gapnumber-field 的 control 部件 gap 覆盖槽。
--xh-number-field-control-hcontrolblock-size
min-block-size
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-_number-field-hnumber-field 的 control 部件 block-size、min-block-size 覆盖槽。
--xh-number-field-control-min-wcontrol
root
min-inline-sizedefault
xh-field-chrome
--xh-control-min-wnumber-field 的 control、root 部件 min-inline-size 覆盖槽。
--xh-number-field-control-pxcontrolpadding-inlinexh-field-chrome0number-field 的 control 部件 padding-inline 覆盖槽。
--xh-number-field-control-radiuscontrolborder-radiusxh-field-chrome--xh-shape-controlnumber-field 的 control 部件 border-radius 覆盖槽。
--xh-number-field-control-shadowcontrolbox-shadowxh-field-chromenonenumber-field 的 control 部件 box-shadow 覆盖槽。
--xh-number-field-control-wrootinline-size
min-inline-size
default--xh-control-wnumber-field 的 root 部件 inline-size、min-inline-size 覆盖槽。
--xh-number-field-gaprootgapdefault--xh-space-1number-field 的 root 部件 gap 覆盖槽。
--xh-number-field-icon-sizecontrol
root
--xh-icon-sizedefault
size=lg
size=sm
xh-field-chrome
--xh-_field-size-glyph-size
--xh-glyph-size-lg
--xh-glyph-size-md
--xh-glyph-size-sm
number-field 的 control、root 部件 --xh-icon-size 覆盖槽。
--xh-number-field-input-aligncontrol
input
text-aligndefaultcenternumber-field 的 control、input 部件 text-align 覆盖槽。
--xh-number-field-input-autofill-bgcontrol
input
box-shadow-webkit-autofill
autofill
xh-field-input
--xh-bg-canvasnumber-field 的 control、input 部件 box-shadow 覆盖槽。
--xh-number-field-input-autofill-fgcontrol
input
-webkit-text-fill-color-webkit-autofill
autofill
xh-field-input
--xh-fg-defaultnumber-field 的 control、input 部件 -webkit-text-fill-color 覆盖槽。
--xh-number-field-input-fgcontrol
input
colorxh-field-input--xh-fg-defaultnumber-field 的 control、input 部件 color 覆盖槽。
--xh-number-field-input-font-sizecontrol
input
font-sizexh-field-input--xh-_number-field-font-sizenumber-field 的 control、input 部件 font-size 覆盖槽。
--xh-number-field-input-pxcontrol
input
padding-inlinedefault--xh-_number-field-pxnumber-field 的 control、input 部件 padding-inline 覆盖槽。
--xh-number-field-input-wcontrol
input
inline-sizedefault5emnumber-field 的 control、input 部件 inline-size 覆盖槽。
--xh-number-field-label-fglabelcolordefault--xh-fg-defaultnumber-field 的 label 部件 color 覆盖槽。
--xh-number-field-label-fg-disabledlabelcolordisabled--xh-fg-subtlenumber-field 的 label 部件 color 覆盖槽。
--xh-number-field-label-font-sizelabelfont-sizedefault--xh-text-label-sizenumber-field 的 label 部件 font-size 覆盖槽。
--xh-number-field-label-font-weightlabelfont-weightdefault--xh-text-label-weightnumber-field 的 label 部件 font-weight 覆盖槽。
--xh-number-field-placeholder-fgcontrol
input
colorplaceholder
xh-field-input
--xh-fg-subtlenumber-field 的 control、input 部件 color 覆盖槽。
--xh-number-field-trigger-bg-activecontrol
decrement-trigger
increment-trigger
background-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-bg-pressednumber-field 的 control、decrement-trigger、increment-trigger 部件 background-color 覆盖槽。
--xh-number-field-trigger-dividercontrol
decrement-trigger
background-imagehas([data-part='input'])--xh-material-soft-separatornumber-field 的 control、decrement-trigger 部件 background-image 覆盖槽。
--xh-number-field-trigger-divider-hcontrol
decrement-trigger
background-sizehas([data-part='input'])--xh-_number-field-divider-hnumber-field 的 control、decrement-trigger 部件 background-size 覆盖槽。
--xh-number-field-trigger-fgcontrol
decrement-trigger
increment-trigger
colordefault--xh-fg-defaultnumber-field 的 control、decrement-trigger、increment-trigger 部件 color 覆盖槽。
--xh-number-field-trigger-fg-hovercontrol
decrement-trigger
increment-trigger
colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-fg-defaultnumber-field 的 control、decrement-trigger、increment-trigger 部件 color 覆盖槽。
--xh-number-field-trigger-font-sizecontrol
decrement-trigger
increment-trigger
font-sizedefault--xh-_number-field-trigger-font-sizenumber-field 的 control、decrement-trigger、increment-trigger 部件 font-size 覆盖槽。
--xh-number-field-trigger-radiuscontrol
decrement-trigger
increment-trigger
border-radiusdefault--xh-shape-insetnumber-field 的 control、decrement-trigger、increment-trigger 部件 border-radius 覆盖槽。
--xh-number-field-trigger-sizecontrol
decrement-trigger
increment-trigger
block-size
inline-size
min-inline-size
default
xh-action-profile=field-inset
--xh-_action-profile-visual-sizenumber-field 的 control、decrement-trigger、increment-trigger 部件 block-size、inline-size、min-inline-size 覆盖槽。

动效 ​

动效角色:按压 · 状态(见动效规范)。

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

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像;另有按 dir 分支的规则。

Released under The MIT License