生态产品多个设计器现已发布正式版

查看生态总览
跳转到内容

Select 选择器 ​

从已知清单中选择一个或多个值,选项收在浮层内。

用法 ​

单选

水果

组件结构 ​

加粗的是必需部件。

data-scope="select":root · label · control · trigger · value-text · indicator · clear-trigger · tag-list · positioner · content · list · footer · group · group-label · item · item-prefix · item-text · item-description · item-suffix · item-indicator · empty · loading · error · retry-trigger · hidden-select

示例 ​

多选 ​

已选项在触发器里排成标签

水果(多选)

已选:apple

受控 ​

由 value 和 value-change 控制

水果

宿主持有的值:banana

禁用 ​

禁止展开和聚焦

水果

变体 ​

outline、subtle 和 ghost

outline
subtle
ghost

颜色 ​

六种语气

brand
neutral
success
warning
danger
info

尺寸 ​

小、中、大三档

小
默认
大

异步加载 ​

展开时加载选项

曲目

当前值:(未选)

宽度 ​

分别设置控件和浮层宽度

缺省宽度
加宽

自定义内容 ​

自定义选项和当前值

负责人

操作入口 ​

通过插槽状态控制开合和值

水果

大量选项 ​

列表内部滚动并支持连打检索

仓位

分组 ​

跨分组保持键盘导航

食材

当前值:(未选)

多选标签 ​

超出数量合并为 +N

技术栈
Vue Svelte Solid

校验 ​

显示无效状态和错误说明

所属部门

这一项必填

滚动加载 ​

到达列表底部加载下一页

工单

已加载 20 / 80 条

命令式聚焦 ​

聚焦触发器

优先级

清空 ​

有值时显示清空按钮

所属小组

底部操作区 ​

固定在滚动列表下方

水果

当前值:(未选)

Popover + Listbox ​

不参与表单的选择

选项副文本 ​

一行放不下的解释写在第 2 行

订阅方案

行首与行尾 ​

两格交给作者,文字与副文本仍由数据铺

状态

长选项虚拟化 ​

一万条选项只写 virtual:浮层里只铺滚到的那一屏,键盘仍跨完整 collection;虚拟化那一块按需加载,没开的不为它付体积

长列表

对象值 ​

选项直接用接口取回来的对象:value 与值变化交出的都是对象本身,给 valueKey 按 id 认,同一个人换成内容相同的新对象也选得中

负责人
钱二(前端体验)

设计指引 ​

何时使用 ​

  • 从已知选项中选择一个或多个值。
  • 需要分组、标签多选或异步加载。

何时不用 ​

特性 ​

  • 通过 hidden-select 参与表单。
  • 不写结构时按标准结构铺出整套部件(Vue 不写默认插槽、React 不给 children、Web Components 不写部件或只写 root),三端出自同一个结构函数;label 给标题文字、clearable 带清空按钮。浮层里列表之后铺空态、在途、取数失败的提示与重试钮,露哪一种按 loading、error 与条数收放;文字取 translations,空态文字可由 empty 给出。Web Components 里标题、空态、条目文字与行首行尾可以写在宿主的直接子节点上接管:slot="label"、slot="empty"、slot="item:<值>"、slot="item-prefix:<值>"、slot="item-suffix:<值>";Vue 用同名插槽,React 给 empty 一个节点。
  • 多选值在触发器里排成标签(不写结构时直接铺出),超出 maxTagCount 后合并为 +N;组合框、树选择与级联选择的多选用同一套呈现。
  • 支持分组、加载、空状态、底部操作区和滚动加载。
  • 选项可逐条声明语气,失效或需要留意的那条自带该族字色与高亮底。
  • 选项可写副文本,第 2 行放一句解释,与标题同列、走 muted 档。
  • 选项来自模板或宿主数据时,用 @xihan-ui/core/options 导出的 findOptionViolation(collection, { tree?, unique?, requireLabel?, strict?, isValue? }) 先校验结构(isValue 换掉缺省只收字符串的 value 检查,供取值是数字、布尔的选项列表用),返回第一处问题的 JSON Pointer 与原因;单选组、复选组、组合框、树选择与级联吃同一套节点形状,同样适用。
  • 行首与行尾两格各有逐条钩子:只想加个图标或计数,不必把整条重搭。
  • 同一层里只要有一条写了行首格,这一层的行首格都至少一个图标宽,没写的条目也空出这一格,正文左缘对齐;整层都没有就不留。
  • 控件使用 Field Chrome,浮层使用 M2 磨砂表面;候选面板与字段盒等宽,长选项在条目里截断。
  • 选中项保留普通文字,通过末端对号表示状态。
  • 关闭时立即退出交互,资源在退场动画结束后释放。
  • 占位态:首次加载时在途占位在文案前转一枚加载环;已有选项时后台刷新保留上一帧、只报 aria-busy,选项照常可选、不淡化(淡到禁用透明度会把文字压到 4.5:1 以下),在途占位让位;空态与加载文字取次要文字、上下内距一档。
  • lazyMount 让列表内容第一次展开时才挂载、之后常驻:页面上 Select 很多(表格每行一个、长表单)时,收起态每个实例背着的整份条目是挂载开销的大头。打开前的选中文字与收起态连打按 collection 计算,所以要配 collection 用;没给时打开前选中文字退回值本身、连打不生效。Web Components 里把条目写进 list 里的一个 <template>,第一次展开才克隆。
  • 值可以是任意类型:value、defaultValue 与 collection 里交进来数字、对象都行,onValueChange、v-model、插槽与 renderItem 拿到的是原值;Vue 与 React 按 value 或 collection 推断值的类型,两处对不上、valueKey 写错字段编译即报错。数字转成字符串当键,对象值给 valueKey(字段名或取值函数),同一个选项每次都是新对象(接口刚取回来)也认得出来,不给按对象身份认;键也是条目的 data-value与随表单提交的值。Vue 的 h() 推断不了泛型,那里按宽松的类型检查;Web Components 经 property 交值,写 querySelector<XhSelectElement<number>>("xh-select") 收窄。

