mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-27 18:56:58 +00:00
♿️(frontend) add visible focus ring on restore focus
Show focus ring when restoring focus, keyboard navigation only
This commit is contained in:
@@ -26,8 +26,21 @@ export function useRestoreFocus(
|
|||||||
|
|
||||||
const prevIsOpenRef = useRef(false)
|
const prevIsOpenRef = useRef(false)
|
||||||
const triggerRef = useRef<HTMLElement | null>(null)
|
const triggerRef = useRef<HTMLElement | null>(null)
|
||||||
|
const cleanupRef = useRef<(() => void) | null>(null)
|
||||||
|
const lastInteractionRef = useRef<'keyboard' | 'mouse' | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Track last interaction type (like native :focus-visible behavior)
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
lastInteractionRef.current = 'keyboard'
|
||||||
|
}
|
||||||
|
const handleMouseDown = () => {
|
||||||
|
lastInteractionRef.current = 'mouse'
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyDown)
|
||||||
|
document.addEventListener('mousedown', handleMouseDown)
|
||||||
|
|
||||||
const wasOpen = prevIsOpenRef.current
|
const wasOpen = prevIsOpenRef.current
|
||||||
|
|
||||||
// Just opened
|
// Just opened
|
||||||
@@ -41,7 +54,28 @@ export function useRestoreFocus(
|
|||||||
if (wasOpen && !isOpen) {
|
if (wasOpen && !isOpen) {
|
||||||
const trigger = triggerRef.current
|
const trigger = triggerRef.current
|
||||||
if (trigger && document.contains(trigger)) {
|
if (trigger && document.contains(trigger)) {
|
||||||
const focus = () => trigger.focus({ preventScroll })
|
const focus = () => {
|
||||||
|
trigger.focus({ preventScroll })
|
||||||
|
// Only show focus ring if last interaction was keyboard (like native :focus-visible)
|
||||||
|
if (lastInteractionRef.current === 'keyboard') {
|
||||||
|
trigger.setAttribute('data-focus-visible', '')
|
||||||
|
// Remove focus ring when focus moves to another element
|
||||||
|
const handleFocusChange = (e: FocusEvent) => {
|
||||||
|
if (e.target !== trigger && document.contains(trigger)) {
|
||||||
|
trigger.removeAttribute('data-focus-visible')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('focusin', handleFocusChange, {
|
||||||
|
once: true,
|
||||||
|
})
|
||||||
|
// Store cleanup for unmount case
|
||||||
|
cleanupRef.current = () => {
|
||||||
|
if (document.contains(trigger)) {
|
||||||
|
trigger.removeAttribute('data-focus-visible')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (restoreFocusRaf) requestAnimationFrame(focus)
|
if (restoreFocusRaf) requestAnimationFrame(focus)
|
||||||
else focus()
|
else focus()
|
||||||
}
|
}
|
||||||
@@ -50,6 +84,14 @@ export function useRestoreFocus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
prevIsOpenRef.current = isOpen
|
prevIsOpenRef.current = isOpen
|
||||||
|
|
||||||
|
// Cleanup: remove focus ring if component unmounts before focus changes
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleKeyDown)
|
||||||
|
document.removeEventListener('mousedown', handleMouseDown)
|
||||||
|
cleanupRef.current?.()
|
||||||
|
cleanupRef.current = null
|
||||||
|
}
|
||||||
}, [
|
}, [
|
||||||
isOpen,
|
isOpen,
|
||||||
onClosed,
|
onClosed,
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ body,
|
|||||||
[data-rac][data-focus-visible]:not(label, .react-aria-Select),
|
[data-rac][data-focus-visible]:not(label, .react-aria-Select),
|
||||||
:is(a, button, input[type='text'], select, textarea):not(
|
:is(a, button, input[type='text'], select, textarea):not(
|
||||||
[data-rac]
|
[data-rac]
|
||||||
):focus-visible {
|
):focus-visible,
|
||||||
|
/* Show focus ring when data-focus-visible is set programmatically (e.g., when restoring focus) */
|
||||||
|
[data-focus-visible]:is(a, button, input[type='text'], select, textarea):focus {
|
||||||
outline: 2px solid var(--colors-focus-ring);
|
outline: 2px solid var(--colors-focus-ring);
|
||||||
outline-offset: 1px;
|
outline-offset: 1px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user