Compare commits

..

No commits in common. "1798313c9c227ea115f3a8a118dfc28ff1f855f0" and "a73b0359321d854127e11e446439104ca92ea27f" have entirely different histories.

11 changed files with 61 additions and 75 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -3,14 +3,13 @@ import { PersonalData } from "./PersonalDataTypes";
export const personalData: PersonalData = { export const personalData: PersonalData = {
updatedDate: '2023-05-26', updatedDate: '2023-05-26',
name: "David Hrdina Němeček", name: "David Hrdina Němeček",
brief: "Software developer. Engineering lead & manager.", brief: "Software developer, people manager.",
contacts: [ contacts: [
{icon: 'browser-firefox', text: 'www.dejvino.cz'}, {icon: 'browser-firefox', text: 'www.dejvino.cz'},
{icon: 'envelope-at', text: 'explosive@dejvino.cz'}, {icon: 'envelope-at', text: 'explosive@dejvino.cz'},
{icon: 'git', text: 'https://git.dejvino.cz'}, {icon: 'git', text: 'https://git.dejvino.cz'},
{icon: 'file-earmark-person', text: 'https://cv.dejvino.cz'}, {icon: 'telephone', text: '+420 111 222 333'},
{icon: 'telephone', text: '+420 775 26 26 32'},
{icon: 'geo-alt', text: 'Brno, Czechia'} {icon: 'geo-alt', text: 'Brno, Czechia'}
], ],
@ -65,7 +64,7 @@ export const personalData: PersonalData = {
position: `Personal projects`, position: `Personal projects`,
description: `Various hardware and software projects. Usually open sourced and published on [projects.dejvino.com](https://projects.dejvino.com) . description: `Various hardware and software projects. Usually open sourced and published on [projects.dejvino.com](https://projects.dejvino.com) .
These include video games, utilities, 3D models, devices with embedded microcontrollers etc.`, These include video games, utilities, 3D models, devices with embedded microcontrollers etc.`,
tags: ['Java', 'Python', 'C/C++', 'Embedded Software', 'OpenSCAD', 'TypeScript', 'Linux', 'Open Source', 'Git', 'Self-hosting'] tags: ['Java', 'Python', 'C/C++', 'Embedded devices', 'OpenSCAD', 'TypeScript', 'Linux', 'Git', 'Self-hosting']
} }
}, },
@ -76,23 +75,21 @@ export const personalData: PersonalData = {
company: `Faculty of Informatics, Masaryk University Brno`, company: `Faculty of Informatics, Masaryk University Brno`,
timerange: '2011 - 2013', timerange: '2011 - 2013',
description: `Master's thesis: Efficient computation and visualization of correlations in medical signals.`, description: `Master's thesis: Efficient computation and visualization of correlations in medical signals.`,
tags: ['C/C++', 'Supercomputers', 'OpenMPI', 'Python', 'Visualization', 'Data science']
}, },
{ {
position: `Bachelor's degree, Parallel and Distributed Systems`, position: `Bachelor's degree, Parallel and Distributed Systems`,
company: `Faculty of Informatics, Masaryk University Brno`, company: `Faculty of Informatics, Masaryk University Brno`,
timerange: '2008 - 2011', timerange: '2008 - 2011',
description: `Bachelor's thesis: Parallel implementation of force-field decompression algorithm.`, description: `Bachelor's thesis: Parallel implementation of force-field decompression algorithm.`,
tags: ['C/C++', 'Supercomputers', 'OpenMPI', 'Data science']
} }
] ]
}, },
skills: { skills: {
primary: ['Java', 'TypeScript', 'JavaScript', 'Linux', 'Engineering Leadership'], primary: ['Java', 'TypeScript', 'JavaScript', 'Engineering leadership', 'Linux'],
secondary: ['SQL', 'Kotlin', 'C/C++', 'NodeJs', 'Git', 'Preact', 'Embedded Software'], secondary: ['SQL', 'Kotlin', 'Go', 'C/C++', 'NodeJs', 'Git', 'Preact', 'Embedded devices'],
languages: ['Czech (native)', 'English (proficient)', 'German (elementary)'], languages: ['Czech (native)', 'English (proficient)', 'German (elementary)'],
//others: ['Driver\'s license (B)'] others: ['Driver\'s license (B)']
}, },
interests: ['Guitars', 'Heavy Metal', 'Mazda MX-5', 'DIY electronics', 'Linux', 'Open source'], interests: ['Guitars', 'Heavy Metal', 'Mazda MX-5', 'DIY electronics', 'Linux', 'Open source'],
}; };