组合 ​

  • 与表单字段组合。
  • 不参与表单时使用 气泡卡片 与 列表框。
  • 长选项列表不写结构时只写 virtual:浮层里只铺滚到的那一屏条目,方向键、Home / End 与按字跳转仍跨完整 collection,自绘滚动条跟着视口走;条目按 estimateSize(缺省 36)起算、渲出后按实测校正,带副文本的两行条目也对得齐。虚拟化那一块按需加载,没开的选择器不为 Virtualizer 的内核付体积;挂载后立刻展开、或 lazyMount 下第一次展开时它还在路上,到了之后焦点照样补进列表。
  • 手写结构时照旧把完整 collection 与 Virtualizer 的 collectionVirtualizer 同时交给根,浮层内只渲染 virtualItems;virtual 只管标准结构。

最佳实践 ​

  • 固定触发器宽度,避免选中值改变布局。
  • 选项较多或需要搜索时使用 Combobox。
  • 自定义内容中的主要文字放在 item-text 中。

反模式 ​

  • 不要用 Select 承载“导出”“删除”等动作。
  • 异步加载时不要省略加载和空状态。

API 参考 ​

产物 ​

层值
自定义元素<xh-select>
Vue 组件XhSelectClearTrigger XhSelectContent XhSelectControl XhSelectEmpty XhSelectError XhSelectFooter XhSelectGroup XhSelectGroupLabel XhSelectIndicator XhSelectItem XhSelectItemDeleteTrigger XhSelectItemDescription XhSelectItemIndicator XhSelectItemPrefix XhSelectItemSuffix XhSelectItemText XhSelectLabel XhSelectList XhSelectLoading XhSelectOverflowTag XhSelectPositioner XhSelectRetryTrigger XhSelectRoot XhSelectTag XhSelectTagLabel XhSelectTagList XhSelectTrigger XhSelectValueText
组合式函数useSelect
状态机selectMachine
皮肤@xihan-ui/styles/select.css

Props ​

属性类型必填说明
labelledboolean作者渲染了 label 部件时置真,由适配器统计而不是判断标题文字是否有值。 为假时可聚焦部件不把 label 的 id 写进 aria-labelledby:指向未渲染的 id 是无效引用,作者写在控件上的 aria-label 也会被它压住。
ariaLabelstring不显示出来的名字,写成 aria-label 落在与字段标签同一个部件上。 放进字段(字段接线)或渲染了 label 部件时名字以它们为准,aria-labelledby 盖过它。
collectionSelectNode<V>[]条目数据,显示文本与禁用的事实源。提供后条目部件只需声明 value, 显示文本也不再从 DOM 查询。未提供时回到文本写在条目中、从 DOM 查询的方式。
valueKeySelectionValueKey<V>对象值怎样取键:字段名或取值函数。同一个选项每次都是新对象(接口刚取回来)时给它; 不给时按对象身份认。值不是对象时用不着。键也是条目的 data-value 与随表单提交的值。
virtualizerCollectionVirtualizer完整 collection 与 Virtualizer 的焦点桥;count 必须与 collection.length 一致。
lazyMountboolean列表内容第一次展开时才挂载,默认 false(随组件一起挂载、收起时只隐藏)。挂过之后一直留着,来回开合不重建。 页面上 Select 很多(表格每行一个、长表单)时打开它:收起态每个实例都背着一整份条目,挂载开销大半在这里。 打开前的选中文字与收起态连打按 collection 计算;没给 collection 时,打开前选中文字退回值本身、连打不生效。
valueV | V[] | null选中值。裸串是单选的简写,null 是受控且无选中,未提供(undefined)才是非受控;内部一律按数组处理。 受控时 cell 直读 prop,写入只发 onValueChange 不落内部值。
defaultValueV | V[] | null非受控初始选中值。与 value 同样接受裸值与 null。
multipleboolean允许选中多项。单选时选完即收起,多选时保持展开继续选。
openboolean展开态。提供即受控:内部不再自行修改,只发 onOpenChange。
defaultOpenboolean
disabledboolean整个控件禁用:trigger 使用原生 disabled,隐藏 select 不参与提交。
readOnlyboolean只读:浮层照常展开与浏览,但选中值不可修改、也不可清空。
invalidboolean校验错误态:trigger 标红并输出 aria-invalid。
loadingboolean条目加载中:列表报告 aria-busy,显示在途占位、隐藏空态占位。
errorboolean整表取数失败:浮层里露出 error 部件(role=alert)与 retry-trigger,空态占位让位;loading 同时为真时在途占位优先。 点重试钮、或浮层里没有高亮条目时按 Enter / Space,派发 onRetry;宿主重新取数时撤掉 error、置上 loading。
translationsPartial<SelectTranslations>读屏文案,默认英文。
maxTagCountnumber多选标签最多显示的数量,其余折叠进 overflowCount、合成 +N 标签;默认 3(SELECT_DEFAULT_MAX_TAG_COUNT)。
requiredboolean原生表单校验:无选中值时提交被拦截。
namestring表单字段名。提供后隐藏 select 才带 name,选中值随表单一并提交。
placeholderstring无选中时 value-text 显示的占位文字。
placementPlacement
offsetnumber
loopboolean方向键到达末尾是否回绕,默认 true。
dirDirection文字方向,默认 ltr。
variantControlVariant形态:outline / subtle / ghost,决定触发器的描边与底色使用方式。默认 outline。
toneTone语气:brand / neutral / success / warning / danger / info,决定聚焦与选中强调使用哪族颜色。
sizeSize尺寸:sm / md / lg,决定触发器高度、内边距与字号档位。
onValueChange(details: SelectValueChangeDetails<V>) => voidvalue 变化意图回调;受控时是唯一出口,非受控时随内部写入一并通知。
onClear() => void用户按清空钮(clear-trigger)清掉了值;先发值变化,再发它。程序化的 clear() 不发。
onOpenChange(details: SelectOpenChangeDetails) => voidopen 变化意图回调;受控时是唯一出口,非受控时随内部转移一并通知。
onRetry() => void整表取数失败后请求重试:点了 retry-trigger,或浮层里没有高亮条目时按了 Enter / Space。

