Remove "framework"
This commit is contained in:
parent
b45bdb18a0
commit
dc86d30eb2
72 changed files with 359 additions and 333 deletions
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"andras",
|
||||||
|
"schmelczer",
|
||||||
|
"webm"
|
||||||
|
]
|
||||||
|
}
|
||||||
25
custom.d.ts
vendored
25
custom.d.ts
vendored
|
|
@ -1,56 +1,45 @@
|
||||||
declare module '*.svg' {
|
declare module '*.svg' {
|
||||||
import { url } from 'src/framework/model/misc';
|
import { url } from 'src/types/url';
|
||||||
const content: url;
|
const content: url;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.png' {
|
declare module '*.png' {
|
||||||
import { ResponsiveImage } from 'src/framework/model/misc';
|
import { ResponsiveImage } from 'src/types/responsive-image';
|
||||||
const content: ResponsiveImage;
|
const content: ResponsiveImage;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.jpg' {
|
declare module '*.jpg' {
|
||||||
import { ResponsiveImage } from 'src/framework/model/misc';
|
import { ResponsiveImage } from 'src/types/responsive-image';
|
||||||
const content: ResponsiveImage;
|
const content: ResponsiveImage;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.jpeg' {
|
declare module '*.jpeg' {
|
||||||
import { ResponsiveImage } from 'src/framework/model/misc';
|
import { ResponsiveImage } from 'src/types/responsive-image';
|
||||||
const content: ResponsiveImage;
|
const content: ResponsiveImage;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.gif' {
|
|
||||||
import { url } from 'src/framework/model/misc';
|
|
||||||
const content: url;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '*.mp4' {
|
declare module '*.mp4' {
|
||||||
import { url } from 'src/framework/model/misc';
|
import { url } from 'src/types/url';
|
||||||
const content: url;
|
const content: url;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.webm' {
|
declare module '*.webm' {
|
||||||
import { url } from 'src/framework/model/misc';
|
import { url } from 'src/types/url';
|
||||||
const content: url;
|
const content: url;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.pdf' {
|
declare module '*.pdf' {
|
||||||
import { url } from 'src/framework/model/misc';
|
import { url } from 'src/types/url';
|
||||||
const content: url;
|
const content: url;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.txt' {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '*.html' {
|
declare module '*.html' {
|
||||||
const content: string;
|
const content: string;
|
||||||
export default content;
|
export default content;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Event } from '../event';
|
import { Event } from '../event';
|
||||||
import { EventHandler } from '../event-handler';
|
import { EventHandler } from '../event-handler';
|
||||||
import { PageElement } from '../../page-element';
|
|
||||||
import { OptionalEvent } from '../optional-event';
|
import { OptionalEvent } from '../optional-event';
|
||||||
|
import { PageElement } from '../../page/page-element';
|
||||||
|
|
||||||
export class OnLoadEvent implements Event {
|
export class OnLoadEvent implements Event {
|
||||||
public constructor(public parent: PageElement) {}
|
public constructor(public parent: PageElement) {}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Event } from './event';
|
import { Event } from './event';
|
||||||
|
|
||||||
export interface EventBroadcaster {
|
export interface EventBroadcaster {
|
||||||
broadcastEvent(event: Event);
|
broadcastEvent(event: Event): void;
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { PageElement } from './page-element';
|
|
||||||
import { OnEventBroadcasterChangedEvent } from './events/concrete-events/on-event-broadcaster-changed-event';
|
|
||||||
import { OnLoadEvent } from './events/concrete-events/on-load-event';
|
|
||||||
|
|
||||||
export class ContainerPage extends PageElement {
|
|
||||||
public constructor(rootElement: HTMLElement, children: Array<PageElement>) {
|
|
||||||
children.filter(e => e.element).forEach(e => rootElement.appendChild(e.element));
|
|
||||||
super(rootElement, children);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setAsMain() {
|
|
||||||
this.broadcastEvent(new OnEventBroadcasterChangedEvent(this));
|
|
||||||
this.broadcastEvent(new OnLoadEvent(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
@forward "styles/index";
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export const last = <T>(list: ArrayLike<T>): T =>
|
|
||||||
list.length >= 1 ? list[list.length - 1] : undefined;
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import { EventBroadcaster } from './events/event-broadcaster';
|
|
||||||
import { EventHandler } from './events/event-handler';
|
|
||||||
import { OnEventBroadcasterChangedEvent } from './events/concrete-events/on-event-broadcaster-changed-event';
|
|
||||||
import { Event } from './events/event';
|
|
||||||
import { OnLoadEvent } from './events/concrete-events/on-load-event';
|
|
||||||
import { OptionalEvent } from './events/optional-event';
|
|
||||||
|
|
||||||
export abstract class PageElement extends EventHandler implements EventBroadcaster {
|
|
||||||
protected eventBroadcaster: EventBroadcaster;
|
|
||||||
|
|
||||||
protected constructor(
|
|
||||||
public readonly element?: HTMLElement,
|
|
||||||
private readonly children: Array<PageElement> = []
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public broadcastEvent(event: Event) {
|
|
||||||
event = this.handle(event);
|
|
||||||
if (event) {
|
|
||||||
this.children.forEach(c => c.broadcastEvent(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public handleOnEventBroadcasterChangedEvent(
|
|
||||||
event: OnEventBroadcasterChangedEvent
|
|
||||||
): OptionalEvent {
|
|
||||||
this.eventBroadcaster = event.broadcaster;
|
|
||||||
return super.handleOnEventBroadcasterChangedEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public handleOnLoadEvent(_: OnLoadEvent): OptionalEvent {
|
|
||||||
return super.handleOnLoadEvent(new OnLoadEvent(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected query(query: string): HTMLElement | null {
|
|
||||||
return this.element?.querySelector(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
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 `
|
|
||||||
<a class="primitive-anchor"
|
|
||||||
href="${this.href}"
|
|
||||||
rel="noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
>${this.text}</a>
|
|
||||||
<br/>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { Primitive } from '../primitive';
|
|
||||||
import { html, ResponsiveImage } from '../../model/misc';
|
|
||||||
import { last } from '../../helper/last';
|
|
||||||
|
|
||||||
export class Image implements Primitive {
|
|
||||||
private static readonly IMAGE_SCREEN_RATIO = 0.8;
|
|
||||||
public constructor(
|
|
||||||
private readonly image: ResponsiveImage,
|
|
||||||
private readonly alt: string
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public toHTML(container = false): html {
|
|
||||||
return `
|
|
||||||
${container ? `<div class="figure-container">` : ''}
|
|
||||||
<img tabindex="0"
|
|
||||||
srcset="${this.image.srcSet}"
|
|
||||||
sizes="${this.getSizes()}"
|
|
||||||
src="${last(this.image.images)?.path}"
|
|
||||||
alt="${this.alt}"
|
|
||||||
/>
|
|
||||||
${container ? `</div>` : ''}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSizes(): string {
|
|
||||||
return (
|
|
||||||
this.image.images
|
|
||||||
.slice(0, -1)
|
|
||||||
.map(d => `(max-width: ${d.width / Image.IMAGE_SCREEN_RATIO}px) ${d.width}px,`)
|
|
||||||
.join('\n') + `\n${last(this.image.images).width}px`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import { Primitive } from '../primitive';
|
|
||||||
import { html } from '../../model/misc';
|
|
||||||
|
|
||||||
export class Text implements Primitive {
|
|
||||||
public constructor(private readonly text: string) {}
|
|
||||||
|
|
||||||
public toHTML(): html {
|
|
||||||
return `<p class="primitive-text">${this.text}</p>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
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(container = false): string {
|
|
||||||
return `
|
|
||||||
${container ? `<div class="figure-container">` : ''}
|
|
||||||
<video ${this.options} ${this.poster ? `poster="${this.poster}` : ''}" >
|
|
||||||
<source src="${this.webm}" type="video/webm"/>
|
|
||||||
<source src="${this.mp4}" type="video/mp4"/>
|
|
||||||
</video>
|
|
||||||
${container ? `</div>` : ''}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { html } from '../model/misc';
|
|
||||||
|
|
||||||
export interface Primitive {
|
|
||||||
toHTML(): html;
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
@forward 'animations/animations';
|
|
||||||
@forward 'dark-mode/dark-mode';
|
|
||||||
@forward 'wrapper';
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
@use 'dark-mode/dark-mode' as *;
|
|
||||||
|
|
||||||
$breakpoint-width: 925px !default;
|
|
||||||
|
|
||||||
@mixin on-small-screen() {
|
|
||||||
@media (max-width: $breakpoint-width - 1px) {
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin on-large-screen() {
|
|
||||||
@media (min-width: $breakpoint-width) {
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { html } from '../model/misc';
|
import { html } from '../types/html';
|
||||||
|
|
||||||
export const createElement = (from: html): HTMLElement => {
|
export const createElement = (from: html): HTMLElement => {
|
||||||
// won't work for all elements, eg.: <td>
|
// won't work for all elements, eg.: <td>
|
||||||
2
src/helper/last.ts
Normal file
2
src/helper/last.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const last = <T>(list: ArrayLike<T>): T | undefined =>
|
||||||
|
list.length >= 1 ? list[list.length - 1] : undefined;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export const addImul = () => {
|
export const polyfillImul = () => {
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
|
||||||
if (!Math.imul)
|
if (!Math.imul)
|
||||||
Math.imul = function(opA, opB) {
|
Math.imul = function(opA, opB) {
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { addImul } from './polyfills';
|
import { polyfillImul } from './polyfill-imul';
|
||||||
|
|
||||||
export class Random {
|
export class Random {
|
||||||
public constructor(private seed: number) {
|
public constructor(private seed: number) {
|
||||||
addImul();
|
polyfillImul();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get next(): number {
|
public get next(): number {
|
||||||
11
src/index.ts
11
src/index.ts
|
|
@ -2,16 +2,9 @@ import './static/no-change/favicon.ico';
|
||||||
import './static/no-change/og-image.jpg';
|
import './static/no-change/og-image.jpg';
|
||||||
import './static/no-change/robots.txt';
|
import './static/no-change/robots.txt';
|
||||||
import './static/no-change/404.html';
|
import './static/no-change/404.html';
|
||||||
|
|
||||||
import './styles.scss';
|
import './styles.scss';
|
||||||
import { create } from './portfolio';
|
import { create } from './portfolio';
|
||||||
|
|
||||||
const initialize = () => {
|
|
||||||
create();
|
|
||||||
addSupportForTabNavigation();
|
|
||||||
removeUnnecessaryOutlines();
|
|
||||||
};
|
|
||||||
|
|
||||||
const addSupportForTabNavigation = () =>
|
const addSupportForTabNavigation = () =>
|
||||||
(document.onkeydown = e => {
|
(document.onkeydown = e => {
|
||||||
if (e.key === ' ') {
|
if (e.key === ' ') {
|
||||||
|
|
@ -25,4 +18,6 @@ const removeUnnecessaryOutlines = () =>
|
||||||
(e.target as HTMLElement)?.blur();
|
(e.target as HTMLElement)?.blur();
|
||||||
});
|
});
|
||||||
|
|
||||||
initialize();
|
create();
|
||||||
|
addSupportForTabNavigation();
|
||||||
|
removeUnnecessaryOutlines();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { Header } from '../../model/portfolio';
|
import { Header } from '../../types/portfolio';
|
||||||
import { html } from '../../framework/model/misc';
|
|
||||||
|
|
||||||
import './about.scss';
|
import './about.scss';
|
||||||
|
import { html } from '../../types/html';
|
||||||
|
|
||||||
export const generate = ({ name, picture }: Header): html => `
|
export const generate = ({ name }: Header): html => `
|
||||||
<section id="about">
|
<section id="about">
|
||||||
${picture.toHTML()}
|
<div class="picture"></div>
|
||||||
<div class="placeholder"></div>
|
<div class="placeholder"></div>
|
||||||
<h1>${name}</h1>
|
<h1>${name}</h1>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { PageContent } from '../content/content';
|
import { PageContent } from '../content/content';
|
||||||
import { Header } from '../../model/portfolio';
|
import { Header } from '../../types/portfolio';
|
||||||
|
|
||||||
import { generate } from './about.html';
|
import { generate } from './about.html';
|
||||||
import { createElement } from '../../framework/helper/create-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
import { ContainerPage } from '../../framework/container-page';
|
|
||||||
import { PageThemeSwitcher } from '../theme-switcher/theme-switcher';
|
import { PageThemeSwitcher } from '../theme-switcher/theme-switcher';
|
||||||
|
import { PageElement } from '../page-element';
|
||||||
|
|
||||||
export class PageHeader extends ContainerPage {
|
export class PageHeader extends PageElement {
|
||||||
public constructor(header: Header) {
|
public constructor(header: Header) {
|
||||||
super(createElement(generate(header)), [
|
super(createElement(generate(header)));
|
||||||
new PageContent(header.about),
|
this.attachElementByReplacing('.picture', header.picture);
|
||||||
new PageThemeSwitcher(),
|
this.attachElement(new PageContent(header.about));
|
||||||
]);
|
this.attachElement(new PageThemeSwitcher());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { html } from '../../framework/model/misc';
|
|
||||||
|
|
||||||
import './background.scss';
|
import './background.scss';
|
||||||
|
import { html } from '../../types/html';
|
||||||
|
|
||||||
export const generate = (): html => `
|
export const generate = (): html => `
|
||||||
<canvas id="background"></canvas>
|
<canvas id="background"></canvas>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { PageElement } from '../../framework/page-element';
|
import { PageElement } from '../page-element';
|
||||||
import { Blob } from './blob';
|
import { Blob } from './blob';
|
||||||
import { generate } from './background.html';
|
import { generate } from './background.html';
|
||||||
import { Animation } from './animation';
|
import { Animation } from './animation';
|
||||||
import { Vec3 } from './vec3';
|
import { Vec3 } from './vec3';
|
||||||
import { Vec2 } from './vec2';
|
import { Vec2 } from './vec2';
|
||||||
import { createElement } from '../../framework/helper/create-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
import { sum } from '../../framework/helper/sum';
|
import { sum } from '../../helper/sum';
|
||||||
import { getHeight } from '../../framework/helper/get-height';
|
import { getHeight } from '../../helper/get-height';
|
||||||
import { OnLoadEvent } from '../../framework/events/concrete-events/on-load-event';
|
import { OnLoadEvent } from '../../events/concrete-events/on-load-event';
|
||||||
import { OnBodyDimensionsChangedEvent } from '../../framework/events/concrete-events/on-body-dimensions-changed-event';
|
import { OptionalEvent } from '../../events/optional-event';
|
||||||
import { OnPageThemeChangedEvent } from '../../framework/events/concrete-events/on-page-theme-changed-event';
|
import { OnBodyDimensionsChangedEvent } from '../../events/concrete-events/on-body-dimensions-changed-event';
|
||||||
import { OptionalEvent } from '../../framework/events/optional-event';
|
import { OnPageThemeChangedEvent } from '../../events/concrete-events/on-page-theme-changed-event';
|
||||||
|
|
||||||
export class PageBackground extends PageElement {
|
export class PageBackground extends PageElement {
|
||||||
public static readonly BLOB_SPACING = 325;
|
public static readonly BLOB_SPACING = 325;
|
||||||
|
|
@ -34,7 +34,7 @@ export class PageBackground extends PageElement {
|
||||||
private readonly end: PageElement
|
private readonly end: PageElement
|
||||||
) {
|
) {
|
||||||
super(createElement(generate()));
|
super(createElement(generate()));
|
||||||
this.canvas = this.element as HTMLCanvasElement;
|
this.canvas = this.htmlRoot as HTMLCanvasElement;
|
||||||
this.ctx = this.canvas.getContext('2d');
|
this.ctx = this.canvas.getContext('2d');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ export class PageBackground extends PageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private resizeBackground(heightChange?: number) {
|
private resizeBackground(heightChange?: number) {
|
||||||
const targetWidth = this.parent.element.clientWidth;
|
const targetWidth = this.parent.htmlRoot.clientWidth;
|
||||||
|
|
||||||
const siblings: Array<HTMLElement> = this.getSiblings();
|
const siblings: Array<HTMLElement> = this.getSiblings();
|
||||||
let targetHeight = sum(siblings.map(getHeight));
|
let targetHeight = sum(siblings.map(getHeight));
|
||||||
|
|
@ -116,11 +116,10 @@ export class PageBackground extends PageElement {
|
||||||
offset *
|
offset *
|
||||||
q
|
q
|
||||||
);
|
);
|
||||||
const topOffset = variableOffset(getHeight(this.start.element), 1);
|
const topOffset = variableOffset(getHeight(this.start.htmlRoot), 1);
|
||||||
const topLeft = this.convertFrom2Dto3D(new Vec2(0, topOffset), blob.z);
|
const topLeft = this.convertFrom2Dto3D(new Vec2(0, topOffset), blob.z);
|
||||||
|
|
||||||
const bottomOffset = variableOffset(getHeight(this.end.element), 0.2);
|
const bottomOffset = variableOffset(getHeight(this.end.htmlRoot), 0.2);
|
||||||
|
|
||||||
const bottomRight = this.convertFrom2Dto3D(
|
const bottomRight = this.convertFrom2Dto3D(
|
||||||
new Vec2(this.canvas.width, this.canvas.height - bottomOffset),
|
new Vec2(this.canvas.width, this.canvas.height - bottomOffset),
|
||||||
blob.z,
|
blob.z,
|
||||||
|
|
@ -134,7 +133,7 @@ export class PageBackground extends PageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSiblings(): Array<HTMLElement> {
|
private getSiblings(): Array<HTMLElement> {
|
||||||
return [this.start, ...this.inBetween, this.end].map(e => e.element);
|
return [this.start, ...this.inBetween, this.end].map(e => e.htmlRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private redraw(timestamp: DOMHighResTimeStamp) {
|
private redraw(timestamp: DOMHighResTimeStamp) {
|
||||||
|
|
@ -144,7 +143,7 @@ export class PageBackground extends PageElement {
|
||||||
this.backgroundSize.step(deltaTime);
|
this.backgroundSize.step(deltaTime);
|
||||||
this.blobs.forEach(b => b.step(deltaTime));
|
this.blobs.forEach(b => b.step(deltaTime));
|
||||||
|
|
||||||
this.scrollPosition = this.parent.element.scrollTop;
|
this.scrollPosition = this.parent.htmlRoot.scrollTop;
|
||||||
|
|
||||||
this.blobs.sort((b1, b2) => b2.z - b1.z);
|
this.blobs.sort((b1, b2) => b2.z - b1.z);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Vec2 } from './vec2';
|
import { Vec2 } from './vec2';
|
||||||
import { Vec3 } from './vec3';
|
import { Vec3 } from './vec3';
|
||||||
import { mixColors } from '../../framework/helper/mix-colors';
|
import { mixColors } from '../../helper/mix-colors';
|
||||||
import { Random } from '../../framework/helper/random';
|
import { Random } from '../../helper/random';
|
||||||
import { Animation } from './animation';
|
import { Animation } from './animation';
|
||||||
import { PageBackground } from './background';
|
import { PageBackground } from './background';
|
||||||
|
|
||||||
|
|
|
||||||
10
src/page/basics/anchor/anchor.html.ts
Normal file
10
src/page/basics/anchor/anchor.html.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import './anchor.scss';
|
||||||
|
import { html } from '../../../types/html';
|
||||||
|
|
||||||
|
export const generate = ({ href, text }: { href: string; text: string }): html => `
|
||||||
|
<a class="primitive-anchor"
|
||||||
|
href="${href}"
|
||||||
|
target="_blank"
|
||||||
|
>${text}</a>
|
||||||
|
<br/>
|
||||||
|
`;
|
||||||
3
src/page/basics/anchor/anchor.scss
Normal file
3
src/page/basics/anchor/anchor.scss
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.primitive-anchor {
|
||||||
|
margin-top: var(--line-height);
|
||||||
|
}
|
||||||
10
src/page/basics/anchor/anchor.ts
Normal file
10
src/page/basics/anchor/anchor.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { PageElement } from '../../page-element';
|
||||||
|
import { createElement } from '../../../helper/create-element';
|
||||||
|
import { generate } from './anchor.html';
|
||||||
|
import { url } from '../../../types/url';
|
||||||
|
|
||||||
|
export class Anchor extends PageElement {
|
||||||
|
public constructor(href: url, text: string) {
|
||||||
|
super(createElement(generate({ href, text })));
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/page/basics/image/image.html.ts
Normal file
25
src/page/basics/image/image.html.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import './image.scss';
|
||||||
|
import { last } from '../../../helper/last';
|
||||||
|
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||||
|
import { html } from '../../../types/html';
|
||||||
|
|
||||||
|
export const generate = ({
|
||||||
|
sizes,
|
||||||
|
image,
|
||||||
|
alt,
|
||||||
|
container,
|
||||||
|
}: {
|
||||||
|
sizes: string;
|
||||||
|
image: ResponsiveImage;
|
||||||
|
alt: string;
|
||||||
|
container: boolean;
|
||||||
|
}): html => `
|
||||||
|
${container ? `<div class="figure-container">` : ''}
|
||||||
|
<img tabindex="0"
|
||||||
|
srcset="${image.srcSet}"
|
||||||
|
sizes="${sizes}"
|
||||||
|
src="${last(image.images)?.path}"
|
||||||
|
alt="${alt}"
|
||||||
|
/>
|
||||||
|
${container ? `</div>` : ''}
|
||||||
|
`;
|
||||||
0
src/page/basics/image/image.scss
Normal file
0
src/page/basics/image/image.scss
Normal file
24
src/page/basics/image/image.ts
Normal file
24
src/page/basics/image/image.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { PageElement } from '../../page-element';
|
||||||
|
import { createElement } from '../../../helper/create-element';
|
||||||
|
import { generate } from './image.html';
|
||||||
|
import { last } from '../../../helper/last';
|
||||||
|
import { ResponsiveImage } from '../../../types/responsive-image';
|
||||||
|
|
||||||
|
export class Image extends PageElement {
|
||||||
|
private static readonly imageScreenRatio = 0.8;
|
||||||
|
|
||||||
|
public constructor(image: ResponsiveImage, alt: string, container = true) {
|
||||||
|
super(
|
||||||
|
createElement(generate({ image, alt, container, sizes: Image.getSizes(image) }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/page/basics/text/text.html.ts
Normal file
6
src/page/basics/text/text.html.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import './text.scss';
|
||||||
|
import { html } from '../../../types/html';
|
||||||
|
|
||||||
|
export const generate = (text: string): html => `
|
||||||
|
<p class="primitive-text">${text}</p>
|
||||||
|
`;
|
||||||
4
src/page/basics/text/text.scss
Normal file
4
src/page/basics/text/text.scss
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
.primitive-text {
|
||||||
|
text-align: left;
|
||||||
|
margin-top: var(--line-height);
|
||||||
|
}
|
||||||
9
src/page/basics/text/text.ts
Normal file
9
src/page/basics/text/text.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { PageElement } from '../../page-element';
|
||||||
|
import { createElement } from '../../../helper/create-element';
|
||||||
|
import { generate } from './text.html';
|
||||||
|
|
||||||
|
export class Text extends PageElement {
|
||||||
|
public constructor(text: string) {
|
||||||
|
super(createElement(generate(text)));
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/page/basics/video/video.html.ts
Normal file
24
src/page/basics/video/video.html.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import './video.scss';
|
||||||
|
import { url } from '../../../types/url';
|
||||||
|
import { html } from '../../../types/html';
|
||||||
|
|
||||||
|
export const generate = ({
|
||||||
|
poster,
|
||||||
|
options,
|
||||||
|
webm,
|
||||||
|
mp4,
|
||||||
|
container,
|
||||||
|
}: {
|
||||||
|
poster: url;
|
||||||
|
options: string;
|
||||||
|
webm: url;
|
||||||
|
mp4: url;
|
||||||
|
container: boolean;
|
||||||
|
}): html => `
|
||||||
|
${container ? `<div class="figure-container">` : ''}
|
||||||
|
<video ${options} ${poster ? `poster="${poster}` : ''}" >
|
||||||
|
<source src="${webm}" type="video/webm"/>
|
||||||
|
<source src="${mp4}" type="video/mp4"/>
|
||||||
|
</video>
|
||||||
|
${container ? `</div>` : ''}
|
||||||
|
`;
|
||||||
0
src/page/basics/video/video.scss
Normal file
0
src/page/basics/video/video.scss
Normal file
16
src/page/basics/video/video.ts
Normal file
16
src/page/basics/video/video.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { PageElement } from '../../page-element';
|
||||||
|
import { createElement } from '../../../helper/create-element';
|
||||||
|
import { generate } from './video.html';
|
||||||
|
import { url } from '../../../types/url';
|
||||||
|
|
||||||
|
export class Video extends PageElement {
|
||||||
|
public constructor(
|
||||||
|
poster: url,
|
||||||
|
mp4: url,
|
||||||
|
webm: url,
|
||||||
|
options?: string,
|
||||||
|
container = true
|
||||||
|
) {
|
||||||
|
super(createElement(generate({ poster, mp4, webm, options, container })));
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/page/body/body.ts
Normal file
13
src/page/body/body.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { PageElement } from '../page-element';
|
||||||
|
import { OnLoadEvent } from '../../events/concrete-events/on-load-event';
|
||||||
|
import { OnEventBroadcasterChangedEvent } from '../../events/concrete-events/on-event-broadcaster-changed-event';
|
||||||
|
|
||||||
|
export class Body extends PageElement {
|
||||||
|
constructor(root: HTMLElement, children: Array<PageElement>) {
|
||||||
|
super(root);
|
||||||
|
children.forEach(c => this.attachElement(c));
|
||||||
|
|
||||||
|
this.broadcastEvent(new OnEventBroadcasterChangedEvent(this));
|
||||||
|
this.broadcastEvent(new OnLoadEvent(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { Content } from '../../model/portfolio';
|
|
||||||
import { html } from '../../framework/model/misc';
|
|
||||||
|
|
||||||
import './content.scss';
|
import './content.scss';
|
||||||
|
import { html } from '../../types/html';
|
||||||
|
|
||||||
export const generate = (content: Content): html => `
|
export const generate = (): html => `
|
||||||
<div class="content">
|
<div class="content"></div>
|
||||||
${content.map(element => element.toHTML()).join('\n')}
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { PageElement } from '../../framework/page-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
import { createElement } from '../../framework/helper/create-element';
|
import { Content } from '../../types/portfolio';
|
||||||
import { Content } from '../../model/portfolio';
|
|
||||||
import { generate } from './content.html';
|
import { generate } from './content.html';
|
||||||
|
import { PageElement } from '../page-element';
|
||||||
|
|
||||||
export class PageContent extends PageElement {
|
export class PageContent extends PageElement {
|
||||||
public constructor(content: Content) {
|
public constructor(content: Content) {
|
||||||
super(createElement(generate(content)));
|
super(createElement(generate()));
|
||||||
|
content.forEach(c => this.attachElement(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Footer } from '../../model/portfolio';
|
import { Footer } from '../../types/portfolio';
|
||||||
import { html } from '../../framework/model/misc';
|
|
||||||
|
|
||||||
import './footer.scss';
|
import './footer.scss';
|
||||||
|
import { html } from '../../types/html';
|
||||||
|
|
||||||
export const generate = ({
|
export const generate = ({
|
||||||
title,
|
title,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { Footer } from '../../model/portfolio';
|
import { Footer } from '../../types/portfolio';
|
||||||
import { PageElement } from '../../framework/page-element';
|
import { PageElement } from '../page-element';
|
||||||
|
|
||||||
import { generate } from './footer.html';
|
import { generate } from './footer.html';
|
||||||
import { createElement } from '../../framework/helper/create-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
|
|
||||||
export class PageFooter extends PageElement {
|
export class PageFooter extends PageElement {
|
||||||
constructor(footer: Footer) {
|
constructor(footer: Footer) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { html } from '../../framework/model/misc';
|
|
||||||
import cancel from '../../static/icons/cancel.svg';
|
import cancel from '../../static/icons/cancel.svg';
|
||||||
|
|
||||||
import './image-viewer.scss';
|
import './image-viewer.scss';
|
||||||
|
import { html } from '../../types/html';
|
||||||
|
|
||||||
export const generate = (): html => `
|
export const generate = (): html => `
|
||||||
<section id="image-viewer">
|
<section id="image-viewer">
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { PageElement } from '../../framework/page-element';
|
import { PageElement } from '../page-element';
|
||||||
|
|
||||||
import { generate } from './image-viewer.html';
|
import { generate } from './image-viewer.html';
|
||||||
import { createElement } from '../../framework/helper/create-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
import { OnLoadEvent } from '../../framework/events/concrete-events/on-load-event';
|
import { OnLoadEvent } from '../../events/concrete-events/on-load-event';
|
||||||
import { OptionalEvent } from '../../framework/events/optional-event';
|
import { OptionalEvent } from '../../events/optional-event';
|
||||||
|
|
||||||
export class PageImageViewer extends PageElement {
|
export class PageImageViewer extends PageElement {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super(createElement(generate()));
|
super(createElement(generate()));
|
||||||
this.element.onclick = () => PageImageViewer.hide(this.element);
|
this.htmlRoot.onclick = () => PageImageViewer.hide(this.htmlRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleOnLoadEvent(event: OnLoadEvent): OptionalEvent {
|
public handleOnLoadEvent(event: OnLoadEvent): OptionalEvent {
|
||||||
|
|
@ -17,7 +17,7 @@ export class PageImageViewer extends PageElement {
|
||||||
const media = Array.prototype.slice.call(document.querySelectorAll('img'));
|
const media = Array.prototype.slice.call(document.querySelectorAll('img'));
|
||||||
|
|
||||||
media
|
media
|
||||||
.filter((e: HTMLElement) => e.parentElement !== this.element)
|
.filter((e: HTMLElement) => e.parentElement !== this.htmlRoot)
|
||||||
.forEach((e: HTMLImageElement) => (e.onclick = this.handleClick.bind(this)));
|
.forEach((e: HTMLImageElement) => (e.onclick = this.handleClick.bind(this)));
|
||||||
return super.handleOnLoadEvent(event);
|
return super.handleOnLoadEvent(event);
|
||||||
}
|
}
|
||||||
|
|
@ -29,12 +29,12 @@ export class PageImageViewer extends PageElement {
|
||||||
const element: HTMLImageElement = new Image();
|
const element: HTMLImageElement = new Image();
|
||||||
element.src = (event.target as HTMLImageElement).src;
|
element.src = (event.target as HTMLImageElement).src;
|
||||||
container.appendChild(element);
|
container.appendChild(element);
|
||||||
PageImageViewer.show(this.element);
|
PageImageViewer.show(this.htmlRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleKeydown(event: KeyboardEvent) {
|
private handleKeydown(event: KeyboardEvent) {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
PageImageViewer.hide(this.element);
|
PageImageViewer.hide(this.htmlRoot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
51
src/page/page-element.ts
Normal file
51
src/page/page-element.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { EventHandler } from '../events/event-handler';
|
||||||
|
import { EventBroadcaster } from '../events/event-broadcaster';
|
||||||
|
import { OnEventBroadcasterChangedEvent } from '../events/concrete-events/on-event-broadcaster-changed-event';
|
||||||
|
import { OptionalEvent } from '../events/optional-event';
|
||||||
|
import { Event } from '../events/event';
|
||||||
|
import { OnLoadEvent } from '../events/concrete-events/on-load-event';
|
||||||
|
|
||||||
|
export abstract class PageElement extends EventHandler implements EventBroadcaster {
|
||||||
|
protected eventBroadcaster: EventBroadcaster;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public readonly htmlRoot?: HTMLElement,
|
||||||
|
protected children: Array<PageElement> = []
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public broadcastEvent(event: Event) {
|
||||||
|
event = this.handle(event);
|
||||||
|
if (event) {
|
||||||
|
this.children.forEach(c => c.broadcastEvent(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleOnEventBroadcasterChangedEvent(
|
||||||
|
event: OnEventBroadcasterChangedEvent
|
||||||
|
): OptionalEvent {
|
||||||
|
this.eventBroadcaster = event.broadcaster;
|
||||||
|
return super.handleOnEventBroadcasterChangedEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleOnLoadEvent(_: OnLoadEvent): OptionalEvent {
|
||||||
|
return super.handleOnLoadEvent(new OnLoadEvent(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected query(query: string): HTMLElement | null {
|
||||||
|
return this.htmlRoot?.querySelector(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected attachElementByReplacing(query: string, element: PageElement) {
|
||||||
|
const old = this.query(query);
|
||||||
|
old.parentElement.replaceChild(element.htmlRoot, old);
|
||||||
|
|
||||||
|
this.children.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected attachElement(element: PageElement) {
|
||||||
|
this.htmlRoot.appendChild(element.htmlRoot);
|
||||||
|
this.children.push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { html } from '../../framework/model/misc';
|
|
||||||
|
|
||||||
import './theme-switcher.scss';
|
import './theme-switcher.scss';
|
||||||
|
import { html } from '../../types/html';
|
||||||
|
|
||||||
export const generate = (): html => `
|
export const generate = (): html => `
|
||||||
<input id="theme-switcher" aria-label="color-theme-switch" type="checkbox" name="switch-theme"/>
|
<input id="theme-switcher" aria-label="color-theme-switch" type="checkbox" name="switch-theme"/>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,18 @@
|
||||||
import { PageElement } from '../../framework/page-element';
|
import { PageElement } from '../page-element';
|
||||||
import { createElement } from '../../framework/helper/create-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
import { generate } from './theme-switcher.html';
|
import { generate } from './theme-switcher.html';
|
||||||
import {
|
import {
|
||||||
isSystemLevelDarkModeEnabled,
|
isSystemLevelDarkModeEnabled,
|
||||||
turnOnDarkMode,
|
turnOnDarkMode,
|
||||||
turnOnLightMode,
|
turnOnLightMode,
|
||||||
} from '../../framework/styles/dark-mode/dark-mode';
|
} from '../../style/dark-mode/dark-mode';
|
||||||
import {
|
import { turnOffAnimations, turnOnAnimations } from '../../style/animations/animations';
|
||||||
turnOffAnimations,
|
import { OnLoadEvent } from '../../events/concrete-events/on-load-event';
|
||||||
turnOnAnimations,
|
import { OptionalEvent } from '../../events/optional-event';
|
||||||
} from '../../framework/styles/animations/animations';
|
import { OnPageThemeChangedEvent } from '../../events/concrete-events/on-page-theme-changed-event';
|
||||||
import { OnLoadEvent } from '../../framework/events/concrete-events/on-load-event';
|
|
||||||
import { OnPageThemeChangedEvent } from '../../framework/events/concrete-events/on-page-theme-changed-event';
|
|
||||||
import { OptionalEvent } from '../../framework/events/optional-event';
|
|
||||||
|
|
||||||
export class PageThemeSwitcher extends PageElement {
|
export class PageThemeSwitcher extends PageElement {
|
||||||
private static readonly LOCAL_STORAGE_KEY = 'dark-mode';
|
private static readonly localStorageKey = 'dark-mode';
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super(createElement(generate()));
|
super(createElement(generate()));
|
||||||
|
|
@ -24,14 +21,14 @@ export class PageThemeSwitcher extends PageElement {
|
||||||
const isDark = storedIsDark !== null ? storedIsDark : isSystemLevelDarkModeEnabled();
|
const isDark = storedIsDark !== null ? storedIsDark : isSystemLevelDarkModeEnabled();
|
||||||
|
|
||||||
if (isDark) {
|
if (isDark) {
|
||||||
(this.element as HTMLInputElement).checked = true;
|
(this.htmlRoot as HTMLInputElement).checked = true;
|
||||||
turnOffAnimations();
|
turnOffAnimations();
|
||||||
turnOnDarkMode();
|
turnOnDarkMode();
|
||||||
setTimeout(() => turnOnAnimations(), 0);
|
setTimeout(() => turnOnAnimations(), 0);
|
||||||
} else {
|
} else {
|
||||||
turnOnLightMode();
|
turnOnLightMode();
|
||||||
}
|
}
|
||||||
this.element.onchange = this.handleThemeChange.bind(this);
|
this.htmlRoot.onchange = this.handleThemeChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleOnLoadEvent(event: OnLoadEvent): OptionalEvent {
|
public handleOnLoadEvent(event: OnLoadEvent): OptionalEvent {
|
||||||
|
|
@ -40,7 +37,7 @@ export class PageThemeSwitcher extends PageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleThemeChange() {
|
private handleThemeChange() {
|
||||||
const isDark = (this.element as HTMLInputElement).checked;
|
const isDark = (this.htmlRoot as HTMLInputElement).checked;
|
||||||
if (isDark) {
|
if (isDark) {
|
||||||
turnOnDarkMode();
|
turnOnDarkMode();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -53,14 +50,14 @@ export class PageThemeSwitcher extends PageElement {
|
||||||
|
|
||||||
private static saveToLocalStorage(darkModeEnabled: boolean) {
|
private static saveToLocalStorage(darkModeEnabled: boolean) {
|
||||||
localStorage?.setItem(
|
localStorage?.setItem(
|
||||||
PageThemeSwitcher.LOCAL_STORAGE_KEY,
|
PageThemeSwitcher.localStorageKey,
|
||||||
JSON.stringify(darkModeEnabled)
|
JSON.stringify(darkModeEnabled)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static loadFromLocalStorage(): boolean | null {
|
private static loadFromLocalStorage(): boolean | null {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(localStorage?.getItem(PageThemeSwitcher.LOCAL_STORAGE_KEY));
|
return JSON.parse(localStorage?.getItem(PageThemeSwitcher.localStorageKey));
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { TimelineElement } from '../../../model/portfolio';
|
import { TimelineElement } from '../../../types/portfolio';
|
||||||
import { html } from '../../../framework/model/misc';
|
|
||||||
|
|
||||||
import './timeline-element.scss';
|
import './timeline-element.scss';
|
||||||
|
import { html } from '../../../types/html';
|
||||||
|
|
||||||
export const generate = (
|
export const generate = (
|
||||||
{ date, title, figure, description, more, link }: TimelineElement,
|
{ date, title, more }: TimelineElement,
|
||||||
showMore: string,
|
showMore: string,
|
||||||
showLess: string
|
showLess: string
|
||||||
): html => `
|
): html => `
|
||||||
|
|
@ -15,8 +15,8 @@ export const generate = (
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>${title}</h2>
|
<h2>${title}</h2>
|
||||||
${figure.toHTML(true)}
|
<div class="figure"></div>
|
||||||
${description.toHTML()}
|
<div class="description"></div>
|
||||||
${
|
${
|
||||||
more
|
more
|
||||||
? `
|
? `
|
||||||
|
|
@ -28,7 +28,7 @@ export const generate = (
|
||||||
`
|
`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
${link ? link.toHTML() : ''}
|
<div class="link"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { TimelineElement } from '../../../model/portfolio';
|
import { TimelineElement } from '../../../types/portfolio';
|
||||||
import { PageContent } from '../../content/content';
|
import { PageContent } from '../../content/content';
|
||||||
import { PageElement } from '../../../framework/page-element';
|
import { PageElement } from '../../page-element';
|
||||||
import { generate } from './timeline-element.html';
|
import { generate } from './timeline-element.html';
|
||||||
import { createElement } from '../../../framework/helper/create-element';
|
import { createElement } from '../../../helper/create-element';
|
||||||
import { OnBodyDimensionsChangedEvent } from '../../../framework/events/concrete-events/on-body-dimensions-changed-event';
|
import { OnBodyDimensionsChangedEvent } from '../../../events/concrete-events/on-body-dimensions-changed-event';
|
||||||
|
|
||||||
export class PageTimelineElement extends PageElement {
|
export class PageTimelineElement extends PageElement {
|
||||||
private isOpen: boolean;
|
private isOpen: boolean;
|
||||||
|
|
@ -18,15 +18,23 @@ export class PageTimelineElement extends PageElement {
|
||||||
|
|
||||||
if (timelineElement.more) {
|
if (timelineElement.more) {
|
||||||
const content = new PageContent(timelineElement.more);
|
const content = new PageContent(timelineElement.more);
|
||||||
super(root, [content]);
|
super(root);
|
||||||
|
this.children = [content];
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
this.more = root.querySelector('.more');
|
this.more = root.querySelector('.more');
|
||||||
this.more.appendChild(content.element);
|
this.more.appendChild(content.htmlRoot);
|
||||||
window.addEventListener('resize', this.handleResize.bind(this));
|
window.addEventListener('resize', this.handleResize.bind(this));
|
||||||
root
|
root
|
||||||
.querySelector('.buttons')
|
.querySelector('.buttons')
|
||||||
.addEventListener('click', this.toggleOpen.bind(this));
|
.addEventListener('click', this.toggleOpen.bind(this));
|
||||||
} else super(root);
|
} else super(root);
|
||||||
|
|
||||||
|
this.attachElementByReplacing('.figure', timelineElement.figure);
|
||||||
|
this.attachElementByReplacing('.description', timelineElement.description);
|
||||||
|
|
||||||
|
if (timelineElement.link) {
|
||||||
|
this.attachElementByReplacing('.link', timelineElement.link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleOpen() {
|
private toggleOpen() {
|
||||||
|
|
@ -46,15 +54,10 @@ export class PageTimelineElement extends PageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private notifyOfHeightChange(deltaHeight: number = undefined) {
|
private notifyOfHeightChange(deltaHeight: number = undefined) {
|
||||||
this.eventBroadcaster?.broadcastEvent(
|
this.eventBroadcaster?.broadcastEvent(new OnBodyDimensionsChangedEvent(deltaHeight));
|
||||||
new OnBodyDimensionsChangedEvent(deltaHeight)
|
|
||||||
);
|
|
||||||
|
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() =>
|
() => this.eventBroadcaster?.broadcastEvent(new OnBodyDimensionsChangedEvent()),
|
||||||
this.eventBroadcaster?.broadcastEvent(
|
|
||||||
new OnBodyDimensionsChangedEvent()
|
|
||||||
),
|
|
||||||
250
|
250
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { html } from '../../framework/model/misc';
|
|
||||||
import './timeline.scss';
|
import './timeline.scss';
|
||||||
|
import { html } from '../../types/html';
|
||||||
|
|
||||||
export const generate = (): html => `
|
export const generate = (): html => `
|
||||||
<div id="timeline"></div>
|
<div id="timeline"></div>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { Timeline } from '../../model/portfolio';
|
import { Timeline } from '../../types/portfolio';
|
||||||
import { PageTimelineElement } from './timeline-element/timeline-element';
|
import { PageTimelineElement } from './timeline-element/timeline-element';
|
||||||
import { generate } from './timeline.html';
|
import { generate } from './timeline.html';
|
||||||
import { createElement } from '../../framework/helper/create-element';
|
import { createElement } from '../../helper/create-element';
|
||||||
import { ContainerPage } from '../../framework/container-page';
|
import { PageElement } from '../page-element';
|
||||||
|
|
||||||
export class PageTimeline extends ContainerPage {
|
export class PageTimeline extends PageElement {
|
||||||
public constructor({ elements, showMoreText, showLessText }: Timeline) {
|
public constructor({ elements, showMoreText, showLessText }: Timeline) {
|
||||||
super(
|
super(createElement(generate()));
|
||||||
createElement(generate()),
|
elements.forEach(e =>
|
||||||
elements.map(e => new PageTimelineElement(e, showMoreText, showLessText))
|
this.attachElement(new PageTimelineElement(e, showMoreText, showLessText))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
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 { PageFooter } from './page/footer/footer';
|
import { PageFooter } from './page/footer/footer';
|
||||||
|
import { Video } from './page/basics/video/video';
|
||||||
|
import { Text } from './page/basics/text/text';
|
||||||
|
import { Image } from './page/basics/image/image';
|
||||||
import { PageHeader } from './page/about/about';
|
import { PageHeader } from './page/about/about';
|
||||||
import { PageTimeline } from './page/timeline/timeline';
|
import { PageTimeline } from './page/timeline/timeline';
|
||||||
import { PageImageViewer } from './page/image-viewer/image-viewer';
|
import { PageImageViewer } from './page/image-viewer/image-viewer';
|
||||||
import { last } from './framework/helper/last';
|
import { last } from './helper/last';
|
||||||
|
|
||||||
import me from './static/media/me.jpg';
|
import me from './static/media/me.jpg';
|
||||||
import forexMP4 from './static/media/forex.mp4';
|
import forexMP4 from './static/media/forex.mp4';
|
||||||
import forexWEBM from './static/media/forex.webm';
|
import forexWEBM from './static/media/forex.webm';
|
||||||
|
|
@ -26,15 +24,17 @@ import led from './static/media/led.jpg';
|
||||||
import cvEnglish from './static/cv/cv_andras_schmelczer.pdf';
|
import cvEnglish from './static/cv/cv_andras_schmelczer.pdf';
|
||||||
import ledMP4 from './static/media/led.mp4';
|
import ledMP4 from './static/media/led.mp4';
|
||||||
import ledWEBM from './static/media/led.webm';
|
import ledWEBM from './static/media/led.webm';
|
||||||
import { ContainerPage } from './framework/container-page';
|
|
||||||
import { PageBackground } from './page/background/background';
|
import { PageBackground } from './page/background/background';
|
||||||
|
import { Anchor } from './page/basics/anchor/anchor';
|
||||||
|
|
||||||
|
import { Body } from './page/body/body';
|
||||||
|
|
||||||
export const create = () => {
|
export const create = () => {
|
||||||
const page = {
|
const page = {
|
||||||
imageViewer: new PageImageViewer(),
|
imageViewer: new PageImageViewer(),
|
||||||
header: new PageHeader({
|
header: new PageHeader({
|
||||||
name: `András Schmelczer`,
|
name: `András Schmelczer`,
|
||||||
picture: new Image(me, `a picture of me`),
|
picture: new Image(me, `a picture of me`, false),
|
||||||
about: [
|
about: [
|
||||||
new Text(
|
new Text(
|
||||||
`I have always been fascinated by the engineering feats that surround us.
|
`I have always been fascinated by the engineering feats that surround us.
|
||||||
|
|
@ -308,12 +308,12 @@ export const create = () => {
|
||||||
curiumVitaes: [{ name: `Curriculum vitae`, url: cvEnglish }],
|
curiumVitaes: [{ name: `Curriculum vitae`, url: cvEnglish }],
|
||||||
email: `andras@schmelczer.dev`,
|
email: `andras@schmelczer.dev`,
|
||||||
lastEditText: `Last modified on `,
|
lastEditText: `Last modified on `,
|
||||||
lastEdit: new Date(2020, 9 - 1, 24), // months are 0 indexed
|
lastEdit: new Date(2020, 11 - 1, 17), // months are 0 indexed
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
new ContainerPage(document.body.querySelector('main'), [
|
new Body(document.querySelector('main'), [
|
||||||
...Object.values(page),
|
...Object.values(page),
|
||||||
new PageBackground(page.header, [page.timeline], page.footer),
|
new PageBackground(page.header, [page.timeline], page.footer),
|
||||||
]).setAsMain();
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,16 @@
|
||||||
@import '../framework/framework';
|
$breakpoint-width: 925px !default;
|
||||||
|
|
||||||
|
@mixin on-small-screen() {
|
||||||
|
@media (max-width: $breakpoint-width - 1px) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin on-large-screen() {
|
||||||
|
@media (min-width: $breakpoint-width) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@mixin center-children() {
|
@mixin center-children() {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
@import '../framework/framework';
|
@use 'mixins' as *;
|
||||||
|
@use 'dark-mode/dark-mode' as *;
|
||||||
|
@use 'animations/animations' as *;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--transition-time: 250ms;
|
--transition-time: 250ms;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
@use 'style/vars';
|
@use 'style/vars';
|
||||||
@use 'style/mixins' as *;
|
@use 'style/mixins' as *;
|
||||||
@use 'style/a';
|
@use 'style/a';
|
||||||
@use 'framework/styles/index';
|
@use 'style/animations/animations';
|
||||||
|
@use 'style/dark-mode/dark-mode';
|
||||||
|
|
||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -46,8 +47,7 @@ html {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transition: background-color linear var(--transition-time),
|
transition: background-color linear var(--transition-time), color var(--transition-time);
|
||||||
color var(--transition-time);
|
|
||||||
|
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +55,7 @@ html {
|
||||||
.figure-container {
|
.figure-container {
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
box-shadow: var(--inset-shadow);
|
box-shadow: var(--inset-shadow);
|
||||||
|
margin-top: var(--line-height);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|
@ -68,19 +69,9 @@ html {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.primitive-text,
|
|
||||||
.primitive-anchor,
|
|
||||||
.figure-container {
|
|
||||||
margin-top: var(--line-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
.primitive-text {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding: env(safe-area-inset-top) env(safe-area-inset-right)
|
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom)
|
||||||
env(safe-area-inset-bottom) env(safe-area-inset-left);
|
env(safe-area-inset-left);
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
|
@ -100,7 +91,7 @@ body {
|
||||||
@include center-children();
|
@include center-children();
|
||||||
}
|
}
|
||||||
|
|
||||||
@include on-large-screen {
|
@media (hover: hover) {
|
||||||
&::-webkit-scrollbar-track,
|
&::-webkit-scrollbar-track,
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
|
||||||
1
src/types/html.ts
Normal file
1
src/types/html.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export type html = string;
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { url } from '../framework/model/misc';
|
import { Video } from '../page/basics/video/video';
|
||||||
import { Primitive } from '../framework/primitives/primitive';
|
import { Text } from '../page/basics/text/text';
|
||||||
import { Image } from '../framework/primitives/implementations/image';
|
import { Image } from '../page/basics/image/image';
|
||||||
import { Anchor } from '../framework/primitives/implementations/anchor';
|
import { Anchor } from '../page/basics/anchor/anchor';
|
||||||
import { Video } from '../framework/primitives/implementations/video';
|
import { PageElement } from '../page/page-element';
|
||||||
import { Text } from '../framework/primitives/implementations/text';
|
import { url } from './url';
|
||||||
|
|
||||||
export interface Portfolio {
|
export interface Portfolio {
|
||||||
header: Header;
|
header: Header;
|
||||||
|
|
@ -45,4 +45,4 @@ export interface CV {
|
||||||
url: url;
|
url: url;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Content = Array<Primitive>;
|
export type Content = Array<PageElement>;
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
export type url = string;
|
import { url } from './url';
|
||||||
|
|
||||||
export type html = string;
|
|
||||||
|
|
||||||
export type ResponsiveImage = {
|
export type ResponsiveImage = {
|
||||||
srcSet: string;
|
srcSet: string;
|
||||||
1
src/types/url.ts
Normal file
1
src/types/url.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export type url = string;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue