Files
panel/src/components/ui/Tooltip.js
RKWojs 0dd988730f feat: Implement internationalization for task management components
- Added translation support for task-related strings in ProjectTaskForm and ProjectTasksSection components.
- Integrated translation for navigation items in the Navigation component.
- Created ProjectCalendarWidget component with Polish translations for project statuses and deadlines.
- Developed Tooltip component for enhanced user experience with tooltips.
- Established a field change history logging system in the database with associated queries.
- Enhanced task update logging to include translated status and priority changes.
- Introduced server-side translations for system messages to improve localization.
2025-09-11 15:49:07 +02:00

96 lines
2.6 KiB
JavaScript

"use client";
import { useState, useRef, useEffect } from "react";
import { createPortal } from "react-dom";
export default function Tooltip({ children, content, className = "" }) {
const [isVisible, setIsVisible] = useState(false);
const [position, setPosition] = useState({ top: 0, left: 0 });
const triggerRef = useRef(null);
const tooltipRef = useRef(null);
const updatePosition = () => {
if (triggerRef.current && tooltipRef.current) {
const triggerRect = triggerRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
const scrollY = window.scrollY;
const scrollX = window.scrollX;
// Calculate position (above the trigger by default)
let top = triggerRect.top + scrollY - tooltipRect.height - 8;
let left = triggerRect.left + scrollX + (triggerRect.width / 2) - (tooltipRect.width / 2);
// Keep tooltip within viewport
if (left < 10) left = 10;
if (left + tooltipRect.width > window.innerWidth - 10) {
left = window.innerWidth - tooltipRect.width - 10;
}
// If tooltip would go above viewport, show below instead
if (top < scrollY + 10) {
top = triggerRect.bottom + scrollY + 8;
}
setPosition({ top, left });
}
};
const handleMouseEnter = () => {
setIsVisible(true);
};
const handleMouseLeave = () => {
setIsVisible(false);
};
useEffect(() => {
if (isVisible) {
// Small delay to ensure tooltip is rendered before positioning
const timer = setTimeout(() => {
updatePosition();
}, 10);
const handleScroll = () => updatePosition();
const handleResize = () => updatePosition();
window.addEventListener("scroll", handleScroll, true);
window.addEventListener("resize", handleResize);
return () => {
clearTimeout(timer);
window.removeEventListener("scroll", handleScroll, true);
window.removeEventListener("resize", handleResize);
};
}
}, [isVisible]);
const tooltip = isVisible && (
<div
ref={tooltipRef}
className={`fixed z-50 px-3 py-2 text-sm bg-gray-900 text-white rounded-lg shadow-lg border max-w-sm ${className}`}
style={{
top: position.top,
left: position.left,
}}
>
{content}
{/* Arrow pointing down */}
<div className="absolute top-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-t-gray-900"></div>
</div>
);
return (
<>
<span
ref={triggerRef}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className="inline-block"
>
{children}
</span>
{typeof document !== "undefined" && createPortal(tooltip, document.body)}
</>
);
}