From 48a55a4a97e0e311782f00c7f370e677d7b52580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schmelczer=20Andr=C3=A1s?= Date: Mon, 6 Jan 2020 21:40:25 +0100 Subject: [PATCH] Refactor --- .idea/codeStyles/Project.xml | 39 ++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/dictionaries/andras.xml | 2 + .idea/workspace.xml | 67 ++- .prettierrc | 3 +- README.md | 3 + package.json | 3 +- src/framework/cache.ts | 0 src/framework/container-page.ts | 18 + src/framework/event-broadcaster.ts | 5 + src/framework/helper/create-element.ts | 1 + src/framework/helper/get-height.ts | 2 +- src/framework/helper/last.ts | 2 +- .../helper/{color-mixer.ts => mix-colors.ts} | 32 +- src/framework/helper/random.ts | 5 +- src/framework/helper/range.ts | 15 - src/framework/page-element.ts | 37 +- src/framework/page.ts | 28 -- .../primitives/implementations/anchor.ts | 13 + .../primitives/implementations/image.ts | 22 + .../primitives/implementations/text.ts | 10 + .../primitives/implementations/video.ts | 21 + src/framework/primitives/primitive.ts | 7 + src/framework/primitives/primitives.scss | 25 ++ src/model/content.ts | 24 -- src/model/portfolio.ts | 37 +- src/page/about/about.html.ts | 12 +- src/page/about/about.scss | 1 - src/page/about/about.ts | 10 +- src/page/background/animation.ts | 40 -- src/page/background/background.html.ts | 4 +- src/page/background/background.scss | 4 +- src/page/background/background.ts | 25 +- src/page/background/blob.ts | 2 +- src/page/background/vec2.ts | 17 - src/page/background/vec3.ts | 23 -- src/page/content/content.scss | 20 - src/page/content/content.ts | 50 +-- src/page/footer/footer.html.ts | 33 +- src/page/footer/footer.scss | 2 + src/page/footer/footer.ts | 3 +- src/page/image-viewer/image-viewer.ts | 11 +- src/page/index.ts | 43 +- .../timeline-element/timeline-element.html.ts | 15 +- .../timeline-element/timeline-element.scss | 3 +- .../timeline-element/timeline-element.ts | 9 +- src/page/timeline/timeline.ts | 20 +- src/portfolio.ts | 386 ++++++++++-------- src/style/vars.scss | 2 +- src/styles.scss | 2 + webpack.config.js | 18 +- 51 files changed, 604 insertions(+), 577 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 src/framework/cache.ts create mode 100644 src/framework/container-page.ts create mode 100644 src/framework/event-broadcaster.ts rename src/framework/helper/{color-mixer.ts => mix-colors.ts} (55%) delete mode 100644 src/framework/helper/range.ts delete mode 100644 src/framework/page.ts create mode 100644 src/framework/primitives/implementations/anchor.ts create mode 100644 src/framework/primitives/implementations/image.ts create mode 100644 src/framework/primitives/implementations/text.ts create mode 100644 src/framework/primitives/implementations/video.ts create mode 100644 src/framework/primitives/primitive.ts create mode 100644 src/framework/primitives/primitives.scss delete mode 100644 src/model/content.ts delete mode 100644 src/page/background/animation.ts delete mode 100644 src/page/background/vec2.ts delete mode 100644 src/page/background/vec3.ts diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..aa3f0aa --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,39 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dictionaries/andras.xml b/.idea/dictionaries/andras.xml index 6de328f..af0839e 100644 --- a/.idea/dictionaries/andras.xml +++ b/.idea/dictionaries/andras.xml @@ -5,6 +5,8 @@ deltatime forex schmelczer + vitaes + önéletrajz \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 2d5b9b8..828feec 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,29 +2,56 @@ - - - - - - - - + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - + + + + + + + diff --git a/.prettierrc b/.prettierrc index 65261d6..ea2afbe 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { "trailingComma": "es5", "tabWidth": 2, - "singleQuote": true + "singleQuote": true, + "endOfLine": "lf" } diff --git a/README.md b/README.md index 77ddaa2..6228e35 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # Timeline +[Click for live version](https://schmelczer.dev) + An easily configurable portfolio. + diff --git a/package.json b/package.json index d52eb66..e876267 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,6 @@ "typescript": "^3.7.3", "webpack": "^4.41.4", "webpack-cli": "^3.3.10", - "webpack-dev-server": "^3.10.1", - "workbox-webpack-plugin": "^4.3.1" + "webpack-dev-server": "^3.10.1" } } diff --git a/src/framework/cache.ts b/src/framework/cache.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/container-page.ts b/src/framework/container-page.ts new file mode 100644 index 0000000..fb1e1d5 --- /dev/null +++ b/src/framework/container-page.ts @@ -0,0 +1,18 @@ +import { PageElement } from './page-element'; +import { PageEventType } from './page-event'; + +export class ContainerPage extends PageElement { + public constructor(rootElement: HTMLElement, children: Array) { + children.forEach(e => rootElement.appendChild(e.element)); + super(rootElement, children); + } + + public setAsMain() { + this.broadcastEvent({ type: PageEventType.onLoad }, this); + + this.broadcastEvent( + { type: PageEventType.eventBroadcasterChanged, data: this }, + this + ); + } +} diff --git a/src/framework/event-broadcaster.ts b/src/framework/event-broadcaster.ts new file mode 100644 index 0000000..8a4633f --- /dev/null +++ b/src/framework/event-broadcaster.ts @@ -0,0 +1,5 @@ +import { PageEvent } from './page-event'; + +export interface EventBroadcaster { + broadcastEvent(event: PageEvent, parent?: EventBroadcaster); +} diff --git a/src/framework/helper/create-element.ts b/src/framework/helper/create-element.ts index a3d28dd..3337cea 100644 --- a/src/framework/helper/create-element.ts +++ b/src/framework/helper/create-element.ts @@ -1,6 +1,7 @@ import { html } from '../../model/misc'; export const createElement = (from: html): HTMLElement => { + // won't work for all elements, eg.: const element: HTMLElement = document.createElement('div'); element.innerHTML = from; return element.firstElementChild as HTMLElement; diff --git a/src/framework/helper/get-height.ts b/src/framework/helper/get-height.ts index aaaf76d..ddd4c90 100644 --- a/src/framework/helper/get-height.ts +++ b/src/framework/helper/get-height.ts @@ -1,7 +1,7 @@ export const getHeight = (e: HTMLElement): number => { + // ignores margin collapse const computedStyle = window.getComputedStyle(e); return ( - // ignores margin collapse e.clientHeight + parseInt(computedStyle.marginTop) + parseInt(computedStyle.marginBottom) + diff --git a/src/framework/helper/last.ts b/src/framework/helper/last.ts index a8b73be..fc4cdfb 100644 --- a/src/framework/helper/last.ts +++ b/src/framework/helper/last.ts @@ -1,2 +1,2 @@ export const last = (list: Array): T => - list.length > 0 ? list[list.length - 1] : undefined; + list.length >= 1 ? list[list.length - 1] : undefined; diff --git a/src/framework/helper/color-mixer.ts b/src/framework/helper/mix-colors.ts similarity index 55% rename from src/framework/helper/color-mixer.ts rename to src/framework/helper/mix-colors.ts index 0f73a2f..81ceb6d 100644 --- a/src/framework/helper/color-mixer.ts +++ b/src/framework/helper/mix-colors.ts @@ -1,25 +1,25 @@ -export const mixColors = ( - hexColorA: string, - hexColorB: string, - quantityA: number -): string => { - const colorA = hexToRGB(normalizeHex(hexColorA)); - const colorB = hexToRGB(normalizeHex(hexColorB)); +export type hex = string; +export type rgb = [number, number, number]; - const mixedColor: [number, number, number] = [ +export const mixColors = ( + hexColorA: hex, + hexColorB: hex, + quantityA: number +): hex => { + const colorA = hexToRGB(hexColorA); + const colorB = hexToRGB(hexColorB); + + const mixedColor: rgb = [ mix(colorA[0], colorB[0], quantityA), mix(colorA[1], colorB[1], quantityA), mix(colorA[2], colorB[2], quantityA), ]; - return RGBToHex(mixedColor); + return rgbToHex(mixedColor); }; -const hexToRGB = ([r1, r2, g1, g2, b1, b2]: string): [ - number, - number, - number -] => { +const hexToRGB = (hex: hex): rgb => { + const [r1, r2, g1, g2, b1, b2] = normalizeHex(hex); return [ Number.parseInt(r1 + r2, 16), Number.parseInt(g1 + g2, 16), @@ -27,7 +27,7 @@ const hexToRGB = ([r1, r2, g1, g2, b1, b2]: string): [ ]; }; -const normalizeHex = (hex: string): string => { +const normalizeHex = (hex: hex): hex => { hex = hex.trim(); if (hex.startsWith('#')) { hex = hex.substr(1); @@ -37,5 +37,5 @@ const normalizeHex = (hex: string): string => { const mix = (a: number, b: number, q: number): number => a * q + b * (1 - q); -const RGBToHex = (rgb: [number, number, number]): string => +const rgbToHex = (rgb: rgb): hex => '#' + rgb.map(n => Math.round(n).toString(16)).join(''); diff --git a/src/framework/helper/random.ts b/src/framework/helper/random.ts index e006521..68a2c05 100644 --- a/src/framework/helper/random.ts +++ b/src/framework/helper/random.ts @@ -2,16 +2,17 @@ export class Random { public constructor(private seed: number) {} public get next(): number { + // result is in [0, 1) return ( ((2 ** 31 - 1) & (this.seed = Math.imul(48271, this.seed))) / 2 ** 31 ); } public choose(list: Array): T { - return list[this.randomInInterval(0, list.length)]; + return list[Math.floor(this.randomInInterval(0, list.length))]; } public randomInInterval(aClosed: number, bOpen: number): number { - return Math.floor((bOpen - aClosed) * this.next) + aClosed; + return (bOpen - aClosed) * this.next + aClosed; } } diff --git a/src/framework/helper/range.ts b/src/framework/helper/range.ts deleted file mode 100644 index 09cb2c7..0000000 --- a/src/framework/helper/range.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const range = ({ - from = 0, - to = Infinity, - step = 1, -}: { - from?: number; - to?: number; - step?: number; -}): Iterable => { - return { - *[Symbol.iterator]() { - for (let i = from; i < to; yield i, i += step) {} - }, - }; -}; diff --git a/src/framework/page-element.ts b/src/framework/page-element.ts index c49656d..d06e3fa 100644 --- a/src/framework/page-element.ts +++ b/src/framework/page-element.ts @@ -1,33 +1,30 @@ -import { PageEvent, PageEventType } from "./page-event"; +import { PageEvent, PageEventType } from './page-event'; +import { EventBroadcaster } from './event-broadcaster'; -export abstract class PageElement { - private element: HTMLElement; +export abstract class PageElement implements EventBroadcaster { + protected eventBroadcaster: EventBroadcaster; - // Getter and setter accessors would have to agree in visibility - public getElement(): HTMLElement { - return this.element; + protected constructor( + private readonly rootElement: HTMLElement, + private readonly children: Array = [] + ) {} + + public get element(): HTMLElement { + return this.rootElement; } - protected setElement(value: HTMLElement) { - this.getElement()?.parentElement?.replaceChild(value, this.getElement()); - this.element = value; - } - - protected eventBroadcaster: PageElement; - - protected constructor(private children: Array = []) {} - - public broadcastEvent(event: PageEvent, parent: PageElement = null) { + public broadcastEvent(event: PageEvent, parent: EventBroadcaster = null) { if (event.type === PageEventType.eventBroadcasterChanged) { this.eventBroadcaster = event.data; } + this.handleEvent(event, parent); this.children.forEach(c => c.broadcastEvent(event, this)); } - protected query(query: string): HTMLElement | null { - return this.getElement()?.querySelector(query); - } + protected handleEvent(event: PageEvent, parent: EventBroadcaster) {} - protected handleEvent(event: PageEvent, parent: PageElement) {} + protected query(query: string): HTMLElement | null { + return this.element?.querySelector(query); + } } diff --git a/src/framework/page.ts b/src/framework/page.ts deleted file mode 100644 index 0ac18bb..0000000 --- a/src/framework/page.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PageElement } from "./page-element"; -import { PageEventType } from "./page-event"; - -export class Page extends PageElement { - public constructor( - children: Array, - private rootElement: HTMLElement, - isRootPage = false - ) { - super(children); - this.setElement(rootElement); - if (isRootPage) { - this.broadcastEvent( - { type: PageEventType.eventBroadcasterChanged, data: this }, - this - ); - } - - children - .map(e => e.getElement()) - .filter(e => e) - .forEach(e => rootElement.appendChild(e)); - - if (isRootPage) { - this.broadcastEvent({ type: PageEventType.onLoad }, this); - } - } -} diff --git a/src/framework/primitives/implementations/anchor.ts b/src/framework/primitives/implementations/anchor.ts new file mode 100644 index 0000000..30bc98e --- /dev/null +++ b/src/framework/primitives/implementations/anchor.ts @@ -0,0 +1,13 @@ +import { Primitive } from '../primitive'; +import { html, url } from '../../../model/misc'; + +export class Anchor implements Primitive { + public constructor( + private readonly href: url, + private readonly text: string + ) {} + + public toHTML(): html { + return `${this.text}`; + } +} diff --git a/src/framework/primitives/implementations/image.ts b/src/framework/primitives/implementations/image.ts new file mode 100644 index 0000000..57f1d87 --- /dev/null +++ b/src/framework/primitives/implementations/image.ts @@ -0,0 +1,22 @@ +import { Primitive } from '../primitive'; +import { html, ResponsiveImage } from '../../../model/misc'; +import { last } from '../../helper/last'; + +export class Image implements Primitive { + public constructor( + private readonly image: ResponsiveImage, + private readonly alt: string + ) {} + + public toHTML(disableInnerShadow = false): html { + return ` + ${!disableInnerShadow ? `
` : ''} + ${this.alt} + ${!disableInnerShadow ? `
` : ''} + `; + } +} diff --git a/src/framework/primitives/implementations/text.ts b/src/framework/primitives/implementations/text.ts new file mode 100644 index 0000000..4760247 --- /dev/null +++ b/src/framework/primitives/implementations/text.ts @@ -0,0 +1,10 @@ +import { Primitive } from '../primitive'; +import { html } from '../../../model/misc'; + +export class Text implements Primitive { + public constructor(private readonly text: string) {} + + public toHTML(): html { + return `

${this.text}

`; + } +} diff --git a/src/framework/primitives/implementations/video.ts b/src/framework/primitives/implementations/video.ts new file mode 100644 index 0000000..1ccdd60 --- /dev/null +++ b/src/framework/primitives/implementations/video.ts @@ -0,0 +1,21 @@ +import { Primitive } from '../primitive'; +import { url } from '../../../model/misc'; + +export class Video implements Primitive { + public constructor( + private readonly poster: url, + private readonly mp4: url, + private readonly webm: url, + private readonly options?: string + ) {} + public toHTML(disableInnerShadow = false): string { + return ` + ${!disableInnerShadow ? `
` : ''} + + ${!disableInnerShadow ? `
` : ''} + `; + } +} diff --git a/src/framework/primitives/primitive.ts b/src/framework/primitives/primitive.ts new file mode 100644 index 0000000..64fc16f --- /dev/null +++ b/src/framework/primitives/primitive.ts @@ -0,0 +1,7 @@ +import { html } from '../../model/misc'; + +import './primitives.scss'; + +export interface Primitive { + toHTML(): html; +} diff --git a/src/framework/primitives/primitives.scss b/src/framework/primitives/primitives.scss new file mode 100644 index 0000000..705bd52 --- /dev/null +++ b/src/framework/primitives/primitives.scss @@ -0,0 +1,25 @@ +@import '../../style/vars'; +@import '../../style/mixins'; + +.figure-container { + font-size: 0; + box-shadow: inset $shadow1, inset $shadow2; + pointer-events: none; + cursor: pointer; + + * { + pointer-events: all; + position: relative; + z-index: -2; + } +} + +.primitive-text, +.primitive-anchor, +.figure-container { + margin-top: $line-height; +} + +.primitive-text { + text-align: left; +} diff --git a/src/model/content.ts b/src/model/content.ts deleted file mode 100644 index a1fd3bb..0000000 --- a/src/model/content.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ResponsiveImage, url } from "./misc"; - -export type Anchor = { - type: "a"; - href: url; - text: string; -}; - -export type Video = { - type: "video"; - mp4: url; - webm: url; - options?: string; -}; - -export type Image = { - type: "img"; - alt: string; - image: ResponsiveImage; -}; - -export type TypedContent = Anchor | Video | Image; - -export type Content = Array; diff --git a/src/model/portfolio.ts b/src/model/portfolio.ts index 4705d83..7f02d24 100644 --- a/src/model/portfolio.ts +++ b/src/model/portfolio.ts @@ -1,29 +1,33 @@ -import { url } from "./misc"; -import { Anchor, Content, Image, Video } from "./content"; +import { url } from './misc'; +import { Primitive } from '../framework/primitives/primitive'; +import { Image } from '../framework/primitives/implementations/image'; +import { Anchor } from '../framework/primitives/implementations/anchor'; +import { Video } from '../framework/primitives/implementations/video'; +import { Text } from '../framework/primitives/implementations/text'; export interface Portfolio { - config: Config; header: Header; - timeline: Array; + timeline: Timeline; footer: Footer; } -export interface Config { - showMore: string; - showLess: string; -} - export interface Header { name: string; picture: Image; about: Content; } +export interface Timeline { + showMoreText: string; + showLessText: string; + elements: Array; +} + export interface TimelineElement { title: string; date: string; figure: Image | Video; - description: string; + description: Text; more?: Content; link?: Anchor; } @@ -31,8 +35,15 @@ export interface TimelineElement { export interface Footer { title: string; email: string; - cv: url; - cvName: string; - lastEditName: string; + curiumVitaes: Array; + lastEditText: string; lastEdit: Date; + gitHub: Anchor; } + +export interface CV { + name: string; + url: url; +} + +export type Content = Array; diff --git a/src/page/about/about.html.ts b/src/page/about/about.html.ts index fe212e7..8c8a4fc 100644 --- a/src/page/about/about.html.ts +++ b/src/page/about/about.html.ts @@ -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 => `
- ${PageContent.parseTypedContent(picture, true)} + ${picture.toHTML(true)}

${name}

-
`; + +`; diff --git a/src/page/about/about.scss b/src/page/about/about.scss index e49d989..778458d 100644 --- a/src/page/about/about.scss +++ b/src/page/about/about.scss @@ -16,7 +16,6 @@ img { @include square($img-size); border-radius: 100%; - cursor: pointer; } p { diff --git a/src/page/about/about.ts b/src/page/about/about.ts index 11d6a5f..5bb90bb 100644 --- a/src/page/about/about.ts +++ b/src/page/about/about.ts @@ -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)]); } } diff --git a/src/page/background/animation.ts b/src/page/background/animation.ts deleted file mode 100644 index 451e2cb..0000000 --- a/src/page/background/animation.ts +++ /dev/null @@ -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; - } -} diff --git a/src/page/background/background.html.ts b/src/page/background/background.html.ts index 5e1fc8d..533b9ed 100644 --- a/src/page/background/background.html.ts +++ b/src/page/background/background.html.ts @@ -1,5 +1,5 @@ -import { html } from "../../model/misc"; -import "./background.scss"; +import { html } from '../../model/misc'; +import './background.scss'; export const generate = (): html => `
diff --git a/src/page/background/background.scss b/src/page/background/background.scss index 0f57011..554c572 100644 --- a/src/page/background/background.scss +++ b/src/page/background/background.scss @@ -1,5 +1,5 @@ -@import "../../style/vars"; -@import "../../style/mixins"; +@import '../../style/vars'; +@import '../../style/mixins'; #background-container { position: fixed; diff --git a/src/page/background/background.ts b/src/page/background/background.ts index 49776a6..75ff8a5 100644 --- a/src/page/background/background.ts +++ b/src/page/background/background.ts @@ -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 = 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 { return Array.prototype.slice - .call(parent.getElement().children) - .filter(e => e !== this.getElement()); + .call(parent.element.children) + .filter(e => e !== this.element); } } diff --git a/src/page/background/blob.ts b/src/page/background/blob.ts index f4e1e8a..ada2eb7 100644 --- a/src/page/background/blob.ts +++ b/src/page/background/blob.ts @@ -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'; diff --git a/src/page/background/vec2.ts b/src/page/background/vec2.ts deleted file mode 100644 index a7be2ac..0000000 --- a/src/page/background/vec2.ts +++ /dev/null @@ -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); - } -} diff --git a/src/page/background/vec3.ts b/src/page/background/vec3.ts deleted file mode 100644 index 00af336..0000000 --- a/src/page/background/vec3.ts +++ /dev/null @@ -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); - } -} diff --git a/src/page/content/content.scss b/src/page/content/content.scss index f713ae1..90d64b6 100644 --- a/src/page/content/content.scss +++ b/src/page/content/content.scss @@ -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; - } } diff --git a/src/page/content/content.ts b/src/page/content/content.ts index be21302..9115b4e 100644 --- a/src/page/content/content.ts +++ b/src/page/content/content.ts @@ -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 ` ${element.text} `; - } - if (element.type === 'video') { - return ` - - `; - } - if (element.type === 'img') { - return ` - ${!disableInnerShadow ? `
` : ''} - ${element.alt} - ${!disableInnerShadow ? `
` : ''} - `; - } - - throw new Error('Unhandled type.'); - } - public constructor(content: Content) { - super(); - - this.setElement( + super( createElement(`
- ${content - .map(element => - PageContent.isTyped(element) - ? PageContent.parseTypedContent(element) - : `

${element}

` - ) - .join('\n')} + ${content.map(element => element.toHTML()).join('\n')}
`) ); diff --git a/src/page/footer/footer.html.ts b/src/page/footer/footer.html.ts index 2e8a768..1e5fb47 100644 --- a/src/page/footer/footer.html.ts +++ b/src/page/footer/footer.html.ts @@ -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 => `

${title}

`; diff --git a/src/page/footer/footer.scss b/src/page/footer/footer.scss index 1db12b9..e2dd0b0 100644 --- a/src/page/footer/footer.scss +++ b/src/page/footer/footer.scss @@ -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; } } diff --git a/src/page/footer/footer.ts b/src/page/footer/footer.ts index c13ba16..b4b3eb3 100644 --- a/src/page/footer/footer.ts +++ b/src/page/footer/footer.ts @@ -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))); } } diff --git a/src/page/image-viewer/image-viewer.ts b/src/page/image-viewer/image-viewer.ts index 937ec7c..67c1769 100644 --- a/src/page/image-viewer/image-viewer.ts +++ b/src/page/image-viewer/image-viewer.ts @@ -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); } } diff --git a/src/page/index.ts b/src/page/index.ts index 0a8a48a..4bfacf7 100644 --- a/src/page/index.ts +++ b/src/page/index.ts @@ -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(); }; diff --git a/src/page/timeline/timeline-element/timeline-element.html.ts b/src/page/timeline/timeline-element/timeline-element.html.ts index 6bbb223..875d768 100644 --- a/src/page/timeline/timeline-element/timeline-element.html.ts +++ b/src/page/timeline/timeline-element/timeline-element.html.ts @@ -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 = (

${title}

- ${PageContent.parseTypedContent(figure)} -

${description}

+ ${figure.toHTML()} + ${description.toHTML()} ${ more ? ` @@ -26,9 +25,9 @@ export const generate = ( ${showLess}
` - : "" + : '' } - ${link ? PageContent.parseTypedContent(link) : ""} + ${link ? link.toHTML() : ''}
`; diff --git a/src/page/timeline/timeline-element/timeline-element.scss b/src/page/timeline/timeline-element/timeline-element.scss index 36d9afa..1350f30 100644 --- a/src/page/timeline/timeline-element/timeline-element.scss +++ b/src/page/timeline/timeline-element/timeline-element.scss @@ -76,8 +76,9 @@ @include sub-title-font(); } - .description { + & > p { font-style: italic; + text-align: center; } .more { diff --git a/src/page/timeline/timeline-element/timeline-element.ts b/src/page/timeline/timeline-element/timeline-element.ts index 0cca8b4..e9f137a 100644 --- a/src/page/timeline/timeline-element/timeline-element.ts +++ b/src/page/timeline/timeline-element/timeline-element.ts @@ -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(); } diff --git a/src/page/timeline/timeline.ts b/src/page/timeline/timeline.ts index e688697..f7dfd54 100644 --- a/src/page/timeline/timeline.ts +++ b/src/page/timeline/timeline.ts @@ -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, - 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); } } diff --git a/src/portfolio.ts b/src/portfolio.ts index 42699c7..2807b06 100644 --- a/src/portfolio.ts +++ b/src/portfolio.ts @@ -1,6 +1,11 @@ import { Portfolio } from './model/portfolio'; +import { Text } from './framework/primitives/implementations/text'; +import { Image } from './framework/primitives/implementations/image'; +import { Video } from './framework/primitives/implementations/video'; +import { Anchor } from './framework/primitives/implementations/anchor'; import me from './static/media/me.jpg'; +import forexGIF from './static/media/forex.gif'; import forexMP4 from './static/media/forex.mp4'; import forexWEBM from './static/media/forex.webm'; import myNotes from './static/media/my-notes.jpg'; @@ -16,196 +21,227 @@ import ledMP4 from './static/media/led.mp4'; import ledWEBM from './static/media/led.webm'; export const portfolio: Portfolio = { - config: { - showMore: `Show details`, - showLess: `Show less`, - }, header: { name: `András Schmelczer`, - picture: { - type: `img`, - image: me, - alt: `a picture of me`, - }, + picture: new Image(me, `a picture of me`), about: [ - `I have always been fascinated by the engineering feats that surround us. - When I realized that someday I might be able to contribute to these achievements, - I knew that is what I need to aim for. As I am finishing my fifth semester at the - Budapest University of Technology and Economics, I feel I am getting closer to it every day.`, - `You can see some of the more interesting projects I have worked on below.`, + new Text( + `I have always been fascinated by the engineering feats that surround us. + When I realized that someday I might be able to contribute to these achievements, + I knew that is what I need to aim for. As I am starting my sixth semester at the + Budapest University of Technology and Economics, I feel I am getting closer to it every day.` + ), + new Text( + `You can see some of the more interesting projects I have worked on below.` + ), ], }, - timeline: [ - { - title: `Predicting foreign exchange rates`, - date: `2019 Autumn`, - figure: { - type: `video`, - options: `autoplay loop muted playsinline`, - webm: forexWEBM, - mp4: forexMP4, + timeline: { + showMoreText: `Show details`, + showLessText: `Show less`, + elements: [ + { + title: `Predicting foreign exchange rates`, + date: `2019 Autumn`, + figure: new Video( + forexGIF, + forexMP4, + forexWEBM, + `autoplay loop muted playsinline` + ), + description: new Text( + `From the animation we can see that my algorithm does a somewhat acceptable job at + predicting (blue graph) the EUR/USD rates (green graph).` + ), + more: [ + new Text( + `In a nutshell, the algorithm (written with Python - NumPy, SciPy, Flask), + extrapolates in the frequency domain. The steps are the following: smoothing the input values, + differentiating, applying a short-time Fourier-transformation with overlapped (and Hanning-windowed) windows, + extrapolating and then applying the inverse of these transformations to the extrapolated values.` + ), + new Text( + `Of course, there is still plenty of room for improvement, but even with this simple algorithm + a mostly profitable trading strategy is viable. In my free time I may put more work into it.` + ), + ], }, - description: `From the animation we can see that my algorithm does a somewhat acceptable job at - predicting (blue graph) the EUR/USD rates (green graph).`, - more: [ - `In a nutshell, the algorithm (written with Python - NumPy, SciPy, Flask), - extrapolates in the frequency domain. The steps are the following: smoothing the input values, - differentiating, applying a short-time Fourier-transformation with overlapped (and Hanning-windowed) windows, - extrapolating and then applying the inverse of these transformations to the extrapolated values.`, - `Of course, there is still plenty of room for improvement, but even with this simple algorithm - a mostly profitable trading strategy is viable. In my free time I may put more work into it.`, - ], - }, - { - date: `2019 November`, - title: `My Notes`, - figure: { - type: `img`, - image: myNotes, - alt: `two screenshots of the application`, + { + date: `2019 November`, + title: `My Notes`, + figure: new Image(myNotes, `two screenshots of the application`), + description: new Text( + `A minimalist note organizer and editor powered by Markwon.` + ), + more: [ + new Anchor( + `https://github.com/schmelczerandras/my-notes`, + `MyNotes on GitHub` + ), + new Text( + `A basic android app for creating and filtering notes written in markdown.` + ), + new Text( + `It was my homework for BME's Android and web development course. + It was also my first experience with Android development.` + ), + ], }, - description: `A minimalist note organizer and editor powered by Markwon.`, - more: [ - { - type: `a`, - href: `https://github.com/schmelczerandras/my-notes`, - text: `MyNotes on GitHub`, - }, - `A basic android app for creating and filtering notes written in markdown.`, - `It was my homework for BME's Android and web development course. - It was also my first experience with Android development.`, - ], - }, - { - date: `2018 October - November`, - title: `Simulating the cooling system of a nuclear facility`, - figure: { - type: `img`, - image: processSimulator, - alt: `a screenshot of the simulator`, + { + date: `2018 October - November`, + title: `Simulating the cooling system of a nuclear facility`, + figure: new Image(processSimulator, `a screenshot of the simulator`), + description: new Text( + `Dynamically calculating the temperatures and flow velocities + in a fluid based cooling system based on a simple model.` + ), + more: [ + new Text( + `A simulated system can contain reactors (heaters / coolers), pumps, heat exchangers, + drains sources, and of course, pipes.` + ), + new Text( + `The algorithm takes advantages of graphs and matrices to get to a next time frame.` + ), + new Text( + `Python is used for the backend along with Flask and NumPy. A REST API facilitates + the communication between the layers. For drawing the frontend HTML5 canvas is utilized.` + ), + ], }, - description: `Dynamically calculating the temperatures and flow velocities - in a fluid based cooling system based on a simple model.`, - more: [ - `A simulated system can contain reactors (heaters / coolers), pumps, heat exchangers, - drains sources, and of course, pipes.`, - `The algorithm takes advantages of graphs and matrices to get to a next time frame.`, - `Python is used for the backend along with Flask and NumPy. A REST API facilitates - the communication between the layers. For drawing the frontend HTML5 canvas is utilized.`, - ], - }, - { - date: `2018 October - November`, - title: `Graph editing application`, - figure: { - type: `img`, - image: processSimulatorInput, - alt: `a picture of the simulator's UI`, + { + date: `2018 October - November`, + title: `Graph editing application`, + figure: new Image( + processSimulatorInput, + `a picture of the simulator's UI` + ), + description: new Text( + `An intuitive editor to create and edit input files for the nuclear facility simulator.` + ), + more: [ + new Text( + `Nodes can be moved with drag&drop gestures. Editing the parameters of elements + can be done on the right panel.` + ), + new Text( + `The UI is built with JavaFX. The output can be exported as JSON or + directly uploaded to the simulation backend.` + ), + ], }, - description: `An intuitive editor to create and edit input files for the nuclear facility simulator.`, - more: [ - `Nodes can be moved with drag&drop gestures. Editing the parameters of elements - can be done on the right panel.`, - `The UI is built with JavaFX. The output can be exported as JSON or - directly uploaded to the simulation backend.`, - ], - }, - { - date: `2018 July - August`, - title: `City simulation`, - figure: { - type: `img`, - image: citySimulation, - alt: `a picture of a low-poly city`, + { + date: `2018 July - August`, + title: `City simulation`, + figure: new Image(citySimulation, `a picture of a low-poly city`), + description: new Text( + `Simulating a city where car crashes are more frequent than usual.` + ), + more: [ + new Text( + `Through a REST API the state of the traffic lights can be changed. + The drivers follow the instructions of the traffic lights, so if a mistake is made, + there will be collisions. There is also support for displaying tweets on a HUD.` + ), + new Text( + `This was created for a Cybersecurity challenge. With the help of this program + the contestants could instantly see the effect of their work.` + ), + new Text( + `The most interesting aspect of this project was building it in a server-client architecture. + The decisions of the agents is calculated server-side. The real challenge was broadcasting + these decisions in a fault-tolerant way using minimal bandwidth.` + ), + new Text( + `The program is made with Unity using C# as the scripting language. The models and animations + were also made by me using Blender.` + ), + ], }, - description: `Simulating a city where car crashes are more frequent than usual.`, - more: [ - `Through a REST API the state of the traffic lights can be changed. - The drivers follow the instructions of the traffic lights, so if a mistake is made, - there will be collisions. There is also support for displaying tweets on a HUD.`, - `This was created for a Cybersecurity challenge. With the help of this program - the contestants could instantly see the effect of their work.`, - `The most interesting aspect of this project was building it in a server-client architecture. - The decisions of the agents is calculated server-side. The real challenge was broadcasting - these decisions in a fault-tolerant way using minimal bandwidth.`, - `The program is made with Unity using C# as the scripting language. The models and animations - were also made by me using Blender.`, - ], - }, - { - date: `2018 June`, - title: `Photo color grader`, - figure: { - type: `img`, - image: color, - alt: `a picture of the app`, + { + date: `2018 June`, + title: `Photo color grader`, + figure: new Image(color, `a picture of the app`), + description: new Text( + `An innovative (at least I thought so) color grader web application.` + ), + more: [ + new Text( + `The most noteworthy feature of this application is the color selector UI. + This program is only intended as a proof-of-concept, I wanted to experiment with + some ideas and this was the outcome.` + ), + new Text( + `You can select some colors and then apply transformations to the other colors as a + function of their distance to the selected color.` + ), + new Text( + `By clicking on a colored circle you can change its settings. + New circles can be created by clicking in the large circle (and they can also be moved by drag & drop).` + ), + new Anchor('color', `schmelczer.dev/color`), + ], }, - description: `An innovative (at least I thought so) color grader web application.`, - more: [ - `The most noteworthy feature of this application is the color selector UI. - This program is only intended as a proof-of-concept, I wanted to experiment with - some ideas and this was the outcome. `, - `You can select some colors and then apply transformations to the other colors as a - function of their distance to the selected color.`, - `By clicking on a colored circle you can change its settings. - New circles can be created by clicking in the large circle (and they can also be moved by drag&drop).`, - { type: `a`, href: `color`, text: `schmelczer.dev/color` }, - ], - }, - { - date: `2017 autumn`, - - title: `Platform game`, - figure: { - type: `img`, - image: platform, - alt: `a picture of the app`, + { + date: `2017 autumn`, + title: `Platform game`, + figure: new Image(platform, `a picture of the app`), + description: new Text( + `A 3D game written in C with the help of SDL 1.2 (I haven't heard of GPU programming at the time).` + ), + more: [ + new Text( + `The maps are randomly generated and fully destroyable. + The player is getting chased by flying enemies. Overall, I find it a really enjoyable game.` + ), + new Text( + `I did this as a homework for my Basics of Programming course.` + ), + ], }, - description: `A 3D game written in C with the help of SDL 1.2 (I haven't heard of GPU programming at the time).`, - more: [ - `The maps are randomly generated and fully destroyable. - The player is getting chased by flying enemies. Overall, I find it a really enjoyable game.`, - `I did this as a homework for my Basics of Programming course.`, - ], - }, - { - date: `2016 summer`, - title: `Photos`, - figure: { - type: `img`, - image: photos, - alt: `a picture of the website`, + { + date: `2016 summer`, + title: `Photos`, + figure: new Image(photos, `a picture of the website`), + description: new Text( + `A simple web page where you can view my photos.` + ), + link: new Anchor(`photos`, `schmelczer.dev/photos`), }, - description: `A simple web page where you can view my photos.`, - link: { type: `a`, href: `photos`, text: `schmelczer.dev/photos` }, - }, - { - date: `2016 spring`, - title: `Lights synchronised to music`, - figure: { - type: `img`, - image: led, - alt: `a picture from the video`, + { + date: `2016 spring`, + title: `Lights synchronised to music`, + figure: new Video(led.src, ledMP4, ledWEBM, `controls`), + description: new Text( + `A full stack application with a built-in + music player which music controls the color of some RGB LED strips.` + ), + more: [ + new Text( + `This was my first non-trivial project which got finished. Obviously, + it is rather far from perfect, but I am still proud that I was able to build it on my own.` + ), + new Text( + `The backend logic is written in Python the FFT is provided by NumPy. + A quite simple frontend for accessing the music player and changing + the settings also got built using vanilla web development technologies.` + ), + ], }, - description: `A full stack application with a built-in - music player which music controls the color of some RGB LED strips.`, - more: [ - `This was my first non-trivial project which got finished. Obviously, - it is rather far from perfect, but I am still proud that I was able to build it on my own.`, - `The backend logic is written in Python the FFT is provided by NumPy. - A quite simple frontend for accessing the music player and changing - the settings also got built using vanilla web development technologies.`, - `Below is a video showing the system in work.`, - { type: `video`, mp4: ledMP4, webm: ledWEBM, options: 'controls' }, - ], - }, - ], + ], + }, footer: { title: `Learn more`, - cv, - email: `schmelczerandras@gmail.com`, - cvName: `Curriculum vitae`, - lastEditName: `Last modified on `, - lastEdit: new Date(2020, 0, 2), // months are 0 indexed + curiumVitaes: [ + { name: `Curriculum vitae (en)`, url: cv }, + { name: `Önéletrajz (hu)`, url: cv }, + ], + email: `andras@schmelczer.dev`, + lastEditText: `Last modified on `, + lastEdit: new Date(2020, 0, 6), // months are 0 indexed + gitHub: new Anchor( + `https://github.com/schmelczerandras/timeline`, + `Find this page on GitHub.` + ), }, }; diff --git a/src/style/vars.scss b/src/style/vars.scss index eb2f3eb..4f6d69f 100644 --- a/src/style/vars.scss +++ b/src/style/vars.scss @@ -1,6 +1,6 @@ @import 'fonts'; -$background: white; +$background: #ffffff; $normal-text-color: #31343f; $light-text-color: #7a7d8e; diff --git a/src/styles.scss b/src/styles.scss index ac04083..5cf2b4f 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,6 +1,7 @@ @import 'style/vars'; @import 'style/mixins'; @import 'style/a'; +@import 'framework/primitives/primitives'; * { margin: 0; @@ -27,6 +28,7 @@ html { body { @include main-font(); height: 100%; + //noinspection CssInvalidFunction padding: env(safe-area-inset-top, 20px) env(safe-area-inset-right, 20px) env(safe-area-inset-bottom, 20px) env(safe-area-inset-left, 20px); diff --git a/webpack.config.js b/webpack.config.js index 1ceacd9..ef757ef 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,7 @@ const TerserJSPlugin = require('terser-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin'); +const Sharp = require('responsive-loader/sharp'); const isProduction = process.env.NODE_ENV === 'production'; @@ -14,7 +15,7 @@ module.exports = { }, devServer: { host: '0.0.0.0', - // disableHostCheck: true + // disableHostCheck: true, }, optimization: { minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})], @@ -23,8 +24,15 @@ module.exports = { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ xhtml: true, - minify: true, template: './src/index.html', + minify: { + collapseWhitespace: true, + removeComments: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true, + }, inlineSource: '.(js|css)$', }), new HtmlWebpackInlineSourcePlugin(), @@ -42,14 +50,14 @@ module.exports = { test: /\.(jpe?g|png)$/i, loader: 'responsive-loader', options: { - adapter: require('responsive-loader/sharp'), + adapter: Sharp, outputPath: 'static/', - sizes: [300, 600, 1200, 2000], + sizes: [200, 400, 800, 1200, 2000], placeholder: false, }, }, { - test: /\.(webm|mp4)$/i, + test: /\.(webm|mp4|gif)$/i, use: [ { loader: 'file-loader',