DirectSurface UIDirectSurface UI
开始使用
文档/设计系统

组件状态标准

组件状态用于表达组件是否可操作、被选中、获得焦点、正在加载或处于异常。状态必须具有稳定语义,并允许真实业务中常见的组合;不能把组件压缩成互斥的单一视觉枚举。

状态总表

状态 语义 典型视觉 交互要求
normal 默认可操作。 默认背景、文本和边框。 可按组件语义响应输入。
hovered 指针位于可交互区域。 stateHoverOverlay 或弱边框变化。 不改变布局,不抢焦点。
pressed 指针或键盘正在激活。 statePressedOverlay 或明确按下反馈。 取消、离开或禁用后恢复。
focused 键盘焦点或文本输入焦点。 focus ring、caret 或 focused border。 可通过键盘继续操作。
selected 当前项被选中或激活。 selection 背景或强调条。 必须可与 hover、pressed、focus 同时表达。
disabled 整体不可交互。 弱化文本和背景。 不响应 pointer、keyboard、shortcut、popup。
readonly 值可看但不可改。 保持可读,弱化编辑入口。 复制、选择和滚动由组件语义决定。
loading 正在加载或处理。 spinner、遮罩、骨架或等待状态。 明确是否阻断输入。
empty 无数据但非错误。 空状态文案或占位。 可保留新增、刷新等操作。
error 数据或操作失败。 error 边框、文本或图标。 保留重试或修正入口。
warning 有风险但仍可继续。 warning 色或提示。 不默认阻断操作。
success 操作成功或状态正常。 success 色或图标。 不长期占据主要视觉层级。

visible 是所有 RenderObject 的基础状态,语义见组件基础状态契约。它会决定组件是否参与布局、绘制、hit test 和焦点,不属于普通交互状态。

组合模型

状态按不同职责分层,而不是使用一个总优先级互相覆盖:

能力门控:visible / disabled / blocking loading
持续语义:selected / readonly / error / warning / success
瞬时输入:pressed / hovered
独立指示:focused

合成顺序:

  1. visible=false 退出布局、绘制、hit test 和焦点。
  2. disabled 或阻断式 loading 关闭输入能力并清理临时状态。
  3. 选择基础 surface:normal、selected 或业务状态面。
  4. 在基础 surface 上合成 hovered;只有组件真实维护 pointer/key down 到 release/cancel 的 pressed 生命周期时,才继续合成 pressed,并让 pressed 优先于 hovered。
  5. 绘制 error/warning 等验证边框或提示。
  6. 最后绘制 focus ring,focus 不替换 selection 或 validation。

因此:

  • selected + hovered:保留 selection 主视觉,再叠加 stateHoverOverlay
  • selected + pressed:仅适用于真实维护 pressed 生命周期的组件;保留 selection 主视觉,再叠加 statePressedOverlay
  • selected + focused:保留 selection,并额外绘制 focus ring。
  • error + focused:保留 error 语义,并额外绘制 focus ring。
  • disabled + any:清理 hover、pressed、drag、popup 和焦点,绘制 disabled 状态。

框架内部样式配方可以使用状态 flags 表达组合;业务代码不应导入或复制内部 derive*Style() 来手工模拟这套规则。

状态视觉强度

一般强度顺序:

normal < hovered < selected;具有 pressed 生命周期时,pressed 提供最强的瞬时反馈

这只是视觉强度,不表示 selected 会覆盖 focused。focus 是独立边框/焦点环,validation 是独立语义层。

stateHoverOverlaystatePressedOverlay 是透明覆盖色,适合叠加在普通 surface 或 selection 上。普通按钮也可以使用完整的 surfaceControlHover / surfaceControlActive。二者选择由组件内部配方决定,页面代码不要混用。

Hover

  • 只在可交互命中区域内出现。
  • 指针离开,或组件被隐藏、禁用时必须消失。
  • 不改变组件尺寸、位置或周围布局。
  • 高密数据行保持轻量,不能因为 hover 重建数据源或触发整页 layout。
  • selected 项被 hover 时继续保留 selected 主视觉。

Pressed

  • 来自明确激活动作:pointer down、Space、Enter 或拖拽 handle。
  • 操作取消、指针释放、组件隐藏或禁用时必须恢复。
  • pressed 不改变尺寸。
  • click 通常在 release 且仍命中时触发,除非组件语义另有说明。
  • selected 项被按下时仍是 selected,只增加更明确的瞬时反馈。

Focused

  • 可键盘操作组件必须能获得焦点并绘制可见 focus ring。
  • focus ring 不占布局尺寸。
  • focus 不等于 selected;当前表格行、当前 tab 和输入焦点是不同语义。
  • pointer 点击是否聚焦由组件契约决定。
  • 组件禁用、隐藏或移出有效交互树后不能继续持有焦点。
  • focus ring 使用 focusBorder 或字段的 fieldFocusBorder,不能遮掉 error border。

Selected

  • 表示业务选择或当前激活项,不等于 focused。
  • 多选组件必须区分 selected 集合、active/focused item 和 selection anchor。
  • 当前文档 tab、当前导航项、当前表格行都属于 selected/active 语义,但可由不同组件配方表达。
  • selectionMuted 用于弱激活,selectionBg 用于明确选择,selectionStrong 用于需要更强识别的选择。
  • 实际选择背景经过组合后,文字颜色使用 resolveSelectionText() 保证可读。

Disabled

禁用后必须:

  • 阻断 pointer、keyboard、shortcut、popup 和 drag。
  • 从焦点顺序中移除,并释放已有焦点。
  • 关闭该组件已经打开的 popup、tooltip、context menu 或 drag preview。
  • 清理 hover、pressed 和拖拽临时状态。
  • 使用 disabled token 绘制,但保持必要内容可读。

disabled 是能力状态,不只是把 alpha 降低。组件不能保留“看起来可操作但事件被吞掉”的视觉。

Readonly

readonly 不等于 disabled:

  • 输入类控件通常保留文本选择、复制和滚动。
  • 选择类控件不打开修改值的 popup。
  • 命令类控件通常不提供 readonly,应使用 disabled。
  • readonly 不应使内容难以阅读。
  • readonly 可以和 focused、selected、error 同时存在,具体交互按组件契约处理。

Loading、Empty、Error

  • loading 要说明是否阻断输入;非阻断 loading 不应禁用整个组件。
  • empty 是正常状态,不使用 error 色。
  • error 需要可读信息或可定位视觉,并保留修正入口。
  • warning 表示风险,不默认阻断操作。
  • success 是结果反馈,不应长期占据页面主要层级。
  • Grid、Tree、List、Chart 等数据组件应把 loading、empty、error 放进组件状态层,不让每个业务页面重复绘制。

字段编辑状态

普通输入和表格内编辑使用字段语义:

  • 默认编辑背景:fieldBg
  • 编辑焦点:fieldFocusBorder
  • 字段内文本选择:fieldSelectionBg

PlainTextEditor、CodeEditor、Markdown 等文档编辑器使用 editor.selectionBg / editor.selectionText。文档编辑器选择范围可能很大,不能与普通字段 token 混为一类。

验收清单

  • hover、pressed、selected、focused 四种状态能否分别识别?
  • selected + hover、selected + focus 是否仍保留两种语义?
  • error + focus 是否同时可见?
  • disabled 是否真正关闭所有输入、popup 和 drag?
  • readonly 是否仍可读,并保留契约允许的复制/滚动?
  • 状态变化是否不改变尺寸、不触发无关布局?
  • 数据行 hover 是否只更新必要绘制区域?
  • 浅色、深色和紧凑主题下,状态顺序与文本对比度是否成立?