AutoFocus
This commit is contained in:
parent
902f7b289f
commit
9edf9777a0
@ -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
|
||||||
|
}
|
||||||
|
@ -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>
|
||||||
)}
|
)}
|
||||||
|
@ -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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user