Enhance CursorTooltip with Visibility Control and CSS Transitions
- Updated the CursorTooltip component to accept a new `visible` prop, allowing for better control over tooltip visibility. - Refactored tooltip rendering logic to utilize CSS transitions for smoother fade-in and fade-out effects, improving user experience. - Enhanced TooltipContext to manage tooltip visibility state and timing, ensuring consistent behavior during interactions. - Updated CSS styles for the cursor tooltip to support new visibility transitions, enhancing visual feedback.
This commit is contained in:
parent
ef57837371
commit
80ac635bf9
@ -361,6 +361,16 @@ code {
|
|||||||
padding: 8px !important;
|
padding: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-tooltip {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor-tooltip.is-visible {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.cursor-tooltip .ant-card {
|
.cursor-tooltip .ant-card {
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
||||||
|
|||||||
@ -15,7 +15,7 @@ const getTooltipRoot = () => {
|
|||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
const CursorTooltip = ({ content, pointRef }) => {
|
const CursorTooltip = ({ content, visible, pointRef }) => {
|
||||||
const tooltipRef = useRef(null)
|
const tooltipRef = useRef(null)
|
||||||
const sizeRef = useRef({ width: 0, height: 0 })
|
const sizeRef = useRef({ width: 0, height: 0 })
|
||||||
const frameRef = useRef(null)
|
const frameRef = useRef(null)
|
||||||
@ -94,23 +94,19 @@ const CursorTooltip = ({ content, pointRef }) => {
|
|||||||
}
|
}
|
||||||
}, [applyPosition, root, schedulePosition])
|
}, [applyPosition, root, schedulePosition])
|
||||||
|
|
||||||
const visible = content != null && content !== false && content !== ''
|
|
||||||
|
|
||||||
if (!root) return null
|
if (!root) return null
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div
|
<div
|
||||||
ref={tooltipRef}
|
ref={tooltipRef}
|
||||||
className='cursor-tooltip'
|
className={`cursor-tooltip${visible ? ' is-visible' : ''}`}
|
||||||
style={{
|
style={{
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
zIndex: 1100,
|
zIndex: 1100,
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
visibility: visible ? 'visible' : 'hidden',
|
willChange: 'transform, opacity'
|
||||||
opacity: visible ? 1 : 0,
|
|
||||||
willChange: 'transform'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
@ -131,6 +127,7 @@ const CursorTooltip = ({ content, pointRef }) => {
|
|||||||
|
|
||||||
CursorTooltip.propTypes = {
|
CursorTooltip.propTypes = {
|
||||||
content: PropTypes.node,
|
content: PropTypes.node,
|
||||||
|
visible: PropTypes.bool,
|
||||||
pointRef: PropTypes.shape({
|
pointRef: PropTypes.shape({
|
||||||
current: PropTypes.shape({
|
current: PropTypes.shape({
|
||||||
x: PropTypes.number,
|
x: PropTypes.number,
|
||||||
|
|||||||
@ -11,11 +11,31 @@ import CursorTooltip from '../common/CursorTooltip'
|
|||||||
|
|
||||||
const TooltipContext = createContext(null)
|
const TooltipContext = createContext(null)
|
||||||
|
|
||||||
|
const HIDE_DELAY_MS = 100
|
||||||
|
const FADE_OUT_MS = 500
|
||||||
|
|
||||||
export const TooltipProvider = ({ children }) => {
|
export const TooltipProvider = ({ children }) => {
|
||||||
const [content, setContent] = useState(null)
|
const [content, setContent] = useState(null)
|
||||||
|
const [visible, setVisible] = useState(false)
|
||||||
const stackRef = useRef([])
|
const stackRef = useRef([])
|
||||||
const hideTimerRef = useRef(null)
|
const hideTimerRef = useRef(null)
|
||||||
|
const fadeTimerRef = useRef(null)
|
||||||
const pointRef = useRef({ x: 0, y: 0 })
|
const pointRef = useRef({ x: 0, y: 0 })
|
||||||
|
const visibleRef = useRef(false)
|
||||||
|
visibleRef.current = visible
|
||||||
|
|
||||||
|
const clearTimers = useCallback(() => {
|
||||||
|
if (hideTimerRef.current != null) {
|
||||||
|
window.clearTimeout(hideTimerRef.current)
|
||||||
|
hideTimerRef.current = null
|
||||||
|
}
|
||||||
|
if (fadeTimerRef.current != null) {
|
||||||
|
window.clearTimeout(fadeTimerRef.current)
|
||||||
|
fadeTimerRef.current = null
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => () => clearTimers(), [clearTimers])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onMove = (event) => {
|
const onMove = (event) => {
|
||||||
@ -25,45 +45,62 @@ export const TooltipProvider = ({ children }) => {
|
|||||||
return () => window.removeEventListener('mousemove', onMove)
|
return () => window.removeEventListener('mousemove', onMove)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const showTooltip = useCallback((id, nextContent, x, y) => {
|
const showTooltip = useCallback(
|
||||||
if (nextContent == null || nextContent === false || nextContent === '') {
|
(id, nextContent, x, y) => {
|
||||||
return
|
if (nextContent == null || nextContent === false || nextContent === '') {
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof x === 'number' && typeof y === 'number') {
|
if (typeof x === 'number' && typeof y === 'number') {
|
||||||
pointRef.current = { x, y }
|
pointRef.current = { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideTimerRef.current != null) {
|
clearTimers()
|
||||||
window.clearTimeout(hideTimerRef.current)
|
|
||||||
hideTimerRef.current = null
|
|
||||||
}
|
|
||||||
|
|
||||||
stackRef.current = [
|
stackRef.current = [
|
||||||
...stackRef.current.filter((entry) => entry.id !== id),
|
...stackRef.current.filter((entry) => entry.id !== id),
|
||||||
{ id, content: nextContent }
|
{ id, content: nextContent }
|
||||||
]
|
]
|
||||||
setContent(nextContent)
|
setContent(nextContent)
|
||||||
}, [])
|
|
||||||
|
|
||||||
const hideTooltip = useCallback((id) => {
|
if (!visibleRef.current) {
|
||||||
stackRef.current = stackRef.current.filter((entry) => entry.id !== id)
|
window.requestAnimationFrame(() => setVisible(true))
|
||||||
const last = stackRef.current[stackRef.current.length - 1]
|
} else {
|
||||||
|
setVisible(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[clearTimers]
|
||||||
|
)
|
||||||
|
|
||||||
if (hideTimerRef.current != null) {
|
const hideTooltip = useCallback(
|
||||||
window.clearTimeout(hideTimerRef.current)
|
(id) => {
|
||||||
}
|
stackRef.current = stackRef.current.filter((entry) => entry.id !== id)
|
||||||
|
const last = stackRef.current[stackRef.current.length - 1]
|
||||||
|
|
||||||
hideTimerRef.current = window.setTimeout(() => {
|
clearTimers()
|
||||||
hideTimerRef.current = null
|
|
||||||
setContent(last?.content ?? null)
|
if (last) {
|
||||||
}, 80)
|
setContent(last.content)
|
||||||
}, [])
|
setVisible(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hideTimerRef.current = window.setTimeout(() => {
|
||||||
|
hideTimerRef.current = null
|
||||||
|
setVisible(false)
|
||||||
|
fadeTimerRef.current = window.setTimeout(() => {
|
||||||
|
fadeTimerRef.current = null
|
||||||
|
setContent(null)
|
||||||
|
}, FADE_OUT_MS)
|
||||||
|
}, HIDE_DELAY_MS)
|
||||||
|
},
|
||||||
|
[clearTimers]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipContext.Provider value={{ showTooltip, hideTooltip }}>
|
<TooltipContext.Provider value={{ showTooltip, hideTooltip }}>
|
||||||
{children}
|
{children}
|
||||||
<CursorTooltip content={content} pointRef={pointRef} />
|
<CursorTooltip content={content} visible={visible} pointRef={pointRef} />
|
||||||
</TooltipContext.Provider>
|
</TooltipContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user