fix(ui): align sidebar pin controls

Keep the pin with the expanded header actions and center the collapsed version link with the navigation rail.
This commit is contained in:
PathGao
2026-07-30 14:46:43 +08:00
parent 5373786faa
commit b2fe233108
3 changed files with 36 additions and 31 deletions

View File

@@ -34,9 +34,9 @@
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
gap: 4px;
height: 58px;
padding: 0 16px 0 24px;
padding: 0 12px 0 16px;
border-bottom: 1px solid var(--ant-color-border-secondary);
user-select: none;
white-space: nowrap;
@@ -53,7 +53,7 @@
.brand-actions {
display: inline-flex;
align-items: center;
gap: 2px;
gap: 0;
flex-shrink: 0;
}
@@ -231,26 +231,26 @@
}
.sidebar-pin {
display: flex;
display: inline-flex;
align-items: center;
gap: 10px;
width: 100%;
height: 34px;
padding: 0 16px;
justify-content: center;
width: 30px;
height: 30px;
padding: 0;
border: none;
border-radius: 6px;
border-radius: 50%;
background: transparent;
color: var(--ant-color-text-secondary);
cursor: pointer;
font-size: 13px;
text-align: left;
transition: background-color 0.2s, color 0.2s;
flex-shrink: 0;
transition: background-color 0.2s, transform 0.15s, color 0.2s;
}
.sidebar-pin:hover,
.sidebar-pin:focus-visible {
background-color: color-mix(in srgb, var(--ant-color-primary) 10%, transparent);
color: var(--ant-color-primary);
transform: scale(1.08);
outline: none;
}
@@ -258,11 +258,6 @@
font-size: 16px;
}
.ant-layout-sider-collapsed .sidebar-pin {
justify-content: center;
padding: 0;
}
.sider-version {
display: flex;
align-items: center;
@@ -278,6 +273,11 @@
transition: color 0.2s;
}
.ant-layout-sider-collapsed .sider-version {
justify-content: center;
padding: 8px 0;
}
.sider-version .anticon {
font-size: 16px;
}

View File

@@ -304,6 +304,16 @@ export default function AppSidebar() {
</div>
{!railCollapsed && (
<div className="brand-actions">
<button
type="button"
className="sidebar-pin"
aria-label={t(pinned ? 'menu.unpinSidebar' : 'menu.pinSidebar')}
aria-pressed={pinned}
title={t(pinned ? 'menu.unpinSidebar' : 'menu.pinSidebar')}
onClick={togglePinned}
>
{pinned ? <PushpinFilled /> : <PushpinOutlined />}
</button>
<DocsButton ariaLabel={t('menu.docs') || 'Documentation'} />
<DonateButton ariaLabel={t('menu.donate') || 'Donate'} />
<ThemeCycleButton
@@ -335,17 +345,6 @@ export default function AppSidebar() {
onClick={onMenuClick}
/>
<div className="sider-footer">
<button
type="button"
className="sidebar-pin"
aria-label={t(pinned ? 'menu.unpinSidebar' : 'menu.pinSidebar')}
aria-pressed={pinned}
title={t(pinned ? 'menu.unpinSidebar' : 'menu.pinSidebar')}
onClick={togglePinned}
>
{pinned ? <PushpinFilled /> : <PushpinOutlined />}
{!railCollapsed && <span>{t(pinned ? 'menu.unpinSidebar' : 'menu.pinSidebar')}</span>}
</button>
<VersionBadge version={panelVersion} collapsed={railCollapsed} />
</div>
</Layout.Sider>

View File

@@ -21,14 +21,20 @@ function renderSidebar() {
);
}
test('keeps the sidebar expanded after pinning it and restores the choice', () => {
test('keeps the sidebar expanded after pinning it from the header and restores the choice', () => {
const first = renderSidebar();
const sidebar = first.container.querySelector('.ant-layout-sider');
const sidebarRoot = first.container.querySelector('.ant-sidebar');
expect(sidebar?.classList.contains('ant-layout-sider-collapsed')).toBe(true);
fireEvent.click(screen.getByRole('button', { name: 'Pin sidebar' }));
fireEvent.mouseLeave(first.container.querySelector('.ant-sidebar')!);
fireEvent.mouseEnter(sidebarRoot!);
const pinButton = screen.getByRole('button', { name: 'Pin sidebar' });
expect(pinButton.closest('.brand-actions')).not.toBeNull();
fireEvent.click(pinButton);
fireEvent.mouseLeave(sidebarRoot!);
expect(sidebar?.classList.contains('ant-layout-sider-collapsed')).toBe(false);
expect(localStorage.getItem('sidebar-pinned')).toBe('true');