Dialog / Sheet 点击外部关闭收紧为「只认蒙层」 (WEB-1497)
版本: 0.10.0 · 类型: 🐛 Bug 修复
问题
Dialog 的 closeOnOverlayClick 名义是「点蒙层关闭」,实现却只是放行 Radix DismissableLayer 的默认判定;Sheet(别名 Drawer)连开关都没有,默认就是「点任意外部即关」。两者共享同一个坑:任何浮在弹层之上且可交互的全局元素被点到时,都会被判成「点了外部」而关闭弹窗 / 面板。
真实事故(下游 USE-88):web4 的客服浮层 ContactSupportWidget(z-9999 + 显式 pointer-events-auto,为了在 Radix 把 body 锁成 pointer-events: none 时仍可交互)。用户在设置弹窗里拉起客服浮层后点它的关闭按钮或拖它的标题栏,设置弹窗被一起关掉。
根因
Radix 的 onPointerDownOutside 只看 pointerdown 的 target 是否落在 Content 子树 / 已注册 DismissableLayerBranch 内,完全不看 z-index 与视觉层叠。浮层在视觉上盖住蒙层这件事,Radix 判定不到,一律算作 outside。
改动文件
packages/design/src/utils/overlay-dismiss.ts(新增)packages/design/src/components/ui/dialog.tsxpackages/design/src/components/ui/sheet.tsxpackages/design/src/components/Dialog/Dialog.tsxpackages/design/src/components/Dialog/__tests__/Dialog.test.tsxpackages/design/src/components/Sheet/Sheet.tsxpackages/design/src/components/Sheet/__tests__/Sheet.test.tsxpackages/design-site/docs/components/dialog.mdxpackages/design-site/docs/components/sheet.mdxpackages/design-site/i18n/zh-CN/docusaurus-plugin-content-docs/current/components/dialog.mdxpackages/design-site/i18n/zh-CN/docusaurus-plugin-content-docs/current/components/sheet.mdx
改动内容
- 新增内部工具
overlay-dismiss.ts,导出shouldDismissOnOutsidePointerDown({ event, overlay })与本地声明的PointerDownOutsideEvent类型(未新增依赖,未对外导出)。判定:蒙层存在时,只有event.detail.originalEvent.target被蒙层元素包含才返回true;蒙层为null(非模态modal={false}下 Radix 的DialogOverlay/SheetOverlay直接不渲染)时返回true,保持「点外部即关」的既有语义。 ui/dialog.tsx、ui/sheet.tsx:DialogContent/SheetContent新增overlayRef,透传给各自的 Overlay;两个 Overlay 补上语义化 classpd-dialog__overlay/pd-sheet__overlay。Dialog.tsx:持有overlayRef,handlePointerDownOutside改为「closeOnOverlayClick为真且判定通过」才放行关闭,否则一律preventDefault()。默认值仍是false,行为不变。DialogContentSlotProps的Omit加上'overlayRef',避免内部 ref 泄漏进contentProps。Sheet.tsx:新增closeOnOverlayClick?: boolean,默认true(刻意与 Dialog 的默认false不同:Sheet 既有行为就是点外部即关,默认true才不破坏现有消费方,只把生效范围收紧到蒙层;传false则点外部一律不关)。SheetOpenOptions自动继承该字段。- 测试:Dialog 新增 2 条(点浮于弹层之上的外部元素不关闭;
modal={false}时仍按旧语义关闭),Sheet 新增 4 条(默认点蒙层关闭、closeOnOverlayClick={false}不关闭、点浮层不关闭、modal={false}仍关闭);两侧既有蒙层用例改用新的语义 class 选择器并断言其存在。 - 文档(
dialog.mdx/sheet.mdx及对应中文 i18n 版本)同步更新closeOnOverlayClick的语义说明。
影响范围
web4 现有 5 处传 closeOnOverlayClick 为真的调用方(features/settings/openSettings.tsx、features/credit/components/CreditDetailModal.tsx、features/search/components/search-dialog/index.tsx、PlanSwitchDialog.tsx、ManageSeatsDialog.tsx)要的都是字面意义的点蒙层,无一依赖「点任意外部就关」,改后无预期回归;顺带修掉「弹窗内 Select / Popover 的 portal 内容被点到会关掉外层弹窗」这类同源问题。仅有的两个非模态调用方(RoutineForm.tsx、EditRoutineForm.tsx,modal={false})都没传 closeOnOverlayClick,且非模态分支已保持旧语义。