跳转到内容

Command 命令面板

覆盖在页面上的检索面板:输入筛选命令,方向键选择,回车执行。

用法

提供一份命令清单,过滤、归组与空态都由组件处理

还没执行过命令

组件结构

加粗的是必需部件。

data-scope="command"trigger · backdrop · positioner · content · input · list · group · group-label · item · item-text · empty · loading · footer

示例

快捷键唤起与手写部件

Mod+K 打开,命中的文字由文本高亮标出,行尾挂载各命令自己的快捷键

按一下唤起命令面板

遮罩形态

variant 只落在 backdrop 层:opaque 压一层底色、blur 模糊背后、transparent 只阻挡点击

远程检索

filter 关闭:传入的 collection 就是当前应显示的条目,筛选归服务端;取数期间 loading 显示在途占位、列表压暗一档,空态让位

还没选过人

设计指引

何时使用

  • 功能分散在多层菜单中,用户知道要做什么但找不到入口。
  • 需要一条跨页面的统一入口:搜索页面、设置、数据,执行动作。
  • 熟练用户需要全程键盘操作:唤起、输入、回车。

何时不用

特性

  • 内置过滤:传入清单后按检索串逐词筛选、按 group 归组,空组自动移除。keywords 让一条命令同时匹配英文名、拼音与旧称。
  • 过滤可以关闭(filter 置否),改由调用方筛选;远端检索使用这一档。
  • 面板默认是模态浮层:捕获焦点、锁定滚动、背景失活,Escape 与点击遮罩收起,收起后焦点归还触发按钮。modal=false 时不渲染遮罩、不拦截页面指针,也不启用这些模态约束;展开期间切换会立即同步。
  • 焦点全程在检索框,活动候选经 aria-activedescendant 报告给读屏;活动候选同步 aria-selected=true,其余候选显式为 false,输入后活动候选自动回到首条。
  • 这里的 aria-selected 遵循 WAI-ARIA 组合框规范中“选中随焦点移动”的模式,只描述当前活动建议;命令执行后不保留持久选中状态,视觉上也不绘制对号或选中底。
  • 两种非条目相位各有部件:空(empty)与在途(loading)。取数期间显示在途占位,空态让位,两者不同时出现。
  • 零可见命令时列表不保留额外空白行;搜索输入和作者提供的状态、底栏仍然在场。未提供 Empty / Loading 文案时不显示空白占位,也不自动生成提示文字。
  • collection 与过滤结果保持数据语义;已挂载节点上的 hidden 会排除对应条目或分组的交互与 ARIA 高亮。未挂载或虚拟候选不按隐藏推断;展开期间替换列表节点后,可见性观察会切换到新节点。
  • closeOnSelect 决定选中后是否收起;连续执行多条命令时关闭它。

组合

  • 唤起可用键盘按键:显示 Mod + K 并开启 register,在回调中执行 setOpen(true)
  • 条目文字中标出命中的字符使用文本高亮,检索串即关键词。
  • 行尾的快捷键提示和注册行为共用同一个 Kbd,避免展示与实际绑定不一致。

最佳实践

  • 命令名写成“动词 + 宾语”(新建用户、导出报表),用户按动作查找。
  • 分组按用户的心智模型划分(页面 / 设置 / 动作),不按代码模块划分。
  • 底部提示条说明三件事:上下键选择、回车执行、Escape 关闭。
  • 命令来自远端时提供在途占位,不让面板停留在空白状态。

反模式

  • 把后台的每个按钮都放进来,面板变成第二份菜单树,检索反而更慢。
  • 只匹配命令的中文全名:用户输入 export 时搜不到,别名应写进 keywords
  • 选中后没有任何反馈:命令应当场生效、导航过去,或给出一条轻提示。

API 参考

产物

