fix: UI/无障碍审查修复(焦点可见/键盘可达/dialog 语义/对比度/触控目标等 B1-B10)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
/** HTML 转义:& < > " ' 全转义 */
|
||||
export function escapeHtml(str) {
|
||||
return String(str == null ? '' : str).replace(/[&<>"']/g, (ch) => {
|
||||
@@ -45,3 +47,42 @@ export function showSnackbar(msg) {
|
||||
setTimeout(() => el.classList.remove('show', 'hide'), 300);
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
/** 弹窗焦点管理(B3):把焦点移到容器内首个可聚焦元素(input/select/textarea/button) */
|
||||
export function focusDialog(container) {
|
||||
if (!container) return;
|
||||
const el = container.querySelector('input, select, textarea, button, [tabindex]:not([tabindex="-1"])');
|
||||
if (el && typeof el.focus === 'function') el.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹窗键盘/焦点管理(B3):
|
||||
* - 打开时记录触发元素,并把焦点移到弹窗内首个可聚焦元素
|
||||
* - Esc 关闭弹窗
|
||||
* - 关闭后焦点还给触发元素
|
||||
* 返回 { dialogRef, onKeyDown }:dialogRef 绑到弹窗容器(.dialog-overlay),onKeyDown 绑其键盘事件。
|
||||
*/
|
||||
export function useDialog(open, onClose) {
|
||||
const dialogRef = useRef(null);
|
||||
const triggerRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
triggerRef.current = document.activeElement;
|
||||
focusDialog(dialogRef.current);
|
||||
} else if (triggerRef.current && typeof triggerRef.current.focus === 'function' && document.body.contains(triggerRef.current)) {
|
||||
triggerRef.current.focus();
|
||||
triggerRef.current = null;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open]);
|
||||
|
||||
const onKeyDown = useCallback((e) => {
|
||||
if (e.key === 'Escape') {
|
||||
e.stopPropagation();
|
||||
if (onClose) onClose();
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
return { dialogRef, onKeyDown };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user