fixed tooltip overshadow

This commit is contained in:
Harivansh Rathi 2024-11-22 00:07:01 -05:00
parent b0c218cd65
commit 4f8e262f75

View file

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { createPortal } from 'react-dom';
import { ChevronLeft, ChevronRight, Check } from 'lucide-react'; import { ChevronLeft, ChevronRight, Check } from 'lucide-react';
import { useThemeContext } from '../contexts/ThemeContext'; import { useThemeContext } from '../contexts/ThemeContext';
import { Habit } from '../types'; import { Habit } from '../types';
@ -44,7 +45,64 @@ export const Calendar: React.FC<CalendarProps> = ({
await onToggleHabit(habitId, date); await onToggleHabit(habitId, date);
}; };
// Add state for tooltip positioning
const [tooltipData, setTooltipData] = React.useState<{
x: number;
y: number;
completedHabits: Habit[];
incompleteHabits: Habit[];
date: string;
isVisible: boolean;
} | null>(null);
// Add ref for timeout
const hideTimeoutRef = React.useRef<NodeJS.Timeout>();
const tooltipRef = React.useRef<HTMLDivElement>(null);
// Modified tooltip handlers
const showTooltip = (e: React.MouseEvent, date: string, completed: Habit[], incomplete: Habit[]) => {
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
}
const rect = e.currentTarget.getBoundingClientRect();
setTooltipData({
x: rect.left + rect.width / 2,
y: rect.top,
completedHabits: completed,
incompleteHabits: incomplete,
date,
isVisible: true
});
};
const hideTooltip = () => {
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
}
hideTimeoutRef.current = setTimeout(() => {
setTooltipData(prev => prev ? { ...prev, isVisible: false } : null);
}, 150); // 150ms delay before hiding
};
const cancelHideTooltip = () => {
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
}
};
// Cleanup timeout on unmount
React.useEffect(() => {
return () => {
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
}
};
}, []);
return ( return (
<>
<div className={`rounded-lg shadow-md p-6 ${theme.calendar.background}`}> <div className={`rounded-lg shadow-md p-6 ${theme.calendar.background}`}>
<div className="flex justify-between items-center mb-8"> <div className="flex justify-between items-center mb-8">
<h2 className={`text-2xl font-bold ${theme.calendar.header}`}> <h2 className={`text-2xl font-bold ${theme.calendar.header}`}>
@ -138,94 +196,36 @@ export const Calendar: React.FC<CalendarProps> = ({
</span> </span>
{habits.length > 0 && ( {habits.length > 0 && (
<div className="absolute bottom-3 left-1/2 transform -translate-x-1/2"> <div className="absolute bottom-3 left-1/2 transform -translate-x-1/2">
<div className="group relative inline-block"> <div
className="relative"
onMouseEnter={(e) => showTooltip(e, date, completedHabits, incompleteHabits)}
onMouseLeave={hideTooltip}
>
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
{isToday && ( {isToday && (
<div className="w-2 h-2 rounded-full bg-blue-500 dark:bg-blue-400" /> <div className="w-2 h-2 rounded-full bg-blue-500 dark:bg-blue-400" />
)} )}
<div <div
className={` className={`
h-5 px-2 rounded-full cursor-pointer h-6 px-2.5 rounded-full cursor-pointer
transition-colors duration-200 flex items-center justify-center transition-all duration-200 flex items-center justify-center gap-1
${completedHabits.length > 0 ${completedHabits.length > 0
? 'bg-[#2ecc71] dark:bg-[#2ecc71] shadow-sm shadow-[#2ecc7150]' ? 'bg-green-100 dark:bg-green-900/30 shadow-[0_2px_10px] shadow-green-900/20 dark:shadow-green-100/20'
: `bg-[#e9e9e8] dark:bg-[#393939]` : `bg-gray-100 dark:bg-gray-800 shadow-sm`
} }
`} `}
> >
<span className="text-[8px] font-medium text-black/70 dark:text-white/70"> <span className={`
text-xs font-medium
${completedHabits.length > 0
? 'text-green-700 dark:text-green-300'
: 'text-gray-600 dark:text-gray-400'
}
`}>
{completedHabits.length}/{habits.length} {completedHabits.length}/{habits.length}
</span> </span>
</div> </div>
</div> </div>
<div
className={`
absolute bottom-full left-1/2 -translate-x-1/2 mb-2
opacity-0 invisible
group-hover:opacity-100 group-hover:visible
transition-all duration-150 ease-in-out
z-50 transform
translate-y-1 group-hover:translate-y-0
`}
>
<div className={`
pointer-events-auto
rounded-lg p-4
min-w-[200px] max-w-[300px]
${theme.calendar.tooltip.background}
${theme.calendar.tooltip.border}
${theme.calendar.tooltip.shadow}
border
backdrop-blur-sm
`}>
{completedHabits.length > 0 && (
<div className="mb-3">
<span className="text-[#2ecc71] font-semibold block mb-2">
Completed
</span>
<ul className="space-y-1.5">
{completedHabits.map(habit => (
<li
key={habit.id}
className={`${theme.text} text-sm truncate flex items-center justify-between`}
>
<span>{habit.name}</span>
<button
onClick={(e) => handleToggleHabit(e, habit.id, date)}
className="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
>
<Check className="h-4 w-4 text-[#2ecc71]" />
</button>
</li>
))}
</ul>
</div>
)}
{incompleteHabits.length > 0 && (
<div>
<span className="text-[#e74c3c] font-semibold block mb-2">
Pending
</span>
<ul className="space-y-1.5">
{incompleteHabits.map(habit => (
<li
key={habit.id}
className={`${theme.text} text-sm truncate flex items-center justify-between group`}
>
<span>{habit.name}</span>
<button
onClick={(e) => handleToggleHabit(e, habit.id, date)}
className="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded opacity-0 group-hover:opacity-100 transition-opacity"
>
<Check className="h-4 w-4 text-gray-400 hover:text-[#2ecc71]" />
</button>
</li>
))}
</ul>
</div>
)}
</div>
</div>
</div> </div>
</div> </div>
)} )}
@ -235,5 +235,106 @@ export const Calendar: React.FC<CalendarProps> = ({
})()} })()}
</div> </div>
</div> </div>
{/* Updated tooltip portal with more subtle animations */}
{tooltipData && createPortal(
<div
ref={tooltipRef}
onMouseEnter={cancelHideTooltip}
onMouseLeave={hideTooltip}
className={`
fixed
transition-all duration-150 ease-in-out
${tooltipData.isVisible
? 'opacity-100 translate-y-0'
: 'opacity-0 translate-y-1'
}
`}
style={{
left: tooltipData.x,
top: tooltipData.y - 8,
transform: 'translate(-50%, -100%)',
pointerEvents: tooltipData.isVisible ? 'auto' : 'none',
}}
>
<div
className={`
rounded-lg p-4
w-[200px]
${theme.calendar.tooltip.background}
${theme.calendar.tooltip.border}
shadow-[0_10px_38px_-10px_rgba(22,23,24,0.35),0_10px_20px_-15px_rgba(22,23,24,0.2)]
border
backdrop-blur-sm
relative
transition-all duration-150 ease-in-out
scale-100 origin-[bottom]
${tooltipData.isVisible ? 'scale-100' : 'scale-98'}
`}
>
{/* Arrow */}
<div
className={`
absolute -bottom-2 left-1/2 -translate-x-1/2
w-4 h-4 rotate-45
${theme.calendar.tooltip.background}
${theme.calendar.tooltip.border}
border-t-0 border-l-0
`}
/>
<div className="relative">
{tooltipData.completedHabits.length > 0 && (
<div className="mb-3">
<span className="text-[#2ecc71] font-semibold block mb-2">
Completed
</span>
<ul className="space-y-1.5">
{tooltipData.completedHabits.map(habit => (
<li
key={habit.id}
className={`${theme.text} text-sm truncate flex items-center justify-between`}
>
<span>{habit.name}</span>
<button
onClick={(e) => handleToggleHabit(e, habit.id, tooltipData.date)}
className="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
>
<Check className="h-4 w-4 text-[#2ecc71]" />
</button>
</li>
))}
</ul>
</div>
)}
{tooltipData.incompleteHabits.length > 0 && (
<div>
<span className="text-[#e74c3c] font-semibold block mb-2">
Pending
</span>
<ul className="space-y-1.5">
{tooltipData.incompleteHabits.map(habit => (
<li
key={habit.id}
className={`${theme.text} text-sm truncate flex items-center justify-between group`}
>
<span>{habit.name}</span>
<button
onClick={(e) => handleToggleHabit(e, habit.id, tooltipData.date)}
className="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded opacity-0 group-hover:opacity-100 transition-opacity"
>
<Check className="h-4 w-4 text-gray-400 hover:text-[#2ecc71]" />
</button>
</li>
))}
</ul>
</div>
)}
</div>
</div>
</div>,
document.body
)}
</>
); );
}; };