feat(SW-184): language switcher mobile/desktop functionality

This commit is contained in:
Erik Tiekstra
2024-08-22 14:21:24 +02:00
parent a2e2cf575e
commit 7ef7b4a544
15 changed files with 407 additions and 83 deletions

82
hooks/useTrapFocus.ts Normal file
View File

@@ -0,0 +1,82 @@
"use client"
import { useCallback, useEffect, useRef, useState } from "react"
import { useHandleKeyPress } from "@/hooks/useHandleKeyPress"
import findTabbableDescendants from "@/utils/tabbable"
const TAB_KEY = "Tab"
const optionsDefault = { focusOnRender: true, returnFocus: true }
type OptionsType = {
focusOnRender?: boolean
returnFocus?: boolean
}
export function useTrapFocus(opts?: OptionsType) {
const options = opts ? { ...optionsDefault, ...opts } : optionsDefault
const ref = useRef<HTMLDivElement>(null)
const previouseFocusedElement = useRef<HTMLElement>(
document.activeElement as HTMLElement
)
const [tabbableElements, setTabbableElements] = useState<HTMLElement[]>([])
// Handle initial focus of the referenced element, and return focus to previously focused element on cleanup
// and find all the tabbable elements in the referenced element
useEffect(() => {
const { current } = ref
if (current) {
const focusableChildNodes = findTabbableDescendants(current)
if (options.focusOnRender) {
current.focus()
}
setTabbableElements(focusableChildNodes)
}
return () => {
const { current } = previouseFocusedElement
if (current instanceof HTMLElement && options.returnFocus) {
current.focus()
}
}
}, [options.focusOnRender, options.returnFocus, ref, setTabbableElements])
const handleUserKeyPress = useCallback(
(event: KeyboardEvent) => {
const { code, shiftKey } = event
const first = tabbableElements[0]
const last = tabbableElements[tabbableElements.length - 1]
const currentActiveElement = document.activeElement
// Scope current tabs to current root element
if (isWithinCurrentElementScope([...tabbableElements, ref.current])) {
if (code === TAB_KEY) {
if (
currentActiveElement === first ||
currentActiveElement === ref.current
) {
// move focus to last element if shift+tab while currently focusing the first tabbable element
if (shiftKey) {
event.preventDefault()
last.focus()
}
}
if (currentActiveElement === last) {
// move focus back to first if tabbing while currently focusing the last tabbable element
if (!shiftKey) {
event.preventDefault()
first.focus()
}
}
}
}
},
[ref, tabbableElements]
)
useHandleKeyPress(handleUserKeyPress)
return ref
}
function isWithinCurrentElementScope(
elementList: (HTMLInputElement | Element | null)[]
) {
const currentActiveElement = document.activeElement
return elementList.includes(currentActiveElement)
}