Skip to content

Select 选择器 ​

由 Select 与 Option 组合而成:Select 持有选中值(v-model:value),每个 Option 描述一个候选项。

  • 收起时触发器显示选中项的 label,展开后从弹层里挑选。
  • label 提供与 Input 一致的浮动标签:空值未聚焦时居中,聚焦、展开或有选中值时上浮到描边缺口。
  • 触发器沿用 Input 描边样式的几何:56px 高、4px 圆角、1px 描边、16px 正文,尾部 24px 箭头。
  • 弹层与 Menu 规格一致:--ds-elevated 底色、阴影、4px 圆角、8px 上下内边距,选项行高 48px。
  • Option 的标识默认为自身索引,也可以用 value 指定。
  • 弹层渲染到 body,宽度跟随触发器,下方空间不足时向上展开,因此不会被父级的 overflow: hidden 裁掉。

子项约定 ​

Select / Option 遵循父子组件以及插槽统一范式:选中值由 v-model:value 持有,Option 用 label、icon 或默认插槽描述内容,标识取 value 或父组件注入的索引。下文只说明 Select 自己的弹层与交互。

基础用法 ​

当前选中值:economy

vue
<script setup lang="ts">
import { ref } from "vue";
import { Option, Select } from "@sharpdotnut/dotsharp";

const cabin = ref("economy");
</script>

<template>
  <Select v-model:value="cabin" class="w-full max-w-xs">
    <Option label="经济舱" value="economy" />
    <Option label="超级经济舱" value="premium" />
    <Option label="商务舱" value="business" />
    <Option label="头等舱" value="first" />
  </Select>
</template>

Select 的根元素是行内块,宽度由外部决定;class="w-full" 之类的作用在根元素上,其余属性与事件透传给触发器按钮。

标签 ​

label 渲染在触发器内部的浮动标签:空值且未聚焦时位于中间,聚焦、展开或有选中值时上浮到描边缺口,和 Input 的 label 行为一致。它同时是触发器的可访问名称。

vue
<script setup lang="ts">
import { ref } from "vue";
import { Option, Select } from "@sharpdotnut/dotsharp";

const cabin = ref("economy");
const empty = ref<string | number>("");
</script>

<template>
  <Select v-model:value="cabin" label="舱位">
    <Option label="经济舱" value="economy" />
    <Option label="商务舱" value="business" />
  </Select>
  <Select v-model:value="empty" label="舱位" placeholder="请选择舱位">
    <Option label="经济舱" value="economy" />
    <Option label="商务舱" value="business" />
  </Select>
</template>

传了 label 后,placeholder 只在标签上浮且没有选中值时出现(聚焦或展开空选择器),两者同时使用不会重叠。 省略 label 时没有浮动标签,placeholder 始终居中显示,与之前的用法一致。

简单表单里需要标签时,优先用这个 label,不必再包一层 FormItem;判断标准见 表单指南 · 什么时候不用 FormItem。

标识:value 与索引 ​

Option 省略 value 时用自己的数字索引(从 0 开始)作为标识,v-model:value 默认是 0,因此首个选项默认选中。

当前选中索引:0

vue
<Select v-model:value="index">
  <Option label="按价格排序" />
  <Option label="按时长排序" />
  <Option label="按起飞时间排序" />
</Select>

索引由 Select 按最终渲染顺序注入,规则见父子组件以及插槽统一范式。

内容:label、icon 与插槽 ​

  • label:选项文本,同时是收起态触发器显示的文字;
  • icon:与 Icon 的 icon 同源,直接给图标数据,渲染在文字之前(尺寸 1.5em);
  • 默认插槽:选项行内的自定义内容,优先于 label。
vue
<script setup lang="ts">
import { ref } from "vue";
import { Option, Select } from "@sharpdotnut/dotsharp";
import explore from "@iconify-icons/material-symbols/explore";
import flight from "@iconify-icons/material-symbols/flight";
import luggage from "@iconify-icons/material-symbols/luggage";

const cabinIcon = ref("business");
</script>

