mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-13 16:45:03 +03:00
fix(a11y): silence live stats and make Select tags removable for screen readers
Ant Design's Spin wraps its content in aria-live="polite", so every WebSocket tick on the dashboard, inbounds, clients and nodes pages was announced, and only the changed number was read, with no label. Pass aria-live="off" on every Spin that wraps content; aria-busy still signals loading. Each vital tile is now a group named by its metric, so "47.2 %" is read as CPU. rc-select renders each tag's remove "x" as an aria-hidden span with no tabIndex, so screen-reader users could only Backspace the last value away, never remove one SNI or inbound from the middle. selectTagRender swaps in a real button named "Remove <value>" and moves focus to the next tag after removal. It is applied to every tags/multiple Select. maxTagCount="responsive" is dropped because it hid selected inbounds behind "+N", where no screen reader could reach them. Two oxlint rules (antd-a11y/spin-content-not-live and antd-a11y/select-tag-remove) keep new code from regressing either fix.
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
import type { AllSetting } from '@/models/setting';
|
||||
import { onNumber } from '@/utils/onNumber';
|
||||
import { SettingListItem } from '@/components/ui';
|
||||
import { GoRegexInput } from '@/components/form';
|
||||
import { GoRegexInput, selectTagRender } from '@/components/form';
|
||||
import { useMediaQuery } from '@/hooks/useMediaQuery';
|
||||
import { catTabLabel } from './catTabLabel';
|
||||
import { sanitizePath, normalizePath } from './uriPath';
|
||||
@@ -418,6 +418,7 @@ export default function SubscriptionFormatsTab({
|
||||
<SettingListItem paddings="small" title={<>{t('pages.settings.direct')} IPs</>}>
|
||||
<Select
|
||||
mode="tags"
|
||||
tagRender={selectTagRender}
|
||||
value={ruleValues('direct', 'ip')}
|
||||
style={{ width: '100%' }}
|
||||
onChange={(v) => setRuleValues('direct', 'ip', DEFAULT_DIRECT_RULES[1], v)}
|
||||
@@ -434,6 +435,7 @@ export default function SubscriptionFormatsTab({
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
tagRender={selectTagRender}
|
||||
value={ruleValues('direct', 'domain')}
|
||||
style={{ width: '100%' }}
|
||||
onChange={(v) =>
|
||||
@@ -467,6 +469,7 @@ export default function SubscriptionFormatsTab({
|
||||
<SettingListItem paddings="small" title={t('pages.xray.blockdomains')}>
|
||||
<Select
|
||||
mode="tags"
|
||||
tagRender={selectTagRender}
|
||||
value={ruleValues('block', 'domain')}
|
||||
style={{ width: '100%' }}
|
||||
onChange={(v) => setRuleValues('block', 'domain', DEFAULT_BLOCK_RULES[0], v)}
|
||||
@@ -476,6 +479,7 @@ export default function SubscriptionFormatsTab({
|
||||
<SettingListItem paddings="small" title={t('pages.xray.blockips')}>
|
||||
<Select
|
||||
mode="tags"
|
||||
tagRender={selectTagRender}
|
||||
value={ruleValues('block', 'ip')}
|
||||
style={{ width: '100%' }}
|
||||
onChange={(v) => setRuleValues('block', 'ip', BLOCK_IP_RULE, v)}
|
||||
|
||||
Reference in New Issue
Block a user