forked from Gitlink/gitlink_help_center
415 lines
12 KiB
JavaScript
415 lines
12 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
|
|
const FontSizeController = () => {
|
|
const [fontSize, setFontSize] = useState('medium');
|
|
const [mounted, setMounted] = useState(false);
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
|
|
// 字体大小配置 - 增加更多选项
|
|
const fontSizes = {
|
|
extraSmall: {
|
|
name: '极小',
|
|
icon: '🔍',
|
|
scale: 0.75, // 12px 基于 16px
|
|
description: '超密集阅读'
|
|
},
|
|
small: {
|
|
name: '小号',
|
|
icon: '🔤',
|
|
scale: 0.875, // 14px 基于 16px
|
|
description: '密集阅读'
|
|
},
|
|
medium: {
|
|
name: '标准',
|
|
icon: '📝',
|
|
scale: 1, // 16px 标准
|
|
description: '推荐大小'
|
|
},
|
|
large: {
|
|
name: '大号',
|
|
icon: '📖',
|
|
scale: 1.125, // 18px 基于 16px
|
|
description: '舒适阅读'
|
|
},
|
|
extraLarge: {
|
|
name: '特大',
|
|
icon: '📚',
|
|
scale: 1.25, // 20px 基于 16px
|
|
description: '长时间阅读'
|
|
},
|
|
huge: {
|
|
name: '巨大',
|
|
icon: '🔍',
|
|
scale: 1.375, // 22px 基于 16px
|
|
description: '视力辅助'
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
// 从localStorage读取保存的字体大小设置
|
|
const savedFontSize = localStorage.getItem('doc-font-size') || 'medium';
|
|
const savedVisibility = localStorage.getItem('font-controller-visible') === 'true';
|
|
setFontSize(savedFontSize);
|
|
setIsVisible(savedVisibility);
|
|
applyFontSize(savedFontSize);
|
|
}, []);
|
|
|
|
// 应用字体大小到文档
|
|
const applyFontSize = (size) => {
|
|
const scale = fontSizes[size].scale;
|
|
|
|
// 创建或更新CSS变量
|
|
const root = document.documentElement;
|
|
root.style.setProperty('--doc-font-scale', scale);
|
|
|
|
// 直接应用到文档内容区域
|
|
const contentSelectors = [
|
|
'article',
|
|
'.markdown',
|
|
'.theme-doc-markdown',
|
|
'[class*="docItemContainer"]',
|
|
'[class*="docItemCol"]'
|
|
];
|
|
|
|
contentSelectors.forEach(selector => {
|
|
const elements = document.querySelectorAll(selector);
|
|
elements.forEach(element => {
|
|
element.style.fontSize = `${scale}rem`;
|
|
});
|
|
});
|
|
|
|
// 调整特定元素
|
|
const specificElements = {
|
|
'h1': `${scale * 2.5}rem`,
|
|
'h2': `${scale * 2}rem`,
|
|
'h3': `${scale * 1.75}rem`,
|
|
'h4': `${scale * 1.5}rem`,
|
|
'h5': `${scale * 1.25}rem`,
|
|
'h6': `${scale * 1.125}rem`,
|
|
'p, li, td, th, blockquote': `${scale}rem`,
|
|
'code': `${scale * 0.875}rem`,
|
|
'.table-of-contents': `${scale * 0.875}rem`
|
|
};
|
|
|
|
Object.entries(specificElements).forEach(([selectors, size]) => {
|
|
const elements = document.querySelectorAll(selectors);
|
|
elements.forEach(element => {
|
|
// 只调整文档内容区域的元素,避免影响导航等
|
|
const isInContent = element.closest('article, .markdown, .theme-doc-markdown');
|
|
if (isInContent) {
|
|
element.style.fontSize = size;
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
// 切换字体大小
|
|
const changeFontSize = (size) => {
|
|
setFontSize(size);
|
|
localStorage.setItem('doc-font-size', size);
|
|
applyFontSize(size);
|
|
showNotification(`✅ 字体大小已切换为:${fontSizes[size].name}`, 'success');
|
|
};
|
|
|
|
// 切换显示/隐藏
|
|
const toggleVisibility = () => {
|
|
const newVisibility = !isVisible;
|
|
setIsVisible(newVisibility);
|
|
localStorage.setItem('font-controller-visible', newVisibility.toString());
|
|
showNotification(newVisibility ? '📝 字体调节器已显示' : '👁️ 字体调节器已隐藏', 'info');
|
|
};
|
|
|
|
// 显示通知
|
|
const showNotification = (message, type = 'info') => {
|
|
try {
|
|
const notification = document.createElement('div');
|
|
notification.textContent = message;
|
|
|
|
const bgColors = {
|
|
success: '#25c2a0',
|
|
info: '#3498db',
|
|
warning: '#f39c12'
|
|
};
|
|
|
|
notification.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
background: ${bgColors[type] || bgColors.info};
|
|
color: white;
|
|
padding: 12px 20px;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
z-index: 10000;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
|
animation: slideIn 0.3s ease;
|
|
max-width: 300px;
|
|
word-wrap: break-word;
|
|
`;
|
|
|
|
// 添加动画样式
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
@keyframes slideIn {
|
|
from { transform: translateX(100%); opacity: 0; }
|
|
to { transform: translateX(0); opacity: 1; }
|
|
}
|
|
`;
|
|
if (!document.head.querySelector('[data-font-notification-style]')) {
|
|
style.setAttribute('data-font-notification-style', 'true');
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
setTimeout(() => {
|
|
if (notification.parentNode) {
|
|
notification.remove();
|
|
}
|
|
}, 3000);
|
|
} catch (error) {
|
|
console.error('显示通知失败:', error);
|
|
}
|
|
};
|
|
|
|
// 如果组件还没挂载,不渲染任何内容
|
|
if (!mounted) {
|
|
return null;
|
|
}
|
|
|
|
const containerStyle = {
|
|
position: 'fixed',
|
|
top: '50%',
|
|
right: '20px', // 移到右侧
|
|
transform: 'translateY(-50%)',
|
|
zIndex: 1001,
|
|
fontFamily: 'system-ui, -apple-system, sans-serif'
|
|
};
|
|
|
|
const toggleButtonStyle = {
|
|
position: 'fixed',
|
|
top: '50%',
|
|
right: isVisible ? '140px' : '20px', // 根据显示状态调整位置
|
|
transform: 'translateY(-50%)',
|
|
zIndex: 1002,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
width: '40px',
|
|
height: '40px',
|
|
backgroundColor: '#3b82f6',
|
|
color: 'white',
|
|
border: 'none',
|
|
borderRadius: '50%',
|
|
cursor: 'pointer',
|
|
fontSize: '16px',
|
|
boxShadow: '0 4px 12px rgba(59, 130, 246, 0.3)',
|
|
transition: 'all 0.3s ease',
|
|
fontFamily: 'system-ui, -apple-system, sans-serif'
|
|
};
|
|
|
|
const buttonStyle = {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
gap: '6px',
|
|
padding: '10px',
|
|
backgroundColor: 'white',
|
|
border: '2px solid #e5e7eb',
|
|
borderRadius: '10px',
|
|
cursor: 'pointer',
|
|
fontSize: '11px',
|
|
fontWeight: '600',
|
|
color: '#374151',
|
|
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
|
transition: 'all 0.2s ease',
|
|
minWidth: '50px',
|
|
textAlign: 'center'
|
|
};
|
|
|
|
const activeButtonStyle = {
|
|
...buttonStyle,
|
|
backgroundColor: '#3b82f6',
|
|
borderColor: '#3b82f6',
|
|
color: 'white',
|
|
transform: 'translateY(-2px)',
|
|
boxShadow: '0 6px 16px rgba(59, 130, 246, 0.3)'
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{/* 切换显示/隐藏按钮 */}
|
|
<button
|
|
onClick={toggleVisibility}
|
|
style={toggleButtonStyle}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.backgroundColor = '#2563eb';
|
|
e.target.style.transform = 'translateY(-50%) scale(1.1)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.backgroundColor = '#3b82f6';
|
|
e.target.style.transform = 'translateY(-50%) scale(1)';
|
|
}}
|
|
title={isVisible ? '隐藏字体调节器' : '显示字体调节器'}
|
|
>
|
|
{isVisible ? '✕' : 'Aa'}
|
|
</button>
|
|
|
|
{/* 字体大小控制面板 */}
|
|
{isVisible && (
|
|
<div style={containerStyle}>
|
|
<div style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '6px',
|
|
backgroundColor: 'white',
|
|
padding: '12px',
|
|
borderRadius: '16px',
|
|
boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
|
|
border: '1px solid #e5e7eb',
|
|
animation: 'slideInFromRight 0.3s ease'
|
|
}}>
|
|
{/* 标题 */}
|
|
<div style={{
|
|
fontSize: '11px',
|
|
fontWeight: '700',
|
|
color: '#6b7280',
|
|
textAlign: 'center',
|
|
marginBottom: '4px',
|
|
letterSpacing: '0.5px'
|
|
}}>
|
|
字体大小
|
|
</div>
|
|
|
|
{/* 当前字体大小显示 */}
|
|
<div style={{
|
|
fontSize: '10px',
|
|
fontWeight: '600',
|
|
color: '#3b82f6',
|
|
textAlign: 'center',
|
|
marginBottom: '8px',
|
|
padding: '4px 8px',
|
|
backgroundColor: '#eff6ff',
|
|
borderRadius: '6px'
|
|
}}>
|
|
当前: {fontSizes[fontSize].name} ({Math.round(fontSizes[fontSize].scale * 16)}px)
|
|
</div>
|
|
|
|
{/* 字体大小选项 */}
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: '1fr 1fr',
|
|
gap: '6px'
|
|
}}>
|
|
{Object.entries(fontSizes).map(([size, config]) => (
|
|
<button
|
|
key={size}
|
|
onClick={() => changeFontSize(size)}
|
|
style={fontSize === size ? activeButtonStyle : buttonStyle}
|
|
onMouseEnter={(e) => {
|
|
if (fontSize !== size) {
|
|
e.target.style.backgroundColor = '#f3f4f6';
|
|
e.target.style.borderColor = '#d1d5db';
|
|
e.target.style.transform = 'translateY(-1px)';
|
|
}
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
if (fontSize !== size) {
|
|
e.target.style.backgroundColor = 'white';
|
|
e.target.style.borderColor = '#e5e7eb';
|
|
e.target.style.transform = 'translateY(0)';
|
|
}
|
|
}}
|
|
title={`${config.name} (${Math.round(config.scale * 16)}px) - ${config.description}`}
|
|
>
|
|
<span style={{ fontSize: '14px' }}>{config.icon}</span>
|
|
<span style={{ fontSize: '9px', fontWeight: '600' }}>
|
|
{config.name}
|
|
</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* 快捷操作按钮 */}
|
|
<div style={{
|
|
display: 'flex',
|
|
gap: '6px',
|
|
marginTop: '8px'
|
|
}}>
|
|
<button
|
|
onClick={() => changeFontSize('medium')}
|
|
style={{
|
|
...buttonStyle,
|
|
backgroundColor: fontSize === 'medium' ? '#6b7280' : '#f9fafb',
|
|
borderColor: fontSize === 'medium' ? '#6b7280' : '#e5e7eb',
|
|
color: fontSize === 'medium' ? 'white' : '#6b7280',
|
|
fontSize: '9px',
|
|
padding: '6px 10px',
|
|
minWidth: 'auto',
|
|
flex: 1
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
if (fontSize !== 'medium') {
|
|
e.target.style.backgroundColor = '#f3f4f6';
|
|
e.target.style.borderColor = '#d1d5db';
|
|
}
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
if (fontSize !== 'medium') {
|
|
e.target.style.backgroundColor = '#f9fafb';
|
|
e.target.style.borderColor = '#e5e7eb';
|
|
}
|
|
}}
|
|
title="重置为标准大小"
|
|
>
|
|
重置
|
|
</button>
|
|
|
|
<button
|
|
onClick={toggleVisibility}
|
|
style={{
|
|
...buttonStyle,
|
|
backgroundColor: '#ef4444',
|
|
borderColor: '#ef4444',
|
|
color: 'white',
|
|
fontSize: '9px',
|
|
padding: '6px 10px',
|
|
minWidth: 'auto',
|
|
flex: 1
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.target.style.backgroundColor = '#dc2626';
|
|
e.target.style.borderColor = '#dc2626';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.target.style.backgroundColor = '#ef4444';
|
|
e.target.style.borderColor = '#ef4444';
|
|
}}
|
|
title="隐藏字体调节器"
|
|
>
|
|
隐藏
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 添加动画样式 */}
|
|
<style>{`
|
|
@keyframes slideInFromRight {
|
|
from {
|
|
transform: translateX(100%) translateY(-50%);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateX(0) translateY(-50%);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
`}</style>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default FontSizeController;
|