<template>
  <Select v-model:value="cabinIcon">
    <Option label="经济舱" value="economy" :icon="flight" />
    <Option label="商务舱" value="business" :icon="luggage" />
    <Option label="探索套餐" value="explore" :icon="explore" />
  </Select>
</template>

插槽只改变选项行的渲染,不改变触发器显示的文字:触发器只读 label。所以用了插槽时 label 仍然必须提供, 否则选中该项后触发器会退回 placeholder(或空)。

vue
<Option label="往返" value="round" :icon="flight">
  <span class="font-medium">往返</span>
  <span class="ml-auto text-xs text-ds-muted">含 2 段航程</span>
</Option>

占位文字与禁用 ​

placeholder 在没有可显示的选中项文本时出现(例如选中项没有 label,或选项列表为空)。没设置 label 时它始终显示;设置了 label 时只在标签上浮后出现,见标签。disabled 让触发器不可操作:

vue
<Select v-model:value="empty" placeholder="请选择舱位">
  <Option label="经济舱" value="economy" />
  <Option label="商务舱" value="business" />
</Select>

<Select v-model:value="disabledValue" disabled>
  <Option label="经济舱" value="economy" />
  <Option label="商务舱" value="business" />
</Select>

单个选项可以用 disabled 禁用:它仍然渲染,但不可点击、不参与键盘移动,aria-disabled 为 true。

vue
<Select v-model:value="cabin">
  <Option label="经济舱" value="economy" />
  <Option label="商务舱(售罄)" value="business" disabled />
</Select>

弹层定位 ​

  • 弹层用 Teleport 渲染到 body,position: fixed,宽度等于触发器宽度。
  • 下方剩余空间放不下且上方更宽裕时改为向上展开;最高 320px,超出时弹层内部滚动。
  • 打开期间窗口滚动、缩放会重新定位;点击弹层与触发器之外的区域关闭。
  • 触发器保持在焦点上,高亮项通过 aria-activedescendant 播报,选项本身不需要获得焦点。

键盘操作 ​

按键收起态展开后
Enter / Space展开选中高亮项并关闭
↓ / ↑展开移动到下一个 / 上一个可用项
Home / End—跳到第一个 / 最后一个可用项
Esc—关闭,不改写选中值
Tab—关闭并正常离开

表单校验 ​

validator 接收当前选中值,返回 false 表示通过;返回 true 或错误文案表示不通过。默认在改变选择与失焦时各校验一次,用 validateOn 调整;控件还暴露 validate() / clearValidate()。完整约定见表单指南。

API ​

Select ​

属性类型默认值说明
v-model:valuestring | number0当前选中值,对应子项的 value 或索引
labelstring—浮动标签;空值未聚焦时居中,其余时候上浮
placeholderstring""没有可显示的选中项文本时显示的文字
disabledbooleanfalse禁用触发器:不可展开、不可用键盘打开
validator(value) => boolean | string—验证函数:false 通过,true / 字符串不通过
validateOn"input" | "blur" | ("input" | "blur")[]["input", "blur"]自动校验时机;传 [] 关闭自动校验
  • 根元素是 div(relative inline-flex flex-col),class / style 落在这里,其余属性与事件透传给触发器按钮。
  • 传 label 时通过 <label for> 关联触发器,标签即按钮的可访问名称;不传时回退到 FormItem 的标签。
  • 触发器是 role="combobox" 的 button,带 aria-haspopup="listbox"、aria-expanded、aria-controls, 展开时同步 aria-activedescendant。

Option ​

属性类型默认值说明
labelstring—选项文本;同时用于收起态触发器显示
iconIconifyIcon | string—图标,渲染在文字/插槽之前,尺寸 1.5em
valuestring | number所在位置索引子项标识
disabledbooleanfalse禁用:不可点击、不参与键盘移动
  • 默认插槽:选项行内容,优先于 label;label 仍需提供,否则触发器无文字可显示。
  • 根元素是 role="option" 的 div,带 aria-selected,选中项显示对勾并着色。
  • 点击已选中的选项也会关闭弹层,但不会触发 update:value。