View File

@ -7,7 +7,7 @@ export default function AboutBrief() {
const person = usePersonContext() const person = usePersonContext()
return ( return (
<Container className='about-brief' fluid> <Container className='about-brief' fluid>
<h1 className='person-name'>{person.name}</h1> <h1>{person.name}</h1>
<div className='brief'>{md(person.brief)}</div> <div className='brief'>{md(person.brief)}</div>
</Container> </Container>
) )

View File

@ -3,6 +3,6 @@ import Image from 'react-bootstrap/Image'
export default function Photo() { export default function Photo() {
return ( return (
<Image className="photo" alt='Photograph of the person' rounded fluid src='photo.png'></Image> <Image alt='Photograph of the person' rounded fluid src='photo.png'></Image>
) )
} }

View File

@ -6,7 +6,7 @@ import TagCloud from './TagCloud';
export default function Skills() { export default function Skills() {
const person = useContext(PersonContext) const person = useContext(PersonContext)
return ( return (
<Container className='skills' fluid> <Container className='skills'>
<h2>Skills</h2> <h2>Skills</h2>
<TagCloud title='Primary' icon='bookmark-star' style='primary' tags={person.skills.primary} /> <TagCloud title='Primary' icon='bookmark-star' style='primary' tags={person.skills.primary} />
{person.skills.secondary && ( {person.skills.secondary && (

View File

@ -12,9 +12,9 @@ export type Props = {
export default function TagCloud(props: Props) { export default function TagCloud(props: Props) {
const containerClasses = ['tag-cloud', 'cloud-' + (props.style || 'standard')] const containerClasses = ['tag-cloud', 'cloud-' + (props.style || 'standard')]
return ( return (
<Container className={containerClasses.join(' ')} fluid> <Container className={containerClasses.join(' ')}>
<h4>{props.icon && (<i className={'bi-' + props.icon}> </i>)}{props.title}</h4> <h4>{props.icon && (<i className={'bi-' + props.icon}> </i>)}{props.title}</h4>
<Container className='tag-badges' fluid> <Container className='tag-badges'>
{props.tags.map((tag: string) => (<Tag key={tag} text={tag} />) )} {props.tags.map((tag: string) => (<Tag key={tag} text={tag} />) )}
</Container> </Container>
</Container> </Container>

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import Container from 'react-bootstrap/Container'; import Container from 'react-bootstrap/Container';
import useInWidthRange from '../../hooks/InWidthRange'; import useSize from '../../hooks/Size';
import { JobListProps } from './types'; import { JobListProps } from './types';
import JobsAccordion from './JobsAccordion'; import JobsAccordion from './JobsAccordion';
import JobsCards, { JobsCardsPlaceholder } from './JobsCards'; import JobsCards, { JobsCardsPlaceholder } from './JobsCards';
@ -10,14 +10,19 @@ export type Props = {
heading: string, heading: string,
} & JobListProps } & JobListProps
export default function JobHistory(props: Props) { const defaultProps = {
const {SizeWrapper, inRange} = useInWidthRange(600) entriesPerRow: 2,
currentHeading: 'Currently',
}
const jobsList = inRange === undefined ? <JobsCardsPlaceholder /> : ( export default function JobHistory(props: Props) {
inRange ? <JobsAccordion {...props} /> : <JobsCards {...props} />) const {SizeWrapper, size} = useSize()
const jobsList = size.width === 0 ? <JobsCardsPlaceholder /> : (
size.width < 600 ? <JobsAccordion {...props} /> : <JobsCards {...props} />)
return ( return (
<Container fluid> <Container>
<h2>{props.heading}</h2> <h2>{props.heading}</h2>
<SizeWrapper> <SizeWrapper>
{jobsList} {jobsList}

View File

@ -8,19 +8,6 @@ body {
padding-top: 1rem; padding-top: 1rem;
padding-bottom: 0.2rem; padding-bottom: 0.2rem;
} }
.person-name {
margin-top: calc(min(6vw, 5rem));
font-weight: bold;
font-size: calc(1.375rem + min(1.4vw, 1rem));
}
.brief {
font-size: 120%;
}
.contacts {
margin-top: 2rem;
}
.footer { .footer {
margin-top: 2rem; margin-top: 2rem;
filter: opacity(75%) filter: opacity(75%)
@ -30,7 +17,7 @@ body {
filter: opacity(75%) filter: opacity(75%)
} }
.container > h2, .container-fluid > h2, h3, h4 { .container > h2, h3, h4 {
margin-top: 1em; margin-top: 1em;
} }
@ -68,7 +55,6 @@ body {
} }
.tag-badges > span { .tag-badges > span {
margin: 0.4em; margin: 0.4em;
color: #222;
} }
.contacts .contact { .contacts .contact {
margin: 0.75em; margin: 0.75em;
@ -79,14 +65,6 @@ body {
.accordion { .accordion {
margin: 1rem; margin: 1rem;
} }
.main-container .accordion-button:not(.collapsed) {
color: #222;
background-color: RGBA(217, 216, 216, 0.3);
}
.main-container .accordion-button.collapsed {
color: #0e489d;
background-color: RGBA(153, 165, 213, 0.3);
}
.multiline { .multiline {
white-space: pre-line; white-space: pre-line;
} }

View File

@ -1,28 +0,0 @@
import React, { ForwardedRef, ReactNode, forwardRef, useEffect, useLayoutEffect, useRef, useState } from "react";
export default function useInWidthRange(threshold: number) {
const areaRef = useRef<HTMLDivElement>(null)
const [inRange, setInRange] = useState<boolean | undefined>(undefined)
function SizeWrapper(props: {children: ReactNode }) {
return (
<div ref={areaRef}>{props.children}</div>
)
}
useEffect(() => {
function updateState() {
if (areaRef.current) {
const width = areaRef.current.offsetWidth
setInRange(width <= threshold)
}
}
updateState()
addEventListener("resize", updateState);
return () => {
removeEventListener("resize", updateState)
};
}, [areaRef, threshold, setInRange])
return {SizeWrapper, inRange}
}

30
src/app/hooks/Size.tsx Normal file
View File

@ -0,0 +1,30 @@
import React, { ForwardedRef, ReactNode, forwardRef, useEffect, useLayoutEffect, useRef, useState } from "react";
export default function useSize() {
const areaRef = useRef<HTMLDivElement>(null)
const [size, setSize] = useState({ width: 0, height: 0 })
function SizeWrapper(props: {children: ReactNode }) {
return (
<div ref={areaRef}>{props.children}</div>
)
}
useEffect(() => {
function updateSize() {
if (areaRef.current) {
setSize({
width: areaRef.current.offsetWidth,
height: areaRef.current.offsetHeight
})
}
}
updateSize()
addEventListener("resize", updateSize);
return () => {
removeEventListener("resize", updateSize)
};
}, [areaRef, setSize])
return {SizeWrapper, size}
}

View File

@ -16,9 +16,13 @@ export default function Home() {
<Container className='main-container' fluid='xxl'> <Container className='main-container' fluid='xxl'>
<Row> <Row>
<Col xs={3} sm={3} lg={2}><Photo /></Col> <Col xs={'auto'} sm={4} lg={2}><Photo /></Col>
<Col xs={9} sm={9} lg={5}><AboutBrief /></Col> <Col>
<Col xs={12} lg={5}><Contacts /></Col> <Row>
<Col xs={'auto'} lg={6}><AboutBrief /></Col>
<Col xs={'auto'} lg={6}><Contacts /></Col>
</Row>
</Col>
</Row> </Row>
<Row> <Row>
<Col xs={12} xl={7}> <Col xs={12} xl={7}>
@ -27,7 +31,7 @@ export default function Home() {
<Row><Education /></Row> <Row><Education /></Row>
</Col> </Col>
<Col> <Col>
<Skills /> <Row><Skills /></Row>
</Col> </Col>
</Row> </Row>
<Row><Footer /></Row> <Row><Footer /></Row>