Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 1x 1x 88x 88x 88x 55x 55x 54x 54x 1x 53x 55x 55x 55x 55x 55x 55x 40x 40x 40x 40x 10x 9x 9x 9x 10x 10x 30x 30x 30x 29x 1x 1x 29x 26x 26x 26x 30x 36x 1x 1x 40x 55x 55x 55x 55x 54x 54x 54x 54x 88x 88x | import { useEffect, useRef, type RefObject } from 'react';
/**
* Auto-focus a ref element with retry logic to handle animation timing.
* Keeps retrying until focus succeeds or max attempts reached.
* Works on both mobile and desktop by detecting when element is actually interactive.
*/
export const useAutoFocus = (ref: RefObject<HTMLElement | null>) => {
const attemptCountRef = useRef(0);
const maxAttemptsRef = useRef(0);
useEffect(() => {
const element = ref.current;
if (!element) return;
const isMobile =
typeof window !== 'undefined' && window.matchMedia
? window.matchMedia('(max-width: 767px)').matches
: false;
// Mobile animations take longer (1000ms+ with delays), desktop is faster
const maxAttempts = isMobile ? 20 : 5;
const delayMs = isMobile ? 100 : 50;
let timeoutId: NodeJS.Timeout | null = null;
let frameId: number | null = null;
let cancelled = false;
const attemptFocus = () => {
if (cancelled) return;
const element = ref.current;
if (!element) return;
// Only focus if element is in the DOM and visible
if (element.offsetParent === null) {
if (attemptCountRef.current < maxAttemptsRef.current) {
attemptCountRef.current++;
timeoutId = setTimeout(attemptFocus, delayMs);
}
return;
}
try {
element.focus();
// Check if focus actually succeeded on next frame
frameId = requestAnimationFrame(() => {
if (document.activeElement === element) {
// Focus succeeded - stop trying
return;
}
// Focus didn't stick, retry
if (attemptCountRef.current < maxAttemptsRef.current) {
attemptCountRef.current++;
timeoutId = setTimeout(attemptFocus, delayMs);
}
});
} catch (error) {
console.warn('[useAutoFocus] Failed to focus element:', error);
}
};
// Start focus attempts
maxAttemptsRef.current = maxAttempts;
attemptCountRef.current = 0;
timeoutId = setTimeout(attemptFocus, 50);
return () => {
cancelled = true;
if (timeoutId) clearTimeout(timeoutId);
if (frameId) cancelAnimationFrame(frameId);
};
}, [ref]);
};
|