feat: Обновление компонента Tooltip с изменением стиля на Tabler UI и улучшением позиционирования. Убрано вычисление координат, теперь подсказка позиционируется относительно триггера с использованием position: absolute.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m37s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m37s
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { useState, useRef, useEffect } from 'react'
|
import { useState, useRef, useEffect } from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tooltip компонент для отображения подсказок
|
* Tooltip компонент в стиле Tabler UI
|
||||||
* Поддерживает keyboard shortcuts и различные позиции
|
* Использует position: absolute относительно trigger элемента
|
||||||
*/
|
*/
|
||||||
function Tooltip({
|
function Tooltip({
|
||||||
children,
|
children,
|
||||||
@@ -13,43 +13,14 @@ function Tooltip({
|
|||||||
className = ''
|
className = ''
|
||||||
}) {
|
}) {
|
||||||
const [isVisible, setIsVisible] = useState(false)
|
const [isVisible, setIsVisible] = useState(false)
|
||||||
const [coords, setCoords] = useState({ x: 0, y: 0 })
|
|
||||||
const timeoutRef = useRef(null)
|
const timeoutRef = useRef(null)
|
||||||
const triggerRef = useRef(null)
|
const triggerRef = useRef(null)
|
||||||
|
|
||||||
const showTooltip = (e) => {
|
const showTooltip = () => {
|
||||||
if (timeoutRef.current) clearTimeout(timeoutRef.current)
|
if (timeoutRef.current) clearTimeout(timeoutRef.current)
|
||||||
|
|
||||||
timeoutRef.current = setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
if (triggerRef.current) {
|
setIsVisible(true)
|
||||||
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)
|
}, delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,129 +40,94 @@ function Tooltip({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<span
|
||||||
<span
|
ref={triggerRef}
|
||||||
ref={triggerRef}
|
onMouseEnter={showTooltip}
|
||||||
onMouseEnter={showTooltip}
|
onMouseLeave={hideTooltip}
|
||||||
onMouseLeave={hideTooltip}
|
onFocus={showTooltip}
|
||||||
onFocus={showTooltip}
|
onBlur={hideTooltip}
|
||||||
onBlur={hideTooltip}
|
className={`tooltip-trigger ${className}`}
|
||||||
className={`tooltip-trigger ${className}`}
|
style={{ position: 'relative', display: 'inline-block' }}
|
||||||
style={{ position: 'relative', display: 'inline-block' }}
|
>
|
||||||
>
|
{children}
|
||||||
{children}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{isVisible && (
|
{isVisible && (
|
||||||
<div
|
<div
|
||||||
className={`tooltip-content tooltip-${position}`}
|
className={`tooltip bs-tooltip-${position} show`}
|
||||||
|
role="tooltip"
|
||||||
style={{
|
style={{
|
||||||
position: 'fixed',
|
position: 'absolute',
|
||||||
left: `${coords.x}px`,
|
zIndex: 1070,
|
||||||
top: `${coords.y}px`,
|
display: 'block',
|
||||||
zIndex: 9999,
|
margin: 0,
|
||||||
pointerEvents: 'none'
|
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-inner">
|
||||||
<div className="tooltip-text">{content}</div>
|
{content}
|
||||||
{shortcut && (
|
{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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</span>
|
||||||
<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>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user