自定义元素<xh-command>
Vue 组件XhCommandContent XhCommandEmpty XhCommandFooter XhCommandGroup XhCommandGroupLabel XhCommandInput XhCommandItem XhCommandItemText XhCommandList XhCommandLoading XhCommandRoot XhCommandTrigger
组合式函数useCommand
状态机commandMachine
皮肤@xihan-ui/styles/command.css

Props

属性类型必填说明
collectionreadonly CommandNode[]命令清单,标题、别名、归组与禁用的事实源。
groupsreadonly CommandGroup[]分组声明,决定组名与组序;清单中出现而这里未声明的组排在后面。
openboolean
defaultOpenboolean
inputValuestring
defaultInputValuestring
filterboolean内置过滤,默认开启。关闭后由调用方自行筛选,传入的 collection 即当前应显示的命令。
caseSensitiveboolean过滤区分大小写,默认不区分。
closeOnSelectboolean选中一条命令后收起面板,默认 true。
modalboolean模态(陷焦点、锁滚动、遮罩交互外关闭),默认 true。
closeOnEscapeboolean
closeOnInteractOutsideboolean
restoreFocusboolean
loopboolean方向键到达末尾是否回绕,默认 true。
loadingboolean命令加载中:列表报告 aria-busy,显示在途占位,隐藏空态占位。
placeholderstring检索框的占位文字。
dirDirection文字方向,默认 ltr。
sizeSize尺寸:sm / md / lg。只影响面板宽度与条目的几何档位。
variantOverlayBackdropVariant遮罩形态:opaque / blur / transparent。写在 backdrop 上,只影响该层的底色与模糊。
translationsPartial<CommandTranslations>
onOpenChange(details: CommandOpenChangeDetails) => voidopen 变化意图回调;受控时是唯一出口,非受控时随内部转移一并通知。
onInputValueChange(details: CommandInputValueChangeDetails) => void检索串变化意图回调。
onSelect(details: CommandSelectDetails) => void选中一条命令:库不执行任何动作,后续行为全部由这里决定。

事件

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

事件载荷说明
open-changeCommandOpenChangeDetailsopen 状态变化;detail 为 { open: boolean, reason?: string }
input-value-changeCommandInputValueChangeDetails检索串变化;detail 为 { inputValue: string }
selectCommandSelectDetails选中一条命令;detail 为 { value: string, label: string }

插槽

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhCommandRootdefaultCommandRootSlotProps
XhCommandRoottrigger铺开时的触发按钮内容;未提供时不渲染触发器(面板改由快捷键或 v-model:open 唤起)。
XhCommandRootitemCommandNodeMeta
XhCommandRootempty
XhCommandRootfooter

状态

公开状态写入 data-state

部件取值
trigger'open' | 'closed'
backdrop'open' | 'closed'
positioner'open' | 'closed'
content'open' | 'closed'
input'open' | 'closed'
list'open' | 'closed'
empty'open' | 'closed'
loading'open' | 'closed'
footer'open' | 'closed'

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

状态open · closed

事件OPEN · TOGGLE · CLOSE · CONTROLLED.OPEN · CONTROLLED.CLOSE · INPUT.CHANGE · INPUT.SET · ITEM.HIGHLIGHT · HIGHLIGHT.CLEAR · ITEM.SELECT · PRESS.START · PRESS.END

判据isOpenControlled · keepsOpenOnSelect · canPress

connect API

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

成员类型说明
openboolean
inputValuestring当前检索串。
groupsreadonly CommandGroupMeta[]过滤归组之后当前应显示的命令,空组已移除。
resultsreadonly CommandNodeMeta[]上述分组视图展平的结果,次序即方向键的移动次序。
highlightedValuestring | null键盘锚点;收起时为 null。
emptyboolean没有剩余条目。
loadingboolean
setOpen(next: boolean) => void
setInputValue(next: string) => void
select(value: string) => void直接选中某条命令,等同于在它上面按回车。
getTriggerProps() => T['button']
getBackdropProps() => T['element']
getPositionerProps() => T['element']
getContentProps() => T['element']
getInputProps() => T['input']
getListProps() => T['element']
getGroupProps(props: CommandGroupProps) => T['element']
getGroupLabelProps(props: CommandGroupProps) => T['element']
getItemProps(props: CommandItemProps) => T['element']
getItemTextProps(props: CommandItemProps) => T['element']
getEmptyProps() => T['element']空态占位:放在 content 中、list 的兄弟。 提供 collection 时由连接层按条数收放;条目手写时不写 hidden,是否显示由作者决定。
getLoadingProps() => T['element']在途占位:与空态占位同一位置,两者不同时显示。
getFooterProps() => T['element']面板底部的提示条:内容由作者决定,这里只提供位置与观感。

