import { useState, useRef, useEffect, useCallback } from "react"; const focusUrlParamName = 'focus' const focusChangedEventName = "focus-changed" const focusedElementClassName = 'focused-element' function triggerElementFocused(elementKey?: string) { const url = new URL(window.location.href); if (elementKey) { url.searchParams.set(focusUrlParamName, elementKey) } else { url.searchParams.delete(focusUrlParamName) } const focusChangeEvent = new Event(focusChangedEventName) history.pushState({}, "", url); window.dispatchEvent(focusChangeEvent) } export function useFocusedElement(elementKey: string) { const [isFocusedElement, setFocusedElement] = useState(false) const focusedClass = isFocusedElement ? focusedElementClassName : '' const elementRef = useRef(null) const focusElement = useCallback(() => { triggerElementFocused(isFocusedElement ? undefined : elementKey) }, [isFocusedElement, elementKey]) useEffect(() => { function updateFocusedState() { const params = new URLSearchParams(window.location.search) const focusedElement = params.get(focusUrlParamName) const focused: boolean = focusedElement && focusedElement == elementKey || false setFocusedElement(focused) } updateFocusedState() addEventListener(focusChangedEventName, updateFocusedState) return () => { removeEventListener(focusChangedEventName, updateFocusedState) } }, [elementKey, setFocusedElement, focusElement]) isFocusedElement && elementRef.current?.scrollIntoView() return {isFocusedElement, focusedClass, elementRef, focusElement} } export function useAutoFocus(elementKey: string) { const {elementRef, focusedClass, focusElement} = useFocusedElement(elementKey); useEffect(() => { let cleanup = () => {} if (elementRef.current) { const elem = elementRef.current elem.onclick = (evt) => { evt.stopPropagation() focusElement() } const classNameBackup = elem.className elem.className += ' focusable ' + focusedClass cleanup = () => { elem.className = classNameBackup } } return cleanup }, [elementRef, focusedClass, focusElement]) return elementRef }