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
+17 -17
View File
@@ -34,9 +34,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 8px; gap: 4px;
height: 58px; height: 58px;
padding: 0 16px 0 24px; padding: 0 12px 0 16px;
border-bottom: 1px solid var(--ant-color-border-secondary); border-bottom: 1px solid var(--ant-color-border-secondary);
user-select: none; user-select: none;
white-space: nowrap; white-space: nowrap;
@@ -53,7 +53,7 @@
.brand-actions { .brand-actions {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 2px; gap: 0;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -231,26 +231,26 @@
} }
.sidebar-pin { .sidebar-pin {
display: flex; display: inline-flex;
align-items: center; align-items: center;
gap: 10px; justify-content: center;
width: 100%; width: 30px;
height: 34px; height: 30px;
padding: 0 16px; padding: 0;
border: none; border: none;
border-radius: 6px; border-radius: 50%;
background: transparent; background: transparent;
color: var(--ant-color-text-secondary); color: var(--ant-color-text-secondary);
cursor: pointer; cursor: pointer;
font-size: 13px; flex-shrink: 0;
text-align: left; transition: background-color 0.2s, transform 0.15s, color 0.2s;
transition: background-color 0.2s, color 0.2s;
} }
.sidebar-pin:hover, .sidebar-pin:hover,
.sidebar-pin:focus-visible { .sidebar-pin:focus-visible {
background-color: color-mix(in srgb, var(--ant-color-primary) 10%, transparent); background-color: color-mix(in srgb, var(--ant-color-primary) 10%, transparent);
color: var(--ant-color-primary); color: var(--ant-color-primary);
transform: scale(1.08);
outline: none; outline: none;
} }
@@ -258,11 +258,6 @@
font-size: 16px; font-size: 16px;
} }
.ant-layout-sider-collapsed .sidebar-pin {
justify-content: center;
padding: 0;
}
.sider-version { .sider-version {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -278,6 +273,11 @@
transition: color 0.2s; transition: color 0.2s;
} }
.ant-layout-sider-collapsed .sider-version {
justify-content: center;
padding: 8px 0;
}
.sider-version .anticon { .sider-version .anticon {
font-size: 16px; font-size: 16px;
} }
+10 -11
View File
@@ -304,6 +304,16 @@ export default function AppSidebar() {
</div> </div>
{!railCollapsed && ( {!railCollapsed && (
<div className="brand-actions"> <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'} /> <DocsButton ariaLabel={t('menu.docs') || 'Documentation'} />
<DonateButton ariaLabel={t('menu.donate') || 'Donate'} /> <DonateButton ariaLabel={t('menu.donate') || 'Donate'} />
<ThemeCycleButton <ThemeCycleButton
@@ -335,17 +345,6 @@ export default function AppSidebar() {
onClick={onMenuClick} onClick={onMenuClick}
/> />
<div className="sider-footer"> <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} /> <VersionBadge version={panelVersion} collapsed={railCollapsed} />
</div> </div>
</Layout.Sider> </Layout.Sider>
+9 -3
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 first = renderSidebar();
const sidebar = first.container.querySelector('.ant-layout-sider'); 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); expect(sidebar?.classList.contains('ant-layout-sider-collapsed')).toBe(true);
fireEvent.click(screen.getByRole('button', { name: 'Pin sidebar' })); fireEvent.mouseEnter(sidebarRoot!);
fireEvent.mouseLeave(first.container.querySelector('.ant-sidebar')!);
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(sidebar?.classList.contains('ant-layout-sider-collapsed')).toBe(false);
expect(localStorage.getItem('sidebar-pinned')).toBe('true'); expect(localStorage.getItem('sidebar-pinned')).toBe('true');