mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-03 03:35:04 +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:
@@ -208,7 +208,11 @@ export default function HostFormModal({
|
||||
{
|
||||
key: 'basic',
|
||||
forceRender: true,
|
||||
label: catTabLabel(<ProfileOutlined />, t('pages.hosts.sections.basic'), isMobile),
|
||||
label: catTabLabel(
|
||||
<ProfileOutlined aria-hidden />,
|
||||
t('pages.hosts.sections.basic'),
|
||||
isMobile,
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<FormField
|
||||
@@ -291,7 +295,7 @@ export default function HostFormModal({
|
||||
key: 'security',
|
||||
forceRender: true,
|
||||
label: catTabLabel(
|
||||
<SafetyCertificateOutlined />,
|
||||
<SafetyCertificateOutlined aria-hidden />,
|
||||
t('pages.hosts.sections.security'),
|
||||
isMobile,
|
||||
),
|
||||
@@ -373,7 +377,7 @@ export default function HostFormModal({
|
||||
key: 'advanced',
|
||||
forceRender: true,
|
||||
label: catTabLabel(
|
||||
<ControlOutlined />,
|
||||
<ControlOutlined aria-hidden />,
|
||||
t('pages.hosts.sections.advanced'),
|
||||
isMobile,
|
||||
),
|
||||
@@ -386,7 +390,7 @@ export default function HostFormModal({
|
||||
key: 'adv-general',
|
||||
forceRender: true,
|
||||
label: catTabLabel(
|
||||
<SettingOutlined />,
|
||||
<SettingOutlined aria-hidden />,
|
||||
t('pages.hosts.sections.general'),
|
||||
isMobile,
|
||||
),
|
||||
@@ -425,7 +429,7 @@ export default function HostFormModal({
|
||||
key: 'adv-mux',
|
||||
forceRender: true,
|
||||
label: catTabLabel(
|
||||
<PartitionOutlined />,
|
||||
<PartitionOutlined aria-hidden />,
|
||||
t('pages.hosts.fields.muxParams'),
|
||||
isMobile,
|
||||
),
|
||||
@@ -445,7 +449,7 @@ export default function HostFormModal({
|
||||
key: 'adv-sockopt',
|
||||
forceRender: true,
|
||||
label: catTabLabel(
|
||||
<DeploymentUnitOutlined />,
|
||||
<DeploymentUnitOutlined aria-hidden />,
|
||||
t('pages.hosts.fields.sockoptParams'),
|
||||
isMobile,
|
||||
),
|
||||
@@ -465,7 +469,7 @@ export default function HostFormModal({
|
||||
key: 'adv-finalmask',
|
||||
forceRender: true,
|
||||
label: catTabLabel(
|
||||
<RocketOutlined />,
|
||||
<RocketOutlined aria-hidden />,
|
||||
t('pages.hosts.fields.finalMask'),
|
||||
isMobile,
|
||||
),
|
||||
@@ -489,7 +493,7 @@ export default function HostFormModal({
|
||||
key: 'clash',
|
||||
forceRender: true,
|
||||
label: catTabLabel(
|
||||
<NodeIndexOutlined />,
|
||||
<NodeIndexOutlined aria-hidden />,
|
||||
t('pages.hosts.sections.clash'),
|
||||
isMobile,
|
||||
),
|
||||
|
||||
@@ -95,7 +95,7 @@ export default function HostList(props: HostListProps) {
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<ArrowUpOutlined />}
|
||||
icon={<ArrowUpOutlined aria-hidden />}
|
||||
aria-label={t('pages.hosts.moveUp')}
|
||||
disabled={idx === 0}
|
||||
onClick={() => onMove(h, 'up')}
|
||||
@@ -105,7 +105,7 @@ export default function HostList(props: HostListProps) {
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<ArrowDownOutlined />}
|
||||
icon={<ArrowDownOutlined aria-hidden />}
|
||||
aria-label={t('pages.hosts.moveDown')}
|
||||
disabled={idx >= count - 1}
|
||||
onClick={() => onMove(h, 'down')}
|
||||
@@ -115,7 +115,7 @@ export default function HostList(props: HostListProps) {
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
icon={<EditOutlined aria-hidden />}
|
||||
aria-label={t('edit')}
|
||||
onClick={() => onEdit(h)}
|
||||
/>
|
||||
@@ -125,7 +125,7 @@ export default function HostList(props: HostListProps) {
|
||||
size="small"
|
||||
type="text"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
icon={<DeleteOutlined aria-hidden />}
|
||||
aria-label={t('delete')}
|
||||
onClick={() => onDelete(h)}
|
||||
/>
|
||||
@@ -139,7 +139,12 @@ export default function HostList(props: HostListProps) {
|
||||
key: 'enable',
|
||||
width: 90,
|
||||
render: (_, h) => (
|
||||
<Switch size="small" checked={!h.isDisabled} onChange={(next) => onToggleEnable(h, next)} />
|
||||
<Switch
|
||||
size="small"
|
||||
checked={!h.isDisabled}
|
||||
aria-label={t('pages.hosts.fields.enable')}
|
||||
onChange={(next) => onToggleEnable(h, next)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -276,7 +281,7 @@ export default function HostList(props: HostListProps) {
|
||||
const toolbar = (
|
||||
<div className="card-toolbar">
|
||||
{selectedGroupIds.length === 0 ? (
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={onAdd}>
|
||||
<Button type="primary" icon={<PlusOutlined aria-hidden />} onClick={onAdd}>
|
||||
{!isMobile && t('pages.hosts.addHost')}
|
||||
</Button>
|
||||
) : (
|
||||
@@ -291,7 +296,7 @@ export default function HostList(props: HostListProps) {
|
||||
</Tag>
|
||||
<Button onClick={() => onBulkEnable(true)}>{t('pages.hosts.bulkEnable')}</Button>
|
||||
<Button onClick={() => onBulkEnable(false)}>{t('pages.hosts.bulkDisable')}</Button>
|
||||
<Button danger icon={<DeleteOutlined />} onClick={onBulkDelete}>
|
||||
<Button danger icon={<DeleteOutlined aria-hidden />} onClick={onBulkDelete}>
|
||||
{t('pages.hosts.bulkDelete')}
|
||||
</Button>
|
||||
</>
|
||||
@@ -316,7 +321,7 @@ export default function HostList(props: HostListProps) {
|
||||
locale={{
|
||||
emptyText: (
|
||||
<div className="card-empty">
|
||||
<GlobalOutlined style={{ fontSize: 32, marginBottom: 8 }} />
|
||||
<GlobalOutlined aria-hidden style={{ fontSize: 32, marginBottom: 8 }} />
|
||||
<div>{t('noData')}</div>
|
||||
</div>
|
||||
),
|
||||
|
||||
@@ -174,7 +174,7 @@ export default function HostsPage() {
|
||||
<Statistic
|
||||
title={t('pages.hosts.summary.total')}
|
||||
value={String(summary.total)}
|
||||
prefix={<GlobalOutlined />}
|
||||
prefix={<GlobalOutlined aria-hidden />}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={8} sm={8} md={8}>
|
||||
@@ -182,7 +182,10 @@ export default function HostsPage() {
|
||||
title={t('pages.hosts.summary.enabled')}
|
||||
value={String(summary.enabled)}
|
||||
prefix={
|
||||
<CheckCircleOutlined style={{ color: 'var(--ant-color-success)' }} />
|
||||
<CheckCircleOutlined
|
||||
aria-hidden
|
||||
style={{ color: 'var(--ant-color-success)' }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
@@ -191,7 +194,10 @@ export default function HostsPage() {
|
||||
title={t('pages.hosts.summary.disabled')}
|
||||
value={String(summary.disabled)}
|
||||
prefix={
|
||||
<StopOutlined style={{ color: 'var(--ant-color-text-quaternary)' }} />
|
||||
<StopOutlined
|
||||
aria-hidden
|
||||
style={{ color: 'var(--ant-color-text-quaternary)' }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
Reference in New Issue
Block a user