feat: Обновление компонента Tooltip с изменением стиля на Tabler UI и улучшением позиционирования. Убрано вычисление координат, теперь подсказка позиционируется относительно триггера с использованием position: absolute.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m37s

This commit is contained in:
2025-11-12 10:54:43 +07:00
parent 807471b7b1
commit 1769c07883
+80 -144
View File
@@ -1,8 +1,8 @@
import { useState, useRef, useEffect } from 'react'
/**
* Tooltip компонент для отображения подсказок
* Поддерживает keyboard shortcuts и различные позиции
* Tooltip компонент в стиле Tabler UI
* Использует position: absolute относительно trigger элемента
*/
function Tooltip({
children,
@@ -13,43 +13,14 @@ function Tooltip({
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) => {
const showTooltip = () => {
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)
}
setIsVisible(true)
}, delay)
}
@@ -69,129 +40,94 @@ function Tooltip({
}, [])
return (
<>
<span
ref={triggerRef}
onMouseEnter={showTooltip}
onMouseLeave={hideTooltip}
onFocus={showTooltip}
onBlur={hideTooltip}
className={`tooltip-trigger ${className}`}
style={{ position: 'relative', display: 'inline-block' }}
>
{children}
</span>
<span
ref={triggerRef}
onMouseEnter={showTooltip}
onMouseLeave={hideTooltip}
onFocus={showTooltip}
onBlur={hideTooltip}
className={`tooltip-trigger ${className}`}
style={{ position: 'relative', display: 'inline-block' }}
>
{children}
{isVisible && (
<div
className={`tooltip-content tooltip-${position}`}
className={`tooltip bs-tooltip-${position} show`}
role="tooltip"
style={{
position: 'fixed',
left: `${coords.x}px`,
top: `${coords.y}px`,
zIndex: 9999,
pointerEvents: 'none'
position: 'absolute',
zIndex: 1070,
display: 'block',
margin: 0,
pointerEvents: 'none',
...(position === 'top' && {
bottom: '100%',
left: '50%',
transform: 'translateX(-50%)',
marginBottom: '8px'
}),
...(position === 'bottom' && {
top: '100%',
left: '50%',
transform: 'translateX(-50%)',
marginTop: '8px'
}),
...(position === 'left' && {
right: '100%',
top: '50%',
transform: 'translateY(-50%)',
marginRight: '8px'
}),
...(position === 'right' && {
left: '100%',
top: '50%',
transform: 'translateY(-50%)',
marginLeft: '8px'
})
}}
>
<div className="tooltip-arrow" style={(() => {
const styles = {
position: 'absolute',
width: '8px',
height: '8px',
background: 'inherit'
}
if (position === 'top') {
styles.bottom = '0'
styles.left = '50%'
styles.transform = 'translateX(-50%) translateY(50%) rotate(45deg)'
} else if (position === 'bottom') {
styles.top = '0'
styles.left = '50%'
styles.transform = 'translateX(-50%) translateY(-50%) rotate(45deg)'
} else if (position === 'left') {
styles.right = '0'
styles.top = '50%'
styles.transform = 'translateX(50%) translateY(-50%) rotate(45deg)'
} else if (position === 'right') {
styles.left = '0'
styles.top = '50%'
styles.transform = 'translateX(-50%) translateY(-50%) rotate(45deg)'
}
return styles
})()}></div>
<div className="tooltip-inner">
<div className="tooltip-text">{content}</div>
{content}
{shortcut && (
<kbd className="tooltip-shortcut ms-2">{shortcut}</kbd>
<kbd className="ms-2" style={{
background: 'rgba(255, 255, 255, 0.2)',
padding: '0.125rem 0.375rem',
borderRadius: '0.25rem',
fontSize: '0.75rem',
fontFamily: 'monospace'
}}>{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);
}
}
@keyframes tooltipFadeInTop {
from {
opacity: 0;
transform: translate(-50%, calc(-100% - 4px)) scale(0.95);
}
to {
opacity: 1;
transform: translate(-50%, calc(-100% - 8px)) scale(1);
}
}
@keyframes tooltipFadeInBottom {
from {
opacity: 0;
transform: translate(-50%, 4px) scale(0.95);
}
to {
opacity: 1;
transform: translate(-50%, 8px) scale(1);
}
}
.tooltip-content.tooltip-top {
animation: tooltipFadeInTop 0.15s ease-out;
}
.tooltip-content.tooltip-bottom {
animation: tooltipFadeInBottom 0.15s ease-out;
}
@media (prefers-color-scheme: dark) {
.tooltip-inner {
background: var(--tblr-gray-800, #1e293b);
}
}
`}</style>
</>
</span>
)
}