mirror of
https://github.com/mue/mue.git
synced 2026-07-15 04:53:48 +02:00
fix(tooltip): exit animation
This commit is contained in:
@@ -4,10 +4,18 @@ import './tooltip.scss';
|
||||
|
||||
function Tooltip({ children, title, style, placement, subtitle }) {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const [reference, setReference] = useState(null);
|
||||
const tooltipId = useRef(`tooltip-${Math.random()}`);
|
||||
const closeTimeout = useRef(null);
|
||||
|
||||
const { x, y, refs, strategy } = useFloating({
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
refs,
|
||||
strategy,
|
||||
placement: computedPlacement,
|
||||
} = useFloating({
|
||||
placement: placement || 'bottom',
|
||||
middleware: [flip(), offset(15), shift()],
|
||||
elements: {
|
||||
@@ -15,21 +23,64 @@ function Tooltip({ children, title, style, placement, subtitle }) {
|
||||
},
|
||||
});
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
// Clear any pending close timeout if mouse re-enters during exit
|
||||
if (closeTimeout.current) {
|
||||
clearTimeout(closeTimeout.current);
|
||||
closeTimeout.current = null;
|
||||
}
|
||||
setIsClosing(false);
|
||||
setShowTooltip(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setIsClosing(true);
|
||||
// Wait for exit animation to complete before unmounting
|
||||
closeTimeout.current = setTimeout(() => {
|
||||
setShowTooltip(false);
|
||||
setIsClosing(false);
|
||||
}, 200); // Match exit animation duration
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
if (closeTimeout.current) {
|
||||
clearTimeout(closeTimeout.current);
|
||||
closeTimeout.current = null;
|
||||
}
|
||||
setIsClosing(false);
|
||||
setShowTooltip(true);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setIsClosing(true);
|
||||
closeTimeout.current = setTimeout(() => {
|
||||
setShowTooltip(false);
|
||||
setIsClosing(false);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
// Determine the data-status attribute value
|
||||
const getStatus = () => {
|
||||
if (!showTooltip && !isClosing) return 'initial';
|
||||
if (isClosing) return 'close';
|
||||
return 'open';
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="tooltip"
|
||||
style={style}
|
||||
onMouseEnter={() => setShowTooltip(true)}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
onFocus={() => setShowTooltip(true)}
|
||||
onBlur={() => setShowTooltip(false)}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
ref={setReference}
|
||||
aria-describedby={tooltipId.current}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
{showTooltip && (
|
||||
{(showTooltip || isClosing) && (
|
||||
<span
|
||||
ref={refs.setFloating}
|
||||
style={{
|
||||
@@ -40,6 +91,8 @@ function Tooltip({ children, title, style, placement, subtitle }) {
|
||||
flexFlow: 'column',
|
||||
}}
|
||||
className="tooltipTitle"
|
||||
data-status={getStatus()}
|
||||
data-placement={computedPlacement}
|
||||
>
|
||||
{title}
|
||||
<span style={{ fontSize: '8px' }}>{subtitle}</span>
|
||||
|
||||
@@ -33,9 +33,44 @@
|
||||
cursor: initial;
|
||||
user-select: none;
|
||||
opacity: 1;
|
||||
animation-name: floating;
|
||||
animation-duration: 0.3s;
|
||||
animation-timing-function: ease-in;
|
||||
transition:
|
||||
opacity 0.2s ease-out,
|
||||
transform 0.2s ease-out;
|
||||
|
||||
// Initial state (not yet shown)
|
||||
&[data-status='initial'] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
// Open state - entrance animation
|
||||
&[data-status='open'] {
|
||||
opacity: 1;
|
||||
transform: translate(0, 0);
|
||||
animation-name: floating;
|
||||
animation-duration: 0.3s;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
|
||||
// Closing state - exit animation
|
||||
&[data-status='close'] {
|
||||
opacity: 0;
|
||||
|
||||
&[data-placement^='top'] {
|
||||
transform: translateY(5px);
|
||||
}
|
||||
|
||||
&[data-placement^='bottom'] {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
&[data-placement^='left'] {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
&[data-placement^='right'] {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#modal {
|
||||
|
||||
Reference in New Issue
Block a user