Popup 与 Overlay
Popup 和 Overlay 用于显示脱离正常布局流的内容,例如下拉框、日期选择、右键菜单、通知、抽屉和模态框。
典型场景
RenderComboBox的下拉选项。RenderDatePicker的日期面板。RenderColorPicker的颜色面板。ContextMenuManager的右键菜单。NotificationManager的 toast 通知。RenderModal的模态框。RenderDrawer的抽屉。
PopupManager
PopupManager 负责管理当前弹层上下文、绘制请求和关闭流程。业务通常不直接操作底层 Popup,而是使用具体组件。
自定义弹层需要明确:
- 锚点是谁。
- 如何计算弹层位置。
- 点击外部是否关闭。
- Escape 是否关闭。
- 焦点如何进入和恢复。
- 关闭时如何释放资源。
PopupOpenOptions.interactionMode 用一个字段表达输入和焦点策略:
| 模式 | pointer | Popup 焦点域 | 典型用途 |
|---|---|---|---|
passive |
不参与 | 不建立 | tooltip、旧式只读 Toast |
nonmodal |
命中实际区域时参与 | 不建立 | 带按钮的 Toast |
popup |
参与 | 建立 | 菜单、下拉、Popover |
旧 interactive: false/true 继续映射为 passive/popup。新代码应只传 interactionMode;两个字段同时传入会抛出参数错误。
closeExisting 和 closeTransient() 会关闭可替换的 passive/popup 项,但保留 nonmodal overlay。因此 Toast 操作打开 Modal 时,只完成当前通知的生命周期,不会清空同栈的其他通知。
局部 OverlayHost
RenderOverlayHost 是布局组件,不通过 PopupManager 挂到全局弹层。它把底层内容和覆盖层内容都放在同一棵 render tree 中,适合当前工作区内的应用中心、局部选择器、局部阻断面板。
选择边界:
- 浮层需要跟随某个输入控件或右键位置时,使用 popup / popover / context menu。
- 浮层只属于当前布局区域,且希望遮罩限制在这个区域内时,使用
RenderOverlayHost。 - 浮层需要全局模态、通知层级或跨工作区显示时,使用 modal / notification / drawer 等组件。
- 浮层属于开发者工具、请求日志、布局检查器这类宿主工具区,并且不应被业务 modal/loading 遮挡时,使用
AppOverlayService.showDockWindow()。
宿主停靠窗口
AppOverlayService.showDockWindow() 会把 RenderWindow 作为 runtime 级 root 停靠在主内容区左侧、右侧或底部。停靠窗口不属于主窗口的 owned windows;主内容 viewport 会被停靠区域挤压,普通 popup、modal、loading 只覆盖主内容区。停靠窗口标题栏提供左侧/右侧/底部/浮动切换,并且相邻主内容的边缘可以拖拽调整尺寸。同一 side 上的多个停靠窗口会共享该 side 的厚度,并沿垂直或水平方向分段显示,避免互相覆盖。
宿主停靠窗口适合:
- Layout Inspector。
- Runtime Diagnostics。
- 请求日志、网络面板、状态监控面板。
它不适合业务流程中的详情抽屉、选择弹窗或表单弹窗;这些仍应使用 Drawer、Modal、Popover、Popup 或局部 RenderOverlayHost。
锚点定位
锚点弹层需要跟随触发控件移动。当页面滚动、布局变化或窗口尺寸变化时,弹层位置必须重新计算。
下拉、日期选择、颜色选择和查询选择都属于锚点弹层。
Overlay 层级
不同弹层有不同层级:
- 普通 popup。
- modal overlay。
- tooltip。
- notification。
层级设计要避免 tooltip 被 modal 遮挡关系弄乱,也要避免非交互层拦截底层事件。
生命周期
弹层关闭后应释放:
- 子组件。
- 输入会话。
- 定时器。
- 事件订阅。
- 临时上下文。
如果弹层内容很重,不应在未显示时长期持有完整组件树。
性能原则
- 大列表下拉应限制最大高度或虚拟化。
- popup 重定位应基于锚点矩形,不应全量布局页面。
- 通知高频出现时要合并或限流。
- 关闭弹层必须断开引用,避免内存泄露。