SelectNode ​

collection 的元素。

字段类型必填说明
valueV是
labelstring展示文本,也是连打检索的取字来源;默认回退为值(不是字符串的值退回它的键)。
disabledboolean条目禁用:方向键跳过它,但它仍可聚焦、仍是导航起点。
toneTone该条选项自身的性质:危险选项写 danger、需要留意的写 warning。不写即与其余条目同档。 只换字色与悬停 / 按下的面,不表达选中与校验;选中的标记与禁用都压过它。 彩字不是唯一通道,要紧的差别仍要配图标或文案。整个选择器的 tone 不下发给条目。
descriptionstring副文本,写入 item-description 部件;未提供时本条不铺该部件。 它是第 2 行的说明,跟着条目走 muted 档,不跟语气;放不下一行的解释才用它, 一句话能说清的写进 label。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
value-changeSelectValueChangeDetails选中值变化;detail 为 { value: 值[] },值是 value / collection 里交进来的原值(字符串、数字或对象)
clear``用户按清空钮(clear-trigger)清掉了值;先发值变化,再发它。程序化的 clear() 不发。
open-changeSelectOpenChangeDetailsopen 状态变化;detail 为 { open: boolean }
retry``整表取数失败后请求重试:点了 retry-trigger,或浮层里没有高亮条目时按了 Enter / Space

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhSelectRootdefaultSelectRootSlotProps<V>
XhSelectRootlabel—
XhSelectRootempty—自动渲染树中空态占位的内容
XhSelectRootitemSelectNodeMeta<V>只填条目的文字槽,副文本与首尾两格照旧各归各的
XhSelectRootitem-prefixSelectNodeMeta<V>只接管行首那一格,其余槽照旧由数据铺
XhSelectRootitem-suffixSelectNodeMeta<V>只接管行尾那一格(计数、徽标、次级图标),其余槽照旧由数据铺

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhSelectGroupvaluestring是
XhSelectItemvalueunknown是条目代表的值,与根上的值同类型:字符串、数字或对象。
XhSelectItemdisabledboolean默认交给 connect 查询 collection,写死 false 会覆盖数据中的禁用。
XhSelectPositionercontainer() => Element | null浮层挂载的容器;未提供时按全局配置,再未提供时挂载到 body。
XhSelectRootvirtualboolean标准结构下开虚拟化:浮层里只铺滚到的那一屏条目,长列表一个开关即可。虚拟化那一块异步加载,没开的不为它付体积; 手写 children 时不使用它,照旧自己套 Virtualizer 再把 collectionVirtualizer 交给 virtualizer。
XhSelectRootestimateSizenumber虚拟化时每条的预估高度(px),缺省 36;条目渲出后按实测校正。
XhSelectRootlabelReactNode标题文字。提供后不必再写 label 部件。
XhSelectRootemptyReactNode自动渲染树中的空态内容,没给时取 translations.empty。
XhSelectRootclearableboolean自动渲染树中是否带清空按钮;手写部件不使用它,写了节点即可清空。
XhSelectRootrenderItem(node: SelectNodeMeta<V>) => ReactNode每个条目的自定义内容;未提供时使用 collection 中的 label。
XhSelectRootrenderItemPrefix(node: SelectNodeMeta<V>) => ReactNode只接管条目行首那一格;其余槽仍由数据铺。
XhSelectRootrenderItemSuffix(node: SelectNodeMeta<V>) => ReactNode只接管条目行尾那一格;其余槽仍由数据铺。
XhSelectRootchildrenSlotChildren<SelectRootSlotProps<V>>
XhSelectTagvalueunknown是它代表哪个选中值,与根上的值同类型(标签插槽里的 tag.value 原样交回来)。

状态 ​

公开状态写入 data-state。

部件取值
root'open' | 'closed'
control'open' | 'closed'
trigger'open' | 'closed'
indicator'open' | 'closed'
positioner'open' | 'closed'
content'open' | 'closed'
list'open' | 'closed'
footer'open' | 'closed'
item'checked' | 'unchecked'
item-prefix'checked' | 'unchecked'
item-text'checked' | 'unchecked'
item-description'checked' | 'unchecked'
item-suffix'checked' | 'unchecked'
item-indicator'checked' | 'unchecked'
empty'open' | 'closed'
loading'open' | 'closed'
error'open' | 'closed'

以下名称仅用于内部状态机。

状态:open · closed

事件:OPEN · TOGGLE · CLOSE · CONTROLLED.OPEN · CONTROLLED.CLOSE · ITEM.HIGHLIGHT · HIGHLIGHT.CLEAR · ITEM.LOST · ITEM.SELECT · VALUE.SET · VALUE.CLEAR · FORM.RESET · PRESS.START · PRESS.END · TAG_LIST.TRACKED

