Enhance Tooltip Component with Parent Listening Feature
- Updated the Tooltip component to include a new prop, `listenParents`, allowing it to listen for mouse events on parent elements, improving tooltip visibility and interaction. - Refactored mouse event handling to manage hover states more effectively, ensuring tooltips behave consistently when interacting with parent elements. - Improved code organization and readability by consolidating event handling logic and utilizing refs for better performance.
This commit is contained in:
parent
8ab2e057bb
commit
ef57837371
@ -1,4 +1,10 @@
|
|||||||
import { cloneElement, isValidElement, useState, useEffect, useContext } from 'react'
|
import {
|
||||||
|
cloneElement,
|
||||||
|
isValidElement,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useContext
|
||||||
|
} from 'react'
|
||||||
import { Layout, Menu, Flex, Button } from 'antd'
|
import { Layout, Menu, Flex, Button } from 'antd'
|
||||||
import { CaretDownFilled } from '@ant-design/icons'
|
import { CaretDownFilled } from '@ant-design/icons'
|
||||||
import CollapseSidebarIcon from '../../Icons/CollapseSidebarIcon'
|
import CollapseSidebarIcon from '../../Icons/CollapseSidebarIcon'
|
||||||
@ -23,7 +29,7 @@ const CollapsedItemIcon = ({ className, icon, title }) => {
|
|||||||
: icon
|
: icon
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={title}>
|
<Tooltip title={title} listenParents={1}>
|
||||||
{iconNode ?? <span className={className} />}
|
{iconNode ?? <span className={className} />}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -13,11 +13,15 @@ const mergeHandler = (original, next) => (event) => {
|
|||||||
original?.(event)
|
original?.(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tooltip = ({ children, title, content }) => {
|
const Tooltip = ({ children, title, content, listenParents = 0 }) => {
|
||||||
const { showTooltip, hideTooltip } = useTooltipContext()
|
const { showTooltip, hideTooltip } = useTooltipContext()
|
||||||
const id = useId()
|
const id = useId()
|
||||||
const tooltipContent = title ?? content
|
const tooltipContent = title ?? content
|
||||||
|
const spanRef = useRef(null)
|
||||||
|
const hoveredTargetsRef = useRef(new Set())
|
||||||
const hoveringRef = useRef(false)
|
const hoveringRef = useRef(false)
|
||||||
|
const tooltipContentRef = useRef(tooltipContent)
|
||||||
|
tooltipContentRef.current = tooltipContent
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => hideTooltip(id)
|
return () => hideTooltip(id)
|
||||||
@ -30,43 +34,86 @@ const Tooltip = ({ children, title, content }) => {
|
|||||||
}, [id, showTooltip, tooltipContent])
|
}, [id, showTooltip, tooltipContent])
|
||||||
|
|
||||||
const onMouseEnter = (event) => {
|
const onMouseEnter = (event) => {
|
||||||
|
hoveredTargetsRef.current.add(event.currentTarget)
|
||||||
hoveringRef.current = true
|
hoveringRef.current = true
|
||||||
showTooltip(id, tooltipContent, event.clientX, event.clientY)
|
showTooltip(id, tooltipContentRef.current, event.clientX, event.clientY)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMouseLeave = () => {
|
const onMouseLeave = (event) => {
|
||||||
|
hoveredTargetsRef.current.delete(event.currentTarget)
|
||||||
|
if (hoveredTargetsRef.current.size > 0) return
|
||||||
hoveringRef.current = false
|
hoveringRef.current = false
|
||||||
hideTooltip(id)
|
hideTooltip(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onMouseEnterRef = useRef(onMouseEnter)
|
||||||
|
const onMouseLeaveRef = useRef(onMouseLeave)
|
||||||
|
onMouseEnterRef.current = onMouseEnter
|
||||||
|
onMouseLeaveRef.current = onMouseLeave
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const span = spanRef.current
|
||||||
|
if (!span || listenParents < 1) return
|
||||||
|
|
||||||
|
const handleEnter = (event) => onMouseEnterRef.current(event)
|
||||||
|
const handleLeave = (event) => onMouseLeaveRef.current(event)
|
||||||
|
|
||||||
|
const parents = []
|
||||||
|
let current = span.parentElement
|
||||||
|
for (let i = 0; i < listenParents && current; i += 1) {
|
||||||
|
parents.push(current)
|
||||||
|
current = current.parentElement
|
||||||
|
}
|
||||||
|
|
||||||
|
parents.forEach((parent) => {
|
||||||
|
parent.addEventListener('mouseenter', handleEnter)
|
||||||
|
parent.addEventListener('mouseleave', handleLeave)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
parents.forEach((parent) => {
|
||||||
|
parent.removeEventListener('mouseenter', handleEnter)
|
||||||
|
parent.removeEventListener('mouseleave', handleLeave)
|
||||||
|
})
|
||||||
|
hoveredTargetsRef.current.clear()
|
||||||
|
}
|
||||||
|
}, [listenParents])
|
||||||
|
|
||||||
|
const wrapWithSpan = (node) => (
|
||||||
|
<span
|
||||||
|
ref={spanRef}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
>
|
||||||
|
{node}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
|
||||||
if (isValidElement(children)) {
|
if (isValidElement(children)) {
|
||||||
const trigger = cloneElement(children, {
|
const trigger = cloneElement(children, {
|
||||||
onMouseEnter: mergeHandler(children.props.onMouseEnter, onMouseEnter),
|
onMouseEnter: mergeHandler(children.props.onMouseEnter, onMouseEnter),
|
||||||
onMouseLeave: mergeHandler(children.props.onMouseLeave, onMouseLeave)
|
onMouseLeave: mergeHandler(children.props.onMouseLeave, onMouseLeave)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (listenParents > 0) {
|
||||||
|
return wrapWithSpan(children)
|
||||||
|
}
|
||||||
|
|
||||||
if (children.props.disabled) {
|
if (children.props.disabled) {
|
||||||
return (
|
return wrapWithSpan(trigger)
|
||||||
<span onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
|
||||||
{trigger}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return trigger
|
return trigger
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return wrapWithSpan(children)
|
||||||
<span onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
|
||||||
{children}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tooltip.propTypes = {
|
Tooltip.propTypes = {
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
title: PropTypes.node,
|
title: PropTypes.node,
|
||||||
content: PropTypes.node
|
content: PropTypes.node,
|
||||||
|
listenParents: PropTypes.number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Tooltip
|
export default Tooltip
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user