iter(g335): 输入类组件补可访问名

This commit is contained in:
HIsheR
2026-10-27 02:09:25 +08:00
parent dcf3614d5c
commit 19d5e90006
9 changed files with 71 additions and 10 deletions
+9
View File
@@ -3401,6 +3401,15 @@
"motivation": "对比度门禁有「正文 × 强调淡底」,却没有「正文 / 语义色 × 语义三色淡底」——而提示条、失败步骤、删除悬停底的文字恰恰压在这些淡底上。补上配对后立刻抓到一处真实不达标:亮色警告色 #9a6b1f 压在自家 10% 淡底上只有 4.12(AA 要 4.5),也就是最需要被读清的警告条文字反而不合格。", "motivation": "对比度门禁有「正文 × 强调淡底」,却没有「正文 / 语义色 × 语义三色淡底」——而提示条、失败步骤、删除悬停底的文字恰恰压在这些淡底上。补上配对后立刻抓到一处真实不达标:亮色警告色 #9a6b1f 压在自家 10% 淡底上只有 4.12(AA 要 4.5),也就是最需要被读清的警告条文字反而不合格。",
"acceptance": "① contrast-audit 的 PAIRS 增加 6 组:正文与语义色各自 × 危险 / 成功 / 警告淡底(阈值 4.5),淡底先压到页面底再求值;② 亮色警告色加深到 #8a5d15(页面底 5.74、警告淡底 5.01),tokens.css 与 tokens.json 双源同步(verify 的名字与值校验要过);③ audit:contrast 全部 24 组配对在亮 / 暗 × 常规 / 高对比四套主题下达标,audit:surface 阶梯仍全部达标(淡底变深只会变明显);④ AGENTS 的门禁说明同步到 24 组;⑤ npm run check 全绿。", "acceptance": "① contrast-audit 的 PAIRS 增加 6 组:正文与语义色各自 × 危险 / 成功 / 警告淡底(阈值 4.5),淡底先压到页面底再求值;② 亮色警告色加深到 #8a5d15(页面底 5.74、警告淡底 5.01),tokens.css 与 tokens.json 双源同步(verify 的名字与值校验要过);③ audit:contrast 全部 24 组配对在亮 / 暗 × 常规 / 高对比四套主题下达标,audit:surface 阶梯仍全部达标(淡底变深只会变明显);④ AGENTS 的门禁说明同步到 24 组;⑤ npm run check 全绿。",
"notes": "" "notes": ""
},
{
"id": "g335",
"domain": "tune",
"status": "open",
"title": "输入类组件补可访问名:读屏不再只念「编辑框 / 滑块」",
"motivation": "库内 13 个组件都有 ariaLabel 入参(switchButton / pagination / jsonViewer / codeBlock …),偏偏最常用的四个输入件没有:textField 与 textarea 的控件上没有可访问名(只有清空 / 显隐按钮有),读屏进去只念「编辑框」;slider 的原生 range 无名,区间模式两个滑块更是完全同名,读屏分不出上下限;inputNumber 的「数字输入」写死,业务上没法改成「数量 / 单价」。",
"acceptance": "① TextFieldProps / TextareaProps / InputNumberProps / SliderProps 增可选 ariaLabel,落成控件 aria-label(缺省沿用「文本输入」「多行文本」「数字输入」「数值」);② slider 区间模式自动拼「下限 / 上限」,两个 range 各有名字;③ inputNumber 的写死字符串改成可覆盖,与其余组件同一口径;④ 四份清单文档补 ariaLabel 参数行(verify 的双向字段校验要过);⑤ npm run check 全绿。",
"notes": ""
} }
] ]
} }
+21 -1
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "ui-arch-registry-index/0.1", "$schema": "ui-arch-registry-index/0.1",
"generatedAt": "2026-10-26T18:05:09.130Z", "generatedAt": "2026-10-26T18:08:56.364Z",
"repoRoot": ".", "repoRoot": ".",
"packages": [ "packages": [
{ {
@@ -444,6 +444,11 @@
"n": "password", "n": "password",
"t": "boolean", "t": "boolean",
"d": "密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本" "d": "密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本"
},
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「收件人」);缺省「文本输入」"
} }
], ],
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })", "example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
@@ -512,6 +517,11 @@
"n": "size", "n": "size",
"t": "'sm' | 'md' | 'lg'", "t": "'sm' | 'md' | 'lg'",
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md" "d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
},
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「数量」);缺省「数字输入」"
} }
], ],
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })", "example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
@@ -574,6 +584,11 @@
"n": "autosize", "n": "autosize",
"t": "boolean | { minRows?, maxRows? }", "t": "boolean | { minRows?, maxRows? }",
"d": "自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数" "d": "自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数"
},
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「备注」);缺省「多行文本」"
} }
], ],
"example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })", "example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })",
@@ -651,6 +666,11 @@
"t": "(value: number) => void", "t": "(value: number) => void",
"k": 1, "k": 1,
"d": "拖动回调" "d": "拖动回调"
},
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「音量」);缺省「数值」,区间模式自动带「下限 / 上限」"
} }
], ],
"example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })", "example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })",
+5 -1
View File
@@ -1,6 +1,6 @@
# @ui-arch 组件库 Agent 文档(无图形版) # @ui-arch 组件库 Agent 文档(无图形版)
> 自动生成于 2026-10-26T18:05:09.130Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 自动生成于 2026-10-26T18:08:56.364Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
## 快速上手 ## 快速上手
@@ -125,6 +125,7 @@ button({ label: '提交', variant: 'primary', onClick: () => console.log('clicke
- maxCount:number —— 字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限 - maxCount:number —— 字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限
- size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md - size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md
- password:boolean —— 密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本 - password:boolean —— 密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本
- ariaLabel:string —— 无可见 label 时的可访问名(如「收件人」);缺省「文本输入」
示例: 示例:
@@ -156,6 +157,7 @@ const name = signal(''); const el = textField({ value: name, placeholder: '输
- prefix:string | HTMLElement —— 前置附加块(textField/inputNumber 在输入框内侧前、textarea 在上方);缺省不渲染 - prefix:string | HTMLElement —— 前置附加块(textField/inputNumber 在输入框内侧前、textarea 在上方);缺省不渲染
- suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染 - suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染
- size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md - size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md
- ariaLabel:string —— 无可见 label 时的可访问名(如「数量」);缺省「数字输入」
示例: 示例:
@@ -186,6 +188,7 @@ const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onC
- suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染 - suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染
- maxCount:number —— 字数上限:同时设原生 maxlength,并在下方附加块显示实时计数;缺省不限 - maxCount:number —— 字数上限:同时设原生 maxlength,并在下方附加块显示实时计数;缺省不限
- autosize:boolean | { minRows?, maxRows? } —— 自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数 - autosize:boolean | { minRows?, maxRows? } —— 自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数
- ariaLabel:string —— 无可见 label 时的可访问名(如「备注」);缺省「多行文本」
示例: 示例:
@@ -217,6 +220,7 @@ const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话
- end:Signal<number> —— 区间上限信号;仅 range 模式使用,不传则组件内部维护 - end:Signal<number> —— 区间上限信号;仅 range 模式使用,不传则组件内部维护
- onRangeChange(回调):(low: number, high: number) => void —— 区间两端变更回调,参数已保证 low ≤ high(越界那一端会被压平) - onRangeChange(回调):(low: number, high: number) => void —— 区间两端变更回调,参数已保证 low ≤ high(越界那一端会被压平)
- onChange(回调):(value: number) => void —— 拖动回调 - onChange(回调):(value: number) => void —— 拖动回调
- ariaLabel:string —— 无可见 label 时的可访问名(如「音量」);缺省「数值」,区间模式自动带「下限 / 上限」
示例: 示例:
+1 -1
View File
@@ -11,7 +11,7 @@ export function inputNumber(props: InputNumberProps): HTMLElement {
const clamp = (v: number): number => Math.min(max, Math.max(min, v)); // 任何来源的值都夹回合法区间 const clamp = (v: number): number => Math.min(max, Math.max(min, v)); // 任何来源的值都夹回合法区间
const quantize = (n: number): number => (props.precision === undefined ? n : Number(n.toFixed(props.precision))); // precision 归一(顺带消除步进浮点尾差) const quantize = (n: number): number => (props.precision === undefined ? n : Number(n.toFixed(props.precision))); // precision 归一(顺带消除步进浮点尾差)
const emit = (v: number): void => { const n = quantize(clamp(v)); props.value(n); if (props.onChange) props.onChange(n); }; // 写回信号并回调 const emit = (v: number): void => { const n = quantize(clamp(v)); props.value(n); if (props.onChange) props.onChange(n); }; // 写回信号并回调
const input = h('input', { class: `ua-input ua-input-number-field${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, inputmode: 'decimal', disabled: props.disabled, 'aria-label': '数字输入' }) as HTMLInputElement; const input = h('input', { class: `ua-input ua-input-number-field${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, inputmode: 'decimal', disabled: props.disabled, 'aria-label': props.ariaLabel ?? '数字输入' }) as HTMLInputElement;
const sync = (): void => { const text = props.precision === undefined ? String(props.value()) : props.value().toFixed(props.precision); if (input.value !== text) input.value = text; }; // 外部变化写回输入框(precision 定长显示) const sync = (): void => { const text = props.precision === undefined ? String(props.value()) : props.value().toFixed(props.precision); if (input.value !== text) input.value = text; }; // 外部变化写回输入框(precision 定长显示)
effect(sync); effect(sync);
input.addEventListener('change', () => { const n = parseFloat(input.value); if (Number.isNaN(n)) sync(); else emit(n); }); // 非法输入回滚当前值 input.addEventListener('change', () => { const n = parseFloat(input.value); if (Number.isNaN(n)) sync(); else emit(n); }); // 非法输入回滚当前值
+5 -5
View File
@@ -13,8 +13,8 @@ export function slider(props: SliderProps): HTMLElement {
const pct = (v: number): number => ((v - min) / (max - min || 1)) * 100; // 分母兜底,避免 min=max 时除零 const pct = (v: number): number => ((v - min) / (max - min || 1)) * 100; // 分母兜底,避免 min=max 时除零
/*** 建一条原生 range:区间模式靠 CSS 叠在同一格,只有拇指可命中 ***/ /*** 建一条原生 range:区间模式靠 CSS 叠在同一格,只有拇指可命中 ***/
const track = (sig: typeof value, onInput: (n: number) => void): HTMLInputElement => { const track = (sig: typeof value, onInput: (n: number) => void, label: string): HTMLInputElement => {
const input = h('input', { type: 'range', class: 'ua-slider-input', min, max, step, disabled }) as HTMLInputElement; const input = h('input', { type: 'range', class: 'ua-slider-input', min, max, step, disabled, 'aria-label': label }) as HTMLInputElement; // 原生 range 不自带名字,读屏只念「滑块」
input.addEventListener('input', () => onInput(Number(input.value))); input.addEventListener('input', () => onInput(Number(input.value)));
effect(() => { effect(() => {
const next = String(sig()); const next = String(sig());
@@ -30,7 +30,7 @@ export function slider(props: SliderProps): HTMLElement {
: null; : null;
if (!props.range) { // —— 单值:一条原生轨,行为与旧版完全一致 —— if (!props.range) { // —— 单值:一条原生轨,行为与旧版完全一致 ——
const input = track(value, (next) => { value(next); props.onChange?.(next); }); const input = track(value, (next) => { value(next); props.onChange?.(next); }, props.ariaLabel ?? '数值');
return h( return h(
'div', 'div',
{ class: 'ua-slider' }, { class: 'ua-slider' },
@@ -42,8 +42,8 @@ export function slider(props: SliderProps): HTMLElement {
// —— 区间:两条原生轨叠放,rail 打底、seg 画出两拇指之间的已选段 —— // —— 区间:两条原生轨叠放,rail 打底、seg 画出两拇指之间的已选段 ——
const rail = h('div', { class: 'ua-slider-rail', 'aria-hidden': 'true' }); const rail = h('div', { class: 'ua-slider-rail', 'aria-hidden': 'true' });
const seg = h('div', { class: 'ua-slider-seg', 'aria-hidden': 'true' }) as HTMLElement; const seg = h('div', { class: 'ua-slider-seg', 'aria-hidden': 'true' }) as HTMLElement;
const lowIn = track(value, (next) => { value(Math.min(next, high())); props.onRangeChange?.(value(), high()); }); // 下限越过上限就压平 const lowIn = track(value, (next) => { value(Math.min(next, high())); props.onRangeChange?.(value(), high()); }, `${props.ariaLabel ?? '数值'}下限`); // 下限越过上限就压平;区间两端各有名字
const highIn = track(high, (next) => { high(Math.max(next, value())); props.onRangeChange?.(value(), high()); }); // 上限低于下限就压平 const highIn = track(high, (next) => { high(Math.max(next, value())); props.onRangeChange?.(value(), high()); }, `${props.ariaLabel ?? '数值'}上限`); // 上限低于下限就压平
lowIn.classList.add('ua-slider-input-low'); // 供 CSS 区分上下层与拇指命中区 lowIn.classList.add('ua-slider-input-low'); // 供 CSS 区分上下层与拇指命中区
highIn.classList.add('ua-slider-input-high'); highIn.classList.add('ua-slider-input-high');
effect(() => { seg.style.left = `${pct(value())}%`; seg.style.width = `${pct(high()) - pct(value())}%`; }); // 已选段随两端重算 effect(() => { seg.style.left = `${pct(value())}%`; seg.style.width = `${pct(high()) - pct(value())}%`; }); // 已选段随两端重算
+1 -1
View File
@@ -4,7 +4,7 @@ import type { TextFieldProps } from './types.js';
/*** 文本输入:value 信号双向绑定,disabled 支持布尔或信号禁用,onInput 为输入回调;prefix/suffix 包附加组,clearable 一键清空,password 密码显隐切换,maxCount 显示字数统计 ***/ /*** 文本输入:value 信号双向绑定,disabled 支持布尔或信号禁用,onInput 为输入回调;prefix/suffix 包附加组,clearable 一键清空,password 密码显隐切换,maxCount 显示字数统计 ***/
export function textField(props: TextFieldProps): HTMLElement { export function textField(props: TextFieldProps): HTMLElement {
const el = h('input', { class: `ua-input${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, placeholder: props.placeholder ?? '', disabled: props.disabled }) as HTMLInputElement; // false/undefined 移除属性,Signal 走响应式 const el = h('input', { class: `ua-input${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, placeholder: props.placeholder ?? '', disabled: props.disabled, 'aria-label': props.ariaLabel ?? '文本输入' }) as HTMLInputElement; // false/undefined 移除属性,Signal 走响应式
if (props.value) { if (props.value) {
const signal = props.value; const signal = props.value;
el.addEventListener('input', () => signal(el.value)); // 用户输入写回信号 el.addEventListener('input', () => signal(el.value)); // 用户输入写回信号
+1 -1
View File
@@ -4,7 +4,7 @@ import type { TextareaProps } from './types.js';
/*** 多行输入:value 信号双向绑定,disabled 支持布尔或信号禁用,onInput 为输入回调;prefix/suffix 包纵向附加组,maxCount 显示字数统计 ***/ /*** 多行输入:value 信号双向绑定,disabled 支持布尔或信号禁用,onInput 为输入回调;prefix/suffix 包纵向附加组,maxCount 显示字数统计 ***/
export function textarea(props: TextareaProps): HTMLElement { export function textarea(props: TextareaProps): HTMLElement {
const el = h('textarea', { class: 'ua-textarea', rows: props.rows ?? 3, placeholder: props.placeholder ?? '', disabled: props.disabled }) as HTMLTextAreaElement; // false/undefined 移除属性,Signal 走响应式 const el = h('textarea', { class: 'ua-textarea', rows: props.rows ?? 3, placeholder: props.placeholder ?? '', disabled: props.disabled, 'aria-label': props.ariaLabel ?? '多行文本' }) as HTMLTextAreaElement; // false/undefined 移除属性,Signal 走响应式
if (props.value) { if (props.value) {
const signal = props.value; const signal = props.value;
el.addEventListener('input', () => signal(el.value)); // 用户输入写回信号 el.addEventListener('input', () => signal(el.value)); // 用户输入写回信号
+4
View File
@@ -32,6 +32,7 @@ export interface ButtonProps {
/*** 文本输入入参 ***/ /*** 文本输入入参 ***/
export interface TextFieldProps { export interface TextFieldProps {
ariaLabel?: string; // 读屏用的字段名(如「收件人」);缺省「文本输入」
size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md
placeholder?: string; // 占位文案 placeholder?: string; // 占位文案
value?: Signal<string>; // 双向绑定的信号 value?: Signal<string>; // 双向绑定的信号
@@ -189,6 +190,7 @@ export interface AccordionProps {
/*** 滑块入参 ***/ /*** 滑块入参 ***/
export interface SliderProps { export interface SliderProps {
ariaLabel?: string; // 读屏用的字段名(如「音量」);缺省「数值」,区间模式自动带上限/下限
value: Signal<number>; // 当前值信号,双向绑定(拖动写回、外部赋值同步) value: Signal<number>; // 当前值信号,双向绑定(拖动写回、外部赋值同步)
min?: number; // 最小值;缺省 0 min?: number; // 最小值;缺省 0
max?: number; // 最大值;缺省 100 max?: number; // 最大值;缺省 100
@@ -204,6 +206,7 @@ export interface SliderProps {
/*** 多行输入入参 ***/ /*** 多行输入入参 ***/
export interface TextareaProps { export interface TextareaProps {
ariaLabel?: string; // 读屏用的字段名(如「备注」);缺省「多行文本」
placeholder?: string; // 占位文案 placeholder?: string; // 占位文案
rows?: number; // 行数;缺省 3 rows?: number; // 行数;缺省 3
value?: Signal<string>; // 双向绑定的信号 value?: Signal<string>; // 双向绑定的信号
@@ -217,6 +220,7 @@ export interface TextareaProps {
/*** 数字输入入参 ***/ /*** 数字输入入参 ***/
export interface InputNumberProps { export interface InputNumberProps {
ariaLabel?: string; // 读屏用的字段名(如「数量」);缺省「数字输入」
size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md
value: Signal<number>; // 当前值信号,双向绑定(按钮/键盘写回、外部赋值同步) value: Signal<number>; // 当前值信号,双向绑定(按钮/键盘写回、外部赋值同步)
min?: number; // 最小值;缺省无下限 min?: number; // 最小值;缺省无下限
+24
View File
@@ -315,6 +315,12 @@
"t": "boolean", "t": "boolean",
"d": "密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本" "d": "密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本"
} }
,
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「收件人」);缺省「文本输入」"
}
], ],
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })", "example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
"steps": [ "steps": [
@@ -383,6 +389,12 @@
"t": "'sm' | 'md' | 'lg'", "t": "'sm' | 'md' | 'lg'",
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md" "d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
} }
,
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「数量」);缺省「数字输入」"
}
], ],
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })", "example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
"steps": [ "steps": [
@@ -445,6 +457,12 @@
"t": "boolean | { minRows?, maxRows? }", "t": "boolean | { minRows?, maxRows? }",
"d": "自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数" "d": "自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数"
} }
,
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「备注」);缺省「多行文本」"
}
], ],
"example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })", "example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })",
"steps": [ "steps": [
@@ -522,6 +540,12 @@
"k": 1, "k": 1,
"d": "拖动回调" "d": "拖动回调"
} }
,
{
"n": "ariaLabel",
"t": "string",
"d": "无可见 label 时的可访问名(如「音量」);缺省「数值」,区间模式自动带「下限 / 上限」"
}
], ],
"example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })", "example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })",
"steps": [ "steps": [