表单
NativeSelect - 原生选择器
平台自己的选择器,能改的地方改了。
用法
什么时候用它
import { NativeSelect } from '@misoto22/folio'说明
原生 <select>,重新做了样式。
这是逃生口,不是默认选择——带样式的那个是 Select。在平台自带的选择器确实更好的地方用它:手机上很长的列表、必须在没有 JavaScript 时也能用的表单、最后一 KB 也要计较的页面。浏览器免费给你首字母跳转和移动端滚轮,这些是实打实的。
它做不到的是:一打开就和这套系统的其余部分长得一样。选项列表由操作系统绘制,所以它一个 token 都不带——这正是它不再是默认值的原因。
className 设的是外层容器的尺寸,不是里面那个 <select> 的。这是这里唯一一个渲染两个元素的控件,而箭头是钉在外层容器末端边缘上的:只给 select 设宽度,会把框缩窄,把箭头留在整行的另一头飘着。外层容器多大,select 就填满多大。
结构
| 部件 | 说明 |
|---|---|
| 外层容器必填 | 包着这两样东西的一个 relative <div>,也是 className 落脚的那个元素。它是这一组里唯一一个 className 不落在字段本身上的控件,因为箭头钉的正是这个盒子:宽度设在别的地方,箭头就会被撂在这一行的最外沿。 |
| 控件盒子必填 | 站在 CONTROL_BASE 上的 <select>,appearance-none 把平台自己的箭头去掉了,行尾留了 pe-9 的内边距,好让最长的那个选项让开我们画的那个箭头。它撑满外层容器,所以外层容器的宽度就是这个字段的宽度。 |
| 箭头必填 | 一个 pointer-events-none 的图标,钉在外层容器的行尾边上。它是我们的,不是平台的,所以选择器打开时它既不翻转也不移动。 |
| 选项列表 | children,打开时由操作系统绘制。里面只有 <option> 和 <optgroup>,这两个都吃不下这套 token。 |
实践建议
推荐
- 要么给它一个明确的空首项,要么给一个 defaultValue。没人碰过的 <select>,第一项就是选中的,所以一张没被碰过的表单,会把列表最上面那一项当作有人选过一样提交上去。
- 分组用 <optgroup>:它是操作系统的选择器真正会渲染的唯一一种结构,而且这里没有 SelectLabel 之于 Select 那样带样式的替代品可退。
- 宽度就用 className 来设。它落在箭头所钉的那个外层容器上,而 select 撑满这个容器,所以箭头会跟着字段的边缘一起走,不会停在这一行原本结束的地方。
避免
- multiple 和 size 熬不过这套样式:appearance-none 加上一个钉在外层容器中间的箭头,会把一个列表框变成一根滚动的列,上面横着一个箭头。改用复选框,或者 multiple 的 Combobox。
- 不要拿第一项当标签。「请选择国家」会被播报成一个可选的值,而且它就是一张没被碰过的表单提交上去的那个值——把名字放进 Field,再给那一项 value="" 和 disabled。
- 不要用 className 去改这个控件自己的字色或边框:它装扮的是外层容器,而里面那个 <select>,无论外面那个盒子怎么说,都还留在 CONTROL_BASE 上。
示例
default
平台自己的选择器,在能改的范围内改了样式:appearance-none 去掉操作系统那个箭头,旁边这个人字形是我们的,所以列表打开时它不会翻转、也不会挪位。它做不到的,是打开之后还像这套系统——那些选项是操作系统画的,一个 token 都不带,这也正是为什么它是逃生口,而 Select 才是默认。真正该拿它的地方,是平台确实更强的场合:手机上一个很长的列表、一个必须在没有 JavaScript 时也能工作的表单、一个在计较最后一千字节的页面。
The platform's own picker — better on a phone, and it works without JavaScript.
a placeholder option
一个空的首项,禁用,再让 defaultValue 指向它。一个没人碰过的 select,第一项本来就已经是选中的,所以一个开在 Australia 上的列表,会替一个从没看过这个字段的读者提交 Australia。把那一项命名成「请选择国家」只是把问题挪了个地方:它会被播报成一个可选的取值,而且一个没人动过的表单发出去的还是它。名字应该在 Field 上。注意 required 也写在 select 上了——Field 自己的 required 是那颗星号和 aria-required,从不会落到控件的属性上,所以这里让浏览器真正拒绝提交的是 select 上的这一个。
Where the invoice is issued from.
option groups
optgroup 是操作系统的选择器唯一真会渲染出来的结构,而且它没有 SelectLabel 给 Select 的那种降级样式——所以它就是一个长的原生列表保持可扫读的方式。宽度来自父元素,而不是 className:className 落在 select 自己身上,而人字形是相对它外面那层容器定位的,所以那样收窄一个字段,会把它自己的箭头孤零零地丢在这一行的最远端。
键盘操作
| 按键 | 作用 |
|---|---|
| Space↓ | 打开系统自带的选择器。 |
| a–z | 首字母跳转,由浏览器自己实现。 |
无障碍
- 首字母跳转和手机滚轮是浏览器白送的。
- 它做不到的是打开之后仍然像这套系统——选项列表由操作系统绘制,带不上任何 token。