Refactor
This commit is contained in:
parent
f054546aa6
commit
48a55a4a97
51 changed files with 604 additions and 577 deletions
|
|
@ -1,12 +1,12 @@
|
|||
import { Header } from "../../model/portfolio";
|
||||
import { html } from "../../model/misc";
|
||||
import { PageContent } from "../content/content";
|
||||
import { Header } from '../../model/portfolio';
|
||||
import { html } from '../../model/misc';
|
||||
|
||||
import "./about.scss";
|
||||
import './about.scss';
|
||||
|
||||
export const generate = ({ name, picture }: Header): html => `
|
||||
<section id="about">
|
||||
${PageContent.parseTypedContent(picture, true)}
|
||||
${picture.toHTML(true)}
|
||||
<div class="placeholder"></div>
|
||||
<h1>${name}</h1>
|
||||
</section>`;
|
||||
</section>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
img {
|
||||
@include square($img-size);
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,10 @@ import { PageElement } from '../../framework/page-element';
|
|||
|
||||
import { generate } from './about.html';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { ContainerPage } from '../../framework/container-page';
|
||||
|
||||
export class PageHeader extends PageElement {
|
||||
export class PageHeader extends ContainerPage {
|
||||
public constructor(header: Header) {
|
||||
const root = createElement(generate(header));
|
||||
const content = new PageContent(header.about);
|
||||
|
||||
super([content]);
|
||||
this.setElement(root);
|
||||
root.appendChild(content.getElement());
|
||||
super(createElement(generate(header)), [new PageContent(header.about)]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
import { Vec2 } from './vec2';
|
||||
|
||||
export class Animation {
|
||||
private _value: Vec2;
|
||||
private elapsedTime = 0;
|
||||
|
||||
public constructor(
|
||||
private from: Vec2,
|
||||
private to: Vec2,
|
||||
private intervalInMs: number,
|
||||
private onChange?: (currentValue: Vec2) => void
|
||||
) {}
|
||||
|
||||
public step(deltaTimeInMs: number) {
|
||||
if (this.elapsedTime === this.intervalInMs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.elapsedTime = Math.min(
|
||||
this.elapsedTime + deltaTimeInMs,
|
||||
this.intervalInMs
|
||||
);
|
||||
const q = this.elapsedTime / this.intervalInMs;
|
||||
|
||||
this._value = new Vec2(
|
||||
Animation.interpolate(this.from.x, this.to.x, q),
|
||||
Animation.interpolate(this.from.y, this.to.y, q)
|
||||
);
|
||||
|
||||
this.onChange?.call(null, this._value);
|
||||
}
|
||||
|
||||
private static interpolate(from: number, to: number, q: number): number {
|
||||
return from + q * (to - from);
|
||||
}
|
||||
|
||||
public get value(): Vec2 {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { html } from "../../model/misc";
|
||||
import "./background.scss";
|
||||
import { html } from '../../model/misc';
|
||||
import './background.scss';
|
||||
|
||||
export const generate = (): html => `
|
||||
<section id="background-container">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@import "../../style/vars";
|
||||
@import "../../style/mixins";
|
||||
@import '../../style/vars';
|
||||
@import '../../style/mixins';
|
||||
|
||||
#background-container {
|
||||
position: fixed;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ export class PageBackground extends PageElement {
|
|||
private readonly blobSpacing = 350;
|
||||
|
||||
public constructor(private start: PageElement, private end: PageElement) {
|
||||
super();
|
||||
this.setElement(createElement(generate()));
|
||||
super(createElement(generate()));
|
||||
Blob.initialize(10, 30, 5);
|
||||
}
|
||||
|
||||
|
|
@ -28,18 +27,16 @@ export class PageBackground extends PageElement {
|
|||
private bindListeners(parent: PageElement) {
|
||||
window.addEventListener('resize', () => this.resize(parent));
|
||||
window.addEventListener('load', () => this.resize(parent));
|
||||
parent
|
||||
.getElement()
|
||||
.addEventListener(
|
||||
'scroll',
|
||||
() => (this.getElement().scrollTop = parent.getElement().scrollTop)
|
||||
);
|
||||
parent.element.addEventListener(
|
||||
'scroll',
|
||||
() => (this.element.scrollTop = parent.element.scrollTop)
|
||||
);
|
||||
}
|
||||
|
||||
private resize(parent: PageElement, heightChange?: number) {
|
||||
const siblings: Array<HTMLElement> = this.getSiblings(parent);
|
||||
|
||||
const width = parent.getElement().clientWidth;
|
||||
const width = parent.element.clientWidth;
|
||||
let height = sum(siblings.map(getHeight));
|
||||
if (heightChange) {
|
||||
height += heightChange;
|
||||
|
|
@ -67,10 +64,10 @@ export class PageBackground extends PageElement {
|
|||
b.transform(
|
||||
random,
|
||||
width,
|
||||
parent.getElement().clientHeight,
|
||||
parent.element.clientHeight,
|
||||
height,
|
||||
getHeight(this.start.getElement()),
|
||||
getHeight(this.end.getElement())
|
||||
getHeight(this.start.element),
|
||||
getHeight(this.end.element)
|
||||
);
|
||||
b.show();
|
||||
}
|
||||
|
|
@ -79,7 +76,7 @@ export class PageBackground extends PageElement {
|
|||
|
||||
private getSiblings(parent: PageElement): Array<HTMLElement> {
|
||||
return Array.prototype.slice
|
||||
.call(parent.getElement().children)
|
||||
.filter(e => e !== this.getElement());
|
||||
.call(parent.element.children)
|
||||
.filter(e => e !== this.element);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { mixColors } from '../../framework/helper/color-mixer';
|
||||
import { mixColors } from '../../framework/helper/mix-colors';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { Random } from '../../framework/helper/random';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
export class Vec2 {
|
||||
public static readonly Zero = new Vec2(0, 0);
|
||||
|
||||
public constructor(public readonly x: number, public readonly y: number) {}
|
||||
|
||||
public add(other: Vec2): Vec2 {
|
||||
return new Vec2(this.x + other.x, this.y + other.y);
|
||||
}
|
||||
|
||||
public subtract(other: Vec2): Vec2 {
|
||||
return new Vec2(this.x - other.x, this.y - other.y);
|
||||
}
|
||||
|
||||
public multiply(other: Vec2): Vec2 {
|
||||
return new Vec2(this.x * other.x, this.y * other.y);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { Vec2 } from './vec2';
|
||||
|
||||
export class Vec3 {
|
||||
public static readonly Zero = new Vec3(0, 0, 0);
|
||||
|
||||
public static from(vec2: Vec2, z: number): Vec3 {
|
||||
return new Vec3(vec2.x, vec2.y, z);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
public readonly x: number,
|
||||
public readonly y: number,
|
||||
public readonly z: number
|
||||
) {}
|
||||
|
||||
public add(other: Vec3): Vec3 {
|
||||
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
|
||||
}
|
||||
|
||||
public multiply(other: Vec3): Vec3 {
|
||||
return new Vec3(this.x * other.x, this.y * other.y, this.z * other.z);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,24 +2,4 @@
|
|||
|
||||
.content {
|
||||
margin-top: $small-margin;
|
||||
|
||||
* {
|
||||
margin-top: $line-height;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.image-container {
|
||||
font-size: 0;
|
||||
box-shadow: inset $shadow1, inset $shadow2;
|
||||
pointer-events: none;
|
||||
cursor: pointer;
|
||||
img {
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
z-index: -2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,14 @@
|
|||
import { Content, TypedContent } from '../../model/content';
|
||||
import './content.scss';
|
||||
import { PageElement } from '../../framework/page-element';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { html } from '../../model/misc';
|
||||
import { last } from '../../framework/helper/last';
|
||||
import { Content } from '../../model/portfolio';
|
||||
|
||||
export class PageContent extends PageElement {
|
||||
private static isTyped(content): content is TypedContent {
|
||||
return (content as TypedContent).type !== undefined;
|
||||
}
|
||||
|
||||
public static parseTypedContent(
|
||||
element: TypedContent,
|
||||
disableInnerShadow?: boolean
|
||||
): html {
|
||||
if (element.type === 'a') {
|
||||
return `<a href="${element.href}" rel="noreferrer" target="_blank"> ${element.text} </a>`;
|
||||
}
|
||||
if (element.type === 'video') {
|
||||
return `
|
||||
<video ${element.options}>
|
||||
<source src="${element.webm}" type="video/webm"/>
|
||||
<source src="${element.mp4}" type="video/mp4"/>
|
||||
</video>
|
||||
`;
|
||||
}
|
||||
if (element.type === 'img') {
|
||||
return `
|
||||
${!disableInnerShadow ? `<div class="image-container">` : ''}
|
||||
<img
|
||||
srcset="${element.image.srcSet}"
|
||||
src="${last(element.image.images).path}"
|
||||
alt="${element.alt}"
|
||||
/>
|
||||
${!disableInnerShadow ? `</div>` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
throw new Error('Unhandled type.');
|
||||
}
|
||||
|
||||
public constructor(content: Content) {
|
||||
super();
|
||||
|
||||
this.setElement(
|
||||
super(
|
||||
createElement(`
|
||||
<div class="content">
|
||||
${content
|
||||
.map(element =>
|
||||
PageContent.isTyped(element)
|
||||
? PageContent.parseTypedContent(element)
|
||||
: `<p>${element}</p>`
|
||||
)
|
||||
.join('\n')}
|
||||
${content.map(element => element.toHTML()).join('\n')}
|
||||
</div>
|
||||
`)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,32 +1,37 @@
|
|||
import { Footer } from "../../model/portfolio";
|
||||
import { html } from "../../model/misc";
|
||||
import emailIcon from "../../static/icons/at.svg";
|
||||
import cvIcon from "../../static/icons/cv.svg";
|
||||
import { Footer } from '../../model/portfolio';
|
||||
import { html } from '../../model/misc';
|
||||
import emailIcon from '../../static/icons/at.svg';
|
||||
import cvIcon from '../../static/icons/cv.svg';
|
||||
|
||||
import "./footer.scss";
|
||||
import './footer.scss';
|
||||
|
||||
export const generate = ({
|
||||
title,
|
||||
email,
|
||||
cv,
|
||||
cvName,
|
||||
lastEditName,
|
||||
lastEdit
|
||||
curiumVitaes,
|
||||
gitHub,
|
||||
lastEditText,
|
||||
lastEdit,
|
||||
}: Footer): html => `
|
||||
<footer id="page-footer">
|
||||
<h2>${title}</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<img src="${cvIcon}" alt="CV" class="no-open" />
|
||||
<a id="cv" href="${cv}" target="_blank">${cvName}</a>
|
||||
</li>
|
||||
${curiumVitaes
|
||||
.map(
|
||||
cv =>
|
||||
`<li>
|
||||
<img src="${cvIcon}" alt="CV" class="no-open" />
|
||||
<a id="cv" href="${cv.url}" target="_blank">${cv.name}</a>
|
||||
</li>`
|
||||
)
|
||||
.join('\n')}
|
||||
<li>
|
||||
<img src="${emailIcon}" alt="email" class="no-open"/>
|
||||
<a id="email" href="mailto:${email}">${email}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<aside class="other">
|
||||
<h6>${lastEditName} ${lastEdit.toLocaleDateString()}</h6>
|
||||
<h6>${lastEditText} <time datetime="${lastEdit.toISOString()}">${lastEdit.toLocaleDateString()}</time></h6>
|
||||
</aside>
|
||||
</footer>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -38,11 +38,13 @@ footer#page-footer {
|
|||
|
||||
aside.other {
|
||||
@include center-children();
|
||||
flex-direction: column;
|
||||
margin: $large-margin auto $line-height auto;
|
||||
width: $body-width;
|
||||
|
||||
h6 {
|
||||
@include insignificant-font();
|
||||
display: inline;
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { createElement } from '../../framework/helper/create-element';
|
|||
|
||||
export class PageFooter extends PageElement {
|
||||
constructor(footer: Footer) {
|
||||
super();
|
||||
this.setElement(createElement(generate(footer)));
|
||||
super(createElement(generate(footer)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ import { createElement } from '../../framework/helper/create-element';
|
|||
|
||||
export class PageImageViewer extends PageElement {
|
||||
public constructor() {
|
||||
super();
|
||||
const root = createElement(generate());
|
||||
this.setElement(root);
|
||||
root.onclick = () => PageImageViewer.hide(root);
|
||||
super(root);
|
||||
}
|
||||
|
||||
protected handleEvent(event: PageEvent, parent: PageElement) {
|
||||
|
|
@ -20,12 +19,12 @@ export class PageImageViewer extends PageElement {
|
|||
document.body.addEventListener('keydown', this.handleKeydown.bind(this));
|
||||
|
||||
const images = Array.prototype.slice.call(
|
||||
parent.getElement().querySelectorAll('img')
|
||||
parent.element.querySelectorAll('img')
|
||||
);
|
||||
images
|
||||
.filter(
|
||||
(img: HTMLImageElement) =>
|
||||
img.parentElement !== this.getElement() &&
|
||||
img.parentElement !== this.element &&
|
||||
!img.classList.contains('no-open')
|
||||
)
|
||||
.forEach(
|
||||
|
|
@ -38,12 +37,12 @@ export class PageImageViewer extends PageElement {
|
|||
'#photo'
|
||||
) as HTMLImageElement).src = (event.target as HTMLImageElement).src;
|
||||
|
||||
PageImageViewer.show(this.getElement());
|
||||
PageImageViewer.show(this.element);
|
||||
}
|
||||
|
||||
private handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') {
|
||||
PageImageViewer.hide(this.getElement());
|
||||
PageImageViewer.hide(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +1,22 @@
|
|||
import { Portfolio } from "../model/portfolio";
|
||||
import { PageBackground } from "./background/background";
|
||||
import { PageHeader } from "./about/about";
|
||||
import { PageTimeline } from "./timeline/timeline";
|
||||
import { PageFooter } from "./footer/footer";
|
||||
import { PageImageViewer } from "./image-viewer/image-viewer";
|
||||
import { Page } from "../framework/page";
|
||||
import { Portfolio } from '../model/portfolio';
|
||||
import { PageBackground } from './background/background';
|
||||
import { PageHeader } from './about/about';
|
||||
import { PageTimeline } from './timeline/timeline';
|
||||
import { PageFooter } from './footer/footer';
|
||||
import { PageImageViewer } from './image-viewer/image-viewer';
|
||||
import { ContainerPage } from '../framework/container-page';
|
||||
|
||||
export const create = ({ config, header, timeline, footer }: Portfolio) => {
|
||||
export const create = ({ header, timeline, footer }: Portfolio) => {
|
||||
const pageHeader = new PageHeader(header);
|
||||
const pageFooter = new PageFooter(footer);
|
||||
|
||||
const bg = new PageBackground(pageHeader, pageFooter);
|
||||
new Page(
|
||||
[
|
||||
new PageImageViewer(),
|
||||
new Page(
|
||||
[
|
||||
pageHeader,
|
||||
new PageTimeline(timeline, config.showMore, config.showLess),
|
||||
pageFooter,
|
||||
bg
|
||||
],
|
||||
document.body.querySelector("main"),
|
||||
false
|
||||
)
|
||||
],
|
||||
document.body,
|
||||
true
|
||||
);
|
||||
new ContainerPage(document.body, [
|
||||
new PageImageViewer(),
|
||||
new ContainerPage(document.body.querySelector('main'), [
|
||||
pageHeader,
|
||||
new PageTimeline(timeline),
|
||||
pageFooter,
|
||||
new PageBackground(pageHeader, pageFooter),
|
||||
]),
|
||||
]).setAsMain();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { TimelineElement } from "../../../model/portfolio";
|
||||
import { html } from "../../../model/misc";
|
||||
import { PageContent } from "../../content/content";
|
||||
import { TimelineElement } from '../../../model/portfolio';
|
||||
import { html } from '../../../model/misc';
|
||||
|
||||
import "./timeline-element.scss";
|
||||
import './timeline-element.scss';
|
||||
|
||||
export const generate = (
|
||||
{ date, title, figure, description, more, link }: TimelineElement,
|
||||
|
|
@ -15,8 +14,8 @@ export const generate = (
|
|||
</div>
|
||||
<div class="card">
|
||||
<h2>${title}</h2>
|
||||
${PageContent.parseTypedContent(figure)}
|
||||
<p class="description">${description}</p>
|
||||
${figure.toHTML()}
|
||||
${description.toHTML()}
|
||||
${
|
||||
more
|
||||
? `
|
||||
|
|
@ -26,9 +25,9 @@ export const generate = (
|
|||
<a class="show-less">${showLess}</a>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
: ''
|
||||
}
|
||||
${link ? PageContent.parseTypedContent(link) : ""}
|
||||
${link ? link.toHTML() : ''}
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -76,8 +76,9 @@
|
|||
@include sub-title-font();
|
||||
}
|
||||
|
||||
.description {
|
||||
& > p {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.more {
|
||||
|
|
|
|||
|
|
@ -18,16 +18,15 @@ export class PageTimelineElement extends PageElement {
|
|||
|
||||
if (timelineElement.more) {
|
||||
const content = new PageContent(timelineElement.more);
|
||||
super([content]);
|
||||
super(root, [content]);
|
||||
this.isOpen = false;
|
||||
this.more = root.querySelector('.more');
|
||||
this.more.appendChild(content.getElement());
|
||||
this.more.appendChild(content.element);
|
||||
window.addEventListener('resize', this.handleResize.bind(this));
|
||||
root
|
||||
.querySelector('.buttons')
|
||||
.addEventListener('click', this.toggleOpen.bind(this));
|
||||
} else super();
|
||||
this.setElement(root);
|
||||
} else super(root);
|
||||
}
|
||||
|
||||
private toggleOpen() {
|
||||
|
|
@ -38,8 +37,8 @@ export class PageTimelineElement extends PageElement {
|
|||
PageTimelineElement.hide(showLess);
|
||||
this.closeMore();
|
||||
} else {
|
||||
PageTimelineElement.hide(showMore);
|
||||
PageTimelineElement.show(showLess);
|
||||
PageTimelineElement.hide(showMore);
|
||||
this.openMore();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,15 @@
|
|||
import { TimelineElement } from '../../model/portfolio';
|
||||
import { Timeline } from '../../model/portfolio';
|
||||
import { PageElement } from '../../framework/page-element';
|
||||
import { PageTimelineElement } from './timeline-element/timeline-element';
|
||||
import { generate } from './timeline.html';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { ContainerPage } from '../../framework/container-page';
|
||||
|
||||
export class PageTimeline extends PageElement {
|
||||
public constructor(
|
||||
timeline: Array<TimelineElement>,
|
||||
showMore: string,
|
||||
showLess: string
|
||||
) {
|
||||
const root = createElement(generate());
|
||||
const elements = timeline.map(
|
||||
e => new PageTimelineElement(e, showMore, showLess)
|
||||
export class PageTimeline extends ContainerPage {
|
||||
public constructor({ elements, showMoreText, showLessText }: Timeline) {
|
||||
super(
|
||||
createElement(generate()),
|
||||
elements.map(e => new PageTimelineElement(e, showMoreText, showLessText))
|
||||
);
|
||||
elements.map(e => e.getElement()).forEach(e => root.appendChild(e));
|
||||
super(elements);
|
||||
this.setElement(root);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue