81 lines
3.9 KiB
JSON
81 lines
3.9 KiB
JSON
{
|
||
"slug": "listpicker",
|
||
"end": "react",
|
||
"curated": true,
|
||
"summary": "React 端多选下拉选择器:受控 value 数组加 onChange 回传整个新数组,支持分组、搜索与全选。",
|
||
"usage": [
|
||
"选中结果是 value 数组,非受控时退回 defaultValue;源码用 value !== undefined ? value : inner 判断,value 一旦传入以内层 state 完全失效。",
|
||
"增删都走 commit(next):受控时只调 onChange,非受控时先 setInner 再回调,回调参数始终是完整数组而非单个值。",
|
||
"options 兼容扁平 [{value,label}] 与分组 [{label,children}],用 options[0].children 判形态,分组时先 flatMap 拍平再参与搜索与全选。",
|
||
"搜索词存在 query state,flatVisible 由 useMemo 按 o.label.includes(query) 过滤,关闭弹层时把 query 一起清空。",
|
||
"点击外部关闭依赖 useEffect 里成对注册的 document click 监听,配合 rootRef.current.contains(e.target) 判定。",
|
||
"标签上的删除叉必须 e.stopPropagation(),否则会连带触发 trigger 的展开/收起。"
|
||
],
|
||
"bestPractices": [
|
||
"filterable 只控制搜索框是否渲染,真正匹配用 label.includes,区分大小写且不支持拼音,模糊匹配需求要在 options 层预先处理。",
|
||
"value 与 defaultValue 同时给定时 value 优先,此时组件无内部状态,必须在 onChange 里回写新数组,否则界面不动。",
|
||
"全选只并入当前可见项 flatVisible 并用 new Set 去重,搜索过滤期间点全选不会选中被过滤掉的项。"
|
||
],
|
||
"apiCarrier": {
|
||
"kind": "props",
|
||
"note": "React 端 6 个 props、唯一回调 onChange;选中值、弹层开合、搜索词都是内部 state,没有 v-model 双向绑定。",
|
||
"table": [
|
||
{
|
||
"name": "value",
|
||
"type": "Array(受控)",
|
||
"desc": "选中的 value 数组;传入即受控,组件不再维护内部选中状态"
|
||
},
|
||
{
|
||
"name": "defaultValue",
|
||
"type": "Array,默认 []",
|
||
"desc": "非受控初始选中数组,仅在挂载时作为 useState 初值生效"
|
||
},
|
||
{
|
||
"name": "options",
|
||
"type": "Array,扁平或分组",
|
||
"desc": "选项数据;元素含 children 时按分组渲染并显示组标题"
|
||
},
|
||
{
|
||
"name": "filterable",
|
||
"type": "Boolean,默认 true",
|
||
"desc": "是否在面板顶部渲染搜索框"
|
||
},
|
||
{
|
||
"name": "placeholder",
|
||
"type": "String,默认 请选择",
|
||
"desc": "无选中项时触发器里显示的灰字提示"
|
||
},
|
||
{
|
||
"name": "onChange",
|
||
"type": "(keys: Array) => void",
|
||
"desc": "任一项增删后回调,参数为变更后的完整选中数组"
|
||
}
|
||
]
|
||
},
|
||
"a11y": [
|
||
"触发器是 div 加 onClick,没有 tabindex、role,也没有 aria-expanded,键盘无法聚焦展开下拉。",
|
||
"选项行同为 div 加 onClick,缺 role=option 与 aria-selected,勾选只体现在 .box 里渲染的 ✓ 字符上,读屏器读不出选中态。",
|
||
"搜索框是原生 input,可用,但无 label 或 aria-label,只靠 placeholder 交代用途。"
|
||
],
|
||
"resources": [
|
||
{
|
||
"file": "frameworks/ListPicker.jsx",
|
||
"kind": "React 实现",
|
||
"desc": "ListPicker.jsx —— 本端实现全文,含结构与交互逻辑"
|
||
},
|
||
{
|
||
"file": "frameworks/ListPicker.css",
|
||
"kind": "样式文件",
|
||
"desc": "frameworks/ListPicker.css —— 组件全部 kole-* 类名与 is-* 状态类的样式定义"
|
||
}
|
||
],
|
||
"exampleIds": [
|
||
"listpicker-ex-1"
|
||
],
|
||
"notes": [
|
||
"props 声明与默认值见 frameworks/ListPicker.jsx:15-22,受控 / 非受控判定在第 25 行。",
|
||
"commit 与全选逻辑见 frameworks/ListPicker.jsx:41-44,点击外部关闭的监听注册与清理见第 47-49 行。",
|
||
"trigger 类名 kole-listpicker-trigger 与 is-open 见 frameworks/ListPicker.jsx:54,未进入 consumedApi.classes 清单。"
|
||
]
|
||
}
|