## 43 · 选择器 Picker ### 43.1 用途 从一组**有限且已知**的选项里选出一项或几项(城市、分值、时间),是「表单里那个下拉框」在触屏上的形态;移动端与桌面端的关键差别是**没有悬停、也没有空格去展开**:下拉框在手机上只能变成从底部升起的滚轮浮层,手指点选即高亮,并且必须有明确的「确定 / 取消」来收口 —— 桌上点一下就走、触屏上误触代价大,用户需要一次反悔的机会。可绑定到 Popup 的底部形态(`position: bottom`),但本组件自带遮罩与面板,不要在弹出层里再套一层。 ### 43.2 结构(anatomy) - `mask`:遮罩,点击关闭(`closeOnMask=false` 时不关) - `picker`:底部浮层面板,`role="dialog"` + `aria-modal="true"` - `picker__header`:取消 / 标题 / 确定三格,标题元素由 `aria-labelledby` 指向 - `picker__columns`:列容器,`mode=single` 一列、`mode=multiple` 多列等分 - `picker__column`:单列,`role="listbox"` + `aria-label` 说明这列在选什么,可滚动 - `picker__option`:单个选项,`role="option"` + `aria-selected`,行高不小于 44px ### 43.3 变体维度 - `mode`:`single`(单列滚轮)/ `multiple`(多列滚轮,各列独立选中;是否联动由宿主的选项决定) - `round`:`false`(直角)/ `true`(靠内容一侧切圆角) ### 43.4 状态 - default:常态 - open:浮层展开(遮罩可点、面板滑入) - closed:收起态(遮罩 `pointer-events: none`,页面可正常滚动与点击) - selected:当前选中项,`is-selected` 与 `aria-selected="true"` 同步表达 - disabled:整块或单个选项置灰且不响应,写 `aria-disabled="true"`(整块禁用用状态类 `is-disabled`) ### 43.5 交互与触控 - 遮罩点击关闭;`closeOnMask=false` 时不关闭 - 选项行高不小于 44px,滚动容器 `-webkit-overflow-scrolling: touch` - 点击选项只改本列高亮(同列其余项取消高亮),不改宿主的值;确认时才提交 - 确定 / 取消按钮热区不小于 44px - 取消防返回:点取消或遮罩丢弃本次点选,宿主侧的值回到打开前的状态 ### 43.6 无障碍 - 浮层 `role="dialog"` + `aria-modal="true"`,标题元素 id 由 `aria-labelledby` 指向 - 每列 `role="listbox"` + `aria-label`,选项 `role="option"` + `aria-selected` - 遮罩 `aria-hidden="true"`(纯装饰,读屏不播报) - 选中值以文本呈现在标题里(不依赖视觉滚动位置),收起态写 `aria-hidden="true"` - 禁用项写 `aria-disabled="true"`,读屏播报不可用 ### 43.7 doNotInvent - 选项数据源与联动规则(由宿主传入 `columns`,本组件不发明城市库或级联关系) - 滚轮惯性 / 吸附动画的物理参数 - 搜索过滤与键盘输入定位(那是 Input / Search 的职责) - 多选(一次选多个值)——本组件是「多列各选一项」,不是「一列选多项」 ### 43.8 unknowns - 面板最大高度是否应随列数增长(当前固定 `max-height` 一列 200px) - 是否要支持「不选」的空值项 - 列数上限(当前实现不限制,但三列以上在 375px 宽度下每列会很窄) ---