mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-12 16:15: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:
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, useId } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Checkbox, Empty, Form, Input, Modal, Select, Tag } from 'antd';
|
||||
import { DownloadOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
@@ -30,6 +30,7 @@ function shortTime(value?: number): string {
|
||||
|
||||
export default function AmneziaWGLogModal({ open, onClose }: AmneziaWGLogModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const uid = useId();
|
||||
const { datepicker } = useDatepicker();
|
||||
const { isMobile } = useMediaQuery();
|
||||
const [rows, setRows] = useState('50');
|
||||
@@ -120,6 +121,7 @@ export default function AmneziaWGLogModal({ open, onClose }: AmneziaWGLogModalPr
|
||||
<Form layout="inline" className="log-toolbar">
|
||||
<Form.Item>
|
||||
<Select
|
||||
aria-label={t('pages.index.logLines')}
|
||||
value={rows}
|
||||
size="small"
|
||||
style={{ width: 100 }}
|
||||
@@ -132,8 +134,9 @@ export default function AmneziaWGLogModal({ open, onClose }: AmneziaWGLogModalPr
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('filter')} className="filter-item">
|
||||
<Form.Item htmlFor={`${uid}-filter`} label={t('filter')} className="filter-item">
|
||||
<Input
|
||||
id={`${uid}-filter`}
|
||||
value={filter}
|
||||
size="small"
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
@@ -151,7 +154,7 @@ export default function AmneziaWGLogModal({ open, onClose }: AmneziaWGLogModalPr
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={download}
|
||||
icon={<DownloadOutlined />}
|
||||
icon={<DownloadOutlined aria-hidden />}
|
||||
aria-label={t('download')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function BackupModal({
|
||||
type="primary"
|
||||
aria-label={t('pages.index.exportDatabase')}
|
||||
onClick={exportDb}
|
||||
icon={<DownloadOutlined />}
|
||||
icon={<DownloadOutlined aria-hidden />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -102,7 +102,7 @@ export default function BackupModal({
|
||||
type="primary"
|
||||
aria-label={t('pages.index.migrationDownload')}
|
||||
onClick={exportMigration}
|
||||
icon={<DownloadOutlined />}
|
||||
icon={<DownloadOutlined aria-hidden />}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -120,7 +120,7 @@ export default function BackupModal({
|
||||
type="primary"
|
||||
aria-label={t('pages.index.importDatabase')}
|
||||
onClick={importDb}
|
||||
icon={<UploadOutlined />}
|
||||
icon={<UploadOutlined aria-hidden />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState, useId } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Alert, Button, Form, Input, Modal, Select, Space, Spin, Typography, message } from 'antd';
|
||||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
@@ -33,6 +33,7 @@ function fileNameFromUrl(url: string): string {
|
||||
|
||||
export default function GeodataSection({ active, onBusy, onClose }: GeodataSectionProps) {
|
||||
const { t } = useTranslation();
|
||||
const uid = useId();
|
||||
const [modal, modalContextHolder] = Modal.useModal();
|
||||
const [messageApi, messageContextHolder] = message.useMessage();
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -180,15 +181,25 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
||||
<Spin spinning={loading}>
|
||||
<Alert type="info" className="mb-12" title={t('pages.index.geodataHint')} showIcon />
|
||||
<Form layout="vertical">
|
||||
<Form.Item label={t('pages.index.geodataCron')} style={{ marginBottom: 8 }}>
|
||||
<Form.Item
|
||||
htmlFor={`${uid}-cron`}
|
||||
label={t('pages.index.geodataCron')}
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<Input
|
||||
id={`${uid}-cron`}
|
||||
value={cron}
|
||||
placeholder={DEFAULT_CRON}
|
||||
onChange={(e) => setCron(e.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('pages.index.geodataOutbound')} style={{ marginBottom: 8 }}>
|
||||
<Form.Item
|
||||
htmlFor={`${uid}-outbound`}
|
||||
label={t('pages.index.geodataOutbound')}
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<Select
|
||||
id={`${uid}-outbound`}
|
||||
style={{ width: '100%' }}
|
||||
allowClear
|
||||
value={outbound}
|
||||
@@ -204,6 +215,7 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
||||
{rows.map((row, i) => (
|
||||
<Space.Compact key={i} style={{ width: '100%' }}>
|
||||
<Input
|
||||
aria-label={`URL ${i + 1}`}
|
||||
style={{ width: '60%' }}
|
||||
placeholder="https://example.com/geosite_custom.dat"
|
||||
value={row.url}
|
||||
@@ -211,6 +223,7 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
||||
onBlur={() => onUrlBlur(i)}
|
||||
/>
|
||||
<Input
|
||||
aria-label={`${t('pages.index.geodataFile')} ${i + 1}`}
|
||||
style={{ width: '40%' }}
|
||||
placeholder={t('pages.index.geodataFile')}
|
||||
value={row.file}
|
||||
@@ -218,14 +231,14 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
||||
/>
|
||||
<Button
|
||||
aria-label={t('delete')}
|
||||
icon={<DeleteOutlined />}
|
||||
icon={<DeleteOutlined aria-hidden />}
|
||||
onClick={() => setRows((p) => p.filter((_, j) => j !== i))}
|
||||
/>
|
||||
</Space.Compact>
|
||||
))}
|
||||
<div className="actions-row">
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
icon={<PlusOutlined aria-hidden />}
|
||||
onClick={() => setRows((p) => [...p, { url: '', file: '' }])}
|
||||
>
|
||||
{t('pages.index.geodataAddFile')}
|
||||
|
||||
@@ -227,7 +227,7 @@ export default function IndexPage() {
|
||||
|
||||
<div className="ov-vitals">
|
||||
<VitalTile
|
||||
icon={<DashboardOutlined />}
|
||||
icon={<DashboardOutlined aria-hidden />}
|
||||
label={t('pages.index.cpu')}
|
||||
percent={status.cpu.percent}
|
||||
statusColor={status.cpu.color}
|
||||
@@ -238,7 +238,7 @@ export default function IndexPage() {
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
<VitalTile
|
||||
icon={<DatabaseOutlined />}
|
||||
icon={<DatabaseOutlined aria-hidden />}
|
||||
label={t('pages.index.memory')}
|
||||
percent={status.mem.percent}
|
||||
statusColor={status.mem.color}
|
||||
@@ -249,7 +249,7 @@ export default function IndexPage() {
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
<VitalTile
|
||||
icon={<SwapOutlined />}
|
||||
icon={<SwapOutlined aria-hidden />}
|
||||
label={t('pages.index.swap')}
|
||||
percent={status.swap.percent}
|
||||
statusColor={status.swap.color}
|
||||
@@ -260,7 +260,7 @@ export default function IndexPage() {
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
<VitalTile
|
||||
icon={<HddOutlined />}
|
||||
icon={<HddOutlined aria-hidden />}
|
||||
label={t('pages.index.storage')}
|
||||
percent={status.disk.percent}
|
||||
statusColor={status.disk.color}
|
||||
@@ -358,7 +358,7 @@ export default function IndexPage() {
|
||||
key="download"
|
||||
onClick={downloadConfig}
|
||||
size={isMobile ? 'small' : 'middle'}
|
||||
icon={<CloudDownloadOutlined />}
|
||||
icon={<CloudDownloadOutlined aria-hidden />}
|
||||
>
|
||||
{isMobile ? 'Download' : 'config.json'}
|
||||
</Button>,
|
||||
@@ -367,7 +367,7 @@ export default function IndexPage() {
|
||||
type="primary"
|
||||
onClick={copyConfig}
|
||||
size={isMobile ? 'small' : 'middle'}
|
||||
icon={<CopyOutlined />}
|
||||
icon={<CopyOutlined aria-hidden />}
|
||||
>
|
||||
Copy
|
||||
</Button>,
|
||||
|
||||
@@ -105,6 +105,7 @@ export default function LogModal({ open, onClose }: LogModalProps) {
|
||||
<Form.Item>
|
||||
<Space.Compact>
|
||||
<Select
|
||||
aria-label={t('pages.index.logLines')}
|
||||
value={rows}
|
||||
size="small"
|
||||
style={{ width: 100 }}
|
||||
@@ -118,6 +119,7 @@ export default function LogModal({ open, onClose }: LogModalProps) {
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
aria-label={t('pages.xray.logLevel')}
|
||||
value={level}
|
||||
size="small"
|
||||
style={{ minWidth: 95 }}
|
||||
@@ -144,7 +146,7 @@ export default function LogModal({ open, onClose }: LogModalProps) {
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={download}
|
||||
icon={<DownloadOutlined />}
|
||||
icon={<DownloadOutlined aria-hidden />}
|
||||
aria-label={t('download')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { formatPanelVersion } from '@/lib/panel-version';
|
||||
import { activateOnKey } from '@/utils/a11y';
|
||||
import type { Status } from '@/models/status';
|
||||
|
||||
interface OverviewActionBarProps {
|
||||
@@ -80,25 +81,30 @@ export default function OverviewActionBar({
|
||||
[
|
||||
{
|
||||
key: 'restart',
|
||||
icon: <ReloadOutlined />,
|
||||
icon: <ReloadOutlined aria-hidden />,
|
||||
text: t('pages.index.restartXray'),
|
||||
onClick: onRestartXray,
|
||||
primary: true,
|
||||
},
|
||||
{
|
||||
key: 'stop',
|
||||
icon: <PoweroffOutlined />,
|
||||
icon: <PoweroffOutlined aria-hidden />,
|
||||
text: t('pages.index.stopXray'),
|
||||
onClick: onStopXray,
|
||||
},
|
||||
],
|
||||
[
|
||||
{ key: 'logs', icon: <BarsOutlined />, text: t('pages.index.logs'), onClick: onOpenLogs },
|
||||
{
|
||||
key: 'logs',
|
||||
icon: <BarsOutlined aria-hidden />,
|
||||
text: t('pages.index.logs'),
|
||||
onClick: onOpenLogs,
|
||||
},
|
||||
...(accessLogEnable
|
||||
? [
|
||||
{
|
||||
key: 'accessLogs',
|
||||
icon: <FileTextOutlined />,
|
||||
icon: <FileTextOutlined aria-hidden />,
|
||||
text: t('pages.index.accessLogs'),
|
||||
onClick: onOpenXrayLogs,
|
||||
},
|
||||
@@ -108,7 +114,7 @@ export default function OverviewActionBar({
|
||||
? [
|
||||
{
|
||||
key: 'amneziawgLogs',
|
||||
icon: <ApiOutlined />,
|
||||
icon: <ApiOutlined aria-hidden />,
|
||||
text: t('pages.index.amneziawgLogs'),
|
||||
onClick: onOpenAmneziaWGLogs,
|
||||
},
|
||||
@@ -116,13 +122,13 @@ export default function OverviewActionBar({
|
||||
: []),
|
||||
{
|
||||
key: 'config',
|
||||
icon: <ControlOutlined />,
|
||||
icon: <ControlOutlined aria-hidden />,
|
||||
text: t('pages.index.config'),
|
||||
onClick: onOpenConfig,
|
||||
},
|
||||
{
|
||||
key: 'backup',
|
||||
icon: <CloudServerOutlined />,
|
||||
icon: <CloudServerOutlined aria-hidden />,
|
||||
text: t('pages.index.backupTitle'),
|
||||
onClick: onOpenBackup,
|
||||
},
|
||||
@@ -130,13 +136,13 @@ export default function OverviewActionBar({
|
||||
[
|
||||
{
|
||||
key: 'history',
|
||||
icon: <AreaChartOutlined />,
|
||||
icon: <AreaChartOutlined aria-hidden />,
|
||||
text: t('pages.index.systemHistoryTitle'),
|
||||
onClick: onOpenSystemHistory,
|
||||
},
|
||||
{
|
||||
key: 'metrics',
|
||||
icon: <ArrowUpOutlined />,
|
||||
icon: <ArrowUpOutlined aria-hidden />,
|
||||
text: t('pages.index.xrayMetricsTitle'),
|
||||
onClick: onOpenXrayMetrics,
|
||||
},
|
||||
@@ -177,8 +183,11 @@ export default function OverviewActionBar({
|
||||
<Tag
|
||||
className="ov-update-tag"
|
||||
color="warning"
|
||||
icon={<CloudDownloadOutlined />}
|
||||
icon={<CloudDownloadOutlined aria-hidden />}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onOpenPanelUpdate}
|
||||
onKeyDown={activateOnKey(onOpenPanelUpdate)}
|
||||
>
|
||||
{`${t('update')} ${formatPanelVersion(latestVersion)}`}
|
||||
</Tag>
|
||||
|
||||
@@ -144,7 +144,12 @@ export default function PanelUpdateModal({
|
||||
<div className="version-list">
|
||||
<div className="version-list-item">
|
||||
<span>{t('pages.index.devChannel')}</span>
|
||||
<Switch checked={!!devChannelEnable} loading={channelBusy} onChange={handleChannel} />
|
||||
<Switch
|
||||
aria-label={t('pages.index.devChannel')}
|
||||
checked={!!devChannelEnable}
|
||||
loading={channelBusy}
|
||||
onChange={handleChannel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -190,7 +195,7 @@ export default function PanelUpdateModal({
|
||||
type="primary"
|
||||
disabled={!info.updateAvailable}
|
||||
onClick={updatePanel}
|
||||
icon={<CloudDownloadOutlined />}
|
||||
icon={<CloudDownloadOutlined aria-hidden />}
|
||||
>
|
||||
{t('pages.index.updatePanel')}
|
||||
</Button>
|
||||
|
||||
@@ -50,7 +50,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'CPU',
|
||||
tabKey: 'pages.index.cpu',
|
||||
title: 'pages.index.historyTitleCpu',
|
||||
icon: <DashboardOutlined />,
|
||||
icon: <DashboardOutlined aria-hidden />,
|
||||
valueMax: 100,
|
||||
unit: '%',
|
||||
stroke: '',
|
||||
@@ -60,7 +60,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'RAM',
|
||||
tabKey: 'pages.index.memory',
|
||||
title: 'pages.index.historyTitleMem',
|
||||
icon: <DatabaseOutlined />,
|
||||
icon: <DatabaseOutlined aria-hidden />,
|
||||
valueMax: 100,
|
||||
unit: '%',
|
||||
stroke: '#7c4dff',
|
||||
@@ -74,7 +74,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Bandwidth',
|
||||
tabKey: 'pages.index.historyTabBandwidth',
|
||||
title: 'pages.index.historyTitleNetwork',
|
||||
icon: <GlobalOutlined />,
|
||||
icon: <GlobalOutlined aria-hidden />,
|
||||
valueMax: null,
|
||||
unit: 'B/s',
|
||||
stroke: '#1890ff',
|
||||
@@ -88,7 +88,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Packets',
|
||||
tabKey: 'pages.index.historyTabPackets',
|
||||
title: 'pages.index.historyTitlePackets',
|
||||
icon: <DeploymentUnitOutlined />,
|
||||
icon: <DeploymentUnitOutlined aria-hidden />,
|
||||
valueMax: null,
|
||||
unit: 'pkt/s',
|
||||
stroke: '#2f54eb',
|
||||
@@ -102,7 +102,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Connections',
|
||||
tabKey: 'pages.index.historyTabConnections',
|
||||
title: 'pages.index.historyTitleConnections',
|
||||
icon: <ApiOutlined />,
|
||||
icon: <ApiOutlined aria-hidden />,
|
||||
valueMax: null,
|
||||
unit: '',
|
||||
stroke: '#597ef7',
|
||||
@@ -116,7 +116,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Disk I/O',
|
||||
tabKey: 'pages.index.historyTabDisk',
|
||||
title: 'pages.index.historyTitleDisk',
|
||||
icon: <HddOutlined />,
|
||||
icon: <HddOutlined aria-hidden />,
|
||||
valueMax: null,
|
||||
unit: 'B/s',
|
||||
stroke: '#eb2f96',
|
||||
@@ -130,7 +130,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Disk Usage',
|
||||
tabKey: 'pages.index.historyTabDiskUsage',
|
||||
title: 'pages.index.historyTitleDiskUsage',
|
||||
icon: <PieChartOutlined />,
|
||||
icon: <PieChartOutlined aria-hidden />,
|
||||
valueMax: 100,
|
||||
unit: '%',
|
||||
stroke: '#13c2c2',
|
||||
@@ -140,7 +140,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Online',
|
||||
tabKey: 'pages.index.historyTabOnline',
|
||||
title: 'pages.index.historyTitleOnline',
|
||||
icon: <TeamOutlined />,
|
||||
icon: <TeamOutlined aria-hidden />,
|
||||
valueMax: null,
|
||||
unit: '',
|
||||
stroke: '#52c41a',
|
||||
@@ -150,7 +150,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Load',
|
||||
tabKey: 'pages.index.historyTabLoad',
|
||||
title: 'pages.index.historyTitleLoad',
|
||||
icon: <LineChartOutlined />,
|
||||
icon: <LineChartOutlined aria-hidden />,
|
||||
valueMax: null,
|
||||
unit: '',
|
||||
stroke: '#fa8c16',
|
||||
@@ -334,6 +334,7 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
||||
<div className="metric-modal-title">
|
||||
<span>{t('pages.index.systemHistoryTitle')}</span>
|
||||
<Select
|
||||
aria-label={t('pages.index.timeRange')}
|
||||
value={bucket}
|
||||
size="small"
|
||||
className="bucket-select"
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function SystemStrip({ status, showIp, onToggleIp }: SystemStripP
|
||||
<div className="ov-strip-grid">
|
||||
<div className="ov-strip-cell">
|
||||
<div className="ov-kicker ov-kicker-icon">
|
||||
<ClockCircleOutlined />
|
||||
<ClockCircleOutlined aria-hidden />
|
||||
{t('pages.index.uptime')}
|
||||
</div>
|
||||
<div className="ov-strip-split">
|
||||
@@ -46,7 +46,7 @@ export default function SystemStrip({ status, showIp, onToggleIp }: SystemStripP
|
||||
|
||||
<div className="ov-strip-cell">
|
||||
<div className="ov-kicker ov-kicker-icon">
|
||||
<DatabaseOutlined />
|
||||
<DatabaseOutlined aria-hidden />
|
||||
{t('pages.index.panel')}
|
||||
</div>
|
||||
<div className="ov-strip-split">
|
||||
@@ -64,7 +64,7 @@ export default function SystemStrip({ status, showIp, onToggleIp }: SystemStripP
|
||||
|
||||
<div className="ov-strip-cell">
|
||||
<div className="ov-kicker ov-kicker-icon">
|
||||
<GlobalOutlined />
|
||||
<GlobalOutlined aria-hidden />
|
||||
{t('pages.index.ipAddresses')}
|
||||
<Tooltip title={t('pages.index.toggleIpVisibility')}>
|
||||
{showIp ? (
|
||||
|
||||
@@ -47,12 +47,12 @@ export default function ThroughputCard({
|
||||
</div>
|
||||
<div className="ov-wide-legend">
|
||||
<div className="ov-legend-label">
|
||||
<ArrowUpOutlined style={{ color: accent }} />
|
||||
<ArrowUpOutlined aria-hidden style={{ color: accent }} />
|
||||
{t('pages.index.upload')}
|
||||
<span className="ov-legend-num">{SizeFormatter.speedFormat(status.netIO.up)}</span>
|
||||
</div>
|
||||
<div className="ov-legend-label">
|
||||
<ArrowDownOutlined style={{ color: downColor }} />
|
||||
<ArrowDownOutlined aria-hidden style={{ color: downColor }} />
|
||||
{t('pages.index.download')}
|
||||
<span className="ov-legend-num">{SizeFormatter.speedFormat(status.netIO.down)}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, useId } from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Checkbox, Form, Input, Modal, Select, Tag } from 'antd';
|
||||
@@ -63,6 +63,7 @@ const AUTO_UPDATE_INTERVAL = 5000;
|
||||
|
||||
export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const uid = useId();
|
||||
const { datepicker } = useDatepicker();
|
||||
const { isMobile } = useMediaQuery();
|
||||
const [rows, setRows] = useState('20');
|
||||
@@ -173,6 +174,7 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
|
||||
<Form layout="inline" className="log-toolbar">
|
||||
<Form.Item>
|
||||
<Select
|
||||
aria-label={t('pages.index.logLines')}
|
||||
value={rows}
|
||||
size="small"
|
||||
style={{ width: 100 }}
|
||||
@@ -186,8 +188,9 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('filter')} className="filter-item">
|
||||
<Form.Item htmlFor={`${uid}-filter`} label={t('filter')} className="filter-item">
|
||||
<Input
|
||||
id={`${uid}-filter`}
|
||||
value={filter}
|
||||
size="small"
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
@@ -214,7 +217,7 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={download}
|
||||
icon={<DownloadOutlined />}
|
||||
icon={<DownloadOutlined aria-hidden />}
|
||||
aria-label={t('download')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -53,7 +53,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Heap',
|
||||
tabKey: 'pages.index.xrayTabHeap',
|
||||
title: 'pages.index.xrayTitleHeap',
|
||||
icon: <DatabaseOutlined />,
|
||||
icon: <DatabaseOutlined aria-hidden />,
|
||||
unit: 'B',
|
||||
stroke: '#7c4dff',
|
||||
},
|
||||
@@ -62,7 +62,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Sys',
|
||||
tabKey: 'pages.index.xrayTabSys',
|
||||
title: 'pages.index.xrayTitleSys',
|
||||
icon: <CloudServerOutlined />,
|
||||
icon: <CloudServerOutlined aria-hidden />,
|
||||
unit: 'B',
|
||||
stroke: '#1890ff',
|
||||
},
|
||||
@@ -71,7 +71,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Objects',
|
||||
tabKey: 'pages.index.xrayTabObjects',
|
||||
title: 'pages.index.xrayTitleObjects',
|
||||
icon: <BlockOutlined />,
|
||||
icon: <BlockOutlined aria-hidden />,
|
||||
unit: '',
|
||||
stroke: '#13c2c2',
|
||||
},
|
||||
@@ -80,7 +80,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'GC Count',
|
||||
tabKey: 'pages.index.xrayTabGcCount',
|
||||
title: 'pages.index.xrayTitleGcCount',
|
||||
icon: <DeleteOutlined />,
|
||||
icon: <DeleteOutlined aria-hidden />,
|
||||
unit: '',
|
||||
stroke: '#fa8c16',
|
||||
},
|
||||
@@ -89,7 +89,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'GC Pause',
|
||||
tabKey: 'pages.index.xrayTabGcPause',
|
||||
title: 'pages.index.xrayTitleGcPause',
|
||||
icon: <PauseCircleOutlined />,
|
||||
icon: <PauseCircleOutlined aria-hidden />,
|
||||
unit: 'ns',
|
||||
stroke: '#f5222d',
|
||||
},
|
||||
@@ -98,7 +98,7 @@ const METRICS: MetricDef[] = [
|
||||
tab: 'Observatory',
|
||||
tabKey: 'pages.index.xrayTabObservatory',
|
||||
title: 'pages.index.xrayTitleObservatory',
|
||||
icon: <EyeOutlined />,
|
||||
icon: <EyeOutlined aria-hidden />,
|
||||
unit: 'ms',
|
||||
stroke: '#52c41a',
|
||||
},
|
||||
@@ -304,6 +304,7 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp
|
||||
<div className="metric-modal-title">
|
||||
<span>{t('pages.index.xrayMetricsTitle')}</span>
|
||||
<Select
|
||||
aria-label={t('pages.index.timeRange')}
|
||||
value={bucket}
|
||||
size="small"
|
||||
className="bucket-select"
|
||||
@@ -362,6 +363,7 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp
|
||||
) : (
|
||||
<div className="obs-controls">
|
||||
<Select
|
||||
aria-label={t('pages.index.xrayObservatoryTagPlaceholder')}
|
||||
value={obsActiveTag}
|
||||
size="small"
|
||||
className="obs-select"
|
||||
|
||||
Reference in New Issue
Block a user