/** * ClickAwayListener Component - Lynx 版 MUI ClickAwayListener * 一比一对应 MUI ClickAwayListener * * 监听点击事件,当点击发生在子元素外部时触发回调 * * 功能: * - 支持 mouseEvent 配置 (onClick, onMouseDown, onMouseUp) * - 支持 touchEvent 配置 (onTouchStart, onTouchEnd) * - 支持 disableReactTree 配置 * - 使用透明遮罩层实现外部点击检测 */ import './ClickAwayListener.css' // Hooks 类型在 lynx.d.ts 中声明 export interface ClickAwayListenerProps { /** 子元素 */ children?: any /** 点击外部时的回调 */ onClickAway: (event?: any) => void /** 是否监听鼠标事件 */ mouseEvent?: 'onClick' | 'onMouseDown' | 'onMouseUp' | false /** 是否监听触摸事件 */ touchEvent?: 'onTouchStart' | 'onTouchEnd' | false /** 是否禁用 React 树检测 */ disableReactTree?: boolean } // ============================================= // ClickAwayListener 类名 // ============================================= export const clickAwayListenerClasses = { root: 'MuiClickAwayListener-root', backdrop: 'MuiClickAwayListener-backdrop', content: 'MuiClickAwayListener-content', } // ============================================= // ClickAwayListener 组件实现 - 完整版 // ============================================= export function ClickAwayListener(props: ClickAwayListenerProps) { const { children, onClickAway, mouseEvent = 'onClick', touchEvent = 'onTouchEnd', disableReactTree = false, ...other } = props // 内容区域引用 const nodeRef = useRef(null) // 是否正在处理合成事件(防止重复触发) const syntheticEventRef = useRef(false) // 激活状态(用于延迟激活,避免立即触发) const activatedRef = useRef(false) // 组件挂载后激活 useEffect(() => { // 延迟激活,避免在点击触发组件显示时立即触发 clickAway const timeoutId = setTimeout(() => { activatedRef.current = true }, 0) return () => { activatedRef.current = false clearTimeout(timeoutId) } }, []) // 处理点击事件 const handleClickAway = useCallback((event: any) => { // 未激活时不处理 if (!activatedRef.current) { return } // 如果是合成事件触发的,跳过 if (syntheticEventRef.current) { syntheticEventRef.current = false return } // 触发回调 if (onClickAway) { onClickAway(event) } }, [onClickAway]) // 处理内容区域点击(阻止冒泡) const handleContentClick = useCallback((event: any) => { // 标记为合成事件,防止触发 clickAway syntheticEventRef.current = true }, []) // 遮罩层样式 const backdropStyle: Record = { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 0, backgroundColor: 'transparent', } // 内容区域样式 const contentStyle: Record = { position: 'relative', zIndex: 1, } return ( {/* 透明遮罩层 - 捕获外部点击 */} {/* 内容区域 - 点击不触发 clickAway */} {children} ) } export default ClickAwayListener