Refactor
This commit is contained in:
parent
f054546aa6
commit
48a55a4a97
51 changed files with 604 additions and 577 deletions
0
src/framework/cache.ts
Normal file
0
src/framework/cache.ts
Normal file
18
src/framework/container-page.ts
Normal file
18
src/framework/container-page.ts
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
5
src/framework/event-broadcaster.ts
Normal file
5
src/framework/event-broadcaster.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { PageEvent } from './page-event';
|
||||
|
||||
export interface EventBroadcaster {
|
||||
broadcastEvent(event: PageEvent, parent?: EventBroadcaster);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) +
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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('');
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/framework/primitives/implementations/anchor.ts
Normal file
13
src/framework/primitives/implementations/anchor.ts
Normal 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>`;
|
||||
}
|
||||
}
|
||||
22
src/framework/primitives/implementations/image.ts
Normal file
22
src/framework/primitives/implementations/image.ts
Normal 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>` : ''}
|
||||
`;
|
||||
}
|
||||
}
|
||||
10
src/framework/primitives/implementations/text.ts
Normal file
10
src/framework/primitives/implementations/text.ts
Normal 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>`;
|
||||
}
|
||||
}
|
||||
21
src/framework/primitives/implementations/video.ts
Normal file
21
src/framework/primitives/implementations/video.ts
Normal 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>` : ''}
|
||||
`;
|
||||
}
|
||||
}
|
||||
7
src/framework/primitives/primitive.ts
Normal file
7
src/framework/primitives/primitive.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { html } from '../../model/misc';
|
||||
|
||||
import './primitives.scss';
|
||||
|
||||
export interface Primitive {
|
||||
toHTML(): html;
|
||||
}
|
||||
25
src/framework/primitives/primitives.scss
Normal file
25
src/framework/primitives/primitives.scss
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue