Cleanup, removal of focus hook. Known bug when using Markdown.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React, { ReactNode } from 'react'
|
||||
import { usePersonContext } from '../hooks/PersonContext'
|
||||
import Container from 'react-bootstrap/esm/Container'
|
||||
import { useAutoFocus } from '../hooks/FocusedElement'
|
||||
import { Col, Row } from 'react-bootstrap'
|
||||
|
||||
export function Contact(props: {icon?: string, text: string}) {
|
||||
@@ -30,9 +29,8 @@ export function Contact(props: {icon?: string, text: string}) {
|
||||
|
||||
export function Contacts() {
|
||||
const person = usePersonContext()
|
||||
const focus = useAutoFocus<HTMLDivElement>('contacts')
|
||||
return (
|
||||
<Container ref={focus} className='contacts' fluid>
|
||||
<Container className='contacts' fluid>
|
||||
<Row>
|
||||
<h2>Contacts</h2>
|
||||
</Row>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import Container from 'react-bootstrap/Container';
|
||||
import { useAutoFocus } from '../hooks/FocusedElement';
|
||||
import Tag from './Tag';
|
||||
|
||||
export type Props = {
|
||||
@@ -11,10 +10,9 @@ export type Props = {
|
||||
}
|
||||
|
||||
export default function TagCloud(props: Props) {
|
||||
const focusRef = useAutoFocus<HTMLDivElement>('tags ' + props.title)
|
||||
const containerClasses = ['tag-cloud', 'cloud-' + (props.style || 'standard')]
|
||||
return (
|
||||
<Container ref={focusRef} className={containerClasses.join(' ')}>
|
||||
<Container className={containerClasses.join(' ')}>
|
||||
<h4>{props.icon && (<i className={'bi-' + props.icon}> </i>)}{props.title}</h4>
|
||||
<Container className='tag-badges'>
|
||||
{props.tags.map((tag: string) => (<Tag key={tag} text={tag} />) )}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import Card from 'react-bootstrap/Card';
|
||||
import { useAutoFocus } from '../../hooks/FocusedElement';
|
||||
import { Placeholder } from 'react-bootstrap';
|
||||
import JobTags, { focusKeyPlaceholder } from './JobTags';
|
||||
import JobTags from './JobTags';
|
||||
import { Job } from '@/PersonalDataTypes';
|
||||
import md from '../Markdown';
|
||||
|
||||
@@ -25,9 +24,8 @@ export function JobCardPlaceholder() {
|
||||
}
|
||||
|
||||
export default function JobCard(props: Props) {
|
||||
const focusRef = useAutoFocus<HTMLDivElement>([props.position, props.company, props.timerange].join(' - '))
|
||||
return (
|
||||
<Card ref={focusRef} className='job-card'>
|
||||
<Card className='job-card'>
|
||||
{props.heading && (
|
||||
<Card.Header>{props.heading}</Card.Header>
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,34 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Job } from '@/PersonalDataTypes';
|
||||
import { Accordion, Row } from 'react-bootstrap';
|
||||
import { Accordion } from 'react-bootstrap';
|
||||
import { JobListProps } from './types';
|
||||
import JobTags from './JobTags';
|
||||
import md from '../Markdown';
|
||||
|
||||
function JobTitle(props: {job: Job, heading?: string}) {
|
||||
const {job, heading} = props
|
||||
return (
|
||||
<div>
|
||||
<div><strong>{job.position}</strong></div>
|
||||
<div>{[job.company, job.timerange].filter(x => x).join(', ')}</div>
|
||||
{heading && (<div>({heading})</div>)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AccordionJobItem(props: {job: Job, eventKey: string, heading?: string}) {
|
||||
const {job, eventKey} = props
|
||||
return (
|
||||
<Accordion.Item eventKey={eventKey}>
|
||||
<Accordion.Header><JobTitle job={job} /></Accordion.Header>
|
||||
<Accordion.Body className='multiline'>
|
||||
{md(job.description)}
|
||||
{job.tags && <JobTags tags={job.tags} />}
|
||||
</Accordion.Body>
|
||||
</Accordion.Item>
|
||||
)
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
heading: string,
|
||||
@@ -19,36 +42,10 @@ const defaultProps = {
|
||||
export default function JobsAccordion(props: JobListProps) {
|
||||
const {jobs} = props
|
||||
const config = {...defaultProps, ...props}
|
||||
function JobTitle(props: {job: Job, heading?: string}) {
|
||||
const {job, heading} = props
|
||||
return (
|
||||
<div>
|
||||
<div><strong>{job.position}</strong></div>
|
||||
<div>{[job.company, job.timerange].filter(x => x).join(', ')}</div>
|
||||
{heading && (<div>({heading})</div>)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Accordion defaultActiveKey={jobs.current ? 'current' : 'previous-0'}>
|
||||
{jobs.current && (
|
||||
<Accordion.Item eventKey="current">
|
||||
<Accordion.Header><JobTitle heading={config.currentHeading} job={jobs.current} /> </Accordion.Header>
|
||||
<Accordion.Body>
|
||||
{md(jobs.current.description)}
|
||||
{jobs.current.tags && <JobTags tags={jobs.current.tags} />}
|
||||
</Accordion.Body>
|
||||
</Accordion.Item>
|
||||
)}
|
||||
{jobs.previous?.map((job, index) => (
|
||||
<Accordion.Item eventKey={`previous-${index}`} key={index}>
|
||||
<Accordion.Header><JobTitle job={job} /></Accordion.Header>
|
||||
<Accordion.Body className='multiline'>
|
||||
{md(job.description)}
|
||||
{job.tags && <JobTags tags={job.tags} />}
|
||||
</Accordion.Body>
|
||||
</Accordion.Item>
|
||||
))}
|
||||
{jobs.current && <AccordionJobItem job={jobs.current} heading={config.currentHeading} eventKey={'current'}/>}
|
||||
{jobs.previous?.map((job, index) => <AccordionJobItem key={index} job={job} eventKey={`previous-${index}`} />)}
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { useState, useRef, useEffect, useCallback } from "react";
|
||||
|
||||
const focusUrlParamName = 'focus'
|
||||
const focusChangedEventName = "focus-changed"
|
||||
const focusedElementClassName = 'focused-element'
|
||||
|
||||
function triggerElementFocused(elementKey?: string) {
|
||||
const url = new URL(window.location.href);
|
||||
if (elementKey) {
|
||||
url.searchParams.set(focusUrlParamName, elementKey)
|
||||
} else {
|
||||
url.searchParams.delete(focusUrlParamName)
|
||||
}
|
||||
const focusChangeEvent = new Event(focusChangedEventName)
|
||||
history.pushState({}, "", url);
|
||||
window.dispatchEvent(focusChangeEvent)
|
||||
}
|
||||
|
||||
export function useFocusedElement<ElementType extends HTMLElement>(elementKey: string) {
|
||||
const [isFocusedElement, setFocusedElement] = useState(false)
|
||||
const focusedClass = isFocusedElement ? focusedElementClassName : ''
|
||||
const elementRef = useRef<ElementType>(null)
|
||||
|
||||
const focusElement = useCallback(() => {
|
||||
triggerElementFocused(isFocusedElement ? undefined : elementKey)
|
||||
}, [isFocusedElement, elementKey])
|
||||
|
||||
useEffect(() => {
|
||||
function updateFocusedState() {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const focusedElement = params.get(focusUrlParamName)
|
||||
const focused: boolean = focusedElement && focusedElement == elementKey || false
|
||||
setFocusedElement(focused)
|
||||
}
|
||||
|
||||
updateFocusedState()
|
||||
addEventListener(focusChangedEventName, updateFocusedState)
|
||||
return () => {
|
||||
removeEventListener(focusChangedEventName, updateFocusedState)
|
||||
}
|
||||
}, [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 = (evt) => {
|
||||
evt.stopPropagation()
|
||||
focusElement()
|
||||
}
|
||||
const classNameBackup = elem.className
|
||||
elem.className += ' focusable ' + focusedClass
|
||||
cleanup = () => {
|
||||
elem.className = classNameBackup
|
||||
}
|
||||
}
|
||||
return cleanup
|
||||
}, [elementRef, focusedClass, focusElement])
|
||||
|
||||
return elementRef
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
export function partition<T>(array: T[]|undefined, entriesPerRow: number): T[][] {
|
||||
return array ? array.reduce((accumulator: T[][], current: T, index) => {
|
||||
if (index % entriesPerRow == 0) {
|
||||
accumulator[accumulator.length] = [current]
|
||||
} else {
|
||||
accumulator[accumulator.length - 1]
|
||||
}
|
||||
return accumulator
|
||||
}, []) : []
|
||||
}
|
||||
Reference in New Issue
Block a user