AutoFocus

This commit is contained in:
Dejvino 2023-05-25 12:13:56 +02:00
parent 902f7b289f
commit 9edf9777a0
3 changed files with 36 additions and 16 deletions

View File

@ -1,4 +1,4 @@
import { useState, useRef, useEffect } from "react";
import { useState, useRef, useEffect, useCallback } from "react";
const focusUrlParamName = 'focus'
const focusChangedEventName = "focus-changed"
@ -16,31 +16,51 @@ function triggerElementFocused(elementKey?: string) {
window.dispatchEvent(focusChangeEvent)
}
export function useFocusedElement(elementKey: string) {
export function useFocusedElement<ElementType extends HTMLElement>(elementKey: string) {
const [isFocusedElement, setFocusedElement] = useState(false)
const focusedClass = isFocusedElement ? focusedElementClassName : ''
const elementRef = useRef<HTMLSpanElement>(null)
const elementRef = useRef<ElementType>(null)
function focusElement() {
const focusElement = useCallback(() => {
triggerElementFocused(isFocusedElement ? undefined : elementKey)
}
}, [isFocusedElement, elementKey])
useEffect(() => {
function getFocusedState() {
function updateFocusedState() {
const params = new URLSearchParams(window.location.search)
const focusedElement = params.get(focusUrlParamName)
const focused: boolean = focusedElement && focusedElement == elementKey || false
setFocusedElement(focused)
}
getFocusedState()
addEventListener(focusChangedEventName, getFocusedState)
updateFocusedState()
addEventListener(focusChangedEventName, updateFocusedState)
return () => {
removeEventListener(focusChangedEventName, getFocusedState)
removeEventListener(focusChangedEventName, updateFocusedState)
}
}, [elementKey, setFocusedElement])
}, [elementKey, setFocusedElement, focusElement])
isFocusedElement && elementRef.current?.scrollIntoView()
return {isFocusedElement, focusedClass, elementRef, focusElement}
}
export function useAutoFocus<ElementType extends HTMLElement>(elementKey: string) {
const {elementRef, focusedClass, focusElement} = useFocusedElement<ElementType>(elementKey);
useEffect(() => {
let cleanup = () => {}
if (elementRef.current) {
const elem = elementRef.current
elem.onclick = focusElement
const classNameBackup = elem.className
elem.className += ' ' + focusedClass
cleanup = () => {
elem.className = classNameBackup
}
}
return cleanup
}, [elementRef, focusedClass, focusElement])
return elementRef
}

View File

@ -1,5 +1,6 @@
import React from 'react';
import Card from 'react-bootstrap/Card';
import { useAutoFocus, useFocusedElement } from '../FocusedElement';
export type Props = {
heading?: string,
@ -10,8 +11,9 @@ export type Props = {
};
export default function JobCard(props: Props) {
const focusRef = useAutoFocus<HTMLDivElement>([props.position, props.company].join(', '))
return (
<Card className='job-card'>
<Card ref={focusRef} className='job-card'>
{props.heading && (
<Card.Header>{props.heading}</Card.Header>
)}

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
import Container from 'react-bootstrap/Container';
import { useFocusedElement } from '../FocusedElement';
import { useAutoFocus, useFocusedElement } from '../FocusedElement';
export type Props = {
title: string,
@ -11,11 +11,9 @@ export type Props = {
function Tag(props: {text: string}) {
const tagKey = 'tag_' + props.text;
const {elementRef, focusedClass, focusElement} = useFocusedElement(tagKey)
const elementRef = useAutoFocus(tagKey)
return (
<span ref={elementRef}
onClick={focusElement}
className={`badge text-bg-light ${focusedClass}`}>{props.text}</span>
<span ref={elementRef} className='badge text-bg-light'>{props.text}</span>
)
}