无障碍

键盘

规格出处:W3C APG

按键生效条件行为
Enter / Spacefocus in trigger打开面板并把焦点移入检索框
Escapeopen关闭面板并把焦点还给 trigger
ArrowDownopen锚点移到下一条命令,禁用的跳过
ArrowUpopen锚点移到上一条命令,禁用的跳过
Homeopen锚点移到首条命令
Endopen锚点移到末条命令
Enteropen, 锚点落在可用命令上选中该命令;长按连发的重复键不重复选中
Enteropen, 锚点落在可用命令上且未加载,按住按住期间锚点命令投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,命令随面板收起一并撤下
Tabopen, modal在面板内循环焦点

ARIA

以下属性由 connect 生成。

部件属性
triggeraria-controlscontent 部件的 id
triggeraria-expanded'true' | 'false'
triggeraria-haspopup'dialog'
contentaria-hidden!open || undefined
contentaria-labeltranslations?.title
contentaria-modal'true' | 'false'
contentrole'dialog'
inputaria-activedescendantitem 部件的 id | undefined
inputaria-autocomplete'list'
inputaria-controlslist 部件的 id
inputaria-expanded'true'
inputaria-haspopup'listbox'
inputaria-labeltranslations?.input
inputrole'combobox'
listaria-busy'true' | undefined
listaria-labeltranslations?.list
listrole'listbox'
grouparia-labelledbygroup-label 部件的 id
grouprole'group'
itemaria-disabled'true' | 'false'
itemaria-selected'true' | 'false'
itemrole'option'
emptyrole'status'
loadingrole'status'

样式参考

皮肤

