mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-26 15:55:02 +03:00
fix(frontend): make the panel usable with screen readers (#6907)
* fix(frontend): make the panel usable with screen readers * fix(frontend): keep mobile tab names and the inbound expiry hint Review of #6907 found two gaps in the screen-reader pass: - On phone-width screens catTabLabel renders only the icon and names the tab by putting aria-label on it, but callers now pass that icon with aria-hidden for the desktop label, so every Settings, Xray and Host category tab lost its name under TalkBack. catTabLabel now clears aria-hidden on the icon it uses as the name. - DateTimePicker forwarded id and aria-label but not aria-describedby, so the inbound expiry hint ("leave blank to never expire") was never attached. It now forwards it on both the Gregorian and Jalali input.
This commit is contained in:
@@ -262,9 +262,9 @@ export default function CommandPalette() {
|
||||
title: c.email,
|
||||
subtitle: `${trafficUsed} / ${trafficTotal}${c.comment ? ` · ${c.comment}` : ''}`,
|
||||
icon: isOnline ? (
|
||||
<CheckCircleFilled style={{ color: '#52c41a' }} />
|
||||
<CheckCircleFilled aria-hidden style={{ color: '#52c41a' }} />
|
||||
) : (
|
||||
<CloseCircleFilled style={{ color: '#ff4d4f' }} />
|
||||
<CloseCircleFilled aria-hidden style={{ color: '#ff4d4f' }} />
|
||||
),
|
||||
action: () => {
|
||||
close();
|
||||
@@ -274,7 +274,7 @@ export default function CommandPalette() {
|
||||
c.subId && allSetting.subURI
|
||||
? {
|
||||
label: t('commandPalette.copySubscription'),
|
||||
icon: <CopyOutlined />,
|
||||
icon: <CopyOutlined aria-hidden />,
|
||||
execute: (e) => {
|
||||
e.stopPropagation();
|
||||
copySubscription(c);
|
||||
@@ -331,7 +331,7 @@ export default function CommandPalette() {
|
||||
category: 'inbounds',
|
||||
title: ib.remark || ib.tag || `Inbound #${ib.id}`,
|
||||
subtitle: `Port ${ib.port || ''}`,
|
||||
icon: <ImportOutlined style={{ color: '#1677ff' }} />,
|
||||
icon: <ImportOutlined aria-hidden style={{ color: '#1677ff' }} />,
|
||||
tag:
|
||||
tags.length > 0 ? (
|
||||
<div style={{ display: 'inline-flex', gap: 4, flexWrap: 'wrap' }}>{tags}</div>
|
||||
@@ -348,7 +348,7 @@ export default function CommandPalette() {
|
||||
path: '/',
|
||||
title: t('menu.dashboard'),
|
||||
keywords: ['overview', 'dashboard', 'cpu', 'ram', 'memory', 'traffic', 'speed'],
|
||||
icon: <DashboardOutlined />,
|
||||
icon: <DashboardOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/inbounds',
|
||||
@@ -365,67 +365,67 @@ export default function CommandPalette() {
|
||||
'hysteria',
|
||||
'masque',
|
||||
],
|
||||
icon: <ImportOutlined />,
|
||||
icon: <ImportOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/clients',
|
||||
title: t('menu.clients'),
|
||||
keywords: ['clients', 'users', 'sub', 'traffic', 'quota'],
|
||||
icon: <TeamOutlined />,
|
||||
icon: <TeamOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/groups',
|
||||
title: t('menu.groups'),
|
||||
keywords: ['groups', 'tags', 'batch'],
|
||||
icon: <TagsOutlined />,
|
||||
icon: <TagsOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/nodes',
|
||||
title: t('menu.nodes'),
|
||||
keywords: ['nodes', 'servers', 'cluster', 'remote nodes'],
|
||||
icon: <ClusterOutlined />,
|
||||
icon: <ClusterOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/hosts',
|
||||
title: t('menu.hosts'),
|
||||
keywords: ['hosts', 'sni', 'domains'],
|
||||
icon: <GlobalOutlined />,
|
||||
icon: <GlobalOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/outbound',
|
||||
title: t('menu.outbounds'),
|
||||
keywords: ['outbounds', 'freedom', 'blackhole', 'socks', 'http', 'warp', 'nord', 'pia'],
|
||||
icon: <ExportOutlined />,
|
||||
icon: <ExportOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/routing',
|
||||
title: t('menu.routing'),
|
||||
keywords: ['routing', 'rules', 'geoip', 'geosite', 'direct', 'block'],
|
||||
icon: <SwapOutlined />,
|
||||
icon: <SwapOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
title: t('menu.settings'),
|
||||
keywords: ['settings', 'config', 'port', 'password', 'ssl', 'telegram'],
|
||||
icon: <SettingOutlined />,
|
||||
icon: <SettingOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray',
|
||||
title: t('menu.xray'),
|
||||
keywords: ['xray', 'templates', 'balancer', 'dns'],
|
||||
icon: <ToolOutlined />,
|
||||
icon: <ToolOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/api-docs',
|
||||
title: t('menu.apiDocs'),
|
||||
keywords: ['api', 'api docs', 'swagger', 'rest api', 'endpoints'],
|
||||
icon: <ApiOutlined />,
|
||||
icon: <ApiOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/sponsors',
|
||||
title: t('menu.sponsors'),
|
||||
keywords: ['sponsors', 'sponsor', 'partners'],
|
||||
icon: <CrownOutlined />,
|
||||
icon: <CrownOutlined aria-hidden />,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -451,63 +451,63 @@ export default function CommandPalette() {
|
||||
title: `${t('menu.settings')} · ${t('pages.settings.panelSettings')}`,
|
||||
subtitle: t('pages.settings.panelSettings'),
|
||||
keywords: ['general', 'webPort', 'webBasePath', 'listenIP', 'ssl', 'certificate'],
|
||||
icon: <SettingOutlined />,
|
||||
icon: <SettingOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings#security',
|
||||
title: `${t('menu.settings')} · ${t('pages.settings.securitySettings')}`,
|
||||
subtitle: t('pages.settings.securitySettings'),
|
||||
keywords: ['security', 'password', 'username', '2fa', 'two factor', 'login limit'],
|
||||
icon: <SafetyOutlined />,
|
||||
icon: <SafetyOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings#telegram',
|
||||
title: `${t('menu.settings')} · ${t('pages.settings.TGBotSettings')}`,
|
||||
subtitle: t('pages.settings.TGBotSettings'),
|
||||
keywords: ['telegram', 'tgbot', 'bot token', 'chat id', 'notifications', 'alerts'],
|
||||
icon: <MessageOutlined />,
|
||||
icon: <MessageOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings#email',
|
||||
title: `${t('menu.settings')} · ${t('pages.settings.emailSettings')}`,
|
||||
subtitle: t('pages.settings.emailSettings'),
|
||||
keywords: ['email', 'smtp', 'mail', 'crash alerts'],
|
||||
icon: <MailOutlined />,
|
||||
icon: <MailOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings#discord',
|
||||
title: `${t('menu.settings')} · ${t('pages.settings.discordSettings')}`,
|
||||
subtitle: t('pages.settings.discordSettings'),
|
||||
keywords: ['discord', 'bot', 'channel', 'notifications', 'alerts'],
|
||||
icon: <DiscordOutlined />,
|
||||
icon: <DiscordOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings#subscription',
|
||||
title: `${t('menu.settings')} · ${t('pages.settings.subSettings')}`,
|
||||
subtitle: t('pages.settings.subSettings'),
|
||||
keywords: ['subscription', 'subPort', 'subURI', 'subDomain', 'reverse proxy'],
|
||||
icon: <CloudServerOutlined />,
|
||||
icon: <CloudServerOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings#subscription-formats',
|
||||
title: `${t('menu.settings')} · ${t('menu.subFormats')}`,
|
||||
subtitle: t('menu.subFormats'),
|
||||
keywords: ['formats', 'clash', 'sing-box', 'v2ray', 'json', 'sub formats'],
|
||||
icon: <CodeOutlined />,
|
||||
icon: <CodeOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/settings#subscription-balancers',
|
||||
title: `${t('menu.settings')} · ${t('pages.settings.subBalancers.menu')}`,
|
||||
subtitle: t('pages.settings.subBalancers.menu'),
|
||||
keywords: ['balancers', 'sub balancers', 'balancer nodes'],
|
||||
icon: <ApartmentOutlined />,
|
||||
icon: <ApartmentOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#basic',
|
||||
title: `${t('menu.xray')} · ${t('pages.xray.basicTemplate')}`,
|
||||
subtitle: t('pages.xray.basicTemplate'),
|
||||
keywords: ['basics', 'freedom strategy', 'happy eyeballs', 'torrent', 'connection'],
|
||||
icon: <ToolOutlined />,
|
||||
icon: <ToolOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#basic',
|
||||
@@ -522,56 +522,56 @@ export default function CommandPalette() {
|
||||
'statsOutbound',
|
||||
'metrics_out',
|
||||
],
|
||||
icon: <DashboardOutlined />,
|
||||
icon: <DashboardOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#basic',
|
||||
title: `${t('menu.xray')} · ${t('pages.xray.connectionLimits')}`,
|
||||
subtitle: t('pages.xray.connectionLimits'),
|
||||
keywords: ['limits', 'idle timeout', 'bufferSize', 'connIdle', 'timeout'],
|
||||
icon: <ClockCircleOutlined />,
|
||||
icon: <ClockCircleOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#basic',
|
||||
title: `${t('menu.xray')} · ${t('pages.xray.logConfigs')}`,
|
||||
subtitle: t('pages.xray.logConfigs'),
|
||||
keywords: ['logs', 'access log', 'error log', 'dns log', 'mask address', 'loglevel'],
|
||||
icon: <FileTextOutlined />,
|
||||
icon: <FileTextOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#balancer',
|
||||
title: `${t('menu.xray')} · ${t('pages.xray.Balancers')}`,
|
||||
subtitle: t('pages.xray.Balancers'),
|
||||
keywords: ['balancers', 'leastPing', 'roundRobin', 'fallback', 'strategy'],
|
||||
icon: <ClusterOutlined />,
|
||||
icon: <ClusterOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#dns',
|
||||
title: `${t('menu.xray')} · DNS`,
|
||||
subtitle: 'DNS',
|
||||
keywords: ['dns', 'dns servers', 'hosts', 'doh', 'dot', 'cloudflare dns'],
|
||||
icon: <DatabaseOutlined />,
|
||||
icon: <DatabaseOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#outbound',
|
||||
title: `${t('menu.xray')} · ${t('pages.xray.Outbounds')}`,
|
||||
subtitle: t('pages.xray.Outbounds'),
|
||||
keywords: ['outbound', 'freedom', 'direct', 'proxy outbounds'],
|
||||
icon: <ExportOutlined />,
|
||||
icon: <ExportOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#routing',
|
||||
title: `${t('menu.xray')} · ${t('pages.xray.basicRouting')}`,
|
||||
subtitle: t('pages.xray.basicRouting'),
|
||||
keywords: ['routing', 'routing rules', 'geoip', 'geosite', 'block', 'direct'],
|
||||
icon: <SwapOutlined />,
|
||||
icon: <SwapOutlined aria-hidden />,
|
||||
},
|
||||
{
|
||||
path: '/xray#advanced',
|
||||
title: `${t('menu.xray')} · ${t('pages.xray.advancedTemplate')}`,
|
||||
subtitle: t('pages.xray.advancedTemplate'),
|
||||
keywords: ['advanced', 'json template', 'advanced config', 'custom json'],
|
||||
icon: <CodeOutlined />,
|
||||
icon: <CodeOutlined aria-hidden />,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -599,7 +599,7 @@ export default function CommandPalette() {
|
||||
title: t('commandPalette.restartXray'),
|
||||
subtitle: t('pages.index.restartXray'),
|
||||
keywords: ['restart', 'xray restart', 'reboot xray'],
|
||||
icon: <ReloadOutlined style={{ color: '#faad14' }} />,
|
||||
icon: <ReloadOutlined aria-hidden style={{ color: '#faad14' }} />,
|
||||
action: restartXray,
|
||||
},
|
||||
{
|
||||
@@ -608,7 +608,7 @@ export default function CommandPalette() {
|
||||
title: t('menu.theme'),
|
||||
subtitle: isUltra ? 'Ultra Dark' : isDark ? 'Dark' : 'Light',
|
||||
keywords: ['theme', 'light', 'dark', 'ultra'],
|
||||
icon: isDark ? <SunOutlined /> : <MoonOutlined />,
|
||||
icon: isDark ? <SunOutlined aria-hidden /> : <MoonOutlined aria-hidden />,
|
||||
action: cycleTheme,
|
||||
},
|
||||
{
|
||||
@@ -617,7 +617,7 @@ export default function CommandPalette() {
|
||||
title: t('pages.inbounds.addInbound'),
|
||||
subtitle: t('menu.inbounds'),
|
||||
keywords: ['add inbound', 'create inbound', 'new port', 'new inbound'],
|
||||
icon: <PlusOutlined style={{ color: '#52c41a' }} />,
|
||||
icon: <PlusOutlined aria-hidden style={{ color: '#52c41a' }} />,
|
||||
action: () => {
|
||||
close();
|
||||
navigate('/inbounds');
|
||||
@@ -629,7 +629,7 @@ export default function CommandPalette() {
|
||||
title: t('pages.clients.addClient'),
|
||||
subtitle: t('menu.clients'),
|
||||
keywords: ['add client', 'create user', 'new client', 'new user'],
|
||||
icon: <PlusOutlined style={{ color: '#52c41a' }} />,
|
||||
icon: <PlusOutlined aria-hidden style={{ color: '#52c41a' }} />,
|
||||
action: () => {
|
||||
close();
|
||||
navigate('/clients');
|
||||
@@ -705,9 +705,9 @@ export default function CommandPalette() {
|
||||
>
|
||||
<div className="command-palette-header">
|
||||
{isClientSearching ? (
|
||||
<LoadingOutlined className="command-palette-search-icon spinning" />
|
||||
<LoadingOutlined aria-hidden className="command-palette-search-icon spinning" />
|
||||
) : (
|
||||
<SearchOutlined className="command-palette-search-icon" />
|
||||
<SearchOutlined aria-hidden className="command-palette-search-icon" />
|
||||
)}
|
||||
<input
|
||||
ref={inputRef}
|
||||
|
||||
Reference in New Issue
Block a user