判据:isOpenControlled · isMultiple · isReadOnly · canPress

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
openboolean
collectionreadonly SelectNodeMeta<V>[]由 collection 推导的条目元信息,按数据顺序排列;未提供 collection 时为空数组。
valueV[]选中集合,按选中先后排列而非文档顺序。单选恒为长度 ≤ 1。
valueKeysstring[]选中值的键,与 value 逐项对应:字符串值即它本身,别的值是编出来的键。条目的 data-value 与随表单提交的都是它。
valueTextstring[]选中项的文本,与 value 逐项等长对应;某项在 DOM 中查询不到条目时该项回退为值本身。
displayTextstringvalue-text 实际显示的文字:有选中时取其文本(多选按半角逗号加空格连接),否则取 placeholder。
multipleboolean是否允许多选。
invalidboolean校验错误态。
errorboolean整表取数失败。
translationsSelectTranslations解析后的读屏文案;error 与 retry-trigger 没写内容时显示其中的 loadError 与 retry。
readOnlyboolean只读态。
canClearboolean当前能否清空:有选中且既不禁用也不只读。
tagsSelectTagMeta<V>[]可见标签(受 maxTagCount 截断),与 value / valueText 同序。
overflowCountnumber被 maxTagCount 折叠的标签数。
overflowTextstring+N 标签显示的文字(由 translations.overflowTag 计算);没有折叠的标签时为空串。
highlightedValueV | null高亮锚点;收起时为 null。
setOpen(next: boolean) => void
setValue(next: V | V[]) => void
clear() => void清空全部选中。
deselect(value: V) => void移除一个选中值。
getRootProps() => T['element']
getLabelProps() => T['element']
getControlProps() => T['element']触发器与清空按钮的收纳容器:两者在其中并排,有值且指针落在盒上(触屏上有值即)时清空按钮替代展开指示符。
getTriggerProps() => T['button']
getValueTextProps() => T['element']
getIndicatorProps() => T['element']
getClearTriggerProps() => T['button']清空按钮:不占 Tab 位;无法清空时整体隐藏;点击清空全部选中、不展开浮层,焦点送回 trigger。
getTagListProps() => T['element']标签行:收纳可见标签与 +N 标签,放在触发器中;无选中时整体 hidden。
getTagProps(props: SelectTagProps<V>) => T['element']标签:一个选中值一个,即库内 tag 的 root(data-scope="tag"):语气、尺寸与禁用从本控件传下,形态按控件的面派生(outline / ghost / 默认使用淡底标签,subtle 使用描边标签),另带 data-value 记录代表的值。放在触发器中即纯展示(不渲染关闭按钮),放在外部配删除按钮可删除。
getTagLabelProps() => T['element']标签文字所在的块(tag 的 label):截断落在这一层;标签与 +N 共用。
getOverflowTagProps() => T['element']被折叠的标签合成的一个:同样是 tag 的 root,显示 overflowText、带 data-count;没有折叠的标签时 hidden。
getItemDeleteTriggerProps(props: SelectTagProps<V>) => T['button']标签删除按钮:即所在标签那份 tag 的 close-trigger(data-scope="tag"),可及名使用 translations.deleteItem,禁用时保留位置、原生 disabled;点击移除所在标签的选中值;须放在标签中。
getPositionerProps() => T['element']
getContentProps() => T['element']浮层外壳:描边、底色、阴影与键盘收口都在它身上。
isContentMounted() => booleancontent 里的列表内容此刻该不该挂载。未开 lazyMount 时恒为真;开了的话第一次展开起为真。 content 节点本身始终在场,只有它的内容按此挂载。
getListProps() => T['element']列表框本体,滚动在这一层;role=listbox 与条目的拥有关系都归它。
getFooterProps() => T['element']浮层底部的操作区,是 list 的兄弟;不在列表框的拥有关系中,也不参与方向键与连打检索。
getEmptyProps() => T['element']空态占位:放在 content 中、list 的兄弟。 提供 collection 时由连接层按条数收放;条目手写时不写 hidden,是否显示由作者决定。
getLoadingProps() => T['element']在途占位:与空态占位同一位置,两者不同时显示:加载期间显示它,空态让位。 提供 collection 时由连接层按条数收放;条目手写时只按 loading 收放。
getErrorProps() => T['element']整表取数失败的提示:与空态、在途占位同一位置,role=alert;error 为真且不在途时露出。
getRetryTriggerProps() => T['button']整表取数失败后的重试按钮:不占 Tab 位,键盘从浮层里按 Enter / Space 重试;点击派发 onRetry。
getGroupProps(props: SelectGroupProps) => T['element']分组容器:role=group,条目挂在其中;分组标题经 aria-labelledby 关联。
getGroupLabelProps(props: SelectGroupProps) => T['element']分组标题:不是选项、不进入导航,只作为本组的可及名。
getItemProps(props: SelectItemProps<V>) => T['element']
getItemPrefixProps(props: SelectItemProps<V>) => T['element']
getItemTextProps(props: SelectItemProps<V>) => T['element']
getItemDescriptionProps(props: SelectItemProps<V>) => T['element']
getItemSuffixProps(props: SelectItemProps<V>) => T['element']
getItemIndicatorProps(props: SelectItemProps<V>) => T['element']
getHiddenSelectProps() => T['select']表单出口:一份视觉隐藏的原生 select,由根部件自行渲染(作者不必手写)。 选项由适配器按当前值补齐,原生提交与 required 校验据此获取值。

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Enter / Spaceclosed, focus in trigger展开列表并把高亮落到当前选中项(无选中则落首个可用条目)
ArrowDownclosed, focus in trigger展开列表并把高亮落到选中项的下一个可用条目
ArrowUpclosed, focus in trigger展开列表并把高亮落到选中项的上一个可用条目
单个可打印字符closed, focus in trigger连打检索命中的条目直接成为选中值(多选是加进集合,已在集合里则不动),列表不展开
Deleteclosed, focus in trigger, 有选中值且未禁用、未只读清空全部选中,列表不展开
Backspaceclosed, focus in trigger, 有选中值且未禁用、未只读单选清空;多选去掉最后一个选中值,列表不展开
ArrowDownopen, focus in content高亮移到下一个条目(禁用项跳过、尽头按 loop 回绕)
ArrowUpopen, focus in content高亮移到上一个条目(禁用项跳过、尽头按 loop 回绕)
Homeopen, focus in content高亮移到首个可用条目
Endopen, focus in content高亮移到末个可用条目
单个可打印字符open, focus in content连打检索移动高亮,不改选中值
Enter / Spaceopen, 单选, 高亮条目未禁用选中高亮条目并关闭列表,焦点归还 trigger
Enter / Spaceheld in item / clear-trigger, 未禁用、未只读按住期间该部件投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,条目随浮层收起一并撤下;没有值可清时清空按钮不进
Enter / Spaceopen, 多选, 高亮条目未禁用切换高亮条目的选中态,列表不收起、焦点留在条目上
Enter / Spaceopen, 整表取数失败(error)且没有高亮条目派发重试,浮层不收起;重试钮不占 Tab 位,键盘从这里重试
Escapeopen关闭列表并把焦点归还 trigger,选中值不变
Tab / Shift+Tabopen关闭列表,焦点不归还 trigger,按 Tab 序列自然离开

