This commit is contained in:
Schmelczer András 2020-01-06 21:40:25 +01:00
parent f054546aa6
commit 48a55a4a97
51 changed files with 604 additions and 577 deletions

0
src/framework/cache.ts Normal file
View file

View file

@ -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<PageElement>) {
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
);
}
}

View file

@ -0,0 +1,5 @@
import { PageEvent } from './page-event';
export interface EventBroadcaster {
broadcastEvent(event: PageEvent, parent?: EventBroadcaster);
}

View file

@ -1,6 +1,7 @@
import { html } from '../../model/misc';
export const createElement = (from: html): HTMLElement => {
// won't work for all elements, eg.: <td>
const element: HTMLElement = document.createElement('div');
element.innerHTML = from;
return element.firstElementChild as HTMLElement;

View file

@ -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) +

View file

@ -1,2 +1,2 @@
export const last = <T>(list: Array<T>): T =>
list.length > 0 ? list[list.length - 1] : undefined;
list.length >= 1 ? list[list.length - 1] : undefined;

View file

@ -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('');

View file

@ -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<T>(list: Array<T>): 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;
}
}

View file

@ -1,15 +0,0 @@
export const range = ({
from = 0,
to = Infinity,
step = 1,
}: {
from?: number;
to?: number;
step?: number;
}): Iterable<number> => {
return {
*[Symbol.iterator]() {
for (let i = from; i < to; yield i, i += step) {}
},
};
};

View file

@ -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<PageElement> = []
) {}
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<PageElement> = []) {}
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);
}
}

View file

@ -1,28 +0,0 @@
import { PageElement } from "./page-element";
import { PageEventType } from "./page-event";
export class Page extends PageElement {
public constructor(
children: Array<PageElement>,
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);
}
}
}

View file

@ -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 `<a class="primitive-anchor" href="${this.href}" rel="noreferrer" target="_blank">${this.text}</a>`;
}
}

View file

@ -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 ? `<div class="figure-container">` : ''}
<img
srcset="${this.image.srcSet}"
src="${last(this.image.images)?.path}"
alt="${this.alt}"
/>
${!disableInnerShadow ? `</div>` : ''}
`;
}
}

View file

@ -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 `<p class="primitive-text">${this.text}</p>`;
}
}

View file

@ -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 ? `<div class="figure-container">` : ''}
<video ${this.options} poster="${this.poster}">
<source src="${this.webm}" type="video/webm"/>
<source src="${this.mp4}" type="video/mp4"/>
</video>
${!disableInnerShadow ? `</div>` : ''}
`;
}
}

View file

@ -0,0 +1,7 @@
import { html } from '../../model/misc';
import './primitives.scss';
export interface Primitive {
toHTML(): html;
}

View file

@ -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;
}