mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-21 09:39:03 +08:00
feat(侧边栏): 优化侧边栏展开收起动画和交互逻辑
重构侧边栏组件,将展开/收起逻辑拆分为独立函数,添加平滑过渡动画 新增显示/隐藏状态控制,优化用户体验 调整CSS样式以支持新的动画效果
This commit is contained in:
@@ -114,12 +114,41 @@ html[theme-mode='dark'] .ss-sidebar .t-menu__item.t-is-active svg:not([fill='non
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
animation: twinkle-animation 3s infinite ease-in-out;
|
animation: twinkle-animation 3s infinite ease-in-out;
|
||||||
transition: background-color 0.3s, color 0.3s;
|
transition: opacity 0.1s ease, transform 0.1s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.global-sidebar-toggle:hover {
|
.global-sidebar-toggle.hidden {
|
||||||
background-color: var(--ss-item-hover);
|
opacity: 0;
|
||||||
color: var(--td-brand-color);
|
pointer-events: none;
|
||||||
|
transform: translateX(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
backgroundColor: var(--ss-card-bg);
|
||||||
|
border-radius: 12px 0 0 12px;
|
||||||
|
box-shadow: -4px 0 16px rgba(0,0,0,0.15);
|
||||||
|
border: 1px solid var(--ss-border-color);
|
||||||
|
border-right: none;
|
||||||
|
padding: 12px 8px;
|
||||||
|
gap: 12px;
|
||||||
|
width: 60px;
|
||||||
|
transition: opacity 0.15s ease, transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content-area.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content-area.visible {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes twinkle-animation {
|
@keyframes twinkle-animation {
|
||||||
|
|||||||
@@ -11,34 +11,47 @@ import {
|
|||||||
|
|
||||||
export const GlobalSidebar: React.FC = () => {
|
export const GlobalSidebar: React.FC = () => {
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false)
|
||||||
|
const [showToggle, setShowToggle] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
const handleExpand = () => {
|
||||||
if (!(window as any).api) return
|
// 1. 先隐藏三角
|
||||||
if (expanded) {
|
setShowToggle(false)
|
||||||
(window as any).api.windowResize(84, 300)
|
|
||||||
} else {
|
// 2. 稍后扩大窗口
|
||||||
(window as any).api.windowResize(24, 300)
|
setTimeout(() => {
|
||||||
}
|
if ((window as any).api) {
|
||||||
}, [expanded])
|
(window as any).api.windowResize(84, 300)
|
||||||
|
}
|
||||||
|
// 3. 最后显示侧边栏内容
|
||||||
|
setTimeout(() => {
|
||||||
|
setExpanded(true)
|
||||||
|
}, 20)
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCollapse = () => {
|
||||||
|
// 1. 先隐藏侧边栏内容
|
||||||
|
setExpanded(false)
|
||||||
|
|
||||||
|
// 2. 稍后缩小窗口
|
||||||
|
setTimeout(() => {
|
||||||
|
if ((window as any).api) {
|
||||||
|
(window as any).api.windowResize(24, 300)
|
||||||
|
}
|
||||||
|
// 3. 最后重新显示三角
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowToggle(true)
|
||||||
|
}, 20)
|
||||||
|
}, 150)
|
||||||
|
}
|
||||||
|
|
||||||
const openMain = () => {
|
const openMain = () => {
|
||||||
if (!(window as any).api) return
|
if (!(window as any).api) return
|
||||||
(window as any).api.openWindow({ key: 'main', route: '/' })
|
(window as any).api.openWindow({ key: 'main', route: '/' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const openQuickPoint = () => {
|
|
||||||
// 统一使用 / 路径,因为主页即是积分操作页
|
|
||||||
openMain()
|
|
||||||
}
|
|
||||||
|
|
||||||
const openLeaderboard = () => {
|
|
||||||
if (!(window as any).api) return
|
|
||||||
(window as any).api.openWindow({ key: 'main', route: '/leaderboard' })
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`global-sidebar-container ${expanded ? 'expanded' : 'collapsed'}`}
|
|
||||||
style={{
|
style={{
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
width: '84px',
|
width: '84px',
|
||||||
@@ -46,70 +59,60 @@ export const GlobalSidebar: React.FC = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
background: 'transparent',
|
background: 'transparent'
|
||||||
userSelect: 'none',
|
|
||||||
position: 'fixed',
|
|
||||||
right: 0,
|
|
||||||
top: 0
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
{/* 日常展示的三角 */}
|
||||||
|
<div
|
||||||
|
onClick={handleExpand}
|
||||||
|
className={`global-sidebar-toggle ${!showToggle ? 'hidden' : ''}`}
|
||||||
|
style={{ willChange: 'opacity, transform' }}
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 侧边栏内容 */}
|
||||||
|
<div
|
||||||
|
className={`sidebar-content-area ${expanded ? 'visible' : 'hidden'}`}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
backgroundColor: 'var(--ss-card-bg)',
|
||||||
alignItems: 'center',
|
height: 'fit-content',
|
||||||
transition: 'transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)',
|
willChange: 'opacity, transform'
|
||||||
transform: expanded ? 'translateX(0)' : 'translateX(60px)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* 侧边栏内容区 */}
|
{/* 顶部的关闭/收起按钮 */}
|
||||||
<div
|
<Button
|
||||||
style={{
|
shape="circle"
|
||||||
display: 'flex',
|
variant="text"
|
||||||
flexDirection: 'column',
|
onClick={handleCollapse}
|
||||||
backgroundColor: 'var(--ss-card-bg)',
|
style={{ marginBottom: '8px', color: 'var(--td-brand-color)' }}
|
||||||
borderRadius: '12px 0 0 12px',
|
|
||||||
boxShadow: '-4px 0 16px rgba(0,0,0,0.15)',
|
|
||||||
border: '1px solid var(--ss-border-color)',
|
|
||||||
borderRight: 'none',
|
|
||||||
padding: '12px 8px',
|
|
||||||
gap: '12px',
|
|
||||||
width: '60px',
|
|
||||||
pointerEvents: expanded ? 'auto' : 'none',
|
|
||||||
flexShrink: 0
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Tooltip content="主界面" placement="left">
|
<ChevronRightIcon size="20px" />
|
||||||
<Button shape="circle" variant="text" onClick={openMain}>
|
</Button>
|
||||||
<HomeIcon size="24px" />
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip content="积分操作" placement="left">
|
<Tooltip content="主界面" placement="left">
|
||||||
<Button shape="circle" variant="text" onClick={openQuickPoint}>
|
<Button shape="circle" variant="text" onClick={openMain}>
|
||||||
<UserAddIcon size="24px" />
|
<HomeIcon size="24px" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip content="排行榜" placement="left">
|
<Tooltip content="积分操作" placement="left">
|
||||||
<Button shape="circle" variant="text" onClick={openLeaderboard}>
|
<Button shape="circle" variant="text" onClick={() => openMain()}>
|
||||||
<ViewListIcon size="24px" />
|
<UserAddIcon size="24px" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip content="设置" placement="left">
|
<Tooltip content="排行榜" placement="left">
|
||||||
<Button shape="circle" variant="text" onClick={() => (window as any).api.openWindow({ key: 'main', route: '/settings' })}>
|
<Button shape="circle" variant="text" onClick={() => (window as any).api.openWindow({ key: 'main', route: '/leaderboard' })}>
|
||||||
<SettingIcon size="24px" />
|
<ViewListIcon size="24px" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 展开/收起手柄 */}
|
<Tooltip content="设置" placement="left">
|
||||||
<div
|
<Button shape="circle" variant="text" onClick={() => (window as any).api.openWindow({ key: 'main', route: '/settings' })}>
|
||||||
onClick={() => setExpanded(!expanded)}
|
<SettingIcon size="24px" />
|
||||||
className="global-sidebar-toggle"
|
</Button>
|
||||||
>
|
</Tooltip>
|
||||||
{expanded ? <ChevronRightIcon /> : <ChevronLeftIcon />}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user