From 9edf9777a0e8eb1631147a242bb40f5c65b89dc2 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Thu, 25 May 2023 12:13:56 +0200 Subject: [PATCH] AutoFocus --- src/app/FocusedElement.tsx | 40 ++++++++++++++++++++++++--------- src/app/components/JobCard.tsx | 4 +++- src/app/components/TagCloud.tsx | 8 +++---- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/app/FocusedElement.tsx b/src/app/FocusedElement.tsx index 1e7b9fa..13e4a94 100644 --- a/src/app/FocusedElement.tsx +++ b/src/app/FocusedElement.tsx @@ -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(elementKey: string) { const [isFocusedElement, setFocusedElement] = useState(false) const focusedClass = isFocusedElement ? focusedElementClassName : '' - const elementRef = useRef(null) + const elementRef = useRef(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(elementKey: string) { + const {elementRef, focusedClass, focusElement} = useFocusedElement(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 +} diff --git a/src/app/components/JobCard.tsx b/src/app/components/JobCard.tsx index 3c14c3a..d4f0ffd 100644 --- a/src/app/components/JobCard.tsx +++ b/src/app/components/JobCard.tsx @@ -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([props.position, props.company].join(', ')) return ( - + {props.heading && ( {props.heading} )} diff --git a/src/app/components/TagCloud.tsx b/src/app/components/TagCloud.tsx index 81a2db8..4ef0951 100644 --- a/src/app/components/TagCloud.tsx +++ b/src/app/components/TagCloud.tsx @@ -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 ( - {props.text} + {props.text} ) }