feat: Улучшение компонентов Dashboard, EmptyState, ErrorAlert и Pagination. Добавлены новые функции, такие как индикаторы тренда, возможность перехода на страницу и улучшенные действия с ошибками. Обновлены стили для повышения удобства использования и доступности.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m54s

This commit is contained in:
2025-10-03 19:11:10 +07:00
parent 72fbb7bde5
commit 18f3a5312a
16 changed files with 2697 additions and 96 deletions
+169
View File
@@ -0,0 +1,169 @@
import { useState, useRef, useEffect } from 'react'
/**
* Tooltip компонент для отображения подсказок
* Поддерживает keyboard shortcuts и различные позиции
*/
function Tooltip({
children,
content,
position = 'top', // top, bottom, left, right
shortcut, // keyboard shortcut to display
delay = 200,
className = ''
}) {
const [isVisible, setIsVisible] = useState(false)
const [coords, setCoords] = useState({ x: 0, y: 0 })
const timeoutRef = useRef(null)
const triggerRef = useRef(null)
const showTooltip = (e) => {
if (timeoutRef.current) clearTimeout(timeoutRef.current)
timeoutRef.current = setTimeout(() => {
if (triggerRef.current) {
const rect = triggerRef.current.getBoundingClientRect()
let x = 0, y = 0
switch (position) {
case 'top':
x = rect.left + rect.width / 2
y = rect.top
break
case 'bottom':
x = rect.left + rect.width / 2
y = rect.bottom
break
case 'left':
x = rect.left
y = rect.top + rect.height / 2
break
case 'right':
x = rect.right
y = rect.top + rect.height / 2
break
default:
x = rect.left + rect.width / 2
y = rect.top
}
setCoords({ x, y })
setIsVisible(true)
}
}, delay)
}
const hideTooltip = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
setIsVisible(false)
}
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
}
}, [])
return (
<>
<span
ref={triggerRef}
onMouseEnter={showTooltip}
onMouseLeave={hideTooltip}
onFocus={showTooltip}
onBlur={hideTooltip}
className={`tooltip-trigger ${className}`}
style={{ position: 'relative', display: 'inline-block' }}
>
{children}
</span>
{isVisible && (
<div
className={`tooltip-content tooltip-${position}`}
style={{
position: 'fixed',
left: `${coords.x}px`,
top: `${coords.y}px`,
zIndex: 9999,
pointerEvents: 'none'
}}
>
<div className="tooltip-inner">
<div className="tooltip-text">{content}</div>
{shortcut && (
<kbd className="tooltip-shortcut ms-2">{shortcut}</kbd>
)}
</div>
</div>
)}
<style jsx>{`
.tooltip-content {
animation: tooltipFadeIn 0.15s ease-out;
}
.tooltip-top {
transform: translate(-50%, calc(-100% - 8px));
}
.tooltip-bottom {
transform: translate(-50%, 8px);
}
.tooltip-left {
transform: translate(calc(-100% - 8px), -50%);
}
.tooltip-right {
transform: translate(8px, -50%);
}
.tooltip-inner {
background: var(--tblr-dark, #1e293b);
color: white;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
font-size: 0.875rem;
box-shadow: var(--shadow-lg);
white-space: nowrap;
display: flex;
align-items: center;
max-width: 300px;
}
.tooltip-shortcut {
background: rgba(255, 255, 255, 0.2);
padding: 0.125rem 0.375rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-family: monospace;
}
@keyframes tooltipFadeIn {
from {
opacity: 0;
transform: translate(-50%, calc(-100% - 4px)) scale(0.95);
}
to {
opacity: 1;
transform: translate(-50%, calc(-100% - 8px)) scale(1);
}
}
@media (prefers-color-scheme: dark) {
.tooltip-inner {
background: var(--tblr-gray-800, #1e293b);
}
}
`}</style>
</>
)
}
export default Tooltip