refactor(frontend): align sub page chrome with login + AntD defaults

- Theme + language buttons now both use AntD `<Button shape="circle"
  size="large" className="toolbar-btn">` with TranslationOutlined and
  the SVG theme icon — identical hover/border behaviour.
- Language popover content switched from hand-rolled `<ul.lang-list>`
  to AntD `<Menu mode="vertical" selectable />`; gains native
  hover/keyboard nav + active highlight.
- Drop `.info-table` `!important` border overrides (8 selectors) so
  Descriptions inherits the AntD theme border colour.
- Drop `.qr-code` padding/background/border-radius overrides; only
  `cursor: pointer` remains (QRCode handles padding/bg itself).
- Remove now-unused `.theme-cycle`, `.lang-list`, `.lang-item*`,
  `.lang-select`, `.settings-popover` rules.
This commit is contained in:
MHSanaei 2026-05-25 03:26:15 +02:00
parent cc1eee0a70
commit 6f9fdb154d
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
2 changed files with 40 additions and 99 deletions

View file

@ -53,49 +53,12 @@
.qr-code { .qr-code {
cursor: pointer; cursor: pointer;
padding: 0 !important;
background: #fff;
border-radius: 4px;
} }
.info-table { .info-table {
margin-top: 12px; margin-top: 12px;
} }
.info-table .ant-descriptions-view,
.info-table .ant-descriptions-view table,
.info-table .ant-descriptions-view th,
.info-table .ant-descriptions-view td {
border-color: rgba(0, 0, 0, 0.18) !important;
}
.info-table tbody > tr > th,
.info-table tbody > tr > td {
border-bottom: 1px solid rgba(0, 0, 0, 0.18) !important;
}
.info-table tbody > tr:last-child > th,
.info-table tbody > tr:last-child > td {
border-bottom: none !important;
}
.is-dark .info-table .ant-descriptions-view,
.is-dark .info-table .ant-descriptions-view table,
.is-dark .info-table .ant-descriptions-view th,
.is-dark .info-table .ant-descriptions-view td {
border-color: rgba(255, 255, 255, 0.18) !important;
}
.is-dark .info-table tbody > tr > th,
.is-dark .info-table tbody > tr > td {
border-bottom: 1px solid rgba(255, 255, 255, 0.18) !important;
}
.is-dark .info-table tbody > tr:last-child > th,
.is-dark .info-table tbody > tr:last-child > td {
border-bottom: none !important;
}
.links-section { .links-section {
margin-top: 16px; margin-top: 16px;
} }
@ -158,49 +121,20 @@
text-align: center; text-align: center;
} }
.settings-popover { .toolbar-btn {
min-width: 220px; width: 40px;
} height: 40px;
min-width: 40px;
.theme-cycle {
width: 32px;
height: 32px;
border-radius: 50%; border-radius: 50%;
border: 1px solid rgba(0, 0, 0, 0.08);
background: var(--bg-card);
color: rgba(0, 0, 0, 0.65);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 0; padding: 0;
transition: background-color 0.2s, transform 0.15s, color 0.2s;
} }
.theme-cycle:hover, .toolbar-btn .anticon {
.theme-cycle:focus-visible { font-size: 18px;
background-color: rgba(64, 150, 255, 0.1);
color: #4096ff;
transform: scale(1.05);
outline: none;
} }
.theme-cycle svg { .toolbar-btn svg {
width: 16px; width: 18px;
height: 16px; height: 18px;
} }
.is-dark .theme-cycle {
border-color: rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.85);
}
.is-dark .theme-cycle:hover,
.is-dark .theme-cycle:focus-visible {
background-color: rgba(64, 150, 255, 0.1);
color: #4096ff;
}
.lang-select {
width: 100%;
}

View file

@ -8,11 +8,11 @@ import {
Descriptions, Descriptions,
Dropdown, Dropdown,
Layout, Layout,
Menu,
message, message,
Popover, Popover,
QRCode, QRCode,
Row, Row,
Select,
Space, Space,
Tag, Tag,
} from 'antd'; } from 'antd';
@ -21,7 +21,7 @@ import {
AppleOutlined, AppleOutlined,
CopyOutlined, CopyOutlined,
DownOutlined, DownOutlined,
SettingOutlined, TranslationOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { ClipboardManager, IntlUtil, LanguageManager } from '@/utils'; import { ClipboardManager, IntlUtil, LanguageManager } from '@/utils';
@ -206,14 +206,14 @@ export default function SubPage() {
{ key: 'ios-happ', label: 'Happ', onClick: () => open(happUrl) }, { key: 'ios-happ', label: 'Happ', onClick: () => open(happUrl) },
], [copy, open, shadowrocketUrl, v2boxUrl, streisandUrl, happUrl]); ], [copy, open, shadowrocketUrl, v2boxUrl, streisandUrl, happUrl]);
const langOptions = useMemo( const langMenuItems = useMemo(
() => LanguageManager.supportedLanguages.map((l: { value: string; name: string; icon: string }) => ({ () => (LanguageManager.supportedLanguages as { value: string; name: string; icon: string }[]).map((l) => ({
value: l.value, key: l.value,
label: ( label: (
<> <Space size={8}>
<span aria-label={l.name}>{l.icon}</span> <span aria-hidden="true">{l.icon}</span>
&nbsp;&nbsp;<span>{l.name}</span> <span>{l.name}</span>
</> </Space>
), ),
})), })),
[], [],
@ -244,32 +244,39 @@ export default function SubPage() {
const cardExtra = ( const cardExtra = (
<Space size={8} align="center"> <Space size={8} align="center">
<button <Button
type="button" shape="circle"
id="sub-theme-cycle" size="large"
className="theme-cycle" className="toolbar-btn"
aria-label={t('menu.theme')} aria-label={t('menu.theme')}
title={t('menu.theme')} title={t('menu.theme')}
onClick={cycleTheme} onClick={cycleTheme}
> >
{themeIcon} {themeIcon}
</button> </Button>
<Popover <Popover
title={t('pages.settings.language')} rootClassName={isDark ? 'dark' : 'light'}
placement="bottomRight" placement="bottomRight"
trigger="click" trigger="click"
styles={{ content: { padding: 4 } }}
content={ content={
<Space orientation="vertical" size={10} className="settings-popover"> <Menu
<Select mode="vertical"
className="lang-select" selectable
value={lang} selectedKeys={[lang]}
onChange={onLangChange} items={langMenuItems}
options={langOptions} onClick={({ key }) => onLangChange(key)}
style={{ border: 'none', minWidth: 160 }}
/> />
</Space>
} }
> >
<Button shape="circle" icon={<SettingOutlined />} /> <Button
shape="circle"
size="large"
className="toolbar-btn"
aria-label={t('pages.settings.language')}
icon={<TranslationOutlined />}
/>
</Popover> </Popover>
</Space> </Space>
); );