@xihan-ui/styles/command.css 使用 [data-scope="command"][data-part="trigger"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides

数据属性

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

部件属性
triggerdata-state'open' | 'closed'
backdropdata-state'open' | 'closed'
backdropdata-variantprops.variant
positionerdata-positioned''
positionerdata-sizeprops.size
positionerdata-state'open' | 'closed'
contentdata-sizeprops.size
contentdata-state'open' | 'closed'
inputdata-state'open' | 'closed'
listdata-state'open' | 'closed'
itemdata-disabled''(条件成立时才出现)
itemdata-highlighted''(条件成立时才出现)
itemdata-pressed''(条件成立时才出现)
itemdata-xh-collection-context'overlay'
itemdata-xh-collection-item''
itemdata-xh-collection-sizeprops.size
item-textdata-disabled''(条件成立时才出现)
item-textdata-highlighted''(条件成立时才出现)
item-textdata-xh-collection-slot'text'
emptydata-state'open' | 'closed'
loadingdata-state'open' | 'closed'
footerdata-state'open' | 'closed'

CSS 变量

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

变量部件CSS 属性状态默认来源说明
--xh-command-backdrop-bgbackdropbackgrounddefault--xh-bg-overlaycommand 的 backdrop 部件 background 覆盖槽。
--xh-command-backdrop-blurbackdropbackdrop-filtervariant=blur--xh-overlay-backdrop-blurcommand 的 backdrop 部件 backdrop-filter 覆盖槽。
--xh-command-backdrop-layerbackdropz-indexdefault--xh-_layercommand 的 backdrop 部件 z-index 覆盖槽。
--xh-command-bgcontentbackgrounddefault--xh-material-elevated-bgcommand 的 content 部件 background 覆盖槽。
--xh-command-bordercontentborderdefault--xh-material-elevated-bordercommand 的 content 部件 border 覆盖槽。
--xh-command-empty-fgemptycolordefault--xh-fg-subtlecommand 的 empty 部件 color 覆盖槽。
--xh-command-empty-font-sizeemptyfont-sizedefault--xh-_command-font-sizecommand 的 empty 部件 font-size 覆盖槽。
--xh-command-empty-pxemptypadding-inlinedefault--xh-_command-pxcommand 的 empty 部件 padding-inline 覆盖槽。
--xh-command-empty-pyemptypadding-blockdefault--xh-space-6command 的 empty 部件 padding-block 覆盖槽。
--xh-command-fgcontentcolordefault--xh-material-elevated-fgcommand 的 content 部件 color 覆盖槽。
--xh-command-footer-borderfooterborder-block-startdefault--xh-border-subtlecommand 的 footer 部件 border-block-start 覆盖槽。
--xh-command-footer-fgfootercolordefault--xh-fg-mutedcommand 的 footer 部件 color 覆盖槽。
--xh-command-footer-font-sizefooterfont-sizedefault--xh-text-caption-sizecommand 的 footer 部件 font-size 覆盖槽。
--xh-command-footer-gapfootergapdefault--xh-space-2command 的 footer 部件 gap 覆盖槽。
--xh-command-footer-pxfooterpadding-inlinedefault--xh-_command-pxcommand 的 footer 部件 padding-inline 覆盖槽。
--xh-command-footer-pyfooterpadding-blockdefault--xh-space-2command 的 footer 部件 padding-block 覆盖槽。
--xh-command-group-gapgroupgapdefault--xh-list-option-gapcommand 的 group 部件 gap 覆盖槽。
--xh-command-group-label-fggroup-labelcolordefault--xh-fg-subtlecommand 的 group-label 部件 color 覆盖槽。
--xh-command-group-label-font-sizegroup-labelfont-sizedefault--xh-text-caption-sizecommand 的 group-label 部件 font-size 覆盖槽。
--xh-command-group-label-font-weightgroup-labelfont-weightdefault--xh-font-weight-mediumcommand 的 group-label 部件 font-weight 覆盖槽。
--xh-command-group-label-pxgroup-labelpadding-inlinedefault--xh-_command-pxcommand 的 group-label 部件 padding-inline 覆盖槽。
--xh-command-group-label-pygroup-labelpadding-blockdefault--xh-space-1command 的 group-label 部件 padding-block 覆盖槽。
--xh-command-group-spacinggroupmargin-block-startdefault--xh-space-1_5command 的 group 部件 margin-block-start 覆盖槽。
--xh-command-icon-sizecontent
positioner
--xh-icon-sizeis([data-part='positioner'], [data-part='content'])
size=lg
size=sm
--xh-glyph-size-lg
--xh-glyph-size-md
--xh-glyph-size-sm
command 的 content、positioner 部件 --xh-icon-size 覆盖槽。
--xh-command-input-autofill-bginputbox-shadow-webkit-autofill
autofill
--xh-bg-surfacecommand 的 input 部件 box-shadow 覆盖槽。
--xh-command-input-autofill-fginput-webkit-text-fill-color-webkit-autofill
autofill
--xh-fg-defaultcommand 的 input 部件 -webkit-text-fill-color 覆盖槽。
--xh-command-input-borderinputborder-block-enddefault--xh-border-subtlecommand 的 input 部件 border-block-end 覆盖槽。
--xh-command-input-fginputcolordefault--xh-fg-defaultcommand 的 input 部件 color 覆盖槽。
--xh-command-input-font-sizeinputfont-sizedefault--xh-text-body-sizecommand 的 input 部件 font-size 覆盖槽。
--xh-command-input-hinputblock-sizedefault--xh-_command-input-hcommand 的 input 部件 block-size 覆盖槽。
--xh-command-input-pxinputpadding-inlinedefault--xh-_command-pxcommand 的 input 部件 padding-inline 覆盖槽。
--xh-command-inset-block-startpositionerpadding-block-startdefault--xh-space-8command 的 positioner 部件 padding-block-start 覆盖槽。
--xh-command-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-subtlecommand 的 item 部件 background-color 覆盖槽。
--xh-command-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-hovercommand 的 item 部件 background-color 覆盖槽。
--xh-command-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-fg-defaultcommand 的 item 部件 color 覆盖槽。
--xh-command-item-font-sizeitemfont-sizedefault--xh-_command-font-sizecommand 的 item 部件 font-size 覆盖槽。
--xh-command-item-gapitemgapdefault--xh-_command-gapcommand 的 item 部件 gap 覆盖槽。
--xh-command-item-leadingitemline-heightdefault--xh-leading-normalcommand 的 item 部件 line-height 覆盖槽。
--xh-command-item-pxitempadding-inlinedefault--xh-_command-pxcommand 的 item 部件 padding-inline 覆盖槽。
--xh-command-item-pyitempadding-blockdefault--xh-_command-item-pycommand 的 item 部件 padding-block 覆盖槽。
--xh-command-item-radiusitemborder-radiusdefault--xh-shape-controlcommand 的 item 部件 border-radius 覆盖槽。
--xh-command-layerpositionerz-indexdefault--xh-_layercommand 的 positioner 部件 z-index 覆盖槽。
--xh-command-list-busy-opacitylistopacitydefault--xh-state-disabled-opacitycommand 的 list 部件 opacity 覆盖槽。
--xh-command-list-gaplistgapdefault--xh-list-option-gapcommand 的 list 部件 gap 覆盖槽。
--xh-command-list-pxlistpadding-inlinedefault--xh-space-2command 的 list 部件 padding-inline 覆盖槽。
--xh-command-list-pylistpadding-blockdefault--xh-space-2command 的 list 部件 padding-block 覆盖槽。
--xh-command-loading-fgloadingcolordefault--xh-fg-subtlecommand 的 loading 部件 color 覆盖槽。
--xh-command-loading-font-sizeloadingfont-sizedefault--xh-_command-font-sizecommand 的 loading 部件 font-size 覆盖槽。
--xh-command-loading-pxloadingpadding-inlinedefault--xh-_command-pxcommand 的 loading 部件 padding-inline 覆盖槽。
--xh-command-loading-pyloadingpadding-blockdefault--xh-space-6command 的 loading 部件 padding-block 覆盖槽。
--xh-command-max-hcontentmax-block-sizedefault--xh-overlay-max-hcommand 的 content 部件 max-block-size 覆盖槽。
--xh-command-max-wcontentmax-inline-sizedefault--xh-_command-max-wcommand 的 content 部件 max-inline-size 覆盖槽。
--xh-command-placeholder-fginputcolorplaceholder--xh-fg-subtlecommand 的 input 部件 color 覆盖槽。
--xh-command-positioner-pbpositionerpadding-block-enddefault--xh-space-4command 的 positioner 部件 padding-block-end 覆盖槽。
--xh-command-positioner-pxpositionerpadding-inlinedefault--xh-space-4command 的 positioner 部件 padding-inline 覆盖槽。
--xh-command-radiuscontentborder-radiusdefault--xh-shape-overlaycommand 的 content 部件 border-radius 覆盖槽。
--xh-command-shadowcontentbox-shadowdefault--xh-material-elevated-shadowcommand 的 content 部件 box-shadow 覆盖槽。

动效

共享关键帧 xh-fade-in · xh-fade-out · xh-overlay-pop-in · xh-pop-out · xh-rise-infamily/motion.css 提供,皮肤 @import 它,单独引入仍成立。时长与缓动读动效令牌,改令牌即改全局节奏。

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

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

RTL

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

Released under The MIT License