ARIA ​

以下属性由 connect 生成。

部件属性值
triggeraria-controlscontent 部件的 id
triggeraria-expanded'true' | 'false'
triggeraria-haspopup'listbox'
triggeraria-invalid'true' | 'false'
triggeraria-labelprops.ariaLabel
triggeraria-labelledbylabel 部件的 id value-text 部件的 id | trigger 部件的 id value-text 部件的 id | value-text 部件的 id
triggeraria-readonly'true' | 'false'
triggerrole'combobox'
indicatoraria-hidden'true'
clear-triggeraria-labeltranslations.clearTrigger
contentaria-hidden!open || undefined
listaria-busy'true' | undefined
listaria-labeltranslations.content
listaria-labelledbylabel 部件的 id value-text 部件的 id | trigger 部件的 id value-text 部件的 id | value-text 部件的 id
listaria-multiselectable'true' | 'false'
listrole'listbox'
grouparia-labelledbygroup-label 部件的 id
grouprole'group'
itemaria-disabled'true' | 'false'
itemaria-selected'true' | 'false'
itemrole'option'
item-prefixaria-hidden'true'
item-indicatoraria-hidden'true'
errorrole'alert'
hidden-selectaria-hidden'true'

样式参考 ​

皮肤 ​

@xihan-ui/styles/select.css 按 [data-scope="select"][data-part="root"] 部件选择器书写,发布产物以挂载类 .xh-scope-select 代替其中的 data-scope(特异性相同),位于 xihan.components 层。覆盖样式使用 xihan.overrides,部件选择器照常可用。

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-disabled''(条件成立时才出现)
rootdata-invalid''(条件成立时才出现)
rootdata-loading''(条件成立时才出现)
rootdata-readonly''(条件成立时才出现)
rootdata-sizeprops.size
rootdata-state'open' | 'closed'
rootdata-toneprops.tone
rootdata-variantprops.variant
labeldata-disabled''(条件成立时才出现)
controldata-disabled''(条件成立时才出现)
controldata-invalid''(条件成立时才出现)
controldata-readonly''(条件成立时才出现)
controldata-state'open' | 'closed'
controldata-variantprops.variant
controldata-xh-action-owner''
controldata-xh-field-chrome''
controldata-xh-field-sizeprops.size
triggerdata-disabled''(条件成立时才出现)
triggerdata-invalid''(条件成立时才出现)
triggerdata-placeholder''(条件成立时才出现)
triggerdata-readonly''(条件成立时才出现)
triggerdata-state'open' | 'closed'
value-textdata-disabled''(条件成立时才出现)
value-textdata-placeholder''(条件成立时才出现)
indicatordata-clearable''(条件成立时才出现)
indicatordata-disabled''(条件成立时才出现)
indicatordata-state'open' | 'closed'
clear-triggerdata-pressed''(条件成立时才出现)
clear-triggerdata-xh-action-control''
clear-triggerdata-xh-action-display'has-value'
clear-triggerdata-xh-action-has-value''(条件成立时才出现)
clear-triggerdata-xh-action-profile'field-inset'
clear-triggerdata-xh-action-sizeprops.size
clear-triggerdata-xh-action-variant'ghost'
tag-listdata-disabled''(条件成立时才出现)
tag-listdata-instant''(条件成立时才出现)
tag-listdata-xh-tag-list''
positionerdata-hidden''(条件成立时才出现)
positionerdata-placement定位引擎算出的实际落位
positionerdata-positioned''(条件成立时才出现)
positionerdata-sizeprops.size
positionerdata-state'open' | 'closed'
positionerdata-toneprops.tone
positionerdata-variantprops.variant
contentdata-instant''(条件成立时才出现)
contentdata-placement定位引擎算出的实际落位
contentdata-state'open' | 'closed'
contentdata-xh-material'frosted'
listdata-state'open' | 'closed'
footerdata-state'open' | 'closed'
itemdata-disabled''(条件成立时才出现)
itemdata-highlighted''(条件成立时才出现)
itemdata-pressed''(条件成立时才出现)
itemdata-state'checked' | 'unchecked'
itemdata-tonemetaOf.get(item.value)?.tone
itemdata-xh-collection-context'overlay'
itemdata-xh-collection-item''
itemdata-xh-collection-sizeprops.size
item-prefixdata-disabled''(条件成立时才出现)
item-prefixdata-state'checked' | 'unchecked'
item-prefixdata-xh-collection-slot'prefix'
item-textdata-disabled''(条件成立时才出现)
item-textdata-state'checked' | 'unchecked'
item-textdata-xh-collection-slot'text'
item-descriptiondata-disabled''(条件成立时才出现)
item-descriptiondata-state'checked' | 'unchecked'
item-descriptiondata-xh-collection-slot'description'
item-suffixdata-disabled''(条件成立时才出现)
item-suffixdata-state'checked' | 'unchecked'
item-suffixdata-xh-collection-slot'suffix'
item-indicatordata-disabled''(条件成立时才出现)
item-indicatordata-state'checked' | 'unchecked'
item-indicatordata-xh-collection-slot'indicator'
emptydata-state'open' | 'closed'
loadingdata-loading''(条件成立时才出现)
loadingdata-state'open' | 'closed'
loadingdata-xh-loading-ring''
errordata-state'open' | 'closed'
retry-triggerdata-xh-action-control''
retry-triggerdata-xh-action-display'always'
retry-triggerdata-xh-action-profile'text'
retry-triggerdata-xh-action-sizeprops.size
retry-triggerdata-xh-action-variant'ghost'
overflow-tagdata-countString(overflowCount)
tagdata-valuev

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-select-action-bgclear-trigger--xh-ink-surface
background-color
default
xh-ink-surface
--xh-_action-variant-bg-restselect 的 clear-trigger 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-select-action-bg-activeclear-triggerbackground-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-bg-pressedselect 的 clear-trigger 部件 background-color 覆盖槽。
--xh-select-action-bg-hoverclear-triggerbackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-bg-hoverselect 的 clear-trigger 部件 background-color 覆盖槽。
--xh-select-action-fgclear-triggercolordefault--xh-fg-field-clearselect 的 clear-trigger 部件 color 覆盖槽。
--xh-select-action-fg-hoverclear-triggercolordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-fg-defaultselect 的 clear-trigger 部件 color 覆盖槽。
--xh-select-action-font-sizeclear-triggerfont-sizedefault--xh-text-secondary-sizeselect 的 clear-trigger 部件 font-size 覆盖槽。
--xh-select-action-radiusclear-triggerborder-radiusdefault--xh-shape-circleselect 的 clear-trigger 部件 border-radius 覆盖槽。
--xh-select-action-sizeclear-trigger
indicator
block-size
inline-size
min-inline-size
default
xh-action-profile=field-inset
--xh-_action-profile-visual-size
--xh-control-action-size
select 的 clear-trigger、indicator 部件 block-size、inline-size、min-inline-size 覆盖槽。
--xh-select-content-backdropcontent-webkit-backdrop-filter
backdrop-filter
xh-material=frosted--xh-_material-backdropselect 的 content 部件 -webkit-backdrop-filter、backdrop-filter 覆盖槽。
--xh-select-content-bgcontentbackgroundnot([data-xh-action-control])
xh-material=frosted
--xh-_material-bgselect 的 content 部件 background 覆盖槽。
--xh-select-content-bordercontentbordernot([data-xh-action-control])
xh-material=frosted
--xh-_material-borderselect 的 content 部件 border 覆盖槽。
--xh-select-content-fgcontentcolornot([data-xh-action-control])
xh-material=frosted
--xh-_material-fgselect 的 content 部件 color 覆盖槽。
--xh-select-content-highlightcontentbackgroundnot([data-xh-action-control])
xh-material=frosted
--xh-_material-highlightselect 的 content 部件 background 覆盖槽。
--xh-select-content-max-hcontentmax-block-sizedefault--xh-overlay-menu-max-hselect 的 content 部件 max-block-size 覆盖槽。
--xh-select-content-max-wcontentmax-inline-sizedefaultnoneselect 的 content 部件 max-inline-size 覆盖槽。
--xh-select-content-min-wcontentinline-sizedefault--xh-overlay-menu-min-wselect 的 content 部件 inline-size 覆盖槽。
--xh-select-content-pxcontentpadding-inlinedefault--xh-space-1select 的 content 部件 padding-inline 覆盖槽。
--xh-select-content-pycontentpadding-blockdefault--xh-space-1select 的 content 部件 padding-block 覆盖槽。
--xh-select-content-radiuscontentborder-radiusdefault--xh-shape-overlayselect 的 content 部件 border-radius 覆盖槽。
--xh-select-content-shadowcontentbox-shadownot([data-xh-action-control])
xh-material=frosted
--xh-_material-shadowselect 的 content 部件 box-shadow 覆盖槽。
--xh-select-control-bgcontrolbackground-colorxh-field-chrome--xh-_field-variant-bg-restselect 的 control 部件 background-color 覆盖槽。
--xh-select-control-bg-disabledcontrolbackground-colordisabled
xh-field-chrome
--xh-_field-variant-bg-disabledselect 的 control 部件 background-color 覆盖槽。
--xh-select-control-bg-hovercontrolbackground-colordisabled
hover
invalid
loading
not(:focus-within)
not([data-disabled])
not([data-invalid])
not([data-loading])
not([data-readonly])
readonly
xh-field-chrome
--xh-_field-variant-bg-hoverselect 的 control 部件 background-color 覆盖槽。
--xh-select-control-bg-readonlycontrolbackground-colorreadonly
xh-field-chrome
--xh-_field-variant-bg-read-onlyselect 的 control 部件 background-color 覆盖槽。
--xh-select-control-bordercontrolborderxh-field-chrome--xh-_field-variant-border-restselect 的 control 部件 border 覆盖槽。
--xh-select-control-border-focuscontrolborder-color
box-shadow
disabled
focus-within
not([data-disabled])
xh-field-chrome
--xh-_field-variant-border-focusselect 的 control 部件 border-color、box-shadow 覆盖槽。
--xh-select-control-border-hovercontrolborder-colordisabled
hover
invalid
loading
not(:focus-within)
not([data-disabled])
not([data-invalid])
not([data-loading])
not([data-readonly])
readonly
xh-field-chrome
--xh-_field-variant-border-hoverselect 的 control 部件 border-color 覆盖槽。
--xh-select-control-border-invalidcontrolborder-colorinvalid
xh-field-chrome
--xh-_field-variant-border-invalidselect 的 control 部件 border-color 覆盖槽。
--xh-select-control-fgcontrolcolorxh-field-chrome--xh-fg-defaultselect 的 control 部件 color 覆盖槽。
--xh-select-control-gapcontrolgapxh-field-chrome--xh-_select-gapselect 的 control 部件 gap 覆盖槽。
--xh-select-control-hcontrolblock-size
min-block-size
has([data-xh-field-input][data-xh-field-layout='multi-tag'])
has([data-xh-field-input][data-xh-field-layout='single-line'])
has([data-xh-field-input][data-xh-field-layout='textarea'])
xh-field-chrome
xh-field-input
xh-field-layout=multi-tag
xh-field-layout=single-line
xh-field-layout=textarea
--xh-_select-hselect 的 control 部件 block-size、min-block-size 覆盖槽。
--xh-select-control-min-wcontrol
root
min-inline-sizedefault
xh-field-chrome
--xh-control-min-wselect 的 control、root 部件 min-inline-size 覆盖槽。
--xh-select-control-pxcontrolpadding-inlinexh-field-chrome--xh-_select-pxselect 的 control 部件 padding-inline 覆盖槽。
--xh-select-control-radiuscontrolborder-radiusxh-field-chrome--xh-shape-controlselect 的 control 部件 border-radius 覆盖槽。
--xh-select-control-shadowcontrolbox-shadowxh-field-chromenoneselect 的 control 部件 box-shadow 覆盖槽。
--xh-select-control-wrootinline-size
min-inline-size
default--xh-control-wselect 的 root 部件 inline-size、min-inline-size 覆盖槽。
--xh-select-empty-fgemptycolordefault--xh-material-frosted-fg-mutedselect 的 empty 部件 color 覆盖槽。
--xh-select-empty-font-sizeemptyfont-sizedefault--xh-_select-font-sizeselect 的 empty 部件 font-size 覆盖槽。
--xh-select-empty-pxemptypadding-inlinedefault--xh-_select-item-pxselect 的 empty 部件 padding-inline 覆盖槽。
--xh-select-empty-pyemptypadding-blockdefault--xh-space-3select 的 empty 部件 padding-block 覆盖槽。
--xh-select-error-fgerrorcolordefault--xh-material-frosted-fg-mutedselect 的 error 部件 color 覆盖槽。
--xh-select-error-font-sizeerrorfont-sizedefault--xh-_select-font-sizeselect 的 error 部件 font-size 覆盖槽。
--xh-select-error-pxerrorpadding-inlinedefault--xh-_select-item-pxselect 的 error 部件 padding-inline 覆盖槽。
--xh-select-error-pyerrorpadding-blockdefault--xh-space-3select 的 error 部件 padding-block 覆盖槽。
--xh-select-footer-borderfooterborder-block-startdefault--xh-material-frosted-separatorselect 的 footer 部件 border-block-start 覆盖槽。
--xh-select-footer-fgfootercolordefault--xh-material-frosted-fg-mutedselect 的 footer 部件 color 覆盖槽。
--xh-select-footer-font-sizefooterfont-sizedefault--xh-text-secondary-sizeselect 的 footer 部件 font-size 覆盖槽。
--xh-select-footer-gapfootergapdefault--xh-space-2select 的 footer 部件 gap 覆盖槽。
--xh-select-footer-pxfooterpadding-inlinedefault--xh-space-2select 的 footer 部件 padding-inline 覆盖槽。
--xh-select-footer-pyfooterpadding-blockdefault--xh-space-2select 的 footer 部件 padding-block 覆盖槽。
--xh-select-gaplabel
root
gap
margin-block-end
default--xh-space-1select 的 label、root 部件 gap、margin-block-end 覆盖槽。
--xh-select-group-gapgroupgapdefault--xh-list-option-gapselect 的 group 部件 gap 覆盖槽。
--xh-select-group-label-fggroup-labelcolordefault--xh-material-frosted-fg-mutedselect 的 group-label 部件 color 覆盖槽。
--xh-select-group-label-font-sizegroup-labelfont-sizedefault--xh-text-caption-sizeselect 的 group-label 部件 font-size 覆盖槽。
--xh-select-group-label-font-weightgroup-labelfont-weightdefault--xh-font-weight-mediumselect 的 group-label 部件 font-weight 覆盖槽。
--xh-select-group-label-pxgroup-labelpadding-inlinedefault--xh-_select-item-pxselect 的 group-label 部件 padding-inline 覆盖槽。
--xh-select-group-label-pygroup-labelpadding-blockdefault--xh-space-1select 的 group-label 部件 padding-block 覆盖槽。
--xh-select-group-separator-colorgroupborder-block-startdefault--xh-material-frosted-separatorselect 的 group 部件 border-block-start 覆盖槽。
--xh-select-group-spacinggrouppadding-block-startdefault--xh-space-1_5select 的 group 部件 padding-block-start 覆盖槽。
--xh-select-icon-sizecontrol
root
--xh-icon-sizedefault
size=lg
size=sm
xh-field-chrome
--xh-_field-size-glyph-size
--xh-glyph-size-lg
--xh-glyph-size-md
--xh-glyph-size-sm
select 的 control、root 部件 --xh-icon-size 覆盖槽。
--xh-select-indicator-fgindicatorcolordefault--xh-fg-mutedselect 的 indicator 部件 color 覆盖槽。
--xh-select-item-bg-hoveritembackground-colordisabled
error
highlighted
hover
is(:focus-visible, [data-highlighted])
is([aria-selected='true'], [data-selected])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
selected
xh-collection-context=overlay
--xh-bg-subtleselect 的 item 部件 background-color 覆盖槽。
--xh-select-item-bg-presseditembackground-colordisabled
error
is(:active, [data-pressed])
is([aria-selected='true'], [data-selected])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
selected
xh-collection-context=overlay
--xh-bg-subtle-hoverselect 的 item 部件 background-color 覆盖槽。
--xh-select-item-check-fgitemcolordisabled
error
highlighted
hover
is(:active, [data-pressed])
is(:focus-visible, [data-highlighted])
is([aria-selected='true'], [data-selected])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
selected
state=checked
xh-collection-context=overlay
xh-collection-slot=indicator
--xh-_select-accentselect 的 item 部件 color 覆盖槽。
--xh-select-item-fgitemcolordefault
disabled
error
highlighted
hover
is(:active, [data-pressed])
is(:focus-visible, [data-highlighted])
is([aria-selected='true'], [data-selected])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
selected
xh-collection-context=overlay
--xh-material-frosted-fgselect 的 item 部件 color 覆盖槽。
--xh-select-item-fg-selecteditemcolordisabled
error
highlighted
hover
is(:active, [data-pressed])
is(:focus-visible, [data-highlighted])
is([aria-selected='true'], [data-selected])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
selected
xh-collection-context=overlay
--xh-select-item-fgselect 的 item 部件 color 覆盖槽。
--xh-select-item-font-sizeitemfont-sizedefault--xh-_select-font-sizeselect 的 item 部件 font-size 覆盖槽。
--xh-select-item-font-weight-selecteditemfont-weightdisabled
error
highlighted
hover
is(:active, [data-pressed])
is(:focus-visible, [data-highlighted])
is([aria-selected='true'], [data-selected])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
selected
xh-collection-context=overlay
--xh-font-weight-mediumselect 的 item 部件 font-weight 覆盖槽。
--xh-select-item-gapitemmargin-inline-end
margin-inline-start
default
xh-collection-slot=indicator
xh-collection-slot=prefix
xh-collection-slot=shortcut
xh-collection-slot=suffix
--xh-_select-gapselect 的 item 部件 margin-inline-end、margin-inline-start 覆盖槽。
--xh-select-item-hitemmin-block-sizedefault--xh-_select-item-hselect 的 item 部件 min-block-size 覆盖槽。
--xh-select-item-indicator-sizeitem-indicator--xh-icon-size
block-size
inline-size
default--xh-control-indicator-sizeselect 的 item-indicator 部件 --xh-icon-size、block-size、inline-size 覆盖槽。
--xh-select-item-leadingitemline-heightdefault--xh-leading-normalselect 的 item 部件 line-height 覆盖槽。
--xh-select-item-pxitempadding-inlinedefault--xh-_select-item-pxselect 的 item 部件 padding-inline 覆盖槽。
--xh-select-item-pyitempadding-blockdefault--xh-_select-item-pyselect 的 item 部件 padding-block 覆盖槽。
--xh-select-item-radiusitemborder-radiusdefault--xh-shape-insetselect 的 item 部件 border-radius 覆盖槽。
--xh-select-label-fglabelcolordefault--xh-fg-mutedselect 的 label 部件 color 覆盖槽。
--xh-select-label-fg-disabledlabelcolordisabled--xh-fg-mutedselect 的 label 部件 color 覆盖槽。
--xh-select-label-font-sizelabelfont-sizedefault--xh-text-label-sizeselect 的 label 部件 font-size 覆盖槽。
--xh-select-label-font-weightlabelfont-weightdefault--xh-text-label-weightselect 的 label 部件 font-weight 覆盖槽。
--xh-select-label-gaplabelmargin-block-enddefault--xh-space-2select 的 label 部件 margin-block-end 覆盖槽。
--xh-select-layerpositionerz-indexdefault--xh-_layerselect 的 positioner 部件 z-index 覆盖槽。
--xh-select-list-gaplistgapdefault--xh-list-option-gapselect 的 list 部件 gap 覆盖槽。
--xh-select-loading-fgloadingcolordefault--xh-material-frosted-fg-mutedselect 的 loading 部件 color 覆盖槽。
--xh-select-loading-font-sizeloadingfont-sizedefault--xh-_select-font-sizeselect 的 loading 部件 font-size 覆盖槽。
--xh-select-loading-pxloadingpadding-inlinedefault--xh-_select-item-pxselect 的 loading 部件 padding-inline 覆盖槽。
--xh-select-loading-pyloadingpadding-blockdefault--xh-space-3select 的 loading 部件 padding-block 覆盖槽。
--xh-select-placeholder-fgvalue-textcolorplaceholder--xh-fg-subtleselect 的 value-text 部件 color 覆盖槽。
--xh-select-tag-list-gaptag-listgapxh-tag-list--xh-space-1select 的 tag-list 部件 gap 覆盖槽。
--xh-select-trigger-fgtriggercolordefault--xh-fg-defaultselect 的 trigger 部件 color 覆盖槽。
--xh-select-trigger-font-sizetriggerfont-sizedefault--xh-_select-font-sizeselect 的 trigger 部件 font-size 覆盖槽。
--xh-select-trigger-gaptriggergapdefault--xh-_select-gapselect 的 trigger 部件 gap 覆盖槽。

动效 ​

动效角色:按压 · 状态 · 切换 · 指示与换位 · 出现 · 列表(见动效规范)。

共享关键帧 xh-fade-in · xh-fade-out · xh-overlay-pop-in · xh-pop-in · xh-pop-out 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立;background-color · border-color · color · rotate · translate 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

皮肤之外还有一段:退场由适配器的退场闸门把关,动画播完才真收起。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

响应式 ​

皮肤另按输入能力分档:hover: hover · hover: none:同一份皮肤在触屏与带指针的设备上不一样,与视口宽度无关。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License