diff --git a/src/app/FocusedElement.tsx b/src/app/FocusedElement.tsx new file mode 100644 index 0000000..1ee2c2b --- /dev/null +++ b/src/app/FocusedElement.tsx @@ -0,0 +1,42 @@ +import { useState, useRef, useEffect } from "react"; + +const focusUrlParamName = 'focus' +const focusChangedEventName = "focus-changed" +const focusedElementClassName = 'focused-element' + +function triggerElementFocused(elementKey: string) { + const url = new URL(window.location.href); + url.searchParams.set(focusUrlParamName, elementKey); + 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) + + function focusElement() { + triggerElementFocused(elementKey) + } + + useEffect(() => { + function getFocusedState() { + 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) + return () => { + removeEventListener(focusChangedEventName, getFocusedState) + } + }, [elementKey, setFocusedElement]) + + isFocusedElement && elementRef.current?.scrollIntoView() + + return {isFocusedElement, focusedClass, elementRef, focusElement} +} diff --git a/src/app/components/TagCloud.tsx b/src/app/components/TagCloud.tsx index 7dd455d..81a2db8 100644 --- a/src/app/components/TagCloud.tsx +++ b/src/app/components/TagCloud.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useRef, useState } from 'react'; import Container from 'react-bootstrap/Container'; +import { useFocusedElement } from '../FocusedElement'; export type Props = { title: string, @@ -8,47 +9,12 @@ export type Props = { style?: 'primary' | 'light' } -const focusChangedEventName = "focus-changed" - -function useFocusedElement(elementKey: string) { - const [isFocusedElement, setFocusedElement] = useState(false); - const focusedClass = isFocusedElement ? 'focused-element' : '' - const elementRef = useRef(null) - - useEffect(() => { - function getFocusedState() { - const params = new URLSearchParams(window.location.search) - const focusedElement = params.get('focus') - const focused: boolean = focusedElement && focusedElement == elementKey || false - setFocusedElement(focused) - } - - getFocusedState() - addEventListener(focusChangedEventName, getFocusedState) - return () => { - removeEventListener(focusChangedEventName, getFocusedState) - } - }, [elementKey, setFocusedElement]) - - isFocusedElement && elementRef.current?.scrollIntoView() - - return {isFocusedElement, focusedClass, elementRef} -} - -function setFocusedElement(elementKey: string) { - const url = new URL(window.location.href); - url.searchParams.set('focus', elementKey); - const focusChangeEvent = new Event(focusChangedEventName); - history.pushState({}, "", url); - window.dispatchEvent(focusChangeEvent) -} - function Tag(props: {text: string}) { const tagKey = 'tag_' + props.text; - const {focusedClass, elementRef} = useFocusedElement(tagKey) + const {elementRef, focusedClass, focusElement} = useFocusedElement(tagKey) return ( setFocusedElement(tagKey)} + onClick={focusElement} className={`badge text-bg-light ${focusedClass}`}>{props.text} ) }