iter(g91): 表单字段补辅助说明 hint
This commit is contained in:
@@ -1084,6 +1084,14 @@
|
||||
"acceptance": "npm run changelog 重新生成(253 提交),changelog:check 不再报漂移(提交本条后按既有约定会再差一条,属正常);npm run check 全绿",
|
||||
"notes": "自补:发版前刷新步骤,按 g64/g77 的既定方式执行",
|
||||
"status": "done"
|
||||
},
|
||||
{
|
||||
"id": "g91",
|
||||
"title": "表单字段补辅助说明 hint",
|
||||
"motivation": "formItem 只有 label 与错误位,没有「格式提示」的位置:调用方只能把说明塞进 placeholder 或标签里,一报错提示就被替换得无处可看,用户不知道正确格式是什么",
|
||||
"acceptance": "FormItemProps 新增可选 hint,渲染在控件下方;校验失败时 hint 让位(hidden)只留错误文案,校验通过自动恢复;样式走令牌;文档参数表(formItem 前缀)与演示同步;npm run check 全绿",
|
||||
"notes": "自补:表单可用性;不改 formItem 返回句柄与 form 聚合逻辑",
|
||||
"status": "open"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "ui-arch-registry-index/0.1",
|
||||
"generatedAt": "2026-10-26T05:08:07.129Z",
|
||||
"generatedAt": "2026-10-26T05:10:23.912Z",
|
||||
"repoRoot": ".",
|
||||
"packages": [
|
||||
{
|
||||
@@ -1697,6 +1697,11 @@
|
||||
"d": "校验时机;缺省 blur(输入时自动清已报错误)",
|
||||
"df": "blur"
|
||||
},
|
||||
{
|
||||
"n": "(formItem)hint",
|
||||
"t": "string",
|
||||
"d": "控件下方辅助说明(格式提示等);报错时让位给错误文案"
|
||||
},
|
||||
{
|
||||
"n": "onReset",
|
||||
"t": "() => void",
|
||||
@@ -1715,7 +1720,8 @@
|
||||
"触发时机用 input / change / focusout(均会冒泡),因此 checkbox / select / 单选组这类包裹式控件同样能失焦校验",
|
||||
"单行输入框内按 Enter 等同点击提交(textarea 内仍是换行);提交按钮为 type=\"submit\",不会与点击回调重复触发",
|
||||
"rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖",
|
||||
"formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交"
|
||||
"formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交",
|
||||
"需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -421,7 +421,7 @@ const content = h(
|
||||
{ submitText: '注册', onSubmit: () => formResult(`提交成功:${username()} / ${email()} / ${city()}`) },
|
||||
[
|
||||
formItem({ label: '用户名', required: true, value: username, rules: [{ min: 2, max: 12, message: '用户名需 2-12 个字符' }] }, textField({ placeholder: '2-12 个字符', value: username })),
|
||||
formItem({ label: '邮箱', required: true, value: email, rules: [{ pattern: /^[\w.-]+@[\w-]+\.[\w.]+$/, message: '邮箱格式不正确' }] }, textField({ placeholder: 'you@example.com', value: email })),
|
||||
formItem({ label: '邮箱', required: true, value: email, hint: '用于登录与找回密码,不会公开', rules: [{ pattern: /^[\w.-]+@[\w-]+\.[\w.]+$/, message: '邮箱格式不正确' }] }, textField({ placeholder: 'you@example.com', value: email })),
|
||||
formItem({ label: '城市', required: true, value: city }, select({ options: cities.map((c) => ({ value: c, label: c })), value: city, placeholder: '请选择城市…' })),
|
||||
formItem(
|
||||
{ label: '邀请码', required: true, value: inviteCode, rules: [{ validator: async (v) => { await new Promise((r) => setTimeout(r, 400)); return v === 'USED' ? '该邀请码已被使用' : true; }, message: '校验未通过' }] },
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||
|
||||
> 自动生成于 2026-10-26T05:08:07.129Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 自动生成于 2026-10-26T05:10:23.912Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||
|
||||
## 快速上手
|
||||
@@ -900,6 +900,7 @@ const open = signal(false); drawer({ title: '详情', open, width: 360, onClose:
|
||||
- (formItem)required:boolean —— 必填:显示红星并自动追加非空规则
|
||||
- (formItem)rules:FormRule[] —— 校验规则:required / min / max / pattern / validator,按序执行取首个失败
|
||||
- (formItem)validateOn(默认 blur):'input' | 'blur' | 'submit' —— 校验时机;缺省 blur(输入时自动清已报错误)
|
||||
- (formItem)hint:string —— 控件下方辅助说明(格式提示等);报错时让位给错误文案
|
||||
- onReset:() => void —— 重置按钮点击回调;组件不自行清空字段,由调用方重置信号;不传 resetText 不渲染按钮
|
||||
|
||||
示例:
|
||||
@@ -921,6 +922,7 @@ const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label:
|
||||
9. 单行输入框内按 Enter 等同点击提交(textarea 内仍是换行);提交按钮为 type="submit",不会与点击回调重复触发
|
||||
10. rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖
|
||||
11. formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交
|
||||
12. 需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行
|
||||
|
||||
现场演示:apps/docs/demo.html#sec-formlab
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@ export function formItem(props: FormItemProps, control: HTMLElement): FormItemHa
|
||||
const mode = props.validateOn ?? 'blur'; // 校验时机;缺省失焦校验
|
||||
const rules = [...(props.required ? [{ required: true }] : []), ...(props.rules ?? [])]; // 必填规则排最前
|
||||
const error = h('div', { class: 'ua-form-error' });
|
||||
const hint = props.hint ? h('div', { class: 'ua-form-hint' }, props.hint) : null; // 辅助说明:报错时隐藏,避免与错误挤在一起
|
||||
const label = h('label', { class: 'ua-form-label' }, props.label, props.required ? h('i', { class: 'ua-form-req' }, '*') : null);
|
||||
const el = h('div', { class: 'ua-form-item' }, label, h('div', { class: 'ua-form-control' }, control, error));
|
||||
const el = h('div', { class: 'ua-form-item' }, label, h('div', { class: 'ua-form-control' }, control, hint, error));
|
||||
let seq = 0; // 校验轮次:异步结论回来时若已有更新的一轮,丢弃旧结果(防乱序覆盖)
|
||||
|
||||
/*** 错误态落点:控件根 + 内部原生控件,保证 select / checkbox 这类包裹式控件也能标红 ***/
|
||||
@@ -48,6 +49,7 @@ export function formItem(props: FormItemProps, control: HTMLElement): FormItemHa
|
||||
/*** 落笔:错误文案 + 控件红框 + aria-invalid,视觉与读屏同源 ***/
|
||||
const paint = (msg: string | null): void => {
|
||||
error.textContent = msg ?? '';
|
||||
if (hint) hint.hidden = !!msg; // 有错时让位:同一位置只留一条提示
|
||||
control.classList.toggle('ua-invalid', !!msg);
|
||||
for (const node of marked()) node.setAttribute('aria-invalid', String(!!msg));
|
||||
};
|
||||
|
||||
@@ -363,6 +363,7 @@ export interface FormItemProps {
|
||||
required?: boolean; // 必填:显示红星并自动追加非空规则
|
||||
rules?: FormRule[]; // 附加校验规则,按序执行
|
||||
validateOn?: 'input' | 'blur' | 'submit'; // 校验时机;缺省 blur
|
||||
hint?: string; // 控件下方辅助说明(格式提示等);报错时让位给错误文案
|
||||
}
|
||||
|
||||
/*** 表单字段句柄:form 聚合校验用 ***/
|
||||
|
||||
@@ -1568,6 +1568,11 @@
|
||||
"d": "校验时机;缺省 blur(输入时自动清已报错误)",
|
||||
"df": "blur"
|
||||
},
|
||||
{
|
||||
"n": "(formItem)hint",
|
||||
"t": "string",
|
||||
"d": "控件下方辅助说明(格式提示等);报错时让位给错误文案"
|
||||
},
|
||||
{
|
||||
"n": "onReset",
|
||||
"t": "() => void",
|
||||
@@ -1586,7 +1591,8 @@
|
||||
"触发时机用 input / change / focusout(均会冒泡),因此 checkbox / select / 单选组这类包裹式控件同样能失焦校验",
|
||||
"单行输入框内按 Enter 等同点击提交(textarea 内仍是换行);提交按钮为 type=\"submit\",不会与点击回调重复触发",
|
||||
"rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖",
|
||||
"formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交"
|
||||
"formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交",
|
||||
"需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1493,6 +1493,14 @@ button.ua-step-dot:hover {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.ua-form-hint {
|
||||
color: var(--ua-color-text-muted);
|
||||
font-size: var(--ua-font-size-sm);
|
||||
margin-top: 4px;
|
||||
}
|
||||
.ua-form-hint[hidden] {
|
||||
display: none;
|
||||
}
|
||||
.ua-form-error {
|
||||
color: var(--ua-color-danger);
|
||||
font-size: var(--ua-font-size-sm);
|
||||
|
||||
Reference in New Issue
Block a user