fix: keep tender and analysis keyboard focus in dialogs

This commit is contained in:
2026-09-06 15:19:54 +03:00
parent ffec9bb54e
commit 1d00f4fa91
4 changed files with 53 additions and 28 deletions
+35
View File
@@ -0,0 +1,35 @@
import {useEffect,useRef} from 'react';
function focusableElements(root) {
return [...root.querySelectorAll('button, a[href], input, select, textarea, summary, [tabindex]')].filter(el=>{
if(el.tabIndex<0 || el.matches(':disabled') || !el.getClientRects().length || el.closest('[inert]'))return false;
if(['hidden','collapse'].includes(getComputedStyle(el).visibility))return false;
// Closed details descendants can have rectangles but cannot receive focus.
for(let parent=el.parentElement;parent && parent!==root;parent=parent.parentElement){
if(parent.tagName==='DETAILS' && !parent.open && !parent.querySelector(':scope > summary')?.contains(el))return false;
}
return true;
});
}
export function useModalFocus(open,onClose) {
const ref=useRef(null),close=useRef(onClose);
close.current=onClose;
useEffect(()=>{
if(!open || !ref.current)return;
const opener=document.activeElement,root=ref.current;
(focusableElements(root)[0] || root).focus();
return ()=>{if(opener?.isConnected)opener.focus();};
},[open]);
const onKeyDown=e=>{
const root=ref.current;
if(e.defaultPrevented || e.target.closest('[role="dialog"]')!==root)return;
if(e.key==='Escape'){e.preventDefault();e.stopPropagation();close.current();return;}
if(e.key!=='Tab')return;
const elements=focusableElements(root),first=elements[0],last=elements.at(-1);
if(!first){e.preventDefault();root.focus();}
else if(e.shiftKey && (document.activeElement===first || document.activeElement===root)){e.preventDefault();last.focus();}
else if(!e.shiftKey && document.activeElement===last){e.preventDefault();first.focus();}
};
return {ref,onKeyDown};
}