Przeglądaj źródła

fix(ui): align sidebar pin controls

Keep the pin with the expanded header actions and center the collapsed version link with the navigation rail.
PathGao 21 godzin temu
rodzic
commit
b2fe233108

+ 17 - 17
frontend/src/layouts/AppSidebar.css

@@ -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;
 }

+ 10 - 11
frontend/src/layouts/AppSidebar.tsx

@@ -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>

+ 9 - 3
frontend/src/test/app-sidebar.test.tsx

@@ -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');