DirectSurface UIDirectSurface UI
开始使用
文档/组件手册

HoverCard 交互式悬浮信息卡

HoverCardPopup 是由指针悬停触发、允许用户进入并操作的 anchored rich popup。它适合语义说明、文档链接、可复制代码和可滚动详情;普通短提示仍应使用 Tooltip,主动打开的操作面板使用 Popover。

import { HoverCardPopup, RenderMarkdownViewer } from 'ds-ui' const card = new HoverCardPopup()card.show({  anchor: tokenAnchor,  anchorRoot: editorRoot,  owner: editor,  placement: 'right-start',  fallbackPlacements: ['left-start', 'bottom-start', 'top-start'],  content: new RenderMarkdownViewer({    markdown: 'Details',    appearance: 'embedded',    shrinkWrap: 'both',  }),})

交互合同

  • popup 以 interactionMode: 'nonmodal' 打开,不建立 Popup focus scope。
  • Card 可接收 pointer、wheel 和 focus;Card 外的 pointer 仍路由到主 Render Tree。
  • notifyAnchorEnter() / notifyAnchorLeave() 与 Card 自身 enter/leave 共同形成默认 150ms 的关闭缓冲。
  • Card 内有 pointer capture、文本拖选或辅助焦点时,关闭计时不会销毁内容。
  • 普通 close()、Escape 和延迟关闭恢复进入 Card 前的非辅助焦点。
  • owner 永久销毁时调用 disposePopup({ restoreFocus: false }),避免重新聚焦已失效控件。
  • Modal、Prompt 或 PopupManager.closeTransient() 会关闭 Card;旧 nonmodal popup 的默认行为保持不变。

主要 API

interface HoverCardPopupOptions {  anchor: PopupAnchor  boundary?: PopupAnchor  content: RenderBox  owner?: object  anchorRoot?: RenderObject  anchorMode?: PopupAnchorMode  placement?: AnchoredPopupPlacement  fallbackPlacements?: readonly AnchoredPopupPlacement[]  overflowPreference?: AnchoredPopupOverflowPreference  gap?: number  inset?: number  minWidth?: number  maxWidth?: number  maxHeight?: number  closeDelayMs?: number  onDismiss?: () => void}

默认几何为 bottom-start、largest-space、gap=6、inset=4、minWidth=220、maxWidth=560、maxHeight=360。fallbackPlacements 按顺序声明备选位置;未传时保持“首选侧→对侧”的兼容行为。完整 popup shadow 会被限制在 boundary 内;未传 boundary 时使用所属 anchorRoot 的 Popup viewport。选定侧面空间不足时,Card 会收缩对应主轴尺寸,长内容由 Card 内部滚动承载,避免 clamp 回锚定内容上方。debugState().rect 只在真实 tooltip-layer paint 后提供;anchor、boundary、theme 或 viewport 改变后会立即失效,等待下一次 paint 重算。

Wheel 与焦点

Card 内容消费 wheel 时,底层控件不会滚动。可滚动内容即使已到顶部或底部,也应继续消费对应纵向 wheel,避免滚动穿透。内容不具备 overflow 且返回 false 时,事件才回退到主 Render Tree。

Card 的 focusable 子项通过 FocusManager.registerAuxiliaryRoot() 进入普通焦点序列,不改变 Modal/Popup scope。Card 关闭时由 FocusManager 验证并恢复可用的原焦点。

释放

替换内容时,旧内容会解除 parent 并释放。永久释放必须调用:

card.disposePopup({ restoreFocus: false })

释放后 Card 不可再次打开,timer、pointer capture、辅助焦点根和内容回调都会被清理。