Refactor and minor fixes
This commit is contained in:
parent
2dc9c45642
commit
fe75f9af88
31 changed files with 187 additions and 193 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
import { PageBackground } from '../page/background/background';
|
import { PageBackground } from '../page/background/background';
|
||||||
import { Image } from '../page/basics/image/image';
|
import { Image } from '../page/basics/image/image.html';
|
||||||
import { PageFooter } from '../page/footer/footer';
|
import { Footer } from '../page/footer/footer.html';
|
||||||
import { PageHeader } from '../page/header/header';
|
import { PageHeader } from '../page/header/header';
|
||||||
import { PageImageViewer } from '../page/image-viewer/image-viewer';
|
import { PageImageViewer } from '../page/image-viewer/image-viewer';
|
||||||
import { Main } from '../page/main/main';
|
import { Main } from '../page/main/main';
|
||||||
|
|
@ -28,7 +28,11 @@ export const create = (): Array<PageElement> => [
|
||||||
new PageBackground(1, 1),
|
new PageBackground(1, 1),
|
||||||
new PageHeader({
|
new PageHeader({
|
||||||
name: `András Schmelczer`,
|
name: `András Schmelczer`,
|
||||||
photo: new Image(meWebP, meJpeg, `a picture of me`, false),
|
photo: Image({
|
||||||
|
imageWebP: meWebP,
|
||||||
|
imageJpeg: meJpeg,
|
||||||
|
alt: `a picture of me`,
|
||||||
|
}),
|
||||||
about: [
|
about: [
|
||||||
`
|
`
|
||||||
I have always been fascinated by the engineering feats that surround us and pervade every aspect
|
I have always been fascinated by the engineering feats that surround us and pervade every aspect
|
||||||
|
|
@ -62,7 +66,7 @@ export const create = (): Array<PageElement> => [
|
||||||
ledsTimelineElement,
|
ledsTimelineElement,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new PageFooter({
|
Footer({
|
||||||
title: `Learn more`,
|
title: `Learn more`,
|
||||||
curriculaVitae: [{ name: `Curriculum vitae`, url: cvEnglish }],
|
curriculaVitae: [{ name: `Curriculum vitae`, url: cvEnglish }],
|
||||||
email: `andras@schmelczer.dev`,
|
email: `andras@schmelczer.dev`,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Image } from '../../page/basics/image/image';
|
import { Image } from '../../page/basics/image/image.html';
|
||||||
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
||||||
import colourJpeg from '../media/color.jpg?format=jpg';
|
import colourJpeg from '../media/color.jpg?format=jpg';
|
||||||
import colourWebP from '../media/color.jpg?format=webp';
|
import colourWebP from '../media/color.jpg?format=webp';
|
||||||
|
|
@ -6,7 +6,12 @@ import colourWebP from '../media/color.jpg?format=webp';
|
||||||
export const colorsTimelineElement: TimelineElementParameters = {
|
export const colorsTimelineElement: TimelineElementParameters = {
|
||||||
title: `Photo colour grader`,
|
title: `Photo colour grader`,
|
||||||
date: `2018 June`,
|
date: `2018 June`,
|
||||||
figure: new Image(colourWebP, colourJpeg, `a picture of the app`),
|
figure: Image({
|
||||||
|
imageWebP: colourWebP,
|
||||||
|
imageJpeg: colourJpeg,
|
||||||
|
alt: `a picture of the app`,
|
||||||
|
container: true,
|
||||||
|
}),
|
||||||
description: `An innovative (at least I thought so) colour grader web application.`,
|
description: `An innovative (at least I thought so) colour grader web application.`,
|
||||||
more: [
|
more: [
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Image } from '../../page/basics/image/image';
|
import { Image } from '../../page/basics/image/image.html';
|
||||||
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
||||||
import myNotesJpeg from '../media/my-notes.png?format=jpg';
|
import myNotesJpeg from '../media/my-notes.png?format=jpg';
|
||||||
import myNotesWebP from '../media/my-notes.png?format=webp';
|
import myNotesWebP from '../media/my-notes.png?format=webp';
|
||||||
|
|
@ -7,7 +7,12 @@ import { GitHub } from '../shared';
|
||||||
export const myNotesTimelineElement: TimelineElementParameters = {
|
export const myNotesTimelineElement: TimelineElementParameters = {
|
||||||
title: `My Notes`,
|
title: `My Notes`,
|
||||||
date: `2019 November`,
|
date: `2019 November`,
|
||||||
figure: new Image(myNotesWebP, myNotesJpeg, `two screenshots of the application`),
|
figure: Image({
|
||||||
|
imageWebP: myNotesWebP,
|
||||||
|
imageJpeg: myNotesJpeg,
|
||||||
|
alt: `two screenshots of the application`,
|
||||||
|
container: true,
|
||||||
|
}),
|
||||||
description: `A minimalist note organiser and editor powered by Markwon.`,
|
description: `A minimalist note organiser and editor powered by Markwon.`,
|
||||||
more: [
|
more: [
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Image } from '../../page/basics/image/image';
|
import { Image } from '../../page/basics/image/image.html';
|
||||||
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
||||||
import processSimulatorInputJpeg from '../media/process-simulator-input.jpg?format=jpg';
|
import processSimulatorInputJpeg from '../media/process-simulator-input.jpg?format=jpg';
|
||||||
import processSimulatorInputWebP from '../media/process-simulator-input.jpg?format=webp';
|
import processSimulatorInputWebP from '../media/process-simulator-input.jpg?format=webp';
|
||||||
|
|
@ -6,11 +6,12 @@ import processSimulatorInputWebP from '../media/process-simulator-input.jpg?form
|
||||||
export const nuclearEditorTimelineElement: TimelineElementParameters = {
|
export const nuclearEditorTimelineElement: TimelineElementParameters = {
|
||||||
title: `Graph editing application`,
|
title: `Graph editing application`,
|
||||||
date: `2018 October - November`,
|
date: `2018 October - November`,
|
||||||
figure: new Image(
|
figure: Image({
|
||||||
processSimulatorInputWebP,
|
imageWebP: processSimulatorInputWebP,
|
||||||
processSimulatorInputJpeg,
|
imageJpeg: processSimulatorInputJpeg,
|
||||||
`a picture of the simulator's UI`
|
alt: `a picture of the simulator's UI`,
|
||||||
),
|
container: true,
|
||||||
|
}),
|
||||||
description: `
|
description: `
|
||||||
An intuitive editor to create and edit input for the nuclear facility simulator.
|
An intuitive editor to create and edit input for the nuclear facility simulator.
|
||||||
`,
|
`,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Image } from '../../page/basics/image/image';
|
import { Image } from '../../page/basics/image/image.html';
|
||||||
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
||||||
import processSimulatorJpeg from '../media/process-simulator.jpg?format=jpg';
|
import processSimulatorJpeg from '../media/process-simulator.jpg?format=jpg';
|
||||||
import processSimulatorWebP from '../media/process-simulator.jpg?format=webp';
|
import processSimulatorWebP from '../media/process-simulator.jpg?format=webp';
|
||||||
|
|
@ -6,11 +6,12 @@ import processSimulatorWebP from '../media/process-simulator.jpg?format=webp';
|
||||||
export const nuclearTimelineElement: TimelineElementParameters = {
|
export const nuclearTimelineElement: TimelineElementParameters = {
|
||||||
title: `Simulating the cooling system of a nuclear facility`,
|
title: `Simulating the cooling system of a nuclear facility`,
|
||||||
date: `2018 October - November`,
|
date: `2018 October - November`,
|
||||||
figure: new Image(
|
figure: Image({
|
||||||
processSimulatorWebP,
|
imageWebP: processSimulatorWebP,
|
||||||
processSimulatorJpeg,
|
imageJpeg: processSimulatorJpeg,
|
||||||
`a screenshot of the simulator`
|
alt: `a screenshot of the simulator`,
|
||||||
),
|
container: true,
|
||||||
|
}),
|
||||||
description: `
|
description: `
|
||||||
The temperatures and flow velocities are dynamically calculated in a fluid-based
|
The temperatures and flow velocities are dynamically calculated in a fluid-based
|
||||||
cooling system based on a simple model.
|
cooling system based on a simple model.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Image } from '../../page/basics/image/image';
|
import { Image } from '../../page/basics/image/image.html';
|
||||||
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
||||||
import photosJpeg from '../media/photos.jpg?format=jpg';
|
import photosJpeg from '../media/photos.jpg?format=jpg';
|
||||||
import photosWebP from '../media/photos.jpg?format=webp';
|
import photosWebP from '../media/photos.jpg?format=webp';
|
||||||
|
|
@ -7,7 +7,12 @@ import { Open } from '../shared';
|
||||||
export const photosTimelineElement: TimelineElementParameters = {
|
export const photosTimelineElement: TimelineElementParameters = {
|
||||||
title: `Photos`,
|
title: `Photos`,
|
||||||
date: `2016 summer`,
|
date: `2016 summer`,
|
||||||
figure: new Image(photosWebP, photosJpeg, `a picture of the website`),
|
figure: Image({
|
||||||
|
imageWebP: photosWebP,
|
||||||
|
imageJpeg: photosJpeg,
|
||||||
|
alt: `a picture of the website`,
|
||||||
|
container: true,
|
||||||
|
}),
|
||||||
description: `A simple webpage where you can view my photos.`,
|
description: `A simple webpage where you can view my photos.`,
|
||||||
more: [
|
more: [
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Image } from '../../page/basics/image/image';
|
import { Image } from '../../page/basics/image/image.html';
|
||||||
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
import { TimelineElementParameters } from '../../page/timeline/timeline-element/timeline-element-parameters';
|
||||||
import towersJpeg from '../media/towers.png?format=jpg';
|
import towersJpeg from '../media/towers.png?format=jpg';
|
||||||
import towersWebP from '../media/towers.png?format=webp';
|
import towersWebP from '../media/towers.png?format=webp';
|
||||||
|
|
@ -7,7 +7,12 @@ import { GitHub, Open } from '../shared';
|
||||||
export const towersTimelineElement: TimelineElementParameters = {
|
export const towersTimelineElement: TimelineElementParameters = {
|
||||||
title: `Towers tracking app`,
|
title: `Towers tracking app`,
|
||||||
date: `2019 August - September`,
|
date: `2019 August - September`,
|
||||||
figure: new Image(towersWebP, towersJpeg, `a picture of the website`),
|
figure: Image({
|
||||||
|
imageWebP: towersWebP,
|
||||||
|
imageJpeg: towersJpeg,
|
||||||
|
alt: `a picture of the website`,
|
||||||
|
container: true,
|
||||||
|
}),
|
||||||
description: `An aesthetic representation of your previous and current goals/tasks.`,
|
description: `An aesthetic representation of your previous and current goals/tasks.`,
|
||||||
more: [
|
more: [
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import openIcon from '../../static/icons/open.svg';
|
||||||
import packageIcon from '../../static/icons/package.svg';
|
import packageIcon from '../../static/icons/package.svg';
|
||||||
import pythonIcon from '../../static/icons/python.svg';
|
import pythonIcon from '../../static/icons/python.svg';
|
||||||
import youtubeIcon from '../../static/icons/youtube.svg';
|
import youtubeIcon from '../../static/icons/youtube.svg';
|
||||||
import { ImageAnchorFactory } from '../page/basics/image-anchor/image-anchor';
|
import { ImageAnchorFactory } from '../page/basics/image-anchor/image-anchor.html';
|
||||||
|
|
||||||
export const GitHub = ImageAnchorFactory(githubIcon, 'Open on GitHub');
|
export const GitHub = ImageAnchorFactory(githubIcon, 'Open on GitHub');
|
||||||
export const NPM = ImageAnchorFactory(packageIcon, 'Open on npm');
|
export const NPM = ImageAnchorFactory(packageIcon, 'Open on npm');
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,14 @@
|
||||||
import { html } from '../../../types/html';
|
|
||||||
import { url } from '../../../types/url';
|
import { url } from '../../../types/url';
|
||||||
import './image-anchor.scss';
|
import './image-anchor.scss';
|
||||||
|
|
||||||
export const generate = ({
|
export const ImageAnchorFactory =
|
||||||
href,
|
(
|
||||||
svg,
|
svg: string,
|
||||||
title,
|
title: string,
|
||||||
shouldDownload,
|
{ shouldDownload = false }: { shouldDownload?: boolean } = {}
|
||||||
}: {
|
) =>
|
||||||
href: url;
|
(href: url) =>
|
||||||
svg: url;
|
`
|
||||||
title: string;
|
|
||||||
shouldDownload: boolean;
|
|
||||||
}): html => `
|
|
||||||
<a class="image-anchor"
|
<a class="image-anchor"
|
||||||
href="${href}"
|
href="${href}"
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import { url } from '../../../types/url';
|
|
||||||
import { generate } from './image-anchor.html';
|
|
||||||
|
|
||||||
export const ImageAnchorFactory =
|
|
||||||
(
|
|
||||||
svg: string,
|
|
||||||
title: string,
|
|
||||||
{ shouldDownload = false }: { shouldDownload?: boolean } = {}
|
|
||||||
) =>
|
|
||||||
(href: url) =>
|
|
||||||
generate({ href, svg, title, shouldDownload });
|
|
||||||
|
|
@ -3,18 +3,18 @@ import { html } from '../../../types/html';
|
||||||
import { ResponsiveImage } from '../../../types/responsive-image';
|
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||||
import './image.scss';
|
import './image.scss';
|
||||||
|
|
||||||
export const generate = ({
|
export const Image = ({
|
||||||
sizes,
|
|
||||||
imageWebP,
|
imageWebP,
|
||||||
imageJpeg,
|
imageJpeg,
|
||||||
alt,
|
alt,
|
||||||
container,
|
container = false,
|
||||||
|
isIgnoredByImageViewer = false,
|
||||||
}: {
|
}: {
|
||||||
sizes: string;
|
|
||||||
imageWebP: ResponsiveImage;
|
imageWebP: ResponsiveImage;
|
||||||
imageJpeg: ResponsiveImage;
|
imageJpeg: ResponsiveImage;
|
||||||
alt: string;
|
alt: string;
|
||||||
container: boolean;
|
container?: boolean;
|
||||||
|
isIgnoredByImageViewer?: boolean;
|
||||||
}): html => `
|
}): html => `
|
||||||
${
|
${
|
||||||
container
|
container
|
||||||
|
|
@ -23,33 +23,39 @@ export const generate = ({
|
||||||
}%">`
|
}%">`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
<picture loading="lazy">
|
<picture loading="lazy">
|
||||||
<source
|
<source
|
||||||
srcset="${imageWebP.srcSet}"
|
srcset="${imageWebP.srcSet}"
|
||||||
sizes="${sizes}"
|
sizes="${getSizes(imageWebP)}"
|
||||||
width="${imageWebP.width}"
|
width="${imageWebP.width}"
|
||||||
height="${imageWebP.height}"
|
height="${imageWebP.height}"
|
||||||
type="image/webp"
|
type="image/webp"
|
||||||
alt="${alt}"
|
alt="${alt}"
|
||||||
/>
|
/>
|
||||||
<source
|
<source
|
||||||
srcset="${imageJpeg.srcSet}"
|
srcset="${imageJpeg.srcSet}"
|
||||||
sizes="${sizes}"
|
sizes="${getSizes(imageJpeg)}"
|
||||||
width="${imageJpeg.width}"
|
width="${imageJpeg.width}"
|
||||||
height="${imageJpeg.height}"
|
height="${imageJpeg.height}"
|
||||||
type="image/jpeg"
|
type="image/jpeg"
|
||||||
alt="${alt}"
|
alt="${alt}"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
tabindex="0"
|
${isIgnoredByImageViewer ? 'image-viewer-ignore' : ''}
|
||||||
loading="lazy"
|
tabindex="0"
|
||||||
srcset="${imageJpeg.srcSet}"
|
loading="lazy"
|
||||||
sizes="${sizes}"
|
width="${imageJpeg.width}"
|
||||||
width="${imageJpeg.width}"
|
height="${imageJpeg.height}"
|
||||||
height="${imageJpeg.height}"
|
src="${last(imageJpeg.images)?.path}"
|
||||||
src="${last(imageJpeg.images)?.path}"
|
alt="${alt}"
|
||||||
alt="${alt}"
|
/>
|
||||||
/>
|
</picture>
|
||||||
</picture>
|
|
||||||
${container ? `</div>` : ''}
|
${container ? `</div>` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const IMAGE_SCREEN_RATIO = 0.8;
|
||||||
|
const getSizes = (image: ResponsiveImage): string =>
|
||||||
|
image.images
|
||||||
|
.slice(0, -1)
|
||||||
|
.map((d) => `(max-width: ${d.width / IMAGE_SCREEN_RATIO}px) ${d.width}px,`)
|
||||||
|
.join('\n') + `\n${last(image.images)!.width}px`;
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import { createElement } from '../../../helper/create-element';
|
|
||||||
import { last } from '../../../helper/last';
|
|
||||||
import { ResponsiveImage } from '../../../types/responsive-image';
|
|
||||||
import { PageElement } from '../../page-element';
|
|
||||||
import { generate } from './image.html';
|
|
||||||
|
|
||||||
export class Image extends PageElement {
|
|
||||||
private static readonly imageScreenRatio = 0.8;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
imageWebP: ResponsiveImage,
|
|
||||||
imageJpeg: ResponsiveImage,
|
|
||||||
alt: string,
|
|
||||||
container = true
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
createElement(
|
|
||||||
generate({
|
|
||||||
imageWebP,
|
|
||||||
imageJpeg,
|
|
||||||
alt,
|
|
||||||
container,
|
|
||||||
sizes: Image.getSizes(imageWebP),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static getSizes(image: ResponsiveImage): string {
|
|
||||||
return (
|
|
||||||
image.images
|
|
||||||
.slice(0, -1)
|
|
||||||
.map((d) => `(max-width: ${d.width / Image.imageScreenRatio}px) ${d.width}px,`)
|
|
||||||
.join('\n') + `\n${last(image.images)!.width}px`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,27 @@
|
||||||
import loading from '../../../../static/icons/loading.svg';
|
import loading from '../../../../static/icons/loading.svg';
|
||||||
import play from '../../../../static/icons/play-button.svg';
|
import play from '../../../../static/icons/play-button.svg';
|
||||||
import { html } from '../../../types/html';
|
import { html } from '../../../types/html';
|
||||||
|
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||||
|
import { Image } from '../../basics/image/image.html';
|
||||||
import './preview.scss';
|
import './preview.scss';
|
||||||
|
|
||||||
export const generate = ({ alt }: { alt: string }): html => `
|
export const generate = ({
|
||||||
|
alt,
|
||||||
|
posterWebP,
|
||||||
|
posterJpeg,
|
||||||
|
}: {
|
||||||
|
alt: string;
|
||||||
|
posterWebP: ResponsiveImage;
|
||||||
|
posterJpeg: ResponsiveImage;
|
||||||
|
}): html => `
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
<img image-viewer-ignore class="poster"/>
|
${Image({
|
||||||
|
imageWebP: posterWebP,
|
||||||
|
imageJpeg: posterJpeg,
|
||||||
|
alt,
|
||||||
|
container: true,
|
||||||
|
isIgnoredByImageViewer: true,
|
||||||
|
})}
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
<div class="loading">${loading}</div>
|
<div class="loading">${loading}</div>
|
||||||
<iframe title="${alt}" allowfullscreen loading="lazy"></iframe>
|
<iframe title="${alt}" allowfullscreen loading="lazy"></iframe>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { createElement } from '../../../helper/create-element';
|
import { createElement } from '../../../helper/create-element';
|
||||||
import { ResponsiveImage } from '../../../types/responsive-image';
|
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||||
import { PageElement } from '../../page-element';
|
import { PageElement } from '../../page-element';
|
||||||
import { Image } from '../image/image';
|
|
||||||
import { generate } from './preview.html';
|
import { generate } from './preview.html';
|
||||||
|
|
||||||
export class Preview extends PageElement {
|
export class Preview extends PageElement {
|
||||||
|
|
@ -11,9 +10,8 @@ export class Preview extends PageElement {
|
||||||
private readonly url: string,
|
private readonly url: string,
|
||||||
alt: string
|
alt: string
|
||||||
) {
|
) {
|
||||||
super(createElement(generate({ alt })));
|
super(createElement(generate({ posterWebP, posterJpeg, alt })));
|
||||||
this.url += '?portfolioView';
|
this.url += '?portfolioView';
|
||||||
this.attachElementByReplacing('.poster', new Image(posterWebP, posterJpeg, alt));
|
|
||||||
this.query('.start-button').addEventListener('click', this.loadContent.bind(this));
|
this.query('.start-button').addEventListener('click', this.loadContent.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
src/page/basics/video/video-parameters.ts
Normal file
10
src/page/basics/video/video-parameters.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||||
|
import { url } from '../../../types/url';
|
||||||
|
|
||||||
|
export interface VideoParameters {
|
||||||
|
mp4: url;
|
||||||
|
webm: url;
|
||||||
|
posterWebP: ResponsiveImage;
|
||||||
|
posterJpeg: ResponsiveImage;
|
||||||
|
invertButton?: boolean;
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,26 @@
|
||||||
import loading from '../../../../static/icons/loading.svg';
|
import loading from '../../../../static/icons/loading.svg';
|
||||||
import play from '../../../../static/icons/play-button.svg';
|
import play from '../../../../static/icons/play-button.svg';
|
||||||
import { html } from '../../../types/html';
|
import { html } from '../../../types/html';
|
||||||
import { VideoParameters } from './video';
|
import { Image } from '../../basics/image/image.html';
|
||||||
|
import { VideoParameters } from './video-parameters';
|
||||||
import './video.scss';
|
import './video.scss';
|
||||||
|
|
||||||
export const generate = ({
|
export const generate = ({
|
||||||
webm,
|
webm,
|
||||||
mp4,
|
mp4,
|
||||||
|
posterWebP,
|
||||||
posterJpeg,
|
posterJpeg,
|
||||||
invertButton,
|
invertButton,
|
||||||
}: VideoParameters): html => `
|
}: VideoParameters): html => `
|
||||||
<div class="figure-container video-container" style="padding-top:${
|
<div class="figure-container video-container" style="padding-top:${
|
||||||
(posterJpeg.height / posterJpeg.width) * 100
|
(posterJpeg.height / posterJpeg.width) * 100
|
||||||
}%">
|
}%">
|
||||||
<img image-viewer-ignore class="poster"/>
|
${Image({
|
||||||
|
imageWebP: posterWebP,
|
||||||
|
imageJpeg: posterJpeg,
|
||||||
|
alt: `thumbnail for the video`,
|
||||||
|
isIgnoredByImageViewer: true,
|
||||||
|
})}
|
||||||
<div class="loading">${loading}</div>
|
<div class="loading">${loading}</div>
|
||||||
<video playsinline preload="none">
|
<video playsinline preload="none">
|
||||||
<source src="${webm}" type="video/webm"/>
|
<source src="${webm}" type="video/webm"/>
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,13 @@
|
||||||
import { createElement } from '../../../helper/create-element';
|
import { createElement } from '../../../helper/create-element';
|
||||||
import { ResponsiveImage } from '../../../types/responsive-image';
|
|
||||||
import { url } from '../../../types/url';
|
|
||||||
import { PageElement } from '../../page-element';
|
import { PageElement } from '../../page-element';
|
||||||
import { Image } from '../image/image';
|
import { VideoParameters } from './video-parameters';
|
||||||
import { generate } from './video.html';
|
import { generate } from './video.html';
|
||||||
|
|
||||||
export interface VideoParameters {
|
|
||||||
mp4: url;
|
|
||||||
webm: url;
|
|
||||||
posterWebP: ResponsiveImage;
|
|
||||||
posterJpeg: ResponsiveImage;
|
|
||||||
invertButton?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Video extends PageElement {
|
export class Video extends PageElement {
|
||||||
private video: HTMLVideoElement;
|
private video: HTMLVideoElement;
|
||||||
|
|
||||||
public constructor(options: VideoParameters) {
|
public constructor(options: VideoParameters) {
|
||||||
super(createElement(generate(options)));
|
super(createElement(generate(options)));
|
||||||
this.attachElementByReplacing(
|
|
||||||
'.poster',
|
|
||||||
new Image(options.posterWebP, options.posterJpeg, `thumbnail for the video`, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.video = this.query('video') as HTMLVideoElement;
|
this.video = this.query('video') as HTMLVideoElement;
|
||||||
this.video.addEventListener('click', this.startVideo.bind(this));
|
this.video.addEventListener('click', this.startVideo.bind(this));
|
||||||
this.video.addEventListener('play', () =>
|
this.video.addEventListener('play', () =>
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,27 @@ import cvIcon from '../../../static/icons/cv.svg';
|
||||||
import emailIcon from '../../../static/icons/email.svg';
|
import emailIcon from '../../../static/icons/email.svg';
|
||||||
import linkedinIcon from '../../../static/icons/linkedin.svg';
|
import linkedinIcon from '../../../static/icons/linkedin.svg';
|
||||||
import { html } from '../../types/html';
|
import { html } from '../../types/html';
|
||||||
import { FooterParameters } from './footer';
|
import { url } from '../../types/url';
|
||||||
import './footer.scss';
|
import './footer.scss';
|
||||||
|
|
||||||
export const generate = ({
|
export const Footer = ({
|
||||||
title,
|
title,
|
||||||
email,
|
email,
|
||||||
curriculaVitae,
|
curriculaVitae,
|
||||||
linkedin,
|
linkedin,
|
||||||
lastEditText,
|
lastEditText,
|
||||||
lastEdit,
|
lastEdit,
|
||||||
}: FooterParameters): html => `
|
}: {
|
||||||
|
title: string;
|
||||||
|
email: url;
|
||||||
|
linkedin: url;
|
||||||
|
curriculaVitae: Array<{
|
||||||
|
name: string;
|
||||||
|
url: url;
|
||||||
|
}>;
|
||||||
|
lastEditText: string;
|
||||||
|
lastEdit: Date;
|
||||||
|
}): html => `
|
||||||
<footer id="footer">
|
<footer id="footer">
|
||||||
<h2>${title}</h2>
|
<h2>${title}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
@ -25,7 +35,7 @@ export const generate = ({
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
.join('\n')}
|
.join('')}
|
||||||
<li>
|
<li>
|
||||||
${linkedinIcon}
|
${linkedinIcon}
|
||||||
<a id="linkedin" target="_blank" href="${linkedin}">Find me on LinkedIn</a>
|
<a id="linkedin" target="_blank" href="${linkedin}">Find me on LinkedIn</a>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
@use '../../style/mixins' as *;
|
@use '../../style/mixins' as *;
|
||||||
|
|
||||||
#footer {
|
footer#footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
margin-top: var(--large-margin);
|
margin-top: var(--large-margin);
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@include link;
|
@include link;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import { createElement } from '../../helper/create-element';
|
|
||||||
import { url } from '../../types/url';
|
|
||||||
import { PageElement } from '../page-element';
|
|
||||||
import { generate } from './footer.html';
|
|
||||||
|
|
||||||
export interface FooterParameters {
|
|
||||||
title: string;
|
|
||||||
email: url;
|
|
||||||
linkedin: url;
|
|
||||||
curriculaVitae: Array<{
|
|
||||||
name: string;
|
|
||||||
url: url;
|
|
||||||
}>;
|
|
||||||
lastEditText: string;
|
|
||||||
lastEdit: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PageFooter extends PageElement {
|
|
||||||
constructor(footer: FooterParameters) {
|
|
||||||
super(createElement(generate(footer)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,14 +4,16 @@ import './header.scss';
|
||||||
export const generate = ({
|
export const generate = ({
|
||||||
name,
|
name,
|
||||||
about,
|
about,
|
||||||
|
photo,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
about: Array<string>;
|
about: Array<string>;
|
||||||
|
photo: html;
|
||||||
}): html => `
|
}): html => `
|
||||||
<section id="about">
|
<section id="about">
|
||||||
<div class="picture"></div>
|
${photo}
|
||||||
<div class="placeholder"></div>
|
<div class="placeholder"></div>
|
||||||
<h1>${name}</h1>
|
<h1>${name}</h1>
|
||||||
${about.map((t) => `<p>${t}</p>`).join('\n')}
|
${about.map((t) => `<p>${t}</p>`).join('')}
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { createElement } from '../../helper/create-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
import { Image } from '../basics/image/image';
|
import { html } from '../../types/html';
|
||||||
import { PageElement } from '../page-element';
|
import { PageElement } from '../page-element';
|
||||||
import { PageThemeSwitcher } from '../theme-switcher/theme-switcher';
|
|
||||||
import { generate } from './header.html';
|
import { generate } from './header.html';
|
||||||
|
import { PageThemeSwitcher } from './theme-switcher/theme-switcher';
|
||||||
|
|
||||||
export class PageHeader extends PageElement {
|
export class PageHeader extends PageElement {
|
||||||
public constructor({
|
public constructor({
|
||||||
|
|
@ -11,12 +11,10 @@ export class PageHeader extends PageElement {
|
||||||
about,
|
about,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
photo: Image;
|
photo: html;
|
||||||
about: Array<string>;
|
about: Array<string>;
|
||||||
}) {
|
}) {
|
||||||
super(createElement(generate({ name, about })));
|
super(createElement(generate({ name, about, photo })));
|
||||||
|
|
||||||
this.attachElementByReplacing('.picture', photo);
|
|
||||||
this.attachElement(new PageThemeSwitcher());
|
this.attachElement(new PageThemeSwitcher());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { html } from '../../types/html';
|
import { html } from '../../../types/html';
|
||||||
import './theme-switcher.scss';
|
import './theme-switcher.scss';
|
||||||
|
|
||||||
export const generate = (): html => `
|
export const generate = (): html => `
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@use '../../style/mixins' as *;
|
@use '../../../style/mixins' as *;
|
||||||
|
|
||||||
#theme-switcher {
|
#theme-switcher {
|
||||||
@include on-large-screen {
|
@include on-large-screen {
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import { createElement } from '../../helper/create-element';
|
import { createElement } from '../../../helper/create-element';
|
||||||
import { turnOffAnimations, turnOnAnimations } from '../../style/animations/animations';
|
import {
|
||||||
|
turnOffAnimations,
|
||||||
|
turnOnAnimations,
|
||||||
|
} from '../../../style/animations/animations';
|
||||||
import {
|
import {
|
||||||
isSystemLevelDarkModeEnabled,
|
isSystemLevelDarkModeEnabled,
|
||||||
turnOnDarkMode,
|
turnOnDarkMode,
|
||||||
turnOnLightMode,
|
turnOnLightMode,
|
||||||
} from '../../style/dark-mode/dark-mode';
|
} from '../../../style/dark-mode/dark-mode';
|
||||||
import { PageElement } from '../page-element';
|
import { PageElement } from '../../page-element';
|
||||||
import { generate } from './theme-switcher.html';
|
import { generate } from './theme-switcher.html';
|
||||||
|
|
||||||
export class PageThemeSwitcher extends PageElement {
|
export class PageThemeSwitcher extends PageElement {
|
||||||
|
|
@ -3,8 +3,11 @@ import { PageElement } from '../page-element';
|
||||||
import { generate } from './main.html';
|
import { generate } from './main.html';
|
||||||
|
|
||||||
export class Main extends PageElement {
|
export class Main extends PageElement {
|
||||||
constructor(...children: Array<PageElement>) {
|
constructor(...children: Array<PageElement | string>) {
|
||||||
super(createElement(generate()), children);
|
const actualChildren = children.map((c) =>
|
||||||
children.forEach((c) => this.attachElement(c));
|
c instanceof PageElement ? c : new PageElement(createElement(c))
|
||||||
|
);
|
||||||
|
super(createElement(generate()), actualChildren);
|
||||||
|
actualChildren.forEach((c) => this.attachElement(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export abstract class PageElement {
|
export class PageElement {
|
||||||
public constructor(
|
public constructor(
|
||||||
public readonly htmlRoot: HTMLElement,
|
public readonly htmlRoot: HTMLElement,
|
||||||
protected children: Array<PageElement> = []
|
protected children: Array<PageElement> = []
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { html } from '../../../types/html';
|
import { html } from '../../../types/html';
|
||||||
import { Image } from '../../basics/image/image';
|
|
||||||
import { Preview } from '../../basics/preview/preview';
|
import { Preview } from '../../basics/preview/preview';
|
||||||
import { Video } from '../../basics/video/video';
|
import { Video } from '../../basics/video/video';
|
||||||
|
|
||||||
export interface TimelineElementParameters {
|
export interface TimelineElementParameters {
|
||||||
date: string;
|
date: string;
|
||||||
figure: Image | Video | Preview;
|
figure: html | Video | Preview;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
more: Array<string>;
|
more: Array<string>;
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export const generate = (
|
||||||
more
|
more
|
||||||
? `
|
? `
|
||||||
<div class="more">
|
<div class="more">
|
||||||
${more.map((t) => `<p>${t}</p>`).join('\n')}
|
${more.map((t) => `<p>${t}</p>`).join('')}
|
||||||
</div>`
|
</div>`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
|
||||||
> a {
|
> a {
|
||||||
@include sub-title-font();
|
@include sub-title-font();
|
||||||
|
|
@ -131,7 +132,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description,
|
||||||
|
.info-button {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,12 @@ export class PageTimelineElement extends PageElement {
|
||||||
addEventListener('resize', this.handleResize.bind(this));
|
addEventListener('resize', this.handleResize.bind(this));
|
||||||
|
|
||||||
this.query('.info-button').addEventListener('click', this.toggleOpen.bind(this));
|
this.query('.info-button').addEventListener('click', this.toggleOpen.bind(this));
|
||||||
this.attachElementByReplacing('.figure', timelineElement.figure);
|
this.attachElementByReplacing(
|
||||||
|
'.figure',
|
||||||
|
timelineElement.figure instanceof PageElement
|
||||||
|
? timelineElement.figure
|
||||||
|
: new PageElement(createElement(timelineElement.figure))
|
||||||
|
);
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue