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 focusUrlParamName = 'focus'
const focusChangedEventName = "focus-changed" const focusChangedEventName = "focus-changed"
@ -16,31 +16,51 @@ function triggerElementFocused(elementKey?: string) {
window.dispatchEvent(focusChangeEvent) window.dispatchEvent(focusChangeEvent)
} }
export function useFocusedElement(elementKey: string) { export function useFocusedElement<ElementType extends HTMLElement>(elementKey: string) {
const [isFocusedElement, setFocusedElement] = useState(false) const [isFocusedElement, setFocusedElement] = useState(false)
const focusedClass = isFocusedElement ? focusedElementClassName : '' const focusedClass = isFocusedElement ? focusedElementClassName : ''
const elementRef = useRef<HTMLSpanElement>(null) const elementRef = useRef<ElementType>(null)
function focusElement() { const focusElement = useCallback(() => {
triggerElementFocused(isFocusedElement ? undefined : elementKey) triggerElementFocused(isFocusedElement ? undefined : elementKey)
} }, [isFocusedElement, elementKey])
useEffect(() => { useEffect(() => {
function getFocusedState() { function updateFocusedState() {
const params = new URLSearchParams(window.location.search) const params = new URLSearchParams(window.location.search)
const focusedElement = params.get(focusUrlParamName) const focusedElement = params.get(focusUrlParamName)
const focused: boolean = focusedElement && focusedElement == elementKey || false const focused: boolean = focusedElement && focusedElement == elementKey || false
setFocusedElement(focused) setFocusedElement(focused)
} }
getFocusedState() updateFocusedState()
addEventListener(focusChangedEventName, getFocusedState) addEventListener(focusChangedEventName, updateFocusedState)
return () => { return () => {
removeEventListener(focusChangedEventName, getFocusedState) removeEventListener(focusChangedEventName, updateFocusedState)
} }
}, [elementKey, setFocusedElement]) }, [elementKey, setFocusedElement, focusElement])
isFocusedElement && elementRef.current?.scrollIntoView() isFocusedElement && elementRef.current?.scrollIntoView()
return {isFocusedElement, focusedClass, elementRef, focusElement} 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 React from 'react';
import Card from 'react-bootstrap/Card'; import Card from 'react-bootstrap/Card';
import { useAutoFocus, useFocusedElement } from '../FocusedElement';
export type Props = { export type Props = {
heading?: string, heading?: string,
@ -10,8 +11,9 @@ export type Props = {
}; };
export default function JobCard(props: Props) { export default function JobCard(props: Props) {
const focusRef = useAutoFocus<HTMLDivElement>([props.position, props.company].join(', '))
return ( return (
<Card className='job-card'> <Card ref={focusRef} className='job-card'>
{props.heading && ( {props.heading && (
<Card.Header>{props.heading}</Card.Header> <Card.Header>{props.heading}</Card.Header>
)} )}

View File

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