Refactor and minor fixes
This commit is contained in:
parent
2dc9c45642
commit
fe75f9af88
31 changed files with 187 additions and 193 deletions
|
|
@ -1,18 +1,14 @@
|
|||
import { html } from '../../../types/html';
|
||||
import { url } from '../../../types/url';
|
||||
import './image-anchor.scss';
|
||||
|
||||
export const generate = ({
|
||||
href,
|
||||
svg,
|
||||
title,
|
||||
shouldDownload,
|
||||
}: {
|
||||
href: url;
|
||||
svg: url;
|
||||
title: string;
|
||||
shouldDownload: boolean;
|
||||
}): html => `
|
||||
export const ImageAnchorFactory =
|
||||
(
|
||||
svg: string,
|
||||
title: string,
|
||||
{ shouldDownload = false }: { shouldDownload?: boolean } = {}
|
||||
) =>
|
||||
(href: url) =>
|
||||
`
|
||||
<a class="image-anchor"
|
||||
href="${href}"
|
||||
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 './image.scss';
|
||||
|
||||
export const generate = ({
|
||||
sizes,
|
||||
export const Image = ({
|
||||
imageWebP,
|
||||
imageJpeg,
|
||||
alt,
|
||||
container,
|
||||
container = false,
|
||||
isIgnoredByImageViewer = false,
|
||||
}: {
|
||||
sizes: string;
|
||||
imageWebP: ResponsiveImage;
|
||||
imageJpeg: ResponsiveImage;
|
||||
alt: string;
|
||||
container: boolean;
|
||||
container?: boolean;
|
||||
isIgnoredByImageViewer?: boolean;
|
||||
}): html => `
|
||||
${
|
||||
container
|
||||
|
|
@ -23,33 +23,39 @@ export const generate = ({
|
|||
}%">`
|
||||
: ''
|
||||
}
|
||||
<picture loading="lazy">
|
||||
<source
|
||||
srcset="${imageWebP.srcSet}"
|
||||
sizes="${sizes}"
|
||||
width="${imageWebP.width}"
|
||||
height="${imageWebP.height}"
|
||||
type="image/webp"
|
||||
alt="${alt}"
|
||||
/>
|
||||
<source
|
||||
srcset="${imageJpeg.srcSet}"
|
||||
sizes="${sizes}"
|
||||
width="${imageJpeg.width}"
|
||||
height="${imageJpeg.height}"
|
||||
type="image/jpeg"
|
||||
alt="${alt}"
|
||||
/>
|
||||
<img
|
||||
tabindex="0"
|
||||
loading="lazy"
|
||||
srcset="${imageJpeg.srcSet}"
|
||||
sizes="${sizes}"
|
||||
width="${imageJpeg.width}"
|
||||
height="${imageJpeg.height}"
|
||||
src="${last(imageJpeg.images)?.path}"
|
||||
alt="${alt}"
|
||||
/>
|
||||
</picture>
|
||||
<picture loading="lazy">
|
||||
<source
|
||||
srcset="${imageWebP.srcSet}"
|
||||
sizes="${getSizes(imageWebP)}"
|
||||
width="${imageWebP.width}"
|
||||
height="${imageWebP.height}"
|
||||
type="image/webp"
|
||||
alt="${alt}"
|
||||
/>
|
||||
<source
|
||||
srcset="${imageJpeg.srcSet}"
|
||||
sizes="${getSizes(imageJpeg)}"
|
||||
width="${imageJpeg.width}"
|
||||
height="${imageJpeg.height}"
|
||||
type="image/jpeg"
|
||||
alt="${alt}"
|
||||
/>
|
||||
<img
|
||||
${isIgnoredByImageViewer ? 'image-viewer-ignore' : ''}
|
||||
tabindex="0"
|
||||
loading="lazy"
|
||||
width="${imageJpeg.width}"
|
||||
height="${imageJpeg.height}"
|
||||
src="${last(imageJpeg.images)?.path}"
|
||||
alt="${alt}"
|
||||
/>
|
||||
</picture>
|
||||
${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 play from '../../../../static/icons/play-button.svg';
|
||||
import { html } from '../../../types/html';
|
||||
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||
import { Image } from '../../basics/image/image.html';
|
||||
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">
|
||||
<img image-viewer-ignore class="poster"/>
|
||||
${Image({
|
||||
imageWebP: posterWebP,
|
||||
imageJpeg: posterJpeg,
|
||||
alt,
|
||||
container: true,
|
||||
isIgnoredByImageViewer: true,
|
||||
})}
|
||||
<div class="overlay">
|
||||
<div class="loading">${loading}</div>
|
||||
<iframe title="${alt}" allowfullscreen loading="lazy"></iframe>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { createElement } from '../../../helper/create-element';
|
||||
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||
import { PageElement } from '../../page-element';
|
||||
import { Image } from '../image/image';
|
||||
import { generate } from './preview.html';
|
||||
|
||||
export class Preview extends PageElement {
|
||||
|
|
@ -11,9 +10,8 @@ export class Preview extends PageElement {
|
|||
private readonly url: string,
|
||||
alt: string
|
||||
) {
|
||||
super(createElement(generate({ alt })));
|
||||
super(createElement(generate({ posterWebP, posterJpeg, alt })));
|
||||
this.url += '?portfolioView';
|
||||
this.attachElementByReplacing('.poster', new Image(posterWebP, posterJpeg, alt));
|
||||
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 play from '../../../../static/icons/play-button.svg';
|
||||
import { html } from '../../../types/html';
|
||||
import { VideoParameters } from './video';
|
||||
import { Image } from '../../basics/image/image.html';
|
||||
import { VideoParameters } from './video-parameters';
|
||||
import './video.scss';
|
||||
|
||||
export const generate = ({
|
||||
webm,
|
||||
mp4,
|
||||
posterWebP,
|
||||
posterJpeg,
|
||||
invertButton,
|
||||
}: VideoParameters): html => `
|
||||
<div class="figure-container video-container" style="padding-top:${
|
||||
(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>
|
||||
<video playsinline preload="none">
|
||||
<source src="${webm}" type="video/webm"/>
|
||||
|
|
|
|||
|
|
@ -1,28 +1,13 @@
|
|||
import { createElement } from '../../../helper/create-element';
|
||||
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||
import { url } from '../../../types/url';
|
||||
import { PageElement } from '../../page-element';
|
||||
import { Image } from '../image/image';
|
||||
import { VideoParameters } from './video-parameters';
|
||||
import { generate } from './video.html';
|
||||
|
||||
export interface VideoParameters {
|
||||
mp4: url;
|
||||
webm: url;
|
||||
posterWebP: ResponsiveImage;
|
||||
posterJpeg: ResponsiveImage;
|
||||
invertButton?: boolean;
|
||||
}
|
||||
|
||||
export class Video extends PageElement {
|
||||
private video: HTMLVideoElement;
|
||||
|
||||
public constructor(options: VideoParameters) {
|
||||
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.addEventListener('click', this.startVideo.bind(this));
|
||||
this.video.addEventListener('play', () =>
|
||||
|
|
|
|||
|
|
@ -2,17 +2,27 @@ import cvIcon from '../../../static/icons/cv.svg';
|
|||
import emailIcon from '../../../static/icons/email.svg';
|
||||
import linkedinIcon from '../../../static/icons/linkedin.svg';
|
||||
import { html } from '../../types/html';
|
||||
import { FooterParameters } from './footer';
|
||||
import { url } from '../../types/url';
|
||||
import './footer.scss';
|
||||
|
||||
export const generate = ({
|
||||
export const Footer = ({
|
||||
title,
|
||||
email,
|
||||
curriculaVitae,
|
||||
linkedin,
|
||||
lastEditText,
|
||||
lastEdit,
|
||||
}: FooterParameters): html => `
|
||||
}: {
|
||||
title: string;
|
||||
email: url;
|
||||
linkedin: url;
|
||||
curriculaVitae: Array<{
|
||||
name: string;
|
||||
url: url;
|
||||
}>;
|
||||
lastEditText: string;
|
||||
lastEdit: Date;
|
||||
}): html => `
|
||||
<footer id="footer">
|
||||
<h2>${title}</h2>
|
||||
<ul>
|
||||
|
|
@ -25,7 +35,7 @@ export const generate = ({
|
|||
</li>
|
||||
`
|
||||
)
|
||||
.join('\n')}
|
||||
.join('')}
|
||||
<li>
|
||||
${linkedinIcon}
|
||||
<a id="linkedin" target="_blank" href="${linkedin}">Find me on LinkedIn</a>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
@use '../../style/mixins' as *;
|
||||
|
||||
#footer {
|
||||
footer#footer {
|
||||
text-align: center;
|
||||
|
||||
margin-top: var(--large-margin);
|
||||
width: 100%;
|
||||
|
||||
a {
|
||||
@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 = ({
|
||||
name,
|
||||
about,
|
||||
photo,
|
||||
}: {
|
||||
name: string;
|
||||
about: Array<string>;
|
||||
photo: html;
|
||||
}): html => `
|
||||
<section id="about">
|
||||
<div class="picture"></div>
|
||||
${photo}
|
||||
<div class="placeholder"></div>
|
||||
<h1>${name}</h1>
|
||||
${about.map((t) => `<p>${t}</p>`).join('\n')}
|
||||
${about.map((t) => `<p>${t}</p>`).join('')}
|
||||
</section>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { createElement } from '../../helper/create-element';
|
||||
import { Image } from '../basics/image/image';
|
||||
import { html } from '../../types/html';
|
||||
import { PageElement } from '../page-element';
|
||||
import { PageThemeSwitcher } from '../theme-switcher/theme-switcher';
|
||||
import { generate } from './header.html';
|
||||
import { PageThemeSwitcher } from './theme-switcher/theme-switcher';
|
||||
|
||||
export class PageHeader extends PageElement {
|
||||
public constructor({
|
||||
|
|
@ -11,12 +11,10 @@ export class PageHeader extends PageElement {
|
|||
about,
|
||||
}: {
|
||||
name: string;
|
||||
photo: Image;
|
||||
photo: html;
|
||||
about: Array<string>;
|
||||
}) {
|
||||
super(createElement(generate({ name, about })));
|
||||
|
||||
this.attachElementByReplacing('.picture', photo);
|
||||
super(createElement(generate({ name, about, photo })));
|
||||
this.attachElement(new PageThemeSwitcher());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { html } from '../../types/html';
|
||||
import { html } from '../../../types/html';
|
||||
import './theme-switcher.scss';
|
||||
|
||||
export const generate = (): html => `
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@use '../../style/mixins' as *;
|
||||
@use '../../../style/mixins' as *;
|
||||
|
||||
#theme-switcher {
|
||||
@include on-large-screen {
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import { createElement } from '../../helper/create-element';
|
||||
import { turnOffAnimations, turnOnAnimations } from '../../style/animations/animations';
|
||||
import { createElement } from '../../../helper/create-element';
|
||||
import {
|
||||
turnOffAnimations,
|
||||
turnOnAnimations,
|
||||
} from '../../../style/animations/animations';
|
||||
import {
|
||||
isSystemLevelDarkModeEnabled,
|
||||
turnOnDarkMode,
|
||||
turnOnLightMode,
|
||||
} from '../../style/dark-mode/dark-mode';
|
||||
import { PageElement } from '../page-element';
|
||||
} from '../../../style/dark-mode/dark-mode';
|
||||
import { PageElement } from '../../page-element';
|
||||
import { generate } from './theme-switcher.html';
|
||||
|
||||
export class PageThemeSwitcher extends PageElement {
|
||||
|
|
@ -3,8 +3,11 @@ import { PageElement } from '../page-element';
|
|||
import { generate } from './main.html';
|
||||
|
||||
export class Main extends PageElement {
|
||||
constructor(...children: Array<PageElement>) {
|
||||
super(createElement(generate()), children);
|
||||
children.forEach((c) => this.attachElement(c));
|
||||
constructor(...children: Array<PageElement | string>) {
|
||||
const actualChildren = children.map((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 readonly htmlRoot: HTMLElement,
|
||||
protected children: Array<PageElement> = []
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { html } from '../../../types/html';
|
||||
import { Image } from '../../basics/image/image';
|
||||
import { Preview } from '../../basics/preview/preview';
|
||||
import { Video } from '../../basics/video/video';
|
||||
|
||||
export interface TimelineElementParameters {
|
||||
date: string;
|
||||
figure: Image | Video | Preview;
|
||||
figure: html | Video | Preview;
|
||||
title: string;
|
||||
description: string;
|
||||
more: Array<string>;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export const generate = (
|
|||
more
|
||||
? `
|
||||
<div class="more">
|
||||
${more.map((t) => `<p>${t}</p>`).join('\n')}
|
||||
${more.map((t) => `<p>${t}</p>`).join('')}
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@
|
|||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: -10px;
|
||||
|
||||
> a {
|
||||
@include sub-title-font();
|
||||
|
|
@ -131,7 +132,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
.description,
|
||||
.info-button {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,12 @@ export class PageTimelineElement extends PageElement {
|
|||
addEventListener('resize', this.handleResize.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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue