Files
srdb/webui/static/js/hooks/useCellPopover.js
2025-10-13 01:36:49 +08:00

156 lines
4.7 KiB
JavaScript

import { useEffect, useRef } from 'preact/hooks';
import domAlign from 'dom-align';
export function useCellPopover() {
const popoverRef = useRef(null);
const hideTimeoutRef = useRef(null);
const targetCellRef = useRef(null);
useEffect(() => {
// 创建 popover 元素
const popover = document.createElement('div');
popover.className = 'srdb-popover';
updatePopoverStyles(popover);
document.body.appendChild(popover);
popoverRef.current = popover;
// 添加样式
addPopoverStyles();
// 监听主题变化
const observer = new MutationObserver(() => {
updatePopoverStyles(popover);
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
// 清理
return () => {
if (popover) {
popover.remove();
}
observer.disconnect();
};
}, []);
const updatePopoverStyles = (popover) => {
const rootStyles = getComputedStyle(document.documentElement);
const bgElevated = rootStyles.getPropertyValue('--bg-elevated').trim();
const textPrimary = rootStyles.getPropertyValue('--text-primary').trim();
const borderColor = rootStyles.getPropertyValue('--border-color').trim();
const shadowMd = rootStyles.getPropertyValue('--shadow-md').trim();
popover.style.cssText = `
position: fixed;
z-index: 9999;
background: ${bgElevated};
border: 1px solid ${borderColor};
border-radius: 8px;
box-shadow: ${shadowMd};
padding: 12px;
max-width: 500px;
max-height: 400px;
overflow: auto;
font-size: 13px;
color: ${textPrimary};
white-space: pre-wrap;
word-break: break-word;
font-family: 'Courier New', monospace;
opacity: 0;
transition: opacity 0.15s ease-in-out, background 0.3s ease, color 0.3s ease;
display: none;
pointer-events: auto;
`;
};
const addPopoverStyles = () => {
if (document.getElementById('srdb-popover-styles')) return;
const style = document.createElement('style');
style.id = 'srdb-popover-styles';
style.textContent = `
.srdb-popover::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.srdb-popover::-webkit-scrollbar-track {
background: var(--bg-surface);
border-radius: 4px;
}
.srdb-popover::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
.srdb-popover::-webkit-scrollbar-thumb:hover {
background: var(--border-hover);
}
`;
document.head.appendChild(style);
};
const showPopover = (e, content) => {
if (!popoverRef.current) return;
// 清除隐藏定时器
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
hideTimeoutRef.current = null;
}
// 只在内容较长时显示
if (content.length < 50) {
return;
}
// 更新 popover
popoverRef.current.textContent = content;
popoverRef.current.style.display = 'block';
targetCellRef.current = e.target;
// 使用 dom-align 定位
domAlign(popoverRef.current, e.target, {
points: ['tl', 'tr'],
offset: [2, 0],
overflow: { adjustX: true, adjustY: true }
});
// 显示动画
setTimeout(() => {
if (popoverRef.current) {
popoverRef.current.style.opacity = '1';
}
}, 10);
// 添加鼠标事件监听
popoverRef.current.addEventListener('mouseenter', keepPopover);
popoverRef.current.addEventListener('mouseleave', hidePopover);
};
const hidePopover = () => {
hideTimeoutRef.current = setTimeout(() => {
if (popoverRef.current) {
popoverRef.current.style.opacity = '0';
setTimeout(() => {
if (popoverRef.current) {
popoverRef.current.style.display = 'none';
}
}, 150);
}
}, 300);
};
const keepPopover = () => {
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
hideTimeoutRef.current = null;
}
};
return {
showPopover,
hidePopover
};
}