Initial commit

This commit is contained in:
undergroundwires
2019-12-31 16:09:39 +01:00
commit 4e7f244190
108 changed files with 17296 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
export class Clipboard {
public static copyText(text: string): void {
const el = document.createElement('textarea');
el.value = text;
el.setAttribute('readonly', ''); // to avoid focus
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
}
}

View File

@@ -0,0 +1,14 @@
import { IEntity } from './IEntity';
export abstract class BaseEntity<TId> implements IEntity<TId> {
constructor(public id: TId) {
if (typeof id !== 'number' && !id) {
throw new Error('Id cannot be null or empty');
}
}
public equals(otherId: TId): boolean {
return this.id === otherId;
}
}

View File

@@ -0,0 +1,5 @@
/** Aggregate root */
export interface IEntity<TId> {
id: TId;
equals(other: TId): boolean;
}

View File

@@ -0,0 +1,4 @@
export interface ISignal<T> {
on(handler: (data: T) => void): void;
off(handler: (data: T) => void): void;
}

View File

@@ -0,0 +1,18 @@
import { ISignal } from './ISignal';
export { ISignal };
export class Signal<T> implements ISignal<T> {
private handlers: Array<(data: T) => void> = [];
public on(handler: (data: T) => void): void {
this.handlers.push(handler);
}
public off(handler: (data: T) => void): void {
this.handlers = this.handlers.filter((h) => h !== handler);
}
public notify(data: T) {
this.handlers.slice(0).forEach((h) => h(data));
}
}

View File

@@ -0,0 +1,9 @@
import { IEntity } from '../Entity/IEntity';
export interface IRepository<TKey, TEntity extends IEntity<TKey>> {
readonly length: number;
getItems(predicate?: (entity: TEntity) => boolean): TEntity[];
addItem(item: TEntity): void;
removeItem(id: TKey): void;
exists(item: TEntity): boolean;
}

View File

@@ -0,0 +1,40 @@
import { IEntity } from '../Entity/IEntity';
import { IRepository } from './IRepository';
export class InMemoryRepository<TKey, TEntity extends IEntity<TKey>> implements IRepository<TKey, TEntity> {
private readonly items: TEntity[];
constructor(items?: TEntity[]) {
this.items = items || new Array<TEntity>();
}
public get length(): number {
return this.items.length;
}
public getItems(predicate?: (entity: TEntity) => boolean): TEntity[] {
return predicate ? this.items.filter(predicate) : this.items;
}
public addItem(item: TEntity): void {
if (!item) {
throw new Error('Item is null');
}
if (this.exists(item)) {
throw new Error(`Cannot add (id: ${item.id}) as it is already exists`);
}
this.items.push(item);
}
public removeItem(id: TKey): void {
const index = this.items.findIndex((item) => item.id === id);
if (index === -1) {
throw new Error(`Cannot remove (id: ${id}) as it does not exist`);
}
this.items.splice(index, 1);
}
public exists(entity: TEntity): boolean {
const index = this.items.findIndex((item) => item.id === entity.id);
return index !== -1;
}
}

View File

@@ -0,0 +1,16 @@
import fileSaver from 'file-saver';
export class SaveFileDialog {
public static saveText(text: string, fileName: string): void {
this.saveBlob(text, 'text/plain;charset=utf-8', fileName);
}
private static saveBlob(file: BlobPart, fileType: string, fileName: string): void {
try {
const blob = new Blob([file], { type: fileType });
fileSaver.saveAs(blob, fileName);
} catch (e) {
window.open('data:' + fileType + ',' + encodeURIComponent(file.toString()), '_blank', '');
}
}
}

View File

@@ -0,0 +1,34 @@
import { Signal } from '../Events/Signal';
export class AsyncLazy<T> {
private valueCreated = new Signal();
private isValueCreated = false;
private isCreatingValue = false;
private value: T | undefined;
constructor(private valueFactory: () => Promise<T>) { }
public setValueFactory(valueFactory: () => Promise<T>) {
this.valueFactory = valueFactory;
}
public async getValueAsync(): Promise<T> {
// If value is already created, return the value directly
if (this.isValueCreated) {
return Promise.resolve(this.value as T);
}
// If value is being created, wait until the value is created and then return it.
if (this.isCreatingValue) {
return new Promise<T>((resolve, reject) => {
// Return/result when valueCreated event is triggered.
this.valueCreated.on(() => resolve(this.value));
});
}
this.isCreatingValue = true;
this.value = await this.valueFactory();
this.isCreatingValue = false;
this.isValueCreated = true;
this.valueCreated.notify(null);
return Promise.resolve(this.value);
}
}